summaryrefslogtreecommitdiffstats
path: root/sfx2/source
diff options
context:
space:
mode:
Diffstat (limited to 'sfx2/source')
-rw-r--r--sfx2/source/accessibility/AccessibilityCheck.cxx24
-rw-r--r--sfx2/source/accessibility/AccessibilityIssue.cxx29
-rw-r--r--sfx2/source/appl/app.cxx561
-rw-r--r--sfx2/source/appl/appbas.cxx154
-rw-r--r--sfx2/source/appl/appbaslib.cxx188
-rw-r--r--sfx2/source/appl/appcfg.cxx738
-rw-r--r--sfx2/source/appl/appchild.cxx66
-rw-r--r--sfx2/source/appl/appdata.cxx127
-rw-r--r--sfx2/source/appl/appdde.cxx570
-rw-r--r--sfx2/source/appl/appdispatchprovider.cxx231
-rw-r--r--sfx2/source/appl/appinit.cxx233
-rw-r--r--sfx2/source/appl/appmain.cxx38
-rw-r--r--sfx2/source/appl/appmisc.cxx210
-rw-r--r--sfx2/source/appl/appopen.cxx1146
-rw-r--r--sfx2/source/appl/appquit.cxx103
-rw-r--r--sfx2/source/appl/appreg.cxx105
-rw-r--r--sfx2/source/appl/appserv.cxx1726
-rw-r--r--sfx2/source/appl/appuno.cxx1842
-rw-r--r--sfx2/source/appl/childwin.cxx615
-rw-r--r--sfx2/source/appl/fileobj.cxx435
-rw-r--r--sfx2/source/appl/fileobj.hxx82
-rw-r--r--sfx2/source/appl/flatpak.cxx99
-rw-r--r--sfx2/source/appl/fwkhelper.cxx52
-rw-r--r--sfx2/source/appl/getbasctlfunction.cxx64
-rw-r--r--sfx2/source/appl/getbasctlfunction.hxx38
-rw-r--r--sfx2/source/appl/helpdispatch.cxx105
-rw-r--r--sfx2/source/appl/helpdispatch.hxx47
-rw-r--r--sfx2/source/appl/helpinterceptor.cxx262
-rw-r--r--sfx2/source/appl/helpinterceptor.hxx141
-rw-r--r--sfx2/source/appl/impldde.cxx348
-rw-r--r--sfx2/source/appl/impldde.hxx72
-rw-r--r--sfx2/source/appl/linkmgr2.cxx714
-rw-r--r--sfx2/source/appl/linksrc.cxx416
-rw-r--r--sfx2/source/appl/lnkbase2.cxx600
-rw-r--r--sfx2/source/appl/macroloader.cxx344
-rw-r--r--sfx2/source/appl/module.cxx267
-rw-r--r--sfx2/source/appl/newhelp.cxx2681
-rw-r--r--sfx2/source/appl/newhelp.hxx511
-rw-r--r--sfx2/source/appl/opengrf.cxx284
-rw-r--r--sfx2/source/appl/openuriexternally.cxx140
-rw-r--r--sfx2/source/appl/preventduplicateinteraction.cxx219
-rw-r--r--sfx2/source/appl/sfxhelp.cxx1406
-rw-r--r--sfx2/source/appl/sfxpicklist.cxx226
-rw-r--r--sfx2/source/appl/shutdownicon.cxx682
-rw-r--r--sfx2/source/appl/shutdownicon.hxx159
-rw-r--r--sfx2/source/appl/shutdowniconaqua.mm486
-rw-r--r--sfx2/source/appl/shutdowniconw32.cxx803
-rw-r--r--sfx2/source/appl/workwin.cxx2430
-rw-r--r--sfx2/source/appl/xpackcreator.cxx164
-rw-r--r--sfx2/source/bastyp/bitset.cxx108
-rw-r--r--sfx2/source/bastyp/fltfnc.cxx1111
-rw-r--r--sfx2/source/bastyp/fltlst.cxx118
-rw-r--r--sfx2/source/bastyp/fltlst.hxx50
-rw-r--r--sfx2/source/bastyp/frmhtml.cxx101
-rw-r--r--sfx2/source/bastyp/frmhtmlw.cxx294
-rw-r--r--sfx2/source/bastyp/helper.cxx229
-rw-r--r--sfx2/source/bastyp/mieclip.cxx103
-rw-r--r--sfx2/source/bastyp/progress.cxx401
-rw-r--r--sfx2/source/bastyp/sfxhtml.cxx345
-rw-r--r--sfx2/source/bastyp/sfxresid.cxx24
-rw-r--r--sfx2/source/commandpopup/CommandPopup.cxx293
-rw-r--r--sfx2/source/config/evntconf.cxx220
-rw-r--r--sfx2/source/control/bindings.cxx1783
-rw-r--r--sfx2/source/control/charmapcontrol.cxx222
-rw-r--r--sfx2/source/control/charwin.cxx263
-rw-r--r--sfx2/source/control/ctrlitem.cxx344
-rw-r--r--sfx2/source/control/dispatch.cxx2076
-rw-r--r--sfx2/source/control/emojicontrol.cxx156
-rw-r--r--sfx2/source/control/emojipopup.cxx73
-rw-r--r--sfx2/source/control/emojiview.cxx224
-rw-r--r--sfx2/source/control/emojiviewitem.cxx84
-rw-r--r--sfx2/source/control/itemdel.cxx77
-rw-r--r--sfx2/source/control/listview.cxx443
-rw-r--r--sfx2/source/control/minfitem.cxx75
-rw-r--r--sfx2/source/control/msg.cxx56
-rw-r--r--sfx2/source/control/msgpool.cxx326
-rw-r--r--sfx2/source/control/objface.cxx443
-rw-r--r--sfx2/source/control/recentdocsview.cxx314
-rw-r--r--sfx2/source/control/recentdocsviewitem.cxx348
-rw-r--r--sfx2/source/control/recentdocsviewitem.hxx67
-rw-r--r--sfx2/source/control/request.cxx763
-rw-r--r--sfx2/source/control/sfxstatuslistener.cxx219
-rw-r--r--sfx2/source/control/shell.cxx743
-rw-r--r--sfx2/source/control/sorgitm.cxx85
-rw-r--r--sfx2/source/control/statcach.cxx503
-rw-r--r--sfx2/source/control/templatecontaineritem.cxx20
-rw-r--r--sfx2/source/control/templatedefaultview.cxx82
-rw-r--r--sfx2/source/control/templatedlglocalview.cxx416
-rw-r--r--sfx2/source/control/templatelocalview.cxx945
-rw-r--r--sfx2/source/control/templateviewitem.cxx124
-rw-r--r--sfx2/source/control/thumbnailview.cxx1220
-rw-r--r--sfx2/source/control/thumbnailviewacc.cxx874
-rw-r--r--sfx2/source/control/thumbnailviewacc.hxx215
-rw-r--r--sfx2/source/control/thumbnailviewitem.cxx314
-rw-r--r--sfx2/source/control/unoctitm.cxx1285
-rw-r--r--sfx2/source/devtools/DevToolsStrings.hrc73
-rw-r--r--sfx2/source/devtools/DevelopmentToolChildWindow.cxx30
-rw-r--r--sfx2/source/devtools/DevelopmentToolDockingWindow.cxx155
-rw-r--r--sfx2/source/devtools/DocumentModelTreeHandler.cxx840
-rw-r--r--sfx2/source/devtools/ObjectInspectorTreeHandler.cxx1384
-rw-r--r--sfx2/source/devtools/SelectionChangeHandler.hxx74
-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
-rw-r--r--sfx2/source/doc/DocumentMetadataAccess.cxx1381
-rw-r--r--sfx2/source/doc/DocumentSigner.cxx121
-rw-r--r--sfx2/source/doc/Metadatable.cxx1602
-rw-r--r--sfx2/source/doc/QuerySaveDocument.cxx39
-rw-r--r--sfx2/source/doc/SfxDocumentMetaData.cxx2214
-rw-r--r--sfx2/source/doc/SfxRedactionHelper.cxx562
-rw-r--r--sfx2/source/doc/autoredactdialog.cxx770
-rw-r--r--sfx2/source/doc/docfac.cxx354
-rw-r--r--sfx2/source/doc/docfile.cxx4752
-rw-r--r--sfx2/source/doc/docfilt.cxx199
-rw-r--r--sfx2/source/doc/docinf.cxx324
-rw-r--r--sfx2/source/doc/docinsert.cxx292
-rw-r--r--sfx2/source/doc/docmacromode.cxx431
-rw-r--r--sfx2/source/doc/docstoragemodifylistener.cxx73
-rw-r--r--sfx2/source/doc/doctempl.cxx1745
-rw-r--r--sfx2/source/doc/doctemplates.cxx2734
-rw-r--r--sfx2/source/doc/doctemplateslocal.cxx213
-rw-r--r--sfx2/source/doc/doctemplateslocal.hxx78
-rw-r--r--sfx2/source/doc/docundomanager.cxx423
-rw-r--r--sfx2/source/doc/exoticfileloadexception.cxx37
-rw-r--r--sfx2/source/doc/exoticfileloadexception.hxx43
-rw-r--r--sfx2/source/doc/frmdescr.cxx57
-rw-r--r--sfx2/source/doc/graphhelp.cxx260
-rw-r--r--sfx2/source/doc/graphhelp.hxx71
-rw-r--r--sfx2/source/doc/guisaveas.cxx1857
-rw-r--r--sfx2/source/doc/iframe.cxx444
-rw-r--r--sfx2/source/doc/new.cxx350
-rw-r--r--sfx2/source/doc/objcont.cxx712
-rw-r--r--sfx2/source/doc/objembed.cxx217
-rw-r--r--sfx2/source/doc/objitem.cxx97
-rw-r--r--sfx2/source/doc/objmisc.cxx1920
-rw-r--r--sfx2/source/doc/objserv.cxx2131
-rw-r--r--sfx2/source/doc/objstor.cxx3791
-rw-r--r--sfx2/source/doc/objstor.hxx29
-rw-r--r--sfx2/source/doc/objxtor.cxx1118
-rw-r--r--sfx2/source/doc/oleprops.cxx1240
-rw-r--r--sfx2/source/doc/oleprops.hxx391
-rw-r--r--sfx2/source/doc/ownsubfilterservice.cxx115
-rw-r--r--sfx2/source/doc/printhelper.cxx807
-rw-r--r--sfx2/source/doc/printhelper.hxx68
-rw-r--r--sfx2/source/doc/saveastemplatedlg.cxx178
-rw-r--r--sfx2/source/doc/sfxbasemodel.cxx4561
-rw-r--r--sfx2/source/doc/sfxmodelfactory.cxx110
-rw-r--r--sfx2/source/doc/signaturestate.cxx59
-rw-r--r--sfx2/source/doc/syspath.cxx37
-rw-r--r--sfx2/source/doc/syspath.hxx32
-rw-r--r--sfx2/source/doc/syspathw32.cxx69
-rw-r--r--sfx2/source/doc/syspathw32.hxx33
-rw-r--r--sfx2/source/doc/templatedlg.cxx1396
-rw-r--r--sfx2/source/doc/watermarkitem.cxx82
-rw-r--r--sfx2/source/doc/zoomitem.cxx172
-rw-r--r--sfx2/source/explorer/nochaos.cxx187
-rw-r--r--sfx2/source/inc/StyleList.hxx239
-rw-r--r--sfx2/source/inc/alienwarn.hxx39
-rw-r--r--sfx2/source/inc/appbaslib.hxx98
-rw-r--r--sfx2/source/inc/appdata.hxx148
-rw-r--r--sfx2/source/inc/appopen.hxx36
-rw-r--r--sfx2/source/inc/asyncfunc.hxx36
-rw-r--r--sfx2/source/inc/documentfontsdialog.hxx49
-rw-r--r--sfx2/source/inc/docundomanager.hxx159
-rw-r--r--sfx2/source/inc/eventsupplier.hxx91
-rw-r--r--sfx2/source/inc/fltoptint.hxx62
-rw-r--r--sfx2/source/inc/helper.hxx41
-rw-r--r--sfx2/source/inc/helpids.h49
-rw-r--r--sfx2/source/inc/hintpost.hxx59
-rw-r--r--sfx2/source/inc/itemdel.hxx30
-rw-r--r--sfx2/source/inc/macroloader.hxx88
-rw-r--r--sfx2/source/inc/nochaos.hxx35
-rw-r--r--sfx2/source/inc/objshimp.hxx153
-rw-r--r--sfx2/source/inc/openflag.hxx33
-rw-r--r--sfx2/source/inc/openuriexternally.hxx40
-rw-r--r--sfx2/source/inc/openurlhint.hxx38
-rw-r--r--sfx2/source/inc/partwnd.hxx63
-rw-r--r--sfx2/source/inc/preview.hxx41
-rw-r--r--sfx2/source/inc/recfloat.hxx61
-rw-r--r--sfx2/source/inc/sfxpicklist.hxx42
-rw-r--r--sfx2/source/inc/sfxtypes.hxx51
-rw-r--r--sfx2/source/inc/sfxurlrelocator.hxx52
-rw-r--r--sfx2/source/inc/slotserv.hxx59
-rw-r--r--sfx2/source/inc/splitwin.hxx125
-rw-r--r--sfx2/source/inc/statcach.hxx154
-rw-r--r--sfx2/source/inc/templatesearchviewitem.hxx28
-rw-r--r--sfx2/source/inc/templdgi.hxx242
-rw-r--r--sfx2/source/inc/tplcitem.hxx45
-rw-r--r--sfx2/source/inc/versdlg.hxx95
-rw-r--r--sfx2/source/inc/workwin.hxx300
-rw-r--r--sfx2/source/inet/inettbc.cxx275
-rw-r--r--sfx2/source/notebookbar/NotebookbarTabControl.cxx385
-rw-r--r--sfx2/source/notebookbar/SfxNotebookBar.cxx584
-rw-r--r--sfx2/source/notify/eventsupplier.cxx471
-rw-r--r--sfx2/source/notify/globalevents.cxx521
-rw-r--r--sfx2/source/notify/hintpost.cxx53
-rw-r--r--sfx2/source/notify/openurlhint.cxx32
-rw-r--r--sfx2/source/safemode/safemode.cxx81
-rw-r--r--sfx2/source/sidebar/AsynchronousCall.cxx71
-rw-r--r--sfx2/source/sidebar/Context.cxx78
-rw-r--r--sfx2/source/sidebar/ContextChangeBroadcaster.cxx127
-rw-r--r--sfx2/source/sidebar/ContextList.cxx103
-rw-r--r--sfx2/source/sidebar/ControllerFactory.cxx239
-rw-r--r--sfx2/source/sidebar/ControllerItem.cxx71
-rw-r--r--sfx2/source/sidebar/Deck.cxx287
-rw-r--r--sfx2/source/sidebar/DeckDescriptor.cxx53
-rw-r--r--sfx2/source/sidebar/DeckLayouter.cxx555
-rw-r--r--sfx2/source/sidebar/DeckTitleBar.cxx124
-rw-r--r--sfx2/source/sidebar/FocusManager.cxx505
-rw-r--r--sfx2/source/sidebar/IContextChangeReceiver.cxx29
-rw-r--r--sfx2/source/sidebar/ILayoutableWindow.cxx29
-rw-r--r--sfx2/source/sidebar/Panel.cxx240
-rw-r--r--sfx2/source/sidebar/PanelDescriptor.cxx57
-rw-r--r--sfx2/source/sidebar/PanelLayout.cxx86
-rw-r--r--sfx2/source/sidebar/PanelTitleBar.cxx123
-rw-r--r--sfx2/source/sidebar/ResourceManager.cxx802
-rw-r--r--sfx2/source/sidebar/Sidebar.cxx128
-rw-r--r--sfx2/source/sidebar/SidebarChildWindow.cxx95
-rw-r--r--sfx2/source/sidebar/SidebarController.cxx1643
-rw-r--r--sfx2/source/sidebar/SidebarDockingWindow.cxx205
-rw-r--r--sfx2/source/sidebar/SidebarModelUpdate.cxx17
-rw-r--r--sfx2/source/sidebar/SidebarPanelBase.cxx197
-rw-r--r--sfx2/source/sidebar/SidebarToolBox.cxx322
-rw-r--r--sfx2/source/sidebar/TabBar.cxx376
-rw-r--r--sfx2/source/sidebar/Theme.cxx675
-rw-r--r--sfx2/source/sidebar/TitleBar.cxx80
-rw-r--r--sfx2/source/sidebar/Tools.cxx113
-rw-r--r--sfx2/source/sidebar/UnoDeck.cxx281
-rw-r--r--sfx2/source/sidebar/UnoDecks.cxx143
-rw-r--r--sfx2/source/sidebar/UnoPanel.cxx295
-rw-r--r--sfx2/source/sidebar/UnoPanels.cxx155
-rw-r--r--sfx2/source/sidebar/UnoSidebar.cxx95
-rw-r--r--sfx2/source/statbar/stbitem.cxx553
-rw-r--r--sfx2/source/styles/StyleManager.cxx35
-rw-r--r--sfx2/source/toolbox/tbxitem.cxx506
-rw-r--r--sfx2/source/toolbox/weldutils.cxx203
-rw-r--r--sfx2/source/view/classificationcontroller.cxx364
-rw-r--r--sfx2/source/view/classificationhelper.cxx985
-rw-r--r--sfx2/source/view/frame.cxx719
-rw-r--r--sfx2/source/view/frame2.cxx404
-rw-r--r--sfx2/source/view/frmload.cxx829
-rw-r--r--sfx2/source/view/impframe.hxx71
-rw-r--r--sfx2/source/view/impviewframe.hxx81
-rw-r--r--sfx2/source/view/ipclient.cxx1147
-rw-r--r--sfx2/source/view/lokcharthelper.cxx369
-rw-r--r--sfx2/source/view/lokhelper.cxx854
-rw-r--r--sfx2/source/view/lokstarmathhelper.cxx174
-rw-r--r--sfx2/source/view/printer.cxx189
-rw-r--r--sfx2/source/view/prnmon.hxx54
-rw-r--r--sfx2/source/view/sfxbasecontroller.cxx1539
-rw-r--r--sfx2/source/view/userinputinterception.cxx263
-rw-r--r--sfx2/source/view/viewfac.cxx56
-rw-r--r--sfx2/source/view/viewfrm.cxx3527
-rw-r--r--sfx2/source/view/viewfrm2.cxx379
-rw-r--r--sfx2/source/view/viewimp.hxx68
-rw-r--r--sfx2/source/view/viewprn.cxx916
-rw-r--r--sfx2/source/view/viewsh.cxx2092
294 files changed, 135704 insertions, 0 deletions
diff --git a/sfx2/source/accessibility/AccessibilityCheck.cxx b/sfx2/source/accessibility/AccessibilityCheck.cxx
new file mode 100644
index 000000000..adb0ddf28
--- /dev/null
+++ b/sfx2/source/accessibility/AccessibilityCheck.cxx
@@ -0,0 +1,24 @@
+/* -*- 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/AccessibilityCheck.hxx>
+
+namespace sfx
+{
+AccessibilityCheck::~AccessibilityCheck() = default;
+
+AccessibilityIssueCollection& AccessibilityCheck::getIssueCollection()
+{
+ return m_aIssueCollection;
+}
+
+} // end sfx namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/accessibility/AccessibilityIssue.cxx b/sfx2/source/accessibility/AccessibilityIssue.cxx
new file mode 100644
index 000000000..b276faf54
--- /dev/null
+++ b/sfx2/source/accessibility/AccessibilityIssue.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/.
+ *
+ */
+
+#include <sfx2/AccessibilityIssue.hxx>
+
+namespace sfx
+{
+AccessibilityIssue::AccessibilityIssue(AccessibilityIssueID eIssueID)
+ : m_eIssueID(eIssueID)
+{
+}
+
+AccessibilityIssue::~AccessibilityIssue() {}
+
+std::vector<std::shared_ptr<AccessibilityIssue>>& AccessibilityIssueCollection::getIssues()
+{
+ return m_aIssues;
+}
+
+} // end sfx namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/appl/app.cxx b/sfx2/source/appl/app.cxx
new file mode 100644
index 000000000..4a85e28f2
--- /dev/null
+++ b/sfx2/source/appl/app.cxx
@@ -0,0 +1,561 @@
+/* -*- 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/log.hxx>
+#include <tools/debug.hxx>
+
+#include <sfx2/app.hxx>
+#include <sfx2/frame.hxx>
+#include <basic/sberrors.hxx>
+
+#include <svl/svdde.hxx>
+#include <unotools/configmgr.hxx>
+#include <com/sun/star/frame/XFrame.hpp>
+#include <comphelper/lok.hxx>
+#include <comphelper/processfactory.hxx>
+#include <com/sun/star/uri/UriReferenceFactory.hpp>
+#include <com/sun/star/uri/XVndSunStarScriptUrl.hpp>
+#include <basic/basmgr.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+#include <sfx2/sfxhelp.hxx>
+#include <sfx2/progress.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/viewsh.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <appdata.hxx>
+#include <sfx2/module.hxx>
+#include <sfx2/event.hxx>
+#include <workwin.hxx>
+#include <sfx2/sidebar/Theme.hxx>
+#include <sfx2/tbxctrl.hxx>
+#include <sfx2/sfxdlg.hxx>
+#include <sfx2/stbitem.hxx>
+#include <sfx2/dockwin.hxx>
+#include <sidebar/ControllerFactory.hxx>
+
+#include <officecfg/Office/Common.hxx>
+#include <rtl/strbuf.hxx>
+#include <memory>
+#include <mutex>
+#include <framework/sfxhelperfunctions.hxx>
+#include <fwkhelper.hxx>
+
+#include "getbasctlfunction.hxx"
+
+using namespace ::com::sun::star;
+
+static SfxApplication* g_pSfxApplication = nullptr;
+
+#if HAVE_FEATURE_XMLHELP
+static SfxHelp* pSfxHelp = nullptr;
+#endif
+
+SfxApplication* SfxApplication::Get()
+{
+ return g_pSfxApplication;
+}
+
+void SfxApplication::SetModule(SfxToolsModule nSharedLib, std::unique_ptr<SfxModule> pModule)
+{
+ assert(g_pSfxApplication != nullptr);
+
+ g_pSfxApplication->pImpl->aModules[nSharedLib] = std::move(pModule);
+}
+
+SfxModule* SfxApplication::GetModule(SfxToolsModule nSharedLib)
+{
+ if (!g_pSfxApplication) // It is possible GetModule is called before SfxApplication is initialised via GetOrCreate()
+ return nullptr;
+ return g_pSfxApplication->pImpl->aModules[nSharedLib].get();
+}
+
+namespace {
+ css::uno::Reference<css::frame::XToolbarController> SfxWeldToolBoxControllerFactory( const css::uno::Reference< css::frame::XFrame >& rFrame, weld::Toolbar* pToolbar, weld::Builder* pBuilder, const OUString& aCommandURL )
+ {
+ SolarMutexGuard aGuard;
+
+ return sfx2::sidebar::ControllerFactory::CreateToolBoxController(
+ *pToolbar, *pBuilder, aCommandURL, rFrame, rFrame->getController(), false);
+ }
+}
+
+SfxApplication* SfxApplication::GetOrCreate()
+{
+ static std::mutex theApplicationMutex;
+
+ // SFX on demand
+ std::unique_lock aGuard(theApplicationMutex);
+ if (!g_pSfxApplication)
+ {
+ SAL_INFO( "sfx.appl", "SfxApplication::SetApp" );
+
+ g_pSfxApplication = new SfxApplication;
+
+ // at the moment a bug may occur when Initialize_Impl returns FALSE,
+ // but this is only temporary because all code that may cause such
+ // a fault will be moved outside the SFX
+ g_pSfxApplication->Initialize_Impl();
+
+ ::framework::SetRefreshToolbars( RefreshToolbars );
+ ::framework::SetToolBoxControllerCreator( SfxToolBoxControllerFactory );
+ ::framework::SetWeldToolBoxControllerCreator( SfxWeldToolBoxControllerFactory );
+ ::framework::SetStatusBarControllerCreator( SfxStatusBarControllerFactory );
+ ::framework::SetDockingWindowCreator( SfxDockingWindowFactory );
+ ::framework::SetIsDockingWindowVisible( IsDockingWindowVisible );
+#if HAVE_FEATURE_XMLHELP
+ Application::SetHelp( pSfxHelp );
+#endif
+#if HAVE_FEATURE_XMLHELP || defined(EMSCRIPTEN)
+ bool bHelpTip = officecfg::Office::Common::Help::Tip::get();
+ bool bExtendedHelpTip = officecfg::Office::Common::Help::ExtendedTip::get();
+ if (!utl::ConfigManager::IsFuzzing() && bHelpTip)
+ Help::EnableQuickHelp();
+ else
+ Help::DisableQuickHelp();
+ if (!utl::ConfigManager::IsFuzzing() && bHelpTip && bExtendedHelpTip)
+ Help::EnableBalloonHelp();
+ else
+ Help::DisableBalloonHelp();
+#endif
+ }
+ return g_pSfxApplication;
+}
+
+SfxApplication::SfxApplication()
+ : pImpl( new SfxAppData_Impl )
+{
+ SetName( "StarOffice" );
+
+ SAL_INFO( "sfx.appl", "{ initialize DDE" );
+
+ bool bOk = InitializeDde();
+
+#ifdef DBG_UTIL
+ if( !bOk )
+ {
+ OStringBuffer aStr("No DDE-Service possible. Error: ");
+ if( GetDdeService() )
+ aStr.append(static_cast<sal_Int32>(GetDdeService()->GetError()));
+ else
+ aStr.append('?');
+ SAL_WARN( "sfx.appl", aStr.getStr() );
+ }
+#else
+ (void)bOk;
+#endif
+
+#if HAVE_FEATURE_XMLHELP
+ pSfxHelp = new SfxHelp;
+#endif
+
+#if HAVE_FEATURE_SCRIPTING
+ StarBASIC::SetGlobalErrorHdl( LINK( this, SfxApplication, GlobalBasicErrorHdl_Impl ) );
+#endif
+
+ SAL_INFO( "sfx.appl", "} initialize DDE" );
+}
+
+SfxApplication::~SfxApplication()
+{
+ SAL_WARN_IF(GetObjectShells_Impl().size() != 0, "sfx.appl", "Memory leak: some object shells were not removed!");
+
+ Broadcast( SfxHint(SfxHintId::Dying) );
+
+ for (auto &module : pImpl->aModules) // Clear modules
+ module.reset();
+
+#if HAVE_FEATURE_XMLHELP
+ delete pSfxHelp;
+ Application::SetHelp();
+#endif
+
+ if ( !pImpl->bDowning )
+ Deinitialize();
+
+ g_pSfxApplication = nullptr;
+}
+
+
+const OUString& SfxApplication::GetLastDir_Impl() const
+
+/* [Description]
+
+ Internal method by which the last set directory with the method
+ <SfxApplication::SetLastDir_Impl()> in SFX is returned.
+
+ This is usually the most recently addressed by the
+ SfxFileDialog directory.
+
+ [Cross-reference]
+ <SfxApplication::SetLastDir_Impl()>
+*/
+
+{
+ return pImpl->aLastDir;
+}
+
+void SfxApplication::SetLastDir_Impl
+(
+ const OUString& rNewDir /* Complete directory path as a string */
+)
+
+/* [Description]
+
+ Internal Method, by which a directory path is set that was last addressed
+ (eg by the SfxFileDialog).
+
+ [Cross-reference]
+ <SfxApplication::GetLastDir_Impl()>
+*/
+
+{
+ pImpl->aLastDir = rNewDir;
+}
+
+
+void SfxApplication::ResetLastDir()
+{
+ pImpl->aLastDir.clear();
+}
+
+
+SfxDispatcher* SfxApplication::GetDispatcher_Impl()
+{
+ return pImpl->pViewFrame ? pImpl->pViewFrame->GetDispatcher() : &*pImpl->pAppDispat;
+}
+
+
+void SfxApplication::SetViewFrame_Impl( SfxViewFrame *pFrame )
+{
+ if ( pFrame != pImpl->pViewFrame )
+ {
+ SfxViewFrame *pOldFrame = pImpl->pViewFrame;
+
+ // DocWinActivate : both frames belong to the same TopWindow
+ // TopWinActivate : both frames belong to different TopWindows
+
+ bool bTaskActivate = pOldFrame != pFrame;
+
+ if ( pOldFrame )
+ {
+ if ( bTaskActivate )
+ NotifyEvent( SfxViewEventHint( SfxEventHintId::DeactivateDoc, GlobalEventConfig::GetEventName(GlobalEventId::DEACTIVATEDOC), pOldFrame->GetObjectShell(), pOldFrame->GetFrame().GetController() ) );
+
+ pOldFrame->DoDeactivate( bTaskActivate, pFrame );
+
+ if( pOldFrame->GetProgress() )
+ pOldFrame->GetProgress()->Suspend();
+ }
+
+ pImpl->pViewFrame = pFrame;
+
+ if( pFrame )
+ {
+ pFrame->DoActivate( bTaskActivate );
+ if ( bTaskActivate && pFrame->GetObjectShell() )
+ {
+ pFrame->GetObjectShell()->PostActivateEvent_Impl( pFrame );
+ NotifyEvent(SfxViewEventHint(SfxEventHintId::ActivateDoc, GlobalEventConfig::GetEventName(GlobalEventId::ACTIVATEDOC), pFrame->GetObjectShell(), pFrame->GetFrame().GetController() ) );
+ }
+
+ SfxProgress *pProgress = pFrame->GetProgress();
+ if ( pProgress )
+ {
+ if( pProgress->IsSuspended() )
+ pProgress->Resume();
+ else
+ pProgress->SetState( pProgress->GetState() );
+ }
+
+ if ( pImpl->pViewFrame->GetViewShell() )
+ {
+ SfxDispatcher* pDisp = pImpl->pViewFrame->GetDispatcher();
+ pDisp->Flush();
+ pDisp->Update_Impl(true);
+ }
+ }
+ }
+
+ // even if the frame actually didn't change, ensure its document is forwarded
+ // to SfxObjectShell::SetCurrentComponent.
+ // Otherwise, the CurrentComponent might not be correct, in case it has meanwhile
+ // been reset to some other document, by some non-SFX component. #i49133#
+ if ( pFrame && pFrame->GetViewShell() )
+ pFrame->GetViewShell()->SetCurrentDocument();
+}
+
+void SfxApplication::SetProgress_Impl
+(
+ SfxProgress *pProgress
+)
+{
+ DBG_ASSERT( ( !pImpl->pProgress && pProgress ) ||
+ ( pImpl->pProgress && !pProgress ),
+ "Progress activation/deactivation mismatch" );
+
+ if ( pImpl->pProgress && pProgress )
+ {
+ pImpl->pProgress->Suspend();
+ delete pImpl->pProgress;
+ }
+
+ pImpl->pProgress = pProgress;
+}
+
+
+sal_uInt16 SfxApplication::GetFreeIndex()
+{
+ return pImpl->aIndexBitSet.GetFreeIndex()+1;
+}
+
+
+void SfxApplication::ReleaseIndex(sal_uInt16 i)
+{
+ pImpl->aIndexBitSet.ReleaseIndex(i-1);
+}
+
+
+weld::Window* SfxApplication::GetTopWindow() const
+{
+ SfxWorkWindow* pWork = GetWorkWindow_Impl( SfxViewFrame::Current() );
+ if (!pWork)
+ return nullptr;
+ vcl::Window* pWindow = pWork->GetWindow();
+ if (!pWindow)
+ return nullptr;
+ return pWindow->GetFrameWeld();
+}
+
+SfxTbxCtrlFactory* SfxApplication::GetTbxCtrlFactory(const std::type_info& rSlotType, sal_uInt16 nSlotID) const
+{
+ // search for a factory with the given slot id
+ for (auto& rFactory : pImpl->maTbxCtrlFactories)
+ if( rFactory.nTypeId == rSlotType && rFactory.nSlotId == nSlotID )
+ return &rFactory;
+
+ // if no factory exists for the given slot id, see if we
+ // have a generic factory with the correct slot type and slot id == 0
+ for (auto& rFactory : pImpl->maTbxCtrlFactories)
+ if( rFactory.nTypeId == rSlotType && rFactory.nSlotId == 0 )
+ return &rFactory;
+
+ return nullptr;
+}
+
+SfxStbCtrlFactory* SfxApplication::GetStbCtrlFactory(const std::type_info& rSlotType, sal_uInt16 nSlotID) const
+{
+ for (auto& rFactory : pImpl->maStbCtrlFactories)
+ if ( rFactory.nTypeId == rSlotType &&
+ ( rFactory.nSlotId == 0 || rFactory.nSlotId == nSlotID ) )
+ return &rFactory;
+ return nullptr;
+}
+
+std::vector<SfxViewFrame*>& SfxApplication::GetViewFrames_Impl() const
+{
+ return pImpl->maViewFrames;
+}
+
+std::vector<SfxViewShell*>& SfxApplication::GetViewShells_Impl() const
+{
+ return pImpl->maViewShells;
+}
+
+std::vector<SfxObjectShell*>& SfxApplication::GetObjectShells_Impl() const
+{
+ return pImpl->maObjShells;
+}
+
+void SfxApplication::Invalidate( sal_uInt16 nId )
+{
+ for( SfxViewFrame* pFrame = SfxViewFrame::GetFirst(); pFrame; pFrame = SfxViewFrame::GetNext( *pFrame ) )
+ Invalidate_Impl( pFrame->GetBindings(), nId );
+}
+
+#if HAVE_FEATURE_SCRIPTING
+
+#ifndef DISABLE_DYNLOADING
+
+typedef long (*basicide_handle_basic_error)(void const *);
+typedef void (*basicide_macro_organizer)(void *, sal_Int16);
+
+#else
+
+extern "C" long basicide_handle_basic_error(void const*);
+extern "C" void basicide_macro_organizer(void*, sal_Int16);
+
+#endif
+
+#endif
+
+IMPL_STATIC_LINK( SfxApplication, GlobalBasicErrorHdl_Impl, StarBASIC*, pStarBasic, bool )
+{
+#if !HAVE_FEATURE_SCRIPTING
+ (void) pStarBasic;
+ return false;
+#else
+
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ OUString aError;
+ std::unique_ptr<ErrorInfo> pErrorInfo = ErrorInfo::GetErrorInfo(StarBASIC::GetErrorCode());
+ if (ErrorStringFactory::CreateString(pErrorInfo.get(), aError))
+ {
+ const SfxViewFrame* pViewFrame = SfxViewFrame::Current();
+ std::shared_ptr<weld::MessageDialog> xBox;
+ xBox.reset(Application::CreateMessageDialog(
+ pViewFrame ? pViewFrame->GetFrameWeld() : nullptr,
+ VclMessageType::Error,
+ VclButtonsType::Ok,
+ aError,
+ true));
+
+ xBox->runAsync(xBox, [](sal_Int32 /*nResult*/) {});
+ }
+ return true;
+ }
+
+#ifndef DISABLE_DYNLOADING
+ basicide_handle_basic_error pSymbol = reinterpret_cast<basicide_handle_basic_error>(sfx2::getBasctlFunction("basicide_handle_basic_error"));
+
+ // call basicide_handle_basic_error in basctl
+ bool bRet = pSymbol( pStarBasic );
+
+#else
+
+ bool bRet = basicide_handle_basic_error( pStarBasic );
+
+#endif
+
+ return bRet;
+
+#endif
+}
+
+bool SfxApplication::IsXScriptURL( const OUString& rScriptURL )
+{
+ bool result = false;
+
+#if !HAVE_FEATURE_SCRIPTING
+ (void) rScriptURL;
+#else
+ css::uno::Reference< css::uno::XComponentContext > xContext =
+ ::comphelper::getProcessComponentContext();
+
+ css::uno::Reference< css::uri::XUriReferenceFactory >
+ xFactory = css::uri::UriReferenceFactory::create( xContext );
+
+ try
+ {
+ css::uno::Reference< css::uri::XVndSunStarScriptUrl >
+ xUrl( xFactory->parse( rScriptURL ), css::uno::UNO_QUERY );
+
+ if ( xUrl.is() )
+ {
+ result = true;
+ }
+ }
+ catch (const css::uno::RuntimeException&)
+ {
+ // ignore, will just return FALSE
+ }
+#endif
+ return result;
+}
+
+OUString
+SfxApplication::ChooseScript(weld::Window *pParent)
+{
+ OUString aScriptURL;
+
+#if HAVE_FEATURE_SCRIPTING
+ SfxAbstractDialogFactory* pFact = SfxAbstractDialogFactory::Create();
+ SAL_INFO( "sfx.appl", "create selector dialog");
+
+ const SfxViewFrame* pViewFrame = SfxViewFrame::Current();
+ const SfxFrame* pFrame = pViewFrame ? &pViewFrame->GetFrame() : nullptr;
+ uno::Reference< frame::XFrame > xFrame( pFrame ? pFrame->GetFrameInterface() : uno::Reference< frame::XFrame >() );
+
+ ScopedVclPtr<AbstractScriptSelectorDialog> pDlg(pFact->CreateScriptSelectorDialog(pParent, xFrame));
+
+ SAL_INFO( "sfx.appl", "done, now exec it");
+
+ sal_uInt16 nRet = pDlg->Execute();
+
+ SAL_INFO( "sfx.appl", "has returned");
+
+ if ( nRet == RET_OK )
+ {
+ aScriptURL = pDlg->GetScriptURL();
+ }
+#else
+ (void) pParent;
+#endif
+ return aScriptURL;
+}
+
+void SfxApplication::MacroOrganizer(weld::Window* pParent, sal_Int16 nTabId)
+{
+#if !HAVE_FEATURE_SCRIPTING
+ (void) pParent;
+ (void) nTabId;
+#else
+
+#ifndef DISABLE_DYNLOADING
+ basicide_macro_organizer pSymbol = reinterpret_cast<basicide_macro_organizer>(sfx2::getBasctlFunction("basicide_macro_organizer"));
+
+ // call basicide_macro_organizer in basctl
+ pSymbol(pParent, nTabId);
+
+#else
+
+ basicide_macro_organizer(pParent, nTabId);
+
+#endif
+
+#endif
+}
+
+ErrCode SfxApplication::CallBasic( const OUString& rCode, BasicManager* pMgr, SbxArray* pArgs, SbxValue* pRet )
+{
+#if !HAVE_FEATURE_SCRIPTING
+ (void) rCode;
+ (void) pMgr;
+ (void) pArgs;
+ (void) pRet;
+ return ERRCODE_BASIC_CANNOT_LOAD;
+#else
+ (void) ERRCODE_BASIC_CANNOT_LOAD; // So that the !HAVE_FEATURE_SCRIPTING case isn't broken again by IWYU
+ return pMgr->ExecuteMacro( rCode, pArgs, pRet);
+#endif
+}
+
+sfx2::sidebar::Theme & SfxApplication::GetSidebarTheme()
+{
+ if (!pImpl->m_pSidebarTheme.is())
+ {
+ pImpl->m_pSidebarTheme.set(new sfx2::sidebar::Theme);
+ pImpl->m_pSidebarTheme->InitializeTheme();
+ }
+ return *pImpl->m_pSidebarTheme;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/appl/appbas.cxx b/sfx2/source/appl/appbas.cxx
new file mode 100644
index 000000000..482b93692
--- /dev/null
+++ b/sfx2/source/appl/appbas.cxx
@@ -0,0 +1,154 @@
+/* -*- 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 <config_features.h>
+
+#include <sal/config.h>
+
+#include <cassert>
+
+#include <com/sun/star/script/XLibraryContainer.hpp>
+#include <officecfg/Office/Common.hxx>
+#include <svl/intitem.hxx>
+#include <svl/eitem.hxx>
+#include <svl/whiter.hxx>
+#include <basic/sbstar.hxx>
+
+#include <sfx2/frame.hxx>
+#include <sfx2/dinfdlg.hxx>
+#include <sfx2/app.hxx>
+#include <sfx2/msg.hxx>
+#include <sfx2/request.hxx>
+#include <sfx2/sfxsids.hrc>
+#include <appdata.hxx>
+#include <basic/basmgr.hxx>
+#include <unotools/configmgr.hxx>
+#include <sorgitm.hxx>
+#include <appbaslib.hxx>
+#include <basic/basicmanagerrepository.hxx>
+
+#define SFX_TYPEMAP
+#include <sfxslots.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::script;
+
+using ::basic::BasicManagerRepository;
+
+
+void SfxApplication::SaveBasicAndDialogContainer() const
+{
+ if ( pImpl->pBasicManager->isValid() )
+ pImpl->pBasicManager->storeAllLibraries();
+}
+
+BasicManager* SfxApplication::GetBasicManager()
+{
+#if !HAVE_FEATURE_SCRIPTING
+ return nullptr;
+#else
+ if (utl::ConfigManager::IsFuzzing())
+ return nullptr;
+ return BasicManagerRepository::getApplicationBasicManager();
+#endif
+}
+
+XLibraryContainer * SfxApplication::GetDialogContainer()
+{
+#if !HAVE_FEATURE_SCRIPTING
+ return nullptr;
+#else
+ if (utl::ConfigManager::IsFuzzing())
+ return nullptr;
+ if ( !pImpl->pBasicManager->isValid() )
+ GetBasicManager();
+ return pImpl->pBasicManager->getLibraryContainer( SfxBasicManagerHolder::DIALOGS );
+#endif
+}
+
+
+XLibraryContainer * SfxApplication::GetBasicContainer()
+{
+#if !HAVE_FEATURE_SCRIPTING
+ return nullptr;
+#else
+ if (utl::ConfigManager::IsFuzzing())
+ return nullptr;
+ if ( !pImpl->pBasicManager->isValid() )
+ GetBasicManager();
+ return pImpl->pBasicManager->getLibraryContainer( SfxBasicManagerHolder::SCRIPTS );
+#endif
+}
+
+StarBASIC* SfxApplication::GetBasic()
+{
+#if !HAVE_FEATURE_SCRIPTING
+ return nullptr;
+#else
+ if (utl::ConfigManager::IsFuzzing())
+ return nullptr;
+ return GetBasicManager()->GetLib(0);
+#endif
+}
+
+void SfxApplication::PropExec_Impl( SfxRequest const &rReq )
+{
+ sal_uInt16 nSID = rReq.GetSlot();
+ switch ( nSID )
+ {
+ case SID_ATTR_UNDO_COUNT:
+ {
+ if (const SfxUInt16Item* pCountItem = rReq.GetArg<SfxUInt16Item>(nSID))
+ {
+ std::shared_ptr< comphelper::ConfigurationChanges > batch(
+ comphelper::ConfigurationChanges::create());
+ officecfg::Office::Common::Undo::Steps::set(
+ pCountItem->GetValue(), batch);
+ batch->commit();
+ }
+ break;
+ }
+
+ default:
+ assert(false);
+ }
+}
+
+void SfxApplication::PropState_Impl( SfxItemSet &rSet )
+{
+ SfxWhichIter aIter(rSet);
+ for ( sal_uInt16 nSID = aIter.FirstWhich(); nSID; nSID = aIter.NextWhich() )
+ {
+ switch ( nSID )
+ {
+ case SID_ATTR_UNDO_COUNT:
+ rSet.Put(
+ SfxUInt16Item(
+ SID_ATTR_UNDO_COUNT,
+ officecfg::Office::Common::Undo::Steps::get()));
+ break;
+
+ default:
+ assert(false);
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/appl/appbaslib.cxx b/sfx2/source/appl/appbaslib.cxx
new file mode 100644
index 000000000..f05afecdd
--- /dev/null
+++ b/sfx2/source/appl/appbaslib.cxx
@@ -0,0 +1,188 @@
+/* -*- 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 <config_features.h>
+
+#include <appbaslib.hxx>
+
+#include <sfx2/app.hxx>
+
+#include <basic/basmgr.hxx>
+#include <tools/diagnose_ex.h>
+#include <com/sun/star/uno/XComponentContext.hpp>
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::script;
+using namespace ::com::sun::star::embed;
+
+
+SfxBasicManagerHolder::SfxBasicManagerHolder()
+ :mpBasicManager( nullptr )
+{
+}
+
+void SfxBasicManagerHolder::Notify(SfxBroadcaster& rBC, SfxHint const& rHint)
+{
+ if (!mpBasicManager || &rBC != mpBasicManager)
+ return;
+ if (SfxHintId::Dying == rHint.GetId())
+ {
+ mpBasicManager = nullptr;
+ mxBasicContainer.clear();
+ mxDialogContainer.clear();
+ }
+}
+
+void SfxBasicManagerHolder::reset( BasicManager* _pBasicManager )
+{
+ impl_releaseContainers();
+
+#if !HAVE_FEATURE_SCRIPTING
+ (void) _pBasicManager;
+#else
+ // Note: we do not delete the old BasicManager. BasicManager instances are
+ // nowadays obtained from the BasicManagerRepository, and the ownership is with
+ // the repository.
+ // @see basic::BasicManagerRepository::getApplicationBasicManager
+ // @see basic::BasicManagerRepository::getDocumentBasicManager
+ mpBasicManager = _pBasicManager;
+
+ if ( !mpBasicManager )
+ return;
+
+ StartListening(*mpBasicManager);
+ try
+ {
+ mxBasicContainer.set( mpBasicManager->GetScriptLibraryContainer(), UNO_QUERY_THROW );
+ mxDialogContainer.set( mpBasicManager->GetDialogLibraryContainer(), UNO_QUERY_THROW );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("sfx.appl");
+ }
+#endif
+}
+
+void SfxBasicManagerHolder::storeAllLibraries()
+{
+#if HAVE_FEATURE_SCRIPTING
+ OSL_PRECOND( isValid(), "SfxBasicManagerHolder::storeAllLibraries: not initialized!" );
+ try
+ {
+ if ( mxBasicContainer.is() )
+ mxBasicContainer->storeLibraries();
+ if ( mxDialogContainer.is() )
+ mxDialogContainer->storeLibraries();
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("sfx.appl");
+ }
+#endif
+}
+
+void SfxBasicManagerHolder::setStorage( const Reference< XStorage >& _rxStorage )
+{
+#if !HAVE_FEATURE_SCRIPTING
+ (void) _rxStorage;
+#else
+ try
+ {
+ if ( mxBasicContainer.is() )
+ mxBasicContainer->setRootStorage( _rxStorage );
+ if ( mxDialogContainer.is() )
+ mxDialogContainer->setRootStorage( _rxStorage );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("sfx.appl");
+ }
+#endif
+}
+
+void SfxBasicManagerHolder::storeLibrariesToStorage( const Reference< XStorage >& _rxStorage )
+{
+#if !HAVE_FEATURE_SCRIPTING
+ (void) _rxStorage;
+#else
+ OSL_PRECOND( isValid(), "SfxBasicManagerHolder::storeLibrariesToStorage: not initialized!" );
+
+ if ( mxBasicContainer.is() )
+ mxBasicContainer->storeLibrariesToStorage( _rxStorage );
+ if ( mxDialogContainer.is() )
+ mxDialogContainer->storeLibrariesToStorage( _rxStorage );
+#endif
+}
+
+XLibraryContainer * SfxBasicManagerHolder::getLibraryContainer( ContainerType _eType )
+{
+ OSL_PRECOND( isValid(), "SfxBasicManagerHolder::getLibraryContainer: not initialized!" );
+
+ switch ( _eType )
+ {
+ case SCRIPTS: return mxBasicContainer.get();
+ case DIALOGS: return mxDialogContainer.get();
+ }
+ OSL_FAIL( "SfxBasicManagerHolder::getLibraryContainer: illegal container type!" );
+ return nullptr;
+}
+
+void SfxBasicManagerHolder::impl_releaseContainers()
+{
+ mxBasicContainer.clear();
+ mxDialogContainer.clear();
+}
+
+bool SfxBasicManagerHolder::LegacyPsswdBinaryLimitExceeded( std::vector< OUString >& sModules )
+{
+#if !HAVE_FEATURE_SCRIPTING
+ (void) sModules;
+#else
+ if ( mpBasicManager )
+ return mpBasicManager->LegacyPsswdBinaryLimitExceeded( sModules );
+#endif
+ return true;
+}
+
+// Service for application library container
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_sfx2_ApplicationDialogLibraryContainer_get_implementation(
+ css::uno::XComponentContext *,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ SfxApplication::GetBasicManager();
+ css::uno::XInterface* pRet = SfxGetpApp()->GetDialogContainer();
+ pRet->acquire();
+ return pRet;
+}
+
+// Service for application library container
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_sfx2_ApplicationScriptLibraryContainer_get_implementation(
+ css::uno::XComponentContext *,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ SfxApplication::GetBasicManager();
+ css::uno::XInterface* pRet = SfxGetpApp()->GetBasicContainer();
+ pRet->acquire();
+ return pRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/appl/appcfg.cxx b/sfx2/source/appl/appcfg.cxx
new file mode 100644
index 000000000..3c8c8c301
--- /dev/null
+++ b/sfx2/source/appl/appcfg.cxx
@@ -0,0 +1,738 @@
+/* -*- 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 <osl/file.hxx>
+
+#include <rtl/ustring.hxx>
+#include <svl/itempool.hxx>
+#include <svl/itemset.hxx>
+#include <svl/aeitem.hxx>
+#include <svl/slstitm.hxx>
+#include <svl/stritem.hxx>
+#include <svl/intitem.hxx>
+#include <svl/eitem.hxx>
+#include <svl/undo.hxx>
+
+#include <sfx2/sfxsids.hrc>
+
+#include <svl/isethint.hxx>
+
+#include <officecfg/Inet.hxx>
+#include <officecfg/Office/Common.hxx>
+#include <officecfg/Office/Recovery.hxx>
+#include <unotools/securityoptions.hxx>
+#include <unotools/pathoptions.hxx>
+#include <svtools/miscopt.hxx>
+#include <svtools/imgdef.hxx>
+#include <sal/log.hxx>
+#include <vcl/idle.hxx>
+
+#include <sfx2/app.hxx>
+#include <sfx2/event.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/objsh.hxx>
+#include <objshimp.hxx>
+#include "shutdownicon.hxx"
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::util;
+using namespace ::com::sun::star::beans;
+
+namespace {
+
+class SfxEventAsyncer_Impl : public SfxListener
+{
+ SfxEventHint aHint;
+ std::unique_ptr<Idle> pIdle;
+
+public:
+
+ virtual void Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) override;
+ explicit SfxEventAsyncer_Impl(const SfxEventHint& rHint);
+ DECL_LINK( IdleHdl, Timer*, void );
+};
+
+}
+
+void SfxEventAsyncer_Impl::Notify( SfxBroadcaster&, const SfxHint& rHint )
+{
+ if( rHint.GetId() == SfxHintId::Dying && pIdle->IsActive() )
+ {
+ pIdle->Stop();
+ delete this;
+ }
+}
+
+
+SfxEventAsyncer_Impl::SfxEventAsyncer_Impl( const SfxEventHint& rHint )
+ : aHint( rHint )
+{
+ if( rHint.GetObjShell() )
+ StartListening( *rHint.GetObjShell() );
+ pIdle.reset( new Idle("sfx::SfxEventAsyncer_Impl pIdle") );
+ pIdle->SetInvokeHandler( LINK(this, SfxEventAsyncer_Impl, IdleHdl) );
+ pIdle->SetPriority( TaskPriority::HIGH_IDLE );
+ pIdle->Start();
+}
+
+
+IMPL_LINK(SfxEventAsyncer_Impl, IdleHdl, Timer*, pAsyncIdle, void)
+{
+ SfxObjectShellRef xRef( aHint.GetObjShell() );
+ pAsyncIdle->Stop();
+ SAL_INFO_IF(!xRef.is(), "sfx.appl", "SfxEvent: " << aHint.GetEventName());
+ SfxGetpApp()->Broadcast( aHint );
+ if ( xRef.is() )
+ xRef->Broadcast( aHint );
+ delete this;
+}
+
+
+void SfxApplication::GetOptions( SfxItemSet& rSet )
+{
+ bool bRet = false;
+
+ const WhichRangesContainer& pRanges = rSet.GetRanges();
+ SvtMiscOptions aMiscOptions;
+
+ for (auto const & pRange : pRanges)
+ {
+ for(sal_uInt16 nWhich = pRange.first; nWhich <= pRange.second; ++nWhich)
+ {
+ switch(nWhich)
+ {
+ case SID_ATTR_BUTTON_BIGSIZE :
+ {
+ if( rSet.Put( SfxBoolItem( SID_ATTR_BUTTON_BIGSIZE, aMiscOptions.AreCurrentSymbolsLarge() ) ) )
+ bRet = true;
+ break;
+ }
+ case SID_ATTR_BACKUP :
+ {
+ bRet = true;
+ if (!officecfg::Office::Common::Save::Document::CreateBackup::isReadOnly())
+ if (!rSet.Put( SfxBoolItem( SID_ATTR_BACKUP,
+ officecfg::Office::Common::Save::Document::CreateBackup::get() )))
+ bRet = false;
+ }
+ break;
+ case SID_ATTR_PRETTYPRINTING:
+ {
+ bRet = true;
+ if (!officecfg::Office::Common::Save::Document::PrettyPrinting::isReadOnly())
+ if (!rSet.Put( SfxBoolItem( SID_ATTR_PRETTYPRINTING,
+ officecfg::Office::Common::Save::Document::PrettyPrinting::get())))
+ bRet = false;
+ }
+ break;
+ case SID_ATTR_WARNALIENFORMAT:
+ {
+ bRet = true;
+ if (!officecfg::Office::Common::Save::Document::WarnAlienFormat::isReadOnly())
+ if (!rSet.Put( SfxBoolItem( SID_ATTR_WARNALIENFORMAT,
+ officecfg::Office::Common::Save::Document::WarnAlienFormat::get() )))
+ bRet = false;
+ }
+ break;
+ case SID_ATTR_AUTOSAVE :
+ {
+ bRet = true;
+ if (!officecfg::Office::Common::Save::Document::AutoSave::isReadOnly())
+ if (!rSet.Put( SfxBoolItem( SID_ATTR_AUTOSAVE,
+ officecfg::Office::Common::Save::Document::AutoSave::get() )))
+ bRet = false;
+ }
+ break;
+ case SID_ATTR_AUTOSAVEPROMPT :
+ {
+ bRet = true;
+ if (!officecfg::Office::Common::Save::Document::AutoSavePrompt::isReadOnly())
+ if (!rSet.Put( SfxBoolItem( SID_ATTR_AUTOSAVEPROMPT,
+ officecfg::Office::Common::Save::Document::AutoSavePrompt::get())))
+ bRet = false;
+ }
+ break;
+ case SID_ATTR_AUTOSAVEMINUTE :
+ {
+ bRet = true;
+ if (!officecfg::Office::Common::Save::Document::AutoSaveTimeIntervall::isReadOnly())
+ if (!rSet.Put( SfxUInt16Item( SID_ATTR_AUTOSAVEMINUTE,
+ officecfg::Office::Common::Save::Document::AutoSaveTimeIntervall::get() )))
+ bRet = false;
+ }
+ break;
+ case SID_ATTR_USERAUTOSAVE :
+ {
+ bRet = true;
+ if (!officecfg::Office::Recovery::AutoSave::UserAutoSaveEnabled::isReadOnly())
+ if (!rSet.Put( SfxBoolItem( SID_ATTR_USERAUTOSAVE,
+ officecfg::Office::Recovery::AutoSave::UserAutoSaveEnabled::get() )))
+ bRet = false;
+ }
+ break;
+ case SID_ATTR_DOCINFO :
+ {
+ bRet = true;
+ if (!officecfg::Office::Common::Save::Document::EditProperty::isReadOnly())
+ if (!rSet.Put( SfxBoolItem( SID_ATTR_DOCINFO,
+ officecfg::Office::Common::Save::Document::EditProperty::get())))
+ bRet = false;
+ }
+ break;
+ case SID_ATTR_WORKINGSET :
+ {
+ bRet = true;
+ if (!officecfg::Office::Common::Save::WorkingSet::isReadOnly())
+ if (!rSet.Put( SfxBoolItem( SID_ATTR_WORKINGSET,
+ officecfg::Office::Common::Save::WorkingSet::get())))
+ bRet = false;
+ }
+ break;
+ case SID_ATTR_SAVEDOCVIEW :
+ {
+ bRet = true;
+ if (!officecfg::Office::Common::Save::Document::ViewInfo::isReadOnly())
+ if (!rSet.Put( SfxBoolItem( SID_ATTR_SAVEDOCVIEW, officecfg::Office::Common::Save::Document::ViewInfo::get())))
+ bRet = false;
+ }
+ break;
+ case SID_ATTR_METRIC :
+ break;
+ case SID_HELPBALLOONS :
+ if(rSet.Put( SfxBoolItem ( SID_HELPBALLOONS,
+ officecfg::Office::Common::Help::ExtendedTip::get() ) ) )
+ bRet = true;
+ break;
+ case SID_HELPTIPS :
+ if(rSet.Put( SfxBoolItem ( SID_HELPTIPS,
+ officecfg::Office::Common::Help::Tip::get() ) ) )
+ bRet = true;
+ break;
+ case SID_HELP_STYLESHEET :
+ if(rSet.Put( SfxStringItem ( SID_HELP_STYLESHEET,
+ officecfg::Office::Common::Help::HelpStyleSheet::get() ) ) )
+ bRet = true;
+ break;
+ case SID_ATTR_UNDO_COUNT :
+ if (rSet.Put(
+ SfxUInt16Item (
+ SID_ATTR_UNDO_COUNT,
+ officecfg::Office::Common::Undo::Steps::get())))
+ {
+ bRet = true;
+ }
+ break;
+ case SID_ATTR_QUICKLAUNCHER :
+ {
+ if ( ShutdownIcon::IsQuickstarterInstalled() )
+ {
+ if ( rSet.Put( SfxBoolItem( SID_ATTR_QUICKLAUNCHER,
+ ShutdownIcon::GetAutostart() ) ) )
+ bRet = true;
+ }
+ else
+ {
+ rSet.DisableItem( SID_ATTR_QUICKLAUNCHER );
+ bRet = true;
+ }
+ break;
+ }
+ case SID_SAVEREL_INET :
+ {
+ bRet = true;
+ if (!officecfg::Office::Common::Save::URL::Internet::isReadOnly())
+ if (!rSet.Put( SfxBoolItem ( SID_SAVEREL_INET,
+ officecfg::Office::Common::Save::URL::Internet::get() )))
+ bRet = false;
+ }
+ break;
+ case SID_SAVEREL_FSYS :
+ {
+ bRet = true;
+ if (!officecfg::Office::Common::Save::URL::FileSystem::isReadOnly())
+ if (!rSet.Put( SfxBoolItem ( SID_SAVEREL_FSYS,
+ officecfg::Office::Common::Save::URL::FileSystem::get() )))
+ bRet = false;
+ }
+ break;
+ case SID_SECURE_URL :
+ {
+ bRet = true;
+ if (!SvtSecurityOptions::IsReadOnly(SvtSecurityOptions::EOption::SecureUrls))
+ {
+ std::vector< OUString > seqURLs = SvtSecurityOptions::GetSecureURLs();
+
+ if( !rSet.Put( SfxStringListItem( SID_SECURE_URL, &seqURLs ) ) )
+ bRet = false;
+ }
+ }
+ break;
+ case SID_INET_PROXY_TYPE :
+ if (rSet.Put(
+ SfxUInt16Item(
+ SID_INET_PROXY_TYPE,
+ (officecfg::Inet::Settings::ooInetProxyType::
+ get().value_or(0)))))
+ {
+ bRet = true;
+ }
+ break;
+ case SID_INET_HTTP_PROXY_NAME :
+ if (rSet.Put(
+ SfxStringItem(
+ SID_INET_HTTP_PROXY_NAME,
+ officecfg::Inet::Settings::ooInetHTTPProxyName::
+ get())))
+ {
+ bRet = true;
+ }
+ break;
+ case SID_INET_HTTP_PROXY_PORT :
+ if (rSet.Put(
+ SfxInt32Item(
+ SID_INET_HTTP_PROXY_PORT,
+ (officecfg::Inet::Settings::
+ ooInetHTTPProxyPort::get().value_or(0)))))
+ {
+ bRet = true;
+ }
+ break;
+ case SID_INET_FTP_PROXY_NAME :
+ if (rSet.Put(
+ SfxStringItem(
+ SID_INET_FTP_PROXY_NAME,
+ officecfg::Inet::Settings::ooInetFTPProxyName::
+ get())))
+ {
+ bRet = true;
+ }
+ break;
+ case SID_INET_FTP_PROXY_PORT :
+ if (rSet.Put(
+ SfxInt32Item(
+ SID_INET_FTP_PROXY_PORT,
+ (officecfg::Inet::Settings::ooInetFTPProxyPort::
+ get().value_or(0)))))
+ {
+ bRet = true;
+ }
+ break;
+ case SID_INET_NOPROXY :
+ if (rSet.Put(
+ SfxStringItem(
+ SID_INET_NOPROXY,
+ (officecfg::Inet::Settings::ooInetNoProxy::
+ get()))))
+ {
+ bRet = true;
+ }
+ break;
+ case SID_ATTR_PATHNAME :
+ {
+ SfxAllEnumItem aValues(SID_ATTR_PATHNAME);
+ SvtPathOptions aPathCfg;
+ for ( sal_uInt16 nProp = static_cast<sal_uInt16>(SvtPathOptions::Paths::AddIn);
+ nProp <= static_cast<sal_uInt16>(SvtPathOptions::Paths::Work); nProp++ )
+ {
+ OUString aValue;
+ switch ( static_cast<SvtPathOptions::Paths>(nProp) )
+ {
+ case SvtPathOptions::Paths::AddIn: osl::FileBase::getFileURLFromSystemPath( aPathCfg.GetAddinPath(), aValue ); break;
+ case SvtPathOptions::Paths::AutoCorrect: aValue = aPathCfg.GetAutoCorrectPath(); break;
+ case SvtPathOptions::Paths::AutoText: aValue = aPathCfg.GetAutoTextPath(); break;
+ case SvtPathOptions::Paths::Backup: aValue = aPathCfg.GetBackupPath(); break;
+ case SvtPathOptions::Paths::Basic: aValue = aPathCfg.GetBasicPath(); break;
+ case SvtPathOptions::Paths::Bitmap: aValue = aPathCfg.GetBitmapPath(); break;
+ case SvtPathOptions::Paths::Config: aValue = aPathCfg.GetConfigPath(); break;
+ case SvtPathOptions::Paths::Dictionary: aValue = aPathCfg.GetDictionaryPath(); break;
+ case SvtPathOptions::Paths::Favorites: aValue = aPathCfg.GetFavoritesPath(); break;
+ case SvtPathOptions::Paths::Filter: osl::FileBase::getFileURLFromSystemPath( aPathCfg.GetFilterPath(), aValue ); break;
+ case SvtPathOptions::Paths::Gallery: aValue = aPathCfg.GetGalleryPath(); break;
+ case SvtPathOptions::Paths::Graphic: aValue = aPathCfg.GetGraphicPath(); break;
+ case SvtPathOptions::Paths::Help: osl::FileBase::getFileURLFromSystemPath( aPathCfg.GetHelpPath(), aValue ); break;
+ case SvtPathOptions::Paths::Linguistic: aValue = aPathCfg.GetLinguisticPath(); break;
+ case SvtPathOptions::Paths::Module: osl::FileBase::getFileURLFromSystemPath( aPathCfg.GetModulePath(), aValue ); break;
+ case SvtPathOptions::Paths::Palette: aValue = aPathCfg.GetPalettePath(); break;
+ case SvtPathOptions::Paths::Plugin: osl::FileBase::getFileURLFromSystemPath( aPathCfg.GetPluginPath(), aValue ); break;
+ case SvtPathOptions::Paths::Storage: osl::FileBase::getFileURLFromSystemPath( aPathCfg.GetStoragePath(), aValue ); break;
+ case SvtPathOptions::Paths::Temp: aValue = aPathCfg.GetTempPath(); break;
+ case SvtPathOptions::Paths::Template: aValue = aPathCfg.GetTemplatePath(); break;
+ case SvtPathOptions::Paths::UserConfig: aValue = aPathCfg.GetUserConfigPath(); break;
+ case SvtPathOptions::Paths::Work: aValue = aPathCfg.GetWorkPath(); break;
+ default: break;
+ }
+ aValues.SetTextByPos( nProp, aValue );
+ }
+
+ if (rSet.Put(aValues))
+ bRet = true;
+ }
+ break;
+
+ default:
+ SAL_INFO( "sfx.appl", "W1:Wrong ID while getting Options!" );
+ break;
+ }
+ SAL_WARN_IF(!bRet, "sfx.appl", "Putting options failed!");
+ }
+ }
+}
+
+// TODO/CLEANUP: Why two SetOptions Methods?
+void SfxApplication::SetOptions_Impl( const SfxItemSet& rSet )
+{
+ SfxItemPool &rPool = GetPool();
+
+ SvtMiscOptions aMiscOptions;
+ std::shared_ptr< comphelper::ConfigurationChanges > batch(
+ comphelper::ConfigurationChanges::create());
+
+ if ( const SfxBoolItem *pItem = rSet.GetItemIfSet(SID_ATTR_BUTTON_BIGSIZE) )
+ {
+ bool bBigSize = pItem->GetValue();
+ aMiscOptions.SetSymbolsSize(
+ sal::static_int_cast< sal_Int16 >(
+ bBigSize ? SFX_SYMBOLS_SIZE_LARGE : SFX_SYMBOLS_SIZE_SMALL ) );
+ SfxViewFrame* pCurrViewFrame = SfxViewFrame::GetFirst();
+ while ( pCurrViewFrame )
+ {
+ // update all "final" dispatchers
+ pCurrViewFrame->GetDispatcher()->Update_Impl(true);
+ pCurrViewFrame = SfxViewFrame::GetNext(*pCurrViewFrame);
+ }
+ }
+
+ // Backup
+ if ( const SfxBoolItem *pItem = rSet.GetItemIfSet(SID_ATTR_BACKUP) )
+ {
+ officecfg::Office::Common::Save::Document::CreateBackup::set(
+ pItem->GetValue(),
+ batch );
+ }
+
+ // PrettyPrinting
+ if ( const SfxBoolItem *pItem = rSet.GetItemIfSet( SID_ATTR_PRETTYPRINTING ) )
+ {
+ officecfg::Office::Common::Save::Document::PrettyPrinting::set(
+ pItem->GetValue(),
+ batch );
+ }
+
+ // WarnAlienFormat
+ if ( const SfxBoolItem *pItem = rSet.GetItemIfSet( SID_ATTR_WARNALIENFORMAT ) )
+ {
+ officecfg::Office::Common::Save::Document::WarnAlienFormat::set(
+ pItem->GetValue(),
+ batch);
+ }
+
+ // AutoSave
+ if ( const SfxBoolItem *pItem = rSet.GetItemIfSet( SID_ATTR_AUTOSAVE ))
+ {
+ officecfg::Office::Common::Save::Document::AutoSave::set(
+ pItem->GetValue(),
+ batch);
+ }
+
+ // AutoSave-Prompt
+ if ( const SfxBoolItem *pItem = rSet.GetItemIfSet( SID_ATTR_AUTOSAVEPROMPT ))
+ {
+ officecfg::Office::Common::Save::Document::AutoSavePrompt::set(
+ pItem->GetValue(),
+ batch);
+ }
+
+ // AutoSave-Time
+ if ( const SfxUInt16Item *pItem = rSet.GetItemIfSet(SID_ATTR_AUTOSAVEMINUTE ))
+ {
+ officecfg::Office::Common::Save::Document::AutoSaveTimeIntervall::set(
+ pItem->GetValue(),
+ batch);
+ }
+
+ // UserAutoSave
+ if ( const SfxBoolItem *pItem = rSet.GetItemIfSet(SID_ATTR_USERAUTOSAVE))
+ {
+ officecfg::Office::Recovery::AutoSave::UserAutoSaveEnabled::set(
+ pItem->GetValue(),
+ batch);
+ }
+
+ // DocInfo
+ if ( const SfxBoolItem *pItem = rSet.GetItemIfSet(SID_ATTR_DOCINFO) )
+ {
+ officecfg::Office::Common::Save::Document::EditProperty::set(
+ pItem->GetValue(),
+ batch);
+ }
+
+ // Mark open Documents
+ if ( const SfxBoolItem *pItem = rSet.GetItemIfSet(SID_ATTR_WORKINGSET))
+ {
+ officecfg::Office::Common::Save::WorkingSet::set(
+ pItem->GetValue(),
+ batch);
+ }
+
+ // Save window settings
+ if ( const SfxBoolItem *pItem = rSet.GetItemIfSet(SID_ATTR_SAVEDOCVIEW))
+ {
+ officecfg::Office::Common::Save::Document::ViewInfo::set(pItem->GetValue(), batch);
+ }
+
+ // Metric
+ const SfxPoolItem* pItem1 = nullptr;
+ if ( SfxItemState::SET == rSet.GetItemState(rPool.GetWhich(SID_ATTR_METRIC), true, &pItem1))
+ {
+ DBG_ASSERT(dynamic_cast< const SfxUInt16Item *>( pItem1 ) != nullptr, "UInt16Item expected");
+ }
+
+ // HelpBalloons
+ if ( const SfxBoolItem *pItem = rSet.GetItemIfSet(SID_HELPBALLOONS))
+ {
+ officecfg::Office::Common::Help::ExtendedTip::set(
+ pItem->GetValue(),
+ batch);
+ }
+
+ // HelpTips
+ if ( const SfxBoolItem *pItem = rSet.GetItemIfSet(SID_HELPTIPS))
+ {
+ officecfg::Office::Common::Help::Tip::set(
+ pItem->GetValue(),
+ batch);
+ }
+
+ if ( const SfxStringItem *pItem = rSet.GetItemIfSet(SID_HELP_STYLESHEET))
+ {
+ OUString sStyleSheet = pItem->GetValue();
+ officecfg::Office::Common::Help::HelpStyleSheet::set(sStyleSheet, batch);
+ }
+
+ // SaveRelINet
+ if ( const SfxBoolItem *pItem = rSet.GetItemIfSet(SID_SAVEREL_INET))
+ {
+ officecfg::Office::Common::Save::URL::Internet::set(
+ pItem->GetValue(),
+ batch);
+ }
+
+ // SaveRelFSys
+ if ( const SfxBoolItem *pItem = rSet.GetItemIfSet(SID_SAVEREL_FSYS))
+ {
+ officecfg::Office::Common::Save::URL::FileSystem::set(
+ pItem->GetValue(),
+ batch);
+ }
+
+ // Undo-Count
+ if ( const SfxUInt16Item *pItem = rSet.GetItemIfSet(SID_ATTR_UNDO_COUNT))
+ {
+ sal_uInt16 nUndoCount = pItem->GetValue();
+ officecfg::Office::Common::Undo::Steps::set(nUndoCount, batch);
+
+ // To catch all Undo-Managers: Iterate over all Frames
+ for ( SfxViewFrame *pFrame = SfxViewFrame::GetFirst();
+ pFrame;
+ pFrame = SfxViewFrame::GetNext(*pFrame) )
+ {
+ // Get the Dispatcher of the Frames
+ SfxDispatcher *pDispat = pFrame->GetDispatcher();
+ pDispat->Flush();
+
+ // Iterate over all SfxShells on the Dispatchers Stack
+ sal_uInt16 nIdx = 0;
+ for ( SfxShell *pSh = pDispat->GetShell(nIdx);
+ pSh;
+ ++nIdx, pSh = pDispat->GetShell(nIdx) )
+ {
+ SfxUndoManager *pShUndoMgr = pSh->GetUndoManager();
+ if ( pShUndoMgr )
+ pShUndoMgr->SetMaxUndoActionCount( nUndoCount );
+ }
+ }
+ }
+
+ // Office autostart
+ if ( const SfxBoolItem *pItem = rSet.GetItemIfSet(SID_ATTR_QUICKLAUNCHER))
+ {
+ ShutdownIcon::SetAutostart( pItem->GetValue() );
+ }
+
+ if ( const SfxUInt16Item *pItem = rSet.GetItemIfSet(SID_INET_PROXY_TYPE))
+ {
+ officecfg::Inet::Settings::ooInetProxyType::set(
+ pItem->GetValue(), batch);
+ }
+
+ if ( const SfxStringItem *pItem = rSet.GetItemIfSet( SID_INET_HTTP_PROXY_NAME ) )
+ {
+ officecfg::Inet::Settings::ooInetHTTPProxyName::set(
+ pItem->GetValue(), batch);
+ }
+ if ( const SfxInt32Item *pItem = rSet.GetItemIfSet( SID_INET_HTTP_PROXY_PORT ) )
+ {
+ officecfg::Inet::Settings::ooInetHTTPProxyPort::set(
+ pItem->GetValue(), batch);
+ }
+ if ( const SfxStringItem *pItem = rSet.GetItemIfSet( SID_INET_FTP_PROXY_NAME ) )
+ {
+ officecfg::Inet::Settings::ooInetFTPProxyName::set(
+ pItem->GetValue(), batch);
+ }
+ if (const SfxInt32Item *pItem = rSet.GetItemIfSet( SID_INET_FTP_PROXY_PORT ) )
+ {
+ officecfg::Inet::Settings::ooInetFTPProxyPort::set(
+ pItem->GetValue(), batch);
+ }
+ if ( const SfxStringItem* pStringItem = rSet.GetItemIfSet(SID_INET_NOPROXY))
+ {
+ officecfg::Inet::Settings::ooInetNoProxy::set(
+ pStringItem->GetValue(), batch);
+ }
+
+ // Secure-Referrer
+ if ( const SfxStringListItem *pListItem = rSet.GetItemIfSet(SID_SECURE_URL))
+ {
+ SvtSecurityOptions::SetSecureURLs( std::vector(pListItem->GetList()) );
+ }
+
+ // Store changed data
+ batch->commit();
+}
+
+
+void SfxApplication::SetOptions(const SfxItemSet &rSet)
+{
+ SvtPathOptions aPathOptions;
+
+ // Data is saved in DocInfo and IniManager
+
+ SfxAllItemSet aSendSet( rSet );
+
+ // PathName
+ if ( const SfxAllEnumItem* pEnumItem = rSet.GetItemIfSet(SID_ATTR_PATHNAME))
+ {
+ sal_uInt32 nCount = pEnumItem->GetTextCount();
+ OUString aNoChangeStr( ' ' );
+ for( sal_uInt32 nPath=0; nPath<nCount; ++nPath )
+ {
+ const OUString& sValue = pEnumItem->GetTextByPos(static_cast<sal_uInt16>(nPath));
+ if ( sValue != aNoChangeStr )
+ {
+ switch( static_cast<SvtPathOptions::Paths>(nPath) )
+ {
+ case SvtPathOptions::Paths::AddIn:
+ {
+ OUString aTmp;
+ if( osl::FileBase::getSystemPathFromFileURL( sValue, aTmp ) == osl::FileBase::E_None )
+ aPathOptions.SetAddinPath( aTmp );
+ break;
+ }
+
+ case SvtPathOptions::Paths::AutoCorrect: aPathOptions.SetAutoCorrectPath( sValue );break;
+ case SvtPathOptions::Paths::AutoText: aPathOptions.SetAutoTextPath( sValue );break;
+ case SvtPathOptions::Paths::Backup: aPathOptions.SetBackupPath( sValue );break;
+ case SvtPathOptions::Paths::Basic: aPathOptions.SetBasicPath( sValue );break;
+ case SvtPathOptions::Paths::Bitmap: aPathOptions.SetBitmapPath( sValue );break;
+ case SvtPathOptions::Paths::Config: aPathOptions.SetConfigPath( sValue );break;
+ case SvtPathOptions::Paths::Dictionary: aPathOptions.SetDictionaryPath( sValue );break;
+ case SvtPathOptions::Paths::Favorites: aPathOptions.SetFavoritesPath( sValue );break;
+ case SvtPathOptions::Paths::Filter:
+ {
+ OUString aTmp;
+ if( osl::FileBase::getSystemPathFromFileURL( sValue, aTmp ) == osl::FileBase::E_None )
+ aPathOptions.SetFilterPath( aTmp );
+ break;
+ }
+ case SvtPathOptions::Paths::Gallery: aPathOptions.SetGalleryPath( sValue );break;
+ case SvtPathOptions::Paths::Graphic: aPathOptions.SetGraphicPath( sValue );break;
+ case SvtPathOptions::Paths::Help:
+ {
+ OUString aTmp;
+ if( osl::FileBase::getSystemPathFromFileURL( sValue, aTmp ) == osl::FileBase::E_None )
+ aPathOptions.SetHelpPath( aTmp );
+ break;
+ }
+
+ case SvtPathOptions::Paths::Linguistic: aPathOptions.SetLinguisticPath( sValue );break;
+ case SvtPathOptions::Paths::Module:
+ {
+ OUString aTmp;
+ if( osl::FileBase::getSystemPathFromFileURL( sValue, aTmp ) == osl::FileBase::E_None )
+ aPathOptions.SetModulePath( aTmp );
+ break;
+ }
+
+ case SvtPathOptions::Paths::Palette: aPathOptions.SetPalettePath( sValue );break;
+ case SvtPathOptions::Paths::Plugin:
+ {
+ OUString aTmp;
+ if( osl::FileBase::getSystemPathFromFileURL( sValue, aTmp ) == osl::FileBase::E_None )
+ aPathOptions.SetPluginPath( aTmp );
+ break;
+ }
+
+ case SvtPathOptions::Paths::Storage:
+ {
+ OUString aTmp;
+ if( osl::FileBase::getSystemPathFromFileURL( sValue, aTmp ) == osl::FileBase::E_None )
+ aPathOptions.SetStoragePath( aTmp );
+ break;
+ }
+
+ case SvtPathOptions::Paths::Temp: aPathOptions.SetTempPath( sValue );break;
+ case SvtPathOptions::Paths::Template: aPathOptions.SetTemplatePath( sValue );break;
+ case SvtPathOptions::Paths::UserConfig: aPathOptions.SetUserConfigPath( sValue );break;
+ case SvtPathOptions::Paths::Work: aPathOptions.SetWorkPath( sValue );break;
+ default: SAL_WARN( "sfx.appl", "SfxApplication::SetOptions_Impl() Invalid path number found for set directories!" );
+ }
+ }
+ }
+
+ aSendSet.ClearItem( SID_ATTR_PATHNAME );
+ }
+
+ SetOptions_Impl( rSet );
+
+ // Undo-Count
+ Broadcast( SfxItemSetHint( rSet ) );
+}
+
+
+void SfxApplication::NotifyEvent( const SfxEventHint& rEventHint, bool bSynchron )
+{
+ SfxObjectShell *pDoc = rEventHint.GetObjShell();
+ if ( pDoc && ( pDoc->IsPreview() || !pDoc->Get_Impl()->bInitialized ) )
+ return;
+
+ if ( bSynchron )
+ {
+ SAL_INFO_IF(!pDoc, "sfx.appl", "SfxEvent: " << rEventHint.GetEventName());
+ Broadcast(rEventHint);
+ if ( pDoc )
+ pDoc->Broadcast( rEventHint );
+ }
+ else
+ new SfxEventAsyncer_Impl( rEventHint );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/appl/appchild.cxx b/sfx2/source/appl/appchild.cxx
new file mode 100644
index 000000000..246eb4413
--- /dev/null
+++ b/sfx2/source/appl/appchild.cxx
@@ -0,0 +1,66 @@
+/* -*- 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/app.hxx>
+#include <appdata.hxx>
+#include <workwin.hxx>
+#include <sfx2/childwin.hxx>
+#include <sfx2/module.hxx>
+#include <sfx2/viewfrm.hxx>
+
+
+void SfxApplication::RegisterChildWindow_Impl( SfxModule *pMod, const SfxChildWinFactory& rFact )
+{
+ if ( pMod )
+ {
+ pMod->RegisterChildWindow( rFact );
+ return;
+ }
+
+ for (size_t nFactory=0; nFactory<pImpl->maFactories.size(); ++nFactory)
+ {
+ if (rFact.nId == pImpl->maFactories[nFactory].nId)
+ {
+ pImpl->maFactories.erase( pImpl->maFactories.begin() + nFactory );
+ }
+ }
+
+ pImpl->maFactories.push_back( rFact );
+}
+
+SfxChildWinFactory* SfxApplication::GetChildWinFactoryById(sal_uInt16 nId) const
+{
+ for (auto& rFactory : pImpl->maFactories)
+ if (rFactory.nId == nId)
+ return &rFactory;
+ return nullptr;
+}
+
+SfxWorkWindow* SfxApplication::GetWorkWindow_Impl(const SfxViewFrame *pFrame) const
+{
+ if ( pFrame )
+ return pFrame->GetFrame().GetWorkWindow_Impl();
+ else if ( pImpl->pViewFrame )
+ return pImpl->pViewFrame->GetFrame().GetWorkWindow_Impl();
+ else
+ return nullptr;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/appl/appdata.cxx b/sfx2/source/appl/appdata.cxx
new file mode 100644
index 000000000..819d70378
--- /dev/null
+++ b/sfx2/source/appl/appdata.cxx
@@ -0,0 +1,127 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <config_features.h>
+
+#include <appdata.hxx>
+#include <sfx2/tbxctrl.hxx>
+#include <sfx2/stbitem.hxx>
+#include <sfx2/childwin.hxx>
+#include <sfx2/doctempl.hxx>
+#include <sfx2/module.hxx>
+#include <sfx2/sidebar/Theme.hxx>
+#include <sfx2/objsh.hxx>
+#include <appbaslib.hxx>
+#include <unoctitm.hxx>
+#include <svl/svdde.hxx>
+
+#include <basic/basicmanagerrepository.hxx>
+#include <basic/basmgr.hxx>
+#include <basic/basrdll.hxx>
+
+using ::basic::BasicManagerRepository;
+using ::basic::BasicManagerCreationListener;
+using ::com::sun::star::uno::Reference;
+using ::com::sun::star::frame::XModel;
+using ::com::sun::star::uno::XInterface;
+
+static BasicDLL* pBasic = nullptr;
+
+class SfxBasicManagerCreationListener : public ::basic::BasicManagerCreationListener
+{
+private:
+ SfxAppData_Impl& m_rAppData;
+
+public:
+ explicit SfxBasicManagerCreationListener(SfxAppData_Impl& _rAppData)
+ : m_rAppData(_rAppData)
+ {
+ }
+
+ virtual ~SfxBasicManagerCreationListener();
+
+ virtual void onBasicManagerCreated( const Reference< XModel >& _rxForDocument, BasicManager& _rBasicManager ) override;
+};
+
+SfxBasicManagerCreationListener::~SfxBasicManagerCreationListener()
+{
+}
+
+void SfxBasicManagerCreationListener::onBasicManagerCreated( const Reference< XModel >& _rxForDocument, BasicManager& _rBasicManager )
+{
+ if ( _rxForDocument == nullptr )
+ m_rAppData.OnApplicationBasicManagerCreated( _rBasicManager );
+}
+
+SfxAppData_Impl::SfxAppData_Impl()
+ : pPool(nullptr)
+ , pProgress(nullptr)
+ , nDocModalMode(0)
+ , nRescheduleLocks(0)
+ , pBasicManager( new SfxBasicManagerHolder )
+ , pBasMgrListener( new SfxBasicManagerCreationListener( *this ) )
+ , pViewFrame( nullptr )
+ , bDowning( true )
+ , bInQuit( false )
+
+{
+ pBasic = new BasicDLL;
+
+#if HAVE_FEATURE_SCRIPTING
+ BasicManagerRepository::registerCreationListener( *pBasMgrListener );
+#endif
+}
+
+SfxAppData_Impl::~SfxAppData_Impl()
+{
+ DeInitDDE();
+ pBasicManager.reset();
+
+#if HAVE_FEATURE_SCRIPTING
+ BasicManagerRepository::revokeCreationListener( *pBasMgrListener );
+ pBasMgrListener.reset();
+#endif
+
+ delete pBasic;
+}
+
+SfxDocumentTemplates* SfxAppData_Impl::GetDocumentTemplates()
+{
+ if ( !pTemplates )
+ pTemplates.emplace();
+ else
+ pTemplates->ReInitFromComponent();
+ return &*pTemplates;
+}
+
+void SfxAppData_Impl::OnApplicationBasicManagerCreated( BasicManager& _rBasicManager )
+{
+#if !HAVE_FEATURE_SCRIPTING
+ (void) _rBasicManager;
+#else
+ pBasicManager->reset( &_rBasicManager );
+
+ // global constants, additionally to the ones already added by createApplicationBasicManager:
+ // ThisComponent
+ Reference< XInterface > xCurrentComponent = SfxObjectShell::GetCurrentComponent();
+ _rBasicManager.SetGlobalUNOConstant( "ThisComponent", css::uno::Any( xCurrentComponent ) );
+#endif
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/appl/appdde.cxx b/sfx2/source/appl/appdde.cxx
new file mode 100644
index 000000000..d725d050f
--- /dev/null
+++ b/sfx2/source/appl/appdde.cxx
@@ -0,0 +1,570 @@
+/* -*- 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 <string_view>
+
+#include <config_features.h>
+#include <rtl/character.hxx>
+#include <rtl/malformeduriexception.hxx>
+#include <rtl/uri.hxx>
+#include <sot/exchange.hxx>
+#include <svl/eitem.hxx>
+#include <basic/sbstar.hxx>
+#include <svl/stritem.hxx>
+#include <svl/svdde.hxx>
+#include <sfx2/lnkbase.hxx>
+#include <sfx2/linkmgr.hxx>
+
+#include <tools/debug.hxx>
+#include <tools/urlobj.hxx>
+#include <tools/diagnose_ex.h>
+#include <unotools/pathoptions.hxx>
+#include <vcl/svapp.hxx>
+
+#include <sfx2/app.hxx>
+#include <appdata.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/sfxsids.hrc>
+#include <sfx2/docfile.hxx>
+#include <ucbhelper/content.hxx>
+#include <comphelper/processfactory.hxx>
+
+#if defined(_WIN32)
+
+static OUString SfxDdeServiceName_Impl( const OUString& sIn )
+{
+ OUStringBuffer sReturn(sIn.getLength());
+
+ for ( sal_uInt16 n = sIn.getLength(); n; --n )
+ {
+ sal_Unicode cChar = sIn[n-1];
+ if (rtl::isAsciiAlphanumeric(cChar))
+ sReturn.append(cChar);
+ }
+
+ return sReturn.makeStringAndClear();
+}
+
+namespace {
+
+class ImplDdeService : public DdeService
+{
+public:
+ explicit ImplDdeService( const OUString& rNm )
+ : DdeService( rNm )
+ {}
+ virtual bool MakeTopic( const OUString& );
+
+ virtual OUString Topics();
+
+ virtual bool SysTopicExecute( const OUString* pStr );
+};
+
+ bool lcl_IsDocument( std::u16string_view rContent )
+ {
+ using namespace com::sun::star;
+
+ bool bRet = false;
+ INetURLObject aObj( rContent );
+ DBG_ASSERT( aObj.GetProtocol() != INetProtocol::NotValid, "Invalid URL!" );
+
+ try
+ {
+ ::ucbhelper::Content aCnt( aObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ), uno::Reference< ucb::XCommandEnvironment >(), comphelper::getProcessComponentContext() );
+ bRet = aCnt.isDocument();
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "sfx.appl", "" );
+ }
+
+ return bRet;
+ }
+}
+
+bool ImplDdeService::MakeTopic( const OUString& rNm )
+{
+ // Workaround for Event after Main() under OS/2
+ // happens when exiting starts the App again
+ if ( !Application::IsInExecute() )
+ return false;
+
+ // The Topic rNm is sought, do we have it?
+ // First only loop over the ObjectShells to find those
+ // with the specific name:
+ bool bRet = false;
+ OUString sNm( rNm.toAsciiLowerCase() );
+ SfxObjectShell* pShell = SfxObjectShell::GetFirst();
+ while( pShell )
+ {
+ OUString sTmp( pShell->GetTitle(SFX_TITLE_FULLNAME) );
+ if( sNm == sTmp.toAsciiLowerCase() )
+ {
+ SfxGetpApp()->AddDdeTopic( pShell );
+ bRet = true;
+ break;
+ }
+ pShell = SfxObjectShell::GetNext( *pShell );
+ }
+
+ if( !bRet )
+ {
+ bool abs;
+ OUString url;
+ try {
+ url = rtl::Uri::convertRelToAbs(SvtPathOptions().GetWorkPath(), rNm);
+ abs = true;
+ } catch (rtl::MalformedUriException &) {
+ abs = false;
+ }
+ if ( abs && lcl_IsDocument( url ) )
+ {
+ // File exists? then try to load it:
+ SfxStringItem aName( SID_FILE_NAME, url );
+ SfxBoolItem aNewView(SID_OPEN_NEW_VIEW, true);
+
+ SfxBoolItem aSilent(SID_SILENT, true);
+ SfxDispatcher* pDispatcher = SfxGetpApp()->GetDispatcher_Impl();
+ const SfxPoolItem* pRet = pDispatcher->ExecuteList(SID_OPENDOC,
+ SfxCallMode::SYNCHRON,
+ { &aName, &aNewView, &aSilent });
+
+ if( auto const item = dynamic_cast< const SfxViewFrameItem *>( pRet );
+ item &&
+ item->GetFrame() &&
+ nullptr != ( pShell = item->GetFrame()->GetObjectShell() ) )
+ {
+ SfxGetpApp()->AddDdeTopic( pShell );
+ bRet = true;
+ }
+ }
+ }
+ return bRet;
+}
+
+OUString ImplDdeService::Topics()
+{
+ OUString sRet;
+ if( GetSysTopic() )
+ sRet += GetSysTopic()->GetName();
+
+ SfxObjectShell* pShell = SfxObjectShell::GetFirst();
+ while( pShell )
+ {
+ if( SfxViewFrame::GetFirst( pShell ) )
+ {
+ if( !sRet.isEmpty() )
+ sRet += "\t";
+ sRet += pShell->GetTitle(SFX_TITLE_FULLNAME);
+ }
+ pShell = SfxObjectShell::GetNext( *pShell );
+ }
+ if( !sRet.isEmpty() )
+ sRet += "\r\n";
+ return sRet;
+}
+
+bool ImplDdeService::SysTopicExecute( const OUString* pStr )
+{
+ return SfxApplication::DdeExecute( *pStr );
+}
+#endif
+
+class SfxDdeDocTopic_Impl : public DdeTopic
+{
+#if defined(_WIN32)
+public:
+ SfxObjectShell* pSh;
+ DdeData aData;
+ css::uno::Sequence< sal_Int8 > aSeq;
+
+ explicit SfxDdeDocTopic_Impl( SfxObjectShell* pShell )
+ : DdeTopic( pShell->GetTitle(SFX_TITLE_FULLNAME) ), pSh( pShell )
+ {}
+
+ virtual DdeData* Get( SotClipboardFormatId ) override;
+ virtual bool Put( const DdeData* ) override;
+ virtual bool Execute( const OUString* ) override;
+ virtual bool StartAdviseLoop() override;
+ virtual bool MakeItem( const OUString& rItem ) override;
+#endif
+};
+
+
+#if defined(_WIN32)
+
+namespace {
+
+/* [Description]
+
+ Checks if 'rCmd' of the event 'rEvent' is (without '(') and then assemble
+ this data into a <ApplicationEvent>, which is then executed through
+ <Application::AppEvent()>. If 'rCmd' is the given event 'rEvent', then
+ TRUE is returned, otherwise FALSE.
+
+ [Example]
+
+ rCmd = "Open(\"d:\doc\doc.sdw\")"
+ rEvent = "Open"
+*/
+bool SfxAppEvent_Impl( const OUString& rCmd, std::u16string_view rEvent,
+ ApplicationEvent::Type eType )
+{
+ OUString sEvent(OUString::Concat(rEvent) + "(");
+ if (rCmd.startsWithIgnoreAsciiCase(sEvent))
+ {
+ sal_Int32 start = sEvent.getLength();
+ if ( rCmd.getLength() - start >= 2 )
+ {
+ // Transform into the ApplicationEvent Format
+ //TODO: I /assume/ that rCmd should match the syntax of
+ // <http://msdn.microsoft.com/en-us/library/ms648995.aspx>
+ // "WM_DDE_EXECUTE message" but does not (handle commands enclosed
+ // in [...]; handle commas separating multiple arguments; handle
+ // double "", ((, )), [[, ]] in quoted arguments); see also the mail
+ // thread starting at <http://lists.freedesktop.org/archives/
+ // libreoffice/2013-July/054779.html> "DDE on Windows."
+ std::vector<OUString> aData;
+ for ( sal_Int32 n = start; n < rCmd.getLength() - 1; )
+ {
+ // Resiliently read arguments either starting with " and
+ // spanning to the next " (if any; TODO: do we need to undo any
+ // escaping within the string?) or with neither " nor SPC and
+ // spanning to the next SPC (if any; TODO: is this from not
+ // wrapped in "..." relevant? it would have been parsed by the
+ // original code even if that was only by accident, so I left it
+ // in), with runs of SPCs treated like single ones:
+ switch ( rCmd[n] )
+ {
+ case '"':
+ {
+ sal_Int32 i = rCmd.indexOf('"', ++n);
+ if (i < 0 || i > rCmd.getLength() - 1) {
+ i = rCmd.getLength() - 1;
+ }
+ aData.push_back(rCmd.copy(n, i - n));
+ n = i + 1;
+ break;
+ }
+ case ' ':
+ ++n;
+ break;
+ default:
+ {
+ sal_Int32 i = rCmd.indexOf(' ', n);
+ if (i < 0 || i > rCmd.getLength() - 1) {
+ i = rCmd.getLength() - 1;
+ }
+ aData.push_back(rCmd.copy(n, i - n));
+ n = i + 1;
+ break;
+ }
+ }
+ }
+
+ GetpApp()->AppEvent( ApplicationEvent(eType, std::move(aData)) );
+ return true;
+ }
+ }
+
+ return false;
+}
+
+}
+
+/* Description]
+
+ This method can be overridden by application developers, to receive
+ DDE-commands directed to their SfxApplication subclass.
+
+ The base implementation understands the API functionality of the
+ relevant SfxApplication subclass in BASIC syntax. Return values can
+ not be transferred, unfortunately.
+*/
+bool SfxApplication::DdeExecute( const OUString& rCmd ) // Expressed in our BASIC-Syntax
+{
+ // Print or Open-Event?
+ if ( !( SfxAppEvent_Impl( rCmd, u"Print", ApplicationEvent::Type::Print ) ||
+ SfxAppEvent_Impl( rCmd, u"Open", ApplicationEvent::Type::Open ) ) )
+ {
+ // all others are BASIC
+ StarBASIC* pBasic = GetBasic();
+ DBG_ASSERT( pBasic, "Where is the Basic???" );
+ SbxVariable* pRet = pBasic->Execute( rCmd );
+ if( !pRet )
+ {
+ SbxBase::ResetError();
+ return false;
+ }
+ }
+ return true;
+}
+
+/* [Description]
+
+ This method can be overridden by application developers, to receive
+ DDE-commands directed to the their SfxApplication subclass.
+
+ The base implementation does nothing and returns 0.
+*/
+bool SfxObjectShell::DdeExecute( const OUString& rCmd ) // Expressed in our BASIC-Syntax
+{
+#if !HAVE_FEATURE_SCRIPTING
+ (void) rCmd;
+#else
+ StarBASIC* pBasic = GetBasic();
+ DBG_ASSERT( pBasic, "Where is the Basic???" ) ;
+ SbxVariable* pRet = pBasic->Execute( rCmd );
+ if( !pRet )
+ {
+ SbxBase::ResetError();
+ return false;
+ }
+#endif
+ return true;
+}
+
+/* [Description]
+
+ This method can be overridden by application developers, to receive
+ DDE-data-requests directed to their SfxApplication subclass.
+
+ The base implementation provides no data and returns false.
+*/
+bool SfxObjectShell::DdeGetData( const OUString&, // the Item to be addressed
+ const OUString&, // in: Format
+ css::uno::Any& )// out: requested data
+{
+ return false;
+}
+
+
+/* [Description]
+
+ This method can be overridden by application developers, to receive
+ DDE-data directed to their SfxApplication subclass.
+
+ The base implementation is not receiving any data and returns false.
+*/
+bool SfxObjectShell::DdeSetData( const OUString&, // the Item to be addressed
+ const OUString&, // in: Format
+ const css::uno::Any& )// out: requested data
+{
+ return false;
+}
+
+#endif
+
+/* [Description]
+
+ This method can be overridden by application developers, to establish
+ a DDE-hotlink to their SfxApplication subclass.
+
+ The base implementation is not generate a link and returns 0.
+*/
+::sfx2::SvLinkSource* SfxObjectShell::DdeCreateLinkSource( const OUString& ) // the Item to be addressed
+{
+ return nullptr;
+}
+
+void SfxObjectShell::ReconnectDdeLink(SfxObjectShell& /*rServer*/)
+{
+}
+
+void SfxObjectShell::ReconnectDdeLinks(SfxObjectShell& rServer)
+{
+ SfxObjectShell* p = GetFirst(nullptr, false);
+ while (p)
+ {
+ if (&rServer != p)
+ p->ReconnectDdeLink(rServer);
+
+ p = GetNext(*p, nullptr, false);
+ }
+}
+
+bool SfxApplication::InitializeDde()
+{
+ int nError = 0;
+#if defined(_WIN32)
+ DBG_ASSERT( !pImpl->pDdeService,
+ "Dde can not be initialized multiple times" );
+
+ pImpl->pDdeService.reset(new ImplDdeService( Application::GetAppName() ));
+ nError = pImpl->pDdeService->GetError();
+ if( !nError )
+ {
+ pImpl->pDocTopics.reset(new SfxDdeDocTopics_Impl);
+
+ // we certainly want to support RTF!
+ pImpl->pDdeService->AddFormat( SotClipboardFormatId::RTF );
+ pImpl->pDdeService->AddFormat( SotClipboardFormatId::RICHTEXT );
+
+ // Config path as a topic because of multiple starts
+ INetURLObject aOfficeLockFile( SvtPathOptions().GetUserConfigPath() );
+ aOfficeLockFile.insertName( u"soffice.lck" );
+ OUString aService( SfxDdeServiceName_Impl(
+ aOfficeLockFile.GetMainURL(INetURLObject::DecodeMechanism::ToIUri) ) );
+ aService = aService.toAsciiUpperCase();
+ pImpl->pDdeService2.reset( new ImplDdeService( aService ));
+ pImpl->pTriggerTopic.reset(new SfxDdeTriggerTopic_Impl);
+ pImpl->pDdeService2->AddTopic( *pImpl->pTriggerTopic );
+ }
+#endif
+ return !nError;
+}
+
+void SfxAppData_Impl::DeInitDDE()
+{
+ pTriggerTopic.reset();
+ pDdeService2.reset();
+ pDocTopics.reset();
+ pDdeService.reset();
+}
+
+#if defined(_WIN32)
+void SfxApplication::AddDdeTopic( SfxObjectShell* pSh )
+{
+ //OV: DDE is disconnected in server mode!
+ if( !pImpl->pDocTopics )
+ return;
+
+ // prevent double submit
+ OUString sShellNm;
+ bool bFnd = false;
+ for (size_t n = pImpl->pDocTopics->size(); n;)
+ {
+ if( (*pImpl->pDocTopics)[ --n ]->pSh == pSh )
+ {
+ // If the document is untitled, is still a new Topic is created!
+ if( !bFnd )
+ {
+ bFnd = true;
+ sShellNm = pSh->GetTitle(SFX_TITLE_FULLNAME).toAsciiLowerCase();
+ }
+ OUString sNm( (*pImpl->pDocTopics)[ n ]->GetName() );
+ if( sShellNm == sNm.toAsciiLowerCase() )
+ return ;
+ }
+ }
+
+ SfxDdeDocTopic_Impl *const pTopic = new SfxDdeDocTopic_Impl(pSh);
+ pImpl->pDocTopics->push_back(pTopic);
+ pImpl->pDdeService->AddTopic( *pTopic );
+}
+#endif
+
+void SfxApplication::RemoveDdeTopic( SfxObjectShell const * pSh )
+{
+#if defined(_WIN32)
+ //OV: DDE is disconnected in server mode!
+ if( !pImpl->pDocTopics )
+ return;
+
+ for (size_t n = pImpl->pDocTopics->size(); n; )
+ {
+ SfxDdeDocTopic_Impl *const pTopic = (*pImpl->pDocTopics)[ --n ];
+ if (pTopic->pSh == pSh)
+ {
+ pImpl->pDdeService->RemoveTopic( *pTopic );
+ delete pTopic;
+ pImpl->pDocTopics->erase( pImpl->pDocTopics->begin() + n );
+ }
+ }
+#else
+ (void) pSh;
+#endif
+}
+
+const DdeService* SfxApplication::GetDdeService() const
+{
+ return pImpl->pDdeService.get();
+}
+
+DdeService* SfxApplication::GetDdeService()
+{
+ return pImpl->pDdeService.get();
+}
+
+#if defined(_WIN32)
+
+DdeData* SfxDdeDocTopic_Impl::Get(SotClipboardFormatId nFormat)
+{
+ OUString sMimeType( SotExchange::GetFormatMimeType( nFormat ));
+ css::uno::Any aValue;
+ bool bRet = pSh->DdeGetData( GetCurItem(), sMimeType, aValue );
+ if( bRet && aValue.hasValue() && ( aValue >>= aSeq ) )
+ {
+ aData = DdeData( aSeq.getConstArray(), aSeq.getLength(), nFormat );
+ return &aData;
+ }
+ aSeq.realloc( 0 );
+ return nullptr;
+}
+
+bool SfxDdeDocTopic_Impl::Put( const DdeData* pData )
+{
+ aSeq = css::uno::Sequence< sal_Int8 >(
+ static_cast<sal_Int8 const *>(pData->getData()), pData->getSize() );
+ bool bRet;
+ if( aSeq.getLength() )
+ {
+ css::uno::Any aValue;
+ aValue <<= aSeq;
+ OUString sMimeType( SotExchange::GetFormatMimeType( pData->GetFormat() ));
+ bRet = pSh->DdeSetData( GetCurItem(), sMimeType, aValue );
+ }
+ else
+ bRet = false;
+ return bRet;
+}
+
+bool SfxDdeDocTopic_Impl::Execute( const OUString* pStr )
+{
+ return pStr && pSh->DdeExecute( *pStr );
+}
+
+bool SfxDdeDocTopic_Impl::MakeItem( const OUString& rItem )
+{
+ AddItem( DdeItem( rItem ) );
+ return true;
+}
+
+bool SfxDdeDocTopic_Impl::StartAdviseLoop()
+{
+ bool bRet = false;
+ ::sfx2::SvLinkSource* pNewObj = pSh->DdeCreateLinkSource( GetCurItem() );
+ if( pNewObj )
+ {
+ // then we also establish a corresponding SvBaseLink
+ OUString sNm, sTmp( Application::GetAppName() );
+ ::sfx2::MakeLnkName( sNm, &sTmp, pSh->GetTitle(SFX_TITLE_FULLNAME), GetCurItem() );
+ new ::sfx2::SvBaseLink( sNm, sfx2::SvBaseLinkObjectType::DdeExternal, pNewObj );
+ bRet = true;
+ }
+ return bRet;
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/appl/appdispatchprovider.cxx b/sfx2/source/appl/appdispatchprovider.cxx
new file mode 100644
index 000000000..d3f64e1d4
--- /dev/null
+++ b/sfx2/source/appl/appdispatchprovider.cxx
@@ -0,0 +1,231 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <com/sun/star/frame/XAppDispatchProvider.hpp>
+#include <com/sun/star/frame/XDispatch.hpp>
+#include <com/sun/star/frame/XFrame.hpp>
+#include <com/sun/star/frame/DispatchDescriptor.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <com/sun/star/lang/XInitialization.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/util/URL.hpp>
+
+#include <comphelper/sequence.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <sfx2/app.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/msg.hxx>
+#include <sfx2/msgpool.hxx>
+#include <sfx2/sfxbasecontroller.hxx>
+#include <unoctitm.hxx>
+#include <vcl/svapp.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::frame;
+using namespace ::com::sun::star::uno;
+
+namespace {
+
+class SfxAppDispatchProvider : public ::cppu::WeakImplHelper< css::frame::XAppDispatchProvider,
+ css::lang::XServiceInfo,
+ css::lang::XInitialization >
+{
+ css::uno::WeakReference < css::frame::XFrame > m_xFrame;
+public:
+ SfxAppDispatchProvider() {}
+
+ virtual void SAL_CALL initialize(
+ css::uno::Sequence<css::uno::Any> const & aArguments) override;
+
+ virtual OUString SAL_CALL getImplementationName() override;
+
+ virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override;
+
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+
+ virtual css::uno::Reference < css::frame::XDispatch > SAL_CALL queryDispatch(
+ const css::util::URL& aURL, const OUString& sTargetFrameName,
+ sal_Int32 eSearchFlags ) override;
+
+ virtual css::uno::Sequence< css::uno::Reference < css::frame::XDispatch > > SAL_CALL queryDispatches(
+ const css::uno::Sequence < css::frame::DispatchDescriptor >& seqDescriptor ) override;
+
+ virtual css::uno::Sequence< sal_Int16 > SAL_CALL getSupportedCommandGroups() override;
+
+ virtual css::uno::Sequence< css::frame::DispatchInformation > SAL_CALL getConfigurableDispatchInformation( sal_Int16 ) override;
+};
+
+void SfxAppDispatchProvider::initialize(
+ css::uno::Sequence<css::uno::Any> const & aArguments)
+{
+ css::uno::Reference<css::frame::XFrame> f;
+ if (aArguments.getLength() != 1 || !(aArguments[0] >>= f)) {
+ throw css::lang::IllegalArgumentException(
+ "SfxAppDispatchProvider::initialize expects one XFrame argument",
+ static_cast<OWeakObject *>(this), 0);
+ }
+ m_xFrame = f;
+}
+
+OUString SAL_CALL SfxAppDispatchProvider::getImplementationName()
+{
+ return "com.sun.star.comp.sfx2.AppDispatchProvider";
+}
+
+sal_Bool SAL_CALL SfxAppDispatchProvider::supportsService( const OUString& sServiceName )
+{
+ return cppu::supportsService(this, sServiceName);
+}
+
+css::uno::Sequence< OUString > SAL_CALL SfxAppDispatchProvider::getSupportedServiceNames()
+{
+ return { "com.sun.star.frame.ProtocolHandler", "com.sun.star.frame.AppDispatchProvider" };
+}
+
+Reference < XDispatch > SAL_CALL SfxAppDispatchProvider::queryDispatch(
+ const util::URL& aURL,
+ const OUString& /*sTargetFrameName*/,
+ sal_Int32 /*eSearchFlags*/ )
+{
+ SolarMutexGuard guard;
+
+ bool bMasterCommand( false );
+ Reference < XDispatch > xDisp;
+ const SfxSlot* pSlot = nullptr;
+ SfxApplication* pApp = SfxGetpApp();
+ if ( !pApp )
+ return xDisp;
+ SfxDispatcher* pAppDisp = pApp->GetAppDispatcher_Impl();
+ if ( aURL.Protocol == "slot:" || aURL.Protocol == "commandId:" )
+ {
+ sal_uInt16 nId = static_cast<sal_uInt16>(aURL.Path.toInt32());
+ SfxShell* pShell;
+ pAppDisp->GetShellAndSlot_Impl( nId, &pShell, &pSlot, true, true );
+ }
+ else if ( aURL.Protocol == ".uno:" )
+ {
+ // Support ".uno" commands. Map commands to slotid
+ bMasterCommand = SfxOfficeDispatch::IsMasterUnoCommand( aURL );
+ if ( bMasterCommand )
+ pSlot = pAppDisp->GetSlot( SfxOfficeDispatch::GetMasterUnoCommand( aURL ) );
+ else
+ pSlot = pAppDisp->GetSlot( aURL.Main );
+ }
+
+ if ( pSlot )
+ {
+ rtl::Reference<SfxOfficeDispatch> pDispatch = new SfxOfficeDispatch( pAppDisp, pSlot, aURL ) ;
+ pDispatch->SetFrame(m_xFrame);
+ pDispatch->SetMasterUnoCommand( bMasterCommand );
+ xDisp = pDispatch;
+ }
+
+ return xDisp;
+}
+
+Sequence< Reference < XDispatch > > SAL_CALL SfxAppDispatchProvider::queryDispatches( const Sequence < DispatchDescriptor >& seqDescriptor )
+{
+ sal_Int32 nCount = seqDescriptor.getLength();
+ uno::Sequence< uno::Reference < frame::XDispatch > > lDispatcher(nCount);
+ std::transform(seqDescriptor.begin(), seqDescriptor.end(), lDispatcher.getArray(),
+ [this](const DispatchDescriptor& rDescr) -> uno::Reference<frame::XDispatch> {
+ return queryDispatch(rDescr.FeatureURL, rDescr.FrameName, rDescr.SearchFlags); });
+ return lDispatcher;
+}
+
+Sequence< sal_Int16 > SAL_CALL SfxAppDispatchProvider::getSupportedCommandGroups()
+{
+ SolarMutexGuard aGuard;
+
+ std::vector< sal_Int16 > aGroupList;
+ SfxSlotPool& rAppSlotPool = SfxGetpApp()->GetAppSlotPool_Impl();
+
+ const SfxSlotMode nMode( SfxSlotMode::TOOLBOXCONFIG|SfxSlotMode::ACCELCONFIG|SfxSlotMode::MENUCONFIG );
+
+ // Select group ( group 0 is internal )
+ for (sal_uInt16 i=0; i< rAppSlotPool.GetGroupCount(); ++i)
+ {
+ rAppSlotPool.SeekGroup(i);
+ const SfxSlot* pSfxSlot = rAppSlotPool.FirstSlot();
+ while ( pSfxSlot )
+ {
+ if ( pSfxSlot->GetMode() & nMode )
+ {
+ sal_Int16 nCommandGroup = MapGroupIDToCommandGroup( pSfxSlot->GetGroupId() );
+ aGroupList.push_back( nCommandGroup );
+ break;
+ }
+ pSfxSlot = rAppSlotPool.NextSlot();
+ }
+ }
+
+ return comphelper::containerToSequence( aGroupList );
+}
+
+Sequence< frame::DispatchInformation > SAL_CALL SfxAppDispatchProvider::getConfigurableDispatchInformation( sal_Int16 nCmdGroup )
+{
+ std::vector< frame::DispatchInformation > aCmdVector;
+
+ SolarMutexGuard aGuard;
+ SfxSlotPool& rAppSlotPool = SfxGetpApp()->GetAppSlotPool_Impl();
+
+ const SfxSlotMode nMode( SfxSlotMode::TOOLBOXCONFIG|SfxSlotMode::ACCELCONFIG|SfxSlotMode::MENUCONFIG );
+
+ // Select group ( group 0 is internal )
+ for (sal_uInt16 i=0; i< rAppSlotPool.GetGroupCount(); ++i)
+ {
+ rAppSlotPool.SeekGroup(i);
+ const SfxSlot* pSfxSlot = rAppSlotPool.FirstSlot();
+ if ( pSfxSlot )
+ {
+ sal_Int16 nCommandGroup = MapGroupIDToCommandGroup( pSfxSlot->GetGroupId() );
+ if ( nCommandGroup == nCmdGroup )
+ {
+ while ( pSfxSlot )
+ {
+ if ( pSfxSlot->GetMode() & nMode )
+ {
+ frame::DispatchInformation aCmdInfo;
+ aCmdInfo.Command = ".uno:" + OUString::createFromAscii(pSfxSlot->GetUnoName());
+ aCmdInfo.GroupId = nCommandGroup;
+ aCmdVector.push_back( aCmdInfo );
+ }
+ pSfxSlot = rAppSlotPool.NextSlot();
+ }
+ }
+ }
+ }
+
+ return comphelper::containerToSequence( aCmdVector );
+}
+
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_sfx2_AppDispatchProvider_get_implementation(
+ css::uno::XComponentContext *,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new SfxAppDispatchProvider);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/appl/appinit.cxx b/sfx2/source/appl/appinit.cxx
new file mode 100644
index 000000000..52afe118e
--- /dev/null
+++ b/sfx2/source/appl/appinit.cxx
@@ -0,0 +1,233 @@
+/* -*- 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 <config_features.h>
+
+#include <sfx2/app.hxx>
+#include <com/sun/star/frame/XTerminateListener.hpp>
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/frame/theGlobalEventBroadcaster.hpp>
+#include <com/sun/star/frame/Desktop.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+
+#include <basic/sbdef.hxx>
+#include <tools/svlibrary.h>
+#include <svtools/soerr.hxx>
+#include <unotools/configmgr.hxx>
+#include <svtools/ehdl.hxx>
+#include <comphelper/processfactory.hxx>
+#include <osl/module.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <cppuhelper/supportsservice.hxx>
+
+#include <vcl/specialchars.hxx>
+#include <vcl/help.hxx>
+#include <vcl/svapp.hxx>
+
+#include <unoctitm.hxx>
+#include <appdata.hxx>
+#include <sfx2/dispatch.hxx>
+#include <nochaos.hxx>
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::frame;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star;
+
+namespace {
+
+class SfxTerminateListener_Impl : public ::cppu::WeakImplHelper< XTerminateListener, XServiceInfo >
+{
+public:
+
+ // XTerminateListener
+ virtual void SAL_CALL queryTermination( const EventObject& aEvent ) override;
+ virtual void SAL_CALL notifyTermination( const EventObject& aEvent ) override;
+ virtual void SAL_CALL disposing( const EventObject& Source ) 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;
+};
+
+}
+
+void SAL_CALL SfxTerminateListener_Impl::disposing( const EventObject& )
+{
+}
+
+void SAL_CALL SfxTerminateListener_Impl::queryTermination( const EventObject& )
+{
+}
+
+void SAL_CALL SfxTerminateListener_Impl::notifyTermination( const EventObject& aEvent )
+{
+ Reference< XDesktop > xDesktop( aEvent.Source, UNO_QUERY );
+ if( xDesktop.is() )
+ xDesktop->removeTerminateListener( this );
+
+ SolarMutexGuard aGuard;
+ utl::ConfigManager::storeConfigItems();
+
+ SfxApplication* pApp = SfxGetpApp();
+ pApp->Broadcast( SfxHint( SfxHintId::Deinitializing ) );
+ pApp->Get_Impl()->mxAppDispatch->ReleaseAll();
+ pApp->Get_Impl()->mxAppDispatch.clear();
+
+ css::uno::Reference< css::uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext();
+ css::uno::Reference< css::document::XDocumentEventListener > xGlobalBroadcaster(css::frame::theGlobalEventBroadcaster::get(xContext), css::uno::UNO_QUERY_THROW);
+
+ css::document::DocumentEvent aEvent2;
+ aEvent2.EventName = "OnCloseApp";
+ xGlobalBroadcaster->documentEventOccured(aEvent2);
+
+ delete pApp;
+ Application::Quit();
+}
+
+OUString SAL_CALL SfxTerminateListener_Impl::getImplementationName()
+{
+ return "com.sun.star.comp.sfx2.SfxTerminateListener";
+}
+
+sal_Bool SAL_CALL SfxTerminateListener_Impl::supportsService( const OUString& sServiceName )
+{
+ return cppu::supportsService(this, sServiceName);
+}
+
+Sequence< OUString > SAL_CALL SfxTerminateListener_Impl::getSupportedServiceNames()
+{
+ // Note: That service does not really exists .-)
+ // But this implementation is not thought to be registered really within our service.rdb.
+ // At least we need the implementation name only to identify these service at the global desktop instance.
+ // The desktop must know, which listener will terminate the SfxApplication in real !
+ // It must call this special listener as last one ... otherwise we shutdown the SfxApplication BEFORE other listener
+ // can react ...
+ return { "com.sun.star.frame.TerminateListener" };
+}
+
+
+typedef bool (*PFunc_getSpecialCharsForEdit)(weld::Widget* i_pParent, const vcl::Font& i_rFont, OUString& o_rOutString);
+
+
+// Lazy binding of the GetSpecialCharsForEdit function as it resides in
+// a library above us.
+
+
+#ifndef DISABLE_DYNLOADING
+
+extern "C" { static void thisModule() {} }
+
+#else
+
+extern "C" bool GetSpecialCharsForEdit(weld::Widget* i_pParent, const vcl::Font& i_rFont, OUString& o_rOutString);
+
+#endif
+
+static OUString SfxGetSpecialCharsForEdit(weld::Widget* pParent, const vcl::Font& rFont)
+{
+ static const PFunc_getSpecialCharsForEdit pfunc_getSpecialCharsForEdit = [] {
+ PFunc_getSpecialCharsForEdit pfunc = nullptr;
+#ifndef DISABLE_DYNLOADING
+ osl::Module aMod;
+ aMod.loadRelative(&thisModule, SVLIBRARY("cui"));
+
+ // get symbol
+ pfunc = reinterpret_cast<PFunc_getSpecialCharsForEdit>(aMod.getFunctionSymbol("GetSpecialCharsForEdit"));
+ DBG_ASSERT( pfunc, "GetSpecialCharsForEdit() not found!" );
+ aMod.release();
+#else
+ pfunc = GetSpecialCharsForEdit;
+#endif
+ return pfunc;
+ }();
+
+ OUString aRet;
+ if ( pfunc_getSpecialCharsForEdit )
+ {
+ SolarMutexGuard aGuard;
+ (*pfunc_getSpecialCharsForEdit)( pParent, rFont, aRet );
+ }
+ return aRet;
+}
+
+
+void SfxApplication::Initialize_Impl()
+{
+#ifdef TLX_VALIDATE
+ StgIo::SetErrorLink( LINK( this, SfxStorageErrHdl, Error ) );
+#endif
+
+ Reference < XDesktop2 > xDesktop = Desktop::create ( ::comphelper::getProcessComponentContext() );
+ xDesktop->addTerminateListener( new SfxTerminateListener_Impl );
+
+ pImpl->mxAppDispatch = new SfxStatusDispatcher;
+
+ // SV-Look
+ Help::EnableContextHelp();
+ Help::EnableExtHelp();
+
+ pImpl->m_pToolsErrorHdl.emplace(
+ RID_ERRHDL, ErrCodeArea::Io, ErrCodeArea::Vcl);
+
+ pImpl->m_pSoErrorHdl.emplace(
+ RID_SO_ERROR_HANDLER, ErrCodeArea::So, ErrCodeArea::So, SvtResLocale());
+#if HAVE_FEATURE_SCRIPTING
+ pImpl->m_pSbxErrorHdl.emplace(
+ RID_BASIC_START, ErrCodeArea::Sbx, ErrCodeArea::Sbx, BasResLocale());
+#endif
+
+ if (!utl::ConfigManager::IsFuzzing())
+ {
+ SolarMutexGuard aGuard;
+ //ensure instantiation of listener that manages the internal recently-used
+ //list
+ pImpl->mxAppPickList.emplace(*this);
+ }
+
+ DBG_ASSERT( !pImpl->pAppDispat, "AppDispatcher already exists" );
+ pImpl->pAppDispat.emplace();
+ pImpl->pSlotPool.emplace();
+
+ Registrations_Impl();
+
+ // initialize the subclass
+ pImpl->bDowning = false;
+
+ // get CHAOS item pool...
+ pImpl->pPool = NoChaos::GetItemPool();
+ SetPool( pImpl->pPool );
+
+ if ( pImpl->bDowning )
+ return;
+
+ // build the app dispatcher
+ pImpl->pAppDispat->Push(*this);
+ pImpl->pAppDispat->Flush();
+ pImpl->pAppDispat->DoActivate_Impl( true );
+
+ {
+ SolarMutexGuard aGuard;
+ // Set special characters callback on vcl edit control
+ vcl::SetGetSpecialCharsFunction(&SfxGetSpecialCharsForEdit);
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/appl/appmain.cxx b/sfx2/source/appl/appmain.cxx
new file mode 100644
index 000000000..5f24bbc8a
--- /dev/null
+++ b/sfx2/source/appl/appmain.cxx
@@ -0,0 +1,38 @@
+/* -*- 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/urihelper.hxx>
+
+#include <appdata.hxx>
+#include <sfx2/app.hxx>
+#include <sfx2/fcontnr.hxx>
+
+
+SfxFilterMatcher& SfxApplication::GetFilterMatcher()
+{
+ if( !pImpl->pMatcher )
+ {
+ pImpl->pMatcher.emplace();
+ URIHelper::SetMaybeFileHdl( LINK(
+ &*pImpl->pMatcher, SfxFilterMatcher, MaybeFileHdl_Impl ) );
+ }
+ return *pImpl->pMatcher;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/appl/appmisc.cxx b/sfx2/source/appl/appmisc.cxx
new file mode 100644
index 000000000..dc1f26a96
--- /dev/null
+++ b/sfx2/source/appl/appmisc.cxx
@@ -0,0 +1,210 @@
+/* -*- 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 <config_folders.h>
+#include <ucbhelper/content.hxx>
+
+#include <vcl/canvastools.hxx>
+#include <vcl/vectorgraphicdata.hxx>
+#include <com/sun/star/rendering/XIntegerReadOnlyBitmap.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/graphic/Primitive2DTools.hpp>
+#include <com/sun/star/uno/Reference.h>
+#include <unotools/configmgr.hxx>
+#include <comphelper/processfactory.hxx>
+#include <osl/diagnose.h>
+#include <rtl/bootstrap.hxx>
+#include <svl/stritem.hxx>
+#include <tools/urlobj.hxx>
+
+#include <sfx2/app.hxx>
+#include <appdata.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/module.hxx>
+#include <sfx2/msgpool.hxx>
+#include <sfx2/sfxsids.hrc>
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/objface.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <drawinglayer/primitive2d/transformprimitive2d.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::util;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::container;
+
+#define ShellClass_SfxApplication
+#include <sfxslots.hxx>
+
+SFX_IMPL_INTERFACE(SfxApplication,SfxShell)
+
+void SfxApplication::InitInterface_Impl()
+{
+ GetStaticInterface()->RegisterStatusBar(StatusBarId::GenericStatusBar);
+
+ GetStaticInterface()->RegisterChildWindow(SID_DOCKWIN_0);
+ GetStaticInterface()->RegisterChildWindow(SID_DOCKWIN_1);
+ GetStaticInterface()->RegisterChildWindow(SID_DOCKWIN_2);
+ GetStaticInterface()->RegisterChildWindow(SID_DOCKWIN_3);
+ GetStaticInterface()->RegisterChildWindow(SID_DOCKWIN_4);
+ GetStaticInterface()->RegisterChildWindow(SID_DOCKWIN_5);
+ GetStaticInterface()->RegisterChildWindow(SID_DOCKWIN_6);
+ GetStaticInterface()->RegisterChildWindow(SID_DOCKWIN_7);
+ GetStaticInterface()->RegisterChildWindow(SID_DOCKWIN_8);
+ GetStaticInterface()->RegisterChildWindow(SID_DOCKWIN_9);
+}
+
+/** Returns the running SfxProgress for the entire application or 0 if
+ none is running for the entire application.
+
+ [Cross-reference]
+
+ <SfxProgress::GetActiveProgress(SfxViewFrame*)>
+ <SfxViewFrame::GetProgress()const>
+*/
+SfxProgress* SfxApplication::GetProgress() const
+{
+ return pImpl->pProgress;
+}
+
+SfxModule* SfxApplication::GetModule_Impl()
+{
+ SfxModule* pModule = SfxModule::GetActiveModule();
+ if ( !pModule )
+ pModule = SfxModule::GetActiveModule( SfxViewFrame::GetFirst( nullptr, false ) );
+ if( pModule )
+ return pModule;
+ else
+ {
+ OSL_FAIL( "No module!" );
+ return nullptr;
+ }
+}
+
+bool SfxApplication::IsDowning() const { return pImpl->bDowning; }
+SfxDispatcher* SfxApplication::GetAppDispatcher_Impl() { return &*pImpl->pAppDispat; }
+SfxSlotPool& SfxApplication::GetAppSlotPool_Impl() const { return *pImpl->pSlotPool; }
+
+static bool FileExists( const INetURLObject& rURL )
+{
+ bool bRet = false;
+
+ if( rURL.GetProtocol() != INetProtocol::NotValid )
+ {
+ try
+ {
+ ::ucbhelper::Content aCnt( rURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ), uno::Reference< ucb::XCommandEnvironment >(), comphelper::getProcessComponentContext() );
+ OUString aTitle;
+
+ aCnt.getPropertyValue("Title") >>= aTitle;
+ bRet = ( !aTitle.isEmpty() );
+ }
+ catch(const Exception&)
+ {
+ return false;
+ }
+ }
+
+ return bRet;
+}
+
+bool SfxApplication::loadBrandSvg(const char *pName, BitmapEx &rBitmap, int nWidth)
+{
+ // Load from disk
+
+ OUString aBaseName = "/" + OUString::createFromAscii( pName );
+
+ OUString uri = "$BRAND_BASE_DIR/" LIBO_ETC_FOLDER + aBaseName + ".svg";
+ rtl::Bootstrap::expandMacros( uri );
+
+ INetURLObject aObj( uri );
+ if ( !FileExists(aObj) )
+ return false;
+
+ VectorGraphicData aVectorGraphicData(aObj.PathToFileName(), VectorGraphicDataType::Svg);
+
+ // transform into [0,0,width,width*aspect] std dimensions
+
+ basegfx::B2DRange aRange(aVectorGraphicData.getRange());
+ const double fAspectRatio(
+ aRange.getHeight() == 0.0 ? 1.0 : aRange.getWidth()/aRange.getHeight());
+ basegfx::B2DHomMatrix aTransform(
+ basegfx::utils::createTranslateB2DHomMatrix(
+ -aRange.getMinX(),
+ -aRange.getMinY()));
+ aTransform.scale(
+ aRange.getWidth() == 0.0 ? 1.0 : nWidth / aRange.getWidth(),
+ (aRange.getHeight() == 0.0
+ ? 1.0 : nWidth / fAspectRatio / aRange.getHeight()));
+ const drawinglayer::primitive2d::Primitive2DReference xTransformRef(
+ new drawinglayer::primitive2d::TransformPrimitive2D(
+ aTransform,
+ drawinglayer::primitive2d::Primitive2DContainer(aVectorGraphicData.getPrimitive2DSequence())));
+
+ // UNO dance to render from drawinglayer
+
+ uno::Reference< uno::XComponentContext > xContext(::comphelper::getProcessComponentContext());
+
+ try
+ {
+ const uno::Reference< graphic::XPrimitive2DRenderer > xPrimitive2DRenderer =
+ graphic::Primitive2DTools::create( xContext );
+
+ // cancel out rasterize's mm2pixel conversion
+ // see fFactor100th_mmToInch in
+ // drawinglayer/source/drawinglayeruno/xprimitive2drenderer.cxx
+ const double fFakeDPI=2.54 * 1000.0;
+
+ geometry::RealRectangle2D aRealRect(
+ 0, 0,
+ nWidth, nWidth / fAspectRatio);
+
+ const uno::Reference< rendering::XBitmap > xBitmap(
+ xPrimitive2DRenderer->rasterize(
+ drawinglayer::primitive2d::Primitive2DContainer{xTransformRef}.toSequence(),
+ uno::Sequence< beans::PropertyValue >(),
+ fFakeDPI,
+ fFakeDPI,
+ aRealRect,
+ 500000));
+
+ if(xBitmap.is())
+ {
+ const uno::Reference< rendering::XIntegerReadOnlyBitmap> xIntBmp(xBitmap, uno::UNO_QUERY_THROW);
+ rBitmap = vcl::unotools::bitmapExFromXBitmap(xIntBmp);
+ return true;
+ }
+ }
+ catch(const uno::Exception&)
+ {
+ OSL_ENSURE(false, "Got no graphic::XPrimitive2DRenderer (!)" );
+ }
+ return false;
+}
+
+/** loads the application logo as used in the impress slideshow pause screen */
+BitmapEx SfxApplication::GetApplicationLogo(tools::Long nWidth)
+{
+ BitmapEx aBitmap;
+ SfxApplication::loadBrandSvg("shell/about", aBitmap, nWidth);
+ return aBitmap;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/appl/appopen.cxx b/sfx2/source/appl/appopen.cxx
new file mode 100644
index 000000000..1c854b9d9
--- /dev/null
+++ b/sfx2/source/appl/appopen.cxx
@@ -0,0 +1,1146 @@
+/* -*- 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/uno/Reference.h>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/beans/NamedValue.hpp>
+#include <com/sun/star/frame/FrameSearchFlag.hpp>
+#include <com/sun/star/frame/XDispatchProvider.hpp>
+#include <com/sun/star/frame/XFrame.hpp>
+#include <com/sun/star/frame/Desktop.hpp>
+#include <com/sun/star/util/URL.hpp>
+#include <com/sun/star/util/URLTransformer.hpp>
+#include <com/sun/star/util/XURLTransformer.hpp>
+#include <com/sun/star/system/SystemShellExecuteException.hpp>
+#include <com/sun/star/document/XTypeDetection.hpp>
+#include <com/sun/star/document/MacroExecMode.hpp>
+#include <com/sun/star/document/UpdateDocMode.hpp>
+#include <com/sun/star/task/ErrorCodeRequest.hpp>
+#include <com/sun/star/task/InteractionHandler.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/embed/ElementModes.hpp>
+#include <com/sun/star/embed/XStorage.hpp>
+#include <com/sun/star/container/XNameAccess.hpp>
+#include <com/sun/star/packages/WrongPasswordException.hpp>
+#include <com/sun/star/uno/Sequence.h>
+#include <com/sun/star/ui/dialogs/TemplateDescription.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <rtl/ustring.hxx>
+
+#include <comphelper/processfactory.hxx>
+#include <comphelper/sequence.hxx>
+#include <comphelper/storagehelper.hxx>
+#include <comphelper/string.hxx>
+#include <comphelper/synchronousdispatch.hxx>
+
+#include <svl/intitem.hxx>
+#include <svl/stritem.hxx>
+#include <svl/eitem.hxx>
+#include <sfx2/doctempl.hxx>
+#include <svtools/sfxecode.hxx>
+#include <preventduplicateinteraction.hxx>
+#include <svtools/ehdl.hxx>
+#include <unotools/pathoptions.hxx>
+#include <unotools/securityoptions.hxx>
+#include <unotools/moduleoptions.hxx>
+#include <unotools/extendedsecurityoptions.hxx>
+#include <comphelper/docpasswordhelper.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+
+#include <sfx2/app.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/docfile.hxx>
+#include <sfx2/docfilt.hxx>
+#include <sfx2/fcontnr.hxx>
+#include <sfx2/objitem.hxx>
+#include <sfx2/objsh.hxx>
+#include <svl/slstitm.hxx>
+#include <appopen.hxx>
+#include <sfx2/request.hxx>
+#include <sfx2/sfxresid.hxx>
+#include <sfx2/viewsh.hxx>
+#include <sfx2/strings.hrc>
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/sfxuno.hxx>
+#include <sfx2/objface.hxx>
+#include <sfx2/filedlghelper.hxx>
+#include <sfx2/templatedlg.hxx>
+#include <sfx2/sfxsids.hrc>
+#include <o3tl/string_view.hxx>
+#include <openuriexternally.hxx>
+
+#include <officecfg/Office/ProtocolHandler.hxx>
+#include <officecfg/Office/Security.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::frame;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::util;
+using namespace ::com::sun::star::task;
+using namespace ::com::sun::star::container;
+using namespace ::cppu;
+using namespace ::sfx2;
+
+void SetTemplate_Impl( const OUString &rFileName,
+ const OUString &rLongName,
+ SfxObjectShell *pDoc)
+{
+ // write TemplateName to DocumentProperties of document
+ // TemplateDate stays as default (=current date)
+ pDoc->ResetFromTemplate( rLongName, rFileName );
+}
+
+namespace {
+
+class SfxDocPasswordVerifier : public ::comphelper::IDocPasswordVerifier
+{
+public:
+ explicit SfxDocPasswordVerifier( const Reference< embed::XStorage >& rxStorage ) :
+ mxStorage( rxStorage ) {}
+
+ virtual ::comphelper::DocPasswordVerifierResult
+ verifyPassword( const OUString& rPassword, uno::Sequence< beans::NamedValue >& o_rEncryptionData ) override;
+ virtual ::comphelper::DocPasswordVerifierResult
+ verifyEncryptionData( const uno::Sequence< beans::NamedValue >& rEncryptionData ) override;
+
+
+private:
+ Reference< embed::XStorage > mxStorage;
+};
+
+}
+
+::comphelper::DocPasswordVerifierResult SfxDocPasswordVerifier::verifyPassword( const OUString& rPassword, uno::Sequence< beans::NamedValue >& o_rEncryptionData )
+{
+ o_rEncryptionData = ::comphelper::OStorageHelper::CreatePackageEncryptionData( rPassword );
+ return verifyEncryptionData( o_rEncryptionData );
+}
+
+
+::comphelper::DocPasswordVerifierResult SfxDocPasswordVerifier::verifyEncryptionData( const uno::Sequence< beans::NamedValue >& rEncryptionData )
+{
+ ::comphelper::DocPasswordVerifierResult eResult = ::comphelper::DocPasswordVerifierResult::WrongPassword;
+ try
+ {
+ // check the encryption data
+ // if the data correct is the stream will be opened successfully
+ // and immediately closed
+ ::comphelper::OStorageHelper::SetCommonStorageEncryptionData( mxStorage, rEncryptionData );
+
+ mxStorage->openStreamElement(
+ "content.xml",
+ embed::ElementModes::READ | embed::ElementModes::NOCREATE );
+
+ // no exception -> success
+ eResult = ::comphelper::DocPasswordVerifierResult::OK;
+ }
+ catch( const packages::WrongPasswordException& )
+ {
+ eResult = ::comphelper::DocPasswordVerifierResult::WrongPassword;
+ }
+ catch( const uno::Exception& )
+ {
+ // unknown error, report it as wrong password
+ // TODO/LATER: we need an additional way to report unknown problems in this case
+ eResult = ::comphelper::DocPasswordVerifierResult::WrongPassword;
+ }
+ return eResult;
+}
+
+
+ErrCode CheckPasswd_Impl
+(
+ SfxObjectShell* pDoc,
+ SfxMedium* pFile // the Medium and its Password should be obtained
+)
+
+/* [Description]
+
+ Ask for the password for a medium, only works if it concerns storage.
+ If the password flag is set in the Document Info, then the password is
+ requested through a user dialogue and the set at the Set of the medium.
+ If the set does not exist the it is created.
+*/
+{
+ ErrCode nRet = ERRCODE_NONE;
+
+ if( !pFile->GetFilter() || pFile->IsStorage() )
+ {
+ uno::Reference< embed::XStorage > xStorage = pFile->GetStorage();
+ if( xStorage.is() )
+ {
+ uno::Reference< beans::XPropertySet > xStorageProps( xStorage, uno::UNO_QUERY );
+ if ( xStorageProps.is() )
+ {
+ bool bIsEncrypted = false;
+ uno::Sequence< uno::Sequence< beans::NamedValue > > aGpgProperties;
+ try {
+ xStorageProps->getPropertyValue("HasEncryptedEntries")
+ >>= bIsEncrypted;
+ xStorageProps->getPropertyValue("EncryptionGpGProperties")
+ >>= aGpgProperties;
+ } catch( uno::Exception& )
+ {
+ // TODO/LATER:
+ // the storage either has no encrypted elements or it's just
+ // does not allow to detect it, probably it should be implemented later
+ }
+
+ if ( bIsEncrypted )
+ {
+ css::uno::Reference<css::awt::XWindow> xWin(pDoc ? pDoc->GetDialogParent(pFile) : nullptr);
+ if (xWin)
+ xWin->setVisible(true);
+
+ nRet = ERRCODE_SFX_CANTGETPASSWD;
+
+ SfxItemSet *pSet = pFile->GetItemSet();
+ if( pSet )
+ {
+ Reference< css::task::XInteractionHandler > xInteractionHandler = pFile->GetInteractionHandler();
+ if( xInteractionHandler.is() )
+ {
+ // use the comphelper password helper to request a password
+ OUString aPassword;
+ const SfxStringItem* pPasswordItem = SfxItemSet::GetItem<SfxStringItem>(pSet, SID_PASSWORD, false);
+ if ( pPasswordItem )
+ aPassword = pPasswordItem->GetValue();
+
+ uno::Sequence< beans::NamedValue > aEncryptionData;
+ const SfxUnoAnyItem* pEncryptionDataItem = SfxItemSet::GetItem<SfxUnoAnyItem>(pSet, SID_ENCRYPTIONDATA, false);
+ if ( pEncryptionDataItem )
+ pEncryptionDataItem->GetValue() >>= aEncryptionData;
+
+ // try if one of the public key entries is
+ // decryptable, then extract session key
+ // from it
+ if ( !aEncryptionData.hasElements() && aGpgProperties.hasElements() )
+ aEncryptionData = ::comphelper::DocPasswordHelper::decryptGpgSession(aGpgProperties);
+
+ // tdf#93389: if recovering a document, encryption data should contain
+ // entries for the real filter, not only for recovery ODF, to keep it
+ // encrypted. Pass this in encryption data.
+ // TODO: pass here the real filter (from AutoRecovery::implts_openDocs)
+ // to marshal this to requestAndVerifyDocPassword
+ if (pSet->GetItemState(SID_DOC_SALVAGE, false) == SfxItemState::SET)
+ {
+ aEncryptionData = comphelper::concatSequences(
+ aEncryptionData, std::initializer_list<beans::NamedValue>{
+ { "ForSalvage", css::uno::Any(true) } });
+ }
+
+ SfxDocPasswordVerifier aVerifier( xStorage );
+ aEncryptionData = ::comphelper::DocPasswordHelper::requestAndVerifyDocPassword(
+ aVerifier, aEncryptionData, aPassword, xInteractionHandler, pFile->GetOrigURL(), comphelper::DocPasswordRequestType::Standard );
+
+ pSet->ClearItem( SID_PASSWORD );
+ pSet->ClearItem( SID_ENCRYPTIONDATA );
+
+ if ( aEncryptionData.hasElements() )
+ {
+ pSet->Put( SfxUnoAnyItem( SID_ENCRYPTIONDATA, uno::Any( aEncryptionData ) ) );
+
+ try
+ {
+ // update the version list of the medium using the new password
+ pFile->GetVersionList();
+ }
+ catch( uno::Exception& )
+ {
+ // TODO/LATER: set the error code
+ }
+
+ nRet = ERRCODE_NONE;
+ }
+ else
+ nRet = ERRCODE_IO_ABORT;
+ }
+ }
+ }
+ }
+ else
+ {
+ OSL_FAIL( "A storage must implement XPropertySet interface!" );
+ nRet = ERRCODE_SFX_CANTGETPASSWD;
+ }
+ }
+ }
+
+ return nRet;
+}
+
+
+ErrCode SfxApplication::LoadTemplate( SfxObjectShellLock& xDoc, const OUString &rFileName, std::unique_ptr<SfxItemSet> pSet )
+{
+ std::shared_ptr<const SfxFilter> pFilter;
+ SfxMedium aMedium( rFileName, ( StreamMode::READ | StreamMode::SHARE_DENYNONE ) );
+
+ if ( !aMedium.GetStorage( false ).is() )
+ aMedium.GetInStream();
+
+ if ( aMedium.GetError() )
+ {
+ return aMedium.GetErrorCode();
+ }
+
+ aMedium.UseInteractionHandler( true );
+ ErrCode nErr = GetFilterMatcher().GuessFilter( aMedium, pFilter, SfxFilterFlags::TEMPLATE, SfxFilterFlags::NONE );
+ if ( ERRCODE_NONE != nErr)
+ {
+ return ERRCODE_SFX_NOTATEMPLATE;
+ }
+
+ if( !pFilter || !pFilter->IsAllowedAsTemplate() )
+ {
+ return ERRCODE_SFX_NOTATEMPLATE;
+ }
+
+ if ( pFilter->GetFilterFlags() & SfxFilterFlags::STARONEFILTER )
+ {
+ DBG_ASSERT( !xDoc.Is(), "Sorry, not implemented!" );
+ SfxStringItem aName( SID_FILE_NAME, rFileName );
+ SfxStringItem aReferer( SID_REFERER, "private:user" );
+ SfxStringItem aFlags( SID_OPTIONS, "T" );
+ SfxBoolItem aHidden( SID_HIDDEN, true );
+ const SfxPoolItem *pRet = GetDispatcher_Impl()->ExecuteList(
+ SID_OPENDOC, SfxCallMode::SYNCHRON,
+ { &aName, &aHidden, &aReferer, &aFlags } );
+ const SfxObjectItem *pObj = dynamic_cast<const SfxObjectItem*>( pRet );
+ if ( pObj )
+ xDoc = dynamic_cast<SfxObjectShell*>( pObj->GetShell() );
+ else
+ {
+ const SfxViewFrameItem *pView = dynamic_cast<const SfxViewFrameItem*>( pRet );
+ if ( pView )
+ {
+ SfxViewFrame *pFrame = pView->GetFrame();
+ if ( pFrame )
+ xDoc = pFrame->GetObjectShell();
+ }
+ }
+
+ if ( !xDoc.Is() )
+ return ERRCODE_SFX_DOLOADFAILED;
+ }
+ else
+ {
+ if ( !xDoc.Is() )
+ xDoc = SfxObjectShell::CreateObject( pFilter->GetServiceName() );
+
+ //pMedium takes ownership of pSet
+ SfxMedium *pMedium = new SfxMedium( rFileName, StreamMode::STD_READ, pFilter, std::move(pSet) );
+ if(!xDoc->DoLoad(pMedium))
+ {
+ ErrCode nErrCode = xDoc->GetErrorCode();
+ xDoc->DoClose();
+ xDoc.Clear();
+ return nErrCode;
+ }
+ }
+
+ try
+ {
+ // TODO: introduce error handling
+
+ uno::Reference< embed::XStorage > xTempStorage = ::comphelper::OStorageHelper::GetTemporaryStorage();
+ if( !xTempStorage.is() )
+ throw uno::RuntimeException();
+
+ xDoc->GetStorage()->copyToStorage( xTempStorage );
+
+ if ( !xDoc->DoSaveCompleted( new SfxMedium( xTempStorage, OUString() ) ) )
+ throw uno::RuntimeException();
+ }
+ catch( uno::Exception& )
+ {
+ xDoc->DoClose();
+ xDoc.Clear();
+
+ // TODO: transfer correct error outside
+ return ERRCODE_SFX_GENERAL;
+ }
+
+ SetTemplate_Impl( rFileName, OUString(), xDoc );
+
+ xDoc->SetNoName();
+ xDoc->InvalidateName();
+ xDoc->SetModified(false);
+ xDoc->ResetError();
+
+ css::uno::Reference< css::frame::XModel > xModel = xDoc->GetModel();
+ if ( xModel.is() )
+ {
+ std::unique_ptr<SfxItemSet> pNew = xDoc->GetMedium()->GetItemSet()->Clone();
+ pNew->ClearItem( SID_PROGRESS_STATUSBAR_CONTROL );
+ pNew->ClearItem( SID_FILTER_NAME );
+ css::uno::Sequence< css::beans::PropertyValue > aArgs;
+ TransformItems( SID_OPENDOC, *pNew, aArgs );
+ sal_Int32 nLength = aArgs.getLength();
+ aArgs.realloc( nLength + 1 );
+ auto pArgs = aArgs.getArray();
+ pArgs[nLength].Name = "Title";
+ pArgs[nLength].Value <<= xDoc->GetTitle( SFX_TITLE_DETECT );
+ xModel->attachResource( OUString(), aArgs );
+ }
+
+ return xDoc->GetErrorCode();
+}
+
+
+void SfxApplication::NewDocDirectExec_Impl( SfxRequest& rReq )
+{
+ const SfxStringItem* pFactoryItem = rReq.GetArg<SfxStringItem>(SID_NEWDOCDIRECT);
+ OUString aFactName;
+ if ( pFactoryItem )
+ aFactName = pFactoryItem->GetValue();
+ else
+ aFactName = SvtModuleOptions().GetDefaultModuleName();
+
+ SfxRequest aReq( SID_OPENDOC, SfxCallMode::SYNCHRON, GetPool() );
+ aReq.AppendItem( SfxStringItem( SID_FILE_NAME, "private:factory/" + aFactName ) );
+ aReq.AppendItem( SfxFrameItem( SID_DOCFRAME, GetFrame() ) );
+ aReq.AppendItem( SfxStringItem( SID_TARGETNAME, "_default" ) );
+
+ // TODO/LATER: Should the other arguments be transferred as well?
+ const SfxStringItem* pDefaultPathItem = rReq.GetArg<SfxStringItem>(SID_DEFAULTFILEPATH);
+ if ( pDefaultPathItem )
+ aReq.AppendItem( *pDefaultPathItem );
+ const SfxStringItem* pDefaultNameItem = rReq.GetArg<SfxStringItem>(SID_DEFAULTFILENAME);
+ if ( pDefaultNameItem )
+ aReq.AppendItem( *pDefaultNameItem );
+
+ SfxGetpApp()->ExecuteSlot( aReq );
+ const SfxViewFrameItem* pItem = dynamic_cast<const SfxViewFrameItem*>( aReq.GetReturnValue() );
+ if ( pItem )
+ rReq.SetReturnValue( SfxFrameItem( 0, pItem->GetFrame() ) );
+}
+
+void SfxApplication::NewDocDirectState_Impl( SfxItemSet &rSet )
+{
+ rSet.Put(SfxStringItem(SID_NEWDOCDIRECT, "private:factory/" + SvtModuleOptions().GetDefaultModuleName()));
+}
+
+void SfxApplication::NewDocExec_Impl( SfxRequest& rReq )
+{
+ // No Parameter from BASIC only Factory given?
+ const SfxStringItem* pTemplNameItem = rReq.GetArg<SfxStringItem>(SID_TEMPLATE_NAME);
+ const SfxStringItem* pTemplFileNameItem = rReq.GetArg<SfxStringItem>(SID_FILE_NAME);
+ const SfxStringItem* pTemplRegionNameItem = rReq.GetArg<SfxStringItem>(SID_TEMPLATE_REGIONNAME);
+
+ SfxObjectShellLock xDoc;
+
+ OUString aTemplateRegion, aTemplateName, aTemplateFileName;
+ bool bDirect = false; // through FileName instead of Region/Template
+ SfxErrorContext aEc(ERRCTX_SFX_NEWDOC);
+ if ( !pTemplNameItem && !pTemplFileNameItem )
+ {
+ bool bNewWin = false;
+ weld::Window* pTopWin = GetTopWindow();
+
+ SfxObjectShell* pCurrentShell = SfxObjectShell::Current();
+ Reference<XModel> xModel;
+ if(pCurrentShell)
+ xModel = pCurrentShell->GetModel();
+
+ SfxTemplateManagerDlg aTemplDlg(rReq.GetFrameWeld());
+
+ if (xModel.is())
+ aTemplDlg.setDocumentModel(xModel);
+
+ int nRet = aTemplDlg.run();
+ if ( nRet == RET_OK )
+ {
+ rReq.Done();
+ if ( pTopWin != GetTopWindow() )
+ {
+ // the dialogue opens a document -> a new TopWindow appears
+ pTopWin = GetTopWindow();
+ bNewWin = true;
+ }
+ }
+
+ if (bNewWin && pTopWin)
+ {
+ // after the destruction of the dialogue its parent comes to top,
+ // but we want that the new document is on top
+ pTopWin->present();
+ }
+
+ return;
+ }
+ else
+ {
+ // Template-Name
+ if ( pTemplNameItem )
+ aTemplateName = pTemplNameItem->GetValue();
+
+ // Template-Region
+ if ( pTemplRegionNameItem )
+ aTemplateRegion = pTemplRegionNameItem->GetValue();
+
+ // Template-File-Name
+ if ( pTemplFileNameItem )
+ {
+ aTemplateFileName = pTemplFileNameItem->GetValue();
+ bDirect = true;
+ }
+ }
+
+ ErrCode lErr = ERRCODE_NONE;
+ if ( !bDirect )
+ {
+ SfxDocumentTemplates aTmpFac;
+ if( aTemplateFileName.isEmpty() )
+ aTmpFac.GetFull( aTemplateRegion, aTemplateName, aTemplateFileName );
+
+ if( aTemplateFileName.isEmpty() )
+ lErr = ERRCODE_SFX_TEMPLATENOTFOUND;
+ }
+
+ INetURLObject aObj( aTemplateFileName );
+ SfxErrorContext aEC( ERRCTX_SFX_LOADTEMPLATE, aObj.PathToFileName() );
+
+ if ( lErr != ERRCODE_NONE )
+ {
+ ErrCode lFatalErr = lErr.IgnoreWarning();
+ if ( lFatalErr )
+ ErrorHandler::HandleError(lErr);
+ }
+ else
+ {
+ SfxCallMode eMode = SfxCallMode::SYNCHRON;
+
+ const SfxPoolItem *pRet=nullptr;
+ SfxStringItem aReferer( SID_REFERER, "private:user" );
+ SfxStringItem aTarget( SID_TARGETNAME, "_default" );
+ if ( !aTemplateFileName.isEmpty() )
+ {
+ DBG_ASSERT( aObj.GetProtocol() != INetProtocol::NotValid, "Illegal URL!" );
+
+ SfxStringItem aName( SID_FILE_NAME, aObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
+ SfxStringItem aTemplName( SID_TEMPLATE_NAME, aTemplateName );
+ SfxStringItem aTemplRegionName( SID_TEMPLATE_REGIONNAME, aTemplateRegion );
+ pRet = GetDispatcher_Impl()->ExecuteList(SID_OPENDOC, eMode,
+ {&aName, &aTarget, &aReferer, &aTemplName, &aTemplRegionName});
+ }
+ else
+ {
+ SfxStringItem aName( SID_FILE_NAME, "private:factory" );
+ pRet = GetDispatcher_Impl()->ExecuteList(SID_OPENDOC, eMode,
+ { &aName, &aTarget, &aReferer } );
+ }
+
+ if ( pRet )
+ rReq.SetReturnValue( *pRet );
+ }
+}
+
+
+namespace {
+
+/**
+ * Check if a given filter type should open the hyperlinked document
+ * natively.
+ *
+ * @param rFilter filter object
+ */
+bool lcl_isFilterNativelySupported(const SfxFilter& rFilter)
+{
+ if (rFilter.IsOwnFormat())
+ return true;
+
+ const OUString& aName = rFilter.GetFilterName();
+ // We can handle all Excel variants natively.
+ return aName.startsWith("MS Excel");
+}
+
+}
+
+void SfxApplication::OpenDocExec_Impl( SfxRequest& rReq )
+{
+ OUString aDocService;
+ const SfxStringItem* pDocSrvItem = rReq.GetArg<SfxStringItem>(SID_DOC_SERVICE);
+ if (pDocSrvItem)
+ aDocService = pDocSrvItem->GetValue();
+
+ sal_uInt16 nSID = rReq.GetSlot();
+ const SfxStringItem* pFileNameItem = rReq.GetArg<SfxStringItem>(SID_FILE_NAME);
+ if ( pFileNameItem )
+ {
+ OUString aCommand( pFileNameItem->GetValue() );
+ const SfxSlot* pSlot = GetInterface()->GetSlot( aCommand );
+ if ( pSlot )
+ {
+ pFileNameItem = nullptr;
+ }
+ else
+ {
+ if ( aCommand.startsWith("slot:") )
+ {
+ sal_uInt16 nSlotId = static_cast<sal_uInt16>(o3tl::toInt32(aCommand.subView(5)));
+ if ( nSlotId == SID_OPENDOC )
+ pFileNameItem = nullptr;
+ }
+ }
+ }
+
+ if ( !pFileNameItem )
+ {
+ // get FileName from dialog
+ std::vector<OUString> aURLList;
+ OUString aFilter;
+ std::optional<SfxAllItemSet> pSet;
+ OUString aPath;
+ const SfxStringItem* pFolderNameItem = rReq.GetArg<SfxStringItem>(SID_PATH);
+ if ( pFolderNameItem )
+ aPath = pFolderNameItem->GetValue();
+ else if ( nSID == SID_OPENTEMPLATE )
+ {
+ aPath = SvtPathOptions().GetTemplatePath();
+ if (!aPath.isEmpty()) // if not empty then get last token
+ aPath = aPath.copy(aPath.lastIndexOf(';')+1); // lastIndexOf+copy works whether separator (';') is there or not
+ }
+
+ sal_Int16 nDialog = SFX2_IMPL_DIALOG_CONFIG;
+ const SfxBoolItem* pSystemDialogItem = rReq.GetArg<SfxBoolItem>(SID_FILE_DIALOG);
+ if ( pSystemDialogItem )
+ nDialog = pSystemDialogItem->GetValue() ? SFX2_IMPL_DIALOG_SYSTEM : SFX2_IMPL_DIALOG_OOO;
+
+ const SfxBoolItem* pRemoteDialogItem = rReq.GetArg<SfxBoolItem>(SID_REMOTE_DIALOG);
+ if ( pRemoteDialogItem && pRemoteDialogItem->GetValue())
+ nDialog = SFX2_IMPL_DIALOG_REMOTE;
+
+ sal_Int16 nDialogType = ui::dialogs::TemplateDescription::FILEOPEN_READONLY_VERSION;
+ FileDialogFlags eDialogFlags = FileDialogFlags::MultiSelection;
+ const SfxBoolItem* pSignPDFItem = rReq.GetArg<SfxBoolItem>(SID_SIGNPDF);
+ if (pSignPDFItem && pSignPDFItem->GetValue())
+ {
+ eDialogFlags |= FileDialogFlags::SignPDF;
+ nDialogType = ui::dialogs::TemplateDescription::FILEOPEN_SIMPLE;
+ }
+
+ OUString sStandardDir;
+
+ const SfxStringItem* pStandardDirItem = rReq.GetArg<SfxStringItem>(SID_STANDARD_DIR);
+ if ( pStandardDirItem )
+ sStandardDir = pStandardDirItem->GetValue();
+
+ css::uno::Sequence< OUString > aDenyList;
+
+ const SfxStringListItem* pDenyListItem = rReq.GetArg<SfxStringListItem>(SID_DENY_LIST);
+ if ( pDenyListItem )
+ pDenyListItem->GetStringList( aDenyList );
+
+ weld::Window* pTopWindow = GetTopWindow();
+ ErrCode nErr = sfx2::FileOpenDialog_Impl(pTopWindow,
+ nDialogType,
+ eDialogFlags, aURLList,
+ aFilter, pSet, &aPath, nDialog, sStandardDir, aDenyList);
+
+ if ( nErr == ERRCODE_ABORT )
+ {
+ aURLList.clear();
+ return;
+ }
+
+ rReq.SetArgs( *pSet );
+ if ( !aFilter.isEmpty() )
+ rReq.AppendItem( SfxStringItem( SID_FILTER_NAME, aFilter ) );
+ rReq.AppendItem( SfxStringItem( SID_TARGETNAME, "_default" ) );
+ rReq.AppendItem( SfxStringItem( SID_REFERER, "private:user" ) );
+ pSet.reset();
+
+ if(!aURLList.empty())
+ {
+ if ( nSID == SID_OPENTEMPLATE )
+ rReq.AppendItem( SfxBoolItem( SID_TEMPLATE, false ) );
+
+ // This helper wraps an existing (or may new created InteractionHandler)
+ // intercept all incoming interactions and provide useful information
+ // later if the following transaction was finished.
+
+ rtl::Reference<sfx2::PreventDuplicateInteraction> pHandler = new sfx2::PreventDuplicateInteraction(comphelper::getProcessComponentContext());
+ uno::Reference<task::XInteractionHandler> xHandler(pHandler);
+ uno::Reference<task::XInteractionHandler> xWrappedHandler;
+
+ // wrap existing handler or create new UUI handler
+ const SfxUnoAnyItem* pInteractionItem = rReq.GetArg<SfxUnoAnyItem>(SID_INTERACTIONHANDLER);
+ if (pInteractionItem)
+ {
+ pInteractionItem->GetValue() >>= xWrappedHandler;
+ rReq.RemoveItem( SID_INTERACTIONHANDLER );
+ }
+ if (xWrappedHandler.is())
+ pHandler->setHandler(xWrappedHandler);
+ else
+ pHandler->useDefaultUUIHandler();
+ rReq.AppendItem( SfxUnoAnyItem(SID_INTERACTIONHANDLER,css::uno::Any(xHandler)) );
+
+ // define rules for this handler
+ css::uno::Type aInteraction = ::cppu::UnoType<css::task::ErrorCodeRequest>::get();
+ ::sfx2::PreventDuplicateInteraction::InteractionInfo aRule(aInteraction);
+ pHandler->addInteractionRule(aRule);
+
+ if (!aDocService.isEmpty())
+ {
+ rReq.RemoveItem(SID_DOC_SERVICE);
+ rReq.AppendItem(SfxStringItem(SID_DOC_SERVICE, aDocService));
+ }
+
+ for (auto const& url : aURLList)
+ {
+ rReq.RemoveItem( SID_FILE_NAME );
+ rReq.AppendItem( SfxStringItem( SID_FILE_NAME, url ) );
+
+ // Run synchronous, so that not the next document is loaded
+ // when rescheduling
+ // TODO/LATER: use URLList argument and always remove one document after another, each step in asynchronous execution, until finished
+ // but only if reschedule is a problem
+ GetDispatcher_Impl()->Execute( SID_OPENDOC, SfxCallMode::SYNCHRON, *rReq.GetArgs() );
+
+ // check for special interaction "NO MORE DOCUMENTS ALLOWED" and
+ // break loop then. Otherwise we risk showing the same interaction more than once.
+ if ( pHandler->getInteractionInfo(aInteraction, &aRule) )
+ {
+ if (aRule.m_nCallCount > 0)
+ {
+ if (aRule.m_xRequest.is())
+ {
+ css::task::ErrorCodeRequest aRequest;
+ if (aRule.m_xRequest->getRequest() >>= aRequest)
+ {
+ if (aRequest.ErrCode == sal_Int32(sal_uInt32(ERRCODE_SFX_NOMOREDOCUMENTSALLOWED)))
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ aURLList.clear();
+ return;
+ }
+ aURLList.clear();
+ }
+
+ bool bHyperlinkUsed = false;
+
+ if ( SID_OPENURL == nSID )
+ {
+ // SID_OPENURL does the same as SID_OPENDOC!
+ rReq.SetSlot( SID_OPENDOC );
+ }
+ else if ( nSID == SID_OPENTEMPLATE )
+ {
+ rReq.AppendItem( SfxBoolItem( SID_TEMPLATE, false ) );
+ }
+ // pass URL to OS by using ShellExecuter or open it internal
+ // if it seems to be an own format.
+ /* Attention!
+ There exist two possibilities to open hyperlinks:
+ a) using SID_OPENHYPERLINK (new)
+ b) using SID_BROWSE (old)
+ */
+ else if ( nSID == SID_OPENHYPERLINK )
+ {
+ rReq.SetSlot( SID_OPENDOC );
+ bHyperlinkUsed = true;
+ }
+
+ // no else here! It's optional ...
+ if (!bHyperlinkUsed)
+ {
+ const SfxBoolItem* pHyperLinkUsedItem = rReq.GetArg<SfxBoolItem>(SID_BROWSE);
+ if ( pHyperLinkUsedItem )
+ bHyperlinkUsed = pHyperLinkUsedItem->GetValue();
+ // no "official" item, so remove it from ItemSet before using UNO-API
+ rReq.RemoveItem( SID_BROWSE );
+ }
+
+ const SfxStringItem* pFileName = rReq.GetArg<SfxStringItem>(SID_FILE_NAME);
+ OUString aFileName = pFileName->GetValue();
+
+ OUString aReferer;
+ const SfxStringItem* pRefererItem = rReq.GetArg<SfxStringItem>(SID_REFERER);
+ if ( pRefererItem )
+ aReferer = pRefererItem->GetValue();
+
+ const SfxStringItem* pFileFlagsItem = rReq.GetArg<SfxStringItem>(SID_OPTIONS);
+ if ( pFileFlagsItem )
+ {
+ const OUString aFileFlags = pFileFlagsItem->GetValue().toAsciiUpperCase();
+ if ( aFileFlags.indexOf('T') >= 0 )
+ {
+ rReq.RemoveItem( SID_TEMPLATE );
+ rReq.AppendItem( SfxBoolItem( SID_TEMPLATE, true ) );
+ }
+
+ if ( aFileFlags.indexOf('H') >= 0 )
+ {
+ rReq.RemoveItem( SID_HIDDEN );
+ rReq.AppendItem( SfxBoolItem( SID_HIDDEN, true ) );
+ }
+
+ if ( aFileFlags.indexOf('R') >= 0 )
+ {
+ rReq.RemoveItem( SID_DOC_READONLY );
+ rReq.AppendItem( SfxBoolItem( SID_DOC_READONLY, true ) );
+ }
+
+ if ( aFileFlags.indexOf('B') >= 0 )
+ {
+ rReq.RemoveItem( SID_PREVIEW );
+ rReq.AppendItem( SfxBoolItem( SID_PREVIEW, true ) );
+ }
+
+ rReq.RemoveItem( SID_OPTIONS );
+ }
+
+ // Mark without URL cannot be handled by hyperlink code
+ if ( bHyperlinkUsed && !aFileName.isEmpty() && aFileName[0] != '#' )
+ {
+ uno::Reference<document::XTypeDetection> xTypeDetection(
+ comphelper::getProcessServiceFactory()->createInstance("com.sun.star.document.TypeDetection"), UNO_QUERY);
+
+ if ( xTypeDetection.is() )
+ {
+ URL aURL;
+
+ aURL.Complete = aFileName;
+ Reference< util::XURLTransformer > xTrans( util::URLTransformer::create( ::comphelper::getProcessComponentContext() ) );
+ xTrans->parseStrict( aURL );
+
+ INetProtocol aINetProtocol = INetURLObject( aURL.Complete ).GetProtocol();
+ auto eMode = officecfg::Office::Security::Hyperlinks::Open::get();
+
+ if ( eMode == SvtExtendedSecurityOptions::OPEN_NEVER && aINetProtocol != INetProtocol::VndSunStarHelp )
+ {
+ SolarMutexGuard aGuard;
+ weld::Window *pWindow = SfxGetpApp()->GetTopWindow();
+
+ std::unique_ptr<weld::MessageDialog> xSecurityWarningBox(Application::CreateMessageDialog(pWindow,
+ VclMessageType::Warning, VclButtonsType::Ok, SfxResId(STR_SECURITY_WARNING_NO_HYPERLINKS)));
+ xSecurityWarningBox->set_title(SfxResId(RID_SECURITY_WARNING_TITLE));
+ xSecurityWarningBox->run();
+ return;
+ }
+
+ std::shared_ptr<const SfxFilter> pFilter{};
+
+ // attempt loading native documents only if they are from a known protocol
+ // it might be sensible to limit the set of protocols even further, but that
+ // may cause regressions, needs further testing
+ // see tdf#136427 for details
+ if (aINetProtocol != INetProtocol::NotValid) {
+ const OUString aTypeName { xTypeDetection->queryTypeByURL( aURL.Main ) };
+ SfxFilterMatcher& rMatcher = SfxGetpApp()->GetFilterMatcher();
+ pFilter = rMatcher.GetFilter4EA( aTypeName );
+ }
+
+ if (!pFilter || !lcl_isFilterNativelySupported(*pFilter))
+ {
+ // hyperlink does not link to own type => special handling (http, ftp) browser and (other external protocols) OS
+ if ( aINetProtocol == INetProtocol::Mailto )
+ {
+ // don't dispatch mailto hyperlink to desktop dispatcher
+ rReq.RemoveItem( SID_TARGETNAME );
+ rReq.AppendItem( SfxStringItem( SID_TARGETNAME, "_self" ) );
+ }
+ else if ( aINetProtocol == INetProtocol::Ftp ||
+ aINetProtocol == INetProtocol::Http ||
+ aINetProtocol == INetProtocol::Https )
+ {
+ sfx2::openUriExternally(aURL.Complete, true, rReq.GetFrameWeld());
+ return;
+ }
+ else
+ {
+ // check for "internal" protocols that should not be forwarded to the system
+ // add special protocols that always should be treated as internal
+ std::vector < OUString > aProtocols { "private:*", "vnd.sun.star.*" };
+
+ // get registered protocol handlers from configuration
+ Reference < XNameAccess > xAccess(officecfg::Office::ProtocolHandler::HandlerSet::get());
+ const Sequence < OUString > aNames = xAccess->getElementNames();
+ for ( const auto& rName : aNames )
+ {
+ Reference < XPropertySet > xSet;
+ Any aRet = xAccess->getByName( rName );
+ aRet >>= xSet;
+ if ( xSet.is() )
+ {
+ // copy protocols
+ aRet = xSet->getPropertyValue("Protocols");
+ Sequence < OUString > aTmp;
+ aRet >>= aTmp;
+
+ aProtocols.insert(aProtocols.end(),std::cbegin(aTmp),std::cend(aTmp));
+ }
+ }
+
+ bool bFound = false;
+ for (const OUString & rProtocol : aProtocols)
+ {
+ WildCard aPattern(rProtocol);
+ if ( aPattern.Matches( aURL.Complete ) )
+ {
+ bFound = true;
+ break;
+ }
+ }
+
+ if ( !bFound )
+ {
+ bool bLoadInternal = false;
+ try
+ {
+ sfx2::openUriExternally(
+ aURL.Complete, pFilter == nullptr, rReq.GetFrameWeld());
+ }
+ catch ( css::system::SystemShellExecuteException& )
+ {
+ rReq.RemoveItem( SID_TARGETNAME );
+ rReq.AppendItem( SfxStringItem( SID_TARGETNAME, "_default" ) );
+ bLoadInternal = true;
+ }
+ if ( !bLoadInternal )
+ return;
+ }
+ }
+ }
+ else
+ {
+ // hyperlink document must be loaded into a new frame
+ rReq.RemoveItem( SID_TARGETNAME );
+ rReq.AppendItem( SfxStringItem( SID_TARGETNAME, "_default" ) );
+ }
+ }
+ }
+
+ if (!SvtSecurityOptions::isSecureMacroUri(aFileName, aReferer))
+ {
+ SfxErrorContext aCtx( ERRCTX_SFX_OPENDOC, aFileName );
+ ErrorHandler::HandleError( ERRCODE_IO_ACCESSDENIED );
+ return;
+ }
+
+ SfxFrame* pTargetFrame = nullptr;
+ Reference< XFrame > xTargetFrame;
+
+ const SfxFrameItem* pFrameItem = rReq.GetArg<SfxFrameItem>(SID_DOCFRAME);
+ if ( pFrameItem )
+ pTargetFrame = pFrameItem->GetFrame();
+
+ if ( !pTargetFrame )
+ {
+ const SfxUnoFrameItem* pUnoFrameItem = rReq.GetArg<SfxUnoFrameItem>(SID_FILLFRAME);
+ if ( pUnoFrameItem )
+ xTargetFrame = pUnoFrameItem->GetFrame();
+ }
+
+ if ( !pTargetFrame && !xTargetFrame.is() && SfxViewFrame::Current() )
+ pTargetFrame = &SfxViewFrame::Current()->GetFrame();
+
+ // check if caller has set a callback
+ std::unique_ptr<SfxLinkItem> pLinkItem;
+
+ // remove from Itemset, because it confuses the parameter transformation
+ if (auto pParamLinkItem = rReq.GetArg<SfxLinkItem>(SID_DONELINK))
+ pLinkItem.reset(pParamLinkItem->Clone());
+
+ rReq.RemoveItem( SID_DONELINK );
+
+ // check if the view must be hidden
+ bool bHidden = false;
+ const SfxBoolItem* pHidItem = rReq.GetArg<SfxBoolItem>(SID_HIDDEN);
+ if ( pHidItem )
+ bHidden = pHidItem->GetValue();
+
+ // This request is a UI call. We have to set the right values inside the MediaDescriptor
+ // for: InteractionHandler, StatusIndicator, MacroExecutionMode and DocTemplate.
+ // But we have to look for already existing values or for real hidden requests.
+ const SfxBoolItem* pPreviewItem = rReq.GetArg<SfxBoolItem>(SID_PREVIEW);
+ if (!bHidden && ( !pPreviewItem || !pPreviewItem->GetValue() ) )
+ {
+ const SfxUnoAnyItem* pInteractionItem = rReq.GetArg<SfxUnoAnyItem>(SID_INTERACTIONHANDLER);
+ const SfxUInt16Item* pMacroExecItem = rReq.GetArg<SfxUInt16Item>(SID_MACROEXECMODE);
+ const SfxUInt16Item* pDocTemplateItem = rReq.GetArg<SfxUInt16Item>(SID_UPDATEDOCMODE);
+
+ if (!pInteractionItem)
+ {
+ Reference < task::XInteractionHandler2 > xHdl = task::InteractionHandler::createWithParent( ::comphelper::getProcessComponentContext(), nullptr );
+ rReq.AppendItem( SfxUnoAnyItem(SID_INTERACTIONHANDLER,css::uno::Any(xHdl)) );
+ }
+ if (!pMacroExecItem)
+ rReq.AppendItem( SfxUInt16Item(SID_MACROEXECMODE,css::document::MacroExecMode::USE_CONFIG) );
+ if (!pDocTemplateItem)
+ rReq.AppendItem( SfxUInt16Item(SID_UPDATEDOCMODE,css::document::UpdateDocMode::ACCORDING_TO_CONFIG) );
+ }
+
+ // extract target name
+ OUString aTarget;
+ const SfxStringItem* pTargetItem = rReq.GetArg<SfxStringItem>(SID_TARGETNAME);
+ if ( pTargetItem )
+ aTarget = pTargetItem->GetValue();
+ else
+ {
+ const SfxBoolItem* pNewViewItem = rReq.GetArg<SfxBoolItem>(SID_OPEN_NEW_VIEW);
+ if ( pNewViewItem && pNewViewItem->GetValue() )
+ aTarget = "_blank" ;
+ }
+
+ if ( bHidden )
+ {
+ aTarget = "_blank";
+ DBG_ASSERT( rReq.IsSynchronCall() || pLinkItem, "Hidden load process must be done synchronously!" );
+ }
+
+ Reference < XController > xController;
+ // if a frame is given, it must be used for the starting point of the targeting mechanism
+ // this code is also used if asynchronous loading is possible, because loadComponent always is synchron
+ if ( !xTargetFrame.is() )
+ {
+ if ( pTargetFrame )
+ {
+ xTargetFrame = pTargetFrame->GetFrameInterface();
+ }
+ else
+ {
+ xTargetFrame = Desktop::create(::comphelper::getProcessComponentContext());
+ }
+ }
+
+ // make URL ready
+ const SfxStringItem* pURLItem = rReq.GetArg<SfxStringItem>(SID_FILE_NAME);
+ aFileName = pURLItem->GetValue();
+ if( aFileName.startsWith("#") ) // Mark without URL
+ {
+ SfxViewFrame *pView = pTargetFrame ? pTargetFrame->GetCurrentViewFrame() : nullptr;
+ if ( !pView )
+ pView = SfxViewFrame::Current();
+ pView->GetViewShell()->JumpToMark( aFileName.copy(1) );
+ rReq.SetReturnValue( SfxViewFrameItem( pView ) );
+ return;
+ }
+
+ // convert items to properties for framework API calls
+ Sequence < PropertyValue > aArgs;
+ TransformItems( SID_OPENDOC, *rReq.GetArgs(), aArgs );
+ // Any Referer (that was relevant in the above call to
+ // SvtSecurityOptions::isSecureMacroUri) is no longer relevant, assuming
+ // this "open" request is initiated directly by the user:
+ auto pArg = std::find_if(std::cbegin(aArgs), std::cend(aArgs),
+ [](const PropertyValue& rArg) { return rArg.Name == "Referer"; });
+ if (pArg != std::cend(aArgs))
+ {
+ auto nIndex = static_cast<sal_Int32>(std::distance(std::cbegin(aArgs), pArg));
+ comphelper::removeElementAt(aArgs, nIndex);
+ }
+
+ // TODO/LATER: either remove LinkItem or create an asynchronous process for it
+ if( bHidden || pLinkItem || rReq.IsSynchronCall() )
+ {
+ // if loading must be done synchron, we must wait for completion to get a return value
+ // find frame by myself; I must know the exact frame to get the controller for the return value from it
+ Reference < XComponent > xComp;
+
+ try
+ {
+ xComp = ::comphelper::SynchronousDispatch::dispatch( xTargetFrame, aFileName, aTarget, aArgs );
+ }
+ catch(const RuntimeException&)
+ {
+ throw;
+ }
+ catch(const css::uno::Exception&)
+ {
+ }
+
+ Reference < XModel > xModel( xComp, UNO_QUERY );
+ if ( xModel.is() )
+ xController = xModel->getCurrentController();
+ else
+ xController.set( xComp, UNO_QUERY );
+
+ }
+ else
+ {
+ URL aURL;
+ aURL.Complete = aFileName;
+ Reference< util::XURLTransformer > xTrans( util::URLTransformer::create( ::comphelper::getProcessComponentContext() ) );
+ xTrans->parseStrict( aURL );
+
+ Reference < XDispatchProvider > xProv( xTargetFrame, UNO_QUERY );
+ Reference < XDispatch > xDisp = xProv.is() ? xProv->queryDispatch( aURL, aTarget, FrameSearchFlag::ALL ) : Reference < XDispatch >();
+ if ( xDisp.is() )
+ xDisp->dispatch( aURL, aArgs );
+ }
+
+ if ( xController.is() )
+ {
+ // try to find the SfxFrame for the controller
+ SfxFrame* pCntrFrame = nullptr;
+ for ( SfxViewShell* pShell = SfxViewShell::GetFirst( false ); pShell; pShell = SfxViewShell::GetNext( *pShell, false ) )
+ {
+ if ( pShell->GetController() == xController )
+ {
+ pCntrFrame = &pShell->GetViewFrame()->GetFrame();
+ break;
+ }
+ }
+
+ if ( pCntrFrame )
+ {
+ SfxObjectShell* pSh = pCntrFrame->GetCurrentDocument();
+ DBG_ASSERT( pSh, "Controller without ObjectShell ?!" );
+
+ rReq.SetReturnValue( SfxViewFrameItem( pCntrFrame->GetCurrentViewFrame() ) );
+
+ if ( bHidden )
+ pSh->RestoreNoDelete();
+ }
+ }
+
+ if (pLinkItem)
+ {
+ const SfxPoolItem* pRetValue = rReq.GetReturnValue();
+ if (pRetValue)
+ {
+ pLinkItem->GetValue().Call(pRetValue);
+ }
+ }
+}
+
+void SfxApplication::OpenRemoteExec_Impl( SfxRequest& rReq )
+{
+ rReq.AppendItem( SfxBoolItem( SID_REMOTE_DIALOG, true ) );
+ GetDispatcher_Impl()->Execute( SID_OPENDOC, SfxCallMode::SYNCHRON, *rReq.GetArgs() );
+}
+
+void SfxApplication::SignPDFExec_Impl(SfxRequest& rReq)
+{
+ rReq.AppendItem(SfxBoolItem(SID_SIGNPDF, true));
+ GetDispatcher_Impl()->Execute(SID_OPENDOC, SfxCallMode::SYNCHRON, *rReq.GetArgs());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/appl/appquit.cxx b/sfx2/source/appl/appquit.cxx
new file mode 100644
index 000000000..b34550dc7
--- /dev/null
+++ b/sfx2/source/appl/appquit.cxx
@@ -0,0 +1,103 @@
+/* -*- 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 <config_features.h>
+
+#include <basic/sbstar.hxx>
+#include <tools/debug.hxx>
+
+#include <sfx2/app.hxx>
+#include <appdata.hxx>
+#include <sfx2/stbitem.hxx>
+#include <sfx2/tbxctrl.hxx>
+#include <sfx2/childwin.hxx>
+#include <sfx2/dispatch.hxx>
+#include <nochaos.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/objsh.hxx>
+#include <appbaslib.hxx>
+#include <basic/basicmanagerrepository.hxx>
+
+using ::basic::BasicManagerRepository;
+
+void SfxApplication::Deinitialize()
+{
+ if (pImpl->bDowning)
+ return;
+
+#if HAVE_FEATURE_SCRIPTING
+ StarBASIC::Stop();
+
+ SaveBasicAndDialogContainer();
+#endif
+
+ pImpl->bDowning = true; // due to Timer from DecAliveCount and QueryExit
+
+ pImpl->pTemplates.reset();
+
+ // By definition there shouldn't be any open view frames when we reach
+ // this method. Therefore this call makes no sense and is the source of
+ // some stack traces, which we don't understand.
+ // For more information see:
+ pImpl->bDowning = false;
+ DBG_ASSERT(!SfxViewFrame::GetFirst(), "existing SfxViewFrame after Execute");
+ DBG_ASSERT(!SfxObjectShell::GetFirst(), "existing SfxObjectShell after Execute");
+ pImpl->pAppDispat->Pop(*this, SfxDispatcherPopFlags::POP_UNTIL);
+ pImpl->pAppDispat->Flush();
+ pImpl->bDowning = true;
+ pImpl->pAppDispat->DoDeactivate_Impl(true, nullptr);
+
+ // Release Controller and others
+ // then the remaining components should also disappear ( Beamer! )
+
+#if HAVE_FEATURE_SCRIPTING
+ BasicManagerRepository::resetApplicationBasicManager();
+ pImpl->pBasicManager->reset(nullptr); // this will also delete pBasMgr
+#endif
+
+ DBG_ASSERT(pImpl->pViewFrame == nullptr, "active foreign ViewFrame");
+
+ // free administration managers
+ pImpl->pAppDispat.reset();
+
+ // from here no SvObjects have to exists
+ pImpl->pMatcher.reset();
+
+ pImpl->pSlotPool.reset();
+ pImpl->maFactories.clear();
+
+ pImpl->maTbxCtrlFactories.clear();
+ pImpl->maStbCtrlFactories.clear();
+ pImpl->maViewFrames.clear();
+ pImpl->maViewShells.clear();
+ pImpl->maObjShells.clear();
+
+ //TODO/CLEANUP
+ //ReleaseArgs could be used instead!
+ pImpl->pPool = nullptr;
+ NoChaos::ReleaseItemPool();
+
+#if HAVE_FEATURE_SCRIPTING
+ pImpl->m_pSbxErrorHdl.reset();
+#endif
+ pImpl->m_pSoErrorHdl.reset();
+ pImpl->m_pToolsErrorHdl.reset();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/appl/appreg.cxx b/sfx2/source/appl/appreg.cxx
new file mode 100644
index 000000000..662e485b6
--- /dev/null
+++ b/sfx2/source/appl/appreg.cxx
@@ -0,0 +1,105 @@
+/* -*- 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/log.hxx>
+
+#include <sfx2/app.hxx>
+#include <appdata.hxx>
+#include <inettbc.hxx>
+#include <sfx2/stbitem.hxx>
+#include <sfx2/infobar.hxx>
+#include <sfx2/module.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <partwnd.hxx>
+#include <sfx2/sfxsids.hrc>
+#include <recfloat.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/viewsh.hxx>
+
+
+void SfxApplication::Registrations_Impl()
+{
+ // Interfaces
+ SfxApplication::RegisterInterface();
+ SfxModule::RegisterInterface();
+ SfxViewFrame::RegisterInterface();
+ SfxObjectShell::RegisterInterface();
+ SfxViewShell::RegisterInterface();
+
+ // ChildWindows
+ SfxRecordingFloatWrapper_Impl::RegisterChildWindow();
+ SfxPartChildWnd_Impl::RegisterChildWindow();
+ SfxDockingWrapper::RegisterChildWindow();
+ SfxInfoBarContainerChild::RegisterChildWindow( true, nullptr, SfxChildWindowFlags::NEVERHIDE );
+
+ // Controller
+ SfxToolBoxControl::RegisterControl(SID_REPEAT);
+ SfxURLToolBoxControl_Impl::RegisterControl(SID_OPENURL);
+}
+
+
+void SfxApplication::RegisterToolBoxControl_Impl( SfxModule *pMod, const SfxTbxCtrlFactory& rFact )
+{
+ if ( pMod )
+ {
+ pMod->RegisterToolBoxControl( rFact );
+ return;
+ }
+
+#ifdef DBG_UTIL
+ for ( size_t n=0; n<pImpl->maTbxCtrlFactories.size(); n++ )
+ {
+ SfxTbxCtrlFactory *pF = &pImpl->maTbxCtrlFactories[n];
+ if ( pF->nTypeId == rFact.nTypeId &&
+ (pF->nSlotId == rFact.nSlotId || pF->nSlotId == 0) )
+ {
+ SAL_INFO("sfx", "TbxController registration is not clearly defined!");
+ }
+ }
+#endif
+
+ pImpl->maTbxCtrlFactories.push_back( rFact );
+}
+
+
+void SfxApplication::RegisterStatusBarControl_Impl( SfxModule *pMod, const SfxStbCtrlFactory& rFact )
+{
+ if ( pMod )
+ {
+ pMod->RegisterStatusBarControl( rFact );
+ return;
+ }
+
+#ifdef DBG_UTIL
+ for ( size_t n=0; n<pImpl->maStbCtrlFactories.size(); n++ )
+ {
+ SfxStbCtrlFactory *pF = &pImpl->maStbCtrlFactories[n];
+ if ( pF->nTypeId == rFact.nTypeId &&
+ (pF->nSlotId == rFact.nSlotId || pF->nSlotId == 0) )
+ {
+ SAL_INFO("sfx", "StbController registration is not clearly defined!");
+ }
+ }
+#endif
+
+ pImpl->maStbCtrlFactories.push_back( rFact );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/appl/appserv.cxx b/sfx2/source/appl/appserv.cxx
new file mode 100644
index 000000000..ee603ab5e
--- /dev/null
+++ b/sfx2/source/appl/appserv.cxx
@@ -0,0 +1,1726 @@
+/* -*- 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 <config_features.h>
+#include <config_wasm_strip.h>
+
+#include <com/sun/star/drawing/ModuleDispatcher.hpp>
+#include <com/sun/star/frame/Desktop.hpp>
+#include <com/sun/star/frame/DispatchResultEvent.hpp>
+#include <com/sun/star/frame/DispatchResultState.hpp>
+#include <com/sun/star/frame/DispatchHelper.hpp>
+#include <com/sun/star/frame/UnknownModuleException.hpp>
+#include <com/sun/star/frame/XLayoutManager.hpp>
+#include <com/sun/star/frame/XSynchronousFrameLoader.hpp>
+#include <com/sun/star/sdbc/DriverManager.hpp>
+#include <com/sun/star/text/ModuleDispatcher.hpp>
+#include <com/sun/star/task/OfficeRestartManager.hpp>
+#include <com/sun/star/task/XInteractionHandler.hpp>
+#include <com/sun/star/ui/dialogs/AddressBookSourcePilot.hpp>
+#include <com/sun/star/ui/UIElementType.hpp>
+#include <com/sun/star/ui/XUIElement.hpp>
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/util/XCloseable.hpp>
+#include <com/sun/star/util/CloseVetoException.hpp>
+#include <org/freedesktop/PackageKit/SyncDbusSessionHelper.hpp>
+
+#include <comphelper/dispatchcommand.hxx>
+#include <comphelper/lok.hxx>
+#include <comphelper/namedvaluecollection.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/propertysequence.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <comphelper/sequence.hxx>
+
+#include <svtools/addresstemplate.hxx>
+#include <svtools/restartdialog.hxx>
+#include <svl/visitem.hxx>
+
+#include <unotools/configmgr.hxx>
+#include <tools/diagnose_ex.h>
+#include <vcl/weld.hxx>
+#include <svl/intitem.hxx>
+#include <svl/eitem.hxx>
+#include <svl/stritem.hxx>
+#include <basic/sbstar.hxx>
+#include <basic/basrdll.hxx>
+#include <basic/sberrors.hxx>
+#include <vcl/help.hxx>
+#include <sal/log.hxx>
+#include <osl/file.hxx>
+#include <vcl/EnumContext.hxx>
+#include <vcl/toolbox.hxx>
+
+#include <unotools/moduleoptions.hxx>
+#include <rtl/bootstrap.hxx>
+
+#include <com/sun/star/frame/ModuleManager.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+
+#include <sfx2/app.hxx>
+#include <sfx2/request.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/msg.hxx>
+#include <sfx2/objface.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/viewsh.hxx>
+#include <sfx2/docfac.hxx>
+#include <sfx2/strings.hrc>
+#include <sfx2/sfxresid.hxx>
+#include <appdata.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/sfxdlg.hxx>
+#include <sfx2/sfxsids.hrc>
+#include <sorgitm.hxx>
+#include <sfx2/sfxhelp.hxx>
+#include <sfx2/zoomitem.hxx>
+#include <sfx2/templatedlg.hxx>
+#include <sfx2/notebookbar/SfxNotebookBar.hxx>
+#include <sfx2/sidebar/SidebarController.hxx>
+#include <sfx2/safemode.hxx>
+#include <sfx2/sfxuno.hxx>
+#include <sfx2/devtools/DevelopmentToolDockingWindow.hxx>
+
+#include <comphelper/types.hxx>
+#include <officecfg/Office/Common.hxx>
+#include <unotools/confignode.hxx>
+#include <memory>
+
+#include <openuriexternally.hxx>
+
+#include "getbasctlfunction.hxx"
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::frame;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::util;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::ui;
+
+namespace
+{
+ OUString lcl_getAppName( vcl::EnumContext::Application eApp )
+ {
+ switch ( eApp )
+ {
+ case vcl::EnumContext::Application::Writer:
+ return "Writer";
+ case vcl::EnumContext::Application::Calc:
+ return "Calc";
+ case vcl::EnumContext::Application::Impress:
+ return "Impress";
+ case vcl::EnumContext::Application::Draw:
+ return "Draw";
+ case vcl::EnumContext::Application::Formula:
+ return "Formula";
+ case vcl::EnumContext::Application::Base:
+ return "Base";
+ default:
+ return OUString();
+ }
+ }
+
+ // lp#527938, debian#602953, fdo#33266, i#105408
+ bool lcl_isBaseAvailable()
+ {
+ try
+ {
+ // if we get css::sdbc::DriverManager, libsdbc2 is there
+ // and the bibliography is assumed to work
+ return css::sdbc::DriverManager::create(comphelper::getProcessComponentContext()).is();
+ }
+ catch (const Exception &)
+ {
+ TOOLS_INFO_EXCEPTION("sfx.appl", "assuming Base to be missing");
+ return false;
+ }
+ }
+ void lcl_tryLoadBibliography()
+ {
+ // lp#527938, debian#602953, fdo#33266, i#105408
+ // make sure we actually can instantiate services from base first
+ if(!lcl_isBaseAvailable())
+ {
+ if (officecfg::Office::Common::PackageKit::EnableBaseInstallation::get())
+ {
+ try
+ {
+ using namespace org::freedesktop::PackageKit;
+ using namespace svtools;
+ Reference< XSyncDbusSessionHelper > xSyncDbusSessionHelper(SyncDbusSessionHelper::create(comphelper::getProcessComponentContext()));
+ Sequence< OUString > vPackages { "libreoffice-base" };
+ xSyncDbusSessionHelper->InstallPackageNames(vPackages, OUString());
+ // I'll be back (hopefully)!
+ SolarMutexGuard aGuard;
+ executeRestartDialog(comphelper::getProcessComponentContext(), nullptr, RESTART_REASON_BIBLIOGRAPHY_INSTALL);
+ }
+ catch (const Exception &)
+ {
+ TOOLS_INFO_EXCEPTION("sfx.appl", "trying to install LibreOffice Base");
+ }
+ }
+ return;
+ }
+
+ try // fdo#48775
+ {
+ SfxStringItem aURL(SID_FILE_NAME, ".component:Bibliography/View1");
+ SfxStringItem aRef(SID_REFERER, "private:user");
+ SfxStringItem aTarget(SID_TARGETNAME, "_blank");
+ const SfxViewFrame* pViewFrame = SfxViewFrame::Current();
+ if ( pViewFrame )
+ pViewFrame->GetDispatcher()->ExecuteList(SID_OPENDOC,
+ SfxCallMode::ASYNCHRON, { &aURL, &aRef, &aTarget });
+ }
+ catch (const Exception &)
+ {
+ TOOLS_INFO_EXCEPTION( "sfx.appl", "trying to load bibliography database");
+ }
+ }
+}
+/// Find the correct location of the document (CREDITS.fodt, etc.), and return
+/// it in rURL if found.
+static bool checkURL( const char *pName, const char *pExt, OUString &rURL )
+{
+ using namespace osl;
+ DirectoryItem aDirItem;
+
+#ifdef MACOSX
+ rURL = "$BRAND_BASE_DIR/Resources/" + OUString::createFromAscii( pName ) +
+ OUString::createFromAscii( pExt );
+#else
+ rURL = "$BRAND_BASE_DIR/" + OUString::createFromAscii( pName ) +
+ OUString::createFromAscii( pExt );
+#endif
+ rtl::Bootstrap::expandMacros( rURL );
+
+ if (!rURL.isEmpty())
+ return DirectoryItem::get( rURL, aDirItem ) == DirectoryItem::E_None;
+ else
+ return false;
+}
+
+/// Displays CREDITS or LICENSE in any of the available version
+static void showDocument( const char* pBaseName )
+{
+ try {
+ Reference < XDesktop2 > xDesktop = Desktop::create( ::comphelper::getProcessComponentContext() );
+ auto args(::comphelper::InitPropertySequence({
+ {"ViewOnly", Any(true)},
+ {"ReadOnly", Any(true)}
+ }));
+
+ OUString aURL;
+ if ( checkURL ( pBaseName, ".fodt", aURL ) ||
+ checkURL ( pBaseName, ".html", aURL ) ||
+ checkURL ( pBaseName, "", aURL ) ) {
+ xDesktop->loadComponentFromURL( aURL, "_blank", 0, args );
+ }
+ } catch (const css::uno::Exception &) {
+ }
+}
+
+namespace
+{
+ Reference<XFrame> GetRequestFrame(const SfxRequest& rReq)
+ {
+ const SfxItemSet* pArgs = rReq.GetInternalArgs_Impl();
+ const SfxUnoFrameItem* pItem = nullptr;
+ Reference <XFrame> xFrame;
+ if (pArgs && (pItem = pArgs->GetItemIfSet(SID_FILLFRAME, false)))
+ {
+ xFrame = pItem->GetFrame();
+ }
+ return xFrame;
+ }
+
+ class LicenseDialog : public weld::GenericDialogController
+ {
+ public:
+ LicenseDialog(weld::Window* pParent)
+ : GenericDialogController(pParent, "sfx/ui/licensedialog.ui", "LicenseDialog")
+ {
+ }
+
+ virtual short run() override
+ {
+ short nRet = GenericDialogController::run();
+ if (nRet == RET_OK)
+ showDocument("LICENSE");
+ return nRet;
+ }
+ };
+
+ class SafeModeQueryDialog : public weld::MessageDialogController
+ {
+ public:
+ SafeModeQueryDialog(weld::Window* pParent)
+ : MessageDialogController(pParent, "sfx/ui/safemodequerydialog.ui", "SafeModeQueryDialog")
+ {
+ }
+
+ virtual short run() override
+ {
+ short nRet = MessageDialogController::run();
+ if (nRet == RET_OK)
+ {
+ sfx2::SafeMode::putFlag();
+ uno::Reference< uno::XComponentContext > xContext = comphelper::getProcessComponentContext();
+ css::task::OfficeRestartManager::get(xContext)->requestRestart(
+ css::uno::Reference< css::task::XInteractionHandler >());
+ }
+ return nRet;
+ }
+ };
+}
+
+weld::Window* SfxRequest::GetFrameWeld() const
+{
+ const SfxItemSet* pIntArgs = GetInternalArgs_Impl();
+ const SfxUnoAnyItem* pItem = nullptr;
+ if (pIntArgs && (pItem = pIntArgs->GetItemIfSet(SID_DIALOG_PARENT, false)))
+ {
+ auto aAny = pItem->GetValue();
+ Reference<awt::XWindow> xWindow;
+ aAny >>= xWindow;
+ return Application::GetFrameWeld(xWindow);
+ }
+
+ Reference<XFrame> xFrame(GetRequestFrame(*this));
+ if (!xFrame)
+ {
+ SAL_WARN("sfx.appl", "no parent for dialogs");
+ return nullptr;
+ }
+ return Application::GetFrameWeld(xFrame->getContainerWindow());
+}
+
+void SfxApplication::MiscExec_Impl( SfxRequest& rReq )
+{
+ bool bDone = false;
+ switch ( rReq.GetSlot() )
+ {
+ case SID_SETOPTIONS:
+ {
+ if( rReq.GetArgs() )
+ SetOptions_Impl( *rReq.GetArgs() );
+ break;
+ }
+
+ case SID_QUITAPP:
+ case SID_LOGOUT:
+ {
+ // protect against reentrant calls
+ if ( pImpl->bInQuit )
+ return;
+
+ if ( rReq.GetSlot() == SID_LOGOUT )
+ {
+ for ( SfxObjectShell *pObjSh = SfxObjectShell::GetFirst();
+ pObjSh; pObjSh = SfxObjectShell::GetNext( *pObjSh ) )
+ {
+ if ( !pObjSh->IsModified() )
+ continue;
+
+ SfxViewFrame* pFrame = SfxViewFrame::GetFirst( pObjSh );
+ if ( !pFrame || !pFrame->GetWindow().IsReallyVisible() )
+ continue;
+
+ if (pObjSh->PrepareClose())
+ pObjSh->SetModified( false );
+ else
+ return;
+ }
+
+ SfxStringItem aNameItem( SID_FILE_NAME, "vnd.sun.star.cmd:logout" );
+ SfxStringItem aReferer( SID_REFERER, "private/user" );
+ pImpl->pAppDispat->ExecuteList(SID_OPENDOC,
+ SfxCallMode::SLOT, { &aNameItem, &aReferer });
+ return;
+ }
+
+ // try from nested requests again after 100ms
+ if( Application::GetDispatchLevel() > 1 )
+ {
+ /* Don't save the request for closing the application and try it later
+ again. This is an UI bound functionality ... and the user will try it again
+ if the dialog is closed. But we should not close the application automatically
+ if this dialog is closed by the user ...
+ So we ignore this request now and wait for a new user decision.
+ */
+ SAL_INFO("sfx.appl", "QueryExit => sal_False, DispatchLevel == " << Application::GetDispatchLevel() );
+ return;
+ }
+
+ // block reentrant calls
+ pImpl->bInQuit = true;
+ Reference < XDesktop2 > xDesktop = Desktop::create ( ::comphelper::getProcessComponentContext() );
+
+ rReq.ForgetAllArgs();
+
+ // if terminate() failed, pImpl->bInQuit will now be sal_False, allowing further calls of SID_QUITAPP
+ bool bTerminated = xDesktop->terminate();
+ if (!bTerminated)
+ // if terminate() was successful, SfxApplication is now dead!
+ pImpl->bInQuit = false;
+
+ // Set return value, terminate if possible
+ rReq.SetReturnValue( SfxBoolItem( rReq.GetSlot(), bTerminated ) );
+ return;
+ }
+
+ case SID_CONFIG:
+ case SID_TOOLBOXOPTIONS:
+ case SID_CONFIGSTATUSBAR:
+ case SID_CONFIGMENU:
+ case SID_CONFIGACCEL:
+ case SID_CONFIGEVENT:
+ {
+ SfxAbstractDialogFactory* pFact =
+ SfxAbstractDialogFactory::Create();
+
+ const SfxStringItem* pStringItem = rReq.GetArg<SfxStringItem>(SID_CONFIG);
+
+ SfxItemSetFixed<SID_CONFIG, SID_CONFIG> aSet( GetPool() );
+
+ if ( pStringItem )
+ {
+ aSet.Put( SfxStringItem(
+ SID_CONFIG, pStringItem->GetValue() ) );
+ }
+
+ Reference <XFrame> xFrame(GetRequestFrame(rReq));
+ ScopedVclPtr<SfxAbstractTabDialog> pDlg(pFact->CreateCustomizeTabDialog(rReq.GetFrameWeld(),
+ &aSet, xFrame ));
+
+ const short nRet = pDlg->Execute();
+
+ if ( nRet )
+ bDone = true;
+ break;
+ }
+
+ case SID_CLOSEDOCS:
+ {
+
+ Reference < XDesktop2 > xDesktop = Desktop::create( ::comphelper::getProcessComponentContext() );
+ Reference< XIndexAccess > xTasks = xDesktop->getFrames();
+ if ( !xTasks.is() )
+ break;
+
+ sal_Int32 n=0;
+ do
+ {
+ if ( xTasks->getCount() <= n )
+ break;
+
+ Any aAny = xTasks->getByIndex(n);
+ Reference < XCloseable > xTask;
+ aAny >>= xTask;
+ try
+ {
+ xTask->close(true);
+ n++;
+ }
+ catch( CloseVetoException& )
+ {
+ }
+ }
+ while( true );
+
+ bool bOk = ( n == 0);
+ rReq.SetReturnValue( SfxBoolItem( 0, bOk ) );
+ bDone = true;
+ break;
+ }
+
+ case SID_SAVEDOCS:
+ {
+ bool bOK = true;
+ for ( SfxObjectShell *pObjSh = SfxObjectShell::GetFirst();
+ pObjSh;
+ pObjSh = SfxObjectShell::GetNext( *pObjSh ) )
+ {
+ SfxRequest aReq( SID_SAVEDOC, SfxCallMode::SLOT, pObjSh->GetPool() );
+ if ( pObjSh->IsModified() && !pObjSh->isSaveLocked())
+ {
+ pObjSh->ExecuteSlot( aReq );
+ const SfxBoolItem *pItem = dynamic_cast<const SfxBoolItem*>( aReq.GetReturnValue() );
+ if ( !pItem || !pItem->GetValue() )
+ bOK = false;
+ }
+ }
+
+ rReq.SetReturnValue( SfxBoolItem( 0, bOK ) );
+ rReq.Done();
+ break;
+ }
+
+ case SID_SEND_FEEDBACK:
+ {
+ OUString module = SfxHelp::GetCurrentModuleIdentifier();
+ OUString sURL(officecfg::Office::Common::Menus::SendFeedbackURL::get() + //officecfg/registry/data/org/openoffice/Office/Common.xcu => https://hub.libreoffice.org/send-feedback/
+ "?LOversion=" + utl::ConfigManager::getAboutBoxProductVersion() +
+ "&LOlocale=" + utl::ConfigManager::getUILocale() +
+ "&LOmodule=" + module.subView(module.lastIndexOf('.') + 1 ) );
+ sfx2::openUriExternally(sURL, false, rReq.GetFrameWeld());
+ break;
+ }
+
+ case SID_Q_AND_A:
+ {
+ // Askbot has URL's normalized to languages, not locales
+ // Get language from locale: ll or lll or ll-CC or lll-CC
+
+ OUString sURL(officecfg::Office::Common::Menus::QA_URL::get() + //https://hub.libreoffice.org/forum/
+ "?LOlocale=" + utl::ConfigManager::getUILocale());
+ sfx2::openUriExternally(sURL, false, rReq.GetFrameWeld());
+ break;
+ }
+ case SID_DOCUMENTATION:
+ {
+ // Open documentation page based on locales
+ OUString sURL(officecfg::Office::Common::Menus::DocumentationURL::get() + //https://hub.libreoffice.org/documentation/
+ "?LOlocale=" + utl::ConfigManager::getUILocale());
+ sfx2::openUriExternally(sURL, false, rReq.GetFrameWeld());
+ break;
+ }
+#if !ENABLE_WASM_STRIP_PINGUSER
+ case SID_GETINVOLVED:
+ {
+ // Open get involved/join us page based on locales
+ OUString sURL(officecfg::Office::Common::Menus::GetInvolvedURL::get() + //https://hub.libreoffice.org/joinus/
+ "?LOlocale=" + utl::ConfigManager::getUILocale());
+ sfx2::openUriExternally(sURL, false, rReq.GetFrameWeld());
+ break;
+ }
+ case SID_DONATION:
+ {
+ // Open donation page based on language + script (BCP47) with language as fall back.
+ OUString aLang = LanguageTag(utl::ConfigManager::getUILocale()).getLanguage();
+ OUString aBcp47 = LanguageTag(utl::ConfigManager::getUILocale()).getBcp47();
+ OUString sURL(officecfg::Office::Common::Menus::DonationURL::get() + //https://hub.libreoffice.org/donation/
+ "?BCP47=" + aBcp47 + "&LOlang=" + aLang );
+ sfx2::openUriExternally(sURL, false, rReq.GetFrameWeld());
+ break;
+ }
+ case SID_WHATSNEW:
+ {
+ // Open release notes depending on version and locale
+ OUString sURL(officecfg::Office::Common::Menus::ReleaseNotesURL::get() + //https://hub.libreoffice.org/ReleaseNotes/
+ "?LOvers=" + utl::ConfigManager::getProductVersion() +
+ "&LOlocale=" + LanguageTag(utl::ConfigManager::getUILocale()).getBcp47() );
+ sfx2::openUriExternally(sURL, false, rReq.GetFrameWeld());
+ break;
+ }
+ case SID_HYPHENATIONMISSING:
+ {
+ // Open wiki page about hyphenation
+ OUString sURL(officecfg::Office::Common::Menus::HyphenationMissingURL::get() + //https://hub.libreoffice.org/HyphenationMissing/
+ "?LOlocale=" + utl::ConfigManager::getUILocale());
+ sfx2::openUriExternally(sURL, false, rReq.GetFrameWeld());
+ break;
+ }
+#endif
+ case SID_SHOW_LICENSE:
+ {
+ LicenseDialog aDialog(rReq.GetFrameWeld());
+ aDialog.run();
+ break;
+ }
+
+ case SID_SHOW_CREDITS:
+ {
+ showDocument( "CREDITS" );
+ break;
+ }
+
+ // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ case SID_HELPINDEX:
+ {
+ Help* pHelp = Application::GetHelp();
+ if ( pHelp )
+ {
+ pHelp->Start(".uno:HelpIndex", rReq.GetFrameWeld()); // show start page
+ bDone = true;
+ }
+ break;
+ }
+
+ // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ case SID_HELPTIPS:
+ {
+ // Evaluate Parameter
+ const SfxBoolItem* pOnItem = rReq.GetArg<SfxBoolItem>(SID_HELPTIPS);
+ bool bOn = pOnItem
+ ? pOnItem->GetValue()
+ : !Help::IsQuickHelpEnabled();
+
+ if ( bOn )
+ Help::EnableQuickHelp();
+ else
+ Help::DisableQuickHelp();
+ auto xChanges = comphelper::ConfigurationChanges::create();
+ officecfg::Office::Common::Help::Tip::set(bOn, xChanges);
+ xChanges->commit();
+ Invalidate(SID_HELPTIPS);
+ bDone = true;
+
+ // Record if possible
+ if ( !rReq.IsAPI() )
+ rReq.AppendItem( SfxBoolItem( SID_HELPTIPS, bOn) );
+ break;
+ }
+ // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ case SID_EXTENDEDHELP:
+ {
+ Help::StartExtHelp();
+ break;
+ }
+ case SID_HELPBALLOONS:
+ {
+ // Evaluate Parameter
+ const SfxBoolItem* pOnItem = rReq.GetArg<SfxBoolItem>(SID_HELPBALLOONS);
+ bool bOn = pOnItem
+ ? pOnItem->GetValue()
+ : !Help::IsBalloonHelpEnabled();
+
+ if ( bOn )
+ Help::EnableBalloonHelp();
+ else
+ Help::DisableBalloonHelp();
+ auto xChanges = comphelper::ConfigurationChanges::create();
+ officecfg::Office::Common::Help::ExtendedTip::set(bOn, xChanges);
+ xChanges->commit();
+ Invalidate(SID_HELPBALLOONS);
+ bDone = true;
+
+ // Record if possible
+ if ( !rReq.IsAPI() )
+ rReq.AppendItem( SfxBoolItem( SID_HELPBALLOONS, bOn) );
+ break;
+ }
+ // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+#if !ENABLE_WASM_STRIP_PINGUSER
+ case SID_TIPOFTHEDAY:
+ {
+ SfxAbstractDialogFactory* pFact = SfxAbstractDialogFactory::Create();
+ ScopedVclPtr<VclAbstractDialog> pDlg(pFact->CreateTipOfTheDayDialog(rReq.GetFrameWeld()));
+ pDlg->StartExecuteAsync(nullptr);
+ bDone = true;
+ break;
+ }
+#endif
+
+ // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ case SID_ABOUT:
+ {
+ SfxAbstractDialogFactory* pFact = SfxAbstractDialogFactory::Create();
+ ScopedVclPtr<VclAbstractDialog> pDlg(pFact->CreateAboutDialog(rReq.GetFrameWeld()));
+ pDlg->StartExecuteAsync(nullptr);
+ bDone = true;
+ break;
+ }
+
+ case SID_TEMPLATE_MANAGER:
+ {
+ SfxTemplateManagerDlg aDialog(rReq.GetFrameWeld());
+ aDialog.run();
+ bDone = true;
+ break;
+ }
+
+ case SID_TEMPLATE_ADDRESSBOOKSOURCE:
+ {
+ svt::AddressBookSourceDialog aDialog(rReq.GetFrameWeld(), ::comphelper::getProcessComponentContext());
+ aDialog.run();
+ bDone = true;
+ break;
+ }
+
+#if HAVE_FEATURE_SCRIPTING
+ case SID_BASICSTOP:
+ StarBASIC::Stop();
+ break;
+
+ case SID_BASICBREAK :
+ BasicDLL::BasicBreak();
+ break;
+#endif
+
+ case SID_ZOOM_50_PERCENT:
+ case SID_ZOOM_75_PERCENT:
+ case SID_ZOOM_100_PERCENT:
+ case SID_ZOOM_150_PERCENT:
+ case SID_ZOOM_200_PERCENT:
+ case SID_ZOOM_OPTIMAL:
+ case SID_ZOOM_ENTIRE_PAGE:
+ case SID_ZOOM_PAGE_WIDTH:
+ {
+ SfxObjectShell* pCurrentShell = SfxObjectShell::Current();
+ if (!pCurrentShell)
+ return;
+
+ // make sure aZoom is initialized with a proper value if SetType
+ // doesn't work
+ SvxZoomItem aZoom( SvxZoomType::PERCENT, 100 );
+
+ switch (rReq.GetSlot())
+ {
+ case SID_ZOOM_50_PERCENT:
+ aZoom.SetValue(50);
+ break;
+ case SID_ZOOM_75_PERCENT:
+ aZoom.SetValue(75);
+ break;
+ case SID_ZOOM_100_PERCENT:
+ aZoom.SetValue(100);
+ break;
+ case SID_ZOOM_150_PERCENT:
+ aZoom.SetValue(150);
+ break;
+ case SID_ZOOM_200_PERCENT:
+ aZoom.SetValue(200);
+ break;
+ case SID_ZOOM_OPTIMAL:
+ aZoom.SetType( SvxZoomType::OPTIMAL );
+ break;
+ case SID_ZOOM_ENTIRE_PAGE:
+ aZoom.SetType( SvxZoomType::WHOLEPAGE );
+ break;
+ case SID_ZOOM_PAGE_WIDTH:
+ aZoom.SetType( SvxZoomType::PAGEWIDTH );
+ break;
+ }
+
+ pCurrentShell->GetDispatcher()->ExecuteList(SID_ATTR_ZOOM, SfxCallMode::ASYNCHRON, { &aZoom });
+
+ break;
+ }
+ case SID_TOOLBAR_MODE:
+ {
+ const SfxStringItem* pModeName = rReq.GetArg<SfxStringItem>( SID_TOOLBAR_MODE );
+
+ if ( !pModeName )
+ {
+ bDone = true;
+ break;
+ }
+
+ OUString aNewName(pModeName->GetValue());
+ uno::Reference< uno::XComponentContext > xContext =
+ ::comphelper::getProcessComponentContext();
+
+ // Get information about current frame and module
+ Reference<XFrame> xCurrentFrame;
+ vcl::EnumContext::Application eCurrentApp = vcl::EnumContext::Application::NONE;
+ OUString aCurrentMode;
+
+ SfxViewFrame* pViewFrame = SfxViewFrame::Current();
+ if( pViewFrame )
+ {
+ xCurrentFrame = pViewFrame->GetFrame().GetFrameInterface();
+
+ const Reference<frame::XModuleManager> xModuleManager = frame::ModuleManager::create( xContext );
+ eCurrentApp = vcl::EnumContext::GetApplicationEnum( xModuleManager->identify( xCurrentFrame ) );
+
+ OUString aPath = "org.openoffice.Office.UI.ToolbarMode/Applications/" +
+ lcl_getAppName( eCurrentApp );
+
+ const utl::OConfigurationTreeRoot aAppNode(
+ xContext,
+ aPath,
+ true);
+ if ( !aAppNode.isValid() )
+ {
+ bDone = true;
+ break;
+ }
+
+ aCurrentMode = comphelper::getString( aAppNode.getNodeValue( "Active" ) );
+
+ if ( aCurrentMode == aNewName )
+ {
+ bDone = true;
+ break;
+ }
+
+ // Save new toolbar mode for a current module
+ aAppNode.setNodeValue( "Active", Any( aNewName ) );
+ aAppNode.commit();
+ }
+
+ // Apply settings for all frames
+ pViewFrame = SfxViewFrame::GetFirst();
+ while( pViewFrame )
+ {
+ Reference<XFrame> xFrame = pViewFrame->GetFrame().GetFrameInterface();
+
+ // We want to change mode only for a current app module, ignore other apps
+ const Reference<frame::XModuleManager> xModuleManager = frame::ModuleManager::create( xContext );
+ vcl::EnumContext::Application eApp = vcl::EnumContext::GetApplicationEnum( xModuleManager->identify( xFrame ) );
+ if ( eApp != eCurrentApp )
+ {
+ pViewFrame = SfxViewFrame::GetNext( *pViewFrame );
+ continue;
+ }
+
+ Reference<css::beans::XPropertySet> xPropSet( xFrame, UNO_QUERY );
+ Reference<css::frame::XLayoutManager> xLayoutManager;
+ if ( xPropSet.is() )
+ {
+ try
+ {
+ Any aValue = xPropSet->getPropertyValue( "LayoutManager" );
+ aValue >>= xLayoutManager;
+ }
+ catch ( const css::uno::RuntimeException& )
+ {
+ throw;
+ }
+ catch ( css::uno::Exception& )
+ {
+ }
+ }
+
+ if ( xLayoutManager.is() )
+ {
+ css::uno::Sequence<OUString> aMandatoryToolbars;
+ css::uno::Sequence<OUString> aUserToolbars;
+ std::vector<OUString> aBackupList;
+ OUString aSidebarMode;
+
+ OUString aPath = "org.openoffice.Office.UI.ToolbarMode/Applications/" +
+ lcl_getAppName( eApp ) +
+ "/Modes";
+
+ // Read mode settings
+ const utl::OConfigurationTreeRoot aModesNode(
+ xContext,
+ aPath,
+ true);
+ if ( !aModesNode.isValid() )
+ {
+ bDone = true;
+ break;
+ }
+
+ const Sequence<OUString> aModeNodeNames( aModesNode.getNodeNames() );
+
+ for ( const auto& rModeNodeName : aModeNodeNames )
+ {
+ const utl::OConfigurationNode aModeNode( aModesNode.openNode( rModeNodeName ) );
+ if ( !aModeNode.isValid() )
+ continue;
+
+ OUString aCommandArg = comphelper::getString( aModeNode.getNodeValue( "CommandArg" ) );
+
+ if ( aCommandArg == aNewName )
+ {
+ aMandatoryToolbars = aModeNode.getNodeValue( "Toolbars" ).get< uno::Sequence<OUString> >();
+ aUserToolbars = aModeNode.getNodeValue( "UserToolbars" ).get< uno::Sequence<OUString> >();
+ aSidebarMode = comphelper::getString( aModeNode.getNodeValue( "Sidebar" ) );
+ break;
+ }
+ }
+
+ // Backup visible toolbar list and hide all toolbars
+ const Sequence<Reference<XUIElement>> aUIElements = xLayoutManager->getElements();
+ for ( const Reference< XUIElement >& xUIElement : aUIElements )
+ {
+ Reference< XPropertySet > xPropertySet( xUIElement, UNO_QUERY );
+ if ( xPropertySet.is() && xUIElement.is() )
+ {
+ try
+ {
+ OUString aResName;
+ sal_Int16 nType( -1 );
+ xPropertySet->getPropertyValue( "Type" ) >>= nType;
+ xPropertySet->getPropertyValue( "ResourceURL" ) >>= aResName;
+
+ if (( nType == css::ui::UIElementType::TOOLBAR ) &&
+ !aResName.isEmpty() )
+ {
+ if ( xLayoutManager->isElementVisible( aResName ) )
+ {
+ aBackupList.push_back( aResName );
+ }
+ xLayoutManager->hideElement( aResName );
+ }
+ }
+ catch ( const Exception& )
+ {
+ }
+ }
+ }
+
+ // Show/Hide the Notebookbar
+ const SfxStringItem pItem(SID_NOTEBOOKBAR, aNewName);
+ pViewFrame->GetDispatcher()->ExecuteList(SID_NOTEBOOKBAR, SfxCallMode::SYNCHRON, {&pItem});
+
+ // Show toolbars
+ for ( const OUString& rName : std::as_const(aMandatoryToolbars) )
+ {
+ xLayoutManager->createElement( rName );
+ xLayoutManager->showElement( rName );
+ }
+
+ for ( const OUString& rName : std::as_const(aUserToolbars) )
+ {
+ xLayoutManager->createElement( rName );
+ xLayoutManager->showElement( rName );
+ }
+
+ // Sidebar
+ pViewFrame->ShowChildWindow( SID_SIDEBAR );
+
+ if (comphelper::LibreOfficeKit::isActive())
+ aSidebarMode = "Opened";
+
+ sfx2::sidebar::SidebarController* pSidebar =
+ sfx2::sidebar::SidebarController::GetSidebarControllerForFrame( xFrame );
+ if ( pSidebar )
+ {
+ if ( aSidebarMode == "Arrow" )
+ {
+ pSidebar->FadeOut();
+ }
+ else if ( aSidebarMode == "Tabs" )
+ {
+ pSidebar->FadeIn();
+ pSidebar->RequestOpenDeck();
+ pSidebar->RequestCloseDeck();
+ }
+ else if ( aSidebarMode == "Opened" )
+ {
+ pSidebar->FadeIn();
+ pSidebar->RequestOpenDeck();
+ }
+ }
+
+ // Save settings
+ if ( pViewFrame == SfxViewFrame::Current() )
+ {
+ css::uno::Sequence<OUString> aBackup( comphelper::containerToSequence(aBackupList) );
+
+ for ( const auto& rModeNodeName : aModeNodeNames )
+ {
+ const utl::OConfigurationNode aModeNode( aModesNode.openNode( rModeNodeName ) );
+ if ( !aModeNode.isValid() )
+ continue;
+
+ OUString aCommandArg = comphelper::getString( aModeNode.getNodeValue( "CommandArg" ) );
+
+ if ( aCommandArg == aCurrentMode )
+ {
+ aModeNode.setNodeValue( "UserToolbars", Any( aBackup ) );
+ break;
+ }
+ }
+ aModesNode.commit();
+ }
+ }
+
+ pViewFrame = SfxViewFrame::GetNext(*pViewFrame);
+ }
+
+ bDone = true;
+ break;
+ }
+ case SID_TOOLBAR_MODE_UI:
+ {
+ SfxAbstractDialogFactory* pFact = SfxAbstractDialogFactory::Create();
+ ScopedVclPtr<VclAbstractDialog> pDlg(
+ pFact->CreateToolbarmodeDialog(rReq.GetFrameWeld()));
+ pDlg->Execute();
+ bDone = true;
+ break;
+ }
+ case SID_AVAILABLE_TOOLBARS:
+ {
+ const SfxStringItem* pToolbarName = rReq.GetArg<SfxStringItem>(SID_AVAILABLE_TOOLBARS);
+
+ if ( pToolbarName )
+ {
+ Reference < XDesktop2 > xDesktop = Desktop::create ( ::comphelper::getProcessComponentContext() );
+ Reference< XFrame > xFrame = xDesktop->getActiveFrame();
+
+ Reference< css::beans::XPropertySet > xPropSet( xFrame, UNO_QUERY );
+ Reference< css::frame::XLayoutManager > xLayoutManager;
+ if ( xPropSet.is() )
+ {
+ try
+ {
+ Any aValue = xPropSet->getPropertyValue("LayoutManager");
+ aValue >>= xLayoutManager;
+ }
+ catch ( const css::uno::RuntimeException& )
+ {
+ throw;
+ }
+ catch ( css::uno::Exception& )
+ {
+ }
+ }
+
+ if ( xLayoutManager.is() )
+ {
+ OUString aToolbarName = "private:resource/toolbar/" +
+ pToolbarName->GetValue();
+
+ // Evaluate Parameter
+ bool bShow( !xLayoutManager->isElementVisible( aToolbarName ));
+
+ if ( bShow )
+ {
+ xLayoutManager->createElement( aToolbarName );
+ xLayoutManager->showElement( aToolbarName );
+ }
+ else
+ xLayoutManager->hideElement( aToolbarName );
+ }
+ }
+
+ bDone = true;
+ break;
+ }
+ case SID_MENUBAR:
+ {
+ sfx2::SfxNotebookBar::ToggleMenubar();
+ bDone = true;
+ break;
+ }
+ case SID_DEVELOPMENT_TOOLS_DOCKING_WINDOW:
+ {
+ SfxViewShell* pViewShell = SfxViewShell::Current();
+ SfxViewFrame* pViewFrame = pViewShell->GetViewFrame();
+ auto nID = rReq.GetSlot();
+ pViewFrame->ToggleChildWindow(nID);
+
+ bDone = true;
+ break;
+ }
+ case SID_INSPECT_SELECTED_OBJECT:
+ {
+ SfxViewShell* pViewShell = SfxViewShell::Current();
+ SfxViewFrame* pViewFrame = pViewShell->GetViewFrame();
+
+ pViewFrame->ShowChildWindow(SID_DEVELOPMENT_TOOLS_DOCKING_WINDOW, true);
+
+ SfxChildWindow* pChild = pViewFrame->GetChildWindow(SID_DEVELOPMENT_TOOLS_DOCKING_WINDOW);
+ if (!pChild)
+ return;
+
+ auto pDockingWin = dynamic_cast<DevelopmentToolDockingWindow*>(pChild->GetWindow());
+ if (pDockingWin)
+ {
+ pDockingWin->changeToCurrentSelection();
+ }
+
+ bDone = true;
+ break;
+ }
+ case SID_SAFE_MODE:
+ {
+ SafeModeQueryDialog aDialog(rReq.GetFrameWeld());
+ aDialog.run();
+ break;
+ }
+ case SID_TOOLBAR_LOCK:
+ {
+ SfxViewFrame* pViewFrame = SfxViewFrame::Current();
+ if (pViewFrame)
+ {
+ Reference<XFrame> xCurrentFrame;
+ uno::Reference<uno::XComponentContext> xContext
+ = ::comphelper::getProcessComponentContext();
+ xCurrentFrame = pViewFrame->GetFrame().GetFrameInterface();
+ const Reference<frame::XModuleManager> xModuleManager
+ = frame::ModuleManager::create(xContext);
+ const utl::OConfigurationTreeRoot aAppNode(
+ xContext, "org.openoffice.Office.UI.GlobalSettings/Toolbars/States", true);
+ if (aAppNode.isValid())
+ {
+ bool isLocked = comphelper::getBOOL(aAppNode.getNodeValue("Locked"));
+ aAppNode.setNodeValue("Locked", Any(!isLocked));
+ aAppNode.commit();
+ //TODO: apply immediately w/o restart needed
+ SolarMutexGuard aGuard;
+ svtools::executeRestartDialog(comphelper::getProcessComponentContext(), nullptr,
+ svtools::RESTART_REASON_UI_CHANGE);
+ }
+ }
+ break;
+ }
+ default:
+ break;
+ }
+
+ if ( bDone )
+ rReq.Done();
+}
+
+void SfxApplication::MiscState_Impl(SfxItemSet &rSet)
+{
+ const WhichRangesContainer & pRanges = rSet.GetRanges();
+ DBG_ASSERT(!pRanges.empty(), "Set without range");
+ for ( auto const & pRange : pRanges )
+ {
+ for(sal_uInt16 nWhich = pRange.first; nWhich <= pRange.second; ++nWhich)
+ {
+ switch(nWhich)
+ {
+ case SID_TEMPLATE_ADDRESSBOOKSOURCE:
+ if ( !SvtModuleOptions().IsModuleInstalled(SvtModuleOptions::EModule::DATABASE) )
+ rSet.Put(SfxVisibilityItem(nWhich, false));
+ break;
+ case SID_QUITAPP:
+ {
+ if ( pImpl->nDocModalMode )
+ rSet.DisableItem(nWhich);
+ else
+ rSet.Put(SfxStringItem(nWhich, SfxResId(STR_QUITAPP)));
+ break;
+ }
+
+ case SID_CONFIG:
+ case SID_TOOLBOXOPTIONS:
+ case SID_CONFIGSTATUSBAR:
+ case SID_CONFIGMENU:
+ case SID_CONFIGACCEL:
+ case SID_CONFIGEVENT:
+ {
+ if( officecfg::Office::Common::Misc::DisableUICustomization::get() )
+ rSet.DisableItem(nWhich);
+ break;
+ }
+
+#if HAVE_FEATURE_SCRIPTING
+ case SID_BASICSTOP:
+ if ( !StarBASIC::IsRunning() )
+ rSet.DisableItem(nWhich);
+ break;
+#endif
+
+ case SID_HELPTIPS:
+ {
+ rSet.Put( SfxBoolItem( SID_HELPTIPS, Help::IsQuickHelpEnabled() ) );
+ }
+ break;
+ case SID_HELPBALLOONS:
+ {
+ rSet.Put( SfxBoolItem( SID_HELPBALLOONS, Help::IsBalloonHelpEnabled() ) );
+ }
+ break;
+
+ case SID_EXTENDEDHELP:
+ {
+ }
+ break;
+
+ case SID_CLOSEDOCS:
+ {
+ Reference < XDesktop2 > xDesktop = Desktop::create( ::comphelper::getProcessComponentContext() );
+ Reference< XIndexAccess > xTasks = xDesktop->getFrames();
+ if ( !xTasks.is() || !xTasks->getCount() )
+ rSet.DisableItem(nWhich);
+ break;
+ }
+
+ case SID_SAVEDOCS:
+ {
+ bool bModified = false;
+ for ( SfxObjectShell *pObjSh = SfxObjectShell::GetFirst();
+ pObjSh;
+ pObjSh = SfxObjectShell::GetNext( *pObjSh ) )
+ {
+ if ( pObjSh->IsModified() && !pObjSh->isSaveLocked() )
+ {
+ bModified = true;
+ break;
+ }
+ }
+
+ if ( !bModified )
+ rSet.DisableItem( nWhich );
+ break;
+ }
+
+ case SID_TEMPLATE_MANAGER:
+ {
+ if ( !officecfg::Office::Common::Misc::ExperimentalMode::get() )
+ {
+ rSet.DisableItem( nWhich );
+ rSet.Put( SfxVisibilityItem( nWhich, false ) );
+ }
+ }
+ break;
+
+ case SID_ZOOM_50_PERCENT:
+ case SID_ZOOM_75_PERCENT:
+ case SID_ZOOM_100_PERCENT:
+ case SID_ZOOM_150_PERCENT:
+ case SID_ZOOM_200_PERCENT:
+ case SID_ZOOM_OPTIMAL:
+ case SID_ZOOM_ENTIRE_PAGE:
+ case SID_ZOOM_PAGE_WIDTH:
+ {
+ SfxObjectShell* pCurrentShell = SfxObjectShell::Current();
+
+ const SfxPoolItem *pItem;
+ SfxItemState aState = pCurrentShell ?
+ pCurrentShell->GetDispatcher()->QueryState(SID_ATTR_ZOOM, pItem) : SfxItemState::DISABLED;
+ if ( aState == SfxItemState::DISABLED )
+ rSet.DisableItem( nWhich );
+ }
+ break;
+
+ case SID_MENUBAR:
+ {
+ Reference < XDesktop2 > xDesktop = Desktop::create ( ::comphelper::getProcessComponentContext() );
+ Reference< XFrame > xFrame = xDesktop->getActiveFrame();
+
+ Reference< css::beans::XPropertySet > xPropSet( xFrame, UNO_QUERY );
+ Reference< css::frame::XLayoutManager > xLayoutManager;
+ if ( xPropSet.is() )
+ {
+ try
+ {
+ Any aValue = xPropSet->getPropertyValue("LayoutManager");
+ aValue >>= xLayoutManager;
+ }
+ catch ( const css::uno::RuntimeException& )
+ {
+ throw;
+ }
+ catch ( css::uno::Exception& )
+ {
+ }
+ }
+
+ if ( xLayoutManager.is() )
+ {
+ const bool bState
+ = xLayoutManager->getElement("private:resource/menubar/menubar").is()
+ && xLayoutManager->isElementVisible(
+ "private:resource/menubar/menubar");
+
+ SfxBoolItem aItem( SID_MENUBAR, bState );
+ rSet.Put( aItem );
+ }
+ break;
+ }
+ case SID_SAFE_MODE:
+ {
+ // no restart in safe mode when already in safe mode
+ if ( Application::IsSafeModeEnabled() )
+ rSet.DisableItem( SID_SAFE_MODE );
+ break;
+ }
+ case SID_DEVELOPMENT_TOOLS_DOCKING_WINDOW:
+ {
+ bool bSuccess = false;
+ auto* pViewShell = SfxViewShell::Current();
+ if (pViewShell)
+ {
+ auto* pViewFrame = pViewShell->GetViewFrame();
+ if (pViewFrame && pViewFrame->KnowsChildWindow(nWhich))
+ {
+ rSet.Put(SfxBoolItem(nWhich, pViewFrame->HasChildWindow(nWhich)));
+ bSuccess = true;
+ }
+ }
+
+ if (!bSuccess)
+ rSet.DisableItem(nWhich);
+ }
+ break;
+ case SID_INSPECT_SELECTED_OBJECT:
+ {
+ bool bSuccess = false;
+ auto* pViewShell = SfxViewShell::Current();
+ if (pViewShell)
+ {
+ auto* pViewFrame = pViewShell->GetViewFrame();
+ if (pViewFrame && pViewFrame->KnowsChildWindow(SID_DEVELOPMENT_TOOLS_DOCKING_WINDOW))
+ {
+ bSuccess = true;
+ }
+ }
+ if (!bSuccess)
+ rSet.DisableItem(nWhich);
+ }
+ break;
+ case SID_TOOLBAR_LOCK:
+ {
+ rSet.Put( SfxBoolItem( SID_TOOLBAR_LOCK, ToolBox::AlwaysLocked() ));
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ }
+}
+
+#if HAVE_FEATURE_SCRIPTING
+
+#ifndef DISABLE_DYNLOADING
+
+typedef rtl_uString* (*basicide_choose_macro)(void*, void*, void*, sal_Bool);
+
+#else
+
+extern "C" rtl_uString* basicide_choose_macro(void*, void*, void*, sal_Bool);
+
+#endif
+
+static OUString ChooseMacro(weld::Window* pParent, const Reference<XModel>& rxLimitToDocument, const Reference<XFrame>& xDocFrame, bool bChooseOnly)
+{
+#ifndef DISABLE_DYNLOADING
+ basicide_choose_macro pSymbol = reinterpret_cast<basicide_choose_macro>(sfx2::getBasctlFunction("basicide_choose_macro"));
+#else
+#define pSymbol basicide_choose_macro
+#endif
+
+ // call basicide_choose_macro in basctl
+ rtl_uString* pScriptURL = pSymbol(pParent, rxLimitToDocument.get(), xDocFrame.get(), bChooseOnly);
+ OUString aScriptURL( pScriptURL );
+ rtl_uString_release( pScriptURL );
+ return aScriptURL;
+
+#ifdef DISABLE_DYNLOADING
+#undef pSymbol
+#endif
+}
+
+#endif
+
+namespace
+{
+#if HAVE_FEATURE_SCRIPTING
+ weld::Window* lcl_getDialogParent(const Reference<XFrame>& rxFrame)
+ {
+ Reference<awt::XWindow> xContainerWindow;
+ if (rxFrame.is())
+ xContainerWindow = rxFrame->getContainerWindow();
+ return Application::GetFrameWeld(xContainerWindow);
+ }
+
+ SfxViewFrame* lcl_getBasicIDEViewFrame( SfxObjectShell const * i_pBasicIDE )
+ {
+ SfxViewFrame* pView = SfxViewFrame::GetFirst( i_pBasicIDE );
+ while ( pView )
+ {
+ if ( pView->GetObjectShell()->GetFactory().GetDocumentServiceName() == "com.sun.star.script.BasicIDE" )
+ break;
+ pView = SfxViewFrame::GetNext( *pView, i_pBasicIDE );
+ }
+ return pView;
+ }
+ Reference< XFrame > lcl_findStartModuleFrame( const Reference<XComponentContext> & rxContext )
+ {
+ try
+ {
+ Reference < XDesktop2 > xDesktop = Desktop::create( rxContext );
+ Reference < XIndexAccess > xContainer( xDesktop->getFrames(), UNO_QUERY_THROW );
+
+ Reference< XModuleManager2 > xCheck = ModuleManager::create(rxContext);
+
+ sal_Int32 nCount = xContainer->getCount();
+ for ( sal_Int32 i=0; i<nCount; ++i )
+ {
+ try
+ {
+ Reference < XFrame > xFrame( xContainer->getByIndex(i), UNO_QUERY_THROW );
+ OUString sModule = xCheck->identify( xFrame );
+ if ( sModule == "com.sun.star.frame.StartModule" )
+ return xFrame;
+ }
+ catch( const UnknownModuleException& )
+ {
+ // silence
+ }
+ catch(const Exception&)
+ {
+ // re-throw, caught below
+ throw;
+ }
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("sfx.appl");
+ }
+ return nullptr;
+ }
+#endif // HAVE_FEATURE_SCRIPTING
+}
+
+void SfxApplication::OfaExec_Impl( SfxRequest& rReq )
+{
+ switch ( rReq.GetSlot() )
+ {
+ case SID_OPTIONS_TREEDIALOG:
+ {
+ OUString sPageURL;
+ const SfxStringItem* pURLItem = rReq.GetArg<SfxStringItem>(SID_OPTIONS_PAGEURL);
+ if ( pURLItem )
+ sPageURL = pURLItem->GetValue();
+ Reference <XFrame> xFrame(GetRequestFrame(rReq));
+ SfxAbstractDialogFactory* pFact = SfxAbstractDialogFactory::Create();
+ VclPtr<VclAbstractDialog> pDlg =
+ pFact->CreateFrameDialog(rReq.GetFrameWeld(), xFrame, rReq.GetSlot(), sPageURL );
+ short nRet = pDlg->Execute();
+ pDlg.disposeAndClear();
+ SfxViewFrame* pView = SfxViewFrame::GetFirst();
+ while ( pView )
+ {
+ if (nRet == RET_OK)
+ {
+ SfxObjectShell* pObjSh = pView->GetObjectShell();
+ if (pObjSh)
+ pObjSh->SetConfigOptionsChecked(false);
+ }
+ pView->GetBindings().InvalidateAll(false);
+ pView = SfxViewFrame::GetNext( *pView );
+ }
+ break;
+ }
+
+ case SID_MORE_DICTIONARIES:
+ {
+ uno::Sequence<beans::PropertyValue> aArgs{ comphelper::makePropertyValue(
+ "AdditionsTag", OUString("Dictionary")) };
+ comphelper::dispatchCommand(".uno:AdditionsDialog", aArgs);
+ break;
+ }
+#if HAVE_FEATURE_SCRIPTING
+ case SID_BASICIDE_APPEAR:
+ {
+ SfxViewFrame* pView = lcl_getBasicIDEViewFrame( nullptr );
+ if ( !pView )
+ {
+ SfxObjectShell* pBasicIDE = SfxObjectShell::CreateObject( "com.sun.star.script.BasicIDE" );
+ pBasicIDE->DoInitNew();
+ pBasicIDE->SetModified( false );
+ try
+ {
+ // load the Basic IDE via direct access to the SFX frame loader. A generic loadComponentFromURL
+ // (which could be done via SfxViewFrame::LoadDocumentIntoFrame) is not feasible here, since the Basic IDE
+ // does not really play nice with the framework's concept. For instance, it is a "singleton document",
+ // which conflicts, at the latest, with the framework's concept of loading into _blank frames.
+ // So, since we know that our frame loader can handle it, we skip the generic framework loader
+ // mechanism, and the type detection (which doesn't know about the Basic IDE).
+ Reference< XComponentContext > xContext( ::comphelper::getProcessComponentContext() );
+ Reference< XSynchronousFrameLoader > xLoader(
+ xContext->getServiceManager()->createInstanceWithContext("com.sun.star.comp.office.FrameLoader", xContext),
+ UNO_QUERY_THROW );
+ ::comphelper::NamedValueCollection aLoadArgs;
+ aLoadArgs.put( "Model", pBasicIDE->GetModel() );
+ aLoadArgs.put( "URL", OUString( "private:factory/sbasic" ) );
+
+ Reference< XFrame > xTargetFrame( lcl_findStartModuleFrame( xContext ) );
+ if ( !xTargetFrame.is() )
+ xTargetFrame = SfxFrame::CreateBlankFrame();
+ ENSURE_OR_THROW( xTargetFrame.is(), "could not obtain a frameto load the Basic IDE into!" );
+
+ xLoader->load( aLoadArgs.getPropertyValues(), xTargetFrame );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("sfx.appl");
+ }
+
+ pView = lcl_getBasicIDEViewFrame( pBasicIDE );
+ if ( pView )
+ pView->SetName( "BASIC:1" );
+ }
+
+ if ( pView )
+ pView->GetFrame().Appear();
+
+ const SfxItemSet* pArgs = rReq.GetArgs();
+ if ( pArgs && pView )
+ {
+ SfxViewShell* pViewShell = pView->GetViewShell();
+ SfxObjectShell* pObjShell = pView->GetObjectShell();
+ if ( pViewShell && pObjShell )
+ {
+ SfxRequest aReq( SID_BASICIDE_SHOWWINDOW, SfxCallMode::SYNCHRON, pObjShell->GetPool() );
+ aReq.SetArgs( *pArgs );
+ pViewShell->ExecuteSlot( aReq );
+ }
+ }
+
+ rReq.Done();
+ }
+ break;
+
+ case SID_BASICCHOOSER:
+ {
+ const SfxItemSet* pArgs = rReq.GetArgs();
+ const SfxBoolItem* pItem;
+ bool bChooseOnly = false;
+ Reference< XModel > xLimitToModel;
+ if(pArgs && (pItem = pArgs->GetItemIfSet(SID_RECORDMACRO, false)) )
+ {
+ bool bRecord = pItem->GetValue();
+ if ( bRecord )
+ {
+ // !Hack
+ bChooseOnly = false;
+ SfxObjectShell* pCurrentShell = SfxObjectShell::Current();
+ OSL_ENSURE( pCurrentShell, "macro recording outside an SFX document?" );
+ if ( pCurrentShell )
+ xLimitToModel = pCurrentShell->GetModel();
+ }
+ }
+
+ Reference <XFrame> xFrame(GetRequestFrame(rReq));
+ rReq.SetReturnValue(SfxStringItem(rReq.GetSlot(), ChooseMacro(rReq.GetFrameWeld(), xLimitToModel, xFrame, bChooseOnly)));
+ rReq.Done();
+ }
+ break;
+
+ case SID_MACROORGANIZER:
+ {
+ SAL_INFO("sfx.appl", "handling SID_MACROORGANIZER");
+ const SfxItemSet* pArgs = rReq.GetArgs();
+ const SfxUInt16Item* pItem;
+ sal_Int16 nTabId = 0;
+ if(pArgs && (pItem = pArgs->GetItemIfSet(SID_MACROORGANIZER, false) ))
+ {
+ nTabId = pItem->GetValue();
+ }
+
+ SfxApplication::MacroOrganizer(rReq.GetFrameWeld(), nTabId);
+ rReq.Done();
+ }
+ break;
+
+ case SID_RUNMACRO:
+ {
+ SfxAbstractDialogFactory* pFact = SfxAbstractDialogFactory::Create();
+ SAL_INFO("sfx.appl", "SfxApplication::OfaExec_Impl: case ScriptOrg");
+
+ Reference <XFrame> xFrame(GetRequestFrame(rReq));
+ if ( !xFrame.is() )
+ {
+ const SfxViewFrame* pViewFrame = SfxViewFrame::Current();
+ if ( pViewFrame )
+ xFrame = pViewFrame->GetFrame().GetFrameInterface();
+ }
+
+ do // artificial loop for flow control
+ {
+ VclPtr<AbstractScriptSelectorDialog> pDlg(pFact->CreateScriptSelectorDialog(lcl_getDialogParent(xFrame), xFrame));
+ OSL_ENSURE( pDlg, "SfxApplication::OfaExec_Impl( SID_RUNMACRO ): no dialog!" );
+ if ( !pDlg )
+ break;
+ pDlg->SetRunLabel();
+
+ pDlg->StartExecuteAsync([pDlg, xFrame](sal_Int32 nDialogResult) {
+ if ( !nDialogResult )
+ {
+ pDlg->disposeOnce();
+ return;
+ }
+
+ Sequence< Any > args;
+ Sequence< sal_Int16 > outIndex;
+ Sequence< Any > outArgs;
+ Any ret;
+
+ Reference< XInterface > xScriptContext;
+
+ Reference< XController > xController;
+ if ( xFrame.is() )
+ xController = xFrame->getController();
+ if ( xController.is() )
+ xScriptContext = xController->getModel();
+ if ( !xScriptContext.is() )
+ xScriptContext = xController;
+
+ SfxObjectShell::CallXScript( xScriptContext, pDlg->GetScriptURL(), args, ret, outIndex, outArgs );
+ pDlg->disposeOnce();
+ });
+ }
+ while ( false );
+ rReq.Done();
+ }
+ break;
+
+ case SID_SCRIPTORGANIZER:
+ {
+ SfxAbstractDialogFactory* pFact = SfxAbstractDialogFactory::Create();
+ SAL_INFO("sfx.appl", "SfxApplication::OfaExec_Impl: case ScriptOrg");
+ const SfxItemSet* pArgs = rReq.GetArgs();
+ const SfxScriptOrganizerItem* pItem;
+ OUString aLanguage;
+ if(pArgs && (pItem = pArgs->GetItemIfSet(SID_SCRIPTORGANIZER, false) ))
+ {
+ aLanguage = pItem->getLanguage();
+ }
+
+ OUString aLang( aLanguage );
+ SAL_INFO("sfx.appl", "SfxApplication::OfaExec_Impl: about to create dialog for: " << aLang);
+ ScopedVclPtr<VclAbstractDialog> pDlg(pFact->CreateSvxScriptOrgDialog(rReq.GetFrameWeld(), aLanguage));
+ if( pDlg )
+ {
+ pDlg->Execute();
+ }
+ else
+ {
+ SAL_WARN("sfx.appl", "no dialog!!!");
+ }
+ rReq.Done();
+ }
+ break;
+#endif // HAVE_FEATURE_SCRIPTING
+
+ case SID_OFFICE_CHECK_PLZ:
+ {
+ bool bRet = false;
+ const SfxStringItem* pStringItem = rReq.GetArg<SfxStringItem>(rReq.GetSlot());
+
+ if ( pStringItem )
+ {
+ bRet = true /*!!!SfxIniManager::CheckPLZ( aPLZ )*/;
+ }
+#if HAVE_FEATURE_SCRIPTING
+ else
+ SbxBase::SetError( ERRCODE_BASIC_WRONG_ARGS );
+#endif
+ rReq.SetReturnValue( SfxBoolItem( rReq.GetSlot(), bRet ) );
+ }
+ break;
+
+ case SID_AUTO_CORRECT_DLG:
+ {
+ SfxAbstractDialogFactory* pFact = SfxAbstractDialogFactory::Create();
+ SfxItemSetFixed<SID_AUTO_CORRECT_DLG, SID_AUTO_CORRECT_DLG> aSet(GetPool());
+ const SfxPoolItem* pItem=nullptr;
+ const SfxItemSet* pSet = rReq.GetArgs();
+ if ( pSet && pSet->GetItemState( SID_AUTO_CORRECT_DLG, false, &pItem ) == SfxItemState::SET )
+ aSet.Put( *pItem );
+
+ ScopedVclPtr<SfxAbstractTabDialog> pDlg(pFact->CreateAutoCorrTabDialog(rReq.GetFrameWeld(), &aSet));
+ pDlg->Execute();
+
+ break;
+ }
+
+ case SID_NEWSD :
+ {
+ SvtModuleOptions aModuleOpt;
+ if ( !aModuleOpt.IsImpress() )
+ {
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(rReq.GetFrameWeld(),
+ VclMessageType::Warning, VclButtonsType::Ok,
+ SfxResId(STR_MODULENOTINSTALLED)));
+ xBox->run();
+ return;
+ }
+
+ Reference< uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext();
+ Reference< frame::XDispatchProvider > xProv = drawing::ModuleDispatcher::create( xContext );
+
+ OUString aCmd = OUString::createFromAscii( GetInterface()->GetSlot( rReq.GetSlot() )->GetUnoName() );
+ Reference< frame::XDispatchHelper > xHelper( frame::DispatchHelper::create(xContext) );
+ Sequence < beans::PropertyValue > aSeq;
+ if ( rReq.GetArgs() )
+ TransformItems( rReq.GetSlot(), *rReq.GetArgs(), aSeq );
+ Any aResult = xHelper->executeDispatch( xProv, aCmd, OUString(), 0, aSeq );
+ frame::DispatchResultEvent aEvent;
+ bool bSuccess = (aResult >>= aEvent) &&
+ (aEvent.State == frame::DispatchResultState::SUCCESS);
+ rReq.SetReturnValue( SfxBoolItem( rReq.GetSlot(), bSuccess ) );
+ }
+ break;
+
+ case FN_LABEL :
+ case FN_BUSINESS_CARD :
+ case FN_XFORMS_INIT :
+ {
+ Reference< uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext();
+ Reference< frame::XDispatchProvider > xProv = text::ModuleDispatcher::create( xContext );
+
+ OUString aCmd = OUString::createFromAscii( GetInterface()->GetSlot( rReq.GetSlot() )->GetUnoName() );
+ Reference< frame::XDispatchHelper > xHelper( frame::DispatchHelper::create(xContext) );
+ Sequence < beans::PropertyValue > aSeq;
+ if ( rReq.GetArgs() )
+ TransformItems( rReq.GetSlot(), *rReq.GetArgs(), aSeq );
+ Any aResult = xHelper->executeDispatch( xProv, aCmd, OUString(), 0, aSeq );
+ frame::DispatchResultEvent aEvent;
+ bool bSuccess = (aResult >>= aEvent) &&
+ (aEvent.State == frame::DispatchResultState::SUCCESS);
+ rReq.SetReturnValue( SfxBoolItem( rReq.GetSlot(), bSuccess ) );
+ }
+ break;
+
+ case SID_ADDRESS_DATA_SOURCE:
+ {
+ try
+ {
+ Reference< uno::XComponentContext > xORB = ::comphelper::getProcessComponentContext();
+ Reference< ui::dialogs::XExecutableDialog > xDialog = ui::dialogs::AddressBookSourcePilot::createWithParent(xORB, nullptr);
+ xDialog->execute();
+ }
+ catch(const css::uno::Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("sfx.appl");
+ }
+ }
+ break;
+
+ case SID_COMP_BIBLIOGRAPHY:
+ lcl_tryLoadBibliography();
+ break;
+ }
+}
+
+void SfxApplication::OfaState_Impl(SfxItemSet &rSet)
+{
+ SvtModuleOptions aModuleOpt;
+
+ if( !aModuleOpt.IsWriter())
+ {
+ rSet.DisableItem( FN_LABEL );
+ rSet.DisableItem( FN_BUSINESS_CARD );
+ rSet.DisableItem( FN_XFORMS_INIT );
+ }
+ if ( comphelper::LibreOfficeKit::isActive() )
+ rSet.DisableItem( SID_AUTO_CORRECT_DLG );
+
+ bool bMacrosDisabled
+ = officecfg::Office::Common::Security::Scripting::DisableMacrosExecution::get();
+ if (bMacrosDisabled)
+ {
+ rSet.DisableItem(SID_RUNMACRO);
+ rSet.DisableItem(SID_MACROORGANIZER);
+ rSet.DisableItem(SID_SCRIPTORGANIZER);
+ rSet.DisableItem(SID_BASICIDE_APPEAR);
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/appl/appuno.cxx b/sfx2/source/appl/appuno.cxx
new file mode 100644
index 000000000..26f0e336a
--- /dev/null
+++ b/sfx2/source/appl/appuno.cxx
@@ -0,0 +1,1842 @@
+/* -*- 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 <fltoptint.hxx>
+#include <sfx2/brokenpackageint.hxx>
+#include <sfx2/docfile.hxx>
+#include <sfx2/frame.hxx>
+#include <sfx2/msg.hxx>
+#include <sfx2/msgpool.hxx>
+#include <sfx2/sfxsids.hrc>
+#include <sfx2/sfxuno.hxx>
+#include <sfxslots.hxx>
+
+#include <sal/config.h>
+#include <sal/log.hxx>
+#include <comphelper/interaction.hxx>
+#include <osl/diagnose.h>
+#include <svl/eitem.hxx>
+#include <svl/intitem.hxx>
+#include <svl/itempool.hxx>
+#include <svl/slstitm.hxx>
+#include <svl/stritem.hxx>
+#include <tools/debug.hxx>
+#include <cppuhelper/implbase.hxx>
+
+#include <com/sun/star/document/BrokenPackageRequest.hpp>
+#include <com/sun/star/document/FilterOptionsRequest.hpp>
+#include <com/sun/star/frame/XFrame.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/io/XInputStream.hpp>
+#include <com/sun/star/io/XOutputStream.hpp>
+#include <com/sun/star/task/XInteractionHandler.hpp>
+#include <com/sun/star/task/XStatusIndicator.hpp>
+#include <com/sun/star/ucb/XContent.hpp>
+
+#include <memory>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::ucb;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::frame;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::io;
+
+// needs to be converted to a better data structure
+SfxFormalArgument const aFormalArgs[] = {
+ { reinterpret_cast<SfxType*>(&aSfxStringItem_Impl), "SuggestedSaveAsName", SID_DEFAULTFILENAME },
+ { reinterpret_cast<SfxType*>(&aSfxStringItem_Impl), "SuggestedSaveAsDir", SID_DEFAULTFILEPATH },
+ { reinterpret_cast<SfxType*>(&aSfxStringItem_Impl), "VersionAuthor", SID_DOCINFO_AUTHOR },
+ { reinterpret_cast<SfxType*>(&aSfxStringItem_Impl), "VersionComment", SID_DOCINFO_COMMENTS },
+ { reinterpret_cast<SfxType*>(&aSfxBoolItem_Impl), "DontTerminateEdit", FN_PARAM_1 },
+ { reinterpret_cast<SfxType*>(&aSfxBoolItem_Impl), "VersionMajor", SID_DOCINFO_MAJOR },
+ { reinterpret_cast<SfxType*>(&aSfxStringItem_Impl), "FilterOptions", SID_FILE_FILTEROPTIONS },
+ { reinterpret_cast<SfxType*>(&aSfxStringItem_Impl), "FilterName", SID_FILTER_NAME },
+ { reinterpret_cast<SfxType*>(&aSfxStringItem_Impl), "Margin1", SID_RULER_MARGIN1 },
+ { reinterpret_cast<SfxType*>(&aSfxStringItem_Impl), "Margin2", SID_RULER_MARGIN2 },
+// { reinterpret_cast<SfxType*>(&aSfxStringItem_Impl), "FileName", SID_FILE_NAME },
+ { reinterpret_cast<SfxType*>(&aSfxStringItem_Impl), "URL", SID_FILE_NAME },
+ { reinterpret_cast<SfxType*>(&aSfxStringItem_Impl), "OpenFlags", SID_OPTIONS },
+ { reinterpret_cast<SfxType*>(&aSfxBoolItem_Impl), "Overwrite", SID_OVERWRITE },
+ { reinterpret_cast<SfxType*>(&aSfxStringItem_Impl), "Password", SID_PASSWORD },
+ { reinterpret_cast<SfxType*>(&aSfxBoolItem_Impl), "PasswordInteraction", SID_PASSWORDINTERACTION },
+ { reinterpret_cast<SfxType*>(&aSfxStringItem_Impl), "Referer", SID_REFERER },
+ { reinterpret_cast<SfxType*>(&aSfxBoolItem_Impl), "SaveTo", SID_SAVETO },
+ { reinterpret_cast<SfxType*>(&aSfxStringItem_Impl), "TemplateName", SID_TEMPLATE_NAME },
+ { reinterpret_cast<SfxType*>(&aSfxStringItem_Impl), "TemplateRegion", SID_TEMPLATE_REGIONNAME },
+// { reinterpret_cast<SfxType*>(&aSfxStringItem_Impl), "Region", SID_TEMPLATE_REGIONNAME },
+// { reinterpret_cast<SfxType*>(&aSfxStringItem_Impl), "Name", SID_TEMPLATE_NAME },
+ { reinterpret_cast<SfxType*>(&aSfxBoolItem_Impl), "Unpacked", SID_UNPACK },
+ { reinterpret_cast<SfxType*>(&aSfxInt16Item_Impl), "Version", SID_VERSION },
+ { reinterpret_cast<SfxType*>(&aSfxBoolItem_Impl), "SaveACopy", SID_SAVEACOPYITEM },
+ { reinterpret_cast<SfxType*>(&aSfxBoolItem_Impl), "NoFileSync", SID_NO_FILE_SYNC },
+ { reinterpret_cast<SfxType*>(&aSfxBoolItem_Impl), "NoThumbnail", SID_NO_THUMBNAIL },
+ { reinterpret_cast<SfxType*>(&aSfxBoolItem_Impl), "NoEmbDataSet", SID_NO_EMBEDDED_DS },
+ { reinterpret_cast<SfxType*>(&aSfxBoolItem_Impl), "IsRedactMode", SID_IS_REDACT_MODE },
+ { reinterpret_cast<SfxType*>(&aSfxStringItem_Impl), "RedactionStyle", SID_REDACTION_STYLE },
+ { reinterpret_cast<SfxType*>(&aSfxStringItem_Impl), "AdditionsTag", FN_PARAM_ADDITIONS_TAG },
+};
+
+sal_uInt16 const nMediaArgsCount = SAL_N_ELEMENTS(aFormalArgs);
+
+constexpr OUStringLiteral sTemplateRegionName = u"TemplateRegionName";
+constexpr OUStringLiteral sTemplateName = u"TemplateName";
+constexpr OUStringLiteral sAsTemplate = u"AsTemplate";
+constexpr OUStringLiteral sOpenNewView = u"OpenNewView";
+constexpr OUStringLiteral sViewId = u"ViewId";
+constexpr OUStringLiteral sPluginMode = u"PluginMode";
+constexpr OUStringLiteral sReadOnly = u"ReadOnly";
+constexpr OUStringLiteral sDdeReconnect = u"DDEReconnect";
+constexpr OUStringLiteral sStartPresentation = u"StartPresentation";
+constexpr OUStringLiteral sFrameName = u"FrameName";
+constexpr OUStringLiteral sMediaType = u"MediaType";
+constexpr OUStringLiteral sPostData = u"PostData";
+constexpr OUStringLiteral sCharacterSet = u"CharacterSet";
+constexpr OUStringLiteral sInputStream = u"InputStream";
+constexpr OUStringLiteral sStream = u"Stream";
+constexpr OUStringLiteral sOutputStream = u"OutputStream";
+constexpr OUStringLiteral sHidden = u"Hidden";
+constexpr OUStringLiteral sPreview = u"Preview";
+constexpr OUStringLiteral sViewOnly = u"ViewOnly";
+constexpr OUStringLiteral sDontEdit = u"DontEdit";
+constexpr OUStringLiteral sSilent = u"Silent";
+constexpr OUStringLiteral sJumpMark = u"JumpMark";
+constexpr OUStringLiteral sSalvagedFile = u"SalvagedFile";
+constexpr OUStringLiteral sStatusInd = u"StatusIndicator";
+constexpr OUStringLiteral sModel = u"Model";
+constexpr OUStringLiteral sFrame = u"Frame";
+constexpr OUStringLiteral sViewData = u"ViewData";
+constexpr OUStringLiteral sFilterData = u"FilterData";
+constexpr OUStringLiteral sSelectionOnly = u"SelectionOnly";
+constexpr OUStringLiteral sMacroExecMode = u"MacroExecutionMode";
+constexpr OUStringLiteral sUpdateDocMode = u"UpdateDocMode";
+constexpr OUStringLiteral sMinimized = u"Minimized";
+constexpr OUStringLiteral sInteractionHdl = u"InteractionHandler";
+constexpr OUStringLiteral sUCBContent = u"UCBContent";
+constexpr OUStringLiteral sRepairPackage = u"RepairPackage";
+constexpr OUStringLiteral sDocumentTitle = u"DocumentTitle";
+constexpr OUStringLiteral sComponentData = u"ComponentData";
+constexpr OUStringLiteral sComponentContext = u"ComponentContext";
+constexpr OUStringLiteral sDocumentBaseURL = u"DocumentBaseURL";
+constexpr OUStringLiteral sHierarchicalDocumentName = u"HierarchicalDocumentName";
+constexpr OUStringLiteral sCopyStreamIfPossible = u"CopyStreamIfPossible";
+constexpr OUStringLiteral sNoAutoSave = u"NoAutoSave";
+constexpr OUStringLiteral sFolderName = u"FolderName";
+constexpr OUStringLiteral sUseSystemDialog = u"UseSystemDialog";
+constexpr OUStringLiteral sStandardDir = u"StandardDir";
+constexpr OUStringLiteral sDenyList = u"DenyList";
+constexpr OUStringLiteral sModifyPasswordInfo = u"ModifyPasswordInfo";
+constexpr OUStringLiteral sSuggestedSaveAsDir = u"SuggestedSaveAsDir";
+constexpr OUStringLiteral sSuggestedSaveAsName = u"SuggestedSaveAsName";
+constexpr OUStringLiteral sEncryptionData = u"EncryptionData";
+constexpr OUStringLiteral sFailOnWarning = u"FailOnWarning";
+constexpr OUStringLiteral sDocumentService = u"DocumentService";
+constexpr OUStringLiteral sFilterProvider = u"FilterProvider";
+constexpr OUStringLiteral sImageFilter = u"ImageFilter";
+constexpr OUStringLiteral sLockContentExtraction = u"LockContentExtraction";
+constexpr OUStringLiteral sLockExport = u"LockExport";
+constexpr OUStringLiteral sLockPrint = u"LockPrint";
+constexpr OUStringLiteral sLockSave = u"LockSave";
+constexpr OUStringLiteral sLockEditDoc = u"LockEditDoc";
+constexpr OUStringLiteral sReplaceable = u"Replaceable";
+
+static bool isMediaDescriptor( sal_uInt16 nSlotId )
+{
+ return ( nSlotId == SID_OPENDOC || nSlotId == SID_EXPORTDOC ||
+ nSlotId == SID_SAVEASDOC || nSlotId == SID_SAVEDOC ||
+ nSlotId == SID_SAVETO || nSlotId == SID_SAVEACOPY ||
+ nSlotId == SID_EXPORTDOCASPDF || nSlotId == SID_DIRECTEXPORTDOCASPDF ||
+ nSlotId == SID_EXPORTDOCASEPUB || nSlotId == SID_DIRECTEXPORTDOCASEPUB ||
+ nSlotId == SID_REDACTDOC || nSlotId == SID_AUTOREDACTDOC ||
+ nSlotId == SID_SAVEACOPYITEM);
+}
+
+void TransformParameters( sal_uInt16 nSlotId, const uno::Sequence<beans::PropertyValue>& rArgs, SfxAllItemSet& rSet, const SfxSlot* pSlot )
+{
+ if ( !pSlot )
+ pSlot = SFX_SLOTPOOL().GetSlot( nSlotId );
+
+ if ( !pSlot )
+ return;
+
+ if ( nSlotId == SID_OPENURL )
+ nSlotId = SID_OPENDOC;
+
+ const sal_Int32 nCount = rArgs.getLength();
+ if ( !nCount )
+ return;
+
+ const beans::PropertyValue* pPropsVal = rArgs.getConstArray();
+ if ( !pSlot->IsMode(SfxSlotMode::METHOD) )
+ {
+ // slot is a property
+ const SfxType* pType = pSlot->GetType();
+ std::unique_ptr<SfxPoolItem> pItem(pType->CreateItem());
+
+ if ( !pItem )
+ {
+ SAL_WARN( "sfx", "No creator method for item: " << nSlotId );
+ return;
+ }
+
+ sal_uInt16 nWhich = rSet.GetPool()->GetWhich(nSlotId);
+ bool bConvertTwips = ( rSet.GetPool()->GetMetric( nWhich ) == MapUnit::MapTwip );
+ pItem->SetWhich( nWhich );
+ sal_uInt16 nSubCount = pType->nAttribs;
+
+ const beans::PropertyValue& rProp = pPropsVal[0];
+ const OUString& rName = rProp.Name;
+ if ( nCount == 1 && rName == OUString( pSlot->pUnoName, strlen( pSlot->pUnoName ), RTL_TEXTENCODING_UTF8 ) )
+ {
+ // there is only one parameter and its name matches the name of the property,
+ // so it's either a simple property or a complex property in one single UNO struct
+ if( pItem->PutValue( rProp.Value, bConvertTwips ? CONVERT_TWIPS : 0 ) )
+ // only use successfully converted items
+ rSet.Put( std::move(pItem) );
+ else
+ {
+ SAL_WARN( "sfx", "Property not convertible: " << pSlot->pUnoName );
+ }
+ }
+#ifdef DBG_UTIL
+ else if ( nSubCount == 0 )
+ {
+ // for a simple property there can be only one parameter and its name *must* match
+ SAL_WARN("sfx.appl", "Property name does not match: " << rName);
+ }
+#endif
+ else
+ {
+ // there is more than one parameter and the property is a complex one
+#ifdef DBG_UTIL
+ // if the dispatch API is used for UI purposes or from the testtool,
+ // it is possible to skip some or all arguments,
+ // but it indicates an error for macro recording;
+ // so this should be notified as a warning only
+ if ( nCount != nSubCount )
+ {
+ SAL_INFO("sfx.appl", "MacroPlayer: wrong number of parameters for slot: " << nSlotId );
+ }
+#endif
+ // complex property; collect sub items from the parameter set and reconstruct complex item
+ sal_uInt16 nFound=0;
+ for ( const beans::PropertyValue& rPropValue : rArgs )
+ {
+ sal_uInt16 nSub;
+ for ( nSub=0; nSub<nSubCount; nSub++ )
+ {
+ // search sub item by name
+ OString aStr = OString::Concat(pSlot->pUnoName) + "." + pType->aAttrib[nSub].pName;
+ if ( rPropValue.Name.equalsAsciiL(aStr.getStr(), aStr.getLength()) )
+ {
+ sal_uInt8 nSubId = static_cast<sal_uInt8>(static_cast<sal_Int8>(pType->aAttrib[nSub].nAID));
+ if ( bConvertTwips )
+ nSubId |= CONVERT_TWIPS;
+ if ( pItem->PutValue( rPropValue.Value, nSubId ) )
+ nFound++;
+ else
+ {
+ SAL_WARN( "sfx.appl", "Property not convertible: " << pSlot->pUnoName);
+ }
+ break;
+ }
+ }
+
+ // there was a parameter with a name that didn't match to any of the members
+ SAL_WARN_IF( nSub >= nSubCount, "sfx.appl", "Property name does not match: " << rPropValue.Name );
+ }
+
+ // at least one part of the complex item must be present; other parts can have default values
+ if ( nFound > 0 )
+ rSet.Put( std::move(pItem) );
+ }
+
+ return;
+ }
+
+#ifdef DBG_UTIL
+ // detect parameters that don't match to any formal argument or one of its members
+ sal_Int32 nFoundArgs = 0;
+#endif
+ // slot is a method
+ bool bIsMediaDescriptor = isMediaDescriptor( nSlotId );
+ sal_uInt16 nMaxArgs = bIsMediaDescriptor ? nMediaArgsCount : pSlot->nArgDefCount;
+ for ( sal_uInt16 nArgs=0; nArgs<nMaxArgs; nArgs++ )
+ {
+ const SfxFormalArgument &rArg = bIsMediaDescriptor ? aFormalArgs[nArgs] : pSlot->GetFormalArgument( nArgs );
+ std::unique_ptr<SfxPoolItem> pItem(rArg.CreateItem());
+ if ( !pItem )
+ {
+ SAL_WARN( "sfx", "No creator method for argument: " << rArg.pName );
+ return;
+ }
+
+ sal_uInt16 nWhich = rSet.GetPool()->GetWhich(rArg.nSlotId);
+ bool bConvertTwips = ( rSet.GetPool()->GetMetric( nWhich ) == MapUnit::MapTwip );
+ pItem->SetWhich( nWhich );
+ const SfxType* pType = rArg.pType;
+ sal_uInt16 nSubCount = pType->nAttribs;
+ if ( nSubCount == 0 )
+ {
+ // "simple" (base type) argument
+ auto pProp = std::find_if(rArgs.begin(), rArgs.end(),
+ [&rArg](const beans::PropertyValue& rProp) { return rProp.Name.equalsAscii(rArg.pName); });
+ if (pProp != rArgs.end())
+ {
+#ifdef DBG_UTIL
+ ++nFoundArgs;
+#endif
+ if( pItem->PutValue( pProp->Value, 0 ) )
+ // only use successfully converted items
+ rSet.Put( std::move(pItem) );
+ else
+ {
+ SAL_WARN( "sfx", "Property not convertible: " << rArg.pName );
+ }
+ }
+ }
+ else
+ {
+ // complex argument, could be passed in one struct
+ bool bAsWholeItem = false;
+ for ( const beans::PropertyValue& rProp : rArgs )
+ {
+ const OUString& rName = rProp.Name;
+ if ( rName == OUString(rArg.pName, strlen(rArg.pName), RTL_TEXTENCODING_UTF8) )
+ {
+ bAsWholeItem = true;
+#ifdef DBG_UTIL
+ ++nFoundArgs;
+#endif
+ if( pItem->PutValue( rProp.Value, 0 ) )
+ // only use successfully converted items
+ rSet.Put( std::move(pItem) );
+ else
+ {
+ SAL_WARN( "sfx", "Property not convertible: " << rArg.pName );
+ }
+ }
+ }
+
+ if ( !bAsWholeItem )
+ {
+ // complex argument; collect sub items from argument array and reconstruct complex item
+ // only put item if at least one member was found and had the correct type
+ // (is this a good idea?! Should we ask for *all* members?)
+ bool bRet = false;
+ for ( const beans::PropertyValue& rProp : rArgs )
+ {
+ for ( sal_uInt16 nSub=0; nSub<nSubCount; nSub++ )
+ {
+ // search sub item by name
+ OString aStr = OString::Concat(rArg.pName) + "." + pType->aAttrib[nSub].pName;
+ if ( rProp.Name.equalsAsciiL(aStr.getStr(), aStr.getLength()) )
+ {
+ // at least one member found ...
+ bRet = true;
+#ifdef DBG_UTIL
+ ++nFoundArgs;
+#endif
+ sal_uInt8 nSubId = static_cast<sal_uInt8>(static_cast<sal_Int8>(pType->aAttrib[nSub].nAID));
+ if ( bConvertTwips )
+ nSubId |= CONVERT_TWIPS;
+ if (!pItem->PutValue( rProp.Value, nSubId ) )
+ {
+ // ... but it was not convertible
+ bRet = false;
+ SAL_WARN( "sfx", "Property not convertible: " << rArg.pName );
+ }
+
+ break;
+ }
+ }
+ }
+
+ if ( bRet )
+ // only use successfully converted items
+ rSet.Put( std::move(pItem) );
+
+ }
+ }
+ }
+
+ // special additional parameters for some slots not seen in the slot definitions
+ // Some of these slots are not considered to be used for macro recording, because they shouldn't be recorded as slots,
+ // but as dispatching or factory or arbitrary URLs to the frame
+ // Some also can use additional arguments that are not recordable (will be changed later,
+ // f.e. "SaveAs" shouldn't support parameters not in the slot definition!)
+ if ( nSlotId == SID_NEWWINDOW )
+ {
+ for ( const beans::PropertyValue& rProp : rArgs )
+ {
+ const OUString& rName = rProp.Name;
+ if ( rName == sFrame )
+ {
+ Reference< XFrame > xFrame;
+ OSL_VERIFY( rProp.Value >>= xFrame );
+ rSet.Put( SfxUnoFrameItem( SID_FILLFRAME, xFrame ) );
+ }
+ else
+ if ( rName == sHidden )
+ {
+ bool bVal = false;
+ if (rProp.Value >>= bVal)
+ rSet.Put( SfxBoolItem( SID_HIDDEN, bVal ) );
+ }
+ }
+ }
+ else if ( bIsMediaDescriptor )
+ {
+ for ( const beans::PropertyValue& rProp : rArgs )
+ {
+#ifdef DBG_UTIL
+ ++nFoundArgs;
+#endif
+ const OUString& aName = rProp.Name;
+ if ( aName == sModel )
+ rSet.Put( SfxUnoAnyItem( SID_DOCUMENT, rProp.Value ) );
+ else if ( aName == sComponentData )
+ {
+ rSet.Put( SfxUnoAnyItem( SID_COMPONENTDATA, rProp.Value ) );
+ }
+ else if ( aName == sComponentContext )
+ {
+ rSet.Put( SfxUnoAnyItem( SID_COMPONENTCONTEXT, rProp.Value ) );
+ }
+ else if ( aName == sStatusInd )
+ {
+ Reference<task::XStatusIndicator> xVal;
+ bool bOK = (rProp.Value >>= xVal);
+ DBG_ASSERT( bOK, "invalid type for StatusIndicator" );
+ if (bOK && xVal.is())
+ rSet.Put( SfxUnoAnyItem( SID_PROGRESS_STATUSBAR_CONTROL, rProp.Value ) );
+ }
+ else if ( aName == sInteractionHdl )
+ {
+ Reference<task::XInteractionHandler> xVal;
+ bool bOK = (rProp.Value >>= xVal);
+ DBG_ASSERT( bOK, "invalid type for InteractionHandler" );
+ if (bOK && xVal.is())
+ rSet.Put( SfxUnoAnyItem( SID_INTERACTIONHANDLER, rProp.Value ) );
+ }
+ else if ( aName == sViewData )
+ rSet.Put( SfxUnoAnyItem( SID_VIEW_DATA, rProp.Value ) );
+ else if ( aName == sFilterData )
+ rSet.Put( SfxUnoAnyItem( SID_FILTER_DATA, rProp.Value ) );
+ else if ( aName == sInputStream )
+ {
+ Reference< XInputStream > xVal;
+ bool bOK = ((rProp.Value >>= xVal) && xVal.is());
+ DBG_ASSERT( bOK, "invalid type for InputStream" );
+ if (bOK)
+ rSet.Put( SfxUnoAnyItem( SID_INPUTSTREAM, rProp.Value ) );
+ }
+ else if ( aName == sStream )
+ {
+ Reference< XInputStream > xVal;
+ bool bOK = ((rProp.Value >>= xVal) && xVal.is());
+ DBG_ASSERT( bOK, "invalid type for Stream" );
+ if (bOK)
+ rSet.Put( SfxUnoAnyItem( SID_STREAM, rProp.Value ) );
+ }
+ else if ( aName == sUCBContent )
+ {
+ Reference< XContent > xVal;
+ bool bOK = ((rProp.Value >>= xVal) && xVal.is());
+ DBG_ASSERT( bOK, "invalid type for UCBContent" );
+ if (bOK)
+ rSet.Put( SfxUnoAnyItem( SID_CONTENT, rProp.Value ) );
+ }
+ else if ( aName == sOutputStream )
+ {
+ Reference< XOutputStream > xVal;
+ bool bOK = ((rProp.Value >>= xVal) && xVal.is());
+ DBG_ASSERT( bOK, "invalid type for OutputStream" );
+ if (bOK)
+ rSet.Put( SfxUnoAnyItem( SID_OUTPUTSTREAM, rProp.Value ) );
+ }
+ else if ( aName == sPostData )
+ {
+ Reference< XInputStream > xVal;
+ bool bOK = (rProp.Value >>= xVal);
+ DBG_ASSERT( bOK, "invalid type for PostData" );
+ if (bOK)
+ rSet.Put( SfxUnoAnyItem( SID_POSTDATA, rProp.Value ) );
+ }
+ else if ( aName == sFrame )
+ {
+ Reference< XFrame > xFrame;
+ bool bOK = (rProp.Value >>= xFrame);
+ DBG_ASSERT( bOK, "invalid type for Frame" );
+ if (bOK)
+ rSet.Put( SfxUnoFrameItem( SID_FILLFRAME, xFrame ) );
+ }
+ else if ( aName == sAsTemplate )
+ {
+ bool bVal = false;
+ bool bOK = (rProp.Value >>= bVal);
+ DBG_ASSERT( bOK, "invalid type for AsTemplate" );
+ if (bOK)
+ rSet.Put( SfxBoolItem( SID_TEMPLATE, bVal ) );
+ }
+ else if ( aName == sOpenNewView )
+ {
+ bool bVal = false;
+ bool bOK = (rProp.Value >>= bVal);
+ DBG_ASSERT( bOK, "invalid type for OpenNewView" );
+ if (bOK)
+ rSet.Put( SfxBoolItem( SID_OPEN_NEW_VIEW, bVal ) );
+ }
+ else if ( aName == sFailOnWarning )
+ {
+ bool bVal = false;
+ bool bOK = (rProp.Value >>= bVal);
+ DBG_ASSERT( bOK, "invalid type for FailOnWarning" );
+ if (bOK)
+ rSet.Put( SfxBoolItem( SID_FAIL_ON_WARNING, bVal ) );
+ }
+ else if ( aName == sViewId )
+ {
+ sal_Int16 nVal = -1;
+ bool bOK = ((rProp.Value >>= nVal) && (nVal != -1));
+ DBG_ASSERT( bOK, "invalid type for ViewId" );
+ if (bOK)
+ rSet.Put( SfxUInt16Item( SID_VIEW_ID, nVal ) );
+ }
+ else if ( aName == sPluginMode )
+ {
+ sal_Int16 nVal = -1;
+ bool bOK = ((rProp.Value >>= nVal) && (nVal != -1));
+ DBG_ASSERT( bOK, "invalid type for PluginMode" );
+ if (bOK)
+ rSet.Put( SfxUInt16Item( SID_PLUGIN_MODE, nVal ) );
+ }
+ else if ( aName == sReadOnly )
+ {
+ bool bVal = false;
+ bool bOK = (rProp.Value >>= bVal);
+ DBG_ASSERT( bOK, "invalid type for ReadOnly" );
+ if (bOK)
+ rSet.Put( SfxBoolItem( SID_DOC_READONLY, bVal ) );
+ }
+ else if ( aName == sDdeReconnect )
+ {
+ bool bVal = true;
+ bool bOK = (rProp.Value >>= bVal);
+ DBG_ASSERT( bOK, "invalid type for DDEReconnect" );
+ if (bOK)
+ rSet.Put( SfxBoolItem( SID_DDE_RECONNECT_ONLOAD, bVal ) );
+ }
+ else if ( aName == sStartPresentation )
+ {
+ bool bVal = false;
+ bool bOK = (rProp.Value >>= bVal);
+ DBG_ASSERT( bOK, "invalid type for StartPresentation" );
+ if (bOK)
+ rSet.Put( SfxBoolItem( SID_DOC_STARTPRESENTATION, bVal ) );
+ }
+ else if ( aName == sSelectionOnly )
+ {
+ bool bVal = false;
+ bool bOK = (rProp.Value >>= bVal);
+ DBG_ASSERT( bOK, "invalid type for SelectionOnly" );
+ if (bOK)
+ rSet.Put( SfxBoolItem( SID_SELECTION, bVal ) );
+ }
+ else if ( aName == sHidden )
+ {
+ bool bVal = false;
+ bool bOK = (rProp.Value >>= bVal);
+ DBG_ASSERT( bOK, "invalid type for Hidden" );
+ if (bOK)
+ rSet.Put( SfxBoolItem( SID_HIDDEN, bVal ) );
+ }
+ else if ( aName == sMinimized )
+ {
+ bool bVal = false;
+ bool bOK = (rProp.Value >>= bVal);
+ DBG_ASSERT( bOK, "invalid type for Minimized" );
+ if (bOK)
+ rSet.Put( SfxBoolItem( SID_MINIMIZED, bVal ) );
+ }
+ else if ( aName == sSilent )
+ {
+ bool bVal = false;
+ bool bOK = (rProp.Value >>= bVal);
+ DBG_ASSERT( bOK, "invalid type for Silent" );
+ if (bOK)
+ rSet.Put( SfxBoolItem( SID_SILENT, bVal ) );
+ }
+ else if ( aName == sPreview )
+ {
+ bool bVal = false;
+ bool bOK = (rProp.Value >>= bVal);
+ DBG_ASSERT( bOK, "invalid type for Preview" );
+ if (bOK)
+ rSet.Put( SfxBoolItem( SID_PREVIEW, bVal ) );
+ }
+ else if ( aName == sViewOnly )
+ {
+ bool bVal = false;
+ bool bOK = (rProp.Value >>= bVal);
+ DBG_ASSERT( bOK, "invalid type for ViewOnly" );
+ if (bOK)
+ rSet.Put( SfxBoolItem( SID_VIEWONLY, bVal ) );
+ }
+ else if ( aName == sDontEdit )
+ {
+ bool bVal = false;
+ bool bOK = (rProp.Value >>= bVal);
+ DBG_ASSERT( bOK, "invalid type for ViewOnly" );
+ if (bOK)
+ rSet.Put( SfxBoolItem( SID_EDITDOC, !bVal ) );
+ }
+ else if ( aName == sUseSystemDialog )
+ {
+ bool bVal = false;
+ bool bOK = (rProp.Value >>= bVal);
+ DBG_ASSERT( bOK, "invalid type for ViewOnly" );
+ if (bOK)
+ rSet.Put( SfxBoolItem( SID_FILE_DIALOG, bVal ) );
+ }
+ else if ( aName == sStandardDir )
+ {
+ OUString sVal;
+ bool bOK = ((rProp.Value >>= sVal) && !sVal.isEmpty());
+ DBG_ASSERT( bOK, "invalid type or value for StandardDir" );
+ if (bOK)
+ rSet.Put( SfxStringItem( SID_STANDARD_DIR, sVal ) );
+ }
+ else if ( aName == sDenyList )
+ {
+ uno::Sequence<OUString> xVal;
+ bool bOK = (rProp.Value >>= xVal);
+ DBG_ASSERT( bOK, "invalid type or value for DenyList" );
+ if (bOK)
+ {
+ SfxStringListItem stringList(SID_DENY_LIST);
+ stringList.SetStringList( xVal );
+ rSet.Put( stringList );
+ }
+ }
+ else if ( aName == "FileName" )
+ {
+ OUString sVal;
+ bool bOK = ((rProp.Value >>= sVal) && !sVal.isEmpty());
+ DBG_ASSERT( bOK, "invalid type or value for FileName" );
+ if (bOK)
+ rSet.Put( SfxStringItem( SID_FILE_NAME, sVal ) );
+ }
+ else if ( aName == sSalvagedFile )
+ {
+ OUString sVal;
+ bool bOK = (rProp.Value >>= sVal);
+ DBG_ASSERT( bOK, "invalid type or value for SalvagedFile" );
+ if (bOK)
+ rSet.Put( SfxStringItem( SID_DOC_SALVAGE, sVal ) );
+ }
+ else if ( aName == sFolderName )
+ {
+ OUString sVal;
+ bool bOK = (rProp.Value >>= sVal);
+ DBG_ASSERT( bOK, "invalid type or value for FolderName" );
+ if (bOK)
+ rSet.Put( SfxStringItem( SID_PATH, sVal ) );
+ }
+ else if ( aName == sFrameName )
+ {
+ OUString sVal;
+ bool bOK = (rProp.Value >>= sVal);
+ DBG_ASSERT( bOK, "invalid type for FrameName" );
+ if (bOK && !sVal.isEmpty())
+ rSet.Put( SfxStringItem( SID_TARGETNAME, sVal ) );
+ }
+ else if ( aName == sMediaType )
+ {
+ OUString sVal;
+ bool bOK = ((rProp.Value >>= sVal) && !sVal.isEmpty());
+ DBG_ASSERT( bOK, "invalid type or value for MediaType" );
+ if (bOK)
+ rSet.Put( SfxStringItem( SID_CONTENTTYPE, sVal ) );
+ }
+ else if ( aName == sTemplateName )
+ {
+ OUString sVal;
+ bool bOK = ((rProp.Value >>= sVal) && !sVal.isEmpty());
+ DBG_ASSERT( bOK, "invalid type or value for TemplateName" );
+ if (bOK)
+ rSet.Put( SfxStringItem( SID_TEMPLATE_NAME, sVal ) );
+ }
+ else if ( aName == sTemplateRegionName )
+ {
+ OUString sVal;
+ bool bOK = ((rProp.Value >>= sVal) && !sVal.isEmpty());
+ DBG_ASSERT( bOK, "invalid type or value for TemplateRegionName" );
+ if (bOK)
+ rSet.Put( SfxStringItem( SID_TEMPLATE_REGIONNAME, sVal ) );
+ }
+ else if ( aName == sJumpMark )
+ {
+ OUString sVal;
+ bool bOK = ((rProp.Value >>= sVal) && !sVal.isEmpty());
+ DBG_ASSERT( bOK, "invalid type or value for JumpMark" );
+ if (bOK)
+ rSet.Put( SfxStringItem( SID_JUMPMARK, sVal ) );
+ }
+ else if ( aName == sCharacterSet )
+ {
+ OUString sVal;
+ bool bOK = ((rProp.Value >>= sVal) && !sVal.isEmpty());
+ DBG_ASSERT( bOK, "invalid type or value for CharacterSet" );
+ if (bOK)
+ rSet.Put( SfxStringItem( SID_CHARSET, sVal ) );
+ }
+ else if ( aName == "FilterFlags" )
+ {
+ OUString sVal;
+ bool bOK = ((rProp.Value >>= sVal) && !sVal.isEmpty());
+ DBG_ASSERT( bOK, "invalid type or value for FilterFlags" );
+ if (bOK)
+ rSet.Put( SfxStringItem( SID_FILE_FILTEROPTIONS, sVal ) );
+ }
+ else if ( aName == sImageFilter )
+ {
+ OUString sVal;
+ bool bOK = ((rProp.Value >>= sVal) && !sVal.isEmpty());
+ DBG_ASSERT( bOK, "invalid type or value for FilterFlags" );
+ if (bOK)
+ rSet.Put( SfxStringItem( SID_CONVERT_IMAGES, sVal ) );
+ }
+ else if ( aName == sMacroExecMode )
+ {
+ sal_Int16 nVal =-1;
+ bool bOK = ((rProp.Value >>= nVal) && (nVal != -1));
+ DBG_ASSERT( bOK, "invalid type for MacroExecMode" );
+ if (bOK)
+ rSet.Put( SfxUInt16Item( SID_MACROEXECMODE, nVal ) );
+ }
+ else if ( aName == sUpdateDocMode )
+ {
+ sal_Int16 nVal =-1;
+ bool bOK = ((rProp.Value >>= nVal) && (nVal != -1));
+ DBG_ASSERT( bOK, "invalid type for UpdateDocMode" );
+ if (bOK)
+ rSet.Put( SfxUInt16Item( SID_UPDATEDOCMODE, nVal ) );
+ }
+ else if ( aName == sRepairPackage )
+ {
+ bool bVal = false;
+ bool bOK = (rProp.Value >>= bVal);
+ DBG_ASSERT( bOK, "invalid type for RepairPackage" );
+ if (bOK)
+ rSet.Put( SfxBoolItem( SID_REPAIRPACKAGE, bVal ) );
+ }
+ else if ( aName == sDocumentTitle )
+ {
+ OUString sVal;
+ bool bOK = ((rProp.Value >>= sVal) && !sVal.isEmpty());
+ DBG_ASSERT( bOK, "invalid type or value for DocumentTitle" );
+ if (bOK)
+ rSet.Put( SfxStringItem( SID_DOCINFO_TITLE, sVal ) );
+ }
+ else if ( aName == sDocumentBaseURL )
+ {
+ OUString sVal;
+ // the base url can be set to empty ( for embedded objects for example )
+ bool bOK = (rProp.Value >>= sVal);
+ DBG_ASSERT( bOK, "invalid type or value for DocumentBaseURL" );
+ if (bOK)
+ rSet.Put( SfxStringItem( SID_DOC_BASEURL, sVal ) );
+ }
+ else if ( aName == sHierarchicalDocumentName )
+ {
+ OUString sVal;
+ bool bOK = ((rProp.Value >>= sVal) && !sVal.isEmpty());
+ DBG_ASSERT( bOK, "invalid type or value for HierarchicalDocumentName" );
+ if (bOK)
+ rSet.Put( SfxStringItem( SID_DOC_HIERARCHICALNAME, sVal ) );
+ }
+ else if ( aName == sCopyStreamIfPossible )
+ {
+ bool bVal = false;
+ bool bOK = (rProp.Value >>= bVal);
+ DBG_ASSERT( bOK, "invalid type for CopyStreamIfPossible" );
+ if (bOK)
+ rSet.Put( SfxBoolItem( SID_COPY_STREAM_IF_POSSIBLE, bVal ) );
+ }
+ else if ( aName == sNoAutoSave )
+ {
+ bool bVal = false;
+ bool bOK = (rProp.Value >>= bVal);
+ DBG_ASSERT( bOK, "invalid type for NoAutoSave" );
+ if (bOK)
+ rSet.Put( SfxBoolItem( SID_NOAUTOSAVE, bVal ) );
+ }
+ else if ( aName == sModifyPasswordInfo )
+ {
+ rSet.Put( SfxUnoAnyItem( SID_MODIFYPASSWORDINFO, rProp.Value ) );
+ }
+ else if ( aName == sEncryptionData )
+ {
+ rSet.Put( SfxUnoAnyItem( SID_ENCRYPTIONDATA, rProp.Value ) );
+ }
+ else if ( aName == sSuggestedSaveAsDir )
+ {
+ OUString sVal;
+ bool bOK = ((rProp.Value >>= sVal) && !sVal.isEmpty());
+ DBG_ASSERT( bOK, "invalid type or value for SuggestedSaveAsDir" );
+ if (bOK)
+ rSet.Put( SfxStringItem( SID_SUGGESTEDSAVEASDIR, sVal ) );
+ }
+ else if ( aName == sSuggestedSaveAsName )
+ {
+ OUString sVal;
+ bool bOK = ((rProp.Value >>= sVal) && !sVal.isEmpty());
+ DBG_ASSERT( bOK, "invalid type or value for SuggestedSaveAsName" );
+ if (bOK)
+ rSet.Put( SfxStringItem( SID_SUGGESTEDSAVEASNAME, sVal ) );
+ }
+ else if (aName == sDocumentService)
+ {
+ OUString aVal;
+ bool bOK = ((rProp.Value >>= aVal) && !aVal.isEmpty());
+ if (bOK)
+ rSet.Put(SfxStringItem(SID_DOC_SERVICE, aVal));
+ }
+ else if (aName == sFilterProvider)
+ {
+ OUString aVal;
+ bool bOK = ((rProp.Value >>= aVal) && !aVal.isEmpty());
+ if (bOK)
+ rSet.Put(SfxStringItem(SID_FILTER_PROVIDER, aVal));
+ }
+ else if (aName == sLockContentExtraction)
+ {
+ bool bVal = false;
+ bool bOK = (rProp.Value >>= bVal);
+ DBG_ASSERT( bOK, "invalid type for LockContentExtraction" );
+ if (bOK)
+ rSet.Put( SfxBoolItem( SID_LOCK_CONTENT_EXTRACTION, bVal ) );
+ }
+ else if (aName == sLockExport)
+ {
+ bool bVal = false;
+ bool bOK = (rProp.Value >>= bVal);
+ DBG_ASSERT( bOK, "invalid type for LockExport" );
+ if (bOK)
+ rSet.Put( SfxBoolItem( SID_LOCK_EXPORT, bVal ) );
+ }
+ else if (aName == sLockPrint)
+ {
+ bool bVal = false;
+ bool bOK = (rProp.Value >>= bVal);
+ DBG_ASSERT( bOK, "invalid type for LockPrint" );
+ if (bOK)
+ rSet.Put( SfxBoolItem( SID_LOCK_PRINT, bVal ) );
+ }
+ else if (aName == sLockSave)
+ {
+ bool bVal = false;
+ bool bOK = (rProp.Value >>= bVal);
+ DBG_ASSERT( bOK, "invalid type for LockSave" );
+ if (bOK)
+ rSet.Put( SfxBoolItem( SID_LOCK_SAVE, bVal ) );
+ }
+ else if (aName == sLockEditDoc)
+ {
+ bool bVal = false;
+ bool bOK = (rProp.Value >>= bVal);
+ DBG_ASSERT( bOK, "invalid type for LockEditDoc" );
+ if (bOK)
+ rSet.Put( SfxBoolItem( SID_LOCK_EDITDOC, bVal ) );
+ }
+ else if (aName == sReplaceable)
+ {
+ bool bVal = false;
+ bool bOK = (rProp.Value >>= bVal);
+ DBG_ASSERT(bOK, "invalid type for Replaceable");
+ if (bOK)
+ rSet.Put(SfxBoolItem(SID_REPLACEABLE, bVal));
+ }
+#ifdef DBG_UTIL
+ else
+ --nFoundArgs;
+#endif
+ }
+ }
+ // API to raise options dialog with a specified options ab page (#i83757#)
+ else
+ {
+ // transform parameter "OptionsPageURL" of slot "OptionsTreeDialog"
+ if ( "OptionsTreeDialog" == OUString( pSlot->pUnoName, strlen(pSlot->pUnoName), RTL_TEXTENCODING_UTF8 ) )
+ {
+ auto pProp = std::find_if(rArgs.begin(), rArgs.end(),
+ [](const PropertyValue& rProp) { return rProp.Name == "OptionsPageURL"; });
+ if (pProp != rArgs.end())
+ {
+ OUString sURL;
+ if ( pProp->Value >>= sURL )
+ rSet.Put( SfxStringItem( SID_OPTIONS_PAGEURL, sURL ) );
+ }
+ }
+ }
+#ifdef DBG_UTIL
+ if ( nFoundArgs == nCount )
+ {
+ // except for the "special" slots: assure that every argument was convertible
+ SAL_INFO( "sfx.appl", "MacroPlayer: Some properties didn't match to any formal argument for slot: "<< pSlot->pUnoName );
+ }
+#endif
+}
+
+void TransformItems( sal_uInt16 nSlotId, const SfxItemSet& rSet, uno::Sequence<beans::PropertyValue>& rArgs, const SfxSlot* pSlot )
+{
+ if ( !pSlot )
+ pSlot = SFX_SLOTPOOL().GetSlot( nSlotId );
+
+ if ( !pSlot)
+ return;
+
+ if ( nSlotId == SID_OPENURL )
+ nSlotId = SID_OPENDOC;
+ if ( nSlotId == SID_SAVEASREMOTE )
+ nSlotId = SID_SAVEASDOC;
+
+ // find number of properties to avoid permanent reallocations in the sequence
+ sal_Int32 nProps=0;
+
+#ifdef DBG_UTIL
+ // trace number of items and compare with number of properties for debugging purposes
+ sal_Int32 nItems=0;
+#endif
+
+ const SfxType *pType = pSlot->GetType();
+ if ( !pSlot->IsMode(SfxSlotMode::METHOD) )
+ {
+ // slot is a property
+ sal_uInt16 nWhich = rSet.GetPool()->GetWhich(nSlotId);
+ if ( rSet.GetItemState( nWhich ) == SfxItemState::SET ) //???
+ {
+ sal_uInt16 nSubCount = pType->nAttribs;
+ if ( nSubCount )
+ // it's a complex property, we want it split into simple types
+ // so we expect to get as many items as we have (sub) members
+ nProps = nSubCount;
+ else
+ // simple property: we expect to get exactly one item
+ nProps++;
+ }
+ else
+ {
+ // we will not rely on the "toggle" ability of some property slots
+ SAL_WARN( "sfx", "Processing property slot without argument: " << nSlotId );
+ }
+
+#ifdef DBG_UTIL
+ nItems++;
+#endif
+ }
+ else
+ {
+ // slot is a method
+ bool bIsMediaDescriptor = isMediaDescriptor( nSlotId );
+ sal_uInt16 nFormalArgs = bIsMediaDescriptor ? nMediaArgsCount : pSlot->GetFormalArgumentCount();
+ for ( sal_uInt16 nArg=0; nArg<nFormalArgs; ++nArg )
+ {
+ // check every formal argument of the method
+ const SfxFormalArgument &rArg = bIsMediaDescriptor ? aFormalArgs[nArg] : pSlot->GetFormalArgument( nArg );
+
+ sal_uInt16 nWhich = rSet.GetPool()->GetWhich( rArg.nSlotId );
+ if ( rSet.GetItemState( nWhich ) == SfxItemState::SET ) //???
+ {
+ sal_uInt16 nSubCount = rArg.pType->nAttribs;
+ if ( nSubCount )
+ // argument has a complex type, we want it split into simple types
+ // so for this argument we expect to get as many items as we have (sub) members
+ nProps += nSubCount;
+ else
+ // argument of simple type: we expect to get exactly one item for it
+ nProps++;
+#ifdef DBG_UTIL
+ nItems++;
+#endif
+ }
+ }
+
+ // special treatment for slots that are *not* meant to be recorded as slots (except SaveAs/To)
+ if ( bIsMediaDescriptor )
+ {
+ sal_Int32 nAdditional=0;
+ if ( rSet.GetItemState( SID_PROGRESS_STATUSBAR_CONTROL ) == SfxItemState::SET )
+ nAdditional++;
+ if ( rSet.GetItemState( SID_INTERACTIONHANDLER ) == SfxItemState::SET )
+ nAdditional++;
+ if ( rSet.GetItemState( SID_DOC_SALVAGE ) == SfxItemState::SET )
+ nAdditional++;
+ if ( rSet.GetItemState( SID_PATH ) == SfxItemState::SET )
+ nAdditional++;
+ if ( rSet.GetItemState( SID_FILE_DIALOG ) == SfxItemState::SET )
+ nAdditional++;
+ if ( rSet.GetItemState( SID_STANDARD_DIR ) == SfxItemState::SET )
+ nAdditional++;
+ if ( rSet.GetItemState( SID_DENY_LIST ) == SfxItemState::SET )
+ nAdditional++;
+ if ( rSet.GetItemState( SID_CONTENT ) == SfxItemState::SET )
+ nAdditional++;
+ if ( rSet.GetItemState( SID_INPUTSTREAM ) == SfxItemState::SET )
+ nAdditional++;
+ if ( rSet.GetItemState( SID_STREAM ) == SfxItemState::SET )
+ nAdditional++;
+ if ( rSet.GetItemState( SID_OUTPUTSTREAM ) == SfxItemState::SET )
+ nAdditional++;
+ if ( rSet.GetItemState( SID_TEMPLATE ) == SfxItemState::SET )
+ nAdditional++;
+ if ( rSet.GetItemState( SID_OPEN_NEW_VIEW ) == SfxItemState::SET )
+ nAdditional++;
+ if ( rSet.GetItemState( SID_FAIL_ON_WARNING ) == SfxItemState::SET )
+ nAdditional++;
+ if ( rSet.GetItemState( SID_VIEW_ID ) == SfxItemState::SET )
+ nAdditional++;
+ if ( rSet.GetItemState( SID_VIEW_DATA ) == SfxItemState::SET )
+ nAdditional++;
+ if ( rSet.GetItemState( SID_FILTER_DATA ) == SfxItemState::SET )
+ nAdditional++;
+ if ( rSet.GetItemState( SID_PLUGIN_MODE ) == SfxItemState::SET )
+ nAdditional++;
+ if ( rSet.GetItemState( SID_DOC_READONLY ) == SfxItemState::SET )
+ nAdditional++;
+ if ( rSet.GetItemState( SID_DDE_RECONNECT_ONLOAD ) == SfxItemState::SET )
+ nAdditional++;
+ if ( rSet.GetItemState( SID_DOC_STARTPRESENTATION ) == SfxItemState::SET )
+ nAdditional++;
+ if ( rSet.GetItemState( SID_SELECTION ) == SfxItemState::SET )
+ nAdditional++;
+ if ( rSet.GetItemState( SID_CONTENTTYPE ) == SfxItemState::SET )
+ nAdditional++;
+ if ( rSet.GetItemState( SID_POSTDATA ) == SfxItemState::SET )
+ nAdditional++;
+ if ( rSet.GetItemState( SID_FILLFRAME ) == SfxItemState::SET )
+ nAdditional++;
+ if ( rSet.GetItemState( SID_CHARSET ) == SfxItemState::SET )
+ nAdditional++;
+ if ( rSet.GetItemState( SID_TARGETNAME ) == SfxItemState::SET )
+ nAdditional++;
+ if ( rSet.GetItemState( SID_TEMPLATE_NAME ) == SfxItemState::SET )
+ nAdditional++;
+ if ( rSet.GetItemState( SID_TEMPLATE_REGIONNAME ) == SfxItemState::SET )
+ nAdditional++;
+ if ( rSet.GetItemState( SID_HIDDEN ) == SfxItemState::SET )
+ nAdditional++;
+ if ( rSet.GetItemState( SID_MINIMIZED ) == SfxItemState::SET )
+ nAdditional++;
+ if ( rSet.GetItemState( SID_PREVIEW ) == SfxItemState::SET )
+ nAdditional++;
+ if ( rSet.GetItemState( SID_VIEWONLY ) == SfxItemState::SET )
+ nAdditional++;
+ if ( rSet.GetItemState( SID_EDITDOC ) == SfxItemState::SET )
+ nAdditional++;
+ if ( rSet.GetItemState( SID_SILENT ) == SfxItemState::SET )
+ nAdditional++;
+ if ( rSet.GetItemState( SID_JUMPMARK ) == SfxItemState::SET )
+ nAdditional++;
+ if ( rSet.GetItemState( SID_DOCUMENT ) == SfxItemState::SET )
+ nAdditional++;
+ if ( rSet.GetItemState( SID_MACROEXECMODE ) == SfxItemState::SET )
+ nAdditional++;
+ if ( rSet.GetItemState( SID_UPDATEDOCMODE ) == SfxItemState::SET )
+ nAdditional++;
+ if ( rSet.GetItemState( SID_REPAIRPACKAGE ) == SfxItemState::SET )
+ nAdditional++;
+ if ( rSet.GetItemState( SID_DOCINFO_TITLE ) == SfxItemState::SET )
+ nAdditional++;
+ if ( rSet.GetItemState( SID_COMPONENTDATA ) == SfxItemState::SET )
+ nAdditional++;
+ if ( rSet.GetItemState( SID_COMPONENTCONTEXT ) == SfxItemState::SET )
+ nAdditional++;
+ if ( rSet.GetItemState( SID_DOC_BASEURL ) == SfxItemState::SET )
+ nAdditional++;
+ if ( rSet.GetItemState( SID_DOC_HIERARCHICALNAME ) == SfxItemState::SET )
+ nAdditional++;
+ if ( rSet.GetItemState( SID_COPY_STREAM_IF_POSSIBLE ) == SfxItemState::SET )
+ nAdditional++;
+ if ( rSet.GetItemState( SID_NOAUTOSAVE ) == SfxItemState::SET )
+ nAdditional++;
+ if ( rSet.GetItemState( SID_MODIFYPASSWORDINFO ) == SfxItemState::SET )
+ nAdditional++;
+ if ( rSet.GetItemState( SID_SUGGESTEDSAVEASDIR ) == SfxItemState::SET )
+ nAdditional++;
+ if ( rSet.GetItemState( SID_ENCRYPTIONDATA ) == SfxItemState::SET )
+ nAdditional++;
+ if ( rSet.GetItemState( SID_SUGGESTEDSAVEASNAME ) == SfxItemState::SET )
+ nAdditional++;
+ if ( rSet.GetItemState( SID_DOC_SERVICE ) == SfxItemState::SET )
+ nAdditional++;
+ if (rSet.HasItem(SID_FILTER_PROVIDER))
+ ++nAdditional;
+ if ( rSet.GetItemState( SID_CONVERT_IMAGES ) == SfxItemState::SET )
+ nAdditional++;
+ if ( rSet.GetItemState( SID_LOCK_CONTENT_EXTRACTION ) == SfxItemState::SET )
+ nAdditional++;
+ if ( rSet.GetItemState( SID_LOCK_EXPORT ) == SfxItemState::SET )
+ nAdditional++;
+ if ( rSet.GetItemState( SID_LOCK_PRINT ) == SfxItemState::SET )
+ nAdditional++;
+ if ( rSet.GetItemState( SID_LOCK_SAVE ) == SfxItemState::SET )
+ nAdditional++;
+ if ( rSet.GetItemState( SID_LOCK_EDITDOC ) == SfxItemState::SET )
+ nAdditional++;
+ if (rSet.GetItemState(SID_REPLACEABLE) == SfxItemState::SET)
+ nAdditional++;
+
+ // consider additional arguments
+ nProps += nAdditional;
+#ifdef DBG_UTIL
+ nItems += nAdditional;
+#endif
+ }
+ }
+
+#ifdef DBG_UTIL
+ // now check the itemset: is there any item that is not convertible using the list of formal arguments
+ // or the table of additional items?!
+ if ( rSet.Count() != nItems )
+ {
+ // detect unknown item and present error message
+ for ( auto const & rPair : rSet.GetRanges() )
+ {
+ sal_uInt16 nStartWhich = rPair.first;
+ sal_uInt16 nEndWhich = rPair.second;
+ for(sal_uInt16 nId = nStartWhich; nId <= nEndWhich; ++nId)
+ {
+ if ( rSet.GetItemState(nId) < SfxItemState::SET ) //???
+ // not really set
+ continue;
+
+ if ( !pSlot->IsMode(SfxSlotMode::METHOD) && nId == rSet.GetPool()->GetWhich( pSlot->GetSlotId() ) )
+ continue;
+
+ bool bIsMediaDescriptor = isMediaDescriptor( nSlotId );
+ sal_uInt16 nFormalArgs = bIsMediaDescriptor ? nMediaArgsCount : pSlot->nArgDefCount;
+ sal_uInt16 nArg;
+ for ( nArg=0; nArg<nFormalArgs; ++nArg )
+ {
+ const SfxFormalArgument &rArg = bIsMediaDescriptor ? aFormalArgs[nArg] : pSlot->GetFormalArgument( nArg );
+ sal_uInt16 nWhich = rSet.GetPool()->GetWhich( rArg.nSlotId );
+ if ( nId == nWhich )
+ break;
+ }
+
+ if ( nArg<nFormalArgs )
+ continue;
+
+ if ( bIsMediaDescriptor )
+ {
+ if ( nId == SID_DOCFRAME )
+ continue;
+ if ( nId == SID_PROGRESS_STATUSBAR_CONTROL )
+ continue;
+ if ( nId == SID_INTERACTIONHANDLER )
+ continue;
+ if ( nId == SID_VIEW_DATA )
+ continue;
+ if ( nId == SID_FILTER_DATA )
+ continue;
+ if ( nId == SID_DOCUMENT )
+ continue;
+ if ( nId == SID_CONTENT )
+ continue;
+ if ( nId == SID_INPUTSTREAM )
+ continue;
+ if ( nId == SID_STREAM )
+ continue;
+ if ( nId == SID_OUTPUTSTREAM )
+ continue;
+ if ( nId == SID_POSTDATA )
+ continue;
+ if ( nId == SID_FILLFRAME )
+ continue;
+ if ( nId == SID_TEMPLATE )
+ continue;
+ if ( nId == SID_OPEN_NEW_VIEW )
+ continue;
+ if ( nId == SID_VIEW_ID )
+ continue;
+ if ( nId == SID_PLUGIN_MODE )
+ continue;
+ if ( nId == SID_DOC_READONLY )
+ continue;
+ if ( nId == SID_DOC_STARTPRESENTATION )
+ continue;
+ if ( nId == SID_SELECTION )
+ continue;
+ if ( nId == SID_HIDDEN )
+ continue;
+ if ( nId == SID_MINIMIZED )
+ continue;
+ if ( nId == SID_SILENT )
+ continue;
+ if ( nId == SID_PREVIEW )
+ continue;
+ if ( nId == SID_VIEWONLY )
+ continue;
+ if ( nId == SID_EDITDOC )
+ continue;
+ if ( nId == SID_TARGETNAME )
+ continue;
+ if ( nId == SID_DOC_SALVAGE )
+ continue;
+ if ( nId == SID_PATH )
+ continue;
+ if ( nId == SID_FILE_DIALOG )
+ continue;
+ if ( nId == SID_STANDARD_DIR )
+ continue;
+ if ( nId == SID_DENY_LIST )
+ continue;
+ if ( nId == SID_CONTENTTYPE )
+ continue;
+ if ( nId == SID_TEMPLATE_NAME )
+ continue;
+ if ( nId == SID_TEMPLATE_REGIONNAME )
+ continue;
+ if ( nId == SID_JUMPMARK )
+ continue;
+ if ( nId == SID_CHARSET )
+ continue;
+ if ( nId == SID_MACROEXECMODE )
+ continue;
+ if ( nId == SID_UPDATEDOCMODE )
+ continue;
+ if ( nId == SID_REPAIRPACKAGE )
+ continue;
+ if ( nId == SID_DOCINFO_TITLE )
+ continue;
+ if ( nId == SID_COMPONENTDATA )
+ continue;
+ if ( nId == SID_COMPONENTCONTEXT )
+ continue;
+ if ( nId == SID_DOC_BASEURL )
+ continue;
+ if ( nId == SID_DOC_HIERARCHICALNAME )
+ continue;
+ if ( nId == SID_COPY_STREAM_IF_POSSIBLE )
+ continue;
+ if ( nId == SID_NOAUTOSAVE )
+ continue;
+ if ( nId == SID_ENCRYPTIONDATA )
+ continue;
+ if ( nId == SID_DOC_SERVICE )
+ continue;
+ if (nId == SID_FILTER_PROVIDER)
+ continue;
+ if ( nId == SID_CONVERT_IMAGES )
+ continue;
+
+ // used only internally
+ if ( nId == SID_SAVETO )
+ continue;
+ if ( nId == SID_SAVEACOPYITEM )
+ continue;
+ if ( nId == SID_MODIFYPASSWORDINFO )
+ continue;
+ if ( nId == SID_SUGGESTEDSAVEASDIR )
+ continue;
+ if ( nId == SID_SUGGESTEDSAVEASNAME )
+ continue;
+ if ( nId == SID_LOCK_CONTENT_EXTRACTION )
+ continue;
+ if ( nId == SID_LOCK_EXPORT )
+ continue;
+ if ( nId == SID_LOCK_PRINT )
+ continue;
+ if ( nId == SID_LOCK_SAVE )
+ continue;
+ if ( nId == SID_LOCK_EDITDOC )
+ continue;
+ if (nId == SID_REPLACEABLE)
+ continue;
+ }
+
+ OString aDbg = "Unknown item detected: " + OString::number(static_cast<sal_Int32>(nId));
+ DBG_ASSERT(nArg<nFormalArgs, aDbg.getStr());
+ }
+ }
+ }
+#endif
+
+ if ( !nProps )
+ return;
+
+ // convert every item into a property
+ uno::Sequence<beans::PropertyValue> aSequ(nProps);
+ beans::PropertyValue *pValue = aSequ.getArray();
+
+ sal_Int32 nActProp=0;
+ if ( !pSlot->IsMode(SfxSlotMode::METHOD) )
+ {
+ // slot is a property
+ sal_uInt16 nWhich = rSet.GetPool()->GetWhich(nSlotId);
+ bool bConvertTwips = ( rSet.GetPool()->GetMetric( nWhich ) == MapUnit::MapTwip );
+ const SfxPoolItem* pItem = rSet.GetItem<SfxPoolItem>(nWhich, false);
+ if ( pItem ) //???
+ {
+ sal_uInt16 nSubCount = pType->nAttribs;
+ if ( !nSubCount )
+ {
+ pValue[nActProp].Name = OUString::createFromAscii(pSlot->pUnoName) ;
+ if ( !pItem->QueryValue( pValue[nActProp].Value ) )
+ {
+ SAL_WARN( "sfx", "Item not convertible: " << nSlotId );
+ }
+ }
+ else
+ {
+ // complex type, add a property value for every member of the struct
+ for ( sal_uInt16 n=1; n<=nSubCount; ++n )
+ {
+ sal_uInt8 nSubId = static_cast<sal_uInt8>(static_cast<sal_Int8>(pType->aAttrib[n-1].nAID));
+ if ( bConvertTwips )
+ nSubId |= CONVERT_TWIPS;
+
+ DBG_ASSERT(( pType->aAttrib[n-1].nAID ) <= 127, "Member ID out of range" );
+ pValue[nActProp].Name = OUString::createFromAscii( pSlot->pUnoName ) +
+ "." +
+ OUString::createFromAscii( pType->aAttrib[n-1].pName );
+ if ( !pItem->QueryValue( pValue[nActProp++].Value, nSubId ) )
+ {
+ SAL_WARN( "sfx", "Sub item " << pType->aAttrib[n-1].nAID
+ << " not convertible in slot: " << nSlotId );
+ }
+ }
+ }
+ }
+
+ rArgs = aSequ;
+ return;
+ }
+
+ // slot is a method
+ sal_uInt16 nFormalArgs = pSlot->GetFormalArgumentCount();
+ for ( sal_uInt16 nArg=0; nArg<nFormalArgs; ++nArg )
+ {
+ const SfxFormalArgument &rArg = pSlot->GetFormalArgument( nArg );
+ sal_uInt16 nWhich = rSet.GetPool()->GetWhich( rArg.nSlotId );
+ bool bConvertTwips = ( rSet.GetPool()->GetMetric( nWhich ) == MapUnit::MapTwip );
+ const SfxPoolItem* pItem = rSet.GetItem<SfxPoolItem>(nWhich, false);
+ if ( pItem ) //???
+ {
+ sal_uInt16 nSubCount = rArg.pType->nAttribs;
+ if ( !nSubCount )
+ {
+ pValue[nActProp].Name = OUString::createFromAscii( rArg.pName ) ;
+ if ( !pItem->QueryValue( pValue[nActProp++].Value ) )
+ {
+ SAL_WARN( "sfx", "Item not convertible: " << rArg.nSlotId );
+ }
+ }
+ else
+ {
+ // complex type, add a property value for every member of the struct
+ for ( sal_uInt16 n = 1; n <= nSubCount; ++n )
+ {
+ sal_uInt8 nSubId = static_cast<sal_uInt8>(static_cast<sal_Int8>(rArg.pType->aAttrib[n-1].nAID));
+ if ( bConvertTwips )
+ nSubId |= CONVERT_TWIPS;
+
+ DBG_ASSERT((rArg.pType->aAttrib[n-1].nAID) <= 127, "Member ID out of range" );
+ pValue[nActProp].Name = OUString::createFromAscii( rArg.pName ) +
+ "." +
+ OUString::createFromAscii( rArg.pType->aAttrib[n-1].pName ) ;
+ if ( !pItem->QueryValue( pValue[nActProp++].Value, nSubId ) )
+ {
+ SAL_WARN( "sfx", "Sub item "
+ << rArg.pType->aAttrib[n-1].nAID
+ << " not convertible in slot: "
+ << rArg.nSlotId );
+ }
+ }
+ }
+ }
+ }
+
+ if ( nSlotId == SID_OPENDOC || nSlotId == SID_EXPORTDOC || nSlotId == SID_SAVEASDOC || nSlotId == SID_SAVEDOC ||
+ nSlotId == SID_SAVETO || nSlotId == SID_EXPORTDOCASPDF || nSlotId == SID_DIRECTEXPORTDOCASPDF ||
+ nSlotId == SID_EXPORTDOCASEPUB || nSlotId == SID_DIRECTEXPORTDOCASEPUB ||
+ nSlotId == SID_REDACTDOC || nSlotId == SID_AUTOREDACTDOC || nSlotId == SID_SAVEACOPY )
+ {
+ if ( const SfxUnoAnyItem *pItem = rSet.GetItemIfSet( SID_COMPONENTDATA, false) )
+ {
+ pValue[nActProp].Name = sComponentData;
+ pValue[nActProp++].Value = pItem->GetValue();
+ }
+ if ( const SfxUnoAnyItem *pItem = rSet.GetItemIfSet( SID_COMPONENTCONTEXT, false) )
+ {
+ pValue[nActProp].Name = sComponentContext;
+ pValue[nActProp++].Value = pItem->GetValue();
+ }
+ if ( const SfxUnoAnyItem *pItem = rSet.GetItemIfSet( SID_PROGRESS_STATUSBAR_CONTROL, false) )
+ {
+ pValue[nActProp].Name = sStatusInd;
+ pValue[nActProp++].Value = pItem->GetValue();
+ }
+ if ( const SfxUnoAnyItem *pItem = rSet.GetItemIfSet( SID_INTERACTIONHANDLER, false) )
+ {
+ pValue[nActProp].Name = sInteractionHdl;
+ pValue[nActProp++].Value = pItem->GetValue();
+ }
+ if ( const SfxUnoAnyItem *pItem = rSet.GetItemIfSet( SID_VIEW_DATA, false) )
+ {
+ pValue[nActProp].Name = sViewData;
+ pValue[nActProp++].Value = pItem->GetValue();
+ }
+ if ( const SfxUnoAnyItem *pItem = rSet.GetItemIfSet( SID_FILTER_DATA, false) )
+ {
+ pValue[nActProp].Name = sFilterData;
+ pValue[nActProp++].Value = pItem->GetValue();
+ }
+ if ( const SfxUnoAnyItem *pItem = rSet.GetItemIfSet( SID_DOCUMENT, false) )
+ {
+ pValue[nActProp].Name = sModel;
+ pValue[nActProp++].Value = pItem->GetValue();
+ }
+ if ( const SfxUnoAnyItem *pItem = rSet.GetItemIfSet( SID_CONTENT, false) )
+ {
+ pValue[nActProp].Name = sUCBContent;
+ pValue[nActProp++].Value = pItem->GetValue();
+ }
+ if ( const SfxUnoAnyItem *pItem = rSet.GetItemIfSet( SID_INPUTSTREAM, false) )
+ {
+ pValue[nActProp].Name = sInputStream;
+ pValue[nActProp++].Value = pItem->GetValue();
+ }
+ if ( const SfxUnoAnyItem *pItem = rSet.GetItemIfSet( SID_STREAM, false) )
+ {
+ pValue[nActProp].Name = sStream;
+ pValue[nActProp++].Value = pItem->GetValue();
+ }
+ if ( const SfxUnoAnyItem *pItem = rSet.GetItemIfSet( SID_OUTPUTSTREAM, false) )
+ {
+ pValue[nActProp].Name = sOutputStream;
+ pValue[nActProp++].Value = pItem->GetValue();
+ }
+ if ( const SfxUnoAnyItem *pItem = rSet.GetItemIfSet( SID_POSTDATA, false) )
+ {
+ pValue[nActProp].Name = sPostData;
+ pValue[nActProp++].Value = pItem->GetValue();
+ }
+ if ( const SfxPoolItem *pItem = nullptr; SfxItemState::SET == rSet.GetItemState( SID_FILLFRAME, false, &pItem) )
+ {
+ pValue[nActProp].Name = sFrame;
+ if ( auto pUsrAnyItem = dynamic_cast< const SfxUnoAnyItem *>( pItem ) )
+ {
+ OSL_FAIL( "TransformItems: transporting an XFrame via an SfxUnoAnyItem is not deprecated!" );
+ pValue[nActProp++].Value = pUsrAnyItem->GetValue();
+ }
+ else if ( auto pUnoFrameItem = dynamic_cast< const SfxUnoFrameItem *>( pItem ) )
+ pValue[nActProp++].Value <<= pUnoFrameItem->GetFrame();
+ else
+ OSL_FAIL( "TransformItems: invalid item type for SID_FILLFRAME!" );
+ }
+ if ( const SfxBoolItem *pItem = rSet.GetItemIfSet( SID_TEMPLATE, false) )
+ {
+ pValue[nActProp].Name = sAsTemplate;
+ pValue[nActProp++].Value <<= pItem->GetValue();
+ }
+ if ( const SfxBoolItem *pItem = rSet.GetItemIfSet( SID_OPEN_NEW_VIEW, false) )
+ {
+ pValue[nActProp].Name = sOpenNewView;
+ pValue[nActProp++].Value <<= pItem->GetValue();
+ }
+ if ( const SfxBoolItem *pItem = rSet.GetItemIfSet( SID_FAIL_ON_WARNING, false) )
+ {
+ pValue[nActProp].Name = sFailOnWarning;
+ pValue[nActProp++].Value <<= pItem->GetValue();
+ }
+ if ( const SfxUInt16Item *pItem = rSet.GetItemIfSet( SID_VIEW_ID, false) )
+ {
+ pValue[nActProp].Name = sViewId;
+ pValue[nActProp++].Value <<= static_cast<sal_Int16>(pItem->GetValue());
+ }
+ if ( const SfxUInt16Item *pItem = rSet.GetItemIfSet( SID_PLUGIN_MODE, false) )
+ {
+ pValue[nActProp].Name = sPluginMode;
+ pValue[nActProp++].Value <<= static_cast<sal_Int16>(pItem->GetValue());
+ }
+ if ( const SfxBoolItem *pItem = rSet.GetItemIfSet( SID_DOC_READONLY, false) )
+ {
+ pValue[nActProp].Name = sReadOnly;
+ pValue[nActProp++].Value <<= pItem->GetValue();
+ }
+ if ( const SfxBoolItem *pItem = rSet.GetItemIfSet( SID_DDE_RECONNECT_ONLOAD, false) )
+ {
+ pValue[nActProp].Name = sDdeReconnect;
+ pValue[nActProp++].Value <<= pItem->GetValue();
+ }
+ if ( const SfxBoolItem *pItem = rSet.GetItemIfSet( SID_DOC_STARTPRESENTATION, false) )
+ {
+ pValue[nActProp].Name = sStartPresentation;
+ pValue[nActProp++].Value <<= pItem->GetValue();
+ }
+ if ( const SfxBoolItem *pItem = rSet.GetItemIfSet( SID_SELECTION, false) )
+ {
+ pValue[nActProp].Name = sSelectionOnly;
+ pValue[nActProp++].Value <<= pItem->GetValue();
+ }
+ if ( const SfxBoolItem *pItem = rSet.GetItemIfSet( SID_HIDDEN, false) )
+ {
+ pValue[nActProp].Name = sHidden;
+ pValue[nActProp++].Value <<= pItem->GetValue();
+ }
+ if ( const SfxBoolItem *pItem = rSet.GetItemIfSet( SID_MINIMIZED, false) )
+ {
+ pValue[nActProp].Name = sMinimized;
+ pValue[nActProp++].Value <<= pItem->GetValue();
+ }
+ if ( const SfxBoolItem *pItem = rSet.GetItemIfSet( SID_SILENT, false) )
+ {
+ pValue[nActProp].Name = sSilent;
+ pValue[nActProp++].Value <<= pItem->GetValue();
+ }
+ if ( const SfxBoolItem *pItem = rSet.GetItemIfSet( SID_PREVIEW, false) )
+ {
+ pValue[nActProp].Name = sPreview;
+ pValue[nActProp++].Value <<= pItem->GetValue();
+ }
+ if ( const SfxBoolItem *pItem = rSet.GetItemIfSet( SID_VIEWONLY, false) )
+ {
+ pValue[nActProp].Name = sViewOnly;
+ pValue[nActProp++].Value <<= pItem->GetValue();
+ }
+ if ( const SfxBoolItem *pItem = rSet.GetItemIfSet( SID_EDITDOC, false) )
+ {
+ pValue[nActProp].Name = sDontEdit;
+ pValue[nActProp++].Value <<= !pItem->GetValue();
+ }
+ if ( const SfxBoolItem *pItem = rSet.GetItemIfSet( SID_FILE_DIALOG, false) )
+ {
+ pValue[nActProp].Name = sUseSystemDialog;
+ pValue[nActProp++].Value <<= pItem->GetValue();
+ }
+ if ( const SfxStringItem *pItem = rSet.GetItemIfSet( SID_STANDARD_DIR, false) )
+ {
+ pValue[nActProp].Name = sStandardDir;
+ pValue[nActProp++].Value <<= pItem->GetValue();
+ }
+ if ( const SfxStringListItem *pItem = rSet.GetItemIfSet( SID_DENY_LIST, false) )
+ {
+ pValue[nActProp].Name = sDenyList;
+
+ css::uno::Sequence< OUString > aList;
+ pItem->GetStringList( aList );
+ pValue[nActProp++].Value <<= aList ;
+ }
+ if ( const SfxStringItem *pItem = rSet.GetItemIfSet( SID_TARGETNAME, false) )
+ {
+ pValue[nActProp].Name = sFrameName;
+ pValue[nActProp++].Value <<= pItem->GetValue();
+ }
+ if ( const SfxStringItem *pItem = rSet.GetItemIfSet( SID_DOC_SALVAGE, false) )
+ {
+ pValue[nActProp].Name = sSalvagedFile;
+ pValue[nActProp++].Value <<= pItem->GetValue();
+ }
+ if ( const SfxStringItem *pItem = rSet.GetItemIfSet( SID_PATH, false) )
+ {
+ pValue[nActProp].Name = sFolderName;
+ pValue[nActProp++].Value <<= pItem->GetValue();
+ }
+ if ( const SfxStringItem *pItem = rSet.GetItemIfSet( SID_CONTENTTYPE, false) )
+ {
+ pValue[nActProp].Name = sMediaType;
+ pValue[nActProp++].Value <<= pItem->GetValue();
+ }
+ if ( const SfxStringItem *pItem = rSet.GetItemIfSet( SID_TEMPLATE_NAME, false) )
+ {
+ pValue[nActProp].Name = sTemplateName;
+ pValue[nActProp++].Value <<= pItem->GetValue();
+ }
+ if ( const SfxStringItem *pItem = rSet.GetItemIfSet( SID_TEMPLATE_REGIONNAME, false) )
+ {
+ pValue[nActProp].Name = sTemplateRegionName;
+ pValue[nActProp++].Value <<= pItem->GetValue();
+ }
+ if ( const SfxStringItem *pItem = rSet.GetItemIfSet( SID_JUMPMARK, false) )
+ {
+ pValue[nActProp].Name = sJumpMark;
+ pValue[nActProp++].Value <<= pItem->GetValue();
+ }
+
+ if ( const SfxStringItem *pItem = rSet.GetItemIfSet( SID_CHARSET, false) )
+ {
+ pValue[nActProp].Name = sCharacterSet;
+ pValue[nActProp++].Value <<= pItem->GetValue();
+ }
+ if ( const SfxUInt16Item *pItem = rSet.GetItemIfSet( SID_MACROEXECMODE, false) )
+ {
+ pValue[nActProp].Name = sMacroExecMode;
+ pValue[nActProp++].Value <<= static_cast<sal_Int16>(pItem->GetValue());
+ }
+ if ( const SfxUInt16Item *pItem = rSet.GetItemIfSet( SID_UPDATEDOCMODE, false) )
+ {
+ pValue[nActProp].Name = sUpdateDocMode;
+ pValue[nActProp++].Value <<= static_cast<sal_Int16>(pItem->GetValue());
+ }
+ if ( const SfxBoolItem *pItem = rSet.GetItemIfSet( SID_REPAIRPACKAGE, false) )
+ {
+ pValue[nActProp].Name = sRepairPackage;
+ pValue[nActProp++].Value <<= pItem->GetValue() ;
+ }
+ if ( const SfxStringItem *pItem = rSet.GetItemIfSet( SID_DOCINFO_TITLE, false) )
+ {
+ pValue[nActProp].Name = sDocumentTitle;
+ pValue[nActProp++].Value <<= pItem->GetValue();
+ }
+ if ( const SfxStringItem *pItem = rSet.GetItemIfSet( SID_DOC_BASEURL, false) )
+ {
+ pValue[nActProp].Name = sDocumentBaseURL;
+ pValue[nActProp++].Value <<= pItem->GetValue();
+ }
+ if ( const SfxStringItem *pItem = rSet.GetItemIfSet( SID_DOC_HIERARCHICALNAME, false) )
+ {
+ pValue[nActProp].Name = sHierarchicalDocumentName;
+ pValue[nActProp++].Value <<= pItem->GetValue();
+ }
+ if ( const SfxBoolItem *pItem = rSet.GetItemIfSet( SID_COPY_STREAM_IF_POSSIBLE, false) )
+ {
+ pValue[nActProp].Name = sCopyStreamIfPossible;
+ pValue[nActProp++].Value <<= pItem->GetValue();
+ }
+ if ( const SfxBoolItem *pItem = rSet.GetItemIfSet( SID_NOAUTOSAVE, false) )
+ {
+ pValue[nActProp].Name = sNoAutoSave;
+ pValue[nActProp++].Value <<= pItem->GetValue() ;
+ }
+ if ( const SfxUnoAnyItem *pItem = rSet.GetItemIfSet( SID_MODIFYPASSWORDINFO, false) )
+ {
+ pValue[nActProp].Name = sModifyPasswordInfo;
+ pValue[nActProp++].Value = pItem->GetValue();
+ }
+ if ( const SfxUnoAnyItem *pItem = rSet.GetItemIfSet( SID_ENCRYPTIONDATA, false) )
+ {
+ pValue[nActProp].Name = sEncryptionData;
+ pValue[nActProp++].Value = pItem->GetValue();
+ }
+ if ( const SfxStringItem *pItem = rSet.GetItemIfSet( SID_SUGGESTEDSAVEASDIR, false) )
+ {
+ pValue[nActProp].Name = sSuggestedSaveAsDir;
+ pValue[nActProp++].Value <<= pItem->GetValue();
+ }
+ if ( const SfxStringItem *pItem = rSet.GetItemIfSet( SID_SUGGESTEDSAVEASNAME, false) )
+ {
+ pValue[nActProp].Name = sSuggestedSaveAsName;
+ pValue[nActProp++].Value <<= pItem->GetValue();
+ }
+ if ( const SfxStringItem *pItem = rSet.GetItemIfSet( SID_DOC_SERVICE, false) )
+ {
+ pValue[nActProp].Name = sDocumentService;
+ pValue[nActProp++].Value <<= pItem->GetValue();
+ }
+ if (const SfxStringItem *pItem = rSet.GetItemIfSet(SID_FILTER_PROVIDER))
+ {
+ pValue[nActProp].Name = sFilterProvider;
+ pValue[nActProp++].Value <<= pItem->GetValue();
+ }
+ if (const SfxStringItem *pItem = rSet.GetItemIfSet(SID_CONVERT_IMAGES))
+ {
+ pValue[nActProp].Name = sImageFilter;
+ pValue[nActProp++].Value <<= pItem->GetValue();
+ }
+ if ( const SfxBoolItem *pItem = rSet.GetItemIfSet( SID_LOCK_CONTENT_EXTRACTION, false) )
+ {
+ pValue[nActProp].Name = sLockContentExtraction;
+ pValue[nActProp++].Value <<= pItem->GetValue() ;
+ }
+ if ( const SfxBoolItem *pItem = rSet.GetItemIfSet( SID_LOCK_EXPORT, false) )
+ {
+ pValue[nActProp].Name = sLockExport;
+ pValue[nActProp++].Value <<= pItem->GetValue() ;
+ }
+ if ( const SfxBoolItem *pItem = rSet.GetItemIfSet( SID_LOCK_PRINT, false) )
+ {
+ pValue[nActProp].Name = sLockPrint;
+ pValue[nActProp++].Value <<= pItem->GetValue() ;
+ }
+ if ( const SfxBoolItem *pItem = rSet.GetItemIfSet( SID_LOCK_SAVE, false) )
+ {
+ pValue[nActProp].Name = sLockSave;
+ pValue[nActProp++].Value <<= pItem->GetValue() ;
+ }
+ if ( const SfxBoolItem *pItem = rSet.GetItemIfSet( SID_LOCK_EDITDOC, false) )
+ {
+ pValue[nActProp].Name = sLockEditDoc;
+ pValue[nActProp++].Value <<= pItem->GetValue();
+ }
+ if (const SfxBoolItem *pItem = rSet.GetItemIfSet(SID_REPLACEABLE, false))
+ {
+ pValue[nActProp].Name = sReplaceable;
+ pValue[nActProp++].Value <<= pItem->GetValue();
+ }
+ }
+
+ rArgs = aSequ;
+}
+
+void SAL_CALL FilterOptionsContinuation::setFilterOptions(
+ const uno::Sequence<beans::PropertyValue>& rProps )
+{
+ rProperties = rProps;
+}
+
+uno::Sequence< beans::PropertyValue > SAL_CALL
+ FilterOptionsContinuation::getFilterOptions()
+{
+ return rProperties;
+}
+
+
+RequestFilterOptions::RequestFilterOptions( uno::Reference< frame::XModel > const & rModel,
+ const uno::Sequence< beans::PropertyValue >& rProperties )
+{
+ uno::Reference< uno::XInterface > temp2;
+ document::FilterOptionsRequest aOptionsRequest( OUString(),
+ temp2,
+ rModel,
+ rProperties );
+
+ m_aRequest <<= aOptionsRequest;
+
+ m_xAbort = new comphelper::OInteractionAbort;
+ m_xOptions = new FilterOptionsContinuation;
+}
+
+uno::Any SAL_CALL RequestFilterOptions::getRequest()
+{
+ return m_aRequest;
+}
+
+uno::Sequence< uno::Reference< task::XInteractionContinuation > >
+ SAL_CALL RequestFilterOptions::getContinuations()
+{
+ return { m_xAbort, m_xOptions };
+}
+
+
+class RequestPackageReparation_Impl : public ::cppu::WeakImplHelper< task::XInteractionRequest >
+{
+ uno::Any m_aRequest;
+ rtl::Reference<comphelper::OInteractionApprove> m_xApprove;
+ rtl::Reference<comphelper::OInteractionDisapprove> m_xDisapprove;
+
+public:
+ explicit RequestPackageReparation_Impl( const OUString& aName );
+ bool isApproved() const;
+ virtual uno::Any SAL_CALL getRequest() override;
+ virtual uno::Sequence< uno::Reference< task::XInteractionContinuation > > SAL_CALL getContinuations() override;
+};
+
+RequestPackageReparation_Impl::RequestPackageReparation_Impl( const OUString& aName )
+{
+ uno::Reference< uno::XInterface > temp2;
+ document::BrokenPackageRequest aBrokenPackageRequest( OUString(), temp2, aName );
+ m_aRequest <<= aBrokenPackageRequest;
+ m_xApprove = new comphelper::OInteractionApprove;
+ m_xDisapprove = new comphelper::OInteractionDisapprove;
+}
+
+bool RequestPackageReparation_Impl::isApproved() const
+{
+ return m_xApprove->wasSelected();
+}
+
+uno::Any SAL_CALL RequestPackageReparation_Impl::getRequest()
+{
+ return m_aRequest;
+}
+
+uno::Sequence< uno::Reference< task::XInteractionContinuation > >
+ SAL_CALL RequestPackageReparation_Impl::getContinuations()
+{
+ return { m_xApprove, m_xDisapprove };
+}
+
+RequestPackageReparation::RequestPackageReparation( const OUString& aName )
+ : mxImpl(new RequestPackageReparation_Impl( aName ))
+{
+}
+
+RequestPackageReparation::~RequestPackageReparation()
+{
+}
+
+bool RequestPackageReparation::isApproved() const
+{
+ return mxImpl->isApproved();
+}
+
+css::uno::Reference < task::XInteractionRequest > RequestPackageReparation::GetRequest() const
+{
+ return mxImpl;
+}
+
+
+class NotifyBrokenPackage_Impl : public ::cppu::WeakImplHelper< task::XInteractionRequest >
+{
+ uno::Any m_aRequest;
+ rtl::Reference<comphelper::OInteractionAbort> m_xAbort;
+
+public:
+ explicit NotifyBrokenPackage_Impl(const OUString& rName);
+ virtual uno::Any SAL_CALL getRequest() override;
+ virtual uno::Sequence< uno::Reference< task::XInteractionContinuation > > SAL_CALL getContinuations() override;
+};
+
+NotifyBrokenPackage_Impl::NotifyBrokenPackage_Impl( const OUString& aName )
+{
+ uno::Reference< uno::XInterface > temp2;
+ document::BrokenPackageRequest aBrokenPackageRequest( OUString(), temp2, aName );
+ m_aRequest <<= aBrokenPackageRequest;
+ m_xAbort = new comphelper::OInteractionAbort;
+}
+
+uno::Any SAL_CALL NotifyBrokenPackage_Impl::getRequest()
+{
+ return m_aRequest;
+}
+
+uno::Sequence< uno::Reference< task::XInteractionContinuation > >
+ SAL_CALL NotifyBrokenPackage_Impl::getContinuations()
+{
+ return { m_xAbort };
+}
+
+NotifyBrokenPackage::NotifyBrokenPackage( const OUString& aName )
+ : mxImpl(new NotifyBrokenPackage_Impl( aName ))
+{
+}
+
+NotifyBrokenPackage::~NotifyBrokenPackage()
+{
+}
+
+css::uno::Reference < task::XInteractionRequest > NotifyBrokenPackage::GetRequest() const
+{
+ return mxImpl;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/appl/childwin.cxx b/sfx2/source/appl/childwin.cxx
new file mode 100644
index 000000000..1740459e7
--- /dev/null
+++ b/sfx2/source/appl/childwin.cxx
@@ -0,0 +1,615 @@
+/* -*- 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 <unotools/viewoptions.hxx>
+#include <com/sun/star/frame/XController.hpp>
+#include <com/sun/star/frame/XFrame.hpp>
+#include <com/sun/star/util/XCloseable.hpp>
+#include <com/sun/star/beans/NamedValue.hpp>
+#include <comphelper/string.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <sal/log.hxx>
+#include <tools/debug.hxx>
+
+#include <vcl/svapp.hxx>
+#include <sfx2/childwin.hxx>
+#include <sfx2/app.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/module.hxx>
+#include <sfx2/dockwin.hxx>
+#include <sfx2/dispatch.hxx>
+#include <workwin.hxx>
+
+#include <sfx2/sfxsids.hrc>
+#include <o3tl/string_view.hxx>
+
+const sal_uInt16 nVersion = 2;
+
+SfxChildWinFactory::SfxChildWinFactory( SfxChildWinCtor pTheCtor, sal_uInt16 nID,
+ sal_uInt16 n )
+ : pCtor(pTheCtor)
+ , nId( nID )
+ , nPos(n)
+{}
+
+struct SfxChildWindow_Impl
+{
+ css::uno::Reference< css::frame::XFrame > xFrame;
+ css::uno::Reference< css::lang::XEventListener > xListener;
+ SfxChildWinFactory aFact = { nullptr, 0, 0 };
+ bool bHideNotDelete;
+ bool bVisible;
+ bool bWantsFocus;
+ SfxWorkWindow* pWorkWin;
+};
+
+namespace {
+
+class DisposeListener : public ::cppu::WeakImplHelper< css::lang::XEventListener >
+{
+ public:
+ DisposeListener( SfxChildWindow* pOwner ,
+ SfxChildWindow_Impl* pData )
+ : m_pOwner( pOwner )
+ , m_pData ( pData )
+ {}
+
+ virtual void SAL_CALL disposing( const css::lang::EventObject& aSource ) override
+ {
+ css::uno::Reference< css::lang::XEventListener > xSelfHold( this );
+
+ css::uno::Reference< css::lang::XComponent > xComp( aSource.Source, css::uno::UNO_QUERY );
+ if( xComp.is() )
+ xComp->removeEventListener( this );
+
+ if( !m_pOwner || !m_pData )
+ return;
+
+ m_pData->xListener.clear();
+
+ if ( m_pData->pWorkWin )
+ {
+ // m_pOwner and m_pData will be killed
+ m_pData->xFrame.clear();
+ m_pData->pWorkWin->GetBindings().Execute( m_pOwner->GetType() );
+ }
+ else
+ {
+ delete m_pOwner;
+ }
+
+ m_pOwner = nullptr;
+ m_pData = nullptr;
+ }
+
+ private:
+ SfxChildWindow* m_pOwner;
+ SfxChildWindow_Impl* m_pData ;
+};
+
+}
+
+bool GetPosSizeFromString( std::u16string_view rStr, Point& rPos, Size& rSize )
+{
+ if ( comphelper::string::getTokenCount(rStr, '/') != 4 )
+ return false;
+
+ sal_Int32 nIdx = 0;
+ rPos.setX( o3tl::toInt32(o3tl::getToken(rStr, 0, '/', nIdx)) );
+ rPos.setY( o3tl::toInt32(o3tl::getToken(rStr, 0, '/', nIdx)) );
+ rSize.setWidth( o3tl::toInt32(o3tl::getToken(rStr, 0, '/', nIdx)) );
+ rSize.setHeight( o3tl::toInt32(o3tl::getToken(rStr, 0, '/', nIdx)) );
+
+ // negative sizes are invalid
+ return rSize.Width() >= 0 && rSize.Height() >= 0;
+}
+
+bool GetSplitSizeFromString( std::u16string_view rStr, Size& rSize )
+{
+ size_t nIndex = rStr.find( ',' );
+ if ( nIndex != std::u16string_view::npos )
+ {
+ std::u16string_view aStr = rStr.substr( nIndex+1 );
+
+ sal_Int32 nCount = comphelper::string::getTokenCount(aStr, ';');
+ if ( nCount != 2 )
+ return false;
+
+ sal_Int32 nIdx{ 0 };
+ rSize.setWidth( o3tl::toInt32(o3tl::getToken(aStr, 0, ';', nIdx )) );
+ rSize.setHeight( o3tl::toInt32(o3tl::getToken(aStr, 0, ';', nIdx )) );
+
+ // negative sizes are invalid
+ return rSize.Width() >= 0 && rSize.Height() >= 0;
+ }
+
+ return false;
+}
+
+SfxChildWindow::SfxChildWindow(vcl::Window *pParentWindow, sal_uInt16 nId)
+ : pParent(pParentWindow)
+ , pImpl(new SfxChildWindow_Impl)
+ , eChildAlignment(SfxChildAlignment::NOALIGNMENT)
+ , nType(nId)
+{
+ pImpl->bHideNotDelete = false;
+ pImpl->bWantsFocus = true;
+ pImpl->bVisible = true;
+ pImpl->pWorkWin = nullptr;
+}
+
+void SfxChildWindow::Destroy()
+{
+ if ( GetFrame().is() )
+ {
+ ClearWorkwin();
+ try
+ {
+ css::uno::Reference < css::util::XCloseable > xClose( GetFrame(), css::uno::UNO_QUERY );
+ if ( xClose.is() )
+ xClose->close( true );
+ else
+ GetFrame()->dispose();
+ }
+ catch (const css::uno::Exception&)
+ {
+ }
+ }
+ else
+ delete this;
+}
+
+void SfxChildWindow::ClearWorkwin()
+{
+ if (pImpl->pWorkWin)
+ {
+ if (pImpl->pWorkWin->GetActiveChild_Impl() == pWindow)
+ pImpl->pWorkWin->SetActiveChild_Impl(nullptr);
+ pImpl->pWorkWin = nullptr;
+ }
+}
+
+SfxChildWindow::~SfxChildWindow()
+{
+ ClearWorkwin();
+ if (xController)
+ {
+ xController->ChildWinDispose();
+ xController.reset();
+ }
+ pWindow.disposeAndClear();
+}
+
+std::unique_ptr<SfxChildWindow> SfxChildWindow::CreateChildWindow( sal_uInt16 nId,
+ vcl::Window *pParent, SfxBindings* pBindings, SfxChildWinInfo const & rInfo)
+{
+ std::unique_ptr<SfxChildWindow> pChild;
+ SfxChildWinFactory* pFact=nullptr;
+ SystemWindowFlags nOldMode = Application::GetSystemWindowMode();
+
+ // First search for ChildWindow in SDT; Overlay windows are realized
+ // by using ChildWindowContext
+ SfxApplication *pApp = SfxGetpApp();
+ {
+ pFact = pApp->GetChildWinFactoryById(nId);
+ if ( pFact )
+ {
+ SfxChildWinInfo& rFactInfo = pFact->aInfo;
+ if ( rInfo.bVisible )
+ {
+ if ( pBindings )
+ pBindings->ENTERREGISTRATIONS();
+ SfxChildWinInfo aInfo = rFactInfo;
+ Application::SetSystemWindowMode( SystemWindowFlags::NOAUTOMODE );
+ pChild = pFact->pCtor( pParent, nId, pBindings, &aInfo );
+ Application::SetSystemWindowMode( nOldMode );
+ if ( pBindings )
+ pBindings->LEAVEREGISTRATIONS();
+ }
+ }
+ }
+
+ SfxDispatcher *pDisp = pBindings ? pBindings->GetDispatcher_Impl() : nullptr;
+ SfxModule *pMod = pDisp ? SfxModule::GetActiveModule( pDisp->GetFrame() ) : nullptr;
+ if (!pChild && pMod)
+ {
+ pFact = pMod->GetChildWinFactoryById(nId);
+ if ( pFact )
+ {
+ if ( rInfo.bVisible )
+ {
+ if ( pBindings )
+ pBindings->ENTERREGISTRATIONS();
+ SfxChildWinInfo aInfo = rInfo;
+ Application::SetSystemWindowMode( SystemWindowFlags::NOAUTOMODE );
+ pChild = pFact->pCtor( pParent, nId, pBindings, &aInfo );
+ Application::SetSystemWindowMode( nOldMode );
+ if ( pBindings )
+ pBindings->LEAVEREGISTRATIONS();
+ }
+ }
+ }
+
+ if (pChild)
+ {
+ assert(pFact && "pChild is returned by a call on pFact, so pFact cannot be null");
+ pChild->SetFactory_Impl( pFact );
+ }
+
+ DBG_ASSERT(pFact && (pChild || !rInfo.bVisible), "ChildWindow-Typ not registered!");
+
+ if (pChild && (!pChild->pWindow && !pChild->xController))
+ {
+ pChild.reset();
+ SAL_INFO("sfx.appl", "ChildWindow has no Window!");
+ }
+
+ return pChild;
+}
+
+
+void SfxChildWindow::SaveStatus(const SfxChildWinInfo& rInfo)
+{
+ sal_uInt16 nID = GetType();
+
+ OUString aInfoVisible = rInfo.bVisible ? OUString("V") : OUString("H");
+
+ OUString aWinData = "V"
+ + OUString::number(static_cast<sal_Int32>(nVersion))
+ + ","
+ + aInfoVisible
+ + ","
+ + OUString::number(static_cast<sal_Int32>(rInfo.nFlags));
+
+ if ( !rInfo.aExtraString.isEmpty() )
+ aWinData += "," + rInfo.aExtraString;
+
+ OUString sName(OUString::number(nID));
+ //Try and save window state per-module, e.g. sidebar on in one application
+ //but off in another
+ if (!rInfo.aModule.isEmpty())
+ sName = rInfo.aModule + "/" + sName;
+ SvtViewOptions aWinOpt(EViewType::Window, sName);
+ aWinOpt.SetWindowState(OStringToOUString(rInfo.aWinState, RTL_TEXTENCODING_UTF8));
+
+ css::uno::Sequence < css::beans::NamedValue > aSeq
+ { { "Data", css::uno::Any(aWinData) } };
+ aWinOpt.SetUserData( aSeq );
+
+ // ... but save status at runtime!
+ pImpl->aFact.aInfo = rInfo;
+}
+
+void SfxChildWindow::SetAlignment(SfxChildAlignment eAlign)
+{
+ eChildAlignment = eAlign;
+}
+
+SfxChildWinInfo SfxChildWindow::GetInfo() const
+{
+ SfxChildWinInfo aInfo(pImpl->aFact.aInfo);
+ if (xController)
+ {
+ weld::Dialog* pDialog = xController->getDialog();
+ aInfo.aPos = pDialog->get_position();
+ aInfo.aSize = pDialog->get_size();
+ WindowStateMask nMask = WindowStateMask::Pos | WindowStateMask::State;
+ if (pDialog->get_resizable())
+ nMask |= WindowStateMask::Size;
+ aInfo.aWinState = pDialog->get_window_state(nMask);
+ }
+ else if (pWindow)
+ {
+ aInfo.aPos = pWindow->GetPosPixel();
+ aInfo.aSize = pWindow->GetSizePixel();
+ if ( pWindow->IsSystemWindow() )
+ {
+ WindowStateMask nMask = WindowStateMask::Pos | WindowStateMask::State;
+ if ( pWindow->GetStyle() & WB_SIZEABLE )
+ nMask |= WindowStateMask::Size;
+ aInfo.aWinState = static_cast<SystemWindow*>(pWindow.get())->GetWindowState( nMask );
+ }
+ else if (DockingWindow* pDockingWindow = dynamic_cast<DockingWindow*>(pWindow.get()))
+ {
+ if (pDockingWindow->GetFloatingWindow())
+ aInfo.aWinState = pDockingWindow->GetFloatingWindow()->GetWindowState();
+ else if (SfxDockingWindow* pSfxDockingWindow = dynamic_cast<SfxDockingWindow*>(pDockingWindow))
+ {
+ SfxChildWinInfo aTmpInfo;
+ pSfxDockingWindow->FillInfo( aTmpInfo );
+ aInfo.aExtraString = aTmpInfo.aExtraString;
+ }
+ }
+ }
+
+ aInfo.bVisible = pImpl->bVisible;
+ aInfo.nFlags = SfxChildWindowFlags::NONE;
+ return aInfo;
+}
+
+sal_uInt16 SfxChildWindow::GetPosition() const
+{
+ return pImpl->aFact.nPos;
+}
+
+void SfxChildWindow::InitializeChildWinFactory_Impl(sal_uInt16 nId, SfxChildWinInfo& rInfo)
+{
+ // load configuration
+
+ std::optional<SvtViewOptions> xWinOpt;
+ // first see if a module specific id exists
+ if (rInfo.aModule.getLength())
+ xWinOpt.emplace(EViewType::Window, rInfo.aModule + "/" + OUString::number(nId));
+
+ // if not then try the generic id
+ if (!xWinOpt || !xWinOpt->Exists())
+ xWinOpt.emplace(EViewType::Window, OUString::number(nId));
+
+ if (xWinOpt->Exists() && xWinOpt->HasVisible() )
+ rInfo.bVisible = xWinOpt->IsVisible(); // set state from configuration. Can be overwritten by UserData, see below
+
+ css::uno::Sequence < css::beans::NamedValue > aSeq = xWinOpt->GetUserData();
+
+ OUString aTmp;
+ if ( aSeq.hasElements() )
+ aSeq[0].Value >>= aTmp;
+
+ OUString aWinData( aTmp );
+ rInfo.aWinState = OUStringToOString(xWinOpt->GetWindowState(), RTL_TEXTENCODING_UTF8);
+
+ if ( aWinData.isEmpty() )
+ return;
+
+ // Search for version ID
+ if ( aWinData[0] != 0x0056 ) // 'V' = 56h
+ // A version ID, so do not use
+ return;
+
+ // Delete 'V'
+ aWinData = aWinData.copy(1);
+
+ // Read version
+ char cToken = ',';
+ sal_Int32 nPos = aWinData.indexOf( cToken );
+ sal_uInt16 nActVersion = static_cast<sal_uInt16>(o3tl::toInt32(aWinData.subView( 0, nPos + 1 )));
+ if ( nActVersion != nVersion )
+ return;
+
+ aWinData = aWinData.copy(nPos+1);
+
+ // Load Visibility: is coded as a char
+ rInfo.bVisible = (aWinData[0] == 0x0056); // 'V' = 56h
+ aWinData = aWinData.copy(1);
+ nPos = aWinData.indexOf( cToken );
+ if (nPos == -1)
+ return;
+
+ sal_Int32 nNextPos = aWinData.indexOf( cToken, 2 );
+ if ( nNextPos != -1 )
+ {
+ // there is extra information
+ rInfo.nFlags = static_cast<SfxChildWindowFlags>(static_cast<sal_uInt16>(o3tl::toInt32(aWinData.subView( nPos+1, nNextPos - nPos - 1 ))));
+ aWinData = aWinData.replaceAt( nPos, nNextPos-nPos+1, u"" );
+ rInfo.aExtraString = aWinData;
+ }
+ else
+ rInfo.nFlags = static_cast<SfxChildWindowFlags>(static_cast<sal_uInt16>(o3tl::toInt32(aWinData.subView( nPos+1 ))));
+}
+
+bool ParentIsFloatingWindow(const vcl::Window *pParent)
+{
+ if (!pParent)
+ return false;
+ if (pParent->GetType() == WindowType::DOCKINGWINDOW || pParent->GetType() == WindowType::TOOLBOX)
+ return static_cast<const DockingWindow*>(pParent)->GetFloatingWindow() != nullptr;
+ if (pParent->GetType() == WindowType::FLOATINGWINDOW)
+ return true;
+ return false;
+}
+
+void SfxChildWindow::SetFactory_Impl( const SfxChildWinFactory *pF )
+{
+ pImpl->aFact = *pF;
+}
+
+void SfxChildWindow::SetHideNotDelete( bool bOn )
+{
+ pImpl->bHideNotDelete = bOn;
+}
+
+bool SfxChildWindow::IsHideNotDelete() const
+{
+ return pImpl->bHideNotDelete;
+}
+
+void SfxChildWindow::SetWantsFocus( bool bSet )
+{
+ pImpl->bWantsFocus = bSet;
+}
+
+bool SfxChildWindow::WantsFocus() const
+{
+ return pImpl->bWantsFocus;
+}
+
+bool SfxChildWinInfo::GetExtraData_Impl
+(
+ SfxChildAlignment *pAlign
+) const
+{
+ // invalid?
+ if ( aExtraString.isEmpty() )
+ return false;
+ OUString aStr;
+ sal_Int32 nPos = aExtraString.indexOf("AL:");
+ if ( nPos == -1 )
+ return false;
+
+ // Try to read the alignment string "ALIGN :(...)", but if
+ // it is not present, then use an older version
+ sal_Int32 n1 = aExtraString.indexOf('(', nPos);
+ if ( n1 != -1 )
+ {
+ sal_Int32 n2 = aExtraString.indexOf(')', n1);
+ if ( n2 != -1 )
+ {
+ // Cut out Alignment string
+ aStr = aExtraString.copy(nPos, n2 - nPos + 1);
+ aStr = aStr.replaceAt(nPos, n1-nPos+1, u"");
+ }
+ }
+
+ // First extract the Alignment
+ if ( aStr.isEmpty() )
+ return false;
+ if ( pAlign )
+ *pAlign = static_cast<SfxChildAlignment>(static_cast<sal_uInt16>(aStr.toInt32()));
+
+ // then the LastAlignment
+ nPos = aStr.indexOf(',');
+ if ( nPos == -1 )
+ return false;
+ aStr = aStr.copy(nPos+1);
+
+ // Then the splitting information
+ nPos = aStr.indexOf(',');
+ if ( nPos == -1 )
+ // No docking in a Splitwindow
+ return true;
+ aStr = aStr.copy(nPos+1);
+ Point aChildPos;
+ Size aChildSize;
+ return GetPosSizeFromString( aStr, aChildPos, aChildSize );
+}
+
+bool SfxChildWindow::IsVisible() const
+{
+ return pImpl->bVisible;
+}
+
+void SfxChildWindow::SetVisible_Impl( bool bVis )
+{
+ pImpl->bVisible = bVis;
+}
+
+void SfxChildWindow::Hide()
+{
+ if (xController)
+ xController->EndDialog(nCloseResponseToJustHide);
+ else
+ pWindow->Hide();
+}
+
+void SfxChildWindow::Show( ShowFlags nFlags )
+{
+ if (xController)
+ {
+ if (!xController->getDialog()->get_visible())
+ {
+ weld::DialogController::runAsync(xController,
+ [this](sal_Int32 nResult) {
+ if (nResult == nCloseResponseToJustHide)
+ return;
+ xController->Close();
+ });
+ }
+ }
+ else
+ pWindow->Show(true, nFlags);
+}
+
+void SfxChildWindow::SetWorkWindow_Impl( SfxWorkWindow* pWin )
+{
+ pImpl->pWorkWin = pWin;
+ if (pWin)
+ {
+ if ( (xController && xController->getDialog()->has_toplevel_focus()) ||
+ (pWindow && pWindow->HasChildPathFocus()) )
+ {
+ pImpl->pWorkWin->SetActiveChild_Impl( pWindow );
+ }
+ }
+}
+
+void SfxChildWindow::Activate_Impl()
+{
+ if(pImpl->pWorkWin!=nullptr)
+ pImpl->pWorkWin->SetActiveChild_Impl( pWindow );
+}
+
+bool SfxChildWindow::QueryClose()
+{
+ bool bAllow = true;
+
+ if ( pImpl->xFrame.is() )
+ {
+ css::uno::Reference< css::frame::XController > xCtrl = pImpl->xFrame->getController();
+ if ( xCtrl.is() )
+ bAllow = xCtrl->suspend( true );
+ }
+
+ if ( bAllow )
+ {
+ if (GetController())
+ {
+ weld::Dialog* pDialog = GetController()->getDialog();
+ bAllow = !pDialog->get_visible() || !pDialog->get_modal();
+ }
+ else if (GetWindow())
+ bAllow = !GetWindow()->IsInModalMode();
+ }
+
+ return bAllow;
+}
+
+const css::uno::Reference< css::frame::XFrame >& SfxChildWindow::GetFrame() const
+{
+ return pImpl->xFrame;
+}
+
+void SfxChildWindow::SetFrame( const css::uno::Reference< css::frame::XFrame > & rFrame )
+{
+ // Do nothing if nothing will be changed ...
+ if( pImpl->xFrame == rFrame )
+ return;
+
+ // ... but stop listening on old frame, if connection exist!
+ if( pImpl->xFrame.is() )
+ pImpl->xFrame->removeEventListener( pImpl->xListener );
+
+ // If new frame is not NULL -> we must guarantee valid listener for disposing events.
+ // Use already existing or create new one.
+ if( rFrame.is() )
+ if( !pImpl->xListener.is() )
+ pImpl->xListener.set( new DisposeListener( this, pImpl.get() ) );
+
+ // Set new frame in data container
+ // and build new listener connection, if necessary.
+ pImpl->xFrame = rFrame;
+ if( pImpl->xFrame.is() )
+ pImpl->xFrame->addEventListener( pImpl->xListener );
+}
+
+void SfxChildWindow::RegisterChildWindow(SfxModule* pMod, const SfxChildWinFactory& rFact)
+{
+ SfxGetpApp()->RegisterChildWindow_Impl( pMod, rFact );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/appl/fileobj.cxx b/sfx2/source/appl/fileobj.cxx
new file mode 100644
index 000000000..18aea4a59
--- /dev/null
+++ b/sfx2/source/appl/fileobj.cxx
@@ -0,0 +1,435 @@
+/* -*- 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 <tools/urlobj.hxx>
+#include <tools/stream.hxx>
+#include <sot/formats.hxx>
+#include <sal/log.hxx>
+#include <sfx2/lnkbase.hxx>
+#include <sfx2/filedlghelper.hxx>
+#include <sot/exchange.hxx>
+#include <com/sun/star/uno/Any.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <sfx2/docfac.hxx>
+#include <com/sun/star/document/XTypeDetection.hpp>
+#include <com/sun/star/container/XNameAccess.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <unotools/mediadescriptor.hxx>
+#include <comphelper/processfactory.hxx>
+#include <sfx2/linkmgr.hxx>
+#include <sfx2/opengrf.hxx>
+#include <sfx2/sfxresid.hxx>
+#include <sfx2/objsh.hxx>
+#include "fileobj.hxx"
+#include <sfx2/strings.hrc>
+#include <vcl/svapp.hxx>
+
+enum class SvFileObjectType
+{
+ Text = 1, Graphic = 2, Object = 3
+};
+
+SvFileObject::SvFileObject()
+ : nPostUserEventId(nullptr)
+ , nType(SvFileObjectType::Text)
+ , bLoadAgain(true)
+ , bSynchron(false)
+ , bLoadError(false)
+ , bWaitForData(false)
+ , bDataReady(false)
+ , bClearMedium(false)
+ , bStateChangeCalled(false)
+{
+}
+
+SvFileObject::~SvFileObject()
+{
+ if (xMed.is())
+ {
+ xMed->SetDoneLink( Link<void*,void>() );
+ xMed.clear();
+ }
+ if (nPostUserEventId)
+ Application::RemoveUserEvent(nPostUserEventId);
+}
+
+bool SvFileObject::GetData( css::uno::Any & rData,
+ const OUString & rMimeType,
+ bool /*bGetSynchron*/ )
+{
+ SotClipboardFormatId nFmt = SotExchange::RegisterFormatMimeType( rMimeType );
+ switch( nType )
+ {
+ case SvFileObjectType::Text:
+ if( SotClipboardFormatId::SIMPLE_FILE == nFmt )
+ {
+ // The media in the application must be opened to lookup the
+ // relative file links!! This is done through the link manager
+ // of the Storage.
+ rData <<= sFileNm;
+ }
+ break;
+
+ case SvFileObjectType::Graphic:
+ if (SotClipboardFormatId::GDIMETAFILE == nFmt
+ || SotClipboardFormatId::BITMAP == nFmt
+ || SotClipboardFormatId::SVXB == nFmt)
+ {
+ rData <<= sFileNm;
+ }
+ break;
+ case SvFileObjectType::Object:
+ // TODO/LATER: possibility to insert a new object
+ rData <<= sFileNm;
+ break;
+ }
+ return true/*0 != aTypeList.Count()*/;
+}
+
+bool SvFileObject::Connect( sfx2::SvBaseLink* pLink )
+{
+ if( !pLink || !pLink->GetLinkManager() )
+ return false;
+
+ // Test if not another link of the same connection already exists
+ sfx2::LinkManager::GetDisplayNames( pLink, nullptr, &sFileNm, nullptr, &sFilter );
+
+ if( sfx2::SvBaseLinkObjectType::ClientGraphic == pLink->GetObjType() )
+ {
+ SfxObjectShellRef pShell = pLink->GetLinkManager()->GetPersist();
+ if( pShell.is() )
+ {
+ if( pShell->IsAbortingImport() )
+ return false;
+
+ if( pShell->GetMedium() )
+ sReferer = pShell->GetMedium()->GetName();
+ }
+ }
+
+ switch( pLink->GetObjType() )
+ {
+ case sfx2::SvBaseLinkObjectType::ClientGraphic:
+ nType = SvFileObjectType::Graphic;
+ bSynchron = pLink->IsSynchron();
+ break;
+
+ case sfx2::SvBaseLinkObjectType::ClientFile:
+ nType = SvFileObjectType::Text;
+ break;
+
+ case sfx2::SvBaseLinkObjectType::ClientOle:
+ nType = SvFileObjectType::Object;
+ // TODO/LATER: introduce own type to be used for exchanging
+ break;
+
+ default:
+ return false;
+ }
+
+ SetUpdateTimeout( 0 );
+
+ // and now register by this or other found Pseudo-Object
+ AddDataAdvise( pLink, SotExchange::GetFormatMimeType( pLink->GetContentType()), 0 );
+ return true;
+}
+
+bool SvFileObject::LoadFile_Impl()
+{
+ // We are still at Loading!!
+ if( bWaitForData || !bLoadAgain || xMed.is() )
+ return false;
+
+ // at the moment on the current DocShell
+ xMed = new SfxMedium( sFileNm, sReferer, StreamMode::STD_READ );
+ SvLinkSource::StreamToLoadFrom aStreamToLoadFrom =
+ getStreamToLoadFrom();
+ xMed->setStreamToLoadFrom(
+ aStreamToLoadFrom.m_xInputStreamToLoadFrom,
+ aStreamToLoadFrom.m_bIsReadOnly);
+
+ if( !bSynchron )
+ {
+ bLoadAgain = bDataReady = false;
+ bWaitForData = true;
+
+ tools::SvRef<SfxMedium> xTmpMed = xMed;
+ xMed->Download( LINK( this, SvFileObject, LoadGrfReady_Impl ) );
+
+ bClearMedium = !xMed.is();
+ if( bClearMedium )
+ xMed = xTmpMed; // If already finished in Download
+ return bDataReady;
+ }
+
+ bWaitForData = true;
+ bDataReady = false;
+ xMed->Download();
+ bLoadAgain = !xMed->IsRemote();
+ bWaitForData = false;
+
+ // Graphic is finished, also send DataChanged of the Status change:
+ SendStateChg_Impl( xMed->GetInStream() && xMed->GetInStream()->GetError()
+ ? sfx2::LinkManager::STATE_LOAD_ERROR : sfx2::LinkManager::STATE_LOAD_OK );
+ return true;
+}
+
+
+/** detect the filter of the given file
+
+ @param _rURL
+ specifies the URL of the file which filter is to detected.<br/>
+ If the URL doesn't denote a valid (existent and accessible) file, the
+ request is silently dropped.
+*/
+static OUString impl_getFilter( const OUString& _rURL )
+{
+ OUString sFilter;
+ if ( _rURL.isEmpty() )
+ return sFilter;
+
+ try
+ {
+ css::uno::Reference< css::document::XTypeDetection > xTypeDetection(
+ ::comphelper::getProcessServiceFactory()->createInstance( "com.sun.star.document.TypeDetection" ),
+ css::uno::UNO_QUERY );
+ if ( xTypeDetection.is() )
+ {
+ utl::MediaDescriptor aDescr;
+ aDescr[ utl::MediaDescriptor::PROP_URL ] <<= _rURL;
+ css::uno::Sequence< css::beans::PropertyValue > aDescrList =
+ aDescr.getAsConstPropertyValueList();
+ OUString sType = xTypeDetection->queryTypeByDescriptor( aDescrList, true );
+ if ( !sType.isEmpty() )
+ {
+ // Honor a selected/detected filter.
+ for (const auto& rDescr : std::as_const(aDescrList))
+ {
+ if (rDescr.Name == "FilterName")
+ {
+ if (rDescr.Value >>= sFilter)
+ break;
+ }
+ }
+ if (sFilter.isEmpty())
+ {
+ css::uno::Reference< css::container::XNameAccess > xTypeCont( xTypeDetection,
+ css::uno::UNO_QUERY );
+ if ( xTypeCont.is() )
+ {
+ /* XXX: for fdo#69948 scenario the sequence returned by
+ * getByName() contains an empty PreferredFilter
+ * property value (since? expected?) */
+ ::comphelper::SequenceAsHashMap lTypeProps( xTypeCont->getByName( sType ) );
+ sFilter = lTypeProps.getUnpackedValueOrDefault(
+ "PreferredFilter", OUString() );
+ }
+ }
+ }
+ }
+ }
+ catch( const css::uno::Exception& )
+ {
+ }
+
+ return sFilter;
+}
+
+void SvFileObject::Edit(weld::Window* pParent, sfx2::SvBaseLink* pLink, const Link<const OUString&, void>& rEndEditHdl)
+{
+ aEndEditLink = rEndEditHdl;
+ OUString sFile, sRange, sTmpFilter;
+ if( !pLink || !pLink->GetLinkManager() )
+ return;
+
+ sfx2::LinkManager::GetDisplayNames( pLink, nullptr, &sFile, &sRange, &sTmpFilter );
+
+ switch( pLink->GetObjType() )
+ {
+ case sfx2::SvBaseLinkObjectType::ClientGraphic:
+ {
+ nType = SvFileObjectType::Graphic; // If not set already
+
+ SvxOpenGraphicDialog aDlg(SfxResId(RID_SVXSTR_EDITGRFLINK), pParent);
+ aDlg.EnableLink(false);
+ aDlg.SetPath( sFile, true );
+ aDlg.SetCurrentFilter( sTmpFilter );
+
+ if( !aDlg.Execute() )
+ {
+ sFile = aDlg.GetPath()
+ + OUStringChar(sfx2::cTokenSeparator)
+ + OUStringChar(sfx2::cTokenSeparator)
+ + aDlg.GetDetectedFilter();
+
+ aEndEditLink.Call( sFile );
+ }
+ else
+ sFile.clear();
+ }
+ break;
+
+ case sfx2::SvBaseLinkObjectType::ClientOle:
+ {
+ nType = SvFileObjectType::Object; // if not set already
+
+ ::sfx2::FileDialogHelper & rFileDlg =
+ pLink->GetInsertFileDialog( OUString() );
+ rFileDlg.SetContext(sfx2::FileDialogHelper::LinkClientOLE);
+ rFileDlg.StartExecuteModal(
+ LINK( this, SvFileObject, DialogClosedHdl ) );
+ }
+ break;
+
+ case sfx2::SvBaseLinkObjectType::ClientFile:
+ {
+ nType = SvFileObjectType::Text; // if not set already
+
+ OUString sFactory;
+ SfxObjectShell* pShell = pLink->GetLinkManager()->GetPersist();
+ if ( pShell )
+ sFactory = pShell->GetFactory().GetFactoryName();
+
+ ::sfx2::FileDialogHelper & rFileDlg =
+ pLink->GetInsertFileDialog(sFactory);
+ rFileDlg.SetContext(sfx2::FileDialogHelper::LinkClientFile);
+ rFileDlg.StartExecuteModal(
+ LINK( this, SvFileObject, DialogClosedHdl ) );
+ }
+ break;
+
+ default:
+ sFile.clear();
+ }
+}
+
+IMPL_LINK_NOARG( SvFileObject, LoadGrfReady_Impl, void*, void )
+{
+ // When we come from here there it can not be an error no more.
+ bLoadError = false;
+ bWaitForData = false;
+
+ if( !bDataReady )
+ {
+ // Graphic is finished, also send DataChanged from Status change
+ bDataReady = true;
+ SendStateChg_Impl( sfx2::LinkManager::STATE_LOAD_OK );
+
+ // and then send the data again
+ NotifyDataChanged();
+ }
+
+ if( bDataReady )
+ {
+ bLoadAgain = true;
+ if( xMed.is() )
+ {
+ xMed->SetDoneLink( Link<void*,void>() );
+ mxDelMed = xMed;
+ nPostUserEventId = Application::PostUserEvent(
+ LINK( this, SvFileObject, DelMedium_Impl ));
+ xMed.clear();
+ }
+ }
+}
+
+IMPL_LINK_NOARG( SvFileObject, DelMedium_Impl, void*, void )
+{
+ nPostUserEventId = nullptr;
+ mxDelMed.clear();
+}
+
+IMPL_LINK( SvFileObject, DialogClosedHdl, sfx2::FileDialogHelper*, _pFileDlg, void )
+{
+ OUString sFile;
+
+ if ( SvFileObjectType::Text == nType || SvFileObjectType::Object == nType )
+ {
+ if ( _pFileDlg && _pFileDlg->GetError() == ERRCODE_NONE )
+ {
+ OUString sURL( _pFileDlg->GetPath() );
+ sFile = sURL + OUStringChar(sfx2::cTokenSeparator)
+ + OUStringChar(sfx2::cTokenSeparator)
+ + impl_getFilter( sURL );
+ }
+ }
+ else
+ {
+ SAL_WARN( "sfx.appl", "SvFileObject::DialogClosedHdl(): wrong file type" );
+ }
+
+ aEndEditLink.Call( sFile );
+}
+
+/*
+ The method determines whether the data-object can be read from a DDE.
+*/
+bool SvFileObject::IsPending() const
+{
+ return SvFileObjectType::Graphic == nType && !bLoadError && bWaitForData;
+}
+
+bool SvFileObject::IsDataComplete() const
+{
+ bool bRet = false;
+ if( SvFileObjectType::Graphic != nType )
+ bRet = true;
+ else if( !bLoadError && !bWaitForData )
+ {
+ SvFileObject* pThis = const_cast<SvFileObject*>(this);
+ if( bDataReady ||
+ ( bSynchron && pThis->LoadFile_Impl() && xMed.is() ) )
+ bRet = true;
+ else
+ {
+ INetURLObject aUrl( sFileNm );
+ if( aUrl.HasError() ||
+ INetProtocol::NotValid == aUrl.GetProtocol() )
+ bRet = true;
+ }
+ }
+ return bRet;
+}
+
+
+void SvFileObject::CancelTransfers()
+{
+ // unsubscribe from the cache if in the middle of loading
+ if( !bDataReady )
+ {
+ // Do not set-up again
+ bLoadAgain = false;
+ bDataReady = bLoadError = bWaitForData = true;
+ SendStateChg_Impl( sfx2::LinkManager::STATE_LOAD_ABORT );
+ }
+}
+
+
+void SvFileObject::SendStateChg_Impl( sfx2::LinkManager::LinkState nState )
+{
+ if( !bStateChangeCalled && HasDataLinks() )
+ {
+ DataChanged( SotExchange::GetFormatName(
+ sfx2::LinkManager::RegisterStatusInfoId()), css::uno::Any(OUString::number( nState )) );
+ bStateChangeCalled = true;
+ }
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/appl/fileobj.hxx b/sfx2/source/appl/fileobj.hxx
new file mode 100644
index 000000000..7362f6b1a
--- /dev/null
+++ b/sfx2/source/appl/fileobj.hxx
@@ -0,0 +1,82 @@
+/* -*- 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_APPL_FILEOBJ_HXX
+#define INCLUDED_SFX2_SOURCE_APPL_FILEOBJ_HXX
+
+#include <sfx2/linksrc.hxx>
+#include <sfx2/docfile.hxx>
+#include <sfx2/linkmgr.hxx>
+
+class Graphic;
+struct ImplSVEvent;
+namespace sfx2 { class FileDialogHelper; }
+
+enum class SvFileObjectType;
+
+class SvFileObject : public sfx2::SvLinkSource
+{
+ OUString sFileNm;
+ OUString sFilter;
+ OUString sReferer;
+ Link<const OUString&, void> aEndEditLink;
+ tools::SvRef<SfxMedium> xMed;
+ ImplSVEvent* nPostUserEventId;
+ tools::SvRef<SfxMedium> mxDelMed;
+
+ SvFileObjectType nType;
+
+ bool bLoadAgain : 1;
+ bool bSynchron : 1;
+ bool bLoadError : 1;
+ bool bWaitForData : 1;
+ bool bDataReady : 1;
+ bool bClearMedium : 1;
+ bool bStateChangeCalled : 1;
+
+ bool LoadFile_Impl();
+ void SendStateChg_Impl( sfx2::LinkManager::LinkState nState );
+
+ DECL_LINK( DelMedium_Impl, void*, void );
+ DECL_LINK( LoadGrfReady_Impl, void*, void );
+ DECL_LINK( DialogClosedHdl, sfx2::FileDialogHelper*, void );
+
+protected:
+ virtual ~SvFileObject() override;
+
+public:
+ SvFileObject();
+
+ virtual bool GetData( css::uno::Any & rData /*out param*/,
+ const OUString & rMimeType,
+ bool bSynchron = false ) override;
+
+ virtual bool Connect( sfx2::SvBaseLink* ) override;
+ virtual void Edit(weld::Window *, sfx2::SvBaseLink *, const Link<const OUString&, void>& rEndEditHdl) override;
+
+ // Ask whether you can access data directly or whether it has to be triggered
+ virtual bool IsPending() const override;
+ virtual bool IsDataComplete() const override;
+
+ void CancelTransfers();
+};
+
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/appl/flatpak.cxx b/sfx2/source/appl/flatpak.cxx
new file mode 100644
index 000000000..14411dafc
--- /dev/null
+++ b/sfx2/source/appl/flatpak.cxx
@@ -0,0 +1,99 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * 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 <sal/config.h>
+
+#include <cassert>
+#include <cstdlib>
+#include <cstring>
+
+#include <osl/file.hxx>
+#include <osl/thread.h>
+#include <rtl/textcvt.h>
+#include <rtl/ustring.h>
+#include <rtl/ustring.hxx>
+#include <sal/log.hxx>
+#include <sfx2/flatpak.hxx>
+#include <tools/debug.hxx>
+#include <unotools/tempfile.hxx>
+#include <unotools/ucbhelper.hxx>
+
+bool flatpak::isFlatpak() {
+ static auto const flatpak = [] { return std::getenv("LIBO_FLATPAK") != nullptr; }();
+ return flatpak;
+}
+
+namespace {
+
+// Must only be accessed with SolarMutex locked:
+struct {
+ bool created = false;
+ OUString url;
+} temporaryHtmlDirectoryStatus;
+
+}
+
+bool flatpak::createTemporaryHtmlDirectory(OUString ** url) {
+ assert(url != nullptr);
+ DBG_TESTSOLARMUTEX();
+ if (!temporaryHtmlDirectoryStatus.created) {
+ auto const env = std::getenv("XDG_CACHE_HOME");
+ if (env == nullptr) {
+ SAL_WARN("sfx.appl", "LIBO_FLATPAK mode but unset XDG_CACHE_HOME");
+ return false;
+ }
+ OUString path;
+ if (!rtl_convertStringToUString(
+ &path.pData, env, std::strlen(env), osl_getThreadTextEncoding(),
+ (RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_ERROR | RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_ERROR
+ | RTL_TEXTTOUNICODE_FLAGS_INVALID_ERROR)))
+ {
+ SAL_WARN(
+ "sfx.appl",
+ "LIBO_FLATPAK mode failure converting XDG_CACHE_HOME \"" << env << "\" encoding");
+ return false;
+ }
+ OUString parent;
+ auto const err = osl::FileBase::getFileURLFromSystemPath(path, parent);
+ if (err != osl::FileBase::E_None) {
+ SAL_WARN(
+ "sfx.appl",
+ "LIBO_FLATPAK mode failure converting XDG_CACHE_HOME \"" << path << "\" to URL: "
+ << err);
+ return false;
+ }
+ if (!parent.endsWith("/")) {
+ parent += "/";
+ }
+ auto const tmp = utl::TempFile(&parent, true);
+ if (!tmp.IsValid()) {
+ SAL_WARN(
+ "sfx.appl", "LIBO_FLATPAK mode failure creating temp dir at <" << parent << ">");
+ return false;
+ }
+ temporaryHtmlDirectoryStatus.url = tmp.GetURL();
+ temporaryHtmlDirectoryStatus.created = true;
+ }
+ *url = &temporaryHtmlDirectoryStatus.url;
+ return true;
+}
+
+void flatpak::removeTemporaryHtmlDirectory() {
+ DBG_TESTSOLARMUTEX();
+ if (temporaryHtmlDirectoryStatus.created) {
+ if (!utl::UCBContentHelper::Kill(temporaryHtmlDirectoryStatus.url)) {
+ SAL_INFO(
+ "sfx.appl",
+ "LIBO_FLATPAK mode failure removing directory <"
+ << temporaryHtmlDirectoryStatus.url << ">");
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/sfx2/source/appl/fwkhelper.cxx b/sfx2/source/appl/fwkhelper.cxx
new file mode 100644
index 000000000..6a7eee8fb
--- /dev/null
+++ b/sfx2/source/appl/fwkhelper.cxx
@@ -0,0 +1,52 @@
+/* -*- 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/uno/Reference.hxx>
+#include <com/sun/star/frame/XFrame.hpp>
+
+#include <vcl/svapp.hxx>
+
+#include <fwkhelper.hxx>
+#include <workwin.hxx>
+#include <sfx2/frame.hxx>
+
+void RefreshToolbars( css::uno::Reference< css::frame::XFrame > const & xFrame )
+{
+ SolarMutexGuard aGuard;
+ if ( !xFrame.is() )
+ return;
+
+ SfxFrame* pFrame=nullptr;
+ for ( pFrame = SfxFrame::GetFirst(); pFrame; pFrame = SfxFrame::GetNext( *pFrame ) )
+ {
+ if ( pFrame->GetFrameInterface() == xFrame )
+ break;
+ }
+
+ if ( pFrame )
+ {
+ SfxWorkWindow* pWrkWin = pFrame->GetWorkWindow_Impl();
+ if ( pWrkWin )
+ pWrkWin->UpdateObjectBars_Impl();
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/appl/getbasctlfunction.cxx b/sfx2/source/appl/getbasctlfunction.cxx
new file mode 100644
index 000000000..fd7f48730
--- /dev/null
+++ b/sfx2/source/appl/getbasctlfunction.cxx
@@ -0,0 +1,64 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * 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 <cassert>
+
+#include <config_features.h>
+#include <config_options.h>
+#include <osl/module.h>
+#include <osl/module.hxx>
+#include <tools/svlibrary.h>
+
+#include "getbasctlfunction.hxx"
+
+#if HAVE_FEATURE_SCRIPTING
+#ifndef DISABLE_DYNLOADING
+
+extern "C" { static void thisModule() {} }
+
+oslGenericFunction sfx2::getBasctlFunction(char const* name)
+{
+ osl::Module aMod;
+
+ // load basctl module
+ auto const ok = aMod.loadRelative(
+ &thisModule,
+#if ENABLE_MERGELIBS
+ SVLIBRARY("merged")
+#else
+ SVLIBRARY("basctl")
+#endif
+ );
+ assert(ok);
+ (void) ok;
+
+ // get symbol
+ auto pSymbol = aMod.getFunctionSymbol(name);
+ assert(pSymbol);
+ aMod.release();
+
+ return pSymbol;
+}
+
+#endif
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/sfx2/source/appl/getbasctlfunction.hxx b/sfx2/source/appl/getbasctlfunction.hxx
new file mode 100644
index 000000000..5fa4bc557
--- /dev/null
+++ b/sfx2/source/appl/getbasctlfunction.hxx
@@ -0,0 +1,38 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+#include <config_features.h>
+
+#include <osl/module.h>
+
+#ifndef DISABLE_DYNLOADING
+#if HAVE_FEATURE_SCRIPTING
+
+namespace sfx2
+{
+oslGenericFunction getBasctlFunction(char const* name);
+}
+
+#endif
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/sfx2/source/appl/helpdispatch.cxx b/sfx2/source/appl/helpdispatch.cxx
new file mode 100644
index 000000000..7b2467a34
--- /dev/null
+++ b/sfx2/source/appl/helpdispatch.cxx
@@ -0,0 +1,105 @@
+/* -*- 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 "helpdispatch.hxx"
+#include "newhelp.hxx"
+#include <tools/debug.hxx>
+
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::frame;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::util;
+
+// class HelpInterceptor_Impl --------------------------------------------
+
+HelpDispatch_Impl::HelpDispatch_Impl( HelpInterceptor_Impl& _rInterceptor,
+ const css::uno::Reference< css::frame::XDispatch >& _xDisp ) :
+
+ m_rInterceptor ( _rInterceptor ),
+ m_xRealDispatch ( _xDisp )
+
+{
+}
+
+
+HelpDispatch_Impl::~HelpDispatch_Impl()
+{
+}
+
+
+// XDispatch
+
+void SAL_CALL HelpDispatch_Impl::dispatch(
+
+ const URL& aURL, const Sequence< PropertyValue >& aArgs )
+
+{
+ DBG_ASSERT( m_xRealDispatch.is(), "invalid dispatch" );
+
+ // search for a keyword (dispatch from the basic ide)
+ bool bHasKeyword = false;
+ OUString sKeyword;
+ for ( const PropertyValue& rArg : aArgs )
+ {
+ if ( rArg.Name == "HelpKeyword" )
+ {
+ OUString sHelpKeyword;
+ if ( ( rArg.Value >>= sHelpKeyword ) && !sHelpKeyword.isEmpty() )
+ {
+ sKeyword = sHelpKeyword;
+ bHasKeyword = !sKeyword.isEmpty();
+ break;
+ }
+ }
+ }
+
+ // if a keyword was found, then open it
+ SfxHelpWindow_Impl* pHelpWin = m_rInterceptor.GetHelpWindow();
+ DBG_ASSERT( pHelpWin, "invalid HelpWindow" );
+ if ( bHasKeyword )
+ {
+ pHelpWin->OpenKeyword( sKeyword );
+ return;
+ }
+
+ pHelpWin->loadHelpContent(aURL.Complete);
+}
+
+
+void SAL_CALL HelpDispatch_Impl::addStatusListener(
+
+ const Reference< XStatusListener >& xControl, const URL& aURL )
+
+{
+ DBG_ASSERT( m_xRealDispatch.is(), "invalid dispatch" );
+ m_xRealDispatch->addStatusListener( xControl, aURL );
+}
+
+
+void SAL_CALL HelpDispatch_Impl::removeStatusListener(
+
+ const Reference< XStatusListener >& xControl, const URL& aURL )
+
+{
+ DBG_ASSERT( m_xRealDispatch.is(), "invalid dispatch" );
+ m_xRealDispatch->removeStatusListener( xControl, aURL );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/appl/helpdispatch.hxx b/sfx2/source/appl/helpdispatch.hxx
new file mode 100644
index 000000000..b0494c6d0
--- /dev/null
+++ b/sfx2/source/appl/helpdispatch.hxx
@@ -0,0 +1,47 @@
+/* -*- 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_APPL_HELPDISPATCH_HXX
+#define INCLUDED_SFX2_SOURCE_APPL_HELPDISPATCH_HXX
+
+#include <com/sun/star/frame/XDispatch.hpp>
+#include <cppuhelper/implbase.hxx>
+
+#include "helpinterceptor.hxx"
+
+class HelpDispatch_Impl : public ::cppu::WeakImplHelper< css::frame::XDispatch >
+{
+private:
+ HelpInterceptor_Impl& m_rInterceptor;
+ css::uno::Reference< css::frame::XDispatch >
+ m_xRealDispatch;
+
+public:
+ HelpDispatch_Impl( HelpInterceptor_Impl& _rInterceptor,
+ const css::uno::Reference< css::frame::XDispatch >& _xDisp );
+ virtual ~HelpDispatch_Impl() override;
+
+ // XDispatch
+ virtual void SAL_CALL dispatch( const css::util::URL& aURL, const css::uno::Sequence< css::beans::PropertyValue >& aArgs ) override;
+ virtual void SAL_CALL addStatusListener( const css::uno::Reference< css::frame::XStatusListener >& xControl, const css::util::URL& aURL ) override;
+ virtual void SAL_CALL removeStatusListener( const css::uno::Reference< css::frame::XStatusListener >& xControl, const css::util::URL& aURL ) override;
+};
+
+#endif // INCLUDED_SFX2_SOURCE_APPL_HELPDISPATCH_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/appl/helpinterceptor.cxx b/sfx2/source/appl/helpinterceptor.cxx
new file mode 100644
index 000000000..a9ff76101
--- /dev/null
+++ b/sfx2/source/appl/helpinterceptor.cxx
@@ -0,0 +1,262 @@
+/* -*- 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 "helpinterceptor.hxx"
+#include "helpdispatch.hxx"
+#include "newhelp.hxx"
+#include <tools/urlobj.hxx>
+#include <tools/debug.hxx>
+
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::frame;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::util;
+using namespace ::com::sun::star::lang;
+
+HelpInterceptor_Impl::HelpInterceptor_Impl() :
+
+ m_pWindow ( nullptr ),
+ m_nCurPos ( 0 )
+
+{
+}
+
+
+HelpInterceptor_Impl::~HelpInterceptor_Impl()
+{
+}
+
+
+void HelpInterceptor_Impl::addURL( const OUString& rURL )
+{
+ size_t nCount = m_vHistoryUrls.size();
+ if ( nCount && m_nCurPos < ( nCount - 1 ) )
+ {
+ m_vHistoryUrls.erase(
+ m_vHistoryUrls.begin() + m_nCurPos + 1,
+ m_vHistoryUrls.end());
+ }
+ Reference<XFrame> xFrame(m_xIntercepted, UNO_QUERY);
+ Reference<XController> xController;
+ if(xFrame.is())
+ xController = xFrame->getController();
+
+ m_aCurrentURL = rURL;
+ m_vHistoryUrls.emplace_back( rURL );
+ m_nCurPos = m_vHistoryUrls.size() - 1;
+// TODO ?
+ if ( m_xListener.is() )
+ {
+ css::frame::FeatureStateEvent aEvent;
+ URL aURL;
+ aURL.Complete = rURL;
+ aEvent.FeatureURL = aURL;
+ aEvent.Source = static_cast<css::frame::XDispatch*>(this);
+ m_xListener->statusChanged( aEvent );
+ }
+
+ m_pWindow->UpdateToolbox();
+}
+
+
+void HelpInterceptor_Impl::setInterception( const Reference< XFrame >& xFrame )
+{
+ m_xIntercepted.set( xFrame, UNO_QUERY );
+
+ if ( m_xIntercepted.is() )
+ m_xIntercepted->registerDispatchProviderInterceptor( static_cast<XDispatchProviderInterceptor*>(this) );
+}
+
+
+bool HelpInterceptor_Impl::HasHistoryPred() const
+{
+ return m_nCurPos > 0;
+}
+
+bool HelpInterceptor_Impl::HasHistorySucc() const
+{
+ return m_nCurPos < ( m_vHistoryUrls.size() - 1 );
+}
+
+
+// XDispatchProvider
+
+Reference< XDispatch > SAL_CALL HelpInterceptor_Impl::queryDispatch(
+
+ const URL& aURL, const OUString& aTargetFrameName, sal_Int32 nSearchFlags )
+
+{
+ Reference< XDispatch > xResult;
+ if ( m_xSlaveDispatcher.is() )
+ xResult = m_xSlaveDispatcher->queryDispatch( aURL, aTargetFrameName, nSearchFlags );
+
+ bool bHelpURL = aURL.Complete.toAsciiLowerCase().match("vnd.sun.star.help",0);
+
+ if ( bHelpURL )
+ {
+ DBG_ASSERT( xResult.is(), "invalid dispatch" );
+ xResult = new HelpDispatch_Impl( *this, xResult );
+ }
+
+ return xResult;
+}
+
+
+Sequence < Reference < XDispatch > > SAL_CALL HelpInterceptor_Impl::queryDispatches(
+
+ const Sequence< DispatchDescriptor >& aDescripts )
+
+{
+ Sequence< Reference< XDispatch > > aReturn( aDescripts.getLength() );
+ std::transform(aDescripts.begin(), aDescripts.end(), aReturn.getArray(),
+ [this](const DispatchDescriptor& rDescr) -> Reference<XDispatch> {
+ return queryDispatch(rDescr.FeatureURL, rDescr.FrameName, rDescr.SearchFlags); });
+ return aReturn;
+}
+
+
+// XDispatchProviderInterceptor
+
+Reference< XDispatchProvider > SAL_CALL HelpInterceptor_Impl::getSlaveDispatchProvider()
+
+{
+ return m_xSlaveDispatcher;
+}
+
+
+void SAL_CALL HelpInterceptor_Impl::setSlaveDispatchProvider( const Reference< XDispatchProvider >& xNewSlave )
+
+{
+ m_xSlaveDispatcher = xNewSlave;
+}
+
+
+Reference< XDispatchProvider > SAL_CALL HelpInterceptor_Impl::getMasterDispatchProvider()
+
+{
+ return m_xMasterDispatcher;
+}
+
+
+void SAL_CALL HelpInterceptor_Impl::setMasterDispatchProvider( const Reference< XDispatchProvider >& xNewMaster )
+
+{
+ m_xMasterDispatcher = xNewMaster;
+}
+
+
+// XInterceptorInfo
+
+Sequence< OUString > SAL_CALL HelpInterceptor_Impl::getInterceptedURLs()
+
+{
+ Sequence<OUString> aURLList { "vnd.sun.star.help://*" };
+ return aURLList;
+}
+
+
+// XDispatch
+
+void SAL_CALL HelpInterceptor_Impl::dispatch(
+ const URL& aURL, const Sequence< css::beans::PropertyValue >& )
+{
+ bool bBack = aURL.Complete == ".uno:Backward";
+ if ( !bBack && aURL.Complete != ".uno:Forward" )
+ return;
+
+ if ( m_vHistoryUrls.empty() )
+ return;
+
+ size_t nPos = ( bBack && m_nCurPos > 0 ) ? --m_nCurPos
+ : ( !bBack && m_nCurPos < m_vHistoryUrls.size() - 1 )
+ ? ++m_nCurPos
+ : std::numeric_limits<std::size_t>::max();
+
+ if ( nPos < std::numeric_limits<std::size_t>::max() )
+ {
+ m_pWindow->loadHelpContent(m_vHistoryUrls[nPos], false); // false => don't add item to history again!
+ }
+
+ m_pWindow->UpdateToolbox();
+}
+
+
+void SAL_CALL HelpInterceptor_Impl::addStatusListener(
+ const Reference< XStatusListener >& xControl, const URL& )
+{
+ DBG_ASSERT( !m_xListener.is(), "listener already exists" );
+ m_xListener = xControl;
+}
+
+
+void SAL_CALL HelpInterceptor_Impl::removeStatusListener(
+ const Reference< XStatusListener >&, const URL&)
+{
+ m_xListener = nullptr;
+}
+
+// HelpListener_Impl -----------------------------------------------------
+
+HelpListener_Impl::HelpListener_Impl( HelpInterceptor_Impl* pInter )
+{
+ pInterceptor = pInter;
+ pInterceptor->addStatusListener( this, css::util::URL() );
+}
+
+
+void SAL_CALL HelpListener_Impl::statusChanged( const css::frame::FeatureStateEvent& Event )
+{
+ INetURLObject aObj( Event.FeatureURL.Complete );
+ aFactory = aObj.GetHost();
+ aChangeLink.Call( *this );
+}
+
+
+void SAL_CALL HelpListener_Impl::disposing( const css::lang::EventObject& )
+{
+ pInterceptor->removeStatusListener( this, css::util::URL() );
+ pInterceptor = nullptr;
+}
+
+HelpStatusListener_Impl::HelpStatusListener_Impl(
+ Reference < XDispatch > const & aDispatch, URL const & rURL)
+{
+ aDispatch->addStatusListener(this, rURL);
+}
+
+HelpStatusListener_Impl::~HelpStatusListener_Impl()
+{
+ if(xDispatch.is())
+ xDispatch->removeStatusListener(this, css::util::URL());
+}
+
+void HelpStatusListener_Impl::statusChanged(
+ const FeatureStateEvent& rEvent )
+{
+ aStateEvent = rEvent;
+}
+
+void HelpStatusListener_Impl::disposing( const EventObject& )
+{
+ xDispatch->removeStatusListener(this, css::util::URL());
+ xDispatch = nullptr;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/appl/helpinterceptor.hxx b/sfx2/source/appl/helpinterceptor.hxx
new file mode 100644
index 000000000..2f08b1ac3
--- /dev/null
+++ b/sfx2/source/appl/helpinterceptor.hxx
@@ -0,0 +1,141 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#ifndef INCLUDED_SFX2_SOURCE_APPL_HELPINTERCEPTOR_HXX
+#define INCLUDED_SFX2_SOURCE_APPL_HELPINTERCEPTOR_HXX
+
+#include <com/sun/star/frame/XDispatchProviderInterceptor.hpp>
+#include <com/sun/star/frame/XInterceptorInfo.hpp>
+#include <com/sun/star/frame/XDispatch.hpp>
+#include <com/sun/star/frame/XDispatchProviderInterception.hpp>
+#include <com/sun/star/frame/XFrame.hpp>
+#include <cppuhelper/implbase.hxx>
+#include <com/sun/star/frame/XStatusListener.hpp>
+#include <tools/link.hxx>
+#include <vcl/vclptr.hxx>
+#include "newhelp.hxx"
+#include <vector>
+
+class SfxHelpWindow_Impl;
+class HelpInterceptor_Impl : public ::cppu::WeakImplHelper<
+ css::frame::XDispatchProviderInterceptor,
+ css::frame::XInterceptorInfo,
+ css::frame::XDispatch >
+
+{
+private:
+friend class HelpDispatch_Impl;
+friend class SfxHelpWindow_Impl;
+
+ // the component which's dispatches we're intercepting
+ css::uno::Reference< css::frame::XDispatchProviderInterception > m_xIntercepted;
+
+ // chaining
+ css::uno::Reference< css::frame::XDispatchProvider > m_xSlaveDispatcher;
+ css::uno::Reference< css::frame::XDispatchProvider > m_xMasterDispatcher;
+
+ css::uno::Reference< css::frame::XStatusListener > m_xListener;
+
+ std::vector<OUString> m_vHistoryUrls;
+ VclPtr<SfxHelpWindow_Impl> m_pWindow;
+ size_t m_nCurPos;
+ OUString m_aCurrentURL;
+
+ void addURL( const OUString& rURL );
+
+public:
+ HelpInterceptor_Impl();
+ virtual ~HelpInterceptor_Impl() override;
+
+ void setInterception( const css::uno::Reference< css::frame::XFrame >& xFrame );
+ const OUString& GetCurrentURL() const { return m_aCurrentURL; }
+
+ bool HasHistoryPred() const; // is there a predecessor for the current in the history
+ bool HasHistorySucc() const; // is there a successor for the current in the history
+
+ // XDispatchProvider
+ virtual css::uno::Reference< css::frame::XDispatch > SAL_CALL
+ queryDispatch( const css::util::URL& aURL, const OUString& aTargetFrameName, sal_Int32 nSearchFlags ) override;
+ virtual css::uno::Sequence< css::uno::Reference< css::frame::XDispatch > > SAL_CALL
+ queryDispatches( const css::uno::Sequence< css::frame::DispatchDescriptor >& aDescripts ) override;
+
+ // XDispatchProviderInterceptor
+ virtual css::uno::Reference< css::frame::XDispatchProvider > SAL_CALL
+ getSlaveDispatchProvider( ) override;
+ virtual void SAL_CALL setSlaveDispatchProvider( const css::uno::Reference< css::frame::XDispatchProvider >& xNewSlave ) override;
+ virtual css::uno::Reference< css::frame::XDispatchProvider > SAL_CALL
+ getMasterDispatchProvider( ) override;
+ virtual void SAL_CALL setMasterDispatchProvider( const css::uno::Reference< css::frame::XDispatchProvider >& xNewMaster ) override;
+
+ // XInterceptorInfo
+ virtual css::uno::Sequence< OUString > SAL_CALL
+ getInterceptedURLs( ) override;
+
+ // XDispatch
+ virtual void SAL_CALL dispatch( const css::util::URL& aURL, const css::uno::Sequence< css::beans::PropertyValue >& aArgs ) override;
+ virtual void SAL_CALL addStatusListener( const css::uno::Reference< css::frame::XStatusListener >& xControl, const css::util::URL& aURL ) override;
+ virtual void SAL_CALL removeStatusListener( const css::uno::Reference< css::frame::XStatusListener >& xControl, const css::util::URL& aURL ) override;
+
+ // extras
+ void InitWaiter( SfxHelpWindow_Impl* pWindow )
+ { m_pWindow = pWindow; }
+ SfxHelpWindow_Impl* GetHelpWindow() const { return m_pWindow; }
+};
+
+// HelpListener_Impl -----------------------------------------------------
+
+class HelpListener_Impl : public ::cppu::WeakImplHelper< css::frame::XStatusListener >
+{
+private:
+ HelpInterceptor_Impl* pInterceptor;
+ Link<HelpListener_Impl&,void> aChangeLink;
+ OUString aFactory;
+
+public:
+ explicit HelpListener_Impl( HelpInterceptor_Impl* pInter );
+
+ virtual void SAL_CALL statusChanged( const css::frame::FeatureStateEvent& Event ) override;
+ virtual void SAL_CALL disposing( const css::lang::EventObject& obj ) override;
+
+ void SetChangeHdl( const Link<HelpListener_Impl&,void>& rLink ) { aChangeLink = rLink; }
+ const OUString& GetFactory() const { return aFactory; }
+};
+// HelpStatusListener_Impl -----------------------------------------------------
+
+class HelpStatusListener_Impl : public cppu::WeakImplHelper< css::frame::XStatusListener >
+{
+private:
+ css::uno::Reference < css::frame::XDispatch > xDispatch;
+ css::frame::FeatureStateEvent aStateEvent;
+
+public:
+ HelpStatusListener_Impl(
+ css::uno::Reference < css::frame::XDispatch > const & xDispatch,
+ css::util::URL const & rURL);
+ virtual ~HelpStatusListener_Impl() override;
+
+ virtual void SAL_CALL statusChanged( const css::frame::FeatureStateEvent& Event ) override;
+ virtual void SAL_CALL disposing( const css::lang::EventObject& obj ) override;
+ const css::frame::FeatureStateEvent&
+ GetStateEvent() const {return aStateEvent;}
+};
+
+
+#endif // INCLUDED_SFX2_SOURCE_APPL_HELPINTERCEPTOR_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/appl/impldde.cxx b/sfx2/source/appl/impldde.cxx
new file mode 100644
index 000000000..c70cb4cb6
--- /dev/null
+++ b/sfx2/source/appl/impldde.cxx
@@ -0,0 +1,348 @@
+/* -*- 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 .
+ */
+
+
+#if defined(_WIN32)
+#include <prewin.h>
+#include <postwin.h>
+#endif
+
+#include "impldde.hxx"
+
+#include <vcl/weld.hxx>
+#include <sot/exchange.hxx>
+#include <rtl/ustring.hxx>
+
+#include <sfx2/lnkbase.hxx>
+#include <sfx2/linkmgr.hxx>
+
+#include <com/sun/star/uno/Any.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+
+#include <svl/svdde.hxx>
+#include <sot/formats.hxx>
+
+using namespace ::com::sun::star::uno;
+
+namespace sfx2
+{
+
+namespace {
+
+class SvDDELinkEditDialog : public weld::GenericDialogController
+{
+ std::unique_ptr<weld::Entry> m_xEdDdeApp;
+ std::unique_ptr<weld::Entry> m_xEdDdeTopic;
+ std::unique_ptr<weld::Entry> m_xEdDdeItem;
+ std::unique_ptr<weld::Button> m_xOKButton;
+
+ DECL_LINK(EditHdl_Impl, weld::Entry&, void);
+public:
+ SvDDELinkEditDialog(weld::Window* pParent, SvBaseLink const*);
+ OUString GetCmd() const;
+};
+
+}
+
+SvDDELinkEditDialog::SvDDELinkEditDialog(weld::Window* pParent, SvBaseLink const * pLink)
+ : GenericDialogController(pParent, "sfx/ui/linkeditdialog.ui", "LinkEditDialog")
+ , m_xEdDdeApp(m_xBuilder->weld_entry("app"))
+ , m_xEdDdeTopic(m_xBuilder->weld_entry("file"))
+ , m_xEdDdeItem(m_xBuilder->weld_entry("category"))
+ , m_xOKButton(m_xBuilder->weld_button("ok"))
+{
+ OUString sServer, sTopic, sItem;
+ sfx2::LinkManager::GetDisplayNames( pLink, &sServer, &sTopic, &sItem );
+
+ m_xEdDdeApp->set_text( sServer );
+ m_xEdDdeTopic->set_text( sTopic );
+ m_xEdDdeItem->set_text( sItem );
+
+ m_xEdDdeApp->connect_changed( LINK( this, SvDDELinkEditDialog, EditHdl_Impl));
+ m_xEdDdeTopic->connect_changed( LINK( this, SvDDELinkEditDialog, EditHdl_Impl));
+ m_xEdDdeItem->connect_changed( LINK( this, SvDDELinkEditDialog, EditHdl_Impl));
+
+ m_xOKButton->set_sensitive(!sServer.isEmpty() && !sTopic.isEmpty() && !sItem.isEmpty());
+}
+
+OUString SvDDELinkEditDialog::GetCmd() const
+{
+ OUString sCmd( m_xEdDdeApp->get_text() ), sRet;
+ ::sfx2::MakeLnkName( sRet, &sCmd, m_xEdDdeTopic->get_text(), m_xEdDdeItem->get_text() );
+ return sRet;
+}
+
+IMPL_LINK_NOARG( SvDDELinkEditDialog, EditHdl_Impl, weld::Entry&, void)
+{
+ m_xOKButton->set_sensitive(!m_xEdDdeApp->get_text().isEmpty() &&
+ !m_xEdDdeTopic->get_text().isEmpty() &&
+ !m_xEdDdeItem->get_text().isEmpty() );
+}
+
+SvDDEObject::SvDDEObject()
+ : pGetData( nullptr )
+{
+ SetUpdateTimeout( 100 );
+ bWaitForData = false;
+}
+
+SvDDEObject::~SvDDEObject()
+{
+ pLink.reset();
+ pRequest.reset();
+ pConnection.reset();
+}
+
+bool SvDDEObject::GetData( css::uno::Any & rData /*out param*/,
+ const OUString & rMimeType,
+ bool bSynchron )
+{
+ if( !pConnection )
+ return false;
+
+ if( pConnection->GetError() ) // then we try once more
+ {
+ OUString sServer( pConnection->GetServiceName() );
+ OUString sTopic( pConnection->GetTopicName() );
+
+ pConnection.reset( new DdeConnection( sServer, sTopic ) );
+ }
+
+ if( bWaitForData ) // we are in a recursive loop, get out again
+ return false;
+
+ // Lock against Reentrance
+ bWaitForData = true;
+
+ // if you want to print, we'll wait until the data is available
+ if( bSynchron )
+ {
+ DdeRequest aReq( *pConnection, sItem, 5000 );
+ aReq.SetDataHdl( LINK( this, SvDDEObject, ImplGetDDEData ) );
+ aReq.SetFormat( SotExchange::GetFormatIdFromMimeType( rMimeType ));
+
+ pGetData = &rData;
+
+ do {
+ aReq.Execute();
+ } while( aReq.GetError() && ImplHasOtherFormat( aReq ) );
+
+ bWaitForData = false;
+ }
+ else
+ {
+ // otherwise it will be executed asynchronously
+ {
+ pRequest.reset( new DdeRequest( *pConnection, sItem ) );
+ pRequest->SetDataHdl( LINK( this, SvDDEObject, ImplGetDDEData ) );
+ pRequest->SetDoneHdl( LINK( this, SvDDEObject, ImplDoneDDEData ) );
+ pRequest->SetFormat( SotExchange::GetFormatIdFromMimeType(
+ rMimeType ) );
+ pRequest->Execute();
+ }
+
+ rData <<= OUString();
+ }
+ return 0 == pConnection->GetError();
+}
+
+
+bool SvDDEObject::Connect( SvBaseLink * pSvLink )
+{
+ SfxLinkUpdateMode nLinkType = pSvLink->GetUpdateMode();
+ if( pConnection ) // Connection is already made
+ {
+ // well, then just add it as dependent
+ AddDataAdvise( pSvLink,
+ SotExchange::GetFormatMimeType( pSvLink->GetContentType()),
+ SfxLinkUpdateMode::ONCALL == nLinkType
+ ? ADVISEMODE_ONLYONCE
+ : 0 );
+ AddConnectAdvise( pSvLink );
+
+ return true;
+ }
+
+ if( !pSvLink->GetLinkManager() )
+ return false;
+
+ OUString sServer, sTopic;
+ sfx2::LinkManager::GetDisplayNames( pSvLink, &sServer, &sTopic, &sItem );
+
+ if( sServer.isEmpty() || sTopic.isEmpty() || sItem.isEmpty() )
+ return false;
+
+ pConnection.reset( new DdeConnection( sServer, sTopic ) );
+ if( pConnection->GetError() )
+ {
+ // check if the DDE server knows the "SYSTEM" topic
+ bool bSysTopic = false;
+ if (!sTopic.equalsIgnoreAsciiCase("SYSTEM"))
+ {
+ DdeConnection aTmp(sServer, "SYSTEM");
+ bSysTopic = !aTmp.GetError();
+ }
+
+ if( bSysTopic )
+ {
+ // if the system topic works then the server is up but just doesn't know the original topic
+ return false;
+ }
+ }
+
+ if( SfxLinkUpdateMode::ALWAYS == nLinkType && !pLink && !pConnection->GetError() )
+ {
+ // Setting up Hot Link, Data will be available at some point later on
+ pLink.reset( new DdeHotLink( *pConnection, sItem ) );
+ pLink->SetDataHdl( LINK( this, SvDDEObject, ImplGetDDEData ) );
+ pLink->SetDoneHdl( LINK( this, SvDDEObject, ImplDoneDDEData ) );
+ pLink->SetFormat( pSvLink->GetContentType() );
+ pLink->Execute();
+ }
+
+ if( pConnection->GetError() )
+ return false;
+
+ AddDataAdvise( pSvLink,
+ SotExchange::GetFormatMimeType( pSvLink->GetContentType()),
+ SfxLinkUpdateMode::ONCALL == nLinkType
+ ? ADVISEMODE_ONLYONCE
+ : 0 );
+ AddConnectAdvise( pSvLink );
+ SetUpdateTimeout( 0 );
+ return true;
+}
+
+void SvDDEObject::Edit(weld::Window* pParent, sfx2::SvBaseLink* pBaseLink, const Link<const OUString&, void>& rEndEditHdl)
+{
+ SvDDELinkEditDialog aDlg(pParent, pBaseLink);
+ if (RET_OK == aDlg.run() && rEndEditHdl.IsSet())
+ {
+ OUString sCommand = aDlg.GetCmd();
+ rEndEditHdl.Call( sCommand );
+ }
+}
+
+bool SvDDEObject::ImplHasOtherFormat( DdeTransaction& rReq )
+{
+ SotClipboardFormatId nFmt = SotClipboardFormatId::NONE;
+ switch( rReq.GetFormat() )
+ {
+ case SotClipboardFormatId::RTF:
+ nFmt = SotClipboardFormatId::STRING;
+ break;
+
+ case SotClipboardFormatId::HTML_SIMPLE:
+ case SotClipboardFormatId::HTML:
+ nFmt = SotClipboardFormatId::RTF;
+ break;
+
+ case SotClipboardFormatId::GDIMETAFILE:
+ nFmt = SotClipboardFormatId::BITMAP;
+ break;
+
+ case SotClipboardFormatId::SVXB:
+ nFmt = SotClipboardFormatId::GDIMETAFILE;
+ break;
+
+ // something else?
+ default: break;
+ }
+ if( nFmt != SotClipboardFormatId::NONE )
+ rReq.SetFormat( nFmt ); // try it once more
+ return SotClipboardFormatId::NONE != nFmt;
+}
+
+bool SvDDEObject::IsPending() const
+/*
+ The method determines whether the data-object can be read from a DDE.
+*/
+{
+ return bWaitForData;
+}
+
+bool SvDDEObject::IsDataComplete() const
+{
+ return bWaitForData;
+}
+
+IMPL_LINK( SvDDEObject, ImplGetDDEData, const DdeData*, pData, void )
+{
+ SotClipboardFormatId nFmt = pData->GetFormat();
+ switch( nFmt )
+ {
+ case SotClipboardFormatId::GDIMETAFILE:
+ break;
+
+ case SotClipboardFormatId::BITMAP:
+ break;
+
+ default:
+ {
+ const char* p = static_cast<char const *>(pData->getData());
+ tools::Long nLen = SotClipboardFormatId::STRING == nFmt ? (p ? strlen( p ) : 0) : pData->getSize();
+
+ Sequence< sal_Int8 > aSeq( reinterpret_cast<const sal_Int8*>(p), nLen );
+ if( pGetData )
+ {
+ *pGetData <<= aSeq; // Copy Data
+ pGetData = nullptr; // reset the pointer here
+ }
+ else
+ {
+ Any aVal;
+ aVal <<= aSeq;
+ DataChanged( SotExchange::GetFormatMimeType(
+ pData->GetFormat() ), aVal );
+ bWaitForData = false;
+ }
+ }
+ }
+}
+
+IMPL_LINK( SvDDEObject, ImplDoneDDEData, bool, bValid, void )
+{
+ if( !bValid && ( pRequest || pLink ))
+ {
+ DdeTransaction* pReq = nullptr;
+ if( !pLink || ( pLink && pLink->IsBusy() ))
+ pReq = pRequest.get(); // only the one that is ready
+ else if( pRequest && pRequest->IsBusy() )
+ pReq = pLink.get(); // only the one that is ready
+
+ if( pReq )
+ {
+ if( ImplHasOtherFormat( *pReq ) )
+ {
+ pReq->Execute();
+ }
+ else if( pReq == pRequest.get() )
+ {
+ bWaitForData = false;
+ }
+ }
+ }
+ else
+ // End waiting
+ bWaitForData = false;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/appl/impldde.hxx b/sfx2/source/appl/impldde.hxx
new file mode 100644
index 000000000..ee2f2ecb9
--- /dev/null
+++ b/sfx2/source/appl/impldde.hxx
@@ -0,0 +1,72 @@
+/* -*- 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_APPL_IMPLDDE_HXX
+#define INCLUDED_SFX2_SOURCE_APPL_IMPLDDE_HXX
+
+#include <rtl/ustring.hxx>
+#include <sfx2/linksrc.hxx>
+#include <tools/link.hxx>
+
+class DdeConnection;
+class DdeData;
+class DdeLink;
+class DdeRequest;
+class DdeTransaction;
+
+namespace sfx2
+{
+
+class SvDDEObject : public SvLinkSource
+{
+ OUString sItem;
+
+ std::unique_ptr<DdeConnection> pConnection;
+ std::unique_ptr<DdeLink> pLink;
+ std::unique_ptr<DdeRequest> pRequest;
+ css::uno::Any * pGetData;
+
+ bool bWaitForData; // waiting for data?
+
+
+ static bool ImplHasOtherFormat( DdeTransaction& );
+ DECL_LINK( ImplGetDDEData, const DdeData*, void );
+ DECL_LINK( ImplDoneDDEData, bool, void );
+
+protected:
+ virtual ~SvDDEObject() override;
+
+public:
+ SvDDEObject();
+
+ virtual bool GetData( css::uno::Any & rData /*out param*/,
+ const OUString & aMimeType,
+ bool bSynchron = false ) override;
+
+ virtual bool Connect( SvBaseLink * ) override;
+ virtual void Edit(weld::Window* pParent, sfx2::SvBaseLink* pBaseLink, const Link<const OUString&, void>& rEndEditHdl) override;
+
+ virtual bool IsPending() const override;
+ virtual bool IsDataComplete() const override;
+};
+
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/appl/linkmgr2.cxx b/sfx2/source/appl/linkmgr2.cxx
new file mode 100644
index 000000000..977ed9851
--- /dev/null
+++ b/sfx2/source/appl/linkmgr2.cxx
@@ -0,0 +1,714 @@
+/* -*- 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 <sfx2/linkmgr.hxx>
+#include <sfx2/sfxsids.hrc>
+#include <com/sun/star/document/UpdateDocMode.hpp>
+#include <osl/file.hxx>
+#include <sfx2/objsh.hxx>
+#include <svl/urihelper.hxx>
+#include <sot/formats.hxx>
+#include <tools/urlobj.hxx>
+#include <sot/exchange.hxx>
+#include <tools/debug.hxx>
+#include <vcl/filter/SvmReader.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+#include <vcl/gdimtf.hxx>
+#include <sfx2/lnkbase.hxx>
+#include <sfx2/app.hxx>
+#include <vcl/graph.hxx>
+#include <svl/stritem.hxx>
+#include <svl/eitem.hxx>
+#include <svl/intitem.hxx>
+#include <i18nlangtag/languagetag.hxx>
+#include <vcl/dibtools.hxx>
+#include <unotools/charclass.hxx>
+#include <unotools/securityoptions.hxx>
+#include <vcl/GraphicLoader.hxx>
+#include <vcl/TypeSerializer.hxx>
+
+#include "fileobj.hxx"
+#include "impldde.hxx"
+#include <sfx2/strings.hrc>
+#include <sfx2/sfxresid.hxx>
+
+#include <com/sun/star/lang/XComponent.hpp>
+#include <com/sun/star/util/XCloseable.hpp>
+
+using ::com::sun::star::uno::UNO_QUERY;
+using ::com::sun::star::uno::Reference;
+using ::com::sun::star::lang::XComponent;
+using ::com::sun::star::util::XCloseable;
+
+namespace sfx2
+{
+
+namespace {
+
+class SvxInternalLink : public sfx2::SvLinkSource
+{
+public:
+ SvxInternalLink() {}
+
+ virtual bool Connect( sfx2::SvBaseLink* ) override;
+};
+
+}
+
+LinkManager::LinkManager(SfxObjectShell* p)
+ : pPersist( p )
+{
+}
+
+LinkManager::~LinkManager()
+{
+ for(tools::SvRef<SvBaseLink> & rTmp : aLinkTbl)
+ {
+ if( rTmp.is() )
+ {
+ rTmp->Disconnect();
+ rTmp->SetLinkManager( nullptr );
+ }
+ }
+}
+
+void LinkManager::InsertCachedComp(const Reference<XComponent>& xComp)
+{
+ maCachedComps.push_back(xComp);
+}
+
+void LinkManager::CloseCachedComps()
+{
+ for (const auto& rxCachedComp : maCachedComps)
+ {
+ Reference<XCloseable> xCloseable(rxCachedComp, UNO_QUERY);
+ if (!xCloseable.is())
+ continue;
+
+ xCloseable->close(true);
+ }
+ maCachedComps.clear();
+}
+
+void LinkManager::Remove( SvBaseLink const *pLink )
+{
+ // No duplicate links inserted
+ bool bFound = false;
+ for( size_t n = 0; n < aLinkTbl.size(); )
+ {
+ tools::SvRef<SvBaseLink>& rTmp = aLinkTbl[ n ];
+ if( pLink == rTmp.get() )
+ {
+ rTmp->Disconnect();
+ rTmp->SetLinkManager( nullptr );
+ rTmp.clear();
+ bFound = true;
+ }
+
+ // Remove empty ones if they exist
+ if( !rTmp.is() )
+ {
+ aLinkTbl.erase( aLinkTbl.begin() + n );
+ if( bFound )
+ return ;
+ }
+ else
+ ++n;
+ }
+}
+
+void LinkManager::Remove( size_t nPos, size_t nCnt )
+{
+ if( !nCnt || nPos >= aLinkTbl.size() )
+ return;
+
+ if (sal::static_int_cast<size_t>(nPos + nCnt) > aLinkTbl.size())
+ nCnt = aLinkTbl.size() - nPos;
+
+ for( size_t n = nPos; n < nPos + nCnt; ++n)
+ {
+ tools::SvRef<SvBaseLink>& rTmp = aLinkTbl[ n ];
+ if( rTmp.is() )
+ {
+ rTmp->Disconnect();
+ rTmp->SetLinkManager( nullptr );
+ }
+ }
+ aLinkTbl.erase( aLinkTbl.begin() + nPos, aLinkTbl.begin() + nPos + nCnt );
+}
+
+bool LinkManager::Insert( SvBaseLink* pLink )
+{
+ for( size_t n = 0; n < aLinkTbl.size(); ++n )
+ {
+ tools::SvRef<SvBaseLink>& rTmp = aLinkTbl[ n ];
+ if( !rTmp.is() )
+ {
+ aLinkTbl.erase( aLinkTbl.begin() + n-- );
+ }
+ else if( pLink == rTmp.get() )
+ return false; // No duplicate links inserted
+ }
+
+ pLink->SetLinkManager( this );
+ aLinkTbl.emplace_back(pLink );
+ return true;
+}
+
+bool LinkManager::InsertLink( SvBaseLink * pLink,
+ SvBaseLinkObjectType nObjType,
+ SfxLinkUpdateMode nUpdateMode,
+ const OUString* pName )
+{
+ // This First
+ pLink->SetObjType( nObjType );
+ if( pName )
+ pLink->SetName( *pName );
+ pLink->SetUpdateMode( nUpdateMode );
+ return Insert( pLink );
+}
+
+void LinkManager::InsertDDELink( SvBaseLink * pLink,
+ const OUString& rServer,
+ std::u16string_view rTopic,
+ std::u16string_view rItem )
+{
+ if( !isClientType( pLink->GetObjType() ) )
+ return;
+
+ OUString sCmd;
+ ::sfx2::MakeLnkName( sCmd, &rServer, rTopic, rItem );
+
+ pLink->SetObjType( SvBaseLinkObjectType::ClientDde );
+ pLink->SetName( sCmd );
+ Insert( pLink );
+}
+
+void LinkManager::InsertDDELink( SvBaseLink * pLink )
+{
+ DBG_ASSERT( isClientType(pLink->GetObjType()), "no OBJECT_CLIENT_SO" );
+ if( !isClientType( pLink->GetObjType() ) )
+ return;
+
+ if( pLink->GetObjType() == SvBaseLinkObjectType::ClientSo )
+ pLink->SetObjType( SvBaseLinkObjectType::ClientDde );
+
+ Insert( pLink );
+}
+
+// Obtain the string for the dialog
+bool LinkManager::GetDisplayNames( const SvBaseLink * pLink,
+ OUString* pType,
+ OUString* pFile,
+ OUString* pLinkStr,
+ OUString* pFilter )
+{
+ bool bRet = false;
+ const OUString& sLNm( pLink->GetLinkSourceName() );
+ if( !sLNm.isEmpty() )
+ {
+ switch( pLink->GetObjType() )
+ {
+ case SvBaseLinkObjectType::ClientFile:
+ case SvBaseLinkObjectType::ClientGraphic:
+ case SvBaseLinkObjectType::ClientOle:
+ {
+ sal_Int32 nPos = 0;
+ OUString sFile( sLNm.getToken( 0, ::sfx2::cTokenSeparator, nPos ) );
+ OUString sRange( sLNm.getToken( 0, ::sfx2::cTokenSeparator, nPos ) );
+
+ if( pFile )
+ *pFile = sFile;
+ if( pLinkStr )
+ *pLinkStr = sRange;
+ if( pFilter )
+ *pFilter = nPos == -1 ? OUString() : sLNm.copy(nPos);
+
+ if( pType )
+ {
+ SvBaseLinkObjectType nObjType = pLink->GetObjType();
+ *pType = SfxResId(
+ ( SvBaseLinkObjectType::ClientFile == nObjType || SvBaseLinkObjectType::ClientOle == nObjType )
+ ? RID_SVXSTR_FILELINK
+ : RID_SVXSTR_GRAPHICLINK);
+ }
+ bRet = true;
+ }
+ break;
+ case SvBaseLinkObjectType::ClientDde:
+ {
+ sal_Int32 nTmp = 0;
+ OUString sServer( sLNm.getToken( 0, cTokenSeparator, nTmp ) );
+ OUString sTopic( sLNm.getToken( 0, cTokenSeparator, nTmp ) );
+
+ if( pType )
+ *pType = sServer;
+ if( pFile )
+ *pFile = sTopic;
+ if( pLinkStr )
+ *pLinkStr = nTmp != -1 ? sLNm.copy(nTmp) : OUString();
+ bRet = true;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ return bRet;
+}
+
+void LinkManager::UpdateAllLinks(
+ bool bAskUpdate,
+ bool bUpdateGrfLinks,
+ weld::Window* pParentWin )
+{
+ // First make a copy of the array in order to update links
+ // links in ... no contact between them!
+ std::vector<SvBaseLink*> aTmpArr;
+ for( size_t n = 0; n < aLinkTbl.size(); ++n )
+ {
+ tools::SvRef<SvBaseLink>& rLink = aLinkTbl[ n ];
+ if( !rLink.is() )
+ {
+ Remove( n-- );
+ continue;
+ }
+ aTmpArr.push_back( rLink.get() );
+ }
+
+ for(SvBaseLink* pLink : aTmpArr)
+ {
+ // search first in the array after the entry
+ bool bFound = false;
+ for(const tools::SvRef<SvBaseLink> & i : aLinkTbl)
+ if( pLink == i.get() )
+ {
+ bFound = true;
+ break;
+ }
+
+ if( !bFound )
+ continue; // was not available!
+
+ // Graphic-Links not to update yet
+ if( !pLink->IsVisible() ||
+ ( !bUpdateGrfLinks && SvBaseLinkObjectType::ClientGraphic == pLink->GetObjType() ))
+ continue;
+
+ if( bAskUpdate )
+ {
+ OUString aMsg = SfxResId(STR_QUERY_UPDATE_LINKS);
+ INetURLObject aURL(pPersist->getDocumentBaseURL());
+ aMsg = aMsg.replaceFirst("%{filename}", aURL.GetLastName());
+
+ std::unique_ptr<weld::MessageDialog> xQueryBox(Application::CreateMessageDialog(pParentWin,
+ VclMessageType::Question, VclButtonsType::YesNo, aMsg));
+ xQueryBox->set_default_response(RET_YES);
+
+ int nRet = xQueryBox->run();
+ if( RET_YES != nRet )
+ {
+ SfxObjectShell* pShell = pLink->GetLinkManager()->GetPersist();
+
+ if(pShell)
+ {
+ comphelper::EmbeddedObjectContainer& rEmbeddedObjectContainer = pShell->getEmbeddedObjectContainer();
+ rEmbeddedObjectContainer.setUserAllowsLinkUpdate(false);
+ }
+
+ return ; // nothing should be updated
+ }
+ bAskUpdate = false; // once is enough
+ }
+
+ pLink->Update();
+ }
+ CloseCachedComps();
+}
+
+SvLinkSourceRef LinkManager::CreateObj( SvBaseLink const * pLink )
+{
+ switch( pLink->GetObjType() )
+ {
+ case SvBaseLinkObjectType::ClientFile:
+ case SvBaseLinkObjectType::ClientGraphic:
+ case SvBaseLinkObjectType::ClientOle:
+ return new SvFileObject;
+ case SvBaseLinkObjectType::Internal:
+ return new SvxInternalLink;
+ case SvBaseLinkObjectType::ClientDde:
+ return new SvDDEObject;
+ default:
+ return SvLinkSourceRef();
+ }
+}
+
+bool LinkManager::InsertServer( SvLinkSource* pObj )
+{
+ // no duplicate inserts
+ if( !pObj )
+ return false;
+
+ return aServerTbl.insert( pObj ).second;
+}
+
+void LinkManager::RemoveServer( SvLinkSource* pObj )
+{
+ aServerTbl.erase( pObj );
+}
+
+void MakeLnkName( OUString& rName, const OUString* pType, std::u16string_view rFile,
+ std::u16string_view rLink, const OUString* pFilter )
+{
+ if( pType )
+ {
+ rName = comphelper::string::strip(*pType, ' ')
+ + OUStringChar(cTokenSeparator);
+ }
+ else
+ rName.clear();
+
+ rName += rFile;
+
+ rName = comphelper::string::strip(rName, ' ')
+ + OUStringChar(cTokenSeparator);
+ rName = comphelper::string::strip(rName, ' ') + rLink;
+ if( pFilter )
+ {
+ rName += OUStringChar(cTokenSeparator) + *pFilter;
+ rName = comphelper::string::strip(rName, ' ');
+ }
+}
+
+void LinkManager::ReconnectDdeLink(SfxObjectShell& rServer)
+{
+ SfxMedium* pMed = rServer.GetMedium();
+ if (!pMed)
+ return;
+
+ const ::sfx2::SvBaseLinks& rLinks = GetLinks();
+ size_t n = rLinks.size();
+
+ for (size_t i = 0; i < n; ++i)
+ {
+ ::sfx2::SvBaseLink* p = rLinks[i].get();
+ OUString aType, aFile, aLink, aFilter;
+ if (!GetDisplayNames(p, &aType, &aFile, &aLink, &aFilter))
+ continue;
+
+ if (aType != "soffice")
+ // DDE connections between OOo apps are always named 'soffice'.
+ continue;
+
+ OUString aTmp;
+ OUString aURL = aFile;
+ if (osl::FileBase::getFileURLFromSystemPath(aFile, aTmp)
+ == osl::FileBase::E_None)
+ aURL = aTmp;
+
+ if (!aURL.equalsIgnoreAsciiCase(pMed->GetName()))
+ // This DDE link is not associated with this server shell... Skip it.
+ continue;
+
+ if (aLink.isEmpty())
+ continue;
+
+ LinkServerShell(aLink, rServer, *p);
+ }
+}
+
+void LinkManager::LinkServerShell(const OUString& rPath, SfxObjectShell& rServer, ::sfx2::SvBaseLink& rLink)
+{
+ ::sfx2::SvLinkSource* pSrvSrc = rServer.DdeCreateLinkSource(rPath);
+ if (pSrvSrc)
+ {
+ css::datatransfer::DataFlavor aFl;
+ SotExchange::GetFormatDataFlavor(rLink.GetContentType(), aFl);
+ rLink.SetObj(pSrvSrc);
+ pSrvSrc->AddDataAdvise(
+ &rLink, aFl.MimeType,
+ SfxLinkUpdateMode::ONCALL == rLink.GetUpdateMode() ? ADVISEMODE_ONLYONCE : 0);
+ }
+}
+
+void LinkManager::InsertFileLink(
+ sfx2::SvBaseLink& rLink, SvBaseLinkObjectType nFileType, std::u16string_view rFileNm,
+ const OUString* pFilterNm, const OUString* pRange)
+{
+ if (!isClientType(rLink.GetObjType()))
+ return;
+
+ OUStringBuffer aBuf(64);
+ aBuf.append(rFileNm);
+ aBuf.append(sfx2::cTokenSeparator);
+
+ if (pRange)
+ aBuf.append(*pRange);
+
+ if (pFilterNm)
+ {
+ aBuf.append(sfx2::cTokenSeparator);
+ aBuf.append(*pFilterNm);
+ }
+
+ OUString aCmd = aBuf.makeStringAndClear();
+ InsertLink(&rLink, nFileType, SfxLinkUpdateMode::ONCALL, &aCmd);
+}
+
+// A transfer is aborted, so cancel all download media
+// (for now this is only of interest for the file links!)
+void LinkManager::CancelTransfers()
+{
+
+ const sfx2::SvBaseLinks& rLnks = GetLinks();
+ for( size_t n = rLnks.size(); n; )
+ {
+ const sfx2::SvBaseLink& rLnk = *rLnks[--n];
+ if (isClientFileType(rLnk.GetObjType()))
+ {
+ if (SvFileObject* pFileObj = static_cast<SvFileObject*>(rLnk.GetObj()))
+ pFileObj->CancelTransfers();
+ }
+ }
+}
+
+// For the purpose of sending Status information from the file object to
+// the base link, there exist a dedicated ClipBoardId. The SvData-object
+// gets the appropriate information as a string
+// For now this is required for file object in conjunction with JavaScript
+// - needs information about Load/Abort/Error
+SotClipboardFormatId LinkManager::RegisterStatusInfoId()
+{
+ static SotClipboardFormatId nFormat = SotClipboardFormatId::NONE;
+
+ if( nFormat == SotClipboardFormatId::NONE )
+ {
+ nFormat = SotExchange::RegisterFormatName(
+ "StatusInfo from SvxInternalLink");
+ }
+ return nFormat;
+}
+
+bool LinkManager::GetGraphicFromAny(const OUString& rMimeType,
+ const css::uno::Any & rValue,
+ Graphic& rGraphic,
+ weld::Window* pParentWin)
+{
+ bool bRet = false;
+
+ if (!rValue.hasValue())
+ return bRet;
+
+ if (rValue.has<OUString>())
+ {
+ OUString sReferer;
+ SfxObjectShell* sh = GetPersist();
+ if (sh && sh->HasName())
+ sReferer = sh->GetMedium()->GetName();
+
+ OUString sURL = rValue.get<OUString>();
+ if (!SvtSecurityOptions::isUntrustedReferer(sReferer))
+ rGraphic = vcl::graphic::loadFromURL(sURL, pParentWin);
+ if (rGraphic.IsNone())
+ rGraphic.SetDefaultType();
+ rGraphic.setOriginURL(sURL);
+ return true;
+ }
+ else if (rValue.has<css::uno::Sequence<sal_Int8>>())
+ {
+ auto aSeq = rValue.get<css::uno::Sequence<sal_Int8>>();
+
+ SvMemoryStream aMemStm( const_cast<sal_Int8 *>(aSeq.getConstArray()), aSeq.getLength(),
+ StreamMode::READ );
+ aMemStm.Seek( 0 );
+
+ switch( SotExchange::GetFormatIdFromMimeType( rMimeType ) )
+ {
+ case SotClipboardFormatId::SVXB:
+ {
+ TypeSerializer aSerializer(aMemStm);
+ aSerializer.readGraphic(rGraphic);
+ bRet = true;
+ }
+ break;
+ case SotClipboardFormatId::GDIMETAFILE:
+ {
+ GDIMetaFile aMtf;
+ SvmReader aReader( aMemStm );
+ aReader.Read( aMtf );
+ rGraphic = aMtf;
+ bRet = true;
+ }
+ break;
+ case SotClipboardFormatId::BITMAP:
+ {
+ Bitmap aBmp;
+ ReadDIB(aBmp, aMemStm, true);
+ rGraphic = BitmapEx(aBmp);
+ bRet = true;
+ }
+ break;
+ default: break;
+ }
+ }
+ return bRet;
+}
+
+static OUString lcl_DDE_RelToAbs( const OUString& rTopic, std::u16string_view rBaseURL )
+{
+ OUString sRet;
+ INetURLObject aURL( rTopic );
+ if( INetProtocol::NotValid == aURL.GetProtocol() )
+ osl::FileBase::getFileURLFromSystemPath(rTopic, sRet);
+ if( sRet.isEmpty() )
+ sRet = URIHelper::SmartRel2Abs( INetURLObject(rBaseURL), rTopic, URIHelper::GetMaybeFileHdl() );
+ return sRet;
+}
+
+bool SvxInternalLink::Connect( sfx2::SvBaseLink* pLink )
+{
+ SfxObjectShell* pFndShell = nullptr;
+ sal_uInt16 nUpdateMode = css::document::UpdateDocMode::NO_UPDATE;
+ OUString sTopic, sItem, sReferer;
+ LinkManager* pLinkMgr = pLink->GetLinkManager();
+ if (pLinkMgr && sfx2::LinkManager::GetDisplayNames(pLink, nullptr, &sTopic, &sItem) && !sTopic.isEmpty())
+ {
+ // first only loop over the DocumentShells the shells and find those
+ // with the name:
+ CharClass aCC( LanguageTag( LANGUAGE_SYSTEM) );
+
+ bool bFirst = true;
+ SfxObjectShell* pShell = pLinkMgr->GetPersist();
+ if( pShell && pShell->GetMedium() )
+ {
+ sReferer = pShell->GetMedium()->GetBaseURL();
+ const SfxUInt16Item* pItem = SfxItemSet::GetItem<SfxUInt16Item>(pShell->GetMedium()->GetItemSet(), SID_UPDATEDOCMODE, false);
+ if ( pItem )
+ nUpdateMode = pItem->GetValue();
+ }
+
+ OUString sNmURL(aCC.lowercase(lcl_DDE_RelToAbs(sTopic, sReferer)));
+
+ if ( !pShell )
+ {
+ bFirst = false;
+ pShell = SfxObjectShell::GetFirst( nullptr, false );
+ }
+
+ OUString sTmp;
+ while( pShell )
+ {
+ if( sTmp.isEmpty() )
+ {
+ sTmp = pShell->GetTitle( SFX_TITLE_FULLNAME );
+ sTmp = lcl_DDE_RelToAbs(sTmp, sReferer );
+ }
+
+
+ sTmp = aCC.lowercase( sTmp );
+ if( sTmp == sNmURL ) // we want these
+ {
+ pFndShell = pShell;
+ break;
+ }
+
+ if( bFirst )
+ {
+ bFirst = false;
+ pShell = SfxObjectShell::GetFirst( nullptr, false );
+ }
+ else
+ pShell = SfxObjectShell::GetNext( *pShell, nullptr, false );
+
+ sTmp.clear();
+ }
+ }
+
+ // empty topics are not allowed - which document is it
+ if( sTopic.isEmpty() )
+ return false;
+
+ if (pFndShell)
+ {
+ sfx2::SvLinkSource* pNewSrc = pFndShell->DdeCreateLinkSource( sItem );
+ if( pNewSrc )
+ {
+ css::datatransfer::DataFlavor aFl;
+ SotExchange::GetFormatDataFlavor( pLink->GetContentType(), aFl );
+
+ pLink->SetObj( pNewSrc );
+ pNewSrc->AddDataAdvise( pLink, aFl.MimeType,
+ SfxLinkUpdateMode::ONCALL == pLink->GetUpdateMode()
+ ? ADVISEMODE_ONLYONCE
+ : 0 );
+ return true;
+ }
+ }
+ else
+ {
+ // then try to download the file:
+ INetURLObject aURL( sTopic );
+ INetProtocol eOld = aURL.GetProtocol();
+ sTopic = lcl_DDE_RelToAbs( sTopic, sReferer );
+ aURL.SetURL( sTopic );
+ if( INetProtocol::NotValid != eOld ||
+ INetProtocol::Http != aURL.GetProtocol() )
+ {
+ SfxStringItem aName( SID_FILE_NAME, sTopic );
+ SfxBoolItem aMinimized(SID_MINIMIZED, true);
+ SfxBoolItem aHidden(SID_HIDDEN, true);
+ SfxStringItem aTarget( SID_TARGETNAME, "_blank" );
+ SfxStringItem aReferer( SID_REFERER, sReferer );
+ SfxUInt16Item aUpdate( SID_UPDATEDOCMODE, nUpdateMode );
+ SfxBoolItem aReadOnly(SID_DOC_READONLY, false);
+
+ // Disable automatic re-connection to avoid this link instance
+ // being destroyed at re-connection.
+ SfxBoolItem aDdeConnect(SID_DDE_RECONNECT_ONLOAD, false);
+
+ // #i14200# (DDE-link crashes wordprocessor)
+ SfxAllItemSet aArgs( SfxGetpApp()->GetPool() );
+ aArgs.Put(aReferer);
+ aArgs.Put(aTarget);
+ aArgs.Put(aHidden);
+ aArgs.Put(aMinimized);
+ aArgs.Put(aName);
+ aArgs.Put(aUpdate);
+ aArgs.Put(aReadOnly);
+ aArgs.Put(aDdeConnect);
+ Reference<XComponent> xComp = SfxObjectShell::CreateAndLoadComponent(aArgs);
+ pFndShell = SfxObjectShell::GetShellFromComponent(xComp);
+ if (xComp.is() && pFndShell && pLinkMgr)
+ {
+ pLinkMgr->InsertCachedComp(xComp);
+ sfx2::LinkManager::LinkServerShell(sItem, *pFndShell, *pLink);
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/appl/linksrc.cxx b/sfx2/source/appl/linksrc.cxx
new file mode 100644
index 000000000..fb9ec1143
--- /dev/null
+++ b/sfx2/source/appl/linksrc.cxx
@@ -0,0 +1,416 @@
+/* -*- 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/linksrc.hxx>
+#include <sfx2/lnkbase.hxx>
+#include <com/sun/star/uno/Any.hxx>
+
+#include <vcl/timer.hxx>
+#include <memory>
+#include <vector>
+#include <algorithm>
+
+
+using namespace ::com::sun::star::uno;
+
+namespace sfx2
+{
+
+namespace {
+
+class SvLinkSourceTimer : public Timer
+{
+ SvLinkSource * pOwner;
+ virtual void Invoke() override;
+public:
+ explicit SvLinkSourceTimer( SvLinkSource * pOwn );
+};
+
+}
+
+SvLinkSourceTimer::SvLinkSourceTimer( SvLinkSource * pOwn )
+ : Timer("sfx2 SvLinkSourceTimer"), pOwner( pOwn )
+{
+}
+
+void SvLinkSourceTimer::Invoke()
+{
+ // Secure against being destroyed in Handler
+ SvLinkSourceRef xHoldAlive( pOwner );
+ pOwner->SendDataChanged();
+}
+
+static void StartTimer( std::unique_ptr<SvLinkSourceTimer>& pTimer, SvLinkSource * pOwner,
+ sal_uInt64 nTimeout )
+{
+ if( !pTimer )
+ {
+ pTimer.reset( new SvLinkSourceTimer( pOwner ) );
+ pTimer->SetTimeout( nTimeout );
+ pTimer->Start();
+ }
+}
+
+namespace {
+
+struct SvLinkSource_Entry_Impl
+{
+ tools::SvRef<SvBaseLink> xSink;
+ OUString aDataMimeType;
+ sal_uInt16 nAdviseModes;
+ bool bIsDataSink;
+
+ SvLinkSource_Entry_Impl( SvBaseLink* pLink, const OUString& rMimeType,
+ sal_uInt16 nAdvMode )
+ : xSink( pLink ), aDataMimeType( rMimeType ),
+ nAdviseModes( nAdvMode ), bIsDataSink( true )
+ {}
+
+ explicit SvLinkSource_Entry_Impl( SvBaseLink* pLink )
+ : xSink( pLink ), nAdviseModes( 0 ), bIsDataSink( false )
+ {}
+};
+
+class SvLinkSource_Array_Impl
+{
+friend class SvLinkSource_EntryIter_Impl;
+private:
+ std::vector<std::unique_ptr<SvLinkSource_Entry_Impl>> mvData;
+
+public:
+ SvLinkSource_Array_Impl() {}
+
+ size_t size() const { return mvData.size(); }
+ SvLinkSource_Entry_Impl *operator[](size_t idx) const { return mvData[idx].get(); }
+ void push_back(SvLinkSource_Entry_Impl* rData) { mvData.emplace_back(rData); }
+
+ void DeleteAndDestroy(SvLinkSource_Entry_Impl const * p)
+ {
+ auto it = std::find_if(mvData.begin(), mvData.end(),
+ [&p](const std::unique_ptr<SvLinkSource_Entry_Impl>& rxData) { return rxData.get() == p; });
+ if (it != mvData.end())
+ mvData.erase(it);
+ }
+};
+
+class SvLinkSource_EntryIter_Impl
+{
+ std::vector<SvLinkSource_Entry_Impl*> aArr;
+ const SvLinkSource_Array_Impl& rOrigArr;
+ sal_uInt16 nPos;
+public:
+ explicit SvLinkSource_EntryIter_Impl( const SvLinkSource_Array_Impl& rArr );
+ SvLinkSource_Entry_Impl* Curr()
+ { return nPos < aArr.size() ? aArr[ nPos ] : nullptr; }
+ SvLinkSource_Entry_Impl* Next();
+ bool IsValidCurrValue( SvLinkSource_Entry_Impl const * pEntry );
+};
+
+}
+
+SvLinkSource_EntryIter_Impl::SvLinkSource_EntryIter_Impl(
+ const SvLinkSource_Array_Impl& rArr )
+ : rOrigArr( rArr ), nPos( 0 )
+{
+ for (auto const & i : rArr.mvData)
+ aArr.push_back(i.get());
+}
+
+bool SvLinkSource_EntryIter_Impl::IsValidCurrValue( SvLinkSource_Entry_Impl const * pEntry )
+{
+ if ( nPos >= aArr.size() )
+ return false;
+ if (aArr[nPos] != pEntry)
+ return false;
+ for (auto const & i : rOrigArr.mvData)
+ if (i.get() == pEntry)
+ return true;
+ return false;
+}
+
+SvLinkSource_Entry_Impl* SvLinkSource_EntryIter_Impl::Next()
+{
+ SvLinkSource_Entry_Impl* pRet = nullptr;
+ if( nPos + 1 < static_cast<sal_uInt16>(aArr.size()) )
+ {
+ ++nPos;
+ if( rOrigArr.size() == aArr.size() &&
+ rOrigArr[ nPos ] == aArr[ nPos ] )
+ pRet = aArr[ nPos ];
+ else
+ {
+ // then we must search the current (or the next) in the orig
+ do {
+ pRet = aArr[ nPos ];
+ for (auto const & i : rOrigArr.mvData)
+ if (i.get() == pRet)
+ return pRet;
+ pRet = nullptr;
+ ++nPos;
+ } while( nPos < aArr.size() );
+
+ if( nPos >= aArr.size() )
+ pRet = nullptr;
+ }
+ }
+ return pRet;
+}
+
+struct SvLinkSource_Impl
+{
+ SvLinkSource_Array_Impl aArr;
+ OUString aDataMimeType;
+ std::unique_ptr<SvLinkSourceTimer>
+ pTimer;
+ sal_uInt64 nTimeout;
+ css::uno::Reference<css::io::XInputStream>
+ m_xInputStreamToLoadFrom;
+ bool m_bIsReadOnly;
+
+ SvLinkSource_Impl()
+ : nTimeout(3000)
+ , m_bIsReadOnly(false)
+ {
+ }
+};
+
+SvLinkSource::SvLinkSource()
+ : pImpl( new SvLinkSource_Impl )
+{
+}
+
+SvLinkSource::~SvLinkSource()
+{
+}
+
+
+SvLinkSource::StreamToLoadFrom SvLinkSource::getStreamToLoadFrom()
+{
+ return StreamToLoadFrom(
+ pImpl->m_xInputStreamToLoadFrom,
+ pImpl->m_bIsReadOnly);
+}
+
+void SvLinkSource::setStreamToLoadFrom(const css::uno::Reference<css::io::XInputStream>& xInputStream, bool bIsReadOnly )
+{
+ pImpl->m_xInputStreamToLoadFrom = xInputStream;
+ pImpl->m_bIsReadOnly = bIsReadOnly;
+}
+
+// #i88291#
+void SvLinkSource::clearStreamToLoadFrom()
+{
+ pImpl->m_xInputStreamToLoadFrom.clear();
+}
+
+void SvLinkSource::Closed()
+{
+ SvLinkSource_EntryIter_Impl aIter( pImpl->aArr );
+ for( SvLinkSource_Entry_Impl* p = aIter.Curr(); p; p = aIter.Next() )
+ if( !p->bIsDataSink )
+ p->xSink->Closed();
+}
+
+sal_uInt64 SvLinkSource::GetUpdateTimeout() const
+{
+ return pImpl->nTimeout;
+}
+
+void SvLinkSource::SetUpdateTimeout( sal_uInt64 nTimeout )
+{
+ pImpl->nTimeout = nTimeout;
+ if( pImpl->pTimer )
+ pImpl->pTimer->SetTimeout( nTimeout );
+}
+
+void SvLinkSource::SendDataChanged()
+{
+ SvLinkSource_EntryIter_Impl aIter( pImpl->aArr );
+ for( SvLinkSource_Entry_Impl* p = aIter.Curr(); p; p = aIter.Next() )
+ {
+ if( p->bIsDataSink )
+ {
+ OUString sDataMimeType( pImpl->aDataMimeType );
+ if( sDataMimeType.isEmpty() )
+ sDataMimeType = p->aDataMimeType;
+
+ Any aVal;
+ if( ( p->nAdviseModes & ADVISEMODE_NODATA ) ||
+ GetData( aVal, sDataMimeType, true ) )
+ {
+ p->xSink->DataChanged( sDataMimeType, aVal );
+
+ if ( !aIter.IsValidCurrValue( p ) )
+ continue;
+
+ if( p->nAdviseModes & ADVISEMODE_ONLYONCE )
+ {
+ pImpl->aArr.DeleteAndDestroy( p );
+ }
+
+ }
+ }
+ }
+ pImpl->pTimer.reset();
+ pImpl->aDataMimeType.clear();
+}
+
+void SvLinkSource::NotifyDataChanged()
+{
+ if( pImpl->nTimeout )
+ StartTimer( pImpl->pTimer, this, pImpl->nTimeout ); // New timeout
+ else
+ {
+ SvLinkSource_EntryIter_Impl aIter( pImpl->aArr );
+ for( SvLinkSource_Entry_Impl* p = aIter.Curr(); p; p = aIter.Next() )
+ if( p->bIsDataSink )
+ {
+ Any aVal;
+ if( ( p->nAdviseModes & ADVISEMODE_NODATA ) ||
+ GetData( aVal, p->aDataMimeType, true ) )
+ {
+ p->xSink->DataChanged( p->aDataMimeType, aVal );
+
+ if ( !aIter.IsValidCurrValue( p ) )
+ continue;
+
+ if( p->nAdviseModes & ADVISEMODE_ONLYONCE )
+ {
+ pImpl->aArr.DeleteAndDestroy( p );
+ }
+ }
+ }
+
+ pImpl->pTimer.reset();
+ }
+}
+
+// notify the sink, the mime type is not
+// a selection criterion
+void SvLinkSource::DataChanged( const OUString & rMimeType,
+ const css::uno::Any & rVal )
+{
+ if( pImpl->nTimeout && !rVal.hasValue() )
+ { // only when no data was included
+ // fire all data to the sink, independent of the requested format
+ pImpl->aDataMimeType = rMimeType;
+ StartTimer( pImpl->pTimer, this, pImpl->nTimeout ); // New timeout
+ }
+ else
+ {
+ SvLinkSource_EntryIter_Impl aIter( pImpl->aArr );
+ for( SvLinkSource_Entry_Impl* p = aIter.Curr(); p; p = aIter.Next() )
+ {
+ if( p->bIsDataSink )
+ {
+ p->xSink->DataChanged( rMimeType, rVal );
+
+ if ( !aIter.IsValidCurrValue( p ) )
+ continue;
+
+ if( p->nAdviseModes & ADVISEMODE_ONLYONCE )
+ {
+ pImpl->aArr.DeleteAndDestroy( p );
+ }
+ }
+ }
+
+ pImpl->pTimer.reset();
+ }
+}
+
+
+// only one link is correct
+void SvLinkSource::AddDataAdvise( SvBaseLink * pLink, const OUString& rMimeType,
+ sal_uInt16 nAdviseModes )
+{
+ SvLinkSource_Entry_Impl* pNew = new SvLinkSource_Entry_Impl(
+ pLink, rMimeType, nAdviseModes );
+ pImpl->aArr.push_back( pNew );
+}
+
+void SvLinkSource::RemoveAllDataAdvise( SvBaseLink const * pLink )
+{
+ SvLinkSource_EntryIter_Impl aIter( pImpl->aArr );
+ for( SvLinkSource_Entry_Impl* p = aIter.Curr(); p; p = aIter.Next() )
+ if( p->bIsDataSink && p->xSink.get() == pLink )
+ {
+ pImpl->aArr.DeleteAndDestroy( p );
+ }
+}
+
+// only one link is correct
+void SvLinkSource::AddConnectAdvise( SvBaseLink * pLink )
+{
+ SvLinkSource_Entry_Impl* pNew = new SvLinkSource_Entry_Impl( pLink );
+ pImpl->aArr.push_back( pNew );
+}
+
+void SvLinkSource::RemoveConnectAdvise( SvBaseLink const * pLink )
+{
+ SvLinkSource_EntryIter_Impl aIter( pImpl->aArr );
+ for( SvLinkSource_Entry_Impl* p = aIter.Curr(); p; p = aIter.Next() )
+ if( !p->bIsDataSink && p->xSink.get() == pLink )
+ {
+ pImpl->aArr.DeleteAndDestroy( p );
+ }
+}
+
+bool SvLinkSource::HasDataLinks() const
+{
+ bool bRet = false;
+ for( sal_uInt16 n = 0, nEnd = pImpl->aArr.size(); n < nEnd; ++n )
+ if( pImpl->aArr[ n ]->bIsDataSink )
+ {
+ bRet = true;
+ break;
+ }
+ return bRet;
+}
+
+// sal_True => waitinmg for data
+bool SvLinkSource::IsPending() const
+{
+ return false;
+}
+
+// sal_True => data complete loaded
+bool SvLinkSource::IsDataComplete() const
+{
+ return true;
+}
+
+bool SvLinkSource::Connect( SvBaseLink* )
+{
+ return true;
+}
+
+bool SvLinkSource::GetData( css::uno::Any &, const OUString &, bool )
+{
+ return false;
+}
+
+void SvLinkSource::Edit(weld::Window *, SvBaseLink *, const Link<const OUString&, void>&)
+{
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/appl/lnkbase2.cxx b/sfx2/source/appl/lnkbase2.cxx
new file mode 100644
index 000000000..7fd3b3d53
--- /dev/null
+++ b/sfx2/source/appl/lnkbase2.cxx
@@ -0,0 +1,600 @@
+/* -*- 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 <sfx2/lnkbase.hxx>
+#include <sot/exchange.hxx>
+#include <com/sun/star/uno/Any.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/ui/dialogs/TemplateDescription.hpp>
+#include <sfx2/linkmgr.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+#include <sfx2/strings.hrc>
+#include <sfx2/sfxresid.hxx>
+#include <sfx2/filedlghelper.hxx>
+#include <tools/debug.hxx>
+#include <svl/svdde.hxx>
+#include <osl/diagnose.h>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+
+namespace sfx2
+{
+
+namespace {
+class ImplDdeItem;
+}
+
+// only for internal management
+struct ImplBaseLinkData
+{
+ struct tClientType
+ {
+ // applies for all links
+ SotClipboardFormatId nCntntType; // Update Format
+ // Not Ole-Links
+ bool bIntrnlLnk; // It is an internal link
+ SfxLinkUpdateMode nUpdateMode; // UpdateMode
+ };
+
+ struct tDDEType
+ {
+ ImplDdeItem* pItem;
+ };
+
+ union {
+ tClientType ClientType;
+ tDDEType DDEType;
+ };
+ ImplBaseLinkData()
+ {
+ ClientType.nCntntType = SotClipboardFormatId::NONE;
+ ClientType.bIntrnlLnk = false;
+ ClientType.nUpdateMode = SfxLinkUpdateMode::NONE;
+ DDEType.pItem = nullptr;
+ }
+};
+
+namespace {
+
+class ImplDdeItem : public DdeGetPutItem
+{
+ SvBaseLink* pLink;
+ DdeData aData;
+ Sequence< sal_Int8 > aSeq; // Datacontainer for DdeData !!!
+ bool bIsValidData : 1;
+ bool bIsInDTOR : 1;
+public:
+#if defined(_WIN32)
+ ImplDdeItem( SvBaseLink& rLink, const OUString& rStr )
+ : DdeGetPutItem( rStr ), pLink( &rLink ), bIsValidData( false ),
+ bIsInDTOR( false )
+ {}
+#endif
+ virtual ~ImplDdeItem() override;
+
+ virtual DdeData* Get( SotClipboardFormatId ) override;
+ virtual bool Put( const DdeData* ) override;
+ virtual void AdviseLoop( bool ) override;
+
+ void Notify()
+ {
+ bIsValidData = false;
+ DdeGetPutItem::NotifyClient();
+ }
+
+ bool IsInDTOR() const { return bIsInDTOR; }
+};
+
+}
+
+SvBaseLink::SvBaseLink()
+ : m_pLinkMgr( nullptr )
+ , m_pParentWin( nullptr )
+ , m_bIsConnect( false )
+ , m_bIsReadOnly(false)
+{
+ mnObjType = SvBaseLinkObjectType::ClientSo;
+ pImplData.reset( new ImplBaseLinkData );
+ bVisible = bSynchron = true;
+ bWasLastEditOK = false;
+}
+
+
+SvBaseLink::SvBaseLink( SfxLinkUpdateMode nUpdateMode, SotClipboardFormatId nContentType )
+ : m_pLinkMgr( nullptr )
+ , m_pParentWin( nullptr )
+ , m_bIsConnect( false )
+ , m_bIsReadOnly(false)
+{
+ mnObjType = SvBaseLinkObjectType::ClientSo;
+ pImplData.reset( new ImplBaseLinkData );
+ bVisible = bSynchron = true;
+ bWasLastEditOK = false;
+
+ // It is going to be an OLE-Link,
+ pImplData->ClientType.nUpdateMode = nUpdateMode;
+ pImplData->ClientType.nCntntType = nContentType;
+ pImplData->ClientType.bIntrnlLnk = false;
+}
+
+#if defined(_WIN32)
+
+static DdeTopic* FindTopic( const OUString & rLinkName, sal_uInt16* pItemStt )
+{
+ if( rLinkName.isEmpty() )
+ return nullptr;
+
+ OUString sNm( rLinkName );
+ sal_Int32 nTokenPos = 0;
+ OUString sService( sNm.getToken( 0, cTokenSeparator, nTokenPos ) );
+
+ DdeServices& rSvc = DdeService::GetServices();
+ for (auto const& elem : rSvc)
+ {
+ if(elem->GetName() == sService)
+ {
+ // then we search for the Topic
+ OUString sTopic( sNm.getToken( 0, cTokenSeparator, nTokenPos ) );
+ if( pItemStt )
+ *pItemStt = nTokenPos;
+
+ std::vector<DdeTopic*>& rTopics = elem->GetTopics();
+
+ for (auto const& topic : rTopics)
+ if( topic->GetName() == sTopic )
+ return topic;
+ break;
+ }
+ }
+ return nullptr;
+}
+
+SvBaseLink::SvBaseLink( const OUString& rLinkName, SvBaseLinkObjectType nObjectType, SvLinkSource* pObj )
+ : m_pLinkMgr( nullptr )
+ , m_pParentWin( nullptr )
+ , m_bIsConnect( false )
+ , m_bIsReadOnly(false)
+{
+ bVisible = bSynchron = true;
+ bWasLastEditOK = false;
+ aLinkName = rLinkName;
+ pImplData.reset( new ImplBaseLinkData );
+ mnObjType = nObjectType;
+
+ if( !pObj )
+ {
+ DBG_ASSERT( pObj, "Where is my left-most object" );
+ return;
+ }
+
+ if( SvBaseLinkObjectType::DdeExternal == mnObjType )
+ {
+ sal_uInt16 nItemStt = 0;
+ DdeTopic* pTopic = FindTopic( aLinkName, &nItemStt );
+ if( pTopic )
+ {
+ // then we have it all together
+ // MM_TODO how do I get the name
+ OUString aStr = aLinkName; // xLinkName->GetDisplayName();
+ aStr = aStr.copy( nItemStt );
+ pImplData->DDEType.pItem = new ImplDdeItem( *this, aStr );
+ pTopic->InsertItem( pImplData->DDEType.pItem );
+
+ // store the Advice
+ xObj = pObj;
+ }
+ }
+ else if( pObj->Connect( this ) )
+ xObj = pObj;
+}
+
+#endif
+
+SvBaseLink::~SvBaseLink()
+{
+ Disconnect();
+
+ if( mnObjType == SvBaseLinkObjectType::DdeExternal )
+ {
+ if( !pImplData->DDEType.pItem->IsInDTOR() )
+ delete pImplData->DDEType.pItem;
+ }
+
+ pImplData.reset();
+}
+
+IMPL_LINK( SvBaseLink, EndEditHdl, const OUString&, _rNewName, void )
+{
+ OUString sNewName = _rNewName;
+ if ( !ExecuteEdit( sNewName ) )
+ sNewName.clear();
+ bWasLastEditOK = !sNewName.isEmpty();
+ m_aEndEditLink.Call( *this );
+}
+
+
+void SvBaseLink::SetObjType( SvBaseLinkObjectType mnObjTypeP )
+{
+ DBG_ASSERT( mnObjType != SvBaseLinkObjectType::ClientDde, "type already set" );
+ DBG_ASSERT( !xObj.is(), "object exist" );
+
+ mnObjType = mnObjTypeP;
+}
+
+
+void SvBaseLink::SetName( const OUString & rNm )
+{
+ aLinkName = rNm;
+}
+
+
+void SvBaseLink::SetObj( SvLinkSource * pObj )
+{
+ DBG_ASSERT( (isClientType(mnObjType) &&
+ pImplData->ClientType.bIntrnlLnk) ||
+ mnObjType == SvBaseLinkObjectType::ClientGraphic,
+ "no intern link" );
+ xObj = pObj;
+}
+
+
+void SvBaseLink::SetLinkSourceName( const OUString & rLnkNm )
+{
+ if( aLinkName == rLnkNm )
+ return;
+
+ AddNextRef(); // should be superfluous
+ // remove old connection
+ Disconnect();
+
+ aLinkName = rLnkNm;
+
+ // New Connection
+ GetRealObject_();
+ ReleaseRef(); // should be superfluous
+}
+
+
+void SvBaseLink::SetUpdateMode( SfxLinkUpdateMode nMode )
+{
+ if( isClientType(mnObjType) &&
+ pImplData->ClientType.nUpdateMode != nMode )
+ {
+ AddNextRef();
+ Disconnect();
+
+ pImplData->ClientType.nUpdateMode = nMode;
+ GetRealObject_();
+ ReleaseRef();
+ }
+}
+
+// #i88291#
+void SvBaseLink::clearStreamToLoadFrom()
+{
+ m_xInputStreamToLoadFrom.clear();
+ if( xObj.is() )
+ {
+ xObj->clearStreamToLoadFrom();
+ }
+}
+
+bool SvBaseLink::Update()
+{
+ if( isClientType(mnObjType) )
+ {
+ AddNextRef();
+ Disconnect();
+
+ GetRealObject_();
+ ReleaseRef();
+ if( xObj.is() )
+ {
+ xObj->setStreamToLoadFrom(m_xInputStreamToLoadFrom,m_bIsReadOnly);
+ OUString sMimeType( SotExchange::GetFormatMimeType(
+ pImplData->ClientType.nCntntType ));
+ Any aData;
+
+ if( xObj->GetData( aData, sMimeType ) )
+ {
+ UpdateResult eRes = DataChanged(sMimeType, aData);
+ bool bSuccess = eRes == SUCCESS;
+ //for manual Updates there is no need to hold the ServerObject
+ if( SvBaseLinkObjectType::ClientDde == mnObjType &&
+ SfxLinkUpdateMode::ONCALL == GetUpdateMode() && xObj.is() )
+ xObj->RemoveAllDataAdvise( this );
+ return bSuccess;
+ }
+ if( xObj.is() )
+ {
+ // should be asynchronous?
+ if( xObj->IsPending() )
+ return true;
+
+ // we do not need the object anymore
+ AddNextRef();
+ Disconnect();
+ ReleaseRef();
+ }
+ }
+ }
+ return false;
+}
+
+
+SfxLinkUpdateMode SvBaseLink::GetUpdateMode() const
+{
+ return isClientType(mnObjType)
+ ? pImplData->ClientType.nUpdateMode
+ : SfxLinkUpdateMode::ONCALL;
+}
+
+
+void SvBaseLink::GetRealObject_( bool bConnect)
+{
+ if( !m_pLinkMgr )
+ return;
+
+ DBG_ASSERT( !xObj.is(), "object already exist" );
+
+ if( SvBaseLinkObjectType::ClientDde == mnObjType )
+ {
+ OUString sServer;
+ if( sfx2::LinkManager::GetDisplayNames( this, &sServer ) &&
+ sServer == Application::GetAppName() ) // internal Link !!!
+ {
+ // so that the Internal link can be created!
+ mnObjType = SvBaseLinkObjectType::Internal;
+ xObj = sfx2::LinkManager::CreateObj( this );
+
+ pImplData->ClientType.bIntrnlLnk = true;
+ mnObjType = SvBaseLinkObjectType::ClientDde; // so we know what it once was!
+ }
+ else
+ {
+ pImplData->ClientType.bIntrnlLnk = false;
+ xObj = sfx2::LinkManager::CreateObj( this );
+ }
+ }
+ else if( isClientType(mnObjType) )
+ xObj = sfx2::LinkManager::CreateObj( this );
+
+ if( bConnect && ( !xObj.is() || !xObj->Connect( this ) ) )
+ Disconnect();
+}
+
+SotClipboardFormatId SvBaseLink::GetContentType() const
+{
+ if( isClientType(mnObjType) )
+ return pImplData->ClientType.nCntntType;
+
+ return SotClipboardFormatId::NONE; // all Formats ?
+}
+
+
+void SvBaseLink::SetContentType( SotClipboardFormatId nType )
+{
+ if( isClientType(mnObjType) )
+ {
+ pImplData->ClientType.nCntntType = nType;
+ }
+}
+
+LinkManager* SvBaseLink::GetLinkManager()
+{
+ return m_pLinkMgr;
+}
+
+const LinkManager* SvBaseLink::GetLinkManager() const
+{
+ return m_pLinkMgr;
+}
+
+void SvBaseLink::SetLinkManager( LinkManager* _pMgr )
+{
+ m_pLinkMgr = _pMgr;
+}
+
+void SvBaseLink::Disconnect()
+{
+ if( xObj.is() )
+ {
+ xObj->RemoveAllDataAdvise( this );
+ xObj->RemoveConnectAdvise( this );
+ xObj.clear();
+ }
+}
+
+SvBaseLink::UpdateResult SvBaseLink::DataChanged( const OUString &, const css::uno::Any & )
+{
+ if ( mnObjType == SvBaseLinkObjectType::DdeExternal )
+ {
+ if( pImplData->DDEType.pItem )
+ pImplData->DDEType.pItem->Notify();
+ }
+ return SUCCESS;
+}
+
+void SvBaseLink::Edit(weld::Window* pParent, const Link<SvBaseLink&,void>& rEndEditHdl )
+{
+ m_pParentWin = pParent;
+ m_aEndEditLink = rEndEditHdl;
+ m_bIsConnect = xObj.is();
+ if( !m_bIsConnect )
+ GetRealObject_( xObj.is() );
+
+ bool bAsync = false;
+ Link<const OUString&, void> aLink = LINK( this, SvBaseLink, EndEditHdl );
+
+ if( isClientType(mnObjType) && pImplData->ClientType.bIntrnlLnk )
+ {
+ if( m_pLinkMgr )
+ {
+ SvLinkSourceRef ref = sfx2::LinkManager::CreateObj( this );
+ if( ref.is() )
+ {
+ ref->Edit( pParent, this, aLink );
+ bAsync = true;
+ }
+ }
+ }
+ else
+ {
+ xObj->Edit( pParent, this, aLink );
+ bAsync = true;
+ }
+
+ if ( !bAsync )
+ {
+ ExecuteEdit( OUString() );
+ bWasLastEditOK = false;
+ m_aEndEditLink.Call( *this );
+ }
+}
+
+bool SvBaseLink::ExecuteEdit( const OUString& _rNewName )
+{
+ if( !_rNewName.isEmpty() )
+ {
+ SetLinkSourceName( _rNewName );
+ if( !Update() )
+ {
+ OUString sApp, sTopic, sItem, sError;
+ sfx2::LinkManager::GetDisplayNames( this, &sApp, &sTopic, &sItem );
+ if( mnObjType == SvBaseLinkObjectType::ClientDde )
+ {
+ sError = SfxResId(STR_DDE_ERROR);
+
+ sal_Int32 nFndPos = sError.indexOf( "%1" );
+ if( -1 != nFndPos )
+ {
+ sError = sError.replaceAt( nFndPos, 2, sApp );
+ nFndPos = nFndPos + sApp.getLength();
+
+ if( -1 != ( nFndPos = sError.indexOf( "%2", nFndPos )))
+ {
+ sError = sError.replaceAt( nFndPos, 2, sTopic );
+ nFndPos = nFndPos + sTopic.getLength();
+
+ if( -1 != ( nFndPos = sError.indexOf( "%3", nFndPos )))
+ sError = sError.replaceAt( nFndPos, 2, sItem );
+ }
+ }
+ }
+ else
+ return false;
+
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(m_pParentWin,
+ VclMessageType::Warning, VclButtonsType::Ok, sError));
+ xBox->run();
+ }
+ }
+ else if( !m_bIsConnect )
+ Disconnect();
+ m_bIsConnect = false;
+ return true;
+}
+
+void SvBaseLink::Closed()
+{
+ if( xObj.is() )
+ xObj->RemoveAllDataAdvise( this );
+}
+
+FileDialogHelper & SvBaseLink::GetInsertFileDialog(const OUString& rFactory)
+{
+ m_pFileDlg.reset( new FileDialogHelper(
+ ui::dialogs::TemplateDescription::FILEOPEN_SIMPLE,
+ FileDialogFlags::Insert, rFactory, SfxFilterFlags::NONE, SfxFilterFlags::NONE, m_pParentWin) );
+ return *m_pFileDlg;
+}
+
+ImplDdeItem::~ImplDdeItem()
+{
+ bIsInDTOR = true;
+ // So that no-one gets the idea to delete the pointer when Disconnecting!
+ tools::SvRef<SvBaseLink> aRef( pLink );
+ aRef->Disconnect();
+}
+
+DdeData* ImplDdeItem::Get( SotClipboardFormatId nFormat )
+{
+ if( pLink->GetObj() )
+ {
+ // is it still valid?
+ if( bIsValidData && nFormat == aData.GetFormat() )
+ return &aData;
+
+ Any aValue;
+ OUString sMimeType( SotExchange::GetFormatMimeType( nFormat ));
+ if( pLink->GetObj()->GetData( aValue, sMimeType ) )
+ {
+ if( aValue >>= aSeq )
+ {
+ aData = DdeData( aSeq.getConstArray(), aSeq.getLength(), nFormat );
+
+ bIsValidData = true;
+ return &aData;
+ }
+ }
+ }
+ aSeq.realloc( 0 );
+ bIsValidData = false;
+ return nullptr;
+}
+
+
+bool ImplDdeItem::Put( const DdeData* )
+{
+ OSL_FAIL( "ImplDdeItem::Put not implemented" );
+ return false;
+}
+
+
+void ImplDdeItem::AdviseLoop( bool bOpen )
+{
+ // Connection is closed, so also unsubscribe link
+ if( !pLink->GetObj() )
+ return;
+
+ if( bOpen )
+ {
+ // A connection is re-established
+ if( SvBaseLinkObjectType::DdeExternal == pLink->GetObjType() )
+ {
+ pLink->GetObj()->AddDataAdvise( pLink, "text/plain;charset=utf-16", ADVISEMODE_NODATA );
+ pLink->GetObj()->AddConnectAdvise( pLink );
+ }
+ }
+ else
+ {
+ // So that no-one gets the idea to delete the pointer
+ // when Disconnecting!
+ tools::SvRef<SvBaseLink> aRef( pLink );
+ aRef->Disconnect();
+ }
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/appl/macroloader.cxx b/sfx2/source/appl/macroloader.cxx
new file mode 100644
index 000000000..bf6dd7669
--- /dev/null
+++ b/sfx2/source/appl/macroloader.cxx
@@ -0,0 +1,344 @@
+/* -*- 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 <config_features.h>
+
+#include <macroloader.hxx>
+
+#include <com/sun/star/frame/DispatchResultState.hpp>
+#include <basic/basmgr.hxx>
+#include <basic/sbuno.hxx>
+#include <basic/sberrors.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <cppuhelper/weak.hxx>
+#include <cppuhelper/weakref.hxx>
+#include <framework/documentundoguard.hxx>
+#include <sfx2/app.hxx>
+#include <sfx2/frame.hxx>
+#include <sfx2/objsh.hxx>
+#include <tools/urlobj.hxx>
+#include <vcl/svapp.hxx>
+
+#include <memory>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::frame;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::util;
+
+SfxMacroLoader::SfxMacroLoader(const css::uno::Sequence< css::uno::Any >& aArguments)
+{
+ Reference < XFrame > xFrame;
+ if ( aArguments.hasElements() )
+ {
+ aArguments[0] >>= xFrame;
+ m_xFrame = xFrame;
+ }
+}
+
+OUString SAL_CALL SfxMacroLoader::getImplementationName()
+{
+ return "com.sun.star.comp.sfx2.SfxMacroLoader";
+}
+
+sal_Bool SAL_CALL SfxMacroLoader::supportsService(OUString const & ServiceName)
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+css::uno::Sequence<OUString> SAL_CALL SfxMacroLoader::getSupportedServiceNames()
+{
+ return { "com.sun.star.frame.ProtocolHandler" };
+}
+
+SfxObjectShell* SfxMacroLoader::GetObjectShell(const Reference <XFrame>& xFrame)
+{
+ SfxObjectShell* pDocShell = nullptr;
+
+ if ( xFrame.is() )
+ {
+ SfxFrame* pFrame=nullptr;
+ for ( pFrame = SfxFrame::GetFirst(); pFrame; pFrame = SfxFrame::GetNext( *pFrame ) )
+ {
+ if ( pFrame->GetFrameInterface() == xFrame )
+ break;
+ }
+
+ if ( pFrame )
+ pDocShell = pFrame->GetCurrentDocument();
+ }
+
+ return pDocShell;
+}
+
+SfxObjectShell* SfxMacroLoader::GetObjectShell_Impl()
+{
+ Reference < XFrame > xFrame( m_xFrame.get(), UNO_QUERY );
+ return SfxMacroLoader::GetObjectShell(xFrame);
+}
+
+uno::Reference<frame::XDispatch> SAL_CALL SfxMacroLoader::queryDispatch(
+ const util::URL& aURL ,
+ const OUString& /*sTargetFrameName*/,
+ sal_Int32 /*nSearchFlags*/ )
+{
+ uno::Reference<frame::XDispatch> xDispatcher;
+ if(aURL.Complete.startsWith("macro:"))
+ xDispatcher = this;
+ return xDispatcher;
+}
+
+
+uno::Sequence< uno::Reference<frame::XDispatch> > SAL_CALL
+ SfxMacroLoader::queryDispatches( const uno::Sequence < frame::DispatchDescriptor >& seqDescriptor )
+{
+ sal_Int32 nCount = seqDescriptor.getLength();
+ uno::Sequence< uno::Reference<frame::XDispatch> > lDispatcher(nCount);
+ std::transform(seqDescriptor.begin(), seqDescriptor.end(), lDispatcher.getArray(),
+ [this](const frame::DispatchDescriptor& rDescr) -> uno::Reference<frame::XDispatch> {
+ return queryDispatch(rDescr.FeatureURL, rDescr.FrameName, rDescr.SearchFlags); });
+ return lDispatcher;
+}
+
+
+void SAL_CALL SfxMacroLoader::dispatchWithNotification(
+ const util::URL& aURL, const uno::Sequence<beans::PropertyValue>& /*lArgs*/,
+ const uno::Reference<frame::XDispatchResultListener>& xListener )
+{
+ SolarMutexGuard aGuard;
+
+ uno::Any aAny;
+ ErrCode nErr = loadMacro( aURL.Complete, aAny, GetObjectShell_Impl() );
+ if( !xListener.is() )
+ return;
+
+ // always call dispatchFinished(), because we didn't load a document but
+ // executed a macro instead!
+ frame::DispatchResultEvent aEvent;
+
+ aEvent.Source = static_cast< ::cppu::OWeakObject* >(this);
+ if( nErr == ERRCODE_NONE )
+ aEvent.State = frame::DispatchResultState::SUCCESS;
+ else
+ aEvent.State = frame::DispatchResultState::FAILURE;
+
+ xListener->dispatchFinished( aEvent ) ;
+}
+
+uno::Any SAL_CALL SfxMacroLoader::dispatchWithReturnValue(
+ const util::URL& aURL, const uno::Sequence<beans::PropertyValue>& )
+{
+ uno::Any aRet;
+ ErrCode nErr = loadMacro( aURL.Complete, aRet, GetObjectShell_Impl() );
+
+ // aRet gets set to a different value only if nErr == ERRCODE_NONE
+ // Return it in such case to preserve the original behaviour
+
+ // In all other cases (nErr != ERRCODE_NONE), the calling code gets
+ // the actual error code back
+ if ( nErr != ERRCODE_NONE )
+ {
+ beans::PropertyValue aErrorCode;
+
+ aErrorCode.Name = "ErrorCode";
+ aErrorCode.Value <<= sal_uInt32(nErr);
+
+ aRet <<= aErrorCode;
+ }
+
+ return aRet;
+}
+
+void SAL_CALL SfxMacroLoader::dispatch(
+ const util::URL& aURL, const uno::Sequence<beans::PropertyValue>& /*lArgs*/ )
+{
+ SolarMutexGuard aGuard;
+
+ uno::Any aAny;
+ loadMacro( aURL.Complete, aAny, GetObjectShell_Impl() );
+}
+
+void SAL_CALL SfxMacroLoader::addStatusListener(
+ const uno::Reference< frame::XStatusListener >& ,
+ const util::URL& )
+{
+ /* TODO
+ How we can handle different listener for further coming or currently running dispatch() jobs
+ without any inconsistency!
+ */
+}
+
+
+void SAL_CALL SfxMacroLoader::removeStatusListener(
+ const uno::Reference< frame::XStatusListener >&,
+ const util::URL& )
+{
+}
+
+ErrCode SfxMacroLoader::loadMacro( const OUString& rURL, css::uno::Any& rRetval, SfxObjectShell* pSh )
+{
+#if !HAVE_FEATURE_SCRIPTING
+ (void) rURL;
+ (void) rRetval;
+ (void) pSh;
+ return ERRCODE_BASIC_PROC_UNDEFINED;
+#else
+ SfxObjectShell* pCurrent = pSh;
+ if ( !pCurrent )
+ // all not full qualified names use the BASIC of the given or current document
+ pCurrent = SfxObjectShell::Current();
+
+ // 'macro:///lib.mod.proc(args)' => macro of App-BASIC
+ // 'macro://[docname|.]/lib.mod.proc(args)' => macro of current or qualified document
+ // 'macro://obj.method(args)' => direct API call, execute it via App-BASIC
+ const OUString& aMacro( rURL );
+ sal_Int32 nThirdSlashPos = aMacro.indexOf( '/', 8 );
+ sal_Int32 nArgsPos = aMacro.indexOf( '(' );
+ BasicManager *pAppMgr = SfxApplication::GetBasicManager();
+ BasicManager *pBasMgr = nullptr;
+ ErrCode nErr = ERRCODE_NONE;
+
+ // should a macro function be executed ( no direct API call)?
+ if ( -1 != nThirdSlashPos && ( -1 == nArgsPos || nThirdSlashPos < nArgsPos ) )
+ {
+ // find BasicManager
+ SfxObjectShell* pDoc = nullptr;
+ OUString aBasMgrName( INetURLObject::decode(aMacro.subView( 8, nThirdSlashPos-8 ), INetURLObject::DecodeMechanism::WithCharset) );
+ if ( aBasMgrName.isEmpty() )
+ pBasMgr = pAppMgr;
+ else if ( aBasMgrName == "." )
+ {
+ // current/actual document
+ pDoc = pCurrent;
+ if (pDoc)
+ pBasMgr = pDoc->GetBasicManager();
+ }
+ else
+ {
+ // full qualified name, find document by name
+ for ( SfxObjectShell *pObjSh = SfxObjectShell::GetFirst();
+ pObjSh && !pBasMgr;
+ pObjSh = SfxObjectShell::GetNext(*pObjSh) )
+ if ( aBasMgrName == pObjSh->GetTitle(SFX_TITLE_APINAME) )
+ {
+ pDoc = pObjSh;
+ pBasMgr = pDoc->GetBasicManager();
+ }
+ }
+
+ if ( pBasMgr )
+ {
+ const bool bIsAppBasic = ( pBasMgr == pAppMgr );
+ const bool bIsDocBasic = ( pBasMgr != pAppMgr );
+
+ if ( pDoc )
+ {
+ // security check for macros from document basic if an SFX doc is given
+ if ( !pDoc->AdjustMacroMode() )
+ // check forbids execution
+ return ERRCODE_IO_ACCESSDENIED;
+ }
+
+ // find BASIC method
+ OUString aQualifiedMethod( INetURLObject::decode(aMacro.subView( nThirdSlashPos+1 ), INetURLObject::DecodeMechanism::WithCharset) );
+ OUString aArgs;
+ if ( -1 != nArgsPos )
+ {
+ // remove arguments from macro name
+ aArgs = aQualifiedMethod.copy( nArgsPos - nThirdSlashPos - 1 );
+ aQualifiedMethod = aQualifiedMethod.copy( 0, nArgsPos - nThirdSlashPos - 1 );
+ }
+
+ if ( pBasMgr->HasMacro( aQualifiedMethod ) )
+ {
+ Any aOldThisComponent;
+ const bool bSetDocMacroMode = ( pDoc != nullptr ) && bIsDocBasic;
+ const bool bSetGlobalThisComponent = ( pDoc != nullptr ) && bIsAppBasic;
+ if ( bSetDocMacroMode )
+ {
+ // mark document: it executes an own macro, so it's in a modal mode
+ pDoc->SetMacroMode_Impl();
+ }
+
+ if ( bSetGlobalThisComponent )
+ {
+ // document is executed via AppBASIC, adjust ThisComponent variable
+ pAppMgr->SetGlobalUNOConstant( "ThisComponent", Any( pDoc->GetModel() ), &aOldThisComponent );
+ }
+
+ // just to let the shell be alive
+ SfxObjectShellRef xKeepDocAlive = pDoc;
+
+ {
+ // attempt to protect the document against the script tampering with its Undo Context
+ std::optional< ::framework::DocumentUndoGuard > pUndoGuard;
+ if ( bIsDocBasic )
+ pUndoGuard.emplace( pDoc->GetModel() );
+
+ // execute the method
+ SbxVariableRef retValRef = new SbxVariable;
+ nErr = pBasMgr->ExecuteMacro( aQualifiedMethod, aArgs, retValRef.get() );
+ if ( nErr == ERRCODE_NONE )
+ rRetval = sbxToUnoValue( retValRef.get() );
+ }
+
+ if ( bSetGlobalThisComponent )
+ {
+ pAppMgr->SetGlobalUNOConstant( "ThisComponent", aOldThisComponent );
+ }
+
+ if ( bSetDocMacroMode )
+ {
+ // remove flag for modal mode
+ pDoc->SetMacroMode_Impl( false );
+ }
+ }
+ else
+ nErr = ERRCODE_BASIC_PROC_UNDEFINED;
+ }
+ else
+ nErr = ERRCODE_IO_NOTEXISTS;
+ }
+ else
+ {
+ // direct API call on a specified object
+ OUString aCall =
+ "[" +
+ INetURLObject::decode(aMacro.subView(6),
+ INetURLObject::DecodeMechanism::WithCharset) +
+ "]";
+ pAppMgr->GetLib(0)->Execute(aCall);
+ nErr = SbxBase::GetError();
+ }
+
+ SbxBase::ResetError();
+ return nErr;
+#endif
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_sfx2_SfxMacroLoader_get_implementation(
+ css::uno::XComponentContext *,
+ css::uno::Sequence<css::uno::Any> const &arguments)
+{
+ return cppu::acquire(new SfxMacroLoader(arguments));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/appl/module.cxx b/sfx2/source/appl/module.cxx
new file mode 100644
index 000000000..fe68c5e57
--- /dev/null
+++ b/sfx2/source/appl/module.cxx
@@ -0,0 +1,267 @@
+/* -*- 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/module.hxx>
+#include <sfx2/app.hxx>
+#include <sfx2/msgpool.hxx>
+#include <sfx2/tbxctrl.hxx>
+#include <sfx2/stbitem.hxx>
+#include <sfx2/childwin.hxx>
+#include <sfx2/docfac.hxx>
+#include <sfx2/objface.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/tabdlg.hxx>
+#include <sfx2/sfxsids.hrc>
+#include <svl/intitem.hxx>
+#include <tools/diagnose_ex.h>
+#include <unotools/resmgr.hxx>
+#include <sal/log.hxx>
+
+#define ShellClass_SfxModule
+#include <sfxslots.hxx>
+#include <optional>
+
+class SfxModule_Impl
+{
+public:
+
+ std::optional<SfxSlotPool> pSlotPool;
+ std::vector<SfxTbxCtrlFactory> maTbxCtrlFactories;
+ std::vector<SfxStbCtrlFactory> maStbCtrlFactories;
+ std::vector<SfxChildWinFactory> maFactories;
+ OString maResName;
+
+ SfxModule_Impl();
+ ~SfxModule_Impl();
+};
+
+SfxModule_Impl::SfxModule_Impl()
+{
+}
+
+SfxModule_Impl::~SfxModule_Impl()
+{
+ pSlotPool.reset();
+ maTbxCtrlFactories.clear();
+ maStbCtrlFactories.clear();
+}
+
+SFX_IMPL_SUPERCLASS_INTERFACE(SfxModule, SfxShell)
+
+SfxModule::SfxModule(const OString& rResName, std::initializer_list<SfxObjectFactory*> pFactoryList)
+ : pImpl(nullptr)
+{
+ Construct_Impl(rResName);
+ for (auto pFactory : pFactoryList)
+ {
+ if (pFactory)
+ pFactory->SetModule_Impl( this );
+ }
+}
+
+void SfxModule::Construct_Impl(const OString& rResName)
+{
+ SfxApplication *pApp = SfxApplication::GetOrCreate();
+ pImpl = new SfxModule_Impl;
+ pImpl->pSlotPool.emplace(&pApp->GetAppSlotPool_Impl());
+ pImpl->maResName = rResName;
+
+ SetPool( &pApp->GetPool() );
+}
+
+SfxModule::~SfxModule()
+{
+ //TODO how to silence useuniqueptr
+ if (true)
+ {
+ delete pImpl;
+ }
+}
+
+std::locale SfxModule::GetResLocale() const
+{
+ return Translate::Create(pImpl->maResName);
+}
+
+SfxSlotPool* SfxModule::GetSlotPool() const
+{
+ return &*pImpl->pSlotPool;
+}
+
+
+void SfxModule::RegisterChildWindow(const SfxChildWinFactory& rFact)
+{
+ DBG_ASSERT( pImpl, "No real Module!" );
+
+ for (size_t nFactory=0; nFactory<pImpl->maFactories.size(); ++nFactory)
+ {
+ if (rFact.nId == pImpl->maFactories[nFactory].nId)
+ {
+ pImpl->maFactories.erase( pImpl->maFactories.begin() + nFactory );
+ SAL_WARN("sfx.appl", "ChildWindow registered multiple times!");
+ return;
+ }
+ }
+
+ pImpl->maFactories.push_back( rFact );
+}
+
+
+void SfxModule::RegisterToolBoxControl( const SfxTbxCtrlFactory& rFact )
+{
+#ifdef DBG_UTIL
+ for ( size_t n=0; n<pImpl->maTbxCtrlFactories.size(); n++ )
+ {
+ SfxTbxCtrlFactory *pF = &pImpl->maTbxCtrlFactories[n];
+ if ( pF->nTypeId == rFact.nTypeId &&
+ (pF->nSlotId == rFact.nSlotId || pF->nSlotId == 0) )
+ {
+ SAL_INFO("sfx.appl", "TbxController-Registering is not clearly defined!");
+ }
+ }
+#endif
+
+ pImpl->maTbxCtrlFactories.push_back( rFact );
+}
+
+
+void SfxModule::RegisterStatusBarControl( const SfxStbCtrlFactory& rFact )
+{
+#ifdef DBG_UTIL
+ for ( size_t n=0; n<pImpl->maStbCtrlFactories.size(); n++ )
+ {
+ SfxStbCtrlFactory *pF = &pImpl->maStbCtrlFactories[n];
+ if ( pF->nTypeId == rFact.nTypeId &&
+ (pF->nSlotId == rFact.nSlotId || pF->nSlotId == 0) )
+ {
+ SAL_INFO("sfx.appl", "TbxController-Registering is not clearly defined!");
+ }
+ }
+#endif
+
+ pImpl->maStbCtrlFactories.push_back( rFact );
+}
+
+
+SfxTbxCtrlFactory* SfxModule::GetTbxCtrlFactory(const std::type_info& rSlotType, sal_uInt16 nSlotID) const
+{
+ // search for a factory with the given slot id
+ for (auto& rFactory : pImpl->maTbxCtrlFactories)
+ if( rFactory.nTypeId == rSlotType && rFactory.nSlotId == nSlotID )
+ return &rFactory;
+
+ // if no factory exists for the given slot id, see if we
+ // have a generic factory with the correct slot type and slot id == 0
+ for (auto& rFactory : pImpl->maTbxCtrlFactories)
+ if( rFactory.nTypeId == rSlotType && rFactory.nSlotId == 0 )
+ return &rFactory;
+
+ return nullptr;
+}
+
+
+SfxStbCtrlFactory* SfxModule::GetStbCtrlFactory(const std::type_info& rSlotType, sal_uInt16 nSlotID) const
+{
+ for (auto& rFactory : pImpl->maStbCtrlFactories)
+ if ( rFactory.nTypeId == rSlotType &&
+ ( rFactory.nSlotId == 0 || rFactory.nSlotId == nSlotID ) )
+ return &rFactory;
+ return nullptr;
+}
+
+SfxChildWinFactory* SfxModule::GetChildWinFactoryById(sal_uInt16 nId) const
+{
+ for (auto& rFactory : pImpl->maFactories)
+ if (rFactory.nId == nId)
+ return &rFactory;
+ return nullptr;
+}
+
+std::unique_ptr<SfxTabPage> SfxModule::CreateTabPage(sal_uInt16, weld::Container*, weld::DialogController*, const SfxItemSet&)
+{
+ return nullptr;
+}
+
+void SfxModule::Invalidate( sal_uInt16 nId )
+{
+ for( SfxViewFrame* pFrame = SfxViewFrame::GetFirst(); pFrame; pFrame = SfxViewFrame::GetNext( *pFrame ) )
+ if ( pFrame->GetObjectShell()->GetModule() == this )
+ Invalidate_Impl( pFrame->GetBindings(), nId );
+}
+
+SfxModule* SfxModule::GetActiveModule( SfxViewFrame* pFrame )
+{
+ if ( !pFrame )
+ pFrame = SfxViewFrame::Current();
+ SfxObjectShell* pSh = nullptr;
+ if( pFrame )
+ pSh = pFrame->GetObjectShell();
+ return pSh ? pSh->GetModule() : nullptr;
+}
+
+FieldUnit SfxModule::GetModuleFieldUnit( css::uno::Reference< css::frame::XFrame > const & i_frame )
+{
+ ENSURE_OR_RETURN( i_frame.is(), "SfxModule::GetModuleFieldUnit: invalid frame!", FieldUnit::MM_100TH );
+
+ // find SfxViewFrame for the given XFrame
+ SfxViewFrame* pViewFrame = SfxViewFrame::GetFirst();
+ while ( pViewFrame != nullptr )
+ {
+ if ( pViewFrame->GetFrame().GetFrameInterface() == i_frame )
+ break;
+ pViewFrame = SfxViewFrame::GetNext( *pViewFrame );
+ }
+ ENSURE_OR_RETURN(
+ pViewFrame != nullptr,
+ "SfxModule::GetModuleFieldUnit: unable to find an SfxViewFrame for the given XFrame",
+ FieldUnit::MM_100TH);
+
+ // find the module
+ SfxModule const * pModule = GetActiveModule( pViewFrame );
+ ENSURE_OR_RETURN(pModule != nullptr,
+ "SfxModule::GetModuleFieldUnit: no SfxModule for the given frame!",
+ FieldUnit::MM_100TH);
+ return pModule->GetFieldUnit();
+}
+
+FieldUnit SfxModule::GetCurrentFieldUnit()
+{
+ FieldUnit eUnit = FieldUnit::INCH;
+ SfxModule* pModule = GetActiveModule();
+ if ( pModule )
+ {
+ const SfxPoolItem* pItem = pModule->GetItem( SID_ATTR_METRIC );
+ if ( pItem )
+ eUnit = static_cast<FieldUnit>(static_cast<const SfxUInt16Item*>(pItem)->GetValue());
+ }
+ else
+ SAL_WARN( "sfx.appl", "GetModuleFieldUnit(): no module found" );
+ return eUnit;
+}
+
+FieldUnit SfxModule::GetFieldUnit() const
+{
+ FieldUnit eUnit = FieldUnit::INCH;
+ const SfxPoolItem* pItem = GetItem( SID_ATTR_METRIC );
+ if ( pItem )
+ eUnit = static_cast<FieldUnit>(static_cast<const SfxUInt16Item*>(pItem)->GetValue());
+ return eUnit;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/appl/newhelp.cxx b/sfx2/source/appl/newhelp.cxx
new file mode 100644
index 000000000..d9fdc9d0c
--- /dev/null
+++ b/sfx2/source/appl/newhelp.cxx
@@ -0,0 +1,2681 @@
+/* -*- 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 "newhelp.hxx"
+#include <sfx2/sfxresid.hxx>
+#include "helpinterceptor.hxx"
+#include <helper.hxx>
+#include <srchdlg.hxx>
+#include <sfx2/sfxhelp.hxx>
+#include <sal/log.hxx>
+#include <tools/debug.hxx>
+#include <tools/diagnose_ex.h>
+#include <o3tl/string_view.hxx>
+
+#include <sfx2/strings.hrc>
+#include <helpids.h>
+#include <bitmaps.hlst>
+
+#include <rtl/ustrbuf.hxx>
+#include <comphelper/configurationhelper.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/string.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/beans/XPropertySetInfo.hpp>
+#include <com/sun/star/container/XIndexAccess.hpp>
+#include <com/sun/star/frame/XComponentLoader.hpp>
+#include <com/sun/star/frame/XTitle.hpp>
+#include <com/sun/star/frame/XLayoutManager.hpp>
+#include <com/sun/star/frame/XController.hpp>
+#include <com/sun/star/frame/XDispatch.hpp>
+#include <com/sun/star/frame/XDispatchProvider.hpp>
+#include <com/sun/star/frame/Frame.hpp>
+#include <com/sun/star/i18n/XBreakIterator.hpp>
+#include <com/sun/star/i18n/WordType.hpp>
+#include <com/sun/star/lang/XComponent.hpp>
+#include <com/sun/star/style/XStyle.hpp>
+#include <com/sun/star/style/XStyleFamiliesSupplier.hpp>
+#include <com/sun/star/text/XText.hpp>
+#include <com/sun/star/text/XTextCursor.hpp>
+#include <com/sun/star/text/XTextDocument.hpp>
+#include <com/sun/star/text/XTextViewCursor.hpp>
+#include <com/sun/star/text/XTextViewCursorSupplier.hpp>
+#include <com/sun/star/ucb/CommandAbortedException.hpp>
+#include <com/sun/star/util/URL.hpp>
+#include <com/sun/star/util/XSearchable.hpp>
+#include <com/sun/star/util/XSearchDescriptor.hpp>
+#include <com/sun/star/util/URLTransformer.hpp>
+#include <com/sun/star/util/XURLTransformer.hpp>
+#include <com/sun/star/util/XModifiable.hpp>
+#include <com/sun/star/util/XCloseable.hpp>
+#include <com/sun/star/util/CloseVetoException.hpp>
+#include <com/sun/star/view/XSelectionSupplier.hpp>
+#include <com/sun/star/view/XViewSettingsSupplier.hpp>
+#include <unotools/historyoptions.hxx>
+#include <unotools/viewoptions.hxx>
+#include <tools/urlobj.hxx>
+#include <svtools/imagemgr.hxx>
+#include <svtools/miscopt.hxx>
+#include <vcl/commandevent.hxx>
+#include <vcl/event.hxx>
+#include <vcl/i18nhelp.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/unohelp.hxx>
+#include <vcl/weld.hxx>
+
+#include <ucbhelper/content.hxx>
+#include <unotools/ucbhelper.hxx>
+
+#include <string_view>
+#include <unordered_map>
+#include <vector>
+
+using namespace ::ucbhelper;
+using namespace ::com::sun::star::ucb;
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::frame;
+using namespace ::com::sun::star::i18n;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::style;
+using namespace ::com::sun::star::text;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::util;
+using namespace ::com::sun::star::view;
+using namespace ::com::sun::star::ui;
+
+using namespace ::comphelper;
+
+// defines ---------------------------------------------------------------
+
+constexpr OUStringLiteral CONFIGNAME_HELPWIN = u"OfficeHelp";
+constexpr OUStringLiteral CONFIGNAME_INDEXWIN = u"OfficeHelpIndex";
+constexpr OUStringLiteral CONFIGNAME_SEARCHPAGE = u"OfficeHelpSearch";
+constexpr OUStringLiteral IMAGE_URL = u"private:factory/";
+
+constexpr OUStringLiteral PROPERTY_KEYWORDLIST = u"KeywordList";
+constexpr OUStringLiteral PROPERTY_KEYWORDREF = u"KeywordRef";
+constexpr OUStringLiteral PROPERTY_ANCHORREF = u"KeywordAnchorForRef";
+constexpr OUStringLiteral PROPERTY_TITLEREF = u"KeywordTitleForRef";
+constexpr OUStringLiteral PROPERTY_TITLE = u"Title";
+constexpr OUStringLiteral HELP_URL = u"vnd.sun.star.help://";
+constexpr OUStringLiteral HELP_SEARCH_TAG = u"/?Query=";
+constexpr OUStringLiteral USERITEM_NAME = u"UserItem";
+
+constexpr OUStringLiteral PACKAGE_SETUP = u"/org.openoffice.Setup";
+constexpr OUStringLiteral PATH_OFFICE_FACTORIES = u"Office/Factories/";
+constexpr OUStringLiteral KEY_HELP_ON_OPEN = u"ooSetupFactoryHelpOnOpen";
+constexpr OUStringLiteral KEY_UI_NAME = u"ooSetupFactoryUIName";
+
+namespace sfx2
+{
+
+
+ /** Prepare a search string for searching or selecting.
+ For searching every search word needs the postfix '*' and the delimiter ' ' if necessary.
+ For selecting the delimiter '|' is required to search with regular expressions.
+ Samples:
+ search string | output for searching | output for selecting
+ -----------------------------------------------------------
+ "text" | "text*" | "text"
+ "text*" | "text*" | "text"
+ "text menu" | "text* menu*" | "text|menu"
+ */
+ static OUString PrepareSearchString( const OUString& rSearchString,
+ const Reference< XBreakIterator >& xBreak, bool bForSearch )
+ {
+ OUStringBuffer sSearchStr;
+ sal_Int32 nStartPos = 0;
+ const lang::Locale aLocale = Application::GetSettings().GetUILanguageTag().getLocale();
+ Boundary aBoundary = xBreak->getWordBoundary(
+ rSearchString, nStartPos, aLocale, WordType::ANYWORD_IGNOREWHITESPACES, true );
+
+ while ( aBoundary.startPos < aBoundary.endPos )
+ {
+ nStartPos = aBoundary.endPos;
+ OUString sSearchToken( rSearchString.copy(
+ static_cast<sal_uInt16>(aBoundary.startPos), static_cast<sal_uInt16>(aBoundary.endPos) - static_cast<sal_uInt16>(aBoundary.startPos) ) );
+ if ( !sSearchToken.isEmpty() && ( sSearchToken.getLength() > 1 || sSearchToken[0] != '.' ) )
+ {
+ if ( bForSearch && sSearchToken[ sSearchToken.getLength() - 1 ] != '*' )
+ sSearchToken += "*";
+
+ if ( sSearchToken.getLength() > 1 ||
+ ( sSearchToken.getLength() > 0 && sSearchToken[ 0 ] != '*' ) )
+ {
+ if ( !sSearchStr.isEmpty() )
+ {
+ if ( bForSearch )
+ sSearchStr.append(" ");
+ else
+ sSearchStr.append("|");
+ }
+ sSearchStr.append(sSearchToken);
+ }
+ }
+ aBoundary = xBreak->nextWord( rSearchString, nStartPos,
+ aLocale, WordType::ANYWORD_IGNOREWHITESPACES );
+ }
+
+ return sSearchStr.makeStringAndClear();
+ }
+
+// namespace sfx2
+}
+
+
+// struct IndexEntry_Impl ------------------------------------------------
+
+namespace {
+
+struct IndexEntry_Impl
+{
+ bool m_bSubEntry;
+ OUString m_aURL;
+
+ IndexEntry_Impl( const OUString& rURL, bool bSubEntry ) :
+ m_bSubEntry( bSubEntry ), m_aURL( rURL ) {}
+};
+
+// struct ContentEntry_Impl ----------------------------------------------
+
+struct ContentEntry_Impl
+{
+ OUString aURL;
+ bool bIsFolder;
+
+ ContentEntry_Impl( const OUString& rURL, bool bFolder ) :
+ aURL( rURL ), bIsFolder( bFolder ) {}
+};
+
+}
+
+void ContentTabPage_Impl::InitRoot()
+{
+ std::vector< OUString > aList =
+ SfxContentHelper::GetHelpTreeViewContents( "vnd.sun.star.hier://com.sun.star.help.TreeView/" );
+
+ for (const OUString & aRow : aList)
+ {
+ sal_Int32 nIdx = 0;
+ OUString aTitle = aRow.getToken( 0, '\t', nIdx );
+ OUString aURL = aRow.getToken( 0, '\t', nIdx );
+ sal_Unicode cFolder = o3tl::getToken(aRow, 0, '\t', nIdx )[0];
+ bool bIsFolder = ( '1' == cFolder );
+ OUString sId;
+ if (bIsFolder)
+ sId = weld::toId(new ContentEntry_Impl(aURL, true));
+ m_xContentBox->insert(nullptr, -1, &aTitle, &sId, nullptr, nullptr, true, m_xScratchIter.get());
+ m_xContentBox->set_image(*m_xScratchIter, aClosedBookImage);
+ }
+}
+
+void ContentTabPage_Impl::ClearChildren(const weld::TreeIter* pParent)
+{
+ std::unique_ptr<weld::TreeIter> xEntry = m_xContentBox->make_iterator(pParent);
+ bool bEntry = m_xContentBox->iter_children(*xEntry);
+ while (bEntry)
+ {
+ ClearChildren(xEntry.get());
+ delete weld::fromId<ContentEntry_Impl*>(m_xContentBox->get_id(*xEntry));
+ bEntry = m_xContentBox->iter_next_sibling(*xEntry);
+ }
+
+}
+
+IMPL_LINK(ContentTabPage_Impl, ExpandingHdl, const weld::TreeIter&, rIter, bool)
+{
+ ContentEntry_Impl* pContentEntry = weld::fromId<ContentEntry_Impl*>(m_xContentBox->get_id(rIter));
+ if (!m_xContentBox->iter_has_child(rIter))
+ {
+ try
+ {
+ if (pContentEntry)
+ {
+ std::vector<OUString > aList = SfxContentHelper::GetHelpTreeViewContents(pContentEntry->aURL);
+
+ for (const OUString & aRow : aList)
+ {
+ sal_Int32 nIdx = 0;
+ OUString aTitle = aRow.getToken( 0, '\t', nIdx );
+ OUString aURL = aRow.getToken( 0, '\t', nIdx );
+ sal_Unicode cFolder = o3tl::getToken(aRow, 0, '\t', nIdx )[0];
+ bool bIsFolder = ( '1' == cFolder );
+ if ( bIsFolder )
+ {
+ OUString sId = weld::toId(new ContentEntry_Impl(aURL, true));
+ m_xContentBox->insert(&rIter, -1, &aTitle, &sId, nullptr, nullptr, true, m_xScratchIter.get());
+ m_xContentBox->set_image(*m_xScratchIter, aClosedBookImage);
+ }
+ else
+ {
+ Any aAny( ::utl::UCBContentHelper::GetProperty( aURL, "TargetURL" ) );
+ OUString sId;
+ OUString aTargetURL;
+ if ( aAny >>= aTargetURL )
+ sId = weld::toId(new ContentEntry_Impl(aTargetURL, false));
+ m_xContentBox->insert(&rIter, -1, &aTitle, &sId, nullptr, nullptr, false, m_xScratchIter.get());
+ m_xContentBox->set_image(*m_xScratchIter, aDocumentImage);
+ }
+ }
+ }
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "sfx.appl", "ContentListBox_Impl::RequestingChildren(): unexpected exception" );
+ }
+ }
+
+ if (!pContentEntry || pContentEntry->bIsFolder)
+ m_xContentBox->set_image(rIter, aOpenBookImage);
+
+ return true;
+}
+
+IMPL_LINK(ContentTabPage_Impl, CollapsingHdl, const weld::TreeIter&, rIter, bool)
+{
+ ContentEntry_Impl* pContentEntry = weld::fromId<ContentEntry_Impl*>(m_xContentBox->get_id(rIter));
+ if (!pContentEntry || pContentEntry->bIsFolder)
+ m_xContentBox->set_image(rIter, aClosedBookImage);
+
+ return true;
+}
+
+OUString ContentTabPage_Impl::GetSelectedEntry() const
+{
+ OUString aRet;
+ ContentEntry_Impl* pEntry = weld::fromId<ContentEntry_Impl*>(m_xContentBox->get_selected_id());
+ if (pEntry && !pEntry->bIsFolder)
+ aRet = pEntry->aURL;
+ return aRet;
+}
+
+// class HelpTabPage_Impl ------------------------------------------------
+HelpTabPage_Impl::HelpTabPage_Impl(weld::Widget* pParent, SfxHelpIndexWindow_Impl* pIdxWin,
+ const OString& rID, const OUString& rUIXMLDescription)
+ : BuilderPage(pParent, nullptr, rUIXMLDescription, rID)
+ , m_pIdxWin(pIdxWin)
+{
+}
+
+HelpTabPage_Impl::~HelpTabPage_Impl()
+{
+}
+
+// class ContentTabPage_Impl ---------------------------------------------
+ContentTabPage_Impl::ContentTabPage_Impl(weld::Widget* pParent, SfxHelpIndexWindow_Impl* pIdxWin)
+ : HelpTabPage_Impl(pParent, pIdxWin, "HelpContentPage",
+ "sfx/ui/helpcontentpage.ui")
+ , m_xContentBox(m_xBuilder->weld_tree_view("content"))
+ , m_xScratchIter(m_xContentBox->make_iterator())
+ , aOpenBookImage(BMP_HELP_CONTENT_BOOK_OPEN)
+ , aClosedBookImage(BMP_HELP_CONTENT_BOOK_CLOSED)
+ , aDocumentImage(BMP_HELP_CONTENT_DOC)
+{
+ m_xContentBox->set_size_request(m_xContentBox->get_approximate_digit_width() * 30,
+ m_xContentBox->get_height_rows(20));
+ m_xContentBox->connect_row_activated(LINK(this, ContentTabPage_Impl, DoubleClickHdl));
+ m_xContentBox->connect_expanding(LINK(this, ContentTabPage_Impl, ExpandingHdl));
+ m_xContentBox->connect_collapsing(LINK(this, ContentTabPage_Impl, CollapsingHdl));
+
+ InitRoot();
+}
+
+IMPL_LINK_NOARG(ContentTabPage_Impl, DoubleClickHdl, weld::TreeView&, bool)
+{
+ aDoubleClickHdl.Call(nullptr);
+ return false;
+}
+
+void ContentTabPage_Impl::SetDoubleClickHdl(const Link<LinkParamNone*, void>& rLink)
+{
+ aDoubleClickHdl = rLink;
+}
+
+ContentTabPage_Impl::~ContentTabPage_Impl()
+{
+ std::unique_ptr<weld::TreeIter> xEntry = m_xContentBox->make_iterator();
+ bool bEntry = m_xContentBox->get_iter_first(*xEntry);
+ while (bEntry)
+ {
+ ClearChildren(xEntry.get());
+ delete weld::fromId<ContentEntry_Impl*>(m_xContentBox->get_id(*xEntry));
+ bEntry = m_xContentBox->iter_next_sibling(*xEntry);
+ }
+}
+
+void IndexTabPage_Impl::SelectExecutableEntry()
+{
+ sal_Int32 nPos = m_xIndexList->find_text(m_xIndexEntry->get_text());
+ if (nPos == -1)
+ return;
+
+ sal_Int32 nOldPos = nPos;
+ OUString aEntryText;
+ IndexEntry_Impl* pEntry = weld::fromId<IndexEntry_Impl*>(m_xIndexList->get_id(nPos));
+ sal_Int32 nCount = m_xIndexList->n_children();
+ while ( nPos < nCount && ( !pEntry || pEntry->m_aURL.isEmpty() ) )
+ {
+ pEntry = weld::fromId<IndexEntry_Impl*>(m_xIndexList->get_id(++nPos));
+ aEntryText = m_xIndexList->get_text(nPos);
+ }
+
+ if ( nOldPos != nPos )
+ m_xIndexEntry->set_text(aEntryText);
+}
+
+// class IndexTabPage_Impl -----------------------------------------------
+IndexTabPage_Impl::IndexTabPage_Impl(weld::Widget* pParent, SfxHelpIndexWindow_Impl* pIdxWin)
+ : HelpTabPage_Impl(pParent, pIdxWin, "HelpIndexPage", "sfx/ui/helpindexpage.ui")
+ , m_xIndexEntry(m_xBuilder->weld_entry("termentry"))
+ , m_xIndexList(m_xBuilder->weld_tree_view("termlist"))
+ , m_xOpenBtn(m_xBuilder->weld_button("display"))
+ , aFactoryIdle("sfx2 appl IndexTabPage_Impl Factory")
+ , aAutoCompleteIdle("sfx2 appl IndexTabPage_Impl AutoComplete")
+ , aKeywordTimer("sfx2::IndexTabPage_Impl aKeywordTimer")
+ , bIsActivated(false)
+ , nRowHeight(m_xIndexList->get_height_rows(1))
+ , nAllHeight(0)
+ , nLastCharCode(0)
+{
+ m_xIndexList->set_size_request(m_xIndexList->get_approximate_digit_width() * 30, -1);
+
+ m_xOpenBtn->connect_clicked(LINK(this, IndexTabPage_Impl, OpenHdl));
+ aFactoryIdle.SetInvokeHandler( LINK(this, IndexTabPage_Impl, IdleHdl ));
+ aAutoCompleteIdle.SetInvokeHandler( LINK(this, IndexTabPage_Impl, AutoCompleteHdl ));
+ aKeywordTimer.SetInvokeHandler( LINK( this, IndexTabPage_Impl, TimeoutHdl ) );
+ m_xIndexList->connect_row_activated(LINK(this, IndexTabPage_Impl, DoubleClickHdl));
+ m_xIndexList->connect_changed(LINK(this, IndexTabPage_Impl, TreeChangeHdl));
+ m_xIndexList->connect_custom_get_size(LINK(this, IndexTabPage_Impl, CustomGetSizeHdl));
+ m_xIndexList->connect_custom_render(LINK(this, IndexTabPage_Impl, CustomRenderHdl));
+ m_xIndexList->set_column_custom_renderer(0, true);
+ m_xIndexList->connect_size_allocate(LINK(this, IndexTabPage_Impl, ResizeHdl));
+ m_xIndexEntry->connect_key_press(LINK(this, IndexTabPage_Impl, KeyInputHdl));
+ m_xIndexEntry->connect_changed(LINK(this, IndexTabPage_Impl, EntryChangeHdl));
+ m_xIndexEntry->connect_activate(LINK(this, IndexTabPage_Impl, ActivateHdl));
+}
+
+IMPL_LINK(IndexTabPage_Impl, ResizeHdl, const Size&, rSize, void)
+{
+ nAllHeight = rSize.Height();
+}
+
+IMPL_LINK_NOARG(IndexTabPage_Impl, CustomGetSizeHdl, weld::TreeView::get_size_args, Size)
+{
+ return Size(m_xIndexList->get_size_request().Width(), nRowHeight);
+}
+
+IMPL_LINK(IndexTabPage_Impl, CustomRenderHdl, weld::TreeView::render_args, aPayload, void)
+{
+ vcl::RenderContext& rRenderContext = std::get<0>(aPayload);
+ const ::tools::Rectangle& rRect = std::get<1>(aPayload);
+ 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());
+
+ Point aPos(rRect.TopLeft());
+ aPos.AdjustY((rRect.GetHeight() - rRenderContext.GetTextHeight()) / 2);
+
+ int nIndex = m_xIndexList->find_id(rId);
+ OUString aEntry(m_xIndexList->get_text(nIndex));
+
+ IndexEntry_Impl* pEntry = weld::fromId<IndexEntry_Impl*>(rId);
+ if (pEntry && pEntry->m_bSubEntry)
+ {
+ // indent sub entries
+ aPos.AdjustX(8);
+ sal_Int32 nPos = aEntry.indexOf(';');
+ rRenderContext.DrawText(aPos, (nPos !=-1) ? aEntry.copy(nPos + 1) : aEntry);
+ }
+ else
+ rRenderContext.DrawText(aPos, aEntry);
+
+ rRenderContext.Pop();
+}
+
+IMPL_LINK_NOARG(IndexTabPage_Impl, TreeChangeHdl, weld::TreeView&, void)
+{
+ m_xIndexEntry->set_text(m_xIndexList->get_selected_text());
+}
+
+IMPL_LINK_NOARG(IndexTabPage_Impl, EntryChangeHdl, weld::Entry&, void)
+{
+ switch (nLastCharCode)
+ {
+ case css::awt::Key::DELETE_WORD_BACKWARD:
+ case css::awt::Key::DELETE_WORD_FORWARD:
+ case css::awt::Key::DELETE_TO_BEGIN_OF_LINE:
+ case css::awt::Key::DELETE_TO_END_OF_LINE:
+ case KEY_BACKSPACE:
+ case KEY_DELETE:
+ aAutoCompleteIdle.Stop();
+ break;
+ default:
+ aAutoCompleteIdle.Start();
+ break;
+ }
+}
+
+IMPL_LINK(IndexTabPage_Impl, KeyInputHdl, const KeyEvent&, rKEvt, bool)
+{
+ const vcl::KeyCode& rKCode = rKEvt.GetKeyCode();
+ if (rKCode.GetModifier()) // only with no modifiers held
+ return false;
+
+ sal_uInt16 nCode = rKCode.GetCode();
+
+ if (nCode == KEY_UP || nCode == KEY_PAGEUP ||
+ nCode == KEY_DOWN || nCode == KEY_PAGEDOWN)
+ {
+// disable_notify_events();
+ sal_Int32 nIndex = m_xIndexList->get_selected_index();
+ sal_Int32 nOrigIndex = nIndex;
+ sal_Int32 nCount = m_xIndexList->n_children();
+ if (nIndex == -1)
+ {
+ m_xIndexList->set_cursor(0);
+ m_xIndexList->select(0);
+ m_xIndexEntry->set_text(m_xIndexList->get_selected_text());
+ }
+ else
+ {
+ if (nCode == KEY_UP)
+ --nIndex;
+ else if (nCode == KEY_DOWN)
+ ++nIndex;
+ else if (nCode == KEY_PAGEUP)
+ {
+ int nVisRows = nAllHeight / nRowHeight;
+ nIndex -= nVisRows;
+ }
+ else if (nCode == KEY_PAGEDOWN)
+ {
+ int nVisRows = nAllHeight / nRowHeight;
+ nIndex += nVisRows;
+ }
+
+ if (nIndex < 0)
+ nIndex = 0;
+ if (nIndex >= nCount)
+ nIndex = nCount - 1;
+
+ if (nIndex != nOrigIndex)
+ {
+ m_xIndexList->set_cursor(nIndex);
+ m_xIndexList->select(nIndex);
+ m_xIndexEntry->set_text(m_xIndexList->get_selected_text());
+ }
+
+// m_xIndexList->grab_focus();
+// g_signal_emit_by_name(pWidget, "key-press-event", pEvent, &ret);
+// m_xIndexEntry->set_text(m_xIndexList->get_selected_text());
+// m_xIndexEntry->grab_focus();
+ }
+ m_xIndexEntry->select_region(0, -1);
+// enable_notify_events();
+// m_bTreeChange = true;
+// m_pEntry->fire_signal_changed();
+// m_bTreeChange = false;
+ return true;
+ }
+
+ nLastCharCode = nCode;
+ return false;
+}
+
+IndexTabPage_Impl::~IndexTabPage_Impl()
+{
+ ClearIndex();
+}
+
+namespace sfx2 {
+
+ typedef std::unordered_map< OUString, int > KeywordInfo;
+}
+
+void IndexTabPage_Impl::InitializeIndex()
+{
+ weld::WaitObject aWaitCursor(m_pIdxWin->GetFrameWeld());
+
+ // By now more than 256 equal entries are not allowed
+ sal_Unicode append[256];
+ for(sal_Unicode & k : append)
+ k = ' ';
+
+ sfx2::KeywordInfo aInfo;
+ m_xIndexList->freeze();
+
+ try
+ {
+ OUStringBuffer aURL(HELP_URL);
+ aURL.append(sFactory);
+ AppendConfigToken(aURL, true);
+
+ Content aCnt( aURL.makeStringAndClear(), Reference< css::ucb::XCommandEnvironment >(), comphelper::getProcessComponentContext() );
+ css::uno::Reference< css::beans::XPropertySetInfo > xInfo = aCnt.getProperties();
+ if ( xInfo->hasPropertyByName( PROPERTY_ANCHORREF ) )
+ {
+ css::uno::Sequence< OUString > aPropSeq{ PROPERTY_KEYWORDLIST, PROPERTY_KEYWORDREF,
+ PROPERTY_ANCHORREF, PROPERTY_TITLEREF };
+
+ // abi: use one possibly remote call only
+ css::uno::Sequence< css::uno::Any > aAnySeq =
+ aCnt.getPropertyValues( aPropSeq );
+
+ css::uno::Sequence< OUString > aKeywordList;
+ css::uno::Sequence< css::uno::Sequence< OUString > > aKeywordRefList;
+ css::uno::Sequence< css::uno::Sequence< OUString > > aAnchorRefList;
+ css::uno::Sequence< css::uno::Sequence< OUString > > aTitleRefList;
+
+ if ( ( aAnySeq[0] >>= aKeywordList ) && ( aAnySeq[1] >>= aKeywordRefList ) &&
+ ( aAnySeq[2] >>= aAnchorRefList ) && ( aAnySeq[3] >>= aTitleRefList ) )
+ {
+ int ndx,tmp;
+ OUString aIndex, aTempString;
+ OUStringBuffer aData( 128 ); // Capacity of up to 128 characters
+ sfx2::KeywordInfo::iterator it;
+
+ for ( int i = 0; i < aKeywordList.getLength(); ++i )
+ {
+ // abi: Do not copy, but use references
+ const OUString& aKeywordPair = aKeywordList[i];
+ DBG_ASSERT( !aKeywordPair.isEmpty(), "invalid help index" );
+ const css::uno::Sequence< OUString >& aRefList = aKeywordRefList[i];
+ const css::uno::Sequence< OUString >& aAnchorList = aAnchorRefList[i];
+ const css::uno::Sequence< OUString >& aTitleList = aTitleRefList[i];
+
+ DBG_ASSERT( aRefList.getLength() == aAnchorList.getLength(),"reference list and title list of different length" );
+
+ ndx = aKeywordPair.indexOf( ';' );
+ const bool insert = ndx != -1;
+
+ OUString sId;
+
+ if ( insert )
+ {
+ aTempString = aKeywordPair.copy( 0, ndx );
+ if ( aIndex != aTempString )
+ {
+ aIndex = aTempString;
+ it = aInfo.emplace(aTempString, 0).first;
+ sId = weld::toId(new IndexEntry_Impl(OUString(), false));
+ if ( (tmp = it->second++) != 0)
+ m_xIndexList->append(
+ sId, aTempString + std::u16string_view(append, tmp));
+ else
+ m_xIndexList->append(sId, aTempString);
+ }
+ }
+ else
+ aIndex.clear();
+
+ sal_uInt32 nRefListLen = aRefList.getLength();
+
+ DBG_ASSERT( aAnchorList.hasElements(), "*IndexTabPage_Impl::InitializeIndex(): AnchorList is empty!" );
+ DBG_ASSERT( nRefListLen, "*IndexTabPage_Impl::InitializeIndex(): RefList is empty!" );
+
+ if ( aAnchorList.hasElements() && nRefListLen )
+ {
+ if ( aAnchorList[0].getLength() > 0 )
+ {
+ aData.append( aRefList[0] ).append( '#' ).append( aAnchorList[0] );
+ sId = weld::toId(new IndexEntry_Impl(aData.makeStringAndClear(), insert));
+ }
+ else
+ sId = weld::toId(new IndexEntry_Impl(aRefList[0], insert));
+ }
+
+ // Assume the token is trimmed
+ it = aInfo.emplace(aKeywordPair, 0).first;
+ if ((tmp = it->second++) != 0)
+ m_xIndexList->append(sId, aKeywordPair + std::u16string_view(append, tmp));
+ else
+ m_xIndexList->append(sId, aKeywordPair);
+
+ for ( sal_uInt32 j = 1; j < nRefListLen ; ++j )
+ {
+ aData
+ .append( aKeywordPair )
+ .append( ' ' )
+ .append( '-' )
+ .append( ' ' )
+ .append( aTitleList[j] );
+
+ aTempString = aData.makeStringAndClear();
+
+ if ( aAnchorList[j].getLength() > 0 )
+ {
+ aData.append( aRefList[j] ).append( '#' ).append( aAnchorList[j] );
+ sId = weld::toId(new IndexEntry_Impl(aData.makeStringAndClear(), insert));
+ }
+ else
+ sId = weld::toId(new IndexEntry_Impl(aRefList[j], insert));
+
+ it = aInfo.emplace(aTempString, 0).first;
+ if ( (tmp = it->second++) != 0 )
+ m_xIndexList->append(
+ sId, aTempString + std::u16string_view(append, tmp));
+ else
+ m_xIndexList->append(sId, aTempString);
+ }
+ }
+ }
+ }
+ }
+ catch( Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "sfx.appl", "IndexTabPage_Impl::InitializeIndex(): unexpected exception" );
+ }
+
+ m_xIndexList->thaw();
+
+ if ( !sKeyword.isEmpty() )
+ aKeywordLink.Call( *this );
+}
+
+void IndexTabPage_Impl::ClearIndex()
+{
+ const sal_Int32 nCount = m_xIndexList->n_children();
+ for ( sal_Int32 i = 0; i < nCount; ++i )
+ delete weld::fromId<IndexEntry_Impl*>(m_xIndexList->get_id(i));
+ m_xIndexList->clear();
+}
+
+IMPL_LINK_NOARG(IndexTabPage_Impl, OpenHdl, weld::Button&, void)
+{
+ aDoubleClickHdl.Call(nullptr);
+}
+
+IMPL_LINK_NOARG(IndexTabPage_Impl, ActivateHdl, weld::Entry&, bool)
+{
+ aDoubleClickHdl.Call(nullptr);
+ return true;
+}
+
+IMPL_LINK_NOARG(IndexTabPage_Impl, DoubleClickHdl, weld::TreeView&, bool)
+{
+ aDoubleClickHdl.Call(nullptr);
+ return true;
+}
+
+IMPL_LINK_NOARG(IndexTabPage_Impl, IdleHdl, Timer*, void)
+{
+ InitializeIndex();
+}
+
+int IndexTabPage_Impl::starts_with(const OUString& rStr, int nStartRow, bool bCaseSensitive)
+{
+ const vcl::I18nHelper& rI18nHelper = Application::GetSettings().GetUILocaleI18nHelper();
+
+ int nRet = nStartRow;
+ int nCount = m_xIndexList->n_children();
+ while (nRet < nCount)
+ {
+ OUString aStr(m_xIndexList->get_text(nRet));
+ const bool bMatch = !bCaseSensitive ? rI18nHelper.MatchString(rStr, aStr) : aStr.startsWith(rStr);
+ if (bMatch)
+ return nRet;
+ ++nRet;
+ }
+
+ return -1;
+}
+
+IMPL_LINK_NOARG(IndexTabPage_Impl, AutoCompleteHdl, Timer*, void)
+{
+ OUString aStartText = m_xIndexEntry->get_text();
+ int nStartPos, nEndPos;
+ m_xIndexEntry->get_selection_bounds(nStartPos, nEndPos);
+ int nMaxSelection = std::max(nStartPos, nEndPos);
+ if (nMaxSelection != aStartText.getLength())
+ return;
+
+ int nActive = m_xIndexList->get_selected_index();
+ int nStart = nActive;
+
+ if (nStart == -1)
+ nStart = 0;
+
+ // Try match case insensitive from current position
+ int nPos = starts_with(aStartText, nStart, false);
+ if (nPos == -1 && nStart != 0)
+ {
+ // Try match case insensitive, but from start
+ nPos = starts_with(aStartText, 0, false);
+ }
+
+ if (nPos == -1)
+ {
+ // Try match case sensitive from current position
+ nPos = starts_with(aStartText, nStart, true);
+ if (nPos == -1 && nStart != 0)
+ {
+ // Try match case sensitive, but from start
+ nPos = starts_with(aStartText, 0, true);
+ }
+ }
+
+ if (nPos != -1)
+ {
+ m_xIndexList->set_cursor(nPos);
+ m_xIndexList->select(nPos);
+ OUString aText = m_xIndexList->get_text(nPos);
+ if (aText != aStartText)
+ m_xIndexEntry->set_text(aText);
+ m_xIndexEntry->select_region(aText.getLength(), aStartText.getLength());
+ }
+}
+
+IMPL_LINK( IndexTabPage_Impl, TimeoutHdl, Timer*, pTimer, void)
+{
+ if(&aKeywordTimer == pTimer && !sKeyword.isEmpty())
+ aKeywordLink.Call(*this);
+}
+
+void IndexTabPage_Impl::Activate()
+{
+ if ( !bIsActivated )
+ {
+ bIsActivated = true;
+ aFactoryIdle.Start();
+ }
+}
+
+void IndexTabPage_Impl::SetDoubleClickHdl(const Link<LinkParamNone*, void>& rLink)
+{
+ aDoubleClickHdl = rLink;
+}
+
+void IndexTabPage_Impl::SetFactory( const OUString& rFactory )
+{
+ OUString sNewFactory( rFactory );
+ DBG_ASSERT( !sNewFactory.isEmpty(), "empty factory" );
+ bool bValid = m_pIdxWin->IsValidFactory( rFactory );
+
+ if ( sFactory.isEmpty() && !bValid )
+ {
+ sNewFactory = SfxHelp::GetDefaultHelpModule();
+ bValid = true;
+ }
+
+ if ( sNewFactory != sFactory && bValid )
+ {
+ sFactory = sNewFactory;
+ ClearIndex();
+ if ( bIsActivated )
+ aFactoryIdle.Start();
+ }
+}
+
+OUString IndexTabPage_Impl::GetSelectedEntry() const
+{
+ OUString aRet;
+ IndexEntry_Impl* pEntry = weld::fromId<IndexEntry_Impl*>(m_xIndexList->get_id(m_xIndexList->find_text(m_xIndexEntry->get_text())));
+ if (pEntry)
+ aRet = pEntry->m_aURL;
+ return aRet;
+}
+
+void IndexTabPage_Impl::SetKeyword( const OUString& rKeyword )
+{
+ sKeyword = rKeyword;
+
+ if (m_xIndexList->n_children() > 0)
+ aKeywordTimer.Start();
+ else if ( !bIsActivated )
+ aFactoryIdle.Start();
+}
+
+
+bool IndexTabPage_Impl::HasKeyword() const
+{
+ bool bRet = false;
+ if ( !sKeyword.isEmpty() )
+ {
+ sal_Int32 nPos = m_xIndexList->find_text( sKeyword );
+ bRet = nPos != -1;
+ }
+
+ return bRet;
+}
+
+
+bool IndexTabPage_Impl::HasKeywordIgnoreCase()
+{
+ bool bRet = false;
+ if ( !sKeyword.isEmpty() )
+ {
+ sal_Int32 nEntries = m_xIndexList->n_children();
+ const vcl::I18nHelper& rI18nHelper = Application::GetSettings().GetLocaleI18nHelper();
+ for ( sal_Int32 n = 0; n < nEntries; n++)
+ {
+ const OUString sIndexItem {m_xIndexList->get_text(n)};
+ if (rI18nHelper.MatchString( sIndexItem, sKeyword ))
+ {
+ sKeyword = sIndexItem;
+ bRet = true;
+ }
+ }
+ }
+
+ return bRet;
+}
+
+void IndexTabPage_Impl::OpenKeyword()
+{
+ if ( !sKeyword.isEmpty() )
+ {
+ m_xIndexEntry->set_text(sKeyword);
+ aDoubleClickHdl.Call(nullptr);
+ sKeyword.clear();
+ }
+}
+
+IMPL_LINK_NOARG(SearchTabPage_Impl, ActivateHdl, weld::ComboBox&, bool)
+{
+ Search();
+ return true;
+}
+
+// class SearchTabPage_Impl ----------------------------------------------
+
+SearchTabPage_Impl::SearchTabPage_Impl(weld::Widget* pParent, SfxHelpIndexWindow_Impl* pIdxWin)
+ : HelpTabPage_Impl(pParent, pIdxWin, "HelpSearchPage",
+ "sfx/ui/helpsearchpage.ui")
+ , m_xSearchED(m_xBuilder->weld_combo_box("search"))
+ , m_xSearchBtn(m_xBuilder->weld_button("find"))
+ , m_xFullWordsCB(m_xBuilder->weld_check_button("completewords"))
+ , m_xScopeCB(m_xBuilder->weld_check_button("headings"))
+ , m_xResultsLB(m_xBuilder->weld_tree_view("results"))
+ , m_xOpenBtn(m_xBuilder->weld_button("display"))
+ , xBreakIterator(vcl::unohelper::CreateBreakIterator())
+{
+ m_xResultsLB->set_size_request(m_xResultsLB->get_approximate_digit_width() * 30,
+ m_xResultsLB->get_height_rows(15));
+
+ m_xSearchBtn->connect_clicked(LINK(this, SearchTabPage_Impl, ClickHdl));
+ m_xSearchED->connect_changed(LINK(this, SearchTabPage_Impl, ModifyHdl));
+ m_xSearchED->connect_entry_activate(LINK(this, SearchTabPage_Impl, ActivateHdl));
+ m_xOpenBtn->connect_clicked(LINK(this, SearchTabPage_Impl, OpenHdl));
+ m_xResultsLB->connect_row_activated(LINK(this, SearchTabPage_Impl, DoubleClickHdl));
+
+ SvtViewOptions aViewOpt( EViewType::TabPage, CONFIGNAME_SEARCHPAGE );
+ if ( aViewOpt.Exists() )
+ {
+ OUString aUserData;
+ Any aUserItem = aViewOpt.GetUserItem( USERITEM_NAME );
+ if ( aUserItem >>= aUserData )
+ {
+ sal_Int32 nIdx {0};
+ bool bChecked = o3tl::toInt32(o3tl::getToken(aUserData, 0, ';', nIdx)) == 1;
+ m_xFullWordsCB->set_active(bChecked);
+ bChecked = o3tl::toInt32(o3tl::getToken(aUserData, 0, ';', nIdx)) == 1;
+ m_xScopeCB->set_active(bChecked);
+
+ while ( nIdx > 0 )
+ {
+ m_xSearchED->append_text( INetURLObject::decode(
+ o3tl::getToken(aUserData, 0, ';', nIdx),
+ INetURLObject::DecodeMechanism::WithCharset ) );
+ }
+ }
+ }
+
+ ModifyHdl(*m_xSearchED);
+}
+
+SearchTabPage_Impl::~SearchTabPage_Impl()
+{
+ SvtViewOptions aViewOpt( EViewType::TabPage, CONFIGNAME_SEARCHPAGE );
+ OUStringBuffer aUserData =
+ OUString::number(m_xFullWordsCB->get_active() ? 1 : 0) +
+ ";" +
+ OUString::number(m_xScopeCB->get_active() ? 1 : 0);
+ sal_Int32 nCount = std::min(m_xSearchED->get_count(), 10); // save only 10 entries
+
+ for ( sal_Int32 i = 0; i < nCount; ++i )
+ {
+ aUserData.append(";" +
+ INetURLObject::encode(
+ m_xSearchED->get_text(i),
+ INetURLObject::PART_UNO_PARAM_VALUE,
+ INetURLObject::EncodeMechanism::All ));
+ }
+
+ Any aUserItem( aUserData.makeStringAndClear() );
+ aViewOpt.SetUserItem( USERITEM_NAME, aUserItem );
+
+ m_xSearchED.reset();
+ m_xSearchBtn.reset();
+ m_xFullWordsCB.reset();
+ m_xScopeCB.reset();
+ m_xResultsLB.reset();
+ m_xOpenBtn.reset();
+}
+
+void SearchTabPage_Impl::ClearSearchResults()
+{
+ m_xResultsLB->clear();
+}
+
+void SearchTabPage_Impl::RememberSearchText( const OUString& rSearchText )
+{
+ for (sal_Int32 i = 0, nEntryCount = m_xSearchED->get_count(); i < nEntryCount; ++i)
+ {
+ if (rSearchText == m_xSearchED->get_text(i))
+ {
+ m_xSearchED->remove(i);
+ break;
+ }
+ }
+
+ m_xSearchED->insert_text(0, rSearchText);
+}
+
+IMPL_LINK_NOARG(SearchTabPage_Impl, ClickHdl, weld::Button&, void)
+{
+ Search();
+}
+
+void SearchTabPage_Impl::Search()
+{
+ OUString aSearchText = comphelper::string::strip(m_xSearchED->get_active_text(), ' ');
+ if ( aSearchText.isEmpty() )
+ return;
+
+ std::unique_ptr<weld::WaitObject> xWaitCursor(new weld::WaitObject(m_pIdxWin->GetFrameWeld()));
+ ClearSearchResults();
+ RememberSearchText( aSearchText );
+ OUStringBuffer aSearchURL(HELP_URL);
+ aSearchURL.append(aFactory);
+ aSearchURL.append(HELP_SEARCH_TAG);
+ if (!m_xFullWordsCB->get_active())
+ aSearchText = sfx2::PrepareSearchString( aSearchText, xBreakIterator, true );
+ aSearchURL.append(aSearchText);
+ AppendConfigToken(aSearchURL, false);
+ if (m_xScopeCB->get_active())
+ aSearchURL.append("&Scope=Heading");
+ std::vector< OUString > aFactories = SfxContentHelper::GetResultSet(aSearchURL.makeStringAndClear());
+ for (const OUString & rRow : aFactories)
+ {
+ sal_Int32 nIdx = 0;
+ OUString aTitle = rRow.getToken(0, '\t', nIdx);
+ OUString sURL(rRow.getToken(1, '\t', nIdx));
+ m_xResultsLB->append(sURL, aTitle);
+ }
+ xWaitCursor.reset();
+
+ if ( aFactories.empty() )
+ {
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(m_xContainer.get(),
+ VclMessageType::Info, VclButtonsType::Ok,
+ SfxResId(STR_INFO_NOSEARCHRESULTS)));
+ xBox->run();
+ }
+}
+
+IMPL_LINK_NOARG(SearchTabPage_Impl, OpenHdl, weld::Button&, void)
+{
+ aDoubleClickHdl.Call(nullptr);
+}
+
+IMPL_LINK(SearchTabPage_Impl, ModifyHdl, weld::ComboBox&, rComboBox, void)
+{
+ OUString aSearchText = comphelper::string::strip(m_xSearchED->get_active_text(), ' ');
+ m_xSearchBtn->set_sensitive(!aSearchText.isEmpty());
+
+ if (rComboBox.changed_by_direct_pick())
+ Search();
+}
+
+IMPL_LINK_NOARG(SearchTabPage_Impl, DoubleClickHdl, weld::TreeView&, bool)
+{
+ aDoubleClickHdl.Call(nullptr);
+ return true;
+}
+
+void SearchTabPage_Impl::SetDoubleClickHdl(const Link<LinkParamNone*, void>& rLink)
+{
+ aDoubleClickHdl = rLink;
+}
+
+OUString SearchTabPage_Impl::GetSelectedEntry() const
+{
+ return m_xResultsLB->get_selected_id();
+}
+
+void SearchTabPage_Impl::ClearPage()
+{
+ ClearSearchResults();
+ m_xSearchED->set_entry_text(OUString());
+}
+
+bool SearchTabPage_Impl::OpenKeyword( const OUString& rKeyword )
+{
+ bool bRet = false;
+ m_xSearchED->set_entry_text(rKeyword);
+ Search();
+ if (m_xResultsLB->n_children() > 0)
+ {
+ // found keyword -> open it
+ m_xResultsLB->select(0);
+ OpenHdl(*m_xOpenBtn);
+ bRet = true;
+ }
+ return bRet;
+}
+
+// class BookmarksTabPage_Impl -------------------------------------------
+
+void BookmarksTabPage_Impl::DoAction(std::string_view rAction)
+{
+ if (rAction == "display")
+ aDoubleClickHdl.Call(nullptr);
+ else if (rAction == "rename")
+ {
+ sal_Int32 nPos = m_xBookmarksBox->get_selected_index();
+ if (nPos != -1)
+ {
+ SfxAddHelpBookmarkDialog_Impl aDlg(m_xBookmarksBox.get(), true);
+ aDlg.SetTitle(m_xBookmarksBox->get_text(nPos));
+ if (aDlg.run() == RET_OK)
+ {
+ OUString sURL = m_xBookmarksBox->get_id(nPos);
+ m_xBookmarksBox->remove(nPos);
+ m_xBookmarksBox->append(sURL, aDlg.GetTitle(),
+ SvFileInformationManager::GetImageId(INetURLObject(rtl::OUStringConcatenation(IMAGE_URL+INetURLObject(sURL).GetHost()))));
+ m_xBookmarksBox->select(m_xBookmarksBox->n_children() - 1);
+ }
+ }
+ }
+ else if (rAction == "delete")
+ {
+ sal_Int32 nPos = m_xBookmarksBox->get_selected_index();
+ if (nPos != -1)
+ {
+ m_xBookmarksBox->remove(nPos);
+ const sal_Int32 nCount = m_xBookmarksBox->n_children();
+ if (nCount)
+ {
+ if (nPos >= nCount)
+ nPos = nCount - 1;
+ m_xBookmarksBox->select(nPos);
+ }
+ }
+ }
+}
+
+IMPL_LINK(BookmarksTabPage_Impl, CommandHdl, const CommandEvent&, rCEvt, bool)
+{
+ if (rCEvt.GetCommand() != CommandEventId::ContextMenu)
+ return false;
+
+ std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(m_xBookmarksBox.get(), "sfx/ui/bookmarkmenu.ui"));
+ std::unique_ptr<weld::Menu> xMenu = xBuilder->weld_menu("menu");
+
+ OString sIdent = xMenu->popup_at_rect(m_xBookmarksBox.get(), ::tools::Rectangle(rCEvt.GetMousePosPixel(), Size(1,1)));
+ if (!sIdent.isEmpty())
+ DoAction(sIdent);
+ return true;
+}
+
+IMPL_LINK(BookmarksTabPage_Impl, KeyInputHdl, const KeyEvent&, rKEvt, bool)
+{
+ bool bHandled = false;
+ sal_uInt16 nCode = rKEvt.GetKeyCode().GetCode();
+ if (KEY_DELETE == nCode && m_xBookmarksBox->n_children() > 0)
+ {
+ DoAction("delete");
+ bHandled = true;
+ }
+ return bHandled;
+}
+
+// class BookmarksTabPage_Impl -------------------------------------------
+BookmarksTabPage_Impl::BookmarksTabPage_Impl(weld::Widget* pParent, SfxHelpIndexWindow_Impl* _pIdxWin)
+ : HelpTabPage_Impl(pParent, _pIdxWin, "HelpBookmarkPage",
+ "sfx/ui/helpbookmarkpage.ui")
+ , m_xBookmarksBox(m_xBuilder->weld_tree_view("bookmarks"))
+ , m_xBookmarksPB(m_xBuilder->weld_button("display"))
+{
+ m_xBookmarksBox->set_size_request(m_xBookmarksBox->get_approximate_digit_width() * 30,
+ m_xBookmarksBox->get_height_rows(20));
+
+ m_xBookmarksPB->connect_clicked( LINK(this, BookmarksTabPage_Impl, OpenHdl));
+ m_xBookmarksBox->connect_row_activated(LINK(this, BookmarksTabPage_Impl, DoubleClickHdl));
+ m_xBookmarksBox->connect_popup_menu(LINK(this, BookmarksTabPage_Impl, CommandHdl));
+ m_xBookmarksBox->connect_key_press(LINK(this, BookmarksTabPage_Impl, KeyInputHdl));
+
+ // load bookmarks from configuration
+ const std::vector< SvtHistoryOptions::HistoryItem > aBookmarkSeq = SvtHistoryOptions::GetList( EHistoryType::HelpBookmarks );
+ for ( const auto& rBookmark : aBookmarkSeq )
+ {
+ AddBookmarks( rBookmark.sTitle, rBookmark.sURL );
+ }
+}
+
+BookmarksTabPage_Impl::~BookmarksTabPage_Impl()
+{
+ // save bookmarks to configuration
+ SvtHistoryOptions::Clear( EHistoryType::HelpBookmarks );
+ const sal_Int32 nCount = m_xBookmarksBox->n_children();
+ for (sal_Int32 i = 0; i < nCount; ++i)
+ {
+ SvtHistoryOptions::AppendItem(EHistoryType::HelpBookmarks, m_xBookmarksBox->get_id(i), "", m_xBookmarksBox->get_text(i), std::nullopt, std::nullopt);
+ }
+
+ m_xBookmarksBox.reset();
+ m_xBookmarksPB.reset();
+}
+
+IMPL_LINK_NOARG(BookmarksTabPage_Impl, OpenHdl, weld::Button&, void)
+{
+ aDoubleClickHdl.Call(nullptr);
+}
+
+IMPL_LINK_NOARG(BookmarksTabPage_Impl, DoubleClickHdl, weld::TreeView&, bool)
+{
+ aDoubleClickHdl.Call(nullptr);
+ return true;
+}
+
+void BookmarksTabPage_Impl::SetDoubleClickHdl(const Link<LinkParamNone*, void>& rLink)
+{
+ aDoubleClickHdl = rLink;
+}
+
+OUString BookmarksTabPage_Impl::GetSelectedEntry() const
+{
+ return m_xBookmarksBox->get_selected_id();
+}
+
+void BookmarksTabPage_Impl::AddBookmarks(const OUString& rTitle, const OUString& rURL)
+{
+ const OUString aImageURL {IMAGE_URL + INetURLObject(rURL).GetHost()};
+ m_xBookmarksBox->append(rURL, rTitle, SvFileInformationManager::GetImageId(INetURLObject(aImageURL)));
+}
+
+OUString SfxHelpWindow_Impl::buildHelpURL(std::u16string_view sFactory ,
+ std::u16string_view sContent ,
+ std::u16string_view sAnchor)
+{
+ OUStringBuffer sHelpURL(256);
+ sHelpURL.append(HELP_URL);
+ sHelpURL.append(sFactory);
+ sHelpURL.append(sContent);
+ AppendConfigToken(sHelpURL, true/*bUseQuestionMark*/);
+ if (!sAnchor.empty())
+ sHelpURL.append(sAnchor);
+ return sHelpURL.makeStringAndClear();
+}
+
+void SfxHelpWindow_Impl::loadHelpContent(const OUString& sHelpURL, bool bAddToHistory)
+{
+ Reference< XComponentLoader > xLoader(getTextFrame(), UNO_QUERY);
+ if (!xLoader.is())
+ return;
+
+ // If a print job runs do not open a new page
+ Reference< XFrame2 > xTextFrame = pTextWin->getFrame();
+ Reference< XController > xTextController ;
+ if (xTextFrame.is())
+ xTextController = xTextFrame->getController ();
+ if ( xTextController.is() && !xTextController->suspend( true ) )
+ {
+ xTextController->suspend( false );
+ return;
+ }
+
+ // save url to history
+ if (bAddToHistory)
+ pHelpInterceptor->addURL(sHelpURL);
+
+ if ( !IsWait() )
+ EnterWait();
+ bool bSuccess = false;
+// TODO implement locale fallback ... see below while(true)
+ {
+ try
+ {
+ Reference< XComponent > xContent = xLoader->loadComponentFromURL(sHelpURL, "_self", 0, Sequence< PropertyValue >());
+ if (xContent.is())
+ {
+ bSuccess = true;
+ }
+ }
+ catch(const RuntimeException&)
+ { throw; }
+ catch(const Exception&)
+ { /*break;*/ }
+
+ /* TODO try next locale ...
+ no further locale available? => break loop and show error page
+ */
+ }
+ openDone(sHelpURL, bSuccess);
+ if ( IsWait() )
+ LeaveWait();
+}
+
+IMPL_LINK(SfxHelpIndexWindow_Impl, ActivatePageHdl, const OString&, rPage, void)
+{
+ GetPage(rPage)->Activate();
+}
+
+SfxHelpIndexWindow_Impl::SfxHelpIndexWindow_Impl(SfxHelpWindow_Impl* _pParent, weld::Container* pContainer)
+ : m_xBuilder(Application::CreateBuilder(pContainer, "sfx/ui/helpcontrol.ui"))
+ , m_xContainer(m_xBuilder->weld_container("HelpControl"))
+ , m_xActiveLB(m_xBuilder->weld_combo_box("active"))
+ , m_xTabCtrl(m_xBuilder->weld_notebook("tabcontrol"))
+ , aIdle("sfx2 appl SfxHelpIndexWindow_Impl")
+ , aIndexKeywordLink(LINK(this, SfxHelpIndexWindow_Impl, KeywordHdl))
+ , pParentWin(_pParent)
+ , bIsInitDone(false)
+{
+ // create the pages
+ GetContentPage();
+ GetIndexPage();
+ GetSearchPage();
+ GetBookmarksPage();
+
+ OString sPageId("index");
+ SvtViewOptions aViewOpt( EViewType::TabDialog, CONFIGNAME_INDEXWIN );
+ if ( aViewOpt.Exists() )
+ {
+ OString sSavedPageId = aViewOpt.GetPageID();
+ if (m_xTabCtrl->get_page_index(sSavedPageId) != -1)
+ sPageId = sSavedPageId;
+ }
+ m_xTabCtrl->set_current_page(sPageId);
+ ActivatePageHdl(sPageId);
+ m_xActiveLB->connect_changed(LINK(this, SfxHelpIndexWindow_Impl, SelectHdl));
+
+ m_xTabCtrl->connect_enter_page(LINK(this, SfxHelpIndexWindow_Impl, ActivatePageHdl));
+
+ aIdle.SetInvokeHandler( LINK( this, SfxHelpIndexWindow_Impl, InitHdl ) );
+ aIdle.Start();
+
+ m_xContainer->show();
+}
+
+SfxHelpIndexWindow_Impl::~SfxHelpIndexWindow_Impl()
+{
+ SvtViewOptions aViewOpt(EViewType::TabDialog, CONFIGNAME_INDEXWIN);
+ aViewOpt.SetPageID(m_xTabCtrl->get_current_page_ident());
+
+ xCPage.reset();
+ xIPage.reset();
+ xSPage.reset();
+ xBPage.reset();
+}
+
+void SfxHelpIndexWindow_Impl::Initialize()
+{
+ OUStringBuffer aHelpURL(HELP_URL);
+ AppendConfigToken(aHelpURL, true);
+ std::vector<OUString> aFactories = SfxContentHelper::GetResultSet(aHelpURL.makeStringAndClear());
+ for (const OUString & rRow : aFactories)
+ {
+ sal_Int32 nIdx = 0;
+ OUString aTitle = rRow.getToken( 0, '\t', nIdx ); // token 0
+ std::u16string_view aURL = o3tl::getToken(rRow, 1, '\t', nIdx ); // token 2
+ OUString aFactory(INetURLObject(aURL).GetHost());
+ m_xActiveLB->append(aFactory, aTitle);
+ }
+
+ if (m_xActiveLB->get_active() == -1)
+ SetActiveFactory();
+}
+
+void SfxHelpIndexWindow_Impl::SetActiveFactory()
+{
+ DBG_ASSERT( xIPage, "index page not initialized" );
+ if (!bIsInitDone && !m_xActiveLB->get_count())
+ {
+ aIdle.Stop();
+ InitHdl( nullptr );
+ }
+
+ for (sal_Int32 i = 0, nEntryCount = m_xActiveLB->get_count(); i < nEntryCount; ++i)
+ {
+ OUString aFactory = m_xActiveLB->get_id(i);
+ aFactory = aFactory.toAsciiLowerCase();
+ if (aFactory == xIPage->GetFactory())
+ {
+ if (m_xActiveLB->get_active() != i)
+ {
+ m_xActiveLB->set_active(i);
+ aSelectFactoryLink.Call(nullptr);
+ }
+ break;
+ }
+ }
+}
+
+HelpTabPage_Impl* SfxHelpIndexWindow_Impl::GetPage(std::string_view rName)
+{
+ HelpTabPage_Impl* pPage = nullptr;
+
+ if (rName == "contents")
+ pPage = GetContentPage();
+ else if (rName == "index")
+ pPage = GetIndexPage();
+ else if (rName == "find")
+ pPage = GetSearchPage();
+ else if (rName == "bookmarks")
+ pPage = GetBookmarksPage();
+
+ assert(pPage && "SfxHelpIndexWindow_Impl::GetCurrentPage(): no current page");
+
+ return pPage;
+}
+
+IMPL_LINK_NOARG(SfxHelpIndexWindow_Impl, SelectHdl, weld::ComboBox&, void)
+{
+ aIdle.Start();
+}
+
+IMPL_LINK_NOARG(SfxHelpIndexWindow_Impl, InitHdl, Timer *, void)
+{
+ bIsInitDone = true;
+ Initialize();
+
+ // now use the timer for selection
+ aIdle.SetInvokeHandler( LINK( this, SfxHelpIndexWindow_Impl, SelectFactoryHdl ) );
+ aIdle.SetPriority( TaskPriority::LOWEST );
+}
+
+IMPL_LINK_NOARG(SfxHelpIndexWindow_Impl, SelectFactoryHdl, Timer *, void)
+{
+ OUString aFactory = m_xActiveLB->get_active_id();
+ if (!aFactory.isEmpty())
+ {
+ SetFactory(aFactory.toAsciiLowerCase(), false);
+ aSelectFactoryLink.Call(this);
+ }
+}
+
+IMPL_LINK_NOARG(SfxHelpIndexWindow_Impl, KeywordHdl, IndexTabPage_Impl&, void)
+{
+ // keyword found on index?
+ bool bIndex = xIPage->HasKeyword();
+
+ if( !bIndex)
+ bIndex = xIPage->HasKeywordIgnoreCase();
+ // then set index or search page as current.
+ OString sPageId = bIndex ? "index" : "find";
+ if (sPageId != m_xTabCtrl->get_current_page_ident())
+ m_xTabCtrl->set_current_page(sPageId);
+
+ // at last we open the keyword
+ if ( bIndex )
+ xIPage->OpenKeyword();
+ else if ( !xSPage->OpenKeyword( sKeyword ) )
+ pParentWin->ShowStartPage();
+}
+
+IMPL_LINK_NOARG(SfxHelpIndexWindow_Impl, IndexTabPageDoubleClickHdl, LinkParamNone*, void)
+{
+ aPageDoubleClickLink.Call(nullptr);
+}
+
+void SfxHelpIndexWindow_Impl::SetDoubleClickHdl(const Link<LinkParamNone*, void>& rLink)
+{
+ aPageDoubleClickLink = rLink;
+}
+
+IMPL_LINK_NOARG(SfxHelpIndexWindow_Impl, ContentTabPageDoubleClickHdl, LinkParamNone*, void)
+{
+ aPageDoubleClickLink.Call(nullptr);
+}
+
+IMPL_LINK_NOARG(SfxHelpIndexWindow_Impl, TabPageDoubleClickHdl, LinkParamNone*, void)
+{
+ aPageDoubleClickLink.Call(nullptr);
+}
+
+void SfxHelpIndexWindow_Impl::SetFactory( const OUString& rFactory, bool bActive )
+{
+ if ( !rFactory.isEmpty() )
+ {
+ GetIndexPage()->SetFactory( rFactory );
+ // the index page made a check if rFactory is valid,
+ // so the index page always returns a valid factory
+ GetSearchPage()->SetFactory( GetIndexPage()->GetFactory() );
+ if ( bActive )
+ SetActiveFactory();
+ }
+}
+
+OUString SfxHelpIndexWindow_Impl::GetSelectedEntry() const
+{
+ OUString sRet;
+
+ OString sName(m_xTabCtrl->get_current_page_ident());
+
+ if (sName == "contents")
+ {
+ sRet = xCPage->GetSelectedEntry();
+ }
+ else if (sName == "index")
+ {
+ sRet = xIPage->GetSelectedEntry();
+ }
+ else if (sName == "find")
+ {
+ sRet = xSPage->GetSelectedEntry();
+ }
+ else if (sName == "bookmarks")
+ {
+ sRet = xBPage->GetSelectedEntry();
+ }
+
+ return sRet;
+}
+
+void SfxHelpIndexWindow_Impl::AddBookmarks( const OUString& rTitle, const OUString& rURL )
+{
+ GetBookmarksPage()->AddBookmarks( rTitle, rURL );
+}
+
+bool SfxHelpIndexWindow_Impl::IsValidFactory( std::u16string_view _rFactory )
+{
+ bool bValid = false;
+ for (sal_Int32 i = 0, nEntryCount = m_xActiveLB->get_count(); i < nEntryCount; ++i)
+ {
+ OUString aFactory = m_xActiveLB->get_id(i);
+ if (aFactory == _rFactory)
+ {
+ bValid = true;
+ break;
+ }
+ }
+ return bValid;
+}
+
+void SfxHelpIndexWindow_Impl::ClearSearchPage()
+{
+ if ( xSPage )
+ xSPage->ClearPage();
+}
+
+void SfxHelpIndexWindow_Impl::GrabFocusBack()
+{
+ OString sName(m_xTabCtrl->get_current_page_ident());
+
+ if (sName == "contents" && xCPage)
+ xCPage->SetFocusOnBox();
+ else if (sName == "index" && xIPage)
+ xIPage->SetFocusOnBox();
+ else if (sName == "find" && xSPage)
+ xSPage->SetFocusOnBox();
+ else if (sName == "bookmarks" && xBPage)
+ xBPage->SetFocusOnBox();
+}
+
+bool SfxHelpIndexWindow_Impl::HasFocusOnEdit() const
+{
+ bool bRet = false;
+ OString sName(m_xTabCtrl->get_current_page_ident());
+ if (sName == "index" && xIPage)
+ bRet = xIPage->HasFocusOnEdit();
+ else if (sName == "find" && xSPage)
+ bRet = xSPage->HasFocusOnEdit();
+ return bRet;
+}
+
+OUString SfxHelpIndexWindow_Impl::GetSearchText() const
+{
+ OUString sRet;
+ OString sName(m_xTabCtrl->get_current_page_ident());
+ if (sName == "find" && xSPage)
+ sRet = xSPage->GetSearchText();
+ return sRet;
+}
+
+bool SfxHelpIndexWindow_Impl::IsFullWordSearch() const
+{
+ bool bRet = false;
+ OString sName(m_xTabCtrl->get_current_page_ident());
+ if (sName == "find" && xSPage)
+ bRet = xSPage->IsFullWordSearch();
+ return bRet;
+}
+
+void SfxHelpIndexWindow_Impl::OpenKeyword( const OUString& rKeyword )
+{
+ sKeyword = rKeyword;
+ DBG_ASSERT( xIPage, "invalid index page" );
+ xIPage->SetKeyword( sKeyword );
+}
+
+void SfxHelpIndexWindow_Impl::SelectExecutableEntry()
+{
+ OString sName(m_xTabCtrl->get_current_page_ident());
+ if (sName == "index" && xIPage )
+ xIPage->SelectExecutableEntry();
+}
+
+weld::Window* SfxHelpIndexWindow_Impl::GetFrameWeld() const
+{
+ return pParentWin->GetFrameWeld();
+}
+
+// class TextWin_Impl ----------------------------------------------------
+TextWin_Impl::TextWin_Impl( vcl::Window* p ) : DockingWindow( p, 0 )
+{
+}
+
+bool TextWin_Impl::EventNotify( NotifyEvent& rNEvt )
+{
+ if( ( rNEvt.GetType() == MouseNotifyEvent::KEYINPUT ) && rNEvt.GetKeyEvent()->GetKeyCode().GetCode() == KEY_TAB )
+ return GetParent()->EventNotify( rNEvt );
+ else
+ return DockingWindow::EventNotify( rNEvt );
+}
+
+
+// remove docking area acceptor from layoutmanager, so it will not layout anything further .-)
+static void lcl_disableLayoutOfFrame(const Reference< XFrame2 >& xFrame)
+{
+ xFrame->setLayoutManager( Reference< XLayoutManager >() );
+}
+
+// class SfxHelpTextWindow_Impl ------------------------------------------
+
+SfxHelpTextWindow_Impl::SfxHelpTextWindow_Impl(SfxHelpWindow_Impl* pHelpWin, weld::Builder& rBuilder, vcl::Window* pParent) :
+
+ Window( pParent, WB_CLIPCHILDREN | WB_TABSTOP | WB_DIALOGCONTROL ),
+
+ xToolBox ( rBuilder.weld_toolbar("toolbar") ),
+ xOnStartupCB ( rBuilder.weld_check_button("checkbutton") ),
+ xMenu ( rBuilder.weld_menu("menu") ),
+ aSelectIdle ( "sfx2 appl SfxHelpTextWindow_Impl Select" ),
+ aIndexOnImage ( BMP_HELP_TOOLBOX_INDEX_ON ),
+ aIndexOffImage ( BMP_HELP_TOOLBOX_INDEX_OFF ),
+ aIndexOnText ( SfxResId( STR_HELP_BUTTON_INDEX_ON ) ),
+ aIndexOffText ( SfxResId( STR_HELP_BUTTON_INDEX_OFF ) ),
+ aOnStartupText ( SfxResId( RID_HELP_ONSTARTUP_TEXT ) ),
+ xHelpWin ( pHelpWin ),
+ pTextWin ( VclPtr<TextWin_Impl>::Create( this ) ),
+ bIsDebug ( false ),
+ bIsIndexOn ( false ),
+ bIsInClose ( false ),
+ bIsFullWordSearch ( false )
+{
+ xFrame = Frame::create( ::comphelper::getProcessComponentContext() );
+ xFrame->initialize( VCLUnoHelper::GetInterface ( pTextWin ) );
+ xFrame->setName( "OFFICE_HELP" );
+ lcl_disableLayoutOfFrame(xFrame);
+
+ xToolBox->set_help_id(HID_HELP_TOOLBOX);
+
+ xToolBox->set_item_tooltip_text("index", aIndexOffText );
+ xToolBox->set_item_help_id("index", HID_HELP_TOOLBOXITEM_INDEX);
+ xToolBox->set_item_help_id("backward", HID_HELP_TOOLBOXITEM_BACKWARD);
+ xToolBox->set_item_help_id("forward", HID_HELP_TOOLBOXITEM_FORWARD);
+ xToolBox->set_item_help_id("start", HID_HELP_TOOLBOXITEM_START);
+ xToolBox->set_item_help_id("print", HID_HELP_TOOLBOXITEM_PRINT);
+ xToolBox->set_item_help_id("bookmarks", HID_HELP_TOOLBOXITEM_BOOKMARKS );
+ xToolBox->set_item_help_id("searchdialog", HID_HELP_TOOLBOXITEM_SEARCHDIALOG);
+
+ InitToolBoxImages();
+ InitOnStartupBox();
+ xOnStartupCB->connect_toggled(LINK(this, SfxHelpTextWindow_Impl, CheckHdl));
+
+ aSelectIdle.SetInvokeHandler( LINK( this, SfxHelpTextWindow_Impl, SelectHdl ) );
+ aSelectIdle.SetPriority( TaskPriority::LOWEST );
+
+ char* pEnv = getenv( "help_debug" );
+ if ( pEnv )
+ bIsDebug = true;
+
+ SvtMiscOptions().AddListenerLink( LINK( this, SfxHelpTextWindow_Impl, NotifyHdl ) );
+}
+
+SfxHelpTextWindow_Impl::~SfxHelpTextWindow_Impl()
+{
+ disposeOnce();
+}
+
+void SfxHelpTextWindow_Impl::dispose()
+{
+ bIsInClose = true;
+ SvtMiscOptions().RemoveListenerLink( LINK( this, SfxHelpTextWindow_Impl, NotifyHdl ) );
+ m_xSrchDlg.reset();
+ xToolBox.reset();
+ xOnStartupCB.reset();
+ xHelpWin.clear();
+ pTextWin.disposeAndClear();
+ vcl::Window::dispose();
+}
+
+bool SfxHelpTextWindow_Impl::HasSelection() const
+{
+ // is there any selection in the text and not only a cursor?
+ bool bRet = false;
+ Reference < XTextRange > xRange = getCursor();
+ if ( xRange.is() )
+ {
+ Reference < XText > xText = xRange->getText();
+ Reference < XTextCursor > xCursor = xText->createTextCursorByRange( xRange );
+ bRet = !xCursor->isCollapsed();
+ }
+
+ return bRet;
+}
+
+void SfxHelpTextWindow_Impl::InitToolBoxImages()
+{
+ xToolBox->set_item_icon_name("index", bIsIndexOn ? aIndexOffImage : aIndexOnImage);
+}
+
+void SfxHelpTextWindow_Impl::InitOnStartupBox()
+{
+ sCurrentFactory = SfxHelp::GetCurrentModuleIdentifier();
+
+ Reference< XComponentContext > xContext = ::comphelper::getProcessComponentContext();
+ const OUString sPath { PATH_OFFICE_FACTORIES + sCurrentFactory };
+
+ // Attention: This check boy knows two states:
+ // 1) Reading of the config key fails with an exception or by getting an empty Any (!) => check box must be hidden
+ // 2) We read sal_True/sal_False => check box must be shown and enabled/disabled
+
+ bool bHideBox = true;
+ bool bHelpAtStartup = false;
+ try
+ {
+ xConfiguration = ConfigurationHelper::openConfig(
+ xContext, PACKAGE_SETUP, EConfigurationModes::Standard );
+ if ( xConfiguration.is() )
+ {
+ Any aAny = ConfigurationHelper::readRelativeKey( xConfiguration, sPath, KEY_HELP_ON_OPEN );
+ if (aAny >>= bHelpAtStartup)
+ bHideBox = false;
+ }
+ }
+ catch( Exception& )
+ {
+ bHideBox = true;
+ }
+
+ if ( bHideBox )
+ xOnStartupCB->hide();
+ else
+ {
+ // detect module name
+ OUString sModuleName;
+
+ if ( xConfiguration.is() )
+ {
+ OUString sTemp;
+ try
+ {
+ Any aAny = ConfigurationHelper::readRelativeKey( xConfiguration, sPath, KEY_UI_NAME );
+ aAny >>= sTemp;
+ }
+ catch( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "sfx.appl", "SfxHelpTextWindow_Impl::InitOnStartupBox()" );
+ }
+ sModuleName = sTemp;
+ }
+
+ if ( !sModuleName.isEmpty() )
+ {
+ // set module name in checkbox text
+ xOnStartupCB->set_label(aOnStartupText.replaceFirst("%MODULENAME", sModuleName));
+ // and show it
+ xOnStartupCB->show();
+ // set check state
+ xOnStartupCB->set_active(bHelpAtStartup);
+ xOnStartupCB->save_state();
+ }
+ }
+}
+
+Reference< XBreakIterator > const & SfxHelpTextWindow_Impl::GetBreakIterator()
+{
+ if ( !xBreakIterator.is() )
+ xBreakIterator = vcl::unohelper::CreateBreakIterator();
+ DBG_ASSERT( xBreakIterator.is(), "Could not create BreakIterator" );
+ return xBreakIterator;
+}
+
+Reference< XTextRange > SfxHelpTextWindow_Impl::getCursor() const
+{
+ // return the current cursor
+ Reference< XTextRange > xCursor;
+
+ try
+ {
+ Reference < XSelectionSupplier > xSelSup( xFrame->getController(), UNO_QUERY );
+ if ( xSelSup.is() )
+ {
+ Any aAny = xSelSup->getSelection();
+ Reference < XIndexAccess > xSelection;
+ if ( aAny >>= xSelection )
+ {
+ if ( xSelection->getCount() == 1 )
+ {
+ aAny = xSelection->getByIndex(0);
+ aAny >>= xCursor;
+ }
+ }
+ }
+ }
+ catch( Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "sfx.appl", "SfxHelpTextWindow_Impl::getCursor(): unexpected exception" );
+ }
+
+ return xCursor;
+}
+
+
+bool SfxHelpTextWindow_Impl::isHandledKey( const vcl::KeyCode& _rKeyCode )
+{
+ bool bRet = false;
+ sal_uInt16 nCode = _rKeyCode.GetCode();
+
+ // the keys <CTRL><A> (select all), <CTRL><C> (copy),
+ // <CTRL><F> (find), <CTRL><P> (print) and <CTRL><W> (close window)
+ // were handled in help
+ if ( _rKeyCode.IsMod1() &&
+ ( KEY_A == nCode || KEY_C == nCode || KEY_F == nCode || KEY_P == nCode || KEY_W == nCode ) )
+ {
+ if ( KEY_F == nCode )
+ DoSearch();
+ else
+ bRet = true;
+ }
+
+ return bRet;
+}
+
+
+IMPL_LINK_NOARG(SfxHelpTextWindow_Impl, SelectHdl, Timer *, void)
+{
+ try
+ {
+ // select the words, which are equal to the search text of the search page
+ Reference < XController > xController = xFrame->getController();
+ if ( xController.is() )
+ {
+ // get document
+ Reference < XSearchable > xSearchable( xController->getModel(), UNO_QUERY );
+ if ( xSearchable.is() )
+ {
+ // create descriptor, set string and find all words
+ Reference < XSearchDescriptor > xSrchDesc = xSearchable->createSearchDescriptor();
+ xSrchDesc->setPropertyValue( "SearchRegularExpression", Any( true ) );
+ if ( bIsFullWordSearch )
+ xSrchDesc->setPropertyValue( "SearchWords", Any( true ) );
+
+ xSrchDesc->setSearchString( sfx2::PrepareSearchString( aSearchText, GetBreakIterator(), false ) );
+ Reference< XIndexAccess > xSelection = xSearchable->findAll( xSrchDesc );
+
+ // then select all found words
+ Reference < XSelectionSupplier > xSelectionSup( xController, UNO_QUERY );
+ if ( xSelectionSup.is() )
+ {
+ xSelectionSup->select( Any(xSelection) );
+ }
+ }
+ }
+ }
+ catch( Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "sfx.appl", "SfxHelpTextWindow_Impl::SelectHdl(): unexpected exception" );
+ }
+}
+
+
+IMPL_LINK_NOARG( SfxHelpTextWindow_Impl, NotifyHdl, LinkParamNone*, void )
+{
+ InitToolBoxImages();
+ Resize();
+}
+
+IMPL_LINK( SfxHelpTextWindow_Impl, FindHdl, sfx2::SearchDialog&, rDlg, void )
+{
+ FindHdl(&rDlg);
+}
+void SfxHelpTextWindow_Impl::FindHdl(sfx2::SearchDialog* pDlg)
+{
+ bool bWrapAround = ( nullptr == pDlg );
+ if ( bWrapAround )
+ pDlg = m_xSrchDlg.get();
+ DBG_ASSERT( pDlg, "invalid search dialog" );
+ try
+ {
+ // select the words, which are equal to the search text of the search page
+ Reference < XController > xController = xFrame->getController();
+ if ( xController.is() )
+ {
+ // get document
+ Reference < XSearchable > xSearchable( xController->getModel(), UNO_QUERY );
+ if ( xSearchable.is() )
+ {
+ // create descriptor, set string and find all words
+ Reference < XSearchDescriptor > xSrchDesc = xSearchable->createSearchDescriptor();
+ xSrchDesc->setPropertyValue( "SearchWords", Any(pDlg->IsOnlyWholeWords()) );
+ xSrchDesc->setPropertyValue( "SearchCaseSensitive", Any(pDlg->IsMarchCase()) );
+ xSrchDesc->setPropertyValue( "SearchBackwards", Any(pDlg->IsSearchBackwards()) );
+ xSrchDesc->setSearchString( pDlg->GetSearchText() );
+ Reference< XInterface > xSelection;
+ Reference< XTextRange > xCursor = getCursor();
+
+ if ( xCursor.is() )
+ {
+ if ( pDlg->IsSearchBackwards() )
+ xCursor = xCursor->getStart();
+ xSelection = xSearchable->findNext( xCursor, xSrchDesc );
+ }
+ else
+ xSelection = xSearchable->findFirst( xSrchDesc );
+
+ // then select the found word
+ if ( xSelection.is() )
+ {
+ Reference < XSelectionSupplier > xSelectionSup( xController, UNO_QUERY );
+ if ( xSelectionSup.is() )
+ {
+ xSelectionSup->select( Any(xSelection) );
+ }
+ }
+ else if ( pDlg->IsWrapAround() && !bWrapAround )
+ {
+ Reference < text::XTextViewCursorSupplier > xCrsrSupp( xController, uno::UNO_QUERY );
+ Reference < text::XTextViewCursor > xTVCrsr = xCrsrSupp->getViewCursor();
+ if ( xTVCrsr.is() )
+ {
+ Reference < text::XTextDocument > xDoc( xController->getModel(), uno::UNO_QUERY );
+ Reference < text::XText > xText = xDoc->getText();
+ if ( xText.is() )
+ {
+ if ( pDlg->IsSearchBackwards() )
+ xTVCrsr->gotoRange( xText->getEnd(), false );
+ else
+ xTVCrsr->gotoRange( xText->getStart(), false );
+ FindHdl( nullptr );
+ }
+ }
+ }
+ else
+ {
+ assert(m_xSrchDlg && "no search dialog");
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(m_xSrchDlg->getDialog(),
+ VclMessageType::Info, VclButtonsType::Ok, SfxResId(STR_INFO_NOSEARCHTEXTFOUND)));
+ xBox->run();
+ m_xSrchDlg->SetFocusOnEdit();
+ }
+ }
+ }
+ }
+ catch( Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "sfx.appl", "SfxHelpTextWindow_Impl::SelectHdl(): unexpected exception" );
+ }
+}
+
+IMPL_LINK_NOARG(SfxHelpTextWindow_Impl, CloseHdl, LinkParamNone*, void)
+{
+ m_xSrchDlg.reset();
+}
+
+IMPL_LINK_NOARG(SfxHelpTextWindow_Impl, CheckHdl, weld::Toggleable&, void)
+{
+ if ( !xConfiguration.is() )
+ return;
+
+ bool bChecked = xOnStartupCB->get_active();
+ try
+ {
+ ConfigurationHelper::writeRelativeKey(
+ xConfiguration, PATH_OFFICE_FACTORIES + sCurrentFactory, KEY_HELP_ON_OPEN, Any( bChecked ) );
+ ConfigurationHelper::flush( xConfiguration );
+ }
+ catch( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "sfx.appl", "SfxHelpTextWindow_Impl::CheckHdl()" );
+ }
+}
+
+void SfxHelpTextWindow_Impl::Resize()
+{
+ Size aSize = GetOutputSizePixel();
+ pTextWin->SetPosSizePixel( Point(0, 0), aSize );
+}
+
+bool SfxHelpTextWindow_Impl::PreNotify( NotifyEvent& rNEvt )
+{
+ bool bDone = false;
+ MouseNotifyEvent nType = rNEvt.GetType();
+ if ( MouseNotifyEvent::COMMAND == nType && rNEvt.GetCommandEvent() )
+ {
+ const CommandEvent* pCmdEvt = rNEvt.GetCommandEvent();
+ vcl::Window* pCmdWin = rNEvt.GetWindow();
+
+ if ( pCmdEvt->GetCommand() == CommandEventId::ContextMenu && pCmdWin != this )
+ {
+ Point aPos;
+ if ( pCmdEvt->IsMouseEvent() )
+ aPos = pCmdEvt->GetMousePosPixel();
+ else
+ aPos = Point( pTextWin->GetPosPixel().X() + 20, 20 );
+
+ xMenu->clear();
+
+ if (bIsIndexOn)
+ xMenu->append("index", aIndexOffText, BMP_HELP_TOOLBOX_INDEX_OFF);
+ else
+ xMenu->append("index", aIndexOnText, BMP_HELP_TOOLBOX_INDEX_ON);
+
+ xMenu->append_separator("separator1");
+ xMenu->append("backward", SfxResId(STR_HELP_BUTTON_PREV), BMP_HELP_TOOLBOX_PREV);
+ xMenu->set_sensitive("backward", xHelpWin->HasHistoryPredecessor());
+ xMenu->append("forward", SfxResId(STR_HELP_BUTTON_NEXT), BMP_HELP_TOOLBOX_NEXT);
+ xMenu->set_sensitive("forward", xHelpWin->HasHistorySuccessor());
+ xMenu->append("start", SfxResId(STR_HELP_BUTTON_START), BMP_HELP_TOOLBOX_START);
+ xMenu->append_separator("separator2");
+ xMenu->append("print", SfxResId(STR_HELP_BUTTON_PRINT), BMP_HELP_TOOLBOX_PRINT);
+ xMenu->append("bookmarks", SfxResId(STR_HELP_BUTTON_ADDBOOKMARK), BMP_HELP_TOOLBOX_BOOKMARKS);
+ xMenu->append("searchdialog", SfxResId(STR_HELP_BUTTON_SEARCHDIALOG), BMP_HELP_TOOLBOX_SEARCHDIALOG);
+ xMenu->append_separator("separator3");
+ xMenu->append_check("selectionmode", SfxResId(STR_HELP_MENU_TEXT_SELECTION_MODE));
+ URL aURL;
+ aURL.Complete = ".uno:SelectTextMode";
+ Reference< util::XURLTransformer > xTrans( util::URLTransformer::create( ::comphelper::getProcessComponentContext() ) );
+ xTrans->parseStrict(aURL);
+ Reference < XDispatch > xDisp = xFrame->queryDispatch( aURL, OUString(), 0 );
+ if(xDisp.is())
+ {
+ rtl::Reference<HelpStatusListener_Impl> pStateListener =
+ new HelpStatusListener_Impl(xDisp, aURL );
+ FeatureStateEvent rEvent = pStateListener->GetStateEvent();
+ bool bCheck = false;
+ rEvent.State >>= bCheck;
+ xMenu->set_active("selectionmode", bCheck);
+ }
+ xMenu->append_separator("separator4");
+ xMenu->append("copy", SfxResId(STR_HELP_MENU_TEXT_COPY), BMP_HELP_TOOLBOX_COPY);
+ xMenu->set_sensitive("copy", HasSelection());
+
+ if ( bIsDebug )
+ {
+ xMenu->append_separator("separator5");
+ xMenu->append("sourceview", SfxResId(STR_HELP_BUTTON_SOURCEVIEW));
+ }
+
+ int x, y, width, height;
+ weld::Window* pTopLevel = GetFrameWeld();
+ xHelpWin->GetContainer()->get_extents_relative_to(*pTopLevel, x, y, width, height);
+ aPos.AdjustX(x);
+ aPos.AdjustY(y);
+
+ xHelpWin->DoAction(xMenu->popup_at_rect(pTopLevel, tools::Rectangle(aPos, Size(1,1))));
+ bDone = true;
+ }
+ }
+ else if ( MouseNotifyEvent::KEYINPUT == nType && rNEvt.GetKeyEvent() )
+ {
+ const KeyEvent* pKEvt = rNEvt.GetKeyEvent();
+ const vcl::KeyCode& rKeyCode = pKEvt->GetKeyCode();
+ sal_uInt16 nKeyGroup = rKeyCode.GetGroup();
+ sal_uInt16 nKey = rKeyCode.GetCode();
+ if ( KEYGROUP_ALPHA == nKeyGroup && !isHandledKey( rKeyCode ) )
+ {
+ // do nothing disables the writer accelerators
+ bDone = true;
+ }
+ else if ( rKeyCode.IsMod1() && ( KEY_F4 == nKey || KEY_W == nKey ) )
+ {
+ // <CTRL><F4> or <CTRL><W> -> close top frame
+ xHelpWin->CloseWindow();
+ bDone = true;
+ }
+ else if ( KEY_TAB == nKey && xOnStartupCB->has_focus() )
+ {
+ xToolBox->grab_focus();
+ bDone = true;
+ }
+ }
+
+ return bDone || Window::PreNotify( rNEvt );
+}
+
+
+void SfxHelpTextWindow_Impl::GetFocus()
+{
+ if ( bIsInClose )
+ return;
+
+ try
+ {
+ if( xFrame.is() )
+ {
+ Reference< css::awt::XWindow > xWindow = xFrame->getComponentWindow();
+ if( xWindow.is() )
+ xWindow->setFocus();
+ }
+ }
+ catch( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "sfx.appl", "SfxHelpTextWindow_Impl::GetFocus()" );
+ }
+}
+
+
+void SfxHelpTextWindow_Impl::DataChanged( const DataChangedEvent& rDCEvt )
+{
+ Window::DataChanged( rDCEvt );
+
+ if ( ( ( rDCEvt.GetType() == DataChangedEventType::SETTINGS ) ||
+ ( rDCEvt.GetType() == DataChangedEventType::DISPLAY ) ) &&
+ ( rDCEvt.GetFlags() & AllSettingsFlags::STYLE ) )
+ {
+ SetBackground( Wallpaper( GetSettings().GetStyleSettings().GetFaceColor() ) );
+ InitToolBoxImages();
+ }
+}
+
+void SfxHelpTextWindow_Impl::ToggleIndex( bool bOn )
+{
+ bIsIndexOn = bOn;
+ if ( bIsIndexOn )
+ {
+ xToolBox->set_item_icon_name("index", aIndexOffImage);
+ xToolBox->set_item_tooltip_text("index", aIndexOffText);
+ }
+ else
+ {
+ xToolBox->set_item_icon_name("index", aIndexOnImage);
+ xToolBox->set_item_tooltip_text("index", aIndexOnText);
+ }
+}
+
+void SfxHelpTextWindow_Impl::SelectSearchText( const OUString& rSearchText, bool _bIsFullWordSearch )
+{
+ aSearchText = rSearchText;
+ bIsFullWordSearch = _bIsFullWordSearch;
+ aSelectIdle.Start();
+}
+
+
+void SfxHelpTextWindow_Impl::SetPageStyleHeaderOff() const
+{
+ bool bSetOff = false;
+ // set off the pagestyle header to prevent print output of the help URL
+ try
+ {
+ Reference < XController > xController = xFrame->getController();
+ Reference < XSelectionSupplier > xSelSup( xController, UNO_QUERY );
+ if ( xSelSup.is() )
+ {
+ Reference < XIndexAccess > xSelection;
+ if ( xSelSup->getSelection() >>= xSelection )
+ {
+ Reference < XTextRange > xRange;
+ if ( xSelection->getByIndex(0) >>= xRange )
+ {
+ Reference < XText > xText = xRange->getText();
+ Reference < XPropertySet > xProps( xText->createTextCursorByRange( xRange ), UNO_QUERY );
+ OUString sStyleName;
+ if ( xProps->getPropertyValue( "PageStyleName" ) >>= sStyleName )
+ {
+ Reference < XStyleFamiliesSupplier > xStyles( xController->getModel(), UNO_QUERY );
+ Reference < XNameContainer > xContainer;
+ if ( xStyles->getStyleFamilies()->getByName( "PageStyles" )
+ >>= xContainer )
+ {
+ Reference < XStyle > xStyle;
+ if ( xContainer->getByName( sStyleName ) >>= xStyle )
+ {
+ Reference < XPropertySet > xPropSet( xStyle, UNO_QUERY );
+ xPropSet->setPropertyValue( "HeaderIsOn", Any( false ) );
+
+ Reference< XModifiable > xReset(xStyles, UNO_QUERY);
+ xReset->setModified(false);
+ bSetOff = true;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ catch( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "sfx.appl", "SfxHelpTextWindow_Impl::SetPageStyleHeaderOff()" );
+ }
+
+ SAL_WARN_IF( !bSetOff, "sfx.appl", "SfxHelpTextWindow_Impl::SetPageStyleHeaderOff(): set off failed" );
+}
+
+
+void SfxHelpTextWindow_Impl::CloseFrame()
+{
+ bIsInClose = true;
+ try
+ {
+ css::uno::Reference< css::util::XCloseable > xCloseable ( xFrame, css::uno::UNO_QUERY );
+ if (xCloseable.is())
+ xCloseable->close(true);
+ }
+ catch( css::util::CloseVetoException& )
+ {
+ }
+}
+
+
+void SfxHelpTextWindow_Impl::DoSearch()
+{
+ if (m_xSrchDlg)
+ return;
+
+ // create the search dialog
+ m_xSrchDlg = std::make_shared<sfx2::SearchDialog>(pTextWin->GetFrameWeld(), "HelpSearchDialog");
+ // set handler
+ m_xSrchDlg->SetFindHdl( LINK( this, SfxHelpTextWindow_Impl, FindHdl ) );
+ m_xSrchDlg->SetCloseHdl( LINK( this, SfxHelpTextWindow_Impl, CloseHdl ) );
+ // get selected text of the help page to set it as the search text
+ Reference< XTextRange > xCursor = getCursor();
+ if ( xCursor.is() )
+ {
+ OUString sText = xCursor->getString();
+ if ( !sText.isEmpty() )
+ m_xSrchDlg->SetSearchText( sText );
+ }
+ sfx2::SearchDialog::runAsync(m_xSrchDlg);
+}
+
+void SfxHelpWindow_Impl::GetFocus()
+{
+ if (pTextWin)
+ pTextWin->GrabFocus();
+ else
+ ResizableDockingWindow::GetFocus();
+}
+
+void SfxHelpWindow_Impl::MakeLayout()
+{
+ Split();
+
+ m_xHelpPaneWindow->set_visible(bIndex);
+}
+
+IMPL_LINK(SfxHelpWindow_Impl, ResizeHdl, const Size&, rSize, void)
+{
+ int nNewWidth = rSize.Width();
+ if (!nNewWidth)
+ return;
+ if (bSplit)
+ nIndexSize = round(m_xContainer->get_position() * 100.0 / nNewWidth);
+ nWidth = nNewWidth;
+ Split();
+ nIndexSize = round(m_xContainer->get_position() * 100.0 / nWidth);
+}
+
+void SfxHelpWindow_Impl::Split()
+{
+ if (!nWidth)
+ return;
+ m_xContainer->set_position(nWidth * nIndexSize / 100);
+ bSplit = true;
+}
+
+void SfxHelpWindow_Impl::LoadConfig()
+{
+ SvtViewOptions aViewOpt( EViewType::Window, CONFIGNAME_HELPWIN );
+ if ( !aViewOpt.Exists() )
+ return;
+ bIndex = aViewOpt.IsVisible();
+
+ Any aUserItem = aViewOpt.GetUserItem( USERITEM_NAME );
+ OUString aUserData;
+ if ( aUserItem >>= aUserData )
+ {
+ DBG_ASSERT( comphelper::string::getTokenCount(aUserData, ';') == 6, "invalid user data" );
+ sal_Int32 nIdx = 0;
+ nIndexSize = o3tl::toInt32(o3tl::getToken(aUserData, 0, ';', nIdx ));
+ o3tl::getToken(aUserData, 0, ';', nIdx); // ignore nTextSize
+ sal_Int32 nOldWidth = o3tl::toInt32(o3tl::getToken(aUserData, 0, ';', nIdx ));
+ sal_Int32 nOldHeight = o3tl::toInt32(o3tl::getToken(aUserData, 0, ';', nIdx ));
+ aWinSize = Size(nOldWidth, nOldHeight);
+ aWinPos.setX( o3tl::toInt32(o3tl::getToken(aUserData, 0, ';', nIdx )) );
+ aWinPos.setY( o3tl::toInt32(o3tl::getToken(aUserData, 0, ';', nIdx )) );
+ }
+
+ pTextWin->ToggleIndex( bIndex );
+}
+
+void SfxHelpWindow_Impl::SaveConfig()
+{
+ SvtViewOptions aViewOpt( EViewType::Window, CONFIGNAME_HELPWIN );
+ sal_Int32 nW = 0, nH = 0;
+
+ if ( xWindow.is() )
+ {
+ css::awt::Rectangle aRect = xWindow->getPosSize();
+ nW = aRect.Width;
+ nH = aRect.Height;
+ }
+
+ aViewOpt.SetVisible( bIndex );
+ VclPtr<vcl::Window> pScreenWin = VCLUnoHelper::GetWindow( xWindow );
+ aWinPos = pScreenWin->GetWindowExtentsRelative( nullptr ).TopLeft();
+ if (bSplit)
+ nIndexSize = round(m_xContainer->get_position() * 100.0 / nWidth);
+ const OUString aUserData = OUString::number( nIndexSize )
+ + ";" + OUString::number( 100 - nIndexSize )
+ + ";" + OUString::number( nW )
+ + ";" + OUString::number( nH )
+ + ";" + OUString::number( aWinPos.X() )
+ + ";" + OUString::number( aWinPos.Y() );
+
+ aViewOpt.SetUserItem( USERITEM_NAME, Any( aUserData ) );
+}
+
+void SfxHelpWindow_Impl::ShowStartPage()
+{
+ loadHelpContent(SfxHelpWindow_Impl::buildHelpURL(xIndexWin->GetFactory(), u"/start", u""));
+}
+
+IMPL_LINK(SfxHelpWindow_Impl, SelectHdl, const OString&, rCurItem, void)
+{
+ bGrabFocusToToolBox = pTextWin->GetToolBox().has_focus();
+ DoAction(rCurItem);
+}
+
+IMPL_LINK_NOARG(SfxHelpWindow_Impl, OpenHdl, LinkParamNone*, void)
+{
+ xIndexWin->SelectExecutableEntry();
+ OUString aEntry = xIndexWin->GetSelectedEntry();
+
+ if ( aEntry.isEmpty() )
+ return;
+
+ OUString sHelpURL;
+
+ bool bComplete = aEntry.toAsciiLowerCase().match("vnd.sun.star.help");
+
+ if (bComplete)
+ sHelpURL = aEntry;
+ else
+ {
+ std::u16string_view aId;
+ OUString aAnchor('#');
+ if ( comphelper::string::getTokenCount(aEntry, '#') == 2 )
+ {
+ sal_Int32 nIdx{ 0 };
+ aId = o3tl::getToken(aEntry, 0, '#', nIdx );
+ aAnchor += o3tl::getToken(aEntry, 0, '#', nIdx );
+ }
+ else
+ aId = aEntry;
+
+ sHelpURL = SfxHelpWindow_Impl::buildHelpURL(xIndexWin->GetFactory(), OUStringConcatenation(OUString::Concat("/") + aId), aAnchor);
+ }
+
+ loadHelpContent(sHelpURL);
+}
+
+IMPL_LINK( SfxHelpWindow_Impl, SelectFactoryHdl, SfxHelpIndexWindow_Impl* , pWin, void )
+{
+ if ( sTitle.isEmpty() )
+ sTitle = GetParent()->GetText();
+
+ Reference< XTitle > xTitle(xFrame, UNO_QUERY);
+ if (xTitle.is ())
+ xTitle->setTitle(sTitle + " - " + xIndexWin->GetActiveFactoryTitle());
+
+ if ( pWin )
+ ShowStartPage();
+ xIndexWin->ClearSearchPage();
+}
+
+
+IMPL_LINK( SfxHelpWindow_Impl, ChangeHdl, HelpListener_Impl&, rListener, void )
+{
+ SetFactory( rListener.GetFactory() );
+}
+
+
+void SfxHelpWindow_Impl::openDone(std::u16string_view sURL ,
+ bool bSuccess)
+{
+ INetURLObject aObj( sURL );
+ if ( aObj.GetProtocol() == INetProtocol::VndSunStarHelp )
+ SetFactory( aObj.GetHost() );
+ if ( IsWait() )
+ LeaveWait();
+ if ( bGrabFocusToToolBox )
+ {
+ pTextWin->GetToolBox().grab_focus();
+ bGrabFocusToToolBox = false;
+ }
+ else
+ xIndexWin->GrabFocusBack();
+ if ( !bSuccess )
+ return;
+
+ // set some view settings: "prevent help tips" and "helpid == 68245"
+ try
+ {
+ Reference < XController > xController = pTextWin->getFrame()->getController();
+ if ( xController.is() )
+ {
+ Reference < XViewSettingsSupplier > xSettings( xController, UNO_QUERY );
+ Reference < XPropertySet > xViewProps = xSettings->getViewSettings();
+ Reference< XPropertySetInfo > xInfo = xViewProps->getPropertySetInfo();
+ xViewProps->setPropertyValue( "ShowContentTips", Any( false ) );
+ xViewProps->setPropertyValue( "ShowGraphics", Any( true ) );
+ xViewProps->setPropertyValue( "ShowTables", Any( true ) );
+ xViewProps->setPropertyValue( "HelpURL", Any( OUString("HID:SFX2_HID_HELP_ONHELP") ) );
+ OUString sProperty( "IsExecuteHyperlinks" );
+ if ( xInfo->hasPropertyByName( sProperty ) )
+ xViewProps->setPropertyValue( sProperty, Any( true ) );
+ xController->restoreViewData(Any());
+ }
+ }
+ catch( Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "sfx.appl", "SfxHelpWindow_Impl::OpenDoneHdl(): unexpected exception" );
+ }
+
+ // When the SearchPage opens the help doc, then select all words, which are equal to its text
+ OUString sSearchText = comphelper::string::strip(xIndexWin->GetSearchText(), ' ');
+ if ( !sSearchText.isEmpty() )
+ pTextWin->SelectSearchText( sSearchText, xIndexWin->IsFullWordSearch() );
+
+ // no page style header -> this prevents a print output of the URL
+ pTextWin->SetPageStyleHeaderOff();
+}
+
+
+SfxHelpWindow_Impl::SfxHelpWindow_Impl(
+ const css::uno::Reference < css::frame::XFrame2 >& rFrame,
+ vcl::Window* pParent ) :
+
+ ResizableDockingWindow(pParent),
+
+ xFrame ( rFrame ),
+ pTextWin ( nullptr ),
+ pHelpInterceptor ( new HelpInterceptor_Impl() ),
+ pHelpListener ( new HelpListener_Impl( pHelpInterceptor ) ),
+ bIndex ( true ),
+ bGrabFocusToToolBox ( false ),
+ bSplit ( false ),
+ nWidth ( 0 ),
+ nIndexSize ( 40 ), // % of width
+ aWinPos ( 0, 0 ),
+ aWinSize ( 0, 0 ),
+ sTitle ( pParent->GetText() )
+{
+ SetStyle(GetStyle() & ~WB_DOCKABLE);
+
+ SetHelpId( HID_HELP_WINDOW );
+
+ m_xBuilder = Application::CreateInterimBuilder(m_xBox.get(), "sfx/ui/helpwindow.ui", false);
+ m_xContainer = m_xBuilder->weld_paned("HelpWindow");
+ m_xContainer->connect_size_allocate(LINK(this, SfxHelpWindow_Impl, ResizeHdl));
+ m_xHelpPaneWindow = m_xBuilder->weld_container("helppanewindow");
+ m_xHelpTextWindow = m_xBuilder->weld_container("helptextwindow");
+ m_xHelpTextXWindow = m_xHelpTextWindow->CreateChildFrame();
+
+ pHelpInterceptor->InitWaiter( this );
+ xIndexWin.reset(new SfxHelpIndexWindow_Impl(this, m_xHelpPaneWindow.get()));
+ xIndexWin->SetDoubleClickHdl( LINK( this, SfxHelpWindow_Impl, OpenHdl ) );
+ xIndexWin->SetSelectFactoryHdl( LINK( this, SfxHelpWindow_Impl, SelectFactoryHdl ) );
+
+ pTextWin = VclPtr<SfxHelpTextWindow_Impl>::Create(this, *m_xBuilder, VCLUnoHelper::GetWindow(m_xHelpTextXWindow));
+ Reference < XFrames > xFrames = rFrame->getFrames();
+ xFrames->append( Reference<XFrame>(pTextWin->getFrame(), UNO_QUERY_THROW) );
+ pTextWin->SetSelectHdl( LINK( this, SfxHelpWindow_Impl, SelectHdl ) );
+ pTextWin->Show();
+ pHelpInterceptor->setInterception( Reference<XFrame>(pTextWin->getFrame(), UNO_QUERY_THROW) );
+ pHelpListener->SetChangeHdl( LINK( this, SfxHelpWindow_Impl, ChangeHdl ) );
+ LoadConfig();
+}
+
+SfxHelpWindow_Impl::~SfxHelpWindow_Impl()
+{
+ disposeOnce();
+}
+
+void SfxHelpWindow_Impl::dispose()
+{
+ SaveConfig();
+ xIndexWin.reset();
+ pTextWin->CloseFrame();
+ pTextWin.disposeAndClear();
+
+ m_xHelpTextXWindow->dispose();
+ m_xHelpTextXWindow.clear();
+ m_xHelpTextWindow.reset();
+ m_xHelpPaneWindow.reset();
+ m_xContainer.reset();
+ m_xBuilder.reset();
+
+ ResizableDockingWindow::dispose();
+}
+
+bool SfxHelpWindow_Impl::PreNotify( NotifyEvent& rNEvt )
+{
+ bool bHandled = false;
+ if ( rNEvt.GetType() == MouseNotifyEvent::KEYINPUT )
+ {
+ // Backward == <ALT><LEFT> or <BACKSPACE> Forward == <ALT><RIGHT>
+ const vcl::KeyCode& rKeyCode = rNEvt.GetKeyEvent()->GetKeyCode();
+ sal_uInt16 nKey = rKeyCode.GetCode();
+ if ( ( rKeyCode.IsMod2() && ( KEY_LEFT == nKey || KEY_RIGHT == nKey ) ) ||
+ ( !rKeyCode.GetModifier() && KEY_BACKSPACE == nKey && !xIndexWin->HasFocusOnEdit() ) )
+ {
+ DoAction( rKeyCode.GetCode() == KEY_RIGHT ? "forward" : "backward" );
+ bHandled = true;
+ }
+ else if ( rKeyCode.IsMod1() && ( KEY_F4 == nKey || KEY_W == nKey ) )
+ {
+ // <CTRL><F4> or <CTRL><W> -> close top frame
+ CloseWindow();
+ bHandled = true;
+ }
+ }
+ return bHandled || Window::PreNotify( rNEvt );
+}
+
+void SfxHelpWindow_Impl::setContainerWindow( const Reference < css::awt::XWindow >& xWin )
+{
+ xWindow = xWin;
+ MakeLayout();
+ if (xWindow.is())
+ {
+ VclPtr<vcl::Window> pScreenWin = VCLUnoHelper::GetWindow(xWindow);
+ if (aWinSize.Width() && aWinSize.Height())
+ pScreenWin->SetPosSizePixel(aWinPos, aWinSize);
+ else
+ pScreenWin->SetPosPixel(aWinPos);
+ }
+}
+
+void SfxHelpWindow_Impl::SetFactory( const OUString& rFactory )
+{
+ xIndexWin->SetFactory( rFactory, true );
+}
+
+void SfxHelpWindow_Impl::SetHelpURL( std::u16string_view rURL )
+{
+ INetURLObject aObj( rURL );
+ if ( aObj.GetProtocol() == INetProtocol::VndSunStarHelp )
+ SetFactory( aObj.GetHost() );
+}
+
+void SfxHelpWindow_Impl::DoAction(std::string_view rActionId)
+{
+ if (rActionId == "index")
+ {
+ bIndex = !bIndex;
+ MakeLayout();
+ pTextWin->ToggleIndex( bIndex );
+ }
+ else if (rActionId == "start")
+ {
+ ShowStartPage();
+ }
+ else if (rActionId == "backward" || rActionId == "forward")
+ {
+ URL aURL;
+ aURL.Complete = ".uno:Backward";
+ if (rActionId == "forward")
+ aURL.Complete = ".uno:Forward";
+ Reference< util::XURLTransformer > xTrans( util::URLTransformer::create( ::comphelper::getProcessComponentContext() ) );
+ xTrans->parseStrict(aURL);
+ pHelpInterceptor->dispatch( aURL, Sequence < PropertyValue >() );
+ }
+ else if (rActionId == "searchdialog")
+ {
+ pTextWin->DoSearch();
+ }
+ else if (rActionId == "print" || rActionId == "sourceview" || rActionId == "copy" || rActionId == "selectionmode")
+ {
+ Reference < XDispatchProvider > xProv = pTextWin->getFrame();
+ if ( xProv.is() )
+ {
+ URL aURL;
+ if (rActionId == "print")
+ aURL.Complete = ".uno:Print";
+ else if (rActionId == "sourceview")
+ aURL.Complete = ".uno:SourceView";
+ else if (rActionId == "copy")
+ aURL.Complete = ".uno:Copy";
+ else // rActionId == "selectionmode"
+ aURL.Complete = ".uno:SelectTextMode";
+ Reference< util::XURLTransformer > xTrans( util::URLTransformer::create( ::comphelper::getProcessComponentContext() ) );
+ xTrans->parseStrict(aURL);
+ Reference < XDispatch > xDisp = xProv->queryDispatch( aURL, OUString(), 0 );
+ if ( xDisp.is() )
+ xDisp->dispatch( aURL, Sequence < PropertyValue >() );
+ }
+ }
+ else if (rActionId == "bookmarks")
+ {
+ OUString aURL = pHelpInterceptor->GetCurrentURL();
+ if ( !aURL.isEmpty() )
+ {
+ try
+ {
+ Content aCnt( aURL, Reference< css::ucb::XCommandEnvironment >(), comphelper::getProcessComponentContext() );
+ css::uno::Reference< css::beans::XPropertySetInfo > xInfo = aCnt.getProperties();
+ if ( xInfo->hasPropertyByName( PROPERTY_TITLE ) )
+ {
+ css::uno::Any aAny = aCnt.getPropertyValue( PROPERTY_TITLE );
+ OUString aValue;
+ if ( aAny >>= aValue )
+ {
+ SfxAddHelpBookmarkDialog_Impl aDlg(GetFrameWeld(), false);
+ aDlg.SetTitle(aValue);
+ if (aDlg.run() == RET_OK )
+ {
+ xIndexWin->AddBookmarks( aDlg.GetTitle(), aURL );
+ }
+ }
+ }
+ }
+ catch( Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "sfx.appl", "SfxHelpWindow_Impl::DoAction(): unexpected exception" );
+ }
+ }
+ }
+}
+
+void SfxHelpWindow_Impl::CloseWindow()
+{
+ try
+ {
+ // search for top frame
+ Reference< XFramesSupplier > xCreator = getTextFrame()->getCreator();
+ while ( xCreator.is() && !xCreator->isTop() )
+ {
+ xCreator = xCreator->getCreator();
+ }
+
+ // when found, close it
+ if ( xCreator.is() && xCreator->isTop() )
+ {
+ Reference < XCloseable > xCloser( xCreator, UNO_QUERY );
+ if ( xCloser.is() )
+ xCloser->close( false );
+ }
+ }
+ catch( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "sfx.appl", "SfxHelpWindow_Impl::CloseWindow()" );
+ }
+}
+
+
+void SfxHelpWindow_Impl::UpdateToolbox()
+{
+ pTextWin->GetToolBox().set_item_sensitive("backward", pHelpInterceptor->HasHistoryPred());
+ pTextWin->GetToolBox().set_item_sensitive("forward", pHelpInterceptor->HasHistorySucc());
+}
+
+
+bool SfxHelpWindow_Impl::HasHistoryPredecessor() const
+{
+ return pHelpInterceptor->HasHistoryPred();
+}
+
+
+bool SfxHelpWindow_Impl::HasHistorySuccessor() const
+{
+ return pHelpInterceptor->HasHistorySucc();
+}
+
+// class SfxAddHelpBookmarkDialog_Impl -----------------------------------
+
+SfxAddHelpBookmarkDialog_Impl::SfxAddHelpBookmarkDialog_Impl(weld::Widget* pParent, bool bRename)
+ : GenericDialogController(pParent, "sfx/ui/bookmarkdialog.ui", "BookmarkDialog")
+ , m_xTitleED(m_xBuilder->weld_entry("entry"))
+ , m_xAltTitle(m_xBuilder->weld_label("alttitle"))
+{
+ if (bRename)
+ m_xDialog->set_title(m_xAltTitle->get_label());
+}
+
+void SfxAddHelpBookmarkDialog_Impl::SetTitle(const OUString& rTitle)
+{
+ m_xTitleED->set_text(rTitle);
+ m_xTitleED->select_region(0, -1);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/appl/newhelp.hxx b/sfx2/source/appl/newhelp.hxx
new file mode 100644
index 000000000..9d5a5e422
--- /dev/null
+++ b/sfx2/source/appl/newhelp.hxx
@@ -0,0 +1,511 @@
+/* -*- 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_APPL_NEWHELP_HXX
+#define INCLUDED_SFX2_SOURCE_APPL_NEWHELP_HXX
+
+#include <com/sun/star/uno/Reference.h>
+#include <com/sun/star/frame/XFrame2.hpp>
+
+#include <rtl/ustrbuf.hxx>
+#include <vcl/builderpage.hxx>
+#include <vcl/dockwin.hxx>
+#include <vcl/idle.hxx>
+#include <vcl/keycod.hxx>
+#include <vcl/weld.hxx>
+#include <vcl/window.hxx>
+
+#include <srchdlg.hxx>
+
+// context menu ids
+#define MID_OPEN 1
+#define MID_RENAME 2
+#define MID_DELETE 3
+
+namespace com::sun::star::awt { class XWindow; }
+namespace com::sun::star::i18n { class XBreakIterator; }
+namespace com::sun::star::text { class XTextRange; }
+
+// class HelpTabPage_Impl ------------------------------------------------
+
+class SfxHelpIndexWindow_Impl;
+
+class HelpTabPage_Impl : public BuilderPage
+{
+protected:
+ SfxHelpIndexWindow_Impl* m_pIdxWin;
+
+public:
+ HelpTabPage_Impl(weld::Widget* pParent, SfxHelpIndexWindow_Impl* _pIdxWin,
+ const OString& rID, const OUString& rUIXMLDescription);
+ virtual ~HelpTabPage_Impl() override;
+};
+
+// class ContentTabPage_Impl ---------------------------------------------
+
+class ContentTabPage_Impl : public HelpTabPage_Impl
+{
+private:
+ std::unique_ptr<weld::TreeView> m_xContentBox;
+ std::unique_ptr<weld::TreeIter> m_xScratchIter;
+ OUString aOpenBookImage;
+ OUString aClosedBookImage;
+ OUString aDocumentImage;
+
+ Link<LinkParamNone*, void> aDoubleClickHdl;
+
+ DECL_LINK(DoubleClickHdl, weld::TreeView&, bool);
+ DECL_LINK(ExpandingHdl, const weld::TreeIter&, bool);
+ DECL_LINK(CollapsingHdl, const weld::TreeIter&, bool);
+
+ void ClearChildren(const weld::TreeIter* pParent);
+ void InitRoot();
+public:
+ ContentTabPage_Impl(weld::Widget* pParent, SfxHelpIndexWindow_Impl* _pIdxWin);
+ virtual ~ContentTabPage_Impl() override;
+
+ void SetDoubleClickHdl(const Link<LinkParamNone*, void>& rLink);
+ OUString GetSelectedEntry() const;
+ void SetFocusOnBox() { m_xContentBox->grab_focus(); }
+};
+
+class IndexTabPage_Impl : public HelpTabPage_Impl
+{
+private:
+ std::unique_ptr<weld::Entry> m_xIndexEntry;
+ std::unique_ptr<weld::TreeView> m_xIndexList;
+ std::unique_ptr<weld::Button> m_xOpenBtn;
+
+ Idle aFactoryIdle;
+ Idle aAutoCompleteIdle;
+ Timer aKeywordTimer;
+ Link<IndexTabPage_Impl&,void> aKeywordLink;
+
+ OUString sFactory;
+ OUString sKeyword;
+
+ bool bIsActivated;
+ int nRowHeight;
+ int nAllHeight;
+ sal_uInt16 nLastCharCode;
+
+ void InitializeIndex();
+ void ClearIndex();
+
+ Link<LinkParamNone*, void> aDoubleClickHdl;
+
+ DECL_LINK(OpenHdl, weld::Button&, void);
+ DECL_LINK(IdleHdl, Timer*, void);
+ DECL_LINK(AutoCompleteHdl, Timer*, void);
+ DECL_LINK(TimeoutHdl, Timer*, void);
+ DECL_LINK(TreeChangeHdl, weld::TreeView&, void);
+ DECL_LINK(EntryChangeHdl, weld::Entry&, void);
+ DECL_LINK(ActivateHdl, weld::Entry&, bool);
+ DECL_LINK(DoubleClickHdl, weld::TreeView&, bool);
+ DECL_LINK(KeyInputHdl, const KeyEvent&, bool);
+ DECL_LINK(CustomGetSizeHdl, weld::TreeView::get_size_args, Size);
+ DECL_LINK(CustomRenderHdl, weld::TreeView::render_args, void);
+ DECL_LINK(ResizeHdl, const Size&, void);
+
+ int starts_with(const OUString& rStr, int nStartRow, bool bCaseSensitive);
+
+public:
+ IndexTabPage_Impl(weld::Widget* pParent, SfxHelpIndexWindow_Impl* pIdxWin);
+ virtual ~IndexTabPage_Impl() override;
+
+ virtual void Activate() override;
+
+ void SetDoubleClickHdl(const Link<LinkParamNone*, void>& rLink);
+ void SetFactory( const OUString& rFactory );
+ const OUString& GetFactory() const { return sFactory; }
+ OUString GetSelectedEntry() const;
+ void SetFocusOnBox() { m_xIndexEntry->grab_focus(); }
+ bool HasFocusOnEdit() const { return m_xIndexEntry->has_focus(); }
+
+ void SetKeywordHdl( const Link<IndexTabPage_Impl&,void>& rLink ) { aKeywordLink = rLink; }
+ void SetKeyword( const OUString& rKeyword );
+ bool HasKeyword() const;
+ bool HasKeywordIgnoreCase();
+ void OpenKeyword();
+
+ void SelectExecutableEntry();
+};
+
+class SearchTabPage_Impl : public HelpTabPage_Impl
+{
+private:
+ std::unique_ptr<weld::ComboBox> m_xSearchED;
+ std::unique_ptr<weld::Button> m_xSearchBtn;
+ std::unique_ptr<weld::CheckButton> m_xFullWordsCB;
+ std::unique_ptr<weld::CheckButton> m_xScopeCB;
+ std::unique_ptr<weld::TreeView> m_xResultsLB;
+ std::unique_ptr<weld::Button> m_xOpenBtn;
+
+ Link<LinkParamNone*, void> aDoubleClickHdl;
+
+ OUString aFactory;
+
+ css::uno::Reference< css::i18n::XBreakIterator >
+ xBreakIterator;
+
+ void ClearSearchResults();
+ void RememberSearchText( const OUString& rSearchText );
+ void Search();
+
+ DECL_LINK(ClickHdl, weld::Button&, void);
+ DECL_LINK(OpenHdl, weld::Button&, void);
+ DECL_LINK(ModifyHdl, weld::ComboBox&, void);
+ DECL_LINK(DoubleClickHdl, weld::TreeView&, bool);
+ DECL_LINK(ActivateHdl, weld::ComboBox&, bool);
+
+public:
+ SearchTabPage_Impl(weld::Widget* pParent, SfxHelpIndexWindow_Impl* pIdxWin);
+ virtual ~SearchTabPage_Impl() override;
+
+ void SetDoubleClickHdl(const Link<LinkParamNone*, void>& rLink);
+ void SetFactory( const OUString& rFactory ) { aFactory = rFactory; }
+ OUString GetSelectedEntry() const;
+ void ClearPage();
+ void SetFocusOnBox() { m_xResultsLB->grab_focus(); }
+ bool HasFocusOnEdit() const { return m_xSearchED->has_focus(); }
+ OUString GetSearchText() const { return m_xSearchED->get_active_text(); }
+ bool IsFullWordSearch() const { return m_xFullWordsCB->get_active(); }
+ bool OpenKeyword( const OUString& rKeyword );
+};
+
+class BookmarksTabPage_Impl : public HelpTabPage_Impl
+{
+private:
+ std::unique_ptr<weld::TreeView> m_xBookmarksBox;
+ std::unique_ptr<weld::Button> m_xBookmarksPB;
+
+ Link<LinkParamNone*, void> aDoubleClickHdl;
+
+ DECL_LINK(OpenHdl, weld::Button&, void);
+ DECL_LINK(DoubleClickHdl, weld::TreeView&, bool);
+ DECL_LINK(CommandHdl, const CommandEvent&, bool);
+ DECL_LINK(KeyInputHdl, const KeyEvent&, bool);
+
+ void DoAction(std::string_view rAction);
+
+public:
+ BookmarksTabPage_Impl(weld::Widget* pParent, SfxHelpIndexWindow_Impl* pIdxWin);
+ virtual ~BookmarksTabPage_Impl() override;
+
+ void SetDoubleClickHdl(const Link<LinkParamNone*, void>& rLink);
+ OUString GetSelectedEntry() const;
+ void AddBookmarks( const OUString& rTitle, const OUString& rURL );
+ void SetFocusOnBox() { m_xBookmarksBox->grab_focus(); }
+};
+
+// class SfxHelpIndexWindow_Impl -----------------------------------------
+
+class SfxHelpWindow_Impl;
+
+class SfxHelpIndexWindow_Impl
+{
+private:
+ std::unique_ptr<weld::Builder> m_xBuilder;
+ std::unique_ptr<weld::Container> m_xContainer;
+ std::unique_ptr<weld::ComboBox> m_xActiveLB;
+ std::unique_ptr<weld::Notebook> m_xTabCtrl;
+
+ Idle aIdle;
+
+ Link<SfxHelpIndexWindow_Impl*,void> aSelectFactoryLink;
+ Link<LinkParamNone*,void> aPageDoubleClickLink;
+ Link<IndexTabPage_Impl&,void> aIndexKeywordLink;
+ OUString sKeyword;
+
+ VclPtr<SfxHelpWindow_Impl> pParentWin;
+
+ std::unique_ptr<ContentTabPage_Impl> xCPage;
+ std::unique_ptr<IndexTabPage_Impl> xIPage;
+ std::unique_ptr<SearchTabPage_Impl> xSPage;
+ std::unique_ptr<BookmarksTabPage_Impl> xBPage;
+
+ bool bIsInitDone;
+
+ void Initialize();
+ void SetActiveFactory();
+ HelpTabPage_Impl* GetPage(std::string_view );
+
+ inline ContentTabPage_Impl* GetContentPage();
+ inline IndexTabPage_Impl* GetIndexPage();
+ inline SearchTabPage_Impl* GetSearchPage();
+ inline BookmarksTabPage_Impl* GetBookmarksPage();
+
+ DECL_LINK(ActivatePageHdl, const OString&, void);
+ DECL_LINK(SelectHdl, weld::ComboBox&, void);
+ DECL_LINK(InitHdl, Timer *, void);
+ DECL_LINK(SelectFactoryHdl, Timer *, void);
+ DECL_LINK(KeywordHdl, IndexTabPage_Impl&, void);
+ DECL_LINK(ContentTabPageDoubleClickHdl, LinkParamNone*, void);
+ DECL_LINK(TabPageDoubleClickHdl, LinkParamNone*, void);
+ DECL_LINK(IndexTabPageDoubleClickHdl, LinkParamNone*, void);
+
+public:
+ explicit SfxHelpIndexWindow_Impl(SfxHelpWindow_Impl* pParent, weld::Container* pContainer);
+ ~SfxHelpIndexWindow_Impl();
+
+ void SetDoubleClickHdl(const Link<LinkParamNone*, void>& rLink);
+ void SetSelectFactoryHdl( const Link<SfxHelpIndexWindow_Impl*,void>& rLink ) { aSelectFactoryLink = rLink; }
+ void SetFactory( const OUString& rFactory, bool bActive );
+ OUString const & GetFactory() const { return xIPage->GetFactory(); }
+ OUString GetSelectedEntry() const;
+ void AddBookmarks( const OUString& rTitle, const OUString& rURL );
+ bool IsValidFactory( std::u16string_view _rFactory );
+ OUString GetActiveFactoryTitle() const { return m_xActiveLB->get_active_text(); }
+ void ClearSearchPage();
+ void GrabFocusBack();
+ bool HasFocusOnEdit() const;
+ OUString GetSearchText() const;
+ bool IsFullWordSearch() const;
+ void OpenKeyword( const OUString& rKeyword );
+ void SelectExecutableEntry();
+
+ weld::Window* GetFrameWeld() const;
+};
+
+// inlines ---------------------------------------------------------------
+
+ContentTabPage_Impl* SfxHelpIndexWindow_Impl::GetContentPage()
+{
+ if (!xCPage)
+ {
+ xCPage.reset(new ContentTabPage_Impl(m_xTabCtrl->get_page("contents"), this));
+ xCPage->SetDoubleClickHdl(LINK(this, SfxHelpIndexWindow_Impl, ContentTabPageDoubleClickHdl));
+ }
+ return xCPage.get();
+}
+
+IndexTabPage_Impl* SfxHelpIndexWindow_Impl::GetIndexPage()
+{
+ if (!xIPage)
+ {
+ xIPage.reset(new IndexTabPage_Impl(m_xTabCtrl->get_page("index"), this));
+ xIPage->SetDoubleClickHdl( LINK(this, SfxHelpIndexWindow_Impl, IndexTabPageDoubleClickHdl) );
+ xIPage->SetKeywordHdl( aIndexKeywordLink );
+ }
+ return xIPage.get();
+}
+
+SearchTabPage_Impl* SfxHelpIndexWindow_Impl::GetSearchPage()
+{
+ if (!xSPage)
+ {
+ xSPage.reset(new SearchTabPage_Impl(m_xTabCtrl->get_page("find"), this));
+ xSPage->SetDoubleClickHdl( LINK(this, SfxHelpIndexWindow_Impl, TabPageDoubleClickHdl) );
+ }
+ return xSPage.get();
+}
+
+BookmarksTabPage_Impl* SfxHelpIndexWindow_Impl::GetBookmarksPage()
+{
+ if (!xBPage)
+ {
+ xBPage.reset(new BookmarksTabPage_Impl(m_xTabCtrl->get_page("bookmarks"), this));
+ xBPage->SetDoubleClickHdl( LINK(this, SfxHelpIndexWindow_Impl, TabPageDoubleClickHdl) );
+ }
+ return xBPage.get();
+}
+
+// class TextWin_Impl ----------------------------------------------------
+class TextWin_Impl : public DockingWindow
+{
+public:
+ explicit TextWin_Impl( vcl::Window* pParent );
+
+ virtual bool EventNotify( NotifyEvent& rNEvt ) override;
+};
+
+// class SfxHelpTextWindow_Impl ------------------------------------------
+
+class SvtMiscOptions;
+class SfxHelpWindow_Impl;
+
+class SfxHelpTextWindow_Impl : public vcl::Window
+{
+private:
+ std::unique_ptr<weld::Toolbar> xToolBox;
+ std::unique_ptr<weld::CheckButton> xOnStartupCB;
+ std::unique_ptr<weld::Menu> xMenu;
+ Idle aSelectIdle;
+ OUString aIndexOnImage;
+ OUString aIndexOffImage;
+ OUString aIndexOnText;
+ OUString aIndexOffText;
+ OUString aSearchText;
+ OUString aOnStartupText;
+ OUString sCurrentFactory;
+
+ VclPtr<SfxHelpWindow_Impl> xHelpWin;
+ VclPtr<vcl::Window> pTextWin;
+ std::shared_ptr<sfx2::SearchDialog> m_xSrchDlg;
+ css::uno::Reference < css::frame::XFrame2 >
+ xFrame;
+ css::uno::Reference< css::i18n::XBreakIterator >
+ xBreakIterator;
+ css::uno::Reference< css::uno::XInterface >
+ xConfiguration;
+ bool bIsDebug;
+ bool bIsIndexOn;
+ bool bIsInClose;
+ bool bIsFullWordSearch;
+
+ bool HasSelection() const;
+ void InitToolBoxImages();
+ void InitOnStartupBox();
+
+ css::uno::Reference< css::i18n::XBreakIterator > const &
+ GetBreakIterator();
+ css::uno::Reference< css::text::XTextRange >
+ getCursor() const;
+ bool isHandledKey( const vcl::KeyCode& _rKeyCode );
+
+ DECL_LINK( SelectHdl, Timer *, void);
+ DECL_LINK( NotifyHdl, LinkParamNone*, void );
+ DECL_LINK( FindHdl, sfx2::SearchDialog&, void );
+ DECL_LINK( CloseHdl, LinkParamNone*, void );
+ DECL_LINK( CheckHdl, weld::Toggleable&, void );
+ void FindHdl(sfx2::SearchDialog*);
+
+public:
+ explicit SfxHelpTextWindow_Impl(SfxHelpWindow_Impl* pHelpWin, weld::Builder& rBuilder, vcl::Window* pParent);
+ virtual ~SfxHelpTextWindow_Impl() override;
+ virtual void dispose() override;
+
+ virtual void Resize() override;
+ virtual bool PreNotify( NotifyEvent& rNEvt ) override;
+ virtual void GetFocus() override;
+ virtual void DataChanged( const DataChangedEvent& rDCEvt ) override;
+
+ const css::uno::Reference < css::frame::XFrame2 >&
+ getFrame() const { return xFrame; }
+
+ void SetSelectHdl(const Link<const OString&, void>& rLink) { xToolBox->connect_clicked(rLink); }
+ void ToggleIndex( bool bOn );
+ void SelectSearchText( const OUString& rSearchText, bool _bIsFullWordSearch );
+ void SetPageStyleHeaderOff() const;
+ weld::Toolbar& GetToolBox() { return *xToolBox; }
+ void CloseFrame();
+ void DoSearch();
+};
+
+// class SfxHelpWindow_Impl ----------------------------------------------
+
+class HelpInterceptor_Impl;
+class HelpListener_Impl;
+class SfxHelpWindow_Impl : public ResizableDockingWindow
+{
+private:
+friend class SfxHelpIndexWindow_Impl;
+
+ std::unique_ptr<weld::Builder> m_xBuilder;
+ std::unique_ptr<weld::Paned> m_xContainer;
+ std::unique_ptr<weld::Container> m_xHelpPaneWindow;
+ std::unique_ptr<weld::Container> m_xHelpTextWindow;
+ css::uno::Reference<css::awt::XWindow> m_xHelpTextXWindow;
+
+ css::uno::Reference < css::awt::XWindow >
+ xWindow;
+ css::uno::Reference < css::frame::XFrame2 >
+ xFrame;
+
+ std::unique_ptr<SfxHelpIndexWindow_Impl> xIndexWin;
+ VclPtr<SfxHelpTextWindow_Impl> pTextWin;
+ HelpInterceptor_Impl* pHelpInterceptor;
+ rtl::Reference<HelpListener_Impl> pHelpListener;
+
+ bool bIndex;
+ bool bGrabFocusToToolBox;
+ bool bSplit;
+ int nWidth;
+ int nIndexSize;
+ Point aWinPos;
+ Size aWinSize;
+ OUString sTitle;
+
+ virtual void GetFocus() override;
+
+ void MakeLayout();
+ void LoadConfig();
+ void SaveConfig();
+ void ShowStartPage();
+ void Split();
+
+ DECL_LINK(SelectHdl, const OString&, void);
+ DECL_LINK(OpenHdl, LinkParamNone*, void);
+ DECL_LINK(SelectFactoryHdl, SfxHelpIndexWindow_Impl*, void);
+ DECL_LINK(ChangeHdl, HelpListener_Impl&, void);
+ DECL_LINK(ResizeHdl, const Size&, void);
+
+public:
+ SfxHelpWindow_Impl( const css::uno::Reference < css::frame::XFrame2 >& rFrame,
+ vcl::Window* pParent );
+ virtual ~SfxHelpWindow_Impl() override;
+ virtual void dispose() override;
+
+ virtual bool PreNotify( NotifyEvent& rNEvt ) override;
+
+ void setContainerWindow( const css::uno::Reference < css::awt::XWindow >& xWin );
+ css::uno::Reference < css::frame::XFrame2 > const &
+ getTextFrame() const { return pTextWin->getFrame(); }
+
+ void SetFactory( const OUString& rFactory );
+ void SetHelpURL( std::u16string_view rURL );
+ void DoAction(std::string_view rAction);
+ void CloseWindow();
+
+ weld::Container* GetContainer() { return m_xHelpTextWindow.get(); }
+
+ void UpdateToolbox();
+ void OpenKeyword( const OUString& rKeyword ) { xIndexWin->OpenKeyword( rKeyword ); }
+
+ bool HasHistoryPredecessor() const; // forward to interceptor
+ bool HasHistorySuccessor() const; // forward to interceptor
+
+ void openDone(std::u16string_view sURL ,
+ bool bSuccess);
+
+ static OUString buildHelpURL(std::u16string_view sFactory ,
+ std::u16string_view sContent ,
+ std::u16string_view sAnchor);
+
+ void loadHelpContent(const OUString& sHelpURL ,
+ bool bAddToHistory = true);
+};
+
+class SfxAddHelpBookmarkDialog_Impl : public weld::GenericDialogController
+{
+private:
+ std::unique_ptr<weld::Entry> m_xTitleED;
+ std::unique_ptr<weld::Label> m_xAltTitle;
+public:
+ SfxAddHelpBookmarkDialog_Impl(weld::Widget* pParent, bool bRename);
+
+ void SetTitle( const OUString& rTitle );
+ OUString GetTitle() const { return m_xTitleED->get_text(); }
+};
+
+/// Appends ?Language=xy&System=abc to the help URL in rURL
+void AppendConfigToken(OUStringBuffer& rURL, bool bQuestionMark);
+
+#endif // INCLUDED_SFX2_SOURCE_APPL_NEWHELP_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/appl/opengrf.cxx b/sfx2/source/appl/opengrf.cxx
new file mode 100644
index 000000000..325c50422
--- /dev/null
+++ b/sfx2/source/appl/opengrf.cxx
@@ -0,0 +1,284 @@
+/* -*- 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 <tools/debug.hxx>
+#include <tools/urlobj.hxx>
+#include <com/sun/star/uno/Reference.h>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <com/sun/star/ui/dialogs/CommonFilePickerElementIds.hpp>
+#include <com/sun/star/ui/dialogs/ExtendedFilePickerElementIds.hpp>
+#include <com/sun/star/ui/dialogs/TemplateDescription.hpp>
+#include <com/sun/star/ui/dialogs/XFilePickerControlAccess.hpp>
+#include <com/sun/star/ui/dialogs/XFilePicker3.hpp>
+#include <o3tl/any.hxx>
+#include <vcl/stdtext.hxx>
+#include <vcl/graphicfilter.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+#include <sfx2/filedlghelper.hxx>
+#include <sfx2/docfile.hxx>
+#include <sfx2/opengrf.hxx>
+#include <sfx2/strings.hrc>
+#include <sfx2/sfxresid.hxx>
+#include <osl/diagnose.h>
+
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::ui::dialogs;
+using namespace ::com::sun::star::uno;
+using namespace ::cppu;
+
+static TranslateId SvxOpenGrfErr2ResId( ErrCode err )
+{
+ if (err == ERRCODE_GRFILTER_OPENERROR)
+ return RID_SVXSTR_GRFILTER_OPENERROR;
+ else if (err == ERRCODE_GRFILTER_IOERROR)
+ return RID_SVXSTR_GRFILTER_IOERROR;
+ else if (err == ERRCODE_GRFILTER_VERSIONERROR)
+ return RID_SVXSTR_GRFILTER_VERSIONERROR;
+ else if (err == ERRCODE_GRFILTER_FILTERERROR)
+ return RID_SVXSTR_GRFILTER_FILTERERROR;
+ else
+ return RID_SVXSTR_GRFILTER_FORMATERROR;
+}
+
+struct SvxOpenGrf_Impl
+{
+ SvxOpenGrf_Impl(weld::Window* pPreferredParent,
+ sal_Int16 nDialogType);
+
+ sfx2::FileDialogHelper aFileDlg;
+ OUString sDetectedFilter;
+ uno::Reference < XFilePickerControlAccess > xCtrlAcc;
+};
+
+
+SvxOpenGrf_Impl::SvxOpenGrf_Impl(weld::Window* pPreferredParent,
+ sal_Int16 nDialogType)
+ : aFileDlg(nDialogType, FileDialogFlags::Graphic, pPreferredParent)
+{
+ uno::Reference < XFilePicker3 > xFP = aFileDlg.GetFilePicker();
+ xCtrlAcc.set(xFP, UNO_QUERY);
+}
+
+
+SvxOpenGraphicDialog::SvxOpenGraphicDialog(const OUString& rTitle, weld::Window* pPreferredParent)
+ : mpImpl(new SvxOpenGrf_Impl(pPreferredParent, ui::dialogs::TemplateDescription::FILEOPEN_LINK_PREVIEW))
+{
+ mpImpl->aFileDlg.SetTitle(rTitle);
+ mpImpl->aFileDlg.SetContext(sfx2::FileDialogHelper::InsertImage);
+}
+
+SvxOpenGraphicDialog::SvxOpenGraphicDialog(const OUString& rTitle, weld::Window* pPreferredParent,
+ sal_Int16 nDialogType)
+ : mpImpl(new SvxOpenGrf_Impl(pPreferredParent, nDialogType))
+{
+ mpImpl->aFileDlg.SetTitle(rTitle);
+ mpImpl->aFileDlg.SetContext(sfx2::FileDialogHelper::InsertImage);
+}
+
+SvxOpenGraphicDialog::~SvxOpenGraphicDialog()
+{
+}
+
+ErrCode SvxOpenGraphicDialog::Execute()
+{
+ ErrCode nImpRet;
+ bool bQuitLoop(false);
+
+ while( !bQuitLoop &&
+ mpImpl->aFileDlg.Execute() == ERRCODE_NONE )
+ {
+ if( !GetPath().isEmpty() )
+ {
+ GraphicFilter& rFilter = GraphicFilter::GetGraphicFilter();
+ INetURLObject aObj( GetPath() );
+
+ // check whether we can load the graphic
+ OUString aCurFilter( GetCurrentFilter() );
+ sal_uInt16 nFormatNum = rFilter.GetImportFormatNumber( aCurFilter );
+ sal_uInt16 nRetFormat = 0;
+ sal_uInt16 nFound = USHRT_MAX;
+
+ // non-local?
+ if ( INetProtocol::File != aObj.GetProtocol() )
+ {
+ SfxMedium aMed( aObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ), StreamMode::READ );
+ aMed.Download();
+ SvStream* pStream = aMed.GetInStream();
+
+ if( pStream )
+ nImpRet = rFilter.CanImportGraphic( aObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ), *pStream, nFormatNum, &nRetFormat );
+ else
+ nImpRet = rFilter.CanImportGraphic( aObj, nFormatNum, &nRetFormat );
+
+ if ( ERRCODE_NONE != nImpRet )
+ {
+ if ( !pStream )
+ nImpRet = rFilter.CanImportGraphic( aObj, GRFILTER_FORMAT_DONTKNOW, &nRetFormat );
+ else
+ nImpRet = rFilter.CanImportGraphic( aObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ), *pStream,
+ GRFILTER_FORMAT_DONTKNOW, &nRetFormat );
+ }
+ }
+ else
+ {
+ nImpRet = rFilter.CanImportGraphic( aObj, nFormatNum, &nRetFormat );
+ if( nImpRet != ERRCODE_NONE )
+ nImpRet = rFilter.CanImportGraphic( aObj, GRFILTER_FORMAT_DONTKNOW, &nRetFormat );
+ }
+
+ if ( ERRCODE_NONE == nImpRet )
+ nFound = nRetFormat;
+
+ // could not load?
+ if ( nFound == USHRT_MAX )
+ {
+ std::unique_ptr<weld::MessageDialog> xWarn(Application::CreateMessageDialog(nullptr,
+ VclMessageType::Warning, VclButtonsType::NONE,
+ SfxResId(SvxOpenGrfErr2ResId(nImpRet))));
+ xWarn->add_button(GetStandardText(StandardButtonType::Retry), RET_RETRY);
+ xWarn->add_button(GetStandardText(StandardButtonType::Cancel), RET_CANCEL);
+ bQuitLoop = xWarn->run() != RET_RETRY;
+ }
+ else
+ {
+ if( rFilter.GetImportFormatCount() )
+ {
+ // store detected appropriate filter
+ OUString aFormatName(rFilter.GetImportFormatName(nFound));
+ SetDetectedFilter(aFormatName);
+ }
+ else
+ {
+ SetDetectedFilter(mpImpl->aFileDlg.GetCurrentFilter());
+ }
+
+ return nImpRet;
+ }
+ }
+ }
+
+ // cancel
+ return ErrCode(sal_uInt32(-1));
+}
+
+
+void SvxOpenGraphicDialog::SetPath( const OUString& rPath, bool bLinkState )
+{
+ mpImpl->aFileDlg.SetDisplayDirectory(rPath);
+ AsLink(bLinkState);
+}
+
+
+void SvxOpenGraphicDialog::EnableLink( bool state )
+{
+ if( !mpImpl->xCtrlAcc.is() )
+ return;
+
+ try
+ {
+ mpImpl->xCtrlAcc->enableControl( ExtendedFilePickerElementIds::CHECKBOX_LINK, state );
+ }
+ catch(const IllegalArgumentException&)
+ {
+#ifdef DBG_UTIL
+ OSL_FAIL( "Cannot enable \"link\" checkbox" );
+#endif
+ }
+}
+
+
+void SvxOpenGraphicDialog::AsLink(bool bState)
+{
+ if( !mpImpl->xCtrlAcc.is() )
+ return;
+
+ try
+ {
+ mpImpl->xCtrlAcc->setValue( ExtendedFilePickerElementIds::CHECKBOX_LINK, 0, Any(bState) );
+ }
+ catch(const IllegalArgumentException&)
+ {
+#ifdef DBG_UTIL
+ OSL_FAIL( "Cannot check \"link\" checkbox" );
+#endif
+ }
+}
+
+
+bool SvxOpenGraphicDialog::IsAsLink() const
+{
+ try
+ {
+ if( mpImpl->xCtrlAcc.is() )
+ {
+ Any aVal = mpImpl->xCtrlAcc->getValue( ExtendedFilePickerElementIds::CHECKBOX_LINK, 0 );
+ DBG_ASSERT(aVal.hasValue(), "Value CBX_INSERT_AS_LINK not found");
+ return aVal.hasValue() && *o3tl::doAccess<bool>(aVal);
+ }
+ }
+ catch(const IllegalArgumentException&)
+ {
+#ifdef DBG_UTIL
+ OSL_FAIL( "Cannot access \"link\" checkbox" );
+#endif
+ }
+
+ return false;
+}
+
+ErrCode SvxOpenGraphicDialog::GetGraphic(Graphic& rGraphic) const
+{
+ return mpImpl->aFileDlg.GetGraphic(rGraphic);
+}
+
+OUString SvxOpenGraphicDialog::GetPath() const
+{
+ return mpImpl->aFileDlg.GetPath();
+}
+
+OUString SvxOpenGraphicDialog::GetCurrentFilter() const
+{
+ return mpImpl->aFileDlg.GetCurrentFilter();
+}
+
+OUString const & SvxOpenGraphicDialog::GetDetectedFilter() const
+{
+ return mpImpl->sDetectedFilter;
+}
+
+void SvxOpenGraphicDialog::SetCurrentFilter(const OUString& rStr)
+{
+ mpImpl->aFileDlg.SetCurrentFilter(rStr);
+}
+
+void SvxOpenGraphicDialog::SetDetectedFilter(const OUString& rStr)
+{
+ mpImpl->sDetectedFilter = rStr;
+}
+
+Reference<ui::dialogs::XFilePickerControlAccess> const & SvxOpenGraphicDialog::GetFilePickerControlAccess() const
+{
+ return mpImpl->xCtrlAcc;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/appl/openuriexternally.cxx b/sfx2/source/appl/openuriexternally.cxx
new file mode 100644
index 000000000..a8aed34fc
--- /dev/null
+++ b/sfx2/source/appl/openuriexternally.cxx
@@ -0,0 +1,140 @@
+/* -*- 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 <sal/config.h>
+
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <com/sun/star/security/AccessControlException.hpp>
+#include <com/sun/star/system/SystemShellExecute.hpp>
+#include <com/sun/star/system/SystemShellExecuteException.hpp>
+#include <com/sun/star/system/SystemShellExecuteFlags.hpp>
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/uno/RuntimeException.hpp>
+#include <comphelper/processfactory.hxx>
+#include <rtl/ustring.hxx>
+#include <sfx2/sfxresid.hxx>
+#include <tools/urlobj.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+#include <openuriexternally.hxx>
+#include <comphelper/lok.hxx>
+#include <LibreOfficeKit/LibreOfficeKitEnums.h>
+
+#include <sfx2/viewsh.hxx>
+#include <sfx2/strings.hrc>
+
+namespace {
+
+class URITools
+{
+private:
+ Timer aOpenURITimer { "sfx2::openUriExternallyTimer" };
+ OUString msURI;
+ weld::Widget* mpDialogParent;
+ bool mbHandleSystemShellExecuteException;
+ DECL_LINK(onOpenURI, Timer*, void);
+
+public:
+ URITools(weld::Widget* pDialogParent)
+ : mpDialogParent(pDialogParent)
+ , mbHandleSystemShellExecuteException(false)
+ {
+ }
+ void openURI(const OUString& sURI, bool bHandleSystemShellExecuteException);
+};
+
+}
+
+void URITools::openURI(const OUString& sURI, bool bHandleSystemShellExecuteException)
+{
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ if (SfxViewShell* pViewShell = SfxViewShell::Current())
+ {
+ pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_HYPERLINK_CLICKED,
+ sURI.toUtf8().getStr());
+ }
+ delete this;
+ return;
+ }
+
+ mbHandleSystemShellExecuteException = bHandleSystemShellExecuteException;
+ msURI = sURI;
+
+ // tdf#116305 Workaround: Use timer to bring browsers to the front
+ aOpenURITimer.SetInvokeHandler(LINK(this, URITools, onOpenURI));
+#ifdef _WIN32
+ // 200ms seems to be the best compromise between responsiveness and success rate
+ aOpenURITimer.SetTimeout(200);
+#else
+ aOpenURITimer.SetTimeout(0);
+#endif
+ aOpenURITimer.Start();
+}
+
+IMPL_LINK_NOARG(URITools, onOpenURI, Timer*, void)
+{
+ std::unique_ptr<URITools> guard(this);
+ css::uno::Reference< css::system::XSystemShellExecute > exec(
+ css::system::SystemShellExecute::create(comphelper::getProcessComponentContext()));
+ for (sal_Int32 flags = css::system::SystemShellExecuteFlags::URIS_ONLY;;) {
+ try {
+ exec->execute(msURI, OUString(), flags);
+ } catch (css::security::AccessControlException & e) {
+ if (e.LackingPermission.hasValue() || flags == 0) {
+ throw css::uno::RuntimeException(
+ "unexpected AccessControlException: " + e.Message);
+ }
+ SolarMutexGuard g;
+ std::unique_ptr<weld::MessageDialog> eb(
+ Application::CreateMessageDialog(
+ mpDialogParent, VclMessageType::Warning, VclButtonsType::OkCancel,
+ SfxResId(STR_DANGEROUS_TO_OPEN)));
+ eb->set_primary_text(eb->get_primary_text().replaceFirst("$(ARG1)", INetURLObject::decode(msURI, INetURLObject::DecodeMechanism::Unambiguous)));
+ if (eb->run() == RET_OK) {
+ flags = 0;
+ continue;
+ }
+ } catch (css::lang::IllegalArgumentException & e) {
+ if (e.ArgumentPosition != 0) {
+ throw css::uno::RuntimeException(
+ "unexpected IllegalArgumentException: " + e.Message);
+ }
+ SolarMutexGuard g;
+ std::unique_ptr<weld::MessageDialog> eb(Application::CreateMessageDialog(mpDialogParent,
+ VclMessageType::Warning, VclButtonsType::Ok,
+ SfxResId(STR_NO_ABS_URI_REF)));
+ eb->set_primary_text(eb->get_primary_text().replaceFirst("$(ARG1)", INetURLObject::decode(msURI, INetURLObject::DecodeMechanism::Unambiguous)));
+ eb->run();
+ } catch (css::system::SystemShellExecuteException & e) {
+ if (!mbHandleSystemShellExecuteException) {
+ throw;
+ }
+ SolarMutexGuard g;
+ std::unique_ptr<weld::MessageDialog> eb(Application::CreateMessageDialog(mpDialogParent,
+ VclMessageType::Warning, VclButtonsType::Ok,
+ SfxResId(STR_NO_WEBBROWSER_FOUND)));
+ eb->set_primary_text(
+ eb->get_primary_text().replaceFirst("$(ARG1)", msURI)
+ .replaceFirst("$(ARG2)", OUString::number(e.PosixError))
+ .replaceFirst("$(ARG3)", e.Message));
+ //TODO: avoid subsequent replaceFirst acting on previous replacement
+ eb->run();
+ }
+ break;
+ }
+}
+
+void sfx2::openUriExternally(const OUString& sURI, bool bHandleSystemShellExecuteException, weld::Widget* pDialogParent)
+{
+ URITools* uriTools = new URITools(pDialogParent);
+ uriTools->openURI(sURI, bHandleSystemShellExecuteException);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/appl/preventduplicateinteraction.cxx b/sfx2/source/appl/preventduplicateinteraction.cxx
new file mode 100644
index 000000000..31dcd113b
--- /dev/null
+++ b/sfx2/source/appl/preventduplicateinteraction.cxx
@@ -0,0 +1,219 @@
+/* -*- 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 <preventduplicateinteraction.hxx>
+
+#include <comphelper/processfactory.hxx>
+#include <osl/diagnose.h>
+
+#include <com/sun/star/task/InteractionHandler.hpp>
+#include <com/sun/star/task/XInteractionAbort.hpp>
+
+namespace sfx2 {
+
+PreventDuplicateInteraction::PreventDuplicateInteraction(const css::uno::Reference< css::uno::XComponentContext >& rxContext)
+ : m_xContext(rxContext)
+{
+}
+
+PreventDuplicateInteraction::~PreventDuplicateInteraction()
+{
+}
+
+void PreventDuplicateInteraction::setHandler(const css::uno::Reference< css::task::XInteractionHandler >& xHandler)
+{
+ // SAFE ->
+ std::unique_lock aLock(m_aLock);
+ m_xWarningDialogsParent.reset();
+ m_xHandler = xHandler;
+ // <- SAFE
+}
+
+void PreventDuplicateInteraction::useDefaultUUIHandler()
+{
+ //if we use the default handler, set the parent to a window belonging to this object so that the dialogs
+ //don't block unrelated windows.
+ m_xWarningDialogsParent.reset(new WarningDialogsParentScope(m_xContext));
+ css::uno::Reference<css::task::XInteractionHandler> xHandler(css::task::InteractionHandler::createWithParent(
+ m_xContext, m_xWarningDialogsParent->GetDialogParent()), css::uno::UNO_QUERY_THROW);
+
+ // SAFE ->
+ std::unique_lock aLock(m_aLock);
+ m_xHandler = xHandler;
+ // <- SAFE
+}
+
+css::uno::Any SAL_CALL PreventDuplicateInteraction::queryInterface( const css::uno::Type& aType )
+{
+ if ( aType.equals( cppu::UnoType<XInteractionHandler2>::get() ) )
+ {
+ std::unique_lock aLock(m_aLock);
+ css::uno::Reference< css::task::XInteractionHandler2 > xHandler( m_xHandler, css::uno::UNO_QUERY );
+ if ( !xHandler.is() )
+ return css::uno::Any();
+ }
+ return ::cppu::WeakImplHelper<css::lang::XInitialization, css::task::XInteractionHandler2>::queryInterface(aType);
+}
+
+void SAL_CALL PreventDuplicateInteraction::handle(const css::uno::Reference< css::task::XInteractionRequest >& xRequest)
+{
+ css::uno::Any aRequest = xRequest->getRequest();
+ bool bHandleIt = true;
+
+ // SAFE ->
+ std::unique_lock aLock(m_aLock);
+
+ auto pIt = std::find_if(m_lInteractionRules.begin(), m_lInteractionRules.end(),
+ [&aRequest](const InteractionInfo& rInfo) { return aRequest.isExtractableTo(rInfo.m_aInteraction); });
+ if (pIt != m_lInteractionRules.end())
+ {
+ InteractionInfo& rInfo = *pIt;
+
+ ++rInfo.m_nCallCount;
+ rInfo.m_xRequest = xRequest;
+ bHandleIt = (rInfo.m_nCallCount <= rInfo.m_nMaxCount);
+ }
+
+ css::uno::Reference< css::task::XInteractionHandler > xHandler = m_xHandler;
+
+ aLock.unlock();
+ // <- SAFE
+
+ if ( bHandleIt && xHandler.is() )
+ {
+ xHandler->handle(xRequest);
+ }
+ else
+ {
+ const css::uno::Sequence< css::uno::Reference< css::task::XInteractionContinuation > > lContinuations = xRequest->getContinuations();
+ for (const auto& rContinuation : lContinuations)
+ {
+ css::uno::Reference< css::task::XInteractionAbort > xAbort(rContinuation, css::uno::UNO_QUERY);
+ if (xAbort.is())
+ {
+ xAbort->select();
+ break;
+ }
+ }
+ }
+}
+
+sal_Bool SAL_CALL PreventDuplicateInteraction::handleInteractionRequest( const css::uno::Reference< css::task::XInteractionRequest >& xRequest )
+{
+ css::uno::Any aRequest = xRequest->getRequest();
+ bool bHandleIt = true;
+
+ // SAFE ->
+ std::unique_lock aLock(m_aLock);
+
+ auto pIt = std::find_if(m_lInteractionRules.begin(), m_lInteractionRules.end(),
+ [&aRequest](const InteractionInfo& rInfo) { return aRequest.isExtractableTo(rInfo.m_aInteraction); });
+ if (pIt != m_lInteractionRules.end())
+ {
+ InteractionInfo& rInfo = *pIt;
+
+ ++rInfo.m_nCallCount;
+ rInfo.m_xRequest = xRequest;
+ bHandleIt = (rInfo.m_nCallCount <= rInfo.m_nMaxCount);
+ }
+
+ css::uno::Reference< css::task::XInteractionHandler2 > xHandler( m_xHandler, css::uno::UNO_QUERY );
+ OSL_ENSURE( xHandler.is() || !m_xHandler.is(),
+ "PreventDuplicateInteraction::handleInteractionRequest: inconsistency!" );
+
+ aLock.unlock();
+ // <- SAFE
+
+ if ( bHandleIt && xHandler.is() )
+ {
+ return xHandler->handleInteractionRequest(xRequest);
+ }
+ else
+ {
+ const css::uno::Sequence< css::uno::Reference< css::task::XInteractionContinuation > > lContinuations = xRequest->getContinuations();
+ for (const auto& rContinuation : lContinuations)
+ {
+ css::uno::Reference< css::task::XInteractionAbort > xAbort(rContinuation, css::uno::UNO_QUERY);
+ if (xAbort.is())
+ {
+ xAbort->select();
+ break;
+ }
+ }
+ }
+ return false;
+}
+
+void PreventDuplicateInteraction::addInteractionRule(const PreventDuplicateInteraction::InteractionInfo& aInteractionInfo)
+{
+ // SAFE ->
+ std::unique_lock aLock(m_aLock);
+
+ auto pIt = std::find_if(m_lInteractionRules.begin(), m_lInteractionRules.end(),
+ [&aInteractionInfo](const InteractionInfo& rInfo) { return rInfo.m_aInteraction == aInteractionInfo.m_aInteraction; });
+ if (pIt != m_lInteractionRules.end())
+ {
+ InteractionInfo& rInfo = *pIt;
+ rInfo.m_nMaxCount = aInteractionInfo.m_nMaxCount;
+ rInfo.m_nCallCount = aInteractionInfo.m_nCallCount;
+ return;
+ }
+
+ m_lInteractionRules.push_back(aInteractionInfo);
+ // <- SAFE
+}
+
+bool PreventDuplicateInteraction::getInteractionInfo(const css::uno::Type& aInteraction,
+ PreventDuplicateInteraction::InteractionInfo* pReturn ) const
+{
+ // SAFE ->
+ std::unique_lock aLock(m_aLock);
+
+ auto pIt = std::find_if(m_lInteractionRules.begin(), m_lInteractionRules.end(),
+ [&aInteraction](const InteractionInfo& rInfo) { return rInfo.m_aInteraction == aInteraction; });
+ if (pIt != m_lInteractionRules.end())
+ {
+ *pReturn = *pIt;
+ return true;
+ }
+ // <- SAFE
+
+ return false;
+}
+
+void SAL_CALL PreventDuplicateInteraction::initialize(const css::uno::Sequence<css::uno::Any>& rArguments)
+{
+ // If we're re-initialized to set a specific new window as a parent then drop our temporary
+ // dialog parent
+ css::uno::Reference<css::lang::XInitialization> xHandler(m_xHandler, css::uno::UNO_QUERY);
+ if (xHandler.is())
+ {
+ m_xWarningDialogsParent.reset();
+ xHandler->initialize(rArguments);
+ }
+}
+
+IMPL_STATIC_LINK_NOARG(WarningDialogsParent, TerminateDesktop, void*, void)
+{
+ css::frame::Desktop::create(comphelper::getProcessComponentContext())->terminate();
+}
+
+} // namespace sfx2
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/appl/sfxhelp.cxx b/sfx2/source/appl/sfxhelp.cxx
new file mode 100644
index 000000000..8295e0392
--- /dev/null
+++ b/sfx2/source/appl/sfxhelp.cxx
@@ -0,0 +1,1406 @@
+/* -*- 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 <config_folders.h>
+#include <sfx2/sfxhelp.hxx>
+
+#include <string_view>
+#include <algorithm>
+#include <cassert>
+#include <cstddef>
+#ifdef MACOSX
+#include <premac.h>
+#include <Foundation/NSString.h>
+#include <CoreFoundation/CFURL.h>
+#include <CoreServices/CoreServices.h>
+#include <postmac.h>
+#endif
+
+#include <sal/log.hxx>
+#include <com/sun/star/uno/Reference.h>
+#include <com/sun/star/frame/Desktop.hpp>
+#include <com/sun/star/frame/UnknownModuleException.hpp>
+#include <com/sun/star/frame/XFrame2.hpp>
+#include <comphelper/processfactory.hxx>
+#include <com/sun/star/awt/XWindow.hpp>
+#include <com/sun/star/awt/XTopWindow.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/frame/FrameSearchFlag.hpp>
+#include <toolkit/helper/vclunohelper.hxx>
+#include <com/sun/star/frame/ModuleManager.hpp>
+#include <unotools/configmgr.hxx>
+#include <unotools/moduleoptions.hxx>
+#include <tools/urlobj.hxx>
+#include <ucbhelper/content.hxx>
+#include <unotools/pathoptions.hxx>
+#include <rtl/byteseq.hxx>
+#include <rtl/ustring.hxx>
+#include <o3tl/string_view.hxx>
+#include <officecfg/Office/Common.hxx>
+#include <osl/process.h>
+#include <osl/file.hxx>
+#include <unotools/tempfile.hxx>
+#include <unotools/securityoptions.hxx>
+#include <rtl/uri.hxx>
+#include <vcl/commandinfoprovider.hxx>
+#include <vcl/keycod.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/locktoplevels.hxx>
+#include <vcl/weld.hxx>
+#include <openuriexternally.hxx>
+
+#include <comphelper/lok.hxx>
+#include <LibreOfficeKit/LibreOfficeKitEnums.h>
+#include <sfx2/viewsh.hxx>
+
+#include "newhelp.hxx"
+#include <sfx2/flatpak.hxx>
+#include <sfx2/sfxresid.hxx>
+#include <helper.hxx>
+#include <sfx2/strings.hrc>
+#include <vcl/svapp.hxx>
+#include <rtl/string.hxx>
+#include <svtools/langtab.hxx>
+#include <tools/diagnose_ex.h>
+
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::frame;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::util;
+using namespace ::com::sun::star::lang;
+
+namespace {
+
+class NoHelpErrorBox
+{
+private:
+ std::unique_ptr<weld::MessageDialog> m_xErrBox;
+public:
+ DECL_STATIC_LINK(NoHelpErrorBox, HelpRequestHdl, weld::Widget&, bool);
+public:
+ explicit NoHelpErrorBox(weld::Widget* pParent)
+ : m_xErrBox(Application::CreateMessageDialog(pParent, VclMessageType::Error, VclButtonsType::Ok,
+ SfxResId(RID_STR_HLPFILENOTEXIST)))
+ {
+ // Error message: "No help available"
+ m_xErrBox->connect_help(LINK(nullptr, NoHelpErrorBox, HelpRequestHdl));
+ }
+ void run()
+ {
+ m_xErrBox->run();
+ }
+};
+
+}
+
+IMPL_STATIC_LINK_NOARG(NoHelpErrorBox, HelpRequestHdl, weld::Widget&, bool)
+{
+ // do nothing, because no help available
+ return false;
+}
+
+static OUString const & HelpLocaleString();
+
+namespace {
+
+/// Root path of the help.
+OUString const & getHelpRootURL()
+{
+ static OUString const s_instURL = []()
+ {
+ OUString tmp = officecfg::Office::Common::Path::Current::Help::get();
+ if (tmp.isEmpty())
+ {
+ // try to determine path from default
+ tmp = "$(instpath)/" LIBO_SHARE_HELP_FOLDER;
+ }
+
+ // replace anything like $(instpath);
+ SvtPathOptions aOptions;
+ tmp = aOptions.SubstituteVariable(tmp);
+
+ OUString url;
+ if (osl::FileBase::getFileURLFromSystemPath(tmp, url) == osl::FileBase::E_None)
+ tmp = url;
+ return tmp;
+ }();
+ return s_instURL;
+}
+
+bool impl_checkHelpLocalePath(OUString const & rpPath)
+{
+ osl::DirectoryItem directoryItem;
+ bool bOK = false;
+
+ osl::FileStatus fileStatus(osl_FileStatus_Mask_Type | osl_FileStatus_Mask_FileURL | osl_FileStatus_Mask_FileName);
+ if (osl::DirectoryItem::get(rpPath, directoryItem) == osl::FileBase::E_None &&
+ directoryItem.getFileStatus(fileStatus) == osl::FileBase::E_None &&
+ fileStatus.isDirectory())
+ {
+ bOK = true;
+ }
+ return bOK;
+}
+
+/// Check for built-in help
+/// Check if help/<lang>/err.html file exist
+bool impl_hasHelpInstalled()
+{
+ if (comphelper::LibreOfficeKit::isActive())
+ return false;
+
+ // detect installed locale
+ static OUString const aLocaleStr = HelpLocaleString();
+
+ OUString helpRootURL = getHelpRootURL() + "/" + aLocaleStr + "/err.html";
+ bool bOK = false;
+ osl::DirectoryItem directoryItem;
+ if(osl::DirectoryItem::get(helpRootURL, directoryItem) == osl::FileBase::E_None){
+ bOK=true;
+ }
+
+ SAL_INFO( "sfx.appl", "Checking old help installed " << bOK);
+ return bOK;
+}
+
+/// Check for html built-in help
+/// Check if help/lang/text folder exist. Only html has it.
+bool impl_hasHTMLHelpInstalled()
+{
+ if (comphelper::LibreOfficeKit::isActive())
+ return false;
+
+ // detect installed locale
+ static OUString const aLocaleStr = HelpLocaleString();
+
+ OUString helpRootURL = getHelpRootURL() + "/" + aLocaleStr + "/text";
+ bool bOK = impl_checkHelpLocalePath( helpRootURL );
+ SAL_INFO( "sfx.appl", "Checking new help (html) installed " << bOK);
+ return bOK;
+}
+
+} // namespace
+
+/// Return the locale we prefer for displaying help
+static OUString const & HelpLocaleString()
+{
+ if (comphelper::LibreOfficeKit::isActive())
+ return comphelper::LibreOfficeKit::getLanguageTag().getBcp47();
+
+ static OUString aLocaleStr;
+ if (!aLocaleStr.isEmpty())
+ return aLocaleStr;
+
+ static const OUStringLiteral aEnglish(u"en-US");
+ // detect installed locale
+ aLocaleStr = utl::ConfigManager::getUILocale();
+
+ if ( aLocaleStr.isEmpty() )
+ {
+ aLocaleStr = aEnglish;
+ return aLocaleStr;
+ }
+
+ // get fall-back language (country)
+ OUString sLang = aLocaleStr;
+ sal_Int32 nSepPos = sLang.indexOf( '-' );
+ if (nSepPos != -1)
+ {
+ sLang = sLang.copy( 0, nSepPos );
+ }
+ OUString sHelpPath("");
+ sHelpPath = getHelpRootURL() + "/" + utl::ConfigManager::getProductVersion() + "/" + aLocaleStr;
+ if (impl_checkHelpLocalePath(sHelpPath))
+ {
+ return aLocaleStr;
+ }
+ sHelpPath = getHelpRootURL() + "/" + utl::ConfigManager::getProductVersion() + "/" + sLang;
+ if (impl_checkHelpLocalePath(sHelpPath))
+ {
+ aLocaleStr = sLang;
+ return aLocaleStr;
+ }
+ sHelpPath = getHelpRootURL() + "/" + aLocaleStr;
+ if (impl_checkHelpLocalePath(sHelpPath))
+ {
+ return aLocaleStr;
+ }
+ sHelpPath = getHelpRootURL() + "/" + sLang;
+ if (impl_checkHelpLocalePath(sHelpPath))
+ {
+ aLocaleStr = sLang;
+ return aLocaleStr;
+ }
+ sHelpPath = getHelpRootURL() + "/" + utl::ConfigManager::getProductVersion() + "/" + aEnglish;
+ if (impl_checkHelpLocalePath(sHelpPath))
+ {
+ aLocaleStr = aEnglish;
+ return aLocaleStr;
+ }
+ sHelpPath = getHelpRootURL() + "/" + aEnglish;
+ if (impl_checkHelpLocalePath(sHelpPath))
+ {
+ aLocaleStr = aEnglish;
+ return aLocaleStr;
+ }
+ return aLocaleStr;
+}
+
+
+
+void AppendConfigToken( OUStringBuffer& rURL, bool bQuestionMark )
+{
+ OUString aLocaleStr = HelpLocaleString();
+
+ // query part exists?
+ if ( bQuestionMark )
+ // no, so start with '?'
+ rURL.append('?');
+ else
+ // yes, so only append with '&'
+ rURL.append('&');
+
+ // set parameters
+ rURL.append("Language=");
+ rURL.append(aLocaleStr);
+ rURL.append("&System=");
+ rURL.append(officecfg::Office::Common::Help::System::get());
+ rURL.append("&Version=");
+ rURL.append(utl::ConfigManager::getProductVersion());
+}
+
+static bool GetHelpAnchor_Impl( std::u16string_view _rURL, OUString& _rAnchor )
+{
+ bool bRet = false;
+
+ try
+ {
+ ::ucbhelper::Content aCnt( INetURLObject( _rURL ).GetMainURL( INetURLObject::DecodeMechanism::NONE ),
+ Reference< css::ucb::XCommandEnvironment >(),
+ comphelper::getProcessComponentContext() );
+ OUString sAnchor;
+ if ( aCnt.getPropertyValue("AnchorName") >>= sAnchor )
+ {
+
+ if ( !sAnchor.isEmpty() )
+ {
+ _rAnchor = sAnchor;
+ bRet = true;
+ }
+ }
+ else
+ {
+ SAL_WARN( "sfx.appl", "Property 'AnchorName' is missing" );
+ }
+ }
+ catch (const css::uno::Exception&)
+ {
+ }
+
+ return bRet;
+}
+
+namespace {
+
+class SfxHelp_Impl
+{
+public:
+ static OUString GetHelpText( const OUString& aCommandURL, const OUString& rModule );
+};
+
+}
+
+OUString SfxHelp_Impl::GetHelpText( const OUString& aCommandURL, const OUString& rModule )
+{
+ // create help url
+ OUStringBuffer aHelpURL( SfxHelp::CreateHelpURL( aCommandURL, rModule ) );
+ // added 'active' parameter
+ sal_Int32 nIndex = aHelpURL.lastIndexOf( '#' );
+ if ( nIndex < 0 )
+ nIndex = aHelpURL.getLength();
+ aHelpURL.insert( nIndex, "&Active=true" );
+ // load help string
+ return SfxContentHelper::GetActiveHelpString( aHelpURL.makeStringAndClear() );
+}
+
+SfxHelp::SfxHelp()
+ : bIsDebug(false)
+ , bLaunchingHelp(false)
+{
+ // read the environment variable "HELP_DEBUG"
+ // if it's set, you will see debug output on active help
+ OUString sHelpDebug;
+ OUString sEnvVarName( "HELP_DEBUG" );
+ osl_getEnvironment( sEnvVarName.pData, &sHelpDebug.pData );
+ bIsDebug = !sHelpDebug.isEmpty();
+}
+
+SfxHelp::~SfxHelp()
+{
+}
+
+static OUString getDefaultModule_Impl()
+{
+ OUString sDefaultModule;
+ SvtModuleOptions aModOpt;
+ if ( aModOpt.IsModuleInstalled( SvtModuleOptions::EModule::WRITER ) )
+ sDefaultModule = "swriter";
+ else if ( aModOpt.IsModuleInstalled( SvtModuleOptions::EModule::CALC ) )
+ sDefaultModule = "scalc";
+ else if ( aModOpt.IsModuleInstalled( SvtModuleOptions::EModule::IMPRESS ) )
+ sDefaultModule = "simpress";
+ else if ( aModOpt.IsModuleInstalled( SvtModuleOptions::EModule::DRAW ) )
+ sDefaultModule = "sdraw";
+ else if ( aModOpt.IsModuleInstalled( SvtModuleOptions::EModule::MATH ) )
+ sDefaultModule = "smath";
+ else if ( aModOpt.IsModuleInstalled( SvtModuleOptions::EModule::CHART ) )
+ sDefaultModule = "schart";
+ else if ( aModOpt.IsModuleInstalled( SvtModuleOptions::EModule::BASIC ) )
+ sDefaultModule = "sbasic";
+ else if ( aModOpt.IsModuleInstalled( SvtModuleOptions::EModule::DATABASE ) )
+ sDefaultModule = "sdatabase";
+ else
+ {
+ SAL_WARN( "sfx.appl", "getDefaultModule_Impl(): no module installed" );
+ }
+ return sDefaultModule;
+}
+
+static OUString getCurrentModuleIdentifier_Impl()
+{
+ OUString sIdentifier;
+ Reference < XComponentContext > xContext = ::comphelper::getProcessComponentContext();
+ Reference < XModuleManager2 > xModuleManager = ModuleManager::create(xContext);
+ Reference < XDesktop2 > xDesktop = Desktop::create(xContext);
+ Reference < XFrame > xCurrentFrame = xDesktop->getCurrentFrame();
+
+ if ( xCurrentFrame.is() )
+ {
+ try
+ {
+ sIdentifier = xModuleManager->identify( xCurrentFrame );
+ }
+ catch (const css::frame::UnknownModuleException&)
+ {
+ SAL_INFO( "sfx.appl", "SfxHelp::getCurrentModuleIdentifier_Impl(): unknown module (help in help?)" );
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "sfx.appl", "SfxHelp::getCurrentModuleIdentifier_Impl(): exception of XModuleManager::identify()" );
+ }
+ }
+
+ return sIdentifier;
+}
+
+namespace
+{
+ OUString MapModuleIdentifier(const OUString &rFactoryShortName)
+ {
+ OUString aFactoryShortName(rFactoryShortName);
+
+ // Map some module identifiers to their "real" help module string.
+ if ( aFactoryShortName == "chart2" )
+ aFactoryShortName = "schart" ;
+ else if ( aFactoryShortName == "BasicIDE" )
+ aFactoryShortName = "sbasic";
+ else if ( aFactoryShortName == "sweb"
+ || aFactoryShortName == "sglobal"
+ || aFactoryShortName == "swxform" )
+ aFactoryShortName = "swriter" ;
+ else if ( aFactoryShortName == "dbquery"
+ || aFactoryShortName == "dbbrowser"
+ || aFactoryShortName == "dbrelation"
+ || aFactoryShortName == "dbtable"
+ || aFactoryShortName == "dbapp"
+ || aFactoryShortName == "dbreport"
+ || aFactoryShortName == "dbtdata"
+ || aFactoryShortName == "swreport"
+ || aFactoryShortName == "swform" )
+ aFactoryShortName = "sdatabase";
+ else if ( aFactoryShortName == "sbibliography"
+ || aFactoryShortName == "sabpilot"
+ || aFactoryShortName == "scanner"
+ || aFactoryShortName == "spropctrlr"
+ || aFactoryShortName == "StartModule" )
+ aFactoryShortName.clear();
+
+ return aFactoryShortName;
+ }
+}
+
+OUString SfxHelp::GetHelpModuleName_Impl(std::u16string_view rHelpID)
+{
+ OUString aFactoryShortName;
+
+ //rhbz#1438876 detect preferred module for this help id, e.g. csv dialog
+ //for calc import before any toplevel is created and so context is
+ //otherwise unknown. Cosmetic, same help is shown in any case because its
+ //in the shared section, but title bar would state "Writer" when context is
+ //expected to be "Calc"
+ std::u16string_view sRemainder;
+ if (o3tl::starts_with(rHelpID, u"modules/", &sRemainder))
+ {
+ std::size_t nEndModule = sRemainder.find(u'/');
+ aFactoryShortName = nEndModule != std::u16string_view::npos
+ ? sRemainder.substr(0, nEndModule) : sRemainder;
+ }
+
+ if (aFactoryShortName.isEmpty())
+ {
+ OUString aModuleIdentifier = getCurrentModuleIdentifier_Impl();
+ if (!aModuleIdentifier.isEmpty())
+ {
+ try
+ {
+ Reference < XModuleManager2 > xModuleManager(
+ ModuleManager::create(::comphelper::getProcessComponentContext()) );
+ Sequence< PropertyValue > lProps;
+ xModuleManager->getByName( aModuleIdentifier ) >>= lProps;
+ auto pProp = std::find_if(std::cbegin(lProps), std::cend(lProps),
+ [](const PropertyValue& rProp) { return rProp.Name == "ooSetupFactoryShortName"; });
+ if (pProp != std::cend(lProps))
+ pProp->Value >>= aFactoryShortName;
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "sfx.appl", "SfxHelp::GetHelpModuleName_Impl()" );
+ }
+ }
+ }
+
+ if (!aFactoryShortName.isEmpty())
+ aFactoryShortName = MapModuleIdentifier(aFactoryShortName);
+ if (aFactoryShortName.isEmpty())
+ aFactoryShortName = getDefaultModule_Impl();
+
+ return aFactoryShortName;
+}
+
+OUString SfxHelp::CreateHelpURL_Impl( const OUString& aCommandURL, const OUString& rModuleName )
+{
+ // build up the help URL
+ OUStringBuffer aHelpURL("vnd.sun.star.help://");
+ bool bHasAnchor = false;
+ OUString aAnchor;
+
+ OUString aModuleName( rModuleName );
+ if (aModuleName.isEmpty())
+ aModuleName = getDefaultModule_Impl();
+
+ aHelpURL.append(aModuleName);
+
+ if ( aCommandURL.isEmpty() )
+ aHelpURL.append("/start");
+ else
+ {
+ aHelpURL.append('/');
+ aHelpURL.append(rtl::Uri::encode(aCommandURL,
+ rtl_UriCharClassRelSegment,
+ rtl_UriEncodeKeepEscapes,
+ RTL_TEXTENCODING_UTF8));
+
+ OUStringBuffer aTempURL = aHelpURL;
+ AppendConfigToken( aTempURL, true );
+ bHasAnchor = GetHelpAnchor_Impl(aTempURL.makeStringAndClear(), aAnchor);
+ }
+
+ AppendConfigToken( aHelpURL, true );
+
+ if ( bHasAnchor )
+ {
+ aHelpURL.append('#');
+ aHelpURL.append(aAnchor);
+ }
+
+ return aHelpURL.makeStringAndClear();
+}
+
+static SfxHelpWindow_Impl* impl_createHelp(Reference< XFrame2 >& rHelpTask ,
+ Reference< XFrame >& rHelpContent)
+{
+ Reference < XDesktop2 > xDesktop = Desktop::create( ::comphelper::getProcessComponentContext() );
+
+ // otherwise - create new help task
+ Reference< XFrame2 > xHelpTask(
+ xDesktop->findFrame( "OFFICE_HELP_TASK", FrameSearchFlag::TASKS | FrameSearchFlag::CREATE),
+ UNO_QUERY);
+ if (!xHelpTask.is())
+ return nullptr;
+
+ // create all internal windows and sub frames ...
+ Reference< css::awt::XWindow > xParentWindow = xHelpTask->getContainerWindow();
+ VclPtr<vcl::Window> pParentWindow = VCLUnoHelper::GetWindow( xParentWindow );
+ VclPtrInstance<SfxHelpWindow_Impl> pHelpWindow( xHelpTask, pParentWindow );
+ Reference< css::awt::XWindow > xHelpWindow = VCLUnoHelper::GetInterface( pHelpWindow );
+
+ Reference< XFrame > xHelpContent;
+ if (xHelpTask->setComponent( xHelpWindow, Reference< XController >() ))
+ {
+ // Customize UI ...
+ xHelpTask->setName("OFFICE_HELP_TASK");
+
+ Reference< XPropertySet > xProps(xHelpTask, UNO_QUERY);
+ if (xProps.is())
+ xProps->setPropertyValue(
+ "Title",
+ Any(SfxResId(STR_HELP_WINDOW_TITLE)));
+
+ pHelpWindow->setContainerWindow( xParentWindow );
+ xParentWindow->setVisible(true);
+ xHelpWindow->setVisible(true);
+
+ // This sub frame is created internally (if we called new SfxHelpWindow_Impl() ...)
+ // It should exist :-)
+ xHelpContent = xHelpTask->findFrame("OFFICE_HELP", FrameSearchFlag::CHILDREN);
+ }
+
+ if (!xHelpContent.is())
+ {
+ pHelpWindow.disposeAndClear();
+ return nullptr;
+ }
+
+ xHelpContent->setName("OFFICE_HELP");
+
+ rHelpTask = xHelpTask;
+ rHelpContent = xHelpContent;
+ return pHelpWindow;
+}
+
+OUString SfxHelp::GetHelpText( const OUString& aCommandURL, const vcl::Window* pWindow )
+{
+ OUString sModuleName = GetHelpModuleName_Impl(aCommandURL);
+ auto aProperties = vcl::CommandInfoProvider::GetCommandProperties(aCommandURL, getCurrentModuleIdentifier_Impl());
+ OUString sRealCommand = vcl::CommandInfoProvider::GetRealCommandForCommand(aProperties);
+ OUString sHelpText = SfxHelp_Impl::GetHelpText( sRealCommand.isEmpty() ? aCommandURL : sRealCommand, sModuleName );
+
+ OString aNewHelpId;
+
+ if (pWindow && sHelpText.isEmpty())
+ {
+ // no help text found -> try with parent help id.
+ vcl::Window* pParent = pWindow->GetParent();
+ while ( pParent )
+ {
+ aNewHelpId = pParent->GetHelpId();
+ sHelpText = SfxHelp_Impl::GetHelpText( OStringToOUString(aNewHelpId, RTL_TEXTENCODING_UTF8), sModuleName );
+ if (!sHelpText.isEmpty())
+ pParent = nullptr;
+ else
+ pParent = pParent->GetParent();
+ }
+
+ if (bIsDebug && sHelpText.isEmpty())
+ aNewHelpId.clear();
+ }
+
+ // add some debug information?
+ if ( bIsDebug )
+ {
+ sHelpText += "\n-------------\n" +
+ sModuleName + ": " + aCommandURL;
+ if ( !aNewHelpId.isEmpty() )
+ {
+ sHelpText += " - " +
+ OStringToOUString(aNewHelpId, RTL_TEXTENCODING_UTF8);
+ }
+ }
+
+ return sHelpText;
+}
+
+OUString SfxHelp::GetHelpText(const OUString& aCommandURL, const weld::Widget* pWidget)
+{
+ OUString sModuleName = GetHelpModuleName_Impl(aCommandURL);
+ auto aProperties = vcl::CommandInfoProvider::GetCommandProperties(aCommandURL, getCurrentModuleIdentifier_Impl());
+ OUString sRealCommand = vcl::CommandInfoProvider::GetRealCommandForCommand(aProperties);
+ OUString sHelpText = SfxHelp_Impl::GetHelpText( sRealCommand.isEmpty() ? aCommandURL : sRealCommand, sModuleName );
+
+ OString aNewHelpId;
+
+ if (pWidget && sHelpText.isEmpty())
+ {
+ // no help text found -> try with parent help id.
+ std::unique_ptr<weld::Widget> xParent(pWidget->weld_parent());
+ while (xParent)
+ {
+ aNewHelpId = xParent->get_help_id();
+ sHelpText = SfxHelp_Impl::GetHelpText( OStringToOUString(aNewHelpId, RTL_TEXTENCODING_UTF8), sModuleName );
+ if (!sHelpText.isEmpty())
+ xParent.reset();
+ else
+ xParent = xParent->weld_parent();
+ }
+
+ if (bIsDebug && sHelpText.isEmpty())
+ aNewHelpId.clear();
+ }
+
+ // add some debug information?
+ if ( bIsDebug )
+ {
+ sHelpText += "\n-------------\n" +
+ sModuleName + ": " + aCommandURL;
+ if ( !aNewHelpId.isEmpty() )
+ {
+ sHelpText += " - " +
+ OStringToOUString(aNewHelpId, RTL_TEXTENCODING_UTF8);
+ }
+ }
+
+ return sHelpText;
+}
+
+OUString SfxHelp::GetURLHelpText(std::u16string_view aURL)
+{
+ bool bCtrlClickHlink = SvtSecurityOptions::IsOptionSet(SvtSecurityOptions::EOption::CtrlClickHyperlink);
+
+ // "ctrl-click to follow link:" for not MacOS
+ // "⌘-click to follow link:" for MacOs
+ vcl::KeyCode aCode(KEY_SPACE);
+ vcl::KeyCode aModifiedCode(KEY_SPACE, KEY_MOD1);
+ OUString aModStr(aModifiedCode.GetName());
+ aModStr = aModStr.replaceFirst(aCode.GetName(), "");
+ aModStr = aModStr.replaceAll("+", "");
+ OUString aHelpStr
+ = bCtrlClickHlink ? SfxResId(STR_CTRLCLICKHYPERLINK) : SfxResId(STR_CLICKHYPERLINK);
+ aHelpStr = aHelpStr.replaceFirst("%{key}", aModStr);
+ aHelpStr = aHelpStr.replaceFirst("%{link}", aURL);
+ return aHelpStr;
+}
+
+void SfxHelp::SearchKeyword( const OUString& rKeyword )
+{
+ Start_Impl(OUString(), static_cast<weld::Widget*>(nullptr), rKeyword);
+}
+
+bool SfxHelp::Start( const OUString& rURL, const vcl::Window* pWindow )
+{
+ if (bLaunchingHelp)
+ return true;
+ bLaunchingHelp = true;
+ bool bRet = Start_Impl( rURL, pWindow );
+ bLaunchingHelp = false;
+ return bRet;
+}
+
+bool SfxHelp::Start(const OUString& rURL, weld::Widget* pWidget)
+{
+ if (bLaunchingHelp)
+ return true;
+ bLaunchingHelp = true;
+ bool bRet = Start_Impl(rURL, pWidget, OUString());
+ bLaunchingHelp = false;
+ return bRet;
+}
+
+/// Redirect the vnd.sun.star.help:// urls to http://help.libreoffice.org
+static bool impl_showOnlineHelp(const OUString& rURL, weld::Widget* pDialogParent)
+{
+ static constexpr OUStringLiteral aInternal(u"vnd.sun.star.help://");
+ if ( rURL.getLength() <= aInternal.getLength() || !rURL.startsWith(aInternal) )
+ return false;
+
+ OUString aHelpLink = officecfg::Office::Common::Help::HelpRootURL::get();
+ OUString aTarget = OUString::Concat("Target=") + rURL.subView(aInternal.getLength());
+ aTarget = aTarget.replaceAll("%2F", "/").replaceAll("?", "&");
+ aHelpLink += aTarget;
+
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ if(SfxViewShell* pViewShell = SfxViewShell::Current())
+ {
+ pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_HYPERLINK_CLICKED,
+ aHelpLink.toUtf8().getStr());
+ return true;
+ }
+ else if (GetpApp())
+ {
+ GetpApp()->libreOfficeKitViewCallback(LOK_CALLBACK_HYPERLINK_CLICKED,
+ aHelpLink.toUtf8().getStr());
+ return true;
+ }
+
+ return false;
+ }
+
+ try
+ {
+#ifdef MACOSX
+ LSOpenCFURLRef(CFURLCreateWithString(kCFAllocatorDefault,
+ CFStringCreateWithCString(kCFAllocatorDefault,
+ aHelpLink.toUtf8().getStr(),
+ kCFStringEncodingUTF8),
+ nullptr),
+ nullptr);
+ (void)pDialogParent;
+#else
+ sfx2::openUriExternally(aHelpLink, false, pDialogParent);
+#endif
+ return true;
+ }
+ catch (const Exception&)
+ {
+ }
+ return false;
+}
+
+namespace {
+
+bool rewriteFlatpakHelpRootUrl(OUString * helpRootUrl) {
+ assert(helpRootUrl != nullptr);
+ //TODO: this function for now assumes that the passed-in *helpRootUrl references
+ // /app/libreoffice/help (which belongs to the org.libreoffice.LibreOffice.Help
+ // extension); it replaces it with the corresponding file URL as seen outside the flatpak
+ // sandbox:
+ struct Failure: public std::exception {};
+ try {
+ static auto const url = [] {
+ // From /.flatpak-info [Instance] section, read
+ // app-path=<path>
+ // app-extensions=...;org.libreoffice.LibreOffice.Help=<sha>;...
+ // lines:
+ osl::File ini("file:///.flatpak-info");
+ auto err = ini.open(osl_File_OpenFlag_Read);
+ if (err != osl::FileBase::E_None) {
+ SAL_WARN("sfx.appl", "LIBO_FLATPAK mode failure opening /.flatpak-info: " << err);
+ throw Failure();
+ }
+ OUString path;
+ OUString extensions;
+ bool havePath = false;
+ bool haveExtensions = false;
+ for (bool instance = false; !(havePath && haveExtensions);) {
+ rtl::ByteSequence bytes;
+ err = ini.readLine(bytes);
+ if (err != osl::FileBase::E_None) {
+ SAL_WARN(
+ "sfx.appl",
+ "LIBO_FLATPAK mode reading /.flatpak-info fails with " << err
+ << " before [Instance] app-path");
+ throw Failure();
+ }
+ std::string_view const line(
+ reinterpret_cast<char const *>(bytes.getConstArray()), bytes.getLength());
+ if (instance) {
+ static constexpr auto keyPath = std::string_view("app-path=");
+ static constexpr auto keyExtensions = std::string_view("app-extensions=");
+ if (!havePath && line.length() >= keyPath.size()
+ && line.substr(0, keyPath.size()) == keyPath.data())
+ {
+ auto const value = line.substr(keyPath.size());
+ if (!rtl_convertStringToUString(
+ &path.pData, value.data(), value.length(),
+ osl_getThreadTextEncoding(),
+ (RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_ERROR
+ | RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_ERROR
+ | RTL_TEXTTOUNICODE_FLAGS_INVALID_ERROR)))
+ {
+ SAL_WARN(
+ "sfx.appl",
+ "LIBO_FLATPAK mode failure converting app-path \"" << value
+ << "\" encoding");
+ throw Failure();
+ }
+ havePath = true;
+ } else if (!haveExtensions && line.length() >= keyExtensions.size()
+ && line.substr(0, keyExtensions.size()) == keyExtensions.data())
+ {
+ auto const value = line.substr(keyExtensions.size());
+ if (!rtl_convertStringToUString(
+ &extensions.pData, value.data(), value.length(),
+ osl_getThreadTextEncoding(),
+ (RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_ERROR
+ | RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_ERROR
+ | RTL_TEXTTOUNICODE_FLAGS_INVALID_ERROR)))
+ {
+ SAL_WARN(
+ "sfx.appl",
+ "LIBO_FLATPAK mode failure converting app-extensions \"" << value
+ << "\" encoding");
+ throw Failure();
+ }
+ haveExtensions = true;
+ } else if (line.length() > 0 && line[0] == '[') {
+ SAL_WARN(
+ "sfx.appl",
+ "LIBO_FLATPAK mode /.flatpak-info lacks [Instance] app-path and"
+ " app-extensions");
+ throw Failure();
+ }
+ } else if (line == "[Instance]") {
+ instance = true;
+ }
+ }
+ ini.close();
+ // Extract <sha> from ...;org.libreoffice.LibreOffice.Help=<sha>;...:
+ OUString sha;
+ for (sal_Int32 i = 0;;) {
+ OUString elem = extensions.getToken(0, ';', i);
+ if (elem.startsWith("org.libreoffice.LibreOffice.Help=", &sha)) {
+ break;
+ }
+ if (i == -1) {
+ SAL_WARN(
+ "sfx.appl",
+ "LIBO_FLATPAK mode /.flatpak-info [Instance] app-extensions \""
+ << extensions << "\" org.libreoffice.LibreOffice.Help");
+ throw Failure();
+ }
+ }
+ // Assuming that <path> is of the form
+ // /.../app/org.libreoffice.LibreOffice/<arch>/<branch>/<sha'>/files
+ // rewrite it as
+ // /.../runtime/org.libreoffice.LibreOffice.Help/<arch>/<branch>/<sha>/files
+ // because the extension's files are stored at a different place than the app's files,
+ // so use this hack until flatpak itself provides a better solution:
+ static constexpr OUStringLiteral segments = u"/app/org.libreoffice.LibreOffice/";
+ auto const i1 = path.lastIndexOf(segments);
+ // use lastIndexOf instead of indexOf, in case the user-controlled prefix /.../
+ // happens to contain such segments
+ if (i1 == -1) {
+ SAL_WARN(
+ "sfx.appl",
+ "LIBO_FLATPAK mode /.flatpak-info [Instance] app-path \"" << path
+ << "\" doesn't contain /app/org.libreoffice.LibreOffice/");
+ throw Failure();
+ }
+ auto const i2 = i1 + segments.getLength();
+ auto i3 = path.indexOf('/', i2);
+ if (i3 == -1) {
+ SAL_WARN(
+ "sfx.appl",
+ "LIBO_FLATPAK mode /.flatpak-info [Instance] app-path \"" << path
+ << "\" doesn't contain branch segment");
+ throw Failure();
+ }
+ i3 = path.indexOf('/', i3 + 1);
+ if (i3 == -1) {
+ SAL_WARN(
+ "sfx.appl",
+ "LIBO_FLATPAK mode /.flatpak-info [Instance] app-path \"" << path
+ << "\" doesn't contain sha segment");
+ throw Failure();
+ }
+ ++i3;
+ auto const i4 = path.indexOf('/', i3);
+ if (i4 == -1) {
+ SAL_WARN(
+ "sfx.appl",
+ "LIBO_FLATPAK mode /.flatpak-info [Instance] app-path \"" << path
+ << "\" doesn't contain files segment");
+ throw Failure();
+ }
+ path = path.subView(0, i1) + OUString::Concat("/runtime/org.libreoffice.LibreOffice.Help/")
+ + path.subView(i2, i3 - i2) + sha + path.subView(i4);
+ // Turn <path> into a file URL:
+ OUString url_;
+ err = osl::FileBase::getFileURLFromSystemPath(path, url_);
+ if (err != osl::FileBase::E_None) {
+ SAL_WARN(
+ "sfx.appl",
+ "LIBO_FLATPAK mode failure converting app-path \"" << path << "\" to URL: "
+ << err);
+ throw Failure();
+ }
+ return url_;
+ }();
+ *helpRootUrl = url;
+ return true;
+ } catch (Failure &) {
+ return false;
+ }
+}
+
+}
+
+// add <noscript> meta for browsers without javascript
+
+constexpr OUStringLiteral SHTML1 = u"<!DOCTYPE HTML><html lang=\"en-US\"><head><meta charset=\"UTF-8\">";
+constexpr OUStringLiteral SHTML2 = u"<noscript><meta http-equiv=\"refresh\" content=\"0; url='";
+constexpr OUStringLiteral SHTML3 = u"/noscript.html'\"></noscript><meta http-equiv=\"refresh\" content=\"1; url='";
+constexpr OUStringLiteral SHTML4 = u"'\"><script type=\"text/javascript\"> window.location.href = \"";
+constexpr OUStringLiteral SHTML5 = u"\";</script><title>Help Page Redirection</title></head><body></body></html>";
+
+// use a tempfile since e.g. xdg-open doesn't support URL-parameters with file:// URLs
+static bool impl_showOfflineHelp(const OUString& rURL, weld::Widget* pDialogParent)
+{
+ OUString aBaseInstallPath = getHelpRootURL();
+ // For the flatpak case, find the pathname outside the flatpak sandbox that corresponds to
+ // aBaseInstallPath, because that is what needs to be stored in aTempFile below:
+ if (flatpak::isFlatpak() && !rewriteFlatpakHelpRootUrl(&aBaseInstallPath)) {
+ return false;
+ }
+
+ OUString aHelpLink( aBaseInstallPath + "/index.html?" );
+ OUString aTarget = OUString::Concat("Target=") + rURL.subView(RTL_CONSTASCII_LENGTH("vnd.sun.star.help://"));
+ aTarget = aTarget.replaceAll("%2F","/").replaceAll("?","&");
+ aHelpLink += aTarget;
+
+ // Get a html tempfile (for the flatpak case, create it in XDG_CACHE_HOME instead of /tmp for
+ // technical reasons, so that it can be accessed by the browser running outside the sandbox):
+ OUString const aExtension(".html");
+ OUString * parent = nullptr;
+ if (flatpak::isFlatpak() && !flatpak::createTemporaryHtmlDirectory(&parent)) {
+ return false;
+ }
+ ::utl::TempFile aTempFile(u"NewHelp", true, &aExtension, parent, false );
+
+ SvStream* pStream = aTempFile.GetStream(StreamMode::WRITE);
+ pStream->SetStreamCharSet(RTL_TEXTENCODING_UTF8);
+
+ OUString aTempStr = SHTML1 + SHTML2 +
+ aBaseInstallPath + "/" + HelpLocaleString() + SHTML3 +
+ aHelpLink + SHTML4 +
+ aHelpLink + SHTML5;
+
+ pStream->WriteUnicodeOrByteText(aTempStr);
+
+ aTempFile.CloseStream();
+ try
+ {
+#ifdef MACOSX
+ LSOpenCFURLRef(CFURLCreateWithString(kCFAllocatorDefault,
+ CFStringCreateWithCString(kCFAllocatorDefault,
+ aTempFile.GetURL().toUtf8().getStr(),
+ kCFStringEncodingUTF8),
+ nullptr),
+ nullptr);
+ (void)pDialogParent;
+#else
+ sfx2::openUriExternally(aTempFile.GetURL(), false, pDialogParent);
+#endif
+ return true;
+ }
+ catch (const Exception&)
+ {
+ }
+ aTempFile.EnableKillingFile();
+ return false;
+}
+
+namespace
+{
+ // tdf#119579 skip floating windows as potential parent for missing help dialog
+ const vcl::Window* GetBestParent(const vcl::Window* pWindow)
+ {
+ while (pWindow)
+ {
+ if (pWindow->IsSystemWindow() && pWindow->GetType() != WindowType::FLOATINGWINDOW)
+ break;
+ pWindow = pWindow->GetParent();
+ }
+ return pWindow;
+ }
+}
+
+namespace {
+
+class HelpManualMessage : public weld::MessageDialogController
+{
+private:
+ std::unique_ptr<weld::CheckButton> m_xHideOfflineHelpCB;
+
+public:
+ HelpManualMessage(weld::Widget* pParent)
+ : MessageDialogController(pParent, "sfx/ui/helpmanual.ui", "onlinehelpmanual", "hidedialog")
+ , m_xHideOfflineHelpCB(m_xBuilder->weld_check_button("hidedialog"))
+ {
+ LanguageType aLangType = Application::GetSettings().GetUILanguageTag().getLanguageType();
+ OUString sLocaleString = SvtLanguageTable::GetLanguageString(aLangType);
+ OUString sPrimText = get_primary_text();
+ set_primary_text(sPrimText.replaceAll("$UILOCALE", sLocaleString));
+ }
+
+ bool GetOfflineHelpPopUp() const { return !m_xHideOfflineHelpCB->get_active(); }
+};
+
+}
+
+bool SfxHelp::Start_Impl(const OUString& rURL, const vcl::Window* pWindow)
+{
+ OUStringBuffer aHelpRootURL("vnd.sun.star.help://");
+ AppendConfigToken(aHelpRootURL, true);
+ SfxContentHelper::GetResultSet(aHelpRootURL.makeStringAndClear());
+
+ /* rURL may be
+ * - a "real" URL
+ * - a HelpID (formerly a long, now a string)
+ * If rURL is a URL, CreateHelpURL should be called for this URL
+ * If rURL is an arbitrary string, the same should happen, but the URL should be tried out
+ * if it delivers real help content. In case only the Help Error Document is returned, the
+ * parent of the window for that help was called, is asked for its HelpID.
+ * For compatibility reasons this upward search is not implemented for "real" URLs.
+ * Help keyword search now is implemented as own method; in former versions it
+ * was done via Help::Start, but this implementation conflicted with the upward search.
+ */
+ OUString aHelpURL;
+ INetURLObject aParser( rURL );
+ INetProtocol nProtocol = aParser.GetProtocol();
+
+ switch ( nProtocol )
+ {
+ case INetProtocol::VndSunStarHelp:
+ // already a vnd.sun.star.help URL -> nothing to do
+ aHelpURL = rURL;
+ break;
+ default:
+ {
+ OUString aHelpModuleName(GetHelpModuleName_Impl(rURL));
+ OUString aRealCommand;
+
+ if ( nProtocol == INetProtocol::Uno )
+ {
+ // Command can be just an alias to another command.
+ auto aProperties = vcl::CommandInfoProvider::GetCommandProperties(rURL, getCurrentModuleIdentifier_Impl());
+ aRealCommand = vcl::CommandInfoProvider::GetRealCommandForCommand(aProperties);
+ }
+
+ // no URL, just a HelpID (maybe empty in case of keyword search)
+ aHelpURL = CreateHelpURL_Impl( aRealCommand.isEmpty() ? rURL : aRealCommand, aHelpModuleName );
+
+ if ( impl_hasHelpInstalled() && pWindow && SfxContentHelper::IsHelpErrorDocument( aHelpURL ) )
+ {
+ // no help found -> try with parent help id.
+ vcl::Window* pParent = pWindow->GetParent();
+ while ( pParent )
+ {
+ OString aHelpId = pParent->GetHelpId();
+ aHelpURL = CreateHelpURL( OStringToOUString(aHelpId, RTL_TEXTENCODING_UTF8), aHelpModuleName );
+
+ if ( !SfxContentHelper::IsHelpErrorDocument( aHelpURL ) )
+ {
+ break;
+ }
+ else
+ {
+ pParent = pParent->GetParent();
+ if (!pParent)
+ {
+ // create help url of start page ( helpid == 0 -> start page)
+ aHelpURL = CreateHelpURL( OUString(), aHelpModuleName );
+ }
+ }
+ }
+ }
+ break;
+ }
+ }
+
+ pWindow = GetBestParent(pWindow);
+ weld::Window* pWeldWindow = pWindow ? pWindow->GetFrameWeld() : nullptr;
+
+ if ( comphelper::LibreOfficeKit::isActive() )
+ {
+ impl_showOnlineHelp(aHelpURL, pWeldWindow);
+ return true;
+ }
+#ifdef MACOSX
+ if (@available(macOS 10.14, *)) {
+ // Workaround: Safari sandboxing prevents it from accessing files in the LibreOffice.app folder
+ // force online-help instead if Safari is default browser.
+ CFURLRef pBrowser = LSCopyDefaultApplicationURLForURL(
+ CFURLCreateWithString(
+ kCFAllocatorDefault,
+ static_cast<CFStringRef>(@"https://www.libreoffice.org"),
+ nullptr),
+ kLSRolesAll, nullptr);
+ if([static_cast<NSString*>(CFURLGetString(pBrowser)) hasSuffix:@"/Applications/Safari.app/"]) {
+ impl_showOnlineHelp(aHelpURL, pWeldWindow);
+ return true;
+ }
+ }
+#endif
+
+ // If the HTML or no help is installed, but aHelpURL nevertheless references valid help content,
+ // that implies that this help content belongs to an extension (and thus would not be available
+ // in neither the offline nor online HTML help); in that case, fall through to the "old-help to
+ // display" code below:
+ if (SfxContentHelper::IsHelpErrorDocument(aHelpURL))
+ {
+ if ( impl_hasHTMLHelpInstalled() && impl_showOfflineHelp(aHelpURL, pWeldWindow) )
+ {
+ return true;
+ }
+
+ if ( !impl_hasHelpInstalled() )
+ {
+ bool bShowOfflineHelpPopUp = officecfg::Office::Common::Help::BuiltInHelpNotInstalledPopUp::get();
+
+ TopLevelWindowLocker aBusy;
+
+ if(bShowOfflineHelpPopUp)
+ {
+ aBusy.incBusy(pWeldWindow);
+ HelpManualMessage aQueryBox(pWeldWindow);
+ short OnlineHelpBox = aQueryBox.run();
+ bShowOfflineHelpPopUp = OnlineHelpBox != RET_OK;
+ auto xChanges = comphelper::ConfigurationChanges::create();
+ officecfg::Office::Common::Help::BuiltInHelpNotInstalledPopUp::set(aQueryBox.GetOfflineHelpPopUp(), xChanges);
+ xChanges->commit();
+ aBusy.decBusy();
+ }
+ if(!bShowOfflineHelpPopUp)
+ {
+ if ( impl_showOnlineHelp(aHelpURL, pWeldWindow) )
+ return true;
+ else
+ {
+ aBusy.incBusy(pWeldWindow);
+ NoHelpErrorBox aErrBox(pWeldWindow);
+ aErrBox.run();
+ aBusy.decBusy();
+ return false;
+ }
+ }
+ else
+ {
+ return false;
+ }
+ }
+ }
+
+ // old-help to display
+ Reference < XDesktop2 > xDesktop = Desktop::create( ::comphelper::getProcessComponentContext() );
+
+ // check if help window is still open
+ // If not, create a new one and return access directly to the internal sub frame showing the help content
+ // search must be done here; search one desktop level could return an arbitrary frame
+ Reference< XFrame2 > xHelp(
+ xDesktop->findFrame( "OFFICE_HELP_TASK", FrameSearchFlag::CHILDREN),
+ UNO_QUERY);
+ Reference< XFrame > xHelpContent = xDesktop->findFrame(
+ "OFFICE_HELP",
+ FrameSearchFlag::CHILDREN);
+
+ SfxHelpWindow_Impl* pHelpWindow = nullptr;
+ if (!xHelp.is())
+ pHelpWindow = impl_createHelp(xHelp, xHelpContent);
+ else
+ pHelpWindow = static_cast<SfxHelpWindow_Impl*>(VCLUnoHelper::GetWindow(xHelp->getComponentWindow()));
+ if (!xHelp.is() || !xHelpContent.is() || !pHelpWindow)
+ return false;
+
+ SAL_INFO("sfx.appl", "HelpId = " << aHelpURL);
+
+ pHelpWindow->SetHelpURL( aHelpURL );
+ pHelpWindow->loadHelpContent(aHelpURL);
+
+ Reference < css::awt::XTopWindow > xTopWindow( xHelp->getContainerWindow(), UNO_QUERY );
+ if ( xTopWindow.is() )
+ xTopWindow->toFront();
+
+ return true;
+}
+
+bool SfxHelp::Start_Impl(const OUString& rURL, weld::Widget* pWidget, const OUString& rKeyword)
+{
+ OUStringBuffer aHelpRootURL("vnd.sun.star.help://");
+ AppendConfigToken(aHelpRootURL, true);
+ SfxContentHelper::GetResultSet(aHelpRootURL.makeStringAndClear());
+
+ /* rURL may be
+ * - a "real" URL
+ * - a HelpID (formerly a long, now a string)
+ * If rURL is a URL, CreateHelpURL should be called for this URL
+ * If rURL is an arbitrary string, the same should happen, but the URL should be tried out
+ * if it delivers real help content. In case only the Help Error Document is returned, the
+ * parent of the window for that help was called, is asked for its HelpID.
+ * For compatibility reasons this upward search is not implemented for "real" URLs.
+ * Help keyword search now is implemented as own method; in former versions it
+ * was done via Help::Start, but this implementation conflicted with the upward search.
+ */
+ OUString aHelpURL;
+ INetURLObject aParser( rURL );
+ INetProtocol nProtocol = aParser.GetProtocol();
+
+ switch ( nProtocol )
+ {
+ case INetProtocol::VndSunStarHelp:
+ // already a vnd.sun.star.help URL -> nothing to do
+ aHelpURL = rURL;
+ break;
+ default:
+ {
+ OUString aHelpModuleName(GetHelpModuleName_Impl(rURL));
+ OUString aRealCommand;
+
+ if ( nProtocol == INetProtocol::Uno )
+ {
+ // Command can be just an alias to another command.
+ auto aProperties = vcl::CommandInfoProvider::GetCommandProperties(rURL, getCurrentModuleIdentifier_Impl());
+ aRealCommand = vcl::CommandInfoProvider::GetRealCommandForCommand(aProperties);
+ }
+
+ // no URL, just a HelpID (maybe empty in case of keyword search)
+ aHelpURL = CreateHelpURL_Impl( aRealCommand.isEmpty() ? rURL : aRealCommand, aHelpModuleName );
+
+ if ( impl_hasHelpInstalled() && pWidget && SfxContentHelper::IsHelpErrorDocument( aHelpURL ) )
+ {
+ bool bUseFinalFallback = true;
+ // no help found -> try ids of parents.
+ pWidget->help_hierarchy_foreach([&aHelpModuleName, &aHelpURL, &bUseFinalFallback](const OString& rHelpId){
+ if (rHelpId.isEmpty())
+ return false;
+ aHelpURL = CreateHelpURL( OStringToOUString(rHelpId, RTL_TEXTENCODING_UTF8), aHelpModuleName);
+ bool bFinished = !SfxContentHelper::IsHelpErrorDocument(aHelpURL);
+ if (bFinished)
+ bUseFinalFallback = false;
+ return bFinished;
+ });
+
+ if (bUseFinalFallback)
+ {
+ // create help url of start page ( helpid == 0 -> start page)
+ aHelpURL = CreateHelpURL( OUString(), aHelpModuleName );
+ }
+ }
+ break;
+ }
+ }
+
+ if ( comphelper::LibreOfficeKit::isActive() )
+ {
+ impl_showOnlineHelp(aHelpURL, pWidget);
+ return true;
+ }
+#ifdef MACOSX
+ if (@available(macOS 10.14, *)) {
+ // Workaround: Safari sandboxing prevents it from accessing files in the LibreOffice.app folder
+ // force online-help instead if Safari is default browser.
+ CFURLRef pBrowser = LSCopyDefaultApplicationURLForURL(
+ CFURLCreateWithString(
+ kCFAllocatorDefault,
+ static_cast<CFStringRef>(@"https://www.libreoffice.org"),
+ nullptr),
+ kLSRolesAll, nullptr);
+ if([static_cast<NSString*>(CFURLGetString(pBrowser)) hasSuffix:@"/Applications/Safari.app/"]) {
+ impl_showOnlineHelp(aHelpURL, pWidget);
+ return true;
+ }
+ }
+#endif
+
+ // If the HTML or no help is installed, but aHelpURL nevertheless references valid help content,
+ // that implies that help content belongs to an extension (and thus would not be available
+ // in neither the offline nor online HTML help); in that case, fall through to the "old-help to
+ // display" code below:
+ if (SfxContentHelper::IsHelpErrorDocument(aHelpURL))
+ {
+ if ( impl_hasHTMLHelpInstalled() && impl_showOfflineHelp(aHelpURL, pWidget) )
+ {
+ return true;
+ }
+
+ if ( !impl_hasHelpInstalled() )
+ {
+ bool bShowOfflineHelpPopUp = officecfg::Office::Common::Help::BuiltInHelpNotInstalledPopUp::get();
+
+ TopLevelWindowLocker aBusy;
+
+ if(bShowOfflineHelpPopUp)
+ {
+ aBusy.incBusy(pWidget);
+ HelpManualMessage aQueryBox(pWidget);
+ short OnlineHelpBox = aQueryBox.run();
+ bShowOfflineHelpPopUp = OnlineHelpBox != RET_OK;
+ auto xChanges = comphelper::ConfigurationChanges::create();
+ officecfg::Office::Common::Help::BuiltInHelpNotInstalledPopUp::set(aQueryBox.GetOfflineHelpPopUp(), xChanges);
+ xChanges->commit();
+ aBusy.decBusy();
+ }
+ if(!bShowOfflineHelpPopUp)
+ {
+ if ( impl_showOnlineHelp(aHelpURL, pWidget) )
+ return true;
+ else
+ {
+ aBusy.incBusy(pWidget);
+ NoHelpErrorBox aErrBox(pWidget);
+ aErrBox.run();
+ aBusy.decBusy();
+ return false;
+ }
+ }
+ else
+ {
+ return false;
+ }
+
+ }
+ }
+
+ // old-help to display
+ Reference < XDesktop2 > xDesktop = Desktop::create( ::comphelper::getProcessComponentContext() );
+
+ // check if help window is still open
+ // If not, create a new one and return access directly to the internal sub frame showing the help content
+ // search must be done here; search one desktop level could return an arbitrary frame
+ Reference< XFrame2 > xHelp(
+ xDesktop->findFrame( "OFFICE_HELP_TASK", FrameSearchFlag::CHILDREN),
+ UNO_QUERY);
+ Reference< XFrame > xHelpContent = xDesktop->findFrame(
+ "OFFICE_HELP",
+ FrameSearchFlag::CHILDREN);
+
+ SfxHelpWindow_Impl* pHelpWindow = nullptr;
+ if (!xHelp.is())
+ pHelpWindow = impl_createHelp(xHelp, xHelpContent);
+ else
+ pHelpWindow = static_cast<SfxHelpWindow_Impl*>(VCLUnoHelper::GetWindow(xHelp->getComponentWindow()));
+ if (!xHelp.is() || !xHelpContent.is() || !pHelpWindow)
+ return false;
+
+ SAL_INFO("sfx.appl", "HelpId = " << aHelpURL);
+
+ pHelpWindow->SetHelpURL( aHelpURL );
+ pHelpWindow->loadHelpContent(aHelpURL);
+ if (!rKeyword.isEmpty())
+ pHelpWindow->OpenKeyword( rKeyword );
+
+ Reference < css::awt::XTopWindow > xTopWindow( xHelp->getContainerWindow(), UNO_QUERY );
+ if ( xTopWindow.is() )
+ xTopWindow->toFront();
+
+ return true;
+}
+
+OUString SfxHelp::CreateHelpURL(const OUString& aCommandURL, const OUString& rModuleName)
+{
+ SfxHelp* pHelp = static_cast< SfxHelp* >(Application::GetHelp());
+ return pHelp ? SfxHelp::CreateHelpURL_Impl( aCommandURL, rModuleName ) : OUString();
+}
+
+OUString SfxHelp::GetDefaultHelpModule()
+{
+ return getDefaultModule_Impl();
+}
+
+OUString SfxHelp::GetCurrentModuleIdentifier()
+{
+ return getCurrentModuleIdentifier_Impl();
+}
+
+bool SfxHelp::IsHelpInstalled()
+{
+ return impl_hasHelpInstalled();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/appl/sfxpicklist.cxx b/sfx2/source/appl/sfxpicklist.cxx
new file mode 100644
index 000000000..22c364669
--- /dev/null
+++ b/sfx2/source/appl/sfxpicklist.cxx
@@ -0,0 +1,226 @@
+/* -*- 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/lok.hxx>
+#include <comphelper/base64.hxx>
+#include <com/sun/star/document/XDocumentProperties.hpp>
+#include <unotools/historyoptions.hxx>
+#include <unotools/useroptions.hxx>
+#include <tools/datetime.hxx>
+#include <tools/urlobj.hxx>
+#include <svl/inethist.hxx>
+#include <vcl/pngwrite.hxx>
+#include <vcl/svapp.hxx>
+#include <officecfg/Office/Common.hxx>
+
+
+#include <sfx2/app.hxx>
+#include <sfxpicklist.hxx>
+#include <sfx2/sfxsids.hrc>
+#include <sfx2/event.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/docfile.hxx>
+#include <openurlhint.hxx>
+#include <sfx2/docfilt.hxx>
+#include <sfx2/viewfrm.hxx>
+
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::util;
+
+class SfxPickListImpl : public SfxListener
+{
+ /**
+ * Adds the given document to the pick list (recent documents) if it satisfies
+ certain requirements, e.g. being writable. Check implementation for requirement
+ details.
+ */
+ static void AddDocumentToPickList( const SfxObjectShell* pDocShell );
+
+public:
+ SfxPickListImpl(SfxApplication& rApp);
+ virtual void Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) override;
+};
+
+void SfxPickListImpl::AddDocumentToPickList( const SfxObjectShell* pDocSh )
+{
+ if (pDocSh->IsAvoidRecentDocs() || comphelper::LibreOfficeKit::isActive())
+ return;
+
+ SfxMedium *pMed = pDocSh->GetMedium();
+ if( !pMed )
+ return;
+
+ // Unnamed Documents and embedded-Documents not in Picklist
+ if ( !pDocSh->HasName() ||
+ SfxObjectCreateMode::STANDARD != pDocSh->GetCreateMode() )
+ return;
+
+ // Help not in History
+ INetURLObject aURL( pDocSh->IsDocShared() ? pDocSh->GetSharedFileURL() : pMed->GetOrigURL() );
+ if ( aURL.GetProtocol() == INetProtocol::VndSunStarHelp )
+ return;
+
+ // add no document that forbids this
+ if ( !pMed->IsUpdatePickList() )
+ return;
+
+ // ignore hidden documents
+ if ( !SfxViewFrame::GetFirst( pDocSh ) )
+ return;
+
+ OUString aTitle = pDocSh->GetTitle(SFX_TITLE_PICKLIST);
+ OUString aFilter;
+ std::shared_ptr<const SfxFilter> pFilter = pMed->GetFilter();
+ if ( pFilter )
+ aFilter = pFilter->GetFilterName();
+
+ std::optional<OUString> aThumbnail;
+
+ // generate the thumbnail
+ //fdo#74834: only generate thumbnail for history if the corresponding option is not disabled in the configuration
+ if (!pDocSh->IsModified() && !Application::IsHeadlessModeEnabled() &&
+ officecfg::Office::Common::History::RecentDocsThumbnail::get())
+ {
+ // not modified => the document matches what is in the shell
+ const SfxUnoAnyItem* pEncryptionDataItem = SfxItemSet::GetItem<SfxUnoAnyItem>(pMed->GetItemSet(), SID_ENCRYPTIONDATA, false);
+ if ( pEncryptionDataItem )
+ {
+ // encrypted document, will show a generic document icon instead
+ aThumbnail = OUString();
+ }
+ else
+ {
+ BitmapEx aResultBitmap = pDocSh->GetPreviewBitmap();
+ if (!aResultBitmap.IsEmpty())
+ {
+ SvMemoryStream aStream(65535, 65535);
+ vcl::PNGWriter aWriter(aResultBitmap);
+ if (aWriter.Write(aStream))
+ {
+ Sequence<sal_Int8> aSequence(static_cast<const sal_Int8*>(aStream.GetData()), aStream.Tell());
+ OUStringBuffer aBuffer;
+ ::comphelper::Base64::encode(aBuffer, aSequence);
+ aThumbnail = aBuffer.makeStringAndClear();
+ }
+ }
+ }
+ }
+ ::std::optional<bool> const oIsReadOnly(pMed->IsOriginallyLoadedReadOnly());
+
+ // add to svtool history options
+ SvtHistoryOptions::AppendItem( EHistoryType::PickList,
+ aURL.GetURLNoPass( INetURLObject::DecodeMechanism::NONE ),
+ aFilter,
+ aTitle,
+ aThumbnail,
+ oIsReadOnly);
+
+ if ( aURL.GetProtocol() == INetProtocol::File )
+ Application::AddToRecentDocumentList( aURL.GetURLNoPass( INetURLObject::DecodeMechanism::NONE ),
+ pFilter ? pFilter->GetMimeType() : OUString(),
+ pFilter ? pFilter->GetServiceName() : OUString() );
+}
+
+SfxPickList::SfxPickList(SfxApplication& rApp)
+ : mxImpl(new SfxPickListImpl(rApp))
+{
+}
+
+SfxPickList::~SfxPickList()
+{
+}
+
+SfxPickListImpl::SfxPickListImpl(SfxApplication& rApp)
+{
+ StartListening(rApp);
+}
+
+void SfxPickListImpl::Notify( SfxBroadcaster&, const SfxHint& rHint )
+{
+ const SfxOpenUrlHint* pOpenUrlHint = dynamic_cast<const SfxOpenUrlHint*>(&rHint);
+ if ( pOpenUrlHint )
+ {
+ INetURLHistory::GetOrCreate()->PutUrl( INetURLObject( pOpenUrlHint->GetDocumentURL() ));
+ }
+
+ const SfxEventHint* pEventHint = dynamic_cast<const SfxEventHint*>(&rHint);
+ if ( !pEventHint )
+ return;
+
+ // only ObjectShell-related events with media interest
+ SfxObjectShell* pDocSh = pEventHint->GetObjShell();
+ if( !pDocSh )
+ return;
+
+ switch ( pEventHint->GetEventId() )
+ {
+ case SfxEventHintId::CreateDoc:
+ {
+ bool bAllowModif = pDocSh->IsEnableSetModified();
+ if ( bAllowModif )
+ pDocSh->EnableSetModified( false );
+
+ using namespace ::com::sun::star;
+ uno::Reference<document::XDocumentProperties> xDocProps(
+ pDocSh->getDocProperties());
+ if (xDocProps.is()) {
+ xDocProps->setAuthor( SvtUserOptions().GetFullName() );
+ ::DateTime now( ::DateTime::SYSTEM );
+ xDocProps->setCreationDate( now.GetUNODateTime() );
+ }
+
+ if ( bAllowModif )
+ pDocSh->EnableSetModified( bAllowModif );
+ }
+ break;
+
+ case SfxEventHintId::OpenDoc:
+ case SfxEventHintId::SaveDocDone:
+ case SfxEventHintId::SaveAsDocDone:
+ case SfxEventHintId::SaveToDocDone:
+ case SfxEventHintId::CloseDoc:
+ {
+ AddDocumentToPickList(pDocSh);
+ }
+ break;
+
+ case SfxEventHintId::SaveAsDoc:
+ {
+ SfxMedium *pMedium = pDocSh->GetMedium();
+ if (!pMedium)
+ return;
+
+ // We're starting a "Save As". Add the current document (if it's
+ // not a "new" document) to the "Recent Documents" list before we
+ // switch to the new path.
+ // If the current document is new, path will be empty.
+ OUString path = pMedium->GetOrigURL();
+ if (!path.isEmpty())
+ {
+ AddDocumentToPickList(pDocSh);
+ }
+ }
+ break;
+ default: break;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/appl/shutdownicon.cxx b/sfx2/source/appl/shutdownicon.cxx
new file mode 100644
index 000000000..8c28c4fa1
--- /dev/null
+++ b/sfx2/source/appl/shutdownicon.cxx
@@ -0,0 +1,682 @@
+/* -*- 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 <sal/log.hxx>
+
+#include "shutdownicon.hxx"
+#include <sfx2/strings.hrc>
+#include <sfx2/app.hxx>
+#include <svtools/imagemgr.hxx>
+#include <com/sun/star/task/InteractionHandler.hpp>
+#include <com/sun/star/frame/Desktop.hpp>
+#include <com/sun/star/frame/TerminationVetoException.hpp>
+#include <com/sun/star/frame/XDispatchResultListener.hpp>
+#include <com/sun/star/frame/XNotifyingDispatch.hpp>
+#include <com/sun/star/frame/XFramesSupplier.hpp>
+#include <com/sun/star/frame/XFrame.hpp>
+#include <com/sun/star/util/URLTransformer.hpp>
+#include <com/sun/star/util/XURLTransformer.hpp>
+#include <com/sun/star/ui/dialogs/XFilePickerControlAccess.hpp>
+#include <com/sun/star/ui/dialogs/XFilePicker3.hpp>
+#include <com/sun/star/ui/dialogs/TemplateDescription.hpp>
+#include <com/sun/star/ui/dialogs/ExtendedFilePickerElementIds.hpp>
+#include <com/sun/star/ui/dialogs/CommonFilePickerElementIds.hpp>
+#include <com/sun/star/ui/dialogs/ControlActions.hpp>
+#include <com/sun/star/document/MacroExecMode.hpp>
+#include <com/sun/star/document/UpdateDocMode.hpp>
+#include <sfx2/filedlghelper.hxx>
+#include <sfx2/docfilt.hxx>
+#include <sfx2/fcontnr.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <comphelper/extract.hxx>
+#include <officecfg/Office/Common.hxx>
+#include <tools/urlobj.hxx>
+#include <tools/debug.hxx>
+#include <osl/file.hxx>
+#include <osl/module.hxx>
+#include <rtl/ref.hxx>
+#include <vcl/svapp.hxx>
+
+#include <sfx2/sfxresid.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::frame;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::util;
+using namespace ::com::sun::star::ui::dialogs;
+using namespace ::sfx2;
+
+class SfxNotificationListener_Impl : public cppu::WeakImplHelper< XDispatchResultListener >
+{
+public:
+ virtual void SAL_CALL dispatchFinished( const DispatchResultEvent& aEvent ) override;
+ virtual void SAL_CALL disposing( const EventObject& aEvent ) override;
+};
+
+void SAL_CALL SfxNotificationListener_Impl::dispatchFinished( const DispatchResultEvent& )
+{
+ ShutdownIcon::LeaveModalMode();
+}
+
+void SAL_CALL SfxNotificationListener_Impl::disposing( const EventObject& )
+{
+}
+
+OUString SAL_CALL ShutdownIcon::getImplementationName()
+{
+ return "com.sun.star.comp.desktop.QuickstartWrapper";
+}
+
+sal_Bool SAL_CALL ShutdownIcon::supportsService(OUString const & ServiceName)
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+css::uno::Sequence<OUString> SAL_CALL ShutdownIcon::getSupportedServiceNames()
+{
+ return { "com.sun.star.office.Quickstart" };
+}
+
+bool ShutdownIcon::bModalMode = false;
+rtl::Reference<ShutdownIcon> ShutdownIcon::pShutdownIcon;
+
+void ShutdownIcon::initSystray()
+{
+ if (m_bInitialized)
+ return;
+ m_bInitialized = true;
+
+#ifdef ENABLE_QUICKSTART_APPLET
+# ifdef _WIN32
+ win32_init_sys_tray();
+# elif defined MACOSX
+ aqua_init_systray();
+# endif // MACOSX
+#endif // ENABLE_QUICKSTART_APPLET
+}
+
+void ShutdownIcon::deInitSystray()
+{
+ if (!m_bInitialized)
+ return;
+
+#ifdef ENABLE_QUICKSTART_APPLET
+# ifdef _WIN32
+ win32_shutdown_sys_tray();
+# elif defined MACOSX
+ aqua_shutdown_systray();
+# endif // MACOSX
+#endif // ENABLE_QUICKSTART_APPLET
+
+ m_bVeto = false;
+
+ m_pFileDlg.reset();
+ m_bInitialized = false;
+}
+
+
+ShutdownIcon::ShutdownIcon( const css::uno::Reference< XComponentContext > & rxContext ) :
+ m_bVeto ( false ),
+ m_bListenForTermination ( false ),
+ m_bSystemDialogs( false ),
+ m_xContext( rxContext ),
+ m_bInitialized( false )
+{
+ m_bSystemDialogs = officecfg::Office::Common::Misc::UseSystemFileDialog::get();
+}
+
+ShutdownIcon::~ShutdownIcon()
+{
+ deInitSystray();
+}
+
+
+void ShutdownIcon::OpenURL( const OUString& aURL, const OUString& rTarget, const Sequence< PropertyValue >& aArgs )
+{
+ if ( !getInstance() || !getInstance()->m_xDesktop.is() )
+ return;
+
+ css::uno::Reference < XDispatchProvider > xDispatchProvider = getInstance()->m_xDesktop;
+ if ( !xDispatchProvider.is() )
+ return;
+
+ css::util::URL aDispatchURL;
+ aDispatchURL.Complete = aURL;
+
+ css::uno::Reference< util::XURLTransformer > xURLTransformer( util::URLTransformer::create( ::comphelper::getProcessComponentContext() ) );
+ try
+ {
+ css::uno::Reference< css::frame::XDispatch > xDispatch;
+
+ xURLTransformer->parseStrict( aDispatchURL );
+ xDispatch = xDispatchProvider->queryDispatch( aDispatchURL, rTarget, 0 );
+ if ( xDispatch.is() )
+ xDispatch->dispatch( aDispatchURL, aArgs );
+ }
+ catch ( css::uno::RuntimeException& )
+ {
+ throw;
+ }
+ catch ( css::uno::Exception& )
+ {
+ }
+}
+
+
+void ShutdownIcon::FileOpen()
+{
+ if ( getInstance() && getInstance()->m_xDesktop.is() )
+ {
+ ::SolarMutexGuard aGuard;
+ EnterModalMode();
+ getInstance()->StartFileDialog();
+ }
+}
+
+
+void ShutdownIcon::FromTemplate()
+{
+ if ( !getInstance() || !getInstance()->m_xDesktop.is() )
+ return;
+
+ css::uno::Reference < css::frame::XFramesSupplier > xDesktop = getInstance()->m_xDesktop;
+ css::uno::Reference < css::frame::XFrame > xFrame( xDesktop->getActiveFrame() );
+ if ( !xFrame.is() )
+ xFrame = xDesktop;
+
+ URL aTargetURL;
+ aTargetURL.Complete = ".uno:NewDoc";
+ css::uno::Reference< util::XURLTransformer > xTrans( util::URLTransformer::create( ::comphelper::getProcessComponentContext() ) );
+ xTrans->parseStrict( aTargetURL );
+
+ css::uno::Reference < css::frame::XDispatchProvider > xProv( xFrame, UNO_QUERY );
+ css::uno::Reference < css::frame::XDispatch > xDisp;
+ if ( xProv.is() )
+ {
+ xDisp = xProv->queryDispatch( aTargetURL, "_self", 0 );
+ }
+ if ( !xDisp.is() )
+ return;
+
+ Sequence<PropertyValue> aArgs { comphelper::makePropertyValue("Referer", OUString("private:user")) };
+ css::uno::Reference< css::frame::XNotifyingDispatch > xNotifier(xDisp, UNO_QUERY);
+ if (xNotifier.is())
+ {
+ EnterModalMode();
+ xNotifier->dispatchWithNotification(aTargetURL, aArgs, new SfxNotificationListener_Impl);
+ }
+ else
+ xDisp->dispatch( aTargetURL, aArgs );
+}
+
+OUString ShutdownIcon::GetUrlDescription( std::u16string_view aUrl )
+{
+ ::SolarMutexGuard aGuard;
+
+ return SvFileInformationManager::GetDescription( INetURLObject( aUrl ) );
+}
+
+void ShutdownIcon::StartFileDialog()
+{
+ ::SolarMutexGuard aGuard;
+
+ bool bDirty = ( m_bSystemDialogs != officecfg::Office::Common::Misc::UseSystemFileDialog::get() );
+
+ if ( m_pFileDlg && bDirty )
+ {
+ // Destroy instance as changing the system file dialog setting
+ // forces us to create a new FileDialogHelper instance!
+ m_pFileDlg.reset();
+ }
+
+ if ( !m_pFileDlg )
+ m_pFileDlg.reset( new FileDialogHelper(
+ ui::dialogs::TemplateDescription::FILEOPEN_READONLY_VERSION,
+ FileDialogFlags::MultiSelection, OUString(), SfxFilterFlags::NONE, SfxFilterFlags::NONE, nullptr ) );
+ m_pFileDlg->StartExecuteModal( LINK( this, ShutdownIcon, DialogClosedHdl_Impl ) );
+}
+
+IMPL_LINK( ShutdownIcon, DialogClosedHdl_Impl, FileDialogHelper*, /*unused*/, void )
+{
+ DBG_ASSERT( m_pFileDlg, "ShutdownIcon, DialogClosedHdl_Impl(): no file dialog" );
+
+ // use constructor for filling up filters automatically!
+ if ( ERRCODE_NONE == m_pFileDlg->GetError() )
+ {
+ css::uno::Reference< XFilePicker3 > xPicker = m_pFileDlg->GetFilePicker();
+
+ try
+ {
+
+ if ( xPicker.is() )
+ {
+
+ css::uno::Reference < XFilePickerControlAccess > xPickerControls ( xPicker, UNO_QUERY );
+
+ Sequence< OUString > sFiles = xPicker->getSelectedFiles();
+ int nFiles = sFiles.getLength();
+
+ css::uno::Reference < css::task::XInteractionHandler2 > xInteraction(
+ task::InteractionHandler::createWithParent(::comphelper::getProcessComponentContext(), nullptr) );
+
+ int nArgs=3;
+ Sequence< PropertyValue > aArgs{
+ comphelper::makePropertyValue("InteractionHandler", xInteraction),
+ comphelper::makePropertyValue("MacroExecutionMode", sal_Int16(css::document::MacroExecMode::USE_CONFIG)),
+ comphelper::makePropertyValue("UpdateDocMode", sal_Int16(css::document::UpdateDocMode::ACCORDING_TO_CONFIG))
+ };
+
+ // use the filedlghelper to get the current filter name,
+ // because it removes the extensions before you get the filter name.
+ OUString aFilterName( m_pFileDlg->GetCurrentFilter() );
+
+ if ( xPickerControls.is() )
+ {
+
+ // Set readonly flag
+
+ bool bReadOnly = false;
+
+
+ xPickerControls->getValue( ExtendedFilePickerElementIds::CHECKBOX_READONLY, 0 ) >>= bReadOnly;
+
+ // Only set property if readonly is set to TRUE
+
+ if ( bReadOnly )
+ {
+ aArgs.realloc( ++nArgs );
+ auto pArgs = aArgs.getArray();
+ pArgs[nArgs-1].Name = "ReadOnly";
+ pArgs[nArgs-1].Value <<= bReadOnly;
+ }
+
+ // Get version string
+
+ sal_Int32 iVersion = -1;
+
+ xPickerControls->getValue( ExtendedFilePickerElementIds::LISTBOX_VERSION, ControlActions::GET_SELECTED_ITEM_INDEX ) >>= iVersion;
+
+ if ( iVersion >= 0 )
+ {
+ sal_Int16 uVersion = static_cast<sal_Int16>(iVersion);
+
+ aArgs.realloc( ++nArgs );
+ auto pArgs = aArgs.getArray();
+ pArgs[nArgs-1].Name = "Version";
+ pArgs[nArgs-1].Value <<= uVersion;
+ }
+
+ // Retrieve the current filter
+
+ if ( aFilterName.isEmpty() )
+ xPickerControls->getValue( CommonFilePickerElementIds::LISTBOX_FILTER, ControlActions::GET_SELECTED_ITEM ) >>= aFilterName;
+
+ }
+
+
+ // Convert UI filter name to internal filter name
+
+ if ( !aFilterName.isEmpty() )
+ {
+ std::shared_ptr<const SfxFilter> pFilter = SfxGetpApp()->GetFilterMatcher().GetFilter4UIName( aFilterName, SfxFilterFlags::NONE, SfxFilterFlags::NOTINFILEDLG );
+
+ if ( pFilter )
+ {
+ aFilterName = pFilter->GetFilterName();
+
+ if ( !aFilterName.isEmpty() )
+ {
+ aArgs.realloc( ++nArgs );
+ auto pArgs = aArgs.getArray();
+ pArgs[nArgs-1].Name = "FilterName";
+ pArgs[nArgs-1].Value <<= aFilterName;
+ }
+ }
+ }
+
+ if ( 1 == nFiles )
+ OpenURL( sFiles[0], "_default", aArgs );
+ else
+ {
+ OUString aBaseDirURL = sFiles[0];
+ if ( !aBaseDirURL.isEmpty() && !aBaseDirURL.endsWith("/") )
+ aBaseDirURL += "/";
+
+ int iFiles;
+ for ( iFiles = 1; iFiles < nFiles; iFiles++ )
+ {
+ OUString aURL = aBaseDirURL + sFiles[iFiles];
+ OpenURL( aURL, "_default", aArgs );
+ }
+ }
+ }
+ }
+ catch ( ... )
+ {
+ }
+ }
+
+#ifdef _WIN32
+ // Destroy dialog to prevent problems with custom controls
+ // This fix is dependent on the dialog settings. Destroying the dialog here will
+ // crash the non-native dialog implementation! Therefore make this dependent on
+ // the settings.
+ if ( officecfg::Office::Common::Misc::UseSystemFileDialog::get() )
+ {
+ m_pFileDlg.reset();
+ }
+#endif
+
+ LeaveModalMode();
+}
+
+
+void ShutdownIcon::addTerminateListener()
+{
+ ShutdownIcon* pInst = getInstance();
+ if ( ! pInst)
+ return;
+
+ if (pInst->m_bListenForTermination)
+ return;
+
+ css::uno::Reference< XDesktop2 > xDesktop = pInst->m_xDesktop;
+ if ( ! xDesktop.is())
+ return;
+
+ xDesktop->addTerminateListener( pInst );
+ pInst->m_bListenForTermination = true;
+}
+
+
+void ShutdownIcon::terminateDesktop()
+{
+ ShutdownIcon* pInst = getInstance();
+ if ( ! pInst)
+ return;
+
+ css::uno::Reference< XDesktop2 > xDesktop = pInst->m_xDesktop;
+ if ( ! xDesktop.is())
+ return;
+
+ // always remove ourselves as listener
+ pInst->m_bListenForTermination = true;
+ xDesktop->removeTerminateListener( pInst );
+
+ // terminate desktop only if no tasks exist
+ css::uno::Reference< XIndexAccess > xTasks = xDesktop->getFrames();
+ if( xTasks.is() && xTasks->getCount() < 1 )
+ Application::Quit();
+
+ // remove the instance pointer
+ ShutdownIcon::pShutdownIcon = nullptr;
+}
+
+
+ShutdownIcon* ShutdownIcon::getInstance()
+{
+ OSL_ASSERT( pShutdownIcon );
+ return pShutdownIcon.get();
+}
+
+
+ShutdownIcon* ShutdownIcon::createInstance()
+{
+ if (pShutdownIcon)
+ return pShutdownIcon.get();
+
+ try {
+ rtl::Reference<ShutdownIcon> pIcon(new ShutdownIcon( comphelper::getProcessComponentContext() ));
+ pIcon->init ();
+ pShutdownIcon = pIcon;
+ } catch (...) {
+ }
+
+ return pShutdownIcon.get();
+}
+
+void ShutdownIcon::init()
+{
+ css::uno::Reference < XDesktop2 > xDesktop = Desktop::create( m_xContext );
+ std::unique_lock aGuard(m_aMutex);
+ m_xDesktop = xDesktop;
+}
+
+
+void ShutdownIcon::disposing(std::unique_lock<std::mutex>&)
+{
+ m_xContext.clear();
+ m_xDesktop.clear();
+
+ deInitSystray();
+}
+
+
+// XEventListener
+void SAL_CALL ShutdownIcon::disposing( const css::lang::EventObject& )
+{
+}
+
+
+// XTerminateListener
+void SAL_CALL ShutdownIcon::queryTermination( const css::lang::EventObject& )
+{
+ SAL_INFO("sfx.appl", "ShutdownIcon::queryTermination: veto is " << m_bVeto);
+ std::unique_lock aGuard( m_aMutex );
+
+ if ( m_bVeto )
+ throw css::frame::TerminationVetoException();
+}
+
+
+void SAL_CALL ShutdownIcon::notifyTermination( const css::lang::EventObject& )
+{
+}
+
+
+void SAL_CALL ShutdownIcon::initialize( const css::uno::Sequence< css::uno::Any>& aArguments )
+{
+ std::unique_lock aGuard( m_aMutex );
+
+ // third argument only sets veto, everything else will be ignored!
+ if (aArguments.getLength() > 2)
+ {
+ bool bVeto = ::cppu::any2bool(aArguments[2]);
+ m_bVeto = bVeto;
+ return;
+ }
+
+ if ( aArguments.getLength() > 0 )
+ {
+ if ( !ShutdownIcon::pShutdownIcon )
+ {
+ try
+ {
+ bool bQuickstart = ::cppu::any2bool( aArguments[0] );
+ if( !bQuickstart && !GetAutostart() )
+ return;
+ aGuard.unlock();
+ init ();
+ aGuard.lock();
+ if ( !m_xDesktop.is() )
+ return;
+
+ /* Create a sub-classed instance - foo */
+ ShutdownIcon::pShutdownIcon = this;
+ initSystray();
+ }
+ catch(const css::lang::IllegalArgumentException&)
+ {
+ }
+ }
+ }
+ if ( aArguments.getLength() > 1 )
+ {
+ bool bAutostart = ::cppu::any2bool( aArguments[1] );
+ if (bAutostart && !GetAutostart())
+ SetAutostart( true );
+ if (!bAutostart && GetAutostart())
+ SetAutostart( false );
+ }
+
+}
+
+
+void ShutdownIcon::EnterModalMode()
+{
+ bModalMode = true;
+}
+
+
+void ShutdownIcon::LeaveModalMode()
+{
+ bModalMode = false;
+}
+
+#ifdef _WIN32
+// defined in shutdowniconw32.cxx
+#elif defined MACOSX
+// defined in shutdowniconaqua.cxx
+#else
+bool ShutdownIcon::IsQuickstarterInstalled()
+{
+ return false;
+}
+#endif
+
+
+#ifdef ENABLE_QUICKSTART_APPLET
+#ifdef _WIN32
+OUString ShutdownIcon::getShortcutName()
+{
+ return GetAutostartFolderNameW32() + "\\" + SfxResId(STR_QUICKSTART_LNKNAME) + ".lnk";
+}
+#endif // _WIN32
+#endif
+
+bool ShutdownIcon::GetAutostart( )
+{
+#if defined MACOSX
+ return true;
+#elif defined ENABLE_QUICKSTART_APPLET
+ bool bRet = false;
+ OUString aShortcut( getShortcutName() );
+ OUString aShortcutUrl;
+ osl::File::getFileURLFromSystemPath( aShortcut, aShortcutUrl );
+ osl::File f( aShortcutUrl );
+ osl::File::RC error = f.open( osl_File_OpenFlag_Read );
+ if( error == osl::File::E_None )
+ {
+ f.close();
+ bRet = true;
+ }
+ return bRet;
+#else // ENABLE_QUICKSTART_APPLET
+ return false;
+#endif
+}
+
+void ShutdownIcon::SetAutostart( bool bActivate )
+{
+#ifdef ENABLE_QUICKSTART_APPLET
+#ifndef MACOSX
+ OUString aShortcut( getShortcutName() );
+#endif
+
+ if( bActivate && IsQuickstarterInstalled() )
+ {
+#ifdef _WIN32
+ EnableAutostartW32( aShortcut );
+#endif
+ }
+ else
+ {
+#ifndef MACOSX
+ OUString aShortcutUrl;
+ ::osl::File::getFileURLFromSystemPath( aShortcut, aShortcutUrl );
+ ::osl::File::remove( aShortcutUrl );
+#endif
+ }
+#else
+ (void)bActivate; // unused variable
+#endif // ENABLE_QUICKSTART_APPLET
+}
+
+const ::sal_Int32 PROPHANDLE_TERMINATEVETOSTATE = 0;
+
+// XFastPropertySet
+void SAL_CALL ShutdownIcon::setFastPropertyValue( ::sal_Int32 nHandle,
+ const css::uno::Any& aValue )
+{
+ switch(nHandle)
+ {
+ case PROPHANDLE_TERMINATEVETOSTATE :
+ {
+ // use new value in case it's a valid information only
+ bool bState( false );
+ if (! (aValue >>= bState))
+ return;
+
+ m_bVeto = bState;
+ if (m_bVeto && ! m_bListenForTermination)
+ addTerminateListener();
+ }
+ break;
+
+ default :
+ throw css::beans::UnknownPropertyException(OUString::number(nHandle));
+ }
+}
+
+// XFastPropertySet
+css::uno::Any SAL_CALL ShutdownIcon::getFastPropertyValue( ::sal_Int32 nHandle )
+{
+ css::uno::Any aValue;
+ switch(nHandle)
+ {
+ case PROPHANDLE_TERMINATEVETOSTATE :
+ {
+ bool bState = (m_bListenForTermination && m_bVeto);
+ aValue <<= bState;
+ }
+ break;
+
+ default :
+ throw css::beans::UnknownPropertyException(OUString::number(nHandle));
+ }
+
+ return aValue;
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_desktop_QuickstartWrapper_get_implementation(
+ css::uno::XComponentContext *context,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new ShutdownIcon(context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/appl/shutdownicon.hxx b/sfx2/source/appl/shutdownicon.hxx
new file mode 100644
index 000000000..51c778e1d
--- /dev/null
+++ b/sfx2/source/appl/shutdownicon.hxx
@@ -0,0 +1,159 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SFX2_SOURCE_APPL_SHUTDOWNICON_HXX
+#define INCLUDED_SFX2_SOURCE_APPL_SHUTDOWNICON_HXX
+
+#include <com/sun/star/frame/XTerminateListener.hpp>
+#include <com/sun/star/frame/XDesktop2.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/lang/XInitialization.hpp>
+#include <com/sun/star/beans/XFastPropertySet.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <rtl/ustring.hxx>
+#include <rtl/ref.hxx>
+#include <comphelper/compbase.hxx>
+#include <tools/link.hxx>
+#include <memory>
+
+extern "C" {
+
+void SAL_DLLPUBLIC_EXPORT plugin_init_sys_tray();
+void SAL_DLLPUBLIC_EXPORT plugin_shutdown_sys_tray();
+
+}
+
+namespace sfx2
+{
+ class FileDialogHelper;
+}
+
+typedef comphelper::WeakComponentImplHelper<
+ css::lang::XInitialization,
+ css::frame::XTerminateListener,
+ css::lang::XServiceInfo,
+ css::beans::XFastPropertySet > ShutdownIconServiceBase;
+
+inline constexpr OUStringLiteral WRITER_URL = u"private:factory/swriter";
+inline constexpr OUStringLiteral CALC_URL = u"private:factory/scalc";
+inline constexpr OUStringLiteral IMPRESS_URL = u"private:factory/simpress";
+inline constexpr OUStringLiteral IMPRESS_WIZARD_URL = u"private:factory/simpress?slot=6686";
+inline constexpr OUStringLiteral DRAW_URL = u"private:factory/sdraw";
+inline constexpr OUStringLiteral MATH_URL = u"private:factory/smath";
+inline constexpr OUStringLiteral BASE_URL = u"private:factory/sdatabase?Interactive";
+inline constexpr OUStringLiteral STARTMODULE_URL = u".uno:ShowStartModule";
+
+class ShutdownIcon : public ShutdownIconServiceBase
+{
+ bool m_bVeto;
+ bool m_bListenForTermination;
+ bool m_bSystemDialogs;
+ std::unique_ptr<sfx2::FileDialogHelper> m_pFileDlg;
+ css::uno::Reference< css::uno::XComponentContext > m_xContext;
+
+ static rtl::Reference<ShutdownIcon> pShutdownIcon; // one instance
+
+ bool m_bInitialized;
+ void initSystray();
+ void deInitSystray();
+
+ static void EnterModalMode();
+ static void LeaveModalMode();
+ static OUString getShortcutName();
+
+ friend class SfxNotificationListener_Impl;
+
+ public:
+ explicit ShutdownIcon( const css::uno::Reference< css::uno::XComponentContext > & rxContext );
+
+ virtual ~ShutdownIcon() override;
+
+ virtual OUString SAL_CALL getImplementationName() override;
+
+ virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override;
+
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+
+ static ShutdownIcon* getInstance();
+ static ShutdownIcon* createInstance();
+
+ static void terminateDesktop();
+ static void addTerminateListener();
+
+ static void FileOpen();
+ static void OpenURL( const OUString& aURL, const OUString& rTarget, const css::uno::Sequence< css::beans::PropertyValue >& =
+ css::uno::Sequence< css::beans::PropertyValue >( 0 ) );
+ static void FromTemplate();
+
+ static void SetAutostart( bool bActivate );
+ static bool GetAutostart();
+ static bool bModalMode;
+
+ /// @throws css::uno::Exception
+ void init();
+
+ static OUString GetUrlDescription( std::u16string_view aUrl );
+
+ void SetVeto( bool bVeto ) { m_bVeto = bVeto;}
+
+ void StartFileDialog();
+ DECL_LINK(DialogClosedHdl_Impl, sfx2::FileDialogHelper*, void);
+
+ static bool IsQuickstarterInstalled();
+
+ // Component Helper - force override
+ virtual void disposing(std::unique_lock<std::mutex>&) override;
+
+ // XEventListener
+ virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override;
+
+ // XTerminateListener
+ virtual void SAL_CALL queryTermination( const css::lang::EventObject& aEvent ) override;
+ virtual void SAL_CALL notifyTermination( const css::lang::EventObject& aEvent ) override;
+
+ // XInitialization
+ virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override;
+
+ // XFastPropertySet
+ virtual void SAL_CALL setFastPropertyValue( ::sal_Int32 nHandle,
+ const css::uno::Any& aValue ) override;
+ virtual css::uno::Any SAL_CALL getFastPropertyValue( ::sal_Int32 nHandle ) override;
+
+ css::uno::Reference< css::frame::XDesktop2 > m_xDesktop;
+
+#ifdef _WIN32
+ static void EnableAutostartW32( const OUString &aShortcutName );
+ static OUString GetAutostartFolderNameW32();
+#endif
+};
+
+extern "C" {
+# ifdef _WIN32
+ // builtin win32 systray
+ void win32_init_sys_tray();
+ void win32_shutdown_sys_tray();
+# elif defined MACOSX
+ void aqua_init_systray();
+ void aqua_shutdown_systray();
+# endif
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/appl/shutdowniconaqua.mm b/sfx2/source/appl/shutdowniconaqua.mm
new file mode 100644
index 000000000..1a692f7ed
--- /dev/null
+++ b/sfx2/source/appl/shutdowniconaqua.mm
@@ -0,0 +1,486 @@
+/* -*- 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 <unotools/moduleoptions.hxx>
+#include <unotools/dynamicmenuoptions.hxx>
+#include <unotools/historyoptions.hxx>
+#include <rtl/ustring.hxx>
+#include <tools/urlobj.hxx>
+#include <osl/file.h>
+#include <osl/diagnose.h>
+#include <comphelper/sequenceashashmap.hxx>
+#include <sfx2/app.hxx>
+#include <sal/macros.h>
+#include <sfx2/sfxresid.hxx>
+#include <sfx2/strings.hrc>
+#include <vcl/svapp.hxx>
+#include "shutdownicon.hxx"
+
+#include <com/sun/star/util/XStringWidth.hpp>
+
+#include <cppuhelper/implbase.hxx>
+
+#include <set>
+#include <vector>
+
+#include <premac.h>
+#include <objc/objc-runtime.h>
+#include <Cocoa/Cocoa.h>
+#include <postmac.h>
+
+#define MI_OPEN 1
+#define MI_WRITER 2
+#define MI_CALC 3
+#define MI_IMPRESS 4
+#define MI_DRAW 5
+#define MI_BASE 6
+#define MI_MATH 7
+#define MI_TEMPLATE 8
+#define MI_STARTMODULE 9
+
+@interface QSMenuExecute : NSObject
+{
+}
+-(void)executeMenuItem: (NSMenuItem*)pItem;
+-(void)dockIconClicked: (NSObject*)pSender;
+@end
+
+@implementation QSMenuExecute
+-(void)executeMenuItem: (NSMenuItem*)pItem
+{
+ switch( [pItem tag] )
+ {
+ case MI_OPEN:
+ ShutdownIcon::FileOpen();
+ break;
+ case MI_WRITER:
+ ShutdownIcon::OpenURL( WRITER_URL, "_default" );
+ break;
+ case MI_CALC:
+ ShutdownIcon::OpenURL( CALC_URL, "_default" );
+ break;
+ case MI_IMPRESS:
+ ShutdownIcon::OpenURL( IMPRESS_URL, "_default" );
+ break;
+ case MI_DRAW:
+ ShutdownIcon::OpenURL( DRAW_URL, "_default" );
+ break;
+ case MI_BASE:
+ ShutdownIcon::OpenURL( BASE_URL, "_default" );
+ break;
+ case MI_MATH:
+ ShutdownIcon::OpenURL( MATH_URL, "_default" );
+ break;
+ case MI_TEMPLATE:
+ ShutdownIcon::FromTemplate();
+ break;
+ case MI_STARTMODULE:
+ ShutdownIcon::OpenURL( STARTMODULE_URL, "_default" );
+ break;
+ default:
+ break;
+ }
+}
+
+-(void)dockIconClicked: (NSObject*)pSender
+{
+ (void)pSender;
+ // start module
+ ShutdownIcon::OpenURL( STARTMODULE_URL, "_default" );
+}
+
+@end
+
+bool ShutdownIcon::IsQuickstarterInstalled()
+{
+ return true;
+}
+
+static NSMenuItem* pDefMenu = nil, *pDockSubMenu = nil;
+static QSMenuExecute* pExecute = nil;
+
+static std::set< OUString > aShortcuts;
+
+static NSString* getAutoreleasedString( const OUString& rStr )
+{
+ return [[[NSString alloc] initWithCharacters: reinterpret_cast<unichar const *>(rStr.getStr()) length: rStr.getLength()] autorelease];
+}
+
+namespace {
+
+struct RecentMenuEntry
+{
+ OUString aURL;
+ OUString aFilter;
+ OUString aTitle;
+ OUString aPassword;
+};
+
+class RecentFilesStringLength : public ::cppu::WeakImplHelper< css::util::XStringWidth >
+{
+ public:
+ RecentFilesStringLength() {}
+
+ // XStringWidth
+ sal_Int32 SAL_CALL queryStringWidth( const OUString& aString ) override
+ {
+ return aString.getLength();
+ }
+};
+
+}
+
+@interface RecentMenuDelegate : NSObject <NSMenuDelegate>
+{
+ std::vector< RecentMenuEntry >* m_pRecentFilesItems;
+}
+-(id)init;
+-(void)dealloc;
+-(void)menuNeedsUpdate:(NSMenu *)menu;
+-(void)executeRecentEntry: (NSMenuItem*)item;
+@end
+
+@implementation RecentMenuDelegate
+-(id)init
+{
+ if( (self = [super init]) )
+ {
+ m_pRecentFilesItems = new std::vector< RecentMenuEntry >();
+ }
+ return self;
+}
+
+-(void)dealloc
+{
+ delete m_pRecentFilesItems;
+ [super dealloc];
+}
+
+-(void)menuNeedsUpdate:(NSMenu *)menu
+{
+ // clear menu
+ int nItems = [menu numberOfItems];
+ while( nItems -- )
+ [menu removeItemAtIndex: 0];
+
+ // update recent item list
+ std::vector< SvtHistoryOptions::HistoryItem > aHistoryList( SvtHistoryOptions::GetList( EHistoryType::PickList ) );
+
+ int nPickListMenuItems = ( aHistoryList.size() > 99 ) ? 99 : aHistoryList.size();
+
+ m_pRecentFilesItems->clear();
+ if( nPickListMenuItems > 0 )
+ {
+ for ( int i = 0; i < nPickListMenuItems; i++ )
+ {
+ const SvtHistoryOptions::HistoryItem & rPickListEntry = aHistoryList[i];
+ RecentMenuEntry aRecentFile;
+ aRecentFile.aURL = rPickListEntry.sURL;
+ aRecentFile.aFilter = rPickListEntry.sFilter;
+ aRecentFile.aTitle = rPickListEntry.sTitle;
+ aRecentFile.aPassword = rPickListEntry.sPassword;
+ m_pRecentFilesItems->push_back( aRecentFile );
+ }
+ }
+
+ // insert new recent items
+ for ( std::vector<RecentMenuEntry>::size_type i = 0; i < m_pRecentFilesItems->size(); i++ )
+ {
+ OUString aMenuTitle;
+ INetURLObject aURL( (*m_pRecentFilesItems)[i].aURL );
+
+ if ( aURL.GetProtocol() == INetProtocol::File )
+ {
+ // Do handle file URL differently => convert it to a system
+ // path and abbreviate it with a special function:
+ OUString aSystemPath( aURL.getFSysPath( FSysStyle::Detect ) );
+ OUString aCompactedSystemPath;
+
+ oslFileError nError = osl_abbreviateSystemPath( aSystemPath.pData, &aCompactedSystemPath.pData, 46, nullptr );
+ if ( !nError )
+ aMenuTitle = aCompactedSystemPath;
+ else
+ aMenuTitle = aSystemPath;
+ }
+ else
+ {
+ // Use INetURLObject to abbreviate all other URLs
+ css::uno::Reference< css::util::XStringWidth > xStringLength( new RecentFilesStringLength() );
+ aMenuTitle = aURL.getAbbreviated( xStringLength, 46, INetURLObject::DecodeMechanism::Unambiguous );
+ }
+
+ NSMenuItem* pNewItem = [[NSMenuItem alloc] initWithTitle: getAutoreleasedString( aMenuTitle )
+ action: @selector(executeRecentEntry:)
+ keyEquivalent: @""];
+ [pNewItem setTag: i];
+ [pNewItem setTarget: self];
+ [pNewItem setEnabled: YES];
+ [menu addItem: pNewItem];
+ [pNewItem autorelease];
+ }
+}
+
+-(void)executeRecentEntry: (NSMenuItem*)item
+{
+ sal_Int32 nIndex = [item tag];
+ if( ( nIndex >= 0 ) && ( nIndex < static_cast<sal_Int32>( m_pRecentFilesItems->size() ) ) )
+ {
+ const RecentMenuEntry& rRecentFile = (*m_pRecentFilesItems)[ nIndex ];
+ int NUM_OF_PICKLIST_ARGS = 3;
+ css::uno::Sequence< css::beans::PropertyValue > aArgsList( NUM_OF_PICKLIST_ARGS );
+ css::beans::PropertyValue* pArgsList = aArgsList.getArray();
+
+ pArgsList[0].Name = "Referer";
+ pArgsList[0].Value <<= OUString( "private:user" );
+
+ // documents in the picklist will never be opened as templates
+ pArgsList[1].Name = "AsTemplate";
+ pArgsList[1].Value <<= false;
+
+ OUString aFilter( rRecentFile.aFilter );
+ sal_Int32 nPos = aFilter.indexOf( '|' );
+ if ( nPos >= 0 )
+ {
+ OUString aFilterOptions;
+
+ if ( nPos < ( aFilter.getLength() - 1 ) )
+ aFilterOptions = aFilter.copy( nPos+1 );
+
+ pArgsList[2].Name = "FilterOptions";
+ pArgsList[2].Value <<= aFilterOptions;
+
+ aFilter = aFilter.copy( 0, nPos-1 );
+ aArgsList.realloc( ++NUM_OF_PICKLIST_ARGS );
+ pArgsList = aArgsList.getArray();
+ }
+
+ pArgsList[NUM_OF_PICKLIST_ARGS-1].Name = "FilterName";
+ pArgsList[NUM_OF_PICKLIST_ARGS-1].Value <<= aFilter;
+
+ ShutdownIcon::OpenURL( rRecentFile.aURL, "_default", aArgsList );
+ }
+}
+@end
+
+static RecentMenuDelegate* pRecentDelegate = nil;
+
+static OUString getShortCut( const OUString& i_rTitle )
+{
+ // create shortcut
+ OUString aKeyEquiv;
+ for( sal_Int32 nIndex = 0; nIndex < i_rTitle.getLength(); nIndex++ )
+ {
+ OUString aShortcut( i_rTitle.copy( nIndex, 1 ).toAsciiLowerCase() );
+ if( aShortcuts.find( aShortcut ) == aShortcuts.end() )
+ {
+ aShortcuts.insert( aShortcut );
+ aKeyEquiv = aShortcut;
+ break;
+ }
+ }
+
+ return aKeyEquiv;
+}
+
+static void appendMenuItem( NSMenu* i_pMenu, NSMenu* i_pDockMenu, const OUString& i_rTitle, int i_nTag, const OUString& i_rKeyEquiv )
+{
+ if( ! i_rTitle.getLength() )
+ return;
+
+ NSMenuItem* pItem = [[NSMenuItem alloc] initWithTitle: getAutoreleasedString( i_rTitle )
+ action: @selector(executeMenuItem:)
+ keyEquivalent: (i_rKeyEquiv.getLength() ? getAutoreleasedString( i_rKeyEquiv ) : @"")
+ ];
+ [pItem setTag: i_nTag];
+ [pItem setTarget: pExecute];
+ [pItem setEnabled: YES];
+ [i_pMenu addItem: pItem];
+
+ if( i_pDockMenu )
+ {
+ // create a similar entry in the dock menu
+ pItem = [[NSMenuItem alloc] initWithTitle: getAutoreleasedString( i_rTitle )
+ action: @selector(executeMenuItem:)
+ keyEquivalent: @""
+ ];
+ [pItem setTag: i_nTag];
+ [pItem setTarget: pExecute];
+ [pItem setEnabled: YES];
+ [i_pDockMenu addItem: pItem];
+ }
+}
+
+static void appendRecentMenu( NSMenu* i_pMenu, NSMenu* i_pDockMenu, const OUString& i_rTitle )
+{
+ if( ! pRecentDelegate )
+ pRecentDelegate = [[RecentMenuDelegate alloc] init];
+
+ NSMenuItem* pItem = [i_pMenu addItemWithTitle: getAutoreleasedString( i_rTitle )
+ action: @selector(executeMenuItem:)
+ keyEquivalent: @""
+ ];
+ [pItem setEnabled: YES];
+ NSMenu* pRecentMenu = [[NSMenu alloc] initWithTitle: getAutoreleasedString( i_rTitle ) ];
+
+ [pRecentMenu setDelegate: pRecentDelegate];
+
+ [pRecentMenu setAutoenablesItems: NO];
+ [pItem setSubmenu: pRecentMenu];
+
+ if( i_pDockMenu )
+ {
+ // create a similar entry in the dock menu
+ pItem = [i_pDockMenu addItemWithTitle: getAutoreleasedString( i_rTitle )
+ action: @selector(executeMenuItem:)
+ keyEquivalent: @""
+ ];
+ [pItem setEnabled: YES];
+ pRecentMenu = [[NSMenu alloc] initWithTitle: getAutoreleasedString( i_rTitle ) ];
+
+ [pRecentMenu setDelegate: pRecentDelegate];
+
+ [pRecentMenu setAutoenablesItems: NO];
+ [pItem setSubmenu: pRecentMenu];
+ }
+}
+
+
+extern "C"
+{
+
+void aqua_init_systray()
+{
+ SolarMutexGuard aGuard;
+
+ ShutdownIcon *pShutdownIcon = ShutdownIcon::getInstance();
+ if( ! pShutdownIcon )
+ return;
+
+ // disable shutdown
+ pShutdownIcon->SetVeto( true );
+ ShutdownIcon::addTerminateListener();
+
+ if( ! pDefMenu )
+ {
+ if( [NSApp respondsToSelector: @selector(addFallbackMenuItem:)] )
+ {
+ aShortcuts.clear();
+
+ pExecute = [[QSMenuExecute alloc] init];
+ pDefMenu = [[NSMenuItem alloc] initWithTitle: getAutoreleasedString( SfxResId(STR_QUICKSTART_FILE) ) action: nullptr keyEquivalent: @""];
+ pDockSubMenu = [[NSMenuItem alloc] initWithTitle: getAutoreleasedString( SfxResId(STR_QUICKSTART_FILE) ) action: nullptr keyEquivalent: @""];
+ NSMenu* pMenu = [[NSMenu alloc] initWithTitle: getAutoreleasedString( SfxResId(STR_QUICKSTART_FILE) )];
+ [pMenu setAutoenablesItems: NO];
+ NSMenu* pDockMenu = [[NSMenu alloc] initWithTitle: getAutoreleasedString( SfxResId(STR_QUICKSTART_FILE) )];
+ [pDockMenu setAutoenablesItems: NO];
+
+ // collect the URLs of the entries in the File/New menu
+ SvtModuleOptions aModuleOptions;
+ std::set< OUString > aFileNewAppsAvailable;
+ std::vector < SvtDynMenuEntry > const aNewMenu = SvtDynamicMenuOptions::GetMenu( EDynamicMenuType::NewMenu );
+
+ for ( SvtDynMenuEntry const & newMenuProp : aNewMenu )
+ {
+ if ( !newMenuProp.sURL.isEmpty() )
+ aFileNewAppsAvailable.insert( newMenuProp.sURL );
+ }
+
+ // describe the menu entries for launching the applications
+ struct MenuEntryDescriptor
+ {
+ SvtModuleOptions::EModule eModuleIdentifier;
+ int nMenuTag;
+ rtl::OUStringConstExpr sURLDescription;
+ } static const aMenuItems[] =
+ {
+ { SvtModuleOptions::EModule::WRITER, MI_WRITER, WRITER_URL },
+ { SvtModuleOptions::EModule::CALC, MI_CALC, CALC_URL },
+ { SvtModuleOptions::EModule::IMPRESS, MI_IMPRESS, IMPRESS_WIZARD_URL },
+ { SvtModuleOptions::EModule::DRAW, MI_DRAW, DRAW_URL },
+ { SvtModuleOptions::EModule::DATABASE, MI_BASE, BASE_URL },
+ { SvtModuleOptions::EModule::MATH, MI_MATH, MATH_URL }
+ };
+
+ // insert entry for startcenter
+ if( aModuleOptions.IsModuleInstalled( SvtModuleOptions::EModule::STARTMODULE ) )
+ {
+ appendMenuItem( pMenu, nil, SfxResId(STR_QUICKSTART_STARTCENTER), MI_STARTMODULE, "n" );
+ if( [NSApp respondsToSelector: @selector(setDockIconClickHandler:)] )
+ [NSApp performSelector:@selector(setDockIconClickHandler:) withObject: pExecute];
+ else
+ OSL_FAIL( "setDockIconClickHandler selector failed on NSApp" );
+
+ }
+
+ // insert the menu entries for launching the applications
+ for ( size_t i = 0; i < SAL_N_ELEMENTS( aMenuItems ); ++i )
+ {
+ if ( !aModuleOptions.IsModuleInstalled( aMenuItems[i].eModuleIdentifier ) )
+ // the complete application is not even installed
+ continue;
+
+ const OUString& sURL( aMenuItems[i].sURLDescription );
+
+ if ( aFileNewAppsAvailable.find( sURL ) == aFileNewAppsAvailable.end() )
+ // the application is installed, but the entry has been configured to *not* appear in the File/New
+ // menu => also let not appear it in the quickstarter
+ continue;
+
+ OUString aKeyEquiv( getShortCut( ShutdownIcon::GetUrlDescription( sURL ) ) );
+
+ appendMenuItem( pMenu, pDockMenu, ShutdownIcon::GetUrlDescription( sURL ), aMenuItems[i].nMenuTag, aKeyEquiv );
+ }
+
+ // insert the remaining menu entries
+
+ // add recent menu
+ appendRecentMenu( pMenu, pDockMenu, SfxResId(STR_QUICKSTART_RECENTDOC) );
+
+ OUString aTitle( SfxResId(STR_QUICKSTART_FROMTEMPLATE) );
+ OUString aKeyEquiv( getShortCut( aTitle ) );
+ appendMenuItem( pMenu, pDockMenu, aTitle, MI_TEMPLATE, aKeyEquiv );
+ aTitle = SfxResId(STR_QUICKSTART_FILEOPEN);
+ aKeyEquiv = getShortCut( aTitle );
+ appendMenuItem( pMenu, pDockMenu, aTitle, MI_OPEN, aKeyEquiv );
+
+ [pDefMenu setSubmenu: pMenu];
+ [NSApp performSelector:@selector(addFallbackMenuItem:) withObject: pDefMenu];
+
+ if( [NSApp respondsToSelector: @selector(addDockMenuItem:)] )
+ {
+ [pDockSubMenu setSubmenu: pDockMenu];
+ // add the submenu
+ [NSApp performSelector:@selector(addDockMenuItem:) withObject: pDockSubMenu];
+ }
+ else
+ OSL_FAIL( "addDockMenuItem selector failed on NSApp" );
+ }
+ else
+ OSL_FAIL( "addFallbackMenuItem selector failed on NSApp" );
+ }
+}
+
+void SAL_DLLPUBLIC_EXPORT aqua_shutdown_systray()
+{
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/appl/shutdowniconw32.cxx b/sfx2/source/appl/shutdowniconw32.cxx
new file mode 100644
index 000000000..2580a8bd1
--- /dev/null
+++ b/sfx2/source/appl/shutdowniconw32.cxx
@@ -0,0 +1,803 @@
+/*
+ * 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 <sal/log.hxx>
+
+#include <unotools/moduleoptions.hxx>
+#include <unotools/dynamicmenuoptions.hxx>
+
+#undef WB_LEFT
+#undef WB_RIGHT
+
+#include "shutdownicon.hxx"
+#include <sfx2/sfxresid.hxx>
+#include <sfx2/strings.hrc>
+#include <shlobj.h>
+#include <objidl.h>
+#include <osl/diagnose.h>
+#include <osl/thread.h>
+#include <systools/win32/qswin32.h>
+#include <comphelper/sequenceashashmap.hxx>
+#include <comphelper/windowserrorstring.hxx>
+#include <o3tl/char16_t2wchar_t.hxx>
+
+#include <set>
+
+
+#define EXECUTER_WINDOWCLASS L"SO Executer Class"
+#define EXECUTER_WINDOWNAME L"SO Executer Window"
+
+
+#define ID_QUICKSTART 1
+#define IDM_EXIT 2
+#define IDM_OPEN 3
+#define IDM_WRITER 4
+#define IDM_CALC 5
+#define IDM_IMPRESS 6
+#define IDM_DRAW 7
+#define IDM_BASE 8
+#define IDM_TEMPLATE 9
+#define IDM_MATH 12
+#define IDM_INSTALL 10
+#define IDM_STARTCENTER 14
+
+
+#define ICON_LO_DEFAULT 1
+#define ICON_TEXT_DOCUMENT 2
+#define ICON_SPREADSHEET_DOCUMENT 4
+#define ICON_DRAWING_DOCUMENT 6
+#define ICON_PRESENTATION_DOCUMENT 8
+#define ICON_TEMPLATE 11
+#define ICON_DATABASE_DOCUMENT 12
+#define ICON_MATH_DOCUMENT 13
+#define ICON_OPEN 5 // See index of open folder icon in shell32.dll
+
+#define SFX_TASKBAR_NOTIFICATION WM_USER+1
+
+static HWND aListenerWindow = nullptr;
+static HWND aExecuterWindow = nullptr;
+static HMENU popupMenu = nullptr;
+
+static void OnMeasureItem(HWND hwnd, LPMEASUREITEMSTRUCT lpmis);
+static void OnDrawItem(HWND hwnd, LPDRAWITEMSTRUCT lpdis);
+
+namespace {
+
+struct MYITEM
+{
+ OUString text;
+ OUString module;
+ UINT iconId;
+};
+
+}
+
+static void addMenuItem( HMENU hMenu, UINT id, UINT iconId, const OUString& text, int& pos, bool bOwnerdraw, const OUString& module )
+{
+ MENUITEMINFOW mi = {};
+
+ mi.cbSize = sizeof( mi );
+ if( id == static_cast<UINT>( -1 ) )
+ {
+ mi.fMask=MIIM_FTYPE;
+ mi.fType=MFT_SEPARATOR;
+ }
+ else
+ {
+ if( bOwnerdraw )
+ {
+ mi.fMask=MIIM_FTYPE | MIIM_STATE | MIIM_ID | MIIM_DATA;
+ mi.fType=MFT_OWNERDRAW;
+
+ MYITEM *pMyItem = new MYITEM;
+ pMyItem->text = text;
+ pMyItem->iconId = iconId;
+ pMyItem->module = module;
+ mi.dwItemData = reinterpret_cast<ULONG_PTR>(pMyItem);
+ }
+ else
+ {
+ mi.fMask=MIIM_STRING | MIIM_STATE | MIIM_ID;
+ mi.dwTypeData = o3tl::toW(
+ const_cast<sal_Unicode *>(text.getStr()));
+ mi.cch = text.getLength();
+ }
+
+ mi.fState = MFS_ENABLED;
+ mi.wID = id;
+ if ( IDM_TEMPLATE == id )
+ mi.fState |= MFS_DEFAULT;
+ }
+
+ InsertMenuItemW( hMenu, pos++, TRUE, &mi );
+}
+
+
+static HMENU createSystrayMenu( )
+{
+ SvtModuleOptions aModuleOptions;
+
+ HMENU hMenu = CreatePopupMenu();
+ int pos=0;
+
+ ShutdownIcon *pShutdownIcon = ShutdownIcon::getInstance();
+ OSL_ENSURE( pShutdownIcon, "ShutdownIcon instance empty!");
+
+ if( !pShutdownIcon )
+ return nullptr;
+
+ // collect the URLs of the entries in the File/New menu
+ ::std::set< OUString > aFileNewAppsAvailable;
+ std::vector< SvtDynMenuEntry > const aNewMenu = SvtDynamicMenuOptions::GetMenu( EDynamicMenuType::NewMenu );
+ for ( SvtDynMenuEntry const & newMenuProp : aNewMenu )
+ {
+ if ( !newMenuProp.sURL.isEmpty() )
+ aFileNewAppsAvailable.insert( newMenuProp.sURL );
+ }
+
+ // describe the menu entries for launching the applications
+ struct MenuEntryDescriptor
+ {
+ SvtModuleOptions::EModule eModuleIdentifier;
+ UINT nMenuItemID;
+ UINT nMenuIconID;
+ rtl::OUStringConstExpr sURLDescription;
+ } static const aMenuItems[] =
+ {
+ { SvtModuleOptions::EModule::WRITER, IDM_WRITER, ICON_TEXT_DOCUMENT, WRITER_URL },
+ { SvtModuleOptions::EModule::CALC, IDM_CALC, ICON_SPREADSHEET_DOCUMENT, CALC_URL },
+ { SvtModuleOptions::EModule::IMPRESS, IDM_IMPRESS,ICON_PRESENTATION_DOCUMENT, IMPRESS_WIZARD_URL },
+ { SvtModuleOptions::EModule::DRAW, IDM_DRAW, ICON_DRAWING_DOCUMENT, DRAW_URL },
+ { SvtModuleOptions::EModule::DATABASE, IDM_BASE, ICON_DATABASE_DOCUMENT, BASE_URL },
+ { SvtModuleOptions::EModule::MATH, IDM_MATH, ICON_MATH_DOCUMENT, MATH_URL },
+ };
+
+ // insert the menu entries for launching the applications
+ for (const auto& [eModuleIdentifier, nMenuItemID, nMenuIconID, sURL] : aMenuItems)
+ {
+ if ( !aModuleOptions.IsModuleInstalled( eModuleIdentifier ) )
+ // the complete application is not even installed
+ continue;
+
+ if ( aFileNewAppsAvailable.find( sURL ) == aFileNewAppsAvailable.end() )
+ // the application is installed, but the entry has been configured to *not* appear in the File/New
+ // menu => also let not appear it in the quickstarter
+ continue;
+
+ addMenuItem( hMenu, nMenuItemID, nMenuIconID,
+ ShutdownIcon::GetUrlDescription( sURL.asView() ), pos, true, "" );
+ }
+
+
+ // insert the remaining menu entries
+ addMenuItem( hMenu, IDM_TEMPLATE, ICON_TEMPLATE,
+ SfxResId( STR_QUICKSTART_FROMTEMPLATE ), pos, true, "");
+ addMenuItem( hMenu, static_cast< UINT >( -1 ), 0, OUString(), pos, false, "" );
+ addMenuItem( hMenu, IDM_OPEN, ICON_OPEN, SfxResId(STR_QUICKSTART_FILEOPEN), pos, true, "SHELL32");
+ addMenuItem( hMenu, static_cast< UINT >( -1 ), 0, OUString(), pos, false, "" );
+ addMenuItem( hMenu, IDM_INSTALL,0, SfxResId(STR_QUICKSTART_PRELAUNCH), pos, false, "" );
+ addMenuItem( hMenu, static_cast< UINT >( -1 ), 0, OUString(), pos, false, "" );
+ addMenuItem( hMenu, IDM_EXIT, 0, SfxResId(STR_QUICKSTART_EXIT), pos, false, "" );
+
+ // indicate status of autostart folder
+ CheckMenuItem( hMenu, IDM_INSTALL, MF_BYCOMMAND | (ShutdownIcon::GetAutostart() ? MF_CHECKED : MF_UNCHECKED) );
+
+ return hMenu;
+}
+
+
+static void deleteSystrayMenu( HMENU hMenu )
+{
+ if( !hMenu || !IsMenu( hMenu ))
+ return;
+
+ MENUITEMINFOW mi = {};
+ mi.cbSize = sizeof( mi );
+ mi.fMask = MIIM_DATA;
+
+ for (UINT pos = 0; GetMenuItemInfoW(hMenu, pos, true, &mi); ++pos)
+ {
+ if (MYITEM* pMyItem = reinterpret_cast<MYITEM*>(mi.dwItemData))
+ delete pMyItem;
+ mi.fMask = MIIM_DATA;
+ }
+}
+
+
+static void addTaskbarIcon( HWND hWnd )
+{
+ OUString strTip = SfxResId(STR_QUICKSTART_TIP);
+
+ // add taskbar icon
+ NOTIFYICONDATAW nid;
+ nid.hIcon = static_cast<HICON>(LoadImageW( GetModuleHandleW( nullptr ), MAKEINTRESOURCEW( ICON_LO_DEFAULT ),
+ IMAGE_ICON, GetSystemMetrics( SM_CXSMICON ), GetSystemMetrics( SM_CYSMICON ),
+ LR_DEFAULTCOLOR | LR_SHARED ));
+
+ wcsncpy( nid.szTip, o3tl::toW(strTip.getStr()), 64 );
+
+ nid.cbSize = sizeof(nid);
+ nid.hWnd = hWnd;
+ nid.uID = ID_QUICKSTART;
+ nid.uCallbackMessage = SFX_TASKBAR_NOTIFICATION;
+ nid.uFlags = NIF_MESSAGE|NIF_TIP|NIF_ICON;
+
+ Shell_NotifyIconW(NIM_ADD, &nid);
+}
+
+
+static LRESULT CALLBACK listenerWndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ static UINT s_uTaskbarRestart = 0;
+ static UINT s_uMsgKillTray = 0;
+
+ switch (uMsg)
+ {
+ case WM_NCCREATE:
+ return TRUE;
+ case WM_CREATE:
+ {
+ // request notification when taskbar is recreated
+ // we then have to add our icon again
+ s_uTaskbarRestart = RegisterWindowMessageW(L"TaskbarCreated");
+ s_uMsgKillTray = RegisterWindowMessageW( SHUTDOWN_QUICKSTART_MESSAGE );
+
+ // create the menu
+ if( !popupMenu )
+ if( (popupMenu = createSystrayMenu( )) == nullptr )
+ return -1;
+
+ // and the icon
+ addTaskbarIcon( hWnd );
+
+ // disable shutdown
+ ShutdownIcon::getInstance()->SetVeto( true );
+ ShutdownIcon::addTerminateListener();
+ }
+ return 0;
+
+ case WM_MEASUREITEM:
+ OnMeasureItem(hWnd, reinterpret_cast<LPMEASUREITEMSTRUCT>(lParam));
+ return TRUE;
+
+ case WM_DRAWITEM:
+ OnDrawItem(hWnd, reinterpret_cast<LPDRAWITEMSTRUCT>(lParam));
+ return TRUE;
+
+ case SFX_TASKBAR_NOTIFICATION:
+ switch( lParam )
+ {
+ case WM_LBUTTONDOWN:
+ {
+ bool const ret = PostMessageW(aExecuterWindow, WM_COMMAND, IDM_STARTCENTER, reinterpret_cast<LPARAM>(hWnd));
+ SAL_WARN_IF(!ret, "sfx.appl", "ERROR: PostMessage() failed!");
+ break;
+ }
+
+ case WM_RBUTTONDOWN:
+ {
+ POINT pt;
+ GetCursorPos(&pt);
+ SetForegroundWindow( hWnd );
+
+ // update status before showing menu, could have been changed from option page
+ CheckMenuItem( popupMenu, IDM_INSTALL, MF_BYCOMMAND| (ShutdownIcon::GetAutostart() ? MF_CHECKED : MF_UNCHECKED) );
+
+ EnableMenuItem( popupMenu, IDM_EXIT, MF_BYCOMMAND | (ShutdownIcon::bModalMode ? MF_GRAYED : MF_ENABLED) );
+ EnableMenuItem( popupMenu, IDM_OPEN, MF_BYCOMMAND | (ShutdownIcon::bModalMode ? MF_GRAYED : MF_ENABLED) );
+ EnableMenuItem( popupMenu, IDM_TEMPLATE, MF_BYCOMMAND | (ShutdownIcon::bModalMode ? MF_GRAYED : MF_ENABLED) );
+ int m = TrackPopupMenuEx( popupMenu, TPM_RETURNCMD|TPM_LEFTALIGN|TPM_RIGHTBUTTON,
+ pt.x, pt.y, hWnd, nullptr );
+ bool const ret = PostMessageW( hWnd, 0, 0, 0 );
+ SAL_WARN_IF(!ret, "sfx.appl", "ERROR: PostMessage() failed!");
+ switch( m )
+ {
+ case IDM_OPEN:
+ case IDM_WRITER:
+ case IDM_CALC:
+ case IDM_IMPRESS:
+ case IDM_DRAW:
+ case IDM_TEMPLATE:
+ case IDM_BASE:
+ case IDM_MATH:
+ break;
+ case IDM_INSTALL:
+ CheckMenuItem( popupMenu, IDM_INSTALL, MF_BYCOMMAND| (ShutdownIcon::GetAutostart() ? MF_CHECKED : MF_UNCHECKED) );
+ break;
+ case IDM_EXIT:
+ // delete taskbar icon
+ NOTIFYICONDATAW nid;
+ nid.cbSize=sizeof(nid);
+ nid.hWnd = hWnd;
+ nid.uID = ID_QUICKSTART;
+ Shell_NotifyIconW(NIM_DELETE, &nid);
+ break;
+ }
+
+ bool const ret2 = PostMessageW(aExecuterWindow, WM_COMMAND, m, reinterpret_cast<LPARAM>(hWnd));
+ SAL_WARN_IF(!ret2, "sfx.appl", "ERROR: PostMessage() failed!");
+ }
+ break;
+ }
+ break;
+ case WM_DESTROY:
+ deleteSystrayMenu( popupMenu );
+ // We don't need the Systray Thread anymore
+ PostQuitMessage( 0 );
+ return DefWindowProcW(hWnd, uMsg, wParam, lParam);
+ default:
+ if( uMsg == s_uTaskbarRestart )
+ {
+ // re-create taskbar icon
+ addTaskbarIcon( hWnd );
+ }
+ else if ( uMsg == s_uMsgKillTray )
+ {
+ // delete taskbar icon
+ NOTIFYICONDATAW nid;
+ nid.cbSize=sizeof(nid);
+ nid.hWnd = hWnd;
+ nid.uID = ID_QUICKSTART;
+ Shell_NotifyIconW(NIM_DELETE, &nid);
+
+ bool const ret = PostMessageW(aExecuterWindow, WM_COMMAND, IDM_EXIT, reinterpret_cast<LPARAM>(hWnd));
+ SAL_WARN_IF(!ret, "sfx.appl", "ERROR: PostMessage() failed!");
+ }
+ else
+ return DefWindowProcW(hWnd, uMsg, wParam, lParam);
+ }
+ return 0;
+}
+
+
+static LRESULT CALLBACK executerWndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ switch (uMsg)
+ {
+ case WM_NCCREATE:
+ return TRUE;
+ case WM_CREATE:
+ return 0;
+
+ case WM_COMMAND:
+ switch( LOWORD(wParam) )
+ {
+ case IDM_OPEN:
+ if ( !ShutdownIcon::bModalMode )
+ ShutdownIcon::FileOpen();
+ break;
+ case IDM_WRITER:
+ ShutdownIcon::OpenURL( WRITER_URL, "_default" );
+ break;
+ case IDM_CALC:
+ ShutdownIcon::OpenURL( CALC_URL, "_default" );
+ break;
+ case IDM_IMPRESS:
+ ShutdownIcon::OpenURL( IMPRESS_WIZARD_URL, "_default" );
+ break;
+ case IDM_DRAW:
+ ShutdownIcon::OpenURL( DRAW_URL, "_default" );
+ break;
+ case IDM_BASE:
+ ShutdownIcon::OpenURL( BASE_URL, "_default" );
+ break;
+ case IDM_MATH:
+ ShutdownIcon::OpenURL( MATH_URL, "_default" );
+ break;
+ case IDM_STARTCENTER:
+ ShutdownIcon::OpenURL( STARTMODULE_URL, "_default" );
+ break;
+ case IDM_TEMPLATE:
+ if ( !ShutdownIcon::bModalMode )
+ ShutdownIcon::FromTemplate();
+ break;
+ case IDM_INSTALL:
+ ShutdownIcon::SetAutostart( !ShutdownIcon::GetAutostart() );
+ break;
+ case IDM_EXIT:
+ // remove listener and
+ // terminate office if running in background
+ if ( !ShutdownIcon::bModalMode )
+ ShutdownIcon::terminateDesktop();
+ break;
+ }
+ break;
+ case WM_DESTROY:
+ default:
+ return DefWindowProcW(hWnd, uMsg, wParam, lParam);
+ }
+ return 0;
+}
+
+
+static DWORD WINAPI SystrayThread( LPVOID /*lpParam*/ )
+{
+ osl_setThreadName("SystrayThread");
+
+ aListenerWindow = CreateWindowExW(0,
+ QUICKSTART_CLASSNAME, // registered class name
+ QUICKSTART_WINDOWNAME, // window name
+ 0, // window style
+ CW_USEDEFAULT, // horizontal position of window
+ CW_USEDEFAULT, // vertical position of window
+ CW_USEDEFAULT, // window width
+ CW_USEDEFAULT, // window height
+ nullptr, // handle to parent or owner window
+ nullptr, // menu handle or child identifier
+ GetModuleHandleW( nullptr ), // handle to application instance
+ nullptr // window-creation data
+ );
+
+ MSG msg;
+
+ for (;;)
+ {
+ int const bRet = GetMessageW(&msg, nullptr, 0, 0);
+ if (bRet == 0)
+ {
+ break;
+ }
+ if (-1 == bRet)
+ {
+ SAL_WARN("sfx.appl", "GetMessageW failed: " << WindowsErrorString(GetLastError()));
+ return 1;
+ }
+ TranslateMessage( &msg );
+ DispatchMessageW( &msg );
+ }
+
+ return msg.wParam; // Exit code of WM_QUIT
+}
+
+
+void win32_init_sys_tray()
+{
+ if ( ShutdownIcon::IsQuickstarterInstalled() )
+ {
+ WNDCLASSEXW listenerClass;
+ listenerClass.cbSize = sizeof(listenerClass);
+ listenerClass.style = 0;
+ listenerClass.lpfnWndProc = listenerWndProc;
+ listenerClass.cbClsExtra = 0;
+ listenerClass.cbWndExtra = 0;
+ listenerClass.hInstance = GetModuleHandleW( nullptr );
+ listenerClass.hIcon = nullptr;
+ listenerClass.hCursor = nullptr;
+ listenerClass.hbrBackground = nullptr;
+ listenerClass.lpszMenuName = nullptr;
+ listenerClass.lpszClassName = QUICKSTART_CLASSNAME;
+ listenerClass.hIconSm = nullptr;
+
+ RegisterClassExW(&listenerClass);
+
+ WNDCLASSEXW executerClass;
+ executerClass.cbSize = sizeof(executerClass);
+ executerClass.style = 0;
+ executerClass.lpfnWndProc = executerWndProc;
+ executerClass.cbClsExtra = 0;
+ executerClass.cbWndExtra = 0;
+ executerClass.hInstance = GetModuleHandleW( nullptr );
+ executerClass.hIcon = nullptr;
+ executerClass.hCursor = nullptr;
+ executerClass.hbrBackground = nullptr;
+ executerClass.lpszMenuName = nullptr;
+ executerClass.lpszClassName = EXECUTER_WINDOWCLASS;
+ executerClass.hIconSm = nullptr;
+
+ RegisterClassExW( &executerClass );
+
+ aExecuterWindow = CreateWindowExW(0,
+ EXECUTER_WINDOWCLASS, // registered class name
+ EXECUTER_WINDOWNAME, // window name
+ 0, // window style
+ CW_USEDEFAULT, // horizontal position of window
+ CW_USEDEFAULT, // vertical position of window
+ CW_USEDEFAULT, // window width
+ CW_USEDEFAULT, // window height
+ nullptr, // handle to parent or owner window
+ nullptr, // menu handle or child identifier
+ GetModuleHandleW( nullptr ), // handle to application instance
+ nullptr // window-creation data
+ );
+
+ DWORD dwThreadId;
+ CloseHandle(CreateThread(nullptr, 0, SystrayThread, nullptr, 0, &dwThreadId));
+ }
+}
+
+
+void win32_shutdown_sys_tray()
+{
+ if ( ShutdownIcon::IsQuickstarterInstalled() )
+ {
+ if( IsWindow( aListenerWindow ) )
+ {
+ DestroyWindow( aListenerWindow );
+ aListenerWindow = nullptr;
+ DestroyWindow( aExecuterWindow );
+ aExecuterWindow = nullptr;
+ }
+ UnregisterClassW( QUICKSTART_CLASSNAME, GetModuleHandleW( nullptr ) );
+ UnregisterClassW( EXECUTER_WINDOWCLASS, GetModuleHandleW( nullptr ) );
+ }
+}
+
+
+void OnMeasureItem(HWND hwnd, LPMEASUREITEMSTRUCT lpmis)
+{
+ MYITEM *pMyItem = reinterpret_cast<MYITEM *>(lpmis->itemData);
+ HDC hdc = GetDC(hwnd);
+ SIZE size;
+
+ NONCLIENTMETRICSW ncm = {};
+ ncm.cbSize = sizeof(ncm);
+
+ SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, 0, &ncm, 0);
+
+ // Assume every menu item can be default and printed bold
+ ncm.lfMenuFont.lfWeight = FW_BOLD;
+
+ HFONT hfntOld = static_cast<HFONT>(SelectObject(hdc, CreateFontIndirectW( &ncm.lfMenuFont )));
+
+ GetTextExtentPoint32W(hdc, o3tl::toW(pMyItem->text.getStr()),
+ pMyItem->text.getLength(), &size);
+
+ lpmis->itemWidth = size.cx + 4 + GetSystemMetrics( SM_CXSMICON );
+ lpmis->itemHeight = std::max<int>(size.cy, GetSystemMetrics( SM_CYSMICON ));
+ lpmis->itemHeight += 4;
+
+ DeleteObject( SelectObject(hdc, hfntOld) );
+ ReleaseDC(hwnd, hdc);
+}
+
+void OnDrawItem(HWND /*hwnd*/, LPDRAWITEMSTRUCT lpdis)
+{
+ MYITEM *pMyItem = reinterpret_cast<MYITEM *>(lpdis->itemData);
+ COLORREF clrPrevText, clrPrevBkgnd;
+ HFONT hfntOld;
+ HBRUSH hbrOld;
+ int x, y;
+ bool fSelected = lpdis->itemState & ODS_SELECTED;
+ bool fDisabled = lpdis->itemState & (ODS_DISABLED | ODS_GRAYED);
+
+ // Set the appropriate foreground and background colors.
+
+ RECT aRect = lpdis->rcItem;
+
+ clrPrevBkgnd = SetBkColor( lpdis->hDC, GetSysColor(COLOR_MENU) );
+
+ if ( fDisabled )
+ clrPrevText = SetTextColor( lpdis->hDC, GetSysColor( COLOR_GRAYTEXT ) );
+ else
+ clrPrevText = SetTextColor( lpdis->hDC, GetSysColor( fSelected ? COLOR_HIGHLIGHTTEXT : COLOR_MENUTEXT ) );
+
+ if ( fSelected )
+ clrPrevBkgnd = SetBkColor( lpdis->hDC, GetSysColor(COLOR_HIGHLIGHT) );
+ else
+ clrPrevBkgnd = SetBkColor( lpdis->hDC, GetSysColor(COLOR_MENU) );
+
+ hbrOld = static_cast<HBRUSH>(SelectObject( lpdis->hDC, CreateSolidBrush( GetBkColor( lpdis->hDC ) ) ));
+
+ // Fill background
+ PatBlt(lpdis->hDC, aRect.left, aRect.top, aRect.right-aRect.left, aRect.bottom-aRect.top, PATCOPY);
+
+ int height = aRect.bottom-aRect.top;
+
+ x = aRect.left;
+ y = aRect.top;
+
+ int cx = GetSystemMetrics( SM_CXSMICON );
+ int cy = GetSystemMetrics( SM_CYSMICON );
+ HICON hIcon( nullptr );
+ HMODULE hModule( GetModuleHandleW( nullptr ) );
+
+ if ( pMyItem->module.getLength() > 0 )
+ {
+ LPCWSTR pModuleName = o3tl::toW( pMyItem->module.getStr() );
+ hModule = GetModuleHandleW( pModuleName );
+ if ( hModule == nullptr )
+ {
+ hModule = LoadLibraryW(pModuleName);
+ }
+ }
+
+ hIcon = static_cast<HICON>(LoadImageW( hModule, MAKEINTRESOURCEW( pMyItem->iconId ),
+ IMAGE_ICON, cx, cy,
+ LR_DEFAULTCOLOR | LR_SHARED ));
+
+
+ HBRUSH hbrIcon = CreateSolidBrush( GetSysColor( COLOR_GRAYTEXT ) );
+
+ DrawStateW( lpdis->hDC, hbrIcon, nullptr, reinterpret_cast<LPARAM>(hIcon), WPARAM(0), x, y+(height-cy)/2, 0, 0, DST_ICON | (fDisabled ? (fSelected ? DSS_MONO : DSS_DISABLED) : DSS_NORMAL) );
+
+ DeleteObject( hbrIcon );
+
+ x += cx + 4; // space for icon
+ aRect.left = x;
+
+ NONCLIENTMETRICSW ncm = {};
+ ncm.cbSize = sizeof(ncm);
+
+ SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, 0, &ncm, 0);
+
+ // Print default menu entry with bold font
+ if ( lpdis->itemState & ODS_DEFAULT )
+ ncm.lfMenuFont.lfWeight = FW_BOLD;
+
+ hfntOld = static_cast<HFONT>(SelectObject(lpdis->hDC, CreateFontIndirectW( &ncm.lfMenuFont )));
+
+
+ SIZE size;
+ GetTextExtentPointW( lpdis->hDC, o3tl::toW(pMyItem->text.getStr()), pMyItem->text.getLength(), &size );
+
+ DrawStateW( lpdis->hDC, nullptr, nullptr, reinterpret_cast<LPARAM>(pMyItem->text.getStr()), WPARAM(0), aRect.left, aRect.top + (height - size.cy)/2, 0, 0, DST_TEXT | (fDisabled && !fSelected ? DSS_DISABLED : DSS_NORMAL) );
+
+ // Restore the original font and colors.
+ DeleteObject( SelectObject( lpdis->hDC, hbrOld ) );
+ DeleteObject( SelectObject( lpdis->hDC, hfntOld) );
+ SetTextColor(lpdis->hDC, clrPrevText);
+ SetBkColor(lpdis->hDC, clrPrevBkgnd);
+}
+
+
+// code from setup2 project
+
+
+static void SHFree_( void *pv )
+{
+ IMalloc *pMalloc;
+ if( NOERROR == SHGetMalloc(&pMalloc) )
+ {
+ pMalloc->Free( pv );
+ pMalloc->Release();
+ }
+}
+
+#define ALLOC(type, n) static_cast<type *>(HeapAlloc(GetProcessHeap(), 0, sizeof(type) * n ))
+#define FREE(p) HeapFree(GetProcessHeap(), 0, p)
+
+static OUString SHGetSpecialFolder( int nFolderID )
+{
+
+ LPITEMIDLIST pidl;
+ HRESULT hHdl = SHGetSpecialFolderLocation( nullptr, nFolderID, &pidl );
+ OUString aFolder;
+
+ if( hHdl == NOERROR )
+ {
+ WCHAR *lpFolderA;
+ lpFolderA = ALLOC( WCHAR, 16000 );
+
+ SHGetPathFromIDListW( pidl, lpFolderA );
+ aFolder = o3tl::toU( lpFolderA );
+
+ FREE( lpFolderA );
+ SHFree_( pidl );
+ }
+ return aFolder;
+}
+
+OUString ShutdownIcon::GetAutostartFolderNameW32()
+{
+ return SHGetSpecialFolder(CSIDL_STARTUP);
+}
+
+static HRESULT WINAPI SHCoCreateInstance( LPVOID lpszReserved, REFCLSID clsid, LPUNKNOWN pUnkUnknown, REFIID iid, LPVOID *ppv )
+{
+ HRESULT hResult = E_NOTIMPL;
+ HMODULE hModShell = GetModuleHandleW( L"SHELL32" );
+
+ if ( hModShell != nullptr )
+ {
+ typedef HRESULT (WINAPI *SHCoCreateInstance_PROC)( LPVOID lpszReserved, REFCLSID clsid, LPUNKNOWN pUnkUnknown, REFIID iid, LPVOID *ppv );
+
+ SHCoCreateInstance_PROC lpfnSHCoCreateInstance = reinterpret_cast<SHCoCreateInstance_PROC>(GetProcAddress( hModShell, MAKEINTRESOURCEA(102) ));
+
+ if ( lpfnSHCoCreateInstance )
+ hResult = lpfnSHCoCreateInstance( lpszReserved, clsid, pUnkUnknown, iid, ppv );
+ }
+ return hResult;
+}
+
+static bool CreateShortcut( const OUString& rAbsObject, const OUString& rAbsObjectPath,
+ const OUString& rAbsShortcut, const OUString& rDescription, const OUString& rParameter )
+{
+ HRESULT hres;
+ IShellLinkW* psl;
+ CLSID clsid_ShellLink = CLSID_ShellLink;
+ CLSID clsid_IShellLink = IID_IShellLinkW;
+
+ hres = CoCreateInstance( clsid_ShellLink, nullptr, CLSCTX_INPROC_SERVER,
+ clsid_IShellLink, reinterpret_cast<void**>(&psl) );
+ if( FAILED(hres) )
+ hres = SHCoCreateInstance( nullptr, clsid_ShellLink, nullptr, clsid_IShellLink, reinterpret_cast<void**>(&psl) );
+
+ if( SUCCEEDED(hres) )
+ {
+ IPersistFile* ppf;
+ psl->SetPath( o3tl::toW(rAbsObject.getStr()) );
+ psl->SetWorkingDirectory( o3tl::toW(rAbsObjectPath.getStr()) );
+ psl->SetDescription( o3tl::toW(rDescription.getStr()) );
+ if( rParameter.getLength() )
+ psl->SetArguments( o3tl::toW(rParameter.getStr()) );
+
+ CLSID clsid_IPersistFile = IID_IPersistFile;
+ hres = psl->QueryInterface( clsid_IPersistFile, reinterpret_cast<void**>(&ppf) );
+
+ if( SUCCEEDED(hres) )
+ {
+ hres = ppf->Save( o3tl::toW(rAbsShortcut.getStr()), TRUE );
+ ppf->Release();
+ } else return false;
+ psl->Release();
+ } else return false;
+ return true;
+}
+
+
+// install/uninstall
+
+static bool FileExistsW( LPCWSTR lpPath )
+{
+ bool bExists = false;
+ WIN32_FIND_DATAW aFindData;
+
+ HANDLE hFind = FindFirstFileW( lpPath, &aFindData );
+
+ if ( INVALID_HANDLE_VALUE != hFind )
+ {
+ bExists = true;
+ FindClose( hFind );
+ }
+
+ return bExists;
+}
+
+bool ShutdownIcon::IsQuickstarterInstalled()
+{
+ wchar_t aPath[_MAX_PATH];
+ GetModuleFileNameW( nullptr, aPath, _MAX_PATH-1);
+
+ OUString aOfficepath( o3tl::toU(aPath) );
+ int i = aOfficepath.lastIndexOf('\\');
+ if( i != -1 )
+ aOfficepath = aOfficepath.copy(0, i);
+
+ OUString quickstartExe(aOfficepath + "\\quickstart.exe");
+
+ return FileExistsW( o3tl::toW(quickstartExe.getStr()) );
+}
+
+void ShutdownIcon::EnableAutostartW32( const OUString &aShortcut )
+{
+ wchar_t aPath[_MAX_PATH];
+ GetModuleFileNameW( nullptr, aPath, _MAX_PATH-1);
+
+ OUString aOfficepath( o3tl::toU(aPath) );
+ int i = aOfficepath.lastIndexOf('\\');
+ if( i != -1 )
+ aOfficepath = aOfficepath.copy(0, i);
+
+ OUString quickstartExe(aOfficepath + "\\quickstart.exe");
+
+ CreateShortcut( quickstartExe, aOfficepath, aShortcut, OUString(), OUString() );
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/appl/workwin.cxx b/sfx2/source/appl/workwin.cxx
new file mode 100644
index 000000000..4874db7ee
--- /dev/null
+++ b/sfx2/source/appl/workwin.cxx
@@ -0,0 +1,2430 @@
+/* -*- 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 <config_features.h>
+#include <config_feature_desktop.h>
+#include <comphelper/lok.hxx>
+#include <comphelper/processfactory.hxx>
+#include <osl/diagnose.h>
+#include <sal/log.hxx>
+#include <sfx2/docfile.hxx>
+#include <sfx2/objface.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/app.hxx>
+#include <workwin.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/module.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/dockwin.hxx>
+#include <sfx2/viewsh.hxx>
+#include <splitwin.hxx>
+#include <sfx2/sfxsids.hrc>
+#include <sfx2/toolbarids.hxx>
+#include <vcl/taskpanelist.hxx>
+#include <vcl/svapp.hxx>
+#include <svl/eitem.hxx>
+#include <tools/svborder.hxx>
+#include <tools/debug.hxx>
+#include <unotools/moduleoptions.hxx>
+#include <com/sun/star/ui/XUIElement.hpp>
+#include <com/sun/star/frame/LayoutManagerEvents.hpp>
+#include <com/sun/star/frame/ModuleManager.hpp>
+#include <com/sun/star/frame/XLayoutManager.hpp>
+#include <com/sun/star/frame/XLayoutManagerEventBroadcaster.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/lang/DisposedException.hpp>
+#include <type_traits>
+#include <unordered_map>
+#include <sfx2/notebookbar/SfxNotebookBar.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+
+namespace {
+
+struct ResIdToResName
+{
+ ToolbarId eId;
+ const char* pName;
+};
+
+}
+
+const ResIdToResName pToolBarResToName[] =
+{
+ { ToolbarId::FullScreenToolbox, "fullscreenbar" },
+ { ToolbarId::EnvToolbox, "standardbar", },
+ { ToolbarId::SvxTbx_Form_Navigation, "formsnavigationbar" },
+ { ToolbarId::SvxTbx_Form_Filter, "formsfilterbar" },
+ { ToolbarId::SvxTbx_Text_Control_Attributes, "formtextobjectbar" },
+ { ToolbarId::SvxTbx_Controls, "formcontrols" },
+ { ToolbarId::SvxTbx_FormDesign, "formdesign" },
+ { ToolbarId::Math_Toolbox, "toolbar" }, //math
+ { ToolbarId::Text_Toolbox_Sc, "textobjectbar" }, //calc
+ { ToolbarId::Draw_Objectbar, "drawobjectbar" },
+ { ToolbarId::Graphic_Objectbar, "graphicobjectbar" },
+ { ToolbarId::Objectbar_Format, "formatobjectbar" },
+ { ToolbarId::Objectbar_Preview, "previewbar" },
+ { ToolbarId::Objectbar_Tools, "toolbar" }, //calc
+ { ToolbarId::Bezier_Toolbox_Sd, "bezierobjectbar" }, //draw/impress
+ { ToolbarId::Gluepoints_Toolbox, "gluepointsobjectbar" },
+ { ToolbarId::Draw_Graf_Toolbox, "graphicobjectbar" },
+ { ToolbarId::Draw_Obj_Toolbox, "drawingobjectbar" }, //impress
+ { ToolbarId::Draw_Text_Toolbox_Sd, "textobjectbar" }, //impress
+ { ToolbarId::Draw_Toolbox_Sd, "toolbar" }, //impress
+ { ToolbarId::Draw_Options_Toolbox, "optionsbar" },
+ { ToolbarId::Draw_CommonTask_Toolbox, "commontaskbar" },
+ { ToolbarId::Graphic_Obj_Toolbox, "drawingobjectbar" }, //draw
+ { ToolbarId::Outline_Toolbox, "outlinetoolbar" }, //impress
+ { ToolbarId::Slide_Toolbox, "slideviewtoolbar" },
+ { ToolbarId::Slide_Obj_Toolbox, "slideviewobjectbar" },
+ { ToolbarId::Bezier_Toolbox_Sw, "bezierobjectbar" },
+ { ToolbarId::Draw_Toolbox_Sw, "drawingobjectbar" },
+ { ToolbarId::Draw_Text_Toolbox_Sw, "drawtextobjectbar" },
+ { ToolbarId::Frame_Toolbox, "frameobjectbar" },
+ { ToolbarId::Grafik_Toolbox, "graphicobjectbar" },
+ { ToolbarId::Num_Toolbox, "numobjectbar" },
+ { ToolbarId::Ole_Toolbox, "oleobjectbar" },
+ { ToolbarId::Table_Toolbox, "tableobjectbar" },
+ { ToolbarId::Text_Toolbox_Sw, "textobjectbar" },
+ { ToolbarId::PView_Toolbox, "previewobjectbar" }, //writer
+ { ToolbarId::Webtools_Toolbox, "toolbar" }, //web
+ { ToolbarId::Webtext_Toolbox, "textobjectbar" },
+ { ToolbarId::Tools_Toolbox, "toolbar" }, //writer
+ { ToolbarId::Webframe_Toolbox, "frameobjectbar" }, //web
+ { ToolbarId::Webgraphic_Toolbox, "graphicobjectbar" },
+ { ToolbarId::Webole_Toolbox, "oleobjectbar" },
+ { ToolbarId::Basicide_Objectbar, "macrobar" },
+ { ToolbarId::Svx_Fontwork_Bar, "fontworkobjectbar" }, //global
+ { ToolbarId::Svx_Extrusion_Bar, "extrusionobjectbar" },
+ { ToolbarId::FormLayer_Toolbox, "formsobjectbar" },
+ { ToolbarId::Module_Toolbox, "viewerbar" }, //writer (plugin)
+ { ToolbarId::Objectbar_App, "viewerbar" }, //calc (plugin)
+ { ToolbarId::Draw_Viewer_Toolbox, "viewerbar" }, //impress(plugin)
+ { ToolbarId::Draw_Media_Toolbox, "mediaobjectbar" }, //draw/impress
+ { ToolbarId::Media_Objectbar, "mediaobjectbar" }, //calc
+ { ToolbarId::Media_Toolbox, "mediaobjectbar" }, //writer
+ { ToolbarId::None, "" }
+};
+
+// Sort the Children according their alignment
+// The order corresponds to the enum SfxChildAlignment (->CHILDWIN.HXX).
+
+constexpr OUStringLiteral g_aLayoutManagerPropName = u"LayoutManager";
+
+// Help to make changes to the alignment compatible!
+LayoutManagerListener::LayoutManagerListener(
+ SfxWorkWindow* pWrkWin ) :
+ m_bHasFrame( false ),
+ m_pWrkWin( pWrkWin )
+{
+}
+
+LayoutManagerListener::~LayoutManagerListener()
+{
+}
+
+void LayoutManagerListener::setFrame( const css::uno::Reference< css::frame::XFrame >& xFrame )
+{
+ SolarMutexGuard aGuard;
+ if ( !m_pWrkWin || m_bHasFrame )
+ return;
+
+ m_xFrame = xFrame;
+ m_bHasFrame = true;
+
+ if ( !xFrame.is() )
+ return;
+
+ css::uno::Reference< css::beans::XPropertySet > xPropSet( xFrame, UNO_QUERY );
+ css::uno::Reference< css::frame::XLayoutManagerEventBroadcaster > xLayoutManager;
+ if ( !xPropSet.is() )
+ return;
+
+ try
+ {
+ Any aValue = xPropSet->getPropertyValue( g_aLayoutManagerPropName );
+ aValue >>= xLayoutManager;
+
+ if ( xLayoutManager.is() )
+ xLayoutManager->addLayoutManagerEventListener(
+ css::uno::Reference< css::frame::XLayoutManagerListener >(this) );
+
+ xPropSet.set( xLayoutManager, UNO_QUERY );
+ if ( xPropSet.is() )
+ {
+ aValue = xPropSet->getPropertyValue( "LockCount" );
+ aValue >>= m_pWrkWin->m_nLock;
+ }
+ }
+ catch ( css::lang::DisposedException& )
+ {
+ }
+ catch ( const css::uno::RuntimeException& )
+ {
+ throw;
+ }
+ catch ( css::uno::Exception& )
+ {
+ }
+}
+
+
+// XComponent
+
+void SAL_CALL LayoutManagerListener::addEventListener(
+ const css::uno::Reference< css::lang::XEventListener >& )
+{
+ // do nothing, only internal class
+}
+
+void SAL_CALL LayoutManagerListener::removeEventListener(
+ const css::uno::Reference< css::lang::XEventListener >& )
+{
+ // do nothing, only internal class
+}
+
+void SAL_CALL LayoutManagerListener::dispose()
+{
+ SolarMutexGuard aGuard;
+
+ // reset member
+ m_pWrkWin = nullptr;
+
+ css::uno::Reference< css::frame::XFrame > xFrame( m_xFrame.get(), css::uno::UNO_QUERY );
+ if ( !xFrame.is() )
+ return;
+
+ m_xFrame.clear();
+ m_bHasFrame = false;
+
+ css::uno::Reference< css::beans::XPropertySet > xPropSet( xFrame, css::uno::UNO_QUERY );
+ css::uno::Reference< css::frame::XLayoutManagerEventBroadcaster > xLayoutManager;
+ if ( !xPropSet.is() )
+ return;
+
+ try
+ {
+ css::uno::Any aValue = xPropSet->getPropertyValue( g_aLayoutManagerPropName );
+ aValue >>= xLayoutManager;
+
+ // remove as listener from layout manager
+ if ( xLayoutManager.is() )
+ xLayoutManager->removeLayoutManagerEventListener(
+ css::uno::Reference< css::frame::XLayoutManagerListener >(this) );
+ }
+ catch ( css::lang::DisposedException& )
+ {
+ }
+ catch ( const css::uno::RuntimeException& )
+ {
+ throw;
+ }
+ catch ( css::uno::Exception& )
+ {
+ }
+}
+
+
+// XEventListener
+
+void SAL_CALL LayoutManagerListener::disposing(
+ const css::lang::EventObject& )
+{
+ SolarMutexGuard aGuard;
+ m_pWrkWin = nullptr;
+ m_bHasFrame = false;
+ m_xFrame.clear();
+}
+
+
+// XLayoutManagerEventListener
+
+void SAL_CALL LayoutManagerListener::layoutEvent(
+ const css::lang::EventObject&,
+ ::sal_Int16 eLayoutEvent,
+ const css::uno::Any& )
+{
+ SolarMutexGuard aGuard;
+ if ( !m_pWrkWin )
+ return;
+
+ if ( eLayoutEvent == css::frame::LayoutManagerEvents::VISIBLE )
+ {
+ m_pWrkWin->MakeVisible_Impl( true );
+ m_pWrkWin->ShowChildren_Impl();
+ m_pWrkWin->ArrangeChildren_Impl();
+ }
+ else if ( eLayoutEvent == css::frame::LayoutManagerEvents::INVISIBLE )
+ {
+ m_pWrkWin->MakeVisible_Impl( false );
+ m_pWrkWin->HideChildren_Impl();
+ m_pWrkWin->ArrangeChildren_Impl();
+ }
+ else if ( eLayoutEvent == css::frame::LayoutManagerEvents::LOCK )
+ {
+ m_pWrkWin->Lock_Impl( true );
+ }
+ else if ( eLayoutEvent == css::frame::LayoutManagerEvents::UNLOCK )
+ {
+ m_pWrkWin->Lock_Impl( false );
+ }
+}
+
+namespace
+{
+ struct ToolbarIdHash
+ {
+ size_t operator()(ToolbarId t) const
+ {
+ typedef std::underlying_type<ToolbarId>::type underlying_type;
+ return std::hash<underlying_type>()(static_cast<underlying_type>(t));
+ }
+ };
+
+ class FilledToolBarResIdToResourceURLMap
+ {
+ private:
+ typedef std::unordered_map<ToolbarId, OUString, ToolbarIdHash> ToolBarResIdToResourceURLMap;
+ ToolBarResIdToResourceURLMap m_aResIdToResourceURLMap;
+ public:
+ FilledToolBarResIdToResourceURLMap()
+ {
+ sal_Int32 nIndex( 0 );
+ while (pToolBarResToName[nIndex].eId != ToolbarId::None)
+ {
+ OUString aResourceURL( OUString::createFromAscii( pToolBarResToName[nIndex].pName ));
+ m_aResIdToResourceURLMap.emplace(pToolBarResToName[nIndex].eId, aResourceURL);
+ ++nIndex;
+ }
+ }
+
+ OUString findURL(ToolbarId eId) const
+ {
+ ToolBarResIdToResourceURLMap::const_iterator aIter = m_aResIdToResourceURLMap.find(eId);
+ if ( aIter != m_aResIdToResourceURLMap.end() )
+ return aIter->second;
+ return OUString();
+ }
+ };
+}
+
+static OUString GetResourceURLFromToolbarId(ToolbarId eId)
+{
+ static FilledToolBarResIdToResourceURLMap theFilledToolBarResIdToResourceURLMap;
+ return theFilledToolBarResIdToResourceURLMap.findURL(eId);
+}
+
+static sal_uInt16 TbxMatch( sal_uInt16 nPos )
+{
+ switch ( nPos )
+ {
+ case SFX_OBJECTBAR_APPLICATION :
+ return 0;
+ case SFX_OBJECTBAR_OPTIONS:
+ return 1;
+ case SFX_OBJECTBAR_MACRO:
+ return 2;
+ case SFX_OBJECTBAR_OBJECT:
+ return 3;
+ case SFX_OBJECTBAR_TOOLS:
+ return 4;
+ case SFX_OBJECTBAR_FULLSCREEN:
+ case SFX_OBJECTBAR_COMMONTASK:
+ case SFX_OBJECTBAR_RECORDING:
+ return nPos+1;
+ default:
+ return nPos;
+ }
+}
+
+static sal_uInt16 ChildAlignValue(SfxChildAlignment eAlign)
+{
+ sal_uInt16 ret = 17;
+
+ switch (eAlign)
+ {
+ case SfxChildAlignment::HIGHESTTOP:
+ ret = 1;
+ break;
+ case SfxChildAlignment::LOWESTBOTTOM:
+ ret = 2;
+ break;
+ case SfxChildAlignment::FIRSTLEFT:
+ ret = 3;
+ break;
+ case SfxChildAlignment::LASTRIGHT:
+ ret = 4;
+ break;
+ case SfxChildAlignment::LEFT:
+ ret = 5;
+ break;
+ case SfxChildAlignment::RIGHT:
+ ret = 6;
+ break;
+ case SfxChildAlignment::FIRSTRIGHT:
+ ret = 7;
+ break;
+ case SfxChildAlignment::LASTLEFT:
+ ret = 8;
+ break;
+ case SfxChildAlignment::TOP:
+ ret = 9;
+ break;
+ case SfxChildAlignment::BOTTOM:
+ ret = 10;
+ break;
+ case SfxChildAlignment::TOOLBOXTOP:
+ ret = 11;
+ break;
+ case SfxChildAlignment::TOOLBOXBOTTOM:
+ ret = 12;
+ break;
+ case SfxChildAlignment::LOWESTTOP:
+ ret = 13;
+ break;
+ case SfxChildAlignment::HIGHESTBOTTOM:
+ ret = 14;
+ break;
+ case SfxChildAlignment::TOOLBOXLEFT:
+ ret = 15;
+ break;
+ case SfxChildAlignment::TOOLBOXRIGHT:
+ ret = 16;
+ break;
+ case SfxChildAlignment::NOALIGNMENT:
+ break; // -Wall not handled...
+ }
+
+ return ret;
+}
+
+void SfxWorkWindow::Sort_Impl()
+{
+ aSortedList.clear();
+ for (size_t i = 0; i < aChildren.size(); ++i)
+ {
+ SfxChild_Impl *pCli = aChildren[i].get();
+ if (pCli)
+ {
+ decltype(aSortedList)::size_type k;
+ for (k=0; k<aSortedList.size(); k++)
+ if (ChildAlignValue( aChildren[aSortedList[k]]->eAlign ) >
+ ChildAlignValue(pCli->eAlign))
+ break;
+ aSortedList.insert( aSortedList.begin() + k, i );
+ }
+ }
+
+ bSorted = true;
+}
+
+constexpr OUStringLiteral g_aStatusBarResName( u"private:resource/statusbar/statusbar" );
+constexpr OUStringLiteral g_aTbxTypeName( u"private:resource/toolbar/" );
+constexpr OUStringLiteral g_aProgressBarResName( u"private:resource/progressbar/progressbar" );
+
+// constructor for workwin of a Frame
+
+SfxWorkWindow::SfxWorkWindow( vcl::Window *pWin, SfxFrame *pFrm, SfxFrame* pMaster ) :
+ pBindings(&pFrm->GetCurrentViewFrame()->GetBindings()),
+ pWorkWin (pWin),
+ pActiveChild( nullptr ),
+ nUpdateMode(SfxVisibilityFlags::Standard),
+ nChildren( 0 ),
+ nOrigMode( SfxVisibilityFlags::Invisible ),
+ bSorted( true ),
+ bDockingAllowed(true),
+ bInternalDockingAllowed(true),
+ bAllChildrenVisible(true),
+#if !defined(ANDROID) || HAVE_FEATURE_ANDROID_LOK
+ bIsFullScreen( false ),
+#else // Fennec-based Android Viewer
+ bIsFullScreen( true ),
+#endif
+#if HAVE_FEATURE_DESKTOP
+ bShowStatusBar( true ),
+#else
+ bShowStatusBar( sal_False ),
+#endif
+ m_nLock( 0 ),
+ pMasterFrame( pMaster ),
+ pFrame( pFrm )
+{
+ DBG_ASSERT (pBindings, "No Bindings!");
+
+ pBindings->SetWorkWindow_Impl( std::unique_ptr<SfxWorkWindow>(this) );
+
+ // For the ObjectBars an integral place in the Childlist is reserved,
+ // so that they always come in a defined order.
+ for (int i=0; i<SFX_OBJECTBAR_MAX; ++i)
+ aChildren.push_back( nullptr );
+
+ // create and initialize layout manager listener
+ Reference< css::frame::XFrame > xFrame = GetFrameInterface();
+ rtl::Reference<LayoutManagerListener> pLayoutManagerListener = new LayoutManagerListener( this );
+ m_xLayoutManagerListener = pLayoutManagerListener;
+ pLayoutManagerListener->setFrame( xFrame );
+
+ SfxShell* pConfigShell = pFrm->GetCurrentViewFrame();
+ if ( pConfigShell && pConfigShell->GetObjectShell() )
+ {
+ bShowStatusBar = ( !pConfigShell->GetObjectShell()->IsInPlaceActive() );
+ bDockingAllowed = true;
+ bInternalDockingAllowed = true;
+ }
+
+ // The required split windows (one for each side) can be created
+ for ( sal_uInt16 n=0; n<SFX_SPLITWINDOWS_MAX; n++ )
+ {
+ // The SplitWindows excludes direct ChildWindows of the WorkWindows
+ // and receives the docked window.
+
+ SfxChildAlignment eAlign =
+ ( n == SFX_SPLITWINDOWS_LEFT ? SfxChildAlignment::LEFT :
+ n == SFX_SPLITWINDOWS_RIGHT ? SfxChildAlignment::RIGHT :
+ n == SFX_SPLITWINDOWS_TOP ? SfxChildAlignment::TOP :
+ SfxChildAlignment::BOTTOM );
+ VclPtr<SfxSplitWindow> pSplitWin = VclPtr<SfxSplitWindow>::Create(pWorkWin, eAlign, this, true );
+ pSplit[n] = pSplitWin;
+ }
+
+ nOrigMode = SfxVisibilityFlags::Standard;
+ nUpdateMode = SfxVisibilityFlags::Standard;
+}
+
+
+// Destructor
+
+SfxWorkWindow::~SfxWorkWindow()
+{
+
+ // Delete SplitWindows
+ for (VclPtr<SfxSplitWindow> & p : pSplit)
+ {
+ if (p->GetWindowCount())
+ ReleaseChild_Impl(*p);
+ p.disposeAndClear();
+ }
+
+ // Delete help structure for Child-Windows
+ DBG_ASSERT( aChildren.empty(), "dangling children" );
+
+ if ( m_xLayoutManagerListener.is() )
+ m_xLayoutManagerListener->dispose();
+}
+
+void SfxWorkWindow::Lock_Impl( bool bLock )
+{
+ if ( bLock )
+ m_nLock++;
+ else
+ --m_nLock;
+ if ( m_nLock<0 )
+ {
+ OSL_FAIL("Lock count underflow!");
+ assert(m_nLock >= 0);
+ m_nLock = 0;
+ }
+
+ if ( !m_nLock )
+ ArrangeChildren_Impl();
+}
+
+
+// Helper method to release the child lists. Should the destructor not be
+// called after this, instead work continues, then space for the object bars
+// and split windows has to be reserved in the same way as in the constructor
+// of SfxWorkWindow.
+
+void SfxWorkWindow::DeleteControllers_Impl()
+{
+
+ // Lock SplitWindows (which means suppressing the Resize-Reaction of the
+ // DockingWindows)
+ for (size_t n=0; n<SFX_SPLITWINDOWS_MAX; n++ )
+ {
+ VclPtr<SfxSplitWindow> const &p = pSplit[n];
+ if (p->GetWindowCount())
+ p->Lock();
+ }
+
+ // Delete Child-Windows
+ while(!aChildWins.empty())
+ {
+ std::unique_ptr<SfxChildWin_Impl> pCW = std::move(*aChildWins.begin());
+ aChildWins.erase(aChildWins.begin());
+ SfxChildWindow *pChild = pCW->pWin;
+ if (pChild)
+ {
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ vcl::Window* pWindow = pChild->GetWindow();
+ if (pWindow)
+ {
+ pWindow->ReleaseLOKNotifier();
+ }
+ }
+ pChild->Hide();
+
+ // If the child window is a direct child window and not in a
+ // SplitWindow, cancel it at the workwindow.
+ // After TH a cancellation on the SplitWindow is not necessary
+ // since this window is also destroyed (see below).
+ if (pCW->pCli)
+ {
+ if (pChild->GetController())
+ ReleaseChild_Impl(*pChild->GetController());
+ else
+ ReleaseChild_Impl(*pChild->GetWindow());
+ }
+
+ pCW->pWin = nullptr;
+ pWorkWin->GetSystemWindow()->GetTaskPaneList()->RemoveWindow( pChild->GetWindow() );
+ pChild->Destroy();
+ }
+
+ // ATTENTION: The array itself is cleared after this loop!!
+ // Therefore we have to set every array entry to zero as it could be
+ // accessed by calling pChild->Destroy().
+ // Window::NotifyAllChildren() calls SfxWorkWindow::DataChanged_Impl for
+ // 8-bit displays (WM_QUERYPALETTECHANGED message due to focus change)!!
+ }
+
+ Reference< css::frame::XFrame > xFrame = GetFrameInterface();
+ Reference< css::beans::XPropertySet > xPropSet( xFrame, UNO_QUERY );
+ Reference< css::frame::XLayoutManager > xLayoutManager;
+ if ( xPropSet.is() )
+ {
+ try
+ {
+ Any aValue = xPropSet->getPropertyValue( g_aLayoutManagerPropName );
+ aValue >>= xLayoutManager;
+ }
+ catch ( Exception& )
+ {
+ }
+ }
+
+ if ( xLayoutManager.is() )
+ {
+ xLayoutManager->reset();
+
+ // Delete StatusBar
+ ResetStatusBar_Impl();
+
+ // Delete ObjectBars (this is done last, so that aChildren does not
+ // receive dead Pointers)
+ for (SfxObjectBar_Impl & i : aObjBarList)
+ {
+ // Not every position must be occupied
+ ToolbarId eId = i.eId;
+ if (eId != ToolbarId::None)
+ i.eId = ToolbarId::None;
+ }
+ }
+
+ // ObjectBars are all released at once, since they occupy a
+ // fixed contiguous area in the array pChild
+ aChildren.clear();
+ bSorted = false;
+
+ nChildren = 0;
+}
+
+
+// for placing the child window.
+
+void SfxWorkWindow::ArrangeChildren_Impl( bool bForce )
+{
+ if ( pFrame->IsClosing_Impl() || ( m_nLock && !bForce ))
+ return;
+
+ SfxInPlaceClient *pClient = nullptr;
+ SfxViewFrame *pF = pFrame->GetCurrentViewFrame();
+ if ( pF && pF->GetViewShell() )
+ pClient = pF->GetViewShell()->GetIPClient();
+
+ if ( pClient )
+ return;
+
+ aClientArea = GetTopRect_Impl();
+ if ( aClientArea.IsEmpty() )
+ return;
+
+ SvBorder aBorder;
+ if ( nChildren && IsVisible_Impl() )
+ aBorder = Arrange_Impl();
+ // If the current application document contains an IPClient, then the
+ // object through SetTopToolFramePixel has to be assigned the available
+ // space. The object will then point to its UITools and sets the app border
+ // (-> SfxInPlaceEnv_Impl:: ArrangeChildren_Impl ()). Otherwise the
+ // app border is set here directly to possibly overwrite the Border that
+ // was set by an object from another document. The object does not set
+ // the SetAppBorder when it removes its UI tools so that no-dithering
+ // ObjectBar arises.
+ // (->SfxInPlaceEnv_Impl::ArrangeChildren_Impl())
+
+ pMasterFrame->SetToolSpaceBorderPixel_Impl( aBorder );
+
+ ArrangeAutoHideWindows( nullptr );
+}
+
+void SfxWorkWindow::FlushPendingChildSizes()
+{
+ // tdf#116865, if any windows are being resized, i.e. their
+ // resize timer is active, then calling GetSizePixel on
+ // them forces the timer to fire and sets the final
+ // size to which they are getting resized towards.
+ for (size_t i = 0; i < aChildren.size(); ++i)
+ {
+ SfxChild_Impl *pCli = aChildren[i].get();
+ if (!pCli || !pCli->pWin)
+ continue;
+ (void)pCli->pWin->GetSizePixel();
+ }
+}
+
+SvBorder SfxWorkWindow::Arrange_Impl()
+
+/* [Description]
+
+ This method organizes all visible child windows so that the docked window
+ sorted in order from the outside to the inside are placed after one
+ another. If a visible window does not fit anymore into the free
+ ClientArea, it is set to "not visible".
+*/
+{
+ //tdf#116865 trigger pending sizing timers now so we arrange
+ //with the final size of the client area.
+ //
+ //Otherwise calling GetSizePixel in the following loop will trigger the
+ //timers, causing reentry into Arrange_Impl again where the inner
+ //Arrange_Impl arranges with the final size, and then returns to this outer
+ //Arrange_Impl which would rearrange with the old client area size
+ FlushPendingChildSizes();
+ aClientArea = GetTopRect_Impl();
+ aUpperClientArea = aClientArea;
+
+ SvBorder aBorder;
+ if ( !nChildren )
+ return aBorder;
+
+ if (!bSorted)
+ Sort_Impl();
+
+ Point aPos;
+ Size aSize;
+ tools::Rectangle aTmp( aClientArea );
+
+ for (sal_uInt16 n : aSortedList)
+ {
+ SfxChild_Impl* pCli = aChildren[n].get();
+ if ( !pCli->pWin )
+ continue;
+
+ // First, we assume that there is room for the window.
+ pCli->nVisible |= SfxChildVisibility::FITS_IN;
+
+ // Skip invisible windows
+ if (pCli->nVisible != SfxChildVisibility::VISIBLE)
+ continue;
+
+ if ( pCli->bResize )
+ aSize = pCli->aSize;
+ else
+ aSize = pCli->pWin->GetSizePixel();
+
+ SvBorder aTemp = aBorder;
+ bool bAllowHiding = true;
+ switch ( pCli->eAlign )
+ {
+ case SfxChildAlignment::HIGHESTTOP:
+ case SfxChildAlignment::TOP:
+ case SfxChildAlignment::TOOLBOXTOP:
+ case SfxChildAlignment::LOWESTTOP:
+ aSize.setWidth( aTmp.GetWidth() );
+ if ( pCli->pWin->GetType() == WindowType::SPLITWINDOW )
+ aSize = static_cast<SplitWindow *>(pCli->pWin.get())->CalcLayoutSizePixel( aSize );
+ bAllowHiding = false;
+ aBorder.Top() += aSize.Height();
+ aPos = aTmp.TopLeft();
+ aTmp.AdjustTop(aSize.Height() );
+ if ( pCli->eAlign == SfxChildAlignment::HIGHESTTOP )
+ aUpperClientArea.AdjustTop(aSize.Height() );
+ break;
+
+ case SfxChildAlignment::LOWESTBOTTOM:
+ case SfxChildAlignment::BOTTOM:
+ case SfxChildAlignment::TOOLBOXBOTTOM:
+ case SfxChildAlignment::HIGHESTBOTTOM:
+ aSize.setWidth( aTmp.GetWidth() );
+ if ( pCli->pWin->GetType() == WindowType::SPLITWINDOW )
+ aSize = static_cast<SplitWindow *>(pCli->pWin.get())->CalcLayoutSizePixel( aSize );
+ aBorder.Bottom() += aSize.Height();
+ aPos = aTmp.BottomLeft();
+ aPos.AdjustY( -(aSize.Height()-1) );
+ aTmp.AdjustBottom( -(aSize.Height()) );
+ if ( pCli->eAlign == SfxChildAlignment::LOWESTBOTTOM )
+ aUpperClientArea.AdjustBottom( -(aSize.Height()) );
+ break;
+
+ case SfxChildAlignment::FIRSTLEFT:
+ case SfxChildAlignment::LEFT:
+ case SfxChildAlignment::LASTLEFT:
+ case SfxChildAlignment::TOOLBOXLEFT:
+ aSize.setHeight( aTmp.GetHeight() );
+ if ( pCli->pWin->GetType() == WindowType::SPLITWINDOW )
+ aSize = static_cast<SplitWindow *>(pCli->pWin.get())->CalcLayoutSizePixel( aSize );
+ bAllowHiding = false;
+ aBorder.Left() += aSize.Width();
+ aPos = aTmp.TopLeft();
+ aTmp.AdjustLeft(aSize.Width() );
+ if ( pCli->eAlign != SfxChildAlignment::TOOLBOXLEFT )
+ aUpperClientArea.AdjustLeft(aSize.Width() );
+ break;
+
+ case SfxChildAlignment::FIRSTRIGHT:
+ case SfxChildAlignment::RIGHT:
+ case SfxChildAlignment::LASTRIGHT:
+ case SfxChildAlignment::TOOLBOXRIGHT:
+ aSize.setHeight( aTmp.GetHeight() );
+ if ( pCli->pWin->GetType() == WindowType::SPLITWINDOW )
+ aSize = static_cast<SplitWindow *>(pCli->pWin.get())->CalcLayoutSizePixel( aSize );
+ aBorder.Right() += aSize.Width();
+ aPos = aTmp.TopRight();
+ aPos.AdjustX( -(aSize.Width()-1) );
+ aTmp.AdjustRight( -(aSize.Width()) );
+ if ( pCli->eAlign != SfxChildAlignment::TOOLBOXRIGHT )
+ aUpperClientArea.AdjustRight( -(aSize.Width()) );
+ break;
+
+ default:
+ pCli->aSize = pCli->pWin->GetSizePixel();
+ pCli->bResize = false;
+ continue;
+ }
+
+ pCli->pWin->SetPosSizePixel( aPos, aSize );
+ pCli->bResize = false;
+ pCli->aSize = aSize;
+ if( bAllowHiding && !RequestTopToolSpacePixel_Impl( aBorder ) )
+ {
+ pCli->nVisible ^= SfxChildVisibility::FITS_IN;
+ aBorder = aTemp;
+ }
+ }
+
+ if ( aClientArea.GetWidth() >= aBorder.Left() + aBorder.Right() )
+ {
+ aClientArea.AdjustLeft(aBorder.Left() );
+ aClientArea.AdjustRight( -(aBorder.Right()) );
+ }
+ else
+ {
+ aBorder.Left() = aClientArea.Left();
+ aBorder.Right() = aClientArea.Right();
+ aClientArea.SetRight( aTmp.Left() );
+ aClientArea.SetLeft( aTmp.Left() );
+ }
+
+ if ( aClientArea.GetHeight() >= aBorder.Top() + aBorder.Bottom() )
+ {
+ aClientArea.AdjustTop(aBorder.Top() );
+ aClientArea.AdjustBottom( -(aBorder.Bottom()) );
+ }
+ else
+ {
+ aBorder.Top() = aClientArea.Top();
+ aBorder.Bottom() = aClientArea.Bottom();
+ aClientArea.SetTop(aTmp.Top());
+ aClientArea.SetBottom(aTmp.Top());
+ }
+
+ return IsDockingAllowed() ? aBorder : SvBorder();
+}
+
+bool SfxWorkWindow::PrepareClose_Impl()
+{
+ for (const std::unique_ptr<SfxChildWin_Impl> &pCW : aChildWins)
+ {
+ SfxChildWindow *pChild = pCW->pWin;
+ if ( pChild && !pChild->QueryClose() )
+ return false;
+ }
+
+ return true;
+}
+
+SfxChild_Impl* SfxWorkWindow::RegisterChild_Impl( vcl::Window& rWindow,
+ SfxChildAlignment eAlign )
+{
+ DBG_ASSERT( aChildren.size() < 255, "too many children" );
+ DBG_ASSERT( SfxChildAlignValid(eAlign), "invalid align" );
+ DBG_ASSERT( !FindChild_Impl(&rWindow), "child registered more than once" );
+
+
+ if ( rWindow.GetParent() != pWorkWin )
+ rWindow.SetParent( pWorkWin );
+
+ auto pChild = std::make_unique<SfxChild_Impl>(rWindow, rWindow.GetSizePixel(),
+ eAlign, rWindow.IsVisible());
+
+ aChildren.push_back(std::move(pChild));
+ bSorted = false;
+ nChildren++;
+ return aChildren.back().get();
+}
+
+SfxChild_Impl* SfxWorkWindow::RegisterChild_Impl(std::shared_ptr<SfxDialogController>& rController,
+ SfxChildAlignment eAlign )
+{
+ DBG_ASSERT( aChildren.size() < 255, "too many children" );
+ DBG_ASSERT( SfxChildAlignValid(eAlign), "invalid align" );
+
+ auto pChild = std::make_unique<SfxChild_Impl>(rController, eAlign);
+
+ aChildren.push_back(std::move(pChild));
+ bSorted = false;
+ nChildren++;
+ return aChildren.back().get();
+}
+
+void SfxWorkWindow::ReleaseChild_Impl( vcl::Window& rWindow )
+{
+
+ SfxChild_Impl *pChild = nullptr;
+ decltype(aChildren)::size_type nPos;
+ for ( nPos = 0; nPos < aChildren.size(); ++nPos )
+ {
+ pChild = aChildren[nPos].get();
+ if ( pChild && pChild->pWin == &rWindow )
+ {
+ bSorted = false;
+ nChildren--;
+ aChildren.erase(aChildren.begin() + nPos);
+ return;
+ }
+ }
+ OSL_FAIL( "releasing unregistered child" );
+}
+
+void SfxWorkWindow::ReleaseChild_Impl(const SfxDialogController& rController)
+{
+
+ SfxChild_Impl *pChild = nullptr;
+ decltype(aChildren)::size_type nPos;
+ for ( nPos = 0; nPos < aChildren.size(); ++nPos )
+ {
+ pChild = aChildren[nPos].get();
+ if (pChild && pChild->xController.get() == &rController)
+ {
+ bSorted = false;
+ nChildren--;
+ aChildren.erase(aChildren.begin() + nPos);
+ return;
+ }
+ }
+ OSL_FAIL( "releasing unregistered child" );
+}
+
+SfxChild_Impl* SfxWorkWindow::FindChild_Impl( const vcl::Window* rWindow ) const
+{
+
+ sal_uInt16 nCount = aChildren.size();
+ for ( sal_uInt16 nPos = 0; nPos < nCount; ++nPos )
+ {
+ SfxChild_Impl *pChild = aChildren[nPos].get();
+ if ( pChild && pChild->pWin == rWindow )
+ return pChild;
+ }
+
+ return nullptr;
+}
+
+void SfxWorkWindow::ShowChildren_Impl()
+{
+ bool bInvisible = ( !IsVisible_Impl() || ( !pWorkWin->IsReallyVisible() && !pWorkWin->IsReallyShown() ));
+
+ for (std::unique_ptr<SfxChild_Impl>& pCli : aChildren)
+ {
+ if (!pCli)
+ continue;
+ SfxChildWin_Impl* pCW = nullptr;
+ if (pCli->pWin || pCli->xController)
+ {
+ // We have to find the SfxChildWin_Impl to retrieve the
+ // SFX_CHILDWIN flags that can influence visibility.
+ for (const std::unique_ptr<SfxChildWin_Impl>& pCWin : aChildWins)
+ {
+ SfxChild_Impl* pChild = pCWin->pCli;
+ if ( pChild == pCli.get() )
+ {
+ pCW = pCWin.get();
+ break;
+ }
+ }
+
+ bool bVisible( !bInvisible );
+ if ( pCW )
+ {
+ // Check flag SFX_CHILDWIN_NEVERHIDE that forces us to show
+ // the child window even in situations where no child window is
+ // visible.
+ SfxChildWindowFlags nFlags = pCW->aInfo.nFlags;
+ bVisible = !bInvisible || ( nFlags & SfxChildWindowFlags::NEVERHIDE );
+ }
+
+ if ( SfxChildVisibility::VISIBLE == (pCli->nVisible & SfxChildVisibility::VISIBLE) && bVisible )
+ {
+ if (pCli->xController)
+ {
+ if (!pCli->xController->getDialog()->get_visible())
+ {
+ auto xController = pCli->xController;
+ weld::DialogController::runAsync(xController,
+ [=](sal_Int32 nResult){
+ if (nResult == nCloseResponseToJustHide)
+ return;
+ xController->Close();
+ });
+ }
+ }
+ else
+ {
+ ShowFlags nFlags = pCli->bSetFocus ? ShowFlags::NONE : ShowFlags::NoFocusChange | ShowFlags::NoActivate;
+ pCli->pWin->Show(true, nFlags);
+ }
+ pCli->bSetFocus = false;
+ }
+ else
+ {
+ if (pCli->xController)
+ {
+ if (pCli->xController->getDialog()->get_visible())
+ pCli->xController->response(RET_CLOSE);
+ }
+ else
+ pCli->pWin->Hide();
+ }
+ }
+ }
+}
+
+
+void SfxWorkWindow::HideChildren_Impl()
+{
+ for ( sal_uInt16 nPos = aChildren.size(); nPos > 0; --nPos )
+ {
+ SfxChild_Impl *pChild = aChildren[nPos-1].get();
+ if (!pChild)
+ continue;
+ if (pChild->xController)
+ pChild->xController->response(RET_CLOSE);
+ else if (pChild->pWin)
+ pChild->pWin->Hide();
+ }
+}
+
+void SfxWorkWindow::ResetObjectBars_Impl()
+{
+ for ( auto & n: aObjBarList )
+ n.bDestroy = true;
+
+ for ( auto & n: aChildWins )
+ n->nId = 0;
+}
+
+void SfxWorkWindow::SetObjectBar_Impl(sal_uInt16 nPos, SfxVisibilityFlags nFlags, ToolbarId eId)
+{
+ DBG_ASSERT( nPos < SFX_OBJECTBAR_MAX, "object bar position overflow" );
+
+ SfxObjectBar_Impl aObjBar;
+ aObjBar.eId = eId;
+ aObjBar.nMode = nFlags;
+
+ for (SfxObjectBar_Impl & rBar : aObjBarList)
+ {
+ if ( rBar.eId == aObjBar.eId )
+ {
+ rBar = aObjBar;
+ return;
+ }
+ }
+
+ aObjBarList.push_back( aObjBar );
+}
+
+bool SfxWorkWindow::IsVisible_Impl( SfxVisibilityFlags nMode ) const
+{
+ switch( nUpdateMode )
+ {
+ case SfxVisibilityFlags::Standard:
+ return true;
+ case SfxVisibilityFlags::Invisible:
+ return false;
+ case SfxVisibilityFlags::Client:
+ case SfxVisibilityFlags::Server:
+ return bool(nMode & nUpdateMode);
+ default:
+ return (nMode & nOrigMode ) ||
+ nOrigMode == SfxVisibilityFlags::Standard;
+ }
+}
+
+void SfxWorkWindow::UpdateObjectBars_Impl()
+{
+ if ( pFrame->IsClosing_Impl() )
+ return;
+
+ UpdateObjectBars_Impl2();
+
+ {
+ ArrangeChildren_Impl( false );
+
+ ShowChildren_Impl();
+ }
+
+ ShowChildren_Impl();
+}
+
+Reference< css::task::XStatusIndicator > SfxWorkWindow::GetStatusIndicator()
+{
+ Reference< css::beans::XPropertySet > xPropSet( GetFrameInterface(), UNO_QUERY );
+ Reference< css::frame::XLayoutManager > xLayoutManager;
+ Reference< css::task::XStatusIndicator > xStatusIndicator;
+
+ if ( xPropSet.is() )
+ {
+ Any aValue = xPropSet->getPropertyValue( g_aLayoutManagerPropName );
+ aValue >>= xLayoutManager;
+ if ( xLayoutManager.is() )
+ {
+ xLayoutManager->createElement( g_aProgressBarResName );
+ xLayoutManager->showElement( g_aProgressBarResName );
+
+ Reference< css::ui::XUIElement > xProgressBar =
+ xLayoutManager->getElement( g_aProgressBarResName );
+ if ( xProgressBar.is() )
+ {
+ xStatusIndicator.set( xProgressBar->getRealInterface(), UNO_QUERY );
+ }
+ }
+ }
+
+ return xStatusIndicator;
+}
+
+
+bool SfxWorkWindow::IsPluginMode( SfxObjectShell const * pObjShell )
+{
+ if ( pObjShell && pObjShell->GetMedium() )
+ {
+ const SfxBoolItem* pViewOnlyItem = SfxItemSet::GetItem<SfxBoolItem>(pObjShell->GetMedium()->GetItemSet(), SID_VIEWONLY, false);
+ if ( pViewOnlyItem && pViewOnlyItem->GetValue() )
+ return true;
+ }
+
+ return false;
+}
+
+
+css::uno::Reference< css::frame::XFrame > SfxWorkWindow::GetFrameInterface()
+{
+ css::uno::Reference< css::frame::XFrame > xFrame;
+
+ SfxDispatcher* pDispatcher( GetBindings().GetDispatcher() );
+ if ( pDispatcher )
+ {
+ SfxViewFrame* pViewFrame = pDispatcher->GetFrame();
+ if ( pViewFrame )
+ xFrame = pViewFrame->GetFrame().GetFrameInterface();
+ }
+
+ return xFrame;
+}
+
+
+void SfxWorkWindow::UpdateObjectBars_Impl2()
+{
+ // Lock SplitWindows (which means suppressing the Resize-Reaction of the
+ // DockingWindows)
+ for ( sal_uInt16 n=0; n<SFX_SPLITWINDOWS_MAX; n++ )
+ {
+ VclPtr<SfxSplitWindow> const & p = pSplit[n];
+ if (p->GetWindowCount())
+ p->Lock();
+ }
+
+ Reference< css::beans::XPropertySet > xPropSet( GetFrameInterface(), UNO_QUERY );
+ Reference< css::frame::XLayoutManager > xLayoutManager;
+
+ if ( xPropSet.is() )
+ {
+ Any aValue = xPropSet->getPropertyValue( g_aLayoutManagerPropName );
+ aValue >>= xLayoutManager;
+ }
+
+ if ( !xLayoutManager.is() )
+ return;
+
+ bool bPluginMode( false );
+ SfxDispatcher* pDispatcher( GetBindings().GetDispatcher() );
+
+ if ( pDispatcher )
+ {
+ SfxViewFrame* pViewFrame = pDispatcher->GetFrame();
+ if ( pViewFrame )
+ bPluginMode = IsPluginMode( pViewFrame->GetObjectShell() );
+ }
+
+ // Iterate over all Toolboxes
+ xLayoutManager->lock();
+ const bool isNotebookBarActive = sfx2::SfxNotebookBar::IsActive();
+ for ( auto const & n: aObjBarList )
+ {
+ ToolbarId eId = n.eId;
+ bool bDestroy = n.bDestroy;
+
+ // Determine the valid mode for the ToolBox
+ SfxVisibilityFlags nTbxMode = n.nMode;
+ bool bFullScreenTbx( nTbxMode & SfxVisibilityFlags::FullScreen );
+ nTbxMode &= ~SfxVisibilityFlags::FullScreen;
+ nTbxMode &= ~SfxVisibilityFlags::Viewer;
+
+ // Is a ToolBox required in this context ?
+ bool bModesMatching = (nUpdateMode != SfxVisibilityFlags::Invisible) && ((nTbxMode & nUpdateMode) == nUpdateMode);
+ if ( bDestroy || isNotebookBarActive)
+ {
+ OUString aTbxId = g_aTbxTypeName + GetResourceURLFromToolbarId(eId);
+ xLayoutManager->destroyElement( aTbxId );
+ }
+ else if ( eId != ToolbarId::None && ( ( bModesMatching && !bIsFullScreen ) ||
+ ( bIsFullScreen && bFullScreenTbx ) ) )
+ {
+ OUString aTbxId = g_aTbxTypeName + GetResourceURLFromToolbarId(eId);
+ if ( !IsDockingAllowed() && !xLayoutManager->isElementFloating( aTbxId ))
+ xLayoutManager->destroyElement( aTbxId );
+ else
+ {
+ xLayoutManager->requestElement( aTbxId );
+ if ( bPluginMode )
+ xLayoutManager->lockWindow( aTbxId );
+ }
+ }
+ else if ( eId != ToolbarId::None )
+ {
+ // Delete the Toolbox at this Position if possible
+ OUString aTbxId = g_aTbxTypeName + GetResourceURLFromToolbarId(eId);
+ xLayoutManager->destroyElement( aTbxId );
+ }
+ }
+
+ UpdateStatusBar_Impl();
+
+ // unlocking automatically forces Layout
+ xLayoutManager->unlock();
+
+ UpdateChildWindows_Impl();
+
+ // Unlock the SplitWindows again
+ for ( sal_uInt16 n=0; n<SFX_SPLITWINDOWS_MAX; n++ )
+ {
+ VclPtr<SfxSplitWindow> const & p = pSplit[n];
+ if (p->GetWindowCount())
+ p->Lock(false);
+ }
+}
+
+void SfxWorkWindow::UpdateChildWindows_Impl()
+{
+ // tdf#100870, tdf#101320: don't use range-based for loop when
+ // container is modified
+ for ( size_t n=0; n<aChildWins.size(); n++ )
+ {
+ // any current or in the context available Childwindows
+ SfxChildWin_Impl *pCW = aChildWins[n].get();
+ SfxChildWindow *pChildWin = pCW->pWin;
+ bool bCreate = false;
+ if ( pCW->nId && (pCW->aInfo.nFlags & SfxChildWindowFlags::ALWAYSAVAILABLE || IsVisible_Impl( pCW->nVisibility ) ) )
+ {
+ // In the context is an appropriate ChildWindow allowed;
+ // it is also turned on?
+ if ( pChildWin == nullptr && pCW->bCreate )
+ {
+ // Internal docking is only used for embedding into another
+ // container. We force the floating state of all floatable
+ // child windows.
+ if ( !bInternalDockingAllowed )
+ {
+ // Special case for all non-floatable child windows. We have
+ // to prevent the creation here!
+ bCreate = !( pCW->aInfo.nFlags & SfxChildWindowFlags::FORCEDOCK );
+ }
+ else if ( !IsDockingAllowed() || bIsFullScreen ) // || !bInternalDocking )
+ {
+ // In Presentation mode or FullScreen only FloatingWindows
+ SfxChildAlignment eAlign;
+ if ( pCW->aInfo.GetExtraData_Impl( &eAlign ) )
+ bCreate = ( eAlign == SfxChildAlignment::NOALIGNMENT );
+ }
+ else
+ bCreate = true;
+
+ if (pCW->aInfo.nFlags & SfxChildWindowFlags::NEVERCLONE)
+ pCW->bCreate = bCreate = false; // Don't create and remember that we haven't created.
+
+ // Currently, no window here, but it is enabled; windows
+ // Create window and if possible theContext
+ if ( bCreate )
+ CreateChildWin_Impl( pCW, false );
+
+ if ( !bAllChildrenVisible && pCW->pCli )
+ pCW->pCli->nVisible &= ~SfxChildVisibility::ACTIVE;
+ }
+ else if ( pChildWin )
+ {
+ // Window already exists, it should also be visible?
+ if ( ( !bIsFullScreen || pChildWin->GetAlignment() == SfxChildAlignment::NOALIGNMENT ) && bAllChildrenVisible )
+ {
+ // Update Mode is compatible; definitely enable it
+ bCreate = true;
+ if ( pCW->pCli )
+ {
+ // The window is a direct Child
+ if ((IsDockingAllowed() && bInternalDockingAllowed)
+ || pCW->pCli->eAlign == SfxChildAlignment::NOALIGNMENT)
+ pCW->pCli->nVisible |= SfxChildVisibility::NOT_HIDDEN;
+ }
+ else
+ {
+ if ( pCW->bCreate && IsDockingAllowed() && bInternalDockingAllowed )
+ // The window ia within a SplitWindow
+ static_cast<SfxDockingWindow*>(pChildWin->GetWindow())->Reappear_Impl();
+ }
+ }
+ }
+ }
+
+ if ( pChildWin && !bCreate )
+ {
+ if ( !pChildWin->QueryClose() || pChildWin->IsHideNotDelete() || Application::IsUICaptured() )
+ {
+ if ( pCW->pCli )
+ {
+ if ( pCW->pCli->nVisible & SfxChildVisibility::NOT_HIDDEN )
+ pCW->pCli->nVisible ^= SfxChildVisibility::NOT_HIDDEN;
+ }
+ else
+ static_cast<SfxDockingWindow*>(pChildWin->GetWindow())->Disappear_Impl();
+ }
+ else
+ RemoveChildWin_Impl( pCW );
+ }
+ }
+}
+
+void SfxWorkWindow::CreateChildWin_Impl( SfxChildWin_Impl *pCW, bool bSetFocus )
+{
+ pCW->aInfo.bVisible = true;
+
+ SfxChildWindow *pChildWin = SfxChildWindow::CreateChildWindow( pCW->nId, pWorkWin, &GetBindings(), pCW->aInfo).release();
+ if (!pChildWin)
+ return;
+
+ if ( bSetFocus )
+ bSetFocus = pChildWin->WantsFocus();
+ pChildWin->SetWorkWindow_Impl( this );
+
+ // At least the extra string is changed during the evaluation,
+ // also get it anewed
+ SfxChildWinInfo aInfo = pChildWin->GetInfo();
+ pCW->aInfo.aExtraString = aInfo.aExtraString;
+ pCW->aInfo.bVisible = aInfo.bVisible;
+ pCW->aInfo.nFlags |= aInfo.nFlags;
+
+ // The creation was successful
+ GetBindings().Invalidate(pCW->nId);
+
+ sal_uInt16 nPos = pChildWin->GetPosition();
+ if (nPos != CHILDWIN_NOPOS)
+ {
+ DBG_ASSERT(nPos < SFX_OBJECTBAR_MAX, "Illegal objectbar position!");
+ if ( aChildren[TbxMatch(nPos)] )// &&
+ {
+ // ChildWindow replaces ObjectBar
+ aChildren[TbxMatch(nPos)]->nVisible ^= SfxChildVisibility::NOT_HIDDEN;
+ }
+ }
+
+ // make childwin keyboard accessible
+ pWorkWin->GetSystemWindow()->GetTaskPaneList()->AddWindow( pChildWin->GetWindow() );
+
+ pCW->pWin = pChildWin;
+
+ if ( pChildWin->GetAlignment() == SfxChildAlignment::NOALIGNMENT || pChildWin->GetWindow()->GetParent() == pWorkWin)
+ {
+ // The window is not docked or docked outside of one split windows
+ // and must therefore be registered explicitly as a Child
+ if (pChildWin->GetController())
+ pCW->pCli = RegisterChild_Impl(pChildWin->GetController(), pChildWin->GetAlignment());
+ else
+ pCW->pCli = RegisterChild_Impl(*(pChildWin->GetWindow()), pChildWin->GetAlignment());
+ pCW->pCli->nVisible = SfxChildVisibility::VISIBLE;
+ if ( pChildWin->GetAlignment() != SfxChildAlignment::NOALIGNMENT && bIsFullScreen )
+ pCW->pCli->nVisible ^= SfxChildVisibility::ACTIVE;
+ pCW->pCli->bSetFocus = bSetFocus;
+ }
+ else
+ {
+ // A docked window which parent is not a WorkingWindow, must lie
+ // in a SplitWindow and thus not be explicitly registered.
+ // This happens already in the initialization of SfxDockingWindows!
+ }
+
+ // Save the information in the INI file
+ SaveStatus_Impl(pChildWin, pCW->aInfo);
+}
+
+void SfxWorkWindow::RemoveChildWin_Impl( SfxChildWin_Impl *pCW )
+{
+ sal_uInt16 nId = pCW->nSaveId;
+ SfxChildWindow *pChildWin = pCW->pWin;
+
+ // Save the information in the INI file
+ SfxChildWindowFlags nFlags = pCW->aInfo.nFlags;
+ pCW->aInfo = pChildWin->GetInfo();
+ pCW->aInfo.nFlags |= nFlags;
+ SaveStatus_Impl(pChildWin, pCW->aInfo);
+
+ pChildWin->Hide();
+
+ if ( pCW->pCli )
+ {
+ // Child window is a direct child window and must therefore unregister
+ // itself from the WorkWindow
+ pCW->pCli = nullptr;
+ if (pChildWin->GetController())
+ ReleaseChild_Impl(*pChildWin->GetController());
+ else
+ ReleaseChild_Impl(*pChildWin->GetWindow());
+ }
+ else
+ {
+ // ChildWindow is within a SplitWindow and unregister itself in
+ // the destructor.
+ }
+
+ pWorkWin->GetSystemWindow()->GetTaskPaneList()->RemoveWindow( pChildWin->GetWindow() );
+ pCW->pWin = nullptr;
+ pChildWin->Destroy();
+
+ GetBindings().Invalidate( nId );
+}
+
+void SfxWorkWindow::ResetStatusBar_Impl()
+{
+ aStatBar.eId = StatusBarId::None;
+}
+
+void SfxWorkWindow::SetStatusBar_Impl(StatusBarId eId)
+{
+ if (eId != StatusBarId::None && bShowStatusBar && IsVisible_Impl())
+ aStatBar.eId = eId;
+}
+
+void SfxWorkWindow::UpdateStatusBar_Impl()
+{
+ Reference< css::beans::XPropertySet > xPropSet( GetFrameInterface(), UNO_QUERY );
+ Reference< css::frame::XLayoutManager > xLayoutManager;
+
+ Any aValue = xPropSet->getPropertyValue( g_aLayoutManagerPropName );
+ aValue >>= xLayoutManager;
+
+ // No status bar, if no ID is required or when in FullScreenView or
+ // if disabled
+ if (aStatBar.eId != StatusBarId::None && IsDockingAllowed() && bInternalDockingAllowed && bShowStatusBar &&
+ !bIsFullScreen)
+ {
+ // Id has changed, thus create a suitable Statusbarmanager, this takes
+ // over the current status bar;
+ if ( xLayoutManager.is() )
+ xLayoutManager->requestElement( g_aStatusBarResName );
+ }
+ else
+ {
+ // Destroy the current StatusBar
+ // The Manager only creates the Status bar, does not destroy it.
+ if ( xLayoutManager.is() )
+ xLayoutManager->destroyElement( g_aStatusBarResName );
+ }
+}
+
+void SfxWorkWindow::MakeVisible_Impl( bool bVis )
+{
+ if ( bVis )
+ nOrigMode = SfxVisibilityFlags::Standard;
+ else
+ nOrigMode = SfxVisibilityFlags::Invisible;
+
+ if ( nOrigMode != nUpdateMode)
+ nUpdateMode = nOrigMode;
+}
+
+bool SfxWorkWindow::IsVisible_Impl() const
+{
+ return nOrigMode != SfxVisibilityFlags::Invisible;
+}
+
+
+void SfxWorkWindow::HidePopups_Impl(bool bHide, sal_uInt16 nId )
+{
+ if (comphelper::LibreOfficeKit::isActive() && bHide)
+ return;
+
+ for (const std::unique_ptr<SfxChildWin_Impl>& i : aChildWins)
+ {
+ SfxChildWindow *pCW = i->pWin;
+ if (pCW && pCW->GetAlignment() == SfxChildAlignment::NOALIGNMENT && pCW->GetType() != nId)
+ {
+ vcl::Window *pWin = pCW->GetWindow();
+ SfxChild_Impl *pChild = FindChild_Impl(pWin);
+ if (!pChild)
+ {
+ SAL_WARN("sfx.appl", "missing SfxChild_Impl child!");
+ continue;
+ }
+ if (bHide)
+ {
+ pChild->nVisible &= ~SfxChildVisibility::ACTIVE;
+ pCW->Hide();
+ }
+ else if ( !comphelper::LibreOfficeKit::isActive() ||
+ SfxChildVisibility::ACTIVE != (pChild->nVisible & SfxChildVisibility::ACTIVE) )
+ {
+ pChild->nVisible |= SfxChildVisibility::ACTIVE;
+ if ( SfxChildVisibility::VISIBLE == (pChild->nVisible & SfxChildVisibility::VISIBLE) )
+ pCW->Show( ShowFlags::NoFocusChange | ShowFlags::NoActivate );
+ }
+ }
+ }
+}
+
+
+void SfxWorkWindow::ConfigChild_Impl(SfxChildIdentifier eChild,
+ SfxDockingConfig eConfig, sal_uInt16 nId)
+{
+ SfxDockingWindow* pDockWin=nullptr;
+ sal_uInt16 nPos = USHRT_MAX;
+ vcl::Window *pWin=nullptr;
+ SfxChildWin_Impl *pCW = nullptr;
+
+ // configure direct childwindow
+ for (const std::unique_ptr<SfxChildWin_Impl>& i : aChildWins)
+ {
+ pCW = i.get();
+ SfxChildWindow *pChild = pCW->pWin;
+ if ( pChild && (pChild->GetType() == nId ))
+ {
+ if (SfxDockingWindow* pSfxDockingWindow = dynamic_cast<SfxDockingWindow*>(pChild->GetWindow()))
+ {
+ // it's a DockingWindow
+ pDockWin = pSfxDockingWindow;
+ }
+ else
+ {
+ // FloatingWindow or ModelessDialog
+ pWin = pChild->GetWindow();
+ }
+ break;
+ }
+ }
+
+ if ( pDockWin )
+ {
+ if ( eChild == SfxChildIdentifier::DOCKINGWINDOW || pDockWin->GetAlignment() == SfxChildAlignment::NOALIGNMENT )
+ {
+ if ( eChild == SfxChildIdentifier::SPLITWINDOW && eConfig == SfxDockingConfig::TOGGLEFLOATMODE)
+ {
+ // DockingWindow was dragged out of a SplitWindow
+ pCW->pCli = RegisterChild_Impl(*pDockWin, pDockWin->GetAlignment());
+ pCW->pCli->nVisible = SfxChildVisibility::VISIBLE;
+ }
+
+ pWin = pDockWin;
+ }
+ else
+ {
+ SfxSplitWindow *pSplitWin = GetSplitWindow_Impl(pDockWin->GetAlignment());
+
+ // configure DockingWindow inside a SplitWindow
+ if ( eConfig == SfxDockingConfig::TOGGLEFLOATMODE)
+ {
+ // DockingWindow was dragged into a SplitWindow
+ pCW->pCli = nullptr;
+ ReleaseChild_Impl(*pDockWin);
+ }
+
+ pWin = pSplitWin->GetSplitWindow();
+ if ( pSplitWin->GetWindowCount() == 1 )
+ static_cast<SplitWindow*>(pWin)->Show( true, ShowFlags::NoFocusChange | ShowFlags::NoActivate );
+ }
+ }
+
+ DBG_ASSERT( pCW, "Unknown window!" );
+
+ if ( !bSorted )
+ // windows may have been registered and released without an update until now
+ Sort_Impl();
+
+ decltype(aSortedList)::size_type n;
+ for ( n=0; n<aSortedList.size(); ++n )
+ {
+ SfxChild_Impl *pChild = aChildren[aSortedList[n]].get();
+ if ( pChild && pChild->pWin == pWin )
+ break;
+ }
+
+ if ( n < aSortedList.size() )
+ // sometimes called while toggling float mode
+ nPos = aSortedList[n];
+
+ switch ( eConfig )
+ {
+ case SfxDockingConfig::SETDOCKINGRECTS :
+ {
+ if (nPos == USHRT_MAX || !pDockWin)
+ return;
+
+ tools::Rectangle aOuterRect( GetTopRect_Impl() );
+ aOuterRect.SetPos( pWorkWin->OutputToScreenPixel( aOuterRect.TopLeft() ));
+ tools::Rectangle aInnerRect( aOuterRect );
+
+ // The current affected window is included in the calculation of
+ // the inner rectangle!
+ for (sal_uInt16 i : aSortedList)
+ {
+ SfxChild_Impl* pCli = aChildren[i].get();
+
+ if ( pCli && pCli->nVisible == SfxChildVisibility::VISIBLE && pCli->pWin )
+ {
+ switch ( pCli->eAlign )
+ {
+ case SfxChildAlignment::TOP:
+ // Object-Toolboxes come always last
+ aInnerRect.AdjustTop(pCli->aSize.Height() );
+ break;
+
+ case SfxChildAlignment::HIGHESTTOP:
+ // Always performed first
+ aInnerRect.AdjustTop(pCli->aSize.Height() );
+ break;
+
+ case SfxChildAlignment::LOWESTTOP:
+ // Is only counted if it is the current window
+ if ( i == nPos )
+ aInnerRect.AdjustTop(pCli->aSize.Height() );
+ break;
+
+ case SfxChildAlignment::BOTTOM:
+ // Object-Toolboxes come always last
+ aInnerRect.AdjustBottom( -(pCli->aSize.Height()) );
+ break;
+
+ case SfxChildAlignment::LOWESTBOTTOM:
+ // Always performed first
+ aInnerRect.AdjustBottom( -(pCli->aSize.Height()) );
+ break;
+
+ case SfxChildAlignment::HIGHESTBOTTOM:
+ // Is only counted if it is the current window
+ if ( i == nPos )
+ aInnerRect.AdjustBottom( -(pCli->aSize.Height()) );
+ break;
+
+ case SfxChildAlignment::LEFT:
+ // Toolboxes come always last
+ aInnerRect.AdjustLeft(pCli->aSize.Width() );
+ break;
+
+ case SfxChildAlignment::FIRSTLEFT:
+ // Always performed first
+ aInnerRect.AdjustLeft(pCli->aSize.Width() );
+ break;
+
+ case SfxChildAlignment::LASTLEFT:
+ // Is only counted if it is the current window
+ if (i == nPos)
+ aInnerRect.AdjustLeft(pCli->aSize.Width() );
+ break;
+
+ case SfxChildAlignment::RIGHT:
+ // Toolboxes come always last
+ aInnerRect.AdjustRight( -(pCli->aSize.Width()) );
+ break;
+
+ case SfxChildAlignment::FIRSTRIGHT:
+ // Is only counted if it is the current window
+ if (i == nPos)
+ aInnerRect.AdjustRight( -(pCli->aSize.Width()) );
+ break;
+
+ case SfxChildAlignment::LASTRIGHT:
+ // Always performed first
+ aInnerRect.AdjustRight( -(pCli->aSize.Width()) );
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+
+ pDockWin->SetDockingRects(aOuterRect, aInnerRect);
+ break;
+ }
+
+ case SfxDockingConfig::ALIGNDOCKINGWINDOW :
+ case SfxDockingConfig::TOGGLEFLOATMODE:
+ {
+ if ( nPos == USHRT_MAX && !pCW )
+ return;
+
+ SfxChildAlignment eAlign = SfxChildAlignment::NOALIGNMENT;
+ SfxChild_Impl *pCli = ( nPos != USHRT_MAX ) ? aChildren[nPos].get() : nullptr;
+ if ( pCli && pDockWin )
+ {
+ eAlign = pDockWin->GetAlignment();
+ if ( eChild == SfxChildIdentifier::DOCKINGWINDOW || eAlign == SfxChildAlignment::NOALIGNMENT)
+ {
+ // configuration inside the SplitWindow, no change for the SplitWindows' configuration
+ pCli->bResize = true;
+ pCli->aSize = pDockWin->GetSizePixel();
+ }
+ }
+
+ if ( pCli )
+ {
+ if( pCli->eAlign != eAlign )
+ {
+ bSorted = false;
+ pCli->eAlign = eAlign;
+ }
+
+ ArrangeChildren_Impl();
+ ShowChildren_Impl();
+ }
+
+ if ( pCW && pCW->pWin )
+ {
+ // store changed configuration
+ SfxChildWindowFlags nFlags = pCW->aInfo.nFlags;
+ pCW->aInfo = pCW->pWin->GetInfo();
+ pCW->aInfo.nFlags |= nFlags;
+ SaveStatus_Impl( pCW->pWin, pCW->aInfo);
+ }
+
+ break;
+ }
+ }
+}
+
+
+void SfxWorkWindow::SetChildWindowVisible_Impl( sal_uInt32 lId, bool bEnabled, SfxVisibilityFlags nMode )
+{
+ sal_uInt16 nId = static_cast<sal_uInt16>( lId & 0xFFFF );
+
+ SfxChildWin_Impl *pCW=nullptr;
+
+ if ( !pCW )
+ {
+ // If no Parent or the Parent us still unknown, then search here
+ sal_uInt16 nCount = aChildWins.size();
+ for (sal_uInt16 n=0; n<nCount; n++)
+ if (aChildWins[n]->nSaveId == nId)
+ {
+ pCW = aChildWins[n].get();
+ break;
+ }
+ }
+
+ if ( !pCW )
+ {
+ // If new, then initialize, add this here depending on the flag or
+ // the Parent
+ pCW = new SfxChildWin_Impl( lId );
+ pCW->nId = nId;
+ InitializeChild_Impl( pCW );
+ aChildWins.push_back( std::unique_ptr<SfxChildWin_Impl>(pCW) );
+ }
+
+ pCW->nId = nId;
+ pCW->nVisibility = nMode;
+ pCW->bEnable = bEnabled;
+}
+
+
+// The on/off status of a ChildWindow is switched
+
+void SfxWorkWindow::ToggleChildWindow_Impl(sal_uInt16 nId, bool bSetFocus)
+{
+ sal_uInt16 nCount = aChildWins.size();
+ sal_uInt16 n;
+ for (n=0; n<nCount; n++)
+ if (aChildWins[n]->nId == nId)
+ break;
+
+ if ( n<nCount )
+ {
+ // The Window is already known
+ SfxChildWin_Impl *pCW = aChildWins[n].get();
+ SfxChildWindow *pChild = pCW->pWin;
+
+ bool bCreationAllowed( true );
+ if ( !bInternalDockingAllowed )
+ {
+ // Special case for all non-floatable child windows. We have
+ // to prevent the creation here!
+ bCreationAllowed = !( pCW->aInfo.nFlags & SfxChildWindowFlags::FORCEDOCK );
+ }
+
+ if ( bCreationAllowed )
+ {
+ if ( pCW->bCreate )
+ {
+ if ( pChild )
+ {
+ if ( pChild->QueryClose() )
+ {
+ pCW->bCreate = false;
+ // The Window should be switched off
+ pChild->SetVisible_Impl( false );
+ RemoveChildWin_Impl( pCW );
+ }
+ }
+ else
+ {
+ // no actual Window exists, yet => just remember the "switched off" state
+ pCW->bCreate = false;
+ }
+ }
+ else
+ {
+ pCW->bCreate = true;
+ if ( pChild )
+ {
+ ShowChildWindow_Impl( nId, true, bSetFocus );
+ }
+ else
+ {
+ // create actual Window
+ CreateChildWin_Impl( pCW, bSetFocus );
+ if ( !pCW->pWin )
+ // no success
+ pCW->bCreate = false;
+ }
+ }
+ }
+
+ ArrangeChildren_Impl();
+ ShowChildren_Impl();
+
+ if ( pCW->bCreate && bCreationAllowed )
+ {
+ if ( !pCW->pCli )
+ {
+ SfxDockingWindow *pDock =
+ static_cast<SfxDockingWindow*>( pCW->pWin->GetWindow() );
+ if ( pDock->IsAutoHide_Impl() )
+ pDock->AutoShow_Impl();
+ }
+ }
+
+ return;
+ }
+
+#ifdef DBG_UTIL
+ nCount = aChildWins.size();
+ for (n=0; n<nCount; n++)
+ if (aChildWins[n]->nSaveId == nId)
+ break;
+
+ if ( n < nCount )
+ {
+ OSL_FAIL("The ChildWindow is not in context!");
+ }
+ else
+ {
+ OSL_FAIL("The ChildWindow is not registered!");
+ }
+#endif
+}
+
+
+bool SfxWorkWindow::HasChildWindow_Impl(sal_uInt16 nId)
+{
+ sal_uInt16 nCount = aChildWins.size();
+ sal_uInt16 n;
+ for (n=0; n<nCount; n++)
+ if (aChildWins[n]->nSaveId == nId)
+ break;
+
+ if (n<nCount)
+ {
+ SfxChildWin_Impl *pCW = aChildWins[n].get();
+ SfxChildWindow *pChild = pCW->pWin;
+ return ( pChild && pCW->bCreate );
+ }
+
+ return false;
+}
+
+bool SfxWorkWindow::IsFloating( sal_uInt16 nId )
+{
+ SfxChildWin_Impl *pCW=nullptr;
+
+ if ( !pCW )
+ {
+ // If no Parent or the Parent us still unknown, then search here
+ sal_uInt16 nCount = aChildWins.size();
+ for (sal_uInt16 n=0; n<nCount; n++)
+ if (aChildWins[n]->nSaveId == nId)
+ {
+ pCW = aChildWins[n].get();
+ break;
+ }
+ }
+
+ if ( !pCW )
+ {
+ // If new, then initialize, add this here depending on the flag or
+ // the Parent
+ pCW = new SfxChildWin_Impl( nId );
+ pCW->bEnable = false;
+ pCW->nId = 0;
+ pCW->nVisibility = SfxVisibilityFlags::Invisible;
+ InitializeChild_Impl( pCW );
+ aChildWins.push_back( std::unique_ptr<SfxChildWin_Impl>(pCW) );
+ }
+
+ SfxChildAlignment eAlign;
+ if ( pCW->aInfo.GetExtraData_Impl( &eAlign ) )
+ return( eAlign == SfxChildAlignment::NOALIGNMENT );
+ else
+ return true;
+}
+
+
+bool SfxWorkWindow::KnowsChildWindow_Impl(sal_uInt16 nId)
+{
+ SfxChildWin_Impl *pCW=nullptr;
+ sal_uInt16 nCount = aChildWins.size();
+ sal_uInt16 n;
+ for (n=0; n<nCount; n++)
+ {
+ pCW = aChildWins[n].get();
+ if ( pCW->nSaveId == nId)
+ break;
+ }
+
+ if (n<nCount)
+ {
+ if ( !(pCW->aInfo.nFlags & SfxChildWindowFlags::ALWAYSAVAILABLE) && !IsVisible_Impl( pCW->nVisibility ) )
+ return false;
+ return pCW->bEnable;
+ }
+ else
+ return false;
+}
+
+
+void SfxWorkWindow::SetChildWindow_Impl(sal_uInt16 nId, bool bOn, bool bSetFocus)
+{
+ SfxChildWin_Impl *pCW=nullptr;
+ SfxWorkWindow *pWork = nullptr;
+
+ if ( !pCW )
+ {
+ // If no Parent or the Parent us still unknown, then search here
+ sal_uInt16 nCount = aChildWins.size();
+ for (sal_uInt16 n=0; n<nCount; n++)
+ if (aChildWins[n]->nSaveId == nId)
+ {
+ pCW = aChildWins[n].get();
+ pWork = this;
+ break;
+ }
+ }
+
+ if ( !pCW )
+ {
+ // If new, then initialize, add this here depending on the flag or
+ // the Parent
+ pCW = new SfxChildWin_Impl( nId );
+ InitializeChild_Impl( pCW );
+ if ( !pWork || pCW->aInfo.nFlags & SfxChildWindowFlags::TASK )
+ pWork = this;
+ pWork->aChildWins.push_back( std::unique_ptr<SfxChildWin_Impl>(pCW) );
+ }
+
+ if ( pCW->bCreate != bOn )
+ pWork->ToggleChildWindow_Impl(nId,bSetFocus);
+}
+
+
+void SfxWorkWindow::ShowChildWindow_Impl(sal_uInt16 nId, bool bVisible, bool bSetFocus)
+{
+ sal_uInt16 nCount = aChildWins.size();
+ SfxChildWin_Impl* pCW=nullptr;
+ sal_uInt16 n;
+ for (n=0; n<nCount; n++)
+ {
+ pCW = aChildWins[n].get();
+ if (pCW->nId == nId)
+ break;
+ }
+
+ if ( n<nCount )
+ {
+ SfxChildWindow *pChildWin = pCW->pWin;
+ if ( pChildWin )
+ {
+ if ( bVisible )
+ {
+ if ( pCW->pCli )
+ {
+ pCW->pCli->bSetFocus = bSetFocus;
+ pCW->pCli->nVisible = SfxChildVisibility::VISIBLE;
+ pChildWin->Show( bSetFocus && pChildWin->WantsFocus() ? ShowFlags::NONE : ShowFlags::NoFocusChange | ShowFlags::NoActivate );
+ }
+ else
+ static_cast<SfxDockingWindow*>(pChildWin->GetWindow())->Reappear_Impl();
+
+ }
+ else
+ {
+ if ( pCW->pCli )
+ {
+ pCW->pCli->nVisible = SfxChildVisibility::VISIBLE ^ SfxChildVisibility::NOT_HIDDEN;
+ pCW->pWin->Hide();
+ }
+ else
+ static_cast<SfxDockingWindow*>(pChildWin->GetWindow())->Disappear_Impl();
+
+ }
+
+ ArrangeChildren_Impl();
+ ShowChildren_Impl();
+ }
+ else if ( bVisible )
+ {
+ SetChildWindow_Impl( nId, true, bSetFocus );
+ pChildWin = pCW->pWin;
+ }
+
+ if ( pChildWin )
+ {
+ pChildWin->SetVisible_Impl( bVisible );
+ SfxChildWindowFlags nFlags = pCW->aInfo.nFlags;
+ pCW->aInfo = pChildWin->GetInfo();
+ pCW->aInfo.nFlags |= nFlags;
+ if ( !pCW->bCreate )
+ SaveStatus_Impl( pChildWin, pCW->aInfo );
+ }
+
+ return;
+ }
+
+#ifdef DBG_UTIL
+ nCount = aChildWins.size();
+ for (n=0; n<nCount; n++)
+ if (aChildWins[n]->nSaveId == nId)
+ break;
+
+ if ( n<nCount )
+ {
+ OSL_FAIL("The ChildWindow is not in context!");
+ }
+ else
+ {
+ OSL_FAIL("The ChildWindow is not registered");
+ }
+#endif
+}
+
+
+SfxChildWindow* SfxWorkWindow::GetChildWindow_Impl(sal_uInt16 nId)
+{
+ sal_uInt16 nCount = aChildWins.size();
+ sal_uInt16 n;
+ for (n=0; n<nCount; n++)
+ if (aChildWins[n]->nSaveId == nId)
+ break;
+
+ if (n<nCount)
+ return aChildWins[n]->pWin;
+ return nullptr;
+}
+
+
+void SfxWorkWindow::ResetChildWindows_Impl()
+{
+ for (std::unique_ptr<SfxChildWin_Impl>& pChildWin : aChildWins)
+ {
+ pChildWin->nId = 0;
+ pChildWin->bEnable = false;
+ }
+}
+
+// returns the size of the area (client area) of the
+// parent windows, in which the ChildWindow can be fitted.
+
+tools::Rectangle SfxWorkWindow::GetTopRect_Impl() const
+{
+ return pMasterFrame->GetTopOuterRectPixel_Impl();
+}
+
+
+// Virtual method to find out if there is room for a ChildWindow in the
+// client area of the parent.
+
+bool SfxWorkWindow::RequestTopToolSpacePixel_Impl( SvBorder aBorder )
+{
+ return !(!IsDockingAllowed() ||
+ aClientArea.GetWidth() < aBorder.Left() + aBorder.Right() ||
+ aClientArea.GetHeight() < aBorder.Top() + aBorder.Bottom());
+}
+
+void SfxWorkWindow::SaveStatus_Impl(SfxChildWindow *pChild, const SfxChildWinInfo &rInfo)
+{
+ // The Status of the Presentation mode is not saved
+ if ( IsDockingAllowed() && bInternalDockingAllowed )
+ pChild->SaveStatus(rInfo);
+}
+
+void SfxWorkWindow::InitializeChild_Impl(SfxChildWin_Impl *pCW)
+{
+ SfxDispatcher *pDisp = pBindings->GetDispatcher_Impl();
+ SfxViewFrame *pViewFrame = pDisp ? pDisp->GetFrame() :nullptr;
+ SfxModule *pMod = pViewFrame ? SfxModule::GetActiveModule(pViewFrame) :nullptr;
+
+ OUString sModule;
+ if (pViewFrame)
+ {
+ try
+ {
+ uno::Reference< frame::XModuleManager2 > xModuleManager(
+ frame::ModuleManager::create(::comphelper::getProcessComponentContext()));
+ sModule = xModuleManager->identify(pViewFrame->GetFrame().GetFrameInterface());
+ SvtModuleOptions::EFactory eFac = SvtModuleOptions::ClassifyFactoryByServiceName(sModule);
+ sModule = SvtModuleOptions::GetFactoryShortName(eFac);
+ }
+ catch (...)
+ {
+ }
+ }
+
+ SfxChildWinFactory* pFact=nullptr;
+ SfxApplication *pApp = SfxGetpApp();
+ {
+ pFact = pApp->GetChildWinFactoryById(pCW->nSaveId);
+ if ( pFact )
+ {
+ pCW->aInfo = pFact->aInfo;
+ pCW->aInfo.aModule = sModule;
+ SfxChildWindow::InitializeChildWinFactory_Impl(
+ pCW->nSaveId, pCW->aInfo);
+ pCW->bCreate = pCW->aInfo.bVisible;
+ SfxChildWindowFlags nFlags = pFact->aInfo.nFlags;
+ if ( nFlags & SfxChildWindowFlags::TASK )
+ pCW->aInfo.nFlags |= SfxChildWindowFlags::TASK;
+ if ( nFlags & SfxChildWindowFlags::CANTGETFOCUS )
+ pCW->aInfo.nFlags |= SfxChildWindowFlags::CANTGETFOCUS;
+ if ( nFlags & SfxChildWindowFlags::FORCEDOCK )
+ pCW->aInfo.nFlags |= SfxChildWindowFlags::FORCEDOCK;
+ pFact->aInfo = pCW->aInfo;
+ return;
+ }
+ }
+
+ if ( !pMod )
+ return;
+
+ pFact = pMod->GetChildWinFactoryById(pCW->nSaveId);
+ if ( !pFact )
+ return;
+
+ pCW->aInfo = pFact->aInfo;
+ pCW->aInfo.aModule = sModule;
+ SfxChildWindow::InitializeChildWinFactory_Impl(
+ pCW->nSaveId, pCW->aInfo);
+ pCW->bCreate = pCW->aInfo.bVisible;
+ SfxChildWindowFlags nFlags = pFact->aInfo.nFlags;
+ if ( nFlags & SfxChildWindowFlags::TASK )
+ pCW->aInfo.nFlags |= SfxChildWindowFlags::TASK;
+ if ( nFlags & SfxChildWindowFlags::CANTGETFOCUS )
+ pCW->aInfo.nFlags |= SfxChildWindowFlags::CANTGETFOCUS;
+ if ( nFlags & SfxChildWindowFlags::FORCEDOCK )
+ pCW->aInfo.nFlags |= SfxChildWindowFlags::FORCEDOCK;
+ if ( nFlags & SfxChildWindowFlags::ALWAYSAVAILABLE )
+ pCW->aInfo.nFlags |= SfxChildWindowFlags::ALWAYSAVAILABLE;
+ pFact->aInfo = pCW->aInfo;
+}
+
+SfxSplitWindow* SfxWorkWindow::GetSplitWindow_Impl( SfxChildAlignment eAlign )
+{
+ switch ( eAlign )
+ {
+ case SfxChildAlignment::TOP:
+ return pSplit[2];
+
+ case SfxChildAlignment::BOTTOM:
+ return pSplit[3];
+
+ case SfxChildAlignment::LEFT:
+ return pSplit[0];
+
+ case SfxChildAlignment::RIGHT:
+ return pSplit[1];
+
+ default:
+ return nullptr;
+ }
+}
+
+void SfxWorkWindow::MakeChildrenVisible_Impl( bool bVis )
+{
+ bAllChildrenVisible = bVis;
+ if ( bVis )
+ {
+ if ( !bSorted )
+ Sort_Impl();
+ for (sal_uInt16 n : aSortedList)
+ {
+ SfxChild_Impl* pCli = aChildren[n].get();
+ if ( (pCli->eAlign == SfxChildAlignment::NOALIGNMENT) || (IsDockingAllowed() && bInternalDockingAllowed) )
+ pCli->nVisible |= SfxChildVisibility::ACTIVE;
+ }
+ }
+ else
+ {
+ if ( !bSorted )
+ Sort_Impl();
+ for (sal_uInt16 n : aSortedList)
+ {
+ SfxChild_Impl* pCli = aChildren[n].get();
+ pCli->nVisible &= ~SfxChildVisibility::ACTIVE;
+ }
+ }
+}
+
+bool SfxWorkWindow::IsAutoHideMode( const SfxSplitWindow *pSplitWin )
+{
+ for (const VclPtr<SfxSplitWindow> & pWin : pSplit)
+ {
+ if ( pWin.get() != pSplitWin && pWin->IsAutoHide( true ) )
+ return true;
+ }
+ return false;
+}
+
+
+void SfxWorkWindow::EndAutoShow_Impl( Point aPos )
+{
+ for (VclPtr<SfxSplitWindow> & p : pSplit)
+ {
+ if ( p && p->IsAutoHide(false) )
+ {
+ Point aLocalPos = p->ScreenToOutputPixel( aPos );
+ tools::Rectangle aRect( Point(), p->GetSizePixel() );
+ if ( !aRect.Contains( aLocalPos ) )
+ p->FadeOut();
+ }
+ }
+}
+
+void SfxWorkWindow::ArrangeAutoHideWindows( SfxSplitWindow *pActSplitWin )
+{
+ if ( m_nLock )
+ return;
+
+ tools::Rectangle aArea( aUpperClientArea );
+ for ( sal_uInt16 n=0; n<SFX_SPLITWINDOWS_MAX; n++ )
+ {
+ // Either dummy window or window in the auto-show-mode are processed
+ // (not pinned, FadeIn).
+ // Only the abandoned window may be invisible, because perhaps its
+ // size is just being calculated before it is displayed.
+ VclPtr<SfxSplitWindow> const & pSplitWin = pSplit[n];
+ bool bDummyWindow = !pSplitWin->IsFadeIn();
+ vcl::Window *pDummy = pSplitWin->GetSplitWindow();
+ vcl::Window *pWin = bDummyWindow ? pDummy : pSplitWin;
+ if ( (pSplitWin->IsPinned() && !bDummyWindow) || (!pWin->IsVisible() && pActSplitWin != pSplitWin) )
+ continue;
+
+ // Width and position of the dummy window as a starting point
+ Size aSize = pDummy->GetSizePixel();
+ Point aPos = pDummy->GetPosPixel();
+
+ switch ( n )
+ {
+ case 0 :
+ {
+ // Left SplitWindow
+ // Get the width of the Window yourself, if no DummyWindow
+ if ( !bDummyWindow )
+ aSize.setWidth( pSplitWin->GetSizePixel().Width() );
+
+ // If a Window is visible to the left, then the free region
+ // starts to the right from it, for example at the Client area
+ tools::Long nLeft = aPos.X() + aSize.Width();
+ if ( nLeft > aArea.Left() )
+ aArea.SetLeft( nLeft );
+ break;
+ }
+ case 1 :
+ {
+ // Right SplitWindow
+ // Position to correct the difference of the widths
+ aPos.AdjustX(aSize.Width() );
+
+ // Get the width of the Window yourself, if no DummyWindow
+ if ( !bDummyWindow )
+ aSize.setWidth( pSplitWin->GetSizePixel().Width() );
+
+ aPos.AdjustX( -(aSize.Width()) );
+
+ // If already a window is opened at the left side, then the
+ // right is not allowed to overlap this one.
+ if ( aPos.X() < aArea.Left() )
+ {
+ aPos.setX( aArea.Left() );
+ aSize.setWidth( aArea.GetWidth() );
+ }
+
+ // If a Window is visible to the right, then the free region
+ // starts to the left from it, for example at the Client area
+ tools::Long nRight = aPos.X();
+ if ( !aArea.IsWidthEmpty() && nRight < aArea.Right() )
+ aArea.SetRight( nRight );
+ break;
+ }
+ case 2 :
+ {
+ // Top SplitWindow
+ // Get the height of the Window yourself, if no DummyWindow
+ if ( !bDummyWindow )
+ aSize.setHeight( pSplitWin->GetSizePixel().Height() );
+
+
+ // Adjust width with regard to if a Window is already open
+ // to the left or right
+ aPos.setX( aArea.Left() );
+ aSize.setWidth( aArea.GetWidth() );
+
+ // If a Window is visible at the top, then the free region
+ // starts beneath it, for example at the Client area
+ tools::Long nTop = aPos.Y() + aSize.Height();
+ if ( nTop > aArea.Top() )
+ aArea.SetTop( nTop );
+ break;
+ }
+ case 3 :
+ {
+ // The bottom SplitWindow
+ // Position to correct the difference of the heights
+ aPos.AdjustY(aSize.Height() );
+
+ // Get the height of the Window yourself, if no DummyWindow
+ if ( !bDummyWindow )
+ aSize.setHeight( pSplitWin->GetSizePixel().Height() );
+
+ aPos.AdjustY( -(aSize.Height()) );
+
+ // Adjust width with regard to if a Window is already open
+ // to the left or right.
+ aPos.setX( aArea.Left() );
+ aSize.setWidth( aArea.GetWidth() );
+
+ // If already a window is opened at the top, then the
+ // bottom one is not allowed to overlap this one.
+ if ( aPos.Y() < aArea.Top() )
+ {
+ aPos.setY( aArea.Top() );
+ aSize.setHeight( aArea.GetHeight() );
+ }
+
+ break;
+ }
+ }
+
+ if ( !bDummyWindow )
+ // the FadeIn-Window is a Floating window, which coordinates are
+ // set in Screen coordinates.
+ pSplitWin->SetPosSizePixel( pWorkWin->OutputToScreenPixel(aPos), aSize );
+ else
+ // the docked DummyWindow
+ pDummy->SetPosSizePixel( aPos, aSize );
+ }
+}
+
+tools::Rectangle SfxWorkWindow::GetFreeArea( bool bAutoHide ) const
+{
+ if ( bAutoHide )
+ {
+ tools::Rectangle aArea( aClientArea );
+ for ( sal_uInt16 n=0; n<SFX_SPLITWINDOWS_MAX; n++ )
+ {
+ if ( pSplit[n]->IsPinned() || !pSplit[n]->IsVisible() )
+ continue;
+
+ Size aSize = pSplit[n]->GetSizePixel();
+ switch ( n )
+ {
+ case 0 :
+ aArea.AdjustLeft(aSize.Width() );
+ break;
+ case 1 :
+ aArea.AdjustRight( -(aSize.Width()) );
+ break;
+ case 2 :
+ aArea.AdjustTop(aSize.Height() );
+ break;
+ case 3 :
+ aArea.AdjustBottom( -(aSize.Height()) );
+ break;
+ }
+ }
+
+ return aArea;
+ }
+ else
+ return aClientArea;
+}
+
+void SfxWorkWindow::SetActiveChild_Impl( vcl::Window *pChild )
+{
+ pActiveChild = pChild;
+}
+
+void SfxWorkWindow::DataChanged_Impl()
+{
+ ArrangeChildren_Impl();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/appl/xpackcreator.cxx b/sfx2/source/appl/xpackcreator.cxx
new file mode 100644
index 000000000..b9aa2b53c
--- /dev/null
+++ b/sfx2/source/appl/xpackcreator.cxx
@@ -0,0 +1,164 @@
+/* -*- 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/ucb/XCommandEnvironment.hpp>
+#include <com/sun/star/io/IOException.hpp>
+#include <com/sun/star/io/XOutputStream.hpp>
+#include <com/sun/star/embed/XPackageStructureCreator.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+
+#include <comphelper/processfactory.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <sot/stg.hxx>
+#include <sot/storage.hxx>
+#include <tools/stream.hxx>
+#include <unotools/tempfile.hxx>
+#include <unotools/ucbhelper.hxx>
+#include <ucbhelper/content.hxx>
+
+using namespace css;
+
+namespace {
+
+class OPackageStructureCreator : public ::cppu::WeakImplHelper< embed::XPackageStructureCreator,
+ lang::XServiceInfo >
+{
+public:
+ OPackageStructureCreator() {}
+
+ // XPackageStructureCreator
+ virtual void SAL_CALL convertToPackage( const OUString& aFolderUrl, const uno::Reference< io::XOutputStream >& xTargetStream ) override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+ virtual uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+};
+
+
+void SAL_CALL OPackageStructureCreator::convertToPackage( const OUString& aFolderUrl,
+ const uno::Reference< io::XOutputStream >& xTargetStream )
+{
+ uno::Reference< ucb::XCommandEnvironment > xComEnv;
+
+ if ( !xTargetStream.is() )
+ throw io::IOException(); // TODO/LATER
+
+ bool bSuccess = false;
+ ::ucbhelper::Content aContent;
+ if( ::ucbhelper::Content::create( aFolderUrl, xComEnv, comphelper::getProcessComponentContext(), aContent ) )
+ {
+ OUString aTempURL = ::utl::TempFile().GetURL();
+ try {
+ if ( aContent.isFolder() )
+ {
+ UCBStorage* pUCBStorage = new UCBStorage( aContent,
+ aFolderUrl,
+ StreamMode::READ,
+ false,
+ true );
+ tools::SvRef<SotStorage> aStorage = new SotStorage( pUCBStorage );
+
+ if ( !aTempURL.isEmpty() )
+ {
+ SvFileStream aTempStream( aTempURL, StreamMode::STD_READWRITE );
+ tools::SvRef<SotStorage> aTargetStorage = new SotStorage( true, aTempStream );
+ aStorage->CopyTo( aTargetStorage.get() );
+ aTargetStorage->Commit();
+
+ if ( aStorage->GetError() || aTargetStorage->GetError() || aTempStream.GetError() )
+ throw io::IOException();
+
+ aTargetStorage = nullptr;
+ aStorage = nullptr;
+
+ aTempStream.Seek( 0 );
+
+ uno::Sequence< sal_Int8 > aSeq( 32000 );
+ sal_uInt32 nRead = 0;
+ do {
+ if ( aSeq.getLength() < 32000 )
+ aSeq.realloc( 32000 );
+
+ nRead = aTempStream.ReadBytes(aSeq.getArray(), 32000);
+ if ( nRead < 32000 )
+ aSeq.realloc( nRead );
+ xTargetStream->writeBytes( aSeq );
+ } while (aTempStream.good() && nRead);
+
+ if ( aTempStream.GetError() )
+ throw io::IOException();
+
+ bSuccess = true;
+ }
+ }
+ }
+ catch (const uno::RuntimeException&)
+ {
+ if ( !aTempURL.isEmpty() )
+ ::utl::UCBContentHelper::Kill( aTempURL );
+
+ throw;
+ }
+ catch (const io::IOException&)
+ {
+ if ( !aTempURL.isEmpty() )
+ ::utl::UCBContentHelper::Kill( aTempURL );
+
+ throw;
+ }
+ catch (const uno::Exception&)
+ {
+ }
+
+ if ( !aTempURL.isEmpty() )
+ ::utl::UCBContentHelper::Kill( aTempURL );
+ }
+
+ if ( !bSuccess )
+ throw io::IOException(); // TODO/LATER: can't proceed with creation
+}
+
+OUString SAL_CALL OPackageStructureCreator::getImplementationName()
+{
+ return "com.sun.star.comp.embed.PackageStructureCreator";
+}
+
+sal_Bool SAL_CALL OPackageStructureCreator::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+uno::Sequence< OUString > SAL_CALL OPackageStructureCreator::getSupportedServiceNames()
+{
+ return { "com.sun.star.embed.PackageStructureCreator", "com.sun.star.comp.embed.PackageStructureCreator" };
+}
+
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_embed_PackageStructureCreator_get_implementation(
+ css::uno::XComponentContext *,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new OPackageStructureCreator());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/bastyp/bitset.cxx b/sfx2/source/bastyp/bitset.cxx
new file mode 100644
index 000000000..b6980a890
--- /dev/null
+++ b/sfx2/source/bastyp/bitset.cxx
@@ -0,0 +1,108 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/log.hxx>
+#include <sal/types.h>
+
+#include <bitset.hxx>
+
+#include <string.h>
+
+// creates the asymmetric difference with another bitset
+
+IndexBitSet& IndexBitSet::operator-=(sal_uInt16 nBit)
+{
+ sal_uInt16 nBlock = nBit / 32;
+ sal_uInt32 nBitVal = 1U << (nBit % 32);
+
+ if ( nBlock >= nBlocks )
+ return *this;
+
+ if ( pBitmap[nBlock] & nBitVal )
+ {
+ pBitmap[nBlock] &= ~nBitVal;
+ }
+
+ return *this;
+}
+
+// unify with a single bit
+
+IndexBitSet& IndexBitSet::operator|=( sal_uInt16 nBit )
+{
+ sal_uInt16 nBlock = nBit / 32;
+ sal_uInt32 nBitVal = 1U << (nBit % 32);
+
+ if ( nBlock >= nBlocks )
+ {
+ sal_uInt32 *pNewMap = new sal_uInt32[nBlock+1];
+ memset( pNewMap + nBlocks, 0, 4 * (nBlock - nBlocks + 1) );
+
+ if ( pBitmap )
+ {
+ memcpy( pNewMap, pBitmap.get(), 4 * nBlocks );
+ }
+ pBitmap.reset(pNewMap);
+ nBlocks = nBlock+1;
+ }
+
+ if ( (pBitmap[nBlock] & nBitVal) == 0 )
+ {
+ pBitmap[nBlock] |= nBitVal;
+ }
+
+ return *this;
+}
+
+
+// determines if the bit is set (may be the only one)
+
+bool IndexBitSet::Contains( sal_uInt16 nBit ) const
+{
+ sal_uInt16 nBlock = nBit / 32;
+ sal_uInt32 nBitVal = 1U << (nBit % 32);
+
+ if ( nBlock >= nBlocks )
+ return false;
+ return ( nBitVal & pBitmap[nBlock] ) == nBitVal;
+}
+
+IndexBitSet::IndexBitSet()
+{
+ nBlocks = 0;
+}
+
+IndexBitSet::~IndexBitSet()
+{
+}
+
+sal_uInt16 IndexBitSet::GetFreeIndex()
+{
+ for(sal_uInt16 i=0;i<SAL_MAX_UINT16;i++)
+ if(!Contains(i))
+ {
+ *this|=i;
+ return i;
+ }
+ SAL_WARN( "sfx", "IndexBitSet contains more than SAL_MAX_UINT16 entries");
+ return 0;
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/bastyp/fltfnc.cxx b/sfx2/source/bastyp/fltfnc.cxx
new file mode 100644
index 000000000..56638fc1c
--- /dev/null
+++ b/sfx2/source/bastyp/fltfnc.cxx
@@ -0,0 +1,1111 @@
+/* -*- 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/uno/Exception.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/beans/NamedValue.hpp>
+#include <com/sun/star/container/XNameAccess.hpp>
+#include <com/sun/star/container/XEnumeration.hpp>
+#include <com/sun/star/document/XTypeDetection.hpp>
+#include <com/sun/star/container/XContainerQuery.hpp>
+#include <com/sun/star/io/XInputStream.hpp>
+#include <com/sun/star/task/XInteractionHandler.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+
+#include <comphelper/sequenceashashmap.hxx>
+
+#include <sot/exchange.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+#include <rtl/ustring.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <sal/log.hxx>
+#include <svl/stritem.hxx>
+
+#include <comphelper/processfactory.hxx>
+
+#include <sal/types.h>
+#include <com/sun/star/uno/Reference.hxx>
+#include <unotools/moduleoptions.hxx>
+#include <unotools/mediadescriptor.hxx>
+#include <tools/urlobj.hxx>
+
+#include <unotools/syslocale.hxx>
+#include <unotools/charclass.hxx>
+
+#include <sfx2/docfilt.hxx>
+#include <sfx2/fcontnr.hxx>
+#include <sfxtypes.hxx>
+#include <sfx2/docfile.hxx>
+#include <sfx2/strings.hrc>
+#include <sfx2/sfxresid.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/sfxsids.hrc>
+#include "fltlst.hxx"
+#include <arrdecl.hxx>
+
+#include <vector>
+#include <memory>
+
+#if defined(DBG_UTIL)
+unsigned SfxStack::nLevel = 0;
+#endif
+
+using namespace com::sun::star;
+
+static SfxFilterList_Impl* pFilterArr = nullptr;
+static bool bFirstRead = true;
+
+static void CreateFilterArr()
+{
+ static SfxFilterList_Impl theSfxFilterArray;
+ pFilterArr = &theSfxFilterArray;
+ static SfxFilterListener theSfxFilterListener;
+}
+
+static OUString ToUpper_Impl( const OUString &rStr )
+{
+ return SvtSysLocale().GetCharClass().uppercase( rStr );
+}
+
+class SfxFilterContainer_Impl
+{
+public:
+ OUString aName;
+
+ explicit SfxFilterContainer_Impl( const OUString& rName )
+ : aName( rName )
+ {
+ }
+};
+
+std::shared_ptr<const SfxFilter> SfxFilterContainer::GetFilter4EA(const OUString& rEA, SfxFilterFlags nMust, SfxFilterFlags nDont) const
+{
+ SfxFilterMatcher aMatch(pImpl->aName);
+ return aMatch.GetFilter4EA(rEA, nMust, nDont);
+}
+
+std::shared_ptr<const SfxFilter> SfxFilterContainer::GetFilter4Extension(const OUString& rExt, SfxFilterFlags nMust, SfxFilterFlags nDont) const
+{
+ SfxFilterMatcher aMatch(pImpl->aName);
+ return aMatch.GetFilter4Extension(rExt, nMust, nDont);
+}
+
+std::shared_ptr<const SfxFilter> SfxFilterContainer::GetFilter4FilterName(const OUString& rName, SfxFilterFlags nMust, SfxFilterFlags nDont) const
+{
+ SfxFilterMatcher aMatch(pImpl->aName);
+ return aMatch.GetFilter4FilterName(rName, nMust, nDont);
+}
+
+std::shared_ptr<const SfxFilter> SfxFilterContainer::GetAnyFilter( SfxFilterFlags nMust, SfxFilterFlags nDont ) const
+{
+ SfxFilterMatcher aMatch( pImpl->aName );
+ return aMatch.GetAnyFilter( nMust, nDont );
+}
+
+
+SfxFilterContainer::SfxFilterContainer( const OUString& rName )
+ : pImpl( new SfxFilterContainer_Impl( rName ) )
+{
+}
+
+
+SfxFilterContainer::~SfxFilterContainer()
+{
+}
+
+
+OUString const & SfxFilterContainer::GetName() const
+{
+ return pImpl->aName;
+}
+
+std::shared_ptr<const SfxFilter> SfxFilterContainer::GetDefaultFilter_Impl( std::u16string_view rName )
+{
+ // Try to find out the type of factory.
+ // Interpret given name as Service- and ShortName!
+ SvtModuleOptions aOpt;
+ SvtModuleOptions::EFactory eFactory = SvtModuleOptions::ClassifyFactoryByServiceName(rName);
+ if (eFactory == SvtModuleOptions::EFactory::UNKNOWN_FACTORY)
+ eFactory = SvtModuleOptions::ClassifyFactoryByShortName(rName);
+
+ // could not classify factory by its service nor by its short name.
+ // Must be an unknown factory! => return NULL
+ if (eFactory == SvtModuleOptions::EFactory::UNKNOWN_FACTORY)
+ return nullptr;
+
+ // For the following code we need some additional information.
+ OUString sServiceName = aOpt.GetFactoryName(eFactory);
+ OUString sDefaultFilter = aOpt.GetFactoryDefaultFilter(eFactory);
+
+ // Try to get the default filter. Don't forget to verify it.
+ // May the set default filter does not exists any longer or
+ // does not fit the given factory.
+ const SfxFilterMatcher aMatcher;
+ std::shared_ptr<const SfxFilter> pFilter = aMatcher.GetFilter4FilterName(sDefaultFilter);
+
+ if (
+ pFilter &&
+ !pFilter->GetServiceName().equalsIgnoreAsciiCase(sServiceName)
+ )
+ {
+ pFilter = nullptr;
+ }
+
+ // If at least no default filter could be located - use any filter of this
+ // factory.
+ if (!pFilter)
+ {
+ if ( bFirstRead )
+ ReadFilters_Impl();
+
+ for (const std::shared_ptr<const SfxFilter>& pCheckFilter : *pFilterArr)
+ {
+ if ( pCheckFilter->GetServiceName().equalsIgnoreAsciiCase(sServiceName) )
+ {
+ pFilter = pCheckFilter;
+ break;
+ }
+ }
+ }
+
+ return pFilter;
+}
+
+
+// Impl-Data is shared between all FilterMatchers of the same factory
+class SfxFilterMatcher_Impl
+{
+public:
+ OUString aName;
+ mutable SfxFilterList_Impl* pList; // is created on demand
+
+ void InitForIterating() const;
+ void Update() const;
+ explicit SfxFilterMatcher_Impl(const OUString &rName)
+ : aName(rName)
+ , pList(nullptr)
+ {
+ }
+ ~SfxFilterMatcher_Impl()
+ {
+ // SfxFilterMatcher_Impl::InitForIterating() will set pList to
+ // either the global filter array matcher pFilterArr, or to
+ // a new SfxFilterList_Impl.
+ if (pList != pFilterArr)
+ delete pList;
+ }
+};
+
+namespace
+{
+ std::vector<std::unique_ptr<SfxFilterMatcher_Impl> > aImplArr;
+ int nSfxFilterMatcherCount;
+
+ SfxFilterMatcher_Impl & getSfxFilterMatcher_Impl(const OUString &rName)
+ {
+ OUString aName;
+
+ if (!rName.isEmpty())
+ aName = SfxObjectShell::GetServiceNameFromFactory(rName);
+
+ // find the impl-Data of any comparable FilterMatcher that was created
+ // previously
+ for (std::unique_ptr<SfxFilterMatcher_Impl>& aImpl : aImplArr)
+ if (aImpl->aName == aName)
+ return *aImpl;
+
+ // first Matcher created for this factory
+ aImplArr.push_back(std::make_unique<SfxFilterMatcher_Impl>(aName));
+ return *aImplArr.back();
+ }
+}
+
+SfxFilterMatcher::SfxFilterMatcher( const OUString& rName )
+ : m_rImpl( getSfxFilterMatcher_Impl(rName) )
+{
+ ++nSfxFilterMatcherCount;
+}
+
+SfxFilterMatcher::SfxFilterMatcher()
+ : m_rImpl( getSfxFilterMatcher_Impl(OUString()) )
+{
+ // global FilterMatcher always uses global filter array (also created on
+ // demand)
+ ++nSfxFilterMatcherCount;
+}
+
+SfxFilterMatcher::~SfxFilterMatcher()
+{
+ --nSfxFilterMatcherCount;
+ if (nSfxFilterMatcherCount == 0)
+ aImplArr.clear();
+}
+
+void SfxFilterMatcher_Impl::Update() const
+{
+ if ( pList )
+ {
+ // this List was already used
+ pList->clear();
+ for (const std::shared_ptr<const SfxFilter>& pFilter : *pFilterArr)
+ {
+ if ( pFilter->GetServiceName() == aName )
+ pList->push_back( pFilter );
+ }
+ }
+}
+
+void SfxFilterMatcher_Impl::InitForIterating() const
+{
+ if ( pList )
+ return;
+
+ if ( bFirstRead )
+ // global filter array has not been created yet
+ SfxFilterContainer::ReadFilters_Impl();
+
+ if ( !aName.isEmpty() )
+ {
+ // matcher of factory: use only filters of that document type
+ pList = new SfxFilterList_Impl;
+ Update();
+ }
+ else
+ {
+ // global matcher: use global filter array
+ pList = pFilterArr;
+ }
+}
+
+std::shared_ptr<const SfxFilter> SfxFilterMatcher::GetAnyFilter( SfxFilterFlags nMust, SfxFilterFlags nDont ) const
+{
+ m_rImpl.InitForIterating();
+ for (const std::shared_ptr<const SfxFilter>& pFilter : *m_rImpl.pList)
+ {
+ SfxFilterFlags nFlags = pFilter->GetFilterFlags();
+ if ( (nFlags & nMust) == nMust && !(nFlags & nDont ) )
+ return pFilter;
+ }
+
+ return nullptr;
+}
+
+
+ErrCode SfxFilterMatcher::GuessFilterIgnoringContent(
+ SfxMedium const & rMedium,
+ std::shared_ptr<const SfxFilter>& rpFilter ) const
+{
+ uno::Reference<document::XTypeDetection> xDetection(
+ comphelper::getProcessServiceFactory()->createInstance("com.sun.star.document.TypeDetection"), uno::UNO_QUERY);
+
+ OUString sTypeName;
+ try
+ {
+ sTypeName = xDetection->queryTypeByURL( rMedium.GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
+ }
+ catch (uno::Exception&)
+ {
+ }
+
+ rpFilter = nullptr;
+ if ( !sTypeName.isEmpty() )
+ {
+ // make sure filter list is initialized
+ m_rImpl.InitForIterating();
+ rpFilter = GetFilter4EA( sTypeName );
+ }
+
+ return rpFilter ? ERRCODE_NONE : ERRCODE_ABORT;
+}
+
+
+ErrCode SfxFilterMatcher::GuessFilter( SfxMedium& rMedium, std::shared_ptr<const SfxFilter>& rpFilter, SfxFilterFlags nMust, SfxFilterFlags nDont ) const
+{
+ return GuessFilterControlDefaultUI( rMedium, rpFilter, nMust, nDont );
+}
+
+
+ErrCode SfxFilterMatcher::GuessFilterControlDefaultUI( SfxMedium& rMedium, std::shared_ptr<const SfxFilter>& rpFilter, SfxFilterFlags nMust, SfxFilterFlags nDont ) const
+{
+ std::shared_ptr<const SfxFilter> pOldFilter = rpFilter;
+
+ // no detection service -> nothing to do !
+ uno::Reference<document::XTypeDetection> xDetection(
+ comphelper::getProcessServiceFactory()->createInstance("com.sun.star.document.TypeDetection"), uno::UNO_QUERY);
+
+ if (!xDetection.is())
+ return ERRCODE_ABORT;
+
+ try
+ {
+ // open the stream one times only ...
+ // Otherwise it will be tried more than once and show the same interaction more than once ...
+
+ OUString sURL( rMedium.GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
+ uno::Reference< io::XInputStream > xInStream = rMedium.GetInputStream();
+ OUString aFilterName;
+ OUString sTypeName;
+
+ // stream exists => deep detection (with preselection ... if possible)
+ if (xInStream.is())
+ {
+ utl::MediaDescriptor aDescriptor;
+
+ aDescriptor[utl::MediaDescriptor::PROP_URL ] <<= sURL;
+ aDescriptor[utl::MediaDescriptor::PROP_INPUTSTREAM ] <<= xInStream;
+ aDescriptor[utl::MediaDescriptor::PROP_INTERACTIONHANDLER] <<= rMedium.GetInteractionHandler();
+ SfxStringItem const * it = static_cast<SfxStringItem const *>(
+ rMedium.GetItemSet()->GetItem(SID_REFERER));
+ if (it != nullptr) {
+ aDescriptor[utl::MediaDescriptor::PROP_REFERRER]
+ <<= it->GetValue();
+ }
+
+ if ( !m_rImpl.aName.isEmpty() )
+ aDescriptor[utl::MediaDescriptor::PROP_DOCUMENTSERVICE] <<= m_rImpl.aName;
+
+ if ( pOldFilter )
+ {
+ aDescriptor[utl::MediaDescriptor::PROP_TYPENAME ] <<= pOldFilter->GetTypeName();
+ aDescriptor[utl::MediaDescriptor::PROP_FILTERNAME] <<= pOldFilter->GetFilterName();
+ }
+
+ uno::Sequence< beans::PropertyValue > lDescriptor = aDescriptor.getAsConstPropertyValueList();
+ sTypeName = xDetection->queryTypeByDescriptor(lDescriptor, true); // lDescriptor is used as In/Out param ... don't use aDescriptor.getAsConstPropertyValueList() directly!
+
+ for (const auto& rProp : std::as_const(lDescriptor))
+ {
+ if (rProp.Name == "FilterName")
+ // Type detection picked a preferred filter for this format.
+ aFilterName = rProp.Value.get<OUString>();
+ }
+ }
+ // no stream exists => try flat detection without preselection as fallback
+ else
+ sTypeName = xDetection->queryTypeByURL(sURL);
+
+ if (!sTypeName.isEmpty())
+ {
+ std::shared_ptr<const SfxFilter> pNewFilter;
+ if (!aFilterName.isEmpty())
+ // Type detection returned a suitable filter for this. Use it.
+ pNewFilter = SfxFilter::GetFilterByName(aFilterName);
+
+ // fdo#78742 respect requested document service if set
+ if (!pNewFilter || (!m_rImpl.aName.isEmpty()
+ && m_rImpl.aName != pNewFilter->GetServiceName()))
+ {
+ // detect filter by given type
+ // In case of this matcher is bound to a particular document type:
+ // If there is no acceptable type for this document at all, the type detection has possibly returned something else.
+ // The DocumentService property is only a preselection, and all preselections are considered as optional!
+ // This "wrong" type will be sorted out now because we match only allowed filters to the detected type
+ uno::Sequence< beans::NamedValue > lQuery { { "Name", css::uno::Any(sTypeName) } };
+
+ pNewFilter = GetFilterForProps(lQuery, nMust, nDont);
+ }
+
+ if (pNewFilter)
+ {
+ rpFilter = pNewFilter;
+ return ERRCODE_NONE;
+ }
+ }
+ }
+ catch (const uno::Exception&)
+ {}
+
+ return ERRCODE_ABORT;
+}
+
+
+bool SfxFilterMatcher::IsFilterInstalled_Impl( const std::shared_ptr<const SfxFilter>& pFilter )
+{
+ if ( pFilter->GetFilterFlags() & SfxFilterFlags::MUSTINSTALL )
+ {
+ // Here could a re-installation be offered
+ OUString aText( SfxResId(STR_FILTER_NOT_INSTALLED) );
+ aText = aText.replaceFirst( "$(FILTER)", pFilter->GetUIName() );
+ std::unique_ptr<weld::MessageDialog> xQueryBox(Application::CreateMessageDialog(nullptr,
+ VclMessageType::Question, VclButtonsType::YesNo,
+ aText));
+ xQueryBox->set_default_response(RET_YES);
+
+ short nRet = xQueryBox->run();
+ if ( nRet == RET_YES )
+ {
+#ifdef DBG_UTIL
+ // Start Setup
+ std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(nullptr,
+ VclMessageType::Info, VclButtonsType::Ok,
+ "Here should the Setup now be starting!"));
+ xInfoBox->run();
+#endif
+ // Installation must still give feedback if it worked or not,
+ // then the Filterflag be deleted
+ }
+
+ return ( !(pFilter->GetFilterFlags() & SfxFilterFlags::MUSTINSTALL) );
+ }
+ else if ( pFilter->GetFilterFlags() & SfxFilterFlags::CONSULTSERVICE )
+ {
+ OUString aText( SfxResId(STR_FILTER_CONSULT_SERVICE) );
+ aText = aText.replaceFirst( "$(FILTER)", pFilter->GetUIName() );
+ std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(nullptr,
+ VclMessageType::Info, VclButtonsType::Ok,
+ aText));
+ xInfoBox->run();
+ return false;
+ }
+ else
+ return true;
+}
+
+
+ErrCode SfxFilterMatcher::DetectFilter( SfxMedium& rMedium, std::shared_ptr<const SfxFilter>& rpFilter ) const
+/* [Description]
+
+ Here the Filter selection box is pulled up. Otherwise GuessFilter
+ */
+
+{
+ std::shared_ptr<const SfxFilter> pOldFilter = rMedium.GetFilter();
+ if ( pOldFilter )
+ {
+ if( !IsFilterInstalled_Impl( pOldFilter ) )
+ pOldFilter = nullptr;
+ else
+ {
+ const SfxStringItem* pSalvageItem = SfxItemSet::GetItem<SfxStringItem>(rMedium.GetItemSet(), SID_DOC_SALVAGE, false);
+ if ( ( pOldFilter->GetFilterFlags() & SfxFilterFlags::PACKED ) && pSalvageItem )
+ // Salvage is always done without packing
+ pOldFilter = nullptr;
+ }
+ }
+
+ std::shared_ptr<const SfxFilter> pFilter = pOldFilter;
+
+ bool bPreview = rMedium.IsPreview_Impl();
+ const SfxStringItem* pReferer = SfxItemSet::GetItem<SfxStringItem>(rMedium.GetItemSet(), SID_REFERER, false);
+ if ( bPreview && rMedium.IsRemote() && ( !pReferer || !pReferer->GetValue().match("private:searchfolder:") ) )
+ return ERRCODE_ABORT;
+
+ ErrCode nErr = GuessFilter( rMedium, pFilter );
+ if ( nErr == ERRCODE_ABORT )
+ return nErr;
+
+ if ( nErr == ERRCODE_IO_PENDING )
+ {
+ rpFilter = pFilter;
+ return nErr;
+ }
+
+ if ( !pFilter )
+ {
+ std::shared_ptr<const SfxFilter> pInstallFilter;
+
+ // Now test the filter which are not installed (ErrCode is irrelevant)
+ GuessFilter( rMedium, pInstallFilter, SfxFilterFlags::IMPORT, SfxFilterFlags::CONSULTSERVICE );
+ if ( pInstallFilter )
+ {
+ if ( IsFilterInstalled_Impl( pInstallFilter ) )
+ // Maybe the filter was installed afterwards.
+ pFilter = pInstallFilter;
+ }
+ else
+ {
+ // Now test the filter, which first must be obtained by Star
+ // (ErrCode is irrelevant)
+ GuessFilter( rMedium, pInstallFilter, SfxFilterFlags::IMPORT, SfxFilterFlags::NONE );
+ if ( pInstallFilter )
+ IsFilterInstalled_Impl( pInstallFilter );
+ }
+ }
+
+ bool bHidden = bPreview;
+ const SfxStringItem* pFlags = SfxItemSet::GetItem<SfxStringItem>(rMedium.GetItemSet(), SID_OPTIONS, false);
+ if ( !bHidden && pFlags )
+ {
+ OUString aFlags( pFlags->GetValue() );
+ aFlags = aFlags.toAsciiUpperCase();
+ if( -1 != aFlags.indexOf( 'H' ) )
+ bHidden = true;
+ }
+ rpFilter = pFilter;
+
+ if ( bHidden )
+ nErr = pFilter ? ERRCODE_NONE : ERRCODE_ABORT;
+ return nErr;
+}
+
+std::shared_ptr<const SfxFilter> SfxFilterMatcher::GetFilterForProps( const css::uno::Sequence < beans::NamedValue >& aSeq, SfxFilterFlags nMust, SfxFilterFlags nDont ) const
+{
+ uno::Reference< lang::XMultiServiceFactory > xServiceManager = ::comphelper::getProcessServiceFactory();
+ if( !xServiceManager )
+ return nullptr;
+
+ static constexpr OUStringLiteral sTypeDetection = u"com.sun.star.document.TypeDetection";
+ uno::Reference< container::XContainerQuery > xTypeCFG( xServiceManager->createInstance( sTypeDetection ), uno::UNO_QUERY );
+ if ( !xTypeCFG )
+ return nullptr;
+
+ // make query for all types matching the properties
+ uno::Reference < css::container::XEnumeration > xEnum = xTypeCFG->createSubSetEnumerationByProperties( aSeq );
+ uno::Sequence<beans::PropertyValue> aProps;
+ while ( xEnum->hasMoreElements() )
+ {
+ static constexpr OUStringLiteral sPreferredFilter = u"PreferredFilter";
+ static constexpr OUStringLiteral sName = u"Name";
+
+ xEnum->nextElement() >>= aProps;
+ OUString aValue, aName;
+ for( const auto & rPropVal : aProps)
+ {
+ if (rPropVal.Name == sPreferredFilter)
+ rPropVal.Value >>= aValue;
+ else if (rPropVal.Name == sName)
+ rPropVal.Value >>= aName;
+ }
+
+ // try to get the preferred filter (works without loading all filters!)
+ if ( !aValue.isEmpty() )
+ {
+ std::shared_ptr<const SfxFilter> pFilter = SfxFilter::GetFilterByName( aValue );
+ if ( !pFilter || (pFilter->GetFilterFlags() & nMust) != nMust || (pFilter->GetFilterFlags() & nDont ) )
+ // check for filter flags
+ // pFilter == 0: if preferred filter is a Writer filter, but Writer module is not installed
+ continue;
+
+ if ( !m_rImpl.aName.isEmpty() )
+ {
+ // if this is not the global FilterMatcher: check if filter matches the document type
+ if ( pFilter->GetServiceName() != m_rImpl.aName )
+ {
+ // preferred filter belongs to another document type; now we must search the filter
+ m_rImpl.InitForIterating();
+ pFilter = GetFilter4EA( aName, nMust, nDont );
+ if ( pFilter )
+ return pFilter;
+ }
+ else
+ return pFilter;
+ }
+ else
+ return pFilter;
+ }
+ }
+
+ return nullptr;
+}
+
+std::shared_ptr<const SfxFilter> SfxFilterMatcher::GetFilter4Mime( const OUString& rMediaType, SfxFilterFlags nMust, SfxFilterFlags nDont ) const
+{
+ if ( m_rImpl.pList )
+ {
+ for (const std::shared_ptr<const SfxFilter>& pFilter : *m_rImpl.pList)
+ {
+ SfxFilterFlags nFlags = pFilter->GetFilterFlags();
+ if ( (nFlags & nMust) == nMust && !(nFlags & nDont ) && pFilter->GetMimeType() == rMediaType )
+ return pFilter;
+ }
+
+ return nullptr;
+ }
+
+ css::uno::Sequence < css::beans::NamedValue > aSeq { { "MediaType", css::uno::Any(rMediaType) } };
+ return GetFilterForProps( aSeq, nMust, nDont );
+}
+
+std::shared_ptr<const SfxFilter> SfxFilterMatcher::GetFilter4EA( const OUString& rType, SfxFilterFlags nMust, SfxFilterFlags nDont ) const
+{
+ if ( m_rImpl.pList )
+ {
+ std::shared_ptr<const SfxFilter> pFirst;
+ for (const std::shared_ptr<const SfxFilter>& pFilter : *m_rImpl.pList)
+ {
+ SfxFilterFlags nFlags = pFilter->GetFilterFlags();
+ if ( (nFlags & nMust) == nMust && !(nFlags & nDont ) && pFilter->GetTypeName() == rType )
+ {
+ if (nFlags & SfxFilterFlags::PREFERED)
+ return pFilter;
+ if (!pFirst)
+ pFirst = pFilter;
+ }
+ }
+ if (pFirst)
+ return pFirst;
+
+ return nullptr;
+ }
+
+ css::uno::Sequence < css::beans::NamedValue > aSeq { { "Name", css::uno::Any(rType) } };
+ return GetFilterForProps( aSeq, nMust, nDont );
+}
+
+std::shared_ptr<const SfxFilter> SfxFilterMatcher::GetFilter4Extension( const OUString& rExt, SfxFilterFlags nMust, SfxFilterFlags nDont ) const
+{
+ if ( m_rImpl.pList )
+ {
+ if (OUString sExt = ToUpper_Impl(rExt); !sExt.isEmpty())
+ {
+ if (sExt[0] != '.')
+ sExt = "." + sExt;
+
+ for (const std::shared_ptr<const SfxFilter>& pFilter : *m_rImpl.pList)
+ {
+ SfxFilterFlags nFlags = pFilter->GetFilterFlags();
+ if ((nFlags & nMust) == nMust && !(nFlags & nDont))
+ {
+ OUString sWildCard = ToUpper_Impl(pFilter->GetWildcard().getGlob());
+
+ WildCard aCheck(sWildCard, ';');
+ if (aCheck.Matches(sExt))
+ return pFilter;
+ }
+ }
+ }
+
+ return nullptr;
+ }
+
+ // Use extension without dot!
+ OUString sExt( rExt );
+ if ( sExt.startsWith(".") )
+ sExt = sExt.copy(1);
+
+ css::uno::Sequence < css::beans::NamedValue > aSeq
+ { { "Extensions", css::uno::Any(uno::Sequence < OUString > { sExt } ) } };
+ return GetFilterForProps( aSeq, nMust, nDont );
+}
+
+std::shared_ptr<const SfxFilter> SfxFilterMatcher::GetFilter4ClipBoardId( SotClipboardFormatId nId, SfxFilterFlags nMust, SfxFilterFlags nDont ) const
+{
+ if (nId == SotClipboardFormatId::NONE)
+ return nullptr;
+
+ css::uno::Sequence < css::beans::NamedValue > aSeq
+ { { "ClipboardFormat", css::uno::Any(SotExchange::GetFormatName( nId )) } };
+ return GetFilterForProps( aSeq, nMust, nDont );
+}
+
+std::shared_ptr<const SfxFilter> SfxFilterMatcher::GetFilter4UIName( std::u16string_view rName, SfxFilterFlags nMust, SfxFilterFlags nDont ) const
+{
+ m_rImpl.InitForIterating();
+ std::shared_ptr<const SfxFilter> pFirstFilter;
+ for (const std::shared_ptr<const SfxFilter>& pFilter : *m_rImpl.pList)
+ {
+ SfxFilterFlags nFlags = pFilter->GetFilterFlags();
+ if ( (nFlags & nMust) == nMust &&
+ !(nFlags & nDont ) && pFilter->GetUIName() == rName )
+ {
+ if ( pFilter->GetFilterFlags() & SfxFilterFlags::PREFERED )
+ return pFilter;
+ else if ( !pFirstFilter )
+ pFirstFilter = pFilter;
+ }
+ }
+ return pFirstFilter;
+}
+
+std::shared_ptr<const SfxFilter> SfxFilterMatcher::GetFilter4FilterName( const OUString& rName, SfxFilterFlags nMust, SfxFilterFlags nDont ) const
+{
+ std::u16string_view aName( rName );
+ sal_Int32 nIndex = rName.indexOf(": ");
+ if ( nIndex != -1 )
+ {
+ SAL_WARN( "sfx.bastyp", "Old filter name used!");
+ aName = rName.subView( nIndex + 2 );
+ }
+
+ if ( bFirstRead )
+ {
+ uno::Reference< lang::XMultiServiceFactory > xServiceManager = ::comphelper::getProcessServiceFactory();
+ uno::Reference< container::XNameAccess > xFilterCFG ;
+ uno::Reference< container::XNameAccess > xTypeCFG ;
+ if( xServiceManager.is() )
+ {
+ static constexpr OUStringLiteral sFilterFactory = u"com.sun.star.document.FilterFactory";
+ static constexpr OUStringLiteral sTypeDetection = u"com.sun.star.document.TypeDetection";
+ xFilterCFG.set( xServiceManager->createInstance( sFilterFactory ), uno::UNO_QUERY );
+ xTypeCFG.set( xServiceManager->createInstance( sTypeDetection ), uno::UNO_QUERY );
+ }
+
+ if( xFilterCFG.is() && xTypeCFG.is() )
+ {
+ if ( !pFilterArr )
+ CreateFilterArr();
+ else
+ {
+ for (const std::shared_ptr<const SfxFilter>& pFilter : *pFilterArr)
+ {
+ SfxFilterFlags nFlags = pFilter->GetFilterFlags();
+ if ((nFlags & nMust) == nMust && !(nFlags & nDont) && pFilter->GetFilterName().equalsIgnoreAsciiCase(aName))
+ return pFilter;
+ }
+ }
+
+ SfxFilterContainer::ReadSingleFilter_Impl( rName, xTypeCFG, xFilterCFG, false );
+ }
+ }
+
+ SfxFilterList_Impl* pList = m_rImpl.pList;
+ if ( !pList )
+ pList = pFilterArr;
+
+ for (const std::shared_ptr<const SfxFilter>& pFilter : *pList)
+ {
+ SfxFilterFlags nFlags = pFilter->GetFilterFlags();
+ if ( (nFlags & nMust) == nMust && !(nFlags & nDont ) && pFilter->GetFilterName().equalsIgnoreAsciiCase(aName))
+ return pFilter;
+ }
+
+ return nullptr;
+}
+
+IMPL_LINK( SfxFilterMatcher, MaybeFileHdl_Impl, OUString*, pString, bool )
+{
+ std::shared_ptr<const SfxFilter> pFilter = GetFilter4Extension( *pString );
+ return pFilter &&
+ !pFilter->GetWildcard().Matches(u"") &&
+ !pFilter->GetWildcard().Matches(u"*.*") &&
+ !pFilter->GetWildcard().Matches(u"*");
+}
+
+
+SfxFilterMatcherIter::SfxFilterMatcherIter(
+ const SfxFilterMatcher& rMatcher,
+ SfxFilterFlags nOrMaskP, SfxFilterFlags nAndMaskP )
+ : nOrMask( nOrMaskP ), nAndMask( nAndMaskP ),
+ nCurrent(0), m_rMatch(rMatcher.m_rImpl)
+{
+ if( nOrMask == static_cast<SfxFilterFlags>(0xffff) ) //Due to faulty build on s
+ nOrMask = SfxFilterFlags::NONE;
+ m_rMatch.InitForIterating();
+}
+
+
+std::shared_ptr<const SfxFilter> SfxFilterMatcherIter::Find_Impl()
+{
+ std::shared_ptr<const SfxFilter> pFilter;
+ while( nCurrent < m_rMatch.pList->size() )
+ {
+ pFilter = (*m_rMatch.pList)[nCurrent++];
+ SfxFilterFlags nFlags = pFilter->GetFilterFlags();
+ if( ((nFlags & nOrMask) == nOrMask ) && !(nFlags & nAndMask ) )
+ break;
+ pFilter = nullptr;
+ }
+
+ return pFilter;
+}
+
+std::shared_ptr<const SfxFilter> SfxFilterMatcherIter::First()
+{
+ nCurrent = 0;
+ return Find_Impl();
+}
+
+
+std::shared_ptr<const SfxFilter> SfxFilterMatcherIter::Next()
+{
+ return Find_Impl();
+}
+
+/*---------------------------------------------------------------
+ helper to build own formatted string from given stringlist by
+ using given separator
+ ---------------------------------------------------------------*/
+static OUString implc_convertStringlistToString( const uno::Sequence< OUString >& lList ,
+ sal_Unicode cSeparator,
+ std::u16string_view sPrefix )
+{
+ OUStringBuffer sString ( 1000 ) ;
+ sal_Int32 nCount = lList.getLength();
+ sal_Int32 nItem = 0 ;
+ for( nItem=0; nItem<nCount; ++nItem )
+ {
+ if( !sPrefix.empty() )
+ {
+ sString.append( sPrefix );
+ }
+ sString.append( lList[nItem] );
+ if( nItem+1<nCount )
+ {
+ sString.append( cSeparator );
+ }
+ }
+ return sString.makeStringAndClear();
+}
+
+
+void SfxFilterContainer::ReadSingleFilter_Impl(
+ const OUString& rName,
+ const uno::Reference< container::XNameAccess >& xTypeCFG,
+ const uno::Reference< container::XNameAccess >& xFilterCFG,
+ bool bUpdate
+ )
+{
+ OUString sFilterName( rName );
+ SfxFilterList_Impl& rList = *pFilterArr;
+ uno::Sequence< beans::PropertyValue > lFilterProperties;
+ uno::Any aResult;
+ try
+ {
+ aResult = xFilterCFG->getByName( sFilterName );
+ }
+ catch( container::NoSuchElementException& )
+ {
+ aResult = uno::Any();
+ }
+
+ if( !(aResult >>= lFilterProperties) )
+ return;
+
+ // collect information to add filter to container
+ // (attention: some information aren't available on filter directly ... you must search for corresponding type too!)
+ SfxFilterFlags nFlags = SfxFilterFlags::NONE;
+ SotClipboardFormatId nClipboardId = SotClipboardFormatId::NONE;
+ sal_Int32 nFormatVersion = 0 ;
+ OUString sMimeType ;
+ OUString sType ;
+ OUString sUIName ;
+ OUString sHumanName ;
+ OUString sDefaultTemplate ;
+ OUString sUserData ;
+ OUString sExtension ;
+ OUString sServiceName ;
+ bool bEnabled = true ;
+
+ // first get directly available properties
+ for( const auto& rFilterProperty : std::as_const(lFilterProperties) )
+ {
+ if ( rFilterProperty.Name == "FileFormatVersion" )
+ {
+ rFilterProperty.Value >>= nFormatVersion;
+ }
+ else if ( rFilterProperty.Name == "TemplateName" )
+ {
+ rFilterProperty.Value >>= sDefaultTemplate;
+ }
+ else if ( rFilterProperty.Name == "Flags" )
+ {
+ sal_Int32 nTmp(0);
+ rFilterProperty.Value >>= nTmp;
+ assert((nTmp & ~o3tl::typed_flags<SfxFilterFlags>::mask) == 0);
+ nFlags = static_cast<SfxFilterFlags>(nTmp);
+ }
+ else if ( rFilterProperty.Name == "UIName" )
+ {
+ rFilterProperty.Value >>= sUIName;
+ }
+ else if ( rFilterProperty.Name == "UserData" )
+ {
+ uno::Sequence< OUString > lUserData;
+ rFilterProperty.Value >>= lUserData;
+ sUserData = implc_convertStringlistToString( lUserData, ',', u"" );
+ }
+ else if ( rFilterProperty.Name == "DocumentService" )
+ {
+ rFilterProperty.Value >>= sServiceName;
+ }
+ else if (rFilterProperty.Name == "ExportExtension")
+ {
+ // Extension preferred by the filter. This takes precedence
+ // over those that are given in the file format type.
+ rFilterProperty.Value >>= sExtension;
+ sExtension = "*." + sExtension;
+ }
+ else if ( rFilterProperty.Name == "Type" )
+ {
+ rFilterProperty.Value >>= sType;
+ // Try to get filter .. but look for any exceptions!
+ // May be filter was deleted by another thread ...
+ try
+ {
+ aResult = xTypeCFG->getByName( sType );
+ }
+ catch (const container::NoSuchElementException&)
+ {
+ aResult = uno::Any();
+ }
+
+ uno::Sequence< beans::PropertyValue > lTypeProperties;
+ if( aResult >>= lTypeProperties )
+ {
+ // get indirect available properties then (types)
+ for( const auto& rTypeProperty : std::as_const(lTypeProperties) )
+ {
+ if ( rTypeProperty.Name == "ClipboardFormat" )
+ {
+ rTypeProperty.Value >>= sHumanName;
+ }
+ else if ( rTypeProperty.Name == "MediaType" )
+ {
+ rTypeProperty.Value >>= sMimeType;
+ }
+ else if ( rTypeProperty.Name == "Extensions" )
+ {
+ if (sExtension.isEmpty())
+ {
+ uno::Sequence< OUString > lExtensions;
+ rTypeProperty.Value >>= lExtensions;
+ sExtension = implc_convertStringlistToString( lExtensions, ';', u"*." );
+ }
+ }
+ }
+ }
+ }
+ else if ( rFilterProperty.Name == "Enabled" )
+ {
+ rFilterProperty.Value >>= bEnabled;
+ }
+
+ }
+
+ if ( sServiceName.isEmpty() )
+ return;
+
+ // old formats are found ... using HumanPresentableName!
+ if( !sHumanName.isEmpty() )
+ {
+ nClipboardId = SotExchange::RegisterFormatName( sHumanName );
+
+ // For external filters ignore clipboard IDs
+ if(nFlags & SfxFilterFlags::STARONEFILTER)
+ {
+ nClipboardId = SotClipboardFormatId::NONE;
+ }
+ }
+ // register SfxFilter
+ // first erase module name from old filter names!
+ // e.g: "scalc: DIF" => "DIF"
+ sal_Int32 nStartRealName = sFilterName.indexOf( ": " );
+ if( nStartRealName != -1 )
+ {
+ SAL_WARN( "sfx.bastyp", "Old format, not supported!");
+ sFilterName = sFilterName.copy( nStartRealName+2 );
+ }
+
+ std::shared_ptr<const SfxFilter> pFilter = bUpdate ? SfxFilter::GetFilterByName( sFilterName ) : nullptr;
+ if (!pFilter)
+ {
+ pFilter = std::make_shared<SfxFilter>( sFilterName ,
+ sExtension ,
+ nFlags ,
+ nClipboardId ,
+ sType ,
+ sMimeType ,
+ sUserData ,
+ sServiceName ,
+ bEnabled );
+ rList.push_back( pFilter );
+ }
+ else
+ {
+ SfxFilter* pFilt = const_cast<SfxFilter*>(pFilter.get());
+ pFilt->maFilterName = sFilterName;
+ pFilt->aWildCard = WildCard(sExtension, ';');
+ pFilt->nFormatType = nFlags;
+ pFilt->lFormat = nClipboardId;
+ pFilt->aTypeName = sType;
+ pFilt->aMimeType = sMimeType;
+ pFilt->aUserData = sUserData;
+ pFilt->aServiceName = sServiceName;
+ pFilt->mbEnabled = bEnabled;
+ }
+
+ SfxFilter* pFilt = const_cast<SfxFilter*>(pFilter.get());
+
+ // Don't forget to set right UIName!
+ // Otherwise internal name is used as fallback ...
+ pFilt->SetUIName( sUIName );
+ pFilt->SetDefaultTemplate( sDefaultTemplate );
+ if( nFormatVersion )
+ {
+ pFilt->SetVersion( nFormatVersion );
+ }
+}
+
+void SfxFilterContainer::ReadFilters_Impl( bool bUpdate )
+{
+ if ( !pFilterArr )
+ CreateFilterArr();
+
+ bFirstRead = false;
+ SfxFilterList_Impl& rList = *pFilterArr;
+
+ try
+ {
+ // get the FilterFactory service to access the registered filters ... and types!
+ uno::Reference< lang::XMultiServiceFactory > xServiceManager = ::comphelper::getProcessServiceFactory();
+ uno::Reference< container::XNameAccess > xFilterCFG ;
+ uno::Reference< container::XNameAccess > xTypeCFG ;
+ if( xServiceManager.is() )
+ {
+ xFilterCFG.set( xServiceManager->createInstance( "com.sun.star.document.FilterFactory" ), uno::UNO_QUERY );
+ xTypeCFG.set( xServiceManager->createInstance( "com.sun.star.document.TypeDetection" ), uno::UNO_QUERY );
+ }
+
+ if( xFilterCFG.is() && xTypeCFG.is() )
+ {
+ // select right query to get right set of filters for search module
+ const uno::Sequence< OUString > lFilterNames = xFilterCFG->getElementNames();
+ if ( lFilterNames.hasElements() )
+ {
+ // If list of filters already exist ...
+ // ReadExternalFilters must work in update mode.
+ // Best way seems to mark all filters NOT_INSTALLED
+ // and change it back for all valid filters afterwards.
+ if( !rList.empty() )
+ {
+ bUpdate = true;
+ for (const std::shared_ptr<const SfxFilter>& pFilter : rList)
+ {
+ SfxFilter* pNonConstFilter = const_cast<SfxFilter*>(pFilter.get());
+ pNonConstFilter->nFormatType |= SFX_FILTER_NOTINSTALLED;
+ }
+ }
+
+ // get all properties of filters ... put it into the filter container
+ for( const OUString& sFilterName : lFilterNames )
+ {
+ // Try to get filter .. but look for any exceptions!
+ // May be filter was deleted by another thread ...
+ ReadSingleFilter_Impl( sFilterName, xTypeCFG, xFilterCFG, bUpdate );
+ }
+ }
+ }
+ }
+ catch(const uno::Exception&)
+ {
+ SAL_WARN( "sfx.bastyp", "SfxFilterContainer::ReadFilter()\nException detected. Possible not all filters could be cached." );
+ }
+
+ if ( bUpdate )
+ {
+ // global filter array was modified, factory specific ones might need an
+ // update too
+ for (const auto& aImpl : aImplArr)
+ aImpl->Update();
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/bastyp/fltlst.cxx b/sfx2/source/bastyp/fltlst.cxx
new file mode 100644
index 000000000..ac7f7df39
--- /dev/null
+++ b/sfx2/source/bastyp/fltlst.cxx
@@ -0,0 +1,118 @@
+/* -*- 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 "fltlst.hxx"
+
+#include <com/sun/star/document/FilterConfigRefresh.hpp>
+#include <comphelper/processfactory.hxx>
+
+#include <sfx2/fcontnr.hxx>
+
+#include <vcl/svapp.hxx>
+#include <cppuhelper/implbase.hxx>
+
+
+// namespaces
+
+using namespace ::com::sun::star;
+
+namespace {
+
+class SfxRefreshListener : public ::cppu::WeakImplHelper<css::util::XRefreshListener>
+{
+ private:
+ SfxFilterListener *m_pOwner;
+
+ public:
+ explicit SfxRefreshListener(SfxFilterListener *pOwner)
+ : m_pOwner(pOwner)
+ {
+ }
+
+ // util.XRefreshListener
+ virtual void SAL_CALL refreshed( const css::lang::EventObject& rEvent ) override
+ {
+ m_pOwner->refreshed(rEvent);
+ }
+
+ // lang.XEventListener
+ virtual void SAL_CALL disposing(const css::lang::EventObject& rEvent) override
+ {
+ m_pOwner->disposing(rEvent);
+ }
+};
+
+}
+
+/*-************************************************************************************************************
+ @short ctor
+ @descr These initialize an instance of a SfxFilterListener class. Created object listen automatically
+ on right FilterFactory-Service for all changes and synchronize right SfxFilterContainer with
+ corresponding framework-cache.
+ We use given "sFactory" value to decide which query must be used to fill "pContainer" with new values.
+ Given "pContainer" hold us alive as uno reference and we use it to synchronize it with framework caches.
+ We will die, if he die! see dtor for further information.
+
+ @seealso dtor
+ @seealso class framework::FilterCache
+ @seealso service ::document::FilterFactory
+
+ @param "sFactory" , short name of module which contains filter container
+ @param "pContainer", pointer to filter container which will be informed
+ @onerror We show some assertions in non product version.
+ Otherwise we do nothing!
+ @threadsafe yes
+*//*-*************************************************************************************************************/
+SfxFilterListener::SfxFilterListener()
+ : m_xFilterCache(document::FilterConfigRefresh::create( comphelper::getProcessComponentContext() ) ),
+ m_xFilterCacheListener(new SfxRefreshListener(this))
+{
+ m_xFilterCache->addRefreshListener( m_xFilterCacheListener );
+}
+
+SfxFilterListener::~SfxFilterListener()
+{
+}
+
+void SfxFilterListener::refreshed( const lang::EventObject& aSource )
+{
+ SolarMutexGuard aGuard;
+ uno::Reference< util::XRefreshable > xContainer( aSource.Source, uno::UNO_QUERY );
+ if(
+ (xContainer.is() ) &&
+ (xContainer==m_xFilterCache)
+ )
+ {
+ SfxFilterContainer::ReadFilters_Impl( true );
+ }
+}
+
+void SfxFilterListener::disposing( const lang::EventObject& aSource )
+{
+ SolarMutexGuard aGuard;
+ uno::Reference< util::XRefreshable > xNotifier( aSource.Source, uno::UNO_QUERY );
+ if (!xNotifier.is())
+ return;
+
+ if (xNotifier == m_xFilterCache)
+ m_xFilterCache.clear();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/bastyp/fltlst.hxx b/sfx2/source/bastyp/fltlst.hxx
new file mode 100644
index 000000000..69dbaf599
--- /dev/null
+++ b/sfx2/source/bastyp/fltlst.hxx
@@ -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/.
+ *
+ * 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_BASTYP_FLTLST_HXX
+#define INCLUDED_SFX2_SOURCE_BASTYP_FLTLST_HXX
+
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/util/XRefreshable.hpp>
+#include <com/sun/star/util/XRefreshListener.hpp>
+#include <com/sun/star/lang/EventObject.hpp>
+
+class SfxFilterListener final
+{
+ private:
+ css::uno::Reference< css::util::XRefreshable > m_xFilterCache;
+ css::uno::Reference< css::util::XRefreshListener > m_xFilterCacheListener;
+
+ public:
+ SfxFilterListener();
+ ~SfxFilterListener();
+
+ public:
+ // XRefreshListener
+ /// @throws css::uno::RuntimeException
+ void refreshed( const css::lang::EventObject& aSource );
+ // XEventListener
+ /// @throws css::uno::RuntimeException
+ void disposing( const css::lang::EventObject& aSource );
+
+};
+
+#endif // INCLUDED_SFX2_SOURCE_BASTYP_FLTLST_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/bastyp/frmhtml.cxx b/sfx2/source/bastyp/frmhtml.cxx
new file mode 100644
index 000000000..a15a6cc7a
--- /dev/null
+++ b/sfx2/source/bastyp/frmhtml.cxx
@@ -0,0 +1,101 @@
+/* -*- 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 <svtools/htmltokn.h>
+
+#include <sfx2/frmdescr.hxx>
+#include <sfx2/frmhtml.hxx>
+
+char const sHTML_SC_yes[] = "YES";
+char const sHTML_SC_no[] = "NO";
+char const sHTML_SC_auto[] = "AUTO";
+
+HTMLOptionEnum<ScrollingMode> const aScrollingTable[] =
+{
+ { sHTML_SC_yes, ScrollingMode::Yes },
+ { sHTML_SC_no, ScrollingMode::No },
+ { sHTML_SC_auto, ScrollingMode::Auto },
+ { nullptr, ScrollingMode(0) }
+};
+
+namespace SfxFrameHTMLParser
+{
+void ParseFrameOptions(
+ SfxFrameDescriptor *pFrame, const HTMLOptions& rOptions, std::u16string_view rBaseURL )
+{
+ // Get and set the options
+ Size aMargin( pFrame->GetMargin() );
+
+ // Netscape seems to set marginwidth to 0 as soon as
+ // marginheight is set, and vice versa.
+ // Netscape does however not allow for a direct
+ // setting to 0, while IE4.0 does
+ // We will not mimic that bug !
+ bool bMarginWidth = false, bMarginHeight = false;
+
+ for (const auto & rOption : rOptions)
+ {
+ switch( rOption.GetToken() )
+ {
+ case HtmlOptionId::SRC:
+ pFrame->SetURL(
+ INetURLObject::GetAbsURL(
+ rBaseURL, rOption.GetString()) );
+ break;
+ case HtmlOptionId::NAME:
+ pFrame->SetName( rOption.GetString() );
+ break;
+ case HtmlOptionId::MARGINWIDTH:
+ aMargin.setWidth( rOption.GetNumber() );
+
+ if( !bMarginHeight )
+ aMargin.setHeight( 0 );
+ bMarginWidth = true;
+ break;
+ case HtmlOptionId::MARGINHEIGHT:
+ aMargin.setHeight( rOption.GetNumber() );
+
+ if( !bMarginWidth )
+ aMargin.setWidth( 0 );
+ bMarginHeight = true;
+ break;
+ case HtmlOptionId::SCROLLING:
+ pFrame->SetScrollingMode( rOption.GetEnum( aScrollingTable, ScrollingMode::Auto ) );
+ break;
+ case HtmlOptionId::FRAMEBORDER:
+ {
+ const OUString& aStr = rOption.GetString();
+ bool bBorder = true;
+ if ( aStr.equalsIgnoreAsciiCase("NO") ||
+ aStr.equalsIgnoreAsciiCase("0") )
+ bBorder = false;
+ pFrame->SetFrameBorder( bBorder );
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ pFrame->SetMargin( aMargin );
+}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/bastyp/frmhtmlw.cxx b/sfx2/source/bastyp/frmhtmlw.cxx
new file mode 100644
index 000000000..d568b1930
--- /dev/null
+++ b/sfx2/source/bastyp/frmhtmlw.cxx
@@ -0,0 +1,294 @@
+/* -*- 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 <svtools/htmlkywd.hxx>
+
+#include <rtl/tencinfo.h>
+#include <sal/log.hxx>
+
+#include <svl/urihelper.hxx>
+#include <tools/stream.hxx>
+#include <tools/debug.hxx>
+#include <unotools/resmgr.hxx>
+#include <svtools/htmlout.hxx>
+
+#include <sfx2/frmdescr.hxx>
+#include <sfx2/frmhtmlw.hxx>
+#include <strings.hxx>
+
+#include <comphelper/processfactory.hxx>
+#include <comphelper/string.hxx>
+
+#include <com/sun/star/script/Converter.hpp>
+#include <com/sun/star/document/XDocumentProperties.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+
+#include <rtl/bootstrap.hxx>
+#include <rtl/strbuf.hxx>
+#include <sax/tools/converter.hxx>
+
+using namespace ::com::sun::star;
+
+char const sHTML_SC_yes[] = "YES";
+char const sHTML_SC_no[] = "NO";
+
+void SfxFrameHTMLWriter::OutMeta( SvStream& rStrm,
+ const char *pIndent,
+ const OUString& rName,
+ const OUString& rContent,
+ bool bHTTPEquiv,
+ OUString *pNonConvertableChars )
+{
+ rStrm.WriteCharPtr( SAL_NEWLINE_STRING );
+ if( pIndent )
+ rStrm.WriteCharPtr( pIndent );
+
+ OStringBuffer sOut;
+ sOut.append("<" OOO_STRING_SVTOOLS_HTML_meta " ")
+ .append(bHTTPEquiv ? OOO_STRING_SVTOOLS_HTML_O_httpequiv : OOO_STRING_SVTOOLS_HTML_O_name).append("=\"");
+ rStrm.WriteOString( sOut.makeStringAndClear() );
+
+ HTMLOutFuncs::Out_String( rStrm, rName, pNonConvertableChars );
+
+ sOut.append("\" " OOO_STRING_SVTOOLS_HTML_O_content "=\"");
+ rStrm.WriteOString( sOut.makeStringAndClear() );
+
+ HTMLOutFuncs::Out_String( rStrm, rContent, pNonConvertableChars ).WriteCharPtr( "\"/>" );
+}
+
+void SfxFrameHTMLWriter::Out_DocInfo( SvStream& rStrm, const OUString& rBaseURL,
+ const uno::Reference<document::XDocumentProperties> & i_xDocProps,
+ const char *pIndent,
+ OUString *pNonConvertableChars )
+{
+ OutMeta( rStrm, pIndent, OOO_STRING_SVTOOLS_HTML_META_content_type, "text/html; charset=utf-8", true,
+ pNonConvertableChars );
+
+ // Title (regardless if empty)
+ rStrm.WriteCharPtr( SAL_NEWLINE_STRING );
+ if( pIndent )
+ rStrm.WriteCharPtr( pIndent );
+ HTMLOutFuncs::Out_AsciiTag( rStrm, OOO_STRING_SVTOOLS_HTML_title );
+ if( i_xDocProps.is() )
+ {
+ const OUString& rTitle = i_xDocProps->getTitle();
+ if( !rTitle.isEmpty() )
+ HTMLOutFuncs::Out_String( rStrm, rTitle, pNonConvertableChars );
+ }
+ HTMLOutFuncs::Out_AsciiTag( rStrm, OOO_STRING_SVTOOLS_HTML_title, false );
+
+ // Target-Frame
+ if( i_xDocProps.is() )
+ {
+ const OUString& rTarget = i_xDocProps->getDefaultTarget();
+ if( !rTarget.isEmpty() )
+ {
+ rStrm.WriteCharPtr( SAL_NEWLINE_STRING );
+ if( pIndent )
+ rStrm.WriteCharPtr( pIndent );
+
+ rStrm.WriteOString( "<" OOO_STRING_SVTOOLS_HTML_base " "
+ OOO_STRING_SVTOOLS_HTML_O_target "=\"" );
+ HTMLOutFuncs::Out_String( rStrm, rTarget, pNonConvertableChars )
+ .WriteCharPtr( "\">" );
+ }
+ }
+
+ // Who we are
+ OUString sGenerator(Translate::ExpandVariables(STR_HTML_GENERATOR));
+ OUString os( "$_OS" );
+ ::rtl::Bootstrap::expandMacros(os);
+ sGenerator = sGenerator.replaceFirst( "%1", os );
+ OutMeta( rStrm, pIndent, OOO_STRING_SVTOOLS_HTML_META_generator, sGenerator, false, pNonConvertableChars );
+
+ if( !i_xDocProps.is() )
+ return;
+
+ // Reload
+ if( (i_xDocProps->getAutoloadSecs() != 0) ||
+ !i_xDocProps->getAutoloadURL().isEmpty() )
+ {
+ OUString sContent = OUString::number(
+ i_xDocProps->getAutoloadSecs() );
+
+ const OUString &rReloadURL = i_xDocProps->getAutoloadURL();
+ if( !rReloadURL.isEmpty() )
+ {
+ sContent += ";URL=" + URIHelper::simpleNormalizedMakeRelative(
+ rBaseURL, rReloadURL);
+ }
+
+ OutMeta( rStrm, pIndent, OOO_STRING_SVTOOLS_HTML_META_refresh, sContent, true,
+ pNonConvertableChars );
+ }
+
+ // Author
+ const OUString& rAuthor = i_xDocProps->getAuthor();
+ if( !rAuthor.isEmpty() )
+ OutMeta( rStrm, pIndent, OOO_STRING_SVTOOLS_HTML_META_author, rAuthor, false,
+ pNonConvertableChars );
+
+ // created
+ ::util::DateTime uDT = i_xDocProps->getCreationDate();
+ OUStringBuffer aBuffer;
+ ::sax::Converter::convertTimeOrDateTime(aBuffer, uDT);
+
+ OutMeta( rStrm, pIndent, OOO_STRING_SVTOOLS_HTML_META_created, aBuffer.makeStringAndClear(), false,
+ pNonConvertableChars );
+
+ // changedby
+ const OUString& rChangedBy = i_xDocProps->getModifiedBy();
+ if( !rChangedBy.isEmpty() )
+ OutMeta( rStrm, pIndent, OOO_STRING_SVTOOLS_HTML_META_changedby, rChangedBy, false,
+ pNonConvertableChars );
+
+ // changed
+ uDT = i_xDocProps->getModificationDate();
+ ::sax::Converter::convertTimeOrDateTime(aBuffer, uDT);
+
+ OutMeta( rStrm, pIndent, OOO_STRING_SVTOOLS_HTML_META_changed, aBuffer.makeStringAndClear(), false,
+ pNonConvertableChars );
+
+ // Subject
+ const OUString& rTheme = i_xDocProps->getSubject();
+ if( !rTheme.isEmpty() )
+ OutMeta( rStrm, pIndent, OOO_STRING_SVTOOLS_HTML_META_classification, rTheme, false,
+ pNonConvertableChars );
+
+ // Description
+ const OUString& rComment = i_xDocProps->getDescription();
+ if( !rComment.isEmpty() )
+ OutMeta( rStrm, pIndent, OOO_STRING_SVTOOLS_HTML_META_description, rComment, false,
+ pNonConvertableChars);
+
+ // Keywords
+ OUString Keywords = ::comphelper::string::convertCommaSeparated(
+ i_xDocProps->getKeywords());
+ if( !Keywords.isEmpty() )
+ OutMeta( rStrm, pIndent, OOO_STRING_SVTOOLS_HTML_META_keywords, Keywords, false,
+ pNonConvertableChars);
+
+ uno::Reference < script::XTypeConverter > xConverter( script::Converter::create(
+ ::comphelper::getProcessComponentContext() ) );
+ uno::Reference<beans::XPropertySet> xUserDefinedProps(
+ i_xDocProps->getUserDefinedProperties(), uno::UNO_QUERY_THROW);
+ uno::Reference<beans::XPropertySetInfo> xPropInfo =
+ xUserDefinedProps->getPropertySetInfo();
+ DBG_ASSERT(xPropInfo.is(), "UserDefinedProperties Info is null");
+ const uno::Sequence<beans::Property> props = xPropInfo->getProperties();
+ for (const auto& rProp : props)
+ {
+ try
+ {
+ OUString name = rProp.Name;
+ uno::Any aStr = xConverter->convertToSimpleType(
+ xUserDefinedProps->getPropertyValue(name),
+ uno::TypeClass_STRING);
+ OUString str;
+ aStr >>= str;
+ OUString valstr(comphelper::string::stripEnd(str, ' '));
+ OutMeta( rStrm, pIndent, name, valstr, false,
+ pNonConvertableChars );
+ }
+ catch (const uno::Exception&)
+ {
+ // may happen with concurrent modification...
+ SAL_INFO("sfx", "SfxFrameHTMLWriter::Out_DocInfo: exception");
+ }
+ }
+}
+
+void SfxFrameHTMLWriter::Out_FrameDescriptor(
+ SvStream& rOut, const OUString& rBaseURL, const uno::Reference < beans::XPropertySet >& xSet,
+ OUString *pNonConvertableChars )
+{
+ try
+ {
+ OStringBuffer sOut;
+ OUString aStr;
+ uno::Any aAny = xSet->getPropertyValue("FrameURL");
+ if ( (aAny >>= aStr) && !aStr.isEmpty() )
+ {
+ OUString aURL = INetURLObject( aStr ).GetMainURL( INetURLObject::DecodeMechanism::ToIUri );
+ if( !aURL.isEmpty() )
+ {
+ aURL = URIHelper::simpleNormalizedMakeRelative(
+ rBaseURL, aURL );
+ sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_src "=\"");
+ rOut.WriteOString( sOut.makeStringAndClear() );
+ HTMLOutFuncs::Out_String( rOut, aURL, pNonConvertableChars );
+ sOut.append('\"');
+ }
+ }
+
+ aAny = xSet->getPropertyValue("FrameName");
+ if ( (aAny >>= aStr) && !aStr.isEmpty() )
+ {
+ sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_name "=\"");
+ rOut.WriteOString( sOut.makeStringAndClear() );
+ HTMLOutFuncs::Out_String( rOut, aStr, pNonConvertableChars );
+ sOut.append('\"');
+ }
+
+ sal_Int32 nVal = SIZE_NOT_SET;
+ aAny = xSet->getPropertyValue("FrameMarginWidth");
+ if ( (aAny >>= nVal) && nVal != SIZE_NOT_SET )
+ {
+ sOut.append(' ').append(OOO_STRING_SVTOOLS_HTML_O_marginwidth)
+ .append('=').append(nVal);
+ }
+ aAny = xSet->getPropertyValue("FrameMarginHeight");
+ if ( (aAny >>= nVal) && nVal != SIZE_NOT_SET )
+ {
+ sOut.append(' ').append(OOO_STRING_SVTOOLS_HTML_O_marginheight)
+ .append('=').append(nVal);
+ }
+
+ bool bVal = true;
+ aAny = xSet->getPropertyValue("FrameIsAutoScroll");
+ if ( (aAny >>= bVal) && !bVal )
+ {
+ aAny = xSet->getPropertyValue("FrameIsScrollingMode");
+ if ( aAny >>= bVal )
+ {
+ const char *pStr = bVal ? sHTML_SC_yes : sHTML_SC_no;
+ sOut.append(OString::Concat(" " OOO_STRING_SVTOOLS_HTML_O_scrolling) +
+ pStr);
+ }
+ }
+
+ // frame border (MS+Netscape-Extension)
+ aAny = xSet->getPropertyValue("FrameIsAutoBorder");
+ if ( (aAny >>= bVal) && !bVal )
+ {
+ aAny = xSet->getPropertyValue("FrameIsBorder");
+ if ( aAny >>= bVal )
+ {
+ const char* pStr = bVal ? sHTML_SC_yes : sHTML_SC_no;
+ sOut.append(' ').append(OOO_STRING_SVTOOLS_HTML_O_frameborder)
+ .append('=').append(pStr);
+ }
+ }
+ rOut.WriteOString( sOut.makeStringAndClear() );
+ }
+ catch (const uno::Exception&)
+ {
+ }
+}
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/bastyp/helper.cxx b/sfx2/source/bastyp/helper.cxx
new file mode 100644
index 000000000..96f3b67f1
--- /dev/null
+++ b/sfx2/source/bastyp/helper.cxx
@@ -0,0 +1,229 @@
+/* -*- 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 <string_view>
+
+#include <helper.hxx>
+#include <com/sun/star/sdbc/XResultSet.hpp>
+#include <com/sun/star/sdbc/XRow.hpp>
+#include <com/sun/star/task/InteractionHandler.hpp>
+#include <com/sun/star/ucb/CommandAbortedException.hpp>
+#include <com/sun/star/ucb/XContentAccess.hpp>
+#include <com/sun/star/ucb/XDynamicResultSet.hpp>
+#include <com/sun/star/io/XInputStream.hpp>
+#include <sal/log.hxx>
+#include <tools/diagnose_ex.h>
+#include <tools/debug.hxx>
+#include <tools/urlobj.hxx>
+#include <ucbhelper/content.hxx>
+#include <ucbhelper/commandenvironment.hxx>
+#include <comphelper/processfactory.hxx>
+
+using namespace com::sun::star;
+using namespace comphelper;
+using namespace osl;
+
+using ::std::vector;
+
+
+std::vector<OUString> SfxContentHelper::GetResultSet( const OUString& rURL )
+{
+ vector<OUString> aList;
+ try
+ {
+ ::ucbhelper::Content aCnt( rURL, uno::Reference< ucb::XCommandEnvironment >(), comphelper::getProcessComponentContext() );
+ uno::Reference< sdbc::XResultSet > xResultSet;
+ uno::Reference< ucb::XDynamicResultSet > xDynResultSet;
+
+ try
+ {
+ xDynResultSet = aCnt.createDynamicCursor( { "Title", "ContentType", "IsFolder" } );
+ if ( xDynResultSet.is() )
+ xResultSet = xDynResultSet->getStaticResultSet();
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "sfx.bastyp", "GetResultSet" );
+ }
+
+
+ if ( xResultSet.is() )
+ {
+ uno::Reference< sdbc::XRow > xRow( xResultSet, uno::UNO_QUERY );
+ uno::Reference< ucb::XContentAccess > xContentAccess( xResultSet, uno::UNO_QUERY );
+
+ try
+ {
+ while ( xResultSet->next() )
+ {
+ OUString aTitle( xRow->getString(1) );
+ OUString aType( xRow->getString(2) );
+ OUString aRow = aTitle +
+ "\t" +
+ aType +
+ "\t" +
+ xContentAccess->queryContentIdentifierString();
+ aList.push_back( aRow );
+ }
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "sfx.bastyp", "XContentAccess::next()" );
+ }
+ }
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "sfx.bastyp", "GetResultSet");
+ }
+
+ return aList;
+}
+
+
+std::vector< OUString > SfxContentHelper::GetHelpTreeViewContents( const OUString& rURL )
+{
+ vector< OUString > aProperties;
+ try
+ {
+ uno::Reference< uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext();
+ uno::Reference< task::XInteractionHandler > xInteractionHandler(
+ task::InteractionHandler::createWithParent(xContext, nullptr), uno::UNO_QUERY_THROW );
+
+ ::ucbhelper::Content aCnt( rURL, new ::ucbhelper::CommandEnvironment( xInteractionHandler, uno::Reference< ucb::XProgressHandler >() ), comphelper::getProcessComponentContext() );
+ uno::Reference< sdbc::XResultSet > xResultSet;
+
+ try
+ {
+ uno::Reference< ucb::XDynamicResultSet > xDynResultSet = aCnt.createDynamicCursor( { "Title", "IsFolder" } );
+ if ( xDynResultSet.is() )
+ xResultSet = xDynResultSet->getStaticResultSet();
+ }
+ catch( const ucb::CommandAbortedException& )
+ {
+ }
+ catch( const uno::Exception& )
+ {
+ }
+
+ if ( xResultSet.is() )
+ {
+ uno::Reference< sdbc::XRow > xRow( xResultSet, uno::UNO_QUERY );
+ uno::Reference< ucb::XContentAccess > xContentAccess( xResultSet, uno::UNO_QUERY );
+
+ try
+ {
+ while ( xResultSet->next() )
+ {
+ OUString aTitle( xRow->getString(1) );
+ bool bFolder = xRow->getBoolean(2);
+ OUString aRow = aTitle + "\t" +
+ xContentAccess->queryContentIdentifierString() + "\t" +
+ (bFolder ? std::u16string_view(u"1") : std::u16string_view(u"0"));
+ aProperties.push_back( aRow );
+ }
+ }
+ catch( const ucb::CommandAbortedException& )
+ {
+ }
+ catch( const uno::Exception& )
+ {
+ }
+ }
+ }
+ catch( const uno::Exception& )
+ {
+ }
+
+ return aProperties;
+}
+
+
+OUString SfxContentHelper::GetActiveHelpString( const OUString& rURL )
+{
+ OUStringBuffer aRet;
+ try
+ {
+ uno::Reference< uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext();
+ uno::Reference< task::XInteractionHandler > xInteractionHandler(
+ task::InteractionHandler::createWithParent(xContext, nullptr), uno::UNO_QUERY_THROW );
+ ::ucbhelper::Content aCnt( rURL, new ::ucbhelper::CommandEnvironment( xInteractionHandler, uno::Reference< ucb::XProgressHandler >() ), comphelper::getProcessComponentContext() );
+ // open the "active help" stream
+ uno::Reference< io::XInputStream > xStream = aCnt.openStream();
+ // and convert it to a String
+ uno::Sequence< sal_Int8 > lData;
+ sal_Int32 nRead = xStream->readBytes( lData, 1024 );
+ while ( nRead > 0 )
+ {
+ OString sOldString( reinterpret_cast<char const *>(lData.getConstArray()), nRead );
+ OUString sString = OStringToOUString( sOldString, RTL_TEXTENCODING_UTF8 );
+ aRet.append( sString );
+
+ nRead = xStream->readBytes( lData, 1024 );
+ }
+ }
+ catch( const uno::Exception& )
+ {
+ }
+
+ return aRet.makeStringAndClear();
+}
+
+
+bool SfxContentHelper::IsHelpErrorDocument( std::u16string_view rURL )
+{
+ bool bRet = false;
+ try
+ {
+ ::ucbhelper::Content aCnt( INetURLObject( rURL ).GetMainURL( INetURLObject::DecodeMechanism::NONE ),
+ uno::Reference< ucb::XCommandEnvironment >(),
+ comphelper::getProcessComponentContext() );
+ if ( !( aCnt.getPropertyValue( "IsErrorDocument" ) >>= bRet ) )
+ {
+ SAL_WARN( "sfx.bastyp", "Property 'IsErrorDocument' is missing" );
+ }
+ }
+ catch( const uno::Exception& )
+ {
+ }
+
+ return bRet;
+}
+
+
+sal_Int64 SfxContentHelper::GetSize( std::u16string_view rContent )
+{
+ sal_Int64 nSize = 0;
+ INetURLObject aObj( rContent );
+ DBG_ASSERT( aObj.GetProtocol() != INetProtocol::NotValid, "Invalid URL!" );
+ try
+ {
+ ::ucbhelper::Content aCnt( aObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ), uno::Reference< ucb::XCommandEnvironment >(), comphelper::getProcessComponentContext() );
+ aCnt.getPropertyValue( "Size" ) >>= nSize;
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "sfx.bastyp", "" );
+ }
+ return nSize;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/bastyp/mieclip.cxx b/sfx2/source/bastyp/mieclip.cxx
new file mode 100644
index 000000000..e40c5bd29
--- /dev/null
+++ b/sfx2/source/bastyp/mieclip.cxx
@@ -0,0 +1,103 @@
+/* -*- 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 <o3tl/safeint.hxx>
+#include <o3tl/string_view.hxx>
+#include <tools/stream.hxx>
+#include <comphelper/string.hxx>
+
+#include <sfx2/mieclip.hxx>
+
+MSE40HTMLClipFormatObj::~MSE40HTMLClipFormatObj()
+{
+}
+
+SvStream* MSE40HTMLClipFormatObj::IsValid( SvStream& rStream )
+{
+ bool bRet = false;
+ pStrm.reset();
+
+ OStringBuffer sLine;
+ sal_Int32 nStt = -1, nEnd = -1, nFragStart = -1, nFragEnd = -1;
+ sal_Int32 nIndex = 0;
+
+ rStream.Seek(STREAM_SEEK_TO_BEGIN);
+ rStream.ResetError();
+
+ if( rStream.ReadLine( sLine ) &&
+ o3tl::getToken(sLine, 0, ':', nIndex ) == "Version" )
+ {
+ while( rStream.ReadLine( sLine ) )
+ {
+ nIndex = 0;
+ std::string_view sTmp(o3tl::getToken(sLine, 0, ':', nIndex));
+ std::string_view sView(sLine);
+ if (sTmp == "StartHTML")
+ nStt = o3tl::toInt32(sView.substr(nIndex));
+ else if (sTmp == "EndHTML")
+ nEnd = o3tl::toInt32(sView.substr(nIndex));
+ else if (sTmp == "StartFragment")
+ nFragStart = o3tl::toInt32(sView.substr(nIndex));
+ else if (sTmp == "EndFragment")
+ nFragEnd = o3tl::toInt32(sView.substr(nIndex));
+ else if (sTmp == "SourceURL")
+ sBaseURL = OStringToOUString( sView.substr(nIndex), RTL_TEXTENCODING_UTF8 );
+
+ if (nEnd >= 0 && nStt >= 0 &&
+ (!sBaseURL.isEmpty() || rStream.Tell() >= o3tl::make_unsigned(nStt)))
+ {
+ bRet = true;
+ break;
+ }
+ }
+ }
+
+ if( bRet )
+ {
+ rStream.Seek( nStt );
+
+ pStrm.reset( new SvMemoryStream( ( nEnd - nStt < 0x10000l
+ ? nEnd - nStt + 32
+ : 0 )) );
+ pStrm->WriteStream( rStream );
+ pStrm->SetStreamSize( nEnd - nStt + 1 );
+ pStrm->Seek( STREAM_SEEK_TO_BEGIN );
+ return pStrm.get();
+ }
+
+ if (nFragStart > 0 && nFragEnd > 0 && nFragEnd > nFragStart)
+ {
+ size_t nSize = nFragEnd - nFragStart + 1;
+ if (nSize < 0x10000L)
+ {
+ rStream.Seek(nFragStart);
+ pStrm.reset( new SvMemoryStream(nSize) );
+ pStrm->WriteStream( rStream );
+ pStrm->SetStreamSize(nSize);
+ pStrm->Seek(STREAM_SEEK_TO_BEGIN);
+ return pStrm.get();
+ }
+ }
+
+ return nullptr;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/bastyp/progress.cxx b/sfx2/source/bastyp/progress.cxx
new file mode 100644
index 000000000..6d73d8316
--- /dev/null
+++ b/sfx2/source/bastyp/progress.cxx
@@ -0,0 +1,401 @@
+/* -*- 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/progress.hxx>
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/task/XStatusIndicatorFactory.hpp>
+
+#include <svl/eitem.hxx>
+#include <tools/debug.hxx>
+#include <sal/log.hxx>
+
+#include <appdata.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/frame.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/app.hxx>
+#include <sfxtypes.hxx>
+#include <sfx2/docfile.hxx>
+#include <sfx2/sfxsids.hrc>
+#include <workwin.hxx>
+#include <sfxbasecontroller_internal.hxx>
+#include <time.h>
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::task;
+
+struct SfxProgress_Impl
+{
+ Reference < XStatusIndicator > xStatusInd;
+ OUString aText;
+ sal_uInt32 nMax;
+ clock_t nCreate;
+ bool bWaitMode;
+ bool bRunning;
+
+ SfxProgress* pActiveProgress;
+ SfxObjectShellRef xObjSh;
+ SfxWorkWindow* pWorkWin;
+ SfxViewFrame* pView;
+
+ explicit SfxProgress_Impl();
+};
+
+SfxProgress_Impl::SfxProgress_Impl()
+ : nMax(0)
+ , nCreate(0)
+ , bWaitMode(false)
+ , bRunning(false)
+ , pActiveProgress(nullptr)
+ , pWorkWin(nullptr)
+ , pView(nullptr)
+{
+}
+
+
+SfxProgress::SfxProgress
+(
+ SfxObjectShell* pObjSh, /* The action is performed on the
+ SfxObjectShell which can be NULL.
+ When it is then the application will be
+ used */
+
+ const OUString& rText, /* Text, which appears before the Statusmonitor
+ in the status line */
+
+ sal_uInt32 nRange, /* Max value for range */
+
+ bool bWait /* Activate the wait-Pointer initially (TRUE) */
+)
+
+/* [Description]
+
+ The constructor of the class SfxProgress switches the SfxObjectShell
+ passed as parameter and SfxViewFrames which display this document in
+ a progress mode. Ie as long as one of those SfxViewFrame instances is
+ active the associated SfxDispatcher and associated Window is disabled.
+ A progress-bar will be displayed in the status bar,
+*/
+
+: pImpl( new SfxProgress_Impl ),
+ nVal(0),
+ bSuspended(true)
+{
+ pImpl->bRunning = true;
+
+ pImpl->xObjSh = pObjSh;
+ pImpl->aText = rText;
+ pImpl->nMax = nRange;
+ pImpl->bWaitMode = bWait;
+ pImpl->nCreate = Get10ThSec();
+ SAL_INFO(
+ "sfx.bastyp",
+ "SfxProgress: created for '" << rText << "' at " << pImpl->nCreate
+ << "ds");
+ pImpl->pWorkWin = nullptr;
+ pImpl->pView = nullptr;
+
+ pImpl->pActiveProgress = GetActiveProgress( pObjSh );
+ if ( pObjSh )
+ pObjSh->SetProgress_Impl(this);
+ else if( !pImpl->pActiveProgress )
+ SfxGetpApp()->SetProgress_Impl(this);
+ Resume();
+}
+
+
+SfxProgress::~SfxProgress()
+
+/* [Description]
+
+ The destructor of the class SfxProgress restores the old status,
+ the documents are released again and the status bar shows the items again.
+*/
+
+{
+ Stop();
+ if ( pImpl->xStatusInd.is() )
+ pImpl->xStatusInd->end();
+}
+
+
+void SfxProgress::Stop()
+
+/* [Description]
+
+ Early Exit of <SfxProgress>.
+*/
+
+{
+ if( pImpl->pActiveProgress )
+ {
+ if ( pImpl->xObjSh.is() && pImpl->xObjSh->GetProgress() == this )
+ pImpl->xObjSh->SetProgress_Impl(nullptr);
+ return;
+ }
+
+ if ( !pImpl->bRunning )
+ return;
+ pImpl->bRunning = false;
+ SAL_INFO(
+ "sfx.bastyp", "SfxProgress: destroyed at " << Get10ThSec() << "ds");
+
+ Suspend();
+ if ( pImpl->xObjSh.is() )
+ pImpl->xObjSh->SetProgress_Impl(nullptr);
+ else
+ SfxGetpApp()->SetProgress_Impl(nullptr);
+}
+
+void SfxProgress::SetState
+(
+ sal_uInt32 nNewVal, /* new value for the progress bar */
+
+ sal_uInt32 nNewRange /* new maximum value, 0 for retaining the old */
+)
+/* [Description]
+
+ Setting the current status, after a time delay Reschedule is called.
+*/
+
+{
+ if( pImpl->pActiveProgress ) return;
+
+ nVal = nNewVal;
+
+ // new Range?
+ if ( nNewRange && nNewRange != pImpl->nMax )
+ {
+ SAL_INFO(
+ "sfx.bastyp",
+ "SfxProgress: range changed from " << pImpl->nMax << " to "
+ << nNewRange);
+ pImpl->nMax = nNewRange;
+ }
+
+ if ( !pImpl->xStatusInd.is() )
+ {
+ // get the active ViewFrame of the document this progress is working on
+ // if it doesn't work on a document, take the current ViewFrame
+ SfxObjectShell* pObjSh = pImpl->xObjSh.get();
+ pImpl->pView = SfxViewFrame::Current();
+ DBG_ASSERT( pImpl->pView || pObjSh, "Can't make progress bar!");
+ if ( pObjSh && ( !pImpl->pView || pObjSh != pImpl->pView->GetObjectShell() ) )
+ {
+ // current document does not belong to current ViewFrame; take it's first visible ViewFrame
+ SfxViewFrame* pDocView = SfxViewFrame::GetFirst( pObjSh );
+ if ( pDocView )
+ pImpl->pView = pDocView;
+ else
+ {
+ // don't show status indicator for hidden documents (only valid while loading)
+ SfxMedium* pMedium = pObjSh->GetMedium();
+ const SfxBoolItem* pHiddenItem = SfxItemSet::GetItem<SfxBoolItem>(pMedium->GetItemSet(), SID_HIDDEN, false);
+ if ( !pHiddenItem || !pHiddenItem->GetValue() )
+ {
+ const SfxUnoAnyItem* pIndicatorItem = SfxItemSet::GetItem<SfxUnoAnyItem>(pMedium->GetItemSet(), SID_PROGRESS_STATUSBAR_CONTROL, false);
+ Reference< XStatusIndicator > xInd;
+ if ( pIndicatorItem && (pIndicatorItem->GetValue()>>=xInd) )
+ pImpl->xStatusInd = xInd;
+ }
+ }
+ }
+ else if ( pImpl->pView )
+ {
+ pImpl->pWorkWin = SfxGetpApp()->GetWorkWindow_Impl( pImpl->pView );
+ if ( pImpl->pWorkWin )
+ pImpl->xStatusInd = pImpl->pWorkWin->GetStatusIndicator();
+ }
+
+ if ( pImpl->xStatusInd.is() )
+ {
+ pImpl->xStatusInd->start( pImpl->aText, pImpl->nMax );
+ pImpl->pView = nullptr;
+ }
+ }
+
+ if ( pImpl->xStatusInd.is() )
+ {
+ pImpl->xStatusInd->setValue( nNewVal );
+ }
+}
+
+
+void SfxProgress::Resume()
+
+/* [Description]
+
+ Resumed the status of the display after an interrupt.
+
+ [Cross-reference]
+
+ <SfxProgress::Suspend()>
+*/
+
+{
+ if( pImpl->pActiveProgress ) return;
+ if ( !bSuspended )
+ return;
+
+ SAL_INFO("sfx.bastyp", "SfxProgress: resumed");
+ if ( pImpl->xStatusInd.is() )
+ {
+ pImpl->xStatusInd->start( pImpl->aText, pImpl->nMax );
+ pImpl->xStatusInd->setValue( nVal );
+ }
+
+ if ( pImpl->bWaitMode )
+ {
+ if ( pImpl->xObjSh.is() )
+ {
+ for ( SfxViewFrame *pFrame = SfxViewFrame::GetFirst(pImpl->xObjSh.get() );
+ pFrame;
+ pFrame = SfxViewFrame::GetNext( *pFrame, pImpl->xObjSh.get() ) )
+ pFrame->GetWindow().EnterWait();
+ }
+ }
+
+ if ( pImpl->xObjSh.is() )
+ {
+ SfxViewFrame *pFrame = SfxViewFrame::GetFirst(pImpl->xObjSh.get());
+ if ( pFrame )
+ pFrame->GetBindings().ENTERREGISTRATIONS();
+ }
+
+ bSuspended = false;
+}
+
+
+void SfxProgress::Suspend()
+
+/* [Description]
+
+ Interrupts the status of the display
+
+ [Cross-reference]
+
+ <SfxProgress::Resume()>
+*/
+
+{
+ if( pImpl->pActiveProgress ) return;
+ if ( bSuspended )
+ return;
+
+ SAL_INFO("sfx.bastyp", "SfxProgress: suspended");
+ bSuspended = true;
+
+ if ( pImpl->xStatusInd.is() )
+ {
+ pImpl->xStatusInd->reset();
+ }
+
+ if ( pImpl->xObjSh.is() )
+ {
+ for ( SfxViewFrame *pFrame =
+ SfxViewFrame::GetFirst(pImpl->xObjSh.get());
+ pFrame;
+ pFrame = SfxViewFrame::GetNext( *pFrame, pImpl->xObjSh.get() ) )
+ pFrame->GetWindow().LeaveWait();
+ }
+ if ( pImpl->xObjSh.is() )
+ {
+ SfxViewFrame *pFrame = SfxViewFrame::GetFirst( pImpl->xObjSh.get() );
+ if ( pFrame )
+ pFrame->GetBindings().LEAVEREGISTRATIONS();
+ }
+}
+
+
+void SfxProgress::Reschedule()
+
+/* [Description]
+
+ Reschedule, callable from the outside
+*/
+
+{
+ SFX_STACK(SfxProgress::Reschedule);
+}
+
+
+SfxProgress* SfxProgress::GetActiveProgress
+(
+ SfxObjectShell const * pDocSh /* the <SfxObjectShell>, which should be
+ queried after a current <SfxProgress>,
+ or 0 if a current SfxProgress for the
+ entire application should be obtained.
+ The pointer only needs at the time of
+ the call to be valid.
+ */
+)
+
+/* [Description]
+
+ This method is used to check whether and which <SfxProgress> is currently
+ active for a specific instance of SfxObjectShell or even an entire
+ application. This can for example be used to check for Time-Out-Events, etc.
+
+ Instead of a pointer to the SfxProgress the SfxObjectShell may be
+ pointed at the SfxProgress of the application, with the query
+ 'SfxProgress:: GetActiveProgress (pMyDocSh)' thus the current
+ SfxProgress of 'pMyDocSh' is delivered, otherwise the SfxProgress of
+ the application or a 0-pointer.
+
+ [Note]
+
+ If no SfxProgress is running in the application and also not at the
+ specified SfxObjectShell, then this method will always return 0,
+ even if one SfxProgress runs on another SfxObjectShell.
+
+ [Cross-reference]
+
+ <SfxApplication::GetProgress()const>
+ <SfxObjectShell::GetProgress()const>
+*/
+
+{
+ if ( !SfxApplication::Get() )
+ return nullptr;
+
+ SfxProgress *pProgress = nullptr;
+ if ( pDocSh )
+ pProgress = pDocSh->GetProgress();
+ if ( !pProgress )
+ pProgress = SfxGetpApp()->GetProgress();
+ return pProgress;
+}
+
+
+void SfxProgress::EnterLock()
+{
+ SfxGetpApp()->Get_Impl()->nRescheduleLocks++;
+}
+
+
+void SfxProgress::LeaveLock()
+{
+ SfxAppData_Impl *pImp = SfxGetpApp()->Get_Impl();
+ DBG_ASSERT( 0 != pImp->nRescheduleLocks, "SFxProgress::LeaveLock but no locks" );
+ pImp->nRescheduleLocks--;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/bastyp/sfxhtml.cxx b/sfx2/source/bastyp/sfxhtml.cxx
new file mode 100644
index 000000000..5907a660b
--- /dev/null
+++ b/sfx2/source/bastyp/sfxhtml.cxx
@@ -0,0 +1,345 @@
+/* -*- 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 <tools/urlobj.hxx>
+#include <tools/debug.hxx>
+
+#include <sfx2/docfile.hxx>
+#include <sfx2/event.hxx>
+#include <openflag.hxx>
+
+#include <svl/numformat.hxx>
+#include <svtools/htmlkywd.hxx>
+#include <svtools/htmltokn.h>
+#include <vcl/imap.hxx>
+#include <vcl/imapcirc.hxx>
+#include <vcl/imapobj.hxx>
+#include <vcl/imappoly.hxx>
+#include <vcl/imaprect.hxx>
+#include <svl/zforlist.hxx>
+
+#include <sfx2/sfxhtml.hxx>
+
+#include <comphelper/string.hxx>
+#include <o3tl/string_view.hxx>
+
+#include <vector>
+
+
+using namespace ::com::sun::star;
+
+
+// <INPUT TYPE=xxx>
+HTMLOptionEnum<IMapObjectType> const aAreaShapeOptEnums[] =
+{
+ { OOO_STRING_SVTOOLS_HTML_SH_rect, IMapObjectType::Rectangle },
+ { OOO_STRING_SVTOOLS_HTML_SH_rectangle, IMapObjectType::Rectangle },
+ { OOO_STRING_SVTOOLS_HTML_SH_circ, IMapObjectType::Circle },
+ { OOO_STRING_SVTOOLS_HTML_SH_circle, IMapObjectType::Circle },
+ { OOO_STRING_SVTOOLS_HTML_SH_poly, IMapObjectType::Polygon },
+ { OOO_STRING_SVTOOLS_HTML_SH_polygon, IMapObjectType::Polygon },
+ { nullptr, IMapObjectType::Rectangle }
+};
+
+SfxHTMLParser::SfxHTMLParser( SvStream& rStream, bool bIsNewDoc,
+ SfxMedium *pMed )
+ : HTMLParser(rStream, bIsNewDoc)
+ , pMedium(pMed)
+ , eScriptType(STARBASIC)
+{
+ DBG_ASSERT( RTL_TEXTENCODING_UTF8 == GetSrcEncoding( ),
+ "SfxHTMLParser::SfxHTMLParser: From where comes ZS?" );
+
+ DBG_ASSERT( !IsSwitchToUCS2(),
+ "SfxHTMLParser::SfxHTMLParser: Switch to UCS2?" );
+
+ // If the file starts with a BOM, switch to UCS2.
+ SetSwitchToUCS2( true );
+}
+
+SfxHTMLParser::~SfxHTMLParser()
+{
+ DBG_ASSERT( !pDLMedium, "Here is a File Download that has got stuck" );
+}
+
+bool SfxHTMLParser::ParseMapOptions(
+ ImageMap* pImageMap, const HTMLOptions& rOptions)
+{
+ DBG_ASSERT( pImageMap, "ParseMapOptions: No Image-Map" );
+
+ OUString aName;
+
+ for (size_t i = rOptions.size(); i; )
+ {
+ const HTMLOption& aOption = rOptions[--i];
+ if ( aOption.GetToken() == HtmlOptionId::NAME )
+ aName = aOption.GetString();
+ }
+
+ if( !aName.isEmpty() )
+ pImageMap->SetName( aName );
+
+ return !aName.isEmpty();
+}
+
+bool SfxHTMLParser::ParseAreaOptions(ImageMap * pImageMap, std::u16string_view rBaseURL,
+ const HTMLOptions& rOptions,
+ SvMacroItemId nEventMouseOver,
+ SvMacroItemId nEventMouseOut )
+{
+ DBG_ASSERT( pImageMap, "ParseAreaOptions: no Image-Map" );
+
+ IMapObjectType nShape = IMapObjectType::Rectangle;
+ std::vector<sal_uInt32> aCoords;
+ OUString aName, aHRef, aAlt, aTarget;
+ bool bNoHRef = false;
+ SvxMacroTableDtor aMacroTbl;
+
+ for (size_t i = rOptions.size(); i; )
+ {
+ SvMacroItemId nEvent = SvMacroItemId::NONE;
+ ScriptType eScrpType = STARBASIC;
+ const HTMLOption& rOption = rOptions[--i];
+ switch( rOption.GetToken() )
+ {
+ case HtmlOptionId::NAME:
+ aName = rOption.GetString();
+ break;
+ case HtmlOptionId::SHAPE:
+ rOption.GetEnum( nShape, aAreaShapeOptEnums );
+ break;
+ case HtmlOptionId::COORDS:
+ rOption.GetNumbers( aCoords );
+ break;
+ case HtmlOptionId::HREF:
+ aHRef = INetURLObject::GetAbsURL( rBaseURL, rOption.GetString() );
+ break;
+ case HtmlOptionId::NOHREF:
+ bNoHRef = true;
+ break;
+ case HtmlOptionId::ALT:
+ aAlt = rOption.GetString();
+ break;
+ case HtmlOptionId::TARGET:
+ aTarget = rOption.GetString();
+ break;
+
+ case HtmlOptionId::ONMOUSEOVER:
+ eScrpType = JAVASCRIPT;
+ [[fallthrough]];
+ case HtmlOptionId::SDONMOUSEOVER:
+ nEvent = nEventMouseOver;
+ goto IMAPOBJ_SETEVENT;
+
+ case HtmlOptionId::ONMOUSEOUT:
+ eScrpType = JAVASCRIPT;
+ [[fallthrough]];
+ case HtmlOptionId::SDONMOUSEOUT:
+ nEvent = nEventMouseOut;
+ goto IMAPOBJ_SETEVENT;
+IMAPOBJ_SETEVENT:
+ if( nEvent != SvMacroItemId::NONE)
+ {
+ OUString sTmp( rOption.GetString() );
+ if( !sTmp.isEmpty() )
+ {
+ sTmp = convertLineEnd(sTmp, GetSystemLineEnd());
+ aMacroTbl.Insert( nEvent, SvxMacro( sTmp, "", eScrpType ));
+ }
+ }
+ break;
+ default: break;
+ }
+ }
+
+ if( bNoHRef )
+ aHRef.clear();
+
+ bool bNewArea = true;
+ switch( nShape )
+ {
+ case IMapObjectType::Rectangle:
+ if( aCoords.size() >=4 )
+ {
+ tools::Rectangle aRect( aCoords[0], aCoords[1],
+ aCoords[2], aCoords[3] );
+ std::unique_ptr<IMapRectangleObject> pMapRObj( new IMapRectangleObject(aRect, aHRef, aAlt, OUString(), aTarget, aName,
+ !bNoHRef ));
+ if( !aMacroTbl.empty() )
+ pMapRObj->SetMacroTable( aMacroTbl );
+ pImageMap->InsertIMapObject( std::move(pMapRObj) );
+ }
+ break;
+ case IMapObjectType::Circle:
+ if( aCoords.size() >=3 )
+ {
+ Point aPoint( aCoords[0], aCoords[1] );
+ std::unique_ptr<IMapCircleObject> pMapCObj(new IMapCircleObject(aPoint, aCoords[2],aHRef, aAlt, OUString(),
+ aTarget, aName, !bNoHRef ));
+ if( !aMacroTbl.empty() )
+ pMapCObj->SetMacroTable( aMacroTbl );
+ pImageMap->InsertIMapObject( std::move(pMapCObj) );
+ }
+ break;
+ case IMapObjectType::Polygon:
+ if( aCoords.size() >=6 )
+ {
+ sal_uInt16 nCount = aCoords.size() / 2;
+ tools::Polygon aPoly( nCount );
+ for( sal_uInt16 i=0; i<nCount; i++ )
+ aPoly[i] = Point( aCoords[2*i], aCoords[2*i+1] );
+ std::unique_ptr<IMapPolygonObject> pMapPObj(new IMapPolygonObject( aPoly, aHRef, aAlt, OUString(), aTarget, aName,
+ !bNoHRef ));
+ if( !aMacroTbl.empty() )
+ pMapPObj->SetMacroTable( aMacroTbl );
+ pImageMap->InsertIMapObject( std::move(pMapPObj) );
+ }
+ break;
+ default:
+ bNewArea = false;
+ }
+
+ return bNewArea;
+}
+
+void SfxHTMLParser::StartFileDownload(const OUString& rURL)
+{
+ DBG_ASSERT( !pDLMedium, "StartFileDownload when active Download" );
+ if( pDLMedium )
+ return;
+
+ pDLMedium.reset( new SfxMedium( rURL, SFX_STREAM_READONLY ) );
+ pDLMedium->Download();
+}
+
+bool SfxHTMLParser::FinishFileDownload( OUString& rStr )
+{
+ bool bOK = pDLMedium && pDLMedium->GetErrorCode() == ERRCODE_NONE;
+ if( bOK )
+ {
+ SvStream* pStream = pDLMedium->GetInStream();
+ DBG_ASSERT( pStream, "No In-Stream received from Medium" );
+
+ SvMemoryStream aStream;
+ if( pStream )
+ aStream.WriteStream( *pStream );
+
+ sal_uInt64 const nLen = aStream.TellEnd();
+ aStream.Seek( 0 );
+ rStr = read_uInt8s_ToOUString(aStream, nLen, RTL_TEXTENCODING_UTF8);
+ }
+
+ pDLMedium.reset();
+
+ return bOK;
+}
+
+void SfxHTMLParser::GetScriptType_Impl( SvKeyValueIterator *pHTTPHeader )
+{
+ aScriptType = SVX_MACRO_LANGUAGE_JAVASCRIPT;
+ eScriptType = JAVASCRIPT;
+ if( !pHTTPHeader )
+ return;
+
+ SvKeyValue aKV;
+ for( bool bCont = pHTTPHeader->GetFirst( aKV ); bCont;
+ bCont = pHTTPHeader->GetNext( aKV ) )
+ {
+ if( aKV.GetKey().equalsIgnoreAsciiCase(
+ OOO_STRING_SVTOOLS_HTML_META_content_script_type ) )
+ {
+ if( !aKV.GetValue().isEmpty() )
+ {
+ OUString aTmp( aKV.GetValue() );
+ if( aTmp.startsWithIgnoreAsciiCase( "text/" ) )
+ aTmp = aTmp.copy( 5 );
+ else if( aTmp.startsWithIgnoreAsciiCase( "application/" ) )
+ aTmp = aTmp.copy( 12 );
+ else
+ break;
+
+ if( aTmp.startsWithIgnoreAsciiCase( "x-" ) ) // MIME-experimental
+ {
+ aTmp = aTmp.copy( 2 );
+ }
+
+ if( aTmp.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_LG_starbasic ) )
+ {
+ eScriptType = STARBASIC;
+ aScriptType = SVX_MACRO_LANGUAGE_STARBASIC;
+ }
+ if( !aTmp.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_LG_javascript ) )
+ {
+ eScriptType = EXTENDED_STYPE;
+ aScriptType = aTmp;
+ }
+ }
+ break;
+ }
+ }
+}
+
+ScriptType SfxHTMLParser::GetScriptType( SvKeyValueIterator *pHTTPHeader ) const
+{
+ if( aScriptType.isEmpty() )
+ const_cast<SfxHTMLParser *>(this)->GetScriptType_Impl( pHTTPHeader );
+
+ return eScriptType;
+}
+
+const OUString& SfxHTMLParser::GetScriptTypeString(
+ SvKeyValueIterator *pHTTPHeader ) const
+{
+ if( aScriptType.isEmpty() )
+ const_cast<SfxHTMLParser *>(this)->GetScriptType_Impl( pHTTPHeader );
+
+ return aScriptType;
+}
+
+double SfxHTMLParser::GetTableDataOptionsValNum( sal_uInt32& nNumForm,
+ LanguageType& eNumLang, const OUString& aValStr, std::u16string_view aNumStr,
+ SvNumberFormatter& rFormatter )
+{
+ LanguageType eParseLang(o3tl::toInt32(aNumStr));
+ sal_uInt32 nParseForm = rFormatter.GetFormatForLanguageIfBuiltIn( 0, eParseLang );
+ double fVal;
+ (void)rFormatter.IsNumberFormat(aValStr, nParseForm, fVal);
+ if ( comphelper::string::getTokenCount(aNumStr, ';') > 2 )
+ {
+ sal_Int32 nIdx {0};
+ eNumLang = LanguageType(o3tl::toInt32(o3tl::getToken(aNumStr, 1, ';', nIdx )));
+ OUString aFormat( aNumStr.substr( nIdx ) );
+ sal_Int32 nCheckPos;
+ SvNumFormatType nType;
+ if ( eNumLang != LANGUAGE_SYSTEM )
+ rFormatter.PutEntry( aFormat, nCheckPos, nType, nNumForm, eNumLang );
+ else
+ rFormatter.PutandConvertEntry( aFormat, nCheckPos, nType, nNumForm,
+ eParseLang, eNumLang, true);
+ }
+ else
+ {
+ eNumLang = LANGUAGE_SYSTEM;
+ nNumForm = rFormatter.GetFormatForLanguageIfBuiltIn( 0, eNumLang );
+ }
+ return fVal;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/bastyp/sfxresid.cxx b/sfx2/source/bastyp/sfxresid.cxx
new file mode 100644
index 000000000..1bc6d68bb
--- /dev/null
+++ b/sfx2/source/bastyp/sfxresid.cxx
@@ -0,0 +1,24 @@
+/* -*- 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/sfxresid.hxx>
+
+OUString SfxResId(TranslateId aId) { return Translate::get(aId, Translate::Create("sfx")); }
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/commandpopup/CommandPopup.cxx b/sfx2/source/commandpopup/CommandPopup.cxx
new file mode 100644
index 000000000..f4cdf9243
--- /dev/null
+++ b/sfx2/source/commandpopup/CommandPopup.cxx
@@ -0,0 +1,293 @@
+/* -*- 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 <commandpopup/CommandPopup.hxx>
+
+#include <sfx2/msgpool.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/msg.hxx>
+#include <sfx2/viewfrm.hxx>
+
+#include <comphelper/processfactory.hxx>
+#include <comphelper/dispatchcommand.hxx>
+
+#include <com/sun/star/ui/theModuleUIConfigurationManagerSupplier.hpp>
+#include <com/sun/star/util/URL.hpp>
+#include <com/sun/star/util/URLTransformer.hpp>
+#include <com/sun/star/i18n/CharacterClassification.hpp>
+
+#include <vcl/commandinfoprovider.hxx>
+#include <vcl/event.hxx>
+#include <vcl/svapp.hxx>
+#include <i18nlangtag/languagetag.hxx>
+
+using namespace css;
+
+MenuContentHandler::MenuContentHandler(uno::Reference<frame::XFrame> const& xFrame)
+ : m_xContext(comphelper::getProcessComponentContext())
+ , m_xFrame(xFrame)
+ , m_xCharacterClassification(i18n::CharacterClassification::create(m_xContext))
+ , m_xURLTransformer(util::URLTransformer::create(m_xContext))
+ , m_sModuleLongName(vcl::CommandInfoProvider::GetModuleIdentifier(xFrame))
+{
+ uno::Reference<ui::XModuleUIConfigurationManagerSupplier> xModuleConfigSupplier;
+ xModuleConfigSupplier.set(ui::theModuleUIConfigurationManagerSupplier::get(m_xContext));
+
+ uno::Reference<ui::XUIConfigurationManager> xConfigurationManager;
+ xConfigurationManager = xModuleConfigSupplier->getUIConfigurationManager(m_sModuleLongName);
+
+ uno::Reference<container::XIndexAccess> xConfigData;
+ xConfigData = xConfigurationManager->getSettings("private:resource/menubar/menubar", false);
+
+ gatherMenuContent(xConfigData, m_aMenuContent);
+}
+
+void MenuContentHandler::gatherMenuContent(
+ uno::Reference<container::XIndexAccess> const& xIndexAccess, MenuContent& rMenuContent)
+{
+ for (sal_Int32 n = 0; n < xIndexAccess->getCount(); n++)
+ {
+ MenuContent aNewContent;
+ uno::Sequence<beans::PropertyValue> aProperties;
+ uno::Reference<container::XIndexAccess> xIndexContainer;
+
+ if (!(xIndexAccess->getByIndex(n) >>= aProperties))
+ continue;
+
+ bool bIsVisible = true;
+ bool bIsEnabled = true;
+
+ for (auto const& rProperty : std::as_const(aProperties))
+ {
+ OUString aPropertyName = rProperty.Name;
+ if (aPropertyName == "CommandURL")
+ rProperty.Value >>= aNewContent.m_aCommandURL;
+ else if (aPropertyName == "ItemDescriptorContainer")
+ rProperty.Value >>= xIndexContainer;
+ else if (aPropertyName == "IsVisible")
+ rProperty.Value >>= bIsVisible;
+ else if (aPropertyName == "Enabled")
+ rProperty.Value >>= bIsEnabled;
+ }
+
+ if (!bIsEnabled || !bIsVisible)
+ continue;
+
+ auto aCommandProperties = vcl::CommandInfoProvider::GetCommandProperties(
+ aNewContent.m_aCommandURL, m_sModuleLongName);
+ OUString aLabel = vcl::CommandInfoProvider::GetLabelForCommand(aCommandProperties);
+ aNewContent.m_aMenuLabel = aLabel;
+ aNewContent.m_aSearchableMenuLabel = toLower(aLabel);
+
+ if (!rMenuContent.m_aFullLabelWithPath.isEmpty())
+ aNewContent.m_aFullLabelWithPath = rMenuContent.m_aFullLabelWithPath + " / ";
+ aNewContent.m_aFullLabelWithPath += aNewContent.m_aMenuLabel;
+
+ aNewContent.m_aTooltip = vcl::CommandInfoProvider::GetTooltipForCommand(
+ aNewContent.m_aCommandURL, aCommandProperties, m_xFrame);
+
+ if (xIndexContainer.is())
+ gatherMenuContent(xIndexContainer, aNewContent);
+
+ rMenuContent.m_aSubMenuContent.push_back(aNewContent);
+ }
+}
+
+void MenuContentHandler::findInMenu(OUString const& rText,
+ std::unique_ptr<weld::TreeView>& rpCommandTreeView,
+ std::vector<CurrentEntry>& rCommandList)
+{
+ m_aAdded.clear();
+
+ OUString aLowerCaseText = toLower(rText);
+
+ auto aTextStartCriterium = [](MenuContent const& rMenuContent, OUString const& rSearchText) {
+ return rMenuContent.m_aSearchableMenuLabel.startsWith(rSearchText);
+ };
+
+ findInMenuRecursive(m_aMenuContent, aLowerCaseText, rpCommandTreeView, rCommandList,
+ aTextStartCriterium);
+
+ auto aTextAllCriterium = [](MenuContent const& rMenuContent, OUString const& rSearchText) {
+ return rMenuContent.m_aSearchableMenuLabel.indexOf(rSearchText) > 0;
+ };
+
+ findInMenuRecursive(m_aMenuContent, aLowerCaseText, rpCommandTreeView, rCommandList,
+ aTextAllCriterium);
+}
+
+void MenuContentHandler::findInMenuRecursive(
+ MenuContent const& rMenuContent, OUString const& rText,
+ std::unique_ptr<weld::TreeView>& rpCommandTreeView, std::vector<CurrentEntry>& rCommandList,
+ std::function<bool(MenuContent const&, OUString const&)> const& rSearchCriterium)
+{
+ for (MenuContent const& aSubContent : rMenuContent.m_aSubMenuContent)
+ {
+ if (rSearchCriterium(aSubContent, rText))
+ {
+ addCommandIfPossible(aSubContent, rpCommandTreeView, rCommandList);
+ }
+ findInMenuRecursive(aSubContent, rText, rpCommandTreeView, rCommandList, rSearchCriterium);
+ }
+}
+
+void MenuContentHandler::addCommandIfPossible(
+ MenuContent const& rMenuContent, const std::unique_ptr<weld::TreeView>& rpCommandTreeView,
+ std::vector<CurrentEntry>& rCommandList)
+{
+ if (m_aAdded.find(rMenuContent.m_aFullLabelWithPath) != m_aAdded.end())
+ return;
+
+ OUString sCommandURL = rMenuContent.m_aCommandURL;
+ util::URL aCommandURL;
+ aCommandURL.Complete = sCommandURL;
+
+ if (!m_xURLTransformer->parseStrict(aCommandURL))
+ return;
+
+ auto* pViewFrame = SfxViewFrame::Current();
+ if (!pViewFrame)
+ return;
+
+ SfxSlotPool& rSlotPool = SfxSlotPool::GetSlotPool(pViewFrame);
+ const SfxSlot* pSlot = rSlotPool.GetUnoSlot(aCommandURL.Path);
+ if (!pSlot)
+ return;
+
+ std::unique_ptr<SfxPoolItem> pState;
+ SfxItemState eState = pViewFrame->GetBindings().QueryState(pSlot->GetSlotId(), pState);
+ if (eState == SfxItemState::DISABLED)
+ return;
+
+ auto xGraphic = vcl::CommandInfoProvider::GetXGraphicForCommand(sCommandURL, m_xFrame);
+ rCommandList.emplace_back(sCommandURL, rMenuContent.m_aTooltip);
+
+ auto pIter = rpCommandTreeView->make_iterator();
+ rpCommandTreeView->insert(nullptr, -1, &rMenuContent.m_aFullLabelWithPath, nullptr, nullptr,
+ nullptr, false, pIter.get());
+ rpCommandTreeView->set_image(*pIter, xGraphic);
+ m_aAdded.insert(rMenuContent.m_aFullLabelWithPath);
+}
+
+OUString MenuContentHandler::toLower(OUString const& rString)
+{
+ const css::lang::Locale& rLocale = Application::GetSettings().GetUILanguageTag().getLocale();
+
+ return m_xCharacterClassification->toLower(rString, 0, rString.getLength(), rLocale);
+}
+
+CommandListBox::CommandListBox(weld::Window* pParent, uno::Reference<frame::XFrame> const& xFrame)
+ : mxBuilder(Application::CreateBuilder(pParent, "sfx/ui/commandpopup.ui"))
+ , mxPopover(mxBuilder->weld_popover("CommandPopup"))
+ , mpEntry(mxBuilder->weld_entry("command_entry"))
+ , mpCommandTreeView(mxBuilder->weld_tree_view("command_treeview"))
+ , mpMenuContentHandler(std::make_unique<MenuContentHandler>(xFrame))
+{
+ mpEntry->connect_changed(LINK(this, CommandListBox, ModifyHdl));
+ mpEntry->connect_key_press(LINK(this, CommandListBox, TreeViewKeyPress));
+ mpCommandTreeView->connect_query_tooltip(LINK(this, CommandListBox, QueryTooltip));
+ mpCommandTreeView->connect_row_activated(LINK(this, CommandListBox, RowActivated));
+
+ Size aFrameSize = pParent->get_size();
+
+ // Set size of the pop-over window
+ tools::Long nWidth = std::max(tools::Long(400), aFrameSize.Width() / 3);
+ mpCommandTreeView->set_size_request(nWidth, 400);
+
+ // Set the location of the pop-over window
+ tools::Rectangle aRect(Point(aFrameSize.Width() / 2, 0), Size(0, 0));
+ mxPopover->popup_at_rect(pParent, aRect);
+ mpEntry->grab_focus();
+}
+
+IMPL_LINK_NOARG(CommandListBox, QueryTooltip, const weld::TreeIter&, OUString)
+{
+ size_t nSelected = mpCommandTreeView->get_selected_index();
+ if (nSelected < maCommandList.size())
+ {
+ auto const& rCurrent = maCommandList[nSelected];
+ return rCurrent.m_aTooltip;
+ }
+ return OUString();
+}
+
+IMPL_LINK_NOARG(CommandListBox, RowActivated, weld::TreeView&, bool)
+{
+ OUString aCommandURL;
+ int nSelected = mpCommandTreeView->get_selected_index();
+ if (nSelected != -1 && nSelected < int(maCommandList.size()))
+ {
+ auto const& rCurrent = maCommandList[nSelected];
+ aCommandURL = rCurrent.m_aCommandURL;
+ }
+ dispatchCommandAndClose(aCommandURL);
+ return true;
+}
+
+IMPL_LINK(CommandListBox, TreeViewKeyPress, const KeyEvent&, rKeyEvent, bool)
+{
+ if (rKeyEvent.GetKeyCode().GetCode() == KEY_DOWN || rKeyEvent.GetKeyCode().GetCode() == KEY_UP)
+ {
+ int nDirection = rKeyEvent.GetKeyCode().GetCode() == KEY_DOWN ? 1 : -1;
+ int nNewIndex = mpCommandTreeView->get_selected_index() + nDirection;
+ nNewIndex = std::clamp(nNewIndex, 0, mpCommandTreeView->n_children() - 1);
+ mpCommandTreeView->select(nNewIndex);
+ mpCommandTreeView->set_cursor(nNewIndex);
+ return true;
+ }
+ else if (rKeyEvent.GetKeyCode().GetCode() == KEY_RETURN)
+ {
+ RowActivated(*mpCommandTreeView);
+ return true;
+ }
+
+ return false;
+}
+
+IMPL_LINK_NOARG(CommandListBox, ModifyHdl, weld::Entry&, void)
+{
+ mpCommandTreeView->clear();
+ maCommandList.clear();
+
+ OUString sText = mpEntry->get_text();
+ if (sText.isEmpty())
+ return;
+
+ mpCommandTreeView->freeze();
+ mpMenuContentHandler->findInMenu(sText, mpCommandTreeView, maCommandList);
+ mpCommandTreeView->thaw();
+
+ if (mpCommandTreeView->n_children() > 0)
+ {
+ mpCommandTreeView->set_cursor(0);
+ mpCommandTreeView->select(0);
+ }
+
+ mpEntry->grab_focus();
+}
+
+void CommandListBox::dispatchCommandAndClose(OUString const& rCommand)
+{
+ mxPopover->popdown();
+
+ if (!rCommand.isEmpty())
+ comphelper::dispatchCommand(rCommand, uno::Sequence<beans::PropertyValue>());
+}
+
+void CommandPopupHandler::showPopup(weld::Window* pParent,
+ css::uno::Reference<css::frame::XFrame> const& xFrame)
+{
+ auto pCommandListBox = std::make_unique<CommandListBox>(pParent, xFrame);
+ pCommandListBox->connect_closed(LINK(this, CommandPopupHandler, PopupModeEnd));
+ mpListBox = std::move(pCommandListBox);
+}
+
+IMPL_LINK_NOARG(CommandPopupHandler, PopupModeEnd, weld::Popover&, void) { mpListBox.reset(); }
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/config/evntconf.cxx b/sfx2/source/config/evntconf.cxx
new file mode 100644
index 000000000..a6d44640a
--- /dev/null
+++ b/sfx2/source/config/evntconf.cxx
@@ -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 .
+ */
+
+
+#include <memory>
+
+#include <sal/log.hxx>
+#include <com/sun/star/uno/Any.hxx>
+#include <comphelper/processfactory.hxx>
+#include <sfx2/evntconf.hxx>
+#include <svl/macitem.hxx>
+#include <tools/diagnose_ex.h>
+
+#include <sfx2/objsh.hxx>
+#include <eventsupplier.hxx>
+
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/container/XNameReplace.hpp>
+#include <com/sun/star/document/XEventsSupplier.hpp>
+#include <com/sun/star/frame/theGlobalEventBroadcaster.hpp>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/uno/Reference.hxx>
+
+
+using namespace com::sun::star;
+
+SfxEventNamesList& SfxEventNamesList::operator=( const SfxEventNamesList& ) = default;
+
+SfxEventNamesList::~SfxEventNamesList()
+{
+}
+
+bool SfxEventNamesItem::operator==( const SfxPoolItem& rAttr ) const
+{
+ assert(SfxPoolItem::operator==(rAttr));
+
+ const SfxEventNamesList& rOwn = aEventsList;
+ const SfxEventNamesList& rOther = static_cast<const SfxEventNamesItem&>( rAttr ).aEventsList;
+
+ if ( rOwn.size() != rOther.size() )
+ return false;
+
+ for ( size_t nNo = 0, nCnt = rOwn.size(); nNo < nCnt; ++nNo )
+ {
+ const SfxEventName &rOwnEvent = rOwn.at( nNo );
+ const SfxEventName &rOtherEvent = rOther.at( nNo );
+ if ( rOwnEvent.mnId != rOtherEvent.mnId ||
+ rOwnEvent.maEventName != rOtherEvent.maEventName ||
+ rOwnEvent.maUIName != rOtherEvent.maUIName )
+ return false;
+ }
+
+ return true;
+
+}
+
+bool SfxEventNamesItem::GetPresentation( SfxItemPresentation,
+ MapUnit,
+ MapUnit,
+ OUString &rText,
+ const IntlWrapper& ) const
+{
+ rText.clear();
+ return false;
+}
+
+SfxEventNamesItem* SfxEventNamesItem::Clone( SfxItemPool *) const
+{
+ return new SfxEventNamesItem(*this);
+}
+
+void SfxEventNamesItem::AddEvent( const OUString& rName, const OUString& rUIName, SvMacroItemId nID )
+{
+ aEventsList.push_back( SfxEventName( nID, rName, !rUIName.isEmpty() ? rUIName : rName ) );
+}
+
+
+static uno::Any CreateEventData_Impl( const SvxMacro *pMacro )
+{
+/*
+ This function converts a SvxMacro into an Any containing three
+ properties. These properties are EventType and Script. Possible
+ values for EventType are StarBasic, JavaScript, ...
+ The Script property should contain the URL to the macro and looks
+ like "macro://./standard.module1.main()"
+
+ If pMacro is NULL, we return an empty property sequence, so PropagateEvent_Impl
+ can delete an event binding.
+*/
+ uno::Any aEventData;
+
+ if ( pMacro )
+ {
+ if ( pMacro->GetScriptType() == STARBASIC )
+ {
+ uno::Sequence < beans::PropertyValue > aProperties(3);
+ beans::PropertyValue *pValues = aProperties.getArray();
+
+ pValues[ 0 ].Name = PROP_EVENT_TYPE;
+ pValues[ 0 ].Value <<= OUString("STAR_BASIC");
+
+ pValues[ 1 ].Name = PROP_LIBRARY;
+ pValues[ 1 ].Value <<= pMacro->GetLibName();
+
+ pValues[ 2 ].Name = PROP_MACRO_NAME;
+ pValues[ 2 ].Value <<= pMacro->GetMacName();
+
+ aEventData <<= aProperties;
+ }
+ else if ( pMacro->GetScriptType() == EXTENDED_STYPE )
+ {
+ uno::Sequence < beans::PropertyValue > aProperties(2);
+ beans::PropertyValue *pValues = aProperties.getArray();
+
+ pValues[ 0 ].Name = PROP_EVENT_TYPE;
+ pValues[ 0 ].Value <<= pMacro->GetLibName();
+
+ pValues[ 1 ].Name = PROP_SCRIPT;
+ pValues[ 1 ].Value <<= pMacro->GetMacName();
+
+ aEventData <<= aProperties;
+ }
+ else if ( pMacro->GetScriptType() == JAVASCRIPT )
+ {
+ uno::Sequence < beans::PropertyValue > aProperties(2);
+ beans::PropertyValue *pValues = aProperties.getArray();
+
+ pValues[ 0 ].Name = PROP_EVENT_TYPE;
+ pValues[ 0 ].Value <<= OUString(SVX_MACRO_LANGUAGE_JAVASCRIPT);
+
+ pValues[ 1 ].Name = PROP_MACRO_NAME;
+ pValues[ 1 ].Value <<= pMacro->GetMacName();
+
+ aEventData <<= aProperties;
+ }
+ else
+ {
+ SAL_WARN( "sfx.config", "CreateEventData_Impl(): ScriptType not supported!");
+ }
+ }
+ else
+ {
+ uno::Sequence < beans::PropertyValue > aProperties;
+ aEventData <<= aProperties;
+ }
+
+ return aEventData;
+}
+
+
+static void PropagateEvent_Impl( SfxObjectShell const *pDoc, const OUString& aEventName, const SvxMacro* pMacro )
+{
+ uno::Reference < document::XEventsSupplier > xSupplier;
+ if ( pDoc )
+ {
+ xSupplier.set( pDoc->GetModel(), uno::UNO_QUERY );
+ }
+ else
+ {
+ xSupplier = frame::theGlobalEventBroadcaster::get(::comphelper::getProcessComponentContext());
+ }
+
+ if ( !xSupplier.is() )
+ return;
+
+ uno::Reference < container::XNameReplace > xEvents = xSupplier->getEvents();
+ if ( !aEventName.isEmpty() )
+ {
+ uno::Any aEventData = CreateEventData_Impl( pMacro );
+
+ try
+ {
+ xEvents->replaceByName( aEventName, aEventData );
+ }
+ catch( const css::lang::IllegalArgumentException& )
+ {
+ TOOLS_WARN_EXCEPTION( "sfx.config", "PropagateEvents_Impl: caught IllegalArgumentException" );
+ }
+ catch( const css::container::NoSuchElementException& )
+ {
+ TOOLS_WARN_EXCEPTION( "sfx.config", "PropagateEvents_Impl: caught NoSuchElementException" );
+ }
+ }
+ else {
+ SAL_INFO( "sfx.config", "PropagateEvents_Impl: Got unknown event" );
+ }
+}
+
+
+void SfxEventConfiguration::ConfigureEvent( const OUString& aName, const SvxMacro& rMacro, SfxObjectShell const *pDoc )
+{
+ std::optional<SvxMacro> pMacro;
+ if ( rMacro.HasMacro() )
+ pMacro.emplace( rMacro.GetMacName(), rMacro.GetLibName(), rMacro.GetScriptType() );
+ PropagateEvent_Impl( pDoc ? pDoc : nullptr, aName, pMacro ? &*pMacro : nullptr );
+}
+
+
+std::unique_ptr<SvxMacro> SfxEventConfiguration::ConvertToMacro( const css::uno::Any& rElement, SfxObjectShell* pDoc )
+{
+ return SfxEvents_Impl::ConvertToMacro( rElement, pDoc );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/control/bindings.cxx b/sfx2/source/control/bindings.cxx
new file mode 100644
index 000000000..4ea062315
--- /dev/null
+++ b/sfx2/source/control/bindings.cxx
@@ -0,0 +1,1783 @@
+/* -*- 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 <iomanip>
+
+#include <comphelper/servicehelper.hxx>
+#include <osl/diagnose.h>
+#include <sal/log.hxx>
+#include <svl/itempool.hxx>
+#include <svl/itemiter.hxx>
+#include <svl/eitem.hxx>
+#include <svl/intitem.hxx>
+#include <svl/stritem.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/timer.hxx>
+#include <com/sun/star/frame/XDispatch.hpp>
+#include <com/sun/star/frame/XDispatchProvider.hpp>
+#include <com/sun/star/frame/DispatchResultState.hpp>
+#include <itemdel.hxx>
+
+//Includes below due to nInReschedule
+#include <sfx2/bindings.hxx>
+#include <sfx2/msg.hxx>
+#include <statcach.hxx>
+#include <sfx2/ctrlitem.hxx>
+#include <sfx2/app.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/module.hxx>
+#include <sfx2/request.hxx>
+#include <workwin.hxx>
+#include <unoctitm.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/msgpool.hxx>
+
+#include <cstddef>
+#include <memory>
+#include <unordered_map>
+#include <utility>
+#include <vector>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::util;
+
+#define TIMEOUT_FIRST 300
+#define TIMEOUT_UPDATING 20
+
+struct SfxFoundCache_Impl
+{
+ sal_uInt16 nWhichId; // If available: Which-Id, else: nSlotId
+ const SfxSlot* pSlot; // Pointer to <Master-Slot>
+ SfxStateCache& rCache; // Pointer to StatusCache
+
+ SfxFoundCache_Impl(sal_uInt16 nW, const SfxSlot *pS, SfxStateCache& rC)
+ : nWhichId(nW)
+ , pSlot(pS)
+ , rCache(rC)
+ {}
+};
+
+class SfxFoundCacheArr_Impl
+{
+ std::vector<SfxFoundCache_Impl> maData;
+
+public:
+
+ SfxFoundCache_Impl& operator[] ( size_t i )
+ {
+ return maData[i];
+ }
+
+ size_t size() const
+ {
+ return maData.size();
+ }
+
+ void push_back( SfxFoundCache_Impl p )
+ {
+ maData.push_back(p);
+ }
+};
+
+class SfxBindings_Impl
+{
+public:
+ css::uno::Reference< css::frame::XDispatchRecorder > xRecorder;
+ css::uno::Reference< css::frame::XDispatchProvider > xProv;
+ std::unique_ptr<SfxWorkWindow> mxWorkWin;
+ SfxBindings* pSubBindings;
+ std::vector<std::unique_ptr<SfxStateCache>> pCaches; // One cache for each binding
+ std::size_t nCachedFunc1; // index for the last one called
+ std::size_t nCachedFunc2; // index for the second last called
+ std::size_t nMsgPos; // Message-Position relative the one to be updated
+ bool bContextChanged;
+ bool bMsgDirty; // Has a MessageServer been invalidated?
+ bool bAllMsgDirty; // Has a MessageServer been invalidated?
+ bool bAllDirty; // After InvalidateAll
+ bool bCtrlReleased; // while EnterRegistrations
+ AutoTimer aAutoTimer { "sfx::SfxBindings aAutoTimer" }; // for volatile Slots
+ bool bInUpdate; // for Assertions
+ bool bInNextJob; // for Assertions
+ bool bFirstRound; // First round in Update
+ sal_uInt16 nOwnRegLevel; // Counts the real Locks, except those of the Super Bindings
+ std::unordered_map< sal_uInt16, bool >
+ m_aInvalidateSlots; // store slots which are invalidated while in update
+};
+
+SfxBindings::SfxBindings()
+: pImpl(new SfxBindings_Impl),
+ pDispatcher(nullptr),
+ nRegLevel(1) // first becomes 0, when the Dispatcher is set
+
+{
+ pImpl->nMsgPos = 0;
+ pImpl->bAllMsgDirty = true;
+ pImpl->bContextChanged = false;
+ pImpl->bMsgDirty = true;
+ pImpl->bAllDirty = true;
+ pImpl->nCachedFunc1 = 0;
+ pImpl->nCachedFunc2 = 0;
+ pImpl->bCtrlReleased = false;
+ pImpl->bFirstRound = false;
+ pImpl->bInNextJob = false;
+ pImpl->bInUpdate = false;
+ pImpl->pSubBindings = nullptr;
+ pImpl->nOwnRegLevel = nRegLevel;
+
+ // all caches are valid (no pending invalidate-job)
+ // create the list of caches
+ pImpl->aAutoTimer.SetInvokeHandler( LINK(this, SfxBindings, NextJob) );
+}
+
+
+SfxBindings::~SfxBindings()
+
+/* [Description]
+
+ Destructor of the SfxBindings class. The one, for each <SfxApplication>
+ existing Instance is automatically destroyed by the <SfxApplication>
+ after the execution of <SfxApplication::Exit()>.
+
+ The still existing <SfxControllerItem> instances, which are registered
+ by the SfxBindings instance, are automatically destroyed in the Destructor.
+ These are usually the Floating-Toolboxen, Value-Sets
+ etc. Arrays of SfxControllerItems may at this time no longer exist.
+*/
+
+{
+ // The SubBindings should not be locked!
+ pImpl->pSubBindings = nullptr;
+
+ ENTERREGISTRATIONS();
+
+ pImpl->aAutoTimer.Stop();
+ DeleteControllers_Impl();
+
+ // Delete Caches
+ pImpl->pCaches.clear();
+
+ pImpl->mxWorkWin.reset();
+}
+
+
+void SfxBindings::DeleteControllers_Impl()
+{
+ // in the first round delete Controllers
+ std::size_t nCount = pImpl->pCaches.size();
+ std::size_t nCache;
+ for ( nCache = 0; nCache < nCount; ++nCache )
+ {
+ // Remember were you are
+ SfxStateCache *pCache = pImpl->pCaches[nCache].get();
+ sal_uInt16 nSlotId = pCache->GetId();
+
+ // Re-align, because the cache may have been reduced
+ std::size_t nNewCount = pImpl->pCaches.size();
+ if ( nNewCount < nCount )
+ {
+ nCache = GetSlotPos(nSlotId);
+ if ( nCache >= nNewCount ||
+ nSlotId != pImpl->pCaches[nCache]->GetId() )
+ --nCache;
+ nCount = nNewCount;
+ }
+ }
+
+ // Delete all Caches
+ for ( nCache = pImpl->pCaches.size(); nCache > 0; --nCache )
+ {
+ // Get Cache via css::sdbcx::Index
+ SfxStateCache *pCache = pImpl->pCaches[ nCache-1 ].get();
+
+ // unbind all controllers in the cache
+ SfxControllerItem *pNext;
+ for ( SfxControllerItem *pCtrl = pCache->GetItemLink();
+ pCtrl; pCtrl = pNext )
+ {
+ pNext = pCtrl->GetItemLink();
+ pCtrl->UnBind();
+ }
+
+ if ( pCache->GetInternalController() )
+ pCache->GetInternalController()->UnBind();
+
+ // Delete Cache
+ pImpl->pCaches.erase(pImpl->pCaches.begin() + nCache - 1);
+ }
+}
+
+
+void SfxBindings::HidePopups( bool bHide )
+{
+ // Hide SfxChildWindows
+ DBG_ASSERT( pDispatcher, "HidePopups not allowed without dispatcher" );
+ if ( pImpl->mxWorkWin )
+ pImpl->mxWorkWin->HidePopups_Impl( bHide );
+}
+
+void SfxBindings::Update_Impl(SfxStateCache& rCache /*The up to date SfxStatusCache*/)
+{
+ if (rCache.GetDispatch().is() && rCache.GetItemLink())
+ {
+ rCache.SetCachedState(true);
+ if (!rCache.GetInternalController())
+ return;
+ }
+
+ if ( !pDispatcher )
+ return;
+
+ // gather together all with the same status method which are dirty
+ SfxDispatcher &rDispat = *pDispatcher;
+ const SfxSlot *pRealSlot = nullptr;
+ const SfxSlotServer* pMsgServer = nullptr;
+ SfxFoundCacheArr_Impl aFound;
+ std::optional<SfxItemSet> pSet = CreateSet_Impl(rCache, pRealSlot, &pMsgServer, aFound);
+ bool bUpdated = false;
+ if ( pSet )
+ {
+ // Query Status
+ if ( rDispat.FillState_( *pMsgServer, *pSet, pRealSlot ) )
+ {
+ // Post Status
+ for ( size_t nPos = 0; nPos < aFound.size(); ++nPos )
+ {
+ const SfxFoundCache_Impl& rFound = aFound[nPos];
+ sal_uInt16 nWhich = rFound.nWhichId;
+ const SfxPoolItem *pItem = nullptr;
+ SfxItemState eState = pSet->GetItemState(nWhich, true, &pItem);
+ if ( eState == SfxItemState::DEFAULT && SfxItemPool::IsWhich(nWhich) )
+ pItem = &pSet->Get(nWhich);
+ UpdateControllers_Impl( rFound, pItem, eState );
+ }
+ bUpdated = true;
+ }
+
+ pSet.reset();
+ }
+
+ if (!bUpdated)
+ {
+ SfxFoundCache_Impl aFoundCache(0, pRealSlot, rCache);
+ UpdateControllers_Impl( aFoundCache, nullptr, SfxItemState::DISABLED);
+ }
+}
+
+void SfxBindings::InvalidateSlotsInMap_Impl()
+{
+ for (auto const& slot : pImpl->m_aInvalidateSlots)
+ Invalidate( slot.first );
+
+ pImpl->m_aInvalidateSlots.clear();
+}
+
+
+void SfxBindings::AddSlotToInvalidateSlotsMap_Impl( sal_uInt16 nId )
+{
+ pImpl->m_aInvalidateSlots[nId] = true;
+}
+
+
+void SfxBindings::Update
+(
+ sal_uInt16 nId // the bound and up-to-date Slot-Id
+)
+{
+ if ( pDispatcher )
+ pDispatcher->Flush();
+
+ if ( pImpl->pSubBindings )
+ pImpl->pSubBindings->Update( nId );
+
+ SfxStateCache* pCache = GetStateCache( nId );
+ if ( !pCache )
+ return;
+
+ pImpl->bInUpdate = true;
+ if ( pImpl->bMsgDirty )
+ {
+ UpdateSlotServer_Impl();
+ pCache = GetStateCache( nId );
+ }
+
+ if (pCache)
+ {
+ bool bInternalUpdate = true;
+ if( pCache->GetDispatch().is() && pCache->GetItemLink() )
+ {
+ pCache->SetCachedState(true);
+ bInternalUpdate = ( pCache->GetInternalController() != nullptr );
+ }
+
+ if ( bInternalUpdate )
+ {
+ // Query Status
+ const SfxSlotServer* pMsgServer = pDispatcher ? pCache->GetSlotServer(*pDispatcher, pImpl->xProv) : nullptr;
+ if ( !pCache->IsControllerDirty() )
+ {
+ pImpl->bInUpdate = false;
+ InvalidateSlotsInMap_Impl();
+ return;
+ }
+ if (!pMsgServer)
+ {
+ pCache->SetState(SfxItemState::DISABLED, nullptr);
+ pImpl->bInUpdate = false;
+ InvalidateSlotsInMap_Impl();
+ return;
+ }
+
+ Update_Impl(*pCache);
+ }
+
+ pImpl->bAllDirty = false;
+ }
+
+ pImpl->bInUpdate = false;
+ InvalidateSlotsInMap_Impl();
+}
+
+
+void SfxBindings::Update()
+{
+ if ( pImpl->pSubBindings )
+ pImpl->pSubBindings->Update();
+
+ if ( !pDispatcher )
+ return;
+
+ if ( nRegLevel )
+ return;
+
+ pImpl->bInUpdate = true;
+ pDispatcher->Flush();
+ pDispatcher->Update_Impl();
+ while ( !NextJob_Impl(nullptr) )
+ ; // loop
+ pImpl->bInUpdate = false;
+ InvalidateSlotsInMap_Impl();
+}
+
+
+void SfxBindings::SetState
+(
+ const SfxItemSet& rSet // status values to be set
+)
+{
+ // when locked then only invalidate
+ if ( nRegLevel )
+ {
+ SfxItemIter aIter(rSet);
+ for ( const SfxPoolItem *pItem = aIter.GetCurItem();
+ pItem;
+ pItem = aIter.NextItem() )
+ Invalidate( pItem->Which() );
+ }
+ else
+ {
+ // Status may be accepted only if all slot-pointers are set
+ if ( pImpl->bMsgDirty )
+ UpdateSlotServer_Impl();
+
+ // Iterate over the itemset, update if the slot bound
+ //! Bug: Use WhichIter and possibly send VoidItems up
+ SfxItemIter aIter(rSet);
+ for ( const SfxPoolItem *pItem = aIter.GetCurItem();
+ pItem;
+ pItem = aIter.NextItem() )
+ {
+ SfxStateCache* pCache =
+ GetStateCache( rSet.GetPool()->GetSlotId(pItem->Which()) );
+ if ( pCache )
+ {
+ // Update status
+ if ( !pCache->IsControllerDirty() )
+ pCache->Invalidate(false);
+ pCache->SetState( SfxItemState::DEFAULT, pItem );
+
+ //! Not implemented: Updates from EnumSlots via master slots
+ }
+ }
+ }
+}
+
+
+void SfxBindings::SetState
+(
+ const SfxPoolItem& rItem // Status value to be set
+)
+{
+ if ( nRegLevel )
+ {
+ Invalidate( rItem.Which() );
+ }
+ else
+ {
+ // Status may be accepted only if all slot-pointers are set
+ if ( pImpl->bMsgDirty )
+ UpdateSlotServer_Impl();
+
+ //update if the slot bound
+ DBG_ASSERT( SfxItemPool::IsSlot( rItem.Which() ),
+ "cannot set items with which-id" );
+ SfxStateCache* pCache = GetStateCache( rItem.Which() );
+ if ( pCache )
+ {
+ // Update Status
+ if ( !pCache->IsControllerDirty() )
+ pCache->Invalidate(false);
+ pCache->SetState( SfxItemState::DEFAULT, &rItem );
+
+ //! Not implemented: Updates from EnumSlots via master slots
+ }
+ }
+}
+
+
+SfxStateCache* SfxBindings::GetAnyStateCache_Impl( sal_uInt16 nId )
+{
+ SfxStateCache* pCache = GetStateCache( nId );
+ if ( !pCache && pImpl->pSubBindings )
+ return pImpl->pSubBindings->GetAnyStateCache_Impl( nId );
+ return pCache;
+}
+
+SfxStateCache* SfxBindings::GetStateCache
+(
+ sal_uInt16 nId /* Slot-Id, which SfxStatusCache is to be found */
+)
+{
+ return GetStateCache(nId, nullptr);
+}
+
+SfxStateCache* SfxBindings::GetStateCache
+(
+ sal_uInt16 nId, /* Slot-Id, which SfxStatusCache is to be found */
+ std::size_t * pPos /* NULL for instance the position from which the
+ bindings are to be searched binary. Returns the
+ position back for where the nId was found,
+ or where it was inserted. */
+)
+{
+ // is the specified function bound?
+ const std::size_t nStart = ( pPos ? *pPos : 0 );
+ const std::size_t nPos = GetSlotPos( nId, nStart );
+
+ if ( nPos < pImpl->pCaches.size() &&
+ pImpl->pCaches[nPos]->GetId() == nId )
+ {
+ if ( pPos )
+ *pPos = nPos;
+ return pImpl->pCaches[nPos].get();
+ }
+ return nullptr;
+}
+
+
+void SfxBindings::InvalidateAll
+(
+ bool bWithMsg /* true Mark Slot Server as invalid
+ false Slot Server remains valid */
+)
+{
+ DBG_ASSERT( !pImpl->bInUpdate, "SfxBindings::Invalidate while in update" );
+
+ if ( pImpl->pSubBindings )
+ pImpl->pSubBindings->InvalidateAll( bWithMsg );
+
+ // everything is already set dirty or downing => nothing to do
+ if ( !pDispatcher ||
+ ( pImpl->bAllDirty && ( !bWithMsg || pImpl->bAllMsgDirty ) ) ||
+ SfxGetpApp()->IsDowning() )
+ {
+ return;
+ }
+
+ pImpl->bAllMsgDirty = pImpl->bAllMsgDirty || bWithMsg;
+ pImpl->bMsgDirty = pImpl->bMsgDirty || pImpl->bAllMsgDirty || bWithMsg;
+ pImpl->bAllDirty = true;
+
+ for (std::unique_ptr<SfxStateCache>& pCache : pImpl->pCaches)
+ pCache->Invalidate(bWithMsg);
+
+ pImpl->nMsgPos = 0;
+ if ( !nRegLevel )
+ {
+ pImpl->aAutoTimer.Stop();
+ pImpl->aAutoTimer.SetTimeout(TIMEOUT_FIRST);
+ pImpl->aAutoTimer.Start();
+ }
+}
+
+
+void SfxBindings::Invalidate
+(
+ const sal_uInt16* pIds /* numerically sorted NULL-terminated array of
+ slot IDs (individual, not as a couple!) */
+)
+{
+ if ( pImpl->bInUpdate )
+ {
+ sal_Int32 i = 0;
+ while ( pIds[i] != 0 )
+ AddSlotToInvalidateSlotsMap_Impl( pIds[i++] );
+
+ if ( pImpl->pSubBindings )
+ pImpl->pSubBindings->Invalidate( pIds );
+ return;
+ }
+
+ if ( pImpl->pSubBindings )
+ pImpl->pSubBindings->Invalidate( pIds );
+
+ // everything is already set dirty or downing => nothing to do
+ if ( !pDispatcher || pImpl->bAllDirty || SfxGetpApp()->IsDowning() )
+ return;
+
+ // Search binary in always smaller areas
+ for ( std::size_t n = GetSlotPos(*pIds);
+ *pIds && n < pImpl->pCaches.size();
+ n = GetSlotPos(*pIds, n) )
+ {
+ // If SID is ever bound, then invalidate the cache
+ SfxStateCache *pCache = pImpl->pCaches[n].get();
+ if ( pCache->GetId() == *pIds )
+ pCache->Invalidate(false);
+
+ // Next SID
+ if ( !*++pIds )
+ break;
+ assert( *pIds > *(pIds-1) );
+ }
+
+ // if not enticed to start update timer
+ pImpl->nMsgPos = 0;
+ if ( !nRegLevel )
+ {
+ pImpl->aAutoTimer.Stop();
+ pImpl->aAutoTimer.SetTimeout(TIMEOUT_FIRST);
+ pImpl->aAutoTimer.Start();
+ }
+}
+
+
+void SfxBindings::InvalidateShell
+(
+ const SfxShell& rSh, /* <SfxShell> whose Slot-Ids should be
+ invalidated */
+ bool bDeep /* true
+ also the SfxShell's inherited slot IDs are invalidated
+
+ false
+ the inherited and not overridden Slot-Ids are
+ invalidated */
+ // for now always bDeep
+)
+{
+ DBG_ASSERT( !pImpl->bInUpdate, "SfxBindings::Invalidate while in update" );
+
+ if ( pImpl->pSubBindings )
+ pImpl->pSubBindings->InvalidateShell( rSh, bDeep );
+
+ if ( !pDispatcher || pImpl->bAllDirty || SfxGetpApp()->IsDowning() )
+ return;
+
+ // flush now already, it is done in GetShellLevel (rsh) anyway,
+ // important so that is set correctly: pImpl-> ball(Msg)Dirty
+ pDispatcher->Flush();
+
+ if ((pImpl->bAllDirty && pImpl->bAllMsgDirty) || SfxGetpApp()->IsDowning())
+ {
+ // if the next one is anyway, then all the servers are collected
+ return;
+ }
+
+ // Find Level
+ sal_uInt16 nLevel = pDispatcher->GetShellLevel(rSh);
+ if ( nLevel == USHRT_MAX )
+ return;
+
+ for (std::unique_ptr<SfxStateCache>& pCache : pImpl->pCaches)
+ {
+ const SfxSlotServer *pMsgServer =
+ pCache->GetSlotServer(*pDispatcher, pImpl->xProv);
+ if ( pMsgServer && pMsgServer->GetShellLevel() == nLevel )
+ pCache->Invalidate(false);
+ }
+ pImpl->nMsgPos = 0;
+ if ( !nRegLevel )
+ {
+ pImpl->aAutoTimer.Stop();
+ pImpl->aAutoTimer.SetTimeout(TIMEOUT_FIRST);
+ pImpl->aAutoTimer.Start();
+ pImpl->bFirstRound = true;
+ }
+}
+
+
+void SfxBindings::Invalidate
+(
+ sal_uInt16 nId // Status value to be set
+)
+{
+ if ( pImpl->bInUpdate )
+ {
+ AddSlotToInvalidateSlotsMap_Impl( nId );
+ if ( pImpl->pSubBindings )
+ pImpl->pSubBindings->Invalidate( nId );
+ return;
+ }
+
+ if ( pImpl->pSubBindings )
+ pImpl->pSubBindings->Invalidate( nId );
+
+ if ( !pDispatcher || pImpl->bAllDirty || SfxGetpApp()->IsDowning() )
+ return;
+
+ SfxStateCache* pCache = GetStateCache(nId);
+ if ( pCache )
+ {
+ pCache->Invalidate(false);
+ pImpl->nMsgPos = std::min(GetSlotPos(nId), pImpl->nMsgPos);
+ if ( !nRegLevel )
+ {
+ pImpl->aAutoTimer.Stop();
+ pImpl->aAutoTimer.SetTimeout(TIMEOUT_FIRST);
+ pImpl->aAutoTimer.Start();
+ }
+ }
+}
+
+
+void SfxBindings::Invalidate
+(
+ sal_uInt16 nId, // Status value to be set
+ bool bWithItem, // Clear StateCache?
+ bool bWithMsg // Get new SlotServer?
+)
+{
+ DBG_ASSERT( !pImpl->bInUpdate, "SfxBindings::Invalidate while in update" );
+
+ if ( pImpl->pSubBindings )
+ pImpl->pSubBindings->Invalidate( nId, bWithItem, bWithMsg );
+
+ if ( SfxGetpApp()->IsDowning() )
+ return;
+
+ SfxStateCache* pCache = GetStateCache(nId);
+ if ( !pCache )
+ return;
+
+ if ( bWithItem )
+ pCache->ClearCache();
+ pCache->Invalidate(bWithMsg);
+
+ if ( !pDispatcher || pImpl->bAllDirty )
+ return;
+
+ pImpl->nMsgPos = std::min(GetSlotPos(nId), pImpl->nMsgPos);
+ if ( !nRegLevel )
+ {
+ pImpl->aAutoTimer.Stop();
+ pImpl->aAutoTimer.SetTimeout(TIMEOUT_FIRST);
+ pImpl->aAutoTimer.Start();
+ }
+}
+
+
+std::size_t SfxBindings::GetSlotPos( sal_uInt16 nId, std::size_t nStartSearchAt )
+{
+ // answer immediately if a function-seek comes repeated
+ if ( pImpl->nCachedFunc1 < pImpl->pCaches.size() &&
+ pImpl->pCaches[pImpl->nCachedFunc1]->GetId() == nId )
+ {
+ return pImpl->nCachedFunc1;
+ }
+ if ( pImpl->nCachedFunc2 < pImpl->pCaches.size() &&
+ pImpl->pCaches[pImpl->nCachedFunc2]->GetId() == nId )
+ {
+ // swap the caches
+ std::swap(pImpl->nCachedFunc1, pImpl->nCachedFunc2);
+ return pImpl->nCachedFunc1;
+ }
+
+ // binary search, if not found, seek to target-position
+ if ( pImpl->pCaches.size() <= nStartSearchAt )
+ {
+ return 0;
+ }
+ if ( pImpl->pCaches.size() == (nStartSearchAt+1) )
+ {
+ return pImpl->pCaches[nStartSearchAt]->GetId() >= nId ? 0 : 1;
+ }
+ std::size_t nLow = nStartSearchAt;
+ std::size_t nMid = 0;
+ std::size_t nHigh = 0;
+ bool bFound = false;
+ nHigh = pImpl->pCaches.size() - 1;
+ while ( !bFound && nLow <= nHigh )
+ {
+ nMid = (nLow + nHigh) >> 1;
+ DBG_ASSERT( nMid < pImpl->pCaches.size(), "bsearch is buggy" );
+ int nDiff = static_cast<int>(nId) - static_cast<int>( (pImpl->pCaches[nMid])->GetId() );
+ if ( nDiff < 0)
+ { if ( nMid == 0 )
+ break;
+ nHigh = nMid - 1;
+ }
+ else if ( nDiff > 0 )
+ { nLow = nMid + 1;
+ if ( nLow == 0 )
+ break;
+ }
+ else
+ bFound = true;
+ }
+ std::size_t nPos = bFound ? nMid : nLow;
+ DBG_ASSERT( nPos <= pImpl->pCaches.size(), "" );
+ DBG_ASSERT( nPos == pImpl->pCaches.size() ||
+ nId <= pImpl->pCaches[nPos]->GetId(), "" );
+ DBG_ASSERT( nPos == nStartSearchAt ||
+ nId > pImpl->pCaches[nPos-1]->GetId(), "" );
+ DBG_ASSERT( ( (nPos+1) >= pImpl->pCaches.size() ) ||
+ nId < pImpl->pCaches[nPos+1]->GetId(), "" );
+ pImpl->nCachedFunc2 = pImpl->nCachedFunc1;
+ pImpl->nCachedFunc1 = nPos;
+ return nPos;
+}
+
+void SfxBindings::RegisterInternal_Impl( SfxControllerItem& rItem )
+{
+ Register_Impl( rItem, true );
+
+}
+
+void SfxBindings::Register( SfxControllerItem& rItem )
+{
+ Register_Impl( rItem, false );
+}
+
+void SfxBindings::Register_Impl( SfxControllerItem& rItem, bool bInternal )
+{
+// DBG_ASSERT( nRegLevel > 0, "registration without EnterRegistrations" );
+ DBG_ASSERT( !pImpl->bInNextJob, "SfxBindings::Register while status-updating" );
+
+ // insert new cache if it does not already exist
+ sal_uInt16 nId = rItem.GetId();
+ std::size_t nPos = GetSlotPos(nId);
+ if ( nPos >= pImpl->pCaches.size() ||
+ pImpl->pCaches[nPos]->GetId() != nId )
+ {
+ pImpl->pCaches.insert( pImpl->pCaches.begin() + nPos, std::make_unique<SfxStateCache>(nId) );
+ DBG_ASSERT( nPos == 0 ||
+ pImpl->pCaches[nPos]->GetId() >
+ pImpl->pCaches[nPos-1]->GetId(), "" );
+ DBG_ASSERT( (nPos == pImpl->pCaches.size()-1) ||
+ pImpl->pCaches[nPos]->GetId() <
+ pImpl->pCaches[nPos+1]->GetId(), "" );
+ pImpl->bMsgDirty = true;
+ }
+
+ // enqueue the new binding
+ if ( bInternal )
+ {
+ pImpl->pCaches[nPos]->SetInternalController( &rItem );
+ }
+ else
+ {
+ SfxControllerItem *pOldItem = pImpl->pCaches[nPos]->ChangeItemLink(&rItem);
+ rItem.ChangeItemLink(pOldItem);
+ }
+}
+
+
+void SfxBindings::Release( SfxControllerItem& rItem )
+{
+ DBG_ASSERT( !pImpl->bInNextJob, "SfxBindings::Release while status-updating" );
+ ENTERREGISTRATIONS();
+
+ // find the bound function
+ sal_uInt16 nId = rItem.GetId();
+ std::size_t nPos = GetSlotPos(nId);
+ SfxStateCache* pCache = (nPos < pImpl->pCaches.size()) ? pImpl->pCaches[nPos].get() : nullptr;
+ if ( pCache && pCache->GetId() == nId )
+ {
+ if ( pCache->GetInternalController() == &rItem )
+ {
+ pCache->ReleaseInternalController();
+ }
+ else
+ {
+ // is this the first binding in the list?
+ SfxControllerItem* pItem = pCache->GetItemLink();
+ if ( pItem == &rItem )
+ pCache->ChangeItemLink( rItem.GetItemLink() );
+ else
+ {
+ // search the binding in the list
+ while ( pItem && pItem->GetItemLink() != &rItem )
+ pItem = pItem->GetItemLink();
+
+ // unlink it if it was found
+ if ( pItem )
+ pItem->ChangeItemLink( rItem.GetItemLink() );
+ }
+ }
+
+ // was this the last controller?
+ if ( pCache->GetItemLink() == nullptr && !pCache->GetInternalController() )
+ {
+ pImpl->bCtrlReleased = true;
+ }
+ }
+
+ LEAVEREGISTRATIONS();
+}
+
+
+const SfxPoolItem* SfxBindings::ExecuteSynchron( sal_uInt16 nId, const SfxPoolItem** ppItems )
+{
+ if( !nId || !pDispatcher )
+ return nullptr;
+
+ return Execute_Impl( nId, ppItems, 0, SfxCallMode::SYNCHRON, nullptr );
+}
+
+bool SfxBindings::Execute( sal_uInt16 nId, const SfxPoolItem** ppItems, SfxCallMode nCallMode )
+{
+ if( !nId || !pDispatcher )
+ return false;
+
+ const SfxPoolItem* pRet = Execute_Impl( nId, ppItems, 0, nCallMode, nullptr );
+ return ( pRet != nullptr );
+}
+
+const SfxPoolItem* SfxBindings::Execute_Impl( sal_uInt16 nId, const SfxPoolItem** ppItems, sal_uInt16 nModi, SfxCallMode nCallMode,
+ const SfxPoolItem **ppInternalArgs, bool bGlobalOnly )
+{
+ SfxStateCache *pCache = GetStateCache( nId );
+ if ( !pCache )
+ {
+ SfxBindings *pBind = pImpl->pSubBindings;
+ while ( pBind )
+ {
+ if ( pBind->GetStateCache( nId ) )
+ return pBind->Execute_Impl( nId, ppItems, nModi, nCallMode, ppInternalArgs, bGlobalOnly );
+ pBind = pBind->pImpl->pSubBindings;
+ }
+ }
+
+ SfxDispatcher &rDispatcher = *pDispatcher;
+ rDispatcher.Flush();
+
+ // get SlotServer (Slot+ShellLevel) and Shell from cache
+ std::unique_ptr<SfxStateCache> xCache;
+ if ( !pCache )
+ {
+ // Execution of non cached slots (Accelerators don't use Controllers)
+ // slot is uncached, use SlotCache to handle external dispatch providers
+ xCache.reset(new SfxStateCache(nId));
+ pCache = xCache.get();
+ }
+
+ pCache->GetSlotServer( rDispatcher, pImpl->xProv ); // make pCache->GetDispatch() up to date
+ if ( pCache->GetDispatch().is() )
+ {
+ DBG_ASSERT( !ppInternalArgs, "Internal args get lost when dispatched!" );
+
+ SfxItemPool &rPool = GetDispatcher()->GetFrame()->GetObjectShell()->GetPool();
+ SfxRequest aReq( nId, nCallMode, rPool );
+ aReq.SetModifier( nModi );
+ if( ppItems )
+ while( *ppItems )
+ aReq.AppendItem( **ppItems++ );
+
+ // cache binds to an external dispatch provider
+ sal_Int16 eRet = pCache->Dispatch( aReq.GetArgs(), nCallMode == SfxCallMode::SYNCHRON );
+ std::unique_ptr<SfxPoolItem> pPool;
+ if ( eRet == css::frame::DispatchResultState::DONTKNOW )
+ pPool.reset( new SfxVoidItem( nId ) );
+ else
+ pPool.reset( new SfxBoolItem( nId, eRet == css::frame::DispatchResultState::SUCCESS) );
+
+ auto pTemp = pPool.get();
+ DeleteItemOnIdle( std::move(pPool) );
+ return pTemp;
+ }
+
+ // slot is handled internally by SfxDispatcher
+ if ( pImpl->bMsgDirty )
+ UpdateSlotServer_Impl();
+
+ SfxShell *pShell=nullptr;
+ const SfxSlot *pSlot=nullptr;
+
+ const SfxSlotServer* pServer = pCache->GetSlotServer( rDispatcher, pImpl->xProv );
+ if ( !pServer )
+ {
+ return nullptr;
+ }
+ else
+ {
+ pShell = rDispatcher.GetShell( pServer->GetShellLevel() );
+ pSlot = pServer->GetSlot();
+ }
+
+ if ( bGlobalOnly )
+ if ( dynamic_cast< const SfxModule *>( pShell ) == nullptr && dynamic_cast< const SfxApplication *>( pShell ) == nullptr && dynamic_cast< const SfxViewFrame *>( pShell ) == nullptr )
+ return nullptr;
+
+ SfxItemPool &rPool = pShell->GetPool();
+ SfxRequest aReq( nId, nCallMode, rPool );
+ aReq.SetModifier( nModi );
+ if( ppItems )
+ while( *ppItems )
+ aReq.AppendItem( **ppItems++ );
+ if ( ppInternalArgs )
+ {
+ SfxAllItemSet aSet( rPool );
+ for ( const SfxPoolItem **pArg = ppInternalArgs; *pArg; ++pArg )
+ aSet.Put( **pArg );
+ aReq.SetInternalArgs_Impl( aSet );
+ }
+
+ Execute_Impl( aReq, pSlot, pShell );
+
+ const SfxPoolItem* pRet = aReq.GetReturnValue();
+ if ( !pRet )
+ {
+ std::unique_ptr<SfxPoolItem> pVoid(new SfxVoidItem( nId ));
+ pRet = pVoid.get();
+ DeleteItemOnIdle( std::move(pVoid) );
+ }
+
+ return pRet;
+}
+
+void SfxBindings::Execute_Impl( SfxRequest& aReq, const SfxSlot* pSlot, SfxShell* pShell )
+{
+ SfxItemPool &rPool = pShell->GetPool();
+
+ if ( SfxSlotKind::Attribute == pSlot->GetKind() )
+ {
+ // Which value has to be mapped for Attribute slots
+ const sal_uInt16 nSlotId = pSlot->GetSlotId();
+ aReq.SetSlot( nSlotId );
+ if ( pSlot->IsMode(SfxSlotMode::TOGGLE) )
+ {
+ // The value is attached to a toggleable attribute (Bools)
+ sal_uInt16 nWhich = pSlot->GetWhich(rPool);
+ SfxItemSet aSet(rPool, nWhich, nWhich);
+ SfxStateFunc pFunc = pSlot->GetStateFnc();
+ (*pFunc)(pShell, aSet);
+ const SfxPoolItem *pOldItem;
+ SfxItemState eState = aSet.GetItemState(nWhich, true, &pOldItem);
+ if ( eState == SfxItemState::DISABLED )
+ return;
+
+ if ( SfxItemState::DEFAULT == eState && SfxItemPool::IsWhich(nWhich) )
+ pOldItem = &aSet.Get(nWhich);
+
+ if ( SfxItemState::SET == eState ||
+ ( SfxItemState::DEFAULT == eState &&
+ SfxItemPool::IsWhich(nWhich) &&
+ pOldItem ) )
+ {
+ if ( auto pOldBoolItem = dynamic_cast< const SfxBoolItem *>( pOldItem ) )
+ {
+ // we can toggle Bools
+ bool bOldValue = pOldBoolItem->GetValue();
+ std::unique_ptr<SfxBoolItem> pNewItem(static_cast<SfxBoolItem*>(pOldItem->Clone()));
+ pNewItem->SetValue( !bOldValue );
+ aReq.AppendItem( *pNewItem );
+ }
+ else if ( auto pOldEnumItem = dynamic_cast< const SfxEnumItemInterface *>( pOldItem ) )
+ {
+ if (pOldEnumItem->HasBoolValue())
+ {
+ // and Enums with Bool-Interface
+ std::unique_ptr<SfxEnumItemInterface> pNewItem(
+ static_cast<SfxEnumItemInterface*>(pOldEnumItem->Clone()));
+ pNewItem->SetBoolValue(!pOldEnumItem->GetBoolValue());
+ aReq.AppendItem( *pNewItem );
+ }
+ }
+ else {
+ OSL_FAIL( "Toggle only for Enums and Bools allowed" );
+ }
+ }
+ else if ( SfxItemState::DONTCARE == eState )
+ {
+ // Create one Status-Item for each Factory
+ std::unique_ptr<SfxPoolItem> pNewItem = pSlot->GetType()->CreateItem();
+ DBG_ASSERT( pNewItem, "Toggle to slot without ItemFactory" );
+ pNewItem->SetWhich( nWhich );
+
+ if ( auto pNewBoolItem = dynamic_cast<SfxBoolItem *>( pNewItem.get() ) )
+ {
+ // we can toggle Bools
+ pNewBoolItem->SetValue( true );
+ aReq.AppendItem( *pNewItem );
+ }
+ else if ( auto pEnumItem = dynamic_cast<SfxEnumItemInterface *>( pNewItem.get() ) )
+ {
+ if (pEnumItem->HasBoolValue())
+ {
+ // and Enums with Bool-Interface
+ pEnumItem->SetBoolValue(true);
+ aReq.AppendItem( *pNewItem );
+ }
+ }
+ else {
+ OSL_FAIL( "Toggle only for Enums and Bools allowed" );
+ }
+ }
+ else {
+ OSL_FAIL( "suspicious Toggle-Slot" );
+ }
+ }
+
+ pDispatcher->Execute_( *pShell, *pSlot, aReq, aReq.GetCallMode() | SfxCallMode::RECORD );
+ }
+ else
+ pDispatcher->Execute_( *pShell, *pSlot, aReq, aReq.GetCallMode() | SfxCallMode::RECORD );
+}
+
+
+void SfxBindings::UpdateSlotServer_Impl()
+{
+ // synchronize
+ pDispatcher->Flush();
+
+ if ( pImpl->bAllMsgDirty )
+ {
+ if ( !nRegLevel )
+ {
+ pImpl->bContextChanged = false;
+ }
+ else
+ pImpl->bContextChanged = true;
+ }
+
+ for (size_t i = 0; i < pImpl->pCaches.size(); ++i)
+ {
+ //GetSlotServer can modify pImpl->pCaches
+ pImpl->pCaches[i]->GetSlotServer(*pDispatcher, pImpl->xProv);
+ }
+ pImpl->bMsgDirty = pImpl->bAllMsgDirty = false;
+
+ Broadcast( SfxHint(SfxHintId::DocChanged) );
+}
+
+
+std::optional<SfxItemSet> SfxBindings::CreateSet_Impl
+(
+ SfxStateCache& rCache, // in: Status-Cache from nId
+ const SfxSlot*& pRealSlot, // out: RealSlot to nId
+ const SfxSlotServer** pMsgServer, // out: Slot-Server to nId
+ SfxFoundCacheArr_Impl& rFound // out: List of Caches for Siblings
+)
+{
+ DBG_ASSERT( !pImpl->bMsgDirty, "CreateSet_Impl with dirty MessageServer" );
+ assert(pDispatcher);
+
+ const SfxSlotServer* pMsgSvr = rCache.GetSlotServer(*pDispatcher, pImpl->xProv);
+ if (!pMsgSvr)
+ return {};
+
+ pRealSlot = nullptr;
+ *pMsgServer = pMsgSvr;
+
+ sal_uInt16 nShellLevel = pMsgSvr->GetShellLevel();
+ SfxShell *pShell = pDispatcher->GetShell( nShellLevel );
+ if ( !pShell ) // rare GPF when browsing through update from Inet-Notify
+ return {};
+
+ SfxItemPool &rPool = pShell->GetPool();
+
+ // get the status method, which is served by the rCache
+ SfxStateFunc pFnc = nullptr;
+ pRealSlot = pMsgSvr->GetSlot();
+
+ pFnc = pRealSlot->GetStateFnc();
+
+ // the RealSlot is always on
+ SfxFoundCache_Impl aFound(pRealSlot->GetWhich(rPool), pRealSlot, rCache);
+ rFound.push_back( aFound );
+
+ // Search through the bindings for slots served by the same function. This , // will only affect slots which are present in the found interface.
+
+ // The position of the Statecaches in StateCache-Array
+ std::size_t nCachePos = pImpl->nMsgPos;
+ const SfxSlot *pSibling = pRealSlot->GetNextSlot();
+
+ // the Slots ODF and interfaces are linked in a circle
+ while ( pSibling > pRealSlot )
+ {
+ SfxStateFunc pSiblingFnc=nullptr;
+ SfxStateCache *pSiblingCache =
+ GetStateCache( pSibling->GetSlotId(), &nCachePos );
+
+ // Is the slot cached ?
+ if ( pSiblingCache )
+ {
+ const SfxSlotServer *pServ = pSiblingCache->GetSlotServer(*pDispatcher, pImpl->xProv);
+ if ( pServ && pServ->GetShellLevel() == nShellLevel )
+ pSiblingFnc = pServ->GetSlot()->GetStateFnc();
+ }
+
+ // Does the slot have to be updated at all?
+ bool bInsert = pSiblingCache && pSiblingCache->IsControllerDirty();
+
+ // It is not enough to ask for the same shell!!
+ bool bSameMethod = pSiblingCache && pFnc == pSiblingFnc;
+
+ if ( bInsert && bSameMethod )
+ {
+ SfxFoundCache_Impl aFoundCache(
+ pSibling->GetWhich(rPool),
+ pSibling, *pSiblingCache);
+
+ rFound.push_back( aFoundCache );
+ }
+
+ pSibling = pSibling->GetNextSlot();
+ }
+
+ // Create a Set from the ranges
+ WhichRangesContainer ranges;
+ size_t i = 0;
+ while ( i < rFound.size() )
+ {
+ const sal_uInt16 nWhich1 = rFound[i].nWhichId;
+ // consecutive numbers
+ for ( ; i < rFound.size()-1; ++i )
+ if ( rFound[i].nWhichId+1 != rFound[i+1].nWhichId )
+ break;
+ const sal_uInt16 nWhich2 = rFound[i++].nWhichId;
+ ranges = ranges.MergeRange(nWhich1, nWhich2);
+ }
+ SfxItemSet aSet(rPool, std::move(ranges));
+ return aSet;
+}
+
+
+void SfxBindings::UpdateControllers_Impl
+(
+ const SfxFoundCache_Impl& rFound, // Cache, Slot, Which etc.
+ const SfxPoolItem* pItem, // item to send to controller
+ SfxItemState eState // state of item
+)
+{
+ SfxStateCache& rCache = rFound.rCache;
+ const SfxSlot* pSlot = rFound.pSlot;
+ DBG_ASSERT( !pSlot || rCache.GetId() == pSlot->GetSlotId(), "SID mismatch" );
+
+ // bound until now, the Controller to update the Slot.
+ if (!rCache.IsControllerDirty())
+ return;
+
+ if ( SfxItemState::DONTCARE == eState )
+ {
+ // ambiguous
+ rCache.SetState( SfxItemState::DONTCARE, INVALID_POOL_ITEM );
+ }
+ else if ( SfxItemState::DEFAULT == eState &&
+ SfxItemPool::IsSlot(rFound.nWhichId) )
+ {
+ // no Status or Default but without Pool
+ SfxVoidItem aVoid(0);
+ rCache.SetState( SfxItemState::UNKNOWN, &aVoid );
+ }
+ else if ( SfxItemState::DISABLED == eState )
+ rCache.SetState(SfxItemState::DISABLED, nullptr);
+ else
+ rCache.SetState(SfxItemState::DEFAULT, pItem);
+}
+
+IMPL_LINK( SfxBindings, NextJob, Timer *, pTimer, void )
+{
+ NextJob_Impl(pTimer);
+}
+
+bool SfxBindings::NextJob_Impl(Timer const * pTimer)
+{
+ const unsigned MAX_INPUT_DELAY = 200;
+
+ if ( Application::GetLastInputInterval() < MAX_INPUT_DELAY && pTimer )
+ {
+ pImpl->aAutoTimer.SetTimeout(TIMEOUT_UPDATING);
+ return true;
+ }
+
+ SfxApplication *pSfxApp = SfxGetpApp();
+
+ if( pDispatcher )
+ pDispatcher->Update_Impl();
+
+ // modifying the SfxObjectInterface-stack without SfxBindings => nothing to do
+ SfxViewFrame* pFrame = pDispatcher ? pDispatcher->GetFrame() : nullptr;
+ if ( (pFrame && !pFrame->GetObjectShell()->AcceptStateUpdate()) || pSfxApp->IsDowning() || pImpl->pCaches.empty() )
+ {
+ return true;
+ }
+ if ( !pDispatcher || !pDispatcher->IsFlushed() )
+ {
+ return true;
+ }
+
+ // if possible Update all server / happens in its own time slice
+ if ( pImpl->bMsgDirty )
+ {
+ UpdateSlotServer_Impl();
+ return false;
+ }
+
+ pImpl->bAllDirty = false;
+ pImpl->aAutoTimer.SetTimeout(TIMEOUT_UPDATING);
+
+ // at least 10 loops and further if more jobs are available but no input
+ bool bPreEmptive = pTimer;
+ sal_uInt16 nLoops = 10;
+ pImpl->bInNextJob = true;
+ const std::size_t nCount = pImpl->pCaches.size();
+ while ( pImpl->nMsgPos < nCount )
+ {
+ // iterate through the bound functions
+ bool bJobDone = false;
+ while ( !bJobDone )
+ {
+ SfxStateCache* pCache = pImpl->pCaches[pImpl->nMsgPos].get();
+ DBG_ASSERT( pCache, "invalid SfxStateCache-position in job queue" );
+ bool bWasDirty = pCache->IsControllerDirty();
+ if ( bWasDirty )
+ {
+ Update_Impl(*pCache);
+ DBG_ASSERT(nCount == pImpl->pCaches.size(), "Reschedule in StateChanged => buff");
+ }
+
+ // skip to next function binding
+ ++pImpl->nMsgPos;
+
+ // keep job if it is not completed, but any input is available
+ bJobDone = pImpl->nMsgPos >= nCount;
+ if ( bJobDone && pImpl->bFirstRound )
+ {
+
+ // Update of the preferred shell has been done, now may
+ // also the others shells be updated
+ bJobDone = false;
+ pImpl->bFirstRound = false;
+ pImpl->nMsgPos = 0;
+ }
+
+ if ( bWasDirty && !bJobDone && bPreEmptive && (--nLoops == 0) )
+ {
+ pImpl->bInNextJob = false;
+ return false;
+ }
+ }
+ }
+
+ pImpl->nMsgPos = 0;
+
+ pImpl->aAutoTimer.Stop();
+
+ // Update round is finished
+ pImpl->bInNextJob = false;
+ Broadcast(SfxHint(SfxHintId::UpdateDone));
+ return true;
+}
+
+
+sal_uInt16 SfxBindings::EnterRegistrations(const char *pFile, int nLine)
+{
+ SAL_INFO(
+ "sfx.control",
+ std::setw(std::min(nRegLevel, sal_uInt16(8))) << ' ' << "this = " << this
+ << " Level = " << nRegLevel << " SfxBindings::EnterRegistrations "
+ << (pFile
+ ? SAL_STREAM("File: " << pFile << " Line: " << nLine) : ""));
+
+ // When bindings are locked, also lock sub bindings.
+ if ( pImpl->pSubBindings )
+ {
+ pImpl->pSubBindings->ENTERREGISTRATIONS();
+
+ // These EnterRegistrations are not "real" for the SubBindings
+ pImpl->pSubBindings->pImpl->nOwnRegLevel--;
+
+ // Synchronize Bindings
+ pImpl->pSubBindings->nRegLevel = nRegLevel + pImpl->pSubBindings->pImpl->nOwnRegLevel + 1;
+ }
+
+ pImpl->nOwnRegLevel++;
+
+ // check if this is the outer most level
+ if ( ++nRegLevel == 1 )
+ {
+ // stop background-processing
+ pImpl->aAutoTimer.Stop();
+
+ // flush the cache
+ pImpl->nCachedFunc1 = 0;
+ pImpl->nCachedFunc2 = 0;
+
+ // Mark if the all of the Caches have disappeared.
+ pImpl->bCtrlReleased = false;
+ }
+
+ return nRegLevel;
+}
+
+
+void SfxBindings::LeaveRegistrations( const char *pFile, int nLine )
+{
+ DBG_ASSERT( nRegLevel, "Leave without Enter" );
+
+ // Only when the SubBindings are still locked by the Superbindings,
+ // remove this lock (i.e. if there are more locks than "real" ones)
+ if ( pImpl->pSubBindings && pImpl->pSubBindings->nRegLevel > pImpl->pSubBindings->pImpl->nOwnRegLevel )
+ {
+ // Synchronize Bindings
+ pImpl->pSubBindings->nRegLevel = nRegLevel + pImpl->pSubBindings->pImpl->nOwnRegLevel;
+
+ // This LeaveRegistrations is not "real" for SubBindings
+ pImpl->pSubBindings->pImpl->nOwnRegLevel++;
+ pImpl->pSubBindings->LEAVEREGISTRATIONS();
+ }
+
+ pImpl->nOwnRegLevel--;
+
+ // check if this is the outer most level
+ if ( --nRegLevel == 0 && SfxGetpApp() && !SfxGetpApp()->IsDowning() )
+ {
+ if ( pImpl->bContextChanged )
+ {
+ pImpl->bContextChanged = false;
+ }
+
+ SfxViewFrame* pFrame = pDispatcher->GetFrame();
+
+ // If possible remove unused Caches, for example prepare PlugInInfo
+ if ( pImpl->bCtrlReleased )
+ {
+ for ( sal_uInt16 nCache = pImpl->pCaches.size(); nCache > 0; --nCache )
+ {
+ // Get Cache via css::sdbcx::Index
+ SfxStateCache *pCache = pImpl->pCaches[nCache-1].get();
+
+ // No interested Controller present
+ if ( pCache->GetItemLink() == nullptr && !pCache->GetInternalController() )
+ {
+ // Remove Cache. Safety: first remove and then delete
+ pImpl->pCaches.erase(pImpl->pCaches.begin() + nCache - 1);
+ }
+ }
+ }
+
+ // restart background-processing
+ pImpl->nMsgPos = 0;
+ if ( !pFrame || !pFrame->GetObjectShell() )
+ return;
+ if ( !pImpl->pCaches.empty() )
+ {
+ pImpl->aAutoTimer.Stop();
+ pImpl->aAutoTimer.SetTimeout(TIMEOUT_FIRST);
+ pImpl->aAutoTimer.Start();
+ }
+ }
+
+ SAL_INFO(
+ "sfx.control",
+ std::setw(std::min(nRegLevel, sal_uInt16(8))) << ' ' << "this = " << this
+ << " Level = " << nRegLevel << " SfxBindings::LeaveRegistrations "
+ << (pFile
+ ? SAL_STREAM("File: " << pFile << " Line: " << nLine) : ""));
+}
+
+
+void SfxBindings::SetDispatcher( SfxDispatcher *pDisp )
+{
+ SfxDispatcher *pOldDispat = pDispatcher;
+ if ( pDisp == pDispatcher )
+ return;
+
+ if ( pOldDispat )
+ {
+ SfxBindings* pBind = pOldDispat->GetBindings();
+ while ( pBind )
+ {
+ if ( pBind->pImpl->pSubBindings == this && pBind->pDispatcher != pDisp )
+ pBind->SetSubBindings_Impl( nullptr );
+ pBind = pBind->pImpl->pSubBindings;
+ }
+ }
+
+ pDispatcher = pDisp;
+
+ css::uno::Reference < css::frame::XDispatchProvider > xProv;
+ if ( pDisp )
+ xProv.set( pDisp->GetFrame()->GetFrame().GetFrameInterface(), UNO_QUERY );
+
+ SetDispatchProvider_Impl( xProv );
+ InvalidateAll( true );
+
+ if ( pDispatcher && !pOldDispat )
+ {
+ if ( pImpl->pSubBindings && pImpl->pSubBindings->pDispatcher != pOldDispat )
+ {
+ OSL_FAIL( "SubBindings already set before activating!" );
+ pImpl->pSubBindings->ENTERREGISTRATIONS();
+ }
+ LEAVEREGISTRATIONS();
+ }
+ else if( !pDispatcher )
+ {
+ ENTERREGISTRATIONS();
+ if ( pImpl->pSubBindings && pImpl->pSubBindings->pDispatcher != pOldDispat )
+ {
+ OSL_FAIL( "SubBindings still set even when deactivating!" );
+ pImpl->pSubBindings->LEAVEREGISTRATIONS();
+ }
+ }
+
+ Broadcast( SfxHint( SfxHintId::DataChanged ) );
+
+ if ( !pDisp )
+ return;
+
+ SfxBindings* pBind = pDisp->GetBindings();
+ while ( pBind && pBind != this )
+ {
+ if ( !pBind->pImpl->pSubBindings )
+ {
+ pBind->SetSubBindings_Impl( this );
+ break;
+ }
+
+ pBind = pBind->pImpl->pSubBindings;
+ }
+}
+
+
+void SfxBindings::ClearCache_Impl( sal_uInt16 nSlotId )
+{
+ SfxStateCache* pCache = GetStateCache(nSlotId);
+ if (!pCache)
+ return;
+ pCache->ClearCache();
+}
+
+
+void SfxBindings::StartUpdate_Impl( bool bComplete )
+{
+ if ( pImpl->pSubBindings )
+ pImpl->pSubBindings->StartUpdate_Impl( bComplete );
+
+ if ( !bComplete )
+ // Update may be interrupted
+ NextJob_Impl(&pImpl->aAutoTimer);
+ else
+ // Update all slots in a row
+ NextJob_Impl(nullptr);
+}
+
+
+SfxItemState SfxBindings::QueryState( sal_uInt16 nSlot, std::unique_ptr<SfxPoolItem> &rpState )
+{
+ css::uno::Reference< css::frame::XDispatch > xDisp;
+ SfxStateCache *pCache = GetStateCache( nSlot );
+ if ( pCache )
+ xDisp = pCache->GetDispatch();
+ if ( xDisp.is() || !pCache )
+ {
+ const SfxSlot* pSlot = SfxSlotPool::GetSlotPool( pDispatcher->GetFrame() ).GetSlot( nSlot );
+ if ( !pSlot || !pSlot->pUnoName )
+ return SfxItemState::DISABLED;
+
+ css::util::URL aURL;
+ OUString aCmd( ".uno:" );
+ aURL.Protocol = aCmd;
+ aURL.Path = OUString::createFromAscii(pSlot->GetUnoName());
+ aCmd += aURL.Path;
+ aURL.Complete = aCmd;
+ aURL.Main = aCmd;
+
+ if ( !xDisp.is() )
+ xDisp = pImpl->xProv->queryDispatch( aURL, OUString(), 0 );
+
+ if ( xDisp.is() )
+ {
+ if (!comphelper::getFromUnoTunnel<SfxOfficeDispatch>(xDisp))
+ {
+ bool bDeleteCache = false;
+ if ( !pCache )
+ {
+ pCache = new SfxStateCache( nSlot );
+ pCache->GetSlotServer( *GetDispatcher_Impl(), pImpl->xProv );
+ bDeleteCache = true;
+ }
+
+ SfxItemState eState = SfxItemState::SET;
+ rtl::Reference<BindDispatch_Impl> xBind(new BindDispatch_Impl( xDisp, aURL, pCache, pSlot ));
+ xDisp->addStatusListener( xBind, aURL );
+ if ( !xBind->GetStatus().IsEnabled )
+ {
+ eState = SfxItemState::DISABLED;
+ }
+ else
+ {
+ css::uno::Any aAny = xBind->GetStatus().State;
+ const css::uno::Type& aType = aAny.getValueType();
+
+ if ( aType == cppu::UnoType<bool>::get() )
+ {
+ bool bTemp = false;
+ aAny >>= bTemp ;
+ rpState.reset(new SfxBoolItem( nSlot, bTemp ));
+ }
+ else if ( aType == ::cppu::UnoType< ::cppu::UnoUnsignedShortType >::get() )
+ {
+ sal_uInt16 nTemp = 0;
+ aAny >>= nTemp ;
+ rpState.reset(new SfxUInt16Item( nSlot, nTemp ));
+ }
+ else if ( aType == cppu::UnoType<sal_uInt32>::get() )
+ {
+ sal_uInt32 nTemp = 0;
+ aAny >>= nTemp ;
+ rpState.reset(new SfxUInt32Item( nSlot, nTemp ));
+ }
+ else if ( aType == cppu::UnoType<OUString>::get() )
+ {
+ OUString sTemp ;
+ aAny >>= sTemp ;
+ rpState.reset(new SfxStringItem( nSlot, sTemp ));
+ }
+ else
+ rpState.reset(new SfxVoidItem( nSlot ));
+ }
+
+ xDisp->removeStatusListener( xBind, aURL );
+ xBind->Release();
+ xBind.clear();
+ if ( bDeleteCache )
+ {
+ delete pCache;
+ pCache = nullptr;
+ }
+ return eState;
+ }
+ }
+ }
+
+ // Then test at the dispatcher to check if the returned items from
+ // there are always DELETE_ON_IDLE, a copy of it has to be made in
+ // order to allow for transition of ownership.
+ const SfxPoolItem *pItem = nullptr;
+ SfxItemState eState = pDispatcher->QueryState( nSlot, pItem );
+ if ( eState == SfxItemState::SET )
+ {
+ DBG_ASSERT( pItem, "SfxItemState::SET but no item!" );
+ if ( pItem )
+ rpState.reset(pItem->Clone());
+ }
+ else if ( eState == SfxItemState::DEFAULT && pItem )
+ {
+ rpState.reset(pItem->Clone());
+ }
+
+ return eState;
+}
+
+void SfxBindings::QueryControlState( sal_uInt16 nSlot, boost::property_tree::ptree& rState )
+{
+ if ( SfxGetpApp()->IsDowning() )
+ return;
+
+ if ( pDispatcher )
+ pDispatcher->Flush();
+
+ if ( pImpl->pSubBindings )
+ pImpl->pSubBindings->QueryControlState( nSlot, rState );
+
+ SfxStateCache* pCache = GetStateCache( nSlot );
+ if ( !pCache )
+ return;
+
+ if ( pImpl->bMsgDirty )
+ {
+ UpdateSlotServer_Impl();
+ pCache = GetStateCache( nSlot );
+ }
+
+ if (pCache && pCache->GetItemLink() )
+ {
+ pCache->GetState(rState);
+ }
+}
+
+sal_uInt16 SfxBindings::QuerySlotId( const util::URL& aURL )
+{
+ if (!pImpl)
+ return 0;
+
+ css::uno::Reference<css::frame::XDispatch> xDispatch =
+ pImpl->xProv->queryDispatch(aURL, OUString(), 0);
+ if (!xDispatch.is())
+ return 0;
+
+ css::uno::Reference<css::lang::XUnoTunnel> xTunnel(xDispatch, css::uno::UNO_QUERY);
+ if (!xTunnel.is())
+ return 0;
+
+ sal_Int64 nHandle = xTunnel->getSomething(SfxOfficeDispatch::getUnoTunnelId());
+ if (!nHandle)
+ return 0;
+
+ SfxOfficeDispatch* pDispatch = reinterpret_cast<SfxOfficeDispatch*>(sal::static_int_cast<sal_IntPtr>(nHandle));
+ return pDispatch->GetId();
+}
+
+void SfxBindings::SetSubBindings_Impl( SfxBindings *pSub )
+{
+ if ( pImpl->pSubBindings )
+ {
+ pImpl->pSubBindings->SetDispatchProvider_Impl( css::uno::Reference< css::frame::XDispatchProvider > () );
+ }
+
+ pImpl->pSubBindings = pSub;
+
+ if ( pSub )
+ {
+ pImpl->pSubBindings->SetDispatchProvider_Impl( pImpl->xProv );
+ }
+}
+
+SfxBindings* SfxBindings::GetSubBindings_Impl() const
+{
+ return pImpl->pSubBindings;
+}
+
+void SfxBindings::SetWorkWindow_Impl( std::unique_ptr<SfxWorkWindow> xWork )
+{
+ pImpl->mxWorkWin = std::move(xWork);
+}
+
+SfxWorkWindow* SfxBindings::GetWorkWindow_Impl() const
+{
+ return pImpl->mxWorkWin.get();
+}
+
+bool SfxBindings::IsInUpdate() const
+{
+ bool bInUpdate = pImpl->bInUpdate;
+ if ( !bInUpdate && pImpl->pSubBindings )
+ bInUpdate = pImpl->pSubBindings->IsInUpdate();
+ return bInUpdate;
+}
+
+void SfxBindings::SetVisibleState( sal_uInt16 nId, bool bShow )
+{
+ SfxStateCache *pCache = GetStateCache( nId );
+ if ( pCache )
+ pCache->SetVisibleState( bShow );
+}
+
+void SfxBindings::SetActiveFrame( const css::uno::Reference< css::frame::XFrame > & rFrame )
+{
+ if ( rFrame.is() || !pDispatcher )
+ SetDispatchProvider_Impl( css::uno::Reference< css::frame::XDispatchProvider > ( rFrame, css::uno::UNO_QUERY ) );
+ else
+ SetDispatchProvider_Impl( css::uno::Reference< css::frame::XDispatchProvider > (
+ pDispatcher->GetFrame()->GetFrame().GetFrameInterface(), css::uno::UNO_QUERY ) );
+}
+
+css::uno::Reference< css::frame::XFrame > SfxBindings::GetActiveFrame() const
+{
+ const css::uno::Reference< css::frame::XFrame > xFrame( pImpl->xProv, css::uno::UNO_QUERY );
+ if ( xFrame.is() || !pDispatcher )
+ return xFrame;
+ else
+ return pDispatcher->GetFrame()->GetFrame().GetFrameInterface();
+}
+
+void SfxBindings::SetDispatchProvider_Impl( const css::uno::Reference< css::frame::XDispatchProvider > & rProv )
+{
+ bool bInvalidate = ( rProv != pImpl->xProv );
+ if ( bInvalidate )
+ {
+ pImpl->xProv = rProv;
+ InvalidateAll( true );
+ }
+
+ if ( pImpl->pSubBindings )
+ pImpl->pSubBindings->SetDispatchProvider_Impl( pImpl->xProv );
+}
+
+const css::uno::Reference< css::frame::XDispatchRecorder >& SfxBindings::GetRecorder() const
+{
+ return pImpl->xRecorder;
+}
+
+void SfxBindings::SetRecorder_Impl( css::uno::Reference< css::frame::XDispatchRecorder > const & rRecorder )
+{
+ pImpl->xRecorder = rRecorder;
+}
+
+void SfxBindings::ContextChanged_Impl()
+{
+ if ( !pImpl->bInUpdate && ( !pImpl->bContextChanged || !pImpl->bAllMsgDirty ) )
+ {
+ InvalidateAll( true );
+ }
+}
+
+uno::Reference < frame::XDispatch > SfxBindings::GetDispatch( const SfxSlot* pSlot, const util::URL& aURL, bool bMasterCommand )
+{
+ uno::Reference < frame::XDispatch > xRet;
+ SfxStateCache* pCache = GetStateCache( pSlot->nSlotId );
+ if ( pCache && !bMasterCommand )
+ xRet = pCache->GetInternalDispatch();
+ if ( !xRet.is() )
+ {
+ // dispatches for slaves are unbound, they don't have a state
+ SfxOfficeDispatch* pDispatch = bMasterCommand ?
+ new SfxOfficeDispatch( pDispatcher, pSlot, aURL ) :
+ new SfxOfficeDispatch( *this, pDispatcher, pSlot, aURL );
+
+ pDispatch->SetMasterUnoCommand( bMasterCommand );
+ xRet.set( pDispatch );
+ if ( !pCache )
+ pCache = GetStateCache( pSlot->nSlotId );
+
+ DBG_ASSERT( pCache, "No cache for OfficeDispatch!" );
+ if ( pCache && !bMasterCommand )
+ pCache->SetInternalDispatch( xRet );
+ }
+
+ return xRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/control/charmapcontrol.cxx b/sfx2/source/control/charmapcontrol.cxx
new file mode 100644
index 000000000..a79da745b
--- /dev/null
+++ b/sfx2/source/control/charmapcontrol.cxx
@@ -0,0 +1,222 @@
+/* -*- 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/dispatchcommand.hxx>
+#include <officecfg/Office/Common.hxx>
+#include <charmapcontrol.hxx>
+#include <charmappopup.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/strings.hrc>
+#include <sfx2/sfxresid.hxx>
+
+using namespace css;
+
+SfxCharmapCtrl::SfxCharmapCtrl(CharmapPopup* pControl, weld::Widget* pParent)
+ : WeldToolbarPopup(pControl->getFrameInterface(), pParent, "sfx/ui/charmapcontrol.ui", "charmapctrl")
+ , m_xControl(pControl)
+ , m_xVirDev(VclPtr<VirtualDevice>::Create())
+ , m_aRecentCharView{SvxCharView(m_xVirDev),
+ SvxCharView(m_xVirDev),
+ SvxCharView(m_xVirDev),
+ SvxCharView(m_xVirDev),
+ SvxCharView(m_xVirDev),
+ SvxCharView(m_xVirDev),
+ SvxCharView(m_xVirDev),
+ SvxCharView(m_xVirDev),
+ SvxCharView(m_xVirDev),
+ SvxCharView(m_xVirDev),
+ SvxCharView(m_xVirDev),
+ SvxCharView(m_xVirDev),
+ SvxCharView(m_xVirDev),
+ SvxCharView(m_xVirDev),
+ SvxCharView(m_xVirDev),
+ SvxCharView(m_xVirDev)}
+ , m_aFavCharView{SvxCharView(m_xVirDev),
+ SvxCharView(m_xVirDev),
+ SvxCharView(m_xVirDev),
+ SvxCharView(m_xVirDev),
+ SvxCharView(m_xVirDev),
+ SvxCharView(m_xVirDev),
+ SvxCharView(m_xVirDev),
+ SvxCharView(m_xVirDev),
+ SvxCharView(m_xVirDev),
+ SvxCharView(m_xVirDev),
+ SvxCharView(m_xVirDev),
+ SvxCharView(m_xVirDev),
+ SvxCharView(m_xVirDev),
+ SvxCharView(m_xVirDev),
+ SvxCharView(m_xVirDev),
+ SvxCharView(m_xVirDev)}
+ , m_xRecentLabel(m_xBuilder->weld_label("label2"))
+ , m_xDlgBtn(m_xBuilder->weld_button("specialchardlg"))
+ , m_xRecentCharView{std::make_unique<weld::CustomWeld>(*m_xBuilder, "viewchar1", m_aRecentCharView[0]),
+ std::make_unique<weld::CustomWeld>(*m_xBuilder, "viewchar2", m_aRecentCharView[1]),
+ std::make_unique<weld::CustomWeld>(*m_xBuilder, "viewchar3", m_aRecentCharView[2]),
+ std::make_unique<weld::CustomWeld>(*m_xBuilder, "viewchar4", m_aRecentCharView[3]),
+ std::make_unique<weld::CustomWeld>(*m_xBuilder, "viewchar5", m_aRecentCharView[4]),
+ std::make_unique<weld::CustomWeld>(*m_xBuilder, "viewchar6", m_aRecentCharView[5]),
+ std::make_unique<weld::CustomWeld>(*m_xBuilder, "viewchar7", m_aRecentCharView[6]),
+ std::make_unique<weld::CustomWeld>(*m_xBuilder, "viewchar8", m_aRecentCharView[7]),
+ std::make_unique<weld::CustomWeld>(*m_xBuilder, "viewchar9", m_aRecentCharView[8]),
+ std::make_unique<weld::CustomWeld>(*m_xBuilder, "viewchar10", m_aRecentCharView[9]),
+ std::make_unique<weld::CustomWeld>(*m_xBuilder, "viewchar11", m_aRecentCharView[10]),
+ std::make_unique<weld::CustomWeld>(*m_xBuilder, "viewchar12", m_aRecentCharView[11]),
+ std::make_unique<weld::CustomWeld>(*m_xBuilder, "viewchar13", m_aRecentCharView[12]),
+ std::make_unique<weld::CustomWeld>(*m_xBuilder, "viewchar14", m_aRecentCharView[13]),
+ std::make_unique<weld::CustomWeld>(*m_xBuilder, "viewchar15", m_aRecentCharView[14]),
+ std::make_unique<weld::CustomWeld>(*m_xBuilder, "viewchar16", m_aRecentCharView[15])}
+ , m_xFavCharView{std::make_unique<weld::CustomWeld>(*m_xBuilder, "favchar1", m_aFavCharView[0]),
+ std::make_unique<weld::CustomWeld>(*m_xBuilder, "favchar2", m_aFavCharView[1]),
+ std::make_unique<weld::CustomWeld>(*m_xBuilder, "favchar3", m_aFavCharView[2]),
+ std::make_unique<weld::CustomWeld>(*m_xBuilder, "favchar4", m_aFavCharView[3]),
+ std::make_unique<weld::CustomWeld>(*m_xBuilder, "favchar5", m_aFavCharView[4]),
+ std::make_unique<weld::CustomWeld>(*m_xBuilder, "favchar6", m_aFavCharView[5]),
+ std::make_unique<weld::CustomWeld>(*m_xBuilder, "favchar7", m_aFavCharView[6]),
+ std::make_unique<weld::CustomWeld>(*m_xBuilder, "favchar8", m_aFavCharView[7]),
+ std::make_unique<weld::CustomWeld>(*m_xBuilder, "favchar9", m_aFavCharView[8]),
+ std::make_unique<weld::CustomWeld>(*m_xBuilder, "favchar10", m_aFavCharView[9]),
+ std::make_unique<weld::CustomWeld>(*m_xBuilder, "favchar11", m_aFavCharView[10]),
+ std::make_unique<weld::CustomWeld>(*m_xBuilder, "favchar12", m_aFavCharView[11]),
+ std::make_unique<weld::CustomWeld>(*m_xBuilder, "favchar13", m_aFavCharView[12]),
+ std::make_unique<weld::CustomWeld>(*m_xBuilder, "favchar14", m_aFavCharView[13]),
+ std::make_unique<weld::CustomWeld>(*m_xBuilder, "favchar15", m_aFavCharView[14]),
+ std::make_unique<weld::CustomWeld>(*m_xBuilder, "favchar16", m_aFavCharView[15])}
+{
+ for(int i = 0; i < 16; i++)
+ {
+ m_aRecentCharView[i].setMouseClickHdl(LINK(this,SfxCharmapCtrl, CharClickHdl));
+ m_aFavCharView[i].setMouseClickHdl(LINK(this,SfxCharmapCtrl, CharClickHdl));
+ }
+
+ m_xDlgBtn->connect_clicked(LINK(this, SfxCharmapCtrl, OpenDlgHdl));
+
+ getRecentCharacterList();
+ updateRecentCharControl();
+ getFavCharacterList();
+ updateFavCharControl();
+}
+
+SfxCharmapCtrl::~SfxCharmapCtrl()
+{
+}
+
+void SfxCharmapCtrl::getFavCharacterList()
+{
+ //retrieve recent character list
+ const css::uno::Sequence< OUString > rFavCharList( officecfg::Office::Common::FavoriteCharacters::FavoriteCharacterList::get() );
+ m_aFavCharList.insert( m_aFavCharList.end(), rFavCharList.begin(), rFavCharList.end() );
+
+ //retrieve recent character font list
+ const css::uno::Sequence< OUString > rFavCharFontList( officecfg::Office::Common::FavoriteCharacters::FavoriteCharacterFontList::get() );
+ m_aFavCharFontList.insert( m_aFavCharFontList.end(), rFavCharFontList.begin(), rFavCharFontList.end() );
+
+ // tdf#135997: make sure that the two lists are same length
+ const auto nCommonLength = std::min(m_aFavCharList.size(), m_aFavCharFontList.size());
+ m_aFavCharList.resize(nCommonLength);
+ m_aFavCharFontList.resize(nCommonLength);
+}
+
+void SfxCharmapCtrl::updateFavCharControl()
+{
+ assert(m_aFavCharList.size() == m_aFavCharFontList.size());
+
+ int i = 0;
+ for ( std::deque< OUString >::iterator it = m_aFavCharList.begin(), it2 = m_aFavCharFontList.begin();
+ it != m_aFavCharList.end() && it2 != m_aFavCharFontList.end();
+ ++it, ++it2, i++)
+ {
+ m_aFavCharView[i].SetText(*it);
+ vcl::Font rFont = m_aFavCharView[i].GetFont();
+ rFont.SetFamilyName( *it2 );
+ m_aFavCharView[i].SetFont(rFont);
+ m_aFavCharView[i].Show();
+ }
+
+ for(; i < 16 ; i++)
+ {
+ m_aFavCharView[i].SetText(OUString());
+ m_aFavCharView[i].Hide();
+ }
+}
+
+void SfxCharmapCtrl::getRecentCharacterList()
+{
+ //retrieve recent character list
+ const css::uno::Sequence< OUString > rRecentCharList( officecfg::Office::Common::RecentCharacters::RecentCharacterList::get() );
+ m_aRecentCharList.insert( m_aRecentCharList.end(), rRecentCharList.begin(), rRecentCharList.end() );
+
+ //retrieve recent character font list
+ const css::uno::Sequence< OUString > rRecentCharFontList( officecfg::Office::Common::RecentCharacters::RecentCharacterFontList::get() );
+ m_aRecentCharFontList.insert( m_aRecentCharFontList.end(), rRecentCharFontList.begin(), rRecentCharFontList.end() );
+
+ // tdf#135997: make sure that the two lists are same length
+ const auto nCommonLength = std::min(m_aRecentCharList.size(), m_aRecentCharFontList.size());
+ m_aRecentCharList.resize(nCommonLength);
+ m_aRecentCharFontList.resize(nCommonLength);
+}
+
+void SfxCharmapCtrl::updateRecentCharControl()
+{
+ assert(m_aRecentCharList.size() == m_aRecentCharFontList.size());
+ int i = 0;
+ for ( std::deque< OUString >::iterator it = m_aRecentCharList.begin(), it2 = m_aRecentCharFontList.begin();
+ it != m_aRecentCharList.end() && it2 != m_aRecentCharFontList.end();
+ ++it, ++it2, i++)
+ {
+ m_aRecentCharView[i].SetText(*it);
+ vcl::Font rFont = m_aRecentCharView[i].GetFont();
+ rFont.SetFamilyName( *it2 );
+ m_aRecentCharView[i].SetFont(rFont);
+ m_aRecentCharView[i].Show();
+ }
+
+ for(; i < 16 ; i++)
+ {
+ m_aRecentCharView[i].SetText(OUString());
+ m_aRecentCharView[i].Hide();
+ }
+
+ //checking if the characters are recently used or no
+ m_xRecentLabel->set_label(m_aRecentCharList.size() > 0 ? SfxResId(STR_RECENT) : SfxResId(STR_NORECENT));
+}
+
+IMPL_LINK(SfxCharmapCtrl, CharClickHdl, SvxCharView*, pView, void)
+{
+ m_xControl->EndPopupMode();
+
+ pView->InsertCharToDoc();
+}
+
+IMPL_LINK_NOARG(SfxCharmapCtrl, OpenDlgHdl, weld::Button&, void)
+{
+ m_xControl->EndPopupMode();
+
+ if (SfxViewFrame* pViewFrm = SfxViewFrame::Current())
+ {
+ uno::Reference<frame::XFrame> xFrame = pViewFrm->GetFrame().GetFrameInterface();
+ comphelper::dispatchCommand(".uno:InsertSymbol", xFrame, {});
+ }
+}
+
+void SfxCharmapCtrl::GrabFocus()
+{
+ m_aFavCharView[0].GrabFocus();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/control/charwin.cxx b/sfx2/source/control/charwin.cxx
new file mode 100644
index 000000000..567f365e6
--- /dev/null
+++ b/sfx2/source/control/charwin.cxx
@@ -0,0 +1,263 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <vcl/settings.hxx>
+#include <vcl/virdev.hxx>
+#include <vcl/event.hxx>
+#include <vcl/svapp.hxx>
+#include <sfx2/charwin.hxx>
+#include <comphelper/dispatchcommand.hxx>
+#include <comphelper/propertyvalue.hxx>
+
+#include <com/sun/star/beans/PropertyValue.hpp>
+
+using namespace com::sun::star;
+
+SvxCharView::SvxCharView(const VclPtr<VirtualDevice>& rVirDev)
+ : mxVirDev(rVirDev)
+ , mnY(0)
+ , maPosition(0, 0)
+ , maHasInsert(true)
+{
+}
+
+void SvxCharView::SetDrawingArea(weld::DrawingArea* pDrawingArea)
+{
+ CustomWidgetController::SetDrawingArea(pDrawingArea);
+ const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
+ vcl::Font aFont = rStyleSettings.GetLabelFont();
+ const Size aFontSize = aFont.GetFontSize();
+ aFont.SetFontSize(Size(aFontSize.Width() * 2.5, aFontSize.Height() * 2.5));
+ mxVirDev->Push(PUSH_ALLFONT);
+ mxVirDev->SetFont(aFont);
+ pDrawingArea->set_size_request(mxVirDev->approximate_digit_width() * 2,
+ mxVirDev->GetTextHeight());
+ mxVirDev->Pop();
+}
+
+void SvxCharView::GetFocus() { Invalidate(); }
+
+void SvxCharView::LoseFocus() { Invalidate(); }
+
+bool SvxCharView::MouseButtonDown(const MouseEvent& rMEvt)
+{
+ if (rMEvt.IsLeft())
+ {
+ if (!(rMEvt.GetClicks() % 2) && maHasInsert)
+ {
+ InsertCharToDoc();
+ }
+
+ maMouseClickHdl.Call(this);
+ }
+
+ if (rMEvt.IsRight())
+ {
+ Point aPosition(rMEvt.GetPosPixel());
+ maPosition = aPosition;
+ GrabFocus();
+ Invalidate();
+ createContextMenu();
+ }
+
+ return true;
+}
+
+bool SvxCharView::KeyInput(const KeyEvent& rKEvt)
+{
+ bool bRet = false;
+ vcl::KeyCode aCode = rKEvt.GetKeyCode();
+ switch (aCode.GetCode())
+ {
+ case KEY_SPACE:
+ case KEY_RETURN:
+ InsertCharToDoc();
+ bRet = true;
+ break;
+ }
+ return bRet;
+}
+
+void SvxCharView::InsertCharToDoc()
+{
+ if (GetText().isEmpty())
+ return;
+
+ uno::Sequence<beans::PropertyValue> aArgs{ comphelper::makePropertyValue("Symbols", GetText()),
+ comphelper::makePropertyValue(
+ "FontName", maFont.GetFamilyName()) };
+
+ comphelper::dispatchCommand(".uno:InsertSymbol", aArgs);
+}
+
+void SvxCharView::createContextMenu()
+{
+ weld::DrawingArea* pDrawingArea = GetDrawingArea();
+ std::unique_ptr<weld::Builder> xBuilder(
+ Application::CreateBuilder(pDrawingArea, "sfx/ui/charviewmenu.ui"));
+ std::unique_ptr<weld::Menu> xItemMenu(xBuilder->weld_menu("charviewmenu"));
+ ContextMenuSelect(
+ xItemMenu->popup_at_rect(pDrawingArea, tools::Rectangle(maPosition, Size(1, 1))));
+ Invalidate();
+}
+
+void SvxCharView::ContextMenuSelect(std::string_view rMenuId)
+{
+ if (rMenuId == "clearchar")
+ maClearClickHdl.Call(this);
+ else if (rMenuId == "clearallchar")
+ maClearAllClickHdl.Call(this);
+}
+
+void SvxCharView::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&)
+{
+ rRenderContext.SetFont(maFont);
+
+ const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
+ const Color aWindowTextColor(rStyleSettings.GetFieldTextColor());
+ Color aHighlightColor(rStyleSettings.GetHighlightColor());
+ Color aHighlightTextColor(rStyleSettings.GetHighlightTextColor());
+ Color aFillColor(rStyleSettings.GetWindowColor());
+ Color aTextColor(rStyleSettings.GetWindowTextColor());
+ Color aShadowColor(rStyleSettings.GetShadowColor());
+
+ const OUString aText = GetText();
+
+ Size aSize(GetOutputSizePixel());
+ tools::Long nAvailWidth = aSize.Width();
+ tools::Long nWinHeight = aSize.Height();
+
+ bool bGotBoundary = true;
+ bool bShrankFont = false;
+ vcl::Font aOrigFont(rRenderContext.GetFont());
+ Size aFontSize(aOrigFont.GetFontSize());
+ ::tools::Rectangle aBoundRect;
+
+ for (tools::Long nFontHeight = aFontSize.Height(); nFontHeight > 0; nFontHeight -= 1)
+ {
+ if (!rRenderContext.GetTextBoundRect(aBoundRect, aText) || aBoundRect.IsEmpty())
+ {
+ bGotBoundary = false;
+ break;
+ }
+
+ //only shrink in the single glyph large view mode
+ tools::Long nTextWidth = aBoundRect.GetWidth();
+ if (nAvailWidth > nTextWidth)
+ break;
+ vcl::Font aFont(aOrigFont);
+ aFontSize.setHeight(nFontHeight);
+ aFont.SetFontSize(aFontSize);
+ rRenderContext.SetFont(aFont);
+ mnY = (nWinHeight - rRenderContext.GetTextHeight()) / 2;
+ bShrankFont = true;
+ }
+
+ Point aPoint(2, mnY);
+
+ if (!bGotBoundary)
+ aPoint.setX((aSize.Width() - rRenderContext.GetTextWidth(aText)) / 2);
+ else
+ {
+ // adjust position
+ aBoundRect += aPoint;
+
+ // vertical adjustment
+ int nYLDelta = aBoundRect.Top();
+ int nYHDelta = aSize.Height() - aBoundRect.Bottom();
+ if (nYLDelta <= 0)
+ aPoint.AdjustY(-(nYLDelta - 1));
+ else if (nYHDelta <= 0)
+ aPoint.AdjustY(nYHDelta - 1);
+
+ // centrally align glyph
+ aPoint.setX(-aBoundRect.Left() + (aSize.Width() - aBoundRect.GetWidth()) / 2);
+ }
+
+ // tdf#111924 - don't lose focus on context menu
+ if (HasFocus() || HasChildFocus())
+ {
+ rRenderContext.SetFillColor(aHighlightColor);
+ rRenderContext.DrawRect(tools::Rectangle(Point(0, 0), aSize));
+
+ rRenderContext.SetTextColor(aHighlightTextColor);
+ rRenderContext.DrawText(aPoint, aText);
+ }
+ else
+ {
+ rRenderContext.SetFillColor(aFillColor);
+ rRenderContext.SetLineColor(aShadowColor);
+ rRenderContext.DrawRect(tools::Rectangle(Point(0, 0), aSize));
+
+ rRenderContext.SetTextColor(aWindowTextColor);
+ rRenderContext.DrawText(aPoint, aText);
+ }
+ rRenderContext.SetFillColor(aFillColor);
+ rRenderContext.SetTextColor(aTextColor);
+
+ if (bShrankFont)
+ rRenderContext.SetFont(aOrigFont);
+}
+
+void SvxCharView::setMouseClickHdl(const Link<SvxCharView*, void>& rLink)
+{
+ maMouseClickHdl = rLink;
+}
+
+void SvxCharView::setClearClickHdl(const Link<SvxCharView*, void>& rLink)
+{
+ maClearClickHdl = rLink;
+}
+
+void SvxCharView::setClearAllClickHdl(const Link<SvxCharView*, void>& rLink)
+{
+ maClearAllClickHdl = rLink;
+}
+
+void SvxCharView::SetFont(const vcl::Font& rFont)
+{
+ tools::Long nWinHeight = GetOutputSizePixel().Height();
+ maFont = rFont;
+ maFont.SetWeight(WEIGHT_NORMAL);
+ maFont.SetAlignment(ALIGN_TOP);
+ maFont.SetFontSize(mxVirDev->PixelToLogic(Size(0, nWinHeight / 2)));
+ maFont.SetTransparent(true);
+
+ mxVirDev->Push(PUSH_ALLFONT);
+ mxVirDev->SetFont(maFont);
+ mnY = (nWinHeight - mxVirDev->GetTextHeight()) / 2;
+ mxVirDev->Pop();
+
+ Invalidate();
+}
+
+void SvxCharView::Resize()
+{
+ SetFont(GetFont()); //force recalculation of size
+}
+
+void SvxCharView::SetText(const OUString& rText)
+{
+ m_sText = rText;
+ Invalidate();
+}
+
+void SvxCharView::SetHasInsert(bool bInsert) { maHasInsert = bInsert; }
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/sfx2/source/control/ctrlitem.cxx b/sfx2/source/control/ctrlitem.cxx
new file mode 100644
index 000000000..28edfec66
--- /dev/null
+++ b/sfx2/source/control/ctrlitem.cxx
@@ -0,0 +1,344 @@
+/* -*- 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/log.hxx>
+#include <svl/itempool.hxx>
+
+#include <sfx2/ctrlitem.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/dispatch.hxx>
+#include <statcach.hxx>
+#include <sfx2/viewfrm.hxx>
+
+// returns the next registered SfxControllerItem with the same id
+
+SfxControllerItem* SfxControllerItem::GetItemLink()
+{
+ return pNext == this ? nullptr : pNext;
+}
+
+
+// returns sal_True if this binding is really bound to a function
+
+bool SfxControllerItem::IsBound() const
+{
+ return pNext != this;
+}
+
+
+// registers with the id at the bindings
+
+void SfxControllerItem::Bind( sal_uInt16 nNewId, SfxBindings *pBindinx )
+{
+ DBG_ASSERT(pBindings || pBindinx, "No Bindings");
+
+ if ( IsBound() ) {
+ DBG_ASSERT(pBindings, "No Bindings");
+ pBindings->Release(*this);
+ }
+
+ nId = nNewId;
+ pNext = nullptr;
+
+ if (pBindinx)
+ pBindings = pBindinx;
+ pBindings->Register(*this);
+}
+
+void SfxControllerItem::BindInternal_Impl( sal_uInt16 nNewId, SfxBindings *pBindinx )
+{
+ DBG_ASSERT(pBindings || pBindinx, "No Bindings");
+
+ if ( IsBound() ) {
+ DBG_ASSERT(pBindings, "No Bindings");
+ pBindings->Release(*this);
+ }
+
+ nId = nNewId;
+ pNext = nullptr;
+
+ if (pBindinx)
+ pBindings = pBindinx;
+ pBindings->RegisterInternal_Impl(*this);
+}
+
+
+void SfxControllerItem::UnBind()
+
+/* [Description]
+
+ Unbinds the connection of this SfxControllerItems with the SfxBindings
+ instance with which it to time is bound. From this time on it does not
+ receive any status notifications (<SfxControllerItem::StateChented()>)
+ anymore.
+
+ [Cross-reference]
+
+ <SfxControllerItem::ReBind()>
+ <SfxControllerItem::ClearCache()>
+*/
+{
+ DBG_ASSERT(pBindings, "No Bindings");
+ DBG_ASSERT( IsBound(), "unbindings unbound SfxControllerItem" );
+
+ pBindings->Release(*this);
+ pNext = this;
+}
+
+
+void SfxControllerItem::ReBind()
+
+/* [Description]
+
+ Binds this SfxControllerItem with the SfxBindings instance again,
+ with which it was last bound. From this time on it does receive status
+ notifications (<SfxControllerItem::StateChented()>) again.
+
+ [Cross-reference]
+
+ <SfxControllerItem::UnBind()>
+ <SfxControllerItem::ClearCache()>
+*/
+
+{
+ DBG_ASSERT(pBindings, "No Bindings");
+ DBG_ASSERT( !IsBound(), "bindings rebound SfxControllerItem" );
+
+ pBindings->Register(*this);
+}
+
+
+void SfxControllerItem::ClearCache()
+
+/* [Description]
+
+ Clears the cache status for this SfxControllerItem. That is by the next
+ status update is the <SfxPoolItem> sent in any case, even if the same was
+ sent before. This is needed if a controller can be switched on and note
+ that status themselves.
+
+ [Example]
+
+ The combined controller for adjusting the surface type and the concrete
+ expression (blue color, or hatching X) can be changed in type, but is then
+ notified of the next selection again, even if it the same data.
+
+ [Cross-reference]
+
+ <SfxControllerItem::UnBind()>
+ <SfxControllerItem::ReBind()>
+*/
+
+
+{
+ DBG_ASSERT(pBindings, "No Bindings");
+
+ pBindings->ClearCache_Impl( GetId() );
+}
+
+// replaces the successor in the list of bindings of the same id
+SfxControllerItem* SfxControllerItem::ChangeItemLink( SfxControllerItem* pNewLink )
+{
+ SfxControllerItem* pOldLink = pNext;
+ pNext = pNewLink;
+ return pOldLink == this ? nullptr : pOldLink;
+}
+
+
+// changes the id of unbound functions (e.g. for sub-menu-ids)
+void SfxControllerItem::SetId( sal_uInt16 nItemId )
+{
+ DBG_ASSERT( !IsBound(), "changing id of bound binding" );
+ nId = nItemId;
+}
+
+// creates an atomic item for a controller without registration.
+SfxControllerItem::SfxControllerItem()
+ : pNext(this)
+ , pBindings(nullptr)
+ , eFallbackCoreMetric(MapUnit::Map100thMM)
+ , nId(0)
+{
+}
+
+// creates a representation of the function nId and registers it
+SfxControllerItem::SfxControllerItem(sal_uInt16 nID, SfxBindings &rBindings)
+ : pNext(this)
+ , pBindings(&rBindings)
+ , eFallbackCoreMetric(MapUnit::Map100thMM)
+ , nId(nID)
+{
+ Bind(nId, &rBindings);
+}
+
+// unregisters the item in the bindings
+SfxControllerItem::~SfxControllerItem()
+{
+ dispose();
+}
+
+void SfxControllerItem::dispose()
+{
+ if ( IsBound() )
+ UnBind();
+}
+
+void SfxControllerItem::StateChangedAtToolBoxControl
+(
+ sal_uInt16, // <SID> of the triggering slot
+ SfxItemState, // <SfxItemState> of 'pState'
+ const SfxPoolItem* // Slot-Status, NULL or IsInvalidItem()
+)
+
+/* [Description]
+
+ This virtual method is called by the SFx to inform the <SfxControllerItem>s
+ is about that state of the slots 'NSID' has changed. The new value and the
+ value determined by this status is given as 'pState' or 'eState'.
+
+ The status of a slot may change, for example when the MDI window is
+ switched or when the slot was invalidated explicitly with
+ <SfxBindings::Invalidate()>.
+
+ Beware! The method is not called when the slot is invalid, however
+ has again assumed the same value.
+
+ This base class need not be called, further interim steps however
+ (eg <SfxToolboxControl> ) should be called.
+*/
+
+{
+}
+
+void SfxControllerItem::GetControlState
+(
+ sal_uInt16,
+ boost::property_tree::ptree&
+)
+{
+}
+
+void SfxStatusForwarder::StateChangedAtToolBoxControl
+(
+ sal_uInt16 nSID, // <SID> of the triggering slot
+ SfxItemState eState, // <SfxItemState> of 'pState'
+ const SfxPoolItem* pState // Slot-Status, NULL or IsInvalidItem()
+)
+
+{
+ pMaster->StateChangedAtToolBoxControl( nSID, eState, pState );
+}
+
+
+SfxStatusForwarder::SfxStatusForwarder(
+ sal_uInt16 nSlotId,
+ SfxControllerItem& rMaster ):
+ SfxControllerItem( nSlotId, rMaster.GetBindings() ),
+ pMaster( &rMaster )
+{
+}
+
+
+SfxItemState SfxControllerItem::GetItemState
+(
+ const SfxPoolItem* pState /* Pointer to <SfxPoolItem>, which
+ Status should be queried. */
+)
+
+/* [Description]
+
+ Static method to determine the status of the SfxPoolItem-Pointers, to be
+ used in the method <SfxControllerItem::StateChanged(const SfxPoolItem*)>
+
+ [Return value]
+
+ SfxItemState SfxItemState::UNKNOWN
+ Enabled, but no further status information available.
+ Typical for <Slot>s, which anyway are sometimes
+ disabled, but otherwise do not change their appearance.
+
+ SfxItemState::DISABLED
+ Disabled and no further status information available.
+ All other values that may appear should be reset to
+ default.
+
+ SfxItemState::DONTCARE
+ Enabled but there were only ambiguous values available
+ (i.e. non that can be queried).
+
+ SfxItemState::DEFAULT
+ Enabled and with available values, which are queried
+ by 'pState'. The Type is thus clearly defined in the
+ entire Program and specified through the Slot.
+*/
+
+{
+ return !pState
+ ? SfxItemState::DISABLED
+ : IsInvalidItem(pState)
+ ? SfxItemState::DONTCARE
+ : pState->IsVoidItem() && !pState->Which()
+ ? SfxItemState::UNKNOWN
+ : SfxItemState::DEFAULT;
+}
+
+
+MapUnit SfxControllerItem::GetCoreMetric() const
+
+/* [Description]
+
+ Gets the measurement unit from the competent pool, in which the Status
+ item exist.
+*/
+
+{
+ SfxStateCache *pCache = pBindings->GetStateCache( nId );
+ SfxDispatcher *pDispat = pBindings->GetDispatcher_Impl();
+
+ if ( !pDispat )
+ {
+ SfxViewFrame* pViewFrame = SfxViewFrame::Current();
+ if ( !pViewFrame )
+ SfxViewFrame::GetFirst();
+ if ( pViewFrame )
+ pDispat = pViewFrame->GetDispatcher();
+ }
+
+ if ( pDispat && pCache )
+ {
+ const SfxSlotServer *pServer = pCache->GetSlotServer( *pDispat );
+ if ( pServer )
+ {
+ SfxShell *pSh = pDispat->GetShell( pServer->GetShellLevel() );
+ SfxItemPool &rPool = pSh->GetPool();
+ sal_uInt16 nWhich = rPool.GetWhich( nId );
+
+ // invalidate slot and its message|slot server as 'global' information
+ // about the validated message|slot server is not made available
+ pCache->Invalidate( true );
+
+ return rPool.GetMetric( nWhich );
+ }
+ }
+
+ SAL_INFO( "sfx.control", "W1: Can not find ItemPool!" );
+ return eFallbackCoreMetric;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/control/dispatch.cxx b/sfx2/source/control/dispatch.cxx
new file mode 100644
index 000000000..17edf97cc
--- /dev/null
+++ b/sfx2/source/control/dispatch.cxx
@@ -0,0 +1,2076 @@
+/* -*- 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 <config_feature_desktop.h>
+
+#include <algorithm>
+#include <cstddef>
+#include <deque>
+#include <vector>
+
+#include <stdlib.h>
+
+#include <boost/property_tree/json_parser.hpp>
+
+#include <com/sun/star/awt/PopupMenuDirection.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/frame/XDispatchRecorderSupplier.hpp>
+#include <com/sun/star/frame/XLayoutManager.hpp>
+#include <com/sun/star/frame/XPopupMenuController.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/ui/ContextMenuExecuteEvent.hpp>
+
+#include <LibreOfficeKit/LibreOfficeKitEnums.h>
+#include <comphelper/lok.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <rtl/strbuf.hxx>
+#include <sal/log.hxx>
+#include <sfx2/app.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/childwin.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/docfile.hxx>
+#include <hintpost.hxx>
+#include <sfx2/ipclient.hxx>
+#include <sfx2/module.hxx>
+#include <sfx2/msg.hxx>
+#include <sfx2/msgpool.hxx>
+#include <sfx2/objface.hxx>
+#include <sfx2/request.hxx>
+#include <sfx2/sfxsids.hrc>
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/viewsh.hxx>
+#include <svl/eitem.hxx>
+#include <svl/itemiter.hxx>
+#include <svl/itempool.hxx>
+#include <toolkit/awt/vclxmenu.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+#include <tools/debug.hxx>
+#include <vcl/idle.hxx>
+#include <vcl/menu.hxx>
+
+#include <sfxtypes.hxx>
+#include <slotserv.hxx>
+#include <workwin.hxx>
+
+typedef std::vector<SfxShell*> SfxShellStack_Impl;
+
+namespace {
+
+struct SfxToDo_Impl
+{
+ SfxShell* pCluster;
+ bool bPush;
+ bool bDelete;
+ bool bDeleted;
+ bool bUntil;
+
+ SfxToDo_Impl( bool bOpPush, bool bOpDelete, bool bOpUntil, SfxShell& rCluster )
+ : pCluster(&rCluster)
+ , bPush(bOpPush)
+ , bDelete(bOpDelete)
+ , bDeleted(false)
+ , bUntil(bOpUntil)
+ {}
+};
+
+struct SfxObjectBars_Impl
+{
+ ToolbarId eId; // ConfigId of the Toolbox
+ sal_uInt16 nPos;
+ SfxVisibilityFlags nFlags; // special visibility flags
+
+ SfxObjectBars_Impl() : eId(ToolbarId::None), nPos(0), nFlags(SfxVisibilityFlags::Invisible) {}
+};
+
+}
+
+struct SfxDispatcher_Impl
+{
+ //When the dispatched is locked, SfxRequests accumulate in aReqArr for
+ //later dispatch when unlocked via Post
+
+ //The pointers are typically deleted in Post, only if we never get around
+ //to posting them do we delete the unposted requests.
+ std::vector<std::unique_ptr<SfxRequest>>
+ aReqArr;
+ SfxShellStack_Impl aStack; // active functionality
+ Idle aIdle { "sfx::SfxDispatcher_Impl aIdle" }; // for Flush
+ std::deque<SfxToDo_Impl> aToDoStack; // not processed Push/Pop
+ SfxViewFrame* pFrame; // NULL or associated Frame
+ tools::SvRef<SfxHintPoster>
+ xPoster; // Execute asynchronous
+ bool bFlushing; // sal_True during Flush //?
+ bool bUpdated; // Update_Impl has run
+ bool bLocked; // No Execute
+ bool bInvalidateOnUnlock; // because someone asked
+ bool bActive; // not to be confused with set!
+ bool* pInCallAliveFlag; // view the Destructor Stack
+ SfxObjectBars_Impl aObjBars[SFX_OBJECTBAR_MAX];
+ SfxObjectBars_Impl aFixedObjBars[SFX_OBJECTBAR_MAX];
+ std::vector<sal_uInt32> aChildWins;
+ bool bNoUI; // UI only from Parent Dispatcher
+ bool bReadOnly; // Document is ReadOnly
+ bool bQuiet; // Only use parent dispatcher
+
+ SfxSlotFilterState nFilterEnabling; // 1==filter enabled slots,
+ // 2==ReadOnlyDoc overturned
+ o3tl::span<sal_uInt16 const>
+ pFilterSIDs; // sorted Array of SIDs
+ SfxDisableFlags nDisableFlags;
+ bool bFlushed;
+ std::deque< std::deque<SfxToDo_Impl> > aToDoCopyStack;
+};
+
+/** This method checks if the stack of the SfxDispatchers is flushed, or if
+ push- or pop- commands are pending.
+*/
+bool SfxDispatcher::IsFlushed() const
+{
+ return xImp->bFlushed;
+}
+
+/** This method performs outstanding push- and pop- commands. For <SfxShell>s,
+ which are new on the stack, the <SfxShell::Activate(bool)> is invoked
+ with bMDI == sal_True, for SfxShells that are removed from the stack, the
+ <SfxShell::Deactivate(bool)> is invoked with bMDI == sal_True
+*/
+void SfxDispatcher::Flush()
+{
+ if (!xImp->bFlushed) FlushImpl();
+}
+
+/** With this method, a <SfxShell> pushed on to the SfxDispatcher.
+ The SfxShell is first marked for push and a timer is set up.
+ First when the timer has counted down to zero the push
+ ( <SfxDispatcher::Flush()> ) is actually performed and the
+ <SfxBindings> is invalidated. While the timer is counting down
+ the opposing push and pop commands on the same SfxShell are
+ leveled out.
+*/
+void SfxDispatcher::Push(SfxShell& rShell)
+
+{
+ Pop( rShell, SfxDispatcherPopFlags::PUSH );
+}
+
+/** This method checks whether a particular <SfxShell> instance is
+ on the SfxDispatcher.
+
+ @returns true The SfxShell instance is on the SfxDispatcher.
+ false The SfxShell instance is not on the SfxDispatcher.
+*/
+bool SfxDispatcher::IsActive(const SfxShell& rShell)
+
+{
+ return CheckVirtualStack(rShell);
+}
+
+/** With this method it can be determined whether the SfxDispatcher is
+ locked or unlocked. A locked SfxDispatcher does not perform <SfxRequest>s
+ and no longer provides any status information. It behaves as if all the
+ slots are disabled.
+
+ The dispatcher is also marked as blocked, if all Dispatcher are locked
+ (<SfxApplication::LockDispatcher()>) or the associated top frame is in the
+ modal-mode and if the specified slot are handled as frame-specific
+ (ie, not served by the application).
+*/
+bool SfxDispatcher::IsLocked() const
+{
+ return xImp->bLocked;
+}
+
+/** With this method it can be determined if the SfxDispacher is the
+ applications dispatcher.
+
+ @return bool it is the application dispatcher.
+*/
+bool SfxDispatcher::IsAppDispatcher() const
+{
+ return !xImp->pFrame;
+}
+
+/** Helper function to check whether a slot can be executed and
+ check the execution itself
+*/
+void SfxDispatcher::Call_Impl(SfxShell& rShell, const SfxSlot &rSlot, SfxRequest &rReq, bool bRecord)
+{
+ SFX_STACK(SfxDispatcher::Call_Impl);
+
+ // The slot may be called (meaning enabled)
+ if ( !rSlot.IsMode(SfxSlotMode::FASTCALL) && !rShell.CanExecuteSlot_Impl(rSlot) && !rShell.IsConditionalFastCall(rReq) )
+ return;
+
+ if ( GetFrame() )
+ {
+ // Recording may start
+ css::uno::Reference< css::beans::XPropertySet > xSet(
+ GetFrame()->GetFrame().GetFrameInterface(),
+ css::uno::UNO_QUERY);
+
+ if ( xSet.is() )
+ {
+ css::uno::Any aProp = xSet->getPropertyValue("DispatchRecorderSupplier");
+ css::uno::Reference< css::frame::XDispatchRecorderSupplier > xSupplier;
+ css::uno::Reference< css::frame::XDispatchRecorder > xRecorder;
+ aProp >>= xSupplier;
+ if(xSupplier.is())
+ xRecorder = xSupplier->getDispatchRecorder();
+
+ if ( bRecord && xRecorder.is() && !rSlot.IsMode(SfxSlotMode::NORECORD) )
+ rReq.Record_Impl( rShell, rSlot, xRecorder, GetFrame() );
+ }
+ }
+ // Get all that is needed, because the slot may not have survived the
+ // Execute if it is a 'pseudo slot' for macros or verbs.
+ bool bAutoUpdate = rSlot.IsMode(SfxSlotMode::AUTOUPDATE);
+
+ // API-call parentheses and document-lock during the calls
+ {
+ // 'this' must respond in the Destructor
+ bool bThisDispatcherAlive = true;
+ bool *pOldInCallAliveFlag = xImp->pInCallAliveFlag;
+ xImp->pInCallAliveFlag = &bThisDispatcherAlive;
+
+ SfxExecFunc pFunc = rSlot.GetExecFnc();
+ (*pFunc)(&rShell, rReq);
+
+ // If 'this' is still alive
+ if ( bThisDispatcherAlive )
+ xImp->pInCallAliveFlag = pOldInCallAliveFlag;
+ else
+ {
+ if ( pOldInCallAliveFlag )
+ {
+ // also protect nested stack frames
+ *pOldInCallAliveFlag = false;
+ }
+
+ // do nothing after this object is dead
+ return;
+ }
+ }
+
+ if ( rReq.IsDone() )
+ {
+ SfxBindings *pBindings = GetBindings();
+
+ // When AutoUpdate update immediately
+ if ( bAutoUpdate && pBindings )
+ {
+ pBindings->Invalidate(rSlot.GetSlotId());
+ pBindings->Update(rSlot.GetSlotId());
+ }
+ }
+}
+
+void SfxDispatcher::Construct_Impl()
+{
+ xImp.reset(new SfxDispatcher_Impl);
+ xImp->bFlushed = true;
+
+ xImp->bFlushing = false;
+ xImp->bUpdated = false;
+ xImp->bLocked = false;
+ xImp->bActive = false;
+ xImp->bNoUI = false;
+ xImp->bReadOnly = false;
+ xImp->bQuiet = false;
+ xImp->pInCallAliveFlag = nullptr;
+ xImp->nFilterEnabling = SfxSlotFilterState::DISABLED;
+ xImp->nDisableFlags = SfxDisableFlags::NONE;
+
+ xImp->bInvalidateOnUnlock = false;
+
+ for (SfxObjectBars_Impl & rObjBar : xImp->aObjBars)
+ rObjBar.eId = ToolbarId::None;
+
+ xImp->xPoster = new SfxHintPoster(std::bind(&SfxDispatcher::PostMsgHandler, this, std::placeholders::_1));
+
+ xImp->aIdle.SetPriority(TaskPriority::HIGH_IDLE );
+ xImp->aIdle.SetInvokeHandler( LINK(this, SfxDispatcher, EventHdl_Impl ) );
+}
+
+SfxDispatcher::SfxDispatcher()
+{
+ Construct_Impl();
+ xImp->pFrame = nullptr;
+}
+
+/** The constructor of the SfxDispatcher class places a stack of empty
+ <SfxShell> pointers. It is not initially locked and is considered flushed.
+*/
+SfxDispatcher::SfxDispatcher(SfxViewFrame *pViewFrame)
+{
+ Construct_Impl();
+ xImp->pFrame = pViewFrame;
+}
+
+/** The destructor of the SfxDispatcher class should not be called when the
+ SfxDispatcher instance is active. It may, however, still be a <SfxShell>
+ pointer on the stack.
+*/
+SfxDispatcher::~SfxDispatcher()
+{
+ SAL_INFO("sfx.control", "Delete Dispatcher " << reinterpret_cast<sal_Int64>(this));
+ DBG_ASSERT( !xImp->bActive, "deleting active Dispatcher" );
+
+ // So that no timer by Reschedule in PlugComm strikes the LeaveRegistrations
+ xImp->aIdle.Stop();
+ xImp->xPoster->SetEventHdl( std::function<void (std::unique_ptr<SfxRequest>)>() );
+
+ // Notify the stack variables in Call_Impl
+ if ( xImp->pInCallAliveFlag )
+ *xImp->pInCallAliveFlag = false;
+
+ // Get bindings and application
+ SfxApplication *pSfxApp = SfxGetpApp();
+ SfxBindings* pBindings = GetBindings();
+
+ // When not flushed, revive the bindings
+ if (pBindings && !pSfxApp->IsDowning() && !xImp->bFlushed)
+ pBindings->DLEAVEREGISTRATIONS();
+
+ // may unregister the bindings
+ while ( pBindings )
+ {
+ if ( pBindings->GetDispatcher_Impl() == this)
+ pBindings->SetDispatcher(nullptr);
+ pBindings = pBindings->GetSubBindings_Impl();
+ }
+}
+
+/** With this method, one or more <SfxShell> are popped from the SfxDispatcher.
+ The SfxShell is marked for popping and a timer is set up. Only when the
+ timer has reached the end, the pop is actually performed
+ ( <SfxDispatcher::Flush()> ) and the <SfxBindings> is invalidated.
+ While the timer is running the opposing push and pop commands on one
+ SfxShell cancel each other out.
+
+ @param rShell the stack to take the SfxShell instance.
+ @param nMode SfxDispatcherPopFlags::POP_UNTIL
+ Also all 'rShell' of SfxShells are taken from the
+ stack.
+
+ SfxDispatcherPopFlags::POP_DELETE
+ All SfxShells actually taken from the stack
+ will be deleted.
+
+ SfxDispatcherPopFlags::PUSH (InPlace use only)
+ The Shell is pushed.
+*/
+void SfxDispatcher::Pop(SfxShell& rShell, SfxDispatcherPopFlags nMode)
+{
+ DBG_ASSERT( rShell.GetInterface(),
+ "pushing SfxShell without previous RegisterInterface()" );
+
+ bool bDelete = bool(nMode & SfxDispatcherPopFlags::POP_DELETE);
+ bool bUntil = bool(nMode & SfxDispatcherPopFlags::POP_UNTIL);
+ bool bPush = bool(nMode & SfxDispatcherPopFlags::PUSH);
+
+ SfxApplication *pSfxApp = SfxGetpApp();
+
+ SAL_INFO(
+ "sfx.control",
+ "-SfxDispatcher(" << this << (bPush ? ")::Push(" : ")::Pop(")
+ << (rShell.GetInterface()
+ ? rShell.GetInterface()->GetClassName() : SAL_STREAM(&rShell))
+ << (bDelete ? ") with delete" : ")")
+ << (bUntil ? " (up to)" : ""));
+
+ // same shell as on top of the to-do stack?
+ if(!xImp->aToDoStack.empty() && xImp->aToDoStack.front().pCluster == &rShell)
+ {
+ // cancel inverse actions
+ if ( xImp->aToDoStack.front().bPush != bPush )
+ xImp->aToDoStack.pop_front();
+ else
+ {
+ DBG_ASSERT( bPush, "SfxInterface pushed more than once" );
+ DBG_ASSERT( !bPush, "SfxInterface popped more than once" );
+ }
+ }
+ else
+ {
+ // Remember Action
+ xImp->aToDoStack.push_front( SfxToDo_Impl(bPush, bDelete, bUntil, rShell) );
+ if (xImp->bFlushed)
+ {
+ SAL_INFO("sfx.control", "Unflushed dispatcher!");
+ xImp->bFlushed = false;
+ xImp->bUpdated = false;
+
+ // Put bindings to sleep
+ SfxBindings* pBindings = GetBindings();
+ if ( pBindings )
+ pBindings->DENTERREGISTRATIONS();
+ }
+ }
+
+ if(!pSfxApp->IsDowning() && !xImp->aToDoStack.empty())
+ {
+ // No immediate update is requested
+ xImp->aIdle.Start();
+ }
+ else
+ {
+ // but to do nothing
+ xImp->aIdle.Stop();
+
+ // Bindings may wake up again
+ if(xImp->aToDoStack.empty())
+ {
+ SfxBindings* pBindings = GetBindings();
+ if ( pBindings )
+ pBindings->DLEAVEREGISTRATIONS();
+ }
+ }
+}
+
+
+/** This handler is called after <SfxDispatcher::Invalidate()> or after
+ changes on the stack (<SfxDispatcher::Push()> and <SfxDispatcher::Pop())
+
+ It flushes the Stack, if it is dirty, thus it actually executes the
+ pending Push and Pop commands.
+*/
+IMPL_LINK_NOARG( SfxDispatcher, EventHdl_Impl, Timer *, void )
+{
+ Flush();
+ Update_Impl();
+ SfxBindings* pBindings = GetBindings();
+ if ( pBindings )
+ pBindings->StartUpdate_Impl();
+}
+
+/** With this method it can be tested whether the <SfxShell> rShell is on the
+ stack, when it was flushed. This way the SfxDispatcher is not actually
+ flushed.
+
+ This method is intended among other things to make assertions possible
+ without the side effect of having to flush the SfxDispatcher.
+*/
+bool SfxDispatcher::CheckVirtualStack(const SfxShell& rShell)
+{
+ SFX_STACK(SfxDispatcher::CheckVirtualStack);
+
+ SfxShellStack_Impl aStack( xImp->aStack );
+ for(std::deque<SfxToDo_Impl>::reverse_iterator i = xImp->aToDoStack.rbegin(); i != xImp->aToDoStack.rend(); ++i)
+ {
+ if(i->bPush)
+ aStack.push_back(i->pCluster);
+ else
+ {
+ SfxShell* pPopped(nullptr);
+ do
+ {
+ DBG_ASSERT( !aStack.empty(), "popping from empty stack" );
+ pPopped = aStack.back();
+ aStack.pop_back();
+ }
+ while(i->bUntil && pPopped != i->pCluster);
+ DBG_ASSERT(pPopped == i->pCluster, "popping unpushed SfxInterface");
+ }
+ }
+
+ bool bReturn = std::find(aStack.begin(), aStack.end(), &rShell) != aStack.end();
+ return bReturn;
+}
+
+/** Determines the position of a given SfxShell in the stack of the dispatcher.
+ If possible this is flushed before.
+
+ [Return value]
+
+ sal_uInt16 == USRT_MAX
+ The SfxShell is not on this SfxDispatcher.
+
+ < USHRT_MAX
+ Position of the SfxShell on the Dispatcher
+ from the top count stating with 0.
+*/
+sal_uInt16 SfxDispatcher::GetShellLevel(const SfxShell& rShell)
+{
+ SFX_STACK(SfxDispatcher::GetShellLevel);
+ Flush();
+
+ for ( size_t n = 0; n < xImp->aStack.size(); ++n )
+ if ( *( xImp->aStack.rbegin() + n ) == &rShell )
+ return n;
+
+ return USHRT_MAX;
+}
+
+/** Returns a pointer to the <SfxShell> which is at the position nIdx
+ (from the top, last pushed is 0) on the stack.
+
+ Thus the SfxDispatcher is not flushed.
+
+ Is the stack not deep enough a NULL-Pointer is returned.
+*/
+SfxShell *SfxDispatcher::GetShell(sal_uInt16 nIdx) const
+{
+ sal_uInt16 nShellCount = xImp->aStack.size();
+ if ( nIdx < nShellCount )
+ return *(xImp->aStack.rbegin() + nIdx);
+ return nullptr;
+}
+
+/** This method returns a pointer to the <SfxBinding> Instance on which the
+ SfxDispatcher is currently bound. A SfxDispatcher is only bound to
+ the SfxBindings when it is <UI-aktiv>. If it is not UI-active,
+ a NULL-pointer is returned.
+
+ The returned pointer is only valid in the immediate context of the method
+ call.
+*/
+SfxBindings* SfxDispatcher::GetBindings() const
+{
+ if ( xImp->pFrame )
+ return &xImp->pFrame->GetBindings();
+ else
+ return nullptr;
+}
+
+/** Returns a pointer to the <SfxViewFrame> instance, which belongs to
+ this SfxDispatcher. If it is about the application dispatcher,
+ a NULL-pointer is returned.
+*/
+SfxViewFrame* SfxDispatcher::GetFrame() const
+{
+ return xImp->pFrame;
+}
+
+/** This method controls the activation of a dispatcher.
+
+ Since the application dispatcher is always active, either as a sub
+ dispatcher of the <SfxViewFrame> dispatcher or as itself, it is never
+ activated as a whole, instead only its individual <SfxShell>s at
+ <SfxDispatcher::Push(SfxShell&)>.
+
+ When activating a SfxDispatcher all of the SfxShells located on its stack
+ are called with the handler <SfxShell::Activate(bool)>, starting with
+ the lowest.
+*/
+void SfxDispatcher::DoActivate_Impl(bool bMDI)
+{
+ SFX_STACK(SfxDispatcher::DoActivate);
+ if ( bMDI )
+ {
+ SAL_INFO("sfx.control", "Activate Dispatcher " << reinterpret_cast<sal_Int64>(this));
+ DBG_ASSERT( !xImp->bActive, "Activation error" );
+
+ xImp->bActive = true;
+ xImp->bUpdated = false;
+ SfxBindings* pBindings = GetBindings();
+ if ( pBindings )
+ {
+ pBindings->SetDispatcher(this);
+ pBindings->SetActiveFrame( xImp->pFrame->GetFrame().GetFrameInterface() );
+ }
+ }
+ else
+ {
+ SAL_INFO("sfx.control", "Non-MDI-Activate Dispatcher " << reinterpret_cast<sal_Int64>(this));
+ }
+
+ if ( IsAppDispatcher() )
+ return;
+
+ for ( int i = int(xImp->aStack.size()) - 1; i >= 0; --i )
+ (*(xImp->aStack.rbegin() + i ))->DoActivate_Impl(xImp->pFrame, bMDI);
+
+ if ( bMDI && xImp->pFrame )
+ {
+ xImp->pFrame->GetFrame().GetWorkWindow_Impl()->HidePopups_Impl( false, 1 );
+ }
+
+ if(!xImp->aToDoStack.empty())
+ {
+ // No immediate update is requested
+ xImp->aIdle.Start();
+ }
+}
+
+/** This method controls the deactivation of a dispatcher.
+
+ Since the application dispatcher is always active, either as a sub
+ dispatcher of the <SfxViewFrame> dispatcher or as itself, it is never
+ deactivated as a whole, instead only its individual <SfxShell>s at
+ <SfxDispatcher::Pop(SfxShell&)>.
+
+ When deactivating a SfxDispatcher all of the SfxShells located on its stack
+ are called with the handler <SfxShell::Deactivate(bool)>, starting with
+ the lowest.
+*/
+void SfxDispatcher::DoDeactivate_Impl(bool bMDI, SfxViewFrame const * pNew)
+{
+ SFX_STACK(SfxDispatcher::DoDeactivate);
+
+ SfxApplication *pSfxApp = SfxGetpApp();
+
+ if ( bMDI )
+ {
+ SAL_INFO("sfx.control", "Deactivate Dispatcher " << this);
+ DBG_ASSERT( xImp->bActive, "Deactivate error" );
+ xImp->bActive = false;
+
+ if ( xImp->pFrame && !(xImp->pFrame->GetObjectShell()->IsInPlaceActive() ) )
+ {
+ SfxWorkWindow *pWorkWin = xImp->pFrame->GetFrame().GetWorkWindow_Impl();
+ if ( pWorkWin )
+ {
+ for (size_t n=0; n<xImp->aChildWins.size();)
+ {
+ SfxChildWindow *pWin = pWorkWin->GetChildWindow_Impl( static_cast<sal_uInt16>( xImp->aChildWins[n] & 0xFFFF ) );
+ if (!pWin || pWin->GetAlignment() == SfxChildAlignment::NOALIGNMENT)
+ xImp->aChildWins.erase(xImp->aChildWins.begin()+n);
+ else
+ n++;
+ }
+ }
+ }
+ }
+ else {
+ SAL_INFO("sfx.control", "Non-MDI-DeActivate Dispatcher " << this);
+ }
+
+ if ( IsAppDispatcher() && !pSfxApp->IsDowning() )
+ return;
+
+ for ( size_t i = 0; i < xImp->aStack.size(); ++i )
+ (*(xImp->aStack.rbegin() + i))->DoDeactivate_Impl(xImp->pFrame, bMDI);
+
+ bool bHidePopups = bMDI && xImp->pFrame;
+ if ( pNew && xImp->pFrame )
+ {
+ css::uno::Reference< css::frame::XFrame > xOldFrame =
+ pNew->GetFrame().GetFrameInterface()->getCreator();
+
+ css::uno::Reference< css::frame::XFrame > xMyFrame =
+ GetFrame()->GetFrame().GetFrameInterface();
+
+ if ( xOldFrame == xMyFrame )
+ bHidePopups = false;
+ }
+
+ if ( bHidePopups )
+ {
+ xImp->pFrame->GetFrame().GetWorkWindow_Impl()->HidePopups_Impl( true, 1 );
+ }
+
+ Flush();
+}
+
+/** This method searches in SfxDispatcher after <SfxShell> , from the Slot Id
+ nSlot currently being handled. For this, the dispatcher is first flushed.
+
+ @param nSlot the searchable Slot-Id
+ @param ppShell the SfxShell, which are currently handled the nSlot
+ @param ppSlot the SfxSlot, which are currently handled the nSlot
+
+ @return int sal_True
+ The SfxShell was found, ppShell and ppSlot are valid.
+
+ sal_False
+ The SfxShell was not found, ppShell and ppSlot are invalid.
+*/
+bool SfxDispatcher::GetShellAndSlot_Impl(sal_uInt16 nSlot, SfxShell** ppShell,
+ const SfxSlot** ppSlot, bool bOwnShellsOnly, bool bRealSlot)
+{
+ SFX_STACK(SfxDispatcher::GetShellAndSlot_Impl);
+
+ Flush();
+ SfxSlotServer aSvr;
+ if ( FindServer_(nSlot, aSvr) )
+ {
+ if ( bOwnShellsOnly && aSvr.GetShellLevel() >= xImp->aStack.size() )
+ return false;
+
+ *ppShell = GetShell(aSvr.GetShellLevel());
+ *ppSlot = aSvr.GetSlot();
+ if ( nullptr == (*ppSlot)->GetExecFnc() && bRealSlot )
+ *ppSlot = (*ppShell)->GetInterface()->GetRealSlot(*ppSlot);
+ // Check only real slots as enum slots don't have an execute function!
+ return !bRealSlot || ((nullptr != *ppSlot) && (nullptr != (*ppSlot)->GetExecFnc()) );
+ }
+
+ return false;
+}
+
+/** This method performs a request for a cached <Slot-Server>.
+
+ @param rShell to the calling <SfxShell>
+ @param rSlot to the calling <SfxSlot>
+ @param rReq function to be performed (Id and optional parameters)
+ @param eCallMode Synchronously, asynchronously or as shown in the slot
+*/
+void SfxDispatcher::Execute_(SfxShell& rShell, const SfxSlot& rSlot,
+ SfxRequest& rReq, SfxCallMode eCallMode)
+{
+ SFX_STACK(SfxDispatcher::Execute_);
+ DBG_ASSERT( !xImp->bFlushing, "recursive call to dispatcher" );
+ DBG_ASSERT( xImp->aToDoStack.empty(), "unprepared InPlace _Execute" );
+
+ if ( IsLocked() )
+ return;
+
+ if ( bool(eCallMode & SfxCallMode::ASYNCHRON) ||
+ ( (eCallMode & SfxCallMode::SYNCHRON) == SfxCallMode::SLOT &&
+ rSlot.IsMode(SfxSlotMode::ASYNCHRON) ) )
+ {
+ sal_uInt16 nShellCount = xImp->aStack.size();
+ for ( sal_uInt16 n=0; n<nShellCount; n++ )
+ {
+ if ( &rShell == *(xImp->aStack.rbegin() + n) )
+ {
+ if ( bool(eCallMode & SfxCallMode::RECORD) )
+ rReq.AllowRecording( true );
+ xImp->xPoster->Post(std::make_unique<SfxRequest>(rReq));
+ return;
+ }
+ }
+ }
+ else
+ Call_Impl( rShell, rSlot, rReq, SfxCallMode::RECORD==(eCallMode&SfxCallMode::RECORD) );
+}
+
+/** Helper function to put from rItem below the Which-ID in the pool of the
+ Item Sets rSet.
+*/
+static void MappedPut_Impl(SfxAllItemSet &rSet, const SfxPoolItem &rItem)
+{
+ // Put with mapped Which-Id if possible
+ const SfxItemPool *pPool = rSet.GetPool();
+ sal_uInt16 nWhich = rItem.Which();
+ if ( SfxItemPool::IsSlot(nWhich) )
+ nWhich = pPool->GetWhich(nWhich);
+ rSet.Put( rItem, nWhich );
+}
+
+const SfxSlot* SfxDispatcher::GetSlot( const OUString& rCommand )
+{
+ // Count the number of Shells on the linked Dispatcher
+ Flush();
+ sal_uInt16 nTotCount = xImp->aStack.size();
+
+ for ( sal_uInt16 i = 0; i < nTotCount; ++i )
+ {
+ SfxShell *pObjShell = GetShell(i);
+ SfxInterface *pIFace = pObjShell->GetInterface();
+ const SfxSlot *pSlot = pIFace->GetSlot( rCommand );
+ if ( pSlot )
+ return pSlot;
+ }
+
+ return nullptr;
+}
+
+const SfxPoolItem* SfxDispatcher::Execute(sal_uInt16 nSlot, SfxCallMode nCall,
+ SfxItemSet const * pArgs, SfxItemSet const * pInternalArgs, sal_uInt16 nModi)
+{
+ if ( IsLocked() )
+ return nullptr;
+
+ SfxShell *pShell = nullptr;
+ const SfxSlot *pSlot = nullptr;
+ if ( GetShellAndSlot_Impl( nSlot, &pShell, &pSlot, false, true ) )
+ {
+ SfxAllItemSet aSet( pShell->GetPool() );
+ if ( pArgs )
+ {
+ SfxItemIter aIter(*pArgs);
+ for ( const SfxPoolItem *pArg = aIter.GetCurItem();
+ pArg;
+ pArg = aIter.NextItem() )
+ MappedPut_Impl( aSet, *pArg );
+ }
+ SfxRequest aReq(nSlot, nCall, aSet);
+ if (pInternalArgs)
+ aReq.SetInternalArgs_Impl( *pInternalArgs );
+ aReq.SetModifier( nModi );
+
+ Execute_( *pShell, *pSlot, aReq, nCall );
+ return aReq.GetReturnValue();
+ }
+ return nullptr;
+}
+
+/** Method to execute a <SfxSlot>s over the Slot-Id.
+
+ @param nSlot the Id of the executing function
+ @param eCall SfxCallMode::SYNCRHON, ..._ASYNCHRON or ..._SLOT
+ @param pArgs Zero terminated C-Array of Parameters
+ @param pInternalArgs Zero terminated C-Array of Parameters
+
+ @return const SfxPoolItem* Pointer to the SfxPoolItem valid to the next run
+ though the Message-Loop, which contains the return
+ value.
+
+ Or a NULL-Pointer, when the function was not
+ executed (for example canceled by the user).
+*/
+const SfxPoolItem* SfxDispatcher::Execute(sal_uInt16 nSlot, SfxCallMode eCall,
+ const SfxPoolItem **pArgs, sal_uInt16 nModi, const SfxPoolItem **pInternalArgs)
+{
+ if ( IsLocked() )
+ return nullptr;
+
+ SfxShell *pShell = nullptr;
+ const SfxSlot *pSlot = nullptr;
+ if ( GetShellAndSlot_Impl( nSlot, &pShell, &pSlot, false, true ) )
+ {
+ std::unique_ptr<SfxRequest> pReq;
+ if ( pArgs && *pArgs )
+ {
+ SfxAllItemSet aSet( pShell->GetPool() );
+ for ( const SfxPoolItem **pArg = pArgs; *pArg; ++pArg )
+ MappedPut_Impl( aSet, **pArg );
+ pReq.reset(new SfxRequest( nSlot, eCall, aSet ));
+ }
+ else
+ pReq.reset(new SfxRequest( nSlot, eCall, pShell->GetPool() ));
+ pReq->SetModifier( nModi );
+ if( pInternalArgs && *pInternalArgs)
+ {
+ SfxAllItemSet aSet( SfxGetpApp()->GetPool() );
+ for ( const SfxPoolItem **pArg = pInternalArgs; *pArg; ++pArg )
+ aSet.Put( **pArg );
+ pReq->SetInternalArgs_Impl( aSet );
+ }
+ Execute_( *pShell, *pSlot, *pReq, eCall );
+ const SfxPoolItem* pRet = pReq->GetReturnValue();
+ return pRet;
+ }
+ return nullptr;
+}
+
+/** Method to execute a <SfxSlot>s over the Slot-Id.
+
+ @param nSlot the Id of the executing function
+ @param eCall SfxCallMode::SYNCRHON, ..._ASYNCHRON or ..._SLOT
+ @param rArgs <SfxItemSet> with the parameters
+
+ @return const SfxPoolItem* Pointer to the SfxPoolItem valid to the next run
+ though the Message-Loop, which contains the return
+ value.
+
+ Or a NULL-Pointer, when the function was not
+ executed (for example canceled by the user).
+*/
+const SfxPoolItem* SfxDispatcher::Execute(sal_uInt16 nSlot, SfxCallMode eCall,
+ const SfxItemSet &rArgs)
+{
+ if ( IsLocked() )
+ return nullptr;
+
+ SfxShell *pShell = nullptr;
+ const SfxSlot *pSlot = nullptr;
+ if ( GetShellAndSlot_Impl( nSlot, &pShell, &pSlot, false, true ) )
+ {
+ SfxAllItemSet aSet( pShell->GetPool() );
+ SfxItemIter aIter(rArgs);
+ for ( const SfxPoolItem *pArg = aIter.GetCurItem();
+ pArg;
+ pArg = aIter.NextItem() )
+ MappedPut_Impl( aSet, *pArg );
+ SfxRequest aReq( nSlot, eCall, aSet );
+ aReq.SetModifier( 0 );
+ Execute_( *pShell, *pSlot, aReq, eCall );
+ return aReq.GetReturnValue();
+ }
+ return nullptr;
+}
+
+/** Method to execute a <SfxSlot>s over the Slot-Id.
+
+ [Note]
+
+ The parameters are copied, can therefore be passed on as the address
+ of stack objects.
+
+ @param nSlot the Id of the executing function
+ @param eCall SfxCallMode::SYNCRHON, ..._ASYNCHRON or ..._SLOT
+ @param args list of SfxPoolItem arguments
+
+ @return Pointer to the SfxPoolItem valid to the next run
+ though the Message-Loop, which contains the return
+ value.
+
+ Or a NULL-Pointer, when the function was not
+ executed (for example canceled by the user).
+
+ [Example]
+
+ pDispatcher->Execute( SID_OPENDOCUMENT, SfxCallMode::SYNCHRON,
+ { &SfxStringItem( SID_FILE_NAME, "\\tmp\\temp.sdd" ),
+ &SfxStringItem( SID_FILTER_NAME, "StarDraw Presentation" ),
+ &SfxBoolItem( SID_DOC_READONLY, sal_False ),
+ });
+*/
+const SfxPoolItem* SfxDispatcher::ExecuteList(sal_uInt16 nSlot, SfxCallMode eCall,
+ std::initializer_list<SfxPoolItem const*> args,
+ std::initializer_list<SfxPoolItem const*> internalargs)
+{
+ if ( IsLocked() )
+ return nullptr;
+
+ SfxShell *pShell = nullptr;
+ const SfxSlot *pSlot = nullptr;
+ if ( GetShellAndSlot_Impl( nSlot, &pShell, &pSlot, false, true ) )
+ {
+ SfxAllItemSet aSet( pShell->GetPool() );
+
+ for (const SfxPoolItem *pArg : args)
+ {
+ assert(pArg);
+ MappedPut_Impl( aSet, *pArg );
+ }
+
+ SfxRequest aReq(nSlot, eCall, aSet);
+
+ if (internalargs.begin() != internalargs.end())
+ {
+ SfxAllItemSet aInternalSet(SfxGetpApp()->GetPool());
+ for (const SfxPoolItem *pArg : internalargs)
+ {
+ assert(pArg);
+ aInternalSet.Put(*pArg);
+ }
+ aReq.SetInternalArgs_Impl(aInternalSet);
+ }
+
+ Execute_( *pShell, *pSlot, aReq, eCall );
+ return aReq.GetReturnValue();
+ }
+ return nullptr;
+}
+
+/** Helper method to receive the asynchronously executed <SfxRequest>s.
+*/
+void SfxDispatcher::PostMsgHandler(std::unique_ptr<SfxRequest> pReq)
+{
+ DBG_ASSERT( !xImp->bFlushing, "recursive call to dispatcher" );
+ SFX_STACK(SfxDispatcher::PostMsgHandler);
+
+ // Has also the Pool not yet died?
+ if ( pReq->IsCancelled() )
+ return;
+
+ if ( !IsLocked() )
+ {
+ Flush();
+ SfxSlotServer aSvr;
+ if ( FindServer_(pReq->GetSlot(), aSvr ) ) // HACK(x), whatever that was supposed to mean
+ {
+ const SfxSlot *pSlot = aSvr.GetSlot();
+ SfxShell *pSh = GetShell(aSvr.GetShellLevel());
+
+ // When the pSlot is a "Pseudoslot" for macros or Verbs, it can
+ // be destroyed in the Call_Impl, thus do not use it anymore!
+ pReq->SetSynchronCall( false );
+ Call_Impl( *pSh, *pSlot, *pReq, pReq->AllowsRecording() ); //! why bRecord?
+ }
+ }
+ else
+ {
+ if ( xImp->bLocked )
+ xImp->aReqArr.emplace_back(std::move(pReq));
+ else
+ xImp->xPoster->Post(std::move(pReq));
+ }
+}
+
+void SfxDispatcher::SetMenu_Impl()
+{
+#if HAVE_FEATURE_DESKTOP
+ if ( !xImp->pFrame )
+ return;
+
+ SfxViewFrame* pTop = xImp->pFrame->GetTopViewFrame();
+ if ( !pTop || pTop->GetBindings().GetDispatcher() != this )
+ return;
+
+ SfxFrame& rFrame = pTop->GetFrame();
+ if ( !rFrame.IsMenuBarOn_Impl() )
+ return;
+
+ css::uno::Reference < css::beans::XPropertySet > xPropSet( rFrame.GetFrameInterface(), css::uno::UNO_QUERY );
+ if ( xPropSet.is() )
+ {
+ css::uno::Reference< css::frame::XLayoutManager > xLayoutManager;
+ css::uno::Any aValue = xPropSet->getPropertyValue("LayoutManager");
+ aValue >>= xLayoutManager;
+ if ( xLayoutManager.is() )
+ {
+ OUString aMenuBarURL( "private:resource/menubar/menubar" );
+ if ( !xLayoutManager->isElementVisible( aMenuBarURL ) )
+ xLayoutManager->createElement( aMenuBarURL );
+ }
+ }
+#endif
+}
+
+void SfxDispatcher::Update_Impl( bool bForce )
+{
+ SFX_STACK(SfxDispatcher::Update_Impl);
+
+ Flush();
+
+ if ( !xImp->pFrame )
+ return;
+
+ bool bUpdate = bForce;
+ if ( xImp->pFrame )
+ {
+ SfxWorkWindow *pWork = xImp->pFrame->GetFrame().GetWorkWindow_Impl();
+ SfxDispatcher *pAct = pWork->GetBindings().GetDispatcher_Impl();
+ if (pAct == this)
+ {
+ if ( !bUpdate )
+ bUpdate = !xImp->bUpdated;
+ xImp->bUpdated = true;
+ }
+ }
+
+ if ( !bUpdate || xImp->pFrame->GetFrame().IsClosing_Impl() )
+ return;
+
+ SfxViewFrame* pTop = xImp->pFrame ? xImp->pFrame->GetTopViewFrame() : nullptr;
+ bool bUIActive = pTop && pTop->GetBindings().GetDispatcher() == this;
+
+ if ( !bUIActive && pTop && GetBindings() == &pTop->GetBindings() )
+ // keep own tools internally for collecting
+ GetBindings()->GetDispatcher()->xImp->bUpdated = false;
+
+ css::uno::Reference< css::frame::XFrame > xFrame;
+ SfxBindings* pBindings = GetBindings();
+ if (pBindings)
+ {
+ pBindings->DENTERREGISTRATIONS();
+ xFrame = pBindings->GetActiveFrame();
+ }
+ css::uno::Reference< css::beans::XPropertySet > xPropSet( xFrame, css::uno::UNO_QUERY );
+ css::uno::Reference< css::frame::XLayoutManager > xLayoutManager;
+ if ( xPropSet.is() )
+ {
+ try
+ {
+ css::uno::Any aValue = xPropSet->getPropertyValue("LayoutManager");
+ aValue >>= xLayoutManager;
+ }
+ catch (const css::uno::Exception&)
+ {
+ }
+ }
+
+ if ( xLayoutManager.is() )
+ xLayoutManager->lock();
+
+ bool bIsIPActive = xImp->pFrame && xImp->pFrame->GetObjectShell()->IsInPlaceActive();
+ SfxInPlaceClient *pClient = xImp->pFrame ? xImp->pFrame->GetViewShell()->GetUIActiveClient() : nullptr;
+ if ( bUIActive && /* !bIsIPActive && */ ( !pClient || !pClient->IsObjectUIActive() ) )
+ SetMenu_Impl();
+
+ SfxWorkWindow *pWorkWin = xImp->pFrame->GetFrame().GetWorkWindow_Impl();
+ pWorkWin->ResetStatusBar_Impl();
+
+ {
+ SfxWorkWindow *pWork = xImp->pFrame->GetFrame().GetWorkWindow_Impl();
+ SfxDispatcher *pAct = pWork->GetBindings().GetDispatcher_Impl();
+ if (pAct == this)
+ {
+ pWork->ResetObjectBars_Impl();
+ pWork->ResetChildWindows_Impl();
+ }
+ }
+
+ bool bIsActive = false;
+ SfxDispatcher *pActDispat = pWorkWin->GetBindings().GetDispatcher_Impl();
+ if ( !bIsActive && this == pActDispat )
+ bIsActive = true;
+
+ Update_Impl_( bUIActive, !bIsIPActive, bIsIPActive, pWorkWin );
+ if (bUIActive || bIsActive)
+ pWorkWin->UpdateObjectBars_Impl();
+
+ if ( pBindings )
+ pBindings->DLEAVEREGISTRATIONS();
+
+ if ( xLayoutManager.is() )
+ xLayoutManager->unlock();
+
+ if ( SfxViewShell::Current() && SfxViewShell::Current()->GetDispatcher() )
+ {
+ const SfxPoolItem *pItem;
+ SfxViewShell::Current()->GetDispatcher()->QueryState(SID_NOTEBOOKBAR, pItem);
+ }
+}
+
+void SfxDispatcher::Update_Impl_( bool bUIActive, bool bIsMDIApp, bool bIsIPOwner, SfxWorkWindow *pTaskWin )
+{
+ SfxWorkWindow *pWorkWin = xImp->pFrame->GetFrame().GetWorkWindow_Impl();
+ bool bIsActive = false;
+ SfxDispatcher *pActDispat = pWorkWin->GetBindings().GetDispatcher_Impl();
+ if ( pActDispat && !bIsActive )
+ {
+ if ( this == pActDispat )
+ bIsActive = true;
+ }
+
+ for (SfxObjectBars_Impl & rObjBar : xImp->aObjBars)
+ rObjBar.eId = ToolbarId::None;
+ xImp->aChildWins.clear();
+
+ // bQuiet: own shells aren't considered for UI and SlotServer
+ // bNoUI: own Shells aren't considered forms UI
+ if ( xImp->bQuiet || xImp->bNoUI || (xImp->pFrame && xImp->pFrame->GetObjectShell()->IsPreview()) )
+ return;
+
+ StatusBarId eStatBarId = StatusBarId::None;
+
+ SfxSlotPool* pSlotPool = &SfxSlotPool::GetSlotPool( GetFrame() );
+ sal_uInt16 nTotCount = xImp->aStack.size();
+ for ( sal_uInt16 nShell = nTotCount; nShell > 0; --nShell )
+ {
+ SfxShell *pShell = GetShell( nShell-1 );
+ SfxInterface *pIFace = pShell->GetInterface();
+
+ // don't consider shells if "Hidden" or "Quiet"
+ bool bReadOnlyShell = IsReadOnlyShell_Impl( nShell-1 );
+ sal_uInt16 nNo;
+ for ( nNo = 0; pIFace && nNo<pIFace->GetObjectBarCount(); ++nNo )
+ {
+ sal_uInt16 nPos = pIFace->GetObjectBarPos(nNo);
+ SfxVisibilityFlags nFlags = pIFace->GetObjectBarFlags(nNo);
+ if ( bReadOnlyShell && !( nFlags & SfxVisibilityFlags::ReadonlyDoc ) )
+ continue;
+
+ // check whether toolbar needs activation of a special feature
+ SfxShellFeature nFeature = pIFace->GetObjectBarFeature(nNo);
+ if ((nFeature != SfxShellFeature::NONE) && !pShell->HasUIFeature(nFeature))
+ continue;
+
+ // check for toolboxes that are exclusively for a viewer
+ if ( xImp->pFrame)
+ {
+ bool bViewerTbx( nFlags & SfxVisibilityFlags::Viewer );
+ SfxObjectShell* pSh = xImp->pFrame->GetObjectShell();
+ const SfxBoolItem* pItem = SfxItemSet::GetItem<SfxBoolItem>(pSh->GetMedium()->GetItemSet(), SID_VIEWONLY, false);
+ bool bIsViewer = pItem && pItem->GetValue();
+ if ( bIsViewer != bViewerTbx )
+ continue;
+ }
+
+ // always register toolbars, allows to switch them on
+ bool bVisible = pIFace->IsObjectBarVisible(nNo);
+ if ( !bVisible )
+ nFlags = SfxVisibilityFlags::Invisible;
+
+ SfxObjectBars_Impl& rBar = xImp->aObjBars[nPos];
+ rBar.nPos = nPos;
+ rBar.nFlags = nFlags;
+ rBar.eId = pIFace->GetObjectBarId(nNo);
+
+ if ( bUIActive || bIsActive )
+ {
+ pWorkWin->SetObjectBar_Impl(nPos, nFlags, rBar.eId);
+ }
+
+ if ( !bVisible )
+ rBar.eId = ToolbarId::None;
+ }
+
+ for ( nNo=0; pIFace && nNo<pIFace->GetChildWindowCount(); nNo++ )
+ {
+ sal_uInt32 nId = pIFace->GetChildWindowId(nNo);
+ const SfxSlot *pSlot = pSlotPool->GetSlot( static_cast<sal_uInt16>(nId) );
+ SAL_WARN_IF( !pSlot, "sfx.control", "Childwindow slot missing: " << nId );
+ if ( bReadOnlyShell )
+ {
+ // only show ChildWindows if their slot is allowed for readonly documents
+ if ( pSlot && !pSlot->IsMode( SfxSlotMode::READONLYDOC ) )
+ continue;
+ }
+
+ SfxShellFeature nFeature = pIFace->GetChildWindowFeature(nNo);
+ if ((nFeature != SfxShellFeature::NONE) && !pShell->HasUIFeature(nFeature))
+ continue;
+
+ // slot decides whether a ChildWindow is shown when document is OLE server or OLE client
+ SfxVisibilityFlags nMode = SfxVisibilityFlags::Standard;
+ if( pSlot )
+ {
+ if ( pSlot->IsMode(SfxSlotMode::CONTAINER) )
+ {
+ if ( pWorkWin->IsVisible_Impl( SfxVisibilityFlags::Client ) )
+ nMode |= SfxVisibilityFlags::Client;
+ }
+ else
+ {
+ if ( pWorkWin->IsVisible_Impl( SfxVisibilityFlags::Server ) )
+ nMode |= SfxVisibilityFlags::Server;
+ }
+ }
+
+ if ( bUIActive || bIsActive )
+ pWorkWin->SetChildWindowVisible_Impl( nId, true, nMode );
+ if ( bUIActive || bIsActive || !pWorkWin->IsFloating( static_cast<sal_uInt16>( nId & 0xFFFF ) ) )
+ xImp->aChildWins.push_back( nId );
+ }
+
+ if ( bIsMDIApp || bIsIPOwner )
+ {
+ StatusBarId eId = pIFace ? pIFace->GetStatusBarId() : StatusBarId::None;
+ if (eId != StatusBarId::None)
+ eStatBarId = eId;
+ }
+ }
+
+ for ( sal_uInt16 nPos=0; nPos<SFX_OBJECTBAR_MAX; nPos++ )
+ {
+ SfxObjectBars_Impl& rFixed = xImp->aFixedObjBars[nPos];
+ if (rFixed.eId != ToolbarId::None)
+ {
+ SfxObjectBars_Impl& rBar = xImp->aObjBars[nPos];
+ rBar = rFixed;
+ pWorkWin->SetObjectBar_Impl(rFixed.nPos, rFixed.nFlags,
+ rFixed.eId);
+ }
+ }
+
+ if ( !pTaskWin || ( !bIsMDIApp && !bIsIPOwner ) )
+ return;
+
+ bool bIsTaskActive = false;
+
+ SfxDispatcher *pActDispatcher = pTaskWin->GetBindings().GetDispatcher_Impl();
+ if ( pActDispatcher && !bIsTaskActive )
+ {
+ if ( this == pActDispatcher )
+ bIsTaskActive = true;
+ }
+
+ if (bIsTaskActive && eStatBarId != StatusBarId::None && xImp->pFrame)
+ {
+ // internal frames also may control statusbar
+ xImp->pFrame->GetFrame().GetWorkWindow_Impl()->SetStatusBar_Impl(eStatBarId);
+ }
+}
+
+/** Helper method to execute the outstanding push and pop commands.
+*/
+void SfxDispatcher::FlushImpl()
+{
+ SFX_STACK(SfxDispatcher::FlushImpl);
+
+ SAL_INFO("sfx.control", "Flushing dispatcher!");
+
+ xImp->aIdle.Stop();
+
+ xImp->bFlushing = !xImp->bFlushing;
+ if ( !xImp->bFlushing )
+ {
+ xImp->bFlushing = true;
+ return;
+ }
+
+ SfxApplication *pSfxApp = SfxGetpApp();
+
+ // Re-build the true stack in the first round
+ std::deque<SfxToDo_Impl> aToDoCopy;
+ bool bModify = false;
+ for(std::deque<SfxToDo_Impl>::reverse_iterator i = xImp->aToDoStack.rbegin(); i != xImp->aToDoStack.rend(); ++i)
+ {
+ bModify = true;
+
+ if(i->bPush)
+ {
+ // Actually push
+ DBG_ASSERT( std::find(xImp->aStack.begin(), xImp->aStack.end(), i->pCluster) == xImp->aStack.end(),
+ "pushed SfxShell already on stack" );
+ xImp->aStack.push_back(i->pCluster);
+ i->pCluster->SetDisableFlags(xImp->nDisableFlags);
+
+ // Mark the moved shell
+ aToDoCopy.push_front(*i);
+ }
+ else
+ {
+ // Actually pop
+ SfxShell* pPopped = nullptr;
+ bool bFound = false;
+ do
+ {
+ DBG_ASSERT( !xImp->aStack.empty(), "popping from empty stack" );
+ pPopped = xImp->aStack.back();
+ xImp->aStack.pop_back();
+ pPopped->SetDisableFlags( SfxDisableFlags::NONE );
+ bFound = (pPopped == i->pCluster);
+
+ // Mark the moved Shell
+ aToDoCopy.push_front(SfxToDo_Impl(false, i->bDelete, false, *pPopped));
+ }
+ while(i->bUntil && !bFound);
+ DBG_ASSERT( bFound, "wrong SfxShell popped" );
+ }
+ }
+ xImp->aToDoStack.clear();
+
+ // Invalidate bindings, if possible
+ if ( !pSfxApp->IsDowning() )
+ {
+ InvalidateBindings_Impl( bModify );
+ }
+
+ xImp->bFlushing = false;
+ xImp->bUpdated = false; // not only when bModify, if Doc/Template-Config
+ xImp->bFlushed = true;
+ SAL_INFO("sfx.control", "Successfully flushed dispatcher!");
+
+ //fdo#70703 FlushImpl may call back into itself so use aToDoCopyStack to talk
+ //to outer levels of ourself. If DoActivate_Impl/DoDeactivate_Impl deletes
+ //an entry, then they will walk back up aToDoCopyStack and set outer
+ //levels's entries to bDeleted
+ xImp->aToDoCopyStack.push_back(aToDoCopy);
+ std::deque<SfxToDo_Impl>& rToDoCopy = xImp->aToDoCopyStack.back();
+ // Activate the Shells and possible delete them in the 2nd round
+ for(std::deque<SfxToDo_Impl>::reverse_iterator i = rToDoCopy.rbegin(); i != rToDoCopy.rend(); ++i)
+ {
+ if (i->bDeleted)
+ continue;
+ if (!xImp->bActive)
+ continue;
+ if (i->bPush)
+ i->pCluster->DoActivate_Impl(xImp->pFrame, true);
+ else
+ i->pCluster->DoDeactivate_Impl(xImp->pFrame, true);
+ }
+
+ aToDoCopy = xImp->aToDoCopyStack.back();
+ xImp->aToDoCopyStack.pop_back();
+
+ for(std::deque<SfxToDo_Impl>::reverse_iterator i = aToDoCopy.rbegin(); i != aToDoCopy.rend(); ++i)
+ {
+ if (i->bDelete && !i->bDeleted)
+ {
+ if (!xImp->aToDoCopyStack.empty())
+ {
+ //fdo#70703 if there is an outer FlushImpl then inform it that
+ //we have deleted this cluster
+ for (auto & elem : xImp->aToDoCopyStack)
+ {
+ for (auto & subelem : elem)
+ {
+ if (subelem.pCluster == i->pCluster)
+ subelem.bDeleted = true;
+ }
+ }
+ }
+ delete i->pCluster;
+ }
+ }
+ bool bAwakeBindings = !aToDoCopy.empty();
+ if( bAwakeBindings )
+ aToDoCopy.clear();
+
+ // If more changes have occurred on the stack when
+ // Activate/Deactivate/Delete:
+ if (!xImp->bFlushed)
+ // If Push/Pop has been called by someone, then also EnterReg was called!
+ FlushImpl();
+
+ if( bAwakeBindings && GetBindings() )
+ GetBindings()->DLEAVEREGISTRATIONS();
+
+ for (SfxObjectBars_Impl & rFixedObjBar : xImp->aFixedObjBars)
+ rFixedObjBar.eId = ToolbarId::None;
+
+ SAL_INFO("sfx.control", "SfxDispatcher(" << this << ")::Flush() done");
+}
+
+/** With this method a filter set, the target slots can be enabled or disabled.
+ The passed array must be retained until the destructor or the next
+ <SetSlotFilter()>, it is not deleted from the dispatcher, so it can thus be
+ static.
+
+ In read-only documents the quasi ReadOnlyDoc Flag of slots can be
+ overturned by the use of 'bEnable == 2', so this will be displayed again.
+ On the other slots it has no effect.
+
+ // HACK(here should be used an enum) ???
+ @param nEnable 1==true: only enable specified slots, disable all other
+ 0==false: disable specified slots, first enable all other
+ @param nCount Number of SIDs in the following Array
+ @param pSIDs sorted Array of 'nCount' SIDs
+
+ [Example]
+
+ Targeted disabling of Slots 1, 2 and 3:
+
+ static sal_uInt16 const pSIDs[] = { 1, 2, 3 };
+ pDisp->SetSlotFilter( sal_False, sizeof(pSIDs)/sizeof(sal_uInt16), pSIDs );
+
+ only permit Slots 5, 6 and 7:
+
+ static sal_uInt16 const pSIDs[] = { 5, 6, 7 };
+ pDisp->SetSlotFilter( sal_True, sizeof(pSIDs)/sizeof(sal_uInt16), pSIDs );
+
+ Turn-off Filter:
+
+ pDisp->SetSlotFilter();
+*/
+void SfxDispatcher::SetSlotFilter(SfxSlotFilterState nEnable,
+ o3tl::span<sal_uInt16 const> pSIDs)
+{
+#ifdef DBG_UTIL
+ // Check Array
+ for ( std::size_t n = 1; n < pSIDs.size(); ++n )
+ DBG_ASSERT( pSIDs[n] > pSIDs[n-1], "SetSlotFilter: SIDs not sorted" );
+#endif
+
+ xImp->nFilterEnabling = nEnable;
+ xImp->pFilterSIDs = pSIDs;
+
+ GetBindings()->InvalidateAll(true);
+}
+
+extern "C" {
+
+static int SfxCompareSIDs_Impl(const void* pSmaller, const void* pBigger)
+{
+ return static_cast<tools::Long>(*static_cast<sal_uInt16 const *>(pSmaller)) - static_cast<tools::Long>(*static_cast<sal_uInt16 const *>(pBigger));
+}
+
+}
+
+/** Searches for 'nSID' in the Filter set by <SetSlotFilter()> and
+ returns sal_True, if the SIDis allowed, or sal_False, if it is
+ disabled by the Filter.
+
+ @return 0 => disabled
+ 1 => enabled
+ 2 => enabled even if ReadOnlyDoc
+*/
+SfxSlotFilterState SfxDispatcher::IsSlotEnabledByFilter_Impl( sal_uInt16 nSID ) const
+{
+ // no filter?
+ if ( xImp->pFilterSIDs.empty() )
+ // => all SIDs allowed
+ return SfxSlotFilterState::ENABLED;
+
+ // search
+ bool bFound = nullptr != bsearch( &nSID, xImp->pFilterSIDs.data(), xImp->pFilterSIDs.size(),
+ sizeof(sal_uInt16), SfxCompareSIDs_Impl );
+
+ // even if ReadOnlyDoc
+ if ( SfxSlotFilterState::ENABLED_READONLY == xImp->nFilterEnabling )
+ return bFound ? SfxSlotFilterState::ENABLED_READONLY : SfxSlotFilterState::ENABLED;
+ // Otherwise after Negative/Positive Filter
+ else if ( SfxSlotFilterState::ENABLED == xImp->nFilterEnabling )
+ return bFound ? SfxSlotFilterState::ENABLED : SfxSlotFilterState::DISABLED;
+ else
+ return bFound ? SfxSlotFilterState::DISABLED : SfxSlotFilterState::ENABLED;
+}
+
+/** This helper method searches for the <Slot-Server> which currently serves
+ the nSlot. As the result, rServe is filled accordingly.
+
+ If known the SfxInterface which is currently served by nSlot can be
+ passed along.
+
+ The SfxDispatcher is flushed while searching for nSlot.
+
+ @param nSlot Slot-Id to search for
+ @param rServer <SfxSlotServer>-Instance to fill
+
+ @return true
+ The Slot was found, rServer is valid.
+
+ false
+ The Slot is currently not served, rServer is invalid.
+*/
+bool SfxDispatcher::FindServer_(sal_uInt16 nSlot, SfxSlotServer& rServer)
+{
+ SFX_STACK(SfxDispatcher::FindServer_);
+
+ // Dispatcher locked? (nevertheless let SID_HELP_PI through)
+ if ( IsLocked() )
+ {
+ xImp->bInvalidateOnUnlock = true;
+ return false;
+ }
+
+ // Count the number of Shells in the linked dispatchers.
+ Flush();
+ sal_uInt16 nTotCount = xImp->aStack.size();
+
+ // Verb-Slot?
+ if (nSlot >= SID_VERB_START && nSlot <= SID_VERB_END)
+ {
+ for ( sal_uInt16 nShell = 0;; ++nShell )
+ {
+ SfxShell *pSh = GetShell(nShell);
+ if ( pSh == nullptr )
+ return false;
+ if ( dynamic_cast< const SfxViewShell *>( pSh ) != nullptr )
+ {
+ const SfxSlot* pSlot = pSh->GetVerbSlot_Impl(nSlot);
+ if ( pSlot )
+ {
+ rServer.SetShellLevel(nShell);
+ rServer.SetSlot( pSlot );
+ return true;
+ }
+ }
+ }
+ }
+
+ // SID check against set filter
+ SfxSlotFilterState nSlotEnableMode = SfxSlotFilterState::DISABLED;
+ if ( xImp->pFrame )
+ {
+ nSlotEnableMode = IsSlotEnabledByFilter_Impl( nSlot );
+ if ( SfxSlotFilterState::DISABLED == nSlotEnableMode )
+ return false;
+ }
+
+ // In Quiet-Mode only Parent-Dispatcher
+ if ( xImp->bQuiet )
+ {
+ return false;
+ }
+
+ bool bReadOnly = ( SfxSlotFilterState::ENABLED_READONLY != nSlotEnableMode && xImp->bReadOnly );
+
+ // search through all the shells of the chained dispatchers
+ // from top to bottom
+ sal_uInt16 nFirstShell = 0;
+ for ( sal_uInt16 i = nFirstShell; i < nTotCount; ++i )
+ {
+ SfxShell *pObjShell = GetShell(i);
+ SfxInterface *pIFace = pObjShell->GetInterface();
+ const SfxSlot *pSlot = pIFace->GetSlot(nSlot);
+
+ if ( pSlot && pSlot->nDisableFlags != SfxDisableFlags::NONE &&
+ ( static_cast<int>(pSlot->nDisableFlags) & static_cast<int>(pObjShell->GetDisableFlags()) ) != 0 )
+ return false;
+
+ if ( pSlot && !( pSlot->nFlags & SfxSlotMode::READONLYDOC ) && bReadOnly )
+ return false;
+
+ if ( pSlot )
+ {
+ // Slot belongs to Container?
+ bool bIsContainerSlot = pSlot->IsMode(SfxSlotMode::CONTAINER);
+ bool bIsInPlace = xImp->pFrame && xImp->pFrame->GetObjectShell()->IsInPlaceActive();
+
+ // Shell belongs to Server?
+ // AppDispatcher or IPFrame-Dispatcher
+ bool bIsServerShell = !xImp->pFrame || bIsInPlace;
+
+ // Of course ShellServer-Slots are also executable even when it is
+ // executed on a container dispatcher without an IPClient.
+ if ( !bIsServerShell )
+ {
+ SfxViewShell *pViewSh = xImp->pFrame->GetViewShell();
+ bIsServerShell = !pViewSh || !pViewSh->GetUIActiveClient();
+ }
+
+ // Shell belongs to Container?
+ // AppDispatcher or no IPFrameDispatcher
+ bool bIsContainerShell = !xImp->pFrame || !bIsInPlace;
+ // Shell and Slot match
+ if ( !( ( bIsContainerSlot && bIsContainerShell ) ||
+ ( !bIsContainerSlot && bIsServerShell ) ) )
+ pSlot = nullptr;
+ }
+
+ if ( pSlot )
+ {
+ rServer.SetSlot(pSlot);
+ rServer.SetShellLevel(i);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/** Helper method to obtain the status of the <Slot-Server>s rSvr.
+ The required slots IDs (partly converted to Which-IDs of the pool)
+ must be present in rstate.
+
+ The SfxDispatcher is flushed before the query.
+
+ @param rSvr Slot-Server to query
+ @param rState SfxItemSet to be filled
+ @param pRealSlot The actual Slot if possible
+*/
+bool SfxDispatcher::FillState_(const SfxSlotServer& rSvr, SfxItemSet& rState,
+ const SfxSlot* pRealSlot)
+{
+ SFX_STACK(SfxDispatcher::FillState_);
+
+ const SfxSlot *pSlot = rSvr.GetSlot();
+ if ( pSlot && IsLocked() )
+ {
+ xImp->bInvalidateOnUnlock = true;
+ return false;
+ }
+
+ if ( pSlot )
+ {
+ DBG_ASSERT(xImp->bFlushed,
+ "Dispatcher not flushed after retrieving slot servers!");
+ if (!xImp->bFlushed)
+ return false;
+
+ // Determine the object and call the Message of this object
+ SfxShell *pSh = GetShell(rSvr.GetShellLevel());
+ DBG_ASSERT(pSh, "ObjectShell not found");
+
+ SfxStateFunc pFunc;
+
+ if (pRealSlot)
+ pFunc = pRealSlot->GetStateFnc();
+ else
+ pFunc = pSlot->GetStateFnc();
+
+ (*pFunc)(pSh, rState);
+#ifdef DBG_UTIL
+ // To examine the conformity of IDL (SlotMap) and current Items
+ if ( rState.Count() )
+ {
+ SfxInterface *pIF = pSh->GetInterface();
+ SfxItemIter aIter( rState );
+ for ( const SfxPoolItem *pItem = aIter.GetCurItem();
+ pItem;
+ pItem = aIter.NextItem() )
+ {
+ if ( !IsInvalidItem(pItem) && !pItem->IsVoidItem() )
+ {
+ sal_uInt16 nSlotId = rState.GetPool()->GetSlotId(pItem->Which());
+ SAL_INFO_IF(
+ typeid(pItem) != *pIF->GetSlot(nSlotId)->GetType()->Type(),
+ "sfx.control",
+ "item-type unequal to IDL (=> no BASIC) with SID: "
+ << nSlotId << " in " << pIF->GetClassName());
+ }
+ }
+ }
+#endif
+
+ return true;
+ }
+
+ return false;
+}
+
+void SfxDispatcher::ExecutePopup( vcl::Window *pWin, const Point *pPos )
+{
+ SfxDispatcher &rDisp = *SfxGetpApp()->GetDispatcher_Impl();
+ sal_uInt16 nShLevel = 0;
+ SfxShell *pSh;
+
+ if ( rDisp.xImp->bQuiet )
+ nShLevel = rDisp.xImp->aStack.size();
+
+ for ( pSh = rDisp.GetShell(nShLevel); pSh; ++nShLevel, pSh = rDisp.GetShell(nShLevel) )
+ {
+ const OUString& rResName = pSh->GetInterface()->GetPopupMenuName();
+ if ( !rResName.isEmpty() )
+ {
+ rDisp.ExecutePopup( rResName, pWin, pPos );
+ return;
+ }
+ }
+}
+
+namespace {
+
+boost::property_tree::ptree fillPopupMenu(Menu* pMenu)
+{
+ // Activate this menu first
+ pMenu->HandleMenuActivateEvent(pMenu);
+ pMenu->HandleMenuDeActivateEvent(pMenu);
+
+ boost::property_tree::ptree aTree;
+ // If last item inserted is some valid text
+ bool bIsLastItemText = false;
+ sal_uInt16 nCount = pMenu->GetItemCount();
+ for (sal_uInt16 nPos = 0; nPos < nCount; nPos++)
+ {
+ boost::property_tree::ptree aItemTree;
+ const MenuItemType aItemType = pMenu->GetItemType(nPos);
+
+ if (aItemType == MenuItemType::DONTKNOW)
+ continue;
+
+ if (aItemType == MenuItemType::SEPARATOR)
+ {
+ if (bIsLastItemText)
+ aItemTree.put("type", "separator");
+ bIsLastItemText = false;
+ }
+ else
+ {
+ const sal_uInt16 nItemId = pMenu->GetItemId(nPos);
+ OUString aCommandURL = pMenu->GetItemCommand(nItemId);
+
+ if (aCommandURL.isEmpty())
+ {
+ const SfxSlot *pSlot = SFX_SLOTPOOL().GetSlot(nItemId);
+ if (pSlot)
+ aCommandURL = pSlot->GetCommandString();
+ }
+
+ const OUString aItemText = pMenu->GetItemText(nItemId);
+ Menu* pPopupSubmenu = pMenu->GetPopupMenu(nItemId);
+
+ if (!aItemText.isEmpty())
+ aItemTree.put("text", aItemText.toUtf8().getStr());
+
+ if (pPopupSubmenu)
+ {
+ boost::property_tree::ptree aSubmenu = ::fillPopupMenu(pPopupSubmenu);
+ if (aSubmenu.empty())
+ continue;
+
+ aItemTree.put("type", "menu");
+ if (!aCommandURL.isEmpty())
+ aItemTree.put("command", aCommandURL.toUtf8().getStr());
+ aItemTree.push_back(std::make_pair("menu", aSubmenu));
+ }
+ else
+ {
+ // no point in exposing choices that don't have the .uno:
+ // command
+ if (aCommandURL.isEmpty())
+ continue;
+
+ aItemTree.put("type", "command");
+ aItemTree.put("command", aCommandURL.toUtf8().getStr());
+ }
+
+ aItemTree.put("enabled", pMenu->IsItemEnabled(nItemId));
+
+ MenuItemBits aItemBits = pMenu->GetItemBits(nItemId);
+ bool bHasChecks = true;
+ if (aItemBits & MenuItemBits::CHECKABLE)
+ aItemTree.put("checktype", "checkmark");
+ else if (aItemBits & MenuItemBits::RADIOCHECK)
+ aItemTree.put("checktype", "radio");
+ else if (aItemBits & MenuItemBits::AUTOCHECK)
+ aItemTree.put("checktype", "auto");
+ else
+ bHasChecks = false;
+
+ if (bHasChecks)
+ aItemTree.put("checked", pMenu->IsItemChecked(nItemId));
+ }
+
+ if (!aItemTree.empty())
+ {
+ aTree.push_back(std::make_pair("", aItemTree));
+ if (aItemType != MenuItemType::SEPARATOR)
+ bIsLastItemText = true;
+ }
+ }
+
+ return aTree;
+}
+
+}
+
+boost::property_tree::ptree SfxDispatcher::fillPopupMenu(const css::uno::Reference<css::awt::XPopupMenu>& rPopupMenu)
+{
+ VCLXMenu* pAwtMenu = comphelper::getFromUnoTunnel<VCLXMenu>(rPopupMenu);
+ PopupMenu* pVCLMenu = static_cast<PopupMenu*>(pAwtMenu->GetMenu());
+ return ::fillPopupMenu(pVCLMenu);
+}
+
+void SfxDispatcher::ExecutePopup( const OUString& rResName, vcl::Window* pWin, const Point* pPos )
+{
+ css::uno::Sequence< css::uno::Any > aArgs{
+ css::uno::Any(comphelper::makePropertyValue( "Value", rResName )),
+ css::uno::Any(comphelper::makePropertyValue( "Frame", GetFrame()->GetFrame().GetFrameInterface() )),
+ css::uno::Any(comphelper::makePropertyValue( "IsContextMenu", true ))
+ };
+
+ css::uno::Reference< css::uno::XComponentContext > xContext = comphelper::getProcessComponentContext();
+ css::uno::Reference< css::frame::XPopupMenuController > xPopupController(
+ xContext->getServiceManager()->createInstanceWithArgumentsAndContext(
+ "com.sun.star.comp.framework.ResourceMenuController", aArgs, xContext ), css::uno::UNO_QUERY );
+
+ css::uno::Reference< css::awt::XPopupMenu > xPopupMenu( xContext->getServiceManager()->createInstanceWithContext(
+ "com.sun.star.awt.PopupMenu", xContext ), css::uno::UNO_QUERY );
+
+ if ( !xPopupController.is() || !xPopupMenu.is() )
+ return;
+
+ vcl::Window* pWindow = pWin ? pWin : xImp->pFrame->GetFrame().GetWorkWindow_Impl()->GetWindow();
+ Point aPos = pPos ? *pPos : pWindow->GetPointerPosPixel();
+
+ css::ui::ContextMenuExecuteEvent aEvent;
+ aEvent.SourceWindow = VCLUnoHelper::GetInterface( pWindow );
+ aEvent.ExecutePosition.X = aPos.X();
+ aEvent.ExecutePosition.Y = aPos.Y();
+
+ xPopupController->setPopupMenu( xPopupMenu );
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ boost::property_tree::ptree aMenu = fillPopupMenu(xPopupMenu);
+ boost::property_tree::ptree aRoot;
+ aRoot.add_child("menu", aMenu);
+
+ std::stringstream aStream;
+ boost::property_tree::write_json(aStream, aRoot, true);
+ if (SfxViewShell* pViewShell = xImp->pFrame->GetViewShell())
+ pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_CONTEXT_MENU, aStream.str().c_str());
+ }
+ else
+ {
+ OUString aMenuURL = "private:resource/popupmenu/" + rResName;
+ if (GetFrame()->GetViewShell()->TryContextMenuInterception(xPopupMenu, aMenuURL, aEvent))
+ {
+ css::uno::Reference<css::awt::XWindowPeer> xParent(aEvent.SourceWindow, css::uno::UNO_QUERY);
+ xPopupMenu->execute(xParent, css::awt::Rectangle(aPos.X(), aPos.Y(), 1, 1), css::awt::PopupMenuDirection::EXECUTE_DOWN);
+ }
+ }
+
+ css::uno::Reference< css::lang::XComponent > xComponent( xPopupController, css::uno::UNO_QUERY );
+ if ( xComponent.is() )
+ xComponent->dispose();
+}
+
+/** With this method the SfxDispatcher can be locked and released. A locked
+ SfxDispatcher does not perform <SfxRequest>s and does no longer provide
+ status information. It behaves as if all the slots were disabled.
+*/
+void SfxDispatcher::Lock( bool bLock )
+{
+ SfxBindings* pBindings = GetBindings();
+ if ( !bLock && xImp->bLocked && xImp->bInvalidateOnUnlock )
+ {
+ if ( pBindings )
+ pBindings->InvalidateAll(true);
+ xImp->bInvalidateOnUnlock = false;
+ }
+ else if ( pBindings )
+ pBindings->InvalidateAll(false);
+ xImp->bLocked = bLock;
+ if ( !bLock )
+ {
+ for(size_t i = 0; i < xImp->aReqArr.size(); ++i)
+ xImp->xPoster->Post(std::move(xImp->aReqArr[i]));
+ xImp->aReqArr.clear();
+ }
+}
+
+ToolbarId SfxDispatcher::GetObjectBarId( sal_uInt16 nPos ) const
+{
+ return xImp->aObjBars[nPos].eId;
+}
+
+void SfxDispatcher::HideUI( bool bHide )
+{
+ bool bWasHidden = xImp->bNoUI;
+ xImp->bNoUI = bHide;
+ if ( xImp->pFrame )
+ {
+ SfxViewFrame* pTop = xImp->pFrame->GetTopViewFrame();
+ if ( pTop && pTop->GetBindings().GetDispatcher() == this )
+ {
+ SfxFrame& rFrame = pTop->GetFrame();
+ if ( rFrame.IsMenuBarOn_Impl() )
+ {
+ css::uno::Reference < css::beans::XPropertySet > xPropSet( rFrame.GetFrameInterface(), css::uno::UNO_QUERY );
+ if ( xPropSet.is() )
+ {
+ css::uno::Reference< css::frame::XLayoutManager > xLayoutManager;
+ css::uno::Any aValue = xPropSet->getPropertyValue("LayoutManager");
+ aValue >>= xLayoutManager;
+ if ( xLayoutManager.is() )
+ xLayoutManager->setVisible( !bHide );
+ }
+ }
+ }
+ }
+
+ if ( bHide != bWasHidden )
+ Update_Impl( true );
+}
+
+void SfxDispatcher::SetReadOnly_Impl( bool bOn )
+{
+ xImp->bReadOnly = bOn;
+}
+
+bool SfxDispatcher::GetReadOnly_Impl() const
+{
+ return xImp->bReadOnly;
+}
+
+/** With 'bOn' the Dispatcher is quasi dead and transfers everything to the
+ Parent-Dispatcher.
+*/
+void SfxDispatcher::SetQuietMode_Impl( bool bOn )
+{
+ xImp->bQuiet = bOn;
+ SfxBindings* pBindings = GetBindings();
+ if ( pBindings )
+ pBindings->InvalidateAll(true);
+}
+
+SfxItemState SfxDispatcher::QueryState( sal_uInt16 nSlot, const SfxPoolItem* &rpState )
+{
+ SfxShell *pShell = nullptr;
+ const SfxSlot *pSlot = nullptr;
+ if ( GetShellAndSlot_Impl( nSlot, &pShell, &pSlot, false, true ) )
+ {
+ rpState = pShell->GetSlotState(nSlot);
+ if ( !rpState )
+ return SfxItemState::DISABLED;
+ else
+ return SfxItemState::DEFAULT;
+ }
+
+ return SfxItemState::DISABLED;
+}
+
+SfxItemState SfxDispatcher::QueryState( sal_uInt16 nSID, css::uno::Any& rAny )
+{
+ SfxShell *pShell = nullptr;
+ const SfxSlot *pSlot = nullptr;
+ if ( GetShellAndSlot_Impl( nSID, &pShell, &pSlot, false, true ) )
+ {
+ const SfxPoolItem* pItem = pShell->GetSlotState( nSID );
+ if ( !pItem )
+ return SfxItemState::DISABLED;
+ else
+ {
+ css::uno::Any aState;
+ if ( !pItem->IsVoidItem() )
+ {
+ sal_uInt16 nSubId( 0 );
+ SfxItemPool& rPool = pShell->GetPool();
+ sal_uInt16 nWhich = rPool.GetWhich( nSID );
+ if ( rPool.GetMetric( nWhich ) == MapUnit::MapTwip )
+ nSubId |= CONVERT_TWIPS;
+ pItem->QueryValue( aState, static_cast<sal_uInt8>(nSubId) );
+ }
+ rAny = aState;
+
+ return SfxItemState::DEFAULT;
+ }
+ }
+
+ return SfxItemState::DISABLED;
+}
+
+bool SfxDispatcher::IsReadOnlyShell_Impl( sal_uInt16 nShell ) const
+{
+ sal_uInt16 nShellCount = xImp->aStack.size();
+ if ( nShell < nShellCount )
+ {
+ SfxShell* pShell = *( xImp->aStack.rbegin() + nShell );
+ if( dynamic_cast< const SfxModule *>( pShell ) != nullptr || dynamic_cast< const SfxApplication *>( pShell ) != nullptr || dynamic_cast< const SfxViewFrame *>( pShell ) != nullptr )
+ return false;
+ else
+ return xImp->bReadOnly;
+ }
+ return true;
+}
+
+void SfxDispatcher::RemoveShell_Impl( SfxShell& rShell )
+{
+ Flush();
+
+ sal_uInt16 nCount = xImp->aStack.size();
+ for ( sal_uInt16 n=0; n<nCount; ++n )
+ {
+ if ( xImp->aStack[n] == &rShell )
+ {
+ xImp->aStack.erase( xImp->aStack.begin() + n );
+ rShell.SetDisableFlags( SfxDisableFlags::NONE );
+ rShell.DoDeactivate_Impl(xImp->pFrame, true);
+ break;
+ }
+ }
+
+ if ( !SfxGetpApp()->IsDowning() )
+ {
+ xImp->bUpdated = false;
+ InvalidateBindings_Impl(true);
+ }
+}
+
+void SfxDispatcher::InvalidateBindings_Impl( bool bModify )
+{
+ // App-Dispatcher?
+ if ( IsAppDispatcher() )
+ {
+ for ( SfxViewFrame *pFrame = SfxViewFrame::GetFirst();
+ pFrame;
+ pFrame = SfxViewFrame::GetNext( *pFrame ) )
+ pFrame->GetBindings().InvalidateAll(bModify);
+ }
+ else
+ {
+ SfxDispatcher *pDisp = GetBindings()->GetDispatcher_Impl();
+ if ( pDisp == this )
+ {
+ GetBindings()->InvalidateAll( bModify );
+ }
+ }
+}
+
+bool SfxDispatcher::IsUpdated_Impl() const
+{
+ return xImp->bUpdated;
+}
+
+void SfxDispatcher::SetDisableFlags( SfxDisableFlags nFlags )
+{
+ xImp->nDisableFlags = nFlags;
+ for ( SfxShellStack_Impl::reverse_iterator it = xImp->aStack.rbegin(); it != xImp->aStack.rend(); ++it )
+ (*it)->SetDisableFlags( nFlags );
+}
+
+SfxDisableFlags SfxDispatcher::GetDisableFlags() const
+{
+ return xImp->nDisableFlags;
+}
+
+SfxModule* SfxDispatcher::GetModule() const
+{
+ for ( sal_uInt16 nShell = 0;; ++nShell )
+ {
+ SfxShell *pSh = GetShell(nShell);
+ if ( pSh == nullptr )
+ return nullptr;
+ if ( auto pModule = dynamic_cast<SfxModule *>( pSh ) )
+ return pModule;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/control/emojicontrol.cxx b/sfx2/source/control/emojicontrol.cxx
new file mode 100644
index 000000000..8b3c20607
--- /dev/null
+++ b/sfx2/source/control/emojicontrol.cxx
@@ -0,0 +1,156 @@
+/* -*- 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 <emojicontrol.hxx>
+#include <emojipopup.hxx>
+#include <emojiview.hxx>
+#include <sfx2/thumbnailviewitem.hxx>
+#include <comphelper/propertysequence.hxx>
+#include <comphelper/dispatchcommand.hxx>
+#include <officecfg/Office/Common.hxx>
+
+constexpr OStringLiteral FILTER_PEOPLE = "people";
+constexpr OStringLiteral FILTER_NATURE = "nature";
+constexpr OStringLiteral FILTER_FOOD = "food";
+constexpr OStringLiteral FILTER_ACTIVITY = "activity";
+constexpr OStringLiteral FILTER_TRAVEL = "travel";
+constexpr OStringLiteral FILTER_OBJECTS = "objects";
+constexpr OStringLiteral FILTER_SYMBOLS = "symbols";
+constexpr OStringLiteral FILTER_FLAGS = "flags";
+constexpr OStringLiteral FILTER_UNICODE9 = "unicode9";
+
+using namespace com::sun::star;
+
+SfxEmojiControl::SfxEmojiControl(const EmojiPopup* pControl, weld::Widget* pParent)
+ : WeldToolbarPopup(pControl->getFrameInterface(), pParent, "sfx/ui/emojicontrol.ui", "emojictrl")
+ , mxPeopleBtn(m_xBuilder->weld_toggle_button(FILTER_PEOPLE))
+ , mxNatureBtn(m_xBuilder->weld_toggle_button(FILTER_NATURE))
+ , mxFoodBtn(m_xBuilder->weld_toggle_button(FILTER_FOOD))
+ , mxActivityBtn(m_xBuilder->weld_toggle_button(FILTER_ACTIVITY))
+ , mxTravelBtn(m_xBuilder->weld_toggle_button(FILTER_TRAVEL))
+ , mxObjectsBtn(m_xBuilder->weld_toggle_button(FILTER_OBJECTS))
+ , mxSymbolsBtn(m_xBuilder->weld_toggle_button(FILTER_SYMBOLS))
+ , mxFlagsBtn(m_xBuilder->weld_toggle_button(FILTER_FLAGS))
+ , mxUnicode9Btn(m_xBuilder->weld_toggle_button(FILTER_UNICODE9))
+ , mxEmojiView(new EmojiView(m_xBuilder->weld_scrolled_window("emoji_win", true)))
+ , mxEmojiWeld(new weld::CustomWeld(*m_xBuilder, "emoji_view", *mxEmojiView))
+{
+ ConvertLabelToUnicode(*mxPeopleBtn);
+ ConvertLabelToUnicode(*mxNatureBtn);
+ ConvertLabelToUnicode(*mxFoodBtn);
+ ConvertLabelToUnicode(*mxActivityBtn);
+ ConvertLabelToUnicode(*mxTravelBtn);
+ ConvertLabelToUnicode(*mxObjectsBtn);
+ ConvertLabelToUnicode(*mxSymbolsBtn);
+ ConvertLabelToUnicode(*mxFlagsBtn);
+ ConvertLabelToUnicode(*mxUnicode9Btn);
+
+ mxPeopleBtn->connect_toggled(LINK(this, SfxEmojiControl, ActivatePageHdl));
+ mxNatureBtn->connect_toggled(LINK(this, SfxEmojiControl, ActivatePageHdl));
+ mxFoodBtn->connect_toggled(LINK(this, SfxEmojiControl, ActivatePageHdl));
+ mxActivityBtn->connect_toggled(LINK(this, SfxEmojiControl, ActivatePageHdl));
+ mxTravelBtn->connect_toggled(LINK(this, SfxEmojiControl, ActivatePageHdl));
+ mxObjectsBtn->connect_toggled(LINK(this, SfxEmojiControl, ActivatePageHdl));
+ mxSymbolsBtn->connect_toggled(LINK(this, SfxEmojiControl, ActivatePageHdl));
+ mxFlagsBtn->connect_toggled(LINK(this, SfxEmojiControl, ActivatePageHdl));
+ mxUnicode9Btn->connect_toggled(LINK(this, SfxEmojiControl, ActivatePageHdl));
+
+ mxEmojiView->setItemMaxTextLength(ITEM_MAX_TEXT_LENGTH);
+ mxEmojiView->setItemDimensions(ITEM_MAX_WIDTH, 0, ITEM_MAX_HEIGHT, ITEM_PADDING);
+
+ mxEmojiView->Populate();
+ ActivatePageHdl(*mxPeopleBtn);
+
+ mxEmojiView->setInsertEmojiHdl(LINK(this, SfxEmojiControl, InsertHdl));
+ mxEmojiView->ShowTooltips(true);
+}
+
+void SfxEmojiControl::GrabFocus()
+{
+ mxEmojiView->GrabFocus();
+}
+
+SfxEmojiControl::~SfxEmojiControl()
+{
+}
+
+void SfxEmojiControl::ConvertLabelToUnicode(weld::ToggleButton& rBtn)
+{
+ OUString sLabel = rBtn.get_label();
+ sal_uInt32 nCodePoint = sLabel.toUInt32(16);
+ const OUString sHexText(&nCodePoint, 1);
+ rBtn.set_label(sHexText);
+}
+
+FILTER_CATEGORY SfxEmojiControl::getFilter(const weld::Toggleable& rCurPageId) const
+{
+ if (&rCurPageId == mxPeopleBtn.get())
+ return FILTER_CATEGORY::PEOPLE;
+ else if (&rCurPageId == mxNatureBtn.get())
+ return FILTER_CATEGORY::NATURE;
+ else if (&rCurPageId == mxFoodBtn.get())
+ return FILTER_CATEGORY::FOOD;
+ else if (&rCurPageId == mxActivityBtn.get())
+ return FILTER_CATEGORY::ACTIVITY;
+ else if (&rCurPageId == mxTravelBtn.get())
+ return FILTER_CATEGORY::TRAVEL;
+ else if (&rCurPageId == mxObjectsBtn.get())
+ return FILTER_CATEGORY::OBJECTS;
+ else if (&rCurPageId == mxSymbolsBtn.get())
+ return FILTER_CATEGORY::SYMBOLS;
+ else if (&rCurPageId == mxFlagsBtn.get())
+ return FILTER_CATEGORY::FLAGS;
+ else if (&rCurPageId == mxUnicode9Btn.get())
+ return FILTER_CATEGORY::UNICODE9;
+
+ return FILTER_CATEGORY::PEOPLE;
+}
+
+IMPL_LINK(SfxEmojiControl, ActivatePageHdl, weld::Toggleable&, rButton, void)
+{
+ mxPeopleBtn->set_active(&rButton == mxPeopleBtn.get());
+ mxNatureBtn->set_active(&rButton == mxNatureBtn.get());
+ mxFoodBtn->set_active(&rButton == mxFoodBtn.get());
+ mxActivityBtn->set_active(&rButton == mxActivityBtn.get());
+ mxTravelBtn->set_active(&rButton == mxTravelBtn.get());
+ mxObjectsBtn->set_active(&rButton == mxObjectsBtn.get());
+ mxSymbolsBtn->set_active(&rButton == mxSymbolsBtn.get());
+ mxFlagsBtn->set_active(&rButton == mxFlagsBtn.get());
+ mxUnicode9Btn->set_active(&rButton == mxUnicode9Btn.get());
+
+ mxEmojiView->filterItems(ViewFilter_Category(getFilter(rButton)));
+}
+
+IMPL_STATIC_LINK(SfxEmojiControl, InsertHdl, ThumbnailViewItem*, pItem, void)
+{
+ const OUString& sHexText = pItem->getTitle();
+ sal_uInt32 cEmojiChar = sHexText.toUInt32(16);
+
+ OUString sFontName(officecfg::Office::Common::Misc::EmojiFont::get());
+
+ uno::Sequence<beans::PropertyValue> aArgs( comphelper::InitPropertySequence({
+ { "Symbols", uno::Any(OUString(&cEmojiChar, 1)) },
+ // add font settings here
+ { "FontName", uno::Any(sFontName) }
+ }));
+
+ comphelper::dispatchCommand(".uno:InsertSymbol", aArgs);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/control/emojipopup.cxx b/sfx2/source/control/emojipopup.cxx
new file mode 100644
index 000000000..237c21418
--- /dev/null
+++ b/sfx2/source/control/emojipopup.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 <emojipopup.hxx>
+#include <emojicontrol.hxx>
+#include <vcl/toolbox.hxx>
+
+EmojiPopup::EmojiPopup(const css::uno::Reference<css::uno::XComponentContext>& rContext)
+ : PopupWindowController(rContext, nullptr, OUString())
+{
+}
+
+void EmojiPopup::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));
+}
+
+EmojiPopup::~EmojiPopup() {}
+
+std::unique_ptr<WeldToolbarPopup> EmojiPopup::weldPopupWindow()
+{
+ return std::make_unique<SfxEmojiControl>(this, m_pToolbar);
+}
+
+VclPtr<vcl::Window> EmojiPopup::createVclPopupWindow(vcl::Window* pParent)
+{
+ mxInterimPopover = VclPtr<InterimToolbarPopup>::Create(
+ getFrameInterface(), pParent,
+ std::make_unique<SfxEmojiControl>(this, pParent->GetFrameWeld()));
+
+ mxInterimPopover->Show();
+
+ return mxInterimPopover;
+}
+
+OUString EmojiPopup::getImplementationName()
+{
+ return "com.sun.star.comp.sfx2.InsertEmojiToolBoxControl";
+}
+
+css::uno::Sequence<OUString> EmojiPopup::getSupportedServiceNames()
+{
+ return { "com.sun.star.frame.ToolbarController" };
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_comp_sfx2_InsertEmojiToolBoxControl_get_implementation(
+ css::uno::XComponentContext* rContext, css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new EmojiPopup(rContext));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/control/emojiview.cxx b/sfx2/source/control/emojiview.cxx
new file mode 100644
index 000000000..6e5bd97b8
--- /dev/null
+++ b/sfx2/source/control/emojiview.cxx
@@ -0,0 +1,224 @@
+/* -*- 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 <osl/file.hxx>
+#include <emojiview.hxx>
+#include <emojiviewitem.hxx>
+#include <rtl/bootstrap.hxx>
+#include <sal/log.hxx>
+#include <config_folders.h>
+#include <officecfg/Office/Common.hxx>
+#include <comphelper/processfactory.hxx>
+#include <vcl/event.hxx>
+#include <vcl/weldutils.hxx>
+#include <o3tl/string_view.hxx>
+
+#include <orcus/json_document_tree.hpp>
+#include <orcus/config.hpp>
+#include <string>
+#include <string_view>
+#include <fstream>
+
+using namespace ::com::sun::star;
+
+bool ViewFilter_Category::isFilteredCategory(FILTER_CATEGORY filter, std::u16string_view rCategory)
+{
+ bool bRet = true;
+
+ if (filter == FILTER_CATEGORY::PEOPLE)
+ bRet = o3tl::starts_with(rCategory, u"people");
+ else if (filter == FILTER_CATEGORY::NATURE)
+ bRet = o3tl::starts_with(rCategory, u"nature");
+ else if (filter == FILTER_CATEGORY::FOOD)
+ bRet = o3tl::starts_with(rCategory, u"food");
+ else if (filter == FILTER_CATEGORY::ACTIVITY)
+ bRet = o3tl::starts_with(rCategory, u"activity");
+ else if (filter == FILTER_CATEGORY::TRAVEL)
+ bRet = o3tl::starts_with(rCategory, u"travel");
+ else if (filter == FILTER_CATEGORY::OBJECTS)
+ bRet = o3tl::starts_with(rCategory, u"objects");
+ else if (filter == FILTER_CATEGORY::SYMBOLS)
+ bRet = o3tl::starts_with(rCategory, u"symbols");
+ else if (filter == FILTER_CATEGORY::FLAGS)
+ bRet = o3tl::starts_with(rCategory, u"flags");
+ else if (filter == FILTER_CATEGORY::UNICODE9)
+ bRet = o3tl::starts_with(rCategory, u"unicode9");
+
+ return bRet;
+}
+
+bool ViewFilter_Category::operator () (const ThumbnailViewItem *pItem)
+{
+ const EmojiViewItem *pViewItem = dynamic_cast<const EmojiViewItem*>(pItem);
+ if (pViewItem)
+ return isFilteredCategory(mCategory, pViewItem->getCategory());
+
+ return true;
+}
+
+EmojiView::EmojiView(std::unique_ptr<weld::ScrolledWindow> xWindow)
+ : ThumbnailView(std::move(xWindow), nullptr)
+{
+ // locate json data file
+ OUString aURL("$BRAND_BASE_DIR/" LIBO_SHARE_FOLDER "/emojiconfig/emoji.json");
+ rtl::Bootstrap::expandMacros(aURL);
+
+ OUString aPath;
+ osl::FileBase::getSystemPathFromFileURL(aURL, aPath);
+ std::string strPath = OUStringToOString(aPath, RTL_TEXTENCODING_UTF8).getStr();
+
+ std::ifstream file(strPath);
+ if(!file.is_open())
+ return;
+
+ msJSONData = std::string((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
+ if(msJSONData.empty())
+ return;
+}
+
+void EmojiView::SetDrawingArea(weld::DrawingArea* pDrawingArea)
+{
+ ThumbnailView::SetDrawingArea(pDrawingArea);
+
+ OUString sFontName(officecfg::Office::Common::Misc::EmojiFont::get());
+ vcl::Font aFont = pDrawingArea->get_font();
+ aFont.SetFamilyName(sFontName);
+ OutputDevice& rDevice = pDrawingArea->get_ref_device();
+ weld::SetPointFont(rDevice, aFont);
+
+ mpItemAttrs->aFontSize.setX(ITEM_MAX_WIDTH - 2*ITEM_PADDING);
+ mpItemAttrs->aFontSize.setY(ITEM_MAX_HEIGHT - 2*ITEM_PADDING);
+}
+
+EmojiView::~EmojiView()
+{
+}
+
+void EmojiView::Populate()
+{
+ if (msJSONData.empty())
+ {
+ SAL_WARN("sfx", "Emoji config data is empty");
+ return;
+ }
+
+ // Populate view using the orcus json parser
+ using node = orcus::json::node;
+
+ // default json config
+ orcus::json_config config;
+
+ orcus::json::document_tree aEmojiInfo;
+
+ // Load JSON string into a document tree.
+ aEmojiInfo.load(msJSONData, config);
+
+ node root = aEmojiInfo.get_document_root();
+ std::vector<std::string_view> keys = root.keys();
+
+ for (auto const& key : keys)
+ {
+ node value = root.child(key);
+
+ if(value.type() == orcus::json::node_t::object)
+ {
+ // iterate each element to get the keys
+ std::vector<std::string_view> aEmojiParams = value.keys();
+ OUString sTitle, sCategory, sName;
+ bool bDuplicate = false;
+
+ for (auto const& emojiParam : aEmojiParams)
+ {
+ node prop = value.child(emojiParam);
+
+ // get values of parameters in AppendItem() function
+ if(emojiParam == "unicode")
+ {
+ sTitle = OStringToOUString(prop.string_value(), RTL_TEXTENCODING_UTF8);
+ }
+ else if(emojiParam == "category")
+ {
+ sCategory = OStringToOUString(prop.string_value(), RTL_TEXTENCODING_UTF8);
+ }
+ else if(emojiParam == "name")
+ {
+ sName = OStringToOUString(prop.string_value(), RTL_TEXTENCODING_UTF8);
+ }
+ else if(emojiParam == "duplicate")
+ {
+ bDuplicate = true;
+ }
+ }
+
+ // Don't append if a duplicate emoji
+ if(!bDuplicate)
+ {
+ AppendItem(sTitle, sCategory, sName);
+ }
+ }
+ }
+}
+
+bool EmojiView::MouseButtonDown( const MouseEvent& rMEvt )
+{
+ GrabFocus();
+
+ if (rMEvt.IsLeft())
+ {
+ size_t nPos = ImplGetItem(rMEvt.GetPosPixel());
+ ThumbnailViewItem* pItem = ImplGetItem(nPos);
+
+ if(pItem)
+ maInsertEmojiHdl.Call(pItem);
+ }
+
+ return true;
+}
+
+bool EmojiView::KeyInput( const KeyEvent& rKEvt )
+{
+ vcl::KeyCode aKeyCode = rKEvt.GetKeyCode();
+
+ if(aKeyCode == ( KEY_MOD1 | KEY_A ) )
+ {
+ for (ThumbnailViewItem* pItem : mFilteredItemList)
+ {
+ if (!pItem->isSelected())
+ {
+ pItem->setSelection(true);
+ }
+ }
+
+ if (IsReallyVisible() && IsUpdateMode())
+ Invalidate();
+ return true;
+ }
+
+ return ThumbnailView::KeyInput(rKEvt);
+}
+
+void EmojiView::setInsertEmojiHdl(const Link<ThumbnailViewItem*, void> &rLink)
+{
+ maInsertEmojiHdl = rLink;
+}
+
+void EmojiView::AppendItem(const OUString &rTitle, const OUString &rCategory, const OUString &rName)
+{
+ std::unique_ptr<EmojiViewItem> pItem(new EmojiViewItem(*this, getNextItemId()));
+
+ pItem->maTitle = rTitle;
+ pItem->setCategory(rCategory);
+ pItem->setHelpText(rName);
+
+ ThumbnailView::AppendItem(std::move(pItem));
+
+ CalculateItemPositions();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/control/emojiviewitem.cxx b/sfx2/source/control/emojiviewitem.cxx
new file mode 100644
index 000000000..99a727315
--- /dev/null
+++ b/sfx2/source/control/emojiviewitem.cxx
@@ -0,0 +1,84 @@
+/* -*- 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 <emojiviewitem.hxx>
+
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonSelectionPrimitive2D.hxx>
+#include <drawinglayer/primitive2d/textlayoutdevice.hxx>
+#include <drawinglayer/processor2d/baseprocessor2d.hxx>
+#include <com/sun/star/lang/Locale.hpp>
+#include <tools/poly.hxx>
+
+using namespace basegfx;
+using namespace basegfx::utils;
+using namespace drawinglayer::attribute;
+using namespace drawinglayer::primitive2d;
+
+EmojiViewItem::EmojiViewItem(ThumbnailView& rView, sal_uInt16 nId)
+ : ThumbnailViewItem(rView, nId)
+{
+}
+
+EmojiViewItem::~EmojiViewItem ()
+{
+}
+
+void EmojiViewItem::calculateItemsPosition (const tools::Long /*nThumbnailHeight*/,
+ const tools::Long /*nPadding*/, sal_uInt32 nMaxTextLength,
+ const ThumbnailItemAttributes *pAttrs)
+{
+ drawinglayer::primitive2d::TextLayouterDevice aTextDev;
+ aTextDev.setFontAttribute(pAttrs->aFontAttr,
+ pAttrs->aFontSize.getX(), pAttrs->aFontSize.getY(),
+ css::lang::Locale() );
+
+ Size aRectSize = maDrawArea.GetSize();
+ Point aPos = maDrawArea.TopCenter();
+
+ // Calculate text position
+ aPos.Move(-aTextDev.getTextWidth(maTitle, 0, nMaxTextLength) / 2,
+ (aRectSize.Height() - aTextDev.getTextHeight()) / 3);
+ maTextPos = aPos;
+}
+
+
+void EmojiViewItem::Paint(drawinglayer::processor2d::BaseProcessor2D *pProcessor,
+ const ThumbnailItemAttributes *pAttrs)
+{
+ BColor aFillColor = pAttrs->aFillColor;
+
+ drawinglayer::primitive2d::Primitive2DContainer aSeq(2);
+ double fTransparence = 0.0;
+
+ // Draw background
+ if( mbSelected && mbHover)
+ aFillColor = pAttrs->aSelectHighlightColor;
+ else if (mbSelected || mbHover)
+ aFillColor = pAttrs->aHighlightColor;
+
+ if (mbHover)
+ fTransparence = pAttrs->fHighlightTransparence;
+
+ aSeq[0] = drawinglayer::primitive2d::Primitive2DReference(
+ new PolyPolygonSelectionPrimitive2D( B2DPolyPolygon(::tools::Polygon(maDrawArea,5,5).getB2DPolygon()),
+ aFillColor,
+ fTransparence,
+ 0.0,
+ true));
+
+ sal_uInt32 nCodePoint = maTitle.toUInt32(16);
+ const OUString sHexText(&nCodePoint, 1);
+
+ addTextPrimitives(sHexText, pAttrs, maTextPos, aSeq);
+
+ pProcessor->process(aSeq);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/control/itemdel.cxx b/sfx2/source/control/itemdel.cxx
new file mode 100644
index 000000000..2b8e7db73
--- /dev/null
+++ b/sfx2/source/control/itemdel.cxx
@@ -0,0 +1,77 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <itemdel.hxx>
+#include <svl/poolitem.hxx>
+#include <vcl/idle.hxx>
+
+#include <tools/debug.hxx>
+
+class SfxItemDisruptor_Impl
+{
+ std::unique_ptr<SfxPoolItem> pItem;
+ Idle m_Idle;
+
+private:
+ DECL_LINK(Delete, Timer*, void);
+
+public:
+ explicit SfxItemDisruptor_Impl(std::unique_ptr<SfxPoolItem> pItemToDesrupt);
+ void LaunchDeleteOnIdle();
+ ~SfxItemDisruptor_Impl();
+ SfxItemDisruptor_Impl(const SfxItemDisruptor_Impl&) = delete;
+ SfxItemDisruptor_Impl& operator=(const SfxItemDisruptor_Impl&) = delete;
+};
+
+SfxItemDisruptor_Impl::SfxItemDisruptor_Impl(std::unique_ptr<SfxPoolItem> pItemToDisrupt)
+ : pItem(std::move(pItemToDisrupt))
+ , m_Idle("sfx::SfxItemDisruptor_Impl m_Idle")
+{
+ m_Idle.SetInvokeHandler(LINK(this, SfxItemDisruptor_Impl, Delete));
+ m_Idle.SetPriority(TaskPriority::DEFAULT_IDLE);
+
+ DBG_ASSERT(0 == pItem->GetRefCount(), "disrupting pooled item");
+ pItem->SetKind(SfxItemKind::DeleteOnIdle);
+}
+
+void SfxItemDisruptor_Impl::LaunchDeleteOnIdle() { m_Idle.Start(); }
+
+SfxItemDisruptor_Impl::~SfxItemDisruptor_Impl()
+{
+ m_Idle.Stop();
+
+ // reset RefCount (was set to SFX_ITEMS_SPECIAL before!)
+ pItem->SetRefCount(0);
+
+ pItem.reset();
+}
+
+IMPL_LINK_NOARG(SfxItemDisruptor_Impl, Delete, Timer*, void) { delete this; }
+
+void DeleteItemOnIdle(std::unique_ptr<SfxPoolItem> pItem)
+{
+ DBG_ASSERT(0 == pItem->GetRefCount(), "deleting item in use");
+ SfxItemDisruptor_Impl* pDesruptor = new SfxItemDisruptor_Impl(std::move(pItem));
+ pDesruptor->LaunchDeleteOnIdle();
+ // coverity[leaked_storage] - pDesruptor takes care of its own destruction at idle time
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/control/listview.cxx b/sfx2/source/control/listview.cxx
new file mode 100644
index 000000000..a53e85d71
--- /dev/null
+++ b/sfx2/source/control/listview.cxx
@@ -0,0 +1,443 @@
+/* -*- 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/listview.hxx>
+
+#include <sfx2/sfxresid.hxx>
+#include <tools/urlobj.hxx>
+#include <tools/datetime.hxx>
+#include <sfx2/strings.hrc>
+#include <osl/file.hxx>
+#include <osl/time.h>
+#include <comphelper/fileurl.hxx>
+
+#include <svtools/svtresid.hxx>
+#include <svtools/strings.hrc>
+#include <unotools/localedatawrapper.hxx>
+#include <unotools/collatorwrapper.hxx>
+#include <unotools/syslocale.hxx>
+#include <unotools/intlwrapper.hxx>
+#include <tools/wintypes.hxx>
+
+#include <bitmaps.hlst>
+#include <rtl/math.hxx>
+
+#include <sfx2/templatelocalview.hxx>
+
+#define COLUMN_IMG_ISDEFAULT 0
+#define COLUMN_NAME 1
+#define COLUMN_CATEGORY 2
+#define COLUMN_APPLICATION 3
+#define COLUMN_MODIFIED 4
+#define COLUMN_SIZE 5
+#define NUMBER_OF_COLUMNS 6
+
+static sal_uInt64 getFileSize(const OUString& rURL);
+static sal_uInt32 getFileModifyTime(const OUString& rURL);
+static OUString getDisplayFileSize(const OUString& rURL);
+static OUString getDisplayFileModifyTime(const OUString& rURL);
+static OUString getApplication(std::u16string_view rURL);
+
+ListView::ListView(std::unique_ptr<weld::TreeView> xTreeView)
+ : mxTreeView(std::move(xTreeView))
+ , mnSortColumn(-2)
+{
+ auto nDigitWidth = mxTreeView->get_approximate_digit_width();
+ std::vector<int> aWidths{
+ static_cast<int>(nDigitWidth * 5), /* Icon Column */
+ static_cast<int>(nDigitWidth * 24), /* Name Column */
+ static_cast<int>(nDigitWidth * 22), /* Category Column */
+ static_cast<int>(nDigitWidth * 15), /* Application Column */
+ static_cast<int>(nDigitWidth * 18) /* Modify Column */
+ };
+
+ mxTreeView->set_column_fixed_widths(aWidths);
+ mxTreeView->set_selection_mode(SelectionMode::Multiple);
+ mxTreeView->connect_query_tooltip(LINK(this, ListView, QueryTooltipHdl));
+}
+ListView::~ListView() {}
+
+void ListView::AppendItem(const OUString& rId, const OUString& rTitle, const OUString& rSubtitle,
+ const OUString& rPath, bool bDefault)
+{
+ INetURLObject aUrl(rPath, INetProtocol::File);
+ OUString sPath = aUrl.getFSysPath(FSysStyle::Detect);
+
+ std::unique_ptr<ListViewItem> pItem(new ListViewItem);
+ pItem->maId = rId;
+ pItem->maTitle = rTitle;
+ pItem->maSubtitle = rSubtitle;
+ pItem->maApplication = getApplication(rPath);
+ pItem->maPath = rPath;
+ pItem->mbDefault = bDefault;
+ pItem->mnModify = getFileModifyTime(rPath);
+ pItem->mnSize = getFileSize(rPath);
+ pItem->maDisplayModify = getDisplayFileModifyTime(rPath);
+ pItem->maDisplaySize = getDisplayFileSize(rPath);
+ pItem->maDisplayPath = sPath;
+
+ OUString sImage("");
+ if (pItem->mbDefault)
+ sImage = BMP_DEFAULT;
+
+ AppendRow(sImage, pItem->maTitle, pItem->maSubtitle, pItem->maApplication,
+ pItem->maDisplayModify, pItem->maDisplaySize, pItem->maId);
+
+ mListViewItems.push_back(std::move(pItem));
+}
+
+void ListView::AppendRow(const OUString& rImage, const OUString& rTitle, const OUString& rSubtitle,
+ const OUString& rApplication, const OUString& rModify,
+ const OUString& rSize, const OUString& rId)
+{
+ std::unique_ptr<weld::TreeIter> xIter(mxTreeView->make_iterator());
+ mxTreeView->append(xIter.get());
+ mxTreeView->set_image(*xIter, rImage, COLUMN_IMG_ISDEFAULT);
+ mxTreeView->set_text(*xIter, rTitle, COLUMN_NAME);
+ mxTreeView->set_text(*xIter, rSubtitle, COLUMN_CATEGORY);
+ mxTreeView->set_text(*xIter, rApplication, COLUMN_APPLICATION);
+ mxTreeView->set_text(*xIter, rModify, COLUMN_MODIFIED);
+ mxTreeView->set_text(*xIter, rSize, COLUMN_SIZE);
+ mxTreeView->set_id(*xIter, rId);
+}
+
+void ListView::UpdateRow(int nIndex, const OUString& rImage, const OUString& rTitle,
+ const OUString& rSubtitle, const OUString& rApplication,
+ const OUString& rModify, const OUString& rSize, const OUString& rId)
+{
+ mxTreeView->set_image(nIndex, rImage, COLUMN_IMG_ISDEFAULT);
+ mxTreeView->set_text(nIndex, rTitle, COLUMN_NAME);
+ mxTreeView->set_text(nIndex, rSubtitle, COLUMN_CATEGORY);
+ mxTreeView->set_text(nIndex, rApplication, COLUMN_APPLICATION);
+ mxTreeView->set_text(nIndex, rModify, COLUMN_MODIFIED);
+ mxTreeView->set_text(nIndex, rSize, COLUMN_SIZE);
+ mxTreeView->set_id(nIndex, rId);
+}
+
+void ListView::ReloadRows()
+{
+ OUString sCursorId = get_id(get_cursor_index());
+ mxTreeView->clear();
+ for (const auto& pItem : mListViewItems)
+ {
+ OUString sImage("");
+ if (pItem->mbDefault)
+ sImage = BMP_DEFAULT;
+ AppendRow(sImage, pItem->maTitle, pItem->maSubtitle, pItem->maApplication,
+ pItem->maDisplayModify, pItem->maDisplaySize, pItem->maId);
+ }
+ unselect_all();
+ if (!sCursorId.isEmpty())
+ {
+ select_id(sCursorId);
+ set_cursor(get_selected_index());
+ }
+}
+
+bool ListView::UpdateRows()
+{
+ if (static_cast<int>(mListViewItems.size()) != mxTreeView->n_children())
+ return false;
+ OUString sCursorId = get_id(get_cursor_index());
+ int nIndex = 0;
+ for (const auto& pItem : mListViewItems)
+ {
+ OUString sImage("");
+ if (pItem->mbDefault)
+ sImage = BMP_DEFAULT;
+ UpdateRow(nIndex, sImage, pItem->maTitle, pItem->maSubtitle, pItem->maApplication,
+ pItem->maDisplayModify, pItem->maDisplaySize, pItem->maId);
+ ++nIndex;
+ }
+ unselect_all();
+ if (!sCursorId.isEmpty())
+ {
+ select_id(sCursorId);
+ set_cursor(get_selected_index());
+ }
+ return true;
+}
+
+IMPL_LINK(ListView, ColumnClickedHdl, const int, col, void)
+{
+ if (col <= 0 || col > NUMBER_OF_COLUMNS)
+ return;
+
+ if (mnSortColumn >= 0 && mnSortColumn != col)
+ mxTreeView->set_sort_indicator(TriState::TRISTATE_INDET, mnSortColumn);
+
+ mxTreeView->set_sort_indicator((mxTreeView->get_sort_indicator(col) == TriState::TRISTATE_TRUE
+ ? TriState::TRISTATE_FALSE
+ : TriState::TRISTATE_TRUE),
+ col);
+ sortColumn(col);
+}
+
+void ListView::sortColumn(const int col)
+{
+ if (col <= 0 || col > NUMBER_OF_COLUMNS)
+ return;
+
+ bool isAscending = mxTreeView->get_sort_indicator(col) != TriState::TRISTATE_FALSE;
+
+ auto comp = [&](std::unique_ptr<ListViewItem> const& pItemA,
+ std::unique_ptr<ListViewItem> const& pItemB) {
+ sal_Int32 res = 0;
+ IntlWrapper aIntlWrapper(SvtSysLocale().GetUILanguageTag());
+ const CollatorWrapper* pCollatorWrapper = aIntlWrapper.getCollator();
+ switch (col)
+ {
+ case COLUMN_NAME:
+ {
+ OUString sNameA = pItemA->maTitle;
+ OUString sNameB = pItemB->maTitle;
+ res = pCollatorWrapper->compareString(sNameA, sNameB);
+ }
+ break;
+ case COLUMN_CATEGORY:
+ {
+ OUString sCategoryA = pItemA->maSubtitle;
+ OUString sCategoryB = pItemB->maSubtitle;
+ res = pCollatorWrapper->compareString(sCategoryA, sCategoryB);
+ }
+ break;
+ case COLUMN_MODIFIED:
+ {
+ sal_uInt32 nModA, nModB;
+ nModA = pItemA->mnModify;
+ nModB = pItemB->mnModify;
+
+ if (nModA < nModB)
+ res = -1;
+ else if (nModA > nModB)
+ res = 1;
+ }
+ break;
+ case COLUMN_SIZE:
+ {
+ sal_uInt64 nSizeA, nSizeB;
+ nSizeA = pItemA->mnSize;
+ nSizeB = pItemB->mnSize;
+
+ if (nSizeA < nSizeB)
+ res = -1;
+ else if (nSizeA > nSizeB)
+ res = 1;
+ }
+ break;
+ case COLUMN_APPLICATION:
+ {
+ OUString sPathA = pItemA->maApplication;
+ OUString sPathB = pItemB->maApplication;
+ res = pCollatorWrapper->compareString(sPathA, sPathB);
+ }
+ break;
+ }
+ return isAscending ? (res > 0) : (res < 0);
+ };
+ std::stable_sort(mListViewItems.begin(), mListViewItems.end(), comp);
+
+ if (!UpdateRows())
+ ReloadRows();
+ mnSortColumn = col;
+}
+
+void ListView::sort() { sortColumn(mnSortColumn); }
+
+void ListView::refreshDefaultColumn()
+{
+ for (const auto& pItem : mListViewItems)
+ {
+ bool bDefault = TemplateLocalView::IsDefaultTemplate(pItem->maPath);
+ if (pItem->mbDefault != bDefault)
+ {
+ pItem->mbDefault = bDefault;
+ OUString sImage("");
+ if (bDefault)
+ sImage = BMP_DEFAULT;
+ mxTreeView->set_image(mxTreeView->find_id(pItem->maId), sImage, COLUMN_IMG_ISDEFAULT);
+ }
+ }
+}
+
+void ListView::rename(const OUString& rId, const OUString& rTitle)
+{
+ mxTreeView->set_text(mxTreeView->find_id(rId), rTitle, COLUMN_NAME);
+ for (const auto& pItem : mListViewItems)
+ if (pItem->maId == rId)
+ {
+ pItem->maTitle = rTitle;
+ break;
+ }
+}
+
+void ListView::clearListView()
+{
+ mxTreeView->clear();
+ mListViewItems.clear();
+}
+
+IMPL_LINK(ListView, QueryTooltipHdl, const weld::TreeIter&, rIter, OUString)
+{
+ OUString sId = mxTreeView->get_id(rIter);
+ for (const auto& pItem : mListViewItems)
+ {
+ if (pItem->maId == sId)
+ return pItem->maDisplayPath;
+ }
+ return OUString();
+}
+
+sal_uInt16 ListView::get_nId(int pos) const
+{
+ return static_cast<sal_uInt16>(mxTreeView->get_id(pos).toInt32());
+}
+
+static sal_uInt32 getFileModifyTime(const OUString& rURL)
+{
+ sal_uInt32 nModify = 0;
+ if (!comphelper::isFileUrl(rURL))
+ return nModify;
+
+ osl::DirectoryItem aItem;
+ if (osl::DirectoryItem::get(rURL, aItem) != osl::DirectoryItem::E_None)
+ return nModify;
+
+ osl::FileStatus aStatus(osl_FileStatus_Mask_ModifyTime);
+ if (aItem.getFileStatus(aStatus) != osl::DirectoryItem::E_None)
+ return nModify;
+
+ TimeValue systemTimeValue = aStatus.getModifyTime();
+
+ nModify = systemTimeValue.Seconds;
+ return nModify;
+}
+static OUString getDisplayFileModifyTime(const OUString& rURL)
+{
+ if (!comphelper::isFileUrl(rURL))
+ return OUString();
+
+ osl::DirectoryItem aItem;
+ if (osl::DirectoryItem::get(rURL, aItem) != osl::DirectoryItem::E_None)
+ return OUString();
+
+ osl::FileStatus aStatus(osl_FileStatus_Mask_ModifyTime);
+ if (aItem.getFileStatus(aStatus) != osl::DirectoryItem::E_None)
+ return OUString();
+
+ TimeValue systemTimeValue = aStatus.getModifyTime();
+ if (systemTimeValue.Seconds == 0)
+ return OUString();
+ TimeValue localTimeValue;
+ osl_getLocalTimeFromSystemTime(&systemTimeValue, &localTimeValue);
+ const SvtSysLocale aSysLocale;
+ const LocaleDataWrapper& rLocaleWrapper = aSysLocale.GetLocaleData();
+ DateTime aDateTime = DateTime::CreateFromUnixTime(localTimeValue.Seconds);
+ OUString aDisplayDateTime
+ = rLocaleWrapper.getDate(aDateTime) + ", " + rLocaleWrapper.getTime(aDateTime, false);
+ return aDisplayDateTime;
+}
+
+static OUString getDisplayFileSize(const OUString& rURL)
+{
+ if (!comphelper::isFileUrl(rURL))
+ return OUString();
+
+ osl::DirectoryItem aItem;
+ if (osl::DirectoryItem::get(rURL, aItem) != osl::DirectoryItem::E_None)
+ return OUString();
+
+ osl::FileStatus aStatus(osl_FileStatus_Mask_FileSize);
+ if (aItem.getFileStatus(aStatus) != osl::DirectoryItem::E_None)
+ return OUString();
+
+ sal_uInt64 nSize = aStatus.getFileSize();
+ double fSize(static_cast<double>(nSize));
+ sal_uInt32 nDec;
+
+ sal_uInt64 nMega = 1024 * 1024;
+ sal_uInt64 nGiga = nMega * 1024;
+
+ OUString aUnitStr(' ');
+
+ if (nSize < 10000)
+ {
+ aUnitStr += SvtResId(STR_SVT_BYTES);
+ nDec = 0;
+ }
+ else if (nSize < nMega)
+ {
+ fSize /= 1024;
+ aUnitStr += SvtResId(STR_SVT_KB);
+ nDec = 1;
+ }
+ else if (nSize < nGiga)
+ {
+ fSize /= nMega;
+ aUnitStr += SvtResId(STR_SVT_MB);
+ nDec = 2;
+ }
+ else
+ {
+ fSize /= nGiga;
+ aUnitStr += SvtResId(STR_SVT_GB);
+ nDec = 3;
+ }
+
+ OUString aSizeStr(
+ ::rtl::math::doubleToUString(fSize, rtl_math_StringFormat_F, nDec,
+ SvtSysLocale().GetLocaleData().getNumDecimalSep()[0]));
+ aSizeStr += aUnitStr;
+
+ return aSizeStr;
+}
+
+static sal_uInt64 getFileSize(const OUString& rURL)
+{
+ sal_uInt64 nSize = 0;
+ if (!comphelper::isFileUrl(rURL))
+ return nSize;
+
+ osl::DirectoryItem aItem;
+ if (osl::DirectoryItem::get(rURL, aItem) != osl::DirectoryItem::E_None)
+ return nSize;
+
+ osl::FileStatus aStatus(osl_FileStatus_Mask_FileSize);
+ if (aItem.getFileStatus(aStatus) != osl::DirectoryItem::E_None)
+ return nSize;
+
+ nSize = aStatus.getFileSize();
+ return nSize;
+}
+
+static OUString getApplication(std::u16string_view rURL)
+{
+ INetURLObject aUrl(rURL);
+ OUString aExt = aUrl.getExtension();
+
+ if (aExt == "ott" || aExt == "stw" || aExt == "oth" || aExt == "dot" || aExt == "dotx")
+ {
+ return SfxResId(STR_DOCUMENT);
+ }
+ else if (aExt == "ots" || aExt == "stc" || aExt == "xlt" || aExt == "xltm" || aExt == "xltx")
+ {
+ return SfxResId(STR_SPREADSHEET);
+ }
+ else if (aExt == "otp" || aExt == "sti" || aExt == "pot" || aExt == "potm" || aExt == "potx")
+ {
+ return SfxResId(STR_PRESENTATION);
+ }
+ else if (aExt == "otg" || aExt == "std")
+ {
+ return SfxResId(STR_DRAWING);
+ }
+ return OUString();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/control/minfitem.cxx b/sfx2/source/control/minfitem.cxx
new file mode 100644
index 000000000..8d8f0a80a
--- /dev/null
+++ b/sfx2/source/control/minfitem.cxx
@@ -0,0 +1,75 @@
+/* -*- 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/minfitem.hxx>
+#include <sal/log.hxx>
+#include <config_features.h>
+
+#if HAVE_FEATURE_SCRIPTING
+
+SfxPoolItem* SfxMacroInfoItem::CreateDefault() { SAL_WARN( "sfx", "No SfxMacroInfItem factory available"); return nullptr; }
+
+
+SfxMacroInfoItem::SfxMacroInfoItem(
+ sal_uInt16 nWhichId, // Slot-ID
+ const BasicManager* pMgr,
+ const OUString &rLibName,
+ const OUString &rModuleName,
+ const OUString &rMethodName,
+ const OUString &rComment) :
+ SfxPoolItem(nWhichId),
+ pBasicManager(pMgr),
+ aLibName(rLibName),
+ aModuleName(rModuleName),
+ aMethodName(rMethodName),
+ aCommentText(rComment)
+{
+}
+
+// op ==
+
+bool SfxMacroInfoItem::operator==( const SfxPoolItem& rCmp) const
+{
+ const SfxMacroInfoItem rItem = static_cast<const SfxMacroInfoItem&>(rCmp);
+ return SfxPoolItem::operator==(rCmp) &&
+ pBasicManager == rItem.pBasicManager &&
+ aLibName == rItem.aLibName &&
+ aModuleName == rItem.aModuleName &&
+ aMethodName == rItem.aMethodName &&
+ aCommentText == rItem.aCommentText;
+}
+
+SfxMacroInfoItem* SfxMacroInfoItem::Clone( SfxItemPool *) const
+{
+ return new SfxMacroInfoItem(*this);
+}
+
+OUString SfxMacroInfoItem::GetQualifiedName() const
+{
+ OUString aMacroName = aLibName +
+ "." +
+ aModuleName +
+ "." +
+ aMethodName;
+ return aMacroName;
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/control/msg.cxx b/sfx2/source/control/msg.cxx
new file mode 100644
index 000000000..c6ed821c1
--- /dev/null
+++ b/sfx2/source/control/msg.cxx
@@ -0,0 +1,56 @@
+/* -*- 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/itempool.hxx>
+#include <sfx2/msg.hxx>
+
+#include <climits>
+
+SfxSlotKind SfxSlot::GetKind() const
+{
+ if( !nMasterSlotId && !nValue)
+ return SfxSlotKind::Standard;
+ if ( nMasterSlotId && fnExec==nullptr && fnState==nullptr )
+ {
+ assert(false);
+ return SfxSlotKind::Standard;
+ }
+ else
+ return SfxSlotKind::Attribute;
+}
+
+
+sal_uInt16 SfxSlot::GetWhich( const SfxItemPool &rPool ) const
+{
+ if ( !nMasterSlotId || nMasterSlotId == USHRT_MAX )
+ const_cast<SfxSlot*>(this) -> nMasterSlotId = rPool.GetWhich(nSlotId);
+ return nMasterSlotId;
+}
+
+OString SfxSlot::GetCommand() const
+{
+ return OString::Concat(".uno:") + pUnoName;
+}
+
+OUString SfxSlot::GetCommandString() const
+{
+ return OStringToOUString(GetCommand(), RTL_TEXTENCODING_UTF8);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/control/msgpool.cxx b/sfx2/source/control/msgpool.cxx
new file mode 100644
index 000000000..f7a94203e
--- /dev/null
+++ b/sfx2/source/control/msgpool.cxx
@@ -0,0 +1,326 @@
+/* -*- 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/log.hxx>
+#include <osl/diagnose.h>
+
+// due to pSlotPool
+#include <appdata.hxx>
+#include <sfx2/msgpool.hxx>
+#include <sfx2/msg.hxx>
+#include <sfx2/app.hxx>
+#include <sfx2/objface.hxx>
+#include <sfx2/sfxresid.hxx>
+#include <sfx2/module.hxx>
+
+#include <sfx2/strings.hrc>
+
+SfxSlotPool::SfxSlotPool(SfxSlotPool *pParent)
+ : _pParentPool( pParent )
+ , _nCurGroup(0)
+ , _nCurInterface(0)
+ , _nCurMsg(0)
+{
+}
+
+SfxSlotPool::~SfxSlotPool()
+{
+ _pParentPool = nullptr;
+ // swap out _vInterfaces because ~SfxInterface() might call ReleaseInterface()
+ std::vector<SfxInterface*> tmpInterfaces;
+ tmpInterfaces.swap(_vInterfaces);
+ for ( SfxInterface *pIF : tmpInterfaces )
+ delete pIF;
+}
+
+namespace
+{
+ TranslateId getGidResId(SfxGroupId nId)
+ {
+ if (nId == SfxGroupId::Intern)
+ return STR_GID_INTERN;
+ else if (nId == SfxGroupId::Application)
+ return STR_GID_APPLICATION;
+ else if (nId == SfxGroupId::View)
+ return STR_GID_VIEW;
+ else if (nId == SfxGroupId::Document)
+ return STR_GID_DOCUMENT;
+ else if (nId == SfxGroupId::Edit)
+ return STR_GID_EDIT;
+ else if (nId == SfxGroupId::Macro)
+ return STR_GID_MACRO;
+ else if (nId == SfxGroupId::Options)
+ return STR_GID_OPTIONS;
+ else if (nId == SfxGroupId::Math)
+ return STR_GID_MATH;
+ else if (nId == SfxGroupId::Navigator)
+ return STR_GID_NAVIGATOR;
+ else if (nId == SfxGroupId::Insert)
+ return STR_GID_INSERT;
+ else if (nId == SfxGroupId::Format)
+ return STR_GID_FORMAT;
+ else if (nId == SfxGroupId::Template)
+ return STR_GID_TEMPLATE;
+ else if (nId == SfxGroupId::Text)
+ return STR_GID_TEXT;
+ else if (nId == SfxGroupId::Frame)
+ return STR_GID_FRAME;
+ else if (nId == SfxGroupId::Graphic)
+ return STR_GID_GRAPHIC;
+ else if (nId == SfxGroupId::Table)
+ return STR_GID_TABLE;
+ else if (nId == SfxGroupId::Enumeration)
+ return STR_GID_ENUMERATION;
+ else if (nId == SfxGroupId::Data)
+ return STR_GID_DATA;
+ else if (nId == SfxGroupId::Special)
+ return STR_GID_SPECIAL;
+ else if (nId == SfxGroupId::Image)
+ return STR_GID_IMAGE;
+ else if (nId == SfxGroupId::Chart)
+ return STR_GID_CHART;
+ else if (nId == SfxGroupId::Explorer)
+ return STR_GID_EXPLORER;
+ else if (nId == SfxGroupId::Connector)
+ return STR_GID_CONNECTOR;
+ else if (nId == SfxGroupId::Modify)
+ return STR_GID_MODIFY;
+ else if (nId == SfxGroupId::Drawing)
+ return STR_GID_DRAWING;
+ else if (nId == SfxGroupId::Controls)
+ return STR_GID_CONTROLS;
+ return {};
+ }
+}
+
+// registers the availability of the Interface of functions
+
+void SfxSlotPool::RegisterInterface( SfxInterface& rInterface )
+{
+ // add to the list of SfxObjectInterface instances
+ _vInterfaces.push_back(&rInterface);
+
+ // Stop at a (single) Null-slot (for syntactic reasons the interfaces
+ // always contain at least one slot)
+ if ( rInterface.Count() != 0 && !rInterface.pSlots[0].nSlotId )
+ return;
+
+ // possibly add Interface-id and group-ids of funcs to the list of groups
+ if ( _pParentPool )
+ {
+ // The Groups in parent Slotpool are also known here
+ _vGroups.insert( _vGroups.end(), _pParentPool->_vGroups.begin(), _pParentPool->_vGroups.end() );
+ }
+
+ for ( size_t nFunc = 0; nFunc < rInterface.Count(); ++nFunc )
+ {
+ SfxSlot &rDef = rInterface.pSlots[nFunc];
+ if ( rDef.GetGroupId() != SfxGroupId::NONE &&
+ std::find(_vGroups.begin(), _vGroups.end(), rDef.GetGroupId()) == _vGroups.end() )
+ {
+ if (rDef.GetGroupId() == SfxGroupId::Intern)
+ _vGroups.insert(_vGroups.begin(), rDef.GetGroupId());
+ else
+ _vGroups.push_back(rDef.GetGroupId());
+ }
+ }
+}
+
+
+const std::type_info* SfxSlotPool::GetSlotType( sal_uInt16 nId ) const
+{
+ const SfxSlot* pSlot = GetSlot( nId );
+ return pSlot ? pSlot->GetType()->Type() : nullptr;
+}
+
+
+// get the first SfxMessage for a special Id (e.g. for getting check-mode)
+
+const SfxSlot* SfxSlotPool::GetSlot( sal_uInt16 nId ) const
+{
+ // First, search their own interfaces
+ for (SfxInterface* _pInterface : _vInterfaces)
+ {
+ const SfxSlot *pDef = _pInterface->GetSlot(nId);
+ if ( pDef )
+ return pDef;
+ }
+
+ // Then try any of the possible existing parent
+ return _pParentPool ? _pParentPool->GetSlot( nId ) : nullptr;
+}
+
+
+// skips to the next group
+
+OUString SfxSlotPool::SeekGroup( sal_uInt16 nNo )
+{
+ // if the group exists, use it
+ if ( nNo < _vGroups.size() )
+ {
+ _nCurGroup = nNo;
+ if ( _pParentPool )
+ {
+ // In most cases, the order of the IDs agree
+ sal_uInt16 nParentCount = _pParentPool->_vGroups.size();
+ if ( nNo < nParentCount && _vGroups[nNo] == _pParentPool->_vGroups[nNo] )
+ _pParentPool->_nCurGroup = nNo;
+ else
+ {
+ // Otherwise search. If the group is not found in the parent
+ // pool, _nCurGroup is set outside the valid range
+ sal_uInt16 i;
+ for ( i=1; i<nParentCount; i++ )
+ if ( _vGroups[nNo] == _pParentPool->_vGroups[i] )
+ break;
+ _pParentPool->_nCurGroup = i;
+ }
+ }
+
+ TranslateId pResId = getGidResId(_vGroups[_nCurGroup]);
+ if (!pResId)
+ {
+ OSL_FAIL( "GroupId-Name not defined in SFX!" );
+ return OUString();
+ }
+
+ return SfxResId(pResId);
+ }
+
+ return OUString();
+}
+
+
+sal_uInt16 SfxSlotPool::GetGroupCount() const
+{
+ return _vGroups.size();
+}
+
+
+// internal search loop
+
+const SfxSlot* SfxSlotPool::SeekSlot( sal_uInt16 nStartInterface )
+{
+ // The numbering starts at the interfaces of the parent pool
+ sal_uInt16 nFirstInterface = _pParentPool ? _pParentPool->_vInterfaces.size() : 0;
+
+ // have reached the end of the Parent-Pools?
+ if ( nStartInterface < nFirstInterface &&
+ _pParentPool->_nCurGroup >= _pParentPool->_vGroups.size() )
+ nStartInterface = nFirstInterface;
+
+ // Is the Interface still in the Parent-Pool?
+ if ( nStartInterface < nFirstInterface )
+ {
+ SAL_WARN_IF(!_pParentPool, "sfx.control", "No parent pool!");
+ _nCurInterface = nStartInterface;
+ return _pParentPool->SeekSlot( nStartInterface );
+ }
+
+ // find the first func-def with the current group id
+ sal_uInt16 nCount = _vInterfaces.size() + nFirstInterface;
+ for ( _nCurInterface = nStartInterface;
+ _nCurInterface < nCount;
+ ++_nCurInterface )
+ {
+ SfxInterface* pInterface = _vInterfaces[_nCurInterface-nFirstInterface];
+ for ( _nCurMsg = 0;
+ _nCurMsg < pInterface->Count();
+ ++_nCurMsg )
+ {
+ const SfxSlot& rMsg = pInterface->pSlots[_nCurMsg];
+ if (rMsg.GetGroupId() == _vGroups.at(_nCurGroup))
+ return &rMsg;
+ }
+ }
+
+ return nullptr;
+}
+
+
+// skips to the next func in the current group
+
+const SfxSlot* SfxSlotPool::NextSlot()
+{
+ // The numbering starts at the interfaces of the parent pool
+ sal_uInt16 nFirstInterface = _pParentPool ? _pParentPool->_vInterfaces.size() : 0;
+
+ if ( _nCurInterface < nFirstInterface && _nCurGroup >= _pParentPool->_vGroups.size() )
+ _nCurInterface = nFirstInterface;
+
+ if ( _nCurInterface < nFirstInterface )
+ {
+ SAL_WARN_IF(!_pParentPool, "sfx.control", "No parent pool!");
+ const SfxSlot *pSlot = _pParentPool->NextSlot();
+ _nCurInterface = _pParentPool->_nCurInterface;
+ if ( pSlot )
+ return pSlot;
+ if ( _nCurInterface == nFirstInterface )
+ // parent pool is ready
+ return SeekSlot( nFirstInterface );
+ }
+
+ sal_uInt16 nInterface = _nCurInterface - nFirstInterface;
+ // possibly we are already at the end
+ if ( nInterface >= _vInterfaces.size() )
+ return nullptr;
+
+ // look for further matching func-defs within the same Interface
+ SfxInterface* pInterface = _vInterfaces[nInterface];
+ while ( ++_nCurMsg < pInterface->Count() )
+ {
+ SfxSlot& rMsg = pInterface->pSlots[_nCurMsg];
+ if (rMsg.GetGroupId() == _vGroups.at(_nCurGroup))
+ return &rMsg;
+ }
+
+ return SeekSlot(++_nCurInterface );
+}
+
+
+// Query SlotName with help text
+
+
+const SfxSlot* SfxSlotPool::GetUnoSlot( const OUString& rName ) const
+{
+ const SfxSlot *pSlot = nullptr;
+ for (auto const & nInterface: _vInterfaces)
+ {
+ pSlot = nInterface->GetSlot( rName );
+ if ( pSlot )
+ break;
+ }
+
+ if ( !pSlot && _pParentPool )
+ pSlot = _pParentPool->GetUnoSlot( rName );
+
+ return pSlot;
+}
+
+SfxSlotPool& SfxSlotPool::GetSlotPool( SfxViewFrame *pFrame )
+{
+ SfxModule *pMod = SfxModule::GetActiveModule( pFrame );
+ if ( pMod && pMod->GetSlotPool() )
+ return *pMod->GetSlotPool();
+ else
+ return *SfxGetpApp()->Get_Impl()->pSlotPool;
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/control/objface.cxx b/sfx2/source/control/objface.cxx
new file mode 100644
index 000000000..5938b68f0
--- /dev/null
+++ b/sfx2/source/control/objface.cxx
@@ -0,0 +1,443 @@
+/* -*- 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 <assert.h>
+#include <stdlib.h>
+
+#include <sal/log.hxx>
+
+#include <sfx2/module.hxx>
+#include <sfx2/objface.hxx>
+#include <sfx2/msg.hxx>
+#include <sfx2/app.hxx>
+#include <sfx2/msgpool.hxx>
+
+extern "C" {
+
+static int
+SfxCompareSlots_qsort( const void* pSmaller, const void* pBigger )
+{
+ return static_cast<int>(static_cast<SfxSlot const *>(pSmaller)->GetSlotId()) -
+ static_cast<int>(static_cast<SfxSlot const *>(pBigger)->GetSlotId());
+}
+
+static int
+SfxCompareSlots_bsearch( const void* pSmaller, const void* pBigger )
+{
+ return static_cast<int>(*static_cast<sal_uInt16 const *>(pSmaller)) -
+ static_cast<int>(static_cast<SfxSlot const *>(pBigger)->GetSlotId());
+}
+
+}
+
+namespace {
+
+struct SfxObjectUI_Impl
+{
+ sal_uInt16 nPos;
+ SfxVisibilityFlags nFlags;
+ sal_uInt32 nObjId;
+ bool bContext;
+ SfxShellFeature nFeature;
+
+ SfxObjectUI_Impl(sal_uInt16 n, SfxVisibilityFlags f, sal_uInt32 nId, SfxShellFeature nFeat) :
+ nPos(n),
+ nFlags(f),
+ nObjId(nId),
+ bContext(false),
+ nFeature(nFeat)
+ {
+ }
+};
+
+}
+
+struct SfxInterface_Impl
+{
+ std::vector<SfxObjectUI_Impl>
+ aObjectBars; // registered ObjectBars
+ std::vector<SfxObjectUI_Impl>
+ aChildWindows; // registered ChildWindows
+ OUString aPopupName; // registered PopupMenu
+ StatusBarId eStatBarResId; // registered StatusBar
+
+ SfxInterface_Impl()
+ : eStatBarResId(StatusBarId::None)
+ {
+ }
+};
+
+static SfxObjectUI_Impl CreateObjectBarUI_Impl(sal_uInt16 nPos, SfxVisibilityFlags nFlags, ToolbarId eId, SfxShellFeature nFeature);
+
+// constructor, registers a new unit
+SfxInterface::SfxInterface( const char *pClassName,
+ bool bUsableSuperClass,
+ SfxInterfaceId nId,
+ const SfxInterface* pParent,
+ SfxSlot &rSlotMap, sal_uInt16 nSlotCount ):
+ pName(pClassName),
+ pGenoType(pParent),
+ nClassId(nId),
+ bSuperClass(bUsableSuperClass),
+ pImplData(new SfxInterface_Impl)
+{
+ SetSlotMap( rSlotMap, nSlotCount );
+}
+
+void SfxInterface::Register( const SfxModule* pMod )
+{
+ if ( pMod )
+ pMod->GetSlotPool()->RegisterInterface(*this);
+ else
+ SfxGetpApp()->GetAppSlotPool_Impl().RegisterInterface(*this);
+}
+
+void SfxInterface::SetSlotMap( SfxSlot& rSlotMap, sal_uInt16 nSlotCount )
+{
+ pSlots = &rSlotMap;
+ nCount = nSlotCount;
+ SfxSlot* pIter = pSlots;
+ if ( 1 == nCount && !pIter->pNextSlot )
+ pIter->pNextSlot = pIter;
+
+ if ( !pIter->pNextSlot )
+ {
+ // sort the SfxSlots by id
+ qsort( pSlots, nCount, sizeof(SfxSlot), SfxCompareSlots_qsort );
+
+ // link masters and slaves
+ sal_uInt16 nIter = 1;
+ for ( pIter = pSlots; nIter <= nCount; ++pIter, ++nIter )
+ {
+
+ assert( nIter == nCount ||
+ pIter->GetSlotId() != (pIter+1)->GetSlotId() );
+
+ if ( nullptr == pIter->GetNextSlot() )
+ {
+ // Slots referring in circle to the next with the same
+ // Status method.
+ SfxSlot *pLastSlot = pIter;
+ for ( sal_uInt16 n = nIter; n < Count(); ++n )
+ {
+ SfxSlot *pCurSlot = pSlots+n;
+ if ( pCurSlot->GetStateFnc() == pIter->GetStateFnc() )
+ {
+ pLastSlot->pNextSlot = pCurSlot;
+ pLastSlot = pCurSlot;
+ }
+ }
+ pLastSlot->pNextSlot = pIter;
+ }
+ }
+ }
+#ifdef DBG_UTIL
+ else
+ {
+ sal_uInt16 nIter = 1;
+ for ( SfxSlot *pNext = pIter+1; nIter < nCount; ++pNext, ++nIter )
+ {
+
+ if ( pNext->GetSlotId() <= pIter->GetSlotId() )
+ SAL_WARN( "sfx.control", "Wrong order" );
+
+ const SfxSlot *pCurSlot = pIter;
+ do
+ {
+ pCurSlot = pCurSlot->pNextSlot;
+ if ( pCurSlot->GetStateFnc() != pIter->GetStateFnc() )
+ {
+ SAL_WARN("sfx.control", "Linked Slots with different State Methods : "
+ << pCurSlot->GetSlotId()
+ << " , " << pIter->GetSlotId() );
+ }
+ }
+ while ( pCurSlot != pIter );
+
+ pIter = pNext;
+ }
+ }
+#endif
+}
+
+
+SfxInterface::~SfxInterface()
+{
+}
+
+
+// searches for the specified func
+
+const SfxSlot* SfxInterface::GetSlot( sal_uInt16 nFuncId ) const
+{
+
+ assert( pSlots );
+ assert( nCount );
+
+ // find the id using binary search
+ void* p = bsearch( &nFuncId, pSlots, nCount, sizeof(SfxSlot),
+ SfxCompareSlots_bsearch );
+ if ( !p && pGenoType )
+ return pGenoType->GetSlot( nFuncId );
+
+ return static_cast<const SfxSlot*>(p);
+}
+
+const SfxSlot* SfxInterface::GetSlot( const OUString& rCommand ) const
+{
+ static const char UNO_COMMAND[] = ".uno:";
+
+ OUString aCommand( rCommand );
+ if ( aCommand.startsWith( UNO_COMMAND ) )
+ aCommand = aCommand.copy( sizeof( UNO_COMMAND )-1 );
+
+ for ( sal_uInt16 n=0; n<nCount; n++ )
+ {
+ if ( (pSlots+n)->pUnoName &&
+ aCommand.compareToIgnoreAsciiCaseAscii( (pSlots+n)->GetUnoName() ) == 0 )
+ return pSlots+n;
+ }
+
+ return pGenoType ? pGenoType->GetSlot( aCommand ) : nullptr;
+}
+
+
+const SfxSlot* SfxInterface::GetRealSlot( const SfxSlot *pSlot ) const
+{
+
+ assert( pSlots );
+ assert( nCount );
+
+ if ( !ContainsSlot_Impl(pSlot) )
+ {
+ if(pGenoType)
+ return pGenoType->GetRealSlot(pSlot);
+ SAL_WARN( "sfx.control", "unknown Slot" );
+ return nullptr;
+ }
+
+ return nullptr;
+}
+
+
+void SfxInterface::RegisterPopupMenu( const OUString& rResourceName )
+{
+ pImplData->aPopupName = rResourceName;
+}
+
+void SfxInterface::RegisterObjectBar(sal_uInt16 nPos, SfxVisibilityFlags nFlags, ToolbarId eId)
+{
+ RegisterObjectBar(nPos, nFlags, eId, SfxShellFeature::NONE);
+}
+
+void SfxInterface::RegisterObjectBar(sal_uInt16 nPos, SfxVisibilityFlags nFlags, ToolbarId eId, SfxShellFeature nFeature)
+{
+ pImplData->aObjectBars.emplace_back( CreateObjectBarUI_Impl(nPos, nFlags, eId, nFeature) );
+}
+
+SfxObjectUI_Impl CreateObjectBarUI_Impl(sal_uInt16 nPos, SfxVisibilityFlags nFlags, ToolbarId eId, SfxShellFeature nFeature)
+{
+ if (nFlags == SfxVisibilityFlags::Invisible)
+ nFlags |= SfxVisibilityFlags::Standard;
+
+ return SfxObjectUI_Impl(nPos, nFlags, static_cast<sal_uInt32>(eId), nFeature);
+}
+
+ToolbarId SfxInterface::GetObjectBarId(sal_uInt16 nNo) const
+{
+ bool bGenoType = (pGenoType != nullptr && pGenoType->UseAsSuperClass());
+ if ( bGenoType )
+ {
+ // Are there toolbars in the super class?
+ sal_uInt16 nBaseCount = pGenoType->GetObjectBarCount();
+ if ( nNo < nBaseCount )
+ // The Super class comes first
+ return pGenoType->GetObjectBarId(nNo);
+ else
+ nNo = nNo - nBaseCount;
+ }
+
+ assert( nNo<pImplData->aObjectBars.size() );
+
+ return static_cast<ToolbarId>(pImplData->aObjectBars[nNo].nObjId);
+}
+
+sal_uInt16 SfxInterface::GetObjectBarPos( sal_uInt16 nNo ) const
+{
+ bool bGenoType = (pGenoType != nullptr && pGenoType->UseAsSuperClass());
+ if ( bGenoType )
+ {
+ // Are there toolbars in the super class?
+ sal_uInt16 nBaseCount = pGenoType->GetObjectBarCount();
+ if ( nNo < nBaseCount )
+ // The Super class comes first
+ return pGenoType->GetObjectBarPos( nNo );
+ else
+ nNo = nNo - nBaseCount;
+ }
+
+ assert( nNo<pImplData->aObjectBars.size() );
+
+ return pImplData->aObjectBars[nNo].nPos;
+}
+
+SfxVisibilityFlags SfxInterface::GetObjectBarFlags( sal_uInt16 nNo ) const
+{
+ bool bGenoType = (pGenoType != nullptr && pGenoType->UseAsSuperClass());
+ if ( bGenoType )
+ {
+ // Are there toolbars in the super class?
+ sal_uInt16 nBaseCount = pGenoType->GetObjectBarCount();
+ if ( nNo < nBaseCount )
+ // The Super class comes first
+ return pGenoType->GetObjectBarFlags( nNo );
+ else
+ nNo = nNo - nBaseCount;
+ }
+
+ assert( nNo<pImplData->aObjectBars.size() );
+
+ return pImplData->aObjectBars[nNo].nFlags;
+}
+
+sal_uInt16 SfxInterface::GetObjectBarCount() const
+{
+ if (pGenoType && pGenoType->UseAsSuperClass())
+ return pImplData->aObjectBars.size() + pGenoType->GetObjectBarCount();
+ else
+ return pImplData->aObjectBars.size();
+}
+
+void SfxInterface::RegisterChildWindow(sal_uInt16 nId, bool bContext)
+{
+ RegisterChildWindow(nId, bContext, SfxShellFeature::NONE);
+}
+
+void SfxInterface::RegisterChildWindow(sal_uInt16 nId, bool bContext, SfxShellFeature nFeature)
+{
+ SfxObjectUI_Impl aUI(0, SfxVisibilityFlags::Invisible, nId, nFeature);
+ aUI.bContext = bContext;
+ pImplData->aChildWindows.emplace_back(aUI);
+}
+
+void SfxInterface::RegisterStatusBar(StatusBarId eId)
+{
+ pImplData->eStatBarResId = eId;
+}
+
+sal_uInt32 SfxInterface::GetChildWindowId (sal_uInt16 nNo) const
+{
+ if ( pGenoType )
+ {
+ // Are there ChildWindows in the superclass?
+ sal_uInt16 nBaseCount = pGenoType->GetChildWindowCount();
+ if ( nNo < nBaseCount )
+ // The Super class comes first
+ return pGenoType->GetChildWindowId( nNo );
+ else
+ nNo = nNo - nBaseCount;
+ }
+
+ assert( nNo<pImplData->aChildWindows.size() );
+
+ sal_uInt32 nRet = pImplData->aChildWindows[nNo].nObjId;
+ if ( pImplData->aChildWindows[nNo].bContext )
+ nRet += sal_uInt16( nClassId ) << 16;
+ return nRet;
+}
+
+SfxShellFeature SfxInterface::GetChildWindowFeature (sal_uInt16 nNo) const
+{
+ if ( pGenoType )
+ {
+ // Are there ChildWindows in the superclass?
+ sal_uInt16 nBaseCount = pGenoType->GetChildWindowCount();
+ if ( nNo < nBaseCount )
+ // The Super class comes first
+ return pGenoType->GetChildWindowFeature( nNo );
+ else
+ nNo = nNo - nBaseCount;
+ }
+
+ assert( nNo<pImplData->aChildWindows.size() );
+
+ return pImplData->aChildWindows[nNo].nFeature;
+}
+
+
+sal_uInt16 SfxInterface::GetChildWindowCount() const
+{
+ if (pGenoType)
+ return pImplData->aChildWindows.size() + pGenoType->GetChildWindowCount();
+ else
+ return pImplData->aChildWindows.size();
+}
+
+const OUString& SfxInterface::GetPopupMenuName() const
+{
+ return pImplData->aPopupName;
+}
+
+StatusBarId SfxInterface::GetStatusBarId() const
+{
+ if (pImplData->eStatBarResId == StatusBarId::None && pGenoType)
+ return pGenoType->GetStatusBarId();
+ else
+ return pImplData->eStatBarResId;
+}
+
+SfxShellFeature SfxInterface::GetObjectBarFeature ( sal_uInt16 nNo ) const
+{
+ bool bGenoType = (pGenoType != nullptr && pGenoType->UseAsSuperClass());
+ if ( bGenoType )
+ {
+ // Are there toolbars in the super class?
+ sal_uInt16 nBaseCount = pGenoType->GetObjectBarCount();
+ if ( nNo < nBaseCount )
+ // The Super class comes first
+ return pGenoType->GetObjectBarFeature( nNo );
+ else
+ nNo = nNo - nBaseCount;
+ }
+
+ assert( nNo<pImplData->aObjectBars.size() );
+
+ return pImplData->aObjectBars[nNo].nFeature;
+}
+
+bool SfxInterface::IsObjectBarVisible(sal_uInt16 nNo) const
+{
+ bool bGenoType = (pGenoType != nullptr && pGenoType->UseAsSuperClass());
+ if ( bGenoType )
+ {
+ // Are there toolbars in the super class?
+ sal_uInt16 nBaseCount = pGenoType->GetObjectBarCount();
+ if ( nNo < nBaseCount )
+ // The Super class comes first
+ return pGenoType->IsObjectBarVisible( nNo );
+ else
+ nNo = nNo - nBaseCount;
+ }
+
+ assert( nNo<pImplData->aObjectBars.size() );
+
+ return true;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/control/recentdocsview.cxx b/sfx2/source/control/recentdocsview.cxx
new file mode 100644
index 000000000..b7a6ba4ad
--- /dev/null
+++ b/sfx2/source/control/recentdocsview.cxx
@@ -0,0 +1,314 @@
+/* -*- 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/log.hxx>
+#include <recentdocsview.hxx>
+#include <sfx2/sfxresid.hxx>
+#include <unotools/historyoptions.hxx>
+#include <vcl/event.hxx>
+#include <vcl/ptrstyle.hxx>
+#include <vcl/svapp.hxx>
+#include <tools/urlobj.hxx>
+#include <com/sun/star/frame/XDispatch.hpp>
+#include <sfx2/strings.hrc>
+#include <bitmaps.hlst>
+#include "recentdocsviewitem.hxx"
+#include <sfx2/app.hxx>
+
+#include <officecfg/Office/Common.hxx>
+
+#include <map>
+
+using namespace ::com::sun::star;
+using namespace com::sun::star::uno;
+using namespace com::sun::star::lang;
+using namespace com::sun::star::beans;
+
+namespace {
+
+/// Set (larger) font for the Welcome message.
+void SetMessageFont(vcl::RenderContext& rRenderContext)
+{
+ vcl::Font aFont(rRenderContext.GetFont());
+ aFont.SetFontHeight(aFont.GetFontHeight() * 1.3);
+ rRenderContext.SetFont(aFont);
+}
+
+}
+
+namespace sfx2
+{
+
+constexpr tools::Long gnTextHeight = 30;
+constexpr tools::Long gnItemPadding = 5;
+
+RecentDocsView::RecentDocsView(std::unique_ptr<weld::ScrolledWindow> xWindow, std::unique_ptr<weld::Menu> xMenu)
+ : ThumbnailView(std::move(xWindow), std::move(xMenu))
+ , mnFileTypes(ApplicationType::TYPE_NONE)
+ , mnLastMouseDownItem(THUMBNAILVIEW_ITEM_NOTFOUND)
+ , maWelcomeLine1(SfxResId(STR_WELCOME_LINE1))
+ , maWelcomeLine2(SfxResId(STR_WELCOME_LINE2))
+ , mpLoadRecentFile(nullptr)
+ , m_nExecuteHdlId(nullptr)
+{
+ tools::Rectangle aScreen = Application::GetScreenPosSizePixel(Application::GetDisplayBuiltInScreen());
+ mnItemMaxSize = std::min(aScreen.GetWidth(),aScreen.GetHeight()) > 800 ? 256 : 192;
+
+ setItemMaxTextLength( 30 );
+ setItemDimensions( mnItemMaxSize, mnItemMaxSize, gnTextHeight, gnItemPadding );
+
+ maFillColor = Color(ColorTransparency, officecfg::Office::Common::Help::StartCenter::StartCenterThumbnailsBackgroundColor::get());
+ maTextColor = Color(ColorTransparency, officecfg::Office::Common::Help::StartCenter::StartCenterThumbnailsTextColor::get());
+ maHighlightColor = Color(ColorTransparency, officecfg::Office::Common::Help::StartCenter::StartCenterThumbnailsHighlightColor::get());
+ maHighlightTextColor = Color(ColorTransparency, officecfg::Office::Common::Help::StartCenter::StartCenterThumbnailsHighlightTextColor::get());
+ mfHighlightTransparence = 0.25;
+
+ UpdateColors();
+}
+
+RecentDocsView::~RecentDocsView()
+{
+ Application::RemoveUserEvent(m_nExecuteHdlId);
+ m_nExecuteHdlId = nullptr;
+ if (mpLoadRecentFile)
+ {
+ mpLoadRecentFile->pView = nullptr;
+ mpLoadRecentFile = nullptr;
+ }
+}
+
+bool RecentDocsView::typeMatchesExtension(ApplicationType type, std::u16string_view rExt)
+{
+ bool bRet = false;
+
+ if (rExt == u"odt" || rExt == u"fodt" || rExt == u"doc" || rExt == u"docx" ||
+ rExt == u"rtf" || rExt == u"txt" || rExt == u"odm" || rExt == u"otm")
+ {
+ bRet = static_cast<bool>(type & ApplicationType::TYPE_WRITER);
+ }
+ else if (rExt == u"ods" || rExt == u"fods" || rExt == u"xls" || rExt == u"xlsx")
+ {
+ bRet = static_cast<bool>(type & ApplicationType::TYPE_CALC);
+ }
+ else if (rExt == u"odp" || rExt == u"fodp" || rExt == u"pps" || rExt == u"ppt" ||
+ rExt == u"pptx")
+ {
+ bRet = static_cast<bool>(type & ApplicationType::TYPE_IMPRESS);
+ }
+ else if (rExt == u"odg" || rExt == u"fodg")
+ {
+ bRet = static_cast<bool>(type & ApplicationType::TYPE_DRAW);
+ }
+ else if (rExt == u"odb")
+ {
+ bRet = static_cast<bool>(type & ApplicationType::TYPE_DATABASE);
+ }
+ else if (rExt == u"odf")
+ {
+ bRet = static_cast<bool>(type & ApplicationType::TYPE_MATH);
+ }
+ else
+ {
+ bRet = static_cast<bool>(type & ApplicationType::TYPE_OTHER);
+ }
+
+ return bRet;
+}
+
+bool RecentDocsView::isAcceptedFile(const INetURLObject& rURL) const
+{
+ const OUString aExt = rURL.getExtension();
+ return (mnFileTypes & ApplicationType::TYPE_WRITER && typeMatchesExtension(ApplicationType::TYPE_WRITER, aExt)) ||
+ (mnFileTypes & ApplicationType::TYPE_CALC && typeMatchesExtension(ApplicationType::TYPE_CALC, aExt)) ||
+ (mnFileTypes & ApplicationType::TYPE_IMPRESS && typeMatchesExtension(ApplicationType::TYPE_IMPRESS, aExt)) ||
+ (mnFileTypes & ApplicationType::TYPE_DRAW && typeMatchesExtension(ApplicationType::TYPE_DRAW, aExt)) ||
+ (mnFileTypes & ApplicationType::TYPE_DATABASE && typeMatchesExtension(ApplicationType::TYPE_DATABASE,aExt)) ||
+ (mnFileTypes & ApplicationType::TYPE_MATH && typeMatchesExtension(ApplicationType::TYPE_MATH, aExt)) ||
+ (mnFileTypes & ApplicationType::TYPE_OTHER && typeMatchesExtension(ApplicationType::TYPE_OTHER, aExt));
+}
+
+void RecentDocsView::insertItem(const OUString &rURL, const OUString &rTitle, const OUString& rThumbnail, bool isReadOnly, sal_uInt16 nId)
+{
+ AppendItem( std::make_unique<RecentDocsViewItem>(*this, rURL, rTitle, rThumbnail, nId, mnItemMaxSize, isReadOnly) );
+}
+
+void RecentDocsView::Reload()
+{
+ Clear();
+
+ std::vector< SvtHistoryOptions::HistoryItem > aHistoryList = SvtHistoryOptions::GetList( EHistoryType::PickList );
+ for ( size_t i = 0; i < aHistoryList.size(); i++ )
+ {
+ const SvtHistoryOptions::HistoryItem& rRecentEntry = aHistoryList[i];
+
+ OUString aURL = rRecentEntry.sURL;
+ const INetURLObject aURLObj(aURL);
+
+ if (!isAcceptedFile(aURLObj))
+ continue;
+
+ //Remove extension from url's last segment and use it as title
+ const OUString aTitle = aURLObj.GetBase(); //DecodeMechanism::WithCharset
+
+ insertItem(aURL, aTitle, rRecentEntry.sThumbnail, rRecentEntry.isReadOnly, i+1);
+ }
+
+ CalculateItemPositions();
+ Invalidate();
+}
+
+bool RecentDocsView::MouseButtonDown( const MouseEvent& rMEvt )
+{
+ if (rMEvt.IsLeft())
+ {
+ mnLastMouseDownItem = ImplGetItem(rMEvt.GetPosPixel());
+
+ // ignore to avoid stuff done in ThumbnailView; we don't do selections etc.
+ return true;
+ }
+
+ return ThumbnailView::MouseButtonDown(rMEvt);
+}
+
+bool RecentDocsView::MouseButtonUp(const MouseEvent& rMEvt)
+{
+ if (rMEvt.IsLeft())
+ {
+ if( rMEvt.GetClicks() > 1 )
+ return true;
+
+ size_t nPos = ImplGetItem(rMEvt.GetPosPixel());
+ ThumbnailViewItem* pItem = ImplGetItem(nPos);
+
+ if (pItem && nPos == mnLastMouseDownItem)
+ {
+ pItem->MouseButtonUp(rMEvt);
+
+ ThumbnailViewItem* pNewItem = ImplGetItem(nPos);
+ if(pNewItem)
+ pNewItem->setHighlight(true);
+ }
+
+ mnLastMouseDownItem = THUMBNAILVIEW_ITEM_NOTFOUND;
+
+ if (pItem)
+ return true;
+ }
+ return ThumbnailView::MouseButtonUp(rMEvt);
+}
+
+void RecentDocsView::OnItemDblClicked(ThumbnailViewItem *pItem)
+{
+ RecentDocsViewItem* pRecentItem = dynamic_cast< RecentDocsViewItem* >(pItem);
+ if (pRecentItem)
+ pRecentItem->OpenDocument();
+}
+
+void RecentDocsView::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle &aRect)
+{
+ ThumbnailView::Paint(rRenderContext, aRect);
+
+ if (!mItemList.empty())
+ return;
+
+ if (maWelcomeImage.IsEmpty())
+ {
+ const tools::Long aWidth(aRect.GetWidth() > aRect.getHeight() ? aRect.GetHeight()/2 : aRect.GetWidth()/2);
+ maWelcomeImage = SfxApplication::GetApplicationLogo(aWidth);
+ }
+
+ // No recent files to be shown yet. Show a welcome screen.
+ rRenderContext.Push(vcl::PushFlags::FONT | vcl::PushFlags::TEXTCOLOR);
+ SetMessageFont(rRenderContext);
+ rRenderContext.SetTextColor(maTextColor);
+
+ tools::Long nTextHeight = rRenderContext.GetTextHeight();
+
+ const Size& rImgSize = maWelcomeImage.GetSizePixel();
+ const Size& rSize = GetOutputSizePixel();
+
+ const int nX = (rSize.Width() - rImgSize.Width())/2;
+ int nY = (rSize.Height() - 3 * nTextHeight - rImgSize.Height())/2;
+ Point aImgPoint(nX, nY);
+ rRenderContext.DrawBitmapEx(aImgPoint, rImgSize, maWelcomeImage);
+
+ nY = nY + rImgSize.Height();
+ rRenderContext.DrawText(tools::Rectangle(0, nY + 1 * nTextHeight, rSize.Width(), nY + nTextHeight),
+ maWelcomeLine1,
+ DrawTextFlags::Center);
+ rRenderContext.DrawText(tools::Rectangle(0, nY + 2 * nTextHeight, rSize.Width(), rSize.Height()),
+ maWelcomeLine2,
+ DrawTextFlags::MultiLine | DrawTextFlags::WordBreak | DrawTextFlags::Center);
+
+ rRenderContext.Pop();
+}
+
+void RecentDocsView::LoseFocus()
+{
+ deselectItems();
+
+ ThumbnailView::LoseFocus();
+}
+
+void RecentDocsView::Clear()
+{
+ Invalidate();
+ ThumbnailView::Clear();
+}
+
+void RecentDocsView::PostLoadRecentUsedFile(LoadRecentFile* pLoadRecentFile)
+{
+ assert(!mpLoadRecentFile);
+ mpLoadRecentFile = pLoadRecentFile;
+ m_nExecuteHdlId = Application::PostUserEvent(LINK(this, RecentDocsView, ExecuteHdl_Impl), pLoadRecentFile);
+}
+
+void RecentDocsView::DispatchedLoadRecentUsedFile()
+{
+ mpLoadRecentFile = nullptr;
+}
+
+IMPL_LINK( RecentDocsView, ExecuteHdl_Impl, void*, p, void )
+{
+ m_nExecuteHdlId = nullptr;
+ LoadRecentFile* pLoadRecentFile = static_cast<LoadRecentFile*>(p);
+ try
+ {
+ // Asynchronous execution as this can lead to our own destruction!
+ // Framework can recycle our current frame and the layout manager disposes all user interface
+ // elements if a component gets detached from its frame!
+ pLoadRecentFile->xDispatch->dispatch( pLoadRecentFile->aTargetURL, pLoadRecentFile->aArgSeq );
+ }
+ catch ( const Exception& )
+ {
+ }
+
+ if (pLoadRecentFile->pView)
+ {
+ pLoadRecentFile->pView->DispatchedLoadRecentUsedFile();
+ pLoadRecentFile->pView->SetPointer(PointerStyle::Arrow);
+ pLoadRecentFile->pView->Enable();
+ }
+
+ delete pLoadRecentFile;
+}
+
+} // namespace sfx2
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/control/recentdocsviewitem.cxx b/sfx2/source/control/recentdocsviewitem.cxx
new file mode 100644
index 000000000..44f103dfb
--- /dev/null
+++ b/sfx2/source/control/recentdocsviewitem.cxx
@@ -0,0 +1,348 @@
+/* -*- 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/beans/XPropertySet.hpp>
+#include <com/sun/star/embed/ElementModes.hpp>
+#include <com/sun/star/embed/StorageFactory.hpp>
+#include <com/sun/star/frame/Desktop.hpp>
+#include <com/sun/star/util/URLTransformer.hpp>
+
+#include <comphelper/base64.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <drawinglayer/primitive2d/discretebitmapprimitive2d.hxx>
+#include <drawinglayer/processor2d/baseprocessor2d.hxx>
+#include <i18nutil/paper.hxx>
+#include <officecfg/Office/Common.hxx>
+#include <recentdocsview.hxx>
+#include <sfx2/templatelocalview.hxx>
+#include <tools/diagnose_ex.h>
+#include <tools/stream.hxx>
+#include <tools/urlobj.hxx>
+#include <unotools/historyoptions.hxx>
+#include <vcl/event.hxx>
+#include <vcl/filter/PngImageReader.hxx>
+#include <vcl/ptrstyle.hxx>
+#include <vcl/virdev.hxx>
+
+#include <map>
+
+#include <bitmaps.hlst>
+#include "recentdocsviewitem.hxx"
+
+using namespace basegfx;
+using namespace com::sun::star;
+using namespace com::sun::star::uno;
+using namespace drawinglayer::primitive2d;
+using namespace drawinglayer::processor2d;
+
+namespace
+{
+bool IsDocEncrypted(const OUString& rURL)
+{
+ bool bIsEncrypted = false;
+
+ try
+ {
+ auto xFactory = embed::StorageFactory::create(comphelper::getProcessComponentContext());
+ auto xStorage(xFactory->createInstanceWithArguments(
+ { uno::Any(rURL), uno::Any(embed::ElementModes::READ) }));
+ if (uno::Reference<beans::XPropertySet> xStorageProps{ xStorage, uno::UNO_QUERY })
+ {
+ try
+ {
+ xStorageProps->getPropertyValue("HasEncryptedEntries") >>= bIsEncrypted;
+ }
+ catch (uno::Exception&)
+ {
+ }
+ }
+ }
+ catch (const uno::Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("sfx", "caught exception trying to find out if doc <"
+ << rURL << "> is encrypted:");
+ }
+
+ return bIsEncrypted;
+}
+
+using Ext2IconMap = std::map<sfx2::ApplicationType, OUString>;
+BitmapEx Url2Icon(std::u16string_view rURL, const Ext2IconMap& rExtToIcon, const OUString& sDefault)
+{
+ auto it = std::find_if(rExtToIcon.begin(), rExtToIcon.end(),
+ [aExt = INetURLObject(rURL).getExtension()](const auto& r)
+ { return sfx2::RecentDocsView::typeMatchesExtension(r.first, aExt); });
+
+ return BitmapEx(it != rExtToIcon.end() ? it->second : sDefault);
+};
+
+BitmapEx getDefaultThumbnail(const OUString& rURL)
+{
+ static const Ext2IconMap BitmapForExtension
+ = { { sfx2::ApplicationType::TYPE_WRITER, SFX_FILE_THUMBNAIL_TEXT },
+ { sfx2::ApplicationType::TYPE_CALC, SFX_FILE_THUMBNAIL_SHEET },
+ { sfx2::ApplicationType::TYPE_IMPRESS, SFX_FILE_THUMBNAIL_PRESENTATION },
+ { sfx2::ApplicationType::TYPE_DRAW, SFX_FILE_THUMBNAIL_DRAWING },
+ { sfx2::ApplicationType::TYPE_DATABASE, SFX_FILE_THUMBNAIL_DATABASE },
+ { sfx2::ApplicationType::TYPE_MATH, SFX_FILE_THUMBNAIL_MATH } };
+
+ static const Ext2IconMap EncryptedBitmapForExtension
+ = { { sfx2::ApplicationType::TYPE_WRITER, BMP_128X128_WRITER_DOC },
+ { sfx2::ApplicationType::TYPE_CALC, BMP_128X128_CALC_DOC },
+ { sfx2::ApplicationType::TYPE_IMPRESS, BMP_128X128_IMPRESS_DOC },
+ { sfx2::ApplicationType::TYPE_DRAW, BMP_128X128_DRAW_DOC },
+ // You can't save a database file with encryption -> no respective icon
+ { sfx2::ApplicationType::TYPE_MATH, BMP_128X128_MATH_DOC } };
+
+ const std::map<sfx2::ApplicationType, OUString>& rWhichMap
+ = IsDocEncrypted(rURL) ? EncryptedBitmapForExtension : BitmapForExtension;
+
+ return Url2Icon(rURL, rWhichMap, SFX_FILE_THUMBNAIL_DEFAULT);
+}
+
+BitmapEx getModuleOverlay(std::u16string_view rURL)
+{
+ static const Ext2IconMap OverlayBitmapForExtension
+ = { { sfx2::ApplicationType::TYPE_WRITER, SFX_FILE_OVERLAY_TEXT },
+ { sfx2::ApplicationType::TYPE_CALC, SFX_FILE_OVERLAY_SHEET },
+ { sfx2::ApplicationType::TYPE_IMPRESS, SFX_FILE_OVERLAY_PRESENTATION },
+ { sfx2::ApplicationType::TYPE_DRAW, SFX_FILE_OVERLAY_DRAWING },
+ { sfx2::ApplicationType::TYPE_DATABASE, SFX_FILE_OVERLAY_DATABASE },
+ { sfx2::ApplicationType::TYPE_MATH, SFX_FILE_OVERLAY_MATH } };
+
+ return Url2Icon(rURL, OverlayBitmapForExtension, SFX_FILE_OVERLAY_DEFAULT);
+}
+};
+
+RecentDocsViewItem::RecentDocsViewItem(sfx2::RecentDocsView &rView, const OUString &rURL,
+ const OUString &rTitle, std::u16string_view const sThumbnailBase64,
+ sal_uInt16 const nId, tools::Long const nThumbnailSize,
+ bool const isReadOnly)
+ : ThumbnailViewItem(rView, nId),
+ mrParentView(rView),
+ maURL(rURL),
+ m_isReadOnly(isReadOnly),
+ m_bRemoveIconHighlighted(false),
+ m_aRemoveRecentBitmap(BMP_RECENTDOC_REMOVE),
+ m_aRemoveRecentBitmapHighlighted(BMP_RECENTDOC_REMOVE_HIGHLIGHTED)
+{
+ OUString aTitle(rTitle);
+ INetURLObject aURLObj(rURL);
+
+ if( aURLObj.GetProtocol() == INetProtocol::File )
+ m_sHelpText = aURLObj.getFSysPath(FSysStyle::Detect);
+ if( m_sHelpText.isEmpty() )
+ m_sHelpText = aURLObj.GetURLNoPass();
+
+ if (aTitle.isEmpty())
+ aTitle = aURLObj.GetLastName(INetURLObject::DecodeMechanism::WithCharset);
+
+ BitmapEx aThumbnail;
+
+ //fdo#74834: only load thumbnail if the corresponding option is not disabled in the configuration
+ if (officecfg::Office::Common::History::RecentDocsThumbnail::get())
+ {
+ if (!sThumbnailBase64.empty())
+ {
+ Sequence<sal_Int8> aDecoded;
+ comphelper::Base64::decode(aDecoded, sThumbnailBase64);
+
+ SvMemoryStream aStream(aDecoded.getArray(), aDecoded.getLength(), StreamMode::READ);
+ vcl::PngImageReader aReader(aStream);
+ aThumbnail = aReader.read();
+ }
+ else if (sfx2::RecentDocsView::typeMatchesExtension(sfx2::ApplicationType::TYPE_DATABASE,
+ aURLObj.getExtension()))
+ {
+ aThumbnail
+ = BitmapEx(nThumbnailSize > 192 ? SFX_THUMBNAIL_BASE_256 : SFX_THUMBNAIL_BASE_192);
+ }
+ }
+
+ if (aThumbnail.IsEmpty())
+ {
+ // 1. Thumbnail absent: get the default thumbnail, checking for encryption.
+ BitmapEx aExt(getDefaultThumbnail(rURL));
+ Size aExtSize(aExt.GetSizePixel());
+
+ // attempt to make it appear as if it is on a piece of paper
+ tools::Long nPaperHeight;
+ tools::Long nPaperWidth;
+ if (sfx2::RecentDocsView::typeMatchesExtension(
+ sfx2::ApplicationType::TYPE_IMPRESS, aURLObj.getExtension()))
+ {
+ // Swap width and height (PAPER_SCREEN_4_3 definition make it needed)
+ PaperInfo aInfo(PAPER_SCREEN_4_3);
+ nPaperHeight = aInfo.getWidth();
+ nPaperWidth = aInfo.getHeight();
+ }
+ else
+ {
+ PaperInfo aInfo(PaperInfo::getSystemDefaultPaper());
+ nPaperHeight = aInfo.getHeight();
+ nPaperWidth = aInfo.getWidth();
+ }
+ double ratio = double(nThumbnailSize) / double(std::max(nPaperHeight, nPaperWidth));
+ Size aThumbnailSize(std::round(nPaperWidth * ratio), std::round(nPaperHeight * ratio));
+
+ if (aExtSize.Width() > aThumbnailSize.Width() || aExtSize.Height() > aThumbnailSize.Height())
+ {
+ aExt = TemplateLocalView::scaleImg(aExt, aThumbnailSize.Width(), aThumbnailSize.Height());
+ aExtSize = aExt.GetSizePixel();
+ }
+
+ // create empty, and copy the default thumbnail in
+ sal_uInt8 nAlpha = 255;
+ aThumbnail = BitmapEx(Bitmap(aThumbnailSize, vcl::PixelFormat::N24_BPP), AlphaMask(aThumbnailSize, &nAlpha));
+
+ aThumbnail.CopyPixel(
+ ::tools::Rectangle(Point((aThumbnailSize.Width() - aExtSize.Width()) / 2, (aThumbnailSize.Height() - aExtSize.Height()) / 2), aExtSize),
+ ::tools::Rectangle(Point(0, 0), aExtSize),
+ &aExt);
+ }
+ else
+ {
+ // 2. Thumbnail present: it's unencrypted document -> add a module overlay.
+ // Pre-scale the thumbnail to the final size before applying the overlay
+ aThumbnail = TemplateLocalView::scaleImg(aThumbnail, nThumbnailSize, nThumbnailSize);
+
+ BitmapEx aModule = getModuleOverlay(rURL);
+ if (!aModule.IsEmpty())
+ {
+ const Size aSize(aThumbnail.GetSizePixel());
+ const Size aOverlaySize(aModule.GetSizePixel());
+ ScopedVclPtr<VirtualDevice> pVirDev(VclPtr<VirtualDevice>::Create());
+ pVirDev->SetOutputSizePixel(aSize);
+ pVirDev->DrawBitmapEx(Point(), aThumbnail);
+ pVirDev->DrawBitmapEx(Point(aSize.Width() - aOverlaySize.Width() - 5,
+ aSize.Height() - aOverlaySize.Height() - 5),
+ aModule);
+ aThumbnail = pVirDev->GetBitmapEx(Point(), aSize);
+ }
+ }
+
+ maTitle = aTitle;
+ maPreview1 = aThumbnail;
+}
+
+::tools::Rectangle RecentDocsViewItem::updateHighlight(bool bVisible, const Point& rPoint)
+{
+ ::tools::Rectangle aRect(ThumbnailViewItem::updateHighlight(bVisible, rPoint));
+
+ if (bVisible && getRemoveIconArea().Contains(rPoint))
+ {
+ if (!m_bRemoveIconHighlighted)
+ aRect.Union(getRemoveIconArea());
+
+ m_bRemoveIconHighlighted = true;
+ }
+ else
+ {
+ if (m_bRemoveIconHighlighted)
+ aRect.Union(getRemoveIconArea());
+
+ m_bRemoveIconHighlighted = false;
+ }
+
+ return aRect;
+}
+
+::tools::Rectangle RecentDocsViewItem::getRemoveIconArea() const
+{
+ ::tools::Rectangle aArea(getDrawArea());
+ Size aSize(m_aRemoveRecentBitmap.GetSizePixel());
+
+ return ::tools::Rectangle(
+ Point(aArea.Right() - aSize.Width() - THUMBNAILVIEW_ITEM_CORNER, aArea.Top() + THUMBNAILVIEW_ITEM_CORNER),
+ aSize);
+}
+
+OUString RecentDocsViewItem::getHelpText() const
+{
+ return m_sHelpText;
+}
+
+void RecentDocsViewItem::Paint(drawinglayer::processor2d::BaseProcessor2D *pProcessor, const ThumbnailItemAttributes *pAttrs)
+{
+ ThumbnailViewItem::Paint(pProcessor, pAttrs);
+
+ // paint the remove icon when highlighted
+ if (isHighlighted())
+ {
+ drawinglayer::primitive2d::Primitive2DContainer aSeq(1);
+
+ Point aIconPos(getRemoveIconArea().TopLeft());
+
+ aSeq[0] = drawinglayer::primitive2d::Primitive2DReference(new DiscreteBitmapPrimitive2D(
+ m_bRemoveIconHighlighted ? m_aRemoveRecentBitmapHighlighted : m_aRemoveRecentBitmap,
+ B2DPoint(aIconPos.X(), aIconPos.Y())));
+
+ pProcessor->process(aSeq);
+ }
+}
+
+void RecentDocsViewItem::MouseButtonUp(const MouseEvent& rMEvt)
+{
+ if (rMEvt.IsLeft())
+ {
+ if (getRemoveIconArea().Contains(rMEvt.GetPosPixel()))
+ {
+ SvtHistoryOptions::DeleteItem(EHistoryType::PickList, maURL);
+ mrParent.Reload();
+ return;
+ }
+
+ OpenDocument();
+ return;
+ }
+}
+
+void RecentDocsViewItem::OpenDocument()
+{
+ // show busy mouse pointer
+ mrParentView.SetPointer(PointerStyle::Wait);
+ mrParentView.Disable();
+
+ Reference<frame::XDispatch> xDispatch;
+ css::util::URL aTargetURL;
+ Sequence<beans::PropertyValue> aArgsList;
+
+ uno::Reference<frame::XDesktop2> xDesktop = frame::Desktop::create(::comphelper::getProcessComponentContext());
+
+ aTargetURL.Complete = maURL;
+ Reference<util::XURLTransformer> xTrans(util::URLTransformer::create(::comphelper::getProcessComponentContext()));
+ xTrans->parseStrict(aTargetURL);
+
+ aArgsList = { comphelper::makePropertyValue("Referer", OUString("private:user")),
+ // documents will never be opened as templates
+ comphelper::makePropertyValue("AsTemplate", false) };
+ if (m_isReadOnly) // tdf#149170 only add if true
+ {
+ aArgsList.realloc(aArgsList.size()+1);
+ aArgsList.getArray()[aArgsList.size()-1] = comphelper::makePropertyValue("ReadOnly", true);
+ }
+
+ xDispatch = xDesktop->queryDispatch(aTargetURL, "_default", 0);
+
+ if (!xDispatch.is())
+ return;
+
+ // Call dispatch asynchronously as we can be destroyed while dispatch is
+ // executed. VCL is not able to survive this as it wants to call listeners
+ // after select!!!
+ sfx2::LoadRecentFile *const pLoadRecentFile = new sfx2::LoadRecentFile;
+ pLoadRecentFile->xDispatch = xDispatch;
+ pLoadRecentFile->aTargetURL = aTargetURL;
+ pLoadRecentFile->aArgSeq = aArgsList;
+ pLoadRecentFile->pView = &mrParentView;
+
+ mrParentView.PostLoadRecentUsedFile(pLoadRecentFile);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/control/recentdocsviewitem.hxx b/sfx2/source/control/recentdocsviewitem.hxx
new file mode 100644
index 000000000..3f5f6d3fa
--- /dev/null
+++ b/sfx2/source/control/recentdocsviewitem.hxx
@@ -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/.
+ */
+
+#ifndef INCLUDED_SFX2_RECENTDOCSVIEWITEM_HXX
+#define INCLUDED_SFX2_RECENTDOCSVIEWITEM_HXX
+
+#include <sfx2/thumbnailviewitem.hxx>
+
+namespace sfx2
+{
+ class RecentDocsView;
+}
+
+class RecentDocsViewItem final : public ThumbnailViewItem
+{
+public:
+ RecentDocsViewItem(sfx2::RecentDocsView &rView, const OUString &rURL,
+ const OUString &rTitle, std::u16string_view sThumbnailBase64, sal_uInt16 nId, tools::Long nThumbnailSize, bool isReadOnly);
+
+ /** Updates own highlight status based on the aPoint position.
+
+ Calls the ancestor's updateHighlight, and then takes care of m_bRemoveIconHighlighted.
+
+ Returns rectangle that needs to be invalidated.
+ */
+ virtual tools::Rectangle updateHighlight(bool bVisible, const Point& rPoint) override;
+
+ /// Text to be used for the tooltip.
+ virtual OUString getHelpText() const override;
+
+ virtual void Paint(drawinglayer::processor2d::BaseProcessor2D *pProcessor,
+ const ThumbnailItemAttributes *pAttrs) override;
+
+ virtual void MouseButtonUp(const MouseEvent& rMEvt) override;
+
+ /// Called when the user clicks a document - it will open it.
+ void OpenDocument();
+
+private:
+ sfx2::RecentDocsView& mrParentView;
+
+ /// Return area where is the icon to remove document from the recent documents.
+ tools::Rectangle getRemoveIconArea() const;
+
+ OUString maURL;
+
+ bool m_isReadOnly = false;
+
+ OUString m_sHelpText;
+
+ /// Is the icon that the user can click to remove the document from the recent documents highlighted?
+ bool m_bRemoveIconHighlighted;
+
+ BitmapEx m_aRemoveRecentBitmap;
+
+ BitmapEx m_aRemoveRecentBitmapHighlighted;
+};
+
+#endif // INCLUDED_SFX2_RECENTDOCSVIEWITEM_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/control/request.cxx b/sfx2/source/control/request.cxx
new file mode 100644
index 000000000..b43d1dd99
--- /dev/null
+++ b/sfx2/source/control/request.cxx
@@ -0,0 +1,763 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <memory>
+#include <com/sun/star/frame/DispatchStatement.hpp>
+#include <com/sun/star/container/XIndexReplace.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/util/URLTransformer.hpp>
+#include <com/sun/star/util/XURLTransformer.hpp>
+#include <com/sun/star/frame/XDispatchRecorderSupplier.hpp>
+#include <svl/itemiter.hxx>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+#include <tools/debug.hxx>
+
+#include <svl/itempool.hxx>
+#include <itemdel.hxx>
+
+#include <comphelper/processfactory.hxx>
+
+#include <svl/hint.hxx>
+
+#include <sfx2/request.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/msg.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/objface.hxx>
+#include <sfx2/sfxuno.hxx>
+
+
+using namespace ::com::sun::star;
+
+struct SfxRequest_Impl: public SfxListener
+
+/* [Description]
+
+ Implementation structure of the <SfxRequest> class.
+*/
+
+{
+ SfxRequest* pAnti; // Owner because of dying pool
+ OUString aTarget; // if possible from target object set by App
+ SfxItemPool* pPool; // ItemSet build with this pool
+ std::unique_ptr<SfxPoolItem> pRetVal; // Return value belongs to itself
+ SfxShell* pShell; // run from this shell
+ const SfxSlot* pSlot; // executed Slot
+ sal_uInt16 nModifier; // which Modifier was pressed?
+ bool bDone; // at all executed
+ bool bIgnored; // Cancelled by the User
+ bool bCancelled; // no longer notify
+ SfxCallMode nCallMode; // Synch/Asynch/API/Record
+ bool bAllowRecording;
+ std::unique_ptr<SfxAllItemSet>
+ pInternalArgs;
+ SfxViewFrame* pViewFrame;
+
+ css::uno::Reference< css::frame::XDispatchRecorder > xRecorder;
+ css::uno::Reference< css::util::XURLTransformer > xTransform;
+
+ explicit SfxRequest_Impl( SfxRequest *pOwner )
+ : pAnti( pOwner)
+ , pPool(nullptr)
+ , pShell(nullptr)
+ , pSlot(nullptr)
+ , nModifier(0)
+ , bDone(false)
+ , bIgnored(false)
+ , bCancelled(false)
+ , nCallMode( SfxCallMode::SYNCHRON )
+ , bAllowRecording( false )
+ , pViewFrame(nullptr)
+ {
+ }
+
+ void SetPool( SfxItemPool *pNewPool );
+ virtual void Notify( SfxBroadcaster &rBC, const SfxHint &rHint ) override;
+ void Record( const uno::Sequence < beans::PropertyValue >& rArgs );
+};
+
+
+void SfxRequest_Impl::Notify( SfxBroadcaster&, const SfxHint &rHint )
+{
+ if ( rHint.GetId() == SfxHintId::Dying )
+ pAnti->Cancel();
+}
+
+
+void SfxRequest_Impl::SetPool( SfxItemPool *pNewPool )
+{
+ if ( pNewPool != pPool )
+ {
+ if ( pPool )
+ EndListening( pPool->BC() );
+ pPool = pNewPool;
+ if ( pNewPool )
+ StartListening( pNewPool->BC() );
+ }
+}
+
+
+SfxRequest::~SfxRequest()
+{
+ // Leave out Done() marked requests with 'rem'
+ if ( pImpl->xRecorder.is() && !pImpl->bDone && !pImpl->bIgnored )
+ pImpl->Record( uno::Sequence < beans::PropertyValue >() );
+
+ // Clear object
+ pArgs.reset();
+ if ( pImpl->pRetVal )
+ DeleteItemOnIdle(std::move(pImpl->pRetVal));
+}
+
+
+SfxRequest::SfxRequest
+(
+ const SfxRequest& rOrig
+)
+: SfxHint( rOrig ),
+ nSlot(rOrig.nSlot),
+ pArgs(rOrig.pArgs? new SfxAllItemSet(*rOrig.pArgs): nullptr),
+ pImpl( new SfxRequest_Impl(this) )
+{
+ pImpl->bAllowRecording = rOrig.pImpl->bAllowRecording;
+ pImpl->bDone = false;
+ pImpl->bIgnored = false;
+ pImpl->pShell = nullptr;
+ pImpl->pSlot = nullptr;
+ pImpl->nCallMode = rOrig.pImpl->nCallMode;
+ pImpl->aTarget = rOrig.pImpl->aTarget;
+ pImpl->nModifier = rOrig.pImpl->nModifier;
+
+ // deep copy needed !
+ pImpl->pInternalArgs.reset( rOrig.pImpl->pInternalArgs ? new SfxAllItemSet(*rOrig.pImpl->pInternalArgs) : nullptr);
+
+ if ( pArgs )
+ pImpl->SetPool( pArgs->GetPool() );
+ else
+ pImpl->SetPool( rOrig.pImpl->pPool );
+
+ // setup macro recording if it was in the original SfxRequest
+ if (!rOrig.pImpl->pViewFrame || !rOrig.pImpl->xRecorder.is())
+ return;
+
+ nSlot = rOrig.nSlot;
+ pImpl->pViewFrame = rOrig.pImpl->pViewFrame;
+ if (pImpl->pViewFrame->GetDispatcher()->GetShellAndSlot_Impl(nSlot, &pImpl->pShell, &pImpl->pSlot, true, true))
+ {
+ pImpl->SetPool( &pImpl->pShell->GetPool() );
+ pImpl->xRecorder = SfxRequest::GetMacroRecorder(pImpl->pViewFrame);
+ if (pImpl->xRecorder)
+ pImpl->xTransform = util::URLTransformer::create(comphelper::getProcessComponentContext());
+ pImpl->aTarget = pImpl->pShell->GetName();
+ }
+ else
+ {
+ SAL_WARN("sfx", "Recording unsupported slot: " << pImpl->pPool->GetSlotId(nSlot));
+ }
+}
+
+
+SfxRequest::SfxRequest
+(
+ SfxViewFrame* pViewFrame,
+ sal_uInt16 nSlotId
+
+)
+
+/* [Description]
+
+ With this constructor events can subsequently be recorded that are not run
+ across SfxDispatcher (eg from KeyInput() or mouse events). For this, a
+ SfxRequest instance is created by this constructor and then proceed
+ exactly as with a SfxRequest that in a <Slot-Execute-Method> is given as a
+ parameter.
+*/
+
+: nSlot(nSlotId),
+ pImpl( new SfxRequest_Impl(this) )
+{
+ pImpl->bDone = false;
+ pImpl->bIgnored = false;
+ pImpl->SetPool( &pViewFrame->GetPool() );
+ pImpl->pShell = nullptr;
+ pImpl->pSlot = nullptr;
+ pImpl->nCallMode = SfxCallMode::SYNCHRON;
+ pImpl->pViewFrame = pViewFrame;
+ if( pImpl->pViewFrame->GetDispatcher()->GetShellAndSlot_Impl( nSlotId, &pImpl->pShell, &pImpl->pSlot, true, true ) )
+ {
+ pImpl->SetPool( &pImpl->pShell->GetPool() );
+ pImpl->xRecorder = SfxRequest::GetMacroRecorder( pViewFrame );
+ if (pImpl->xRecorder)
+ pImpl->xTransform = util::URLTransformer::create(comphelper::getProcessComponentContext());
+ pImpl->aTarget = pImpl->pShell->GetName();
+ }
+ else
+ {
+ SAL_WARN( "sfx", "Recording unsupported slot: " << pImpl->pPool->GetSlotId(nSlotId) );
+ }
+}
+
+
+SfxRequest::SfxRequest
+(
+ sal_uInt16 nSlotId, // executed <Slot-Id>
+ SfxCallMode nMode, // Synch/API/...
+ SfxItemPool& rPool // necessary for the SfxItemSet for parameters
+)
+
+// creates a SfxRequest without arguments
+
+: nSlot(nSlotId),
+ pImpl( new SfxRequest_Impl(this) )
+{
+ pImpl->bDone = false;
+ pImpl->bIgnored = false;
+ pImpl->SetPool( &rPool );
+ pImpl->pShell = nullptr;
+ pImpl->pSlot = nullptr;
+ pImpl->nCallMode = nMode;
+}
+
+SfxRequest::SfxRequest
+(
+ const SfxSlot* pSlot, // executed <Slot-Id>
+ const css::uno::Sequence < css::beans::PropertyValue >& rArgs,
+ SfxCallMode nMode, // Synch/API/...
+ SfxItemPool& rPool // necessary for the SfxItemSet for parameters
+)
+: nSlot(pSlot->GetSlotId()),
+ pArgs(new SfxAllItemSet(rPool)),
+ pImpl( new SfxRequest_Impl(this) )
+{
+ pImpl->bDone = false;
+ pImpl->bIgnored = false;
+ pImpl->SetPool( &rPool );
+ pImpl->pShell = nullptr;
+ pImpl->pSlot = nullptr;
+ pImpl->nCallMode = nMode;
+ TransformParameters( nSlot, rArgs, *pArgs, pSlot );
+}
+
+
+SfxRequest::SfxRequest
+(
+ sal_uInt16 nSlotId,
+ SfxCallMode nMode,
+ const SfxAllItemSet& rSfxArgs
+)
+
+// creates a SfxRequest with arguments
+
+: nSlot(nSlotId),
+ pArgs(new SfxAllItemSet(rSfxArgs)),
+ pImpl( new SfxRequest_Impl(this) )
+{
+ pImpl->bDone = false;
+ pImpl->bIgnored = false;
+ pImpl->SetPool( rSfxArgs.GetPool() );
+ pImpl->pShell = nullptr;
+ pImpl->pSlot = nullptr;
+ pImpl->nCallMode = nMode;
+}
+
+
+SfxRequest::SfxRequest
+(
+ sal_uInt16 nSlotId,
+ SfxCallMode nMode,
+ const SfxAllItemSet& rSfxArgs,
+ const SfxAllItemSet& rSfxInternalArgs
+)
+: SfxRequest(nSlotId, nMode, rSfxArgs)
+{
+ SetInternalArgs_Impl(rSfxInternalArgs);
+}
+
+SfxCallMode SfxRequest::GetCallMode() const
+{
+ return pImpl->nCallMode;
+}
+
+
+bool SfxRequest::IsSynchronCall() const
+{
+ return SfxCallMode::SYNCHRON == ( SfxCallMode::SYNCHRON & pImpl->nCallMode );
+}
+
+
+void SfxRequest::SetSynchronCall( bool bSynchron )
+{
+ if ( bSynchron )
+ pImpl->nCallMode |= SfxCallMode::SYNCHRON;
+ else
+ pImpl->nCallMode &= ~SfxCallMode::SYNCHRON;
+}
+
+void SfxRequest::SetInternalArgs_Impl( const SfxAllItemSet& rArgs )
+{
+ pImpl->pInternalArgs.reset( new SfxAllItemSet( rArgs ) );
+}
+
+const SfxItemSet* SfxRequest::GetInternalArgs_Impl() const
+{
+ return pImpl->pInternalArgs.get();
+}
+
+
+void SfxRequest_Impl::Record
+(
+ const uno::Sequence < beans::PropertyValue >& rArgs // current Parameter
+)
+
+/* [Description]
+
+ Internal helper method to create a repeatable description of the just
+ executed SfxRequest.
+*/
+
+{
+ if(!xRecorder.is())
+ return;
+
+ OUString aCmd = ".uno:" + OUString::createFromAscii( pSlot->GetUnoName() );
+
+ uno::Reference< container::XIndexReplace > xReplace( xRecorder, uno::UNO_QUERY );
+ if ( xReplace.is() && aCmd == ".uno:InsertText" )
+ {
+ sal_Int32 nCount = xReplace->getCount();
+ if ( nCount )
+ {
+ frame::DispatchStatement aStatement;
+ uno::Any aElement = xReplace->getByIndex(nCount-1);
+ if ( (aElement >>= aStatement) && aStatement.aCommand == aCmd )
+ {
+ OUString aStr;
+ OUString aNew;
+ aStatement.aArgs[0].Value >>= aStr;
+ rArgs[0].Value >>= aNew;
+ aStr += aNew;
+ aStatement.aArgs.getArray()[0].Value <<= aStr;
+ aElement <<= aStatement;
+ xReplace->replaceByIndex( nCount-1, aElement );
+ return;
+ }
+ }
+ }
+
+ css::util::URL aURL;
+ aURL.Complete = aCmd;
+ xTransform->parseStrict(aURL);
+
+ if (bDone)
+ xRecorder->recordDispatch(aURL,rArgs);
+ else
+ xRecorder->recordDispatchAsComment(aURL,rArgs);
+}
+
+
+void SfxRequest::Record_Impl
+(
+ SfxShell& rSh, // the <SfxShell>, which has executed the Request
+ const SfxSlot& rSlot, // the <SfxSlot>, which has executed the Request
+ const css::uno::Reference< css::frame::XDispatchRecorder >& xRecorder,
+ SfxViewFrame* pViewFrame
+)
+
+/* [Description]
+
+ This internal method marks the specified SfxMakro SfxRequest as recorded in
+ SfxMakro. Pointer to the parameters in Done() is used again, thus has to
+ still be alive.
+*/
+
+{
+ pImpl->pShell = &rSh;
+ pImpl->pSlot = &rSlot;
+ pImpl->xRecorder = xRecorder;
+ if (pImpl->xRecorder && !pImpl->xTransform)
+ pImpl->xTransform = util::URLTransformer::create(comphelper::getProcessComponentContext());
+ pImpl->aTarget = rSh.GetName();
+ pImpl->pViewFrame = pViewFrame;
+}
+
+
+void SfxRequest::SetArgs( const SfxAllItemSet& rArgs )
+{
+ pArgs.reset(new SfxAllItemSet(rArgs));
+ pImpl->SetPool( pArgs->GetPool() );
+}
+
+
+void SfxRequest::AppendItem(const SfxPoolItem &rItem)
+{
+ if(!pArgs)
+ pArgs.reset( new SfxAllItemSet(*pImpl->pPool) );
+ pArgs->Put(rItem, rItem.Which());
+}
+
+
+void SfxRequest::RemoveItem( sal_uInt16 nID )
+{
+ if (pArgs)
+ {
+ pArgs->ClearItem(nID);
+ if ( !pArgs->Count() )
+ pArgs.reset();
+ }
+}
+
+void SfxRequest::SetReturnValue(const SfxPoolItem &rItem)
+{
+ DBG_ASSERT(!pImpl->pRetVal, "Set Return value multiple times?");
+ pImpl->pRetVal.reset(rItem.Clone());
+}
+
+
+const SfxPoolItem* SfxRequest::GetReturnValue() const
+{
+ return pImpl->pRetVal.get();
+}
+
+
+void SfxRequest::Done
+(
+ const SfxItemSet& rSet /* parameters passed on by the application,
+ that for example were asked for by the user
+ in a dialogue, 0 if no parameters have been
+ set */
+)
+
+/* [Description]
+
+ This method must be called in the <Execute-Method> of the <SfxSlot>s, which
+ has performed the SfxRequest when the execution actually took place. If
+ 'Done()' is not called, then the SfxRequest is considered canceled.
+
+ Any return values are passed only when 'Done()' was called. Similar, when
+ recording a macro only true statements are generated if 'Done()' was
+ called; for SfxRequests that were not identified as such will instead
+ be commented out by inserting ('rem').
+
+ [Note]
+
+ 'Done ()' is not called, for example when a dialog started by the function
+ was canceled by the user or if the execution could not be performed due to
+ a wrong context (without use of separate <SfxShell>s). 'Done ()' will be
+ launched, when executing the function led to a regular error
+ (for example, file could not be opened).
+*/
+
+{
+ Done_Impl( &rSet );
+
+ // Keep items if possible, so they can be queried by StarDraw.
+ if ( !pArgs )
+ {
+ pArgs.reset( new SfxAllItemSet( rSet ) );
+ pImpl->SetPool( pArgs->GetPool() );
+ }
+ else
+ {
+ SfxItemIter aIter(rSet);
+ for (const SfxPoolItem* pItem = aIter.GetCurItem(); pItem; pItem = aIter.NextItem())
+ {
+ if(!IsInvalidItem(pItem))
+ pArgs->Put(*pItem,pItem->Which());
+ }
+ }
+}
+
+
+void SfxRequest::Done( bool bRelease )
+// [<SfxRequest::Done(SfxItemSet&)>]
+{
+ Done_Impl( pArgs.get() );
+ if( bRelease )
+ pArgs.reset();
+}
+
+
+void SfxRequest::ForgetAllArgs()
+{
+ pArgs.reset();
+ pImpl->pInternalArgs.reset();
+}
+
+
+bool SfxRequest::IsCancelled() const
+{
+ return pImpl->bCancelled;
+}
+
+
+void SfxRequest::Cancel()
+
+/* [Description]
+
+ Marks this request as no longer executable. For example, if called when
+ the target (more precisely, its pool) dies.
+*/
+
+{
+ pImpl->bCancelled = true;
+ pImpl->SetPool( nullptr );
+ pArgs.reset();
+}
+
+
+void SfxRequest::Ignore()
+
+/* [Description]
+
+ If this method is called instead of <SfxRequest::Done()>, then this
+ request is not recorded.
+
+ [Example]
+
+ The selecting of tools in StarDraw should not be recorded, but the same
+ slots are to be used from the generation of the tools to the generated
+ objects. Thus can NoRecords not be specified, i.e. should not be recorded.
+*/
+
+{
+ // Mark as actually executed
+ pImpl->bIgnored = true;
+}
+
+
+void SfxRequest::Done_Impl
+(
+ const SfxItemSet* pSet /* parameters passed on by the application,
+ that for example were asked for by the user
+ in a dialogue, 0 if no parameters have been
+ set */
+
+)
+
+/* [Description]
+
+ Internal method to mark SfxRequest with 'done' and to evaluate the
+ parameters in 'pSet' in case it is recorded.
+*/
+
+{
+ // Mark as actually executed
+ pImpl->bDone = true;
+
+ // not Recording
+ if ( !pImpl->xRecorder.is() )
+ return;
+
+ // was running a different slot than requested (Delegation)
+ if ( nSlot != pImpl->pSlot->GetSlotId() )
+ {
+ // Search Slot again
+ pImpl->pSlot = pImpl->pShell->GetInterface()->GetSlot(nSlot);
+ DBG_ASSERT( pImpl->pSlot, "delegated SlotId not found" );
+ if ( !pImpl->pSlot ) // playing it safe
+ return;
+ }
+
+ // recordable?
+ // new Recording uses UnoName!
+ SAL_WARN_IF( !pImpl->pSlot->pUnoName, "sfx", "Recording not exported slot: "
+ << pImpl->pSlot->GetSlotId() );
+
+ if ( !pImpl->pSlot->pUnoName ) // playing it safe
+ return;
+
+ // often required values
+ SfxItemPool &rPool = pImpl->pShell->GetPool();
+
+ // Property-Slot?
+ if ( !pImpl->pSlot->IsMode(SfxSlotMode::METHOD) )
+ {
+ // get the property as SfxPoolItem
+ const SfxPoolItem *pItem(nullptr);
+ const sal_uInt16 nWhich(rPool.GetWhich(pImpl->pSlot->GetSlotId()));
+ const bool bItemStateSet(nullptr != pSet);
+ const SfxItemState eState(bItemStateSet ? pSet->GetItemState( nWhich, false, &pItem ) : SfxItemState::DEFAULT);
+ SAL_WARN_IF( !bItemStateSet || SfxItemState::SET != eState, "sfx", "Recording property not available: "
+ << pImpl->pSlot->GetSlotId() );
+ uno::Sequence < beans::PropertyValue > aSeq;
+
+ if ( bItemStateSet && SfxItemState::SET == eState )
+ TransformItems( pImpl->pSlot->GetSlotId(), *pSet, aSeq, pImpl->pSlot );
+
+ pImpl->Record( aSeq );
+ }
+
+ // record everything in a single statement?
+ else if ( pImpl->pSlot->IsMode(SfxSlotMode::RECORDPERSET) )
+ {
+ uno::Sequence < beans::PropertyValue > aSeq;
+ if ( pSet )
+ TransformItems( pImpl->pSlot->GetSlotId(), *pSet, aSeq, pImpl->pSlot );
+ pImpl->Record( aSeq );
+ }
+
+ // record each item as a single statement
+ else if ( pImpl->pSlot->IsMode(SfxSlotMode::RECORDPERITEM) )
+ {
+ if ( pSet )
+ {
+ // iterate over Items
+ SfxItemIter aIter(*pSet);
+ for ( const SfxPoolItem* pItem = aIter.GetCurItem(); pItem; pItem = aIter.NextItem() )
+ {
+ // to determine the slot ID for the individual item
+ sal_uInt16 nSlotId = rPool.GetSlotId( pItem->Which() );
+ if ( nSlotId == nSlot )
+ {
+ // play it safe; repair the wrong flags
+ OSL_FAIL( "recursion RecordPerItem - use RecordPerSet!" );
+ SfxSlot *pSlot = const_cast<SfxSlot*>(pImpl->pSlot);
+ pSlot->nFlags &= ~SfxSlotMode::RECORDPERITEM;
+ pSlot->nFlags &= SfxSlotMode::RECORDPERSET;
+ }
+
+ // Record a Sub-Request
+ SfxRequest aReq( pImpl->pViewFrame, nSlotId );
+ if ( aReq.pImpl->pSlot )
+ aReq.AppendItem( *pItem );
+ aReq.Done();
+ }
+ }
+ else
+ {
+ //HACK(think about this again)
+ pImpl->Record( uno::Sequence < beans::PropertyValue >() );
+ }
+ }
+}
+
+
+bool SfxRequest::IsDone() const
+
+/* [Description]
+
+ With this method it can be queried whether the SfxRequest was actually
+ executed or not. If a SfxRequest was not executed, then this is for example
+ because it was canceled by the user or the context for this request was
+ wrong, this was not implemented on a separate <SfxShell>.
+
+ SfxRequest instances that return false will not be recorded.
+
+ [Cross-reference]
+
+ <SfxRequest::Done(const SfxItemSet&)>
+ <SfxRequest::Done()>
+*/
+
+{
+ return pImpl->bDone;
+}
+
+
+css::uno::Reference< css::frame::XDispatchRecorder > SfxRequest::GetMacroRecorder( SfxViewFrame const * pView )
+
+/* [Description]
+
+ This recorder is an attempt for dispatch () to get calls from the Frame.
+ This is then available through a property by a supplier but only when
+ recording was turned on.
+
+ (See also SfxViewFrame::MiscExec_Impl() and SID_RECORDING)
+*/
+
+{
+ css::uno::Reference< css::frame::XDispatchRecorder > xRecorder;
+
+ if (!pView)
+ pView = SfxViewFrame::Current();
+
+ if (!pView)
+ return xRecorder;
+
+ css::uno::Reference< css::beans::XPropertySet > xSet(
+ pView->GetFrame().GetFrameInterface(),
+ css::uno::UNO_QUERY);
+
+ if(xSet.is())
+ {
+ css::uno::Any aProp = xSet->getPropertyValue("DispatchRecorderSupplier");
+ css::uno::Reference< css::frame::XDispatchRecorderSupplier > xSupplier;
+ aProp >>= xSupplier;
+ if(xSupplier.is())
+ xRecorder = xSupplier->getDispatchRecorder();
+ }
+
+ return xRecorder;
+}
+
+bool SfxRequest::HasMacroRecorder( SfxViewFrame const * pView )
+{
+ return GetMacroRecorder( pView ).is();
+}
+
+
+bool SfxRequest::IsAPI() const
+
+/* [Description]
+
+ Returns true if this SfxRequest was generated by an API (for example BASIC),
+ otherwise false.
+*/
+
+{
+ return SfxCallMode::API == ( SfxCallMode::API & pImpl->nCallMode );
+}
+
+
+void SfxRequest::SetModifier( sal_uInt16 nModi )
+{
+ pImpl->nModifier = nModi;
+}
+
+
+sal_uInt16 SfxRequest::GetModifier() const
+{
+ return pImpl->nModifier;
+}
+
+
+void SfxRequest::AllowRecording( bool bSet )
+{
+ pImpl->bAllowRecording = bSet;
+}
+
+bool SfxRequest::AllowsRecording() const
+{
+ bool bAllow = pImpl->bAllowRecording;
+ if( !bAllow )
+ bAllow = ( SfxCallMode::API != ( SfxCallMode::API & pImpl->nCallMode ) ) &&
+ ( SfxCallMode::RECORD == ( SfxCallMode::RECORD & pImpl->nCallMode ) );
+ return bAllow;
+}
+
+void SfxRequest::ReleaseArgs()
+{
+ pArgs.reset();
+ pImpl->pInternalArgs.reset();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/control/sfxstatuslistener.cxx b/sfx2/source/control/sfxstatuslistener.cxx
new file mode 100644
index 000000000..5ee3d471f
--- /dev/null
+++ b/sfx2/source/control/sfxstatuslistener.cxx
@@ -0,0 +1,219 @@
+/* -*- 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/sfxstatuslistener.hxx>
+#include <svl/poolitem.hxx>
+#include <svl/eitem.hxx>
+#include <svl/stritem.hxx>
+#include <svl/intitem.hxx>
+#include <svl/visitem.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/servicehelper.hxx>
+#include <vcl/svapp.hxx>
+#include <com/sun/star/util/URLTransformer.hpp>
+#include <com/sun/star/util/XURLTransformer.hpp>
+#include <com/sun/star/lang/XUnoTunnel.hpp>
+#include <com/sun/star/frame/status/ItemStatus.hpp>
+#include <com/sun/star/frame/status/Visibility.hpp>
+#include <com/sun/star/frame/XDispatchProvider.hpp>
+
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/dispatch.hxx>
+#include <unoctitm.hxx>
+#include <sfx2/msgpool.hxx>
+#include <sfx2/msg.hxx>
+
+using namespace ::cppu;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::frame;
+using namespace ::com::sun::star::frame::status;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::util;
+
+SfxStatusListener::SfxStatusListener( const Reference< XDispatchProvider >& rDispatchProvider, sal_uInt16 nSlotId, const OUString& rCommand ) :
+ m_nSlotID( nSlotId ),
+ m_xDispatchProvider( rDispatchProvider )
+{
+ m_aCommand.Complete = rCommand;
+ Reference< XURLTransformer > xTrans( URLTransformer::create( ::comphelper::getProcessComponentContext() ) );
+ xTrans->parseStrict( m_aCommand );
+ if ( rDispatchProvider.is() )
+ m_xDispatch = rDispatchProvider->queryDispatch( m_aCommand, OUString(), 0 );
+}
+
+SfxStatusListener::~SfxStatusListener()
+{
+}
+
+// old sfx controller item C++ API
+void SfxStatusListener::StateChangedAtStatusListener( SfxItemState, const SfxPoolItem* )
+{
+ // must be implemented by sub class
+}
+
+void SfxStatusListener::UnBind()
+{
+ if ( m_xDispatch.is() )
+ {
+ Reference< XStatusListener > aStatusListener(this);
+ m_xDispatch->removeStatusListener( aStatusListener, m_aCommand );
+ m_xDispatch.clear();
+ }
+}
+
+void SfxStatusListener::ReBind()
+{
+ Reference< XStatusListener > aStatusListener(this);
+ if ( m_xDispatch.is() )
+ m_xDispatch->removeStatusListener( aStatusListener, m_aCommand );
+ if ( m_xDispatchProvider.is() )
+ {
+ try
+ {
+ m_xDispatch = m_xDispatchProvider->queryDispatch( m_aCommand, OUString(), 0 );
+ if ( m_xDispatch.is() )
+ m_xDispatch->addStatusListener( aStatusListener, m_aCommand );
+ }
+ catch( Exception& )
+ {
+ }
+ }
+}
+
+// new UNO API
+void SAL_CALL SfxStatusListener::dispose()
+{
+ if ( m_xDispatch.is() && !m_aCommand.Complete.isEmpty() )
+ {
+ try
+ {
+ Reference< XStatusListener > aStatusListener(this);
+ m_xDispatch->removeStatusListener( aStatusListener, m_aCommand );
+ }
+ catch ( Exception& )
+ {
+ }
+ }
+
+ m_xDispatch.clear();
+ m_xDispatchProvider.clear();
+}
+
+void SAL_CALL SfxStatusListener::addEventListener( const Reference< XEventListener >& )
+{
+ // do nothing - this is a wrapper class which does not support listeners
+}
+
+void SAL_CALL SfxStatusListener::removeEventListener( const Reference< XEventListener >& )
+{
+ // do nothing - this is a wrapper class which does not support listeners
+}
+
+void SAL_CALL SfxStatusListener::disposing( const EventObject& Source )
+{
+ SolarMutexGuard aGuard;
+
+ if ( Source.Source == Reference< XInterface >( m_xDispatch, UNO_QUERY ))
+ m_xDispatch.clear();
+ else if ( Source.Source == Reference< XInterface >( m_xDispatchProvider, UNO_QUERY ))
+ m_xDispatchProvider.clear();
+}
+
+void SAL_CALL SfxStatusListener::statusChanged( const FeatureStateEvent& rEvent)
+{
+ SolarMutexGuard aGuard;
+
+ SfxViewFrame* pViewFrame = nullptr;
+ if ( m_xDispatch.is() )
+ {
+ Reference< XUnoTunnel > xTunnel( m_xDispatch, UNO_QUERY );
+ if (auto pDisp = comphelper::getFromUnoTunnel<SfxOfficeDispatch>(xTunnel))
+ pViewFrame = pDisp->GetDispatcher_Impl()->GetFrame();
+ }
+
+ SfxSlotPool& rPool = SfxSlotPool::GetSlotPool( pViewFrame );
+ const SfxSlot* pSlot = rPool.GetSlot( m_nSlotID );
+
+ SfxItemState eState = SfxItemState::DISABLED;
+ std::unique_ptr<SfxPoolItem> pItem;
+ if ( rEvent.IsEnabled )
+ {
+ eState = SfxItemState::DEFAULT;
+ css::uno::Type aType = rEvent.State.getValueType();
+
+ if ( aType == ::cppu::UnoType<void>::get() )
+ {
+ pItem.reset(new SfxVoidItem( m_nSlotID ));
+ eState = SfxItemState::UNKNOWN;
+ }
+ else if ( aType == cppu::UnoType< bool >::get() )
+ {
+ bool bTemp = false;
+ rEvent.State >>= bTemp ;
+ pItem.reset(new SfxBoolItem( m_nSlotID, bTemp ));
+ }
+ else if ( aType == cppu::UnoType< ::cppu::UnoUnsignedShortType >::get() )
+ {
+ sal_uInt16 nTemp = 0;
+ rEvent.State >>= nTemp ;
+ pItem.reset(new SfxUInt16Item( m_nSlotID, nTemp ));
+ }
+ else if ( aType == cppu::UnoType<sal_uInt32>::get() )
+ {
+ sal_uInt32 nTemp = 0;
+ rEvent.State >>= nTemp ;
+ pItem.reset(new SfxUInt32Item( m_nSlotID, nTemp ));
+ }
+ else if ( aType == cppu::UnoType<OUString>::get() )
+ {
+ OUString sTemp ;
+ rEvent.State >>= sTemp ;
+ pItem.reset(new SfxStringItem( m_nSlotID, sTemp ));
+ }
+ else if ( aType == cppu::UnoType< css::frame::status::ItemStatus >::get() )
+ {
+ ItemStatus aItemStatus;
+ rEvent.State >>= aItemStatus;
+ eState = static_cast<SfxItemState>(aItemStatus.State);
+ pItem.reset(new SfxVoidItem( m_nSlotID ));
+ }
+ else if ( aType == cppu::UnoType< css::frame::status::Visibility >::get() )
+ {
+ Visibility aVisibilityStatus;
+ rEvent.State >>= aVisibilityStatus;
+ pItem.reset(new SfxVisibilityItem( m_nSlotID, aVisibilityStatus.bVisible ));
+ }
+ else
+ {
+ if ( pSlot )
+ pItem = pSlot->GetType()->CreateItem();
+ if ( pItem )
+ {
+ pItem->SetWhich( m_nSlotID );
+ pItem->PutValue( rEvent.State, 0 );
+ }
+ else
+ pItem.reset(new SfxVoidItem( m_nSlotID ));
+ }
+ }
+
+ StateChangedAtStatusListener( eState, pItem.get() );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/control/shell.cxx b/sfx2/source/control/shell.cxx
new file mode 100644
index 000000000..ab3f7e869
--- /dev/null
+++ b/sfx2/source/control/shell.cxx
@@ -0,0 +1,743 @@
+/* -*- 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/embed/VerbDescriptor.hpp>
+#include <com/sun/star/embed/VerbAttributes.hpp>
+#include <officecfg/Office/Common.hxx>
+#include <rtl/ustring.hxx>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+#include <svl/itempool.hxx>
+#include <svl/setitem.hxx>
+#include <svl/undo.hxx>
+#include <itemdel.hxx>
+#include <svtools/asynclink.hxx>
+#include <unotools/configmgr.hxx>
+#include <comphelper/lok.hxx>
+#include <sfx2/shell.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/objface.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/viewsh.hxx>
+#include <sfx2/request.hxx>
+#include <sfx2/sfxsids.hrc>
+#include <statcach.hxx>
+#include <sidebar/ContextChangeBroadcaster.hxx>
+#include <com/sun/star/ui/dialogs/XSLTFilterDialog.hpp>
+#include <tools/debug.hxx>
+
+#include <memory>
+#include <vector>
+#include <map>
+
+#include <desktop/crashreport.hxx>
+
+using namespace com::sun::star;
+
+struct SfxShell_Impl: public SfxBroadcaster
+{
+ OUString aObjectName; // Name of Sbx-Objects
+ // Maps the Which() field to a pointer to a SfxPoolItem
+ std::map<sal_uInt16, std::unique_ptr<SfxPoolItem>>
+ m_Items; // Data exchange on Item level
+ SfxViewShell* pViewSh; // SfxViewShell if Shell is
+ // ViewFrame/ViewShell/SubShell list
+ SfxViewFrame* pFrame; // Frame, if <UI-active>
+ SfxRepeatTarget* pRepeatTarget; // SbxObjectRef xParent;
+ bool bActive;
+ SfxDisableFlags nDisableFlags;
+ std::unique_ptr<svtools::AsynchronLink> pExecuter;
+ std::unique_ptr<svtools::AsynchronLink> pUpdater;
+ std::vector<std::unique_ptr<SfxSlot> > aSlotArr;
+
+ css::uno::Sequence < css::embed::VerbDescriptor > aVerbList;
+ ::sfx2::sidebar::ContextChangeBroadcaster maContextChangeBroadcaster;
+
+ SfxShell_Impl()
+ : pViewSh(nullptr)
+ , pFrame(nullptr)
+ , pRepeatTarget(nullptr)
+ , bActive(false)
+ , nDisableFlags(SfxDisableFlags::NONE)
+ {
+ }
+
+ virtual ~SfxShell_Impl() override { pExecuter.reset(); pUpdater.reset();}
+};
+
+
+void SfxShell::EmptyExecStub(SfxShell *, SfxRequest &)
+{
+}
+
+void SfxShell::EmptyStateStub(SfxShell *, SfxItemSet &)
+{
+}
+
+SfxShell::SfxShell()
+: pImpl(new SfxShell_Impl),
+ pPool(nullptr),
+ pUndoMgr(nullptr)
+{
+}
+
+SfxShell::SfxShell( SfxViewShell *pViewSh )
+: pImpl(new SfxShell_Impl),
+ pPool(nullptr),
+ pUndoMgr(nullptr)
+{
+ pImpl->pViewSh = pViewSh;
+}
+
+SfxShell::~SfxShell()
+{
+}
+
+void SfxShell::SetName( const OUString &rName )
+{
+ pImpl->aObjectName = rName;
+}
+
+const OUString& SfxShell::GetName() const
+{
+ return pImpl->aObjectName;
+}
+
+SfxDispatcher* SfxShell::GetDispatcher() const
+{
+ return pImpl->pFrame ? pImpl->pFrame->GetDispatcher() : nullptr;
+}
+
+SfxViewShell* SfxShell::GetViewShell() const
+{
+ return pImpl->pViewSh;
+}
+
+SfxViewFrame* SfxShell::GetFrame() const
+{
+ if ( pImpl->pFrame )
+ return pImpl->pFrame;
+ if ( pImpl->pViewSh )
+ return pImpl->pViewSh->GetViewFrame();
+ return nullptr;
+}
+
+const SfxPoolItem* SfxShell::GetItem
+(
+ sal_uInt16 nSlotId // Slot-Id of the querying <SfxPoolItem>s
+) const
+{
+ auto const it = pImpl->m_Items.find( nSlotId );
+ if (it != pImpl->m_Items.end())
+ return it->second.get();
+ return nullptr;
+}
+
+void SfxShell::PutItem
+(
+ const SfxPoolItem& rItem /* Instance, of which a copy is created,
+ which is stored in the SfxShell in a list. */
+)
+{
+ DBG_ASSERT( dynamic_cast< const SfxSetItem* >( &rItem) == nullptr, "SetItems aren't allowed here" );
+ DBG_ASSERT( SfxItemPool::IsSlot( rItem.Which() ),
+ "items with Which-Ids aren't allowed here" );
+
+ // MSC made a mess here of WNT/W95, beware of changes
+ SfxPoolItem *pItem = rItem.Clone();
+ SfxPoolItemHint aItemHint( pItem );
+ sal_uInt16 nWhich = rItem.Which();
+
+ auto const it = pImpl->m_Items.find(nWhich);
+ if (it != pImpl->m_Items.end())
+ {
+ // Replace Item
+ pImpl->m_Items.erase( it );
+ pImpl->m_Items.insert(std::make_pair(nWhich, std::unique_ptr<SfxPoolItem>(pItem)));
+
+ // if active, notify Bindings
+ SfxDispatcher *pDispat = GetDispatcher();
+ if ( pDispat )
+ {
+ SfxBindings* pBindings = pDispat->GetBindings();
+ pBindings->Broadcast( aItemHint );
+ sal_uInt16 nSlotId = nWhich; //pItem->GetSlotId();
+ SfxStateCache* pCache = pBindings->GetStateCache( nSlotId );
+ if ( pCache )
+ {
+ pCache->SetState( SfxItemState::DEFAULT, pItem, true );
+ pCache->SetCachedState( true );
+ }
+ }
+ return;
+ }
+ else
+ {
+ Broadcast( aItemHint );
+ pImpl->m_Items.insert(std::make_pair(nWhich, std::unique_ptr<SfxPoolItem>(pItem)));
+ }
+}
+
+SfxInterface* SfxShell::GetInterface() const
+{
+ return GetStaticInterface();
+}
+
+SfxUndoManager* SfxShell::GetUndoManager()
+{
+ return pUndoMgr;
+}
+
+void SfxShell::SetUndoManager( SfxUndoManager *pNewUndoMgr )
+{
+ OSL_ENSURE( ( pUndoMgr == nullptr ) || ( pNewUndoMgr == nullptr ) || ( pUndoMgr == pNewUndoMgr ),
+ "SfxShell::SetUndoManager: exchanging one non-NULL manager with another non-NULL manager? Suspicious!" );
+ // there's at least one client of our UndoManager - the DocumentUndoManager at the SfxBaseModel - which
+ // caches the UndoManager, and registers itself as listener. If exchanging non-NULL UndoManagers is really
+ // a supported scenario (/me thinks it is not), then we would need to notify all such clients instances.
+
+ pUndoMgr = pNewUndoMgr;
+ if (pUndoMgr && !utl::ConfigManager::IsFuzzing())
+ {
+ pUndoMgr->SetMaxUndoActionCount(
+ officecfg::Office::Common::Undo::Steps::get());
+ }
+}
+
+SfxRepeatTarget* SfxShell::GetRepeatTarget() const
+{
+ return pImpl->pRepeatTarget;
+}
+
+void SfxShell::SetRepeatTarget( SfxRepeatTarget *pTarget )
+{
+ pImpl->pRepeatTarget = pTarget;
+}
+
+void SfxShell::Invalidate
+(
+ sal_uInt16 nId /* Invalidated Slot-Id or Which-Id.
+ If these are 0 (default), then all
+ by this Shell currently handled Slot-Ids are
+ invalidated. */
+)
+{
+ if ( !GetViewShell() )
+ {
+ OSL_FAIL( "wrong Invalidate method called!" );
+ return;
+ }
+
+ Invalidate_Impl( GetViewShell()->GetViewFrame()->GetBindings(), nId );
+}
+
+void SfxShell::Invalidate_Impl( SfxBindings& rBindings, sal_uInt16 nId )
+{
+ if ( nId == 0 )
+ {
+ rBindings.InvalidateShell( *this );
+ }
+ else
+ {
+ const SfxInterface *pIF = GetInterface();
+ do
+ {
+ const SfxSlot *pSlot = pIF->GetSlot(nId);
+ if ( pSlot )
+ {
+ // Invalidate the Slot itself
+ rBindings.Invalidate( pSlot->GetSlotId() );
+ return;
+ }
+
+ pIF = pIF->GetGenoType();
+ }
+
+ while ( pIF );
+
+ SAL_INFO( "sfx.control", "W3: invalidating slot-id unknown in shell" );
+ }
+}
+
+void SfxShell::HandleOpenXmlFilterSettings(SfxRequest & rReq)
+{
+ try
+ {
+ uno::Reference < ui::dialogs::XExecutableDialog > xDialog = ui::dialogs::XSLTFilterDialog::create( ::comphelper::getProcessComponentContext() );
+ xDialog->execute();
+ }
+ catch (const uno::Exception&)
+ {
+ }
+ rReq.Ignore ();
+}
+
+void SfxShell::DoActivate_Impl( SfxViewFrame *pFrame, bool bMDI )
+{
+ SfxObjectShell* pObjectShell = GetObjectShell();
+ if ( pObjectShell )
+ {
+ const OUString sActiveDocName = pObjectShell->GetTitle();
+ if( !pImpl->aObjectName.startsWith(sActiveDocName) )
+ {
+ CrashReporter::setActiveSfxObjectName(pImpl->aObjectName);
+ }
+ }
+ else
+ {
+ CrashReporter::setActiveSfxObjectName(pImpl->aObjectName);
+ }
+
+#ifdef DBG_UTIL
+ const SfxInterface *p_IF = GetInterface();
+ if ( !p_IF )
+ return;
+#endif
+ SAL_INFO(
+ "sfx.control",
+ "SfxShell::DoActivate() " << this << " " << GetInterface()->GetClassName()
+ << " bMDI " << (bMDI ? "MDI" : ""));
+
+ if ( bMDI )
+ {
+ // Remember Frame, in which it was activated
+ pImpl->pFrame = pFrame;
+ pImpl->bActive = true;
+ }
+
+ // Notify Subclass
+ Activate(bMDI);
+}
+
+void SfxShell::DoDeactivate_Impl( SfxViewFrame const *pFrame, bool bMDI )
+{
+#ifdef DBG_UTIL
+ const SfxInterface *p_IF = GetInterface();
+ if ( !p_IF )
+ return;
+#endif
+ SAL_INFO(
+ "sfx.control",
+ "SfxShell::DoDeactivate()" << this << " " << GetInterface()->GetClassName()
+ << " bMDI " << (bMDI ? "MDI" : ""));
+
+ // Only when it comes from a Frame
+ // (not when for instance by popping BASIC-IDE from AppDisp)
+ if ( bMDI && pImpl->pFrame == pFrame )
+ {
+ // deliver
+ pImpl->pFrame = nullptr;
+ pImpl->bActive = false;
+ }
+
+ // Notify Subclass
+ Deactivate(bMDI);
+}
+
+bool SfxShell::IsActive() const
+{
+ return pImpl->bActive;
+}
+
+void SfxShell::Activate
+(
+ bool /*bMDI*/ /* TRUE
+ the <SfxDispatcher>, on which the SfxShell is
+ located, is activated or the SfxShell instance
+ was pushed on an active SfxDispatcher.
+ (compare with SystemWindow::IsMDIActivate())
+
+ FALSE
+ the <SfxViewFrame>, on which SfxDispatcher
+ the SfxShell instance is located, was
+ activated. (for example by a closing dialog) */
+)
+{
+ BroadcastContextForActivation(true);
+}
+
+void SfxShell::Deactivate
+(
+ bool /*bMDI*/ /* TRUE
+ the <SfxDispatcher>, on which the SfxShell is
+ located, is inactivated or the SfxShell instance
+ was popped on an active SfxDispatcher.
+ (compare with SystemWindow::IsMDIActivate())
+
+ FALSE
+ the <SfxViewFrame>, on which SfxDispatcher
+ the SfxShell instance is located, was
+ deactivated. (for example by a dialog) */
+)
+{
+ BroadcastContextForActivation(false);
+}
+
+bool SfxShell::CanExecuteSlot_Impl( const SfxSlot &rSlot )
+{
+ // Get Slot status
+ SfxItemPool &rPool = GetPool();
+ const sal_uInt16 nId = rSlot.GetWhich( rPool );
+ SfxItemSet aSet(rPool, nId, nId);
+ SfxStateFunc pFunc = rSlot.GetStateFnc();
+ (*pFunc)( this, aSet );
+ return aSet.GetItemState(nId) != SfxItemState::DISABLED;
+}
+
+bool SfxShell::IsConditionalFastCall( const SfxRequest &rReq )
+{
+ sal_uInt16 nId = rReq.GetSlot();
+ bool bRet = false;
+
+ if (nId == SID_UNDO || nId == SID_REDO)
+ {
+ const SfxItemSet* pArgs = rReq.GetArgs();
+ if (pArgs && pArgs->HasItem(SID_REPAIRPACKAGE))
+ bRet = true;
+ }
+ return bRet;
+}
+
+
+static void ShellCall_Impl( void* pObj, void* pArg )
+{
+ static_cast<SfxShell*>(pObj)->ExecuteSlot( *static_cast<SfxRequest*>(pArg) );
+}
+
+void SfxShell::ExecuteSlot( SfxRequest& rReq, bool bAsync )
+{
+ if( !bAsync )
+ ExecuteSlot( rReq );
+ else
+ {
+ if( !pImpl->pExecuter )
+ pImpl->pExecuter.reset( new svtools::AsynchronLink(
+ Link<void*,void>( this, ShellCall_Impl ) ) );
+ pImpl->pExecuter->Call( new SfxRequest( rReq ) );
+ }
+}
+
+const SfxPoolItem* SfxShell::ExecuteSlot
+(
+ SfxRequest &rReq, // the relayed <SfxRequest>
+ const SfxInterface* pIF // default = 0 means get virtually
+)
+{
+ if ( !pIF )
+ pIF = GetInterface();
+
+ sal_uInt16 nSlot = rReq.GetSlot();
+ const SfxSlot* pSlot = nullptr;
+ if ( nSlot >= SID_VERB_START && nSlot <= SID_VERB_END )
+ pSlot = GetVerbSlot_Impl(nSlot);
+ if ( !pSlot )
+ pSlot = pIF->GetSlot(nSlot);
+ DBG_ASSERT( pSlot, "slot not supported" );
+
+ SfxExecFunc pFunc = pSlot->GetExecFnc();
+ if ( pFunc )
+ (*pFunc)( this, rReq );
+
+ return rReq.GetReturnValue();
+}
+
+const SfxPoolItem* SfxShell::GetSlotState
+(
+ sal_uInt16 nSlotId, // Slot-Id to the Slots in question
+ const SfxInterface* pIF, // default = 0 means get virtually
+ SfxItemSet* pStateSet // SfxItemSet of the Slot-State method
+)
+{
+ // Get Slot on the given Interface
+ if ( !pIF )
+ pIF = GetInterface();
+ SfxItemState eState(SfxItemState::DEFAULT);
+ bool bItemStateSet(false);
+ SfxItemPool &rPool = GetPool();
+
+ const SfxSlot* pSlot = nullptr;
+ if ( nSlotId >= SID_VERB_START && nSlotId <= SID_VERB_END )
+ pSlot = GetVerbSlot_Impl(nSlotId);
+ if ( !pSlot )
+ pSlot = pIF->GetSlot(nSlotId);
+ if ( pSlot )
+ // Map on Which-Id if possible
+ nSlotId = pSlot->GetWhich( rPool );
+
+ // Get Item and Item status
+ const SfxPoolItem *pItem = nullptr;
+ SfxItemSet aSet( rPool, nSlotId, nSlotId ); // else pItem dies too soon
+ if ( nullptr != pSlot )
+ {
+ // Call Status method
+ SfxStateFunc pFunc = pSlot->GetStateFnc();
+ if ( pFunc )
+ (*pFunc)( this, aSet );
+ eState = aSet.GetItemState( nSlotId, true, &pItem );
+ bItemStateSet = true;
+
+ // get default Item if possible
+ if ( eState == SfxItemState::DEFAULT )
+ {
+ if ( SfxItemPool::IsWhich(nSlotId) )
+ pItem = &rPool.GetDefaultItem(nSlotId);
+ else
+ eState = SfxItemState::DONTCARE;
+ }
+ }
+
+ // Evaluate Item and item status and possibly maintain them in pStateSet
+ std::unique_ptr<SfxPoolItem> pRetItem;
+ if ( !bItemStateSet || eState <= SfxItemState::DISABLED )
+ {
+ if ( pStateSet )
+ pStateSet->DisableItem(nSlotId);
+ return nullptr;
+ }
+ else if ( bItemStateSet && eState == SfxItemState::DONTCARE )
+ {
+ if ( pStateSet )
+ pStateSet->ClearItem(nSlotId);
+ pRetItem.reset( new SfxVoidItem(0) );
+ }
+ else // bItemStateSet && eState >= SfxItemState::DEFAULT
+ {
+ if ( pStateSet && pStateSet->Put( *pItem ) )
+ return &pStateSet->Get( pItem->Which() );
+ pRetItem.reset(pItem->Clone());
+ }
+ auto pTemp = pRetItem.get();
+ DeleteItemOnIdle(std::move(pRetItem));
+
+ return pTemp;
+}
+
+static SFX_EXEC_STUB(SfxShell, VerbExec)
+static void SfxStubSfxShellVerbState(SfxShell *, SfxItemSet& rSet)
+{
+ SfxShell::VerbState( rSet );
+}
+
+void SfxShell::SetVerbs(const css::uno::Sequence < css::embed::VerbDescriptor >& aVerbs)
+{
+ SfxViewShell *pViewSh = dynamic_cast<SfxViewShell*>( this );
+
+ DBG_ASSERT(pViewSh, "Only call SetVerbs at the ViewShell!");
+ if ( !pViewSh )
+ return;
+
+ // First make all Statecaches dirty, so that no-one no longer tries to use
+ // the Slots
+ {
+ SfxBindings *pBindings =
+ pViewSh->GetViewFrame()->GetDispatcher()->GetBindings();
+ sal_uInt16 nCount = pImpl->aSlotArr.size();
+ for (sal_uInt16 n1=0; n1<nCount ; n1++)
+ {
+ sal_uInt16 nId = SID_VERB_START + n1;
+ pBindings->Invalidate(nId, false, true);
+ }
+ }
+
+ sal_uInt16 nr=0;
+ for (sal_Int32 n=0; n<aVerbs.getLength(); n++)
+ {
+ sal_uInt16 nSlotId = SID_VERB_START + nr++;
+ DBG_ASSERT(nSlotId <= SID_VERB_END, "Too many Verbs!");
+ if (nSlotId > SID_VERB_END)
+ break;
+
+ SfxSlot *pNewSlot = new SfxSlot;
+ pNewSlot->nSlotId = nSlotId;
+ pNewSlot->nGroupId = SfxGroupId::NONE;
+
+ // Verb slots must be executed asynchronously, so that they can be
+ // destroyed while executing.
+ pNewSlot->nFlags = SfxSlotMode::ASYNCHRON | SfxSlotMode::CONTAINER;
+ pNewSlot->nMasterSlotId = 0;
+ pNewSlot->nValue = 0;
+ pNewSlot->fnExec = SFX_STUB_PTR(SfxShell,VerbExec);
+ pNewSlot->fnState = SFX_STUB_PTR(SfxShell,VerbState);
+ pNewSlot->pType = nullptr; // HACK(SFX_TYPE(SfxVoidItem)) ???
+ pNewSlot->nArgDefCount = 0;
+ pNewSlot->pFirstArgDef = nullptr;
+ pNewSlot->pUnoName = nullptr;
+
+ if (!pImpl->aSlotArr.empty())
+ {
+ SfxSlot& rSlot = *pImpl->aSlotArr[0];
+ pNewSlot->pNextSlot = rSlot.pNextSlot;
+ rSlot.pNextSlot = pNewSlot;
+ }
+ else
+ pNewSlot->pNextSlot = pNewSlot;
+
+ pImpl->aSlotArr.insert(pImpl->aSlotArr.begin() + static_cast<sal_uInt16>(n), std::unique_ptr<SfxSlot>(pNewSlot));
+ }
+
+ pImpl->aVerbList = aVerbs;
+
+ // The status of SID_OBJECT is collected in the controller directly on
+ // the Shell, it is thus enough to encourage a new status update
+ SfxBindings* pBindings = pViewSh->GetViewFrame()->GetDispatcher()->GetBindings();
+ pBindings->Invalidate(SID_OBJECT, true, true);
+}
+
+const css::uno::Sequence < css::embed::VerbDescriptor >& SfxShell::GetVerbs() const
+{
+ return pImpl->aVerbList;
+}
+
+void SfxShell::VerbExec(SfxRequest& rReq)
+{
+ sal_uInt16 nId = rReq.GetSlot();
+ SfxViewShell *pViewShell = GetViewShell();
+ if ( !pViewShell )
+ return;
+
+ bool bReadOnly = pViewShell->GetObjectShell()->IsReadOnly();
+ const css::uno::Sequence < css::embed::VerbDescriptor > aList = pViewShell->GetVerbs();
+ sal_Int32 nVerb = 0;
+ for (const auto& rVerb : aList)
+ {
+ // check for ReadOnly verbs
+ if ( bReadOnly && !(rVerb.VerbAttributes & embed::VerbAttributes::MS_VERBATTR_NEVERDIRTIES) )
+ continue;
+
+ // check for verbs that shouldn't appear in the menu
+ if ( !(rVerb.VerbAttributes & embed::VerbAttributes::MS_VERBATTR_ONCONTAINERMENU) )
+ continue;
+
+ if (nId == SID_VERB_START + nVerb++)
+ {
+ pViewShell->DoVerb(rVerb.VerbID);
+ rReq.Done();
+ return;
+ }
+ }
+}
+
+void SfxShell::VerbState(SfxItemSet& )
+{
+}
+
+const SfxSlot* SfxShell::GetVerbSlot_Impl(sal_uInt16 nId) const
+{
+ css::uno::Sequence < css::embed::VerbDescriptor > rList = pImpl->aVerbList;
+
+ DBG_ASSERT(nId >= SID_VERB_START && nId <= SID_VERB_END,"Wrong VerbId!");
+ sal_uInt16 nIndex = nId - SID_VERB_START;
+ DBG_ASSERT(nIndex < rList.getLength(),"Wrong VerbId!");
+
+ if (nIndex < rList.getLength())
+ return pImpl->aSlotArr[nIndex].get();
+ else
+ return nullptr;
+}
+
+SfxObjectShell* SfxShell::GetObjectShell()
+{
+ if ( GetViewShell() )
+ return GetViewShell()->GetViewFrame()->GetObjectShell();
+ else
+ return nullptr;
+}
+
+bool SfxShell::HasUIFeature(SfxShellFeature) const
+{
+ return false;
+}
+
+static void DispatcherUpdate_Impl( void*, void* pArg )
+{
+ static_cast<SfxDispatcher*>(pArg)->Update_Impl( true );
+ static_cast<SfxDispatcher*>(pArg)->GetBindings()->InvalidateAll(false);
+}
+
+void SfxShell::UIFeatureChanged()
+{
+ SfxViewFrame *pFrame = GetFrame();
+ if ( pFrame && pFrame->IsVisible() )
+ {
+ // Also force an update, if dispatcher is already updated otherwise
+ // something my get stuck in the bunkered tools. Asynchronous call to
+ // prevent recursion.
+ if ( !pImpl->pUpdater )
+ pImpl->pUpdater.reset( new svtools::AsynchronLink( Link<void*,void>( this, DispatcherUpdate_Impl ) ) );
+
+ // Multiple views allowed
+ pImpl->pUpdater->Call( pFrame->GetDispatcher(), true );
+ }
+}
+
+void SfxShell::SetDisableFlags( SfxDisableFlags nFlags )
+{
+ pImpl->nDisableFlags = nFlags;
+}
+
+SfxDisableFlags SfxShell::GetDisableFlags() const
+{
+ return pImpl->nDisableFlags;
+}
+
+std::optional<SfxItemSet> SfxShell::CreateItemSet( sal_uInt16 )
+{
+ return {};
+}
+
+void SfxShell::ApplyItemSet( sal_uInt16, const SfxItemSet& )
+{
+}
+
+void SfxShell::SetContextName (const OUString& rsContextName)
+{
+ pImpl->maContextChangeBroadcaster.Initialize(rsContextName);
+}
+
+void SfxShell::SetViewShell_Impl( SfxViewShell* pView )
+{
+ pImpl->pViewSh = pView;
+}
+
+void SfxShell::BroadcastContextForActivation (const bool bIsActivated)
+{
+ // Avoids activation and de-activation (can be seen on switching view) from causing
+ // the sidebar to re-build. Such switching can happen as we change view to render
+ // using LOK for example, and is un-necessary for Online.
+ if (comphelper::LibreOfficeKit::isDialogPainting())
+ return;
+
+ SfxViewFrame* pViewFrame = GetFrame();
+ if (pViewFrame != nullptr)
+ {
+ if (bIsActivated)
+ pImpl->maContextChangeBroadcaster.Activate(pViewFrame->GetFrame().GetFrameInterface());
+ else
+ pImpl->maContextChangeBroadcaster.Deactivate(pViewFrame->GetFrame().GetFrameInterface());
+ }
+}
+
+bool SfxShell::SetContextBroadcasterEnabled (const bool bIsEnabled)
+{
+ return pImpl->maContextChangeBroadcaster.SetBroadcasterEnabled(bIsEnabled);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/control/sorgitm.cxx b/sfx2/source/control/sorgitm.cxx
new file mode 100644
index 000000000..a9005619a
--- /dev/null
+++ b/sfx2/source/control/sorgitm.cxx
@@ -0,0 +1,85 @@
+/* -*- 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/sfxsids.hrc>
+#include <sorgitm.hxx>
+#include <osl/diagnose.h>
+
+SfxPoolItem* SfxScriptOrganizerItem::CreateDefault() { return new SfxScriptOrganizerItem; }
+
+
+SfxScriptOrganizerItem::SfxScriptOrganizerItem()
+{
+}
+
+SfxScriptOrganizerItem* SfxScriptOrganizerItem::Clone( SfxItemPool * ) const
+{
+ return new SfxScriptOrganizerItem( *this );
+}
+
+bool SfxScriptOrganizerItem::operator==( const SfxPoolItem& rItem) const
+{
+ return SfxStringItem::operator==(rItem) &&
+ aLanguage == static_cast<const SfxScriptOrganizerItem &>(rItem).aLanguage;
+}
+
+
+bool SfxScriptOrganizerItem::QueryValue( css::uno::Any& rVal, sal_uInt8 nMemberId ) const
+{
+ OUString aValue;
+ nMemberId &= ~CONVERT_TWIPS;
+ switch ( nMemberId )
+ {
+ case 0:
+ case MID_SCRIPT_ORGANIZER_LANGUAGE:
+ aValue = aLanguage;
+ break;
+ default:
+ OSL_FAIL("Wrong MemberId!");
+ return false;
+ }
+
+ rVal <<= aValue;
+
+ return true;
+}
+
+bool SfxScriptOrganizerItem::PutValue( const css::uno::Any& rVal, sal_uInt8 nMemberId )
+{
+ OUString aValue;
+ bool bRet = false;
+ nMemberId &= ~CONVERT_TWIPS;
+ switch ( nMemberId )
+ {
+ case 0:
+ case MID_SCRIPT_ORGANIZER_LANGUAGE:
+ bRet = (rVal >>= aValue);
+ if ( bRet )
+ aLanguage = aValue;
+ break;
+ default:
+ OSL_FAIL("Wrong MemberId!");
+ return false;
+ }
+
+ return bRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/control/statcach.cxx b/sfx2/source/control/statcach.cxx
new file mode 100644
index 000000000..ef1eaf1a1
--- /dev/null
+++ b/sfx2/source/control/statcach.cxx
@@ -0,0 +1,503 @@
+/* -*- 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 <framework/dispatchhelper.hxx>
+#include <com/sun/star/frame/DispatchResultState.hpp>
+#include <com/sun/star/frame/XFrame.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <comphelper/servicehelper.hxx>
+#include <svl/eitem.hxx>
+#include <svl/intitem.hxx>
+#include <svl/stritem.hxx>
+#include <svl/visitem.hxx>
+
+#include <sfx2/app.hxx>
+#include <statcach.hxx>
+#include <sfx2/msg.hxx>
+#include <sfx2/ctrlitem.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/sfxuno.hxx>
+#include <unoctitm.hxx>
+#include <sfx2/msgpool.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <tools/diagnose_ex.h>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::util;
+
+BindDispatch_Impl::BindDispatch_Impl( const css::uno::Reference< css::frame::XDispatch > & rDisp, const css::util::URL& rURL, SfxStateCache *pStateCache, const SfxSlot* pS )
+ : xDisp( rDisp )
+ , aURL( rURL )
+ , pCache( pStateCache )
+ , pSlot( pS )
+{
+ DBG_ASSERT( pCache && pSlot, "Invalid BindDispatch!");
+ aStatus.IsEnabled = true;
+}
+
+void SAL_CALL BindDispatch_Impl::disposing( const css::lang::EventObject& )
+{
+ if ( xDisp.is() )
+ {
+ xDisp->removeStatusListener( static_cast<css::frame::XStatusListener*>(this), aURL );
+ xDisp.clear();
+ }
+}
+
+void SAL_CALL BindDispatch_Impl::statusChanged( const css::frame::FeatureStateEvent& rEvent )
+{
+ aStatus = rEvent;
+ if ( !pCache )
+ return;
+
+ css::uno::Reference< css::frame::XStatusListener > xKeepAlive(this);
+ if ( aStatus.Requery )
+ pCache->Invalidate( true );
+ else
+ {
+ std::unique_ptr<SfxPoolItem> pItem;
+ sal_uInt16 nId = pCache->GetId();
+ SfxItemState eState = SfxItemState::DISABLED;
+ if ( !aStatus.IsEnabled )
+ {
+ // default
+ }
+ else if (aStatus.State.hasValue())
+ {
+ eState = SfxItemState::DEFAULT;
+ css::uno::Any aAny = aStatus.State;
+
+ const css::uno::Type& aType = aAny.getValueType();
+ if ( aType == cppu::UnoType< bool >::get() )
+ {
+ bool bTemp = false;
+ aAny >>= bTemp ;
+ pItem.reset( new SfxBoolItem( nId, bTemp ) );
+ }
+ else if ( aType == ::cppu::UnoType< ::cppu::UnoUnsignedShortType >::get() )
+ {
+ sal_uInt16 nTemp = 0;
+ aAny >>= nTemp ;
+ pItem.reset( new SfxUInt16Item( nId, nTemp ) );
+ }
+ else if ( aType == cppu::UnoType<sal_uInt32>::get() )
+ {
+ sal_uInt32 nTemp = 0;
+ aAny >>= nTemp ;
+ pItem.reset( new SfxUInt32Item( nId, nTemp ) );
+ }
+ else if ( aType == cppu::UnoType<OUString>::get() )
+ {
+ OUString sTemp ;
+ aAny >>= sTemp ;
+ pItem.reset( new SfxStringItem( nId, sTemp ) );
+ }
+ else
+ {
+ if ( pSlot )
+ pItem = pSlot->GetType()->CreateItem();
+ if ( pItem )
+ {
+ pItem->SetWhich( nId );
+ pItem->PutValue( aAny, 0 );
+ }
+ else
+ pItem.reset( new SfxVoidItem( nId ) );
+ }
+ }
+ else
+ {
+ // DONTCARE status
+ pItem.reset( new SfxVoidItem(0) );
+ eState = SfxItemState::UNKNOWN;
+ }
+
+ for ( SfxControllerItem *pCtrl = pCache->GetItemLink();
+ pCtrl;
+ pCtrl = pCtrl->GetItemLink() )
+ pCtrl->StateChangedAtToolBoxControl( nId, eState, pItem.get() );
+ }
+}
+
+void BindDispatch_Impl::Release()
+{
+ if ( xDisp.is() )
+ {
+ try
+ {
+ xDisp->removeStatusListener(static_cast<css::frame::XStatusListener*>(this), aURL);
+ }
+ catch (const lang::DisposedException&)
+ {
+ TOOLS_WARN_EXCEPTION("sfx", "BindDispatch_Impl::Release: xDisp is disposed: ");
+ }
+ xDisp.clear();
+ }
+ pCache = nullptr;
+}
+
+
+sal_Int16 BindDispatch_Impl::Dispatch( const css::uno::Sequence < css::beans::PropertyValue >& aProps, bool bForceSynchron )
+{
+ sal_Int16 eRet = css::frame::DispatchResultState::DONTKNOW;
+
+ if ( xDisp.is() && aStatus.IsEnabled )
+ {
+ ::rtl::Reference< ::framework::DispatchHelper > xHelper( new ::framework::DispatchHelper(nullptr));
+ css::uno::Any aResult = xHelper->executeDispatch(xDisp, aURL, bForceSynchron, aProps);
+
+ css::frame::DispatchResultEvent aEvent;
+ aResult >>= aEvent;
+
+ eRet = aEvent.State;
+ }
+
+ return eRet;
+}
+
+
+// This constructor for an invalid cache that is updated in the first request.
+
+SfxStateCache::SfxStateCache( sal_uInt16 nFuncId ):
+ nId(nFuncId),
+ pInternalController(nullptr),
+ pController(nullptr),
+ pLastItem( nullptr ),
+ eLastState( SfxItemState::UNKNOWN ),
+ bItemVisible( true )
+{
+ bCtrlDirty = true;
+ bSlotDirty = true;
+ bItemDirty = true;
+}
+
+
+// The Destructor checks by assertion, even if controllers are registered.
+
+SfxStateCache::~SfxStateCache()
+{
+ DBG_ASSERT( pController == nullptr && pInternalController == nullptr, "there are still Controllers registered" );
+ if ( !IsInvalidItem(pLastItem) )
+ delete pLastItem;
+ if ( mxDispatch.is() )
+ mxDispatch->Release();
+}
+
+
+// invalidates the cache (next request will force update)
+void SfxStateCache::Invalidate( bool bWithMsg )
+{
+ bCtrlDirty = true;
+ if ( bWithMsg )
+ {
+ bSlotDirty = true;
+ aSlotServ.SetSlot( nullptr );
+ if ( mxDispatch.is() )
+ mxDispatch->Release();
+ mxDispatch.clear();
+ }
+}
+
+
+// gets the corresponding function from the dispatcher or the cache
+
+const SfxSlotServer* SfxStateCache::GetSlotServer( SfxDispatcher &rDispat , const css::uno::Reference< css::frame::XDispatchProvider > & xProv )
+{
+
+ if ( bSlotDirty )
+ {
+ // get the SlotServer; we need it for internal controllers anyway, but also in most cases
+ rDispat.FindServer_( nId, aSlotServ );
+
+ DBG_ASSERT( !mxDispatch.is(), "Old Dispatch not removed!" );
+
+ // we don't need to check the dispatch provider if we only have an internal controller
+ if ( xProv.is() )
+ {
+ const SfxSlot* pSlot = aSlotServ.GetSlot();
+ if ( !pSlot )
+ // get the slot - even if it is disabled on the dispatcher
+ pSlot = SfxSlotPool::GetSlotPool( rDispat.GetFrame() ).GetSlot( nId );
+
+ if ( !pSlot || !pSlot->pUnoName )
+ {
+ bSlotDirty = false;
+ bCtrlDirty = true;
+ return aSlotServ.GetSlot()? &aSlotServ: nullptr;
+ }
+
+ // create the dispatch URL from the slot data
+ css::util::URL aURL;
+ OUString aCmd = ".uno:";
+ aURL.Protocol = aCmd;
+ aURL.Path = OUString::createFromAscii( pSlot->GetUnoName() );
+ aCmd += aURL.Path;
+ aURL.Complete = aCmd;
+ aURL.Main = aCmd;
+
+ // try to get a dispatch object for this command
+ css::uno::Reference< css::frame::XDispatch > xDisp = xProv->queryDispatch( aURL, OUString(), 0 );
+ if ( xDisp.is() )
+ {
+ // test the dispatch object if it is just a wrapper for a SfxDispatcher
+ css::uno::Reference< css::lang::XUnoTunnel > xTunnel( xDisp, css::uno::UNO_QUERY );
+ if (auto pDisp = comphelper::getFromUnoTunnel<SfxOfficeDispatch>(xTunnel))
+ {
+ // The intercepting object is an SFX component
+ // If this dispatch object does not use the wanted dispatcher or the AppDispatcher, it's treated like any other UNO component
+ // (intercepting by internal dispatches)
+ SfxDispatcher *pDispatcher = pDisp->GetDispatcher_Impl();
+ if ( pDispatcher == &rDispat || pDispatcher == SfxGetpApp()->GetAppDispatcher_Impl() )
+ {
+ // so we can use it directly
+ bSlotDirty = false;
+ bCtrlDirty = true;
+ return aSlotServ.GetSlot()? &aSlotServ: nullptr;
+ }
+ }
+
+ // so the dispatch object isn't a SfxDispatcher wrapper or it is one, but it uses another dispatcher, but not rDispat
+ mxDispatch = new BindDispatch_Impl( xDisp, aURL, this, pSlot );
+
+ // flags must be set before adding StatusListener because the dispatch object will set the state
+ bSlotDirty = false;
+ bCtrlDirty = true;
+ xDisp->addStatusListener( mxDispatch, aURL );
+ }
+ else if ( rDispat.GetFrame() )
+ {
+ css::uno::Reference < css::frame::XDispatchProvider > xFrameProv(
+ rDispat.GetFrame()->GetFrame().GetFrameInterface(), css::uno::UNO_QUERY );
+ if ( xFrameProv != xProv )
+ return GetSlotServer( rDispat, xFrameProv );
+ }
+ }
+
+ bSlotDirty = false;
+ bCtrlDirty = true;
+ }
+
+ // we *always* return a SlotServer (if there is one); but in case of an external dispatch we might not use it
+ // for the "real" (non internal) controllers
+ return aSlotServ.GetSlot()? &aSlotServ: nullptr;
+}
+
+
+// Set Status in all Controllers
+
+void SfxStateCache::SetState
+(
+ SfxItemState eState, // <SfxItemState> from 'pState'
+ const SfxPoolItem* pState, // Slot Status, 0 or -1
+ bool bMaybeDirty
+)
+
+/* [Description]
+
+ This method distributes the status of all of this SID bound
+ <SfxControllerItem>s. If the value is the same as before, and if neither
+ controller was registered nor invalidated inbetween, then no value is
+ passed. This way the flickering is for example avoided in ListBoxes.
+*/
+{
+ SetState_Impl( eState, pState, bMaybeDirty );
+}
+
+void SfxStateCache::GetState
+(
+ boost::property_tree::ptree& rState
+)
+{
+ if ( !mxDispatch.is() && pController )
+ {
+ for ( SfxControllerItem *pCtrl = pController;
+ pCtrl;
+ pCtrl = pCtrl->GetItemLink() )
+ pCtrl->GetControlState( nId, rState );
+ }
+}
+
+void SfxStateCache::SetVisibleState( bool bShow )
+{
+ if ( bShow == bItemVisible )
+ return;
+
+ SfxItemState eState( SfxItemState::DEFAULT );
+ const SfxPoolItem* pState( nullptr );
+ bool bDeleteItem( false );
+
+ bItemVisible = bShow;
+ if ( bShow )
+ {
+ if ( IsInvalidItem(pLastItem) || ( pLastItem == nullptr ))
+ {
+ pState = new SfxVoidItem( nId );
+ bDeleteItem = true;
+ }
+ else
+ pState = pLastItem;
+
+ eState = eLastState;
+ }
+ else
+ {
+ pState = new SfxVisibilityItem( nId, false );
+ bDeleteItem = true;
+ }
+
+ // Update Controller
+ if ( !mxDispatch.is() && pController )
+ {
+ for ( SfxControllerItem *pCtrl = pController;
+ pCtrl;
+ pCtrl = pCtrl->GetItemLink() )
+ pCtrl->StateChangedAtToolBoxControl( nId, eState, pState );
+ }
+
+ if ( pInternalController )
+ pInternalController->StateChangedAtToolBoxControl( nId, eState, pState );
+
+ if ( bDeleteItem )
+ delete pState;
+}
+
+
+void SfxStateCache::SetState_Impl
+(
+ SfxItemState eState, // <SfxItemState> from 'pState'
+ const SfxPoolItem* pState, // Slot Status, 0 or -1
+ bool bMaybeDirty
+)
+{
+ // If a hard update occurs between enter- and leave-registrations is a
+ // can also intermediate Cached exist without controller.
+ if ( !pController && !pInternalController )
+ return;
+
+ DBG_ASSERT( bMaybeDirty || !bSlotDirty, "setting state of dirty message" );
+ DBG_ASSERT( SfxControllerItem::GetItemState(pState) == eState, "invalid SfxItemState" );
+
+ // does the controller have to be notified at all?
+ bool bNotify = bItemDirty;
+ if ( !bItemDirty )
+ {
+ bool bBothAvailable = pLastItem && pState &&
+ !IsInvalidItem(pState) && !IsInvalidItem(pLastItem);
+ DBG_ASSERT( !bBothAvailable || pState != pLastItem, "setting state with own item" );
+ if ( bBothAvailable )
+ bNotify = typeid(*pState) != typeid(*pLastItem) ||
+ *pState != *pLastItem;
+ else
+ bNotify = ( pState != pLastItem ) || ( eState != eLastState );
+ }
+
+ if ( bNotify )
+ {
+ // Update Controller
+ if ( !mxDispatch.is() && pController )
+ {
+ for ( SfxControllerItem *pCtrl = pController;
+ pCtrl;
+ pCtrl = pCtrl->GetItemLink() )
+ pCtrl->StateChangedAtToolBoxControl( nId, eState, pState );
+ }
+
+ if ( pInternalController )
+ static_cast<SfxDispatchController_Impl *>(pInternalController)->StateChanged( nId, eState, pState, &aSlotServ );
+
+ // Remember new value
+ if ( !IsInvalidItem(pLastItem) )
+ {
+ delete pLastItem;
+ pLastItem = nullptr;
+ }
+ if ( pState && !IsInvalidItem(pState) )
+ pLastItem = pState->Clone();
+ else
+ pLastItem = nullptr;
+ eLastState = eState;
+ bItemDirty = false;
+ }
+
+ bCtrlDirty = false;
+}
+
+
+// Set old status again in all the controllers
+
+void SfxStateCache::SetCachedState( bool bAlways )
+{
+ DBG_ASSERT(pController==nullptr||pController->GetId()==nId, "Cache with wrong ControllerItem" );
+
+ // Only update if cached item exists and also able to process.
+ // (If the State is sent, it must be ensured that a SlotServer is present,
+ // see SfxControllerItem:: GetCoreMetric())
+ if ( !(bAlways || ( !bItemDirty && !bSlotDirty )) )
+ return;
+
+ // Update Controller
+ if ( !mxDispatch.is() && pController )
+ {
+ for ( SfxControllerItem *pCtrl = pController;
+ pCtrl;
+ pCtrl = pCtrl->GetItemLink() )
+ pCtrl->StateChangedAtToolBoxControl( nId, eLastState, pLastItem );
+ }
+
+ if ( pInternalController )
+ static_cast<SfxDispatchController_Impl *>(pInternalController)->StateChanged( nId, eLastState, pLastItem, &aSlotServ );
+
+ // Controller is now ok
+ bCtrlDirty = true;
+}
+
+
+css::uno::Reference< css::frame::XDispatch > SfxStateCache::GetDispatch() const
+{
+ if ( mxDispatch.is() )
+ return mxDispatch->xDisp;
+ return css::uno::Reference< css::frame::XDispatch > ();
+}
+
+sal_Int16 SfxStateCache::Dispatch( const SfxItemSet* pSet, bool bForceSynchron )
+{
+ // protect pDispatch against destruction in the call
+ rtl::Reference<BindDispatch_Impl> xKeepAlive( mxDispatch );
+ sal_Int16 eRet = css::frame::DispatchResultState::DONTKNOW;
+
+ if ( mxDispatch.is() )
+ {
+ uno::Sequence < beans::PropertyValue > aArgs;
+ if (pSet)
+ TransformItems( nId, *pSet, aArgs );
+
+ eRet = mxDispatch->Dispatch( aArgs, bForceSynchron );
+ }
+
+ return eRet;
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/control/templatecontaineritem.cxx b/sfx2/source/control/templatecontaineritem.cxx
new file mode 100644
index 000000000..f8b07c6f6
--- /dev/null
+++ b/sfx2/source/control/templatecontaineritem.cxx
@@ -0,0 +1,20 @@
+/* -*- 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 <templatecontaineritem.hxx>
+
+TemplateContainerItem::TemplateContainerItem(sal_uInt16 nId)
+ : mnId(nId)
+ , mnRegionId(0)
+{
+}
+
+TemplateContainerItem::~TemplateContainerItem() {}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/control/templatedefaultview.cxx b/sfx2/source/control/templatedefaultview.cxx
new file mode 100644
index 000000000..da174413e
--- /dev/null
+++ b/sfx2/source/control/templatedefaultview.cxx
@@ -0,0 +1,82 @@
+/* -*- 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 <templatedefaultview.hxx>
+#include <sfx2/thumbnailview.hxx>
+#include <templateviewitem.hxx>
+#include <sfx2/sfxresid.hxx>
+#include <vcl/event.hxx>
+#include <vcl/svapp.hxx>
+
+#include <sfx2/strings.hrc>
+
+#include <officecfg/Office/Common.hxx>
+
+constexpr int gnItemPadding(5); //TODO:: Change padding to 10. It looks really crowded and occupied.
+constexpr tools::Long gnTextHeight = 30;
+
+TemplateDefaultView::TemplateDefaultView(std::unique_ptr<weld::ScrolledWindow> xWindow,
+ std::unique_ptr<weld::Menu> xMenu)
+ : TemplateLocalView(std::move(xWindow), std::move(xMenu))
+{
+ tools::Rectangle aScreen = Application::GetScreenPosSizePixel(Application::GetDisplayBuiltInScreen());
+ tools::Long nItemMaxSize = std::min(aScreen.GetWidth(),aScreen.GetHeight()) > 800 ? 256 : 192;
+ ThumbnailView::setItemDimensions( nItemMaxSize, nItemMaxSize, gnTextHeight, gnItemPadding );
+ updateThumbnailDimensions(nItemMaxSize);
+
+ // startcenter specific settings
+ maFillColor = Color(ColorTransparency, officecfg::Office::Common::Help::StartCenter::StartCenterThumbnailsBackgroundColor::get());
+ maTextColor = Color(ColorTransparency, officecfg::Office::Common::Help::StartCenter::StartCenterThumbnailsTextColor::get());
+ maHighlightColor = Color(ColorTransparency, officecfg::Office::Common::Help::StartCenter::StartCenterThumbnailsHighlightColor::get());
+ maHighlightTextColor = Color(ColorTransparency, officecfg::Office::Common::Help::StartCenter::StartCenterThumbnailsHighlightTextColor::get());
+ mfHighlightTransparence = 0.25;
+
+ UpdateColors();
+}
+
+void TemplateDefaultView::showAllTemplates()
+{
+ mnCurRegionId = 0;
+
+ insertItems(maAllTemplates, false);
+}
+
+bool TemplateDefaultView::KeyInput( const KeyEvent& rKEvt )
+{
+ return ThumbnailView::KeyInput(rKEvt);
+}
+
+bool TemplateDefaultView::MouseButtonDown( const MouseEvent& rMEvt )
+{
+ if( rMEvt.IsLeft() && rMEvt.GetClicks() == 1 )
+ {
+ size_t nPos = ImplGetItem(rMEvt.GetPosPixel());
+ ThumbnailViewItem* pItem = ImplGetItem(nPos);
+ TemplateViewItem* pViewItem = dynamic_cast<TemplateViewItem*>(pItem);
+ if(pViewItem)
+ maOpenTemplateHdl.Call(pViewItem);
+ return true;
+ }
+
+ return TemplateLocalView::MouseButtonDown(rMEvt);
+}
+
+void TemplateDefaultView::createContextMenu()
+{
+ mxContextMenu->clear();
+ mxContextMenu->append("open",SfxResId(STR_OPEN));
+ mxContextMenu->append("edit",SfxResId(STR_EDIT_TEMPLATE));
+ deselectItems();
+ maSelectedItem->setSelection(true);
+ maItemStateHdl.Call(maSelectedItem);
+ ContextMenuSelectHdl(mxContextMenu->popup_at_rect(GetDrawingArea(), tools::Rectangle(maPosition, Size(1,1))));
+ Invalidate();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/control/templatedlglocalview.cxx b/sfx2/source/control/templatedlglocalview.cxx
new file mode 100644
index 000000000..704468681
--- /dev/null
+++ b/sfx2/source/control/templatedlglocalview.cxx
@@ -0,0 +1,416 @@
+/* -*- 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/templatedlglocalview.hxx>
+
+#include <comphelper/string.hxx>
+#include <sfx2/inputdlg.hxx>
+#include <templateviewitem.hxx>
+#include <sfx2/sfxresid.hxx>
+#include <templatecontaineritem.hxx>
+#include <sfx2/strings.hrc>
+#include <vcl/commandevent.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/event.hxx>
+#include <sfx2/doctempl.hxx>
+#include <bitmaps.hlst>
+
+TemplateDlgLocalView::TemplateDlgLocalView(std::unique_ptr<weld::ScrolledWindow> xWindow,
+ std::unique_ptr<weld::Menu> xMenu,
+ std::unique_ptr<weld::TreeView> xTreeView)
+ : TemplateLocalView(std::move(xWindow), std::move(xMenu))
+ , ListView(std::move(xTreeView))
+ , mViewMode(TemplateViewMode::eThumbnailView)
+{
+ mxTreeView->connect_row_activated(LINK(this, TemplateDlgLocalView, RowActivatedHdl));
+ mxTreeView->connect_column_clicked(LINK(this, ListView, ColumnClickedHdl));
+ mxTreeView->connect_changed(LINK(this, TemplateDlgLocalView, ListViewChangedHdl));
+ mxTreeView->connect_popup_menu(LINK(this, TemplateDlgLocalView, PopupMenuHdl));
+ mxTreeView->connect_key_press(LINK(this, TemplateDlgLocalView, KeyPressHdl));
+}
+
+void TemplateDlgLocalView::showAllTemplates()
+{
+ mnCurRegionId = 0;
+
+ insertItems(maAllTemplates, false, true);
+
+ maOpenRegionHdl.Call(nullptr);
+}
+
+void TemplateDlgLocalView::showRegion(TemplateContainerItem const* pItem)
+{
+ mnCurRegionId = pItem->mnRegionId + 1;
+
+ insertItems(pItem->maTemplates, true, false);
+
+ maOpenRegionHdl.Call(nullptr);
+}
+
+void TemplateDlgLocalView::showRegion(std::u16string_view rName)
+{
+ for (auto const& pRegion : maRegions)
+ {
+ if (pRegion->maTitle == rName)
+ {
+ showRegion(pRegion.get());
+ break;
+ }
+ }
+}
+
+void TemplateDlgLocalView::reload()
+{
+ mpDocTemplates->Update();
+ OUString sCurRegionName = getRegionItemName(mnCurRegionId);
+ Populate();
+ mnCurRegionId = getRegionId(sCurRegionName);
+
+ // Check if we are currently browsing a region or root folder
+ if (mnCurRegionId)
+ {
+ sal_uInt16 nRegionId = mnCurRegionId - 1; //Is offset by 1
+
+ for (auto const& pRegion : maRegions)
+ {
+ if (pRegion->mnRegionId == nRegionId)
+ {
+ showRegion(pRegion.get());
+ break;
+ }
+ }
+ }
+ else
+ showAllTemplates();
+
+ //No items should be selected by default
+ ThumbnailView::deselectItems();
+ ListView::unselect_all();
+}
+
+void TemplateDlgLocalView::createContextMenu(const bool bIsDefault, const bool bIsBuiltIn,
+ const bool bIsSingleSel, const OUString& rDefaultImg)
+{
+ mxContextMenu->clear();
+ mxContextMenu->append("open", SfxResId(STR_OPEN), BMP_MENU_OPEN);
+ mxContextMenu->append("edit", SfxResId(STR_EDIT_TEMPLATE), BMP_MENU_EDIT);
+
+ if (!bIsDefault)
+ mxContextMenu->append("default", SfxResId(STR_DEFAULT_TEMPLATE), rDefaultImg);
+ else
+ mxContextMenu->append("default", SfxResId(STR_RESET_DEFAULT), rDefaultImg);
+
+ mxContextMenu->append_separator("separator1");
+ mxContextMenu->append("rename", SfxResId(STR_SFX_RENAME), BMP_MENU_RENAME);
+ mxContextMenu->append("delete", SfxResId(STR_DELETE_TEMPLATE), BMP_MENU_DELETE);
+ mxContextMenu->append_separator("separator2");
+ mxContextMenu->append("move", SfxResId(STR_MOVE), BMP_MENU_MOVE);
+ mxContextMenu->append("export", SfxResId(STR_EXPORT), BMP_MENU_EXPORT);
+
+ if (!bIsSingleSel)
+ {
+ mxContextMenu->set_sensitive("open", false);
+ mxContextMenu->set_sensitive("edit", false);
+ mxContextMenu->set_sensitive("default", false);
+ mxContextMenu->set_sensitive("rename", false);
+ }
+ if (bIsBuiltIn)
+ {
+ mxContextMenu->set_sensitive("rename", false);
+ mxContextMenu->set_sensitive("delete", false);
+ }
+ if (mViewMode == TemplateViewMode::eThumbnailView)
+ {
+ ContextMenuSelectHdl(mxContextMenu->popup_at_rect(
+ GetDrawingArea(), tools::Rectangle(maPosition, Size(1, 1))));
+ Invalidate();
+ }
+ else if (mViewMode == TemplateViewMode::eListView)
+ ContextMenuSelectHdl(mxContextMenu->popup_at_rect(
+ mxTreeView.get(), tools::Rectangle(maPosition, Size(1, 1))));
+}
+
+void TemplateDlgLocalView::ContextMenuSelectHdl(std::string_view rIdent)
+{
+ if (rIdent == "open")
+ maOpenTemplateHdl.Call(maSelectedItem);
+ else if (rIdent == "edit")
+ maEditTemplateHdl.Call(maSelectedItem);
+ else if (rIdent == "rename")
+ {
+ InputDialog aTitleEditDlg(GetDrawingArea(), SfxResId(STR_RENAME_TEMPLATE));
+ aTitleEditDlg.set_title(SfxResId(STR_WINDOW_TITLE_RENAME_TEMPLATE));
+ OUString sOldTitle = maSelectedItem->getTitle();
+ aTitleEditDlg.SetEntryText(sOldTitle);
+ aTitleEditDlg.HideHelpBtn();
+
+ auto aCurRegionItems = getFilteredItems([&](const TemplateItemProperties& rItem) {
+ return rItem.aRegionName == getRegionName(maSelectedItem->mnRegionId);
+ });
+ OUString sTooltip(SfxResId(STR_TOOLTIP_ERROR_RENAME_TEMPLATE));
+ sTooltip = sTooltip.replaceFirst("$2", getRegionName(maSelectedItem->mnRegionId));
+ aTitleEditDlg.setCheckEntry([&](OUString sNewTitle) {
+ if (sNewTitle.isEmpty() || sNewTitle == sOldTitle)
+ return true;
+ for (const auto& rItem : aCurRegionItems)
+ {
+ if (rItem.aName == sNewTitle)
+ {
+ aTitleEditDlg.SetTooltip(sTooltip.replaceFirst("$1", sNewTitle));
+ return false;
+ }
+ }
+ return true;
+ });
+ if (!aTitleEditDlg.run())
+ return;
+ OUString sNewTitle = comphelper::string::strip(aTitleEditDlg.GetEntryText(), ' ');
+
+ if (!sNewTitle.isEmpty() && sNewTitle != sOldTitle)
+ {
+ maSelectedItem->setTitle(sNewTitle);
+ ListView::rename(OUString::number(maSelectedItem->mnId), maSelectedItem->maTitle);
+ }
+ }
+ else if (rIdent == "delete")
+ {
+ std::unique_ptr<weld::MessageDialog> xQueryDlg(Application::CreateMessageDialog(
+ GetDrawingArea(), VclMessageType::Question, VclButtonsType::YesNo,
+ SfxResId(STR_QMSG_SEL_TEMPLATE_DELETE)));
+ if (xQueryDlg->run() != RET_YES)
+ return;
+
+ maDeleteTemplateHdl.Call(maSelectedItem);
+ reload();
+ }
+ else if (rIdent == "default")
+ {
+ maDefaultTemplateHdl.Call(maSelectedItem);
+ ListView::refreshDefaultColumn();
+ }
+ else if (rIdent == "move")
+ {
+ maMoveTemplateHdl.Call(maSelectedItem);
+ }
+ else if (rIdent == "export")
+ {
+ maExportTemplateHdl.Call(maSelectedItem);
+ }
+}
+
+void TemplateDlgLocalView::insertFilteredItems()
+{
+ ListView::clearListView();
+ for (const ThumbnailViewItem* rItem : mFilteredItemList)
+ {
+ const TemplateViewItem* pViewItem = static_cast<const TemplateViewItem*>(rItem);
+ if (!pViewItem)
+ return;
+ bool isDefault = pViewItem->IsDefaultTemplate();
+ OUString sId = OUString::number(pViewItem->mnId);
+ ListView::AppendItem(sId, rItem->maTitle, getRegionName(pViewItem->mnRegionId),
+ pViewItem->getPath(), isDefault);
+ }
+ ListView::sort();
+}
+
+void TemplateDlgLocalView::insertItems(const std::vector<TemplateItemProperties>& rTemplates,
+ bool isRegionSelected = true,
+ bool bShowCategoryInTooltip = false)
+{
+ TemplateLocalView::insertItems(rTemplates, isRegionSelected, bShowCategoryInTooltip);
+ insertFilteredItems();
+}
+
+void TemplateDlgLocalView::setTemplateViewMode(TemplateViewMode eMode) { mViewMode = eMode; }
+
+void TemplateDlgLocalView::Show()
+{
+ if (mViewMode == TemplateViewMode::eListView)
+ {
+ ThumbnailView::Hide();
+ ListView::ShowListView();
+ }
+ else
+ {
+ ThumbnailView::Show();
+ ListView::HideListView();
+ }
+ syncCursor();
+}
+void TemplateDlgLocalView::Hide()
+{
+ ThumbnailView::Hide();
+ ListView::HideListView();
+}
+
+bool TemplateDlgLocalView::IsVisible() const
+{
+ return ThumbnailView::IsVisible() || ListView::IsListViewVisible();
+}
+
+void TemplateDlgLocalView::syncCursor()
+{
+ if (mViewMode == TemplateViewMode::eListView)
+ {
+ ListView::unselect_all();
+ int nIndex = -1;
+
+ for (auto it = mFilteredItemList.cbegin(); it != mFilteredItemList.cend(); ++it)
+ {
+ if ((*it)->mbSelected)
+ {
+ nIndex = -1;
+ nIndex = ListView::get_index((*it)->mnId);
+ if (nIndex >= 0)
+ {
+ ListView::set_cursor(nIndex);
+ ListView::select(nIndex);
+ break;
+ }
+ }
+ }
+ updateSelection();
+ }
+ else
+ {
+ ThumbnailView::deselectItems();
+ std::vector<int> aSelRows = ListView::get_selected_rows();
+ if (aSelRows.empty())
+ return;
+ sal_uInt16 nCursorId = ListView::get_cursor_nId();
+ ThumbnailView::SelectItem(nCursorId);
+ MakeItemVisible(nCursorId);
+
+ for (auto it = mFilteredItemList.begin(); it != mFilteredItemList.end(); ++it)
+ {
+ if ((*it)->mnId == nCursorId)
+ {
+ mpStartSelRange = it;
+ break;
+ }
+ }
+
+ size_t nPos = GetItemPos(nCursorId);
+ ThumbnailViewItem* pItem = ImplGetItem(nPos);
+ const TemplateViewItem* pViewItem = dynamic_cast<const TemplateViewItem*>(pItem);
+ if (pViewItem)
+ maSelectedItem = dynamic_cast<TemplateViewItem*>(pItem);
+ }
+}
+
+void TemplateDlgLocalView::updateSelection()
+{
+ ThumbnailView::deselectItems();
+ for (auto nIndex : ListView::get_selected_rows())
+ {
+ ThumbnailView::SelectItem(ListView::get_nId(nIndex));
+ }
+
+ sal_uInt16 nCursorId = ListView::get_cursor_nId();
+ size_t nPos = GetItemPos(nCursorId);
+ ThumbnailViewItem* pItem = ImplGetItem(nPos);
+ const TemplateViewItem* pViewItem = dynamic_cast<const TemplateViewItem*>(pItem);
+ if (pViewItem)
+ maSelectedItem = dynamic_cast<TemplateViewItem*>(pItem);
+ return;
+}
+
+IMPL_LINK_NOARG(TemplateDlgLocalView, RowActivatedHdl, weld::TreeView&, bool)
+{
+ maOpenTemplateHdl.Call(maSelectedItem);
+ return true;
+}
+
+IMPL_LINK(TemplateDlgLocalView, PopupMenuHdl, const CommandEvent&, rCEvt, bool)
+{
+ if (rCEvt.GetCommand() != CommandEventId::ContextMenu)
+ return false;
+
+ if (rCEvt.IsMouseEvent())
+ {
+ if (ListView::get_selected_rows().empty())
+ return true;
+ Point aPosition(rCEvt.GetMousePosPixel());
+ maPosition = aPosition;
+ updateSelection();
+ if (maSelectedItem)
+ maCreateContextMenuHdl.Call(maSelectedItem);
+ return true;
+ }
+ else
+ {
+ if (ListView::get_selected_rows().empty())
+ return true;
+ maPosition = Point(0, 0);
+ updateSelection();
+ if (maSelectedItem)
+ maCreateContextMenuHdl.Call(maSelectedItem);
+ return true;
+ }
+}
+
+IMPL_LINK_NOARG(TemplateDlgLocalView, ListViewChangedHdl, weld::TreeView&, void)
+{
+ updateSelection();
+}
+
+bool TemplateDlgLocalView::KeyInput(const KeyEvent& rKEvt)
+{
+ vcl::KeyCode aKeyCode = rKEvt.GetKeyCode();
+
+ if (aKeyCode == (KEY_MOD1 | KEY_A))
+ {
+ for (ThumbnailViewItem* pItem : mFilteredItemList)
+ {
+ if (!pItem->isSelected())
+ {
+ pItem->setSelection(true);
+ maItemStateHdl.Call(pItem);
+ }
+ }
+
+ if (IsReallyVisible() && IsUpdateMode())
+ Invalidate();
+ return true;
+ }
+ else if (aKeyCode == KEY_DELETE && !mFilteredItemList.empty())
+ {
+ std::unique_ptr<weld::MessageDialog> xQueryDlg(Application::CreateMessageDialog(
+ GetDrawingArea(), VclMessageType::Question, VclButtonsType::YesNo,
+ SfxResId(STR_QMSG_SEL_TEMPLATE_DELETE)));
+ if (xQueryDlg->run() != RET_YES)
+ return true;
+
+ maDeleteTemplateHdl.Call(maSelectedItem);
+ reload();
+ }
+
+ return ThumbnailView::KeyInput(rKEvt);
+}
+
+IMPL_LINK(TemplateDlgLocalView, KeyPressHdl, const KeyEvent&, rKEvt, bool)
+{
+ vcl::KeyCode aKeyCode = rKEvt.GetKeyCode();
+
+ if (aKeyCode == KEY_DELETE && !mFilteredItemList.empty()
+ && !ListView::get_selected_rows().empty())
+ {
+ std::unique_ptr<weld::MessageDialog> xQueryDlg(Application::CreateMessageDialog(
+ mxTreeView.get(), VclMessageType::Question, VclButtonsType::YesNo,
+ SfxResId(STR_QMSG_SEL_TEMPLATE_DELETE)));
+ if (xQueryDlg->run() != RET_YES)
+ return true;
+
+ maDeleteTemplateHdl.Call(maSelectedItem);
+ reload();
+ }
+ return false;
+}
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/control/templatelocalview.cxx b/sfx2/source/control/templatelocalview.cxx
new file mode 100644
index 000000000..a223b62e1
--- /dev/null
+++ b/sfx2/source/control/templatelocalview.cxx
@@ -0,0 +1,945 @@
+/* -*- 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/templatelocalview.hxx>
+
+#include <comphelper/string.hxx>
+#include <sfx2/doctempl.hxx>
+#include <sfx2/inputdlg.hxx>
+#include <sfx2/sfxresid.hxx>
+#include <templatecontaineritem.hxx>
+#include <templateviewitem.hxx>
+#include <sfx2/docfac.hxx>
+#include <tools/urlobj.hxx>
+#include <unotools/moduleoptions.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+#include <vcl/commandevent.hxx>
+#include <vcl/event.hxx>
+
+#include <sfx2/strings.hrc>
+#include <bitmaps.hlst>
+
+#include <comphelper/processfactory.hxx>
+#include <com/sun/star/util/thePathSettings.hpp>
+#include <unotools/ucbhelper.hxx>
+#include <sfxurlrelocator.hxx>
+#include <../doc/doctemplateslocal.hxx>
+
+using namespace ::com::sun::star;
+
+bool ViewFilter_Application::isFilteredExtension(FILTER_APPLICATION filter, std::u16string_view rExt)
+{
+ bool bRet = rExt == u"ott" || rExt == u"stw" || rExt == u"oth" || rExt == u"dot" || rExt == u"dotx" || rExt == u"otm"
+ || rExt == u"ots" || rExt == u"stc" || rExt == u"xlt" || rExt == u"xltm" || rExt == u"xltx"
+ || rExt == u"otp" || rExt == u"sti" || rExt == u"pot" || rExt == u"potm" || rExt == u"potx"
+ || rExt == u"otg" || rExt == u"std";
+
+ if (filter == FILTER_APPLICATION::WRITER)
+ {
+ bRet = rExt == u"ott" || rExt == u"stw" || rExt == u"oth" || rExt == u"dot" || rExt == u"dotx" || rExt == u"otm";
+ }
+ else if (filter == FILTER_APPLICATION::CALC)
+ {
+ bRet = rExt == u"ots" || rExt == u"stc" || rExt == u"xlt" || rExt == u"xltm" || rExt == u"xltx";
+ }
+ else if (filter == FILTER_APPLICATION::IMPRESS)
+ {
+ bRet = rExt == u"otp" || rExt == u"sti" || rExt == u"pot" || rExt == u"potm" || rExt == u"potx";
+ }
+ else if (filter == FILTER_APPLICATION::DRAW)
+ {
+ bRet = rExt == u"otg" || rExt == u"std";
+ }
+
+ return bRet;
+}
+
+bool ViewFilter_Application::isValid (std::u16string_view rPath) const
+{
+ INetURLObject aUrl(rPath);
+ return isFilteredExtension(mApp, aUrl.getExtension());
+}
+
+bool ViewFilter_Application::operator () (const ThumbnailViewItem *pItem)
+{
+ const TemplateViewItem *pTempItem = dynamic_cast<const TemplateViewItem*>(pItem);
+ if (pTempItem)
+ return isValid(pTempItem->getPath());
+
+ return true;
+}
+
+void TemplateLocalView::updateThumbnailDimensions(tools::Long itemMaxSize)
+{
+ mnThumbnailWidth = itemMaxSize;
+ mnThumbnailHeight = itemMaxSize;
+}
+
+TemplateLocalView::TemplateLocalView(std::unique_ptr<weld::ScrolledWindow> xWindow,
+ std::unique_ptr<weld::Menu> xMenu)
+ : ThumbnailView(std::move(xWindow), std::move(xMenu))
+ , mnCurRegionId(0)
+ , maSelectedItem(nullptr)
+ , mnThumbnailWidth(TEMPLATE_THUMBNAIL_MAX_WIDTH)
+ , mnThumbnailHeight(TEMPLATE_THUMBNAIL_MAX_HEIGHT)
+ , maPosition(0,0)
+ , mpDocTemplates(new SfxDocumentTemplates)
+{
+}
+
+TemplateLocalView::~TemplateLocalView()
+{
+}
+
+void TemplateLocalView::Populate()
+{
+ maRegions.clear();
+ maAllTemplates.clear();
+
+ sal_uInt16 nCount = mpDocTemplates->GetRegionCount();
+ for (sal_uInt16 i = 0; i < nCount; ++i)
+ {
+ OUString aRegionName(mpDocTemplates->GetFullRegionName(i));
+
+ std::unique_ptr<TemplateContainerItem> pItem(new TemplateContainerItem( i+1 ));
+ pItem->mnRegionId = i;
+ pItem->maTitle = aRegionName;
+
+ sal_uInt16 nEntries = mpDocTemplates->GetCount(i);
+
+ for (sal_uInt16 j = 0; j < nEntries; ++j)
+ {
+ OUString aName = mpDocTemplates->GetName(i,j);
+ OUString aURL = mpDocTemplates->GetPath(i,j);
+
+ TemplateItemProperties aProperties;
+ aProperties.nId = j+1;
+ aProperties.nDocId = j;
+ aProperties.nRegionId = i;
+ aProperties.aName = aName;
+ aProperties.aPath = aURL;
+ aProperties.aRegionName = aRegionName;
+ aProperties.aThumbnail = TemplateLocalView::fetchThumbnail(aURL,
+ mnThumbnailWidth,
+ mnThumbnailHeight);
+
+ pItem->maTemplates.push_back(aProperties);
+ maAllTemplates.push_back(aProperties);
+ }
+
+ maRegions.push_back(std::move(pItem));
+ }
+}
+
+void TemplateLocalView::reload()
+{
+ mpDocTemplates->Update();
+ OUString sCurRegionName = getRegionItemName(mnCurRegionId);
+ Populate();
+ mnCurRegionId = getRegionId(sCurRegionName);
+
+ // Check if we are currently browsing a region or root folder
+ if (mnCurRegionId)
+ {
+ sal_uInt16 nRegionId = mnCurRegionId - 1; //Is offset by 1
+
+ for (auto const & pRegion : maRegions)
+ {
+ if (pRegion->mnRegionId == nRegionId)
+ {
+ showRegion(pRegion.get());
+ break;
+ }
+ }
+ }
+ else
+ showAllTemplates();
+
+ //No items should be selected by default
+ deselectItems();
+}
+
+void TemplateLocalView::showAllTemplates()
+{
+ mnCurRegionId = 0;
+
+ insertItems(maAllTemplates, false, true);
+
+ maOpenRegionHdl.Call(nullptr);
+}
+
+void TemplateLocalView::showRegion(TemplateContainerItem const *pItem)
+{
+ mnCurRegionId = pItem->mnRegionId+1;
+
+ insertItems(pItem->maTemplates);
+
+ maOpenRegionHdl.Call(nullptr);
+}
+
+TemplateContainerItem* TemplateLocalView::getRegion(std::u16string_view rName)
+{
+ for (auto const & pRegion : maRegions)
+ if (pRegion->maTitle == rName)
+ return pRegion.get();
+
+ return nullptr;
+}
+
+void TemplateLocalView::ContextMenuSelectHdl(std::string_view rIdent)
+{
+ if (rIdent == "open")
+ maOpenTemplateHdl.Call(maSelectedItem);
+ else if (rIdent == "edit")
+ maEditTemplateHdl.Call(maSelectedItem);
+ else if (rIdent == "rename")
+ {
+ InputDialog aTitleEditDlg(GetDrawingArea(), SfxResId(STR_RENAME_TEMPLATE));
+ OUString sOldTitle = maSelectedItem->getTitle();
+ aTitleEditDlg.SetEntryText(sOldTitle);
+ aTitleEditDlg.HideHelpBtn();
+
+ auto aCurRegionItems = getFilteredItems([&](const TemplateItemProperties& rItem) {
+ return rItem.aRegionName == getRegionName(maSelectedItem->mnRegionId);
+ });
+ OUString sTooltip(SfxResId(STR_TOOLTIP_ERROR_RENAME_TEMPLATE));
+ sTooltip = sTooltip.replaceFirst("$2", getRegionName(maSelectedItem->mnRegionId));
+ aTitleEditDlg.setCheckEntry([&](OUString sNewTitle) {
+ if (sNewTitle.isEmpty() || sNewTitle == sOldTitle)
+ return true;
+ for (const auto& rItem : aCurRegionItems)
+ {
+ if (rItem.aName == sNewTitle)
+ {
+ aTitleEditDlg.SetTooltip(sTooltip.replaceFirst("$1", sNewTitle));
+ return false;
+ }
+ }
+ return true;
+ });
+ if (!aTitleEditDlg.run())
+ return;
+ OUString sNewTitle = comphelper::string::strip(aTitleEditDlg.GetEntryText(), ' ');
+
+ if ( !sNewTitle.isEmpty() && sNewTitle != sOldTitle )
+ {
+ maSelectedItem->setTitle(sNewTitle);
+ }
+ }
+ else if (rIdent == "delete")
+ {
+ std::unique_ptr<weld::MessageDialog> xQueryDlg(Application::CreateMessageDialog(GetDrawingArea(), VclMessageType::Question, VclButtonsType::YesNo,
+ SfxResId(STR_QMSG_SEL_TEMPLATE_DELETE)));
+ if (xQueryDlg->run() != RET_YES)
+ return;
+
+ maDeleteTemplateHdl.Call(maSelectedItem);
+ reload();
+ }
+ else if (rIdent == "default")
+ maDefaultTemplateHdl.Call(maSelectedItem);
+}
+
+sal_uInt16 TemplateLocalView::getRegionId(size_t pos) const
+{
+ assert(pos < maRegions.size());
+
+ return maRegions[pos]->mnId;
+}
+
+sal_uInt16 TemplateLocalView::getRegionId(std::u16string_view sRegion) const
+{
+ for (auto const & pRegion : maRegions)
+ {
+ if (pRegion->maTitle == sRegion)
+ return pRegion->mnId;
+ }
+
+ return 0;
+}
+
+OUString TemplateLocalView::getRegionName(const sal_uInt16 nRegionId) const
+{
+ return mpDocTemplates->GetRegionName(nRegionId);
+}
+
+OUString TemplateLocalView::getRegionItemName(const sal_uInt16 nItemId) const
+{
+ for (auto const & pRegion : maRegions)
+ {
+ if (pRegion->mnId == nItemId)
+ return pRegion->maTitle;
+ }
+
+ return OUString();
+}
+
+std::vector<OUString> TemplateLocalView::getFolderNames()
+{
+ size_t n = maRegions.size();
+ std::vector<OUString> ret(n);
+
+ for (size_t i = 0; i < n; ++i)
+ ret[i] = maRegions[i]->maTitle;
+
+ return ret;
+}
+
+std::vector<TemplateItemProperties>
+TemplateLocalView::getFilteredItems(const std::function<bool (const TemplateItemProperties&)> &rFunc) const
+{
+ std::vector<TemplateItemProperties> aItems;
+
+ if (mnCurRegionId)
+ {
+ TemplateContainerItem *pFolderItem = maRegions[mnCurRegionId-1].get();
+
+ for (const TemplateItemProperties & rItemProps : pFolderItem->maTemplates)
+ {
+ if (rFunc(rItemProps))
+ aItems.push_back(rItemProps);
+ }
+ }
+ else
+ {
+ for (auto const & pFolderItem : maRegions)
+ {
+ for (const TemplateItemProperties & rItemProps : pFolderItem->maTemplates)
+ {
+ if (rFunc(rItemProps))
+ aItems.push_back(rItemProps);
+ }
+ }
+ }
+
+ return aItems;
+}
+
+sal_uInt16 TemplateLocalView::createRegion(const OUString &rName)
+{
+ sal_uInt16 nRegionId = mpDocTemplates->GetRegionCount(); // Next regionId
+ sal_uInt16 nItemId = maRegions.size() + 1;
+
+ if (!mpDocTemplates->InsertDir(rName,nRegionId))
+ return 0;
+
+ // Insert to the region cache list and to the thumbnail item list
+ std::unique_ptr<TemplateContainerItem> pItem(new TemplateContainerItem( nItemId ));
+ pItem->mnRegionId = nRegionId;
+ pItem->maTitle = rName;
+
+ maRegions.push_back(std::move(pItem));
+
+ return nItemId;
+}
+
+bool TemplateLocalView::renameRegion(std::u16string_view rTitle, const OUString &rNewTitle)
+{
+ TemplateContainerItem *pRegion = getRegion(rTitle);
+
+ if(pRegion)
+ {
+ sal_uInt16 nRegionId = pRegion->mnRegionId;
+ return mpDocTemplates->SetName( rNewTitle, nRegionId, USHRT_MAX/*nDocId*/ );
+ }
+ return false;
+}
+
+bool TemplateLocalView::removeRegion(const sal_uInt16 nItemId)
+{
+ sal_uInt16 nRegionId = USHRT_MAX;
+
+ // Remove from the region cache list
+ for (auto pRegionIt = maRegions.begin(); pRegionIt != maRegions.end();)
+ {
+ if ( (*pRegionIt)->mnId == nItemId )
+ {
+ if (!mpDocTemplates->Delete((*pRegionIt)->mnRegionId,USHRT_MAX))
+ return false;
+
+ nRegionId = (*pRegionIt)->mnRegionId;
+
+ pRegionIt = maRegions.erase(pRegionIt);
+ }
+ else
+ {
+ // Synchronize regions cache ids with SfxDocumentTemplates
+ if (nRegionId != USHRT_MAX && (*pRegionIt)->mnRegionId > nRegionId)
+ --(*pRegionIt)->mnRegionId;
+
+ ++pRegionIt;
+ }
+ }
+
+ if (nRegionId == USHRT_MAX)
+ return false;
+
+ // Synchronize view regions ids with SfxDocumentTemplates
+ for (auto const& region : maRegions)
+ {
+ if (region->mnRegionId > nRegionId)
+ --region->mnRegionId;
+ }
+
+ return true;
+}
+
+bool TemplateLocalView::removeTemplate (const sal_uInt16 nItemId, const sal_uInt16 nSrcItemId)
+{
+ for (auto const & pRegion : maRegions)
+ {
+ if (pRegion->mnId == nSrcItemId)
+ {
+ TemplateContainerItem *pItem = pRegion.get();
+ auto pIter = std::find_if(pItem->maTemplates.begin(), pItem->maTemplates.end(),
+ [nItemId](const TemplateItemProperties& rTemplate) { return rTemplate.nId == nItemId; });
+ if (pIter != pItem->maTemplates.end())
+ {
+ if (!mpDocTemplates->Delete(pItem->mnRegionId,pIter->nDocId))
+ return false;
+
+ pIter = pItem->maTemplates.erase(pIter);
+
+ if (pRegion->mnRegionId == mnCurRegionId-1)
+ {
+ RemoveItem(nItemId);
+ Invalidate();
+ }
+
+ // Update Doc Idx for all templates that follow
+ for (; pIter != pItem->maTemplates.end(); ++pIter)
+ pIter->nDocId = pIter->nDocId - 1;
+ }
+
+ CalculateItemPositions();
+ break;
+ }
+ }
+
+ return true;
+}
+
+void TemplateLocalView::moveTemplates(const std::set<const ThumbnailViewItem*, selection_cmp_fn> &rItems,
+ const sal_uInt16 nTargetItem)
+{
+ TemplateContainerItem *pTarget = nullptr;
+ TemplateContainerItem *pSrc = nullptr;
+
+ for (auto const & pRegion : maRegions)
+ {
+ if (pRegion->mnId == nTargetItem)
+ pTarget = pRegion.get();
+ }
+
+ if (!pTarget)
+ return;
+
+ bool refresh = false;
+
+ sal_uInt16 nTargetRegion = pTarget->mnRegionId;
+ sal_uInt16 nTargetIdx = mpDocTemplates->GetCount(nTargetRegion); // Next Idx
+ std::vector<sal_uInt16> aItemIds; // List of moved items ids (also prevents the invalidation of rItems iterators when we remove them as we go)
+
+ std::set<const ThumbnailViewItem*,selection_cmp_fn>::const_iterator aSelIter;
+ for ( aSelIter = rItems.begin(); aSelIter != rItems.end(); ++aSelIter, ++nTargetIdx )
+ {
+ const TemplateViewItem *pViewItem = static_cast<const TemplateViewItem*>(*aSelIter);
+ sal_uInt16 nSrcRegionId = pViewItem->mnRegionId;
+
+ for (auto const & pRegion : maRegions)
+ {
+ if (pRegion->mnRegionId == nSrcRegionId)
+ pSrc = pRegion.get();
+ }
+
+ if(pSrc)
+ {
+ bool bCopy = !mpDocTemplates->Move(nTargetRegion,nTargetIdx,nSrcRegionId,pViewItem->mnDocId);
+
+ if (bCopy)
+ {
+ OUString sQuery = SfxResId(STR_MSG_QUERY_COPY).replaceFirst("$1", pViewItem->maTitle).replaceFirst("$2",
+ getRegionName(nTargetRegion));
+ std::unique_ptr<weld::MessageDialog> xQueryDlg(Application::CreateMessageDialog(GetDrawingArea(), VclMessageType::Question, VclButtonsType::YesNo, sQuery));
+ if (xQueryDlg->run() != RET_YES)
+ {
+ OUString sMsg(SfxResId(STR_MSG_ERROR_LOCAL_MOVE));
+ sMsg = sMsg.replaceFirst("$1",getRegionName(nTargetRegion));
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(GetDrawingArea(),
+ VclMessageType::Warning, VclButtonsType::Ok, sMsg.replaceFirst( "$2",pViewItem->maTitle)));
+ xBox->run();
+
+ return; //return if any single move operation fails
+ }
+
+ if (!mpDocTemplates->Copy(nTargetRegion,nTargetIdx,nSrcRegionId,pViewItem->mnDocId))
+ {
+ continue;
+ }
+ }
+
+ // move template to destination
+
+ TemplateItemProperties aTemplateItem;
+ aTemplateItem.nId = nTargetIdx + 1;
+ aTemplateItem.nDocId = nTargetIdx;
+ aTemplateItem.nRegionId = nTargetRegion;
+ aTemplateItem.aName = pViewItem->maTitle;
+ aTemplateItem.aPath = mpDocTemplates->GetPath(nTargetRegion,nTargetIdx);
+ aTemplateItem.aRegionName = pViewItem->maHelpText;
+ aTemplateItem.aThumbnail = pViewItem->maPreview1;
+
+ pTarget->maTemplates.push_back(aTemplateItem);
+
+ if (!bCopy)
+ {
+ // remove template from region cached data
+
+ std::vector<TemplateItemProperties>::iterator pPropIter;
+ for (pPropIter = pSrc->maTemplates.begin(); pPropIter != pSrc->maTemplates.end();)
+ {
+ if (pPropIter->nDocId == pViewItem->mnDocId)
+ {
+ pPropIter = pSrc->maTemplates.erase(pPropIter);
+ aItemIds.push_back(pViewItem->mnDocId + 1);//mnid
+ }
+ else
+ {
+ // Keep region document id synchronized with SfxDocumentTemplates
+ if (pPropIter->nDocId > pViewItem->mnDocId)
+ --pPropIter->nDocId;
+
+ ++pPropIter;
+ }
+ }
+
+ // Keep view document id synchronized with SfxDocumentTemplates
+ for (auto const& item : mItemList)
+ {
+ auto pTemplateViewItem = static_cast<TemplateViewItem*>(item.get());
+ if (pTemplateViewItem->mnDocId > pViewItem->mnDocId)
+ --pTemplateViewItem->mnDocId;
+ }
+ }
+ }
+
+ refresh = true;
+ }
+
+ // Remove items from the current view
+ for (auto const& itemId : aItemIds)
+ RemoveItem(itemId);
+
+ if (refresh)
+ {
+ CalculateItemPositions();
+ Invalidate();
+ }
+}
+
+bool TemplateLocalView::copyFrom (TemplateContainerItem *pItem, const OUString &rPath)
+{
+ sal_uInt16 nId = 1;
+ sal_uInt16 nDocId = 0;
+ sal_uInt16 nRegionId = pItem->mnRegionId;
+ OUString aPath(rPath);
+
+ if (!pItem->maTemplates.empty())
+ {
+ nId = pItem->maTemplates.back().nId+1;
+ nDocId = pItem->maTemplates.back().nDocId+1;
+ }
+
+ if (mpDocTemplates->CopyFrom(nRegionId,nDocId,aPath))
+ {
+ TemplateItemProperties aTemplate;
+ aTemplate.nId = nId;
+ aTemplate.nDocId = nDocId;
+ aTemplate.nRegionId = nRegionId;
+ aTemplate.aName = aPath;
+ aTemplate.aThumbnail = TemplateLocalView::fetchThumbnail(rPath,
+ TEMPLATE_THUMBNAIL_MAX_WIDTH,
+ TEMPLATE_THUMBNAIL_MAX_HEIGHT);
+ aTemplate.aPath = rPath;
+ aTemplate.aRegionName = getRegionName(nRegionId);
+
+ pItem->maTemplates.push_back(aTemplate);
+
+ CalculateItemPositions();
+
+ return true;
+ }
+
+ return false;
+}
+
+bool TemplateLocalView::exportTo(const sal_uInt16 nItemId, const sal_uInt16 nRegionItemId, std::u16string_view rName)
+{
+ for (auto const & pRegItem : maRegions)
+ {
+ if (pRegItem->mnId == nRegionItemId)
+ {
+ for (auto const& elem : pRegItem->maTemplates)
+ {
+ if (elem.nId == nItemId)
+ {
+ return mpDocTemplates->CopyTo(pRegItem->mnRegionId,elem.nDocId,rName);
+ }
+ }
+
+ break;
+ }
+ }
+
+ return false;
+}
+
+bool TemplateLocalView::renameItem(ThumbnailViewItem* pItem, const OUString& sNewTitle)
+{
+ sal_uInt16 nRegionId = 0;
+ sal_uInt16 nDocId = USHRT_MAX;
+ TemplateViewItem* pDocItem = dynamic_cast<TemplateViewItem*>( pItem );
+
+ if ( pDocItem )
+ {
+ nRegionId = pDocItem->mnRegionId;
+ nDocId = pDocItem->mnDocId;
+ }
+
+ bool bRes = mpDocTemplates->SetName( sNewTitle, nRegionId, nDocId );
+ if(bRes)
+ {
+ for (auto & pRegion : maRegions)
+ {
+ if (pRegion->mnId == nRegionId + 1 )
+ {
+ for(auto & aTemplate : pRegion->maTemplates)
+ {
+ if(aTemplate.nId == nDocId + 1)
+ {
+ aTemplate.aName = sNewTitle;
+ break;
+ }
+ }
+ break;
+ }
+ }
+ OUString sRegionName;
+ for (auto & aTemplate : maAllTemplates)
+ {
+ if (aTemplate.nRegionId == nRegionId && aTemplate.nDocId == nDocId)
+ {
+ aTemplate.aName = sNewTitle;
+ sRegionName = aTemplate.aRegionName;
+ break;
+ }
+ }
+
+ OUString sHelpText = SfxResId(STR_TEMPLATE_TOOLTIP);
+ sHelpText = (sHelpText.replaceFirst("$1", sNewTitle)).replaceFirst("$2", sRegionName);
+ pItem->setHelpText(sHelpText);
+ pItem->maTitle = sNewTitle;
+ }
+ return bRes;
+}
+
+void TemplateLocalView::insertItems(const std::vector<TemplateItemProperties> &rTemplates, bool isRegionSelected, bool bShowCategoryInTooltip)
+{
+ std::vector<std::unique_ptr<ThumbnailViewItem>> aItems(rTemplates.size());
+ for (size_t i = 0, n = rTemplates.size(); i < n; ++i )
+ {
+ const TemplateItemProperties *pCur = &rTemplates[i];
+
+ std::unique_ptr<TemplateViewItem> pChild;
+ if(isRegionSelected)
+ pChild.reset(new TemplateViewItem(*this, pCur->nId));
+ else
+ pChild.reset(new TemplateViewItem(*this, i+1));
+
+ pChild->mnDocId = pCur->nDocId;
+ pChild->mnRegionId = pCur->nRegionId;
+ pChild->maTitle = pCur->aName;
+ pChild->setPath(pCur->aPath);
+
+ if(!bShowCategoryInTooltip)
+ pChild->setHelpText(pCur->aName);
+ else
+ {
+ OUString sHelpText = SfxResId(STR_TEMPLATE_TOOLTIP);
+ sHelpText = (sHelpText.replaceFirst("$1", pCur->aName)).replaceFirst("$2", pCur->aRegionName);
+ pChild->setHelpText(sHelpText);
+ }
+
+ pChild->maPreview1 = pCur->aThumbnail;
+
+ if(IsDefaultTemplate(pCur->aPath))
+ pChild->showDefaultIcon(true);
+
+ if ( pCur->aThumbnail.IsEmpty() )
+ {
+ // Use the default thumbnail if we have nothing else
+ pChild->maPreview1 = TemplateLocalView::getDefaultThumbnail(pCur->aPath);
+ }
+
+ aItems[i] = std::move(pChild);
+ }
+
+ updateItems(std::move(aItems));
+}
+
+bool TemplateLocalView::MouseButtonDown( const MouseEvent& rMEvt )
+{
+ GrabFocus();
+ return ThumbnailView::MouseButtonDown(rMEvt);
+}
+
+bool TemplateLocalView::Command(const CommandEvent& rCEvt)
+{
+ if (rCEvt.GetCommand() != CommandEventId::ContextMenu)
+ return CustomWidgetController::Command(rCEvt);
+
+ if (rCEvt.IsMouseEvent())
+ {
+ size_t nPos = ImplGetItem(rCEvt.GetMousePosPixel());
+ Point aPosition(rCEvt.GetMousePosPixel());
+ maPosition = aPosition;
+ ThumbnailViewItem* pItem = ImplGetItem(nPos);
+ const TemplateViewItem *pViewItem = dynamic_cast<const TemplateViewItem*>(pItem);
+
+ if(pViewItem)
+ {
+ if(!pItem->isSelected())
+ {
+ deselectItems();
+ pItem->setSelection(true);
+ maItemStateHdl.Call(pItem);
+ }
+ maSelectedItem = dynamic_cast<TemplateViewItem*>(pItem);
+ maCreateContextMenuHdl.Call(pItem);
+ }
+ }
+ else
+ {
+ for (ThumbnailViewItem* pItem : mFilteredItemList)
+ {
+ if (pItem->isSelected())
+ {
+ tools::Rectangle aRect = pItem->getDrawArea();
+ maPosition = aRect.Center();
+ maSelectedItem = dynamic_cast<TemplateViewItem*>(pItem);
+ maCreateContextMenuHdl.Call(pItem);
+ break;
+ }
+ }
+ }
+ return true;
+}
+
+bool TemplateLocalView::KeyInput( const KeyEvent& rKEvt )
+{
+ vcl::KeyCode aKeyCode = rKEvt.GetKeyCode();
+
+ if(aKeyCode == ( KEY_MOD1 | KEY_A ) )
+ {
+ for (ThumbnailViewItem* pItem : mFilteredItemList)
+ {
+ if (!pItem->isSelected())
+ {
+ pItem->setSelection(true);
+ maItemStateHdl.Call(pItem);
+ }
+ }
+
+ if (IsReallyVisible() && IsUpdateMode())
+ Invalidate();
+ return true;
+ }
+ else if( aKeyCode == KEY_DELETE && !mFilteredItemList.empty())
+ {
+ std::unique_ptr<weld::MessageDialog> xQueryDlg(Application::CreateMessageDialog(GetDrawingArea(), VclMessageType::Question, VclButtonsType::YesNo,
+ SfxResId(STR_QMSG_SEL_TEMPLATE_DELETE)));
+ if (xQueryDlg->run() != RET_YES)
+ return true;
+
+ //copy to avoid changing filtered item list during deletion
+ ThumbnailValueItemList mFilteredItemListCopy = mFilteredItemList;
+
+ for (ThumbnailViewItem* pItem : mFilteredItemListCopy)
+ {
+ if (pItem->isSelected())
+ {
+ maDeleteTemplateHdl.Call(pItem);
+ }
+ }
+ reload();
+ }
+
+ return ThumbnailView::KeyInput(rKEvt);
+}
+
+void TemplateLocalView::setOpenRegionHdl(const Link<void*,void> &rLink)
+{
+ maOpenRegionHdl = rLink;
+}
+
+void TemplateLocalView::setCreateContextMenuHdl(const Link<ThumbnailViewItem*,void> &rLink)
+{
+ maCreateContextMenuHdl = rLink;
+}
+
+void TemplateLocalView::setOpenTemplateHdl(const Link<ThumbnailViewItem*,void> &rLink)
+{
+ maOpenTemplateHdl = rLink;
+}
+
+void TemplateLocalView::setEditTemplateHdl(const Link<ThumbnailViewItem*,void> &rLink)
+{
+ maEditTemplateHdl = rLink;
+}
+
+void TemplateLocalView::setDeleteTemplateHdl(const Link<void*,void> &rLink)
+{
+ maDeleteTemplateHdl = rLink;
+}
+
+void TemplateLocalView::setDefaultTemplateHdl(const Link<ThumbnailViewItem*,void> &rLink)
+{
+ maDefaultTemplateHdl = rLink;
+}
+
+void TemplateLocalView::setMoveTemplateHdl(const Link<void*,void> &rLink)
+{
+ maMoveTemplateHdl = rLink;
+}
+
+void TemplateLocalView::setExportTemplateHdl(const Link<void*,void> &rLink)
+{
+ maExportTemplateHdl = rLink;
+}
+
+BitmapEx TemplateLocalView::scaleImg (const BitmapEx &rImg, tools::Long width, tools::Long height)
+{
+ BitmapEx aImg = rImg;
+
+ if (!rImg.IsEmpty())
+ {
+ Size aSize = rImg.GetSizePixel();
+
+ if (aSize.Width() == 0)
+ aSize.setWidth( 1 );
+
+ if (aSize.Height() == 0)
+ aSize.setHeight( 1 );
+
+ // make the picture fit the given width/height constraints
+ double nRatio = std::min(double(width)/double(aSize.Width()), double(height)/double(aSize.Height()));
+
+ aImg.Scale(nRatio, nRatio);
+ }
+
+ return aImg;
+}
+
+bool TemplateLocalView::IsDefaultTemplate(const OUString& rPath)
+{
+ SvtModuleOptions aModOpt;
+ const css::uno::Sequence<OUString> &aServiceNames = aModOpt.GetAllServiceNames();
+
+ return std::any_of(aServiceNames.begin(), aServiceNames.end(), [&rPath](const OUString& rName) {
+ return SfxObjectFactory::GetStandardTemplate(rName).match(rPath); });
+}
+
+void TemplateLocalView::RemoveDefaultTemplateIcon(std::u16string_view rPath)
+{
+ for (const std::unique_ptr<ThumbnailViewItem>& pItem : mItemList)
+ {
+ TemplateViewItem* pViewItem = dynamic_cast<TemplateViewItem*>(pItem.get());
+ if (pViewItem && pViewItem->getPath().match(rPath))
+ {
+ pViewItem->showDefaultIcon(false);
+ Invalidate();
+ return;
+ }
+ }
+}
+
+BitmapEx TemplateLocalView::getDefaultThumbnail( std::u16string_view rPath )
+{
+ BitmapEx aImg;
+ INetURLObject aUrl(rPath);
+ OUString aExt = aUrl.getExtension();
+
+ if ( ViewFilter_Application::isFilteredExtension( FILTER_APPLICATION::WRITER, aExt) )
+ aImg = BitmapEx(SFX_THUMBNAIL_TEXT);
+ else if ( ViewFilter_Application::isFilteredExtension( FILTER_APPLICATION::CALC, aExt) )
+ aImg = BitmapEx(SFX_THUMBNAIL_SHEET);
+ else if ( ViewFilter_Application::isFilteredExtension( FILTER_APPLICATION::IMPRESS, aExt) )
+ aImg = BitmapEx(SFX_THUMBNAIL_PRESENTATION);
+ else if ( ViewFilter_Application::isFilteredExtension( FILTER_APPLICATION::DRAW, aExt) )
+ aImg = BitmapEx(SFX_THUMBNAIL_DRAWING);
+
+ return aImg;
+}
+
+BitmapEx TemplateLocalView::fetchThumbnail (const OUString &msURL, tools::Long width, tools::Long height)
+{
+ return TemplateLocalView::scaleImg(ThumbnailView::readThumbnail(msURL), width, height);
+}
+
+void TemplateLocalView::OnItemDblClicked (ThumbnailViewItem *pItem)
+{
+ TemplateViewItem* pViewItem = dynamic_cast<TemplateViewItem*>(pItem);
+
+ if( pViewItem )
+ maOpenTemplateHdl.Call(pViewItem);
+}
+
+bool TemplateLocalView::IsInternalTemplate(const OUString& rPath)
+{
+ uno::Reference< uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext();
+ css::uno::Reference< css::util::XPathSettings > xPathSettings = css::util::thePathSettings::get(xContext);
+ uno::Sequence<OUString> aInternalTemplateDirs;
+ uno::Any aAny = xPathSettings->getPropertyValue("Template_internal");
+ aAny >>= aInternalTemplateDirs;
+ SfxURLRelocator_Impl aRelocator(xContext);
+ for (OUString& rInternalTemplateDir : asNonConstRange(aInternalTemplateDirs))
+ {
+ aRelocator.makeRelocatableURL(rInternalTemplateDir);
+ aRelocator.makeAbsoluteURL(rInternalTemplateDir);
+ if(::utl::UCBContentHelper::IsSubPath(rInternalTemplateDir, rPath))
+ return true;
+ }
+ return false;
+}
+
+bool TemplateLocalView::IsBuiltInRegion(const OUString& rRegionName)
+{
+ bool isBuiltInCategory = false;
+ auto aGroupNames = DocTemplLocaleHelper::GetBuiltInGroupNames();
+ isBuiltInCategory = std::find(aGroupNames.begin(), aGroupNames.end(),
+ rRegionName) != aGroupNames.end();
+ if(isBuiltInCategory)
+ return true;
+ //check if it contains any internal template
+ for(const auto& rItem : maRegions)
+ {
+ if(rItem->maTitle == rRegionName)
+ {
+ for(const auto& rTemplateItem : rItem->maTemplates)
+ {
+ if(IsInternalTemplate(rTemplateItem.aPath))
+ return true;
+ }
+ break;
+ }
+ }
+ return false;
+}
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/control/templateviewitem.cxx b/sfx2/source/control/templateviewitem.cxx
new file mode 100644
index 000000000..28ff1f431
--- /dev/null
+++ b/sfx2/source/control/templateviewitem.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/.
+ */
+
+#include <templateviewitem.hxx>
+
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <drawinglayer/attribute/fillgraphicattribute.hxx>
+#include <drawinglayer/primitive2d/fillgraphicprimitive2d.hxx>
+#include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonSelectionPrimitive2D.hxx>
+#include <drawinglayer/primitive2d/discretebitmapprimitive2d.hxx>
+#include <drawinglayer/processor2d/baseprocessor2d.hxx>
+#include <tools/poly.hxx>
+#include <vcl/graph.hxx>
+
+#include <bitmaps.hlst>
+
+using namespace basegfx;
+using namespace basegfx::utils;
+using namespace drawinglayer::attribute;
+using namespace drawinglayer::primitive2d;
+
+TemplateViewItem::TemplateViewItem(ThumbnailView& rView, sal_uInt16 nId)
+ : ThumbnailViewItem(rView, nId),
+ mnRegionId(USHRT_MAX),
+ mnDocId(USHRT_MAX),
+ maDefaultBitmap(BMP_DEFAULT),
+ mbIsDefaultTemplate(false)
+{
+}
+
+TemplateViewItem::~TemplateViewItem ()
+{
+}
+
+::tools::Rectangle TemplateViewItem::getDefaultIconArea() const
+{
+ ::tools::Rectangle aArea(getDrawArea());
+ Size aSize(maDefaultBitmap.GetSizePixel());
+
+ return ::tools::Rectangle(
+ Point(aArea.Left() + THUMBNAILVIEW_ITEM_CORNER, aArea.Top() + THUMBNAILVIEW_ITEM_CORNER),
+ aSize);
+}
+
+void TemplateViewItem::Paint(drawinglayer::processor2d::BaseProcessor2D *pProcessor,
+ const ThumbnailItemAttributes *pAttrs)
+{
+ BColor aFillColor = pAttrs->aFillColor;
+
+ drawinglayer::primitive2d::Primitive2DContainer aSeq(5);
+ double fTransparence = 0.0;
+
+ // Draw background
+ if( mbSelected && mbHover)
+ aFillColor = pAttrs->aSelectHighlightColor;
+ else if (mbSelected || mbHover)
+ {
+ aFillColor = pAttrs->aHighlightColor;
+ if (mbHover)
+ fTransparence = pAttrs->fHighlightTransparence;
+ }
+
+ aSeq[0] = drawinglayer::primitive2d::Primitive2DReference(
+ new PolyPolygonSelectionPrimitive2D( B2DPolyPolygon(::tools::Polygon(maDrawArea,5,5).getB2DPolygon()),
+ aFillColor,
+ fTransparence,
+ 0.0,
+ true));
+
+ // Draw thumbnail
+ Size aImageSize = maPreview1.GetSizePixel();
+
+ float fWidth = aImageSize.Width();
+ float fHeight = aImageSize.Height();
+ float fPosX = maPrev1Pos.getX();
+ float fPosY = maPrev1Pos.getY();
+
+ B2DPolygon aBounds;
+ aBounds.append(B2DPoint(fPosX,fPosY));
+ aBounds.append(B2DPoint(fPosX+fWidth,fPosY));
+ aBounds.append(B2DPoint(fPosX+fWidth,fPosY+fHeight));
+ aBounds.append(B2DPoint(fPosX,fPosY+fHeight));
+ aBounds.setClosed(true);
+
+ aSeq[1] = drawinglayer::primitive2d::Primitive2DReference( new PolyPolygonColorPrimitive2D(
+ B2DPolyPolygon(aBounds), COL_WHITE.getBColor()));
+
+ aSeq[2] = drawinglayer::primitive2d::Primitive2DReference( new FillGraphicPrimitive2D(
+ createTranslateB2DHomMatrix(maPrev1Pos.X(),maPrev1Pos.Y()),
+ FillGraphicAttribute(Graphic(maPreview1),
+ B2DRange(
+ B2DPoint(0,0),
+ B2DPoint(aImageSize.Width(),aImageSize.Height())),
+ false)
+ ));
+
+ // draw thumbnail borders
+ aSeq[3] = drawinglayer::primitive2d::Primitive2DReference(createBorderLine(aBounds));
+
+ if(mbIsDefaultTemplate)
+ {
+ Point aIconPos(getDefaultIconArea().TopLeft());
+
+ aSeq[4] = drawinglayer::primitive2d::Primitive2DReference(new DiscreteBitmapPrimitive2D( maDefaultBitmap,
+ B2DPoint(aIconPos.X(), aIconPos.Y())));
+ }
+
+ addTextPrimitives(maTitle, pAttrs, maTextPos, aSeq);
+
+ pProcessor->process(aSeq);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
+
+
diff --git a/sfx2/source/control/thumbnailview.cxx b/sfx2/source/control/thumbnailview.cxx
new file mode 100644
index 000000000..a678df5b1
--- /dev/null
+++ b/sfx2/source/control/thumbnailview.cxx
@@ -0,0 +1,1220 @@
+/* -*- 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/thumbnailview.hxx>
+#include <sfx2/thumbnailviewitem.hxx>
+
+#include <utility>
+
+#include "thumbnailviewacc.hxx"
+
+#include <basegfx/color/bcolortools.hxx>
+#include <comphelper/processfactory.hxx>
+#include <drawinglayer/attribute/fontattribute.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
+#include <drawinglayer/primitive2d/Primitive2DContainer.hxx>
+#include <drawinglayer/primitive2d/textlayoutdevice.hxx>
+#include <drawinglayer/processor2d/baseprocessor2d.hxx>
+#include <drawinglayer/processor2d/processorfromoutputdevice.hxx>
+#include <o3tl/safeint.hxx>
+#include <rtl/ustring.hxx>
+#include <sal/log.hxx>
+#include <svtools/optionsdrawinglayer.hxx>
+#include <tools/diagnose_ex.h>
+#include <unotools/ucbstreamhelper.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/event.hxx>
+#include <vcl/filter/PngImageReader.hxx>
+#include <vcl/weldutils.hxx>
+
+#include <com/sun/star/accessibility/AccessibleEventId.hpp>
+#include <com/sun/star/embed/ElementModes.hpp>
+#include <com/sun/star/embed/StorageFactory.hpp>
+#include <com/sun/star/embed/XStorage.hpp>
+
+#include <memory>
+
+using namespace basegfx;
+using namespace basegfx::utils;
+using namespace drawinglayer::attribute;
+using namespace drawinglayer::primitive2d;
+
+constexpr int gnFineness = 5;
+
+bool ThumbnailView::renameItem(ThumbnailViewItem*, const OUString&)
+{
+ // Do nothing by default
+ return false;
+}
+
+BitmapEx ThumbnailView::readThumbnail(const OUString &msURL)
+{
+ using namespace ::com::sun::star;
+ using namespace ::com::sun::star::uno;
+
+ // Load the thumbnail from a template document.
+ uno::Reference<io::XInputStream> xIStream;
+
+ uno::Reference< uno::XComponentContext > xContext(::comphelper::getProcessComponentContext());
+ try
+ {
+ uno::Reference<lang::XSingleServiceFactory> xStorageFactory = embed::StorageFactory::create(xContext);
+
+ uno::Sequence<uno::Any> aArgs{ uno::Any(msURL), uno::Any(embed::ElementModes::READ) };
+ uno::Reference<embed::XStorage> xDocStorage (
+ xStorageFactory->createInstanceWithArguments(aArgs),
+ uno::UNO_QUERY);
+
+ try
+ {
+ if (xDocStorage.is())
+ {
+ uno::Reference<embed::XStorage> xStorage (
+ xDocStorage->openStorageElement(
+ "Thumbnails",
+ embed::ElementModes::READ));
+ if (xStorage.is())
+ {
+ uno::Reference<io::XStream> xThumbnailCopy (
+ xStorage->cloneStreamElement("thumbnail.png"));
+ if (xThumbnailCopy.is())
+ xIStream = xThumbnailCopy->getInputStream();
+ }
+ }
+ }
+ catch (const uno::Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("sfx",
+ "caught exception while trying to access Thumbnail/thumbnail.png of " << msURL);
+ }
+
+ try
+ {
+ // An (older) implementation had a bug - The storage
+ // name was "Thumbnail" instead of "Thumbnails". The
+ // old name is still used as fallback but this code can
+ // be removed soon.
+ if ( ! xIStream.is())
+ {
+ uno::Reference<embed::XStorage> xStorage (
+ xDocStorage->openStorageElement( "Thumbnail",
+ embed::ElementModes::READ));
+ if (xStorage.is())
+ {
+ uno::Reference<io::XStream> xThumbnailCopy (
+ xStorage->cloneStreamElement("thumbnail.png"));
+ if (xThumbnailCopy.is())
+ xIStream = xThumbnailCopy->getInputStream();
+ }
+ }
+ }
+ catch (const uno::Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("sfx",
+ "caught exception while trying to access Thumbnails/thumbnail.png of " << msURL);
+ }
+ }
+ catch (const uno::Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("sfx",
+ "caught exception while trying to access thumbnail of "
+ << msURL);
+ }
+
+ // Extract the image from the stream.
+ BitmapEx aThumbnail;
+ if (xIStream.is())
+ {
+ std::unique_ptr<SvStream> pStream (
+ ::utl::UcbStreamHelper::CreateStream (xIStream));
+ vcl::PngImageReader aReader (*pStream);
+ aThumbnail = aReader.read ();
+ }
+
+ // Note that the preview is returned without scaling it to the desired
+ // width. This gives the caller the chance to take advantage of a
+ // possibly larger resolution then was asked for.
+ return aThumbnail;
+}
+
+ThumbnailView::ThumbnailView(std::unique_ptr<weld::ScrolledWindow> xWindow, std::unique_ptr<weld::Menu> xMenu)
+ : mnThumbnailHeight(0)
+ , mnDisplayHeight(0)
+ , mnVItemSpace(-1)
+ , mbAllowVScrollBar(xWindow->get_vpolicy() != VclPolicyType::NEVER)
+ , mbSelectOnFocus(true)
+ , mpItemAttrs(new ThumbnailItemAttributes)
+ , mxScrolledWindow(std::move(xWindow))
+ , mxContextMenu(std::move(xMenu))
+{
+ ImplInit();
+ mxScrolledWindow->connect_vadjustment_changed(LINK(this, ThumbnailView, ImplScrollHdl));
+}
+
+ThumbnailView::~ThumbnailView()
+{
+ css::uno::Reference< css::lang::XComponent> xComponent(mxAccessible, css::uno::UNO_QUERY);
+
+ if (xComponent.is())
+ xComponent->dispose();
+
+ mpItemAttrs.reset();
+
+ ImplDeleteItems();
+}
+
+bool ThumbnailView::MouseMove(const MouseEvent& rMEvt)
+{
+ size_t nItemCount = mFilteredItemList.size();
+ Point aPoint = rMEvt.GetPosPixel();
+
+ for (size_t i = 0; i < nItemCount; i++)
+ {
+ ThumbnailViewItem *pItem = mFilteredItemList[i];
+ ::tools::Rectangle aToInvalidate(pItem->updateHighlight(pItem->mbVisible && !rMEvt.IsLeaveWindow(), aPoint));
+ if (!aToInvalidate.IsEmpty() && IsReallyVisible() && IsUpdateMode())
+ Invalidate(aToInvalidate);
+ }
+
+ return true;
+}
+
+OUString ThumbnailView::RequestHelp(tools::Rectangle& rHelpRect)
+{
+ if (!mbShowTooltips)
+ return OUString();
+
+ Point aPos = rHelpRect.TopLeft();
+ size_t nItemCount = mFilteredItemList.size();
+ for (size_t i = 0; i < nItemCount; i++)
+ {
+ ThumbnailViewItem *pItem = mFilteredItemList[i];
+ if (!pItem->mbVisible)
+ continue;
+ const tools::Rectangle& rDrawArea = pItem->getDrawArea();
+ if (pItem->mbVisible && rDrawArea.Contains(aPos))
+ {
+ rHelpRect = rDrawArea;
+ return pItem->getHelpText();
+ }
+ }
+
+ return OUString();
+}
+
+void ThumbnailView::AppendItem(std::unique_ptr<ThumbnailViewItem> pItem)
+{
+ if (maFilterFunc(pItem.get()))
+ {
+ // Save current start,end range, iterator might get invalidated
+ size_t nSelStartPos = 0;
+ ThumbnailViewItem *pSelStartItem = nullptr;
+
+ if (mpStartSelRange != mFilteredItemList.end())
+ {
+ pSelStartItem = *mpStartSelRange;
+ nSelStartPos = mpStartSelRange - mFilteredItemList.begin();
+ }
+
+ mFilteredItemList.push_back(pItem.get());
+ mpStartSelRange = pSelStartItem != nullptr ? mFilteredItemList.begin() + nSelStartPos : mFilteredItemList.end();
+ }
+
+ mItemList.push_back(std::move(pItem));
+}
+
+void ThumbnailView::ImplInit()
+{
+ mnItemWidth = 0;
+ mnItemHeight = 0;
+ mnItemPadding = 0;
+ mnVisLines = 0;
+ mnLines = 0;
+ mnFirstLine = 0;
+ mnCols = 0;
+ mbScroll = false;
+ mbHasVisibleItems = false;
+ mbShowTooltips = false;
+ mbDrawMnemonics = false;
+ maFilterFunc = ViewFilterAll();
+
+ const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings();
+ maFillColor = rSettings.GetFieldColor();
+ maTextColor = rSettings.GetWindowTextColor();
+ maHighlightColor = rSettings.GetHighlightColor();
+ maHighlightTextColor = rSettings.GetHighlightTextColor();
+ maSelectHighlightColor = rSettings.GetActiveColor();
+ maSelectHighlightTextColor = rSettings.GetActiveTextColor();
+
+ mfHighlightTransparence = SvtOptionsDrawinglayer::GetTransparentSelectionPercent() * 0.01;
+
+ mpStartSelRange = mFilteredItemList.end();
+
+ UpdateColors();
+
+ mpItemAttrs->nMaxTextLength = 0;
+}
+
+void ThumbnailView::UpdateColors()
+{
+ mpItemAttrs->aFillColor = maFillColor.getBColor();
+ mpItemAttrs->aTextColor = maTextColor.getBColor();
+ mpItemAttrs->aHighlightColor = maHighlightColor.getBColor();
+ mpItemAttrs->aHighlightTextColor = maHighlightTextColor.getBColor();
+ mpItemAttrs->aSelectHighlightColor = maSelectHighlightColor.getBColor();
+ mpItemAttrs->aSelectHighlightTextColor = maSelectHighlightTextColor.getBColor();
+ mpItemAttrs->fHighlightTransparence = mfHighlightTransparence;
+}
+
+void ThumbnailView::ImplDeleteItems()
+{
+ const size_t n = mItemList.size();
+
+ for ( size_t i = 0; i < n; ++i )
+ {
+ ThumbnailViewItem *const pItem = mItemList[i].get();
+
+ // deselect all current selected items and fire events
+ if (pItem->isSelected())
+ {
+ pItem->setSelection(false);
+ maItemStateHdl.Call(pItem);
+
+ // fire accessible event???
+ }
+
+ if ( pItem->isVisible() && ImplHasAccessibleListeners() )
+ {
+ css::uno::Any aOldAny, aNewAny;
+
+ aOldAny <<= pItem->GetAccessible( false );
+ ImplFireAccessibleEvent( css::accessibility::AccessibleEventId::CHILD, aOldAny, aNewAny );
+ }
+
+ mItemList[i].reset();
+ }
+
+ mItemList.clear();
+ mFilteredItemList.clear();
+
+ mpStartSelRange = mFilteredItemList.end();
+}
+
+void ThumbnailView::DrawItem(ThumbnailViewItem const *pItem)
+{
+ if (pItem->isVisible())
+ {
+ ::tools::Rectangle aRect = pItem->getDrawArea();
+
+ if (!aRect.IsEmpty())
+ Invalidate(aRect);
+ }
+}
+
+void ThumbnailView::OnItemDblClicked (ThumbnailViewItem*)
+{
+}
+
+css::uno::Reference< css::accessibility::XAccessible > ThumbnailView::CreateAccessible()
+{
+ mxAccessible.set(new ThumbnailViewAcc(this));
+ return mxAccessible;
+}
+
+const css::uno::Reference< css::accessibility::XAccessible > & ThumbnailView::getAccessible() const
+{
+ return mxAccessible;
+}
+
+void ThumbnailView::CalculateItemPositions(bool bScrollBarUsed)
+{
+ if (!mnItemHeight || !mnItemWidth)
+ return;
+
+ Size aWinSize = GetOutputSizePixel();
+ size_t nItemCount = mFilteredItemList.size();
+
+ // calculate window scroll ratio
+ float nScrollRatio;
+ if (bScrollBarUsed)
+ {
+ nScrollRatio = static_cast<float>(mxScrolledWindow->vadjustment_get_value()) /
+ static_cast<float>(mxScrolledWindow->vadjustment_get_upper() -
+ mxScrolledWindow->vadjustment_get_page_size());
+ }
+ else
+ nScrollRatio = 0;
+
+ // calculate ScrollBar width
+ tools::Long nScrBarWidth = mbAllowVScrollBar ? mxScrolledWindow->get_scroll_thickness() : 0;
+
+ // calculate maximum number of visible columns
+ mnCols = static_cast<sal_uInt16>((aWinSize.Width()-nScrBarWidth) / mnItemWidth);
+
+ if (!mnCols)
+ mnCols = 1;
+
+ // calculate maximum number of visible rows
+ mnVisLines = static_cast<sal_uInt16>(aWinSize.Height() / mnItemHeight);
+
+ // calculate empty space
+ tools::Long nHSpace = aWinSize.Width()-nScrBarWidth - mnCols*mnItemWidth;
+ tools::Long nVSpace = aWinSize.Height() - mnVisLines*mnItemHeight;
+ tools::Long nHItemSpace = nHSpace / (mnCols+1);
+ tools::Long nVItemSpace = mnVItemSpace;
+ if (nVItemSpace == -1) // auto, split up extra space to use as vertical spacing
+ nVItemSpace = nVSpace / (mnVisLines+1);
+
+ // calculate maximum number of rows
+ // Floor( (M+N-1)/N )==Ceiling( M/N )
+ mnLines = (static_cast<tools::Long>(nItemCount)+mnCols-1) / mnCols;
+
+ if ( !mnLines )
+ mnLines = 1;
+
+ if ( mnLines <= mnVisLines )
+ mnFirstLine = 0;
+ else if ( mnFirstLine > o3tl::make_unsigned(mnLines-mnVisLines) )
+ mnFirstLine = static_cast<sal_uInt16>(mnLines-mnVisLines);
+
+ mbHasVisibleItems = true;
+
+ tools::Long nFullSteps = (mnLines > mnVisLines) ? mnLines - mnVisLines + 1 : 1;
+
+ tools::Long nItemHeightOffset = mnItemHeight + nVItemSpace;
+ tools::Long nHiddenLines = static_cast<tools::Long>((nFullSteps - 1) * nScrollRatio);
+
+ // calculate offsets
+ tools::Long nStartX = nHItemSpace;
+ tools::Long nStartY = nVItemSpace;
+
+ // calculate and draw items
+ tools::Long x = nStartX;
+ tools::Long y = nStartY - ((nFullSteps - 1) * nScrollRatio - nHiddenLines) * nItemHeightOffset;
+
+ // draw items
+ // Unless we are scrolling (via scrollbar) we just use the precalculated
+ // mnFirstLine -- our nHiddenLines calculation takes into account only
+ // what the user has done with the scrollbar but not any changes of selection
+ // using the keyboard, meaning we could accidentally hide the selected item
+ // if we believe the scrollbar (fdo#72287).
+ size_t nFirstItem = (bScrollBarUsed ? nHiddenLines : mnFirstLine) * mnCols;
+ size_t nLastItem = nFirstItem + (mnVisLines + 1) * mnCols;
+
+ // If want also draw parts of items in the last line,
+ // then we add one more line if parts of this line are visible
+
+ size_t nCurCount = 0;
+ for ( size_t i = 0; i < nItemCount; i++ )
+ {
+ ThumbnailViewItem *const pItem = mFilteredItemList[i];
+
+ if ((nCurCount >= nFirstItem) && (nCurCount < nLastItem))
+ {
+ if( !pItem->isVisible())
+ {
+ if ( ImplHasAccessibleListeners() )
+ {
+ css::uno::Any aOldAny, aNewAny;
+
+ aNewAny <<= pItem->GetAccessible( false );
+ ImplFireAccessibleEvent( css::accessibility::AccessibleEventId::CHILD, aOldAny, aNewAny );
+ }
+
+ pItem->show(true);
+
+ maItemStateHdl.Call(pItem);
+ }
+
+ pItem->setDrawArea(::tools::Rectangle( Point(x,y), Size(mnItemWidth, mnItemHeight) ));
+ pItem->calculateItemsPosition(mnThumbnailHeight,mnItemPadding,mpItemAttrs->nMaxTextLength,mpItemAttrs.get());
+
+ if ( !((nCurCount+1) % mnCols) )
+ {
+ x = nStartX;
+ y += mnItemHeight+nVItemSpace;
+ }
+ else
+ x += mnItemWidth+nHItemSpace;
+ }
+ else
+ {
+ if( pItem->isVisible())
+ {
+ if ( ImplHasAccessibleListeners() )
+ {
+ css::uno::Any aOldAny, aNewAny;
+
+ aOldAny <<= pItem->GetAccessible( false );
+ ImplFireAccessibleEvent( css::accessibility::AccessibleEventId::CHILD, aOldAny, aNewAny );
+ }
+
+ pItem->show(false);
+
+ maItemStateHdl.Call(pItem);
+ }
+
+ }
+
+ ++nCurCount;
+ }
+
+ // arrange ScrollBar, set values and show it
+ mnLines = (nCurCount+mnCols-1)/mnCols;
+
+ // check if scroll is needed
+ mbScroll = mnLines > mnVisLines;
+
+ mxScrolledWindow->vadjustment_set_upper(mnLines * gnFineness);
+ mxScrolledWindow->vadjustment_set_page_size(mnVisLines * gnFineness);
+ if (!bScrollBarUsed)
+ mxScrolledWindow->vadjustment_set_value(static_cast<tools::Long>(mnFirstLine)*gnFineness);
+ tools::Long nPageSize = mnVisLines;
+ if ( nPageSize < 1 )
+ nPageSize = 1;
+ mxScrolledWindow->vadjustment_set_page_increment(nPageSize*gnFineness);
+ if (mbAllowVScrollBar)
+ mxScrolledWindow->set_vpolicy(mbScroll ? VclPolicyType::ALWAYS : VclPolicyType::NEVER);
+}
+
+size_t ThumbnailView::ImplGetItem( const Point& rPos ) const
+{
+ if ( !mbHasVisibleItems )
+ {
+ return THUMBNAILVIEW_ITEM_NOTFOUND;
+ }
+
+ for (size_t i = 0; i < mFilteredItemList.size(); ++i)
+ {
+ if (mFilteredItemList[i]->isVisible() && mFilteredItemList[i]->getDrawArea().Contains(rPos))
+ return i;
+ }
+
+ return THUMBNAILVIEW_ITEM_NOTFOUND;
+}
+
+ThumbnailViewItem* ThumbnailView::ImplGetItem( size_t nPos )
+{
+ return ( nPos < mFilteredItemList.size() ) ? mFilteredItemList[nPos] : nullptr;
+}
+
+sal_uInt16 ThumbnailView::ImplGetVisibleItemCount() const
+{
+ sal_uInt16 nRet = 0;
+ const size_t nItemCount = mItemList.size();
+
+ for ( size_t n = 0; n < nItemCount; ++n )
+ {
+ if ( mItemList[n]->isVisible() )
+ ++nRet;
+ }
+
+ return nRet;
+}
+
+ThumbnailViewItem* ThumbnailView::ImplGetVisibleItem( sal_uInt16 nVisiblePos )
+{
+ const size_t nItemCount = mItemList.size();
+
+ for ( size_t n = 0; n < nItemCount; ++n )
+ {
+ ThumbnailViewItem *const pItem = mItemList[n].get();
+
+ if ( pItem->isVisible() && !nVisiblePos-- )
+ return pItem;
+ }
+
+ return nullptr;
+}
+
+void ThumbnailView::ImplFireAccessibleEvent( short nEventId, const css::uno::Any& rOldValue, const css::uno::Any& rNewValue )
+{
+ ThumbnailViewAcc* pAcc = ThumbnailViewAcc::getImplementation(mxAccessible);
+
+ if( pAcc )
+ pAcc->FireAccessibleEvent( nEventId, rOldValue, rNewValue );
+}
+
+bool ThumbnailView::ImplHasAccessibleListeners() const
+{
+ ThumbnailViewAcc* pAcc = ThumbnailViewAcc::getImplementation(mxAccessible);
+ return( pAcc && pAcc->HasAccessibleListeners() );
+}
+
+IMPL_LINK_NOARG(ThumbnailView, ImplScrollHdl, weld::ScrolledWindow&, void)
+{
+ CalculateItemPositions(true);
+ if (IsReallyVisible() && IsUpdateMode())
+ Invalidate();
+}
+
+bool ThumbnailView::KeyInput( const KeyEvent& rKEvt )
+{
+ bool bHandled = true;
+
+ // Get the last selected item in the list
+ size_t nLastPos = 0;
+ bool bFoundLast = false;
+ for ( tools::Long i = mFilteredItemList.size() - 1; !bFoundLast && i >= 0; --i )
+ {
+ ThumbnailViewItem* pItem = mFilteredItemList[i];
+ if ( pItem->isSelected() )
+ {
+ nLastPos = i;
+ bFoundLast = true;
+ }
+ }
+
+ bool bValidRange = false;
+ bool bHasSelRange = mpStartSelRange != mFilteredItemList.end();
+ size_t nNextPos = nLastPos;
+ vcl::KeyCode aKeyCode = rKEvt.GetKeyCode();
+ ThumbnailViewItem* pNext = nullptr;
+
+ if (aKeyCode.IsShift() && bHasSelRange)
+ {
+ //If the last element selected is the start range position
+ //search for the first selected item
+ size_t nSelPos = mpStartSelRange - mFilteredItemList.begin();
+
+ if (nLastPos == nSelPos)
+ {
+ while (nLastPos && mFilteredItemList[nLastPos-1]->isSelected())
+ --nLastPos;
+ }
+ }
+
+ switch ( aKeyCode.GetCode() )
+ {
+ case KEY_RIGHT:
+ if (!mFilteredItemList.empty())
+ {
+ if ( bFoundLast && nLastPos + 1 < mFilteredItemList.size() )
+ {
+ bValidRange = true;
+ nNextPos = nLastPos + 1;
+ }
+
+ pNext = mFilteredItemList[nNextPos];
+ }
+ break;
+ case KEY_LEFT:
+ if (!mFilteredItemList.empty())
+ {
+ if ( nLastPos > 0 )
+ {
+ bValidRange = true;
+ nNextPos = nLastPos - 1;
+ }
+
+ pNext = mFilteredItemList[nNextPos];
+ }
+ break;
+ case KEY_DOWN:
+ if (!mFilteredItemList.empty())
+ {
+ if ( bFoundLast )
+ {
+ //If we are in the second last row just go the one in
+ //the row below, if there's not row below just go to the
+ //last item but for the last row don't do anything.
+ if ( nLastPos + mnCols < mFilteredItemList.size( ) )
+ {
+ bValidRange = true;
+ nNextPos = nLastPos + mnCols;
+ }
+ else
+ {
+ int curRow = nLastPos/mnCols;
+
+ if (curRow < mnLines-1)
+ nNextPos = mFilteredItemList.size()-1;
+ }
+ }
+
+ pNext = mFilteredItemList[nNextPos];
+ }
+ break;
+ case KEY_UP:
+ if (!mFilteredItemList.empty())
+ {
+ if ( nLastPos >= mnCols )
+ {
+ bValidRange = true;
+ nNextPos = nLastPos - mnCols;
+ }
+
+ pNext = mFilteredItemList[nNextPos];
+ }
+ break;
+ case KEY_RETURN:
+ {
+ if ( bFoundLast )
+ OnItemDblClicked( mFilteredItemList[nLastPos] );
+ }
+ [[fallthrough]];
+ default:
+ bHandled = CustomWidgetController::KeyInput(rKEvt);
+ }
+
+ if ( pNext )
+ {
+ if (aKeyCode.IsShift() && bValidRange)
+ {
+ std::pair<size_t,size_t> aRange;
+ size_t nSelPos = mpStartSelRange - mFilteredItemList.begin();
+
+ if (nLastPos < nSelPos)
+ {
+ if (nNextPos > nLastPos)
+ {
+ if ( nNextPos > nSelPos)
+ aRange = std::make_pair(nLastPos,nNextPos);
+ else
+ aRange = std::make_pair(nLastPos,nNextPos-1);
+ }
+ else
+ aRange = std::make_pair(nNextPos,nLastPos-1);
+ }
+ else if (nLastPos == nSelPos)
+ {
+ if (nNextPos > nLastPos)
+ aRange = std::make_pair(nLastPos+1,nNextPos);
+ else
+ aRange = std::make_pair(nNextPos,nLastPos-1);
+ }
+ else
+ {
+ if (nNextPos > nLastPos)
+ aRange = std::make_pair(nLastPos+1,nNextPos);
+ else
+ {
+ if ( nNextPos < nSelPos)
+ aRange = std::make_pair(nNextPos,nLastPos);
+ else
+ aRange = std::make_pair(nNextPos+1,nLastPos);
+ }
+ }
+
+ for (size_t i = aRange.first; i <= aRange.second; ++i)
+ {
+ if (i != nSelPos)
+ {
+ ThumbnailViewItem *pCurItem = mFilteredItemList[i];
+
+ pCurItem->setSelection(!pCurItem->isSelected());
+
+ DrawItem(pCurItem);
+
+ maItemStateHdl.Call(pCurItem);
+ }
+ }
+ }
+ else if (!aKeyCode.IsShift())
+ {
+ deselectItems();
+ SelectItem(pNext->mnId);
+
+ //Mark it as the selection range start position
+ mpStartSelRange = mFilteredItemList.begin() + nNextPos;
+ }
+
+ MakeItemVisible(pNext->mnId);
+ }
+ return bHandled;
+}
+
+void ThumbnailView::MakeItemVisible( sal_uInt16 nItemId )
+{
+ // Get the item row
+ size_t nPos = 0;
+ bool bFound = false;
+ for ( size_t i = 0; !bFound && i < mFilteredItemList.size(); ++i )
+ {
+ ThumbnailViewItem* pItem = mFilteredItemList[i];
+ if ( pItem->mnId == nItemId )
+ {
+ nPos = i;
+ bFound = true;
+ }
+ }
+ sal_uInt16 nRow = mnCols ? nPos / mnCols : 0;
+
+ // Move the visible rows as little as possible to include that one
+ if ( nRow < mnFirstLine )
+ mnFirstLine = nRow;
+ else if ( nRow > mnFirstLine + mnVisLines )
+ mnFirstLine = nRow - mnVisLines;
+
+ CalculateItemPositions();
+ Invalidate();
+}
+
+bool ThumbnailView::MouseButtonDown( const MouseEvent& rMEvt )
+{
+ GrabFocus();
+
+ if (!rMEvt.IsLeft())
+ {
+ return CustomWidgetController::MouseButtonDown( rMEvt );
+ }
+
+ size_t nPos = ImplGetItem(rMEvt.GetPosPixel());
+ ThumbnailViewItem* pItem = ImplGetItem(nPos);
+
+ if ( !pItem )
+ {
+ deselectItems();
+ return CustomWidgetController::MouseButtonDown( rMEvt );
+ }
+
+ if ( rMEvt.GetClicks() == 2 )
+ {
+ OnItemDblClicked(pItem);
+ return true;
+ }
+
+ if(rMEvt.GetClicks() == 1)
+ {
+ if (rMEvt.IsMod1())
+ {
+ //Keep selected item group state and just invert current desired one state
+ pItem->setSelection(!pItem->isSelected());
+
+ //This one becomes the selection range start position if it changes its state to selected otherwise resets it
+ mpStartSelRange = pItem->isSelected() ? mFilteredItemList.begin() + nPos : mFilteredItemList.end();
+ }
+ else if (rMEvt.IsShift() && mpStartSelRange != mFilteredItemList.end())
+ {
+ std::pair<size_t,size_t> aNewRange;
+ aNewRange.first = mpStartSelRange - mFilteredItemList.begin();
+ aNewRange.second = nPos;
+
+ if (aNewRange.first > aNewRange.second)
+ std::swap(aNewRange.first,aNewRange.second);
+
+ //Deselect the ones outside of it
+ for (size_t i = 0, n = mFilteredItemList.size(); i < n; ++i)
+ {
+ ThumbnailViewItem *pCurItem = mFilteredItemList[i];
+
+ if (pCurItem->isSelected() && (i < aNewRange.first || i > aNewRange.second))
+ {
+ pCurItem->setSelection(false);
+
+ DrawItem(pCurItem);
+
+ maItemStateHdl.Call(pCurItem);
+ }
+ }
+
+ size_t nSelPos = mpStartSelRange - mFilteredItemList.begin();
+
+ //Select the items between start range and the selected item
+ if (nSelPos != nPos)
+ {
+ int dir = nSelPos < nPos ? 1 : -1;
+ size_t nCurPos = nSelPos + dir;
+
+ while (nCurPos != nPos)
+ {
+ ThumbnailViewItem *pCurItem = mFilteredItemList[nCurPos];
+
+ if (!pCurItem->isSelected())
+ {
+ pCurItem->setSelection(true);
+
+ DrawItem(pCurItem);
+
+ maItemStateHdl.Call(pCurItem);
+ }
+
+ nCurPos += dir;
+ }
+ }
+
+ pItem->setSelection(true);
+ }
+ else
+ {
+ //If we got a group of selected items deselect the rest and only keep the desired one
+ //mark items as not selected to not fire unnecessary change state events.
+ pItem->setSelection(false);
+ deselectItems();
+ pItem->setSelection(true);
+
+ //Mark as initial selection range position and reset end one
+ mpStartSelRange = mFilteredItemList.begin() + nPos;
+ }
+
+ if (!pItem->isHighlighted())
+ DrawItem(pItem);
+
+ maItemStateHdl.Call(pItem);
+
+ //fire accessible event??
+ }
+ return true;
+}
+
+void ThumbnailView::SetDrawingArea(weld::DrawingArea* pDrawingArea)
+{
+ CustomWidgetController::SetDrawingArea(pDrawingArea);
+
+ OutputDevice& rDevice = pDrawingArea->get_ref_device();
+ weld::SetPointFont(rDevice, pDrawingArea->get_font());
+ mpItemAttrs->aFontAttr = getFontAttributeFromVclFont(mpItemAttrs->aFontSize, rDevice.GetFont(), false, true);
+
+ SetOutputSizePixel(pDrawingArea->get_preferred_size());
+}
+
+void ThumbnailView::Paint(vcl::RenderContext& rRenderContext, const ::tools::Rectangle& /*rRect*/)
+{
+ rRenderContext.Push(vcl::PushFlags::ALL);
+
+ rRenderContext.SetTextFillColor();
+ rRenderContext.SetBackground(maFillColor);
+
+ size_t nItemCount = mItemList.size();
+
+ // Draw background
+ drawinglayer::primitive2d::Primitive2DContainer aSeq(1);
+ aSeq[0] = drawinglayer::primitive2d::Primitive2DReference(
+ new PolyPolygonColorPrimitive2D(
+ B2DPolyPolygon( ::tools::Polygon(::tools::Rectangle(Point(), GetOutputSizePixel()), 0, 0).getB2DPolygon()),
+ maFillColor.getBColor()));
+
+ // Create the processor and process the primitives
+ const drawinglayer::geometry::ViewInformation2D aNewViewInfos;
+
+ std::unique_ptr<drawinglayer::processor2d::BaseProcessor2D> pProcessor(
+ drawinglayer::processor2d::createBaseProcessor2DFromOutputDevice(rRenderContext, aNewViewInfos));
+ pProcessor->process(aSeq);
+
+ // draw items
+ for (size_t i = 0; i < nItemCount; i++)
+ {
+ ThumbnailViewItem *const pItem = mItemList[i].get();
+ if (!pItem->isVisible())
+ continue;
+ pItem->Paint(pProcessor.get(), mpItemAttrs.get());
+ }
+
+ rRenderContext.Pop();
+}
+
+void ThumbnailView::GetFocus()
+{
+ if (mbSelectOnFocus)
+ {
+ // Select the first item if nothing selected
+ int nSelected = -1;
+ for (size_t i = 0, n = mItemList.size(); i < n && nSelected == -1; ++i)
+ {
+ if (mItemList[i]->isSelected())
+ nSelected = i;
+ }
+
+ if (nSelected == -1 && !mItemList.empty())
+ {
+ ThumbnailViewItem* pFirst = nullptr;
+ if (!mFilteredItemList.empty()) {
+ pFirst = mFilteredItemList[0];
+ } else {
+ pFirst = mItemList[0].get();
+ }
+
+ SelectItem(pFirst->mnId);
+ }
+ }
+
+ // Tell the accessible object that we got the focus.
+ ThumbnailViewAcc* pAcc = ThumbnailViewAcc::getImplementation(mxAccessible);
+ if( pAcc )
+ pAcc->GetFocus();
+
+ CustomWidgetController::GetFocus();
+}
+
+void ThumbnailView::LoseFocus()
+{
+ CustomWidgetController::LoseFocus();
+
+ // Tell the accessible object that we lost the focus.
+ ThumbnailViewAcc* pAcc = ThumbnailViewAcc::getImplementation(mxAccessible);
+ if( pAcc )
+ pAcc->LoseFocus();
+}
+
+void ThumbnailView::Resize()
+{
+ CustomWidgetController::Resize();
+ CalculateItemPositions();
+
+ if ( IsReallyVisible() && IsUpdateMode() )
+ Invalidate();
+}
+
+void ThumbnailView::RemoveItem( sal_uInt16 nItemId )
+{
+ size_t nPos = GetItemPos( nItemId );
+
+ if ( nPos == THUMBNAILVIEW_ITEM_NOTFOUND )
+ return;
+
+ if ( nPos < mFilteredItemList.size() ) {
+
+ // keep it alive until after we have deleted it from the filter item list
+ std::unique_ptr<ThumbnailViewItem> xKeepAliveViewItem;
+
+ // delete item from the thumbnail list
+ for (auto it = mItemList.begin(); it != mItemList.end(); ++it)
+ {
+ if ((*it)->mnId == nItemId)
+ {
+ xKeepAliveViewItem = std::move(*it);
+ mItemList.erase(it);
+ break;
+ }
+ }
+
+ // delete item from the filter item list
+ ThumbnailValueItemList::iterator it = mFilteredItemList.begin();
+ ::std::advance( it, nPos );
+
+ if ((*it)->isSelected())
+ {
+ (*it)->setSelection(false);
+ maItemStateHdl.Call(*it);
+ }
+
+ mFilteredItemList.erase( it );
+ mpStartSelRange = mFilteredItemList.end();
+ }
+
+ CalculateItemPositions();
+
+ if ( IsReallyVisible() && IsUpdateMode() )
+ Invalidate();
+}
+
+void ThumbnailView::Clear()
+{
+ ImplDeleteItems();
+
+ // reset variables
+ mnFirstLine = 0;
+
+ CalculateItemPositions();
+
+ if ( IsReallyVisible() && IsUpdateMode() )
+ Invalidate();
+}
+
+void ThumbnailView::updateItems (std::vector<std::unique_ptr<ThumbnailViewItem>> items)
+{
+ ImplDeleteItems();
+
+ // reset variables
+ mnFirstLine = 0;
+
+ mItemList = std::move(items);
+
+ filterItems(maFilterFunc);
+}
+
+size_t ThumbnailView::GetItemPos( sal_uInt16 nItemId ) const
+{
+ for ( size_t i = 0, n = mFilteredItemList.size(); i < n; ++i ) {
+ if ( mFilteredItemList[i]->mnId == nItemId ) {
+ return i;
+ }
+ }
+ return THUMBNAILVIEW_ITEM_NOTFOUND;
+}
+
+sal_uInt16 ThumbnailView::GetItemId( size_t nPos ) const
+{
+ return ( nPos < mFilteredItemList.size() ) ? mFilteredItemList[nPos]->mnId : 0 ;
+}
+
+sal_uInt16 ThumbnailView::GetItemId( const Point& rPos ) const
+{
+ size_t nItemPos = ImplGetItem( rPos );
+ if ( nItemPos != THUMBNAILVIEW_ITEM_NOTFOUND )
+ return GetItemId( nItemPos );
+
+ return 0;
+}
+
+sal_uInt16 ThumbnailView::getNextItemId() const
+{
+ return mItemList.empty() ? 1 : mItemList.back()->mnId + 1;
+}
+
+void ThumbnailView::setItemMaxTextLength(sal_uInt32 nLength)
+{
+ mpItemAttrs->nMaxTextLength = nLength;
+}
+
+void ThumbnailView::setItemDimensions(tools::Long itemWidth, tools::Long thumbnailHeight, tools::Long displayHeight, int itemPadding)
+{
+ mnItemWidth = itemWidth + 2*itemPadding;
+ mnThumbnailHeight = thumbnailHeight;
+ mnDisplayHeight = displayHeight;
+ mnItemPadding = itemPadding;
+ mnItemHeight = mnDisplayHeight + mnThumbnailHeight + 2*itemPadding;
+}
+
+void ThumbnailView::SelectItem( sal_uInt16 nItemId )
+{
+ size_t nItemPos = GetItemPos( nItemId );
+ if ( nItemPos == THUMBNAILVIEW_ITEM_NOTFOUND )
+ return;
+
+ ThumbnailViewItem* pItem = mFilteredItemList[nItemPos];
+ if (pItem->isSelected())
+ return;
+
+ pItem->setSelection(true);
+ maItemStateHdl.Call(pItem);
+
+ if (IsReallyVisible() && IsUpdateMode())
+ Invalidate();
+
+ bool bNewOut = IsReallyVisible() && IsUpdateMode();
+
+ // if necessary scroll to the visible area
+ if (mbScroll && nItemId && mnCols)
+ {
+ sal_uInt16 nNewLine = static_cast<sal_uInt16>(nItemPos / mnCols);
+ if ( nNewLine < mnFirstLine )
+ {
+ mnFirstLine = nNewLine;
+ }
+ else if ( mnVisLines != 0 && nNewLine > o3tl::make_unsigned(mnFirstLine+mnVisLines-1) )
+ {
+ mnFirstLine = static_cast<sal_uInt16>(nNewLine-mnVisLines+1);
+ }
+ }
+
+ if ( bNewOut )
+ {
+ if ( IsReallyVisible() && IsUpdateMode() )
+ Invalidate();
+ }
+
+ if( !ImplHasAccessibleListeners() )
+ return;
+
+ // focus event (select)
+ ThumbnailViewItemAcc* pItemAcc = ThumbnailViewItemAcc::getImplementation( pItem->GetAccessible( false ) );
+
+ if( pItemAcc )
+ {
+ css::uno::Any aOldAny, aNewAny;
+ aNewAny <<= css::uno::Reference< css::uno::XInterface >(
+ static_cast< ::cppu::OWeakObject* >( pItemAcc ));
+ ImplFireAccessibleEvent( css::accessibility::AccessibleEventId::ACTIVE_DESCENDANT_CHANGED, aOldAny, aNewAny );
+ }
+
+ // selection event
+ css::uno::Any aOldAny, aNewAny;
+ ImplFireAccessibleEvent( css::accessibility::AccessibleEventId::SELECTION_CHANGED, aOldAny, aNewAny );
+}
+
+bool ThumbnailView::IsItemSelected( sal_uInt16 nItemId ) const
+{
+ size_t nItemPos = GetItemPos( nItemId );
+ if ( nItemPos == THUMBNAILVIEW_ITEM_NOTFOUND )
+ return false;
+
+ ThumbnailViewItem* pItem = mFilteredItemList[nItemPos];
+ return pItem->isSelected();
+}
+
+void ThumbnailView::deselectItems()
+{
+ for (std::unique_ptr<ThumbnailViewItem>& p : mItemList)
+ {
+ if (p->isSelected())
+ {
+ p->setSelection(false);
+
+ maItemStateHdl.Call(p.get());
+ }
+ }
+
+ if (IsReallyVisible() && IsUpdateMode())
+ Invalidate();
+}
+
+void ThumbnailView::ShowTooltips( bool bShowTooltips )
+{
+ mbShowTooltips = bShowTooltips;
+}
+
+void ThumbnailView::DrawMnemonics( bool bDrawMnemonics )
+{
+ mbDrawMnemonics = bDrawMnemonics;
+}
+
+void ThumbnailView::filterItems(const std::function<bool (const ThumbnailViewItem*)> &func)
+{
+ mnFirstLine = 0; // start at the top of the list instead of the current position
+ maFilterFunc = func;
+
+ size_t nSelPos = 0;
+ bool bHasSelRange = false;
+ ThumbnailViewItem *curSel = mpStartSelRange != mFilteredItemList.end() ? *mpStartSelRange : nullptr;
+
+ mFilteredItemList.clear();
+
+ for (size_t i = 0, n = mItemList.size(); i < n; ++i)
+ {
+ ThumbnailViewItem *const pItem = mItemList[i].get();
+
+ if (maFilterFunc(pItem))
+ {
+ if (curSel == pItem)
+ {
+ nSelPos = i;
+ bHasSelRange = true;
+ }
+
+ mFilteredItemList.push_back(pItem);
+ }
+ else
+ {
+ if( pItem->isVisible())
+ {
+ if ( ImplHasAccessibleListeners() )
+ {
+ css::uno::Any aOldAny, aNewAny;
+
+ aOldAny <<= pItem->GetAccessible( false );
+ ImplFireAccessibleEvent( css::accessibility::AccessibleEventId::CHILD, aOldAny, aNewAny );
+ }
+
+ pItem->show(false);
+ pItem->setSelection(false);
+
+ maItemStateHdl.Call(pItem);
+ }
+ }
+ }
+
+ mpStartSelRange = bHasSelRange ? mFilteredItemList.begin() + nSelPos : mFilteredItemList.end();
+ CalculateItemPositions();
+
+ Invalidate();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/control/thumbnailviewacc.cxx b/sfx2/source/control/thumbnailviewacc.cxx
new file mode 100644
index 000000000..4492980cc
--- /dev/null
+++ b/sfx2/source/control/thumbnailviewacc.cxx
@@ -0,0 +1,874 @@
+/* -*- 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 "thumbnailviewacc.hxx"
+
+#include <comphelper/servicehelper.hxx>
+#include <sfx2/thumbnailview.hxx>
+#include <sfx2/thumbnailviewitem.hxx>
+#include <unotools/accessiblestatesethelper.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <sal/log.hxx>
+#include <tools/debug.hxx>
+#include <tools/diagnose_ex.h>
+
+#include <com/sun/star/accessibility/AccessibleEventId.hpp>
+#include <com/sun/star/accessibility/AccessibleRole.hpp>
+#include <com/sun/star/accessibility/AccessibleStateType.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+
+using namespace ::com::sun::star;
+
+ThumbnailViewAcc::ThumbnailViewAcc( ThumbnailView* pParent ) :
+ mpParent( pParent )
+{
+}
+
+ThumbnailViewAcc::~ThumbnailViewAcc()
+{
+}
+
+const uno::Sequence< sal_Int8 >& ThumbnailViewAcc::getUnoTunnelId()
+{
+ static const comphelper::UnoIdInit theSfxValueSetAccUnoTunnelId;
+ return theSfxValueSetAccUnoTunnelId.getSeq();
+}
+
+ThumbnailViewAcc* ThumbnailViewAcc::getImplementation( const uno::Reference< uno::XInterface >& rxData )
+ noexcept
+{
+ try
+ {
+ return comphelper::getFromUnoTunnel<ThumbnailViewAcc>(rxData);
+ }
+ catch(const css::uno::Exception&)
+ {
+ return nullptr;
+ }
+}
+
+uno::Reference< accessibility::XAccessibleContext > SAL_CALL ThumbnailViewAcc::getAccessibleContext()
+{
+ ThrowIfDisposed();
+ return this;
+}
+
+sal_Int32 SAL_CALL ThumbnailViewAcc::getAccessibleChildCount()
+{
+ const SolarMutexGuard aSolarGuard;
+ ThrowIfDisposed();
+
+ sal_Int32 nCount = mpParent->ImplGetVisibleItemCount();
+ return nCount;
+}
+
+uno::Reference< accessibility::XAccessible > SAL_CALL ThumbnailViewAcc::getAccessibleChild( sal_Int32 i )
+{
+ ThrowIfDisposed();
+ const SolarMutexGuard aSolarGuard;
+ ThumbnailViewItem* pItem = getItem (sal::static_int_cast< sal_uInt16 >(i));
+
+ if( !pItem )
+ throw lang::IndexOutOfBoundsException();
+
+ uno::Reference< accessibility::XAccessible > xRet = pItem->GetAccessible( /*bIsTransientChildrenDisabled*/false );
+ return xRet;
+}
+
+uno::Reference< accessibility::XAccessible > SAL_CALL ThumbnailViewAcc::getAccessibleParent()
+{
+ ThrowIfDisposed();
+ const SolarMutexGuard aSolarGuard;
+ return mpParent->GetDrawingArea()->get_accessible_parent();
+}
+
+sal_Int32 SAL_CALL ThumbnailViewAcc::getAccessibleIndexInParent()
+{
+ ThrowIfDisposed();
+ const SolarMutexGuard aSolarGuard;
+
+ // -1 for child not found/no parent (according to specification)
+ sal_Int32 nRet = -1;
+
+ uno::Reference<accessibility::XAccessible> xParent(getAccessibleParent());
+ if (!xParent)
+ return nRet;
+
+ try
+ {
+ uno::Reference<accessibility::XAccessibleContext> xParentContext(xParent->getAccessibleContext());
+
+ // iterate over parent's children and search for this object
+ if ( xParentContext.is() )
+ {
+ sal_Int32 nChildCount = xParentContext->getAccessibleChildCount();
+ for ( sal_Int32 nChild = 0; ( nChild < nChildCount ) && ( -1 == nRet ); ++nChild )
+ {
+ uno::Reference<XAccessible> xChild(xParentContext->getAccessibleChild(nChild));
+ if ( xChild.get() == this )
+ nRet = nChild;
+ }
+ }
+ }
+ catch (const uno::Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "sfx", "OAccessibleContextHelper::getAccessibleIndexInParent" );
+ }
+
+ return nRet;
+}
+
+sal_Int16 SAL_CALL ThumbnailViewAcc::getAccessibleRole()
+{
+ ThrowIfDisposed();
+ // #i73746# As the Java Access Bridge (v 2.0.1) uses "managesDescendants"
+ // always if the role is LIST, we need a different role in this case
+ return accessibility::AccessibleRole::LIST;
+}
+
+OUString SAL_CALL ThumbnailViewAcc::getAccessibleDescription()
+{
+ ThrowIfDisposed();
+ return "ThumbnailView";
+}
+
+OUString SAL_CALL ThumbnailViewAcc::getAccessibleName()
+{
+ ThrowIfDisposed();
+ const SolarMutexGuard aSolarGuard;
+ OUString aRet;
+
+ if (mpParent)
+ {
+ aRet = mpParent->GetAccessibleName();
+ }
+
+ return aRet;
+}
+
+uno::Reference< accessibility::XAccessibleRelationSet > SAL_CALL ThumbnailViewAcc::getAccessibleRelationSet()
+{
+ ThrowIfDisposed();
+ return uno::Reference< accessibility::XAccessibleRelationSet >();
+}
+
+uno::Reference< accessibility::XAccessibleStateSet > SAL_CALL ThumbnailViewAcc::getAccessibleStateSet()
+{
+ ThrowIfDisposed();
+ rtl::Reference<::utl::AccessibleStateSetHelper> pStateSet = new ::utl::AccessibleStateSetHelper();
+
+ // Set some states.
+ pStateSet->AddState (accessibility::AccessibleStateType::ENABLED);
+ pStateSet->AddState (accessibility::AccessibleStateType::SENSITIVE);
+ pStateSet->AddState (accessibility::AccessibleStateType::SHOWING);
+ pStateSet->AddState (accessibility::AccessibleStateType::VISIBLE);
+ pStateSet->AddState (accessibility::AccessibleStateType::MANAGES_DESCENDANTS);
+ pStateSet->AddState (accessibility::AccessibleStateType::FOCUSABLE);
+
+ return pStateSet;
+}
+
+lang::Locale SAL_CALL ThumbnailViewAcc::getLocale()
+{
+ ThrowIfDisposed();
+ const SolarMutexGuard aSolarGuard;
+ uno::Reference< accessibility::XAccessible > xParent( getAccessibleParent() );
+ lang::Locale aRet( "", "", "" );
+
+ if( xParent.is() )
+ {
+ uno::Reference< accessibility::XAccessibleContext > xParentContext( xParent->getAccessibleContext() );
+
+ if( xParentContext.is() )
+ aRet = xParentContext->getLocale ();
+ }
+
+ return aRet;
+}
+
+void SAL_CALL ThumbnailViewAcc::addAccessibleEventListener( const uno::Reference< accessibility::XAccessibleEventListener >& rxListener )
+{
+ ThrowIfDisposed();
+ std::unique_lock aGuard (m_aMutex);
+
+ if( !rxListener.is() )
+ return;
+
+ bool bFound = false;
+
+ for (auto const& eventListener : mxEventListeners)
+ {
+ if( eventListener == rxListener )
+ {
+ bFound = true;
+ break;
+ }
+ }
+
+ if (!bFound)
+ mxEventListeners.push_back( rxListener );
+}
+
+void SAL_CALL ThumbnailViewAcc::removeAccessibleEventListener( const uno::Reference< accessibility::XAccessibleEventListener >& rxListener )
+{
+ ThrowIfDisposed();
+ std::unique_lock aGuard (m_aMutex);
+
+ if( rxListener.is() )
+ {
+ std::vector< uno::Reference< accessibility::XAccessibleEventListener > >::iterator aIter =
+ std::find(mxEventListeners.begin(), mxEventListeners.end(), rxListener);
+
+ if (aIter != mxEventListeners.end())
+ mxEventListeners.erase( aIter );
+ }
+}
+
+sal_Bool SAL_CALL ThumbnailViewAcc::containsPoint( const awt::Point& aPoint )
+{
+ ThrowIfDisposed();
+ const awt::Rectangle aRect( getBounds() );
+ const Point aSize( aRect.Width, aRect.Height );
+ const Point aNullPoint, aTestPoint( aPoint.X, aPoint.Y );
+
+ return tools::Rectangle( aNullPoint, aSize ).Contains( aTestPoint );
+}
+
+uno::Reference< accessibility::XAccessible > SAL_CALL ThumbnailViewAcc::getAccessibleAtPoint( const awt::Point& aPoint )
+{
+ ThrowIfDisposed();
+ const SolarMutexGuard aSolarGuard;
+ const sal_uInt16 nItemId = mpParent->GetItemId( Point( aPoint.X, aPoint.Y ) );
+ uno::Reference< accessibility::XAccessible > xRet;
+
+ if ( nItemId )
+ {
+ const size_t nItemPos = mpParent->GetItemPos( nItemId );
+
+ if( THUMBNAILVIEW_ITEM_NONEITEM != nItemPos )
+ {
+ ThumbnailViewItem *const pItem = mpParent->mFilteredItemList[nItemPos];
+ xRet = pItem->GetAccessible( /*bIsTransientChildrenDisabled*/false );
+ }
+ }
+
+ return xRet;
+}
+
+awt::Rectangle SAL_CALL ThumbnailViewAcc::getBounds()
+{
+ ThrowIfDisposed();
+ const SolarMutexGuard aSolarGuard;
+ const Point aOutPos;
+ const Size aOutSize( mpParent->GetOutputSizePixel() );
+ awt::Rectangle aRet;
+
+ aRet.X = aOutPos.X();
+ aRet.Y = aOutPos.Y();
+ aRet.Width = aOutSize.Width();
+ aRet.Height = aOutSize.Height();
+
+ return aRet;
+}
+
+awt::Point SAL_CALL ThumbnailViewAcc::getLocation()
+{
+ ThrowIfDisposed();
+ const awt::Rectangle aRect( getBounds() );
+ awt::Point aRet;
+
+ aRet.X = aRect.X;
+ aRet.Y = aRect.Y;
+
+ return aRet;
+}
+
+awt::Point SAL_CALL ThumbnailViewAcc::getLocationOnScreen()
+{
+ ThrowIfDisposed();
+ const SolarMutexGuard aSolarGuard;
+ awt::Point aScreenLoc(0, 0);
+
+ uno::Reference<accessibility::XAccessible> xParent(getAccessibleParent());
+ if (xParent)
+ {
+ uno::Reference<accessibility::XAccessibleContext> xParentContext(xParent->getAccessibleContext());
+ uno::Reference<accessibility::XAccessibleComponent> xParentComponent(xParentContext, css::uno::UNO_QUERY);
+ OSL_ENSURE( xParentComponent.is(), "ThumbnailViewAcc::getLocationOnScreen: no parent component!" );
+ if ( xParentComponent.is() )
+ {
+ awt::Point aParentScreenLoc( xParentComponent->getLocationOnScreen() );
+ awt::Point aOwnRelativeLoc( getLocation() );
+ aScreenLoc.X = aParentScreenLoc.X + aOwnRelativeLoc.X;
+ aScreenLoc.Y = aParentScreenLoc.Y + aOwnRelativeLoc.Y;
+ }
+ }
+
+ return aScreenLoc;
+}
+
+awt::Size SAL_CALL ThumbnailViewAcc::getSize()
+{
+ ThrowIfDisposed();
+ const awt::Rectangle aRect( getBounds() );
+ awt::Size aRet;
+
+ aRet.Width = aRect.Width;
+ aRet.Height = aRect.Height;
+
+ return aRet;
+}
+
+void SAL_CALL ThumbnailViewAcc::grabFocus()
+{
+ ThrowIfDisposed();
+ const SolarMutexGuard aSolarGuard;
+ mpParent->GrabFocus();
+}
+
+sal_Int32 SAL_CALL ThumbnailViewAcc::getForeground( )
+{
+ ThrowIfDisposed();
+ Color nColor = Application::GetSettings().GetStyleSettings().GetWindowTextColor();
+ return static_cast<sal_Int32>(nColor);
+}
+
+sal_Int32 SAL_CALL ThumbnailViewAcc::getBackground( )
+{
+ ThrowIfDisposed();
+ Color nColor = Application::GetSettings().GetStyleSettings().GetWindowColor();
+ return static_cast<sal_Int32>(nColor);
+}
+
+void SAL_CALL ThumbnailViewAcc::selectAccessibleChild( sal_Int32 nChildIndex )
+{
+ ThrowIfDisposed();
+ const SolarMutexGuard aSolarGuard;
+ ThumbnailViewItem* pItem = getItem (sal::static_int_cast< sal_uInt16 >(nChildIndex));
+
+ if(pItem == nullptr)
+ throw lang::IndexOutOfBoundsException();
+
+ mpParent->SelectItem( pItem->mnId );
+}
+
+sal_Bool SAL_CALL ThumbnailViewAcc::isAccessibleChildSelected( sal_Int32 nChildIndex )
+{
+ ThrowIfDisposed();
+ const SolarMutexGuard aSolarGuard;
+ ThumbnailViewItem* pItem = getItem (sal::static_int_cast< sal_uInt16 >(nChildIndex));
+
+ if (pItem == nullptr)
+ throw lang::IndexOutOfBoundsException();
+
+ return mpParent->IsItemSelected( pItem->mnId );
+}
+
+void SAL_CALL ThumbnailViewAcc::clearAccessibleSelection()
+{
+ ThrowIfDisposed();
+}
+
+void SAL_CALL ThumbnailViewAcc::selectAllAccessibleChildren()
+{
+ ThrowIfDisposed();
+ // unsupported due to single selection only
+}
+
+sal_Int32 SAL_CALL ThumbnailViewAcc::getSelectedAccessibleChildCount()
+{
+ ThrowIfDisposed();
+ const SolarMutexGuard aSolarGuard;
+ sal_Int32 nRet = 0;
+
+ for( sal_uInt16 i = 0, nCount = getItemCount(); i < nCount; i++ )
+ {
+ ThumbnailViewItem* pItem = getItem (i);
+
+ if( pItem && mpParent->IsItemSelected( pItem->mnId ) )
+ ++nRet;
+ }
+
+ return nRet;
+}
+
+uno::Reference< accessibility::XAccessible > SAL_CALL ThumbnailViewAcc::getSelectedAccessibleChild( sal_Int32 nSelectedChildIndex )
+{
+ ThrowIfDisposed();
+ const SolarMutexGuard aSolarGuard;
+ uno::Reference< accessibility::XAccessible > xRet;
+
+ for( sal_uInt16 i = 0, nCount = getItemCount(), nSel = 0; ( i < nCount ) && !xRet.is(); i++ )
+ {
+ ThumbnailViewItem* pItem = getItem(i);
+
+ if( pItem && mpParent->IsItemSelected( pItem->mnId ) && ( nSelectedChildIndex == static_cast< sal_Int32 >( nSel++ ) ) )
+ xRet = pItem->GetAccessible( /*bIsTransientChildrenDisabled*/false );
+ }
+
+ return xRet;
+}
+
+void SAL_CALL ThumbnailViewAcc::deselectAccessibleChild( sal_Int32 )
+{
+ ThrowIfDisposed();
+ const SolarMutexGuard aSolarGuard;
+ // Because of the single selection we can reset the whole selection when
+ // the specified child is currently selected.
+//FIXME TODO if (isAccessibleChildSelected(nChildIndex))
+//FIXME TODO ;
+}
+
+sal_Int64 SAL_CALL ThumbnailViewAcc::getSomething( const uno::Sequence< sal_Int8 >& rId )
+{
+ return comphelper::getSomethingImpl(rId, this);
+}
+
+void ThumbnailViewAcc::disposing(std::unique_lock<std::mutex>& rGuard)
+{
+ ::std::vector<uno::Reference<accessibility::XAccessibleEventListener> > aListenerListCopy;
+
+ // unlock because we need to take solar and the lock mutex in the correct order
+ rGuard.unlock();
+ {
+ const SolarMutexGuard aSolarGuard;
+ std::unique_lock aGuard (m_aMutex);
+
+ // Reset the pointer to the parent. It has to be the one who has
+ // disposed us because he is dying.
+ mpParent = nullptr;
+
+ if (mxEventListeners.empty())
+ return;
+
+ // Make a copy of the list and clear the original.
+ aListenerListCopy = std::move(mxEventListeners);
+ }
+
+ // Inform all listeners that this objects is disposing.
+ lang::EventObject aEvent (static_cast<accessibility::XAccessible*>(this));
+ for (auto const& listener : aListenerListCopy)
+ {
+ try
+ {
+ listener->disposing (aEvent);
+ }
+ catch(const uno::Exception&)
+ {
+ // Ignore exceptions.
+ }
+ }
+}
+
+sal_uInt16 ThumbnailViewAcc::getItemCount() const
+{
+ return mpParent->ImplGetVisibleItemCount();
+}
+
+ThumbnailViewItem* ThumbnailViewAcc::getItem (sal_uInt16 nIndex) const
+{
+ return mpParent->ImplGetVisibleItem (nIndex);
+}
+
+void ThumbnailViewAcc::ThrowIfDisposed()
+{
+ if (m_bDisposed)
+ {
+ SAL_WARN("sfx", "Calling disposed object. Throwing exception:");
+ throw lang::DisposedException (
+ "object has been already disposed",
+ static_cast<uno::XWeak*>(this));
+ }
+ else
+ {
+ DBG_ASSERT (mpParent!=nullptr, "ValueSetAcc not disposed but mpParent == NULL");
+ }
+}
+
+ThumbnailViewItemAcc::ThumbnailViewItemAcc( ThumbnailViewItem* pParent, bool bIsTransientChildrenDisabled ) :
+ mpParent( pParent ),
+ mbIsTransientChildrenDisabled( bIsTransientChildrenDisabled )
+{
+}
+
+ThumbnailViewItemAcc::~ThumbnailViewItemAcc()
+{
+}
+
+void ThumbnailViewItemAcc::ParentDestroyed()
+{
+ std::scoped_lock aGuard( maMutex );
+ mpParent = nullptr;
+}
+
+void ThumbnailViewAcc::FireAccessibleEvent( short nEventId, const uno::Any& rOldValue, const uno::Any& rNewValue )
+{
+ if( !nEventId )
+ return;
+
+ ::std::vector< uno::Reference< accessibility::XAccessibleEventListener > > aTmpListeners( mxEventListeners );
+ accessibility::AccessibleEventObject aEvtObject;
+
+ aEvtObject.EventId = nEventId;
+ aEvtObject.Source = static_cast<uno::XWeak*>(this);
+ aEvtObject.NewValue = rNewValue;
+ aEvtObject.OldValue = rOldValue;
+
+ for (auto const& tmpListener : aTmpListeners)
+ {
+ try
+ {
+ tmpListener->notifyEvent( aEvtObject );
+ }
+ catch(const uno::Exception&)
+ {
+ }
+ }
+}
+
+const uno::Sequence< sal_Int8 >& ThumbnailViewItemAcc::getUnoTunnelId()
+{
+ static const comphelper::UnoIdInit theValueItemAccUnoTunnelId;
+ return theValueItemAccUnoTunnelId.getSeq();
+}
+
+ThumbnailViewItemAcc* ThumbnailViewItemAcc::getImplementation( const uno::Reference< uno::XInterface >& rxData )
+ noexcept
+{
+ try
+ {
+ return comphelper::getFromUnoTunnel<ThumbnailViewItemAcc>(rxData);
+ }
+ catch(const css::uno::Exception&)
+ {
+ return nullptr;
+ }
+}
+
+void ThumbnailViewAcc::GetFocus()
+{
+ // Broadcast the state change.
+ css::uno::Any aOldState, aNewState;
+ aNewState <<= css::accessibility::AccessibleStateType::FOCUSED;
+ FireAccessibleEvent(
+ css::accessibility::AccessibleEventId::STATE_CHANGED,
+ aOldState, aNewState);
+}
+
+void ThumbnailViewAcc::LoseFocus()
+{
+ // Broadcast the state change.
+ css::uno::Any aOldState, aNewState;
+ aOldState <<= css::accessibility::AccessibleStateType::FOCUSED;
+ FireAccessibleEvent(
+ css::accessibility::AccessibleEventId::STATE_CHANGED,
+ aOldState, aNewState);
+}
+
+uno::Reference< accessibility::XAccessibleContext > SAL_CALL ThumbnailViewItemAcc::getAccessibleContext()
+{
+ return this;
+}
+
+sal_Int32 SAL_CALL ThumbnailViewItemAcc::getAccessibleChildCount()
+{
+ return 0;
+}
+
+uno::Reference< accessibility::XAccessible > SAL_CALL ThumbnailViewItemAcc::getAccessibleChild( sal_Int32 )
+{
+ throw lang::IndexOutOfBoundsException();
+}
+
+uno::Reference< accessibility::XAccessible > SAL_CALL ThumbnailViewItemAcc::getAccessibleParent()
+{
+ const SolarMutexGuard aSolarGuard;
+ uno::Reference< accessibility::XAccessible > xRet;
+
+ if( mpParent )
+ xRet = mpParent->mrParent.getAccessible();
+
+ return xRet;
+}
+
+sal_Int32 SAL_CALL ThumbnailViewItemAcc::getAccessibleIndexInParent()
+{
+ const SolarMutexGuard aSolarGuard;
+ // The index defaults to -1 to indicate the child does not belong to its
+ // parent.
+ sal_Int32 nIndexInParent = -1;
+
+ if( mpParent )
+ {
+ bool bDone = false;
+
+ sal_uInt16 nCount = mpParent->mrParent.ImplGetVisibleItemCount();
+ ThumbnailViewItem* pItem;
+ for (sal_uInt16 i=0; i<nCount && !bDone; i++)
+ {
+ // Guard the retrieval of the i-th child with a try/catch block
+ // just in case the number of children changes in the meantime.
+ try
+ {
+ pItem = mpParent->mrParent.ImplGetVisibleItem (i);
+ }
+ catch (const lang::IndexOutOfBoundsException&)
+ {
+ pItem = nullptr;
+ }
+
+ // Do not create an accessible object for the test.
+ if (pItem != nullptr && pItem->mxAcc.is())
+ if (pItem->GetAccessible( mbIsTransientChildrenDisabled ).get() == this )
+ {
+ nIndexInParent = i;
+ bDone = true;
+ }
+ }
+ }
+
+ return nIndexInParent;
+}
+
+sal_Int16 SAL_CALL ThumbnailViewItemAcc::getAccessibleRole()
+{
+ return accessibility::AccessibleRole::LIST_ITEM;
+}
+
+OUString SAL_CALL ThumbnailViewItemAcc::getAccessibleDescription()
+{
+ return OUString();
+}
+
+OUString SAL_CALL ThumbnailViewItemAcc::getAccessibleName()
+{
+ const SolarMutexGuard aSolarGuard;
+ OUString aRet;
+
+ if( mpParent )
+ {
+ aRet = mpParent->maTitle;
+
+ if( aRet.isEmpty() )
+ {
+ aRet = "Item " + OUString::number(static_cast<sal_Int32>(mpParent->mnId));
+ }
+ }
+
+ return aRet;
+}
+
+uno::Reference< accessibility::XAccessibleRelationSet > SAL_CALL ThumbnailViewItemAcc::getAccessibleRelationSet()
+{
+ return uno::Reference< accessibility::XAccessibleRelationSet >();
+}
+
+uno::Reference< accessibility::XAccessibleStateSet > SAL_CALL ThumbnailViewItemAcc::getAccessibleStateSet()
+{
+ const SolarMutexGuard aSolarGuard;
+ rtl::Reference<::utl::AccessibleStateSetHelper> pStateSet = new ::utl::AccessibleStateSetHelper;
+
+ if( mpParent )
+ {
+ pStateSet->AddState (accessibility::AccessibleStateType::ENABLED);
+ pStateSet->AddState (accessibility::AccessibleStateType::SENSITIVE);
+ pStateSet->AddState (accessibility::AccessibleStateType::SHOWING);
+ pStateSet->AddState (accessibility::AccessibleStateType::VISIBLE);
+ if ( !mbIsTransientChildrenDisabled )
+ pStateSet->AddState (accessibility::AccessibleStateType::TRANSIENT);
+
+ // SELECTABLE
+ pStateSet->AddState( accessibility::AccessibleStateType::SELECTABLE );
+ // pStateSet->AddState( accessibility::AccessibleStateType::FOCUSABLE );
+
+ // SELECTED
+ if( mpParent->isSelected() )
+ {
+ pStateSet->AddState( accessibility::AccessibleStateType::SELECTED );
+ // pStateSet->AddState( accessibility::AccessibleStateType::FOCUSED );
+ }
+ }
+
+ return pStateSet;
+}
+
+lang::Locale SAL_CALL ThumbnailViewItemAcc::getLocale()
+{
+ const SolarMutexGuard aSolarGuard;
+ uno::Reference< accessibility::XAccessible > xParent( getAccessibleParent() );
+ lang::Locale aRet( "", "", "" );
+
+ if( xParent.is() )
+ {
+ uno::Reference< accessibility::XAccessibleContext > xParentContext( xParent->getAccessibleContext() );
+
+ if( xParentContext.is() )
+ aRet = xParentContext->getLocale();
+ }
+
+ return aRet;
+}
+
+void SAL_CALL ThumbnailViewItemAcc::addAccessibleEventListener( const uno::Reference< accessibility::XAccessibleEventListener >& rxListener )
+{
+ std::scoped_lock aGuard( maMutex );
+
+ if( !rxListener.is() )
+ return;
+
+ bool bFound = false;
+
+ for (auto const& eventListener : mxEventListeners)
+ {
+ if( eventListener == rxListener )
+ {
+ bFound = true;
+ break;
+ }
+ }
+
+ if (!bFound)
+ mxEventListeners.push_back( rxListener );
+}
+
+void SAL_CALL ThumbnailViewItemAcc::removeAccessibleEventListener( const uno::Reference< accessibility::XAccessibleEventListener >& rxListener )
+{
+ std::scoped_lock aGuard( maMutex );
+
+ if( rxListener.is() )
+ {
+ std::vector< uno::Reference< accessibility::XAccessibleEventListener > >::iterator aIter =
+ std::find(mxEventListeners.begin(), mxEventListeners.end(), rxListener);
+
+ if (aIter != mxEventListeners.end())
+ mxEventListeners.erase( aIter );
+ }
+}
+
+sal_Bool SAL_CALL ThumbnailViewItemAcc::containsPoint( const awt::Point& aPoint )
+{
+ const awt::Rectangle aRect( getBounds() );
+ const Point aSize( aRect.Width, aRect.Height );
+ const Point aNullPoint, aTestPoint( aPoint.X, aPoint.Y );
+
+ return tools::Rectangle( aNullPoint, aSize ).Contains( aTestPoint );
+}
+
+uno::Reference< accessibility::XAccessible > SAL_CALL ThumbnailViewItemAcc::getAccessibleAtPoint( const awt::Point& )
+{
+ uno::Reference< accessibility::XAccessible > xRet;
+ return xRet;
+}
+
+awt::Rectangle SAL_CALL ThumbnailViewItemAcc::getBounds()
+{
+ const SolarMutexGuard aSolarGuard;
+ awt::Rectangle aRet;
+
+ if( mpParent )
+ {
+ tools::Rectangle aRect( mpParent->getDrawArea() );
+ tools::Rectangle aParentRect;
+
+ // get position of the accessible parent in screen coordinates
+ uno::Reference< XAccessible > xParent = getAccessibleParent();
+ if ( xParent.is() )
+ {
+ uno::Reference<XAccessibleComponent> xParentComponent(xParent->getAccessibleContext(), uno::UNO_QUERY);
+ if (xParentComponent.is())
+ {
+ awt::Size aParentSize = xParentComponent->getSize();
+ aParentRect = tools::Rectangle(0, 0, aParentSize.Width, aParentSize.Height);
+ }
+ }
+
+ aRect.Intersection( aParentRect );
+
+ aRet.X = aRect.Left();
+ aRet.Y = aRect.Top();
+ aRet.Width = aRect.GetWidth();
+ aRet.Height = aRect.GetHeight();
+ }
+
+ return aRet;
+}
+
+awt::Point SAL_CALL ThumbnailViewItemAcc::getLocation()
+{
+ const awt::Rectangle aRect( getBounds() );
+ awt::Point aRet;
+
+ aRet.X = aRect.X;
+ aRet.Y = aRect.Y;
+
+ return aRet;
+}
+
+// get position of the accessible parent in screen coordinates
+awt::Point SAL_CALL ThumbnailViewItemAcc::getLocationOnScreen()
+{
+ const SolarMutexGuard aSolarGuard;
+ awt::Point aRet;
+
+ if (mpParent)
+ {
+ const Point aPos = mpParent->getDrawArea().TopLeft();
+ const Point aScreenPos(mpParent->mrParent.GetDrawingArea()->get_accessible_location_on_screen());
+
+ aRet.X = aPos.X() + aScreenPos.X();
+ aRet.Y = aPos.Y() + aScreenPos.X();
+ }
+
+ return aRet;
+}
+
+awt::Size SAL_CALL ThumbnailViewItemAcc::getSize()
+{
+ const awt::Rectangle aRect( getBounds() );
+ awt::Size aRet;
+
+ aRet.Width = aRect.Width;
+ aRet.Height = aRect.Height;
+
+ return aRet;
+}
+
+void SAL_CALL ThumbnailViewItemAcc::grabFocus()
+{
+ // nothing to do
+}
+
+sal_Int32 SAL_CALL ThumbnailViewItemAcc::getForeground( )
+{
+ Color nColor = Application::GetSettings().GetStyleSettings().GetWindowTextColor();
+ return static_cast<sal_Int32>(nColor);
+}
+
+sal_Int32 SAL_CALL ThumbnailViewItemAcc::getBackground( )
+{
+ return static_cast<sal_Int32>(Application::GetSettings().GetStyleSettings().GetWindowColor());
+}
+
+sal_Int64 SAL_CALL ThumbnailViewItemAcc::getSomething( const uno::Sequence< sal_Int8 >& rId )
+{
+ return comphelper::getSomethingImpl(rId, this);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/control/thumbnailviewacc.hxx b/sfx2/source/control/thumbnailviewacc.hxx
new file mode 100644
index 000000000..17ff4b3fe
--- /dev/null
+++ b/sfx2/source/control/thumbnailviewacc.hxx
@@ -0,0 +1,215 @@
+/* -*- 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_CONTROL_THUMBNAILVIEWACC_HXX
+#define INCLUDED_SFX2_SOURCE_CONTROL_THUMBNAILVIEWACC_HXX
+
+#include <cppuhelper/implbase.hxx>
+#include <comphelper/compbase.hxx>
+
+#include <com/sun/star/lang/XUnoTunnel.hpp>
+#include <com/sun/star/accessibility/XAccessible.hpp>
+#include <com/sun/star/accessibility/XAccessibleContext.hpp>
+#include <com/sun/star/accessibility/XAccessibleComponent.hpp>
+#include <com/sun/star/accessibility/XAccessibleSelection.hpp>
+#include <com/sun/star/accessibility/XAccessibleEventBroadcaster.hpp>
+
+#include <vector>
+
+class ThumbnailView;
+class ThumbnailViewItem;
+
+typedef comphelper::WeakComponentImplHelper<
+ css::accessibility::XAccessible,
+ css::accessibility::XAccessibleEventBroadcaster,
+ css::accessibility::XAccessibleContext,
+ css::accessibility::XAccessibleComponent,
+ css::accessibility::XAccessibleSelection,
+ css::lang::XUnoTunnel >
+ ValueSetAccComponentBase;
+
+class ThumbnailViewAcc :
+ public ValueSetAccComponentBase
+{
+public:
+
+ ThumbnailViewAcc( ThumbnailView* pParent );
+ virtual ~ThumbnailViewAcc() override;
+
+ void FireAccessibleEvent( short nEventId,
+ const css::uno::Any& rOldValue,
+ const css::uno::Any& rNewValue );
+
+ bool HasAccessibleListeners() const { return( mxEventListeners.size() > 0 ); }
+
+ static ThumbnailViewAcc* getImplementation( const css::uno::Reference< css::uno::XInterface >& rxData ) noexcept;
+
+public:
+ /** Called by the corresponding ValueSet when it gets the focus.
+ Stores the new focus state and broadcasts a state change event.
+ */
+ void GetFocus();
+
+ /** Called by the corresponding ValueSet when it loses the focus.
+ Stores the new focus state and broadcasts a state change event.
+ */
+ void LoseFocus();
+
+ // XAccessible
+ virtual css::uno::Reference< css::accessibility::XAccessibleContext > SAL_CALL getAccessibleContext( ) override;
+
+ // XAccessibleEventBroadcaster
+ virtual void SAL_CALL addAccessibleEventListener( const css::uno::Reference< css::accessibility::XAccessibleEventListener >& xListener ) override;
+ virtual void SAL_CALL removeAccessibleEventListener( const css::uno::Reference< css::accessibility::XAccessibleEventListener >& xListener ) override;
+
+ // XAccessibleContext
+ virtual sal_Int32 SAL_CALL getAccessibleChildCount( ) override;
+ virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getAccessibleChild( sal_Int32 i ) override;
+ virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getAccessibleParent( ) override;
+ virtual sal_Int32 SAL_CALL getAccessibleIndexInParent( ) override;
+ virtual sal_Int16 SAL_CALL getAccessibleRole( ) override;
+ virtual OUString SAL_CALL getAccessibleDescription( ) override;
+ virtual OUString SAL_CALL getAccessibleName( ) override;
+ virtual css::uno::Reference< css::accessibility::XAccessibleRelationSet > SAL_CALL getAccessibleRelationSet( ) override;
+ virtual css::uno::Reference< css::accessibility::XAccessibleStateSet > SAL_CALL getAccessibleStateSet( ) override;
+ virtual css::lang::Locale SAL_CALL getLocale( ) override;
+
+ // XAccessibleComponent
+ virtual sal_Bool SAL_CALL containsPoint( const css::awt::Point& aPoint ) override;
+ virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getAccessibleAtPoint( const css::awt::Point& aPoint ) override;
+ virtual css::awt::Rectangle SAL_CALL getBounds( ) override;
+ virtual css::awt::Point SAL_CALL getLocation( ) override;
+ virtual css::awt::Point SAL_CALL getLocationOnScreen( ) override;
+ virtual css::awt::Size SAL_CALL getSize( ) override;
+ virtual void SAL_CALL grabFocus( ) override;
+ virtual sal_Int32 SAL_CALL getForeground( ) override;
+ virtual sal_Int32 SAL_CALL getBackground( ) override;
+
+ // XAccessibleSelection
+ virtual void SAL_CALL selectAccessibleChild( sal_Int32 nChildIndex ) override;
+ virtual sal_Bool SAL_CALL isAccessibleChildSelected( sal_Int32 nChildIndex ) override;
+ virtual void SAL_CALL clearAccessibleSelection( ) override;
+ virtual void SAL_CALL selectAllAccessibleChildren( ) override;
+ virtual sal_Int32 SAL_CALL getSelectedAccessibleChildCount( ) override;
+ virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getSelectedAccessibleChild( sal_Int32 nSelectedChildIndex ) override;
+ virtual void SAL_CALL deselectAccessibleChild( sal_Int32 nSelectedChildIndex ) override;
+
+ // XUnoTunnel
+ static const css::uno::Sequence< sal_Int8 >& getUnoTunnelId();
+ virtual sal_Int64 SAL_CALL getSomething( const css::uno::Sequence< sal_Int8 >& rId ) override;
+
+private:
+ ::std::vector< css::uno::Reference<
+ css::accessibility::XAccessibleEventListener > > mxEventListeners;
+ ThumbnailView* mpParent;
+
+ /** Tell all listeners that the object is dying. This callback is
+ usually called from the WeakComponentImplHelper class.
+ */
+ virtual void disposing(std::unique_lock<std::mutex>&) override;
+
+ /** Return the number of items. This takes the None-Item into account.
+ */
+ sal_uInt16 getItemCount() const;
+
+ /** Return the item associated with the given index. The None-Item is
+ taken into account which, when present, is taken to be the first
+ (with index 0) item.
+ @param nIndex
+ Index of the item to return. The index 0 denotes the None-Item
+ when present.
+ @return
+ Returns NULL when the given index is out of range.
+ */
+ ThumbnailViewItem* getItem (sal_uInt16 nIndex) const;
+
+ /** Check whether or not the object has been disposed (or is in the
+ state of being disposed). If that is the case then
+ DisposedException is thrown to inform the (indirect) caller of the
+ foul deed.
+
+ @throws css::lang::DisposedException
+ */
+ void ThrowIfDisposed();
+};
+
+
+class ThumbnailViewItemAcc : public ::cppu::WeakImplHelper< css::accessibility::XAccessible,
+ css::accessibility::XAccessibleEventBroadcaster,
+ css::accessibility::XAccessibleContext,
+ css::accessibility::XAccessibleComponent,
+ css::lang::XUnoTunnel >
+{
+private:
+
+ ::std::vector< css::uno::Reference< css::accessibility::XAccessibleEventListener > >
+ mxEventListeners;
+ std::mutex maMutex;
+ ThumbnailViewItem* mpParent;
+ bool mbIsTransientChildrenDisabled;
+
+public:
+
+ ThumbnailViewItemAcc( ThumbnailViewItem* pParent, bool bIsTransientChildrenDisabled );
+ virtual ~ThumbnailViewItemAcc() override;
+
+ void ParentDestroyed();
+
+ static ThumbnailViewItemAcc* getImplementation( const css::uno::Reference< css::uno::XInterface >& rxData ) noexcept;
+
+public:
+
+ // XAccessible
+ virtual css::uno::Reference< css::accessibility::XAccessibleContext > SAL_CALL getAccessibleContext( ) override;
+
+ // XAccessibleEventBroadcaster
+ virtual void SAL_CALL addAccessibleEventListener( const css::uno::Reference< css::accessibility::XAccessibleEventListener >& xListener ) override;
+ virtual void SAL_CALL removeAccessibleEventListener( const css::uno::Reference< css::accessibility::XAccessibleEventListener >& xListener ) override;
+
+ // XAccessibleContext
+ virtual sal_Int32 SAL_CALL getAccessibleChildCount( ) override;
+ virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getAccessibleChild( sal_Int32 i ) override;
+ virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getAccessibleParent( ) override;
+ virtual sal_Int32 SAL_CALL getAccessibleIndexInParent( ) override;
+ virtual sal_Int16 SAL_CALL getAccessibleRole( ) override;
+ virtual OUString SAL_CALL getAccessibleDescription( ) override;
+ virtual OUString SAL_CALL getAccessibleName( ) override;
+ virtual css::uno::Reference< css::accessibility::XAccessibleRelationSet > SAL_CALL getAccessibleRelationSet( ) override;
+ virtual css::uno::Reference< css::accessibility::XAccessibleStateSet > SAL_CALL getAccessibleStateSet( ) override;
+ virtual css::lang::Locale SAL_CALL getLocale( ) override;
+
+ // XAccessibleComponent
+ virtual sal_Bool SAL_CALL containsPoint( const css::awt::Point& aPoint ) override;
+ virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getAccessibleAtPoint( const css::awt::Point& aPoint ) override;
+ virtual css::awt::Rectangle SAL_CALL getBounds( ) override;
+ virtual css::awt::Point SAL_CALL getLocation( ) override;
+ virtual css::awt::Point SAL_CALL getLocationOnScreen( ) override;
+ virtual css::awt::Size SAL_CALL getSize( ) override;
+ virtual void SAL_CALL grabFocus( ) override;
+ virtual sal_Int32 SAL_CALL getForeground( ) override;
+ virtual sal_Int32 SAL_CALL getBackground( ) override;
+
+ // XUnoTunnel
+ static const css::uno::Sequence< sal_Int8 >& getUnoTunnelId();
+ virtual sal_Int64 SAL_CALL getSomething( const css::uno::Sequence< sal_Int8 >& rId ) override;
+};
+
+#endif // INCLUDED_SFX2_SOURCE_CONTROL_THUMBNAILVIEWACC_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/control/thumbnailviewitem.cxx b/sfx2/source/control/thumbnailviewitem.cxx
new file mode 100644
index 000000000..5562acc1e
--- /dev/null
+++ b/sfx2/source/control/thumbnailviewitem.cxx
@@ -0,0 +1,314 @@
+/* -*- 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/thumbnailviewitem.hxx>
+#include <sfx2/thumbnailview.hxx>
+#include "thumbnailviewacc.hxx"
+
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <drawinglayer/attribute/fillgraphicattribute.hxx>
+#include <drawinglayer/attribute/fontattribute.hxx>
+#include <drawinglayer/attribute/lineattribute.hxx>
+#include <drawinglayer/primitive2d/fillgraphicprimitive2d.hxx>
+#include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonSelectionPrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolygonStrokePrimitive2D.hxx>
+#include <drawinglayer/primitive2d/textlayoutdevice.hxx>
+#include <drawinglayer/primitive2d/textprimitive2d.hxx>
+#include <drawinglayer/processor2d/baseprocessor2d.hxx>
+#include <vcl/graph.hxx>
+#include <vcl/outdev.hxx>
+#include <vcl/texteng.hxx>
+
+using namespace basegfx;
+using namespace basegfx::utils;
+using namespace ::com::sun::star;
+using namespace drawinglayer::attribute;
+using namespace drawinglayer::primitive2d;
+
+ThumbnailViewItem::ThumbnailViewItem(ThumbnailView& rView, sal_uInt16 nId)
+ : mrParent(rView)
+ , mnId(nId)
+ , mbVisible(true)
+ , mbBorder(true)
+ , mbSelected(false)
+ , mbHover(false)
+{
+}
+
+ThumbnailViewItem::~ThumbnailViewItem()
+{
+ if( mxAcc.is() )
+ {
+ static_cast< ThumbnailViewItemAcc* >( mxAcc.get() )->ParentDestroyed();
+ }
+}
+
+void ThumbnailViewItem::show (bool bVisible)
+{
+ mbVisible = bVisible;
+}
+
+void ThumbnailViewItem::setSelection (bool state)
+{
+ mbSelected = state;
+}
+
+void ThumbnailViewItem::setHighlight (bool state)
+{
+ mbHover = state;
+}
+
+::tools::Rectangle ThumbnailViewItem::updateHighlight(bool bVisible, const Point& rPoint)
+{
+ bool bNeedsPaint = false;
+
+ if (bVisible && getDrawArea().Contains(rPoint))
+ {
+ if (!isHighlighted())
+ bNeedsPaint = true;
+ setHighlight(true);
+ }
+ else
+ {
+ if (isHighlighted())
+ bNeedsPaint = true;
+ setHighlight(false);
+ }
+
+ if (bNeedsPaint)
+ return getDrawArea();
+
+ return ::tools::Rectangle();
+}
+
+void ThumbnailViewItem::setTitle (const OUString& rTitle)
+{
+ if (mrParent.renameItem(this, rTitle))
+ maTitle = rTitle;
+}
+
+uno::Reference< accessibility::XAccessible > const & ThumbnailViewItem::GetAccessible( bool bIsTransientChildrenDisabled )
+{
+ if( !mxAcc.is() )
+ mxAcc = new ThumbnailViewItemAcc( this, bIsTransientChildrenDisabled );
+
+ return mxAcc;
+}
+
+void ThumbnailViewItem::setDrawArea (const ::tools::Rectangle &area)
+{
+ maDrawArea = area;
+}
+
+void ThumbnailViewItem::calculateItemsPosition (const tools::Long nThumbnailHeight,
+ const tools::Long nPadding, sal_uInt32 nMaxTextLength,
+ const ThumbnailItemAttributes *pAttrs)
+{
+ drawinglayer::primitive2d::TextLayouterDevice aTextDev;
+ aTextDev.setFontAttribute(pAttrs->aFontAttr,
+ pAttrs->aFontSize.getX(), pAttrs->aFontSize.getY(),
+ css::lang::Locale() );
+
+ Size aImageSize = maPreview1.GetSizePixel();
+
+ // Calculate thumbnail position
+ const Point aPos = maDrawArea.TopCenter();
+ maPrev1Pos = aPos + Point(-aImageSize.Width() / 2, nPadding + (nThumbnailHeight - aImageSize.Height()) / 2);
+
+ // Calculate text position
+ maTextPos = aPos + Point(-aTextDev.getTextWidth(maTitle, 0, nMaxTextLength) / 2, nThumbnailHeight + nPadding * 2);
+}
+
+void ThumbnailViewItem::Paint (drawinglayer::processor2d::BaseProcessor2D *pProcessor,
+ const ThumbnailItemAttributes *pAttrs)
+{
+ BColor aFillColor = pAttrs->aFillColor;
+ drawinglayer::primitive2d::Primitive2DContainer aSeq(4);
+ double fTransparence = 0.0;
+
+ // Draw background
+ if( mbSelected && mbHover)
+ aFillColor = pAttrs->aSelectHighlightColor;
+ else if (mbSelected || mbHover)
+ {
+ aFillColor = pAttrs->aHighlightColor;
+ if (mbHover)
+ fTransparence = pAttrs->fHighlightTransparence;
+ }
+
+ sal_uInt32 nPrimitive = 0;
+ aSeq[nPrimitive++] = drawinglayer::primitive2d::Primitive2DReference(
+ new PolyPolygonSelectionPrimitive2D( B2DPolyPolygon(::tools::Polygon(maDrawArea, THUMBNAILVIEW_ITEM_CORNER, THUMBNAILVIEW_ITEM_CORNER).getB2DPolygon()),
+ aFillColor,
+ fTransparence,
+ 0.0,
+ true));
+
+ // Draw thumbnail
+ Point aPos = maPrev1Pos;
+ Size aImageSize = maPreview1.GetSizePixel();
+
+ aSeq[nPrimitive++] = drawinglayer::primitive2d::Primitive2DReference( new FillGraphicPrimitive2D(
+ createTranslateB2DHomMatrix(aPos.X(),aPos.Y()),
+ FillGraphicAttribute(Graphic(maPreview1),
+ B2DRange(
+ B2DPoint(0,0),
+ B2DPoint(aImageSize.Width(),aImageSize.Height())),
+ false)
+ ));
+
+ if (mbBorder)
+ {
+ // draw thumbnail borders
+ float fWidth = aImageSize.Width() - 1;
+ float fHeight = aImageSize.Height() - 1;
+ float fPosX = maPrev1Pos.getX();
+ float fPosY = maPrev1Pos.getY();
+
+ B2DPolygon aBounds;
+ aBounds.append(B2DPoint(fPosX,fPosY));
+ aBounds.append(B2DPoint(fPosX+fWidth,fPosY));
+ aBounds.append(B2DPoint(fPosX+fWidth,fPosY+fHeight));
+ aBounds.append(B2DPoint(fPosX,fPosY+fHeight));
+ aBounds.setClosed(true);
+
+ aSeq[nPrimitive++] = drawinglayer::primitive2d::Primitive2DReference(createBorderLine(aBounds));
+ }
+
+ // Draw text below thumbnail
+ addTextPrimitives(maTitle, pAttrs, maTextPos, aSeq);
+
+ pProcessor->process(aSeq);
+}
+
+void ThumbnailViewItem::addTextPrimitives (const OUString& rText, const ThumbnailItemAttributes *pAttrs, Point aPos, drawinglayer::primitive2d::Primitive2DContainer& rSeq)
+{
+ // adjust text drawing position according to text font
+ drawinglayer::primitive2d::TextLayouterDevice aTextDev;
+ aTextDev.setFontAttribute(
+ pAttrs->aFontAttr,
+ pAttrs->aFontSize.getX(),
+ pAttrs->aFontSize.getY(),
+ css::lang::Locale());
+
+ aPos.setY(aPos.getY() + aTextDev.getTextHeight());
+
+ sal_Int32 nMnemonicPos = -1;
+ OUString aOrigText(mrParent.isDrawMnemonic() ? OutputDevice::GetNonMnemonicString(rText, nMnemonicPos) : rText);
+
+ TextEngine aTextEngine;
+ aTextEngine.SetFont(getVclFontFromFontAttribute(pAttrs->aFontAttr,
+ pAttrs->aFontSize.getX(), pAttrs->aFontSize.getY(), 0,
+ css::lang::Locale()));
+ aTextEngine.SetMaxTextWidth(maDrawArea.getWidth());
+ aTextEngine.SetText(aOrigText);
+
+ sal_Int32 nPrimitives = rSeq.size();
+ sal_Int32 nFinalPrimCount = nPrimitives + aTextEngine.GetLineCount(0);
+ rSeq.resize(nFinalPrimCount);
+
+ // Create the text primitives
+ sal_uInt16 nLineStart = 0;
+ OUString aText(aOrigText);
+ for (sal_uInt16 i=0; i < aTextEngine.GetLineCount(0); ++i)
+ {
+ sal_Int32 nLineLength = aTextEngine.GetLineLen(0, i);
+ double nLineWidth = aTextDev.getTextWidth (aText, nLineStart, nLineLength);
+
+ bool bTooLong = (aPos.getY() + aTextEngine.GetCharHeight()) > maDrawArea.Bottom();
+ if (bTooLong && (nLineLength + nLineStart) < aOrigText.getLength())
+ {
+ // Add the '...' to the last line to show, even though it may require to shorten the line
+ double nDotsWidth = aTextDev.getTextWidth("...",0,3);
+
+ sal_Int32 nLength = nLineLength - 1;
+ while ( nDotsWidth + aTextDev.getTextWidth(aText, nLineStart, nLength) > maDrawArea.getWidth() && nLength > 0)
+ {
+ --nLength;
+ }
+
+ aText = OUString::Concat(aText.subView(0, nLineStart+nLength)) + "...";
+ nLineLength = nLength + 3;
+ }
+
+ double nLineX = maDrawArea.Left() + (maDrawArea.getWidth() - nLineWidth) / 2.0;
+
+ basegfx::B2DHomMatrix aTextMatrix( createScaleTranslateB2DHomMatrix(
+ pAttrs->aFontSize.getX(), pAttrs->aFontSize.getY(),
+ nLineX, double( aPos.Y() ) ) );
+
+ // setup color
+ BColor aTextColor = pAttrs->aTextColor;
+ if(mbSelected)
+ {
+ if (mbHover)
+ aTextColor = pAttrs->aSelectHighlightTextColor;
+ else
+ aTextColor = pAttrs->aHighlightTextColor;
+ }
+
+ rSeq[nPrimitives++] = drawinglayer::primitive2d::Primitive2DReference(
+ new TextSimplePortionPrimitive2D(aTextMatrix,
+ aText, nLineStart, nLineLength,
+ std::vector<double>(),
+ pAttrs->aFontAttr,
+ css::lang::Locale(),
+ aTextColor));
+
+ if (nMnemonicPos != -1 && nMnemonicPos >= nLineStart && nMnemonicPos < nLineStart + nLineLength)
+ {
+ rSeq.resize(nFinalPrimCount + 1);
+
+ auto aCaretPositions = aTextDev.getCaretPositions(aText, nLineStart, nLineLength);
+
+ auto lc_x1 = aCaretPositions[2*(nMnemonicPos - nLineStart)];
+ auto lc_x2 = aCaretPositions[2*(nMnemonicPos - nLineStart)+1];
+ auto fMnemonicWidth = std::abs(lc_x1 - lc_x2);
+ auto fMnemonicHeight = aTextDev.getUnderlineHeight();
+
+ auto fPosX = nLineX + std::min(lc_x1, lc_x2);
+ auto fPosY = aPos.Y() + aTextDev.getUnderlineOffset();
+
+ B2DPolygon aLine;
+ aLine.append(B2DPoint(fPosX, fPosY));
+ aLine.append(B2DPoint(fPosX + fMnemonicWidth, fPosY));
+
+ drawinglayer::attribute::LineAttribute aLineAttribute(Color(aTextColor).getBColor(), fMnemonicHeight);
+
+ rSeq[nPrimitives++] = drawinglayer::primitive2d::Primitive2DReference(
+ new PolygonStrokePrimitive2D(aLine, aLineAttribute));
+ }
+
+ nLineStart += nLineLength;
+ aPos.setY(aPos.getY() + aTextEngine.GetCharHeight());
+
+ if (bTooLong)
+ break;
+ }
+}
+
+rtl::Reference<drawinglayer::primitive2d::PolygonHairlinePrimitive2D>
+ThumbnailViewItem::createBorderLine (const basegfx::B2DPolygon& rPolygon)
+{
+ return new PolygonHairlinePrimitive2D(rPolygon, Color(128, 128, 128).getBColor());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/control/unoctitm.cxx b/sfx2/source/control/unoctitm.cxx
new file mode 100644
index 000000000..57ef3404b
--- /dev/null
+++ b/sfx2/source/control/unoctitm.cxx
@@ -0,0 +1,1285 @@
+/* -*- 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 <config_java.h>
+
+#include <tools/debug.hxx>
+#include <svl/eitem.hxx>
+#include <svl/intitem.hxx>
+#include <svl/itemset.hxx>
+#include <svl/visitem.hxx>
+#include <svtools/javacontext.hxx>
+#include <svtools/javainteractionhandler.hxx>
+#include <svl/itempool.hxx>
+#include <tools/urlobj.hxx>
+#include <com/sun/star/awt/FontDescriptor.hpp>
+#include <com/sun/star/awt/Point.hpp>
+#include <com/sun/star/awt/Size.hpp>
+#include <com/sun/star/util/URLTransformer.hpp>
+#include <com/sun/star/util/XURLTransformer.hpp>
+#include <com/sun/star/frame/XFrame.hpp>
+#include <com/sun/star/frame/status/FontHeight.hpp>
+#include <com/sun/star/frame/status/ItemStatus.hpp>
+#include <com/sun/star/frame/status/ItemState.hpp>
+#include <com/sun/star/frame/status/Template.hpp>
+#include <com/sun/star/frame/DispatchResultState.hpp>
+#include <com/sun/star/frame/status/Visibility.hpp>
+#include <comphelper/processfactory.hxx>
+#include <uno/current_context.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/uitest/logger.hxx>
+#include <boost/property_tree/json_parser.hpp>
+#include <tools/json_writer.hxx>
+
+#include <sfx2/app.hxx>
+#include <unoctitm.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/frame.hxx>
+#include <sfx2/ctrlitem.hxx>
+#include <sfx2/sfxuno.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/sfxsids.hrc>
+#include <sfx2/request.hxx>
+#include <sfx2/msg.hxx>
+#include <sfx2/viewsh.hxx>
+#include <slotserv.hxx>
+#include <rtl/ustring.hxx>
+#include <sfx2/lokhelper.hxx>
+
+#include <memory>
+#include <string_view>
+
+#include <sal/log.hxx>
+#include <LibreOfficeKit/LibreOfficeKitEnums.h>
+#include <comphelper/lok.hxx>
+#include <comphelper/servicehelper.hxx>
+
+#include <desktop/crashreport.hxx>
+#include <vcl/threadex.hxx>
+#include <unotools/mediadescriptor.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::util;
+
+namespace {
+
+enum URLTypeId
+{
+ URLType_BOOL,
+ URLType_BYTE,
+ URLType_SHORT,
+ URLType_LONG,
+ URLType_HYPER,
+ URLType_STRING,
+ URLType_FLOAT,
+ URLType_DOUBLE,
+ URLType_COUNT
+};
+
+}
+
+const char* const URLTypeNames[URLType_COUNT] =
+{
+ "bool",
+ "byte",
+ "short",
+ "long",
+ "hyper",
+ "string",
+ "float",
+ "double"
+};
+
+static void InterceptLOKStateChangeEvent( sal_uInt16 nSID, SfxViewFrame* pViewFrame, const css::frame::FeatureStateEvent& aEvent, const SfxPoolItem* pState );
+
+void SfxStatusDispatcher::ReleaseAll()
+{
+ css::lang::EventObject aObject;
+ aObject.Source = static_cast<cppu::OWeakObject*>(this);
+ std::unique_lock aGuard(maMutex);
+ maListeners.disposeAndClear( aGuard, aObject );
+}
+
+void SfxStatusDispatcher::sendStatusChanged(const OUString& rURL, const css::frame::FeatureStateEvent& rEvent)
+{
+ std::unique_lock aGuard(maMutex);
+ ::comphelper::OInterfaceContainerHelper4<css::frame::XStatusListener>* pContnr = maListeners.getContainer(rURL);
+ if (!pContnr)
+ return;
+ pContnr->forEach(aGuard,
+ [&rEvent](const css::uno::Reference<css::frame::XStatusListener>& xListener)
+ {
+ xListener->statusChanged(rEvent);
+ }
+ );
+}
+
+void SAL_CALL SfxStatusDispatcher::dispatch( const css::util::URL&, const css::uno::Sequence< css::beans::PropertyValue >& )
+{
+}
+
+void SAL_CALL SfxStatusDispatcher::dispatchWithNotification(
+ const css::util::URL&,
+ const css::uno::Sequence< css::beans::PropertyValue >&,
+ const css::uno::Reference< css::frame::XDispatchResultListener >& )
+{
+}
+
+SfxStatusDispatcher::SfxStatusDispatcher()
+{
+}
+
+void SAL_CALL SfxStatusDispatcher::addStatusListener(const css::uno::Reference< css::frame::XStatusListener > & aListener, const css::util::URL& aURL)
+{
+ {
+ std::unique_lock aGuard(maMutex);
+ maListeners.addInterface( aGuard, aURL.Complete, aListener );
+ }
+ if ( aURL.Complete == ".uno:LifeTime" )
+ {
+ css::frame::FeatureStateEvent aEvent;
+ aEvent.FeatureURL = aURL;
+ aEvent.Source = static_cast<css::frame::XDispatch*>(this);
+ aEvent.IsEnabled = true;
+ aEvent.Requery = false;
+ aListener->statusChanged( aEvent );
+ }
+}
+
+void SAL_CALL SfxStatusDispatcher::removeStatusListener( const css::uno::Reference< css::frame::XStatusListener > & aListener, const css::util::URL& aURL )
+{
+ std::unique_lock aGuard(maMutex);
+ maListeners.removeInterface( aGuard, aURL.Complete, aListener );
+}
+
+
+// XUnoTunnel
+sal_Int64 SAL_CALL SfxOfficeDispatch::getSomething( const css::uno::Sequence< sal_Int8 >& aIdentifier )
+{
+ return comphelper::getSomethingImpl(aIdentifier, this);
+}
+
+SfxOfficeDispatch::SfxOfficeDispatch( SfxBindings& rBindings, SfxDispatcher* pDispat, const SfxSlot* pSlot, const css::util::URL& rURL )
+ : pImpl( new SfxDispatchController_Impl( this, &rBindings, pDispat, pSlot, rURL ))
+{
+ // pImpl is an adapter that shows a css::frame::XDispatch-Interface to the outside and uses a SfxControllerItem to monitor a state
+
+}
+
+SfxOfficeDispatch::SfxOfficeDispatch( SfxDispatcher* pDispat, const SfxSlot* pSlot, const css::util::URL& rURL )
+ : pImpl( new SfxDispatchController_Impl( this, nullptr, pDispat, pSlot, rURL ))
+{
+ // pImpl is an adapter that shows a css::frame::XDispatch-Interface to the outside and uses a SfxControllerItem to monitor a state
+}
+
+SfxOfficeDispatch::~SfxOfficeDispatch()
+{
+ if ( pImpl )
+ {
+ // when dispatch object is released, destroy its connection to this object and destroy it
+ pImpl->UnBindController();
+
+ // Ensure that SfxDispatchController_Impl is deleted while the solar mutex is locked, since
+ // that derives from SfxListener.
+ SolarMutexGuard aGuard;
+ pImpl.reset();
+ }
+}
+
+const css::uno::Sequence< sal_Int8 >& SfxOfficeDispatch::getUnoTunnelId()
+{
+ // {38 57 CA 80 09 36 11 d4 83 FE 00 50 04 52 6B 21}
+ static const sal_uInt8 pGUID[16] = { 0x38, 0x57, 0xCA, 0x80, 0x09, 0x36, 0x11, 0xd4, 0x83, 0xFE, 0x00, 0x50, 0x04, 0x52, 0x6B, 0x21 };
+ static css::uno::Sequence< sal_Int8 > seqID(reinterpret_cast<const sal_Int8*>(pGUID), 16) ;
+ return seqID ;
+}
+
+#if HAVE_FEATURE_JAVA
+// The JavaContext contains an interaction handler which is used when
+// the creation of a Java Virtual Machine fails. There shall only be one
+// user notification (message box) even if the same error (interaction)
+// reoccurs. The effect is, that if a user selects a menu entry than they
+// may get only one notification that a JRE is not selected.
+// This function checks if a JavaContext is already available (typically
+// created by Desktop::Main() in app.cxx), and creates new one if not.
+namespace {
+std::unique_ptr< css::uno::ContextLayer > EnsureJavaContext()
+{
+ css::uno::Reference< css::uno::XCurrentContext > xContext(css::uno::getCurrentContext());
+ if (xContext.is())
+ {
+ css::uno::Reference< css::task::XInteractionHandler > xHandler;
+ xContext->getValueByName(JAVA_INTERACTION_HANDLER_NAME) >>= xHandler;
+ if (xHandler.is())
+ return nullptr; // No need to add new layer: JavaContext already present
+ }
+ return std::make_unique< css::uno::ContextLayer >(new svt::JavaContext(xContext));
+}
+}
+#endif
+
+void SAL_CALL SfxOfficeDispatch::dispatch( const css::util::URL& aURL, const css::uno::Sequence< css::beans::PropertyValue >& aArgs )
+{
+ // ControllerItem is the Impl class
+ if ( pImpl )
+ {
+#if HAVE_FEATURE_JAVA
+ std::unique_ptr< css::uno::ContextLayer > layer(EnsureJavaContext());
+#endif
+ utl::MediaDescriptor aDescriptor(aArgs);
+ bool bOnMainThread = aDescriptor.getUnpackedValueOrDefault("OnMainThread", false);
+ if (bOnMainThread)
+ {
+ // Make sure that we own the solar mutex, otherwise later
+ // vcl::SolarThreadExecutor::execute() will release the solar mutex, even if it's owned by
+ // an other thread, leading to an std::abort() at the end.
+ SolarMutexGuard aGuard;
+ vcl::solarthread::syncExecute([this, &aURL, &aArgs]() {
+ pImpl->dispatch(aURL, aArgs,
+ css::uno::Reference<css::frame::XDispatchResultListener>());
+ });
+ }
+ else
+ {
+ pImpl->dispatch(aURL, aArgs,
+ css::uno::Reference<css::frame::XDispatchResultListener>());
+ }
+ }
+}
+
+void SAL_CALL SfxOfficeDispatch::dispatchWithNotification( const css::util::URL& aURL,
+ const css::uno::Sequence< css::beans::PropertyValue >& aArgs,
+ const css::uno::Reference< css::frame::XDispatchResultListener >& rListener )
+{
+ // ControllerItem is the Impl class
+ if ( pImpl )
+ {
+#if HAVE_FEATURE_JAVA
+ std::unique_ptr< css::uno::ContextLayer > layer(EnsureJavaContext());
+#endif
+ pImpl->dispatch( aURL, aArgs, rListener );
+ }
+}
+
+void SAL_CALL SfxOfficeDispatch::addStatusListener(const css::uno::Reference< css::frame::XStatusListener > & aListener, const css::util::URL& aURL)
+{
+ {
+ std::unique_lock aGuard(maMutex);
+ maListeners.addInterface( aGuard, aURL.Complete, aListener );
+ }
+ if ( pImpl )
+ {
+ // ControllerItem is the Impl class
+ pImpl->addStatusListener( aListener, aURL );
+ }
+}
+
+SfxDispatcher* SfxOfficeDispatch::GetDispatcher_Impl()
+{
+ return pImpl->GetDispatcher();
+}
+
+sal_uInt16 SfxOfficeDispatch::GetId() const
+{
+ return pImpl ? pImpl->GetId() : 0;
+}
+
+void SfxOfficeDispatch::SetFrame(const css::uno::Reference< css::frame::XFrame >& xFrame)
+{
+ if ( pImpl )
+ pImpl->SetFrame( xFrame );
+}
+
+void SfxOfficeDispatch::SetMasterUnoCommand( bool bSet )
+{
+ if ( pImpl )
+ pImpl->setMasterSlaveCommand( bSet );
+}
+
+// Determine if URL contains a master/slave command which must be handled a little bit different
+bool SfxOfficeDispatch::IsMasterUnoCommand( const css::util::URL& aURL )
+{
+ return aURL.Protocol == ".uno:" && ( aURL.Path.indexOf( '.' ) > 0 );
+}
+
+OUString SfxOfficeDispatch::GetMasterUnoCommand( const css::util::URL& aURL )
+{
+ OUString aMasterCommand;
+ if ( IsMasterUnoCommand( aURL ))
+ {
+ sal_Int32 nIndex = aURL.Path.indexOf( '.' );
+ if ( nIndex > 0 )
+ aMasterCommand = aURL.Path.copy( 0, nIndex );
+ }
+
+ return aMasterCommand;
+}
+
+SfxDispatchController_Impl::SfxDispatchController_Impl(
+ SfxOfficeDispatch* pDisp,
+ SfxBindings* pBind,
+ SfxDispatcher* pDispat,
+ const SfxSlot* pSlot,
+ const css::util::URL& rURL )
+ : aDispatchURL( rURL )
+ , pDispatcher( pDispat )
+ , pBindings( pBind )
+ , pLastState( nullptr )
+ , pDispatch( pDisp )
+ , bMasterSlave( false )
+ , bVisible( true )
+{
+ if ( aDispatchURL.Protocol == "slot:" && pSlot->pUnoName )
+ {
+ aDispatchURL.Complete = ".uno:" + OUString::createFromAscii(pSlot->pUnoName);
+ Reference< XURLTransformer > xTrans( URLTransformer::create( ::comphelper::getProcessComponentContext() ) );
+ xTrans->parseStrict( aDispatchURL );
+ }
+
+ sal_uInt16 nSlot = pSlot->GetSlotId();
+ SetId( nSlot );
+ if ( pBindings )
+ {
+ // Bind immediately to enable the cache to recycle dispatches when asked for the same command
+ // a command in "slot" or in ".uno" notation must be treated as identical commands!
+ pBindings->ENTERREGISTRATIONS();
+ BindInternal_Impl( nSlot, pBindings );
+ pBindings->LEAVEREGISTRATIONS();
+ }
+ assert(pDispatcher);
+ assert(SfxApplication::Get()->GetAppDispatcher_Impl() == pDispatcher
+ || pDispatcher->GetFrame() != nullptr);
+ if (pDispatcher->GetFrame())
+ {
+ StartListening(*pDispatcher->GetFrame());
+ }
+ else
+ {
+ StartListening(*SfxApplication::Get());
+ }
+}
+
+void SfxDispatchController_Impl::Notify(SfxBroadcaster& rBC, SfxHint const& rHint)
+{
+ if (rHint.GetId() == SfxHintId::Dying)
+ { // both pBindings and pDispatcher are dead if SfxViewFrame is dead
+ pBindings = nullptr;
+ pDispatcher = nullptr;
+ EndListening(rBC);
+ }
+}
+
+SfxDispatchController_Impl::~SfxDispatchController_Impl()
+{
+ if ( pLastState && !IsInvalidItem( pLastState ) )
+ delete pLastState;
+
+ if ( pDispatch )
+ {
+ // disconnect
+ pDispatch->pImpl = nullptr;
+
+ // force all listeners to release the dispatch object
+ pDispatch->ReleaseAll();
+ }
+}
+
+void SfxDispatchController_Impl::SetFrame(const css::uno::Reference< css::frame::XFrame >& _xFrame)
+{
+ xFrame = _xFrame;
+}
+
+void SfxDispatchController_Impl::setMasterSlaveCommand( bool bSet )
+{
+ bMasterSlave = bSet;
+}
+
+void SfxDispatchController_Impl::UnBindController()
+{
+ pDispatch = nullptr;
+ if ( IsBound() )
+ {
+ GetBindings().ENTERREGISTRATIONS();
+ SfxControllerItem::UnBind();
+ GetBindings().LEAVEREGISTRATIONS();
+ }
+}
+
+void SfxDispatchController_Impl::addParametersToArgs( const css::util::URL& aURL, css::uno::Sequence< css::beans::PropertyValue >& rArgs )
+{
+ // Extract the parameter from the URL and put them into the property value sequence
+ sal_Int32 nQueryIndex = aURL.Complete.indexOf( '?' );
+ if ( nQueryIndex <= 0 )
+ return;
+
+ OUString aParamString( aURL.Complete.copy( nQueryIndex+1 ));
+ sal_Int32 nIndex = 0;
+ do
+ {
+ OUString aToken = aParamString.getToken( 0, '&', nIndex );
+
+ sal_Int32 nParmIndex = 0;
+ OUString aParamType;
+ OUString aParamName = aToken.getToken( 0, '=', nParmIndex );
+ OUString aValue = aToken.getToken( 0, '=', nParmIndex );
+
+ if ( !aParamName.isEmpty() )
+ {
+ nParmIndex = 0;
+ aToken = aParamName;
+ aParamName = aToken.getToken( 0, ':', nParmIndex );
+ aParamType = aToken.getToken( 0, ':', nParmIndex );
+ }
+
+ sal_Int32 nLen = rArgs.getLength();
+ rArgs.realloc( nLen+1 );
+ auto pArgs = rArgs.getArray();
+ pArgs[nLen].Name = aParamName;
+
+ if ( aParamType.isEmpty() )
+ {
+ // Default: LONG
+ pArgs[nLen].Value <<= aValue.toInt32();
+ }
+ else if ( aParamType.equalsAsciiL( URLTypeNames[URLType_BOOL], 4 ))
+ {
+ // sal_Bool support
+ pArgs[nLen].Value <<= aValue.toBoolean();
+ }
+ else if ( aParamType.equalsAsciiL( URLTypeNames[URLType_BYTE], 4 ))
+ {
+ // sal_uInt8 support
+ pArgs[nLen].Value <<= sal_Int8( aValue.toInt32() );
+ }
+ else if ( aParamType.equalsAsciiL( URLTypeNames[URLType_LONG], 4 ))
+ {
+ // LONG support
+ pArgs[nLen].Value <<= aValue.toInt32();
+ }
+ else if ( aParamType.equalsAsciiL( URLTypeNames[URLType_SHORT], 5 ))
+ {
+ // SHORT support
+ pArgs[nLen].Value <<= sal_Int16( aValue.toInt32() );
+ }
+ else if ( aParamType.equalsAsciiL( URLTypeNames[URLType_HYPER], 5 ))
+ {
+ // HYPER support
+ pArgs[nLen].Value <<= aValue.toInt64();
+ }
+ else if ( aParamType.equalsAsciiL( URLTypeNames[URLType_FLOAT], 5 ))
+ {
+ // FLOAT support
+ pArgs[nLen].Value <<= aValue.toFloat();
+ }
+ else if ( aParamType.equalsAsciiL( URLTypeNames[URLType_STRING], 6 ))
+ {
+ // STRING support
+ pArgs[nLen].Value <<= INetURLObject::decode( aValue, INetURLObject::DecodeMechanism::WithCharset );
+ }
+ else if ( aParamType.equalsAsciiL( URLTypeNames[URLType_DOUBLE], 6))
+ {
+ // DOUBLE support
+ pArgs[nLen].Value <<= aValue.toDouble();
+ }
+ }
+ while ( nIndex >= 0 );
+}
+
+MapUnit SfxDispatchController_Impl::GetCoreMetric( SfxItemPool const & rPool, sal_uInt16 nSlotId )
+{
+ sal_uInt16 nWhich = rPool.GetWhich( nSlotId );
+ return rPool.GetMetric( nWhich );
+}
+
+OUString SfxDispatchController_Impl::getSlaveCommand( const css::util::URL& rURL )
+{
+ OUString aSlaveCommand;
+ sal_Int32 nIndex = rURL.Path.indexOf( '.' );
+ if (( nIndex > 0 ) && ( nIndex < rURL.Path.getLength() ))
+ aSlaveCommand = rURL.Path.copy( nIndex+1 );
+ return aSlaveCommand;
+}
+
+namespace {
+
+void collectUIInformation(const util::URL& rURL, const css::uno::Sequence< css::beans::PropertyValue >& rArgs)
+{
+ static const char* pFile = std::getenv("LO_COLLECT_UIINFO");
+ if (!pFile)
+ return;
+
+ UITestLogger::getInstance().logCommand(
+ OUStringConcatenation("Send UNO Command (\"" + rURL.Complete + "\") "), rArgs);
+}
+
+}
+
+void SfxDispatchController_Impl::dispatch( const css::util::URL& aURL,
+ const css::uno::Sequence< css::beans::PropertyValue >& aArgs,
+ const css::uno::Reference< css::frame::XDispatchResultListener >& rListener )
+{
+ if ( aURL.Protocol == ".uno:")
+ {
+ CrashReporter::logUnoCommand(aURL.Path);
+ }
+ collectUIInformation(aURL, aArgs);
+
+ SolarMutexGuard aGuard;
+
+ if (comphelper::LibreOfficeKit::isActive() &&
+ SfxViewShell::Current()->isBlockedCommand(aURL.Complete))
+ {
+ tools::JsonWriter aTree;
+ aTree.put("code", "");
+ aTree.put("kind", "BlockedCommand");
+ aTree.put("cmd", aURL.Complete);
+ aTree.put("message", "Blocked feature");
+ aTree.put("viewID", SfxViewShell::Current()->GetViewShellId().get());
+
+ SfxViewShell::Current()->libreOfficeKitViewCallback(LOK_COMMAND_BLOCKED, aTree.extractData());
+ return;
+ }
+
+ if (
+ !(pDispatch &&
+ (
+ (aURL.Protocol == ".uno:" && aURL.Path == aDispatchURL.Path) ||
+ (aURL.Protocol == "slot:" && aURL.Path.toInt32() == GetId())
+ ))
+ )
+ return;
+
+ if ( !pDispatcher && pBindings )
+ pDispatcher = GetBindings().GetDispatcher_Impl();
+
+ css::uno::Sequence< css::beans::PropertyValue > lNewArgs;
+ sal_Int32 nCount = aArgs.getLength();
+
+ // Support for URL based arguments
+ INetURLObject aURLObj( aURL.Complete );
+ if ( aURLObj.HasParam() )
+ addParametersToArgs( aURL, lNewArgs );
+
+ // Try to find call mode and frame name inside given arguments...
+ SfxCallMode nCall = SfxCallMode::RECORD;
+ sal_Int32 nMarkArg = -1;
+
+ // Filter arguments which shouldn't be part of the sequence property value
+ sal_uInt16 nModifier(0);
+ std::vector< css::beans::PropertyValue > aAddArgs;
+ for( sal_Int32 n=0; n<nCount; n++ )
+ {
+ const css::beans::PropertyValue& rProp = aArgs[n];
+ if( rProp.Name == "SynchronMode" )
+ {
+ bool bTemp;
+ if( rProp.Value >>= bTemp )
+ nCall = bTemp ? SfxCallMode::SYNCHRON : SfxCallMode::ASYNCHRON;
+ }
+ else if( rProp.Name == "Bookmark" )
+ {
+ nMarkArg = n;
+ aAddArgs.push_back( aArgs[n] );
+ }
+ else if( rProp.Name == "KeyModifier" )
+ rProp.Value >>= nModifier;
+ else
+ aAddArgs.push_back( aArgs[n] );
+ }
+
+ // Add needed arguments to sequence property value
+ sal_uInt32 nAddArgs = aAddArgs.size();
+ if ( nAddArgs > 0 )
+ {
+ sal_uInt32 nIndex( lNewArgs.getLength() );
+
+ lNewArgs.realloc( nIndex + nAddArgs );
+ std::copy(aAddArgs.begin(), aAddArgs.end(), std::next(lNewArgs.getArray(), nIndex));
+ }
+
+ // Overwrite possible detected synchron argument, if real listener exists (currently no other way)
+ if ( rListener.is() )
+ nCall = SfxCallMode::SYNCHRON;
+
+ if( GetId() == SID_JUMPTOMARK && nMarkArg == - 1 )
+ {
+ // we offer dispatches for SID_JUMPTOMARK if the URL points to a bookmark inside the document
+ // so we must retrieve this as an argument from the parsed URL
+ lNewArgs.realloc( lNewArgs.getLength()+1 );
+ auto& el = lNewArgs.getArray()[lNewArgs.getLength()-1];
+ el.Name = "Bookmark";
+ el.Value <<= aURL.Mark;
+ }
+
+ css::uno::Reference< css::frame::XFrame > xFrameRef(xFrame.get(), css::uno::UNO_QUERY);
+ if (! xFrameRef.is() && pDispatcher)
+ {
+ SfxViewFrame* pViewFrame = pDispatcher->GetFrame();
+ if (pViewFrame)
+ xFrameRef = pViewFrame->GetFrame().GetFrameInterface();
+ }
+
+ bool bSuccess = false;
+ const SfxPoolItem* pItem = nullptr;
+ MapUnit eMapUnit( MapUnit::Map100thMM );
+
+ // Extra scope so that aInternalSet is destroyed before
+ // rListener->dispatchFinished potentially calls
+ // framework::Desktop::terminate -> SfxApplication::Deinitialize ->
+ // ~CntItemPool:
+ if (pDispatcher)
+ {
+ SfxAllItemSet aInternalSet( SfxGetpApp()->GetPool() );
+ if (xFrameRef.is()) // an empty set is no problem ... but an empty frame reference can be a problem !
+ aInternalSet.Put( SfxUnoFrameItem( SID_FILLFRAME, xFrameRef ) );
+
+ SfxShell* pShell( nullptr );
+ // #i102619# Retrieve metric from shell before execution - the shell could be destroyed after execution
+ if ( pDispatcher->GetBindings() )
+ {
+ if ( !pDispatcher->IsLocked() )
+ {
+ const SfxSlot *pSlot = nullptr;
+ if ( pDispatcher->GetShellAndSlot_Impl( GetId(), &pShell, &pSlot, false, false ) )
+ {
+ if ( bMasterSlave )
+ {
+ // Extract slave command and add argument to the args list. Master slot MUST
+ // have an argument that has the same name as the master slot and type is SfxStringItem.
+ sal_Int32 nIndex = lNewArgs.getLength();
+ lNewArgs.realloc( nIndex+1 );
+ auto plNewArgs = lNewArgs.getArray();
+ plNewArgs[nIndex].Name = OUString::createFromAscii( pSlot->pUnoName );
+ plNewArgs[nIndex].Value <<= SfxDispatchController_Impl::getSlaveCommand( aDispatchURL );
+ }
+
+ eMapUnit = GetCoreMetric( pShell->GetPool(), GetId() );
+ std::optional<SfxAllItemSet> xSet(pShell->GetPool());
+ TransformParameters(GetId(), lNewArgs, *xSet, pSlot);
+ if (xSet->Count())
+ {
+ // execute with arguments - call directly
+ pItem = pDispatcher->Execute(GetId(), nCall, &*xSet, &aInternalSet, nModifier);
+ if ( pItem != nullptr )
+ {
+ if (const SfxBoolItem* pBoolItem = dynamic_cast<const SfxBoolItem*>(pItem))
+ bSuccess = pBoolItem->GetValue();
+ else if ( !pItem->IsVoidItem() )
+ bSuccess = true; // all other types are true
+ }
+ // else bSuccess = false look to line 664 it is false
+ }
+ else
+ {
+ // Be sure to delete this before we send a dispatch
+ // request, which will destroy the current shell.
+ xSet.reset();
+
+ // execute using bindings, enables support for toggle/enum etc.
+ SfxRequest aReq( GetId(), nCall, pShell->GetPool() );
+ aReq.SetModifier( nModifier );
+ aReq.SetInternalArgs_Impl(aInternalSet);
+ pDispatcher->GetBindings()->Execute_Impl( aReq, pSlot, pShell );
+ pItem = aReq.GetReturnValue();
+ bSuccess = aReq.IsDone() || pItem != nullptr;
+ }
+ }
+ else
+ SAL_INFO("sfx.control", "MacroPlayer: Unknown slot dispatched!");
+ }
+ }
+ else
+ {
+ eMapUnit = GetCoreMetric( SfxGetpApp()->GetPool(), GetId() );
+ // AppDispatcher
+ SfxAllItemSet aSet( SfxGetpApp()->GetPool() );
+ TransformParameters( GetId(), lNewArgs, aSet );
+
+ if ( aSet.Count() )
+ pItem = pDispatcher->Execute(GetId(), nCall, &aSet, &aInternalSet, nModifier);
+ else
+ // SfxRequests take empty sets as argument sets, GetArgs() returning non-zero!
+ pItem = pDispatcher->Execute(GetId(), nCall, nullptr, &aInternalSet, nModifier);
+
+ // no bindings, no invalidate ( usually done in SfxDispatcher::Call_Impl()! )
+ if (SfxApplication* pApp = SfxApplication::Get())
+ {
+ SfxDispatcher* pAppDispat = pApp->GetAppDispatcher_Impl();
+ if ( pAppDispat )
+ {
+ const SfxPoolItem* pState=nullptr;
+ SfxItemState eState = pDispatcher->QueryState( GetId(), pState );
+ StateChangedAtToolBoxControl( GetId(), eState, pState );
+ }
+ }
+
+ bSuccess = (pItem != nullptr);
+ }
+ }
+
+ if ( !rListener.is() )
+ return;
+
+ css::frame::DispatchResultEvent aEvent;
+ if ( bSuccess )
+ aEvent.State = css::frame::DispatchResultState::SUCCESS;
+ else
+ aEvent.State = css::frame::DispatchResultState::FAILURE;
+
+ aEvent.Source = static_cast<css::frame::XDispatch*>(pDispatch);
+ if ( bSuccess && pItem && !pItem->IsVoidItem() )
+ {
+ sal_uInt16 nSubId( 0 );
+ if ( eMapUnit == MapUnit::MapTwip )
+ nSubId |= CONVERT_TWIPS;
+ pItem->QueryValue( aEvent.Result, static_cast<sal_uInt8>(nSubId) );
+ }
+
+ rListener->dispatchFinished( aEvent );
+}
+
+SfxDispatcher* SfxDispatchController_Impl::GetDispatcher()
+{
+ if ( !pDispatcher && pBindings )
+ pDispatcher = GetBindings().GetDispatcher_Impl();
+ return pDispatcher;
+}
+
+void SfxDispatchController_Impl::addStatusListener(const css::uno::Reference< css::frame::XStatusListener > & aListener, const css::util::URL& aURL)
+{
+ SolarMutexGuard aGuard;
+ if ( !pDispatch )
+ return;
+
+ // Use alternative QueryState call to have a valid UNO representation of the state.
+ css::uno::Any aState;
+ if ( !pDispatcher && pBindings )
+ pDispatcher = GetBindings().GetDispatcher_Impl();
+ SfxItemState eState = pDispatcher ? pDispatcher->QueryState( GetId(), aState ) : SfxItemState::DONTCARE;
+
+ if ( eState == SfxItemState::DONTCARE )
+ {
+ // Use special uno struct to transport don't care state
+ css::frame::status::ItemStatus aItemStatus;
+ aItemStatus.State = css::frame::status::ItemState::DONT_CARE;
+ aState <<= aItemStatus;
+ }
+
+ css::frame::FeatureStateEvent aEvent;
+ aEvent.FeatureURL = aURL;
+ aEvent.Source = static_cast<css::frame::XDispatch*>(pDispatch);
+ aEvent.Requery = false;
+ if ( bVisible )
+ {
+ aEvent.IsEnabled = eState != SfxItemState::DISABLED;
+ aEvent.State = aState;
+ }
+ else
+ {
+ css::frame::status::Visibility aVisibilityStatus;
+ aVisibilityStatus.bVisible = false;
+
+ // MBA: we might decide to *not* disable "invisible" slots, but this would be
+ // a change that needs to adjust at least the testtool
+ aEvent.IsEnabled = false;
+ aEvent.State <<= aVisibilityStatus;
+ }
+
+ aListener->statusChanged( aEvent );
+}
+
+void SfxDispatchController_Impl::sendStatusChanged(const OUString& rURL, const css::frame::FeatureStateEvent& rEvent)
+{
+ pDispatch->sendStatusChanged(rURL, rEvent);
+}
+
+void SfxDispatchController_Impl::StateChanged( sal_uInt16 nSID, SfxItemState eState, const SfxPoolItem* pState, SfxSlotServer const * pSlotServ )
+{
+ if ( !pDispatch )
+ return;
+
+ // Bindings instance notifies controller about a state change, listeners must be notified also
+ // Don't cache visibility state changes as they are volatile. We need our real state to send it
+ // to our controllers after visibility is set to true.
+ bool bNotify = true;
+ if ( pState && !IsInvalidItem( pState ) )
+ {
+ if ( auto pVisibilityItem = dynamic_cast< const SfxVisibilityItem *>( pState ) )
+ bVisible = pVisibilityItem->GetValue();
+ else
+ {
+ if (pLastState && !IsInvalidItem(pLastState))
+ {
+ bNotify = typeid(*pState) != typeid(*pLastState) || *pState != *pLastState;
+ delete pLastState;
+ }
+ pLastState = !IsInvalidItem(pState) ? pState->Clone() : pState;
+ bVisible = true;
+ }
+ }
+ else
+ {
+ if ( pLastState && !IsInvalidItem( pLastState ) )
+ delete pLastState;
+ pLastState = pState;
+ }
+
+ if (!bNotify)
+ return;
+
+ css::uno::Any aState;
+ if ( ( eState >= SfxItemState::DEFAULT ) && pState && !IsInvalidItem( pState ) && !pState->IsVoidItem() )
+ {
+ // Retrieve metric from pool to have correct sub ID when calling QueryValue
+ sal_uInt16 nSubId( 0 );
+ MapUnit eMapUnit( MapUnit::Map100thMM );
+
+ // retrieve the core metric
+ // it's enough to check the objectshell, the only shell that does not use the pool of the document
+ // is SfxViewFrame, but it hasn't any metric parameters
+ // TODO/LATER: what about the FormShell? Does it use any metric data?! Perhaps it should use the Pool of the document!
+ if ( pSlotServ && pDispatcher )
+ {
+ SfxShell* pShell = pDispatcher->GetShell( pSlotServ->GetShellLevel() );
+ DBG_ASSERT( pShell, "Can't get core metric without shell!" );
+ if ( pShell )
+ eMapUnit = GetCoreMetric( pShell->GetPool(), nSID );
+ }
+
+ if ( eMapUnit == MapUnit::MapTwip )
+ nSubId |= CONVERT_TWIPS;
+
+ pState->QueryValue( aState, static_cast<sal_uInt8>(nSubId) );
+ }
+ else if ( eState == SfxItemState::DONTCARE )
+ {
+ // Use special uno struct to transport don't care state
+ css::frame::status::ItemStatus aItemStatus;
+ aItemStatus.State = css::frame::status::ItemState::DONT_CARE;
+ aState <<= aItemStatus;
+ }
+
+ css::frame::FeatureStateEvent aEvent;
+ aEvent.FeatureURL = aDispatchURL;
+ aEvent.Source = static_cast<css::frame::XDispatch*>(pDispatch);
+ aEvent.IsEnabled = eState != SfxItemState::DISABLED;
+ aEvent.Requery = false;
+ aEvent.State = aState;
+
+ if (pDispatcher && pDispatcher->GetFrame())
+ {
+ InterceptLOKStateChangeEvent(nSID, pDispatcher->GetFrame(), aEvent, pState);
+ }
+
+ const std::vector<OUString> aContainedTypes = pDispatch->getContainedTypes();
+ for (const OUString& rName: aContainedTypes)
+ {
+ if (rName == aDispatchURL.Main || rName == aDispatchURL.Complete)
+ sendStatusChanged(rName, aEvent);
+ }
+}
+
+void SfxDispatchController_Impl::StateChangedAtToolBoxControl( sal_uInt16 nSID, SfxItemState eState, const SfxPoolItem* pState )
+{
+ StateChanged( nSID, eState, pState, nullptr );
+}
+
+static void InterceptLOKStateChangeEvent(sal_uInt16 nSID, SfxViewFrame* pViewFrame, const css::frame::FeatureStateEvent& aEvent, const SfxPoolItem* pState)
+{
+ if (!comphelper::LibreOfficeKit::isActive())
+ return;
+
+ OUStringBuffer aBuffer;
+ aBuffer.append(aEvent.FeatureURL.Complete);
+ aBuffer.append(u'=');
+
+ if (aEvent.FeatureURL.Path == "Bold" ||
+ aEvent.FeatureURL.Path == "CenterPara" ||
+ aEvent.FeatureURL.Path == "CharBackgroundExt" ||
+ aEvent.FeatureURL.Path == "ControlCodes" ||
+ aEvent.FeatureURL.Path == "DefaultBullet" ||
+ aEvent.FeatureURL.Path == "DefaultNumbering" ||
+ aEvent.FeatureURL.Path == "Italic" ||
+ aEvent.FeatureURL.Path == "JustifyPara" ||
+ aEvent.FeatureURL.Path == "LeftPara" ||
+ aEvent.FeatureURL.Path == "OutlineFont" ||
+ aEvent.FeatureURL.Path == "RightPara" ||
+ aEvent.FeatureURL.Path == "Shadowed" ||
+ aEvent.FeatureURL.Path == "SpellOnline" ||
+ aEvent.FeatureURL.Path == "OnlineAutoFormat" ||
+ aEvent.FeatureURL.Path == "SubScript" ||
+ aEvent.FeatureURL.Path == "SuperScript" ||
+ aEvent.FeatureURL.Path == "Strikeout" ||
+ aEvent.FeatureURL.Path == "Underline" ||
+ aEvent.FeatureURL.Path == "ModifiedStatus" ||
+ aEvent.FeatureURL.Path == "TrackChanges" ||
+ aEvent.FeatureURL.Path == "ShowTrackedChanges" ||
+ aEvent.FeatureURL.Path == "NextTrackedChange" ||
+ aEvent.FeatureURL.Path == "PreviousTrackedChange" ||
+ aEvent.FeatureURL.Path == "AlignLeft" ||
+ aEvent.FeatureURL.Path == "AlignHorizontalCenter" ||
+ aEvent.FeatureURL.Path == "AlignRight" ||
+ aEvent.FeatureURL.Path == "DocumentRepair" ||
+ aEvent.FeatureURL.Path == "ObjectAlignLeft" ||
+ aEvent.FeatureURL.Path == "ObjectAlignRight" ||
+ aEvent.FeatureURL.Path == "AlignCenter" ||
+ aEvent.FeatureURL.Path == "AlignUp" ||
+ aEvent.FeatureURL.Path == "AlignMiddle" ||
+ aEvent.FeatureURL.Path == "AlignDown" ||
+ aEvent.FeatureURL.Path == "TraceChangeMode" ||
+ aEvent.FeatureURL.Path == "FormatPaintbrush" ||
+ aEvent.FeatureURL.Path == "FreezePanes" ||
+ aEvent.FeatureURL.Path == "Sidebar" ||
+ aEvent.FeatureURL.Path == "SheetRightToLeft" ||
+ aEvent.FeatureURL.Path == "SpacePara1" ||
+ aEvent.FeatureURL.Path == "SpacePara15" ||
+ aEvent.FeatureURL.Path == "SpacePara2")
+ {
+ bool bTemp = false;
+ aEvent.State >>= bTemp;
+ aBuffer.append(bTemp);
+ }
+ else if (aEvent.FeatureURL.Path == "CharFontName")
+ {
+ css::awt::FontDescriptor aFontDesc;
+ aEvent.State >>= aFontDesc;
+ aBuffer.append(aFontDesc.Name);
+ }
+ else if (aEvent.FeatureURL.Path == "FontHeight")
+ {
+ css::frame::status::FontHeight aFontHeight;
+ aEvent.State >>= aFontHeight;
+ aBuffer.append(aFontHeight.Height);
+ }
+ else if (aEvent.FeatureURL.Path == "StyleApply")
+ {
+ css::frame::status::Template aTemplate;
+ aEvent.State >>= aTemplate;
+ aBuffer.append(aTemplate.StyleName);
+ }
+ else if (aEvent.FeatureURL.Path == "BackColor" ||
+ aEvent.FeatureURL.Path == "BackgroundColor" ||
+ aEvent.FeatureURL.Path == "CharBackColor" ||
+ aEvent.FeatureURL.Path == "Color" ||
+ aEvent.FeatureURL.Path == "FontColor" ||
+ aEvent.FeatureURL.Path == "FrameLineColor" ||
+ aEvent.FeatureURL.Path == "GlowColor")
+ {
+ sal_Int32 nColor = -1;
+ aEvent.State >>= nColor;
+ aBuffer.append(nColor);
+ }
+ else if (aEvent.FeatureURL.Path == "Undo" ||
+ aEvent.FeatureURL.Path == "Redo")
+ {
+ const SfxUInt32Item* pUndoConflict = dynamic_cast< const SfxUInt32Item * >( pState );
+ if ( pUndoConflict && pUndoConflict->GetValue() > 0 )
+ {
+ aBuffer.append("disabled");
+ }
+ else
+ {
+ aBuffer.append(aEvent.IsEnabled ? std::u16string_view(u"enabled") : std::u16string_view(u"disabled"));
+ }
+ }
+ else if (aEvent.FeatureURL.Path == "Cut" ||
+ aEvent.FeatureURL.Path == "Copy" ||
+ aEvent.FeatureURL.Path == "Paste" ||
+ aEvent.FeatureURL.Path == "SelectAll" ||
+ aEvent.FeatureURL.Path == "InsertAnnotation" ||
+ aEvent.FeatureURL.Path == "DeleteAnnotation" ||
+ aEvent.FeatureURL.Path == "ResolveAnnotation" ||
+ aEvent.FeatureURL.Path == "ResolveAnnotationThread" ||
+ aEvent.FeatureURL.Path == "InsertRowsBefore" ||
+ aEvent.FeatureURL.Path == "InsertRowsAfter" ||
+ aEvent.FeatureURL.Path == "InsertColumnsBefore" ||
+ aEvent.FeatureURL.Path == "InsertColumnsAfter" ||
+ aEvent.FeatureURL.Path == "MergeCells" ||
+ aEvent.FeatureURL.Path == "InsertObjectChart" ||
+ aEvent.FeatureURL.Path == "InsertSection" ||
+ aEvent.FeatureURL.Path == "InsertAnnotation" ||
+ aEvent.FeatureURL.Path == "InsertPagebreak" ||
+ aEvent.FeatureURL.Path == "InsertColumnBreak" ||
+ aEvent.FeatureURL.Path == "HyperlinkDialog" ||
+ aEvent.FeatureURL.Path == "InsertSymbol" ||
+ aEvent.FeatureURL.Path == "InsertPage" ||
+ aEvent.FeatureURL.Path == "DeletePage" ||
+ aEvent.FeatureURL.Path == "DuplicatePage" ||
+ aEvent.FeatureURL.Path == "DeleteRows" ||
+ aEvent.FeatureURL.Path == "DeleteColumns" ||
+ aEvent.FeatureURL.Path == "DeleteTable" ||
+ aEvent.FeatureURL.Path == "SelectTable" ||
+ aEvent.FeatureURL.Path == "EntireRow" ||
+ aEvent.FeatureURL.Path == "EntireColumn" ||
+ aEvent.FeatureURL.Path == "EntireCell" ||
+ aEvent.FeatureURL.Path == "SortAscending" ||
+ aEvent.FeatureURL.Path == "SortDescending" ||
+ aEvent.FeatureURL.Path == "AcceptAllTrackedChanges" ||
+ aEvent.FeatureURL.Path == "RejectAllTrackedChanges" ||
+ aEvent.FeatureURL.Path == "AcceptTrackedChange" ||
+ aEvent.FeatureURL.Path == "RejectTrackedChange" ||
+ aEvent.FeatureURL.Path == "NextTrackedChange" ||
+ aEvent.FeatureURL.Path == "PreviousTrackedChange" ||
+ aEvent.FeatureURL.Path == "FormatGroup" ||
+ aEvent.FeatureURL.Path == "ObjectBackOne" ||
+ aEvent.FeatureURL.Path == "SendToBack" ||
+ aEvent.FeatureURL.Path == "ObjectForwardOne" ||
+ aEvent.FeatureURL.Path == "BringToFront" ||
+ aEvent.FeatureURL.Path == "WrapRight" ||
+ aEvent.FeatureURL.Path == "WrapThrough" ||
+ aEvent.FeatureURL.Path == "WrapLeft" ||
+ aEvent.FeatureURL.Path == "WrapIdeal" ||
+ aEvent.FeatureURL.Path == "WrapOn" ||
+ aEvent.FeatureURL.Path == "WrapOff" ||
+ aEvent.FeatureURL.Path == "UpdateCurIndex" ||
+ aEvent.FeatureURL.Path == "InsertCaptionDialog" ||
+ aEvent.FeatureURL.Path == "MergeCells" ||
+ aEvent.FeatureURL.Path == "SplitTable" ||
+ aEvent.FeatureURL.Path == "SplitCell" ||
+ aEvent.FeatureURL.Path == "DeleteNote" ||
+ aEvent.FeatureURL.Path == "AcceptChanges" ||
+ aEvent.FeatureURL.Path == "SetDefault" ||
+ aEvent.FeatureURL.Path == "ParaLeftToRight" ||
+ aEvent.FeatureURL.Path == "ParaRightToLeft" ||
+ aEvent.FeatureURL.Path == "ParaspaceIncrease" ||
+ aEvent.FeatureURL.Path == "ParaspaceDecrease" ||
+ aEvent.FeatureURL.Path == "TableDialog" ||
+ aEvent.FeatureURL.Path == "FormatCellDialog" ||
+ aEvent.FeatureURL.Path == "FontDialog" ||
+ aEvent.FeatureURL.Path == "ParagraphDialog" ||
+ aEvent.FeatureURL.Path == "OutlineBullet" ||
+ aEvent.FeatureURL.Path == "InsertIndexesEntry" ||
+ aEvent.FeatureURL.Path == "TransformDialog" ||
+ aEvent.FeatureURL.Path == "EditRegion" ||
+ aEvent.FeatureURL.Path == "ThesaurusDialog" ||
+ aEvent.FeatureURL.Path == "OutlineRight" ||
+ aEvent.FeatureURL.Path == "OutlineLeft" ||
+ aEvent.FeatureURL.Path == "OutlineDown" ||
+ aEvent.FeatureURL.Path == "OutlineUp" ||
+ aEvent.FeatureURL.Path == "FormatArea" ||
+ aEvent.FeatureURL.Path == "FormatLine" ||
+ aEvent.FeatureURL.Path == "FormatColumns" ||
+ aEvent.FeatureURL.Path == "Watermark" ||
+ aEvent.FeatureURL.Path == "InsertBreak" ||
+ aEvent.FeatureURL.Path == "InsertEndnote" ||
+ aEvent.FeatureURL.Path == "InsertFootnote" ||
+ aEvent.FeatureURL.Path == "InsertReferenceField" ||
+ aEvent.FeatureURL.Path == "InsertBookmark" ||
+ aEvent.FeatureURL.Path == "InsertAuthoritiesEntry" ||
+ aEvent.FeatureURL.Path == "InsertMultiIndex" ||
+ aEvent.FeatureURL.Path == "InsertField" ||
+ aEvent.FeatureURL.Path == "InsertPageNumberField" ||
+ aEvent.FeatureURL.Path == "InsertPageCountField" ||
+ aEvent.FeatureURL.Path == "InsertDateField" ||
+ aEvent.FeatureURL.Path == "InsertTitleField" ||
+ aEvent.FeatureURL.Path == "InsertFieldCtrl" ||
+ aEvent.FeatureURL.Path == "CharmapControl" ||
+ aEvent.FeatureURL.Path == "EnterGroup" ||
+ aEvent.FeatureURL.Path == "LeaveGroup" ||
+ aEvent.FeatureURL.Path == "Combine" ||
+ aEvent.FeatureURL.Path == "Merge" ||
+ aEvent.FeatureURL.Path == "Dismantle" ||
+ aEvent.FeatureURL.Path == "Substract" ||
+ aEvent.FeatureURL.Path == "DistributeSelection" ||
+ aEvent.FeatureURL.Path == "Intersect" ||
+ aEvent.FeatureURL.Path == "ResetAttributes" ||
+ aEvent.FeatureURL.Path == "IncrementIndent" ||
+ aEvent.FeatureURL.Path == "DecrementIndent" ||
+ aEvent.FeatureURL.Path == "EditHeaderAndFooter" ||
+ aEvent.FeatureURL.Path == "InsertSparkline" ||
+ aEvent.FeatureURL.Path == "DeleteSparkline" ||
+ aEvent.FeatureURL.Path == "DeleteSparklineGroup" ||
+ aEvent.FeatureURL.Path == "EditSparklineGroup" ||
+ aEvent.FeatureURL.Path == "EditSparkline" ||
+ aEvent.FeatureURL.Path == "GroupSparklines" ||
+ aEvent.FeatureURL.Path == "UngroupSparklines" ||
+ aEvent.FeatureURL.Path == "FormatSparklineMenu" ||
+ aEvent.FeatureURL.Path == "NumberFormatDecDecimals" ||
+ aEvent.FeatureURL.Path == "NumberFormatIncDecimals")
+ {
+ aBuffer.append(aEvent.IsEnabled ? std::u16string_view(u"enabled") : std::u16string_view(u"disabled"));
+ }
+ else if (aEvent.FeatureURL.Path == "AssignLayout" ||
+ aEvent.FeatureURL.Path == "StatusSelectionMode" ||
+ aEvent.FeatureURL.Path == "Signature" ||
+ aEvent.FeatureURL.Path == "SelectionMode" ||
+ aEvent.FeatureURL.Path == "StatusBarFunc" ||
+ aEvent.FeatureURL.Path == "FreezePanesColumn" ||
+ aEvent.FeatureURL.Path == "FreezePanesRow")
+ {
+ sal_Int32 aInt32;
+
+ if (aEvent.IsEnabled && (aEvent.State >>= aInt32))
+ {
+ aBuffer.append(aInt32);
+ }
+ }
+ else if (aEvent.FeatureURL.Path == "TransformPosX" ||
+ aEvent.FeatureURL.Path == "TransformPosY" ||
+ aEvent.FeatureURL.Path == "TransformWidth" ||
+ aEvent.FeatureURL.Path == "TransformHeight")
+ {
+ const SfxViewShell* pViewShell = SfxViewShell::Current();
+ if (aEvent.IsEnabled && pViewShell && pViewShell->isLOKMobilePhone())
+ {
+ boost::property_tree::ptree aTree;
+ boost::property_tree::ptree aState;
+ OUString aStr(aEvent.FeatureURL.Complete);
+
+ aTree.put("commandName", aStr.toUtf8().getStr());
+ pViewFrame->GetBindings().QueryControlState(nSID, aState);
+ aTree.add_child("state", aState);
+
+ aBuffer.setLength(0);
+ std::stringstream aStream;
+ boost::property_tree::write_json(aStream, aTree);
+ aBuffer.appendAscii(aStream.str().c_str());
+ }
+ }
+ else if (aEvent.FeatureURL.Path == "StatusDocPos" ||
+ aEvent.FeatureURL.Path == "RowColSelCount" ||
+ aEvent.FeatureURL.Path == "StatusPageStyle" ||
+ aEvent.FeatureURL.Path == "StateTableCell" ||
+ aEvent.FeatureURL.Path == "StateWordCount" ||
+ aEvent.FeatureURL.Path == "PageStyleName" ||
+ aEvent.FeatureURL.Path == "PageStatus" ||
+ aEvent.FeatureURL.Path == "LayoutStatus" ||
+ aEvent.FeatureURL.Path == "Scale" ||
+ aEvent.FeatureURL.Path == "Context")
+ {
+ OUString aString;
+
+ if (aEvent.IsEnabled && (aEvent.State >>= aString))
+ {
+ aBuffer.append(aString);
+ }
+ }
+ else if (aEvent.FeatureURL.Path == "InsertMode" ||
+ aEvent.FeatureURL.Path == "WrapText" ||
+ aEvent.FeatureURL.Path == "NumberFormatCurrency" ||
+ aEvent.FeatureURL.Path == "NumberFormatPercent" ||
+ aEvent.FeatureURL.Path == "NumberFormatDecimal" ||
+ aEvent.FeatureURL.Path == "NumberFormatDate" ||
+ aEvent.FeatureURL.Path == "ShowResolvedAnnotations")
+ {
+ bool aBool;
+
+ if (aEvent.IsEnabled && (aEvent.State >>= aBool))
+ {
+ aBuffer.append(OUString::boolean(aBool));
+ }
+ }
+ else if (aEvent.FeatureURL.Path == "ToggleMergeCells")
+ {
+ bool aBool;
+
+ if (aEvent.IsEnabled && (aEvent.State >>= aBool))
+ {
+ aBuffer.append(OUString::boolean(aBool));
+ }
+ else
+ {
+ aBuffer.append("disabled");
+ }
+ }
+ else if (aEvent.FeatureURL.Path == "Position")
+ {
+ css::awt::Point aPoint;
+
+ if (aEvent.IsEnabled && (aEvent.State >>= aPoint))
+ {
+ aBuffer.append( OUString::number(aPoint.X) + " / " + OUString::number(aPoint.Y));
+ }
+ }
+ else if (aEvent.FeatureURL.Path == "Size")
+ {
+ css::awt::Size aSize;
+
+ if (aEvent.IsEnabled && (aEvent.State >>= aSize))
+ {
+ aBuffer.append( OUString::number(aSize.Width) + " x " + OUString::number(aSize.Height) );
+ }
+ }
+ else if (aEvent.FeatureURL.Path == "LanguageStatus" ||
+ aEvent.FeatureURL.Path == "StatePageNumber")
+ {
+ css::uno::Sequence< OUString > aSeq;
+
+ if (aEvent.IsEnabled)
+ {
+ OUString sValue;
+ if (aEvent.State >>= sValue)
+ {
+ aBuffer.append(sValue);
+ }
+ else if (aEvent.State >>= aSeq)
+ {
+ aBuffer.append(aSeq[0]);
+ }
+ }
+ }
+ else if (aEvent.FeatureURL.Path == "InsertPageHeader" ||
+ aEvent.FeatureURL.Path == "InsertPageFooter")
+ {
+ if (aEvent.IsEnabled)
+ {
+ css::uno::Sequence< OUString > aSeq;
+ if (aEvent.State >>= aSeq)
+ {
+ aBuffer.append(u'{');
+ for (sal_Int32 itSeq = 0; itSeq < aSeq.getLength(); itSeq++)
+ {
+ aBuffer.append("\"" + aSeq[itSeq]);
+ if (itSeq != aSeq.getLength() - 1)
+ aBuffer.append("\":true,");
+ else
+ aBuffer.append("\":true");
+ }
+ aBuffer.append(u'}');
+ }
+ }
+ }
+ else if (aEvent.FeatureURL.Path == "TableColumWidth" ||
+ aEvent.FeatureURL.Path == "TableRowHeight")
+ {
+ sal_Int32 nValue;
+ if (aEvent.State >>= nValue)
+ {
+ float nScaleValue = 1000.0;
+ nValue *= nScaleValue;
+ sal_Int32 nConvertedValue = o3tl::convert(nValue, o3tl::Length::twip, o3tl::Length::in);
+ aBuffer.append(nConvertedValue / nScaleValue);
+ }
+ }
+ else
+ {
+ // Try to send JSON state version
+ SfxLokHelper::sendUnoStatus(pViewFrame->GetViewShell(), pState);
+
+ return;
+ }
+
+ OUString payload = aBuffer.makeStringAndClear();
+ if (const SfxViewShell* pViewShell = pViewFrame->GetViewShell())
+ pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_STATE_CHANGED, payload.toUtf8().getStr());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/devtools/DevToolsStrings.hrc b/sfx2/source/devtools/DevToolsStrings.hrc
new file mode 100644
index 000000000..4c3f3e9ce
--- /dev/null
+++ b/sfx2/source/devtools/DevToolsStrings.hrc
@@ -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/.
+ *
+ */
+
+#pragma once
+
+#define NC_(Context, String) TranslateId(Context, reinterpret_cast<char const *>(u8##String))
+
+#define STR_TEXT_PORTION NC_("STR_TEXT_PORTION", "Text Portion %1")
+#define STR_PARAGRAPH NC_("STR_PARAGRAPH", "Paragraph %1")
+#define STR_SHAPE NC_("STR_SHAPE", "Shape %1")
+#define STR_PAGE NC_("STR_PAGE", "Page %1")
+#define STR_SLIDE NC_("STR_SLIDE", "Slide %1")
+#define STR_MASTER_SLIDE NC_("STR_MASTER_SLIDE", "Master Slide %1")
+#define STR_SHEET NC_("STR_SHEET", "Sheet %1")
+
+#define STR_SHAPES_ENTRY NC_("STR_SHAPES_NODE", "Shapes")
+#define STR_CHARTS_ENTRY NC_("STR_CHARTS_ENTRY", "Charts")
+#define STR_PIVOT_TABLES_ENTRY NC_("STR_PIVOT_TABLES_ENTRY", "Pivot Tables")
+#define STR_DOCUMENT_ENTRY NC_("STR_DOCUMENT_ENTRY", "Document")
+#define STR_SHEETS_ENTRY NC_("STR_SHEETS_ENTRY", "Sheets")
+#define STR_STYLES_ENTRY NC_("STR_STYLES_ENTRY", "Styles")
+#define STR_SLIDES_ENTRY NC_("STR_SLIDES_ENTRY", "Slides")
+#define STR_MASTER_SLIDES_ENTRY NC_("STR_MASTER_SLIDES_ENTRY", "Master Slides")
+#define STR_PAGES_ENTRY NC_("STR_PAGES_ENTRY", "Pages")
+#define STR_PARAGRAPHS_ENTRY NC_("STR_PARAGRAPHS_ENTRY", "Paragraphs")
+#define STR_TABLES_ENTRY NC_("STR_TABLES_ENTRY", "Tables")
+#define STR_FRAMES_ENTRY NC_("STR_FRAMES_ENTRY", "Frames")
+#define STR_GRAPHIC_OBJECTS_ENTRY NC_("STR_GRAPHIC_OBJECTS_ENTRY", "Graphic Objects")
+#define STR_EMBEDDED_OBJECTS_ENTRY NC_("STR_EMBEDDED_OBJECTS_ENTRY", "Embedded Objects")
+
+#define STR_ANY_VALUE_TRUE NC_("STR_ANY_VALUE_TRUE", "True")
+#define STR_ANY_VALUE_FALSE NC_("STR_ANY_VALUE_FALSE", "False")
+#define STR_ANY_VALUE_NULL NC_("STR_ANY_VALUE_NULL", "Null")
+#define STR_CLASS_UNKNOWN NC_("STR_CLASS_UNKNOWN", "Unknown")
+
+#define STR_METHOD_TYPE_OBJECT NC_("STR_METHOD_TYPE_OBJECT", "object")
+#define STR_METHOD_TYPE_STRUCT NC_("STR_METHOD_TYPE_STRUCT", "struct")
+#define STR_METHOD_TYPE_ENUM NC_("STR_METHOD_TYPE_ENUM", "enum")
+#define STR_METHOD_TYPE_SEQUENCE NC_("STR_METHOD_TYPE_SEQUENCE", "sequence")
+
+#define STR_PROPERTY_TYPE_IS_NAMED_CONTAINER NC_("STR_PROPERTY_TYPE_IS_NAMED_CONTAINER", "name container")
+#define STR_PROPERTY_TYPE_IS_INDEX_CONTAINER NC_("STR_PROPERTY_TYPE_IS_INDEX_CONTAINER", "index container")
+#define STR_PROPERTY_TYPE_IS_ENUMERATION NC_("STR_PROPERTY_TYPE_IS_ENUMERATION", "enumeration")
+
+#define STR_PARMETER_MODE_IN NC_("STR_PARMETER_MODE_IN", "[in]")
+#define STR_PARMETER_MODE_OUT NC_("STR_PARMETER_MODE_OUT", "[out]")
+#define STR_PARMETER_MODE_IN_AND_OUT NC_("STR_PARMETER_MODE_IN_AND_OUT", "[in&out]")
+
+#define STR_PROPERTY_ATTRIBUTE_IS_ATTRIBUTE NC_("STR_PROPERTY_ATTRIBUTE_IS_ATTRIBUTE", "attribute")
+#define STR_PROPERTY_ATTRIBUTE_GET NC_("STR_PROPERTY_ATTRIBUTE_GET", "get")
+#define STR_PROPERTY_ATTRIBUTE_SET NC_("STR_PROPERTY_ATTRIBUTE_SET", "set")
+#define STR_PROPERTY_ATTRIBUTE_MAYBEVOID NC_("STR_PROPERTY_ATTRIBUTE_MAYBEVOID", "may be void")
+#define STR_PROPERTY_ATTRIBUTE_READONLY NC_("STR_PROPERTY_ATTRIBUTE_READONLY", "read-only")
+#define STR_PROPERTY_ATTRIBUTE_WRITEONLY NC_("STR_PROPERTY_ATTRIBUTE_WRITEONLY", "write-only")
+#define STR_PROPERTY_ATTRIBUTE_REMOVABLE NC_("STR_PROPERTY_ATTRIBUTE_REMOVABLE", "removeable")
+#define STR_PROPERTY_ATTRIBUTE_BOUND NC_("STR_PROPERTY_ATTRIBUTE_BOUND", "bound")
+#define STR_PROPERTY_ATTRIBUTE_CONSTRAINED NC_("STR_PROPERTY_ATTRIBUTE_CONSTRAINED", "constrained")
+#define STR_PROPERTY_ATTRIBUTE_TRANSIENT NC_("STR_PROPERTY_ATTRIBUTE_TRANSIENT", "transient")
+#define STR_PROPERTY_ATTRIBUTE_MAYBEAMBIGUOUS NC_("STR_PROPERTY_ATTRIBUTE_MAYBEAMBIGUOUS", "may be ambiguous")
+#define STR_PROPERTY_ATTRIBUTE_MAYBEDEFAULT NC_("STR_PROPERTY_ATTRIBUTE_MAYBEDEFAULT", "may be default")
+
+#define STR_PROPERTY_VALUE_SEQUENCE NC_("STR_PROPERTY_VALUE_SEQUENCE", "<Sequence [%1]>")
+#define STR_PROPERTY_VALUE_OBJECT NC_("STR_PROPERTY_VALUE_OBJECT", "<Object@%1>")
+#define STR_PROPERTY_VALUE_STRUCT NC_("STR_PROPERTY_VALUE_STRUCT", "<Struct>")
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/devtools/DevelopmentToolChildWindow.cxx b/sfx2/source/devtools/DevelopmentToolChildWindow.cxx
new file mode 100644
index 000000000..6b160bfa0
--- /dev/null
+++ b/sfx2/source/devtools/DevelopmentToolChildWindow.cxx
@@ -0,0 +1,30 @@
+/* -*- 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/devtools/DevelopmentToolChildWindow.hxx>
+#include <sfx2/devtools/DevelopmentToolDockingWindow.hxx>
+#include <sfx2/sfxsids.hrc>
+
+SFX_IMPL_DOCKINGWINDOW_WITHID(DevelopmentToolChildWindow, SID_DEVELOPMENT_TOOLS_DOCKING_WINDOW);
+
+DevelopmentToolChildWindow::DevelopmentToolChildWindow(vcl::Window* pParentWindow, sal_uInt16 nId,
+ SfxBindings* pBindings,
+ SfxChildWinInfo* pInfo)
+ : SfxChildWindow(pParentWindow, nId)
+{
+ VclPtr<DevelopmentToolDockingWindow> pWin
+ = VclPtr<DevelopmentToolDockingWindow>::Create(pBindings, this, pParentWindow);
+ SetWindow(pWin);
+ SetAlignment(SfxChildAlignment::BOTTOM);
+ pWin->SetSizePixel(Size(0, 290));
+ pWin->Initialize(pInfo);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/devtools/DevelopmentToolDockingWindow.cxx b/sfx2/source/devtools/DevelopmentToolDockingWindow.cxx
new file mode 100644
index 000000000..817647ca9
--- /dev/null
+++ b/sfx2/source/devtools/DevelopmentToolDockingWindow.cxx
@@ -0,0 +1,155 @@
+/* -*- 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 <memory>
+
+#include <sfx2/devtools/DevelopmentToolDockingWindow.hxx>
+
+#include <com/sun/star/view/XSelectionSupplier.hpp>
+
+#include <sfx2/dispatch.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/viewfrm.hxx>
+
+#include "SelectionChangeHandler.hxx"
+
+using namespace css;
+
+DevelopmentToolDockingWindow::DevelopmentToolDockingWindow(SfxBindings* pInputBindings,
+ SfxChildWindow* pChildWindow,
+ vcl::Window* pParent)
+ : SfxDockingWindow(pInputBindings, pChildWindow, pParent, "DevelopmentTool",
+ "sfx/ui/developmenttool.ui")
+ , mpObjectInspectorWidgets(new ObjectInspectorWidgets(m_xBuilder))
+ , mpDocumentModelTreeView(m_xBuilder->weld_tree_view("leftside_treeview_id"))
+ , mpDomToolbar(m_xBuilder->weld_toolbar("dom_toolbar"))
+ , maDocumentModelTreeHandler(
+ mpDocumentModelTreeView,
+ pInputBindings->GetDispatcher()->GetFrame()->GetObjectShell()->GetBaseModel())
+ , maObjectInspectorTreeHandler(mpObjectInspectorWidgets)
+{
+ mpDocumentModelTreeView->connect_changed(
+ LINK(this, DevelopmentToolDockingWindow, DocumentModelTreeViewSelectionHandler));
+ mpDomToolbar->connect_clicked(
+ LINK(this, DevelopmentToolDockingWindow, DomToolbarButtonClicked));
+
+ auto* pViewFrame = pInputBindings->GetDispatcher()->GetFrame();
+
+ uno::Reference<frame::XController> xController = pViewFrame->GetFrame().GetController();
+
+ mxRoot = pInputBindings->GetDispatcher()->GetFrame()->GetObjectShell()->GetBaseModel();
+
+ maDocumentModelTreeHandler.inspectDocument();
+ mxSelectionListener.set(new SelectionChangeHandler(xController, this));
+ mxSelectionSupplier.set(xController, css::uno::UNO_QUERY);
+
+ maObjectInspectorTreeHandler.introspect(mxRoot);
+}
+
+IMPL_LINK(DevelopmentToolDockingWindow, DocumentModelTreeViewSelectionHandler, weld::TreeView&,
+ rView, void)
+{
+ if (mpDomToolbar->get_item_active("dom_current_selection_toggle"))
+ return;
+
+ OUString sID = rView.get_selected_id();
+ auto xObject = DocumentModelTreeHandler::getObjectByID(sID);
+ if (xObject.is())
+ maObjectInspectorTreeHandler.introspect(xObject);
+}
+
+IMPL_LINK(DevelopmentToolDockingWindow, DomToolbarButtonClicked, const OString&, rSelectionId, void)
+{
+ if (rSelectionId == "dom_refresh_button")
+ {
+ maDocumentModelTreeHandler.inspectDocument();
+ }
+ else if (rSelectionId == "dom_current_selection_toggle")
+ {
+ updateSelection();
+ }
+}
+
+DevelopmentToolDockingWindow::~DevelopmentToolDockingWindow() { disposeOnce(); }
+
+void DevelopmentToolDockingWindow::dispose()
+{
+ // Stop and remove the listener
+ auto* pSelectionChangeHandler
+ = dynamic_cast<SelectionChangeHandler*>(mxSelectionListener.get());
+ if (pSelectionChangeHandler)
+ pSelectionChangeHandler->stopListening();
+
+ mxSelectionListener = uno::Reference<view::XSelectionChangeListener>();
+
+ // dispose DOM and object inspector handlers
+ maDocumentModelTreeHandler.dispose();
+ maObjectInspectorTreeHandler.dispose();
+
+ // dispose welded objects
+ mpObjectInspectorWidgets.reset();
+ mpDomToolbar.reset();
+ mpDocumentModelTreeView.reset();
+
+ SfxDockingWindow::dispose();
+}
+
+void DevelopmentToolDockingWindow::updateSelection()
+{
+ bool bActive = mpDomToolbar->get_item_active("dom_current_selection_toggle");
+ if (bActive)
+ {
+ maObjectInspectorTreeHandler.introspect(mxCurrentSelection);
+ maDocumentModelTreeHandler.selectObject(mxCurrentSelection);
+ }
+ else
+ {
+ mpDocumentModelTreeView->set_sensitive(true);
+ }
+}
+
+void DevelopmentToolDockingWindow::ToggleFloatingMode()
+{
+ SfxDockingWindow::ToggleFloatingMode();
+
+ if (GetFloatingWindow())
+ GetFloatingWindow()->SetMinOutputSizePixel(Size(300, 300));
+
+ Invalidate();
+}
+
+void DevelopmentToolDockingWindow::selectionChanged(
+ uno::Reference<uno::XInterface> const& xInterface)
+{
+ mxCurrentSelection = xInterface;
+ updateSelection();
+}
+
+void DevelopmentToolDockingWindow::changeToCurrentSelection()
+{
+ if (mxSelectionSupplier.is())
+ {
+ css::uno::Any aAny = mxSelectionSupplier->getSelection();
+ if (aAny.hasValue())
+ {
+ auto xInterface = aAny.get<css::uno::Reference<css::uno::XInterface>>();
+ if (xInterface.is())
+ {
+ maObjectInspectorTreeHandler.introspect(xInterface);
+ mpDomToolbar->set_item_active("dom_current_selection_toggle", true);
+ return;
+ }
+ }
+ }
+ mpDomToolbar->set_item_active("dom_current_selection_toggle", false);
+ maObjectInspectorTreeHandler.introspect(mxRoot);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/devtools/DocumentModelTreeHandler.cxx b/sfx2/source/devtools/DocumentModelTreeHandler.cxx
new file mode 100644
index 000000000..c5358c02b
--- /dev/null
+++ b/sfx2/source/devtools/DocumentModelTreeHandler.cxx
@@ -0,0 +1,840 @@
+/* -*- 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 <memory>
+
+#include <sfx2/devtools/DocumentModelTreeHandler.hxx>
+
+#include <sfx2/sfxresid.hxx>
+#include "DevToolsStrings.hrc"
+
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/container/XNamed.hpp>
+#include <com/sun/star/container/XEnumerationAccess.hpp>
+
+#include <com/sun/star/drawing/XDrawPage.hpp>
+#include <com/sun/star/drawing/XDrawPages.hpp>
+#include <com/sun/star/drawing/XDrawPagesSupplier.hpp>
+#include <com/sun/star/drawing/XDrawPageSupplier.hpp>
+#include <com/sun/star/drawing/XMasterPagesSupplier.hpp>
+#include <com/sun/star/sheet/XSpreadsheetDocument.hpp>
+#include <com/sun/star/sheet/XSpreadsheet.hpp>
+#include <com/sun/star/sheet/XDataPilotTablesSupplier.hpp>
+#include <com/sun/star/sheet/XDataPilotTables.hpp>
+#include <com/sun/star/table/XTableChartsSupplier.hpp>
+#include <com/sun/star/table/XTableCharts.hpp>
+#include <com/sun/star/text/XTextDocument.hpp>
+#include <com/sun/star/text/XTextTablesSupplier.hpp>
+#include <com/sun/star/text/XTextFramesSupplier.hpp>
+#include <com/sun/star/text/XTextGraphicObjectsSupplier.hpp>
+#include <com/sun/star/text/XTextEmbeddedObjectsSupplier.hpp>
+#include <com/sun/star/style/XStyleFamiliesSupplier.hpp>
+
+using namespace css;
+
+namespace
+{
+// returns a name of the object, if available
+OUString lclGetNamed(uno::Reference<uno::XInterface> const& xObject)
+{
+ uno::Reference<container::XNamed> xNamed(xObject, uno::UNO_QUERY);
+ if (!xNamed.is())
+ return OUString();
+ return xNamed->getName();
+}
+
+/** DocumentModelTreeEntry is an object "attached" to a tree node.
+ *
+ * It represents an object that is "attached" to the tree view an is
+ * responsible to provide the UNO object associated with the current
+ * node and on demand create and fill the children of the said node.
+ */
+class DocumentModelTreeEntry
+{
+protected:
+ OUString maString;
+ css::uno::Reference<css::uno::XInterface> mxObject;
+
+public:
+ DocumentModelTreeEntry(OUString const& rString,
+ css::uno::Reference<css::uno::XInterface> const& xObject)
+ : maString(rString)
+ , mxObject(xObject)
+ {
+ }
+
+ virtual ~DocumentModelTreeEntry() {}
+
+ /// the node string shown in the tree view
+ OUString& getString() { return maString; }
+
+ /// should show the expander for the tree view node
+ virtual bool shouldShowExpander() { return false; }
+
+ /// The main UNO object for this entry
+ virtual css::uno::Reference<css::uno::XInterface> getMainObject() { return mxObject; }
+
+ /// Create and fill the children to the parent tree view node.
+ virtual void fill(std::unique_ptr<weld::TreeView>& /*pDocumentModelTree*/,
+ weld::TreeIter const& /*rParent*/)
+ {
+ }
+};
+
+// append an entry to a input TreeView to a parent
+void lclAppendToParentEntry(const std::unique_ptr<weld::TreeView>& rTree,
+ weld::TreeIter const& rParent, DocumentModelTreeEntry* pEntry)
+{
+ OUString sId(weld::toId(pEntry));
+ OUString const& rString = pEntry->getString();
+ rTree->insert(&rParent, -1, &rString, &sId, nullptr, nullptr, pEntry->shouldShowExpander(),
+ nullptr);
+}
+
+// append a root entry to a input TreeView
+OUString lclAppend(const std::unique_ptr<weld::TreeView>& rTree, DocumentModelTreeEntry* pEntry)
+{
+ OUString sId(weld::toId(pEntry));
+ OUString const& rString = pEntry->getString();
+ rTree->insert(nullptr, -1, &rString, &sId, nullptr, nullptr, pEntry->shouldShowExpander(),
+ nullptr);
+ return sId;
+}
+
+/** Entry that represents a object, which implements a XNameAccess */
+class NameAccessTreeEntry : public DocumentModelTreeEntry
+{
+protected:
+ NameAccessTreeEntry(OUString const& rString, uno::Reference<uno::XInterface> const& xObject)
+ : DocumentModelTreeEntry(rString, xObject)
+ {
+ }
+
+ bool shouldShowExpander() override
+ {
+ uno::Reference<container::XNameAccess> xNameAccess(getMainObject(), uno::UNO_QUERY);
+ return xNameAccess.is() && xNameAccess->getElementNames().getLength() > 0;
+ }
+
+ /// A generic fill when the UNO object implements XNameAccess interface
+ void fill(std::unique_ptr<weld::TreeView>& pDocumentModelTree,
+ weld::TreeIter const& rParent) override
+ {
+ uno::Reference<container::XNameAccess> xNameAccess(getMainObject(), uno::UNO_QUERY);
+ xNameAccess.set(getMainObject(), uno::UNO_QUERY);
+ if (!xNameAccess.is())
+ return;
+
+ const uno::Sequence<OUString> aNames = xNameAccess->getElementNames();
+ for (auto const& rName : aNames)
+ {
+ uno::Reference<uno::XInterface> xObject(xNameAccess->getByName(rName), uno::UNO_QUERY);
+ auto pEntry = std::make_unique<DocumentModelTreeEntry>(rName, xObject);
+ lclAppendToParentEntry(pDocumentModelTree, rParent, pEntry.release());
+ }
+ }
+};
+
+/** Entry that represents the document root object */
+class DocumentRootEntry : public DocumentModelTreeEntry
+{
+public:
+ DocumentRootEntry(OUString const& rString, uno::Reference<uno::XInterface> const& xObject)
+ : DocumentModelTreeEntry(rString, xObject)
+ {
+ }
+
+ bool shouldShowExpander() override { return false; }
+};
+
+/** Represents a paragraph object (XParagraph) */
+class ParagraphEntry : public DocumentModelTreeEntry
+{
+public:
+ ParagraphEntry(OUString const& rString,
+ css::uno::Reference<css::uno::XInterface> const& xObject)
+ : DocumentModelTreeEntry(rString, xObject)
+ {
+ }
+
+ bool shouldShowExpander() override
+ {
+ uno::Reference<container::XEnumerationAccess> xEnumAccess(getMainObject(), uno::UNO_QUERY);
+ if (!xEnumAccess.is())
+ return false;
+ auto xTextPortions = xEnumAccess->createEnumeration();
+ if (!xTextPortions.is())
+ return false;
+ return xTextPortions->hasMoreElements();
+ }
+
+ void fill(std::unique_ptr<weld::TreeView>& pDocumentModelTree,
+ weld::TreeIter const& rParent) override
+ {
+ uno::Reference<container::XEnumerationAccess> xEnumAccess(getMainObject(), uno::UNO_QUERY);
+ if (!xEnumAccess.is())
+ return;
+
+ uno::Reference<container::XEnumeration> xTextPortions = xEnumAccess->createEnumeration();
+ if (!xTextPortions.is())
+ return;
+
+ for (sal_Int32 i = 0; xTextPortions->hasMoreElements(); i++)
+ {
+ uno::Reference<text::XTextRange> const xTextPortion(xTextPortions->nextElement(),
+ uno::UNO_QUERY);
+ OUString aString = lclGetNamed(xTextPortion);
+ if (aString.isEmpty())
+ {
+ OUString aNumber = OUString::number(i + 1);
+ aString = SfxResId(STR_TEXT_PORTION).replaceFirst("%1", aNumber);
+ }
+
+ auto pEntry = std::make_unique<DocumentModelTreeEntry>(aString, xTextPortion);
+ lclAppendToParentEntry(pDocumentModelTree, rParent, pEntry.release());
+ }
+ }
+};
+
+/** Represents a list of paragraphs */
+class ParagraphsEntry : public DocumentModelTreeEntry
+{
+public:
+ ParagraphsEntry(OUString const& rString,
+ css::uno::Reference<css::uno::XInterface> const& xObject)
+ : DocumentModelTreeEntry(rString, xObject)
+ {
+ }
+
+ css::uno::Reference<css::uno::XInterface> getMainObject() override
+ {
+ uno::Reference<text::XTextDocument> xDocument(mxObject, uno::UNO_QUERY);
+ if (!xDocument.is())
+ return mxObject;
+
+ return xDocument->getText()->getText();
+ }
+
+ bool shouldShowExpander() override
+ {
+ uno::Reference<container::XEnumerationAccess> xEnumAccess(getMainObject(), uno::UNO_QUERY);
+ if (!xEnumAccess.is())
+ return false;
+ auto xParagraphEnum = xEnumAccess->createEnumeration();
+ if (!xParagraphEnum.is())
+ return false;
+ return xParagraphEnum->hasMoreElements();
+ }
+
+ void fill(std::unique_ptr<weld::TreeView>& pDocumentModelTree,
+ weld::TreeIter const& rParent) override
+ {
+ uno::Reference<container::XEnumerationAccess> xEnumAccess(getMainObject(), uno::UNO_QUERY);
+ if (!xEnumAccess.is())
+ return;
+
+ uno::Reference<container::XEnumeration> xParagraphEnum = xEnumAccess->createEnumeration();
+
+ if (!xParagraphEnum.is())
+ return;
+
+ for (sal_Int32 i = 0; xParagraphEnum->hasMoreElements(); i++)
+ {
+ uno::Reference<text::XTextContent> const xParagraph(xParagraphEnum->nextElement(),
+ uno::UNO_QUERY);
+ OUString aString = lclGetNamed(xParagraph);
+ if (aString.isEmpty())
+ {
+ aString = SfxResId(STR_PARAGRAPH).replaceFirst("%1", OUString::number(i + 1));
+ }
+
+ auto pEntry = std::make_unique<ParagraphEntry>(aString, xParagraph);
+ lclAppendToParentEntry(pDocumentModelTree, rParent, pEntry.release());
+ }
+ }
+};
+
+/** Represents a list of shapes */
+class ShapesEntry : public DocumentModelTreeEntry
+{
+public:
+ ShapesEntry(OUString const& rString, css::uno::Reference<css::uno::XInterface> const& xObject)
+ : DocumentModelTreeEntry(rString, xObject)
+ {
+ }
+
+ css::uno::Reference<css::uno::XInterface> getMainObject() override
+ {
+ uno::Reference<drawing::XDrawPageSupplier> xSupplier(mxObject, uno::UNO_QUERY);
+ if (!xSupplier.is())
+ return mxObject;
+ return xSupplier->getDrawPage();
+ }
+
+ bool shouldShowExpander() override
+ {
+ uno::Reference<container::XIndexAccess> xShapes(getMainObject(), uno::UNO_QUERY);
+ return xShapes.is() && xShapes->getCount() > 0;
+ }
+
+ void fill(std::unique_ptr<weld::TreeView>& pDocumentModelTree,
+ weld::TreeIter const& rParent) override
+ {
+ uno::Reference<container::XIndexAccess> xShapes(getMainObject(), uno::UNO_QUERY);
+ if (!xShapes.is())
+ return;
+ for (sal_Int32 nIndexShapes = 0; nIndexShapes < xShapes->getCount(); ++nIndexShapes)
+ {
+ uno::Reference<uno::XInterface> xShape(xShapes->getByIndex(nIndexShapes),
+ uno::UNO_QUERY);
+ OUString aShapeName = lclGetNamed(xShape);
+ if (aShapeName.isEmpty())
+ {
+ aShapeName
+ = SfxResId(STR_SHAPE).replaceFirst("%1", OUString::number(nIndexShapes + 1));
+ }
+
+ auto pEntry = std::make_unique<DocumentModelTreeEntry>(aShapeName, xShape);
+ lclAppendToParentEntry(pDocumentModelTree, rParent, pEntry.release());
+ }
+ }
+};
+
+/** Represents a list of tables */
+class TablesEntry : public NameAccessTreeEntry
+{
+public:
+ TablesEntry(OUString const& rString, css::uno::Reference<css::uno::XInterface> const& xObject)
+ : NameAccessTreeEntry(rString, xObject)
+ {
+ }
+
+ css::uno::Reference<css::uno::XInterface> getMainObject() override
+ {
+ uno::Reference<text::XTextTablesSupplier> xSupplier(mxObject, uno::UNO_QUERY);
+ if (!xSupplier.is())
+ return mxObject;
+ return xSupplier->getTextTables();
+ }
+};
+
+/** Represents a list of frames */
+class FramesEntry : public NameAccessTreeEntry
+{
+public:
+ FramesEntry(OUString const& rString, css::uno::Reference<css::uno::XInterface> const& xObject)
+ : NameAccessTreeEntry(rString, xObject)
+ {
+ }
+
+ css::uno::Reference<css::uno::XInterface> getMainObject() override
+ {
+ uno::Reference<text::XTextFramesSupplier> xSupplier(mxObject, uno::UNO_QUERY);
+ if (!xSupplier.is())
+ return mxObject;
+ return xSupplier->getTextFrames();
+ }
+};
+
+/** Represents a list of writer graphic objects */
+class WriterGraphicObjectsEntry : public NameAccessTreeEntry
+{
+public:
+ WriterGraphicObjectsEntry(OUString const& rString,
+ css::uno::Reference<css::uno::XInterface> const& xObject)
+ : NameAccessTreeEntry(rString, xObject)
+ {
+ }
+
+ css::uno::Reference<css::uno::XInterface> getMainObject() override
+ {
+ uno::Reference<text::XTextGraphicObjectsSupplier> xSupplier(mxObject, uno::UNO_QUERY);
+ if (!xSupplier.is())
+ return mxObject;
+ return xSupplier->getGraphicObjects();
+ }
+};
+
+/** Represents a list of writer embedded (OLE) objects */
+class EmbeddedObjectsEntry : public NameAccessTreeEntry
+{
+public:
+ EmbeddedObjectsEntry(OUString const& rString,
+ css::uno::Reference<css::uno::XInterface> const& xObject)
+ : NameAccessTreeEntry(rString, xObject)
+ {
+ }
+
+ css::uno::Reference<css::uno::XInterface> getMainObject() override
+ {
+ uno::Reference<text::XTextEmbeddedObjectsSupplier> xSupplier(mxObject, uno::UNO_QUERY);
+ if (!xSupplier.is())
+ return mxObject;
+ return xSupplier->getEmbeddedObjects();
+ }
+};
+
+/** Represents a style family, which contains a list of styles */
+class StylesFamilyEntry : public NameAccessTreeEntry
+{
+public:
+ StylesFamilyEntry(OUString const& rString,
+ css::uno::Reference<css::uno::XInterface> const& xObject)
+ : NameAccessTreeEntry(rString, xObject)
+ {
+ }
+};
+
+/** Represents a list of style families */
+class StylesFamiliesEntry : public DocumentModelTreeEntry
+{
+public:
+ StylesFamiliesEntry(OUString const& rString,
+ css::uno::Reference<css::uno::XInterface> const& xObject)
+ : DocumentModelTreeEntry(rString, xObject)
+ {
+ }
+
+ css::uno::Reference<css::uno::XInterface> getMainObject() override
+ {
+ uno::Reference<style::XStyleFamiliesSupplier> xSupplier(mxObject, uno::UNO_QUERY);
+ if (!xSupplier.is())
+ return mxObject;
+ return xSupplier->getStyleFamilies();
+ }
+
+ bool shouldShowExpander() override
+ {
+ uno::Reference<container::XNameAccess> xStyleFamilies(getMainObject(), uno::UNO_QUERY);
+ return xStyleFamilies.is() && xStyleFamilies->getElementNames().getLength() > 0;
+ }
+
+ void fill(std::unique_ptr<weld::TreeView>& pDocumentModelTree,
+ weld::TreeIter const& rParent) override
+ {
+ uno::Reference<container::XNameAccess> xStyleFamilies(getMainObject(), uno::UNO_QUERY);
+ if (!xStyleFamilies.is())
+ return;
+
+ const uno::Sequence<OUString> aNames = xStyleFamilies->getElementNames();
+ for (auto const& rFamilyName : aNames)
+ {
+ uno::Reference<uno::XInterface> xStyleFamily(xStyleFamilies->getByName(rFamilyName),
+ uno::UNO_QUERY);
+
+ auto pStylesFamilyEntry
+ = std::make_unique<StylesFamilyEntry>(rFamilyName, xStyleFamily);
+ lclAppendToParentEntry(pDocumentModelTree, rParent, pStylesFamilyEntry.release());
+ }
+ }
+};
+
+/** Represents a list of pages */
+class PagesEntry : public DocumentModelTreeEntry
+{
+public:
+ PagesEntry(OUString const& rString, css::uno::Reference<css::uno::XInterface> const& xObject)
+ : DocumentModelTreeEntry(rString, xObject)
+ {
+ }
+
+ css::uno::Reference<css::uno::XInterface> getMainObject() override
+ {
+ uno::Reference<drawing::XDrawPagesSupplier> xSupplier(mxObject, uno::UNO_QUERY);
+ if (!xSupplier.is())
+ return mxObject;
+ return xSupplier->getDrawPages();
+ }
+
+ bool shouldShowExpander() override
+ {
+ uno::Reference<drawing::XDrawPages> xDrawPages(getMainObject(), uno::UNO_QUERY);
+ return xDrawPages.is() && xDrawPages->getCount() > 0;
+ }
+
+ void fill(std::unique_ptr<weld::TreeView>& pDocumentModelTree,
+ weld::TreeIter const& rParent) override
+ {
+ uno::Reference<drawing::XDrawPages> xDrawPages(getMainObject(), uno::UNO_QUERY);
+ for (sal_Int32 i = 0; i < xDrawPages->getCount(); ++i)
+ {
+ uno::Reference<drawing::XDrawPage> xPage(xDrawPages->getByIndex(i), uno::UNO_QUERY);
+ if (!xPage.is())
+ continue;
+
+ OUString aPageString = lclGetNamed(xPage);
+ if (aPageString.isEmpty())
+ aPageString = SfxResId(STR_PAGE).replaceFirst("%1", OUString::number(i + 1));
+
+ auto pShapesEntry = std::make_unique<ShapesEntry>(aPageString, xPage);
+ lclAppendToParentEntry(pDocumentModelTree, rParent, pShapesEntry.release());
+ }
+ }
+};
+
+/** Represents a list of (Impress) slides */
+class SlidesEntry : public DocumentModelTreeEntry
+{
+public:
+ SlidesEntry(OUString const& rString, css::uno::Reference<css::uno::XInterface> const& xObject)
+ : DocumentModelTreeEntry(rString, xObject)
+ {
+ }
+
+ css::uno::Reference<css::uno::XInterface> getMainObject() override
+ {
+ uno::Reference<drawing::XDrawPagesSupplier> xSupplier(mxObject, uno::UNO_QUERY);
+ if (!xSupplier.is())
+ return mxObject;
+ return xSupplier->getDrawPages();
+ }
+
+ bool shouldShowExpander() override
+ {
+ uno::Reference<drawing::XDrawPages> xDrawPages(getMainObject(), uno::UNO_QUERY);
+ return xDrawPages.is() && xDrawPages->getCount() > 0;
+ }
+
+ void fill(std::unique_ptr<weld::TreeView>& pDocumentModelTree,
+ weld::TreeIter const& rParent) override
+ {
+ uno::Reference<drawing::XDrawPages> xDrawPages(getMainObject(), uno::UNO_QUERY);
+ for (sal_Int32 i = 0; i < xDrawPages->getCount(); ++i)
+ {
+ uno::Reference<drawing::XDrawPage> xPage(xDrawPages->getByIndex(i), uno::UNO_QUERY);
+ if (!xPage.is())
+ continue;
+
+ OUString aPageString = lclGetNamed(xPage);
+ if (aPageString.isEmpty())
+ aPageString = SfxResId(STR_SLIDE).replaceFirst("%1", OUString::number(i + 1));
+
+ auto pShapesEntry = std::make_unique<ShapesEntry>(aPageString, xPage);
+ lclAppendToParentEntry(pDocumentModelTree, rParent, pShapesEntry.release());
+ }
+ }
+};
+
+/** Represents a list of (Impress) master slides */
+class MasterSlidesEntry : public DocumentModelTreeEntry
+{
+public:
+ MasterSlidesEntry(OUString const& rString,
+ css::uno::Reference<css::uno::XInterface> const& xObject)
+ : DocumentModelTreeEntry(rString, xObject)
+ {
+ }
+
+ css::uno::Reference<css::uno::XInterface> getMainObject() override
+ {
+ uno::Reference<drawing::XMasterPagesSupplier> xSupplier(mxObject, uno::UNO_QUERY);
+ if (!xSupplier.is())
+ return mxObject;
+ return xSupplier->getMasterPages();
+ }
+
+ bool shouldShowExpander() override
+ {
+ uno::Reference<drawing::XDrawPages> xDrawPages(getMainObject(), uno::UNO_QUERY);
+ return xDrawPages.is() && xDrawPages->getCount() > 0;
+ }
+
+ void fill(std::unique_ptr<weld::TreeView>& pDocumentModelTree,
+ weld::TreeIter const& rParent) override
+ {
+ uno::Reference<drawing::XDrawPages> xDrawPages(getMainObject(), uno::UNO_QUERY);
+ for (sal_Int32 i = 0; i < xDrawPages->getCount(); ++i)
+ {
+ uno::Reference<drawing::XDrawPage> xPage(xDrawPages->getByIndex(i), uno::UNO_QUERY);
+ if (!xPage.is())
+ continue;
+
+ OUString aPageString = lclGetNamed(xPage);
+ if (aPageString.isEmpty())
+ {
+ aPageString
+ = SfxResId(STR_MASTER_SLIDE).replaceFirst("%1", OUString::number(i + 1));
+ }
+
+ auto pShapesEntry = std::make_unique<ShapesEntry>(aPageString, xPage);
+ lclAppendToParentEntry(pDocumentModelTree, rParent, pShapesEntry.release());
+ }
+ }
+};
+
+/** Represents a list of charts */
+class ChartsEntry : public NameAccessTreeEntry
+{
+public:
+ ChartsEntry(OUString const& rString, css::uno::Reference<css::uno::XInterface> const& xObject)
+ : NameAccessTreeEntry(rString, xObject)
+ {
+ }
+
+ css::uno::Reference<css::uno::XInterface> getMainObject() override
+ {
+ uno::Reference<table::XTableChartsSupplier> xSupplier(mxObject, uno::UNO_QUERY);
+ if (!xSupplier.is())
+ return mxObject;
+ return xSupplier->getCharts();
+ }
+
+ void fill(std::unique_ptr<weld::TreeView>& pDocumentModelTree,
+ weld::TreeIter const& rParent) override
+ {
+ uno::Reference<table::XTableCharts> xCharts(getMainObject(), uno::UNO_QUERY);
+ if (!xCharts.is())
+ return;
+ NameAccessTreeEntry::fill(pDocumentModelTree, rParent);
+ }
+};
+
+/** Represents a list of pivot tables */
+class PivotTablesEntry : public NameAccessTreeEntry
+{
+public:
+ PivotTablesEntry(OUString const& rString,
+ css::uno::Reference<css::uno::XInterface> const& xObject)
+ : NameAccessTreeEntry(rString, xObject)
+ {
+ }
+
+ bool shouldShowExpander() override { return true; }
+
+ css::uno::Reference<css::uno::XInterface> getMainObject() override
+ {
+ uno::Reference<sheet::XDataPilotTablesSupplier> xSupplier(mxObject, uno::UNO_QUERY);
+ if (!xSupplier.is())
+ return mxObject;
+ return xSupplier->getDataPilotTables();
+ }
+
+ void fill(std::unique_ptr<weld::TreeView>& pDocumentModelTree,
+ weld::TreeIter const& rParent) override
+ {
+ uno::Reference<sheet::XDataPilotTables> xPivotTables(getMainObject(), uno::UNO_QUERY);
+ if (!xPivotTables.is())
+ return;
+ NameAccessTreeEntry::fill(pDocumentModelTree, rParent);
+ }
+};
+
+/** Represents a (Calc) sheet */
+class SheetEntry : public DocumentModelTreeEntry
+{
+public:
+ SheetEntry(OUString const& rString, css::uno::Reference<css::uno::XInterface> const& xObject)
+ : DocumentModelTreeEntry(rString, xObject)
+ {
+ }
+
+ bool shouldShowExpander() override { return true; }
+
+ void fill(std::unique_ptr<weld::TreeView>& pDocumentModelTree,
+ weld::TreeIter const& rParent) override
+ {
+ auto pShapesEntry
+ = std::make_unique<ShapesEntry>(SfxResId(STR_SHAPES_ENTRY), getMainObject());
+ lclAppendToParentEntry(pDocumentModelTree, rParent, pShapesEntry.release());
+
+ auto pChartsEntry
+ = std::make_unique<ChartsEntry>(SfxResId(STR_CHARTS_ENTRY), getMainObject());
+ lclAppendToParentEntry(pDocumentModelTree, rParent, pChartsEntry.release());
+
+ auto pPivotTablesEntry
+ = std::make_unique<PivotTablesEntry>(SfxResId(STR_PIVOT_TABLES_ENTRY), getMainObject());
+ lclAppendToParentEntry(pDocumentModelTree, rParent, pPivotTablesEntry.release());
+ }
+};
+
+/** Represents a list of (Calc) sheet */
+class SheetsEntry : public DocumentModelTreeEntry
+{
+public:
+ SheetsEntry(OUString const& rString, css::uno::Reference<css::uno::XInterface> const& xObject)
+ : DocumentModelTreeEntry(rString, xObject)
+ {
+ }
+
+ css::uno::Reference<css::uno::XInterface> getMainObject() override
+ {
+ uno::Reference<sheet::XSpreadsheetDocument> xSheetDocument(mxObject, uno::UNO_QUERY);
+ if (!xSheetDocument.is())
+ return mxObject;
+ return xSheetDocument->getSheets();
+ }
+
+ bool shouldShowExpander() override
+ {
+ uno::Reference<container::XIndexAccess> xIndexAccess(getMainObject(), uno::UNO_QUERY);
+ return xIndexAccess.is() && xIndexAccess->getCount() > 0;
+ }
+
+ void fill(std::unique_ptr<weld::TreeView>& pDocumentModelTree,
+ weld::TreeIter const& rParent) override
+ {
+ uno::Reference<container::XIndexAccess> xIndexAccesss(getMainObject(), uno::UNO_QUERY);
+ if (!xIndexAccesss.is())
+ return;
+
+ for (sal_Int32 i = 0; i < xIndexAccesss->getCount(); ++i)
+ {
+ uno::Reference<sheet::XSpreadsheet> xSheet(xIndexAccesss->getByIndex(i),
+ uno::UNO_QUERY);
+ OUString aString = lclGetNamed(xSheet);
+ if (aString.isEmpty())
+ aString = SfxResId(STR_SHEET).replaceFirst("%1", OUString::number(i + 1));
+ auto pEntry = std::make_unique<SheetEntry>(aString, xSheet);
+ lclAppendToParentEntry(pDocumentModelTree, rParent, pEntry.release());
+ }
+ }
+};
+
+} // end anonymous namespace
+
+DocumentModelTreeHandler::DocumentModelTreeHandler(
+ std::unique_ptr<weld::TreeView>& pDocumentModelTree,
+ css::uno::Reference<css::uno::XInterface> const& xDocument)
+ : mpDocumentModelTree(pDocumentModelTree)
+ , mxDocument(xDocument)
+{
+ mpDocumentModelTree->connect_expanding(LINK(this, DocumentModelTreeHandler, ExpandingHandler));
+}
+
+uno::Reference<uno::XInterface> DocumentModelTreeHandler::getObjectByID(OUString const& rID)
+{
+ uno::Reference<uno::XInterface> xObject;
+ if (rID.isEmpty())
+ return xObject;
+ auto* pEntry = weld::fromId<DocumentModelTreeEntry*>(rID);
+ return pEntry->getMainObject();
+}
+
+void DocumentModelTreeHandler::clearAll()
+{
+ // destroy all DocumentModelTreeEntries from the tree
+ mpDocumentModelTree->all_foreach([this](weld::TreeIter& rEntry) {
+ OUString sID = mpDocumentModelTree->get_id(rEntry);
+ auto* pEntry = weld::fromId<DocumentModelTreeEntry*>(sID);
+ delete pEntry;
+ return false;
+ });
+ mpDocumentModelTree->clear();
+}
+
+void DocumentModelTreeHandler::clearChildren(weld::TreeIter const& rParent)
+{
+ bool bChild = false;
+ do
+ {
+ bChild = mpDocumentModelTree->iter_has_child(rParent);
+ if (bChild)
+ {
+ std::unique_ptr<weld::TreeIter> pChild = mpDocumentModelTree->make_iterator(&rParent);
+ bChild = mpDocumentModelTree->iter_children(*pChild);
+ if (bChild)
+ {
+ clearChildren(*pChild);
+ OUString sID = mpDocumentModelTree->get_id(*pChild);
+ auto* pEntry = weld::fromId<DocumentModelTreeEntry*>(sID);
+ delete pEntry;
+ mpDocumentModelTree->remove(*pChild);
+ }
+ }
+ } while (bChild);
+}
+
+void DocumentModelTreeHandler::dispose()
+{
+ mpDocumentModelTree->all_foreach([this](weld::TreeIter& rEntry) {
+ OUString sID = mpDocumentModelTree->get_id(rEntry);
+ auto* pEntry = weld::fromId<DocumentModelTreeEntry*>(sID);
+ delete pEntry;
+ return false;
+ });
+}
+
+IMPL_LINK(DocumentModelTreeHandler, ExpandingHandler, weld::TreeIter const&, rParent, bool)
+{
+ OUString sID = mpDocumentModelTree->get_id(rParent);
+ if (sID.isEmpty())
+ return true;
+
+ clearChildren(rParent);
+ auto* pEntry = weld::fromId<DocumentModelTreeEntry*>(sID);
+ pEntry->fill(mpDocumentModelTree, rParent);
+
+ return true;
+}
+
+void DocumentModelTreeHandler::selectObject(
+ css::uno::Reference<css::uno::XInterface> const& xInterface)
+{
+ mpDocumentModelTree->unselect_all();
+
+ mpDocumentModelTree->all_foreach([this, xInterface](weld::TreeIter& rEntry) {
+ OUString sID = mpDocumentModelTree->get_id(rEntry);
+ auto* pEntry = weld::fromId<DocumentModelTreeEntry*>(sID);
+ if (xInterface == pEntry->getMainObject())
+ {
+ mpDocumentModelTree->select(rEntry);
+ return true;
+ }
+ return false;
+ });
+}
+
+void DocumentModelTreeHandler::inspectDocument()
+{
+ clearAll();
+
+ uno::Reference<lang::XServiceInfo> xDocumentServiceInfo(mxDocument, uno::UNO_QUERY_THROW);
+
+ lclAppend(mpDocumentModelTree, new DocumentRootEntry(SfxResId(STR_DOCUMENT_ENTRY), mxDocument));
+
+ if (xDocumentServiceInfo->supportsService("com.sun.star.sheet.SpreadsheetDocument"))
+ {
+ lclAppend(mpDocumentModelTree, new SheetsEntry(SfxResId(STR_SHEETS_ENTRY), mxDocument));
+ lclAppend(mpDocumentModelTree,
+ new StylesFamiliesEntry(SfxResId(STR_STYLES_ENTRY), mxDocument));
+ }
+ else if (xDocumentServiceInfo->supportsService(
+ "com.sun.star.presentation.PresentationDocument"))
+ {
+ lclAppend(mpDocumentModelTree, new SlidesEntry(SfxResId(STR_SLIDES_ENTRY), mxDocument));
+ lclAppend(mpDocumentModelTree,
+ new MasterSlidesEntry(SfxResId(STR_MASTER_SLIDES_ENTRY), mxDocument));
+ lclAppend(mpDocumentModelTree,
+ new StylesFamiliesEntry(SfxResId(STR_STYLES_ENTRY), mxDocument));
+ }
+ else if (xDocumentServiceInfo->supportsService("com.sun.star.drawing.DrawingDocument"))
+ {
+ lclAppend(mpDocumentModelTree, new PagesEntry(SfxResId(STR_PAGES_ENTRY), mxDocument));
+ lclAppend(mpDocumentModelTree,
+ new StylesFamiliesEntry(SfxResId(STR_STYLES_ENTRY), mxDocument));
+ }
+ else if (xDocumentServiceInfo->supportsService("com.sun.star.text.TextDocument")
+ || xDocumentServiceInfo->supportsService("com.sun.star.text.WebDocument"))
+ {
+ lclAppend(mpDocumentModelTree,
+ new ParagraphsEntry(SfxResId(STR_PARAGRAPHS_ENTRY), mxDocument));
+ lclAppend(mpDocumentModelTree, new ShapesEntry(SfxResId(STR_SHAPES_ENTRY), mxDocument));
+ lclAppend(mpDocumentModelTree, new TablesEntry(SfxResId(STR_TABLES_ENTRY), mxDocument));
+ lclAppend(mpDocumentModelTree, new FramesEntry(SfxResId(STR_FRAMES_ENTRY), mxDocument));
+ lclAppend(mpDocumentModelTree,
+ new WriterGraphicObjectsEntry(SfxResId(STR_GRAPHIC_OBJECTS_ENTRY), mxDocument));
+ lclAppend(mpDocumentModelTree,
+ new EmbeddedObjectsEntry(SfxResId(STR_EMBEDDED_OBJECTS_ENTRY), mxDocument));
+ lclAppend(mpDocumentModelTree,
+ new StylesFamiliesEntry(SfxResId(STR_STYLES_ENTRY), mxDocument));
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/devtools/ObjectInspectorTreeHandler.cxx b/sfx2/source/devtools/ObjectInspectorTreeHandler.cxx
new file mode 100644
index 000000000..18c4206e0
--- /dev/null
+++ b/sfx2/source/devtools/ObjectInspectorTreeHandler.cxx
@@ -0,0 +1,1384 @@
+/* -*- 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 <memory>
+
+#include <sfx2/devtools/ObjectInspectorTreeHandler.hxx>
+#include <sfx2/sfxresid.hxx>
+#include <vcl/svapp.hxx>
+#include "DevToolsStrings.hrc"
+
+#include <com/sun/star/beans/theIntrospection.hpp>
+#include <com/sun/star/beans/XIntrospection.hpp>
+#include <com/sun/star/beans/XIntrospectionAccess.hpp>
+#include <com/sun/star/beans/PropertyConcept.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/beans/MethodConcept.hpp>
+
+#include <com/sun/star/reflection/theCoreReflection.hpp>
+#include <com/sun/star/reflection/XIdlReflection.hpp>
+#include <com/sun/star/reflection/XIdlMethod.hpp>
+#include <com/sun/star/reflection/XIdlArray.hpp>
+#include <com/sun/star/reflection/XEnumTypeDescription.hpp>
+
+#include <com/sun/star/container/XNamed.hpp>
+#include <com/sun/star/container/XHierarchicalNameAccess.hpp>
+#include <com/sun/star/container/XIndexAccess.hpp>
+#include <com/sun/star/container/XNameAccess.hpp>
+#include <com/sun/star/container/XEnumerationAccess.hpp>
+
+#include <com/sun/star/script/Invocation.hpp>
+#include <com/sun/star/script/XInvocation2.hpp>
+#include <com/sun/star/script/MemberType.hpp>
+
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/lang/XTypeProvider.hpp>
+
+#include <comphelper/processfactory.hxx>
+#include <comphelper/extract.hxx>
+
+#include <vcl/settings.hxx>
+#include <i18nlangtag/languagetag.hxx>
+
+using namespace css;
+
+namespace
+{
+constexpr OUStringLiteral constTypeDescriptionManagerSingletonName
+ = u"/singletons/com.sun.star.reflection.theTypeDescriptionManager";
+
+OUString enumValueToEnumName(uno::Any const& aValue,
+ uno::Reference<uno::XComponentContext> const& xContext)
+{
+ sal_Int32 nIntValue = 0;
+ if (!cppu::enum2int(nIntValue, aValue))
+ return OUString();
+
+ uno::Reference<container::XHierarchicalNameAccess> xManager;
+ xManager.set(xContext->getValueByName(constTypeDescriptionManagerSingletonName),
+ uno::UNO_QUERY);
+
+ uno::Reference<reflection::XEnumTypeDescription> xTypeDescription;
+ xTypeDescription.set(xManager->getByHierarchicalName(aValue.getValueType().getTypeName()),
+ uno::UNO_QUERY);
+
+ const uno::Sequence<sal_Int32> aValues = xTypeDescription->getEnumValues();
+ sal_Int32 nValuesIndex = std::find(aValues.begin(), aValues.end(), nIntValue) - aValues.begin();
+ uno::Sequence<OUString> aNames = xTypeDescription->getEnumNames();
+ return aNames[nValuesIndex];
+}
+
+OUString getInterfaceImplementationClass(uno::Reference<uno::XInterface> const& xInterface)
+{
+ auto xServiceInfo = uno::Reference<lang::XServiceInfo>(xInterface, uno::UNO_QUERY);
+ if (xServiceInfo.is())
+ return xServiceInfo->getImplementationName();
+ return OUString();
+}
+
+/** converts basic any value to a string */
+OUString convertBasicValueToString(const uno::Any& aValue,
+ const uno::Reference<uno::XComponentContext>& xContext)
+{
+ OUString aRetStr;
+
+ // return early if we don't have any value
+ if (!aValue.hasValue())
+ return SfxResId(STR_ANY_VALUE_NULL);
+
+ uno::TypeClass eType = aValue.getValueTypeClass();
+
+ switch (eType)
+ {
+ case uno::TypeClass_BOOLEAN:
+ {
+ bool bBool = aValue.get<bool>();
+ aRetStr = bBool ? SfxResId(STR_ANY_VALUE_TRUE) : SfxResId(STR_ANY_VALUE_FALSE);
+ break;
+ }
+ case uno::TypeClass_CHAR:
+ {
+ sal_Unicode aChar = aValue.get<sal_Unicode>();
+ aRetStr = OUString::number(aChar);
+ break;
+ }
+ case uno::TypeClass_STRING:
+ {
+ aRetStr = u"\"" + aValue.get<OUString>() + u"\"";
+ break;
+ }
+ case uno::TypeClass_FLOAT:
+ {
+ auto aNumber = aValue.get<float>();
+ aRetStr = OUString::number(aNumber);
+ break;
+ }
+ case uno::TypeClass_DOUBLE:
+ {
+ auto aNumber = aValue.get<double>();
+ aRetStr = OUString::number(aNumber);
+ break;
+ }
+ case uno::TypeClass_BYTE:
+ {
+ auto aNumber = aValue.get<sal_Int8>();
+ aRetStr = OUString::number(aNumber);
+ break;
+ }
+ case uno::TypeClass_SHORT:
+ {
+ auto aNumber = aValue.get<sal_Int16>();
+ aRetStr = OUString::number(aNumber);
+ break;
+ }
+ case uno::TypeClass_LONG:
+ {
+ auto aNumber = aValue.get<sal_Int32>();
+ aRetStr = OUString::number(aNumber);
+ break;
+ }
+ case uno::TypeClass_HYPER:
+ {
+ auto aNumber = aValue.get<sal_Int64>();
+ aRetStr = OUString::number(aNumber);
+ break;
+ }
+ case uno::TypeClass_UNSIGNED_SHORT:
+ {
+ auto aNumber = aValue.get<sal_uInt16>();
+ aRetStr = OUString::number(aNumber);
+ break;
+ }
+ case uno::TypeClass_UNSIGNED_LONG:
+ {
+ auto aNumber = aValue.get<sal_uInt32>();
+ aRetStr = OUString::number(aNumber);
+ break;
+ }
+ case uno::TypeClass_UNSIGNED_HYPER:
+ {
+ auto aNumber = aValue.get<sal_uInt64>();
+ aRetStr = OUString::number(aNumber);
+ break;
+ }
+ case uno::TypeClass_TYPE:
+ {
+ auto aType = aValue.get<uno::Type>();
+ aRetStr = aType.getTypeName();
+ break;
+ }
+ case uno::TypeClass_ENUM:
+ {
+ aRetStr = enumValueToEnumName(aValue, xContext);
+ break;
+ }
+
+ default:
+ break;
+ }
+ return aRetStr;
+}
+
+// returns a name of the object, if available
+OUString getInterfaceName(uno::Reference<uno::XInterface> const& xInterface,
+ const uno::Reference<uno::XComponentContext>& xContext)
+{
+ uno::Reference<container::XNamed> xNamed(xInterface, uno::UNO_QUERY);
+ if (xNamed.is())
+ return xNamed->getName();
+
+ auto xInvocationFactory = css::script::Invocation::create(xContext);
+ uno::Sequence<uno::Any> aParameters = { uno::Any(xInterface) };
+ auto xInvocationInterface = xInvocationFactory->createInstanceWithArguments(aParameters);
+ if (xInvocationInterface.is())
+ {
+ uno::Reference<script::XInvocation2> xInvocation(xInvocationInterface, uno::UNO_QUERY);
+ if (xInvocation.is() && xInvocation->hasProperty("Name"))
+ {
+ uno::Any aAny = xInvocation->getValue("Name");
+ if (aAny.hasValue() && aAny.getValueTypeClass() == uno::TypeClass_STRING)
+ return aAny.get<OUString>();
+ }
+ }
+ return OUString();
+}
+
+OUString convertAnyToString(const uno::Any& aValue,
+ const uno::Reference<uno::XComponentContext>& xContext)
+{
+ // return early if we don't have any value
+ if (!aValue.hasValue())
+ return SfxResId(STR_ANY_VALUE_NULL);
+
+ OUString aRetStr;
+
+ uno::TypeClass eType = aValue.getValueTypeClass();
+
+ switch (eType)
+ {
+ case uno::TypeClass_INTERFACE:
+ {
+ uno::Reference<uno::XInterface> xInterface(aValue, uno::UNO_QUERY);
+ if (!xInterface.is())
+ aRetStr = SfxResId(STR_ANY_VALUE_NULL);
+ else
+ {
+ OUString aImplementationClass = getInterfaceImplementationClass(xInterface);
+ if (aImplementationClass.isEmpty())
+ aImplementationClass = SfxResId(STR_CLASS_UNKNOWN);
+ aRetStr
+ = SfxResId(STR_PROPERTY_VALUE_OBJECT).replaceFirst("%1", aImplementationClass);
+
+ OUString aString = getInterfaceName(xInterface, xContext);
+ if (!aString.isEmpty())
+ aRetStr += " {" + aString + "}";
+ }
+ break;
+ }
+ case uno::TypeClass_STRUCT:
+ {
+ aRetStr = SfxResId(STR_PROPERTY_VALUE_STRUCT);
+ break;
+ }
+ default:
+ {
+ aRetStr = convertBasicValueToString(aValue, xContext);
+ break;
+ }
+ }
+ return aRetStr;
+}
+
+OUString convertAnyToShortenedString(const uno::Any& aValue,
+ const uno::Reference<uno::XComponentContext>& xContext)
+{
+ // return early if we don't have any value
+ if (!aValue.hasValue())
+ return SfxResId(STR_ANY_VALUE_NULL);
+
+ OUString aRetStr;
+
+ uno::TypeClass eType = aValue.getValueTypeClass();
+
+ constexpr const sal_Int32 constMaxStringLength = 60;
+
+ switch (eType)
+ {
+ case uno::TypeClass_INTERFACE:
+ {
+ aRetStr = convertAnyToString(aValue, xContext);
+
+ if (aRetStr.getLength() > constMaxStringLength + 3)
+ aRetStr = OUString::Concat(aRetStr.subView(0, constMaxStringLength)) + u"...";
+ break;
+ }
+ case uno::TypeClass_STRING:
+ {
+ OUString aString = convertAnyToString(aValue, xContext);
+ if (aString.getLength() > constMaxStringLength + 4)
+ aString = OUString::Concat(aString.subView(0, constMaxStringLength)) + u"\"...";
+ aRetStr = aString.replaceAll("\n", " ");
+ break;
+ }
+ default:
+ {
+ aRetStr = convertAnyToString(aValue, xContext);
+ break;
+ }
+ }
+ return aRetStr;
+}
+
+/** converts an any's type to a string (in a short form) */
+OUString getAnyType(const uno::Any& aValue)
+{
+ OUString aTypeName = aValue.getValueType().getTypeName();
+ return aTypeName.replaceAll("com.sun.star", "css");
+}
+
+/** converts a Type to a XIdlClass */
+uno::Reference<reflection::XIdlClass>
+convertTypeToIdlClass(const uno::Type& rType,
+ const uno::Reference<uno::XComponentContext>& xContext)
+{
+ auto xReflection = reflection::theCoreReflection::get(xContext);
+ return xReflection->forName(rType.getTypeName());
+}
+
+// Object inspector nodes
+
+/** Object inspector node's main interface
+ *
+ * The interface for the "attached" object to a tree view nodes that
+ * are added to the tree views of the object inspector part. The node
+ * can return the main value of the node (object name) and if present
+ * also the values for additional columns. It signals if a tree needs
+ * an expander and fills the children of the tree is any exists.
+ *
+ */
+class ObjectInspectorNodeInterface
+{
+public:
+ ObjectInspectorNodeInterface() = default;
+
+ virtual ~ObjectInspectorNodeInterface() {}
+
+ // main value (object name) of the tree view node
+ virtual OUString getObjectName() = 0;
+
+ // should show the expander for the tree view node
+ virtual bool shouldShowExpander() { return false; }
+
+ // fill the children for the current tree view node
+ virtual void fillChildren(std::unique_ptr<weld::TreeView>& rTree, const weld::TreeIter* pParent)
+ = 0;
+
+ // fill any additional column values for the current tree view node
+ virtual std::vector<std::pair<sal_Int32, OUString>> getColumnValues()
+ {
+ return std::vector<std::pair<sal_Int32, OUString>>();
+ }
+};
+
+// appends the node to the root of the tree view
+OUString lclAppendNode(const std::unique_ptr<weld::TreeView>& pTree,
+ ObjectInspectorNodeInterface* pEntry)
+{
+ OUString sName = pEntry->getObjectName();
+ OUString sId(weld::toId(pEntry));
+ std::unique_ptr<weld::TreeIter> pCurrent = pTree->make_iterator();
+ pTree->insert(nullptr, -1, &sName, &sId, nullptr, nullptr, pEntry->shouldShowExpander(),
+ pCurrent.get());
+ pTree->set_text_emphasis(*pCurrent, true, 0);
+
+ for (auto const& rPair : pEntry->getColumnValues())
+ {
+ pTree->set_text(*pCurrent, rPair.second, rPair.first);
+ }
+
+ return sId;
+}
+
+// appends the node to the parent
+OUString lclAppendNodeToParent(const std::unique_ptr<weld::TreeView>& pTree,
+ const weld::TreeIter* pParent, ObjectInspectorNodeInterface* pEntry)
+{
+ OUString sName = pEntry->getObjectName();
+ OUString sId(weld::toId(pEntry));
+ std::unique_ptr<weld::TreeIter> pCurrent = pTree->make_iterator();
+ pTree->insert(pParent, -1, &sName, &sId, nullptr, nullptr, pEntry->shouldShowExpander(),
+ pCurrent.get());
+ pTree->set_text_emphasis(*pCurrent, true, 0);
+
+ for (auto const& rPair : pEntry->getColumnValues())
+ {
+ pTree->set_text(*pCurrent, rPair.second, rPair.first);
+ }
+
+ return sId;
+}
+
+/** Node that represent just a simple string with no children or columns */
+class SimpleStringNode : public ObjectInspectorNodeInterface
+{
+protected:
+ OUString msName;
+
+public:
+ SimpleStringNode(OUString const& rName)
+ : msName(rName)
+ {
+ }
+
+ void fillChildren(std::unique_ptr<weld::TreeView>& /*rTree*/,
+ const weld::TreeIter* /*pParent*/) override
+ {
+ }
+
+ OUString getObjectName() override { return msName; }
+};
+
+/** Node represents a method of an object */
+class MethodNode : public ObjectInspectorNodeInterface
+{
+private:
+ uno::Reference<reflection::XIdlMethod> mxMethod;
+
+public:
+ MethodNode(uno::Reference<reflection::XIdlMethod> const& xMethod)
+ : mxMethod(xMethod)
+ {
+ }
+
+ OUString getObjectName() override { return mxMethod->getName(); }
+
+ static OUString simpleTypeName(uno::Reference<reflection::XIdlClass> const& xClass)
+ {
+ switch (xClass->getTypeClass())
+ {
+ case uno::TypeClass_INTERFACE:
+ return SfxResId(STR_METHOD_TYPE_OBJECT);
+ case uno::TypeClass_STRUCT:
+ return SfxResId(STR_METHOD_TYPE_STRUCT);
+ case uno::TypeClass_ENUM:
+ return SfxResId(STR_METHOD_TYPE_ENUM);
+ case uno::TypeClass_SEQUENCE:
+ return SfxResId(STR_METHOD_TYPE_SEQUENCE);
+ default:
+ break;
+ }
+ return xClass->getName();
+ }
+
+ std::vector<std::pair<sal_Int32, OUString>> getColumnValues() override
+ {
+ OUString aOutString;
+ auto xClass = mxMethod->getReturnType();
+ aOutString = simpleTypeName(xClass);
+
+ OUString aInString;
+ const auto aParameters = mxMethod->getParameterInfos();
+ bool bFirst = true;
+ for (auto const& rParameterInfo : aParameters)
+ {
+ if (!bFirst)
+ aInString += ", ";
+ else
+ bFirst = false;
+
+ switch (rParameterInfo.aMode)
+ {
+ case reflection::ParamMode_IN:
+ aInString += SfxResId(STR_PARMETER_MODE_IN) + " ";
+ break;
+ case reflection::ParamMode_OUT:
+ aInString += SfxResId(STR_PARMETER_MODE_OUT) + " ";
+ break;
+ case reflection::ParamMode_INOUT:
+ aInString += SfxResId(STR_PARMETER_MODE_IN_AND_OUT) + " ";
+ break;
+ default:
+ break;
+ }
+
+ aInString += rParameterInfo.aName + " : " + simpleTypeName(rParameterInfo.aType);
+ }
+
+ OUString aImplementationClass = mxMethod->getDeclaringClass()->getName();
+
+ return {
+ { 1, aOutString },
+ { 2, aInString },
+ { 3, aImplementationClass },
+ };
+ }
+
+ void fillChildren(std::unique_ptr<weld::TreeView>& /*rTree*/,
+ const weld::TreeIter* /*pParent*/) override
+ {
+ }
+};
+
+/** Node represents a class (XIdlClass) of an object.
+ *
+ * Children are superclasses of the current class. XInterface superclass
+ * is ignored.
+ *
+ */
+class ClassNode : public ObjectInspectorNodeInterface
+{
+private:
+ uno::Reference<reflection::XIdlClass> mxClass;
+
+ static bool isXInterface(uno::Reference<reflection::XIdlClass> const& xClass)
+ {
+ return xClass->getName() == "com.sun.star.uno.XInterface";
+ }
+
+public:
+ ClassNode(uno::Reference<reflection::XIdlClass> const& xClass)
+ : mxClass(xClass)
+ {
+ }
+
+ bool shouldShowExpander() override
+ {
+ auto const& xSuperClasses = mxClass->getSuperclasses();
+ return xSuperClasses.getLength() > 2
+ || (xSuperClasses.getLength() == 1 && !isXInterface(xSuperClasses[0]));
+ }
+
+ OUString getObjectName() override { return mxClass->getName(); }
+
+ // Fill superclasses
+ void fillChildren(std::unique_ptr<weld::TreeView>& rTree,
+ const weld::TreeIter* pParent) override
+ {
+ auto const& xSuperClasses = mxClass->getSuperclasses();
+ for (auto const& xSuper : xSuperClasses)
+ {
+ if (!isXInterface(xSuper))
+ lclAppendNodeToParent(rTree, pParent, new ClassNode(xSuper));
+ }
+ }
+};
+
+/** Node represents a basic value, that can be any object, sequence, struct */
+class BasicValueNode : public SimpleStringNode
+{
+protected:
+ uno::Any maAny;
+ OUString mrInfo;
+ uno::Reference<uno::XComponentContext> mxContext;
+
+ ObjectInspectorNodeInterface*
+ createNodeObjectForAny(OUString const& rName, const uno::Any& rAny, OUString const& mrInfo);
+
+public:
+ BasicValueNode(OUString const& rName, uno::Any const& rAny, OUString const& rInfo,
+ uno::Reference<uno::XComponentContext> const& xContext)
+ : SimpleStringNode(rName)
+ , maAny(rAny)
+ , mrInfo(rInfo)
+ , mxContext(xContext)
+ {
+ }
+
+ const uno::Any& getAny() const { return maAny; }
+
+ bool shouldShowExpander() override
+ {
+ if (maAny.hasValue())
+ {
+ switch (maAny.getValueType().getTypeClass())
+ {
+ case uno::TypeClass_INTERFACE:
+ {
+ uno::Reference<uno::XInterface> xInterface(maAny, uno::UNO_QUERY);
+ return xInterface.is();
+ }
+ case uno::TypeClass_SEQUENCE:
+ return true;
+ default:
+ break;
+ }
+ }
+ return false;
+ }
+
+ std::vector<std::pair<sal_Int32, OUString>> getColumnValues() override
+ {
+ OUString aValue = convertAnyToShortenedString(maAny, mxContext);
+ OUString aType = getAnyType(maAny);
+
+ return { { 1, aValue }, { 2, aType }, { 3, mrInfo } };
+ }
+};
+
+/** Node represents a property */
+class GenericPropertiesNode : public BasicValueNode
+{
+public:
+ GenericPropertiesNode(OUString const& rName, uno::Any const& rAny, OUString const& rInfo,
+ uno::Reference<uno::XComponentContext> const& xContext)
+ : BasicValueNode(rName, rAny, rInfo, xContext)
+ {
+ }
+
+ void fillChildren(std::unique_ptr<weld::TreeView>& pTree,
+ const weld::TreeIter* pParent) override;
+};
+
+/** Node represents a struct */
+class StructNode : public BasicValueNode
+{
+public:
+ StructNode(OUString const& rName, uno::Any const& rAny, OUString const& rInfo,
+ uno::Reference<uno::XComponentContext> const& xContext)
+ : BasicValueNode(rName, rAny, rInfo, xContext)
+ {
+ }
+
+ bool shouldShowExpander() override { return true; }
+
+ void fillChildren(std::unique_ptr<weld::TreeView>& pTree,
+ const weld::TreeIter* pParent) override;
+};
+
+/** Node represents a sequence */
+class SequenceNode : public BasicValueNode
+{
+ uno::Reference<reflection::XIdlArray> mxIdlArray;
+
+public:
+ SequenceNode(OUString const& rName, uno::Any const& rAny, OUString const& rInfo,
+ uno::Reference<uno::XComponentContext> const& xContext)
+ : BasicValueNode(rName, rAny, rInfo, xContext)
+ {
+ auto xClass = convertTypeToIdlClass(maAny.getValueType(), mxContext);
+ mxIdlArray = xClass->getArray();
+ }
+
+ bool shouldShowExpander() override
+ {
+ // Show expander only if the sequence has elements
+ int nLength = mxIdlArray->getLen(maAny);
+ return nLength > 0;
+ }
+
+ void fillChildren(std::unique_ptr<weld::TreeView>& pTree,
+ const weld::TreeIter* pParent) override
+ {
+ int nLength = mxIdlArray->getLen(maAny);
+
+ for (int i = 0; i < nLength; i++)
+ {
+ uno::Any aArrayValue = mxIdlArray->get(maAny, i);
+
+ auto* pObjectInspectorNode
+ = createNodeObjectForAny(OUString::number(i), aArrayValue, "");
+ if (pObjectInspectorNode)
+ lclAppendNodeToParent(pTree, pParent, pObjectInspectorNode);
+ }
+ }
+
+ std::vector<std::pair<sal_Int32, OUString>> getColumnValues() override
+ {
+ int nLength = mxIdlArray->getLen(maAny);
+
+ OUString aType = getAnyType(maAny).replaceAll(u"[]", u"");
+ aType += u"[" + OUString::number(nLength) + u"]";
+
+ OUString aValue
+ = SfxResId(STR_PROPERTY_VALUE_SEQUENCE).replaceFirst("%1", OUString::number(nLength));
+
+ return {
+ { 1, aValue },
+ { 2, aType },
+ };
+ }
+};
+
+void GenericPropertiesNode::fillChildren(std::unique_ptr<weld::TreeView>& pTree,
+ const weld::TreeIter* pParent)
+{
+ if (!maAny.hasValue())
+ return;
+
+ try
+ {
+ const auto xNameAccess = uno::Reference<container::XNameAccess>(maAny, uno::UNO_QUERY);
+ if (xNameAccess.is())
+ {
+ const uno::Sequence<OUString> aNames = xNameAccess->getElementNames();
+ for (OUString const& rName : aNames)
+ {
+ uno::Any aAny = xNameAccess->getByName(rName);
+ auto* pObjectInspectorNode = createNodeObjectForAny(
+ u"@" + rName, aAny, SfxResId(STR_PROPERTY_TYPE_IS_NAMED_CONTAINER));
+ lclAppendNodeToParent(pTree, pParent, pObjectInspectorNode);
+ }
+ }
+ }
+ catch (...)
+ {
+ }
+
+ try
+ {
+ const auto xIndexAccess = uno::Reference<container::XIndexAccess>(maAny, uno::UNO_QUERY);
+ if (xIndexAccess.is())
+ {
+ for (sal_Int32 nIndex = 0; nIndex < xIndexAccess->getCount(); ++nIndex)
+ {
+ uno::Any aAny = xIndexAccess->getByIndex(nIndex);
+ auto* pObjectInspectorNode
+ = createNodeObjectForAny(u"@" + OUString::number(nIndex), aAny,
+ SfxResId(STR_PROPERTY_TYPE_IS_INDEX_CONTAINER));
+ lclAppendNodeToParent(pTree, pParent, pObjectInspectorNode);
+ }
+ }
+ }
+ catch (...)
+ {
+ }
+
+ try
+ {
+ const auto xEnumAccess
+ = uno::Reference<container::XEnumerationAccess>(maAny, uno::UNO_QUERY);
+ if (xEnumAccess.is())
+ {
+ uno::Reference<container::XEnumeration> xEnumeration = xEnumAccess->createEnumeration();
+ if (xEnumeration.is())
+ {
+ for (sal_Int32 nIndex = 0; xEnumeration->hasMoreElements(); nIndex++)
+ {
+ uno::Any aAny = xEnumeration->nextElement();
+ auto* pObjectInspectorNode
+ = createNodeObjectForAny(u"@" + OUString::number(nIndex), aAny,
+ SfxResId(STR_PROPERTY_TYPE_IS_ENUMERATION));
+ lclAppendNodeToParent(pTree, pParent, pObjectInspectorNode);
+ }
+ }
+ }
+ }
+ catch (...)
+ {
+ }
+
+ auto xInvocationFactory = css::script::Invocation::create(mxContext);
+ uno::Sequence<uno::Any> aParameters = { maAny };
+ auto xInvocationInterface = xInvocationFactory->createInstanceWithArguments(aParameters);
+ if (!xInvocationInterface.is())
+ return;
+
+ uno::Reference<script::XInvocation2> xInvocation(xInvocationInterface, uno::UNO_QUERY);
+ if (!xInvocation.is())
+ return;
+
+ auto const& xInvocationAccess = xInvocation->getIntrospection();
+ if (!xInvocationAccess.is())
+ return;
+
+ uno::Sequence<script::InvocationInfo> aInvocationInfoSequence;
+ try
+ {
+ aInvocationInfoSequence = xInvocation->getInfo();
+ }
+ catch (...)
+ {
+ }
+
+ for (auto const& aInvocationInfo : std::as_const(aInvocationInfoSequence))
+ {
+ if (aInvocationInfo.eMemberType == script::MemberType_PROPERTY)
+ {
+ uno::Any aCurrentAny;
+ auto const& aPropertyName = aInvocationInfo.aName;
+
+ bool bIsAttribute = false;
+ bool bIsGetSetMethod = false;
+ bool bMethodGet = false;
+ bool bMethodSet = false;
+ bool bMethodIs = false;
+ try
+ {
+ aCurrentAny = xInvocation->getValue(aPropertyName);
+ bIsAttribute = xInvocationAccess->hasProperty(aPropertyName,
+ beans::PropertyConcept::ATTRIBUTES);
+ bIsGetSetMethod = xInvocationAccess->hasProperty(aPropertyName,
+ beans::PropertyConcept::METHODS);
+ if (bIsGetSetMethod)
+ {
+ bMethodGet = xInvocationAccess->hasMethod(u"get" + aPropertyName,
+ beans::MethodConcept::PROPERTY);
+ bMethodSet = xInvocationAccess->hasMethod(u"set" + aPropertyName,
+ beans::MethodConcept::PROPERTY);
+ bMethodIs = xInvocationAccess->hasMethod(u"is" + aPropertyName,
+ beans::MethodConcept::PROPERTY);
+ }
+ }
+ catch (...)
+ {
+ }
+
+ std::vector<OUString> aInfoCollection;
+ if (bIsAttribute)
+ aInfoCollection.push_back(SfxResId(STR_PROPERTY_ATTRIBUTE_IS_ATTRIBUTE));
+ if (bIsGetSetMethod)
+ {
+ bool bHasGet = false;
+ OUString aString;
+ if (bMethodGet || bMethodIs)
+ {
+ aString += SfxResId(STR_PROPERTY_ATTRIBUTE_GET);
+ bHasGet = true;
+ }
+ if (bMethodSet)
+ {
+ if (bHasGet)
+ aString += u"+";
+ aString += SfxResId(STR_PROPERTY_ATTRIBUTE_SET);
+ }
+ aInfoCollection.push_back(aString);
+ if (bMethodSet && !bHasGet)
+ aInfoCollection.push_back(SfxResId(STR_PROPERTY_ATTRIBUTE_WRITEONLY));
+ }
+ if (aInvocationInfo.PropertyAttribute & beans::PropertyAttribute::MAYBEVOID)
+ aInfoCollection.push_back(SfxResId(STR_PROPERTY_ATTRIBUTE_MAYBEVOID));
+ if (aInvocationInfo.PropertyAttribute & beans::PropertyAttribute::READONLY)
+ aInfoCollection.push_back(SfxResId(STR_PROPERTY_ATTRIBUTE_READONLY));
+ if (aInvocationInfo.PropertyAttribute & beans::PropertyAttribute::REMOVABLE)
+ aInfoCollection.push_back(SfxResId(STR_PROPERTY_ATTRIBUTE_REMOVABLE));
+ if (aInvocationInfo.PropertyAttribute & beans::PropertyAttribute::BOUND)
+ aInfoCollection.push_back(SfxResId(STR_PROPERTY_ATTRIBUTE_BOUND));
+ if (aInvocationInfo.PropertyAttribute & beans::PropertyAttribute::CONSTRAINED)
+ aInfoCollection.push_back(SfxResId(STR_PROPERTY_ATTRIBUTE_CONSTRAINED));
+ if (aInvocationInfo.PropertyAttribute & beans::PropertyAttribute::TRANSIENT)
+ aInfoCollection.push_back(SfxResId(STR_PROPERTY_ATTRIBUTE_TRANSIENT));
+ if (aInvocationInfo.PropertyAttribute & beans::PropertyAttribute::MAYBEAMBIGUOUS)
+ aInfoCollection.push_back(SfxResId(STR_PROPERTY_ATTRIBUTE_MAYBEAMBIGUOUS));
+ if (aInvocationInfo.PropertyAttribute & beans::PropertyAttribute::MAYBEDEFAULT)
+ aInfoCollection.push_back(SfxResId(STR_PROPERTY_ATTRIBUTE_MAYBEDEFAULT));
+
+ bool bSet = false;
+ OUString aInfoString;
+ for (auto const& rString : aInfoCollection)
+ {
+ if (bSet)
+ aInfoString += ", ";
+ else
+ bSet = true;
+
+ aInfoString += rString;
+ }
+
+ auto* pObjectInspectorNode
+ = createNodeObjectForAny(aPropertyName, aCurrentAny, aInfoString);
+ if (pObjectInspectorNode)
+ lclAppendNodeToParent(pTree, pParent, pObjectInspectorNode);
+ }
+ }
+}
+
+void StructNode::fillChildren(std::unique_ptr<weld::TreeView>& pTree, const weld::TreeIter* pParent)
+{
+ auto xReflection = reflection::theCoreReflection::get(mxContext);
+ uno::Reference<reflection::XIdlClass> xClass
+ = xReflection->forName(maAny.getValueType().getTypeName());
+
+ const auto xFields = xClass->getFields();
+
+ for (auto const& xField : xFields)
+ {
+ OUString aFieldName = xField->getName();
+ uno::Any aFieldValue = xField->get(maAny);
+
+ auto* pObjectInspectorNode = createNodeObjectForAny(aFieldName, aFieldValue, "");
+ if (pObjectInspectorNode)
+ {
+ lclAppendNodeToParent(pTree, pParent, pObjectInspectorNode);
+ }
+ }
+}
+
+ObjectInspectorNodeInterface* BasicValueNode::createNodeObjectForAny(OUString const& rName,
+ const uno::Any& rAny,
+ OUString const& rInfo)
+{
+ switch (rAny.getValueType().getTypeClass())
+ {
+ case uno::TypeClass_INTERFACE:
+ return new GenericPropertiesNode(rName, rAny, rInfo, mxContext);
+
+ case uno::TypeClass_SEQUENCE:
+ return new SequenceNode(rName, rAny, rInfo, mxContext);
+
+ case uno::TypeClass_STRUCT:
+ return new StructNode(rName, rAny, rInfo, mxContext);
+
+ default:
+ break;
+ }
+
+ return new BasicValueNode(rName, rAny, rInfo, mxContext);
+}
+
+} // end anonymous namespace
+
+// Object inspector tree view helper functions
+namespace
+{
+ObjectInspectorNodeInterface* getSelectedNode(weld::TreeView const& rTreeView)
+{
+ OUString sID = rTreeView.get_selected_id();
+ if (sID.isEmpty())
+ return nullptr;
+
+ if (auto* pNode = weld::fromId<ObjectInspectorNodeInterface*>(sID))
+ return pNode;
+
+ return nullptr;
+}
+
+uno::Reference<uno::XInterface> getSelectedXInterface(weld::TreeView const& rTreeView)
+{
+ uno::Reference<uno::XInterface> xInterface;
+
+ if (auto* pNode = getSelectedNode(rTreeView))
+ {
+ if (auto* pBasicValueNode = dynamic_cast<BasicValueNode*>(pNode))
+ {
+ uno::Any aAny = pBasicValueNode->getAny();
+ xInterface.set(aAny, uno::UNO_QUERY);
+ }
+ }
+
+ return xInterface;
+}
+
+} // end anonymous namespace
+
+ObjectInspectorTreeHandler::ObjectInspectorTreeHandler(
+ std::unique_ptr<ObjectInspectorWidgets>& pObjectInspectorWidgets)
+ : mpObjectInspectorWidgets(pObjectInspectorWidgets)
+ , mxContext(comphelper::getProcessComponentContext())
+ , mxSorter(mxContext, Application::GetSettings().GetLanguageTag().getLocale())
+{
+ mpObjectInspectorWidgets->mpInterfacesTreeView->connect_expanding(
+ LINK(this, ObjectInspectorTreeHandler, ExpandingHandlerInterfaces));
+ mpObjectInspectorWidgets->mpServicesTreeView->connect_expanding(
+ LINK(this, ObjectInspectorTreeHandler, ExpandingHandlerServices));
+ mpObjectInspectorWidgets->mpPropertiesTreeView->connect_expanding(
+ LINK(this, ObjectInspectorTreeHandler, ExpandingHandlerProperties));
+ mpObjectInspectorWidgets->mpMethodsTreeView->connect_expanding(
+ LINK(this, ObjectInspectorTreeHandler, ExpandingHandlerMethods));
+
+ mpObjectInspectorWidgets->mpPropertiesTreeView->connect_popup_menu(
+ LINK(this, ObjectInspectorTreeHandler, PopupMenuHandler));
+
+ mpObjectInspectorWidgets->mpInterfacesTreeView->connect_changed(
+ LINK(this, ObjectInspectorTreeHandler, SelectionChanged));
+ mpObjectInspectorWidgets->mpServicesTreeView->connect_changed(
+ LINK(this, ObjectInspectorTreeHandler, SelectionChanged));
+ mpObjectInspectorWidgets->mpPropertiesTreeView->connect_changed(
+ LINK(this, ObjectInspectorTreeHandler, SelectionChanged));
+ mpObjectInspectorWidgets->mpMethodsTreeView->connect_changed(
+ LINK(this, ObjectInspectorTreeHandler, SelectionChanged));
+
+ mpObjectInspectorWidgets->mpInterfacesTreeView->make_sorted();
+ mpObjectInspectorWidgets->mpServicesTreeView->make_sorted();
+ mpObjectInspectorWidgets->mpPropertiesTreeView->make_sorted();
+ mpObjectInspectorWidgets->mpMethodsTreeView->make_sorted();
+
+ setSortFunction(mpObjectInspectorWidgets->mpInterfacesTreeView);
+ setSortFunction(mpObjectInspectorWidgets->mpServicesTreeView);
+ setSortFunction(mpObjectInspectorWidgets->mpPropertiesTreeView);
+ setSortFunction(mpObjectInspectorWidgets->mpMethodsTreeView);
+
+ mpObjectInspectorWidgets->mpInterfacesTreeView->connect_column_clicked(
+ LINK(this, ObjectInspectorTreeHandler, HeaderBarClick));
+ mpObjectInspectorWidgets->mpServicesTreeView->connect_column_clicked(
+ LINK(this, ObjectInspectorTreeHandler, HeaderBarClick));
+ mpObjectInspectorWidgets->mpPropertiesTreeView->connect_column_clicked(
+ LINK(this, ObjectInspectorTreeHandler, HeaderBarClick));
+ mpObjectInspectorWidgets->mpMethodsTreeView->connect_column_clicked(
+ LINK(this, ObjectInspectorTreeHandler, HeaderBarClick));
+
+ mpObjectInspectorWidgets->mpToolbar->connect_clicked(
+ LINK(this, ObjectInspectorTreeHandler, ToolbarButtonClicked));
+ mpObjectInspectorWidgets->mpToolbar->set_item_sensitive("inspect", false);
+ mpObjectInspectorWidgets->mpToolbar->set_item_sensitive("back", false);
+
+ mpObjectInspectorWidgets->mpNotebook->connect_leave_page(
+ LINK(this, ObjectInspectorTreeHandler, NotebookLeavePage));
+ mpObjectInspectorWidgets->mpNotebook->connect_enter_page(
+ LINK(this, ObjectInspectorTreeHandler, NotebookEnterPage));
+
+ auto nPropertiesDigitWidth
+ = mpObjectInspectorWidgets->mpPropertiesTreeView->get_approximate_digit_width();
+ std::vector<int> aPropertiesWidths(4, nPropertiesDigitWidth * 30);
+ mpObjectInspectorWidgets->mpPropertiesTreeView->set_column_fixed_widths(aPropertiesWidths);
+
+ auto nMethodsDigitWidth
+ = mpObjectInspectorWidgets->mpMethodsTreeView->get_approximate_digit_width();
+ std::vector<int> aMethodsWidths{ static_cast<int>(nMethodsDigitWidth * 30),
+ static_cast<int>(nMethodsDigitWidth * 15),
+ static_cast<int>(nMethodsDigitWidth * 30),
+ static_cast<int>(nMethodsDigitWidth * 50) };
+ mpObjectInspectorWidgets->mpMethodsTreeView->set_column_fixed_widths(aMethodsWidths);
+
+ mpObjectInspectorWidgets->mpPaned->set_position(160);
+}
+
+void ObjectInspectorTreeHandler::setSortFunction(std::unique_ptr<weld::TreeView>& pTreeView)
+{
+ pTreeView->set_sort_func(
+ [this, &pTreeView](const weld::TreeIter& rLeft, const weld::TreeIter& rRight) {
+ return compare(pTreeView, rLeft, rRight);
+ });
+}
+
+sal_Int32 ObjectInspectorTreeHandler::compare(std::unique_ptr<weld::TreeView>& pTreeView,
+ const weld::TreeIter& rLeft,
+ const weld::TreeIter& rRight)
+{
+ int nSortColumn = pTreeView->get_sort_column();
+
+ OUString sLeft = pTreeView->get_text(rLeft, nSortColumn);
+ OUString sRight = pTreeView->get_text(rRight, nSortColumn);
+ sal_Int32 nCompare = mxSorter.compare(sLeft, sRight);
+ return nCompare;
+}
+
+void ObjectInspectorTreeHandler::handleExpanding(std::unique_ptr<weld::TreeView>& pTreeView,
+ weld::TreeIter const& rParent)
+{
+ OUString sID = pTreeView->get_id(rParent);
+ if (sID.isEmpty())
+ return;
+
+ clearObjectInspectorChildren(pTreeView, rParent);
+ auto* pNode = weld::fromId<ObjectInspectorNodeInterface*>(sID);
+ pNode->fillChildren(pTreeView, &rParent);
+}
+
+IMPL_LINK(ObjectInspectorTreeHandler, ExpandingHandlerInterfaces, weld::TreeIter const&, rParent,
+ bool)
+{
+ handleExpanding(mpObjectInspectorWidgets->mpInterfacesTreeView, rParent);
+ return true;
+}
+
+IMPL_LINK(ObjectInspectorTreeHandler, ExpandingHandlerServices, weld::TreeIter const&, rParent,
+ bool)
+{
+ handleExpanding(mpObjectInspectorWidgets->mpServicesTreeView, rParent);
+ return true;
+}
+
+IMPL_LINK(ObjectInspectorTreeHandler, ExpandingHandlerProperties, weld::TreeIter const&, rParent,
+ bool)
+{
+ handleExpanding(mpObjectInspectorWidgets->mpPropertiesTreeView, rParent);
+ return true;
+}
+
+IMPL_LINK(ObjectInspectorTreeHandler, ExpandingHandlerMethods, weld::TreeIter const&, rParent, bool)
+{
+ handleExpanding(mpObjectInspectorWidgets->mpMethodsTreeView, rParent);
+ return true;
+}
+
+IMPL_LINK(ObjectInspectorTreeHandler, SelectionChanged, weld::TreeView&, rTreeView, void)
+{
+ bool bHaveNodeWithObject = false;
+ mpObjectInspectorWidgets->mpTextView->set_text("");
+ if (mpObjectInspectorWidgets->mpPropertiesTreeView.get() == &rTreeView)
+ {
+ auto* pNode = getSelectedNode(rTreeView);
+ if (auto* pBasicValueNode = dynamic_cast<BasicValueNode*>(pNode))
+ {
+ uno::Any aAny = pBasicValueNode->getAny();
+ uno::Reference<uno::XInterface> xInterface(aAny, uno::UNO_QUERY);
+ bHaveNodeWithObject = xInterface.is();
+ mpObjectInspectorWidgets->mpTextView->set_text(convertAnyToString(aAny, mxContext));
+ }
+ }
+
+ mpObjectInspectorWidgets->mpToolbar->set_item_sensitive("inspect", bHaveNodeWithObject);
+}
+
+static void updateOrder(const std::unique_ptr<weld::TreeView>& pTreeView, sal_Int32 nColumn)
+{
+ pTreeView->set_sort_column(nColumn);
+
+ bool bSortAtoZ = pTreeView->get_sort_order();
+ pTreeView->set_sort_order(!bSortAtoZ);
+ pTreeView->set_sort_indicator(!bSortAtoZ ? TRISTATE_TRUE : TRISTATE_FALSE, nColumn);
+}
+
+IMPL_LINK(ObjectInspectorTreeHandler, HeaderBarClick, int, nColumn, void)
+{
+ auto rPageId = mpObjectInspectorWidgets->mpNotebook->get_current_page_ident();
+
+ if (rPageId == "object_inspector_interfaces_tab")
+ updateOrder(mpObjectInspectorWidgets->mpInterfacesTreeView, nColumn);
+ else if (rPageId == "object_inspector_services_tab")
+ updateOrder(mpObjectInspectorWidgets->mpServicesTreeView, nColumn);
+ else if (rPageId == "object_inspector_properties_tab")
+ updateOrder(mpObjectInspectorWidgets->mpPropertiesTreeView, nColumn);
+ else if (rPageId == "object_inspector_methods_tab")
+ updateOrder(mpObjectInspectorWidgets->mpMethodsTreeView, nColumn);
+}
+
+IMPL_LINK(ObjectInspectorTreeHandler, PopupMenuHandler, const CommandEvent&, rCommandEvent, bool)
+{
+ if (rCommandEvent.GetCommand() != CommandEventId::ContextMenu)
+ return false;
+
+ auto xInterface = getSelectedXInterface(*mpObjectInspectorWidgets->mpPropertiesTreeView);
+ if (xInterface.is())
+ {
+ std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(
+ mpObjectInspectorWidgets->mpPropertiesTreeView.get(), "sfx/ui/devtoolsmenu.ui"));
+ std::unique_ptr<weld::Menu> xMenu(xBuilder->weld_menu("inspect_menu"));
+
+ OString sCommand(
+ xMenu->popup_at_rect(mpObjectInspectorWidgets->mpPropertiesTreeView.get(),
+ tools::Rectangle(rCommandEvent.GetMousePosPixel(), Size(1, 1))));
+
+ if (sCommand == "inspect")
+ {
+ addToStack(uno::Any(xInterface));
+ inspectObject(xInterface);
+ }
+ }
+ return true;
+}
+
+IMPL_LINK(ObjectInspectorTreeHandler, ToolbarButtonClicked, const OString&, rSelectionId, void)
+{
+ if (rSelectionId == "inspect")
+ {
+ auto xInterface = getSelectedXInterface(*mpObjectInspectorWidgets->mpPropertiesTreeView);
+ if (xInterface.is())
+ {
+ addToStack(uno::Any(xInterface));
+ inspectObject(xInterface);
+ }
+ }
+ else if (rSelectionId == "back")
+ {
+ uno::Any aAny = popFromStack();
+ if (aAny.hasValue())
+ {
+ uno::Reference<uno::XInterface> xInterface(aAny, uno::UNO_QUERY);
+ inspectObject(xInterface);
+ }
+ }
+ else if (rSelectionId == "refresh")
+ {
+ auto rPageId = mpObjectInspectorWidgets->mpNotebook->get_current_page_ident();
+ NotebookEnterPage(rPageId);
+ }
+}
+
+IMPL_LINK(ObjectInspectorTreeHandler, NotebookEnterPage, const OString&, rPageId, void)
+{
+ uno::Any aAny = maInspectionStack.back();
+ if (!aAny.hasValue())
+ return;
+
+ uno::Reference<uno::XInterface> xInterface(aAny, uno::UNO_QUERY);
+ if (rPageId == "object_inspector_interfaces_tab")
+ {
+ mpObjectInspectorWidgets->mpInterfacesTreeView->freeze();
+ clearAll(mpObjectInspectorWidgets->mpInterfacesTreeView);
+ appendInterfaces(xInterface);
+ mpObjectInspectorWidgets->mpInterfacesTreeView->thaw();
+ }
+ else if (rPageId == "object_inspector_services_tab")
+ {
+ mpObjectInspectorWidgets->mpServicesTreeView->freeze();
+ clearAll(mpObjectInspectorWidgets->mpServicesTreeView);
+ appendServices(xInterface);
+ mpObjectInspectorWidgets->mpServicesTreeView->thaw();
+ }
+ else if (rPageId == "object_inspector_properties_tab")
+ {
+ mpObjectInspectorWidgets->mpPropertiesTreeView->freeze();
+ clearAll(mpObjectInspectorWidgets->mpPropertiesTreeView);
+ appendProperties(xInterface);
+ mpObjectInspectorWidgets->mpPropertiesTreeView->thaw();
+ }
+ else if (rPageId == "object_inspector_methods_tab")
+ {
+ mpObjectInspectorWidgets->mpMethodsTreeView->freeze();
+ clearAll(mpObjectInspectorWidgets->mpMethodsTreeView);
+ appendMethods(xInterface);
+ mpObjectInspectorWidgets->mpMethodsTreeView->thaw();
+ }
+}
+
+IMPL_LINK(ObjectInspectorTreeHandler, NotebookLeavePage, const OString&, rPageId, bool)
+{
+ if (rPageId == "object_inspector_interfaces_tab")
+ {
+ mpObjectInspectorWidgets->mpInterfacesTreeView->freeze();
+ clearAll(mpObjectInspectorWidgets->mpInterfacesTreeView);
+ mpObjectInspectorWidgets->mpInterfacesTreeView->thaw();
+ }
+ else if (rPageId == "object_inspector_services_tab")
+ {
+ mpObjectInspectorWidgets->mpServicesTreeView->freeze();
+ clearAll(mpObjectInspectorWidgets->mpServicesTreeView);
+ mpObjectInspectorWidgets->mpServicesTreeView->thaw();
+ }
+ else if (rPageId == "object_inspector_properties_tab")
+ {
+ mpObjectInspectorWidgets->mpPropertiesTreeView->freeze();
+ clearAll(mpObjectInspectorWidgets->mpPropertiesTreeView);
+ mpObjectInspectorWidgets->mpPropertiesTreeView->thaw();
+ }
+ else if (rPageId == "object_inspector_methods_tab")
+ {
+ mpObjectInspectorWidgets->mpMethodsTreeView->freeze();
+ clearAll(mpObjectInspectorWidgets->mpMethodsTreeView);
+ mpObjectInspectorWidgets->mpMethodsTreeView->thaw();
+ }
+ return true;
+}
+
+void ObjectInspectorTreeHandler::clearObjectInspectorChildren(
+ std::unique_ptr<weld::TreeView>& pTreeView, weld::TreeIter const& rParent)
+{
+ bool bChild = false;
+ do
+ {
+ bChild = pTreeView->iter_has_child(rParent);
+ if (bChild)
+ {
+ std::unique_ptr<weld::TreeIter> pChild = pTreeView->make_iterator(&rParent);
+ bChild = pTreeView->iter_children(*pChild);
+ if (bChild)
+ {
+ clearObjectInspectorChildren(pTreeView, *pChild);
+ OUString sID = pTreeView->get_id(*pChild);
+ auto* pEntry = weld::fromId<ObjectInspectorNodeInterface*>(sID);
+ delete pEntry;
+ pTreeView->remove(*pChild);
+ }
+ }
+ } while (bChild);
+}
+
+/** Deletes all the node objects in a tree view */
+void ObjectInspectorTreeHandler::clearAll(std::unique_ptr<weld::TreeView>& pTreeView)
+{
+ // destroy all ObjectInspectorNodes from the tree
+ pTreeView->all_foreach([&pTreeView](weld::TreeIter& rEntry) {
+ OUString sID = pTreeView->get_id(rEntry);
+ auto* pEntry = weld::fromId<ObjectInspectorNodeInterface*>(sID);
+ delete pEntry;
+ return false;
+ });
+ pTreeView->clear();
+}
+
+/** Append interfaces to the "interfaces" tree view */
+void ObjectInspectorTreeHandler::appendInterfaces(uno::Reference<uno::XInterface> const& xInterface)
+{
+ if (!xInterface.is())
+ return;
+
+ uno::Reference<lang::XTypeProvider> xTypeProvider(xInterface, uno::UNO_QUERY);
+ if (xTypeProvider.is())
+ {
+ const auto xSequenceTypes = xTypeProvider->getTypes();
+ for (auto const& xType : xSequenceTypes)
+ {
+ auto xClass = convertTypeToIdlClass(xType, mxContext);
+ lclAppendNode(mpObjectInspectorWidgets->mpInterfacesTreeView, new ClassNode(xClass));
+ }
+ }
+}
+
+/** Append services to the "services" tree view */
+void ObjectInspectorTreeHandler::appendServices(uno::Reference<uno::XInterface> const& xInterface)
+{
+ if (!xInterface.is())
+ return;
+
+ auto xServiceInfo = uno::Reference<lang::XServiceInfo>(xInterface, uno::UNO_QUERY);
+ const uno::Sequence<OUString> aServiceNames(xServiceInfo->getSupportedServiceNames());
+ for (auto const& aServiceName : aServiceNames)
+ {
+ lclAppendNode(mpObjectInspectorWidgets->mpServicesTreeView,
+ new SimpleStringNode(aServiceName));
+ }
+}
+
+/** Append properties to the "properties" tree view */
+void ObjectInspectorTreeHandler::appendProperties(uno::Reference<uno::XInterface> const& xInterface)
+{
+ if (!xInterface.is())
+ return;
+ GenericPropertiesNode aNode("", uno::Any(xInterface), "", mxContext);
+ aNode.fillChildren(mpObjectInspectorWidgets->mpPropertiesTreeView, nullptr);
+}
+
+/** Append methods to the "methods" tree view */
+void ObjectInspectorTreeHandler::appendMethods(uno::Reference<uno::XInterface> const& xInterface)
+{
+ if (!xInterface.is())
+ return;
+
+ uno::Reference<beans::XIntrospection> xIntrospection = beans::theIntrospection::get(mxContext);
+ auto xIntrospectionAccess = xIntrospection->inspect(uno::Any(xInterface));
+
+ const auto xMethods = xIntrospectionAccess->getMethods(beans::MethodConcept::ALL);
+ for (auto const& xMethod : xMethods)
+ {
+ lclAppendNode(mpObjectInspectorWidgets->mpMethodsTreeView, new MethodNode(xMethod));
+ }
+}
+
+// Update the back button state depending if there are objects in the stack
+void ObjectInspectorTreeHandler::updateBackButtonState()
+{
+ mpObjectInspectorWidgets->mpToolbar->set_item_sensitive("back", maInspectionStack.size() > 1);
+}
+
+// Clears all the objects from the stack
+void ObjectInspectorTreeHandler::clearStack()
+{
+ maInspectionStack.clear();
+ updateBackButtonState();
+}
+
+// Adds an object to the stack
+void ObjectInspectorTreeHandler::addToStack(css::uno::Any const& rAny)
+{
+ maInspectionStack.push_back(rAny);
+ updateBackButtonState();
+}
+
+// Removes an object from the back of the stack and return it
+css::uno::Any ObjectInspectorTreeHandler::popFromStack()
+{
+ maInspectionStack.pop_back();
+ uno::Any aAny = maInspectionStack.back();
+ updateBackButtonState();
+ return aAny;
+}
+
+// Inspect the input object in the object inspector
+void ObjectInspectorTreeHandler::inspectObject(uno::Reference<uno::XInterface> const& xInterface)
+{
+ if (!xInterface.is())
+ return;
+
+ // Set implementation name
+ OUString aImplementationName = getInterfaceImplementationClass(xInterface);
+ mpObjectInspectorWidgets->mpClassNameLabel->set_label(aImplementationName);
+ sal_Int32 nStrLen = aImplementationName.getLength();
+ sal_Int32 nDigitWidth
+ = mpObjectInspectorWidgets->mpClassNameLabel->get_approximate_digit_width();
+
+ //get_about_digit_width() returns an approximate value. To always see the full class name (nStrLen+2)
+ mpObjectInspectorWidgets->mpClassNameLabel->set_size_request((nStrLen + 2) * nDigitWidth, -1);
+
+ // Fire entering the current opened page manually
+ auto rPageId = mpObjectInspectorWidgets->mpNotebook->get_current_page_ident();
+ NotebookEnterPage(rPageId);
+}
+
+// Inspect the input object in the object inspector.
+// Make the input object the root of the stack (clear all other
+// objects from the stack).
+void ObjectInspectorTreeHandler::introspect(uno::Reference<uno::XInterface> const& xInterface)
+{
+ clearStack();
+ addToStack(uno::Any(xInterface));
+ inspectObject(xInterface);
+}
+
+void ObjectInspectorTreeHandler::dispose()
+{
+ // We need to clear all the nodes
+ clearAll(mpObjectInspectorWidgets->mpInterfacesTreeView);
+ clearAll(mpObjectInspectorWidgets->mpServicesTreeView);
+ clearAll(mpObjectInspectorWidgets->mpPropertiesTreeView);
+ clearAll(mpObjectInspectorWidgets->mpMethodsTreeView);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/devtools/SelectionChangeHandler.hxx b/sfx2/source/devtools/SelectionChangeHandler.hxx
new file mode 100644
index 000000000..15a2b3596
--- /dev/null
+++ b/sfx2/source/devtools/SelectionChangeHandler.hxx
@@ -0,0 +1,74 @@
+/* -*- 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/.
+ *
+ */
+
+#pragma once
+
+#include <sfx2/devtools/DevelopmentToolDockingWindow.hxx>
+
+#include <com/sun/star/frame/XController.hpp>
+#include <com/sun/star/view/XSelectionSupplier.hpp>
+
+#include <comphelper/compbase.hxx>
+
+typedef comphelper::WeakComponentImplHelper<css::view::XSelectionChangeListener>
+ SelectionChangeHandlerInterfaceBase;
+
+/** Selection change handler to listen to document selection changes.
+ *
+ * Listens to the changes and notifies the docking window with a new
+ * selected object, when a change happens.
+ */
+class SelectionChangeHandler final : public SelectionChangeHandlerInterfaceBase
+{
+private:
+ css::uno::Reference<css::frame::XController> mxController;
+ VclPtr<DevelopmentToolDockingWindow> mpDockingWindow;
+
+public:
+ SelectionChangeHandler(const css::uno::Reference<css::frame::XController>& rxController,
+ DevelopmentToolDockingWindow* pDockingWindow)
+ : mxController(rxController)
+ , mpDockingWindow(pDockingWindow)
+ {
+ css::uno::Reference<css::view::XSelectionSupplier> xSupplier(mxController,
+ css::uno::UNO_QUERY);
+ xSupplier->addSelectionChangeListener(this);
+ }
+
+ ~SelectionChangeHandler() { mpDockingWindow.disposeAndClear(); }
+
+ virtual void SAL_CALL selectionChanged(const css::lang::EventObject& /*rEvent*/) override
+ {
+ css::uno::Reference<css::view::XSelectionSupplier> xSupplier(mxController,
+ css::uno::UNO_QUERY);
+ if (xSupplier.is())
+ {
+ css::uno::Any aAny = xSupplier->getSelection();
+ auto xInterface = aAny.get<css::uno::Reference<css::uno::XInterface>>();
+ mpDockingWindow->selectionChanged(xInterface);
+ }
+ }
+
+ void stopListening()
+ {
+ css::uno::Reference<css::view::XSelectionSupplier> xSupplier(mxController,
+ css::uno::UNO_QUERY);
+ xSupplier->removeSelectionChangeListener(this);
+ }
+
+ virtual void SAL_CALL disposing(const css::lang::EventObject& /*rEvent*/) override {}
+ using comphelper::WeakComponentImplHelperBase::disposing;
+
+private:
+ SelectionChangeHandler(const SelectionChangeHandler&) = delete;
+ SelectionChangeHandler& operator=(const SelectionChangeHandler&) = delete;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
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: */
diff --git a/sfx2/source/doc/DocumentMetadataAccess.cxx b/sfx2/source/doc/DocumentMetadataAccess.cxx
new file mode 100644
index 000000000..3bb670c7b
--- /dev/null
+++ b/sfx2/source/doc/DocumentMetadataAccess.cxx
@@ -0,0 +1,1381 @@
+/* -*- 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/DocumentMetadataAccess.hxx>
+
+#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/embed/ElementModes.hpp>
+#include <com/sun/star/embed/XStorage.hpp>
+#include <com/sun/star/embed/XTransactedObject.hpp>
+#include <com/sun/star/frame/XTransientDocumentsDocumentContentIdentifierFactory.hpp>
+#include <com/sun/star/task/ErrorCodeIOException.hpp>
+#include <com/sun/star/ucb/InteractiveAugmentedIOException.hpp>
+#include <com/sun/star/rdf/FileFormat.hpp>
+#include <com/sun/star/rdf/ParseException.hpp>
+#include <com/sun/star/rdf/RepositoryException.hpp>
+#include <com/sun/star/rdf/URIs.hpp>
+#include <com/sun/star/rdf/Statement.hpp>
+#include <com/sun/star/rdf/URI.hpp>
+#include <com/sun/star/rdf/Repository.hpp>
+
+#include <rtl/ustrbuf.hxx>
+#include <rtl/uri.hxx>
+#include <rtl/bootstrap.hxx>
+#include <sal/log.hxx>
+
+#include <comphelper/interaction.hxx>
+#include <unotools/mediadescriptor.hxx>
+#include <comphelper/sequence.hxx>
+#include <comphelper/storagehelper.hxx>
+#include <cppuhelper/exc_hlp.hxx>
+#include <o3tl/string_view.hxx>
+
+#include <sfx2/docfile.hxx>
+#include <sfx2/XmlIdRegistry.hxx>
+#include <sfx2/objsh.hxx>
+#include <tools/diagnose_ex.h>
+
+#include <libxml/tree.h>
+
+#include <vector>
+#include <set>
+#include <string_view>
+
+#include <com/sun/star/uri/XUriReference.hpp>
+#include <com/sun/star/uri/UriReferenceFactory.hpp>
+
+
+/*
+ Note: in the context of this implementation, all rdf.QueryExceptions and
+ rdf.RepositoryExceptions are RuntimeExceptions, and will be reported as such.
+
+ This implementation assumes that it is only used with ODF documents, not mere
+ ODF packages. In other words, we enforce that metadata files must not be
+ called reserved names.
+ */
+
+using namespace ::com::sun::star;
+
+namespace sfx2 {
+
+
+bool isValidNCName(std::u16string_view i_rIdref)
+{
+ const OString id(
+ OUStringToOString(i_rIdref, RTL_TEXTENCODING_UTF8) );
+ return !(xmlValidateNCName(
+ reinterpret_cast<const unsigned char*>(id.getStr()), 0));
+}
+
+
+constexpr OUStringLiteral s_content = u"content.xml";
+constexpr OUStringLiteral s_styles = u"styles.xml";
+constexpr OUStringLiteral s_manifest = u"manifest.rdf";
+const char s_odfmime [] = "application/vnd.oasis.opendocument.";
+
+
+static bool isContentFile(std::u16string_view i_rPath)
+{
+ return i_rPath == s_content;
+}
+
+static bool isStylesFile (std::u16string_view i_rPath)
+{
+ return i_rPath == s_styles;
+}
+
+bool isValidXmlId(std::u16string_view i_rStreamName,
+ std::u16string_view i_rIdref)
+{
+ return isValidNCName(i_rIdref)
+ && (isContentFile(i_rStreamName) || isStylesFile(i_rStreamName));
+}
+
+static bool isReservedFile(std::u16string_view i_rPath)
+{
+ return isContentFile(i_rPath) || isStylesFile(i_rPath) || i_rPath == u"meta.xml" || i_rPath == u"settings.xml";
+}
+
+
+uno::Reference<rdf::XURI> createBaseURI(
+ uno::Reference<uno::XComponentContext> const & i_xContext,
+ uno::Reference<frame::XModel> const & i_xModel,
+ OUString const & i_rPkgURI, std::u16string_view i_rSubDocument)
+{
+ if (!i_xContext.is() || (!i_xModel.is() && i_rPkgURI.isEmpty())) {
+ throw uno::RuntimeException();
+ }
+
+ OUString pkgURI(i_rPkgURI);
+
+ // tdf#123293 chicken/egg problem when loading from stream: there is no URI,
+ // and also the model doesn't have a storage yet, so we need to get the
+ // tdoc URI without a storage...
+ if (pkgURI.isEmpty())
+ {
+ assert(i_xModel.is());
+ uno::Reference<frame::XTransientDocumentsDocumentContentIdentifierFactory>
+ const xTDDCIF(
+ i_xContext->getServiceManager()->createInstanceWithContext(
+ "com.sun.star.ucb.TransientDocumentsContentProvider",
+ i_xContext),
+ uno::UNO_QUERY_THROW);
+ uno::Reference<ucb::XContentIdentifier> const xContentId(
+ xTDDCIF->createDocumentContentIdentifier(i_xModel));
+ SAL_WARN_IF(!xContentId.is(), "sfx", "createBaseURI: cannot create ContentIdentifier");
+ if (!xContentId.is())
+ {
+ throw uno::RuntimeException("createBaseURI: cannot create ContentIdentifier");
+ }
+ pkgURI = xContentId->getContentIdentifier();
+ assert(!pkgURI.isEmpty());
+ if (!pkgURI.isEmpty() && !pkgURI.endsWith("/"))
+ {
+ pkgURI += "/";
+ }
+ }
+
+ // #i108078# workaround non-hierarchical vnd.sun.star.expand URIs
+ // this really should be done somewhere else, not here.
+ if (pkgURI.matchIgnoreAsciiCase("vnd.sun.star.expand:"))
+ {
+ // expand it here (makeAbsolute requires hierarchical URI)
+ pkgURI = pkgURI.copy( RTL_CONSTASCII_LENGTH("vnd.sun.star.expand:") );
+ if (!pkgURI.isEmpty()) {
+ pkgURI = ::rtl::Uri::decode(
+ pkgURI, rtl_UriDecodeStrict, RTL_TEXTENCODING_UTF8);
+ if (pkgURI.isEmpty()) {
+ throw uno::RuntimeException();
+ }
+ ::rtl::Bootstrap::expandMacros(pkgURI);
+ }
+ }
+
+ const uno::Reference<uri::XUriReferenceFactory> xUriFactory =
+ uri::UriReferenceFactory::create( i_xContext);
+ uno::Reference< uri::XUriReference > xBaseURI;
+
+ const uno::Reference< uri::XUriReference > xPkgURI(
+ xUriFactory->parse(pkgURI), uno::UNO_SET_THROW );
+ xPkgURI->clearFragment();
+
+ // need to know whether the storage is a FileSystemStorage
+ // XServiceInfo would be better, but it is not implemented
+// if ( pkgURI.getLength() && ::utl::UCBContentHelper::IsFolder(pkgURI) )
+ if (true) {
+ xBaseURI.set( xPkgURI, uno::UNO_SET_THROW );
+ }
+ OUStringBuffer buf(64);
+ if (!xBaseURI->getUriReference().endsWith("/"))
+ {
+ const sal_Int32 count( xBaseURI->getPathSegmentCount() );
+ if (count > 0)
+ {
+ buf.append(xBaseURI->getPathSegment(count - 1));
+ }
+ buf.append('/');
+ }
+ if (!i_rSubDocument.empty())
+ {
+ buf.append(i_rSubDocument);
+ buf.append('/');
+ }
+ if (!buf.isEmpty())
+ {
+ const uno::Reference< uri::XUriReference > xPathURI(
+ xUriFactory->parse(buf.makeStringAndClear()), uno::UNO_SET_THROW );
+ xBaseURI.set(
+ xUriFactory->makeAbsolute(xBaseURI, xPathURI,
+ true, uri::RelativeUriExcessParentSegments_ERROR),
+ uno::UNO_SET_THROW);
+ }
+
+ return rdf::URI::create(i_xContext, xBaseURI->getUriReference());
+}
+
+
+struct DocumentMetadataAccess_Impl
+{
+ // note: these are all initialized in constructor, and loadFromStorage
+ const uno::Reference<uno::XComponentContext> m_xContext;
+ const SfxObjectShell & m_rXmlIdRegistrySupplier;
+ uno::Reference<rdf::XURI> m_xBaseURI;
+ uno::Reference<rdf::XRepository> m_xRepository;
+ uno::Reference<rdf::XNamedGraph> m_xManifest;
+ DocumentMetadataAccess_Impl(
+ uno::Reference<uno::XComponentContext> const& i_xContext,
+ SfxObjectShell const & i_rRegistrySupplier)
+ : m_xContext(i_xContext)
+ , m_rXmlIdRegistrySupplier(i_rRegistrySupplier)
+ {
+ OSL_ENSURE(m_xContext.is(), "context null");
+ }
+};
+
+// this is... a hack.
+template<sal_Int16 Constant>
+static uno::Reference<rdf::XURI> const &
+getURI(uno::Reference< uno::XComponentContext > const & i_xContext)
+{
+ static uno::Reference< rdf::XURI > xURI(
+ rdf::URI::createKnown(i_xContext, Constant), uno::UNO_SET_THROW);
+ return xURI;
+}
+
+
+/** would storing the file to a XStorage succeed? */
+static bool isFileNameValid(std::u16string_view i_rFileName)
+{
+ if (i_rFileName.empty()) return false;
+ if (i_rFileName[0] == '/') return false; // no absolute paths!
+ sal_Int32 idx(0);
+ do {
+ const OUString segment(
+ o3tl::getToken(i_rFileName, 0, u'/', idx) );
+ if (segment.isEmpty() || // no empty segments
+ segment == "." || // no . segments
+ segment == ".." || // no .. segments
+ !::comphelper::OStorageHelper::IsValidZipEntryFileName(
+ segment, false)) // no invalid characters
+ return false;
+ } while (idx >= 0);
+ return true;
+}
+
+/** split a uri hierarchy into first segment and rest */
+static bool
+splitPath(OUString const & i_rPath,
+ OUString & o_rDir, OUString& o_rRest)
+{
+ const sal_Int32 idx(i_rPath.indexOf(u'/'));
+ if (idx < 0 || idx >= i_rPath.getLength()) {
+ o_rDir.clear();
+ o_rRest = i_rPath;
+ return true;
+ } else if (idx == 0 || idx == i_rPath.getLength() - 1) {
+ // input must not start or end with '/'
+ return false;
+ } else {
+ o_rDir = i_rPath.copy(0, idx);
+ o_rRest = i_rPath.copy(idx+1);
+ return true;
+ }
+}
+
+static bool
+splitXmlId(OUString const & i_XmlId,
+ OUString & o_StreamName, OUString& o_Idref )
+{
+ const sal_Int32 idx(i_XmlId.indexOf(u'#'));
+ if ((idx <= 0) || (idx >= i_XmlId.getLength() - 1)) {
+ return false;
+ } else {
+ o_StreamName = i_XmlId.copy(0, idx);
+ o_Idref = i_XmlId.copy(idx+1);
+ return isValidXmlId(o_StreamName, o_Idref);
+ }
+}
+
+
+static uno::Reference<rdf::XURI>
+getURIForStream(struct DocumentMetadataAccess_Impl const & i_rImpl,
+ OUString const& i_rPath)
+{
+ const uno::Reference<rdf::XURI> xURI(
+ rdf::URI::createNS( i_rImpl.m_xContext,
+ i_rImpl.m_xBaseURI->getStringValue(), i_rPath),
+ uno::UNO_SET_THROW);
+ return xURI;
+}
+
+/** add statements declaring i_xResource to be a file of type i_xType with
+ path i_rPath to manifest, with optional additional types i_pTypes */
+static void
+addFile(struct DocumentMetadataAccess_Impl const & i_rImpl,
+ uno::Reference<rdf::XURI> const& i_xType,
+ OUString const & i_rPath,
+ const uno::Sequence < uno::Reference< rdf::XURI > > * i_pTypes)
+{
+ try {
+ const uno::Reference<rdf::XURI> xURI( getURIForStream(
+ i_rImpl, i_rPath) );
+
+ i_rImpl.m_xManifest->addStatement(i_rImpl.m_xBaseURI,
+ getURI<rdf::URIs::PKG_HASPART>(i_rImpl.m_xContext),
+ xURI);
+ i_rImpl.m_xManifest->addStatement(xURI,
+ getURI<rdf::URIs::RDF_TYPE>(i_rImpl.m_xContext),
+ i_xType);
+ if (i_pTypes) {
+ for (const auto& rType : *i_pTypes) {
+ i_rImpl.m_xManifest->addStatement(xURI,
+ getURI<rdf::URIs::RDF_TYPE>(i_rImpl.m_xContext),
+ rType);
+ }
+ }
+ } catch (const uno::RuntimeException &) {
+ throw;
+ } catch (const uno::Exception &) {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw lang::WrappedTargetRuntimeException(
+ "addFile: exception", /*this*/nullptr, anyEx);
+ }
+}
+
+/** add content.xml or styles.xml to manifest */
+static bool
+addContentOrStylesFileImpl(struct DocumentMetadataAccess_Impl const & i_rImpl,
+ const OUString & i_rPath)
+{
+ uno::Reference<rdf::XURI> xType;
+ if (isContentFile(i_rPath)) {
+ xType.set(getURI<rdf::URIs::ODF_CONTENTFILE>(i_rImpl.m_xContext));
+ } else if (isStylesFile(i_rPath)) {
+ xType.set(getURI<rdf::URIs::ODF_STYLESFILE>(i_rImpl.m_xContext));
+ } else {
+ return false;
+ }
+ addFile(i_rImpl, xType, i_rPath, nullptr);
+ return true;
+}
+
+/** add metadata file to manifest */
+static void
+addMetadataFileImpl(struct DocumentMetadataAccess_Impl const & i_rImpl,
+ const OUString & i_rPath,
+ const uno::Sequence < uno::Reference< rdf::XURI > > & i_rTypes)
+{
+ addFile(i_rImpl,
+ getURI<rdf::URIs::PKG_METADATAFILE>(i_rImpl.m_xContext),
+ i_rPath, &i_rTypes);
+}
+
+/** remove a file from the manifest */
+static void
+removeFile(struct DocumentMetadataAccess_Impl const & i_rImpl,
+ uno::Reference<rdf::XURI> const& i_xPart)
+{
+ if (!i_xPart.is()) throw uno::RuntimeException();
+ try {
+ i_rImpl.m_xManifest->removeStatements(i_rImpl.m_xBaseURI,
+ getURI<rdf::URIs::PKG_HASPART>(i_rImpl.m_xContext),
+ i_xPart);
+ i_rImpl.m_xManifest->removeStatements(i_xPart,
+ getURI<rdf::URIs::RDF_TYPE>(i_rImpl.m_xContext), nullptr);
+ } catch (const uno::RuntimeException &) {
+ throw;
+ } catch (const uno::Exception &) {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw lang::WrappedTargetRuntimeException(
+ "removeFile: exception",
+ nullptr, anyEx);
+ }
+}
+
+static ::std::vector< uno::Reference< rdf::XURI > >
+getAllParts(struct DocumentMetadataAccess_Impl const & i_rImpl)
+{
+ ::std::vector< uno::Reference< rdf::XURI > > ret;
+ try {
+ const uno::Reference<container::XEnumeration> xEnum(
+ i_rImpl.m_xManifest->getStatements( i_rImpl.m_xBaseURI,
+ getURI<rdf::URIs::PKG_HASPART>(i_rImpl.m_xContext), nullptr),
+ uno::UNO_SET_THROW);
+ while (xEnum->hasMoreElements()) {
+ rdf::Statement stmt;
+ if (!(xEnum->nextElement() >>= stmt)) {
+ throw uno::RuntimeException();
+ }
+ const uno::Reference<rdf::XURI> xPart(stmt.Object,
+ uno::UNO_QUERY);
+ if (!xPart.is()) continue;
+ ret.push_back(xPart);
+ }
+ return ret;
+ } catch (const uno::RuntimeException &) {
+ throw;
+ } catch (const uno::Exception &) {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw lang::WrappedTargetRuntimeException(
+ "getAllParts: exception",
+ nullptr, anyEx);
+ }
+}
+
+static bool
+isPartOfType(struct DocumentMetadataAccess_Impl const & i_rImpl,
+ uno::Reference<rdf::XURI> const & i_xPart,
+ uno::Reference<rdf::XURI> const & i_xType)
+{
+ if (!i_xPart.is() || !i_xType.is()) throw uno::RuntimeException();
+ try {
+ const uno::Reference<container::XEnumeration> xEnum(
+ i_rImpl.m_xManifest->getStatements(i_xPart,
+ getURI<rdf::URIs::RDF_TYPE>(i_rImpl.m_xContext),
+ i_xType),
+ uno::UNO_SET_THROW);
+ return xEnum->hasMoreElements();
+ } catch (const uno::RuntimeException &) {
+ throw;
+ } catch (const uno::Exception &) {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw lang::WrappedTargetRuntimeException(
+ "isPartOfType: exception",
+ nullptr, anyEx);
+ }
+}
+
+static ::std::vector<uno::Reference<rdf::XURI>>
+getAllParts(struct DocumentMetadataAccess_Impl const& i_rImpl,
+ const uno::Reference<rdf::XURI>& i_xType)
+{
+ ::std::vector<uno::Reference<rdf::XURI>> ret;
+ try
+ {
+ const uno::Reference<container::XEnumeration> xEnum(
+ i_rImpl.m_xManifest->getStatements(i_rImpl.m_xBaseURI,
+ getURI<rdf::URIs::PKG_HASPART>(i_rImpl.m_xContext),
+ nullptr),
+ uno::UNO_SET_THROW);
+ while (xEnum->hasMoreElements())
+ {
+ rdf::Statement stmt;
+ if (!(xEnum->nextElement() >>= stmt))
+ {
+ throw uno::RuntimeException();
+ }
+ const uno::Reference<rdf::XURI> xPart(stmt.Object, uno::UNO_QUERY);
+ if (!xPart.is())
+ continue;
+
+ const uno::Reference<container::XEnumeration> xEnum2(
+ i_rImpl.m_xManifest->getStatements(
+ xPart, getURI<rdf::URIs::RDF_TYPE>(i_rImpl.m_xContext), i_xType),
+ uno::UNO_SET_THROW);
+ if (xEnum2->hasMoreElements())
+ ret.emplace_back(xPart);
+ }
+ return ret;
+ }
+ catch (const uno::RuntimeException&)
+ {
+ throw;
+ }
+ catch (const uno::Exception& e)
+ {
+ throw lang::WrappedTargetRuntimeException("getAllParts: exception", nullptr,
+ uno::Any(e));
+ }
+}
+
+static ucb::InteractiveAugmentedIOException
+mkException( OUString const & i_rMessage,
+ ucb::IOErrorCode const i_ErrorCode,
+ OUString const & i_rUri, OUString const & i_rResource)
+{
+ ucb::InteractiveAugmentedIOException iaioe;
+ iaioe.Message = i_rMessage;
+ iaioe.Classification = task::InteractionClassification_ERROR;
+ iaioe.Code = i_ErrorCode;
+
+ const beans::PropertyValue uriProp("Uri",
+ -1, uno::Any(i_rUri), static_cast<beans::PropertyState>(0));
+ const beans::PropertyValue rnProp(
+ "ResourceName",
+ -1, uno::Any(i_rResource), static_cast<beans::PropertyState>(0));
+ iaioe.Arguments = { uno::Any(uriProp), uno::Any(rnProp) };
+ return iaioe;
+}
+
+/** error handling policy.
+ <p>If a handler is given, ask it how to proceed:
+ <ul><li>(default:) cancel import, raise exception</li>
+ <li>ignore the error and continue</li>
+ <li>retry the action that led to the error</li></ul></p>
+ N.B.: must not be called before DMA is fully initialized!
+ @returns true iff caller should retry
+ */
+static bool
+handleError( ucb::InteractiveAugmentedIOException const & i_rException,
+ const uno::Reference<task::XInteractionHandler> & i_xHandler)
+{
+ if (!i_xHandler.is()) {
+ throw lang::WrappedTargetException(
+ "DocumentMetadataAccess::loadMetadataFromStorage: exception",
+ /* *this*/ nullptr, uno::Any(i_rException));
+ }
+
+ ::rtl::Reference< ::comphelper::OInteractionRequest > pRequest(
+ new ::comphelper::OInteractionRequest(uno::Any(i_rException)) );
+ ::rtl::Reference< ::comphelper::OInteractionRetry > pRetry(
+ new ::comphelper::OInteractionRetry );
+ ::rtl::Reference< ::comphelper::OInteractionApprove > pApprove(
+ new ::comphelper::OInteractionApprove );
+ ::rtl::Reference< ::comphelper::OInteractionAbort > pAbort(
+ new ::comphelper::OInteractionAbort );
+
+ pRequest->addContinuation( pApprove );
+ pRequest->addContinuation( pAbort );
+ // actually call the handler
+ i_xHandler->handle( pRequest );
+ if (pRetry->wasSelected()) {
+ return true;
+ } else if (pApprove->wasSelected()) {
+ return false;
+ } else {
+ OSL_ENSURE(pAbort->wasSelected(), "no continuation selected?");
+ throw lang::WrappedTargetException(
+ "DocumentMetadataAccess::loadMetadataFromStorage: exception",
+ /* *this*/ nullptr, uno::Any(i_rException));
+ }
+}
+
+/** check if storage has content.xml/styles.xml;
+ e.g. ODB files seem to only have content.xml */
+static void
+collectFilesFromStorage(uno::Reference<embed::XStorage> const& i_xStorage,
+ std::set< OUString > & o_rFiles)
+{
+ try {
+ if (i_xStorage->hasByName(s_content) &&
+ i_xStorage->isStreamElement(s_content))
+ {
+ o_rFiles.insert(s_content);
+ }
+ if (i_xStorage->hasByName(s_styles) &&
+ i_xStorage->isStreamElement(s_styles))
+ {
+ o_rFiles.insert(s_styles);
+ }
+ } catch (const uno::Exception &) {
+ TOOLS_WARN_EXCEPTION("sfx", "collectFilesFromStorage");
+ }
+}
+
+/** import a metadata file into repository */
+static void
+readStream(struct DocumentMetadataAccess_Impl & i_rImpl,
+ uno::Reference< embed::XStorage > const & i_xStorage,
+ OUString const & i_rPath,
+ OUString const & i_rBaseURI)
+{
+ try {
+ OUString dir;
+ OUString rest;
+ if (!splitPath(i_rPath, dir, rest)) throw uno::RuntimeException();
+ if (dir.isEmpty()) {
+ if (!i_xStorage->isStreamElement(i_rPath)) {
+ throw mkException(
+ "readStream: is not a stream",
+ ucb::IOErrorCode_NO_FILE, i_rBaseURI + i_rPath, i_rPath);
+ }
+ const uno::Reference<io::XStream> xStream(
+ i_xStorage->openStreamElement(i_rPath,
+ embed::ElementModes::READ), uno::UNO_SET_THROW);
+ const uno::Reference<io::XInputStream> xInStream(
+ xStream->getInputStream(), uno::UNO_SET_THROW );
+ const uno::Reference<rdf::XURI> xBaseURI(
+ rdf::URI::create(i_rImpl.m_xContext, i_rBaseURI));
+ const uno::Reference<rdf::XURI> xURI(
+ rdf::URI::createNS(i_rImpl.m_xContext,
+ i_rBaseURI, i_rPath));
+ i_rImpl.m_xRepository->importGraph(rdf::FileFormat::RDF_XML,
+ xInStream, xURI, xBaseURI);
+ } else {
+ if (!i_xStorage->isStorageElement(dir)) {
+ throw mkException(
+ "readStream: is not a directory",
+ ucb::IOErrorCode_NO_DIRECTORY, i_rBaseURI + dir, dir);
+ }
+ const uno::Reference<embed::XStorage> xDir(
+ i_xStorage->openStorageElement(dir,
+ embed::ElementModes::READ));
+ const uno::Reference< beans::XPropertySet > xDirProps(xDir,
+ uno::UNO_QUERY_THROW);
+ try {
+ OUString mimeType;
+ xDirProps->getPropertyValue(
+ utl::MediaDescriptor::PROP_MEDIATYPE )
+ >>= mimeType;
+ if (mimeType.startsWith(s_odfmime)) {
+ SAL_WARN("sfx", "readStream: refusing to recurse into embedded document");
+ return;
+ }
+ } catch (const uno::Exception &) { }
+ readStream(i_rImpl, xDir, rest, i_rBaseURI+dir+"/" );
+ }
+ } catch (const container::NoSuchElementException & e) {
+ throw mkException(e.Message, ucb::IOErrorCode_NOT_EXISTING_PATH,
+ i_rBaseURI + i_rPath, i_rPath);
+ } catch (const io::IOException & e) {
+ throw mkException(e.Message, ucb::IOErrorCode_CANT_READ,
+ i_rBaseURI + i_rPath, i_rPath);
+ } catch (const rdf::ParseException & e) {
+ throw mkException(e.Message, ucb::IOErrorCode_WRONG_FORMAT,
+ i_rBaseURI + i_rPath, i_rPath);
+ }
+}
+
+/** import a metadata file into repository */
+static void
+importFile(struct DocumentMetadataAccess_Impl & i_rImpl,
+ uno::Reference<embed::XStorage> const & i_xStorage,
+ OUString const & i_rBaseURI,
+ uno::Reference<task::XInteractionHandler> const & i_xHandler,
+ const OUString& i_rPath)
+{
+retry:
+ try {
+ readStream(i_rImpl, i_xStorage, i_rPath, i_rBaseURI);
+ } catch (const ucb::InteractiveAugmentedIOException & e) {
+ if (handleError(e, i_xHandler)) goto retry;
+ } catch (const uno::RuntimeException &) {
+ throw;
+ } catch (const uno::Exception &) {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw lang::WrappedTargetRuntimeException(
+ "importFile: exception",
+ nullptr, anyEx);
+ }
+}
+
+/** actually write a metadata file to the storage */
+static void
+exportStream(struct DocumentMetadataAccess_Impl const & i_rImpl,
+ uno::Reference< embed::XStorage > const & i_xStorage,
+ uno::Reference<rdf::XURI> const & i_xGraphName,
+ OUString const & i_rFileName,
+ OUString const & i_rBaseURI)
+{
+ const uno::Reference<io::XStream> xStream(
+ i_xStorage->openStreamElement(i_rFileName,
+ embed::ElementModes::WRITE | embed::ElementModes::TRUNCATE),
+ uno::UNO_SET_THROW);
+ const uno::Reference< beans::XPropertySet > xStreamProps(xStream,
+ uno::UNO_QUERY);
+ if (xStreamProps.is()) { // this is NOT supported in FileSystemStorage
+ xStreamProps->setPropertyValue(
+ "MediaType",
+ uno::Any(OUString("application/rdf+xml")));
+ }
+ const uno::Reference<io::XOutputStream> xOutStream(
+ xStream->getOutputStream(), uno::UNO_SET_THROW );
+ const uno::Reference<rdf::XURI> xBaseURI(
+ rdf::URI::create(i_rImpl.m_xContext, i_rBaseURI));
+ i_rImpl.m_xRepository->exportGraph(rdf::FileFormat::RDF_XML,
+ xOutStream, i_xGraphName, xBaseURI);
+}
+
+/** write a metadata file to the storage */
+static void
+writeStream(struct DocumentMetadataAccess_Impl & i_rImpl,
+ uno::Reference< embed::XStorage > const & i_xStorage,
+ uno::Reference<rdf::XURI> const & i_xGraphName,
+ OUString const & i_rPath,
+ OUString const & i_rBaseURI)
+{
+ OUString dir;
+ OUString rest;
+ if (!splitPath(i_rPath, dir, rest)) throw uno::RuntimeException();
+ try {
+ if (dir.isEmpty()) {
+ exportStream(i_rImpl, i_xStorage, i_xGraphName, i_rPath,
+ i_rBaseURI);
+ } else {
+ const uno::Reference<embed::XStorage> xDir(
+ i_xStorage->openStorageElement(dir,
+ embed::ElementModes::WRITE));
+ const uno::Reference< beans::XPropertySet > xDirProps(xDir,
+ uno::UNO_QUERY_THROW);
+ try {
+ OUString mimeType;
+ xDirProps->getPropertyValue(
+ utl::MediaDescriptor::PROP_MEDIATYPE )
+ >>= mimeType;
+ if (mimeType.startsWith(s_odfmime)) {
+ SAL_WARN("sfx", "writeStream: refusing to recurse into embedded document");
+ return;
+ }
+ } catch (const uno::Exception &) { }
+ writeStream(i_rImpl, xDir, i_xGraphName, rest, i_rBaseURI+dir+"/");
+ uno::Reference<embed::XTransactedObject> const xTransaction(
+ xDir, uno::UNO_QUERY);
+ if (xTransaction.is()) {
+ xTransaction->commit();
+ }
+ }
+ } catch (const uno::RuntimeException &) {
+ throw;
+ } catch (const io::IOException &) {
+ throw;
+ }
+}
+
+static void
+initLoading(struct DocumentMetadataAccess_Impl & i_rImpl,
+ const uno::Reference< embed::XStorage > & i_xStorage,
+ const uno::Reference<rdf::XURI> & i_xBaseURI,
+ const uno::Reference<task::XInteractionHandler> & i_xHandler)
+{
+retry:
+ // clear old data
+ i_rImpl.m_xManifest.clear();
+ // init BaseURI
+ i_rImpl.m_xBaseURI = i_xBaseURI;
+
+ // create repository
+ i_rImpl.m_xRepository.clear();
+ i_rImpl.m_xRepository.set(rdf::Repository::create(i_rImpl.m_xContext),
+ uno::UNO_SET_THROW);
+
+ // try to delay raising errors until after initialization is done
+ uno::Any rterr;
+ ucb::InteractiveAugmentedIOException iaioe;
+ bool err(false);
+
+ const uno::Reference <rdf::XURI> xManifest(
+ getURIForStream(i_rImpl, s_manifest));
+ try {
+ readStream(i_rImpl, i_xStorage, s_manifest, i_xBaseURI->getStringValue());
+ } catch (const ucb::InteractiveAugmentedIOException & e) {
+ // no manifest.rdf: this is not an error in ODF < 1.2
+ if (ucb::IOErrorCode_NOT_EXISTING_PATH != e.Code) {
+ iaioe = e;
+ err = true;
+ }
+ } catch (const uno::Exception & e) {
+ rterr <<= e;
+ }
+
+ // init manifest graph
+ const uno::Reference<rdf::XNamedGraph> xManifestGraph(
+ i_rImpl.m_xRepository->getGraph(xManifest));
+ i_rImpl.m_xManifest.set(xManifestGraph.is() ? xManifestGraph :
+ i_rImpl.m_xRepository->createGraph(xManifest), uno::UNO_SET_THROW);
+
+ // document statement
+ i_rImpl.m_xManifest->addStatement(i_rImpl.m_xBaseURI,
+ getURI<rdf::URIs::RDF_TYPE>(i_rImpl.m_xContext),
+ getURI<rdf::URIs::PKG_DOCUMENT>(i_rImpl.m_xContext));
+
+ OSL_ENSURE(i_rImpl.m_xBaseURI.is(), "base URI is null");
+ OSL_ENSURE(i_rImpl.m_xRepository.is(), "repository is null");
+ OSL_ENSURE(i_rImpl.m_xManifest.is(), "manifest is null");
+
+ if (rterr.hasValue()) {
+ throw lang::WrappedTargetRuntimeException(
+ "DocumentMetadataAccess::loadMetadataFromStorage: "
+ "exception", nullptr, rterr);
+ }
+
+ if (err && handleError(iaioe, i_xHandler))
+ goto retry;
+}
+
+/** init Impl struct */
+static void init(struct DocumentMetadataAccess_Impl & i_rImpl)
+{
+ try {
+
+ i_rImpl.m_xManifest.set(i_rImpl.m_xRepository->createGraph(
+ getURIForStream(i_rImpl, s_manifest)),
+ uno::UNO_SET_THROW);
+
+ // insert the document statement
+ i_rImpl.m_xManifest->addStatement(i_rImpl.m_xBaseURI,
+ getURI<rdf::URIs::RDF_TYPE>(i_rImpl.m_xContext),
+ getURI<rdf::URIs::PKG_DOCUMENT>(i_rImpl.m_xContext));
+ } catch (const uno::Exception &) {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw lang::WrappedTargetRuntimeException(
+ "init: unexpected exception", nullptr,
+ anyEx);
+ }
+
+ // add top-level content files
+ if (!addContentOrStylesFileImpl(i_rImpl, s_content)) {
+ throw uno::RuntimeException();
+ }
+ if (!addContentOrStylesFileImpl(i_rImpl, s_styles)) {
+ throw uno::RuntimeException();
+ }
+}
+
+
+DocumentMetadataAccess::DocumentMetadataAccess(
+ uno::Reference< uno::XComponentContext > const & i_xContext,
+ const SfxObjectShell & i_rRegistrySupplier)
+ : m_pImpl(new DocumentMetadataAccess_Impl(i_xContext, i_rRegistrySupplier))
+{
+ // no initialization: must call loadFrom...
+}
+
+DocumentMetadataAccess::DocumentMetadataAccess(
+ uno::Reference< uno::XComponentContext > const & i_xContext,
+ const SfxObjectShell & i_rRegistrySupplier,
+ OUString const & i_rURI)
+ : m_pImpl(new DocumentMetadataAccess_Impl(i_xContext, i_rRegistrySupplier))
+{
+ OSL_ENSURE(!i_rURI.isEmpty(), "DMA::DMA: no URI given!");
+ OSL_ENSURE(i_rURI.endsWith("/"), "DMA::DMA: URI without / given!");
+ if (!i_rURI.endsWith("/")) throw uno::RuntimeException();
+ m_pImpl->m_xBaseURI.set(rdf::URI::create(m_pImpl->m_xContext, i_rURI));
+ m_pImpl->m_xRepository.set(rdf::Repository::create(m_pImpl->m_xContext),
+ uno::UNO_SET_THROW);
+
+ // init repository
+ init(*m_pImpl);
+
+ OSL_ENSURE(m_pImpl->m_xBaseURI.is(), "base URI is null");
+ OSL_ENSURE(m_pImpl->m_xRepository.is(), "repository is null");
+ OSL_ENSURE(m_pImpl->m_xManifest.is(), "manifest is null");
+}
+
+DocumentMetadataAccess::~DocumentMetadataAccess()
+{
+}
+
+// css::rdf::XRepositorySupplier:
+uno::Reference< rdf::XRepository > SAL_CALL
+DocumentMetadataAccess::getRDFRepository()
+{
+ OSL_ENSURE(m_pImpl->m_xRepository.is(), "repository not initialized");
+ return m_pImpl->m_xRepository;
+}
+
+// css::rdf::XNode:
+OUString SAL_CALL
+DocumentMetadataAccess::getStringValue()
+{
+ return m_pImpl->m_xBaseURI->getStringValue();
+}
+
+// css::rdf::XURI:
+OUString SAL_CALL
+DocumentMetadataAccess::getNamespace()
+{
+ return m_pImpl->m_xBaseURI->getNamespace();
+}
+
+OUString SAL_CALL
+DocumentMetadataAccess::getLocalName()
+{
+ return m_pImpl->m_xBaseURI->getLocalName();
+}
+
+// css::rdf::XDocumentMetadataAccess:
+uno::Reference< rdf::XMetadatable > SAL_CALL
+DocumentMetadataAccess::getElementByMetadataReference(
+ const css::beans::StringPair & i_rReference)
+{
+ const IXmlIdRegistry * pReg(
+ m_pImpl->m_rXmlIdRegistrySupplier.GetXmlIdRegistry() );
+ if (!pReg) {
+ throw uno::RuntimeException(
+ "DocumentMetadataAccess::getElementByXmlId: no registry", *this);
+ }
+ return pReg->GetElementByMetadataReference(i_rReference);
+}
+
+uno::Reference< rdf::XMetadatable > SAL_CALL
+DocumentMetadataAccess::getElementByURI(
+ const uno::Reference< rdf::XURI > & i_xURI )
+{
+ if (!i_xURI.is()) {
+ throw lang::IllegalArgumentException(
+ "DocumentMetadataAccess::getElementByURI: URI is null", *this, 0);
+ }
+
+ const OUString baseURI( m_pImpl->m_xBaseURI->getStringValue() );
+ const OUString name( i_xURI->getStringValue() );
+ if (!name.match(baseURI)) {
+ return nullptr;
+ }
+ OUString path;
+ OUString idref;
+ if (!splitXmlId(name.copy(baseURI.getLength()), path, idref)) {
+ return nullptr;
+ }
+
+ return getElementByMetadataReference( beans::StringPair(path, idref) );
+}
+
+uno::Sequence<uno::Reference<rdf::XURI>> SAL_CALL
+DocumentMetadataAccess::getMetadataGraphsWithType(const uno::Reference<rdf::XURI>& i_xType)
+{
+ if (!i_xType.is())
+ {
+ throw lang::IllegalArgumentException("DocumentMetadataAccess::getMetadataGraphsWithType: "
+ "type is null",
+ *this, 0);
+ }
+
+ return ::comphelper::containerToSequence(getAllParts(*m_pImpl, i_xType));
+}
+
+uno::Reference<rdf::XURI> SAL_CALL
+DocumentMetadataAccess::addMetadataFile(const OUString & i_rFileName,
+ const uno::Sequence < uno::Reference< rdf::XURI > > & i_rTypes)
+{
+ if (!isFileNameValid(i_rFileName)) {
+ throw lang::IllegalArgumentException(
+ "DocumentMetadataAccess::addMetadataFile: invalid FileName",
+ *this, 0);
+ }
+ if (isReservedFile(i_rFileName)) {
+ throw lang::IllegalArgumentException(
+ "DocumentMetadataAccess::addMetadataFile:"
+ "invalid FileName: reserved", *this, 0);
+ }
+ if (std::any_of(i_rTypes.begin(), i_rTypes.end(),
+ [](const uno::Reference< rdf::XURI >& rType) { return !rType.is(); })) {
+ throw lang::IllegalArgumentException(
+ "DocumentMetadataAccess::addMetadataFile: "
+ "null type", *this, 2);
+ }
+
+ const uno::Reference<rdf::XURI> xGraphName(
+ getURIForStream(*m_pImpl, i_rFileName) );
+
+ try {
+ m_pImpl->m_xRepository->createGraph(xGraphName);
+ } catch (const rdf::RepositoryException &) {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw lang::WrappedTargetRuntimeException(
+ "DocumentMetadataAccess::addMetadataFile: exception",
+ *this, anyEx);
+ // note: all other exceptions are propagated
+ }
+
+ addMetadataFileImpl(*m_pImpl, i_rFileName, i_rTypes);
+ return xGraphName;
+}
+
+uno::Reference<rdf::XURI> SAL_CALL
+DocumentMetadataAccess::importMetadataFile(::sal_Int16 i_Format,
+ const uno::Reference< io::XInputStream > & i_xInStream,
+ const OUString & i_rFileName,
+ const uno::Reference< rdf::XURI > & i_xBaseURI,
+ const uno::Sequence < uno::Reference< rdf::XURI > > & i_rTypes)
+{
+ if (!isFileNameValid(i_rFileName)) {
+ throw lang::IllegalArgumentException(
+ "DocumentMetadataAccess::importMetadataFile: invalid FileName",
+ *this, 0);
+ }
+ if (isReservedFile(i_rFileName)) {
+ throw lang::IllegalArgumentException(
+ "DocumentMetadataAccess::importMetadataFile:"
+ "invalid FileName: reserved", *this, 0);
+ }
+ if (std::any_of(i_rTypes.begin(), i_rTypes.end(),
+ [](const uno::Reference< rdf::XURI >& rType) { return !rType.is(); })) {
+ throw lang::IllegalArgumentException(
+ "DocumentMetadataAccess::importMetadataFile: null type",
+ *this, 5);
+ }
+
+ const uno::Reference<rdf::XURI> xGraphName(
+ getURIForStream(*m_pImpl, i_rFileName) );
+
+ try {
+ m_pImpl->m_xRepository->importGraph(
+ i_Format, i_xInStream, xGraphName, i_xBaseURI);
+ } catch (const rdf::RepositoryException &) {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw lang::WrappedTargetRuntimeException(
+ "DocumentMetadataAccess::importMetadataFile: "
+ "RepositoryException", *this, anyEx);
+ // note: all other exceptions are propagated
+ }
+
+ // add to manifest
+ addMetadataFileImpl(*m_pImpl, i_rFileName, i_rTypes);
+ return xGraphName;
+}
+
+void SAL_CALL
+DocumentMetadataAccess::removeMetadataFile(
+ const uno::Reference< rdf::XURI > & i_xGraphName)
+{
+ try {
+ m_pImpl->m_xRepository->destroyGraph(i_xGraphName);
+ } catch (const rdf::RepositoryException &) {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw lang::WrappedTargetRuntimeException(
+ "DocumentMetadataAccess::removeMetadataFile: "
+ "RepositoryException", *this, anyEx);
+ // note: all other exceptions are propagated
+ }
+
+ // remove file from manifest
+ removeFile(*m_pImpl, i_xGraphName);
+}
+
+void SAL_CALL
+DocumentMetadataAccess::addContentOrStylesFile(
+ const OUString & i_rFileName)
+{
+ if (!isFileNameValid(i_rFileName)) {
+ throw lang::IllegalArgumentException(
+ "DocumentMetadataAccess::addContentOrStylesFile: "
+ "invalid FileName", *this, 0);
+ }
+
+ if (!addContentOrStylesFileImpl(*m_pImpl, i_rFileName)) {
+ throw lang::IllegalArgumentException(
+ "DocumentMetadataAccess::addContentOrStylesFile: "
+ "invalid FileName: must end with content.xml or styles.xml",
+ *this, 0);
+ }
+}
+
+void SAL_CALL
+DocumentMetadataAccess::removeContentOrStylesFile(
+ const OUString & i_rFileName)
+{
+ if (!isFileNameValid(i_rFileName)) {
+ throw lang::IllegalArgumentException(
+ "DocumentMetadataAccess::removeContentOrStylesFile: "
+ "invalid FileName", *this, 0);
+ }
+
+ try {
+ const uno::Reference<rdf::XURI> xPart(
+ getURIForStream(*m_pImpl, i_rFileName) );
+ const uno::Reference<container::XEnumeration> xEnum(
+ m_pImpl->m_xManifest->getStatements( m_pImpl->m_xBaseURI,
+ getURI<rdf::URIs::PKG_HASPART>(m_pImpl->m_xContext),
+ xPart),
+ uno::UNO_SET_THROW);
+ if (!xEnum->hasMoreElements()) {
+ throw container::NoSuchElementException(
+ "DocumentMetadataAccess::removeContentOrStylesFile: "
+ "cannot find stream in manifest graph: " + i_rFileName,
+ *this);
+ }
+
+ // remove file from manifest
+ removeFile(*m_pImpl, xPart);
+
+ } catch (const uno::RuntimeException &) {
+ throw;
+ } catch (const uno::Exception &) {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw lang::WrappedTargetRuntimeException(
+ "DocumentMetadataAccess::removeContentOrStylesFile: exception",
+ *this, anyEx);
+ }
+}
+
+void SAL_CALL DocumentMetadataAccess::loadMetadataFromStorage(
+ const uno::Reference< embed::XStorage > & i_xStorage,
+ const uno::Reference<rdf::XURI> & i_xBaseURI,
+ const uno::Reference<task::XInteractionHandler> & i_xHandler)
+{
+ if (!i_xStorage.is()) {
+ throw lang::IllegalArgumentException(
+ "DocumentMetadataAccess::loadMetadataFromStorage: "
+ "storage is null", *this, 0);
+ }
+ if (!i_xBaseURI.is()) {
+ throw lang::IllegalArgumentException(
+ "DocumentMetadataAccess::loadMetadataFromStorage: "
+ "base URI is null", *this, 1);
+ }
+ const OUString baseURI( i_xBaseURI->getStringValue());
+ if (baseURI.indexOf('#') >= 0) {
+ throw lang::IllegalArgumentException(
+ "DocumentMetadataAccess::loadMetadataFromStorage: "
+ "base URI not absolute", *this, 1);
+ }
+ if (!baseURI.endsWith("/")) {
+ throw lang::IllegalArgumentException(
+ "DocumentMetadataAccess::loadMetadataFromStorage: "
+ "base URI does not end with slash", *this, 1);
+ }
+
+ initLoading(*m_pImpl, i_xStorage, i_xBaseURI, i_xHandler);
+
+ std::set< OUString > StgFiles;
+ collectFilesFromStorage(i_xStorage, StgFiles);
+
+ std::vector< OUString > MfstMetadataFiles;
+
+ try {
+ const ::std::vector< uno::Reference< rdf::XURI > > parts(
+ getAllParts(*m_pImpl) );
+ const uno::Reference<rdf::XURI>& xContentFile(
+ getURI<rdf::URIs::ODF_CONTENTFILE>(m_pImpl->m_xContext));
+ const uno::Reference<rdf::XURI>& xStylesFile(
+ getURI<rdf::URIs::ODF_STYLESFILE>(m_pImpl->m_xContext));
+ const uno::Reference<rdf::XURI>& xMetadataFile(
+ getURI<rdf::URIs::PKG_METADATAFILE>(m_pImpl->m_xContext));
+ const sal_Int32 len( baseURI.getLength() );
+ for (const auto& rxPart : parts) {
+ const OUString name(rxPart->getStringValue());
+ if (!name.match(baseURI)) {
+ SAL_WARN("sfx", "loadMetadataFromStorage: graph not in document: " << name);
+ continue;
+ }
+ const OUString relName( name.copy(len) );
+ if (relName == s_manifest) {
+ SAL_WARN("sfx", "loadMetadataFromStorage: found ourselves a recursive manifest!");
+ continue;
+ }
+ // remove found items from StgFiles
+ StgFiles.erase(relName);
+ if (isContentFile(relName)) {
+ if (!isPartOfType(*m_pImpl, rxPart, xContentFile)) {
+ const uno::Reference <rdf::XURI> xName(
+ getURIForStream(*m_pImpl, relName) );
+ // add missing type statement
+ m_pImpl->m_xManifest->addStatement(xName,
+ getURI<rdf::URIs::RDF_TYPE>(m_pImpl->m_xContext),
+ xContentFile);
+ }
+ } else if (isStylesFile(relName)) {
+ if (!isPartOfType(*m_pImpl, rxPart, xStylesFile)) {
+ const uno::Reference <rdf::XURI> xName(
+ getURIForStream(*m_pImpl, relName) );
+ // add missing type statement
+ m_pImpl->m_xManifest->addStatement(xName,
+ getURI<rdf::URIs::RDF_TYPE>(m_pImpl->m_xContext),
+ xStylesFile);
+ }
+ } else if (isReservedFile(relName)) {
+ SAL_WARN("sfx", "loadMetadataFromStorage: reserved file name in manifest");
+ } else {
+ if (isPartOfType(*m_pImpl, rxPart, xMetadataFile)) {
+ MfstMetadataFiles.push_back(relName);
+ }
+ // do not add statement for MetadataFile; it could be
+ // something else! just ignore it...
+ }
+ }
+ } catch (const uno::RuntimeException &) {
+ throw;
+ } catch (const uno::Exception &) {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw lang::WrappedTargetRuntimeException(
+ "DocumentMetadataAccess::loadMetadataFromStorage: "
+ "exception", *this, anyEx);
+ }
+
+ for (const auto& aStgFile : StgFiles)
+ addContentOrStylesFileImpl(*m_pImpl, aStgFile);
+
+ for (const auto& aMfstMetadataFile : MfstMetadataFiles)
+ importFile(*m_pImpl, i_xStorage, baseURI, i_xHandler, aMfstMetadataFile);
+}
+
+void SAL_CALL DocumentMetadataAccess::storeMetadataToStorage(
+ const uno::Reference< embed::XStorage > & i_xStorage)
+{
+ if (!i_xStorage.is()) {
+ throw lang::IllegalArgumentException(
+ "DocumentMetadataAccess::storeMetadataToStorage: "
+ "storage is null", *this, 0);
+ }
+
+ // export manifest
+ const uno::Reference <rdf::XURI> xManifest(
+ getURIForStream(*m_pImpl, s_manifest) );
+ const OUString baseURI( m_pImpl->m_xBaseURI->getStringValue() );
+ try {
+ writeStream(*m_pImpl, i_xStorage, xManifest, s_manifest, baseURI);
+ } catch (const uno::RuntimeException &) {
+ throw;
+ } catch (const io::IOException &) {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw lang::WrappedTargetException(
+ "storeMetadataToStorage: IO exception", *this, anyEx);
+ } catch (const uno::Exception &) {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw lang::WrappedTargetRuntimeException(
+ "storeMetadataToStorage: exception", *this, anyEx);
+ }
+
+ // export metadata streams
+ try {
+ const uno::Sequence<uno::Reference<rdf::XURI> > graphs(
+ m_pImpl->m_xRepository->getGraphNames());
+ const sal_Int32 len( baseURI.getLength() );
+ for (const uno::Reference<rdf::XURI>& xName : graphs) {
+ const OUString name(xName->getStringValue());
+ if (!name.match(baseURI)) {
+ SAL_WARN("sfx", "storeMetadataToStorage: graph not in document: " << name);
+ continue;
+ }
+ const OUString relName( name.copy(len) );
+ if (relName == s_manifest) {
+ continue;
+ }
+ if (!isFileNameValid(relName) || isReservedFile(relName)) {
+ SAL_WARN("sfx", "storeMetadataToStorage: invalid file name: " << relName);
+ continue;
+ }
+ try {
+ writeStream(*m_pImpl, i_xStorage, xName, relName, baseURI);
+ } catch (const uno::RuntimeException &) {
+ throw;
+ } catch (const io::IOException &) {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw lang::WrappedTargetException(
+ "storeMetadataToStorage: IO exception",
+ *this, anyEx);
+ } catch (const uno::Exception &) {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw lang::WrappedTargetRuntimeException(
+ "storeMetadataToStorage: exception",
+ *this, anyEx);
+ }
+ }
+ } catch (const rdf::RepositoryException &) {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw lang::WrappedTargetRuntimeException(
+ "storeMetadataToStorage: exception", *this, anyEx);
+ }
+}
+
+void SAL_CALL
+DocumentMetadataAccess::loadMetadataFromMedium(
+ const uno::Sequence< beans::PropertyValue > & i_rMedium)
+{
+ uno::Reference<io::XInputStream> xIn;
+ utl::MediaDescriptor md(i_rMedium);
+ OUString URL;
+ md[ utl::MediaDescriptor::PROP_URL ] >>= URL;
+ OUString BaseURL;
+ md[ utl::MediaDescriptor::PROP_DOCUMENTBASEURL ] >>= BaseURL;
+ if (md.addInputStream()) {
+ md[ utl::MediaDescriptor::PROP_INPUTSTREAM ] >>= xIn;
+ }
+ if (!xIn.is() && URL.isEmpty()) {
+ throw lang::IllegalArgumentException(
+ "DocumentMetadataAccess::loadMetadataFromMedium: "
+ "invalid medium: no URL, no input stream", *this, 0);
+ }
+ uno::Reference<embed::XStorage> xStorage;
+ try {
+ if (xIn.is()) {
+ xStorage = ::comphelper::OStorageHelper::GetStorageFromInputStream(
+ xIn, m_pImpl->m_xContext);
+ } else { // fallback to url
+ xStorage = ::comphelper::OStorageHelper::GetStorageFromURL2(
+ URL, embed::ElementModes::READ, m_pImpl->m_xContext);
+ }
+ } catch (const uno::RuntimeException &) {
+ throw;
+ } catch (const io::IOException &) {
+ throw;
+ } catch (const uno::Exception &) {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw lang::WrappedTargetException(
+ "DocumentMetadataAccess::loadMetadataFromMedium: "
+ "exception", *this, anyEx);
+ }
+ if (!xStorage.is()) {
+ throw uno::RuntimeException(
+ "DocumentMetadataAccess::loadMetadataFromMedium: "
+ "cannot get Storage", *this);
+ }
+ uno::Reference<rdf::XURI> xBaseURI;
+ try {
+ xBaseURI = createBaseURI(m_pImpl->m_xContext, nullptr, BaseURL);
+ } catch (const uno::Exception &) {
+ // fall back to URL
+ try {
+ xBaseURI = createBaseURI(m_pImpl->m_xContext, nullptr, URL);
+ } catch (const uno::Exception &) {
+ OSL_FAIL("cannot create base URI");
+ }
+ }
+ uno::Reference<task::XInteractionHandler> xIH;
+ md[ utl::MediaDescriptor::PROP_INTERACTIONHANDLER ] >>= xIH;
+ loadMetadataFromStorage(xStorage, xBaseURI, xIH);
+}
+
+void SAL_CALL
+DocumentMetadataAccess::storeMetadataToMedium(
+ const uno::Sequence< beans::PropertyValue > & i_rMedium)
+{
+ utl::MediaDescriptor md(i_rMedium);
+ OUString URL;
+ md[ utl::MediaDescriptor::PROP_URL ] >>= URL;
+ if (URL.isEmpty()) {
+ throw lang::IllegalArgumentException(
+ "DocumentMetadataAccess::storeMetadataToMedium: "
+ "invalid medium: no URL", *this, 0);
+ }
+
+ SfxMedium aMedium(i_rMedium);
+ uno::Reference<embed::XStorage> xStorage(aMedium.GetOutputStorage());
+
+ bool sfx(false);
+ if (xStorage.is()) {
+ sfx = true;
+ } else {
+ xStorage = ::comphelper::OStorageHelper::GetStorageFromURL2(
+ URL, embed::ElementModes::WRITE, m_pImpl->m_xContext);
+ }
+
+ if (!xStorage.is()) {
+ throw uno::RuntimeException(
+ "DocumentMetadataAccess::storeMetadataToMedium: "
+ "cannot get Storage", *this);
+ }
+ // set MIME type of the storage
+ utl::MediaDescriptor::const_iterator iter
+ = md.find(utl::MediaDescriptor::PROP_MEDIATYPE);
+ if (iter != md.end()) {
+ uno::Reference< beans::XPropertySet > xProps(xStorage,
+ uno::UNO_QUERY_THROW);
+ try {
+ // this is NOT supported in FileSystemStorage
+ xProps->setPropertyValue(
+ utl::MediaDescriptor::PROP_MEDIATYPE,
+ iter->second);
+ } catch (const uno::Exception &) { }
+ }
+ storeMetadataToStorage(xStorage);
+
+ if (!sfx)
+ return;
+
+ const bool bOk = aMedium.Commit();
+ aMedium.Close();
+ if ( !bOk ) {
+ ErrCode nError = aMedium.GetError();
+ if ( nError == ERRCODE_NONE ) {
+ nError = ERRCODE_IO_GENERAL;
+ }
+ task::ErrorCodeIOException ex(
+ "DocumentMetadataAccess::storeMetadataToMedium Commit failed: " + nError.toHexString(),
+ uno::Reference< uno::XInterface >(), sal_uInt32(nError));
+ throw lang::WrappedTargetException(OUString(), *this,
+ uno::Any(ex));
+ }
+}
+
+} // namespace sfx2
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/doc/DocumentSigner.cxx b/sfx2/source/doc/DocumentSigner.cxx
new file mode 100644
index 000000000..0106a6477
--- /dev/null
+++ b/sfx2/source/doc/DocumentSigner.cxx
@@ -0,0 +1,121 @@
+/* -*- 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/DocumentSigner.hxx>
+
+#include <tools/stream.hxx>
+#include <unotools/ucbstreamhelper.hxx>
+#include <unotools/streamwrap.hxx>
+
+#include <comphelper/storagehelper.hxx>
+#include <comphelper/processfactory.hxx>
+
+#include <com/sun/star/embed/XStorage.hpp>
+#include <com/sun/star/embed/XTransactedObject.hpp>
+#include <com/sun/star/security/DocumentDigitalSignatures.hpp>
+#include <com/sun/star/io/IOException.hpp>
+#include <com/sun/star/io/XStream.hpp>
+
+using namespace css;
+
+namespace sfx2
+{
+bool DocumentSigner::signDocument(uno::Reference<security::XCertificate> const& rxCertificate)
+{
+ std::unique_ptr<SvStream> pStream(
+ utl::UcbStreamHelper::CreateStream(m_aUrl, StreamMode::READ | StreamMode::WRITE));
+ uno::Reference<io::XStream> xInputStream(new utl::OStreamWrapper(std::move(pStream)));
+
+ bool bResult = false;
+ uno::Reference<embed::XStorage> xWriteableZipStore;
+ try
+ {
+ xWriteableZipStore = comphelper::OStorageHelper::GetStorageOfFormatFromStream(
+ ZIP_STORAGE_FORMAT_STRING, xInputStream);
+ }
+ catch (const io::IOException&)
+ {
+ }
+
+ OUString aODFVersion(comphelper::OStorageHelper::GetODFVersionFromStorage(xWriteableZipStore));
+
+ uno::Reference<security::XDocumentDigitalSignatures> xSigner(
+ security::DocumentDigitalSignatures::createWithVersionAndValidSignature(
+ comphelper::getProcessComponentContext(), aODFVersion,
+ /*bHasValidDocumentSignature*/ true));
+
+ try
+ {
+ uno::Reference<embed::XStorage> xMetaInf;
+ if (xWriteableZipStore.is() && xWriteableZipStore->hasByName("META-INF"))
+ {
+ xMetaInf = xWriteableZipStore->openStorageElement("META-INF",
+ embed::ElementModes::READWRITE);
+ if (!xMetaInf.is())
+ throw uno::RuntimeException();
+ }
+ if (xMetaInf.is())
+ {
+ uno::Reference<embed::XStorage> xStorage
+ = comphelper::OStorageHelper::GetStorageOfFormatFromStream(
+ ZIP_STORAGE_FORMAT_STRING, xInputStream);
+
+ // ODF.
+ uno::Reference<io::XStream> xStream;
+ xStream.set(
+ xMetaInf->openStreamElement(xSigner->getDocumentContentSignatureDefaultStreamName(),
+ embed::ElementModes::READWRITE),
+ uno::UNO_SET_THROW);
+ bool bSuccess = xSigner->signDocumentWithCertificate(rxCertificate, xStorage, xStream);
+ if (bSuccess)
+ {
+ uno::Reference<embed::XTransactedObject> xTransact(xMetaInf, uno::UNO_QUERY_THROW);
+ xTransact->commit();
+ xTransact.set(xWriteableZipStore, uno::UNO_QUERY_THROW);
+ xTransact->commit();
+ bResult = true;
+ }
+ }
+ else if (xWriteableZipStore.is())
+ {
+ uno::Reference<embed::XStorage> xStorage
+ = comphelper::OStorageHelper::GetStorageOfFormatFromStream(
+ ZIP_STORAGE_FORMAT_STRING, xInputStream);
+
+ // OOXML.
+ uno::Reference<io::XStream> xStream;
+
+ // We need read-write to be able to add the signature relation.
+ bool bSuccess = xSigner->signDocumentWithCertificate(rxCertificate, xStorage, xStream);
+
+ if (bSuccess)
+ {
+ uno::Reference<embed::XTransactedObject> xTransact(xWriteableZipStore,
+ uno::UNO_QUERY_THROW);
+ xTransact->commit();
+ bResult = true;
+ }
+ }
+ else
+ {
+ // Something not ZIP based: e.g. PDF.
+ bResult = xSigner->signDocumentWithCertificate(
+ rxCertificate, uno::Reference<embed::XStorage>(), xInputStream);
+ }
+ }
+ catch (const uno::Exception&)
+ {
+ }
+ return bResult;
+}
+
+} // namespace sfx2
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/doc/Metadatable.cxx b/sfx2/source/doc/Metadatable.cxx
new file mode 100644
index 000000000..1906967ca
--- /dev/null
+++ b/sfx2/source/doc/Metadatable.cxx
@@ -0,0 +1,1602 @@
+/* -*- 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 <sal/log.hxx>
+
+#include <osl/diagnose.h>
+#include <sfx2/Metadatable.hxx>
+#include <sfx2/XmlIdRegistry.hxx>
+
+#include <vcl/svapp.hxx>
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <comphelper/random.hxx>
+#include <tools/diagnose_ex.h>
+
+#include <algorithm>
+#include <memory>
+#include <string_view>
+#include <unordered_map>
+#if OSL_DEBUG_LEVEL > 0
+#include <typeinfo>
+#endif
+
+
+/** XML ID handling.
+
+ There is an abstract base class <type>XmlIdRegistry</type>, with
+ 2 subclasses <type>XmlIdRegistryDocument</type> for "normal" documents,
+ and <type>XmlIdRegistryClipboard</type> for clipboard documents.
+ These classes are responsible for managing XML IDs for all elements
+ of the model. Only the implementation of the <type>Metadatable</type>
+ base class needs to know the registries, so they are not in the header.
+
+ The handling of XML IDs differs between clipboard and non-clipboard
+ documents in several aspects. Most importantly, non-clipboard documents
+ can have several elements associated with one XML ID.
+ This is necessary because of the weird undo implementation:
+ deleting a text node moves the deleted node to the undo array, but
+ executing undo will then create a <em>copy</em> of that node in the
+ document array. These 2 nodes must have the same XML ID, because
+ we cannot know whether the user will do a redo next, or something else.
+
+ Because we need to have a mechanism for several objects per XML ID anyway,
+ we use that also to enable some usability features:
+ The document registry has a list of Metadatables per XML ID.
+ This list is sorted by priority, i.e., the first element has highest
+ priority. When inserting copies, care must be taken that they are inserted
+ at the right position: either before or after the source.
+ This is done by <method>Metadatable::RegisterAsCopyOf</method>.
+ When a text node is split, then both resulting text nodes are inserted
+ into the list. If the user then deletes one text node, the other one
+ will have the XML ID.
+ Also, when a Metadatable is copied to the clipboard and then pasted,
+ the copy is inserted into the list. If the user then deletes the source,
+ the XML ID is not lost.
+ The goal is that it should be hard to lose an XML ID by accident, which
+ is especially important as long as we do not have an UI that displays them.
+
+ There are two subclasses of <type>Metadatable</type>:
+ <ul><li><type>MetadatableClipboard</type>: for copies in the clipboard</li>
+ <li><type>MetadatableUndo</type>: for undo, because a Metadatable
+ may be destroyed on delete and a new one created on undo.</li></ul>
+ These serve only to track the position in an XML ID list in a document
+ registry, so that future actions can insert objects at the right position.
+ Unfortunately, inserting dummy objects seems to be necessary:
+ <ul><li>it is not sufficient to just remember the saved id, because then
+ the relative priorities might change when executing the undo</li>
+ <li>it is not sufficient to record the position as an integer, because
+ if we delete a text node and then undo, the node will be copied(!),
+ and we will have one more node in the list.<li>
+ <li>it is not sufficient to record the pointer of the previous/next
+ Metadatable, because if we delete a text node, undo, and then
+ do something to clear the redo array, the original text node is
+ destroyed, and is replaced by the copy created by undo</li></ul>
+
+ If content from a non-clipboard document is copied into a clipboard
+ document, a dummy <type>MetadatableClipboard</type> is inserted into the
+ non-clipboard document registry in order to track the position of the
+ source element. When the clipboard content is pasted back into the source
+ document, this dummy object is used to associate the pasted element with
+ that same XML ID.
+
+ If a <type>Metadatable</type> is deleted or merged,
+ <method>Metadatable::CreateUndo</method> is called, and returns a
+ <type>MetadatableUndo<type> instance, which can be used to undo the action
+ by passing it to <method>Metadatable::RestoreMetadata</method>.
+
+ */
+
+
+using namespace ::com::sun::star;
+
+using ::sfx2::isValidXmlId;
+
+
+namespace sfx2 {
+
+constexpr OUStringLiteral s_content = u"content.xml";
+constexpr OUStringLiteral s_styles = u"styles.xml";
+
+static bool isContentFile(std::u16string_view i_rPath)
+{
+ return i_rPath == s_content;
+}
+
+static bool isStylesFile (std::u16string_view i_rPath)
+{
+ return i_rPath == s_styles;
+}
+
+
+// XML ID handling ---------------------------------------------------
+
+/** handles registration of XMetadatable.
+
+ This class is responsible for guaranteeing that XMetadatable objects
+ always have XML IDs that are unique within a stream.
+
+ This is an abstract base class; see subclasses XmlIdRegistryDocument and
+ XmlIdRegistryClipboard.
+
+ @see SwDoc::GetXmlIdRegistry
+ @see SwDocShell::GetXmlIdRegistry
+ */
+class XmlIdRegistry : public sfx2::IXmlIdRegistry
+{
+
+public:
+ XmlIdRegistry();
+
+ /** get the ODF element with the given metadata reference. */
+ virtual css::uno::Reference< css::rdf::XMetadatable >
+ GetElementByMetadataReference(
+ const css::beans::StringPair & i_rReference) const
+ override;
+
+ /** register an ODF element at a newly generated, unique metadata reference.
+
+ <p>
+ Find a fresh XML ID, and register it for the element.
+ The generated ID does not occur in any stream of the document.
+ </p>
+ */
+ virtual void RegisterMetadatableAndCreateID(Metadatable& i_xObject) = 0;
+
+ /** try to register an ODF element at a given XML ID, or update its
+ registration to a different XML ID.
+
+ <p>
+ If the given new metadata reference is not already occupied in the
+ document, unregister the element at its old metadata reference if
+ it has one, and register the new metadata reference for the element.
+ Note that this method only ensures that XML IDs are unique per stream,
+ so using the same XML ID in both content.xml and styles.xml is allowed.
+ </p>
+
+ @returns
+ true iff the element has successfully been registered
+ */
+ virtual bool TryRegisterMetadatable(Metadatable& i_xObject,
+ OUString const& i_rStreamName, OUString const& i_rIdref)
+ = 0;
+
+ /** unregister an ODF element.
+
+ <p>
+ Unregister the element at its metadata reference.
+ Does not remove the metadata reference from the element.
+ </p>
+
+ @see RemoveXmlIdForElement
+ */
+ virtual void UnregisterMetadatable(Metadatable const&) = 0;
+
+ /** get the metadata reference for the given element. */
+ css::beans::StringPair
+ GetXmlIdForElement(Metadatable const&) const;
+
+ /** remove the metadata reference for the given element. */
+ virtual void RemoveXmlIdForElement(Metadatable const&) = 0;
+
+protected:
+
+ virtual bool LookupXmlId(const Metadatable& i_xObject,
+ OUString & o_rStream, OUString & o_rIdref) const = 0;
+
+ virtual Metadatable* LookupElement(const OUString & i_rStreamName,
+ const OUString & i_rIdref) const = 0;
+};
+
+// XmlIdRegistryDocument ---------------------------------------------
+
+namespace {
+
+/** non-clipboard documents */
+class XmlIdRegistryDocument : public XmlIdRegistry
+{
+
+public:
+ XmlIdRegistryDocument();
+
+ virtual ~XmlIdRegistryDocument() override;
+
+ virtual void RegisterMetadatableAndCreateID(Metadatable& i_xObject) override;
+
+ virtual bool TryRegisterMetadatable(Metadatable& i_xObject,
+ OUString const& i_rStreamName, OUString const& i_rIdref) override;
+
+ virtual void UnregisterMetadatable(Metadatable const&) override;
+
+ virtual void RemoveXmlIdForElement(Metadatable const&) override;
+
+ /** register i_rCopy as a copy of i_rSource,
+ with precedence iff i_bCopyPrecedesSource is true */
+ void RegisterCopy(Metadatable const& i_rSource, Metadatable & i_rCopy,
+ const bool i_bCopyPrecedesSource);
+
+ /** create a Undo Metadatable for i_rObject. */
+ static std::shared_ptr<MetadatableUndo> CreateUndo(
+ Metadatable const& i_rObject);
+
+ /** merge i_rMerged and i_rOther into i_rMerged. */
+ void JoinMetadatables(Metadatable & i_rMerged, Metadatable const& i_rOther);
+
+ // unfortunately public, Metadatable::RegisterAsCopyOf needs this
+ virtual bool LookupXmlId(const Metadatable& i_xObject,
+ OUString & o_rStream, OUString & o_rIdref) const override;
+
+private:
+
+ virtual Metadatable* LookupElement(const OUString & i_rStreamName,
+ const OUString & i_rIdref) const override;
+
+ struct XmlIdRegistry_Impl;
+ ::std::unique_ptr<XmlIdRegistry_Impl> m_pImpl;
+};
+
+}
+
+// MetadatableUndo ---------------------------------------------------
+
+/** the horrible Undo Metadatable: is inserted into lists to track position */
+class MetadatableUndo : public Metadatable
+{
+ /// as determined by the stream of the source in original document
+ const bool m_isInContent;
+public:
+ explicit MetadatableUndo(const bool i_isInContent)
+ : m_isInContent(i_isInContent) { }
+ virtual ::sfx2::XmlIdRegistry& GetRegistry() override
+ {
+ // N.B. for Undo, m_pReg is initialized by registering this as copy in
+ // CreateUndo; it is never cleared
+ OSL_ENSURE(m_pReg, "no m_pReg in MetadatableUndo ?");
+ return *m_pReg;
+ }
+ virtual bool IsInClipboard() const override { return false; }
+ virtual bool IsInUndo() const override { return true; }
+ virtual bool IsInContent() const override { return m_isInContent; }
+ virtual css::uno::Reference< css::rdf::XMetadatable > MakeUnoObject() override
+ { OSL_FAIL("MetadatableUndo::MakeUnoObject"); throw; }
+};
+
+// MetadatableClipboard ----------------------------------------------
+
+/** the horrible Clipboard Metadatable: inserted into lists to track position */
+class MetadatableClipboard : public Metadatable
+{
+ /// as determined by the stream of the source in original document
+ const bool m_isInContent;
+public:
+ explicit MetadatableClipboard(const bool i_isInContent)
+ : m_isInContent(i_isInContent) { }
+ virtual ::sfx2::XmlIdRegistry& GetRegistry() override
+ {
+ // N.B. for Clipboard, m_pReg is initialized by registering this as copy in
+ // RegisterAsCopyOf; it is only cleared by OriginNoLongerInBusinessAnymore
+ assert(m_pReg && "no m_pReg in MetadatableClipboard ?");
+ return *m_pReg;
+ }
+ virtual bool IsInClipboard() const override { return true; }
+ virtual bool IsInUndo() const override { return false; }
+ virtual bool IsInContent() const override { return m_isInContent; }
+ virtual css::uno::Reference< css::rdf::XMetadatable > MakeUnoObject() override
+ { OSL_FAIL("MetadatableClipboard::MakeUnoObject"); throw; }
+ void OriginNoLongerInBusinessAnymore() { m_pReg = nullptr; }
+};
+
+// XmlIdRegistryClipboard --------------------------------------------
+
+namespace {
+
+class XmlIdRegistryClipboard : public XmlIdRegistry
+{
+
+public:
+ XmlIdRegistryClipboard();
+
+ virtual void RegisterMetadatableAndCreateID(Metadatable& i_xObject) override;
+
+ virtual bool TryRegisterMetadatable(Metadatable& i_xObject,
+ OUString const& i_rStreamName, OUString const& i_rIdref) override;
+
+ virtual void UnregisterMetadatable(Metadatable const&) override;
+
+ virtual void RemoveXmlIdForElement(Metadatable const&) override;
+
+ /** register i_rCopy as a copy of i_rSource */
+ MetadatableClipboard & RegisterCopyClipboard(Metadatable & i_rCopy,
+ beans::StringPair const & i_rReference,
+ const bool i_isLatent);
+
+ /** get the Metadatable that links i_rObject to its origin registry */
+ MetadatableClipboard const* SourceLink(Metadatable const& i_rObject);
+
+private:
+ virtual bool LookupXmlId(const Metadatable& i_xObject,
+ OUString & o_rStream, OUString & o_rIdref) const override;
+
+ virtual Metadatable* LookupElement(const OUString & i_rStreamName,
+ const OUString & i_rIdref) const override;
+
+ /** create a Clipboard Metadatable for i_rObject. */
+ static std::shared_ptr<MetadatableClipboard> CreateClipboard(
+ const bool i_isInContent);
+
+ struct XmlIdRegistry_Impl;
+ ::std::unique_ptr<XmlIdRegistry_Impl> m_pImpl;
+};
+
+}
+
+// XmlIdRegistry
+
+::sfx2::IXmlIdRegistry * createXmlIdRegistry(const bool i_DocIsClipboard)
+{
+ return i_DocIsClipboard
+ ? static_cast<XmlIdRegistry*>( new XmlIdRegistryClipboard )
+ : static_cast<XmlIdRegistry*>( new XmlIdRegistryDocument );
+}
+
+XmlIdRegistry::XmlIdRegistry()
+{
+}
+
+css::uno::Reference< css::rdf::XMetadatable >
+XmlIdRegistry::GetElementByMetadataReference(
+ const beans::StringPair & i_rReference) const
+{
+ Metadatable* pObject( LookupElement(i_rReference.First,
+ i_rReference.Second) );
+ return pObject ? pObject->MakeUnoObject() : nullptr;
+}
+
+beans::StringPair
+XmlIdRegistry::GetXmlIdForElement(const Metadatable& i_rObject) const
+{
+ OUString path;
+ OUString idref;
+ if (LookupXmlId(i_rObject, path, idref))
+ {
+ if (LookupElement(path, idref) == &i_rObject)
+ {
+ return beans::StringPair(path, idref);
+ }
+ }
+ return beans::StringPair();
+}
+
+
+/// generate unique xml:id
+template< typename T >
+static OUString create_id(const
+ std::unordered_map< OUString, T > & i_rXmlIdMap)
+{
+ static bool bHack = (getenv("LIBO_ONEWAY_STABLE_ODF_EXPORT") != nullptr);
+ static const char prefix[] = "id"; // prefix for generated xml:id
+ typename std::unordered_map< OUString, T >
+ ::const_iterator iter;
+ OUString id;
+
+ if (bHack)
+ {
+ static sal_Int64 nIdCounter = SAL_CONST_INT64(4000000000);
+ do
+ {
+ id = prefix + OUString::number(nIdCounter++);
+ iter = i_rXmlIdMap.find(id);
+ }
+ while (iter != i_rXmlIdMap.end());
+ }
+ else
+ {
+ do
+ {
+ unsigned int const n(comphelper::rng::uniform_uint_distribution(0,
+ std::numeric_limits<unsigned int>::max()));
+ id = prefix + OUString::number(n);
+ iter = i_rXmlIdMap.find(id);
+ }
+ while (iter != i_rXmlIdMap.end());
+ }
+ return id;
+}
+
+
+// Document XML ID Registry (_Impl)
+
+/// element list
+typedef ::std::vector< Metadatable* > XmlIdVector_t;
+
+/// Idref -> (content.xml element list, styles.xml element list)
+typedef std::unordered_map< OUString,
+ ::std::pair< XmlIdVector_t, XmlIdVector_t > > XmlIdMap_t;
+
+namespace {
+
+/// pointer hash template
+template<typename T> struct PtrHash
+{
+ size_t operator() (T const * i_pT) const
+ {
+ return reinterpret_cast<size_t>(i_pT);
+ }
+};
+
+}
+
+/// element -> (stream name, idref)
+typedef std::unordered_map< const Metadatable*,
+ ::std::pair< OUString, OUString>, PtrHash<Metadatable> >
+ XmlIdReverseMap_t;
+
+struct XmlIdRegistryDocument::XmlIdRegistry_Impl
+{
+ XmlIdRegistry_Impl() {}
+
+ bool TryInsertMetadatable(Metadatable& i_xObject,
+ std::u16string_view i_rStream, const OUString & i_rIdref);
+
+ bool LookupXmlId(const Metadatable& i_xObject,
+ OUString & o_rStream, OUString & o_rIdref) const;
+
+ Metadatable* LookupElement(std::u16string_view i_rStreamName,
+ const OUString & i_rIdref) const;
+
+ const XmlIdVector_t * LookupElementVector(
+ std::u16string_view i_rStreamName,
+ const OUString & i_rIdref) const;
+
+ XmlIdVector_t * LookupElementVector(
+ std::u16string_view i_rStreamName,
+ const OUString & i_rIdref)
+ {
+ return const_cast<XmlIdVector_t*>(
+ const_cast<const XmlIdRegistry_Impl*>(this)
+ ->LookupElementVector(i_rStreamName, i_rIdref));
+ }
+
+ XmlIdMap_t m_XmlIdMap;
+ XmlIdReverseMap_t m_XmlIdReverseMap;
+};
+
+
+static void
+rmIter(XmlIdMap_t & i_rXmlIdMap, XmlIdMap_t::iterator const& i_rIter,
+ std::u16string_view i_rStream, Metadatable const& i_rObject)
+{
+ if (i_rIter != i_rXmlIdMap.end())
+ {
+ XmlIdVector_t & rVector( isContentFile(i_rStream)
+ ? i_rIter->second.first : i_rIter->second.second );
+ rVector.erase(std::remove(rVector.begin(), rVector.end(), &const_cast<Metadatable&>(i_rObject)));
+ if (i_rIter->second.first.empty() && i_rIter->second.second.empty())
+ {
+ i_rXmlIdMap.erase(i_rIter);
+ }
+ }
+}
+
+
+const XmlIdVector_t *
+XmlIdRegistryDocument::XmlIdRegistry_Impl::LookupElementVector(
+ std::u16string_view i_rStreamName,
+ const OUString & i_rIdref) const
+{
+ const XmlIdMap_t::const_iterator iter( m_XmlIdMap.find(i_rIdref) );
+ if (iter != m_XmlIdMap.end())
+ {
+ OSL_ENSURE(!iter->second.first.empty() || !iter->second.second.empty(),
+ "null entry in m_XmlIdMap");
+ return (isContentFile(i_rStreamName))
+ ? &iter->second.first
+ : &iter->second.second;
+ }
+ else
+ {
+ return nullptr;
+ }
+}
+
+Metadatable*
+XmlIdRegistryDocument::XmlIdRegistry_Impl::LookupElement(
+ std::u16string_view i_rStreamName,
+ const OUString & i_rIdref) const
+{
+ if (!isValidXmlId(i_rStreamName, i_rIdref))
+ {
+ throw lang::IllegalArgumentException("illegal XmlId", nullptr, 0);
+ }
+
+ const XmlIdVector_t * pList( LookupElementVector(i_rStreamName, i_rIdref) );
+ if (pList)
+ {
+ const XmlIdVector_t::const_iterator iter(
+ ::std::find_if(pList->begin(), pList->end(),
+ [](Metadatable* item)->bool {
+ return !(item->IsInUndo() || item->IsInClipboard());
+ } ) ) ;
+ if (iter != pList->end())
+ {
+ return *iter;
+ }
+ }
+ return nullptr;
+}
+
+bool
+XmlIdRegistryDocument::XmlIdRegistry_Impl::LookupXmlId(
+ const Metadatable& i_rObject,
+ OUString & o_rStream, OUString & o_rIdref) const
+{
+ const XmlIdReverseMap_t::const_iterator iter(
+ m_XmlIdReverseMap.find(&i_rObject) );
+ if (iter != m_XmlIdReverseMap.end())
+ {
+ OSL_ENSURE(!iter->second.first.isEmpty(),
+ "null stream in m_XmlIdReverseMap");
+ OSL_ENSURE(!iter->second.second.isEmpty(),
+ "null id in m_XmlIdReverseMap");
+ o_rStream = iter->second.first;
+ o_rIdref = iter->second.second;
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+bool
+XmlIdRegistryDocument::XmlIdRegistry_Impl::TryInsertMetadatable(
+ Metadatable & i_rObject,
+ std::u16string_view i_rStreamName, const OUString & i_rIdref)
+{
+ const bool bContent( isContentFile(i_rStreamName) );
+ OSL_ENSURE(isContentFile(i_rStreamName) || isStylesFile(i_rStreamName),
+ "invalid stream");
+
+ XmlIdVector_t * pList( LookupElementVector(i_rStreamName, i_rIdref) );
+ if (pList)
+ {
+ if (pList->empty())
+ {
+ pList->push_back( &i_rObject );
+ return true;
+ }
+ else
+ {
+ // this is only called from TryRegister now, so check
+ // if all elements in the list are deleted (in undo) or
+ // placeholders, then "steal" the id from them
+ if ( std::none_of(pList->begin(), pList->end(),
+ [](Metadatable* item)->bool {
+ return !(item->IsInUndo() || item->IsInClipboard());
+ } ) )
+ {
+ pList->insert(pList->begin(), &i_rObject );
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+ }
+ else
+ {
+ m_XmlIdMap.insert(::std::make_pair(i_rIdref, bContent
+ ? ::std::make_pair( XmlIdVector_t( 1, &i_rObject ), XmlIdVector_t() )
+ : ::std::make_pair( XmlIdVector_t(), XmlIdVector_t( 1, &i_rObject ) )));
+ return true;
+ }
+}
+
+
+// Document XML ID Registry
+
+
+XmlIdRegistryDocument::XmlIdRegistryDocument()
+ : m_pImpl( new XmlIdRegistry_Impl )
+{
+}
+
+static void
+removeLink(Metadatable* i_pObject)
+{
+ OSL_ENSURE(i_pObject, "null in list ???");
+ if (!i_pObject) return;
+ if (i_pObject->IsInClipboard())
+ {
+ MetadatableClipboard* pLink(
+ dynamic_cast<MetadatableClipboard*>( i_pObject ) );
+ OSL_ENSURE(pLink, "IsInClipboard, but no MetadatableClipboard ?");
+ if (pLink)
+ {
+ pLink->OriginNoLongerInBusinessAnymore();
+ }
+ }
+}
+
+XmlIdRegistryDocument::~XmlIdRegistryDocument()
+{
+ // notify all list elements that are actually in the clipboard
+ for (const auto& aXmlId : m_pImpl->m_XmlIdMap) {
+ for (auto aLink : aXmlId.second.first)
+ removeLink(aLink);
+ for (auto aLink : aXmlId.second.second)
+ removeLink(aLink);
+ }
+}
+
+bool
+XmlIdRegistryDocument::LookupXmlId(
+ const Metadatable& i_rObject,
+ OUString & o_rStream, OUString & o_rIdref) const
+{
+ return m_pImpl->LookupXmlId(i_rObject, o_rStream, o_rIdref);
+}
+
+Metadatable*
+XmlIdRegistryDocument::LookupElement(
+ const OUString & i_rStreamName,
+ const OUString & i_rIdref) const
+{
+ return m_pImpl->LookupElement(i_rStreamName, i_rIdref);
+}
+
+bool
+XmlIdRegistryDocument::TryRegisterMetadatable(Metadatable & i_rObject,
+ OUString const& i_rStreamName, OUString const& i_rIdref)
+{
+ SAL_INFO("sfx", "TryRegisterMetadatable: " << &i_rObject << " (" << i_rStreamName << "#" << i_rIdref << ")");
+
+ OSL_ENSURE(!dynamic_cast<MetadatableUndo*>(&i_rObject),
+ "TryRegisterMetadatable called for MetadatableUndo?");
+ OSL_ENSURE(!dynamic_cast<MetadatableClipboard*>(&i_rObject),
+ "TryRegisterMetadatable called for MetadatableClipboard?");
+
+ if (!isValidXmlId(i_rStreamName, i_rIdref))
+ {
+ throw lang::IllegalArgumentException("illegal XmlId", nullptr, 0);
+ }
+ if (i_rObject.IsInContent()
+ ? !isContentFile(i_rStreamName)
+ : !isStylesFile(i_rStreamName))
+ {
+ throw lang::IllegalArgumentException("illegal XmlId: wrong stream", nullptr, 0);
+ }
+
+ OUString old_path;
+ OUString old_idref;
+ m_pImpl->LookupXmlId(i_rObject, old_path, old_idref);
+ if (old_path == i_rStreamName && old_idref == i_rIdref)
+ {
+ return (m_pImpl->LookupElement(old_path, old_idref) == &i_rObject);
+ }
+ XmlIdMap_t::iterator old_id( m_pImpl->m_XmlIdMap.end() );
+ if (!old_idref.isEmpty())
+ {
+ old_id = m_pImpl->m_XmlIdMap.find(old_idref);
+ OSL_ENSURE(old_id != m_pImpl->m_XmlIdMap.end(), "old id not found");
+ }
+ if (m_pImpl->TryInsertMetadatable(i_rObject, i_rStreamName, i_rIdref))
+ {
+ rmIter(m_pImpl->m_XmlIdMap, old_id, old_path, i_rObject);
+ m_pImpl->m_XmlIdReverseMap[&i_rObject] =
+ ::std::make_pair(i_rStreamName, i_rIdref);
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+void
+XmlIdRegistryDocument::RegisterMetadatableAndCreateID(Metadatable & i_rObject)
+{
+ SAL_INFO("sfx", "RegisterMetadatableAndCreateID: " << &i_rObject);
+
+ OSL_ENSURE(!dynamic_cast<MetadatableUndo*>(&i_rObject),
+ "RegisterMetadatableAndCreateID called for MetadatableUndo?");
+ OSL_ENSURE(!dynamic_cast<MetadatableClipboard*>(&i_rObject),
+ "RegisterMetadatableAndCreateID called for MetadatableClipboard?");
+
+ const bool isInContent( i_rObject.IsInContent() );
+ const OUString stream(
+ isInContent ? OUString(s_content) : OUString(s_styles) );
+ // check if we have a latent xmlid, and if yes, remove it
+ OUString old_path;
+ OUString old_idref;
+ m_pImpl->LookupXmlId(i_rObject, old_path, old_idref);
+
+ XmlIdMap_t::iterator old_id( m_pImpl->m_XmlIdMap.end() );
+ if (!old_idref.isEmpty())
+ {
+ old_id = m_pImpl->m_XmlIdMap.find(old_idref);
+ OSL_ENSURE(old_id != m_pImpl->m_XmlIdMap.end(), "old id not found");
+ if (m_pImpl->LookupElement(old_path, old_idref) == &i_rObject)
+ {
+ return;
+ }
+ else
+ {
+ // remove latent xmlid
+ rmIter(m_pImpl->m_XmlIdMap, old_id, old_path, i_rObject);
+ }
+ }
+
+ // create id
+ const OUString id( create_id(m_pImpl->m_XmlIdMap) );
+ OSL_ENSURE(m_pImpl->m_XmlIdMap.find(id) == m_pImpl->m_XmlIdMap.end(),
+ "created id is in use");
+ m_pImpl->m_XmlIdMap.insert(::std::make_pair(id, isInContent
+ ? ::std::make_pair( XmlIdVector_t( 1, &i_rObject ), XmlIdVector_t() )
+ : ::std::make_pair( XmlIdVector_t(), XmlIdVector_t( 1, &i_rObject ) )));
+ m_pImpl->m_XmlIdReverseMap[&i_rObject] = ::std::make_pair(stream, id);
+}
+
+void XmlIdRegistryDocument::UnregisterMetadatable(const Metadatable& i_rObject)
+{
+ SAL_INFO("sfx", "UnregisterMetadatable: " << &i_rObject);
+
+ OUString path;
+ OUString idref;
+ if (!m_pImpl->LookupXmlId(i_rObject, path, idref))
+ {
+ OSL_FAIL("unregister: no xml id?");
+ return;
+ }
+ const XmlIdMap_t::iterator iter( m_pImpl->m_XmlIdMap.find(idref) );
+ if (iter != m_pImpl->m_XmlIdMap.end())
+ {
+ rmIter(m_pImpl->m_XmlIdMap, iter, path, i_rObject);
+ }
+}
+
+void XmlIdRegistryDocument::RemoveXmlIdForElement(const Metadatable& i_rObject)
+{
+ SAL_INFO("sfx", "RemoveXmlIdForElement: " << &i_rObject);
+
+ const XmlIdReverseMap_t::iterator iter(
+ m_pImpl->m_XmlIdReverseMap.find(&i_rObject) );
+ if (iter != m_pImpl->m_XmlIdReverseMap.end())
+ {
+ OSL_ENSURE(!iter->second.second.isEmpty(),
+ "null id in m_XmlIdReverseMap");
+ m_pImpl->m_XmlIdReverseMap.erase(iter);
+ }
+}
+
+
+void XmlIdRegistryDocument::RegisterCopy(Metadatable const& i_rSource,
+ Metadatable & i_rCopy, const bool i_bCopyPrecedesSource)
+{
+ SAL_INFO("sfx", "RegisterCopy: " << &i_rSource << " -> " << &i_rCopy << " (" << i_bCopyPrecedesSource << ")");
+
+ // potential sources: clipboard, undo array, splitNode
+ // assumption: stream change can only happen via clipboard, and is handled
+ // by Metadatable::RegisterAsCopyOf
+ OSL_ENSURE(i_rSource.IsInUndo() || i_rCopy.IsInUndo() ||
+ (i_rSource.IsInContent() == i_rCopy.IsInContent()),
+ "RegisterCopy: not in same stream?");
+
+ OUString path;
+ OUString idref;
+ if (!m_pImpl->LookupXmlId( i_rSource, path, idref ))
+ {
+ OSL_FAIL("no xml id?");
+ return;
+ }
+ XmlIdVector_t * pList ( m_pImpl->LookupElementVector(path, idref) );
+ OSL_ENSURE( ::std::find( pList->begin(), pList->end(), &i_rCopy )
+ == pList->end(), "copy already registered???");
+ XmlIdVector_t::iterator srcpos(
+ ::std::find( pList->begin(), pList->end(), &i_rSource ) );
+ OSL_ENSURE(srcpos != pList->end(), "source not in list???");
+ if (srcpos == pList->end())
+ {
+ return;
+ }
+ if (i_bCopyPrecedesSource)
+ {
+ pList->insert( srcpos, &i_rCopy );
+ }
+ else
+ {
+ // for undo push_back does not work! must insert right after source
+ pList->insert( ++srcpos, &i_rCopy );
+ }
+ m_pImpl->m_XmlIdReverseMap.insert(::std::make_pair(&i_rCopy,
+ ::std::make_pair(path, idref)));
+}
+
+std::shared_ptr<MetadatableUndo>
+XmlIdRegistryDocument::CreateUndo(Metadatable const& i_rObject)
+{
+ SAL_INFO("sfx", "CreateUndo: " << &i_rObject);
+
+ return std::make_shared<MetadatableUndo>(
+ i_rObject.IsInContent() );
+}
+
+/*
+i_rMerged is both a source and the target node of the merge
+i_rOther is the other source, and will be deleted after the merge
+
+dimensions: none|latent|actual empty|nonempty
+i_rMerged(1) i_rOther(2) result
+ *|empty *|empty => 1|2 (arbitrary)
+ *|empty *|nonempty => 2
+ *|nonempty *|empty => 1
+ none|nonempty none|nonempty => none
+ none|nonempty latent|nonempty => 2
+latent|nonempty none|nonempty => 1
+latent|nonempty latent|nonempty => 1|2
+ *|nonempty actual|nonempty => 2
+actual|nonempty *|nonempty => 1
+actual|nonempty actual|nonempty => 1|2
+*/
+void
+XmlIdRegistryDocument::JoinMetadatables(
+ Metadatable & i_rMerged, Metadatable const & i_rOther)
+{
+ SAL_INFO("sfx", "JoinMetadatables: " << &i_rMerged << " <- " << &i_rOther);
+
+ bool mergedOwnsRef;
+ OUString path;
+ OUString idref;
+ if (m_pImpl->LookupXmlId(i_rMerged, path, idref))
+ {
+ mergedOwnsRef = (m_pImpl->LookupElement(path, idref) == &i_rMerged);
+ }
+ else
+ {
+ OSL_FAIL("JoinMetadatables: no xmlid?");
+ return;
+ }
+ if (!mergedOwnsRef)
+ {
+ i_rMerged.RemoveMetadataReference();
+ i_rMerged.RegisterAsCopyOf(i_rOther, true);
+ return;
+ }
+ // other cases: merged has actual ref and is nonempty,
+ // other has latent/actual ref and is nonempty: other loses => nothing to do
+}
+
+
+// Clipboard XML ID Registry (_Impl)
+
+namespace {
+
+struct RMapEntry
+{
+ RMapEntry() {}
+ RMapEntry(OUString const& i_rStream,
+ OUString const& i_rXmlId,
+ std::shared_ptr<MetadatableClipboard> const& i_pLink
+ = std::shared_ptr<MetadatableClipboard>())
+ : m_Stream(i_rStream), m_XmlId(i_rXmlId), m_xLink(i_pLink)
+ {}
+ OUString m_Stream;
+ OUString m_XmlId;
+ // this would have been an auto_ptr, if only that would have compiled...
+ std::shared_ptr<MetadatableClipboard> m_xLink;
+};
+
+}
+
+/// element -> (stream name, idref, source)
+typedef std::unordered_map< const Metadatable*,
+ struct RMapEntry,
+ PtrHash<Metadatable> >
+ ClipboardXmlIdReverseMap_t;
+
+/// Idref -> (content.xml element, styles.xml element)
+typedef std::unordered_map< OUString,
+ ::std::pair< Metadatable*, Metadatable* > >
+ ClipboardXmlIdMap_t;
+
+struct XmlIdRegistryClipboard::XmlIdRegistry_Impl
+{
+ XmlIdRegistry_Impl() {}
+
+ bool TryInsertMetadatable(Metadatable& i_xObject,
+ std::u16string_view i_rStream, const OUString & i_rIdref);
+
+ bool LookupXmlId(const Metadatable& i_xObject,
+ OUString & o_rStream, OUString & o_rIdref,
+ MetadatableClipboard const* &o_rpLink) const;
+
+ Metadatable* LookupElement(std::u16string_view i_rStreamName,
+ const OUString & i_rIdref) const;
+
+ Metadatable* const* LookupEntry(std::u16string_view i_rStreamName,
+ const OUString & i_rIdref) const;
+
+ ClipboardXmlIdMap_t m_XmlIdMap;
+ ClipboardXmlIdReverseMap_t m_XmlIdReverseMap;
+};
+
+
+static void
+rmIter(ClipboardXmlIdMap_t & i_rXmlIdMap,
+ ClipboardXmlIdMap_t::iterator const& i_rIter,
+ std::u16string_view i_rStream, Metadatable const& i_rObject)
+{
+ if (i_rIter == i_rXmlIdMap.end())
+ return;
+
+ Metadatable *& rMeta = isContentFile(i_rStream)
+ ? i_rIter->second.first : i_rIter->second.second;
+ if (rMeta == &i_rObject)
+ {
+ rMeta = nullptr;
+ }
+ if (!i_rIter->second.first && !i_rIter->second.second)
+ {
+ i_rXmlIdMap.erase(i_rIter);
+ }
+}
+
+
+Metadatable* const*
+XmlIdRegistryClipboard::XmlIdRegistry_Impl::LookupEntry(
+ std::u16string_view i_rStreamName,
+ const OUString & i_rIdref) const
+{
+ if (!isValidXmlId(i_rStreamName, i_rIdref))
+ {
+ throw lang::IllegalArgumentException("illegal XmlId", nullptr, 0);
+ }
+
+ const ClipboardXmlIdMap_t::const_iterator iter( m_XmlIdMap.find(i_rIdref) );
+ if (iter != m_XmlIdMap.end())
+ {
+ OSL_ENSURE(iter->second.first || iter->second.second,
+ "null entry in m_XmlIdMap");
+ return (isContentFile(i_rStreamName))
+ ? &iter->second.first
+ : &iter->second.second;
+ }
+ else
+ {
+ return nullptr;
+ }
+}
+
+Metadatable*
+XmlIdRegistryClipboard::XmlIdRegistry_Impl::LookupElement(
+ std::u16string_view i_rStreamName,
+ const OUString & i_rIdref) const
+{
+ Metadatable * const * ppEntry = LookupEntry(i_rStreamName, i_rIdref);
+ return ppEntry ? *ppEntry : nullptr;
+}
+
+bool
+XmlIdRegistryClipboard::XmlIdRegistry_Impl::LookupXmlId(
+ const Metadatable& i_rObject,
+ OUString & o_rStream, OUString & o_rIdref,
+ MetadatableClipboard const* &o_rpLink) const
+{
+ const ClipboardXmlIdReverseMap_t::const_iterator iter(
+ m_XmlIdReverseMap.find(&i_rObject) );
+ if (iter != m_XmlIdReverseMap.end())
+ {
+ OSL_ENSURE(!iter->second.m_Stream.isEmpty(),
+ "null stream in m_XmlIdReverseMap");
+ OSL_ENSURE(!iter->second.m_XmlId.isEmpty(),
+ "null id in m_XmlIdReverseMap");
+ o_rStream = iter->second.m_Stream;
+ o_rIdref = iter->second.m_XmlId;
+ o_rpLink = iter->second.m_xLink.get();
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+bool
+XmlIdRegistryClipboard::XmlIdRegistry_Impl::TryInsertMetadatable(
+ Metadatable & i_rObject,
+ std::u16string_view i_rStreamName, const OUString & i_rIdref)
+{
+ bool bContent( isContentFile(i_rStreamName) );
+ OSL_ENSURE(isContentFile(i_rStreamName) || isStylesFile(i_rStreamName),
+ "invalid stream");
+
+ Metadatable ** ppEntry = const_cast<Metadatable**>(LookupEntry(i_rStreamName, i_rIdref));
+ if (ppEntry)
+ {
+ if (*ppEntry)
+ {
+ return false;
+ }
+ else
+ {
+ *ppEntry = &i_rObject;
+ return true;
+ }
+ }
+ else
+ {
+ m_XmlIdMap.insert(::std::make_pair(i_rIdref, bContent
+ ? ::std::make_pair( &i_rObject, static_cast<Metadatable*>(nullptr) )
+ : ::std::make_pair( static_cast<Metadatable*>(nullptr), &i_rObject )));
+ return true;
+ }
+}
+
+
+// Clipboard XML ID Registry
+
+
+XmlIdRegistryClipboard::XmlIdRegistryClipboard()
+ : m_pImpl( new XmlIdRegistry_Impl )
+{
+}
+
+bool
+XmlIdRegistryClipboard::LookupXmlId(
+ const Metadatable& i_rObject,
+ OUString & o_rStream, OUString & o_rIdref) const
+{
+ const MetadatableClipboard * pLink;
+ return m_pImpl->LookupXmlId(i_rObject, o_rStream, o_rIdref, pLink);
+}
+
+Metadatable*
+XmlIdRegistryClipboard::LookupElement(
+ const OUString & i_rStreamName,
+ const OUString & i_rIdref) const
+{
+ return m_pImpl->LookupElement(i_rStreamName, i_rIdref);
+}
+
+bool
+XmlIdRegistryClipboard::TryRegisterMetadatable(Metadatable & i_rObject,
+ OUString const& i_rStreamName, OUString const& i_rIdref)
+{
+ SAL_INFO("sfx", "TryRegisterMetadatable: " << &i_rObject << " (" << i_rStreamName << "#" << i_rIdref <<")");
+
+ OSL_ENSURE(!dynamic_cast<MetadatableUndo*>(&i_rObject),
+ "TryRegisterMetadatable called for MetadatableUndo?");
+ OSL_ENSURE(!dynamic_cast<MetadatableClipboard*>(&i_rObject),
+ "TryRegisterMetadatable called for MetadatableClipboard?");
+
+ if (!isValidXmlId(i_rStreamName, i_rIdref))
+ {
+ throw lang::IllegalArgumentException("illegal XmlId", nullptr, 0);
+ }
+ if (i_rObject.IsInContent()
+ ? !isContentFile(i_rStreamName)
+ : !isStylesFile(i_rStreamName))
+ {
+ throw lang::IllegalArgumentException("illegal XmlId: wrong stream", nullptr, 0);
+ }
+
+ OUString old_path;
+ OUString old_idref;
+ const MetadatableClipboard * pLink;
+ m_pImpl->LookupXmlId(i_rObject, old_path, old_idref, pLink);
+ if (old_path == i_rStreamName && old_idref == i_rIdref)
+ {
+ return (m_pImpl->LookupElement(old_path, old_idref) == &i_rObject);
+ }
+ ClipboardXmlIdMap_t::iterator old_id( m_pImpl->m_XmlIdMap.end() );
+ if (!old_idref.isEmpty())
+ {
+ old_id = m_pImpl->m_XmlIdMap.find(old_idref);
+ OSL_ENSURE(old_id != m_pImpl->m_XmlIdMap.end(), "old id not found");
+ }
+ if (m_pImpl->TryInsertMetadatable(i_rObject, i_rStreamName, i_rIdref))
+ {
+ rmIter(m_pImpl->m_XmlIdMap, old_id, old_path, i_rObject);
+ m_pImpl->m_XmlIdReverseMap[&i_rObject] =
+ RMapEntry(i_rStreamName, i_rIdref);
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+void
+XmlIdRegistryClipboard::RegisterMetadatableAndCreateID(Metadatable & i_rObject)
+{
+ SAL_INFO("sfx", "RegisterMetadatableAndCreateID: " << &i_rObject);
+
+ OSL_ENSURE(!dynamic_cast<MetadatableUndo*>(&i_rObject),
+ "RegisterMetadatableAndCreateID called for MetadatableUndo?");
+ OSL_ENSURE(!dynamic_cast<MetadatableClipboard*>(&i_rObject),
+ "RegisterMetadatableAndCreateID called for MetadatableClipboard?");
+
+ bool isInContent( i_rObject.IsInContent() );
+ OUString stream(
+ isInContent ? OUString(s_content) : OUString(s_styles) );
+
+ OUString old_path;
+ OUString old_idref;
+ LookupXmlId(i_rObject, old_path, old_idref);
+ if (!old_idref.isEmpty() &&
+ (m_pImpl->LookupElement(old_path, old_idref) == &i_rObject))
+ {
+ return;
+ }
+
+ // create id
+ const OUString id( create_id(m_pImpl->m_XmlIdMap) );
+ OSL_ENSURE(m_pImpl->m_XmlIdMap.find(id) == m_pImpl->m_XmlIdMap.end(),
+ "created id is in use");
+ m_pImpl->m_XmlIdMap.insert(::std::make_pair(id, isInContent
+ ? ::std::make_pair( &i_rObject, static_cast<Metadatable*>(nullptr) )
+ : ::std::make_pair( static_cast<Metadatable*>(nullptr), &i_rObject )));
+ // N.B.: if i_rObject had a latent XmlId, then we implicitly delete the
+ // MetadatableClipboard and thus the latent XmlId here
+ m_pImpl->m_XmlIdReverseMap[&i_rObject] = RMapEntry(stream, id);
+}
+
+void XmlIdRegistryClipboard::UnregisterMetadatable(const Metadatable& i_rObject)
+{
+ SAL_INFO("sfx", "UnregisterMetadatable: " << &i_rObject);
+
+ OUString path;
+ OUString idref;
+ const MetadatableClipboard * pLink;
+ if (!m_pImpl->LookupXmlId(i_rObject, path, idref, pLink))
+ {
+ OSL_FAIL("unregister: no xml id?");
+ return;
+ }
+ const ClipboardXmlIdMap_t::iterator iter( m_pImpl->m_XmlIdMap.find(idref) );
+ if (iter != m_pImpl->m_XmlIdMap.end())
+ {
+ rmIter(m_pImpl->m_XmlIdMap, iter, path, i_rObject);
+ }
+}
+
+
+void XmlIdRegistryClipboard::RemoveXmlIdForElement(const Metadatable& i_rObject)
+{
+ SAL_INFO("sfx", "RemoveXmlIdForElement: " << &i_rObject);
+
+ ClipboardXmlIdReverseMap_t::iterator iter(
+ m_pImpl->m_XmlIdReverseMap.find(&i_rObject) );
+ if (iter != m_pImpl->m_XmlIdReverseMap.end())
+ {
+ OSL_ENSURE(!iter->second.m_XmlId.isEmpty(),
+ "null id in m_XmlIdReverseMap");
+ m_pImpl->m_XmlIdReverseMap.erase(iter);
+ }
+}
+
+
+std::shared_ptr<MetadatableClipboard>
+XmlIdRegistryClipboard::CreateClipboard(const bool i_isInContent)
+{
+ SAL_INFO("sfx", "CreateClipboard:");
+
+ return std::make_shared<MetadatableClipboard>(
+ i_isInContent );
+}
+
+MetadatableClipboard &
+XmlIdRegistryClipboard::RegisterCopyClipboard(Metadatable & i_rCopy,
+ beans::StringPair const & i_rReference,
+ const bool i_isLatent)
+{
+ SAL_INFO("sfx", "RegisterCopyClipboard: " << &i_rCopy
+ << " -> (" << i_rReference.First << "#" << i_rReference.Second << ") (" << i_isLatent << ")");
+
+ // N.B.: when copying to the clipboard, the selection is always inserted
+ // into the body, even if the source is a header/footer!
+ // so we do not check whether the stream is right in this function
+
+ if (!isValidXmlId(i_rReference.First, i_rReference.Second))
+ {
+ throw lang::IllegalArgumentException("illegal XmlId", nullptr, 0);
+ }
+
+ if (!i_isLatent)
+ {
+ // this should succeed assuming clipboard has a single source document
+ const bool success( m_pImpl->TryInsertMetadatable(i_rCopy,
+ i_rReference.First, i_rReference.Second) );
+ OSL_ENSURE(success, "RegisterCopyClipboard: TryInsert failed?");
+ }
+ const std::shared_ptr<MetadatableClipboard> xLink(
+ CreateClipboard( isContentFile(i_rReference.First)) );
+ m_pImpl->m_XmlIdReverseMap.insert(::std::make_pair(&i_rCopy,
+ RMapEntry(i_rReference.First, i_rReference.Second, xLink)));
+ return *xLink;
+}
+
+MetadatableClipboard const*
+XmlIdRegistryClipboard::SourceLink(Metadatable const& i_rObject)
+{
+ OUString path;
+ OUString idref;
+ const MetadatableClipboard * pLink( nullptr );
+ m_pImpl->LookupXmlId(i_rObject, path, idref, pLink);
+ return pLink;
+}
+
+
+// Metadatable mixin
+
+
+Metadatable::~Metadatable()
+{
+ RemoveMetadataReference();
+}
+
+void Metadatable::RemoveMetadataReference()
+{
+ try
+ {
+ if (m_pReg)
+ {
+ m_pReg->UnregisterMetadatable( *this );
+ m_pReg->RemoveXmlIdForElement( *this );
+ m_pReg = nullptr;
+ }
+ }
+ catch (const uno::Exception &)
+ {
+ TOOLS_WARN_EXCEPTION( "sfx.doc", "Metadatable::RemoveMetadataReference");
+ }
+}
+
+// css::rdf::XMetadatable:
+beans::StringPair
+Metadatable::GetMetadataReference() const
+{
+ if (m_pReg)
+ {
+ return m_pReg->GetXmlIdForElement(*this);
+ }
+ return beans::StringPair();
+}
+
+void Metadatable::SetMetadataReference( const css::beans::StringPair & i_rReference)
+{
+ if (i_rReference.Second.isEmpty())
+ {
+ RemoveMetadataReference();
+ }
+ else
+ {
+ OUString streamName( i_rReference.First );
+ if (streamName.isEmpty())
+ {
+ // handle empty stream name as auto-detect.
+ // necessary for importing flat file format.
+ streamName = IsInContent() ? OUString(s_content) : OUString(s_styles);
+ }
+ XmlIdRegistry & rReg( dynamic_cast<XmlIdRegistry&>( GetRegistry() ) );
+ if (!rReg.TryRegisterMetadatable(*this, streamName, i_rReference.Second))
+ {
+ throw lang::IllegalArgumentException(
+ "Metadatable::SetMetadataReference: argument is invalid", /*this*/nullptr, 0);
+ }
+
+ m_pReg = &rReg;
+ }
+}
+
+void Metadatable::EnsureMetadataReference()
+{
+ XmlIdRegistry& rReg(
+ m_pReg ? *m_pReg : dynamic_cast<XmlIdRegistry&>( GetRegistry() ) );
+ rReg.RegisterMetadatableAndCreateID( *this );
+ m_pReg = &rReg;
+}
+
+static const ::sfx2::IXmlIdRegistry& GetRegistryConst(Metadatable const& i_rObject)
+{
+ return const_cast< Metadatable& >( i_rObject ).GetRegistry();
+}
+
+void
+Metadatable::RegisterAsCopyOf(Metadatable const & i_rSource,
+ const bool i_bCopyPrecedesSource)
+{
+ OSL_ENSURE(typeid(*this) == typeid(i_rSource)
+ || typeid(i_rSource) == typeid(MetadatableUndo)
+ || typeid(*this) == typeid(MetadatableUndo)
+ || typeid(i_rSource) == typeid(MetadatableClipboard)
+ || typeid(*this) == typeid(MetadatableClipboard),
+ "RegisterAsCopyOf element with different class?");
+ OSL_ENSURE(!m_pReg, "RegisterAsCopyOf called on element with XmlId?");
+
+ if (m_pReg)
+ {
+ RemoveMetadataReference();
+ }
+
+ try
+ {
+ if (i_rSource.m_pReg)
+ {
+ XmlIdRegistry & rReg(
+ dynamic_cast<XmlIdRegistry&>( GetRegistry() ) );
+ if (i_rSource.m_pReg == &rReg)
+ {
+ OSL_ENSURE(!IsInClipboard(),
+ "RegisterAsCopy: both in clipboard?");
+ if (!IsInClipboard())
+ {
+ XmlIdRegistryDocument & rRegDoc(
+ dynamic_cast<XmlIdRegistryDocument&>( rReg ) );
+ rRegDoc.RegisterCopy(i_rSource, *this,
+ i_bCopyPrecedesSource);
+ m_pReg = &rRegDoc;
+ }
+ return;
+ }
+ // source is in different document
+ XmlIdRegistryDocument * pRegDoc(
+ dynamic_cast<XmlIdRegistryDocument *>(&rReg) );
+ XmlIdRegistryClipboard * pRegClp(
+ dynamic_cast<XmlIdRegistryClipboard*>(&rReg) );
+
+ if (pRegClp)
+ {
+ beans::StringPair SourceRef(
+ i_rSource.m_pReg->GetXmlIdForElement(i_rSource) );
+ bool isLatent( SourceRef.Second.isEmpty() );
+ XmlIdRegistryDocument * pSourceRegDoc(
+ dynamic_cast<XmlIdRegistryDocument*>(i_rSource.m_pReg) );
+ OSL_ENSURE(pSourceRegDoc, "RegisterAsCopyOf: 2 clipboards?");
+ if (!pSourceRegDoc) return;
+ // this is a copy _to_ the clipboard
+ if (isLatent)
+ {
+ pSourceRegDoc->LookupXmlId(i_rSource,
+ SourceRef.First, SourceRef.Second);
+ }
+ Metadatable & rLink(
+ pRegClp->RegisterCopyClipboard(*this, SourceRef, isLatent));
+ m_pReg = pRegClp;
+ // register as copy in the non-clipboard registry
+ pSourceRegDoc->RegisterCopy(i_rSource, rLink,
+ false); // i_bCopyPrecedesSource);
+ rLink.m_pReg = pSourceRegDoc;
+ }
+ else if (pRegDoc)
+ {
+ XmlIdRegistryClipboard * pSourceRegClp(
+ dynamic_cast<XmlIdRegistryClipboard*>(i_rSource.m_pReg) );
+ OSL_ENSURE(pSourceRegClp,
+ "RegisterAsCopyOf: 2 non-clipboards?");
+ if (!pSourceRegClp) return;
+ const MetadatableClipboard * pLink(
+ pSourceRegClp->SourceLink(i_rSource) );
+ // may happen if src got its id via UNO call
+ if (!pLink) return;
+ // only register copy if clipboard content is from this SwDoc!
+ if (&GetRegistryConst(*pLink) == pRegDoc)
+ {
+ // this is a copy _from_ the clipboard; check if the
+ // element is still in the same stream
+ // N.B.: we check the stream of pLink, not of i_rSource!
+ bool srcInContent( pLink->IsInContent() );
+ bool tgtInContent( IsInContent() );
+ if (srcInContent == tgtInContent)
+ {
+ pRegDoc->RegisterCopy(*pLink, *this,
+ true); // i_bCopyPrecedesSource);
+ m_pReg = pRegDoc;
+ }
+ // otherwise: stream change! do not register!
+ }
+ }
+ else
+ {
+ OSL_FAIL("neither RegDoc nor RegClp cannot happen");
+ }
+ }
+ }
+ catch (const uno::Exception &)
+ {
+ TOOLS_WARN_EXCEPTION( "sfx.doc", "Metadatable::RegisterAsCopyOf");
+ }
+}
+
+std::shared_ptr<MetadatableUndo> Metadatable::CreateUndo() const
+{
+ OSL_ENSURE(!IsInUndo(), "CreateUndo called for object in undo?");
+ OSL_ENSURE(!IsInClipboard(), "CreateUndo called for object in clipboard?");
+ try
+ {
+ if (!IsInClipboard() && !IsInUndo() && m_pReg)
+ {
+ XmlIdRegistryDocument * pRegDoc(
+ dynamic_cast<XmlIdRegistryDocument*>( m_pReg ) );
+ assert(pRegDoc);
+ std::shared_ptr<MetadatableUndo> xUndo(
+ sfx2::XmlIdRegistryDocument::CreateUndo(*this) );
+ pRegDoc->RegisterCopy(*this, *xUndo, false);
+ xUndo->m_pReg = pRegDoc;
+ return xUndo;
+ }
+ }
+ catch (const uno::Exception &)
+ {
+ TOOLS_WARN_EXCEPTION( "sfx.doc", "Metadatable::CreateUndo");
+ }
+ return std::shared_ptr<MetadatableUndo>();
+}
+
+std::shared_ptr<MetadatableUndo> Metadatable::CreateUndoForDelete()
+{
+ std::shared_ptr<MetadatableUndo> const xUndo( CreateUndo() );
+ RemoveMetadataReference();
+ return xUndo;
+}
+
+void Metadatable::RestoreMetadata(
+ std::shared_ptr<MetadatableUndo> const& i_pUndo)
+{
+ OSL_ENSURE(!IsInUndo(), "RestoreMetadata called for object in undo?");
+ OSL_ENSURE(!IsInClipboard(),
+ "RestoreMetadata called for object in clipboard?");
+ if (IsInClipboard() || IsInUndo()) return;
+ RemoveMetadataReference();
+ if (i_pUndo)
+ {
+ RegisterAsCopyOf(*i_pUndo, true);
+ }
+}
+
+void
+Metadatable::JoinMetadatable(Metadatable const & i_rOther,
+ const bool i_isMergedEmpty, const bool i_isOtherEmpty)
+{
+ OSL_ENSURE(!IsInUndo(), "JoinMetadatables called for object in undo?");
+ OSL_ENSURE(!IsInClipboard(),
+ "JoinMetadatables called for object in clipboard?");
+ if (IsInClipboard() || IsInUndo()) return;
+
+ if (i_isOtherEmpty && !i_isMergedEmpty)
+ {
+ // other is empty, thus loses => nothing to do
+ return;
+ }
+ if (i_isMergedEmpty && !i_isOtherEmpty)
+ {
+ RemoveMetadataReference();
+ RegisterAsCopyOf(i_rOther, true);
+ return;
+ }
+
+ if (!i_rOther.m_pReg)
+ {
+ // other doesn't have xmlid, thus loses => nothing to do
+ return;
+ }
+ if (!m_pReg)
+ {
+ RegisterAsCopyOf(i_rOther, true);
+ // assumption: i_rOther will be deleted, so don't unregister it here
+ return;
+ }
+ try
+ {
+ XmlIdRegistryDocument * pRegDoc(
+ dynamic_cast<XmlIdRegistryDocument*>( m_pReg ) );
+ OSL_ENSURE(pRegDoc, "JoinMetadatable: no pRegDoc?");
+ if (pRegDoc)
+ {
+ pRegDoc->JoinMetadatables(*this, i_rOther);
+ }
+ }
+ catch (const uno::Exception &)
+ {
+ TOOLS_WARN_EXCEPTION( "sfx.doc", "Metadatable::JoinMetadatable");
+ }
+}
+
+
+// XMetadatable mixin
+
+// css::rdf::XNode:
+OUString SAL_CALL MetadatableMixin::getStringValue()
+{
+ return getNamespace() + getLocalName();
+}
+
+// css::rdf::XURI:
+OUString SAL_CALL MetadatableMixin::getLocalName()
+{
+ SolarMutexGuard aGuard;
+ beans::StringPair mdref( getMetadataReference() );
+ if (mdref.Second.isEmpty())
+ {
+ ensureMetadataReference(); // N.B.: side effect!
+ mdref = getMetadataReference();
+ }
+ return mdref.First + "#" + mdref.Second;
+}
+
+OUString SAL_CALL MetadatableMixin::getNamespace()
+{
+ SolarMutexGuard aGuard;
+ const uno::Reference< frame::XModel > xModel( GetModel() );
+ const uno::Reference< rdf::XURI > xDMA( xModel, uno::UNO_QUERY_THROW );
+ return xDMA->getStringValue();
+}
+
+// css::rdf::XMetadatable:
+beans::StringPair SAL_CALL
+MetadatableMixin::getMetadataReference()
+{
+ SolarMutexGuard aGuard;
+
+ Metadatable *const pObject( GetCoreObject() );
+ if (!pObject)
+ {
+ throw uno::RuntimeException(
+ "MetadatableMixin: cannot get core object; not inserted?",
+ *this);
+ }
+ return pObject->GetMetadataReference();
+}
+
+void SAL_CALL
+MetadatableMixin::setMetadataReference(
+ const beans::StringPair & i_rReference)
+{
+ SolarMutexGuard aGuard;
+
+ Metadatable *const pObject( GetCoreObject() );
+ if (!pObject)
+ {
+ throw uno::RuntimeException(
+ "MetadatableMixin: cannot get core object; not inserted?",
+ *this);
+ }
+ return pObject->SetMetadataReference(i_rReference);
+}
+
+void SAL_CALL MetadatableMixin::ensureMetadataReference()
+{
+ SolarMutexGuard aGuard;
+
+ Metadatable *const pObject( GetCoreObject() );
+ if (!pObject)
+ {
+ throw uno::RuntimeException(
+ "MetadatableMixin: cannot get core object; not inserted?",
+ *this);
+ }
+ return pObject->EnsureMetadataReference();
+}
+
+} // namespace sfx2
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/doc/QuerySaveDocument.cxx b/sfx2/source/doc/QuerySaveDocument.cxx
new file mode 100644
index 000000000..4abc612dc
--- /dev/null
+++ b/sfx2/source/doc/QuerySaveDocument.cxx
@@ -0,0 +1,39 @@
+/* -*- 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/QuerySaveDocument.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+
+short ExecuteQuerySaveDocument(weld::Widget* _pParent, std::u16string_view _rTitle)
+{
+ if (Application::IsHeadlessModeEnabled() || getenv("SAL_NO_QUERYSAVE"))
+ {
+ // don't block Desktop::terminate() if there's no user to ask
+ return RET_NO;
+ }
+
+ std::unique_ptr<weld::Builder> xBuilder(
+ Application::CreateBuilder(_pParent, "sfx/ui/querysavedialog.ui"));
+ std::unique_ptr<weld::MessageDialog> xQBox(xBuilder->weld_message_dialog("QuerySaveDialog"));
+ xQBox->set_primary_text(xQBox->get_primary_text().replaceFirst("$(DOC)", _rTitle));
+ return xQBox->run();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/doc/SfxDocumentMetaData.cxx b/sfx2/source/doc/SfxDocumentMetaData.cxx
new file mode 100644
index 000000000..6392bd4aa
--- /dev/null
+++ b/sfx2/source/doc/SfxDocumentMetaData.cxx
@@ -0,0 +1,2214 @@
+/* -*- 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 <sal/log.hxx>
+
+#include <cppuhelper/compbase.hxx>
+#include <cppuhelper/exc_hlp.hxx>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/document/XDocumentProperties.hpp>
+#include <com/sun/star/lang/XInitialization.hpp>
+#include <com/sun/star/util/XCloneable.hpp>
+#include <com/sun/star/util/XModifiable.hpp>
+#include <com/sun/star/xml/sax/SAXException.hpp>
+#include <com/sun/star/xml/sax/XSAXSerializable.hpp>
+
+#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
+#include <com/sun/star/lang/EventObject.hpp>
+#include <com/sun/star/beans/IllegalTypeException.hpp>
+#include <com/sun/star/beans/PropertyExistException.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/beans/XPropertySetInfo.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/task/ErrorCodeIOException.hpp>
+#include <com/sun/star/embed/XStorage.hpp>
+#include <com/sun/star/embed/XTransactedObject.hpp>
+#include <com/sun/star/embed/ElementModes.hpp>
+#include <com/sun/star/io/WrongFormatException.hpp>
+#include <com/sun/star/io/XStream.hpp>
+#include <com/sun/star/document/XImporter.hpp>
+#include <com/sun/star/document/XExporter.hpp>
+#include <com/sun/star/document/XFilter.hpp>
+#include <com/sun/star/xml/sax/Writer.hpp>
+#include <com/sun/star/xml/sax/Parser.hpp>
+#include <com/sun/star/xml/sax/XFastParser.hpp>
+#include <com/sun/star/xml/dom/DOMException.hpp>
+#include <com/sun/star/xml/dom/XDocument.hpp>
+#include <com/sun/star/xml/dom/XElement.hpp>
+#include <com/sun/star/xml/dom/DocumentBuilder.hpp>
+#include <com/sun/star/xml/dom/NodeType.hpp>
+#include <com/sun/star/xml/xpath/XPathAPI.hpp>
+#include <com/sun/star/util/Date.hpp>
+#include <com/sun/star/util/Time.hpp>
+#include <com/sun/star/util/DateWithTimezone.hpp>
+#include <com/sun/star/util/DateTimeWithTimezone.hpp>
+#include <com/sun/star/util/Duration.hpp>
+
+#include <rtl/ustrbuf.hxx>
+#include <tools/datetime.hxx>
+#include <tools/diagnose_ex.h>
+#include <osl/mutex.hxx>
+#include <comphelper/fileformat.h>
+#include <cppuhelper/basemutex.hxx>
+#include <comphelper/interfacecontainer3.hxx>
+#include <comphelper/storagehelper.hxx>
+#include <unotools/mediadescriptor.hxx>
+#include <comphelper/sequence.hxx>
+#include <sot/storage.hxx>
+#include <sfx2/docfile.hxx>
+#include <sax/tools/converter.hxx>
+#include <i18nlangtag/languagetag.hxx>
+#include <optional>
+
+#include <algorithm>
+#include <utility>
+#include <vector>
+#include <map>
+#include <cstring>
+#include <limits>
+
+
+#include <cppuhelper/implbase.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <com/sun/star/document/XCompatWriterDocProperties.hpp>
+#include <com/sun/star/beans/PropertyBag.hpp>
+
+/**
+ * This file contains the implementation of the service
+ * com.sun.star.document.DocumentProperties.
+ * This service enables access to the meta-data stored in documents.
+ * Currently, this service only handles documents in ODF format.
+ *
+ * The implementation uses an XML DOM to store the properties.
+ * This approach was taken because it allows for preserving arbitrary XML data
+ * in loaded documents, which will be stored unmodified when saving the
+ * document again.
+ *
+ * Upon access, some properties are directly read from and updated in the DOM.
+ * Exception: it seems impossible to get notified upon addition of a property
+ * to a com.sun.star.beans.PropertyBag, which is used for storing user-defined
+ * properties; because of this, user-defined properties are updated in the
+ * XML DOM only when storing the document.
+ * Exception 2: when setting certain properties which correspond to attributes
+ * in the XML DOM, we want to remove the corresponding XML element. Detecting
+ * this condition can get messy, so we store all such properties as members,
+ * and update the DOM tree only when storing the document (in
+ * <method>updateUserDefinedAndAttributes</method>).
+ *
+ */
+
+/// anonymous implementation namespace
+namespace {
+
+/// a list of attribute-lists, where attribute means name and content
+typedef std::vector<std::vector<std::pair<OUString, OUString> > >
+ AttrVector;
+
+typedef ::cppu::WeakComponentImplHelper<
+ css::lang::XServiceInfo,
+ css::document::XDocumentProperties,
+ css::lang::XInitialization,
+ css::util::XCloneable,
+ css::util::XModifiable,
+ css::xml::sax::XSAXSerializable>
+ SfxDocumentMetaData_Base;
+
+class SfxDocumentMetaData:
+ private ::cppu::BaseMutex,
+ public SfxDocumentMetaData_Base
+{
+public:
+ explicit SfxDocumentMetaData(
+ css::uno::Reference< css::uno::XComponentContext > const & context);
+ SfxDocumentMetaData(const SfxDocumentMetaData&) = delete;
+ SfxDocumentMetaData& operator=(const SfxDocumentMetaData&) = delete;
+
+ // css::lang::XServiceInfo:
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService(
+ const OUString & ServiceName) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL
+ getSupportedServiceNames() override;
+
+ // css::lang::XComponent:
+ virtual void SAL_CALL dispose() override;
+
+ // css::document::XDocumentProperties:
+ virtual OUString SAL_CALL getAuthor() override;
+ virtual void SAL_CALL setAuthor(const OUString & the_value) override;
+ virtual OUString SAL_CALL getGenerator() override;
+ virtual void SAL_CALL setGenerator(const OUString & the_value) override;
+ virtual css::util::DateTime SAL_CALL getCreationDate() override;
+ virtual void SAL_CALL setCreationDate(const css::util::DateTime & the_value) override;
+ virtual OUString SAL_CALL getTitle() override;
+ virtual void SAL_CALL setTitle(const OUString & the_value) override;
+ virtual OUString SAL_CALL getSubject() override;
+ virtual void SAL_CALL setSubject(const OUString & the_value) override;
+ virtual OUString SAL_CALL getDescription() override;
+ virtual void SAL_CALL setDescription(const OUString & the_value) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getKeywords() override;
+ virtual void SAL_CALL setKeywords(
+ const css::uno::Sequence< OUString > & the_value) override;
+ virtual css::lang::Locale SAL_CALL getLanguage() override;
+ virtual void SAL_CALL setLanguage(const css::lang::Locale & the_value) override;
+ virtual OUString SAL_CALL getModifiedBy() override;
+ virtual void SAL_CALL setModifiedBy(const OUString & the_value) override;
+ virtual css::util::DateTime SAL_CALL getModificationDate() override;
+ virtual void SAL_CALL setModificationDate(
+ const css::util::DateTime & the_value) override;
+ virtual OUString SAL_CALL getPrintedBy() override;
+ virtual void SAL_CALL setPrintedBy(const OUString & the_value) override;
+ virtual css::util::DateTime SAL_CALL getPrintDate() override;
+ virtual void SAL_CALL setPrintDate(const css::util::DateTime & the_value) override;
+ virtual OUString SAL_CALL getTemplateName() override;
+ virtual void SAL_CALL setTemplateName(const OUString & the_value) override;
+ virtual OUString SAL_CALL getTemplateURL() override;
+ virtual void SAL_CALL setTemplateURL(const OUString & the_value) override;
+ virtual css::util::DateTime SAL_CALL getTemplateDate() override;
+ virtual void SAL_CALL setTemplateDate(const css::util::DateTime & the_value) override;
+ virtual OUString SAL_CALL getAutoloadURL() override;
+ virtual void SAL_CALL setAutoloadURL(const OUString & the_value) override;
+ virtual ::sal_Int32 SAL_CALL getAutoloadSecs() override;
+ virtual void SAL_CALL setAutoloadSecs(::sal_Int32 the_value) override;
+ virtual OUString SAL_CALL getDefaultTarget() override;
+ virtual void SAL_CALL setDefaultTarget(const OUString & the_value) override;
+ virtual css::uno::Sequence< css::beans::NamedValue > SAL_CALL
+ getDocumentStatistics() override;
+ virtual void SAL_CALL setDocumentStatistics(
+ const css::uno::Sequence< css::beans::NamedValue > & the_value) override;
+ virtual ::sal_Int16 SAL_CALL getEditingCycles() override;
+ virtual void SAL_CALL setEditingCycles(::sal_Int16 the_value) override;
+ virtual ::sal_Int32 SAL_CALL getEditingDuration() override;
+ virtual void SAL_CALL setEditingDuration(::sal_Int32 the_value) override;
+ virtual void SAL_CALL resetUserData(const OUString & the_value) override;
+ virtual css::uno::Reference< css::beans::XPropertyContainer > SAL_CALL
+ getUserDefinedProperties() override;
+ virtual void SAL_CALL loadFromStorage(
+ const css::uno::Reference< css::embed::XStorage > & Storage,
+ const css::uno::Sequence< css::beans::PropertyValue > & Medium) override;
+ virtual void SAL_CALL loadFromMedium(const OUString & URL,
+ const css::uno::Sequence< css::beans::PropertyValue > & Medium) override;
+ virtual void SAL_CALL storeToStorage(
+ const css::uno::Reference< css::embed::XStorage > & Storage,
+ const css::uno::Sequence< css::beans::PropertyValue > & Medium) override;
+ virtual void SAL_CALL storeToMedium(const OUString & URL,
+ const css::uno::Sequence< css::beans::PropertyValue > & Medium) override;
+
+ // css::lang::XInitialization:
+ virtual void SAL_CALL initialize(
+ const css::uno::Sequence< css::uno::Any > & aArguments) override;
+
+ // css::util::XCloneable:
+ virtual css::uno::Reference<css::util::XCloneable> SAL_CALL createClone() override;
+
+ // css::util::XModifiable:
+ virtual sal_Bool SAL_CALL isModified( ) override;
+ virtual void SAL_CALL setModified( sal_Bool bModified ) override;
+
+ // css::util::XModifyBroadcaster:
+ virtual void SAL_CALL addModifyListener(
+ const css::uno::Reference< css::util::XModifyListener > & xListener) override;
+ virtual void SAL_CALL removeModifyListener(
+ const css::uno::Reference< css::util::XModifyListener > & xListener) override;
+
+ // css::xml::sax::XSAXSerializable
+ virtual void SAL_CALL serialize(
+ const css::uno::Reference<css::xml::sax::XDocumentHandler>& i_xHandler,
+ const css::uno::Sequence< css::beans::StringPair >& i_rNamespaces) override;
+
+protected:
+ virtual ~SfxDocumentMetaData() override {}
+ virtual rtl::Reference<SfxDocumentMetaData> createMe( css::uno::Reference< css::uno::XComponentContext > const & context ) { return new SfxDocumentMetaData( context ); };
+ const css::uno::Reference< css::uno::XComponentContext > m_xContext;
+
+ /// for notification
+ ::comphelper::OInterfaceContainerHelper3<css::util::XModifyListener> m_NotifyListeners;
+ /// flag: false means not initialized yet, or disposed
+ bool m_isInitialized;
+ /// flag
+ bool m_isModified;
+ /// meta-data DOM tree
+ css::uno::Reference< css::xml::dom::XDocument > m_xDoc;
+ /// meta-data super node in the meta-data DOM tree
+ css::uno::Reference< css::xml::dom::XNode> m_xParent;
+ /// standard meta data (single occurrence)
+ std::map< OUString, css::uno::Reference<css::xml::dom::XNode> >
+ m_meta;
+ /// standard meta data (multiple occurrences)
+ std::map< OUString,
+ std::vector<css::uno::Reference<css::xml::dom::XNode> > > m_metaList;
+ /// user-defined meta data (meta:user-defined) @ATTENTION may be null!
+ css::uno::Reference<css::beans::XPropertyContainer> m_xUserDefined;
+ // now for some meta-data attributes; these are not updated directly in the
+ // DOM because updates (detecting "empty" elements) would be quite messy
+ OUString m_TemplateName;
+ OUString m_TemplateURL;
+ css::util::DateTime m_TemplateDate;
+ OUString m_AutoloadURL;
+ sal_Int32 m_AutoloadSecs;
+ OUString m_DefaultTarget;
+
+ /// check if we are initialized properly
+ void checkInit() const;
+ /// initialize state from given DOM tree
+ void init(const css::uno::Reference<css::xml::dom::XDocument>& i_xDom);
+ /// update element in DOM tree
+ void updateElement(const OUString & i_name,
+ std::vector<std::pair<OUString, OUString> >* i_pAttrs = nullptr);
+ /// update user-defined meta data and attributes in DOM tree
+ void updateUserDefinedAndAttributes();
+ /// create empty DOM tree (XDocument)
+ css::uno::Reference<css::xml::dom::XDocument> createDOM() const;
+ /// extract base URL (necessary for converting relative links)
+ css::uno::Reference<css::beans::XPropertySet> getURLProperties(
+ const css::uno::Sequence<css::beans::PropertyValue> & i_rMedium) const;
+ /// get text of standard meta data element
+ OUString getMetaText(const char* i_name) const;
+ /// set text of standard meta data element iff not equal to existing text
+ bool setMetaText(const OUString& i_name,
+ const OUString & i_rValue);
+ /// set text of standard meta data element iff not equal to existing text
+ void setMetaTextAndNotify(const OUString& i_name,
+ const OUString & i_rValue);
+ /// get text of standard meta data element's attribute
+ OUString getMetaAttr(const OUString& i_name,
+ const OUString& i_attr) const;
+ /// get text of a list of standard meta data elements (multiple occ.)
+ css::uno::Sequence< OUString > getMetaList(
+ const char* i_name) const;
+ /// set text of a list of standard meta data elements (multiple occ.)
+ bool setMetaList(const OUString& i_name,
+ const css::uno::Sequence< OUString > & i_rValue,
+ AttrVector const*);
+ void createUserDefined();
+};
+
+typedef ::cppu::ImplInheritanceHelper< SfxDocumentMetaData, css::document::XCompatWriterDocProperties > CompatWriterDocPropsImpl_BASE;
+
+class CompatWriterDocPropsImpl : public CompatWriterDocPropsImpl_BASE
+{
+ OUString msManager;
+ OUString msCategory;
+ OUString msCompany;
+protected:
+ virtual rtl::Reference<SfxDocumentMetaData> createMe( css::uno::Reference< css::uno::XComponentContext > const & context ) override { return new CompatWriterDocPropsImpl( context ); };
+public:
+ explicit CompatWriterDocPropsImpl( css::uno::Reference< css::uno::XComponentContext > const & context) : CompatWriterDocPropsImpl_BASE( context ) {}
+
+// XCompatWriterDocPropsImpl
+ virtual OUString SAL_CALL getManager() override { return msManager; }
+ virtual void SAL_CALL setManager( const OUString& _manager ) override { msManager = _manager; }
+ virtual OUString SAL_CALL getCategory() override { return msCategory; }
+ virtual void SAL_CALL setCategory( const OUString& _category ) override { msCategory = _category; }
+ virtual OUString SAL_CALL getCompany() override { return msCompany; }
+ virtual void SAL_CALL setCompany( const OUString& _company ) override { msCompany = _company; }
+
+// XServiceInfo
+ virtual OUString SAL_CALL getImplementationName( ) override
+ {
+ return "CompatWriterDocPropsImpl";
+ }
+
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override
+ {
+ return cppu::supportsService(this, ServiceName);
+ }
+
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override
+ {
+ css::uno::Sequence<OUString> aServiceNames { "com.sun.star.writer.DocumentProperties" };
+ return aServiceNames;
+ }
+};
+
+constexpr OUStringLiteral sMetaPageCount = u"meta:page-count";
+constexpr OUStringLiteral sMetaTableCount = u"meta:table-count";
+constexpr OUStringLiteral sMetaDrawCount = u"meta:draw-count";
+constexpr OUStringLiteral sMetaImageCount = u"meta:image-count";
+constexpr OUStringLiteral sMetaObjectCount = u"meta:object-count";
+constexpr OUStringLiteral sMetaOleObjectCount = u"meta:ole-object-count";
+constexpr OUStringLiteral sMetaParagraphCount = u"meta:paragraph-count";
+constexpr OUStringLiteral sMetaWordCount = u"meta:word-count";
+constexpr OUStringLiteral sMetaCharacterCount = u"meta:character-count";
+constexpr OUStringLiteral sMetaRowCount = u"meta:row-count";
+constexpr OUStringLiteral sMetaFrameCount = u"meta:frame-count";
+constexpr OUStringLiteral sMetaSentenceCount = u"meta:sentence-count";
+constexpr OUStringLiteral sMetaSyllableCount = u"meta:syllable-count";
+constexpr OUStringLiteral sMetaNonWhitespaceCharacterCount = u"meta:non-whitespace-character-count";
+constexpr OUStringLiteral sMetaCellCount = u"meta:cell-count";
+
+// NB: keep these two arrays in sync!
+constexpr rtl::OUStringConstExpr s_stdStatAttrs[] = {
+ sMetaPageCount,
+ sMetaTableCount,
+ sMetaDrawCount,
+ sMetaImageCount,
+ sMetaObjectCount,
+ sMetaOleObjectCount,
+ sMetaParagraphCount,
+ sMetaWordCount,
+ sMetaCharacterCount,
+ sMetaRowCount,
+ sMetaFrameCount,
+ sMetaSentenceCount,
+ sMetaSyllableCount,
+ sMetaNonWhitespaceCharacterCount,
+ sMetaCellCount
+};
+
+// NB: keep these two arrays in sync!
+const char* s_stdStats[] = {
+ "PageCount",
+ "TableCount",
+ "DrawCount",
+ "ImageCount",
+ "ObjectCount",
+ "OLEObjectCount",
+ "ParagraphCount",
+ "WordCount",
+ "CharacterCount",
+ "RowCount",
+ "FrameCount",
+ "SentenceCount",
+ "SyllableCount",
+ "NonWhitespaceCharacterCount",
+ "CellCount",
+ nullptr
+};
+
+const char* s_stdMeta[] = {
+ "meta:generator", // string
+ "dc:title", // string
+ "dc:description", // string
+ "dc:subject", // string
+ "meta:initial-creator", // string
+ "dc:creator", // string
+ "meta:printed-by", // string
+ "meta:creation-date", // dateTime
+ "dc:date", // dateTime
+ "meta:print-date", // dateTime
+ "meta:template", // XLink
+ "meta:auto-reload",
+ "meta:hyperlink-behaviour",
+ "dc:language", // language
+ "meta:editing-cycles", // nonNegativeInteger
+ "meta:editing-duration", // duration
+ "meta:document-statistic", // ... // note: statistic is singular, no s!
+ nullptr
+};
+
+constexpr OUStringLiteral sMetaKeyword = u"meta:keyword";
+constexpr OUStringLiteral sMetaUserDefined = u"meta:user-defined";
+constexpr rtl::OUStringConstExpr s_stdMetaList[] {
+ sMetaKeyword, // string*
+ sMetaUserDefined, // ...*
+};
+
+constexpr OUStringLiteral s_nsXLink = u"http://www.w3.org/1999/xlink";
+constexpr OUStringLiteral s_nsDC = u"http://purl.org/dc/elements/1.1/";
+constexpr OUStringLiteral s_nsODF = u"urn:oasis:names:tc:opendocument:xmlns:office:1.0";
+constexpr OUStringLiteral s_nsODFMeta = u"urn:oasis:names:tc:opendocument:xmlns:meta:1.0";
+// constexpr OUStringLiteral s_nsOOo = "http://openoffice.org/2004/office"; // not used (yet?)
+
+constexpr OUStringLiteral s_meta = u"meta.xml";
+
+bool isValidDate(const css::util::Date & i_rDate)
+{
+ return i_rDate.Month > 0;
+}
+
+bool isValidDateTime(const css::util::DateTime & i_rDateTime)
+{
+ return i_rDateTime.Month > 0;
+}
+
+std::pair< OUString, OUString >
+getQualifier(const OUString& nm) {
+ sal_Int32 ix = nm.indexOf(u':');
+ if (ix == -1) {
+ return std::make_pair(OUString(), nm);
+ } else {
+ return std::make_pair(nm.copy(0,ix), nm.copy(ix+1));
+ }
+}
+
+// get namespace for standard qualified names
+// NB: only call this with statically known strings!
+OUString getNameSpace(const OUString& i_qname) noexcept
+{
+ OUString ns;
+ OUString n = getQualifier(i_qname).first;
+ if ( n == "xlink" ) ns = s_nsXLink;
+ if ( n == "dc" ) ns = s_nsDC;
+ if ( n == "office" ) ns = s_nsODF;
+ if ( n == "meta" ) ns = s_nsODFMeta;
+ assert(!ns.isEmpty());
+ return ns;
+}
+
+bool
+textToDateOrDateTime(css::util::Date & io_rd, css::util::DateTime & io_rdt,
+ bool & o_rIsDateTime, std::optional<sal_Int16> & o_rTimeZone,
+ const OUString& i_text) noexcept
+{
+ if (::sax::Converter::parseDateOrDateTime(
+ &io_rd, io_rdt, o_rIsDateTime, &o_rTimeZone, i_text)) {
+ return true;
+ } else {
+ SAL_WARN_IF(!i_text.isEmpty(), "sfx.doc", "Invalid date: " << i_text);
+ return false;
+ }
+}
+
+// convert string to date/time
+bool
+textToDateTime(css::util::DateTime & io_rdt, const OUString& i_text) noexcept
+{
+ if (::sax::Converter::parseDateTime(io_rdt, i_text)) {
+ return true;
+ } else {
+ SAL_WARN_IF(!i_text.isEmpty(), "sfx.doc", "Invalid date: " << i_text);
+ return false;
+ }
+}
+
+// convert string to date/time with default return value
+css::util::DateTime
+textToDateTimeDefault(const OUString& i_text) noexcept
+{
+ css::util::DateTime dt;
+ static_cast<void> (textToDateTime(dt, i_text));
+ // on conversion error: return default value (unchanged)
+ return dt;
+}
+
+// convert date to string
+OUString
+dateToText(css::util::Date const& i_rd,
+ sal_Int16 const*const pTimeZone) noexcept
+{
+ if (isValidDate(i_rd)) {
+ OUStringBuffer buf;
+ ::sax::Converter::convertDate(buf, i_rd, pTimeZone);
+ return buf.makeStringAndClear();
+ } else {
+ return OUString();
+ }
+}
+
+
+// convert date/time to string
+OUString
+dateTimeToText(css::util::DateTime const& i_rdt,
+ sal_Int16 const*const pTimeZone = nullptr) noexcept
+{
+ if (isValidDateTime(i_rdt)) {
+ OUStringBuffer buf(32);
+ ::sax::Converter::convertDateTime(buf, i_rdt, pTimeZone, true);
+ return buf.makeStringAndClear();
+ } else {
+ return OUString();
+ }
+}
+
+// convert string to duration
+bool
+textToDuration(css::util::Duration& io_rDur, OUString const& i_rText)
+noexcept
+{
+ if (::sax::Converter::convertDuration(io_rDur, i_rText)) {
+ return true;
+ } else {
+ SAL_WARN_IF(!i_rText.isEmpty(), "sfx.doc", "Invalid duration: " << i_rText);
+ return false;
+ }
+}
+
+sal_Int32 textToDuration(OUString const& i_rText) noexcept
+{
+ css::util::Duration d;
+ if (textToDuration(d, i_rText)) {
+ // #i107372#: approximate years/months
+ const sal_Int32 days( (d.Years * 365) + (d.Months * 30) + d.Days );
+ return (days * (24*3600))
+ + (d.Hours * 3600) + (d.Minutes * 60) + d.Seconds;
+ } else {
+ return 0; // default
+ }
+}
+
+// convert duration to string
+OUString durationToText(css::util::Duration const& i_rDur) noexcept
+{
+ OUStringBuffer buf;
+ ::sax::Converter::convertDuration(buf, i_rDur);
+ return buf.makeStringAndClear();
+}
+
+// convert duration to string
+OUString durationToText(sal_Int32 i_value) noexcept
+{
+ css::util::Duration ud;
+ ud.Days = static_cast<sal_Int16>(i_value / (24 * 3600));
+ ud.Hours = static_cast<sal_Int16>((i_value % (24 * 3600)) / 3600);
+ ud.Minutes = static_cast<sal_Int16>((i_value % 3600) / 60);
+ ud.Seconds = static_cast<sal_Int16>(i_value % 60);
+ ud.NanoSeconds = 0;
+ return durationToText(ud);
+}
+
+// extract base URL (necessary for converting relative links)
+css::uno::Reference< css::beans::XPropertySet >
+SfxDocumentMetaData::getURLProperties(
+ const css::uno::Sequence< css::beans::PropertyValue > & i_rMedium) const
+{
+ css::uno::Reference< css::beans::XPropertyBag> xPropArg = css::beans::PropertyBag::createDefault( m_xContext );
+ try {
+ css::uno::Any baseUri;
+ for (const auto& rProp : i_rMedium) {
+ if (rProp.Name == "DocumentBaseURL") {
+ baseUri = rProp.Value;
+ } else if (rProp.Name == "URL") {
+ if (!baseUri.hasValue()) {
+ baseUri = rProp.Value;
+ }
+ } else if (rProp.Name == "HierarchicalDocumentName") {
+ xPropArg->addProperty(
+ "StreamRelPath",
+ css::beans::PropertyAttribute::MAYBEVOID,
+ rProp.Value);
+ }
+ }
+ if (baseUri.hasValue()) {
+ xPropArg->addProperty(
+ "BaseURI", css::beans::PropertyAttribute::MAYBEVOID,
+ baseUri);
+ }
+ xPropArg->addProperty("StreamName",
+ css::beans::PropertyAttribute::MAYBEVOID,
+ css::uno::Any(OUString(s_meta)));
+ } catch (const css::uno::Exception &) {
+ // ignore
+ }
+ return css::uno::Reference< css::beans::XPropertySet>(xPropArg,
+ css::uno::UNO_QUERY_THROW);
+}
+
+// return the text of the (hopefully unique, i.e., normalize first!) text
+// node _below_ the given node
+/// @throws css::uno::RuntimeException
+OUString
+getNodeText(const css::uno::Reference<css::xml::dom::XNode>& i_xNode)
+{
+ if (!i_xNode.is())
+ throw css::uno::RuntimeException("SfxDocumentMetaData::getNodeText: argument is null", i_xNode);
+ for (css::uno::Reference<css::xml::dom::XNode> c = i_xNode->getFirstChild();
+ c.is();
+ c = c->getNextSibling()) {
+ if (c->getNodeType() == css::xml::dom::NodeType_TEXT_NODE) {
+ try {
+ return c->getNodeValue();
+ } catch (const css::xml::dom::DOMException &) { // too big?
+ return OUString();
+ }
+ }
+ }
+ return OUString();
+}
+
+OUString
+SfxDocumentMetaData::getMetaText(const char* i_name) const
+// throw (css::uno::RuntimeException)
+{
+ checkInit();
+
+ const OUString name( OUString::createFromAscii(i_name) );
+ assert(m_meta.find(name) != m_meta.end());
+ css::uno::Reference<css::xml::dom::XNode> xNode = m_meta.find(name)->second;
+ return (xNode.is()) ? getNodeText(xNode) : OUString();
+}
+
+bool
+SfxDocumentMetaData::setMetaText(const OUString& name,
+ const OUString & i_rValue)
+ // throw (css::uno::RuntimeException)
+{
+ checkInit();
+
+ assert(m_meta.find(name) != m_meta.end());
+ css::uno::Reference<css::xml::dom::XNode> xNode = m_meta.find(name)->second;
+
+ try {
+ if (i_rValue.isEmpty()) {
+ if (xNode.is()) { // delete
+ m_xParent->removeChild(xNode);
+ xNode.clear();
+ m_meta[name] = xNode;
+ return true;
+ } else {
+ return false;
+ }
+ } else {
+ if (xNode.is()) { // update
+ for (css::uno::Reference<css::xml::dom::XNode> c =
+ xNode->getFirstChild();
+ c.is();
+ c = c->getNextSibling()) {
+ if (c->getNodeType() == css::xml::dom::NodeType_TEXT_NODE) {
+ if (c->getNodeValue() != i_rValue) {
+ c->setNodeValue(i_rValue);
+ return true;
+ } else {
+ return false;
+ }
+ }
+ }
+ } else { // insert
+ xNode.set(m_xDoc->createElementNS(getNameSpace(name), name),
+ css::uno::UNO_QUERY_THROW);
+ m_xParent->appendChild(xNode);
+ m_meta[name] = xNode;
+ }
+ css::uno::Reference<css::xml::dom::XNode> xTextNode(
+ m_xDoc->createTextNode(i_rValue), css::uno::UNO_QUERY_THROW);
+ xNode->appendChild(xTextNode);
+ return true;
+ }
+ } catch (const css::xml::dom::DOMException &) {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw css::lang::WrappedTargetRuntimeException(
+ "SfxDocumentMetaData::setMetaText: DOM exception",
+ css::uno::Reference<css::uno::XInterface>(*this), anyEx);
+ }
+}
+
+void
+SfxDocumentMetaData::setMetaTextAndNotify(const OUString & i_name,
+ const OUString & i_rValue)
+ // throw (css::uno::RuntimeException)
+{
+ ::osl::ClearableMutexGuard g(m_aMutex);
+ if (setMetaText(i_name, i_rValue)) {
+ g.clear();
+ setModified(true);
+ }
+}
+
+OUString
+SfxDocumentMetaData::getMetaAttr(const OUString& name, const OUString& i_attr) const
+// throw (css::uno::RuntimeException)
+{
+ assert(m_meta.find(name) != m_meta.end());
+ css::uno::Reference<css::xml::dom::XNode> xNode = m_meta.find(name)->second;
+ if (xNode.is()) {
+ css::uno::Reference<css::xml::dom::XElement> xElem(xNode,
+ css::uno::UNO_QUERY_THROW);
+ return xElem->getAttributeNS(getNameSpace(i_attr),
+ getQualifier(i_attr).second);
+ } else {
+ return OUString();
+ }
+}
+
+css::uno::Sequence< OUString>
+SfxDocumentMetaData::getMetaList(const char* i_name) const
+// throw (css::uno::RuntimeException)
+{
+ checkInit();
+ OUString name = OUString::createFromAscii(i_name);
+ assert(m_metaList.find(name) != m_metaList.end());
+ std::vector<css::uno::Reference<css::xml::dom::XNode> > const & vec =
+ m_metaList.find(name)->second;
+ css::uno::Sequence< OUString> ret(vec.size());
+ std::transform(vec.begin(), vec.end(), ret.getArray(),
+ [](const auto& node) { return getNodeText(node); });
+ return ret;
+}
+
+bool
+SfxDocumentMetaData::setMetaList(const OUString& name,
+ const css::uno::Sequence<OUString> & i_rValue,
+ AttrVector const* i_pAttrs)
+ // throw (css::uno::RuntimeException)
+{
+ checkInit();
+ assert((i_pAttrs == nullptr) ||
+ (static_cast<size_t>(i_rValue.getLength()) == i_pAttrs->size()));
+
+ try {
+ assert(m_metaList.find(name) != m_metaList.end());
+ std::vector<css::uno::Reference<css::xml::dom::XNode> > & vec =
+ m_metaList[name];
+
+ // if nothing changed, do nothing
+ // alas, this does not check for permutations, or attributes...
+ if (nullptr == i_pAttrs) {
+ if (static_cast<size_t>(i_rValue.getLength()) == vec.size()) {
+ bool isEqual(true);
+ for (sal_Int32 i = 0; i < i_rValue.getLength(); ++i) {
+ css::uno::Reference<css::xml::dom::XNode> xNode(vec.at(i));
+ if (xNode.is()) {
+ OUString val = getNodeText(xNode);
+ if (val != i_rValue[i]) {
+ isEqual = false;
+ break;
+ }
+ }
+ }
+ if (isEqual) return false;
+ }
+ }
+
+ // remove old meta data nodes
+ {
+ std::vector<css::uno::Reference<css::xml::dom::XNode> >
+ ::reverse_iterator it(vec.rbegin());
+ try {
+ for ( ;it != vec.rend(); ++it)
+ {
+ m_xParent->removeChild(*it);
+ }
+ }
+ catch (...)
+ {
+ // Clean up already removed nodes
+ vec.erase(it.base(), vec.end());
+ throw;
+ }
+ vec.clear();
+ }
+
+ // insert new meta data nodes into DOM tree
+ for (sal_Int32 i = 0; i < i_rValue.getLength(); ++i) {
+ css::uno::Reference<css::xml::dom::XElement> xElem(
+ m_xDoc->createElementNS(getNameSpace(name), name),
+ css::uno::UNO_SET_THROW);
+ css::uno::Reference<css::xml::dom::XNode> xNode(xElem,
+ css::uno::UNO_QUERY_THROW);
+ css::uno::Reference<css::xml::dom::XNode> xTextNode(
+ m_xDoc->createTextNode(i_rValue[i]), css::uno::UNO_QUERY_THROW);
+ // set attributes
+ if (i_pAttrs != nullptr) {
+ for (auto const& elem : (*i_pAttrs)[i])
+ {
+ xElem->setAttributeNS(getNameSpace(elem.first),
+ elem.first, elem.second);
+ }
+ }
+ xNode->appendChild(xTextNode);
+ m_xParent->appendChild(xNode);
+ vec.push_back(xNode);
+ }
+
+ return true;
+ } catch (const css::xml::dom::DOMException &) {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw css::lang::WrappedTargetRuntimeException(
+ "SfxDocumentMetaData::setMetaList: DOM exception",
+ css::uno::Reference<css::uno::XInterface>(*this), anyEx);
+ }
+}
+
+// convert property list to string list and attribute list
+std::pair<css::uno::Sequence< OUString>, AttrVector>
+propsToStrings(css::uno::Reference<css::beans::XPropertySet> const & i_xPropSet)
+{
+ ::std::vector< OUString > values;
+ AttrVector attrs;
+
+ css::uno::Reference<css::beans::XPropertySetInfo> xSetInfo
+ = i_xPropSet->getPropertySetInfo();
+ css::uno::Sequence<css::beans::Property> props = xSetInfo->getProperties();
+
+ for (sal_Int32 i = 0; i < props.getLength(); ++i) {
+ if (props[i].Attributes & css::beans::PropertyAttribute::TRANSIENT) {
+ continue;
+ }
+ const OUString name = props[i].Name;
+ css::uno::Any any;
+ try {
+ any = i_xPropSet->getPropertyValue(name);
+ } catch (const css::uno::Exception &) {
+ // ignore
+ }
+ const css::uno::Type & type = any.getValueType();
+ std::vector<std::pair<OUString, OUString> > as;
+ as.emplace_back("meta:name", name);
+ static constexpr OUStringLiteral vt = u"meta:value-type";
+
+ // convert according to type
+ if (type == ::cppu::UnoType<bool>::get()) {
+ bool b = false;
+ any >>= b;
+ OUStringBuffer buf;
+ ::sax::Converter::convertBool(buf, b);
+ values.push_back(buf.makeStringAndClear());
+ as.emplace_back(vt, OUString("boolean"));
+ } else if (type == ::cppu::UnoType< OUString>::get()) {
+ OUString s;
+ any >>= s;
+ values.push_back(s);
+// #i90847# OOo 2.x does stupid things if value-type="string";
+// fortunately string is default anyway, so we can just omit it
+// #i107502#: however, OOo 2.x only reads 4 user-defined without @value-type
+// => best backward compatibility: first 4 without @value-type, rest with
+ if (4 <= i)
+ {
+ as.emplace_back(vt, OUString("string"));
+ }
+ } else if (type == ::cppu::UnoType<css::util::DateTime>::get()) {
+ css::util::DateTime dt;
+ any >>= dt;
+ values.push_back(dateTimeToText(dt));
+ as.emplace_back(vt, OUString("date"));
+ } else if (type == ::cppu::UnoType<css::util::Date>::get()) {
+ css::util::Date d;
+ any >>= d;
+ values.push_back(dateToText(d, nullptr));
+ as.emplace_back(vt,OUString("date"));
+ } else if (type == ::cppu::UnoType<css::util::DateTimeWithTimezone>::get()) {
+ css::util::DateTimeWithTimezone dttz;
+ any >>= dttz;
+ values.push_back(dateTimeToText(dttz.DateTimeInTZ, &dttz.Timezone));
+ as.emplace_back(vt, OUString("date"));
+ } else if (type == ::cppu::UnoType<css::util::DateWithTimezone>::get()) {
+ css::util::DateWithTimezone dtz;
+ any >>= dtz;
+ values.push_back(dateToText(dtz.DateInTZ, &dtz.Timezone));
+ as.emplace_back(vt, OUString("date"));
+ } else if (type == ::cppu::UnoType<css::util::Time>::get()) {
+ // #i97029#: replaced by Duration
+ // Time is supported for backward compatibility with OOo 3.x, x<=2
+ css::util::Time ut;
+ any >>= ut;
+ css::util::Duration ud;
+ ud.Hours = ut.Hours;
+ ud.Minutes = ut.Minutes;
+ ud.Seconds = ut.Seconds;
+ ud.NanoSeconds = ut.NanoSeconds;
+ values.push_back(durationToText(ud));
+ as.emplace_back(vt, OUString("time"));
+ } else if (type == ::cppu::UnoType<css::util::Duration>::get()) {
+ css::util::Duration ud;
+ any >>= ud;
+ values.push_back(durationToText(ud));
+ as.emplace_back(vt, OUString("time"));
+ } else if (::cppu::UnoType<double>::get().isAssignableFrom(type)) {
+ // support not just double, but anything that can be converted
+ double d = 0;
+ any >>= d;
+ OUStringBuffer buf;
+ ::sax::Converter::convertDouble(buf, d);
+ values.push_back(buf.makeStringAndClear());
+ as.emplace_back(vt, OUString("float"));
+ } else {
+ SAL_WARN("sfx.doc", "Unsupported property type: " << any.getValueTypeName() );
+ continue;
+ }
+ attrs.push_back(as);
+ }
+
+ return std::make_pair(comphelper::containerToSequence(values), attrs);
+}
+
+// remove the given element from the DOM, and iff i_pAttrs != 0 insert new one
+void
+SfxDocumentMetaData::updateElement(const OUString& name,
+ std::vector<std::pair<OUString, OUString> >* i_pAttrs)
+{
+ try {
+ // remove old element
+ css::uno::Reference<css::xml::dom::XNode> xNode =
+ m_meta.find(name)->second;
+ if (xNode.is()) {
+ m_xParent->removeChild(xNode);
+ xNode.clear();
+ }
+ // add new element
+ if (nullptr != i_pAttrs) {
+ css::uno::Reference<css::xml::dom::XElement> xElem(
+ m_xDoc->createElementNS(getNameSpace(name), name),
+ css::uno::UNO_SET_THROW);
+ xNode.set(xElem, css::uno::UNO_QUERY_THROW);
+ // set attributes
+ for (auto const& elem : *i_pAttrs)
+ {
+ xElem->setAttributeNS(getNameSpace(elem.first),
+ elem.first, elem.second);
+ }
+ m_xParent->appendChild(xNode);
+ }
+ m_meta[name] = xNode;
+ } catch (const css::xml::dom::DOMException &) {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw css::lang::WrappedTargetRuntimeException(
+ "SfxDocumentMetaData::updateElement: DOM exception",
+ css::uno::Reference<css::uno::XInterface>(*this), anyEx);
+ }
+}
+
+// update user-defined meta data in DOM tree
+void SfxDocumentMetaData::updateUserDefinedAndAttributes()
+{
+ createUserDefined();
+ const css::uno::Reference<css::beans::XPropertySet> xPSet(m_xUserDefined,
+ css::uno::UNO_QUERY_THROW);
+ const std::pair<css::uno::Sequence< OUString>, AttrVector>
+ udStringsAttrs( propsToStrings(xPSet) );
+ (void) setMetaList("meta:user-defined", udStringsAttrs.first,
+ &udStringsAttrs.second);
+
+ // update elements with attributes
+ std::vector<std::pair<OUString, OUString> > attributes;
+ if (!m_TemplateName.isEmpty() || !m_TemplateURL.isEmpty()
+ || isValidDateTime(m_TemplateDate)) {
+ attributes.emplace_back("xlink:type", OUString("simple"));
+ attributes.emplace_back("xlink:actuate", OUString("onRequest"));
+ attributes.emplace_back("xlink:title", m_TemplateName);
+ attributes.emplace_back("xlink:href", m_TemplateURL );
+ if (isValidDateTime(m_TemplateDate)) {
+ attributes.emplace_back(
+ "meta:date", dateTimeToText(m_TemplateDate));
+ }
+ updateElement("meta:template", &attributes);
+ } else {
+ updateElement("meta:template");
+ }
+ attributes.clear();
+
+ if (!m_AutoloadURL.isEmpty() || (0 != m_AutoloadSecs)) {
+ attributes.emplace_back("xlink:href", m_AutoloadURL );
+ attributes.emplace_back("meta:delay",
+ durationToText(m_AutoloadSecs));
+ updateElement("meta:auto-reload", &attributes);
+ } else {
+ updateElement("meta:auto-reload");
+ }
+ attributes.clear();
+
+ if (!m_DefaultTarget.isEmpty()) {
+ attributes.emplace_back(
+ "office:target-frame-name",
+ m_DefaultTarget);
+ // xlink:show: _blank -> new, any other value -> replace
+ const char* show = m_DefaultTarget == "_blank" ? "new" : "replace";
+ attributes.emplace_back(
+ "xlink:show",
+ OUString::createFromAscii(show));
+ updateElement("meta:hyperlink-behaviour", &attributes);
+ } else {
+ updateElement("meta:hyperlink-behaviour");
+ }
+ attributes.clear();
+}
+
+// create empty DOM tree (XDocument)
+css::uno::Reference<css::xml::dom::XDocument>
+SfxDocumentMetaData::createDOM() const // throw (css::uno::RuntimeException)
+{
+ css::uno::Reference<css::xml::dom::XDocumentBuilder> xBuilder( css::xml::dom::DocumentBuilder::create(m_xContext) );
+ css::uno::Reference<css::xml::dom::XDocument> xDoc = xBuilder->newDocument();
+ if (!xDoc.is())
+ throw css::uno::RuntimeException(
+ "SfxDocumentMetaData::createDOM: cannot create new document",
+ *const_cast<SfxDocumentMetaData*>(this));
+ return xDoc;
+}
+
+void
+SfxDocumentMetaData::checkInit() const // throw (css::uno::RuntimeException)
+{
+ if (!m_isInitialized) {
+ throw css::uno::RuntimeException(
+ "SfxDocumentMetaData::checkInit: not initialized",
+ *const_cast<SfxDocumentMetaData*>(this));
+ }
+ assert(m_xDoc.is() && m_xParent.is());
+}
+
+void extractTagAndNamespaceUri(std::u16string_view aChildNodeName,
+ std::u16string_view& rTagName, std::u16string_view& rNamespaceURI)
+{
+ size_t idx = aChildNodeName.find(':');
+ assert(idx != std::u16string_view::npos);
+ std::u16string_view aPrefix = aChildNodeName.substr(0, idx);
+ rTagName = aChildNodeName.substr(idx + 1);
+ if (aPrefix == u"dc")
+ rNamespaceURI = s_nsDC;
+ else if (aPrefix == u"meta")
+ rNamespaceURI = s_nsODFMeta;
+ else if (aPrefix == u"office")
+ rNamespaceURI = s_nsODF;
+ else
+ assert(false);
+}
+
+
+css::uno::Reference<css::xml::dom::XElement> getChildNodeByName(
+ const css::uno::Reference<css::xml::dom::XNode>& xNode,
+ std::u16string_view aChildNodeName)
+{
+ css::uno::Reference< css::xml::dom::XNodeList > xList = xNode->getChildNodes();
+ if (!xList)
+ return nullptr;
+ std::u16string_view aTagName, aNamespaceURI;
+ extractTagAndNamespaceUri(aChildNodeName, aTagName, aNamespaceURI);
+
+ const sal_Int32 nLength(xList->getLength());
+ for (sal_Int32 a(0); a < nLength; a++)
+ {
+ const css::uno::Reference< css::xml::dom::XElement > xChild(xList->item(a), css::uno::UNO_QUERY);
+ if (xChild && xChild->getNodeName() == aTagName && aNamespaceURI == xChild->getNamespaceURI())
+ return xChild;
+ }
+ return nullptr;
+}
+
+
+std::vector<css::uno::Reference<css::xml::dom::XNode> > getChildNodeListByName(
+ const css::uno::Reference<css::xml::dom::XNode>& xNode,
+ std::u16string_view aChildNodeName)
+{
+ css::uno::Reference< css::xml::dom::XNodeList > xList = xNode->getChildNodes();
+ if (!xList)
+ return {};
+ std::u16string_view aTagName, aNamespaceURI;
+ extractTagAndNamespaceUri(aChildNodeName, aTagName, aNamespaceURI);
+ std::vector<css::uno::Reference<css::xml::dom::XNode>> aList;
+ const sal_Int32 nLength(xList->getLength());
+ for (sal_Int32 a(0); a < nLength; a++)
+ {
+ const css::uno::Reference< css::xml::dom::XElement > xChild(xList->item(a), css::uno::UNO_QUERY);
+ if (xChild && xChild->getNodeName() == aTagName && aNamespaceURI == xChild->getNamespaceURI())
+ aList.push_back(xChild);
+ }
+ return aList;
+}
+
+// initialize state from DOM tree
+void SfxDocumentMetaData::init(
+ const css::uno::Reference<css::xml::dom::XDocument>& i_xDoc)
+{
+ if (!i_xDoc.is())
+ throw css::uno::RuntimeException("SfxDocumentMetaData::init: no DOM tree given", *this);
+
+ m_isInitialized = false;
+ m_xDoc = i_xDoc;
+
+ // select nodes for standard meta data stuff
+ // NB: we do not handle the single-XML-file ODF variant, which would
+ // have the root element office:document.
+ // The root of such documents must be converted in the importer!
+ css::uno::Reference<css::xml::dom::XNode> xDocNode(
+ m_xDoc, css::uno::UNO_QUERY_THROW);
+ m_xParent.clear();
+ try {
+ css::uno::Reference<css::xml::dom::XNode> xChild = getChildNodeByName(xDocNode, u"office:document-meta");
+ if (xChild)
+ m_xParent = getChildNodeByName(xChild, u"office:meta");
+ } catch (const css::uno::Exception &) {
+ }
+
+ if (!m_xParent.is()) {
+ // all this create/append stuff may throw DOMException
+ try {
+ css::uno::Reference<css::xml::dom::XElement> xRElem;
+ css::uno::Reference<css::xml::dom::XNode> xNode(
+ i_xDoc->getFirstChild());
+ while (xNode.is()) {
+ if (css::xml::dom::NodeType_ELEMENT_NODE ==xNode->getNodeType())
+ {
+ if ( xNode->getNamespaceURI() == s_nsODF && xNode->getLocalName() == "document-meta" )
+ {
+ xRElem.set(xNode, css::uno::UNO_QUERY_THROW);
+ break;
+ }
+ else
+ {
+ SAL_INFO("sfx.doc", "SfxDocumentMetaData::init(): "
+ "deleting unexpected root element: "
+ << xNode->getLocalName());
+ i_xDoc->removeChild(xNode);
+ xNode = i_xDoc->getFirstChild(); // start over
+ }
+ } else {
+ xNode = xNode->getNextSibling();
+ }
+ }
+ if (!xRElem.is()) {
+ static constexpr OUStringLiteral sOfficeDocumentMeta = u"office:document-meta";
+ xRElem = i_xDoc->createElementNS(
+ s_nsODF, sOfficeDocumentMeta);
+ css::uno::Reference<css::xml::dom::XNode> xRNode(xRElem,
+ css::uno::UNO_QUERY_THROW);
+ i_xDoc->appendChild(xRNode);
+ }
+ static constexpr OUStringLiteral sOfficeVersion = u"office:version";
+ xRElem->setAttributeNS(s_nsODF, sOfficeVersion, "1.0");
+ // does not exist, otherwise m_xParent would not be null
+ static constexpr OUStringLiteral sOfficeMeta = u"office:meta";
+ css::uno::Reference<css::xml::dom::XNode> xParent (
+ i_xDoc->createElementNS(s_nsODF, sOfficeMeta),
+ css::uno::UNO_QUERY_THROW);
+ xRElem->appendChild(xParent);
+ m_xParent = xParent;
+ } catch (const css::xml::dom::DOMException &) {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw css::lang::WrappedTargetRuntimeException(
+ "SfxDocumentMetaData::init: DOM exception",
+ css::uno::Reference<css::uno::XInterface>(*this), anyEx);
+ }
+ }
+
+
+ // select nodes for elements of which we only handle one occurrence
+ for (const char **pName = s_stdMeta; *pName != nullptr; ++pName) {
+ OUString name = OUString::createFromAscii(*pName);
+ // NB: If a document contains more than one occurrence of a
+ // meta-data element, we arbitrarily pick one of them here.
+ // We do not remove the others, i.e., when we write the
+ // document, it will contain the duplicates unchanged.
+ // The ODF spec says that handling multiple occurrences is
+ // application-specific.
+ css::uno::Reference<css::xml::dom::XNode> xNode =
+ getChildNodeByName(m_xParent, name);
+ // Do not create an empty element if it is missing;
+ // for certain elements, such as dateTime, this would be invalid
+ m_meta[name] = xNode;
+ }
+
+ // select nodes for elements of which we handle all occurrences
+ for (const auto & name : s_stdMetaList) {
+ std::vector<css::uno::Reference<css::xml::dom::XNode> > nodes =
+ getChildNodeListByName(m_xParent, OUString(name));
+ m_metaList[name] = nodes;
+ }
+
+ // initialize members corresponding to attributes from DOM nodes
+ static constexpr OUStringLiteral sMetaTemplate = u"meta:template";
+ static constexpr OUStringLiteral sMetaAutoReload = u"meta:auto-reload";
+ static constexpr OUStringLiteral sMetaHyperlinkBehaviour = u"meta:hyperlink-behaviour";
+ m_TemplateName = getMetaAttr(sMetaTemplate, "xlink:title");
+ m_TemplateURL = getMetaAttr(sMetaTemplate, "xlink:href");
+ m_TemplateDate =
+ textToDateTimeDefault(getMetaAttr(sMetaTemplate, "meta:date"));
+ m_AutoloadURL = getMetaAttr(sMetaAutoReload, "xlink:href");
+ m_AutoloadSecs =
+ textToDuration(getMetaAttr(sMetaAutoReload, "meta:delay"));
+ m_DefaultTarget =
+ getMetaAttr(sMetaHyperlinkBehaviour, "office:target-frame-name");
+
+
+ std::vector<css::uno::Reference<css::xml::dom::XNode> > & vec =
+ m_metaList[OUString("meta:user-defined")];
+ m_xUserDefined.clear(); // #i105826#: reset (may be re-initialization)
+ if ( !vec.empty() )
+ {
+ createUserDefined();
+ }
+
+ // user-defined meta data: initialize PropertySet from DOM nodes
+ for (auto const& elem : vec)
+ {
+ css::uno::Reference<css::xml::dom::XElement> xElem(elem,
+ css::uno::UNO_QUERY_THROW);
+ css::uno::Any any;
+ OUString name = xElem->getAttributeNS(s_nsODFMeta, "name");
+ OUString type = xElem->getAttributeNS(s_nsODFMeta, "value-type");
+ OUString text = getNodeText(elem);
+ if ( type == "float" ) {
+ double d;
+ if (::sax::Converter::convertDouble(d, text)) {
+ any <<= d;
+ } else {
+ SAL_WARN("sfx.doc", "Invalid float: " << text);
+ continue;
+ }
+ } else if ( type == "date" ) {
+ bool isDateTime;
+ css::util::Date d;
+ css::util::DateTime dt;
+ std::optional<sal_Int16> nTimeZone;
+ if (textToDateOrDateTime(d, dt, isDateTime, nTimeZone, text)) {
+ if (isDateTime) {
+ if (nTimeZone) {
+ any <<= css::util::DateTimeWithTimezone(dt,
+ *nTimeZone);
+ } else {
+ any <<= dt;
+ }
+ } else {
+ if (nTimeZone) {
+ any <<= css::util::DateWithTimezone(d, *nTimeZone);
+ } else {
+ any <<= d;
+ }
+ }
+ } else {
+ SAL_WARN("sfx.doc", "Invalid date: " << text);
+ continue;
+ }
+ } else if ( type == "time" ) {
+ css::util::Duration ud;
+ if (textToDuration(ud, text)) {
+ any <<= ud;
+ } else {
+ SAL_WARN("sfx.doc", "Invalid time: " << text);
+ continue;
+ }
+ } else if ( type == "boolean" ) {
+ bool b;
+ if (::sax::Converter::convertBool(b, text)) {
+ any <<= b;
+ } else {
+ SAL_WARN("sfx.doc", "Invalid boolean: " << text);
+ continue;
+ }
+ } else { // default
+ any <<= text;
+ }
+ try {
+ m_xUserDefined->addProperty(name,
+ css::beans::PropertyAttribute::REMOVABLE, any);
+ } catch (const css::beans::PropertyExistException &) {
+ SAL_WARN("sfx.doc", "Duplicate: " << name);
+ // ignore; duplicate
+ } catch (const css::beans::IllegalTypeException &) {
+ SAL_INFO("sfx.doc", "SfxDocumentMetaData: illegal type: " << name);
+ } catch (const css::lang::IllegalArgumentException &) {
+ SAL_INFO("sfx.doc", "SfxDocumentMetaData: illegal arg: " << name);
+ }
+ }
+
+ m_isModified = false;
+ m_isInitialized = true;
+}
+
+
+SfxDocumentMetaData::SfxDocumentMetaData(
+ css::uno::Reference< css::uno::XComponentContext > const & context)
+ : BaseMutex()
+ , SfxDocumentMetaData_Base(m_aMutex)
+ , m_xContext(context)
+ , m_NotifyListeners(m_aMutex)
+ , m_isInitialized(false)
+ , m_isModified(false)
+ , m_AutoloadSecs(0)
+{
+ assert(context.is());
+ assert(context->getServiceManager().is());
+ init(createDOM());
+}
+
+// com.sun.star.uno.XServiceInfo:
+OUString SAL_CALL
+SfxDocumentMetaData::getImplementationName()
+{
+ return "SfxDocumentMetaData";
+}
+
+sal_Bool SAL_CALL
+SfxDocumentMetaData::supportsService(OUString const & serviceName)
+{
+ return cppu::supportsService(this, serviceName);
+}
+
+css::uno::Sequence< OUString > SAL_CALL
+SfxDocumentMetaData::getSupportedServiceNames()
+{
+ css::uno::Sequence< OUString > s { "com.sun.star.document.DocumentProperties" };
+ return s;
+}
+
+
+// css::lang::XComponent:
+void SAL_CALL SfxDocumentMetaData::dispose()
+{
+ ::osl::MutexGuard g(m_aMutex);
+ if (!m_isInitialized) {
+ return;
+ }
+ WeakComponentImplHelperBase::dispose(); // superclass
+ m_NotifyListeners.disposeAndClear(css::lang::EventObject(
+ static_cast< ::cppu::OWeakObject* >(this)));
+ m_isInitialized = false;
+ m_meta.clear();
+ m_metaList.clear();
+ m_xParent.clear();
+ m_xDoc.clear();
+ m_xUserDefined.clear();
+}
+
+
+// css::document::XDocumentProperties:
+OUString SAL_CALL
+SfxDocumentMetaData::getAuthor()
+{
+ ::osl::MutexGuard g(m_aMutex);
+ return getMetaText("meta:initial-creator");
+}
+
+void SAL_CALL SfxDocumentMetaData::setAuthor(const OUString & the_value)
+{
+ setMetaTextAndNotify("meta:initial-creator", the_value);
+}
+
+
+OUString SAL_CALL
+SfxDocumentMetaData::getGenerator()
+{
+ ::osl::MutexGuard g(m_aMutex);
+ return getMetaText("meta:generator");
+}
+
+void SAL_CALL
+SfxDocumentMetaData::setGenerator(const OUString & the_value)
+{
+ setMetaTextAndNotify("meta:generator", the_value);
+}
+
+css::util::DateTime SAL_CALL
+SfxDocumentMetaData::getCreationDate()
+{
+ ::osl::MutexGuard g(m_aMutex);
+ return textToDateTimeDefault(getMetaText("meta:creation-date"));
+}
+
+void SAL_CALL
+SfxDocumentMetaData::setCreationDate(const css::util::DateTime & the_value)
+{
+ setMetaTextAndNotify("meta:creation-date", dateTimeToText(the_value));
+}
+
+OUString SAL_CALL
+SfxDocumentMetaData::getTitle()
+{
+ ::osl::MutexGuard g(m_aMutex);
+ return getMetaText("dc:title");
+}
+
+void SAL_CALL SfxDocumentMetaData::setTitle(const OUString & the_value)
+{
+ setMetaTextAndNotify("dc:title", the_value);
+}
+
+OUString SAL_CALL
+SfxDocumentMetaData::getSubject()
+{
+ ::osl::MutexGuard g(m_aMutex);
+ return getMetaText("dc:subject");
+}
+
+void SAL_CALL
+SfxDocumentMetaData::setSubject(const OUString & the_value)
+{
+ setMetaTextAndNotify("dc:subject", the_value);
+}
+
+OUString SAL_CALL
+SfxDocumentMetaData::getDescription()
+{
+ ::osl::MutexGuard g(m_aMutex);
+ return getMetaText("dc:description");
+}
+
+void SAL_CALL
+SfxDocumentMetaData::setDescription(const OUString & the_value)
+{
+ setMetaTextAndNotify("dc:description", the_value);
+}
+
+css::uno::Sequence< OUString >
+SAL_CALL SfxDocumentMetaData::getKeywords()
+{
+ ::osl::MutexGuard g(m_aMutex);
+ return getMetaList("meta:keyword");
+}
+
+void SAL_CALL
+SfxDocumentMetaData::setKeywords(
+ const css::uno::Sequence< OUString > & the_value)
+{
+ ::osl::ClearableMutexGuard g(m_aMutex);
+ if (setMetaList("meta:keyword", the_value, nullptr)) {
+ g.clear();
+ setModified(true);
+ }
+}
+
+css::lang::Locale SAL_CALL
+ SfxDocumentMetaData::getLanguage()
+{
+ ::osl::MutexGuard g(m_aMutex);
+ css::lang::Locale loc( LanguageTag::convertToLocale( getMetaText("dc:language"), false));
+ return loc;
+}
+
+void SAL_CALL
+SfxDocumentMetaData::setLanguage(const css::lang::Locale & the_value)
+{
+ OUString text( LanguageTag::convertToBcp47( the_value, false));
+ setMetaTextAndNotify("dc:language", text);
+}
+
+OUString SAL_CALL
+SfxDocumentMetaData::getModifiedBy()
+{
+ ::osl::MutexGuard g(m_aMutex);
+ return getMetaText("dc:creator");
+}
+
+void SAL_CALL
+SfxDocumentMetaData::setModifiedBy(const OUString & the_value)
+{
+ setMetaTextAndNotify("dc:creator", the_value);
+}
+
+css::util::DateTime SAL_CALL
+SfxDocumentMetaData::getModificationDate()
+{
+ ::osl::MutexGuard g(m_aMutex);
+ return textToDateTimeDefault(getMetaText("dc:date"));
+}
+
+void SAL_CALL
+SfxDocumentMetaData::setModificationDate(const css::util::DateTime & the_value)
+{
+ setMetaTextAndNotify("dc:date", dateTimeToText(the_value));
+}
+
+OUString SAL_CALL
+SfxDocumentMetaData::getPrintedBy()
+{
+ ::osl::MutexGuard g(m_aMutex);
+ return getMetaText("meta:printed-by");
+}
+
+void SAL_CALL
+SfxDocumentMetaData::setPrintedBy(const OUString & the_value)
+{
+ setMetaTextAndNotify("meta:printed-by", the_value);
+}
+
+css::util::DateTime SAL_CALL
+SfxDocumentMetaData::getPrintDate()
+{
+ ::osl::MutexGuard g(m_aMutex);
+ return textToDateTimeDefault(getMetaText("meta:print-date"));
+}
+
+void SAL_CALL
+SfxDocumentMetaData::setPrintDate(const css::util::DateTime & the_value)
+{
+ setMetaTextAndNotify("meta:print-date", dateTimeToText(the_value));
+}
+
+OUString SAL_CALL
+SfxDocumentMetaData::getTemplateName()
+{
+ ::osl::MutexGuard g(m_aMutex);
+ checkInit();
+ return m_TemplateName;
+}
+
+void SAL_CALL
+SfxDocumentMetaData::setTemplateName(const OUString & the_value)
+{
+ ::osl::ClearableMutexGuard g(m_aMutex);
+ checkInit();
+ if (m_TemplateName != the_value) {
+ m_TemplateName = the_value;
+ g.clear();
+ setModified(true);
+ }
+}
+
+OUString SAL_CALL
+SfxDocumentMetaData::getTemplateURL()
+{
+ ::osl::MutexGuard g(m_aMutex);
+ checkInit();
+ return m_TemplateURL;
+}
+
+void SAL_CALL
+SfxDocumentMetaData::setTemplateURL(const OUString & the_value)
+{
+ ::osl::ClearableMutexGuard g(m_aMutex);
+ checkInit();
+ if (m_TemplateURL != the_value) {
+ m_TemplateURL = the_value;
+ g.clear();
+ setModified(true);
+ }
+}
+
+css::util::DateTime SAL_CALL
+SfxDocumentMetaData::getTemplateDate()
+{
+ ::osl::MutexGuard g(m_aMutex);
+ checkInit();
+ return m_TemplateDate;
+}
+
+void SAL_CALL
+SfxDocumentMetaData::setTemplateDate(const css::util::DateTime & the_value)
+{
+ ::osl::ClearableMutexGuard g(m_aMutex);
+ checkInit();
+ if (m_TemplateDate != the_value) {
+ m_TemplateDate = the_value;
+ g.clear();
+ setModified(true);
+ }
+}
+
+OUString SAL_CALL
+SfxDocumentMetaData::getAutoloadURL()
+{
+ ::osl::MutexGuard g(m_aMutex);
+ checkInit();
+ return m_AutoloadURL;
+}
+
+void SAL_CALL
+SfxDocumentMetaData::setAutoloadURL(const OUString & the_value)
+{
+ ::osl::ClearableMutexGuard g(m_aMutex);
+ checkInit();
+ if (m_AutoloadURL != the_value) {
+ m_AutoloadURL = the_value;
+ g.clear();
+ setModified(true);
+ }
+}
+
+::sal_Int32 SAL_CALL
+SfxDocumentMetaData::getAutoloadSecs()
+{
+ ::osl::MutexGuard g(m_aMutex);
+ checkInit();
+ return m_AutoloadSecs;
+}
+
+void SAL_CALL
+SfxDocumentMetaData::setAutoloadSecs(::sal_Int32 the_value)
+{
+ if (the_value < 0)
+ throw css::lang::IllegalArgumentException(
+ "SfxDocumentMetaData::setAutoloadSecs: argument is negative",
+ *this, 0);
+ ::osl::ClearableMutexGuard g(m_aMutex);
+ checkInit();
+ if (m_AutoloadSecs != the_value) {
+ m_AutoloadSecs = the_value;
+ g.clear();
+ setModified(true);
+ }
+}
+
+OUString SAL_CALL
+SfxDocumentMetaData::getDefaultTarget()
+{
+ ::osl::MutexGuard g(m_aMutex);
+ checkInit();
+ return m_DefaultTarget;
+}
+
+void SAL_CALL
+SfxDocumentMetaData::setDefaultTarget(const OUString & the_value)
+{
+ ::osl::ClearableMutexGuard g(m_aMutex);
+ checkInit();
+ if (m_DefaultTarget != the_value) {
+ m_DefaultTarget = the_value;
+ g.clear();
+ setModified(true);
+ }
+}
+
+css::uno::Sequence< css::beans::NamedValue > SAL_CALL
+SfxDocumentMetaData::getDocumentStatistics()
+{
+ ::osl::MutexGuard g(m_aMutex);
+ checkInit();
+ ::std::vector<css::beans::NamedValue> stats;
+ for (size_t i = 0; s_stdStats[i] != nullptr; ++i) {
+ OUString text = getMetaAttr("meta:document-statistic", s_stdStatAttrs[i]);
+ if (text.isEmpty()) continue;
+ css::beans::NamedValue stat;
+ stat.Name = OUString::createFromAscii(s_stdStats[i]);
+ sal_Int32 val;
+ css::uno::Any any;
+ if (!::sax::Converter::convertNumber(val, text, 0) || (val < 0)) {
+ val = 0;
+ SAL_WARN("sfx.doc", "Invalid number: " << text);
+ }
+ any <<= val;
+ stat.Value = any;
+ stats.push_back(stat);
+ }
+
+ return ::comphelper::containerToSequence(stats);
+}
+
+void SAL_CALL
+SfxDocumentMetaData::setDocumentStatistics(
+ const css::uno::Sequence< css::beans::NamedValue > & the_value)
+{
+ {
+ osl::MutexGuard g(m_aMutex);
+ checkInit();
+ std::vector<std::pair<OUString, OUString> > attributes;
+ for (const auto& rValue : the_value) {
+ const OUString name = rValue.Name;
+ // inefficiently search for matching attribute
+ for (size_t j = 0; s_stdStats[j] != nullptr; ++j) {
+ if (name.equalsAscii(s_stdStats[j])) {
+ const css::uno::Any any = rValue.Value;
+ sal_Int32 val = 0;
+ if (any >>= val) {
+ attributes.emplace_back(s_stdStatAttrs[j],
+ OUString::number(val));
+ }
+ else {
+ SAL_WARN("sfx.doc", "Invalid statistic: " << name);
+ }
+ break;
+ }
+ }
+ }
+ updateElement("meta:document-statistic", &attributes);
+ }
+ setModified(true);
+}
+
+::sal_Int16 SAL_CALL
+SfxDocumentMetaData::getEditingCycles()
+{
+ ::osl::MutexGuard g(m_aMutex);
+ OUString text = getMetaText("meta:editing-cycles");
+ sal_Int32 ret;
+ if (::sax::Converter::convertNumber(ret, text,
+ 0, std::numeric_limits<sal_Int16>::max())) {
+ return static_cast<sal_Int16>(ret);
+ } else {
+ return 0;
+ }
+}
+
+void SAL_CALL
+SfxDocumentMetaData::setEditingCycles(::sal_Int16 the_value)
+{
+ if (the_value < 0)
+ throw css::lang::IllegalArgumentException(
+ "SfxDocumentMetaData::setEditingCycles: argument is negative",
+ *this, 0);
+ setMetaTextAndNotify("meta:editing-cycles", OUString::number(the_value));
+}
+
+::sal_Int32 SAL_CALL
+SfxDocumentMetaData::getEditingDuration()
+{
+ ::osl::MutexGuard g(m_aMutex);
+ return textToDuration(getMetaText("meta:editing-duration"));
+}
+
+void SAL_CALL
+SfxDocumentMetaData::setEditingDuration(::sal_Int32 the_value)
+{
+ if (the_value < 0)
+ throw css::lang::IllegalArgumentException(
+ "SfxDocumentMetaData::setEditingDuration: argument is negative",
+ *this, 0);
+ setMetaTextAndNotify("meta:editing-duration", durationToText(the_value));
+}
+
+void SAL_CALL
+SfxDocumentMetaData::resetUserData(const OUString & the_value)
+{
+ ::osl::ClearableMutexGuard g(m_aMutex);
+
+ bool bModified( false );
+ bModified |= setMetaText("meta:initial-creator", the_value);
+ ::DateTime now( ::DateTime::SYSTEM );
+ css::util::DateTime uDT(now.GetUNODateTime());
+ bModified |= setMetaText("meta:creation-date", dateTimeToText(uDT));
+ bModified |= setMetaText("dc:creator", OUString());
+ bModified |= setMetaText("meta:printed-by", OUString());
+ bModified |= setMetaText("dc:date", dateTimeToText(css::util::DateTime()));
+ bModified |= setMetaText("meta:print-date",
+ dateTimeToText(css::util::DateTime()));
+ bModified |= setMetaText("meta:editing-duration", durationToText(0));
+ bModified |= setMetaText("meta:editing-cycles",
+ "1");
+
+ if (bModified) {
+ g.clear();
+ setModified(true);
+ }
+}
+
+
+css::uno::Reference< css::beans::XPropertyContainer > SAL_CALL
+SfxDocumentMetaData::getUserDefinedProperties()
+{
+ ::osl::MutexGuard g(m_aMutex);
+ checkInit();
+ createUserDefined();
+ return m_xUserDefined;
+}
+
+
+void SAL_CALL
+SfxDocumentMetaData::loadFromStorage(
+ const css::uno::Reference< css::embed::XStorage > & xStorage,
+ const css::uno::Sequence< css::beans::PropertyValue > & Medium)
+{
+ if (!xStorage.is())
+ throw css::lang::IllegalArgumentException("SfxDocumentMetaData::loadFromStorage: argument is null", *this, 0);
+ ::osl::MutexGuard g(m_aMutex);
+
+ // open meta data file
+ css::uno::Reference<css::io::XStream> xStream(
+ xStorage->openStreamElement(
+ s_meta,
+ css::embed::ElementModes::READ) );
+ if (!xStream.is()) throw css::uno::RuntimeException();
+ css::uno::Reference<css::io::XInputStream> xInStream =
+ xStream->getInputStream();
+ if (!xInStream.is()) throw css::uno::RuntimeException();
+
+ // create DOM parser service
+ css::uno::Reference<css::lang::XMultiComponentFactory> xMsf (
+ m_xContext->getServiceManager());
+ css::xml::sax::InputSource input;
+ input.aInputStream = xInStream;
+
+ sal_uInt64 version = SotStorage::GetVersion( xStorage );
+ // Oasis is also the default (0)
+ bool bOasis = ( version > SOFFICE_FILEFORMAT_60 || version == 0 );
+ const char *pServiceName = bOasis
+ ? "com.sun.star.document.XMLOasisMetaImporter"
+ : "com.sun.star.document.XMLMetaImporter";
+
+ // set base URL
+ css::uno::Reference<css::beans::XPropertySet> xPropArg =
+ getURLProperties(Medium);
+ try {
+ xPropArg->getPropertyValue("BaseURI")
+ >>= input.sSystemId;
+ input.sSystemId += OUString::Concat("/") + s_meta;
+ } catch (const css::uno::Exception &) {
+ input.sSystemId = s_meta;
+ }
+ css::uno::Sequence< css::uno::Any > args{ css::uno::Any(xPropArg) };
+
+ // the underlying SvXMLImport implements XFastParser, XImporter, XFastDocumentHandler
+ css::uno::Reference<XInterface> xFilter =
+ xMsf->createInstanceWithArgumentsAndContext(
+ OUString::createFromAscii(pServiceName), args, m_xContext);
+ assert(xFilter);
+ css::uno::Reference<css::xml::sax::XFastParser> xFastParser(xFilter, css::uno::UNO_QUERY);
+ css::uno::Reference<css::document::XImporter> xImp(xFilter, css::uno::UNO_QUERY_THROW);
+ xImp->setTargetDocument(css::uno::Reference<css::lang::XComponent>(this));
+ try {
+ if (xFastParser)
+ xFastParser->parseStream(input);
+ else
+ {
+ css::uno::Reference<css::xml::sax::XDocumentHandler> xDocHandler(xFilter, css::uno::UNO_QUERY_THROW);
+ css::uno::Reference<css::xml::sax::XParser> xParser = css::xml::sax::Parser::create(m_xContext);
+ xParser->setDocumentHandler(xDocHandler);
+ xParser->parseStream(input);
+ }
+ } catch (const css::xml::sax::SAXException &) {
+ throw css::io::WrongFormatException(
+ "SfxDocumentMetaData::loadFromStorage:"
+ " XML parsing exception", *this);
+ }
+ // NB: the implementation of XMLOasisMetaImporter calls initialize
+ checkInit();
+}
+
+void SAL_CALL
+SfxDocumentMetaData::storeToStorage(
+ const css::uno::Reference< css::embed::XStorage > & xStorage,
+ const css::uno::Sequence< css::beans::PropertyValue > & Medium)
+{
+ if (!xStorage.is())
+ throw css::lang::IllegalArgumentException(
+ "SfxDocumentMetaData::storeToStorage: argument is null", *this, 0);
+ ::osl::MutexGuard g(m_aMutex);
+ checkInit();
+
+ // update user-defined meta data in DOM tree
+// updateUserDefinedAndAttributes(); // this will be done in serialize!
+
+ // write into storage
+ css::uno::Reference<css::io::XStream> xStream =
+ xStorage->openStreamElement(s_meta,
+ css::embed::ElementModes::WRITE
+ | css::embed::ElementModes::TRUNCATE);
+ if (!xStream.is()) throw css::uno::RuntimeException();
+ css::uno::Reference< css::beans::XPropertySet > xStreamProps(xStream,
+ css::uno::UNO_QUERY_THROW);
+ xStreamProps->setPropertyValue(
+ "MediaType",
+ css::uno::Any(OUString("text/xml")));
+ xStreamProps->setPropertyValue(
+ "Compressed",
+ css::uno::Any(false));
+ xStreamProps->setPropertyValue(
+ "UseCommonStoragePasswordEncryption",
+ css::uno::Any(false));
+ css::uno::Reference<css::io::XOutputStream> xOutStream =
+ xStream->getOutputStream();
+ if (!xOutStream.is()) throw css::uno::RuntimeException();
+ css::uno::Reference<css::lang::XMultiComponentFactory> xMsf (
+ m_xContext->getServiceManager());
+ css::uno::Reference<css::xml::sax::XWriter> xSaxWriter(
+ css::xml::sax::Writer::create(m_xContext));
+ xSaxWriter->setOutputStream(xOutStream);
+
+ const sal_uInt64 version = SotStorage::GetVersion( xStorage );
+ // Oasis is also the default (0)
+ const bool bOasis = ( version > SOFFICE_FILEFORMAT_60 || version == 0 );
+ const char *pServiceName = bOasis
+ ? "com.sun.star.document.XMLOasisMetaExporter"
+ : "com.sun.star.document.XMLMetaExporter";
+
+ // set base URL
+ css::uno::Reference<css::beans::XPropertySet> xPropArg =
+ getURLProperties(Medium);
+ css::uno::Sequence< css::uno::Any > args{ css::uno::Any(xSaxWriter), css::uno::Any(xPropArg) };
+
+ css::uno::Reference<css::document::XExporter> xExp(
+ xMsf->createInstanceWithArgumentsAndContext(
+ OUString::createFromAscii(pServiceName), args, m_xContext),
+ css::uno::UNO_QUERY_THROW);
+ xExp->setSourceDocument(css::uno::Reference<css::lang::XComponent>(this));
+ css::uno::Reference<css::document::XFilter> xFilter(xExp,
+ css::uno::UNO_QUERY_THROW);
+ if (!xFilter->filter(css::uno::Sequence< css::beans::PropertyValue >())) {
+ throw css::io::IOException(
+ "SfxDocumentMetaData::storeToStorage: cannot filter", *this);
+ }
+ css::uno::Reference<css::embed::XTransactedObject> xTransaction(
+ xStorage, css::uno::UNO_QUERY);
+ if (xTransaction.is()) {
+ xTransaction->commit();
+ }
+}
+
+void SAL_CALL
+SfxDocumentMetaData::loadFromMedium(const OUString & URL,
+ const css::uno::Sequence< css::beans::PropertyValue > & Medium)
+{
+ css::uno::Reference<css::io::XInputStream> xIn;
+ utl::MediaDescriptor md(Medium);
+ // if we have a URL parameter, it replaces the one in the media descriptor
+ if (!URL.isEmpty()) {
+ md[ utl::MediaDescriptor::PROP_URL ] <<= URL;
+ md[ utl::MediaDescriptor::PROP_READONLY ] <<= true;
+ }
+ if (md.addInputStream()) {
+ md[ utl::MediaDescriptor::PROP_INPUTSTREAM ] >>= xIn;
+ }
+ css::uno::Reference<css::embed::XStorage> xStorage;
+ try {
+ if (xIn.is()) {
+ xStorage = ::comphelper::OStorageHelper::GetStorageFromInputStream(
+ xIn, m_xContext);
+ } else { // fallback to url parameter
+ xStorage = ::comphelper::OStorageHelper::GetStorageFromURL(
+ URL, css::embed::ElementModes::READ, m_xContext);
+ }
+ } catch (const css::uno::RuntimeException &) {
+ throw;
+ } catch (const css::io::IOException &) {
+ throw;
+ } catch (const css::uno::Exception &) {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw css::lang::WrappedTargetException(
+ "SfxDocumentMetaData::loadFromMedium: exception",
+ css::uno::Reference<css::uno::XInterface>(*this),
+ anyEx);
+ }
+ if (!xStorage.is()) {
+ throw css::uno::RuntimeException(
+ "SfxDocumentMetaData::loadFromMedium: cannot get Storage",
+ *this);
+ }
+ loadFromStorage(xStorage, md.getAsConstPropertyValueList());
+}
+
+void SAL_CALL
+SfxDocumentMetaData::storeToMedium(const OUString & URL,
+ const css::uno::Sequence< css::beans::PropertyValue > & Medium)
+{
+ utl::MediaDescriptor md(Medium);
+ if (!URL.isEmpty()) {
+ md[ utl::MediaDescriptor::PROP_URL ] <<= URL;
+ }
+ SfxMedium aMedium(md.getAsConstPropertyValueList());
+ css::uno::Reference<css::embed::XStorage> xStorage
+ = aMedium.GetOutputStorage();
+
+
+ if (!xStorage.is()) {
+ throw css::uno::RuntimeException(
+ "SfxDocumentMetaData::storeToMedium: cannot get Storage",
+ *this);
+ }
+ // set MIME type of the storage
+ utl::MediaDescriptor::const_iterator iter
+ = md.find(utl::MediaDescriptor::PROP_MEDIATYPE);
+ if (iter != md.end()) {
+ css::uno::Reference< css::beans::XPropertySet > xProps(xStorage,
+ css::uno::UNO_QUERY_THROW);
+ xProps->setPropertyValue(
+ utl::MediaDescriptor::PROP_MEDIATYPE,
+ iter->second);
+ }
+ storeToStorage(xStorage, md.getAsConstPropertyValueList());
+
+
+ const bool bOk = aMedium.Commit();
+ aMedium.Close();
+ if ( !bOk ) {
+ ErrCode nError = aMedium.GetError();
+ if ( nError == ERRCODE_NONE ) {
+ nError = ERRCODE_IO_GENERAL;
+ }
+
+ throw css::task::ErrorCodeIOException(
+ "SfxDocumentMetaData::storeToMedium <" + URL + "> Commit failed: " + nError.toHexString(),
+ css::uno::Reference< css::uno::XInterface >(), sal_uInt32(nError));
+
+ }
+}
+
+// css::lang::XInitialization:
+void SAL_CALL SfxDocumentMetaData::initialize( const css::uno::Sequence< css::uno::Any > & aArguments)
+{
+ // possible arguments:
+ // - no argument: default initialization (empty DOM)
+ // - 1 argument, XDocument: initialize with given DOM and empty base URL
+ // NB: links in document must be absolute
+
+ ::osl::MutexGuard g(m_aMutex);
+ css::uno::Reference<css::xml::dom::XDocument> xDoc;
+
+ for (sal_Int32 i = 0; i < aArguments.getLength(); ++i) {
+ const css::uno::Any any = aArguments[i];
+ if (!(any >>= xDoc)) {
+ throw css::lang::IllegalArgumentException(
+ "SfxDocumentMetaData::initialize: argument must be XDocument",
+ *this, static_cast<sal_Int16>(i));
+ }
+ if (!xDoc.is()) {
+ throw css::lang::IllegalArgumentException(
+ "SfxDocumentMetaData::initialize: argument is null",
+ *this, static_cast<sal_Int16>(i));
+ }
+ }
+
+ if (!xDoc.is()) {
+ // For a new document, we create a new DOM tree here.
+ xDoc = createDOM();
+ }
+
+ init(xDoc);
+}
+
+// css::util::XCloneable:
+css::uno::Reference<css::util::XCloneable> SAL_CALL
+SfxDocumentMetaData::createClone()
+{
+ ::osl::MutexGuard g(m_aMutex);
+ checkInit();
+
+ rtl::Reference<SfxDocumentMetaData> pNew = createMe(m_xContext);
+
+ // NB: do not copy the modification listeners, only DOM
+ css::uno::Reference<css::xml::dom::XDocument> xDoc = createDOM();
+ try {
+ updateUserDefinedAndAttributes();
+ // deep copy of root node
+ css::uno::Reference<css::xml::dom::XNode> xRoot(
+ m_xDoc->getDocumentElement(), css::uno::UNO_QUERY_THROW);
+ css::uno::Reference<css::xml::dom::XNode> xRootNew(
+ xDoc->importNode(xRoot, true));
+ xDoc->appendChild(xRootNew);
+ pNew->init(xDoc);
+ } catch (const css::uno::RuntimeException &) {
+ throw;
+ } catch (const css::uno::Exception &) {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw css::lang::WrappedTargetRuntimeException(
+ "SfxDocumentMetaData::createClone: exception",
+ css::uno::Reference<css::uno::XInterface>(*this), anyEx);
+ }
+ return css::uno::Reference<css::util::XCloneable> (pNew);
+}
+
+// css::util::XModifiable:
+sal_Bool SAL_CALL SfxDocumentMetaData::isModified( )
+{
+ ::osl::MutexGuard g(m_aMutex);
+ checkInit();
+ css::uno::Reference<css::util::XModifiable> xMB(m_xUserDefined,
+ css::uno::UNO_QUERY);
+ return m_isModified || (xMB.is() && xMB->isModified());
+}
+
+void SAL_CALL SfxDocumentMetaData::setModified( sal_Bool bModified )
+{
+ css::uno::Reference<css::util::XModifiable> xMB;
+ { // do not lock mutex while notifying (#i93514#) to prevent deadlock
+ ::osl::MutexGuard g(m_aMutex);
+ checkInit();
+ m_isModified = bModified;
+ if ( !bModified && m_xUserDefined.is() )
+ {
+ xMB.set(m_xUserDefined, css::uno::UNO_QUERY);
+ assert(xMB.is() &&
+ "SfxDocumentMetaData::setModified: PropertyBag not Modifiable?");
+ }
+ }
+ if (bModified) {
+ try {
+ css::uno::Reference<css::uno::XInterface> xThis(*this);
+ css::lang::EventObject event(xThis);
+ m_NotifyListeners.notifyEach(&css::util::XModifyListener::modified,
+ event);
+ } catch (const css::uno::RuntimeException &) {
+ throw;
+ } catch (const css::uno::Exception &) {
+ // ignore
+ TOOLS_WARN_EXCEPTION("sfx.doc", "setModified");
+ }
+ } else {
+ if (xMB.is()) {
+ xMB->setModified(false);
+ }
+ }
+}
+
+// css::util::XModifyBroadcaster:
+void SAL_CALL SfxDocumentMetaData::addModifyListener(
+ const css::uno::Reference< css::util::XModifyListener > & xListener)
+{
+ ::osl::MutexGuard g(m_aMutex);
+ checkInit();
+ m_NotifyListeners.addInterface(xListener);
+ css::uno::Reference<css::util::XModifyBroadcaster> xMB(m_xUserDefined,
+ css::uno::UNO_QUERY);
+ if (xMB.is()) {
+ xMB->addModifyListener(xListener);
+ }
+}
+
+void SAL_CALL SfxDocumentMetaData::removeModifyListener(
+ const css::uno::Reference< css::util::XModifyListener > & xListener)
+{
+ ::osl::MutexGuard g(m_aMutex);
+ checkInit();
+ m_NotifyListeners.removeInterface(xListener);
+ css::uno::Reference<css::util::XModifyBroadcaster> xMB(m_xUserDefined,
+ css::uno::UNO_QUERY);
+ if (xMB.is()) {
+ xMB->removeModifyListener(xListener);
+ }
+}
+
+// css::xml::sax::XSAXSerializable
+void SAL_CALL SfxDocumentMetaData::serialize(
+ const css::uno::Reference<css::xml::sax::XDocumentHandler>& i_xHandler,
+ const css::uno::Sequence< css::beans::StringPair >& i_rNamespaces)
+{
+ ::osl::MutexGuard g(m_aMutex);
+ checkInit();
+ updateUserDefinedAndAttributes();
+ css::uno::Reference<css::xml::sax::XSAXSerializable> xSAXable(m_xDoc,
+ css::uno::UNO_QUERY_THROW);
+ xSAXable->serialize(i_xHandler, i_rNamespaces);
+}
+
+void SfxDocumentMetaData::createUserDefined()
+{
+ // user-defined meta data: create PropertyBag which only accepts property
+ // values of allowed types
+ if ( m_xUserDefined.is() )
+ return;
+
+ css::uno::Sequence<css::uno::Type> types{
+ ::cppu::UnoType<bool>::get(),
+ ::cppu::UnoType< OUString>::get(),
+ ::cppu::UnoType<css::util::DateTime>::get(),
+ ::cppu::UnoType<css::util::Date>::get(),
+ ::cppu::UnoType<css::util::DateTimeWithTimezone>::get(),
+ ::cppu::UnoType<css::util::DateWithTimezone>::get(),
+ ::cppu::UnoType<css::util::Duration>::get(),
+ ::cppu::UnoType<float>::get(),
+ ::cppu::UnoType<double>::get(),
+ ::cppu::UnoType<sal_Int16>::get(),
+ ::cppu::UnoType<sal_Int32>::get(),
+ ::cppu::UnoType<sal_Int64>::get(),
+ // Time is supported for backward compatibility with OOo 3.x, x<=2
+ ::cppu::UnoType<css::util::Time>::get()
+ };
+ // #i94175#: ODF allows empty user-defined property names!
+ m_xUserDefined.set(
+ css::beans::PropertyBag::createWithTypes( m_xContext, types, true/*AllowEmptyPropertyName*/, false/*AutomaticAddition*/ ),
+ css::uno::UNO_QUERY_THROW);
+
+ const css::uno::Reference<css::util::XModifyBroadcaster> xMB(
+ m_xUserDefined, css::uno::UNO_QUERY);
+ if (xMB.is())
+ {
+ const std::vector<css::uno::Reference<css::util::XModifyListener> >
+ listeners(m_NotifyListeners.getElements());
+ for (const auto& l : listeners) {
+ xMB->addModifyListener(l);
+ }
+ }
+}
+
+} // closing anonymous implementation namespace
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+CompatWriterDocPropsImpl_get_implementation(
+ css::uno::XComponentContext *context,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new CompatWriterDocPropsImpl(context));
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+SfxDocumentMetaData_get_implementation(
+ css::uno::XComponentContext *context,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new SfxDocumentMetaData(context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/doc/SfxRedactionHelper.cxx b/sfx2/source/doc/SfxRedactionHelper.cxx
new file mode 100644
index 000000000..beb96b55d
--- /dev/null
+++ b/sfx2/source/doc/SfxRedactionHelper.cxx
@@ -0,0 +1,562 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * 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 <SfxRedactionHelper.hxx>
+#include <autoredactdialog.hxx>
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/drawing/XDrawPagesSupplier.hpp>
+#include <com/sun/star/drawing/LineStyle.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/graphic/XGraphic.hpp>
+#include <com/sun/star/frame/XLayoutManager.hpp>
+
+// For page margin related methods
+#include <com/sun/star/style/XStyle.hpp>
+#include <com/sun/star/style/XStyleFamiliesSupplier.hpp>
+#include <com/sun/star/text/XPageCursor.hpp>
+#include <com/sun/star/text/XTextViewCursorSupplier.hpp>
+#include <com/sun/star/sheet/XSpreadsheetView.hpp>
+
+// Search util
+#include <i18nutil/searchopt.hxx>
+#include <com/sun/star/util/SearchAlgorithms.hpp>
+#include <com/sun/star/util/SearchAlgorithms2.hpp>
+#include <com/sun/star/util/SearchFlags.hpp>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <i18nlangtag/languagetag.hxx>
+#include <unotools/textsearch.hxx>
+
+#include <sfx2/request.hxx>
+#include <sfx2/sfxsids.hrc>
+#include <sfx2/viewfrm.hxx>
+
+#include <svl/eitem.hxx>
+#include <svl/stritem.hxx>
+
+#include <svtools/DocumentToGraphicRenderer.hxx>
+
+#include <tools/gen.hxx>
+#include <tools/stream.hxx>
+#include <tools/diagnose_ex.h>
+
+#include <vcl/gdimtf.hxx>
+#include <vcl/graph.hxx>
+#include <sal/log.hxx>
+
+#include <vcl/wmf.hxx>
+#include <vcl/metaact.hxx>
+#include <vcl/outdev.hxx>
+#include <vcl/vcllayout.hxx>
+#include <o3tl/string_view.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::uno;
+
+bool SfxRedactionHelper::isRedactMode(const SfxRequest& rReq)
+{
+ const SfxItemSet* pArgs = rReq.GetArgs();
+ if (pArgs)
+ {
+ const SfxBoolItem* pIsRedactMode = rReq.GetArg<SfxBoolItem>(SID_IS_REDACT_MODE);
+ if (pIsRedactMode && pIsRedactMode->GetValue())
+ return true;
+ }
+
+ return false;
+}
+
+OUString SfxRedactionHelper::getStringParam(const SfxRequest& rReq, sal_uInt16 nParamId)
+{
+ OUString sStringParam;
+
+ const SfxItemSet* pArgs = rReq.GetArgs();
+ if (!pArgs)
+ return sStringParam;
+
+ const SfxStringItem* pStringArg = rReq.GetArg<SfxStringItem>(nParamId);
+ if (!pStringArg)
+ return sStringParam;
+
+ sStringParam = pStringArg->GetValue();
+ return sStringParam;
+}
+
+namespace
+{
+/*
+ * Roundtrip the gdimetafile to and from WMF
+ * to get rid of the position and size irregularities
+ * We better check the conversion method to see what it
+ * actually does to correct these issues, and do it ourselves.
+ * */
+void fixMetaFile(GDIMetaFile& tmpMtf)
+{
+ SvMemoryStream aDestStrm(65535, 65535);
+ ConvertGDIMetaFileToWMF(tmpMtf, aDestStrm, nullptr, false);
+ aDestStrm.Seek(0);
+
+ tmpMtf.Clear();
+
+ ReadWindowMetafile(aDestStrm, tmpMtf);
+}
+
+/*
+ * Sets page margins for a Draw page. Negative values are considered erroneous
+ * */
+void setPageMargins(const uno::Reference<beans::XPropertySet>& xPageProperySet,
+ const PageMargins& aPageMargins)
+{
+ if (aPageMargins.nTop < 0 || aPageMargins.nBottom < 0 || aPageMargins.nLeft < 0
+ || aPageMargins.nRight < 0)
+ return;
+
+ xPageProperySet->setPropertyValue("BorderTop", css::uno::Any(aPageMargins.nTop));
+ xPageProperySet->setPropertyValue("BorderBottom", css::uno::Any(aPageMargins.nBottom));
+ xPageProperySet->setPropertyValue("BorderLeft", css::uno::Any(aPageMargins.nLeft));
+ xPageProperySet->setPropertyValue("BorderRight", css::uno::Any(aPageMargins.nRight));
+}
+
+// #i10613# Extracted from ImplCheckRect::ImplCreate
+tools::Rectangle ImplCalcActionBounds(const MetaAction& rAct, const OutputDevice& rOut,
+ sal_Int32 nStrStartPos, sal_Int32 nStrEndPos)
+{
+ tools::Rectangle aActionBounds;
+
+ switch (rAct.GetType())
+ {
+ case MetaActionType::TEXTARRAY:
+ {
+ const MetaTextArrayAction& rTextAct = static_cast<const MetaTextArrayAction&>(rAct);
+ const OUString aString(rTextAct.GetText().copy(rTextAct.GetIndex(), rTextAct.GetLen()));
+
+ if (!aString.isEmpty())
+ {
+ // #105987# ImplLayout takes everything in logical coordinates
+ std::unique_ptr<SalLayout> pSalLayout1 = rOut.ImplLayout(
+ aString, 0, nStrStartPos, rTextAct.GetPoint(), 0, rTextAct.GetDXArray());
+ std::unique_ptr<SalLayout> pSalLayout2 = rOut.ImplLayout(
+ aString, 0, nStrEndPos, rTextAct.GetPoint(), 0, rTextAct.GetDXArray());
+ if (pSalLayout2)
+ {
+ tools::Rectangle aBoundRect2(rOut.ImplGetTextBoundRect(*pSalLayout2));
+ aActionBounds = rOut.PixelToLogic(aBoundRect2);
+ }
+ if (pSalLayout1 && nStrStartPos > 0)
+ {
+ tools::Rectangle aBoundRect1(rOut.ImplGetTextBoundRect(*pSalLayout1));
+ aActionBounds.SetLeft(rOut.PixelToLogic(aBoundRect1).Right());
+ }
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ if (!aActionBounds.IsEmpty())
+ {
+ // fdo#40421 limit current action's output to clipped area
+ if (rOut.IsClipRegion())
+ return rOut.GetClipRegion().GetBoundRect().Intersection(aActionBounds);
+ else
+ return aActionBounds;
+ }
+ else
+ return aActionBounds;
+}
+
+} // End of anon namespace
+
+void SfxRedactionHelper::getPageMetaFilesFromDoc(std::vector<GDIMetaFile>& aMetaFiles,
+ std::vector<::Size>& aPageSizes, sal_Int32 nPages,
+ DocumentToGraphicRenderer& aRenderer)
+{
+ for (sal_Int32 nPage = 1; nPage <= nPages; ++nPage)
+ {
+ ::Size aDocumentSizePixel = aRenderer.getDocumentSizeInPixels(nPage);
+ ::Point aLogicPos;
+ ::Point aCalcPageLogicPos;
+ ::Size aCalcPageContentSize;
+ ::Size aLogic = aRenderer.getDocumentSizeIn100mm(nPage, &aLogicPos, &aCalcPageLogicPos,
+ &aCalcPageContentSize);
+
+ aPageSizes.push_back(aLogic);
+
+ Graphic aGraphic = aRenderer.renderToGraphic(nPage, aDocumentSizePixel, aDocumentSizePixel,
+ COL_TRANSPARENT, true);
+ auto& rGDIMetaFile = const_cast<GDIMetaFile&>(aGraphic.GetGDIMetaFile());
+
+ // Set preferred map unit and size on the metafile, so the Shape size
+ // will be correct in MM.
+ MapMode aMapMode;
+ aMapMode.SetMapUnit(MapUnit::Map100thMM);
+
+ rGDIMetaFile.SetPrefMapMode(aMapMode);
+ rGDIMetaFile.SetPrefSize(aLogic);
+
+ fixMetaFile(rGDIMetaFile);
+
+ aMetaFiles.push_back(rGDIMetaFile);
+ }
+}
+
+void SfxRedactionHelper::addPagesToDraw(
+ const uno::Reference<XComponent>& xComponent, sal_Int32 nPages,
+ const std::vector<GDIMetaFile>& aMetaFiles, const std::vector<::Size>& aPageSizes,
+ const PageMargins& aPageMargins,
+ const std::vector<std::pair<RedactionTarget, OUString>>& r_aTableTargets, bool bIsAutoRedact)
+{
+ // Access the draw pages
+ uno::Reference<drawing::XDrawPagesSupplier> xDrawPagesSupplier(xComponent, uno::UNO_QUERY);
+ uno::Reference<drawing::XDrawPages> xDrawPages = xDrawPagesSupplier->getDrawPages();
+
+ uno::Reference<css::lang::XMultiServiceFactory> xFactory(xComponent, uno::UNO_QUERY);
+
+ for (sal_Int32 nPage = 0; nPage < nPages; ++nPage)
+ {
+ GDIMetaFile rGDIMetaFile = aMetaFiles[nPage];
+ Graphic aGraphic(rGDIMetaFile);
+
+ sal_Int32 nPageHeight(aPageSizes[nPage].Height());
+ sal_Int32 nPageWidth(aPageSizes[nPage].Width());
+
+ uno::Reference<graphic::XGraphic> xGraph = aGraphic.GetXGraphic();
+ uno::Reference<drawing::XDrawPage> xPage = xDrawPages->insertNewByIndex(nPage);
+
+ // Set page size & margins
+ uno::Reference<beans::XPropertySet> xPageProperySet(xPage, uno::UNO_QUERY);
+ xPageProperySet->setPropertyValue("Height", css::uno::Any(nPageHeight));
+ xPageProperySet->setPropertyValue("Width", css::uno::Any(nPageWidth));
+
+ setPageMargins(xPageProperySet, aPageMargins);
+
+ // Create and insert the shape
+ uno::Reference<drawing::XShape> xShape(
+ xFactory->createInstance("com.sun.star.drawing.GraphicObjectShape"), uno::UNO_QUERY);
+ uno::Reference<beans::XPropertySet> xShapeProperySet(xShape, uno::UNO_QUERY);
+ xShapeProperySet->setPropertyValue("Graphic", uno::Any(xGraph));
+ xShapeProperySet->setPropertyValue("MoveProtect", uno::Any(true));
+ xShapeProperySet->setPropertyValue("SizeProtect", uno::Any(true));
+
+ // Set size
+ xShape->setSize(
+ awt::Size(rGDIMetaFile.GetPrefSize().Width(), rGDIMetaFile.GetPrefSize().Height()));
+
+ xPage->add(xShape);
+
+ if (bIsAutoRedact && !r_aTableTargets.empty())
+ {
+ for (const auto& targetPair : r_aTableTargets)
+ {
+ autoRedactPage(targetPair.first, rGDIMetaFile, xPage, xComponent);
+ }
+ }
+ }
+
+ // Remove the extra page at the beginning
+ uno::Reference<drawing::XDrawPage> xPage(xDrawPages->getByIndex(0), uno::UNO_QUERY_THROW);
+ xDrawPages->remove(xPage);
+}
+
+void SfxRedactionHelper::showRedactionToolbar(const SfxViewFrame* pViewFrame)
+{
+ if (!pViewFrame)
+ return;
+
+ Reference<frame::XFrame> xFrame = pViewFrame->GetFrame().GetFrameInterface();
+ Reference<css::beans::XPropertySet> xPropSet(xFrame, UNO_QUERY);
+ Reference<css::frame::XLayoutManager> xLayoutManager;
+
+ if (!xPropSet.is())
+ return;
+
+ try
+ {
+ Any aValue = xPropSet->getPropertyValue("LayoutManager");
+ aValue >>= xLayoutManager;
+ xLayoutManager->createElement("private:resource/toolbar/redactionbar");
+ xLayoutManager->showElement("private:resource/toolbar/redactionbar");
+ }
+ catch (const css::uno::RuntimeException&)
+ {
+ throw;
+ }
+ catch (css::uno::Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("sfx.doc", "Exception while trying to show the Redaction Toolbar!");
+ }
+}
+
+PageMargins
+SfxRedactionHelper::getPageMarginsForWriter(const css::uno::Reference<css::frame::XModel>& xModel)
+{
+ PageMargins aPageMargins = { -1, -1, -1, -1 };
+
+ Reference<text::XTextViewCursorSupplier> xTextViewCursorSupplier(xModel->getCurrentController(),
+ UNO_QUERY);
+ if (!xTextViewCursorSupplier.is())
+ {
+ SAL_WARN("sfx.doc", "Ref to xTextViewCursorSupplier is null in setPageMargins().");
+ return aPageMargins;
+ }
+
+ Reference<text::XPageCursor> xCursor(xTextViewCursorSupplier->getViewCursor(), UNO_QUERY);
+
+ uno::Reference<beans::XPropertySet> xPageProperySet(xCursor, UNO_QUERY);
+ OUString sPageStyleName;
+ Any aValue = xPageProperySet->getPropertyValue("PageStyleName");
+ aValue >>= sPageStyleName;
+
+ Reference<css::style::XStyleFamiliesSupplier> xStyleFamiliesSupplier(xModel, UNO_QUERY);
+ if (!xStyleFamiliesSupplier.is())
+ {
+ SAL_WARN("sfx.doc", "Ref to xStyleFamiliesSupplier is null in setPageMargins().");
+ return aPageMargins;
+ }
+ uno::Reference<container::XNameAccess> xStyleFamilies
+ = xStyleFamiliesSupplier->getStyleFamilies();
+
+ if (!xStyleFamilies.is())
+ return aPageMargins;
+
+ uno::Reference<container::XNameAccess> xPageStyles(xStyleFamilies->getByName("PageStyles"),
+ UNO_QUERY);
+
+ if (!xPageStyles.is())
+ return aPageMargins;
+
+ uno::Reference<css::style::XStyle> xPageStyle(xPageStyles->getByName(sPageStyleName),
+ UNO_QUERY);
+
+ if (!xPageStyle.is())
+ return aPageMargins;
+
+ uno::Reference<beans::XPropertySet> xPageProperties(xPageStyle, uno::UNO_QUERY);
+
+ if (!xPageProperties.is())
+ return aPageMargins;
+
+ xPageProperties->getPropertyValue("LeftMargin") >>= aPageMargins.nLeft;
+ xPageProperties->getPropertyValue("RightMargin") >>= aPageMargins.nRight;
+ xPageProperties->getPropertyValue("TopMargin") >>= aPageMargins.nTop;
+ xPageProperties->getPropertyValue("BottomMargin") >>= aPageMargins.nBottom;
+
+ return aPageMargins;
+}
+
+PageMargins
+SfxRedactionHelper::getPageMarginsForCalc(const css::uno::Reference<css::frame::XModel>& xModel)
+{
+ PageMargins aPageMargins = { -1, -1, -1, -1 };
+ OUString sPageStyleName("Default");
+
+ css::uno::Reference<css::sheet::XSpreadsheetView> xSpreadsheetView(
+ xModel->getCurrentController(), UNO_QUERY);
+
+ if (!xSpreadsheetView.is())
+ {
+ SAL_WARN("sfx.doc", "Ref to xSpreadsheetView is null in getPageMarginsForCalc().");
+ return aPageMargins;
+ }
+
+ uno::Reference<beans::XPropertySet> xSheetProperties(xSpreadsheetView->getActiveSheet(),
+ UNO_QUERY);
+
+ xSheetProperties->getPropertyValue("PageStyle") >>= sPageStyleName;
+
+ Reference<css::style::XStyleFamiliesSupplier> xStyleFamiliesSupplier(xModel, UNO_QUERY);
+ if (!xStyleFamiliesSupplier.is())
+ {
+ SAL_WARN("sfx.doc", "Ref to xStyleFamiliesSupplier is null in getPageMarginsForCalc().");
+ return aPageMargins;
+ }
+ uno::Reference<container::XNameAccess> xStyleFamilies
+ = xStyleFamiliesSupplier->getStyleFamilies();
+
+ if (!xStyleFamilies.is())
+ return aPageMargins;
+
+ uno::Reference<container::XNameAccess> xPageStyles(xStyleFamilies->getByName("PageStyles"),
+ UNO_QUERY);
+
+ if (!xPageStyles.is())
+ return aPageMargins;
+
+ uno::Reference<css::style::XStyle> xPageStyle(xPageStyles->getByName(sPageStyleName),
+ UNO_QUERY);
+
+ if (!xPageStyle.is())
+ return aPageMargins;
+
+ uno::Reference<beans::XPropertySet> xPageProperties(xPageStyle, uno::UNO_QUERY);
+
+ if (!xPageProperties.is())
+ return aPageMargins;
+
+ xPageProperties->getPropertyValue("LeftMargin") >>= aPageMargins.nLeft;
+ xPageProperties->getPropertyValue("RightMargin") >>= aPageMargins.nRight;
+ xPageProperties->getPropertyValue("TopMargin") >>= aPageMargins.nTop;
+ xPageProperties->getPropertyValue("BottomMargin") >>= aPageMargins.nBottom;
+
+ return aPageMargins;
+}
+
+void SfxRedactionHelper::searchInMetaFile(const RedactionTarget& rRedactionTarget,
+ const GDIMetaFile& rMtf,
+ std::vector<::tools::Rectangle>& aRedactionRectangles,
+ const uno::Reference<XComponent>& xComponent)
+{
+ // Initialize search
+ i18nutil::SearchOptions2 aSearchOptions;
+ fillSearchOptions(aSearchOptions, rRedactionTarget);
+
+ utl::TextSearch textSearch(aSearchOptions);
+ static tools::Long aLastFontHeight = 0;
+
+ MetaAction* pCurrAct;
+
+ for (pCurrAct = const_cast<GDIMetaFile&>(rMtf).FirstAction(); pCurrAct;
+ pCurrAct = const_cast<GDIMetaFile&>(rMtf).NextAction())
+ {
+ // Watch for TEXTARRAY actions.
+ // They contain the text of paragraphs.
+ if (pCurrAct->GetType() == MetaActionType::TEXTARRAY)
+ {
+ MetaTextArrayAction* pMetaTextArrayAction = static_cast<MetaTextArrayAction*>(pCurrAct);
+
+ // Search operation takes place here
+ OUString sText = pMetaTextArrayAction->GetText();
+ sal_Int32 nStart = 0;
+ sal_Int32 nEnd = sText.getLength();
+
+ bool bFound = textSearch.SearchForward(sText, &nStart, &nEnd);
+
+ // If found the string, add the corresponding rectangle to the collection
+ while (bFound)
+ {
+ OutputDevice* pOutputDevice
+ = SfxObjectShell::GetShellFromComponent(xComponent)->GetDocumentRefDev();
+ tools::Rectangle aNewRect(
+ ImplCalcActionBounds(*pMetaTextArrayAction, *pOutputDevice, nStart, nEnd));
+
+ if (!aNewRect.IsEmpty())
+ {
+ // Calculate the difference between current wrong value and value should it be.
+ // Add the difference to current value.
+ // Then increase 10% of the new value to make it look better.
+ aNewRect.SetTop(aNewRect.Bottom() - aLastFontHeight - aLastFontHeight / 10);
+ aRedactionRectangles.push_back(aNewRect);
+ }
+
+ // Search for the next occurrence
+ nStart = nEnd;
+ nEnd = sText.getLength();
+ bFound = textSearch.SearchForward(sText, &nStart, &nEnd);
+ }
+ }
+ else if (pCurrAct->GetType() == MetaActionType::FONT)
+ {
+ const MetaFontAction* pFontAct = static_cast<const MetaFontAction*>(pCurrAct);
+ aLastFontHeight = pFontAct->GetFont().GetFontSize().getHeight();
+ }
+ }
+}
+
+void SfxRedactionHelper::addRedactionRectToPage(
+ const uno::Reference<XComponent>& xComponent, const uno::Reference<drawing::XDrawPage>& xPage,
+ const std::vector<::tools::Rectangle>& aNewRectangles)
+{
+ if (!xComponent.is() || !xPage.is())
+ return;
+
+ if (aNewRectangles.empty())
+ return;
+
+ uno::Reference<css::lang::XMultiServiceFactory> xFactory(xComponent, uno::UNO_QUERY);
+
+ for (auto const& aNewRectangle : aNewRectangles)
+ {
+ uno::Reference<drawing::XShape> xRectShape(
+ xFactory->createInstance("com.sun.star.drawing.RectangleShape"), uno::UNO_QUERY);
+ uno::Reference<beans::XPropertySet> xRectShapeProperySet(xRectShape, uno::UNO_QUERY);
+
+ xRectShapeProperySet->setPropertyValue("Name",
+ uno::Any(OUString("RectangleRedactionShape")));
+ xRectShapeProperySet->setPropertyValue("FillTransparence",
+ css::uno::Any(static_cast<sal_Int16>(50)));
+ xRectShapeProperySet->setPropertyValue("FillColor", css::uno::Any(COL_GRAY7));
+ xRectShapeProperySet->setPropertyValue(
+ "LineStyle", css::uno::Any(css::drawing::LineStyle::LineStyle_NONE));
+
+ xRectShape->setSize(awt::Size(aNewRectangle.GetWidth(), aNewRectangle.GetHeight()));
+ xRectShape->setPosition(awt::Point(aNewRectangle.Left(), aNewRectangle.Top()));
+
+ xPage->add(xRectShape);
+ }
+}
+
+void SfxRedactionHelper::autoRedactPage(const RedactionTarget& rRedactionTarget,
+ const GDIMetaFile& rGDIMetaFile,
+ const uno::Reference<drawing::XDrawPage>& xPage,
+ const uno::Reference<XComponent>& xComponent)
+{
+ if (rRedactionTarget.sContent.isEmpty())
+ return;
+
+ // Search for the redaction strings, and get the rectangle coordinates
+ std::vector<::tools::Rectangle> aRedactionRectangles;
+ searchInMetaFile(rRedactionTarget, rGDIMetaFile, aRedactionRectangles, xComponent);
+
+ // Add the redaction rectangles to the page
+ addRedactionRectToPage(xComponent, xPage, aRedactionRectangles);
+}
+
+namespace
+{
+const LanguageTag& GetAppLanguageTag() { return Application::GetSettings().GetLanguageTag(); }
+}
+
+void SfxRedactionHelper::fillSearchOptions(i18nutil::SearchOptions2& rSearchOpt,
+ const RedactionTarget& rTarget)
+{
+ if (rTarget.sType == RedactionTargetType::REDACTION_TARGET_REGEX
+ || rTarget.sType == RedactionTargetType::REDACTION_TARGET_PREDEFINED)
+ {
+ rSearchOpt.algorithmType = util::SearchAlgorithms_REGEXP;
+ rSearchOpt.AlgorithmType2 = util::SearchAlgorithms2::REGEXP;
+ }
+ else
+ {
+ rSearchOpt.algorithmType = util::SearchAlgorithms_ABSOLUTE;
+ rSearchOpt.AlgorithmType2 = util::SearchAlgorithms2::ABSOLUTE;
+ }
+
+ rSearchOpt.Locale = GetAppLanguageTag().getLocale();
+ if (rTarget.sType == RedactionTargetType::REDACTION_TARGET_PREDEFINED)
+ {
+ auto nPredefIndex = o3tl::toUInt32(o3tl::getToken(rTarget.sContent, 0, ';'));
+ rSearchOpt.searchString = m_aPredefinedTargets[nPredefIndex];
+ }
+ else
+ rSearchOpt.searchString = rTarget.sContent;
+
+ rSearchOpt.replaceString.clear();
+
+ if (!rTarget.bCaseSensitive && rTarget.sType != RedactionTargetType::REDACTION_TARGET_REGEX
+ && rTarget.sType != RedactionTargetType::REDACTION_TARGET_PREDEFINED)
+ rSearchOpt.transliterateFlags |= TransliterationFlags::IGNORE_CASE;
+ if (rTarget.bWholeWords)
+ rSearchOpt.searchFlag |= util::SearchFlags::NORM_WORD_ONLY;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/sfx2/source/doc/autoredactdialog.cxx b/sfx2/source/doc/autoredactdialog.cxx
new file mode 100644
index 000000000..6509f8c47
--- /dev/null
+++ b/sfx2/source/doc/autoredactdialog.cxx
@@ -0,0 +1,770 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * 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 <autoredactdialog.hxx>
+
+#include <sfx2/filedlghelper.hxx>
+#include <sfx2/sfxresid.hxx>
+#include <sfx2/strings.hrc>
+
+#include <osl/file.hxx>
+#include <sal/log.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+#include <unotools/viewoptions.hxx>
+#include <o3tl/string_view.hxx>
+
+#include <com/sun/star/ui/dialogs/TemplateDescription.hpp>
+
+#include <boost/property_tree/json_parser.hpp>
+
+constexpr OUStringLiteral FILEDIALOG_FILTER_JSON = u"*.json";
+
+int TargetsTable::GetRowByTargetName(std::u16string_view sName)
+{
+ for (int i = 0, nCount = m_xControl->n_children(); i < nCount; ++i)
+ {
+ RedactionTarget* pTarget = weld::fromId<RedactionTarget*>(m_xControl->get_id(i));
+ if (pTarget->sName == sName)
+ {
+ return i;
+ }
+ }
+ return -1;
+}
+
+TargetsTable::TargetsTable(std::unique_ptr<weld::TreeView> xControl)
+ : m_xControl(std::move(xControl))
+{
+ m_xControl->set_size_request(555, 250);
+ std::vector<int> aWidths{ 100, 50, 200, 105, 105 };
+ m_xControl->set_column_fixed_widths(aWidths);
+ m_xControl->set_selection_mode(SelectionMode::Multiple);
+}
+
+namespace
+{
+OUString getTypeName(RedactionTargetType nType)
+{
+ OUString sTypeName(SfxResId(STR_REDACTION_TARGET_TYPE_UNKNOWN));
+
+ switch (nType)
+ {
+ case RedactionTargetType::REDACTION_TARGET_TEXT:
+ sTypeName = SfxResId(STR_REDACTION_TARGET_TYPE_TEXT);
+ break;
+ case RedactionTargetType::REDACTION_TARGET_REGEX:
+ sTypeName = SfxResId(STR_REDACTION_TARGET_TYPE_REGEX);
+ break;
+ case RedactionTargetType::REDACTION_TARGET_PREDEFINED:
+ sTypeName = SfxResId(STR_REDACTION_TARGET_TYPE_PREDEF);
+ break;
+ case RedactionTargetType::REDACTION_TARGET_UNKNOWN:
+ sTypeName = SfxResId(STR_REDACTION_TARGET_TYPE_UNKNOWN);
+ break;
+ }
+
+ return sTypeName;
+}
+
+/// Returns TypeID to be used in the add/edit target dialog
+OUString getTypeID(RedactionTargetType nType)
+{
+ OUString sTypeID("unknown");
+
+ switch (nType)
+ {
+ case RedactionTargetType::REDACTION_TARGET_TEXT:
+ sTypeID = "text";
+ break;
+ case RedactionTargetType::REDACTION_TARGET_REGEX:
+ sTypeID = "regex";
+ break;
+ case RedactionTargetType::REDACTION_TARGET_PREDEFINED:
+ sTypeID = "predefined";
+ break;
+ case RedactionTargetType::REDACTION_TARGET_UNKNOWN:
+ sTypeID = "unknown";
+ break;
+ }
+
+ return sTypeID;
+}
+}
+
+void TargetsTable::InsertTarget(RedactionTarget* pTarget)
+{
+ if (!pTarget)
+ {
+ SAL_WARN("sfx.doc", "pTarget is null in TargetsTable::InsertTarget()");
+ return;
+ }
+
+ // Check if the name is empty or invalid (clashing with another entry's name)
+ if (pTarget->sName.isEmpty() || GetRowByTargetName(pTarget->sName) != -1)
+ {
+ pTarget->sName = GetNameProposal();
+ }
+
+ OUString sContent = pTarget->sContent;
+
+ if (pTarget->sType == RedactionTargetType::REDACTION_TARGET_PREDEFINED)
+ {
+ //selection_num;selection_name
+ sContent = sContent.getToken(1, ';');
+ }
+
+ // Add to the end
+ int nRow = m_xControl->n_children();
+ m_xControl->append(weld::toId(pTarget), pTarget->sName);
+ m_xControl->set_text(nRow, getTypeName(pTarget->sType), 1);
+ m_xControl->set_text(nRow, sContent, 2);
+ m_xControl->set_text(
+ nRow, pTarget->bCaseSensitive ? SfxResId(STR_REDACTION_YES) : SfxResId(STR_REDACTION_NO),
+ 3);
+ m_xControl->set_text(
+ nRow, pTarget->bWholeWords ? SfxResId(STR_REDACTION_YES) : SfxResId(STR_REDACTION_NO), 4);
+}
+
+RedactionTarget* TargetsTable::GetTargetByName(std::u16string_view sName)
+{
+ int nEntry = GetRowByTargetName(sName);
+ if (nEntry == -1)
+ return nullptr;
+
+ return weld::fromId<RedactionTarget*>(m_xControl->get_id(nEntry));
+}
+
+OUString TargetsTable::GetNameProposal() const
+{
+ OUString sDefaultTargetName(SfxResId(STR_REDACTION_TARGET));
+ sal_Int32 nHighestTargetId = 0;
+ for (int i = 0, nCount = m_xControl->n_children(); i < nCount; ++i)
+ {
+ RedactionTarget* pTarget = weld::fromId<RedactionTarget*>(m_xControl->get_id(i));
+ const OUString& sName = pTarget->sName;
+ sal_Int32 nIndex = 0;
+ if (o3tl::getToken(sName, 0, ' ', nIndex) == sDefaultTargetName)
+ {
+ sal_Int32 nCurrTargetId = o3tl::toInt32(o3tl::getToken(sName, 0, ' ', nIndex));
+ nHighestTargetId = std::max<sal_Int32>(nHighestTargetId, nCurrTargetId);
+ }
+ }
+ return sDefaultTargetName + " " + OUString::number(nHighestTargetId + 1);
+}
+
+void TargetsTable::setRowData(int nRowIndex, const RedactionTarget* pTarget)
+{
+ OUString sContent = pTarget->sContent;
+
+ if (pTarget->sType == RedactionTargetType::REDACTION_TARGET_PREDEFINED)
+ {
+ //selection_num;selection_name
+ sContent = sContent.getToken(1, ';');
+ }
+
+ m_xControl->set_text(nRowIndex, pTarget->sName, 0);
+ m_xControl->set_text(nRowIndex, getTypeName(pTarget->sType), 1);
+ m_xControl->set_text(nRowIndex, sContent, 2);
+ m_xControl->set_text(
+ nRowIndex,
+ pTarget->bCaseSensitive ? SfxResId(STR_REDACTION_YES) : SfxResId(STR_REDACTION_NO), 3);
+ m_xControl->set_text(
+ nRowIndex, pTarget->bWholeWords ? SfxResId(STR_REDACTION_YES) : SfxResId(STR_REDACTION_NO),
+ 4);
+}
+
+IMPL_LINK_NOARG(SfxAutoRedactDialog, Load, weld::Button&, void)
+{
+ //Load a targets list from a previously saved file (a json file?)
+ // ask for filename, where we should load the new config data from
+ StartFileDialog(StartFileDialogType::Open, SfxResId(STR_REDACTION_LOAD_TARGETS));
+}
+
+IMPL_LINK_NOARG(SfxAutoRedactDialog, Save, weld::Button&, void)
+{
+ //Allow saving the targets into a file
+ StartFileDialog(StartFileDialogType::SaveAs, SfxResId(STR_REDACTION_SAVE_TARGETS));
+}
+
+IMPL_LINK_NOARG(SfxAutoRedactDialog, AddHdl, weld::Button&, void)
+{
+ // Open the Add Target dialog, create a new target and insert into the targets vector and the listbox
+ SfxAddTargetDialog aAddTargetDialog(getDialog(), m_xTargetsBox->GetNameProposal());
+
+ bool bIncomplete;
+ do
+ {
+ bIncomplete = false;
+
+ if (aAddTargetDialog.run() != RET_OK)
+ return;
+
+ if (aAddTargetDialog.getName().isEmpty()
+ || aAddTargetDialog.getType() == RedactionTargetType::REDACTION_TARGET_UNKNOWN
+ || aAddTargetDialog.getContent().isEmpty())
+ {
+ bIncomplete = true;
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(
+ getDialog(), VclMessageType::Warning, VclButtonsType::Ok,
+ SfxResId(STR_REDACTION_FIELDS_REQUIRED)));
+ xBox->run();
+ }
+ else if (m_xTargetsBox->GetTargetByName(aAddTargetDialog.getName()))
+ {
+ bIncomplete = true;
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(
+ getDialog(), VclMessageType::Warning, VclButtonsType::Ok,
+ SfxResId(STR_REDACTION_TARGET_NAME_CLASH)));
+ xBox->run();
+ }
+
+ } while (bIncomplete);
+
+ //Alright, we now have everything we need to construct a new target
+ RedactionTarget* redactiontarget = new RedactionTarget(
+ { aAddTargetDialog.getName(), aAddTargetDialog.getType(), aAddTargetDialog.getContent(),
+ aAddTargetDialog.isCaseSensitive(), aAddTargetDialog.isWholeWords(), 0 });
+
+ // Only the visual/display part
+ m_xTargetsBox->InsertTarget(redactiontarget);
+
+ // Actually add to the targets vector
+ if (m_xTargetsBox->GetTargetByName(redactiontarget->sName))
+ m_aTableTargets.emplace_back(redactiontarget, redactiontarget->sName);
+ else
+ {
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(
+ getDialog(), VclMessageType::Warning, VclButtonsType::Ok,
+ SfxResId(STR_REDACTION_TARGET_ADD_ERROR)));
+ xBox->run();
+ delete redactiontarget;
+ }
+}
+
+IMPL_LINK_NOARG(SfxAutoRedactDialog, EditHdl, weld::Button&, void)
+{
+ sal_Int32 nSelectedRow = m_xTargetsBox->get_selected_index();
+
+ // No selection, nothing to edit
+ if (nSelectedRow < 0)
+ return;
+
+ // Only one entry should be selected for editing
+ if (m_xTargetsBox->get_selected_rows().size() > 1)
+ {
+ //Warn the user about multiple selections
+ std::unique_ptr<weld::MessageDialog> xBox(
+ Application::CreateMessageDialog(getDialog(), VclMessageType::Error, VclButtonsType::Ok,
+ SfxResId(STR_REDACTION_MULTI_EDIT)));
+ xBox->run();
+ return;
+ }
+
+ // Get the redaction target to be edited
+ RedactionTarget* pTarget = weld::fromId<RedactionTarget*>(m_xTargetsBox->get_id(nSelectedRow));
+
+ // Construct and run the edit target dialog
+ SfxAddTargetDialog aEditTargetDialog(getDialog(), pTarget->sName, pTarget->sType,
+ pTarget->sContent, pTarget->bCaseSensitive,
+ pTarget->bWholeWords);
+
+ bool bIncomplete;
+ do
+ {
+ bIncomplete = false;
+
+ if (aEditTargetDialog.run() != RET_OK)
+ return;
+
+ if (aEditTargetDialog.getName().isEmpty()
+ || aEditTargetDialog.getType() == RedactionTargetType::REDACTION_TARGET_UNKNOWN
+ || aEditTargetDialog.getContent().isEmpty())
+ {
+ bIncomplete = true;
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(
+ getDialog(), VclMessageType::Warning, VclButtonsType::Ok,
+ SfxResId(STR_REDACTION_FIELDS_REQUIRED)));
+ xBox->run();
+ }
+ else if (aEditTargetDialog.getName() != pTarget->sName
+ && m_xTargetsBox->GetTargetByName(aEditTargetDialog.getName()))
+ {
+ bIncomplete = true;
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(
+ getDialog(), VclMessageType::Warning, VclButtonsType::Ok,
+ SfxResId(STR_REDACTION_TARGET_NAME_CLASH)));
+ xBox->run();
+ }
+
+ } while (bIncomplete);
+
+ // Update the redaction target
+ pTarget->sName = aEditTargetDialog.getName();
+ pTarget->sType = aEditTargetDialog.getType();
+ pTarget->sContent = aEditTargetDialog.getContent();
+ pTarget->bCaseSensitive = aEditTargetDialog.isCaseSensitive();
+ pTarget->bWholeWords = aEditTargetDialog.isWholeWords();
+
+ // And sync the targets box row with the actual target data
+ m_xTargetsBox->setRowData(nSelectedRow, pTarget);
+}
+IMPL_LINK_NOARG(SfxAutoRedactDialog, DoubleClickEditHdl, weld::TreeView&, bool)
+{
+ if (m_xEditBtn->get_sensitive())
+ m_xEditBtn->clicked();
+ return true;
+}
+IMPL_LINK_NOARG(SfxAutoRedactDialog, DeleteHdl, weld::Button&, void)
+{
+ std::vector<int> aSelectedRows = m_xTargetsBox->get_selected_rows();
+
+ //No selection, so nothing to delete
+ if (aSelectedRows.empty())
+ return;
+
+ if (aSelectedRows.size() > 1)
+ {
+ OUString sMsg(SfxResId(STR_REDACTION_MULTI_DELETE)
+ .replaceFirst("$(TARGETSCOUNT)", OUString::number(aSelectedRows.size())));
+ //Warn the user about multiple deletions
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(
+ getDialog(), VclMessageType::Question, VclButtonsType::OkCancel, sMsg));
+ if (xBox->run() == RET_CANCEL)
+ return;
+ }
+
+ // After each delete, the indexes of the following items decrease by one.
+ int delta = 0;
+ for (const auto& i : aSelectedRows)
+ {
+ m_aTableTargets.erase(m_aTableTargets.begin() + (i - delta));
+ m_xTargetsBox->remove(i - delta++);
+ }
+}
+
+namespace
+{
+boost::property_tree::ptree redactionTargetToJSON(const RedactionTarget* pTarget)
+{
+ boost::property_tree::ptree aNode;
+ aNode.put("sName", pTarget->sName.toUtf8().getStr());
+ aNode.put("eType", pTarget->sType);
+ aNode.put("sContent", pTarget->sContent.toUtf8().getStr());
+ aNode.put("bWholeWords", pTarget->bWholeWords);
+ aNode.put("bCaseSensitive", pTarget->bCaseSensitive);
+ aNode.put("nID", pTarget->nID);
+
+ return aNode;
+}
+
+std::unique_ptr<RedactionTarget>
+JSONtoRedactionTarget(const boost::property_tree::ptree::value_type& rValue)
+{
+ OUString sName = OUString::fromUtf8(rValue.second.get<std::string>("sName").c_str());
+ RedactionTargetType eType
+ = static_cast<RedactionTargetType>(atoi(rValue.second.get<std::string>("eType").c_str()));
+ OUString sContent = OUString::fromUtf8(rValue.second.get<std::string>("sContent").c_str());
+ bool bCaseSensitive
+ = OUString::fromUtf8(rValue.second.get<std::string>("bCaseSensitive").c_str()).toBoolean();
+ bool bWholeWords
+ = OUString::fromUtf8(rValue.second.get<std::string>("bWholeWords").c_str()).toBoolean();
+ sal_uInt32 nID = atoi(rValue.second.get<std::string>("nID").c_str());
+
+ return std::unique_ptr<RedactionTarget>(
+ new RedactionTarget{ sName, eType, sContent, bCaseSensitive, bWholeWords, nID });
+}
+}
+
+IMPL_LINK_NOARG(SfxAutoRedactDialog, LoadHdl, sfx2::FileDialogHelper*, void)
+{
+ assert(m_pFileDlg);
+
+ OUString sTargetsFile;
+ if (ERRCODE_NONE == m_pFileDlg->GetError())
+ sTargetsFile = m_pFileDlg->GetPath();
+
+ if (sTargetsFile.isEmpty())
+ return;
+
+ OUString sSysPath;
+ osl::File::getSystemPathFromFileURL(sTargetsFile, sSysPath);
+ sTargetsFile = sSysPath;
+
+ weld::WaitObject aWaitObject(getDialog());
+
+ try
+ {
+ // Create path string, and read JSON from file
+ std::string sPathStr(OUStringToOString(sTargetsFile, RTL_TEXTENCODING_UTF8).getStr());
+
+ boost::property_tree::ptree aTargetsJSON;
+
+ boost::property_tree::read_json(sPathStr, aTargetsJSON);
+
+ // Clear the dialog
+ clearTargets();
+
+ // Recreate & add the targets to the dialog
+ for (const boost::property_tree::ptree::value_type& rValue :
+ aTargetsJSON.get_child("RedactionTargets"))
+ {
+ addTarget(JSONtoRedactionTarget(rValue));
+ }
+ }
+ catch (css::uno::Exception& e)
+ {
+ SAL_WARN("sfx.doc",
+ "Exception caught while trying to load the targets JSON from file: " << e.Message);
+ return;
+ //TODO: Warn the user with a message box
+ }
+}
+
+IMPL_LINK_NOARG(SfxAutoRedactDialog, SaveHdl, sfx2::FileDialogHelper*, void)
+{
+ assert(m_pFileDlg);
+
+ OUString sTargetsFile;
+ if (ERRCODE_NONE == m_pFileDlg->GetError())
+ sTargetsFile = m_pFileDlg->GetPath();
+
+ if (sTargetsFile.isEmpty())
+ return;
+
+ OUString sSysPath;
+ osl::File::getSystemPathFromFileURL(sTargetsFile, sSysPath);
+ sTargetsFile = sSysPath;
+
+ weld::WaitObject aWaitObject(getDialog());
+
+ try
+ {
+ // Put the targets into a JSON array
+ boost::property_tree::ptree aTargetsArray;
+ for (const auto& targetPair : m_aTableTargets)
+ {
+ aTargetsArray.push_back(
+ std::make_pair("", redactionTargetToJSON(targetPair.first.get())));
+ }
+
+ // Build the JSON tree
+ boost::property_tree::ptree aTargetsTree;
+ aTargetsTree.add_child("RedactionTargets", aTargetsArray);
+
+ // Create path string, and write JSON to file
+ std::string sPathStr(OUStringToOString(sTargetsFile, RTL_TEXTENCODING_UTF8).getStr());
+
+ boost::property_tree::write_json(sPathStr, aTargetsTree);
+ }
+ catch (css::uno::Exception& e)
+ {
+ SAL_WARN("sfx.doc",
+ "Exception caught while trying to save the targets JSON to file: " << e.Message);
+ return;
+ //TODO: Warn the user with a message box
+ }
+}
+
+void SfxAutoRedactDialog::StartFileDialog(StartFileDialogType nType, const OUString& rTitle)
+{
+ OUString aFilterAllStr(SfxResId(STR_SFX_FILTERNAME_ALL));
+ OUString aFilterJsonStr(SfxResId(STR_REDACTION_JSON_FILE_FILTER));
+
+ bool bSave = nType == StartFileDialogType::SaveAs;
+ short nDialogType = bSave ? css::ui::dialogs::TemplateDescription::FILESAVE_AUTOEXTENSION
+ : css::ui::dialogs::TemplateDescription::FILEOPEN_SIMPLE;
+ m_pFileDlg.reset(new sfx2::FileDialogHelper(nDialogType, FileDialogFlags::NONE, getDialog()));
+
+ m_pFileDlg->SetTitle(rTitle);
+ m_pFileDlg->AddFilter(aFilterAllStr, FILEDIALOG_FILTER_ALL);
+ m_pFileDlg->AddFilter(aFilterJsonStr, FILEDIALOG_FILTER_JSON);
+ m_pFileDlg->SetCurrentFilter(aFilterJsonStr);
+
+ Link<sfx2::FileDialogHelper*, void> aDlgClosedLink
+ = bSave ? LINK(this, SfxAutoRedactDialog, SaveHdl)
+ : LINK(this, SfxAutoRedactDialog, LoadHdl);
+ m_pFileDlg->SetContext(sfx2::FileDialogHelper::AutoRedact);
+ m_pFileDlg->StartExecuteModal(aDlgClosedLink);
+}
+
+void SfxAutoRedactDialog::addTarget(std::unique_ptr<RedactionTarget> pTarget)
+{
+ // Only the visual/display part
+ m_xTargetsBox->InsertTarget(pTarget.get());
+
+ // Actually add to the targets vector
+ auto name = pTarget->sName;
+ if (m_xTargetsBox->GetTargetByName(name))
+ m_aTableTargets.emplace_back(std::move(pTarget), name);
+ else
+ {
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(
+ getDialog(), VclMessageType::Warning, VclButtonsType::Ok,
+ SfxResId(STR_REDACTION_TARGET_ADD_ERROR)));
+ xBox->run();
+ }
+}
+
+void SfxAutoRedactDialog::clearTargets()
+{
+ // Clear the targets box
+ m_xTargetsBox->clear();
+
+ // Clear the targets vector
+ m_aTableTargets.clear();
+}
+
+SfxAutoRedactDialog::SfxAutoRedactDialog(weld::Window* pParent)
+ : SfxDialogController(pParent, "sfx/ui/autoredactdialog.ui", "AutoRedactDialog")
+ , m_bIsValidState(true)
+ , m_bTargetsCopied(false)
+ , m_xRedactionTargetsLabel(m_xBuilder->weld_label("labelRedactionTargets"))
+ , m_xTargetsBox(new TargetsTable(m_xBuilder->weld_tree_view("targets")))
+ , m_xLoadBtn(m_xBuilder->weld_button("btnLoadTargets"))
+ , m_xSaveBtn(m_xBuilder->weld_button("btnSaveTargets"))
+ , m_xAddBtn(m_xBuilder->weld_button("add"))
+ , m_xEditBtn(m_xBuilder->weld_button("edit"))
+ , m_xDeleteBtn(m_xBuilder->weld_button("delete"))
+{
+ // Can be used to remember the last set of redaction targets?
+ OUString sExtraData;
+ SvtViewOptions aDlgOpt(EViewType::Dialog,
+ OStringToOUString(m_xDialog->get_help_id(), RTL_TEXTENCODING_UTF8));
+
+ if (aDlgOpt.Exists())
+ {
+ css::uno::Any aUserItem = aDlgOpt.GetUserItem("UserItem");
+ aUserItem >>= sExtraData;
+ }
+
+ // update the targets configuration if necessary
+ if (!sExtraData.isEmpty())
+ {
+ weld::WaitObject aWaitCursor(m_xDialog.get());
+
+ try
+ {
+ // Create path string, and read JSON from file
+ boost::property_tree::ptree aTargetsJSON;
+ std::stringstream aStream(std::string(sExtraData.toUtf8()));
+
+ boost::property_tree::read_json(aStream, aTargetsJSON);
+
+ // Recreate & add the targets to the dialog
+ for (const boost::property_tree::ptree::value_type& rValue :
+ aTargetsJSON.get_child("RedactionTargets"))
+ {
+ addTarget(JSONtoRedactionTarget(rValue));
+ }
+ }
+ catch (css::uno::Exception& e)
+ {
+ SAL_WARN("sfx.doc",
+ "Exception caught while trying to load the last dialog state: " << e.Message);
+ return;
+ //TODO: Warn the user with a message box
+ }
+ }
+
+ // Handler connections
+ m_xLoadBtn->connect_clicked(LINK(this, SfxAutoRedactDialog, Load));
+ m_xSaveBtn->connect_clicked(LINK(this, SfxAutoRedactDialog, Save));
+ m_xAddBtn->connect_clicked(LINK(this, SfxAutoRedactDialog, AddHdl));
+ m_xEditBtn->connect_clicked(LINK(this, SfxAutoRedactDialog, EditHdl));
+ m_xDeleteBtn->connect_clicked(LINK(this, SfxAutoRedactDialog, DeleteHdl));
+ m_xTargetsBox->connect_row_activated(LINK(this, SfxAutoRedactDialog, DoubleClickEditHdl));
+}
+
+SfxAutoRedactDialog::~SfxAutoRedactDialog()
+{
+ if (m_aTableTargets.empty())
+ {
+ // Clear the dialog data
+ SvtViewOptions aDlgOpt(EViewType::Dialog,
+ OStringToOUString(m_xDialog->get_help_id(), RTL_TEXTENCODING_UTF8));
+ aDlgOpt.Delete();
+ return;
+ }
+
+ try
+ {
+ // Put the targets into a JSON array
+ boost::property_tree::ptree aTargetsArray;
+ for (const auto& targetPair : m_aTableTargets)
+ {
+ aTargetsArray.push_back(
+ std::make_pair("", redactionTargetToJSON(targetPair.first.get())));
+ }
+
+ // Build the JSON tree
+ boost::property_tree::ptree aTargetsTree;
+ aTargetsTree.add_child("RedactionTargets", aTargetsArray);
+ std::stringstream aStream;
+
+ boost::property_tree::write_json(aStream, aTargetsTree, false);
+
+ OUString sUserDataStr(OUString::fromUtf8(aStream.str().c_str()));
+
+ // Store the dialog data
+ SvtViewOptions aDlgOpt(EViewType::Dialog,
+ OStringToOUString(m_xDialog->get_help_id(), RTL_TEXTENCODING_UTF8));
+ aDlgOpt.SetUserItem("UserItem", css::uno::Any(sUserDataStr));
+
+ if (!m_bTargetsCopied)
+ clearTargets();
+ }
+ catch (css::uno::Exception& e)
+ {
+ SAL_WARN("sfx.doc",
+ "Exception caught while trying to store the dialog state: " << e.Message);
+ return;
+ //TODO: Warn the user with a message box
+ }
+}
+
+bool SfxAutoRedactDialog::hasTargets() const
+{
+ //TODO: Add also some validity checks?
+ if (m_aTableTargets.empty())
+ return false;
+
+ return true;
+}
+
+bool SfxAutoRedactDialog::getTargets(std::vector<std::pair<RedactionTarget, OUString>>& r_aTargets)
+{
+ if (m_aTableTargets.empty())
+ return true;
+
+ for (auto const& rPair : m_aTableTargets)
+ r_aTargets.push_back({ *rPair.first, rPair.second });
+ m_bTargetsCopied = true;
+ return true;
+}
+
+IMPL_LINK_NOARG(SfxAddTargetDialog, SelectTypeHdl, weld::ComboBox&, void)
+{
+ if (m_xType->get_active_id() == "predefined")
+ {
+ // Hide the usual content widgets
+ // We will just set the id as content
+ // And handle with proper regex in the SfxRedactionHelper
+ m_xLabelContent->set_sensitive(false);
+ m_xLabelContent->set_visible(false);
+ m_xContent->set_sensitive(false);
+ m_xContent->set_visible(false);
+ m_xWholeWords->set_sensitive(false);
+ m_xWholeWords->set_visible(false);
+ m_xCaseSensitive->set_sensitive(false);
+ m_xCaseSensitive->set_visible(false);
+
+ // And show the predefined targets
+ m_xLabelPredefContent->set_sensitive(true);
+ m_xLabelPredefContent->set_visible(true);
+ m_xPredefContent->set_sensitive(true);
+ m_xPredefContent->set_visible(true);
+ }
+ else
+ {
+ m_xLabelPredefContent->set_sensitive(false);
+ m_xLabelPredefContent->set_visible(false);
+ m_xPredefContent->set_sensitive(false);
+ m_xPredefContent->set_visible(false);
+
+ m_xLabelContent->set_sensitive(true);
+ m_xLabelContent->set_visible(true);
+ m_xContent->set_sensitive(true);
+ m_xContent->set_visible(true);
+ m_xWholeWords->set_sensitive(true);
+ m_xWholeWords->set_visible(true);
+ m_xCaseSensitive->set_sensitive(true);
+ m_xCaseSensitive->set_visible(true);
+ }
+}
+
+SfxAddTargetDialog::SfxAddTargetDialog(weld::Window* pParent, const OUString& rName)
+ : GenericDialogController(pParent, "sfx/ui/addtargetdialog.ui", "AddTargetDialog")
+ , m_xName(m_xBuilder->weld_entry("name"))
+ , m_xType(m_xBuilder->weld_combo_box("type"))
+ , m_xLabelContent(m_xBuilder->weld_label("label_content"))
+ , m_xContent(m_xBuilder->weld_entry("content"))
+ , m_xLabelPredefContent(m_xBuilder->weld_label("label_content_predef"))
+ , m_xPredefContent(m_xBuilder->weld_combo_box("content_predef"))
+ , m_xCaseSensitive(m_xBuilder->weld_check_button("checkboxCaseSensitive"))
+ , m_xWholeWords(m_xBuilder->weld_check_button("checkboxWholeWords"))
+{
+ m_xName->set_text(rName);
+ m_xName->select_region(0, rName.getLength());
+
+ m_xType->connect_changed(LINK(this, SfxAddTargetDialog, SelectTypeHdl));
+}
+
+SfxAddTargetDialog::SfxAddTargetDialog(weld::Window* pParent, const OUString& sName,
+ const RedactionTargetType& eTargetType,
+ const OUString& sContent, bool bCaseSensitive,
+ bool bWholeWords)
+ : GenericDialogController(pParent, "sfx/ui/addtargetdialog.ui", "AddTargetDialog")
+ , m_xName(m_xBuilder->weld_entry("name"))
+ , m_xType(m_xBuilder->weld_combo_box("type"))
+ , m_xLabelContent(m_xBuilder->weld_label("label_content"))
+ , m_xContent(m_xBuilder->weld_entry("content"))
+ , m_xLabelPredefContent(m_xBuilder->weld_label("label_content_predef"))
+ , m_xPredefContent(m_xBuilder->weld_combo_box("content_predef"))
+ , m_xCaseSensitive(m_xBuilder->weld_check_button("checkboxCaseSensitive"))
+ , m_xWholeWords(m_xBuilder->weld_check_button("checkboxWholeWords"))
+{
+ m_xName->set_text(sName);
+ m_xName->select_region(0, sName.getLength());
+
+ m_xType->set_active_id(getTypeID(eTargetType));
+ m_xType->connect_changed(LINK(this, SfxAddTargetDialog, SelectTypeHdl));
+
+ if (eTargetType == RedactionTargetType::REDACTION_TARGET_PREDEFINED)
+ {
+ SelectTypeHdl(*m_xPredefContent);
+ m_xPredefContent->set_active(o3tl::toInt32(o3tl::getToken(sContent, 0, ';')));
+ }
+ else
+ {
+ m_xContent->set_text(sContent);
+ }
+
+ m_xCaseSensitive->set_active(bCaseSensitive);
+ m_xWholeWords->set_active(bWholeWords);
+
+ set_title(SfxResId(STR_REDACTION_EDIT_TARGET));
+}
+
+RedactionTargetType SfxAddTargetDialog::getType() const
+{
+ OUString sTypeID = m_xType->get_active_id();
+
+ if (sTypeID == "text")
+ return RedactionTargetType::REDACTION_TARGET_TEXT;
+ else if (sTypeID == "regex")
+ return RedactionTargetType::REDACTION_TARGET_REGEX;
+ else if (sTypeID == "predefined")
+ return RedactionTargetType::REDACTION_TARGET_PREDEFINED;
+ else
+ return RedactionTargetType::REDACTION_TARGET_UNKNOWN;
+}
+
+OUString SfxAddTargetDialog::getContent() const
+{
+ if (m_xType->get_active_id() == "predefined")
+ {
+ return OUString(OUString::number(m_xPredefContent->get_active()) + ";"
+ + m_xPredefContent->get_active_text());
+ }
+
+ return m_xContent->get_text();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/sfx2/source/doc/docfac.cxx b/sfx2/source/doc/docfac.cxx
new file mode 100644
index 000000000..a222ef7e4
--- /dev/null
+++ b/sfx2/source/doc/docfac.cxx
@@ -0,0 +1,354 @@
+/* -*- 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/container/XNameAccess.hpp>
+#include <com/sun/star/ucb/SimpleFileAccess.hpp>
+#include <com/sun/star/document/XTypeDetection.hpp>
+#include <com/sun/star/frame/ModuleManager.hpp>
+#include <com/sun/star/frame/XLoadable.hpp>
+#include <com/sun/star/frame/XStorable.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <unotools/moduleoptions.hxx>
+#include <comphelper/sequenceashashmap.hxx>
+#include <comphelper/configurationhelper.hxx>
+
+#include <sfx2/docfilt.hxx>
+#include <sfx2/docfac.hxx>
+#include <sfx2/viewfac.hxx>
+#include <sfx2/fcontnr.hxx>
+#include <sfx2/module.hxx>
+#include "syspath.hxx"
+#include <osl/file.hxx>
+#include <osl/security.hxx>
+
+#include <sal/log.hxx>
+#include <tools/debug.hxx>
+#include <tools/globname.hxx>
+
+#include <memory>
+
+using namespace ::com::sun::star;
+
+
+struct SfxObjectFactory_Impl
+{
+ std::vector<SfxViewFactory*> aViewFactoryArr;// List of <SfxViewFactory>s
+ OUString aServiceName;
+ SfxFilterContainer* pFilterContainer;
+ SfxModule* pModule;
+ SvGlobalName aClassName;
+
+ SfxObjectFactory_Impl() :
+ pFilterContainer ( nullptr ),
+ pModule ( nullptr )
+ {}
+};
+
+SfxFilterContainer* SfxObjectFactory::GetFilterContainer() const
+{
+ return pImpl->pFilterContainer;
+}
+
+SfxObjectFactory::SfxObjectFactory
+(
+ const SvGlobalName& rName,
+ const OUString& sName
+) : m_sFactoryName( sName ),
+ pImpl( new SfxObjectFactory_Impl )
+{
+ pImpl->pFilterContainer = new SfxFilterContainer( m_sFactoryName );
+ pImpl->aClassName = rName;
+}
+
+SfxObjectFactory::~SfxObjectFactory()
+{
+ delete pImpl->pFilterContainer;
+}
+
+
+void SfxObjectFactory::RegisterViewFactory
+(
+ SfxViewFactory &rFactory
+)
+{
+#if OSL_DEBUG_LEVEL > 0
+ {
+ const OUString sViewName( rFactory.GetAPIViewName() );
+ for (auto const& viewFactory : pImpl->aViewFactoryArr)
+ {
+ if ( viewFactory->GetAPIViewName() != sViewName )
+ continue;
+ SAL_WARN( "sfx", "SfxObjectFactory::RegisterViewFactory: duplicate view name: " << sViewName );
+ break;
+ }
+ }
+#endif
+ auto it = std::find_if(pImpl->aViewFactoryArr.begin(), pImpl->aViewFactoryArr.end(),
+ [&rFactory](SfxViewFactory* pFactory) { return pFactory->GetOrdinal() > rFactory.GetOrdinal(); });
+ pImpl->aViewFactoryArr.insert(it, &rFactory);
+}
+
+
+sal_uInt16 SfxObjectFactory::GetViewFactoryCount() const
+{
+ return pImpl->aViewFactoryArr.size();
+}
+
+
+SfxViewFactory& SfxObjectFactory::GetViewFactory(sal_uInt16 i) const
+{
+ return *pImpl->aViewFactoryArr[i];
+}
+
+
+SfxModule* SfxObjectFactory::GetModule() const
+{
+ return pImpl->pModule;
+}
+
+void SfxObjectFactory::SetModule_Impl( SfxModule *pMod )
+{
+ pImpl->pModule = pMod;
+}
+
+void SfxObjectFactory::SetSystemTemplate( const OUString& rServiceName, const OUString& rTemplateName )
+{
+ static const int nMaxPathSize = 16000;
+
+ const OUString sConfPath = "Office/Factories/" + rServiceName;
+ static constexpr OUStringLiteral PROP_DEF_TEMPL_CHANGED
+ = u"ooSetupFactorySystemDefaultTemplateChanged";
+
+ static const char DEF_TPL_STR[] = "/soffice.";
+
+ OUString sUserTemplateURL;
+ OUString sPath;
+ sal_Unicode aPathBuffer[nMaxPathSize];
+ if ( SystemPath::GetUserTemplateLocation( aPathBuffer, nMaxPathSize ))
+ sPath = OUString( aPathBuffer );
+ osl::FileBase::getFileURLFromSystemPath( sPath, sUserTemplateURL );
+
+ if ( sUserTemplateURL.isEmpty())
+ return;
+
+ try
+ {
+ uno::Reference< lang::XMultiServiceFactory > xFactory = ::comphelper::getProcessServiceFactory();
+ uno::Reference< uno::XInterface > xConfig = ::comphelper::ConfigurationHelper::openConfig(
+ ::comphelper::getProcessComponentContext(), "/org.openoffice.Setup", ::comphelper::EConfigurationModes::Standard );
+
+ OUString aActualFilter;
+ ::comphelper::ConfigurationHelper::readRelativeKey( xConfig, sConfPath, "ooSetupFactoryActualFilter" ) >>= aActualFilter;
+ bool bChanged(false);
+ ::comphelper::ConfigurationHelper::readRelativeKey( xConfig, sConfPath, PROP_DEF_TEMPL_CHANGED ) >>= bChanged;
+
+ uno::Reference< container::XNameAccess > xFilterFactory(
+ xFactory->createInstance( "com.sun.star.document.FilterFactory" ), uno::UNO_QUERY_THROW );
+ uno::Reference< container::XNameAccess > xTypeDetection(
+ xFactory->createInstance( "com.sun.star.document.TypeDetection" ), uno::UNO_QUERY_THROW );
+
+ OUString aActualFilterTypeName;
+ uno::Sequence< beans::PropertyValue > aActuralFilterData;
+ xFilterFactory->getByName( aActualFilter ) >>= aActuralFilterData;
+ for ( const auto& rProp : std::as_const(aActuralFilterData) )
+ if ( rProp.Name == "Type" )
+ rProp.Value >>= aActualFilterTypeName;
+ ::comphelper::SequenceAsHashMap aProps1( xTypeDetection->getByName( aActualFilterTypeName ) );
+ uno::Sequence< OUString > aAllExt =
+ aProps1.getUnpackedValueOrDefault("Extensions", uno::Sequence< OUString >() );
+ //To-do: check if aAllExt is empty first
+ const OUString aExt = DEF_TPL_STR + aAllExt[0];
+
+ sUserTemplateURL += aExt;
+
+ uno::Reference<ucb::XSimpleFileAccess3> xSimpleFileAccess(
+ ucb::SimpleFileAccess::create( ::comphelper::getComponentContext(xFactory) ) );
+
+ OUString aBackupURL;
+ ::osl::Security().getConfigDir(aBackupURL);
+ aBackupURL += "/temp";
+
+ if ( !xSimpleFileAccess->exists( aBackupURL ) )
+ xSimpleFileAccess->createFolder( aBackupURL );
+
+ aBackupURL += aExt;
+
+ if ( !rTemplateName.isEmpty() )
+ {
+ if ( xSimpleFileAccess->exists( sUserTemplateURL ) && !bChanged )
+ xSimpleFileAccess->copy( sUserTemplateURL, aBackupURL );
+
+ uno::Reference< document::XTypeDetection > xTypeDetector( xTypeDetection, uno::UNO_QUERY );
+ ::comphelper::SequenceAsHashMap aProps2( xTypeDetection->getByName( xTypeDetector->queryTypeByURL( rTemplateName ) ) );
+ OUString aFilterName =
+ aProps2.getUnpackedValueOrDefault("PreferredFilter", OUString() );
+
+ uno::Sequence< beans::PropertyValue > aArgs{
+ comphelper::makePropertyValue("FilterName", aFilterName),
+ comphelper::makePropertyValue("AsTemplate", true),
+ comphelper::makePropertyValue("URL", rTemplateName)
+ };
+
+ uno::Reference< frame::XLoadable > xLoadable( xFactory->createInstance( rServiceName ), uno::UNO_QUERY );
+ xLoadable->load( aArgs );
+
+ aArgs.realloc( 2 );
+ auto pArgs = aArgs.getArray();
+ pArgs[1].Name = "Overwrite";
+ pArgs[1].Value <<= true;
+
+ uno::Reference< frame::XStorable > xStorable( xLoadable, uno::UNO_QUERY );
+ xStorable->storeToURL( sUserTemplateURL, aArgs );
+ ::comphelper::ConfigurationHelper::writeRelativeKey( xConfig, sConfPath, PROP_DEF_TEMPL_CHANGED, uno::Any( true ));
+ ::comphelper::ConfigurationHelper::flush( xConfig );
+ }
+ else
+ {
+ DBG_ASSERT( bChanged, "invalid ooSetupFactorySystemDefaultTemplateChanged value!" );
+
+ xSimpleFileAccess->copy( aBackupURL, sUserTemplateURL );
+ xSimpleFileAccess->kill( aBackupURL );
+ ::comphelper::ConfigurationHelper::writeRelativeKey( xConfig, sConfPath, PROP_DEF_TEMPL_CHANGED, uno::Any( false ));
+ ::comphelper::ConfigurationHelper::flush( xConfig );
+ }
+ }
+ catch(const uno::Exception&)
+ {
+ }
+}
+
+void SfxObjectFactory::SetStandardTemplate( const OUString& rServiceName, const OUString& rTemplate )
+{
+ SvtModuleOptions::EFactory eFac = SvtModuleOptions::ClassifyFactoryByServiceName(rServiceName);
+ if (eFac == SvtModuleOptions::EFactory::UNKNOWN_FACTORY)
+ eFac = SvtModuleOptions::ClassifyFactoryByShortName(rServiceName);
+ if (eFac != SvtModuleOptions::EFactory::UNKNOWN_FACTORY)
+ {
+ SetSystemTemplate( rServiceName, rTemplate );
+ SvtModuleOptions().SetFactoryStandardTemplate(eFac, rTemplate);
+ }
+}
+
+OUString SfxObjectFactory::GetStandardTemplate( std::u16string_view rServiceName )
+{
+ SvtModuleOptions::EFactory eFac = SvtModuleOptions::ClassifyFactoryByServiceName(rServiceName);
+ if (eFac == SvtModuleOptions::EFactory::UNKNOWN_FACTORY)
+ eFac = SvtModuleOptions::ClassifyFactoryByShortName(rServiceName);
+
+ if (eFac != SvtModuleOptions::EFactory::UNKNOWN_FACTORY)
+ return SvtModuleOptions().GetFactoryStandardTemplate(eFac);
+
+ return OUString();
+}
+
+std::shared_ptr<const SfxFilter> SfxObjectFactory::GetTemplateFilter() const
+{
+ sal_uInt16 nVersion=0;
+ SfxFilterMatcher aMatcher ( m_sFactoryName );
+ SfxFilterMatcherIter aIter( aMatcher );
+ std::shared_ptr<const SfxFilter> pFilter;
+ std::shared_ptr<const SfxFilter> pTemp = aIter.First();
+ while ( pTemp )
+ {
+ if( pTemp->IsOwnFormat() && pTemp->IsOwnTemplateFormat() && ( pTemp->GetVersion() > nVersion ) )
+ {
+ pFilter = pTemp;
+ nVersion = static_cast<sal_uInt16>(pTemp->GetVersion());
+ }
+
+ pTemp = aIter.Next();
+ }
+
+ return pFilter;
+}
+
+void SfxObjectFactory::SetDocumentServiceName( const OUString& rServiceName )
+{
+ pImpl->aServiceName = rServiceName;
+}
+
+const OUString& SfxObjectFactory::GetDocumentServiceName() const
+{
+ return pImpl->aServiceName;
+}
+
+const SvGlobalName& SfxObjectFactory::GetClassId() const
+{
+ return pImpl->aClassName;
+}
+
+OUString SfxObjectFactory::GetFactoryURL() const
+{
+ return "private:factory/" + m_sFactoryName;
+}
+
+OUString SfxObjectFactory::GetModuleName() const
+{
+ try
+ {
+ css::uno::Reference< css::uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext();
+
+ css::uno::Reference< css::frame::XModuleManager2 > xModuleManager(
+ css::frame::ModuleManager::create(xContext));
+
+ ::comphelper::SequenceAsHashMap aPropSet( xModuleManager->getByName(GetDocumentServiceName()) );
+ return aPropSet.getUnpackedValueOrDefault("ooSetupFactoryUIName", OUString());
+ }
+ catch(const css::uno::RuntimeException&)
+ {
+ throw;
+ }
+ catch(const css::uno::Exception&)
+ {
+ }
+
+ return OUString();
+}
+
+
+sal_uInt16 SfxObjectFactory::GetViewNo_Impl( const SfxInterfaceId i_nViewId, const sal_uInt16 i_nFallback ) const
+{
+ for ( sal_uInt16 curViewNo = 0; curViewNo < GetViewFactoryCount(); ++curViewNo )
+ {
+ const SfxInterfaceId curViewId = GetViewFactory( curViewNo ).GetOrdinal();
+ if ( i_nViewId == curViewId )
+ return curViewNo;
+ }
+ return i_nFallback;
+}
+
+SfxViewFactory* SfxObjectFactory::GetViewFactoryByViewName( std::u16string_view i_rViewName ) const
+{
+ for ( sal_uInt16 nViewNo = 0;
+ nViewNo < GetViewFactoryCount();
+ ++nViewNo
+ )
+ {
+ SfxViewFactory& rViewFac( GetViewFactory( nViewNo ) );
+ if ( ( rViewFac.GetAPIViewName() == i_rViewName )
+ || ( rViewFac.GetLegacyViewName() == i_rViewName )
+ )
+ return &rViewFac;
+ }
+ return nullptr;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/doc/docfile.cxx b/sfx2/source/doc/docfile.cxx
new file mode 100644
index 000000000..fafe05d15
--- /dev/null
+++ b/sfx2/source/doc/docfile.cxx
@@ -0,0 +1,4752 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * 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 <config_features.h>
+
+#ifdef UNX
+#include <sys/stat.h>
+#endif
+
+#include <sfx2/docfile.hxx>
+#include <sfx2/signaturestate.hxx>
+
+#include <com/sun/star/task/InteractionHandler.hpp>
+#include <com/sun/star/task/XStatusIndicator.hpp>
+#include <com/sun/star/uno/Reference.h>
+#include <com/sun/star/ucb/XContent.hpp>
+#include <com/sun/star/container/XChild.hpp>
+#include <com/sun/star/document/XDocumentRevisionListPersistence.hpp>
+#include <com/sun/star/document/LockedDocumentRequest.hpp>
+#include <com/sun/star/document/LockedOnSavingRequest.hpp>
+#include <com/sun/star/document/OwnLockOnDocumentRequest.hpp>
+#include <com/sun/star/document/LockFileIgnoreRequest.hpp>
+#include <com/sun/star/document/LockFileCorruptRequest.hpp>
+#include <com/sun/star/document/ChangedByOthersRequest.hpp>
+#include <com/sun/star/document/ReloadEditableRequest.hpp>
+#include <com/sun/star/embed/XTransactedObject.hpp>
+#include <com/sun/star/embed/ElementModes.hpp>
+#include <com/sun/star/embed/UseBackupException.hpp>
+#include <com/sun/star/embed/XOptimizedStorage.hpp>
+#include <com/sun/star/frame/Desktop.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/frame/XTerminateListener.hpp>
+#include <com/sun/star/graphic/XGraphic.hpp>
+#include <com/sun/star/ucb/ContentCreationException.hpp>
+#include <com/sun/star/ucb/InteractiveIOException.hpp>
+#include <com/sun/star/ucb/CommandFailedException.hpp>
+#include <com/sun/star/ucb/CommandAbortedException.hpp>
+#include <com/sun/star/ucb/InteractiveLockingLockedException.hpp>
+#include <com/sun/star/ucb/InteractiveNetworkWriteException.hpp>
+#include <com/sun/star/ucb/Lock.hpp>
+#include <com/sun/star/ucb/NameClashException.hpp>
+#include <com/sun/star/ucb/XCommandEnvironment.hpp>
+#include <com/sun/star/ucb/XProgressHandler.hpp>
+#include <com/sun/star/io/XOutputStream.hpp>
+#include <com/sun/star/io/XInputStream.hpp>
+#include <com/sun/star/io/XTruncate.hpp>
+#include <com/sun/star/io/XSeekable.hpp>
+#include <com/sun/star/lang/XSingleServiceFactory.hpp>
+#include <com/sun/star/ucb/InsertCommandArgument.hpp>
+#include <com/sun/star/ucb/NameClash.hpp>
+#include <com/sun/star/beans/NamedValue.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/security/DocumentDigitalSignatures.hpp>
+#include <com/sun/star/security/XCertificate.hpp>
+#include <tools/urlobj.hxx>
+#include <tools/fileutil.hxx>
+#include <unotools/configmgr.hxx>
+#include <unotools/tempfile.hxx>
+#include <comphelper/fileurl.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <comphelper/interaction.hxx>
+#include <comphelper/sequence.hxx>
+#include <comphelper/simplefileaccessinteraction.hxx>
+#include <comphelper/string.hxx>
+#include <framework/interaction.hxx>
+#include <utility>
+#include <svl/stritem.hxx>
+#include <svl/eitem.hxx>
+#include <svtools/sfxecode.hxx>
+#include <svl/itemset.hxx>
+#include <svl/intitem.hxx>
+#include <svtools/svparser.hxx>
+#include <sal/log.hxx>
+
+#include <unotools/streamwrap.hxx>
+
+#include <osl/file.hxx>
+
+#include <comphelper/storagehelper.hxx>
+#include <unotools/mediadescriptor.hxx>
+#include <comphelper/docpasswordhelper.hxx>
+#include <tools/datetime.hxx>
+#include <unotools/pathoptions.hxx>
+#include <svtools/asynclink.hxx>
+#include <ucbhelper/commandenvironment.hxx>
+#include <unotools/ucbstreamhelper.hxx>
+#include <unotools/ucbhelper.hxx>
+#include <unotools/progresshandlerwrap.hxx>
+#include <ucbhelper/content.hxx>
+#include <ucbhelper/interactionrequest.hxx>
+#include <sot/storage.hxx>
+#include <svl/documentlockfile.hxx>
+#include <svl/msodocumentlockfile.hxx>
+#include <com/sun/star/document/DocumentRevisionListPersistence.hpp>
+
+#include <sfx2/app.hxx>
+#include <sfx2/frame.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/fcontnr.hxx>
+#include <sfx2/docfilt.hxx>
+#include <sfx2/sfxsids.hrc>
+#include <sfx2/sfxuno.hxx>
+#include <openflag.hxx>
+#include <officecfg/Office/Common.hxx>
+#include <comphelper/propertysequence.hxx>
+#include <vcl/weld.hxx>
+#include <vcl/svapp.hxx>
+#include <tools/diagnose_ex.h>
+#include <unotools/fltrcfg.hxx>
+#include <sfx2/digitalsignatures.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <comphelper/threadpool.hxx>
+#include <o3tl/string_view.hxx>
+#include <condition_variable>
+
+#include <com/sun/star/io/WrongFormatException.hpp>
+
+#include <memory>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::graphic;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::ucb;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::io;
+using namespace ::com::sun::star::security;
+
+namespace
+{
+
+struct ReadOnlyMediumEntry
+{
+ ReadOnlyMediumEntry(std::shared_ptr<std::recursive_mutex> pMutex,
+ std::shared_ptr<bool> pIsDestructed)
+ : _pMutex(pMutex)
+ , _pIsDestructed(pIsDestructed)
+ {
+ }
+ std::shared_ptr<std::recursive_mutex> _pMutex;
+ std::shared_ptr<bool> _pIsDestructed;
+};
+
+}
+
+static std::mutex g_chkReadOnlyGlobalMutex;
+static bool g_bChkReadOnlyTaskRunning = false;
+static std::unordered_map<SfxMedium*, std::shared_ptr<ReadOnlyMediumEntry>> g_newReadOnlyDocs;
+static std::unordered_map<SfxMedium*, std::shared_ptr<ReadOnlyMediumEntry>> g_existingReadOnlyDocs;
+
+namespace {
+
+#if HAVE_FEATURE_MULTIUSER_ENVIRONMENT
+
+bool IsSystemFileLockingUsed()
+{
+#if HAVE_FEATURE_MACOSX_SANDBOX
+ return true;
+#else
+ return officecfg::Office::Common::Misc::UseDocumentSystemFileLocking::get();
+#endif
+}
+
+
+bool IsOOoLockFileUsed()
+{
+#if HAVE_FEATURE_MACOSX_SANDBOX
+ return false;
+#else
+ return officecfg::Office::Common::Misc::UseDocumentOOoLockFile::get();
+#endif
+}
+
+bool IsLockingUsed()
+{
+ return officecfg::Office::Common::Misc::UseLocking::get();
+}
+
+#endif
+
+#if HAVE_FEATURE_MULTIUSER_ENVIRONMENT
+bool IsWebDAVLockingUsed()
+{
+ return officecfg::Office::Common::Misc::UseWebDAVFileLocking::get();
+}
+#endif
+
+/// Gets default attributes of a file:// URL.
+sal_uInt64 GetDefaultFileAttributes(const OUString& rURL)
+{
+ sal_uInt64 nRet = 0;
+
+ if (!comphelper::isFileUrl(rURL))
+ return nRet;
+
+ // Make sure the file exists (and create it if not).
+ osl::File aFile(rURL);
+ osl::File::RC nRes = aFile.open(osl_File_OpenFlag_Create);
+ if (nRes != osl::File::E_None && nRes != osl::File::E_EXIST)
+ return nRet;
+
+ aFile.close();
+
+ osl::DirectoryItem aItem;
+ if (osl::DirectoryItem::get(rURL, aItem) != osl::DirectoryItem::E_None)
+ return nRet;
+
+ osl::FileStatus aStatus(osl_FileStatus_Mask_Attributes);
+ if (aItem.getFileStatus(aStatus) != osl::DirectoryItem::E_None)
+ return nRet;
+
+ nRet = aStatus.getAttributes();
+ return nRet;
+}
+
+/// Determines if rURL is safe to move or not.
+bool IsFileMovable(const INetURLObject& rURL)
+{
+#ifdef MACOSX
+ (void)rURL;
+ // Hide extension macOS-specific file property would be lost.
+ return false;
+#else
+
+ if (rURL.GetProtocol() != INetProtocol::File)
+ // Not a file:// URL.
+ return false;
+
+#ifdef UNX
+ OUString sPath = rURL.getFSysPath(FSysStyle::Unix);
+ if (sPath.isEmpty())
+ return false;
+
+ struct stat buf;
+ if (lstat(sPath.toUtf8().getStr(), &buf) != 0)
+ return false;
+
+ // Hardlink or symlink: osl::File::move() doesn't play with these nicely.
+ if (buf.st_nlink > 1 || S_ISLNK(buf.st_mode))
+ return false;
+#elif defined _WIN32
+ if (tools::IsMappedWebDAVPath(rURL.GetMainURL(INetURLObject::DecodeMechanism::NONE)))
+ return false;
+#endif
+
+ return true;
+#endif
+}
+
+class CheckReadOnlyTaskTerminateListener
+ : public ::cppu::WeakImplHelper<css::frame::XTerminateListener>
+{
+public:
+ // XEventListener
+ void SAL_CALL disposing(const css::lang::EventObject& Source) override;
+
+ // XTerminateListener
+ void SAL_CALL queryTermination(const css::lang::EventObject& aEvent) override;
+ void SAL_CALL notifyTermination(const css::lang::EventObject& aEvent) override;
+
+ bool bIsTerminated = false;
+ std::condition_variable mCond;
+ std::mutex mMutex;
+};
+
+class CheckReadOnlyTask : public comphelper::ThreadTask
+{
+public:
+ CheckReadOnlyTask(const std::shared_ptr<comphelper::ThreadTaskTag>& pTag);
+ ~CheckReadOnlyTask();
+
+ virtual void doWork() override;
+
+private:
+ rtl::Reference<CheckReadOnlyTaskTerminateListener> m_xListener;
+};
+
+} // anonymous namespace
+
+CheckReadOnlyTask::CheckReadOnlyTask(const std::shared_ptr<comphelper::ThreadTaskTag>& pTag)
+ : ThreadTask(pTag)
+ , m_xListener(new CheckReadOnlyTaskTerminateListener)
+{
+ Reference<css::frame::XDesktop> xDesktop
+ = css::frame::Desktop::create(comphelper::getProcessComponentContext());
+ if (xDesktop.is() && m_xListener != nullptr)
+ {
+ xDesktop->addTerminateListener(m_xListener);
+ }
+}
+
+CheckReadOnlyTask::~CheckReadOnlyTask()
+{
+ Reference<css::frame::XDesktop> xDesktop
+ = css::frame::Desktop::create(comphelper::getProcessComponentContext());
+ if (xDesktop.is() && m_xListener != nullptr)
+ {
+ std::unique_lock<std::mutex> lock(m_xListener->mMutex);
+ if (!m_xListener->bIsTerminated)
+ {
+ lock.unlock();
+ xDesktop->removeTerminateListener(m_xListener);
+ }
+ }
+}
+
+namespace
+{
+void SAL_CALL
+CheckReadOnlyTaskTerminateListener::disposing(const css::lang::EventObject& /*Source*/)
+{
+}
+
+void SAL_CALL
+CheckReadOnlyTaskTerminateListener::queryTermination(const css::lang::EventObject& /*aEvent*/)
+{
+}
+
+void SAL_CALL
+CheckReadOnlyTaskTerminateListener::notifyTermination(const css::lang::EventObject& /*aEvent*/)
+{
+ std::unique_lock<std::mutex> lock(mMutex);
+ bIsTerminated = true;
+ lock.unlock();
+ mCond.notify_one();
+}
+}
+
+class SfxMedium_Impl
+{
+public:
+ StreamMode m_nStorOpenMode;
+ ErrCode m_eError;
+
+ ::ucbhelper::Content aContent;
+ bool bUpdatePickList:1;
+ bool bIsTemp:1;
+ bool bDownloadDone:1;
+ bool bIsStorage:1;
+ bool bUseInteractionHandler:1;
+ bool bAllowDefaultIntHdl:1;
+ bool bDisposeStorage:1;
+ bool bStorageBasedOnInStream:1;
+ bool m_bSalvageMode:1;
+ bool m_bVersionsAlreadyLoaded:1;
+ bool m_bLocked:1;
+ bool m_bMSOLockFileCreated : 1;
+ bool m_bDisableUnlockWebDAV:1;
+ bool m_bGotDateTime:1;
+ bool m_bRemoveBackup:1;
+ bool m_bOriginallyReadOnly:1;
+ bool m_bOriginallyLoadedReadOnly:1;
+ bool m_bTriedStorage:1;
+ bool m_bRemote:1;
+ bool m_bInputStreamIsReadOnly:1;
+ bool m_bInCheckIn:1;
+ bool m_bDisableFileSync = false;
+ bool m_bNotifyWhenEditable = false;
+
+ OUString m_aName;
+ OUString m_aLogicName;
+ OUString m_aLongName;
+
+ mutable std::shared_ptr<SfxItemSet> m_pSet;
+ mutable std::unique_ptr<INetURLObject> m_pURLObj;
+
+ std::shared_ptr<const SfxFilter> m_pFilter;
+ std::shared_ptr<const SfxFilter> m_pCustomFilter;
+
+ std::shared_ptr<std::recursive_mutex> m_pCheckEditableWorkerMutex;
+ std::shared_ptr<bool> m_pIsDestructed;
+ ImplSVEvent* m_pReloadEvent;
+
+ std::unique_ptr<SvStream> m_pInStream;
+ std::unique_ptr<SvStream> m_pOutStream;
+
+ OUString aOrigURL;
+ DateTime aExpireTime;
+ SfxFrameWeakRef wLoadTargetFrame;
+ SvKeyValueIteratorRef xAttributes;
+
+ svtools::AsynchronLink aDoneLink;
+
+ uno::Sequence < util::RevisionTag > aVersions;
+
+ std::unique_ptr<::utl::TempFile> pTempFile;
+
+ uno::Reference<embed::XStorage> xStorage;
+ uno::Reference<embed::XStorage> m_xZipStorage;
+ uno::Reference<io::XInputStream> m_xInputStreamToLoadFrom;
+ uno::Reference<io::XInputStream> xInputStream;
+ uno::Reference<io::XStream> xStream;
+ uno::Reference<io::XStream> m_xLockingStream;
+ uno::Reference<task::XInteractionHandler> xInteraction;
+
+ ErrCode nLastStorageError;
+
+ OUString m_aBackupURL;
+
+ // the following member is changed and makes sense only during saving
+ // TODO/LATER: in future the signature state should be controlled by the medium not by the document
+ // in this case the member will hold this information
+ SignatureState m_nSignatureState;
+
+ bool m_bHasEmbeddedObjects = false;
+
+ util::DateTime m_aDateTime;
+
+ uno::Sequence<beans::PropertyValue> m_aArgs;
+
+ explicit SfxMedium_Impl();
+ ~SfxMedium_Impl();
+ SfxMedium_Impl(const SfxMedium_Impl&) = delete;
+ SfxMedium_Impl& operator=(const SfxMedium_Impl&) = delete;
+
+ OUString getFilterMimeType() const
+ { return !m_pFilter ? OUString() : m_pFilter->GetMimeType(); }
+};
+
+SfxMedium_Impl::SfxMedium_Impl() :
+ m_nStorOpenMode(SFX_STREAM_READWRITE),
+ m_eError(ERRCODE_NONE),
+ bUpdatePickList(true),
+ bIsTemp( false ),
+ bDownloadDone( true ),
+ bIsStorage( false ),
+ bUseInteractionHandler( true ),
+ bAllowDefaultIntHdl( false ),
+ bDisposeStorage( false ),
+ bStorageBasedOnInStream( false ),
+ m_bSalvageMode( false ),
+ m_bVersionsAlreadyLoaded( false ),
+ m_bLocked( false ),
+ m_bMSOLockFileCreated( false ),
+ m_bDisableUnlockWebDAV( false ),
+ m_bGotDateTime( false ),
+ m_bRemoveBackup( false ),
+ m_bOriginallyReadOnly(false),
+ m_bOriginallyLoadedReadOnly(false),
+ m_bTriedStorage(false),
+ m_bRemote(false),
+ m_bInputStreamIsReadOnly(false),
+ m_bInCheckIn(false),
+ m_pReloadEvent(nullptr),
+ aExpireTime( DateTime( DateTime::SYSTEM ) + static_cast<sal_Int32>(10) ),
+ nLastStorageError( ERRCODE_NONE ),
+ m_nSignatureState( SignatureState::NOSIGNATURES )
+{
+}
+
+
+SfxMedium_Impl::~SfxMedium_Impl()
+{
+ aDoneLink.ClearPendingCall();
+
+ pTempFile.reset();
+ m_pSet.reset();
+ std::unique_lock<std::recursive_mutex> chkEditLock;
+ if (m_pCheckEditableWorkerMutex != nullptr)
+ chkEditLock = std::unique_lock<std::recursive_mutex>(*m_pCheckEditableWorkerMutex);
+ m_pURLObj.reset();
+}
+
+void SfxMedium::ResetError()
+{
+ pImpl->m_eError = ERRCODE_NONE;
+ if( pImpl->m_pInStream )
+ pImpl->m_pInStream->ResetError();
+ if( pImpl->m_pOutStream )
+ pImpl->m_pOutStream->ResetError();
+}
+
+ErrCode const & SfxMedium::GetLastStorageCreationState() const
+{
+ return pImpl->nLastStorageError;
+}
+
+void SfxMedium::SetError(ErrCode nError)
+{
+ pImpl->m_eError = nError;
+}
+
+ErrCode SfxMedium::GetErrorCode() const
+{
+ ErrCode lError = pImpl->m_eError;
+ if(!lError && pImpl->m_pInStream)
+ lError = pImpl->m_pInStream->GetErrorCode();
+ if(!lError && pImpl->m_pOutStream)
+ lError = pImpl->m_pOutStream->GetErrorCode();
+ return lError;
+}
+
+void SfxMedium::CheckFileDate( const util::DateTime& aInitDate )
+{
+ GetInitFileDate( true );
+ if ( pImpl->m_aDateTime.Seconds == aInitDate.Seconds
+ && pImpl->m_aDateTime.Minutes == aInitDate.Minutes
+ && pImpl->m_aDateTime.Hours == aInitDate.Hours
+ && pImpl->m_aDateTime.Day == aInitDate.Day
+ && pImpl->m_aDateTime.Month == aInitDate.Month
+ && pImpl->m_aDateTime.Year == aInitDate.Year )
+ return;
+
+ uno::Reference< task::XInteractionHandler > xHandler = GetInteractionHandler();
+
+ if ( !xHandler.is() )
+ return;
+
+ try
+ {
+ ::rtl::Reference< ::ucbhelper::InteractionRequest > xInteractionRequestImpl = new ::ucbhelper::InteractionRequest( uno::Any(
+ document::ChangedByOthersRequest() ) );
+ uno::Sequence< uno::Reference< task::XInteractionContinuation > > aContinuations{
+ new ::ucbhelper::InteractionAbort( xInteractionRequestImpl.get() ),
+ new ::ucbhelper::InteractionApprove( xInteractionRequestImpl.get() )
+ };
+ xInteractionRequestImpl->setContinuations( aContinuations );
+
+ xHandler->handle( xInteractionRequestImpl );
+
+ ::rtl::Reference< ::ucbhelper::InteractionContinuation > xSelected = xInteractionRequestImpl->getSelection();
+ if ( uno::Reference< task::XInteractionAbort >( xSelected.get(), uno::UNO_QUERY ).is() )
+ {
+ SetError(ERRCODE_ABORT);
+ }
+ }
+ catch ( const uno::Exception& )
+ {}
+}
+
+bool SfxMedium::DocNeedsFileDateCheck() const
+{
+ return ( !IsReadOnly() && ( GetURLObject().GetProtocol() == INetProtocol::File ||
+ GetURLObject().isAnyKnownWebDAVScheme() ) );
+}
+
+util::DateTime const & SfxMedium::GetInitFileDate( bool bIgnoreOldValue )
+{
+ if ( ( bIgnoreOldValue || !pImpl->m_bGotDateTime ) && !pImpl->m_aLogicName.isEmpty() )
+ {
+ try
+ {
+ // add a default css::ucb::XCommandEnvironment
+ // in order to have the WebDAV UCP provider manage http/https authentication correctly
+ ::ucbhelper::Content aContent( GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE ),
+ utl::UCBContentHelper::getDefaultCommandEnvironment(),
+ comphelper::getProcessComponentContext() );
+
+ aContent.getPropertyValue("DateModified") >>= pImpl->m_aDateTime;
+ pImpl->m_bGotDateTime = true;
+ }
+ catch ( const css::uno::Exception& )
+ {
+ }
+ }
+
+ return pImpl->m_aDateTime;
+}
+
+
+Reference < XContent > SfxMedium::GetContent() const
+{
+ if ( !pImpl->aContent.get().is() )
+ {
+ Reference < css::ucb::XContent > xContent;
+
+ // tdf#95144 add a default css::ucb::XCommandEnvironment
+ // in order to have the WebDAV UCP provider manage https protocol certificates correctly
+ css:: uno::Reference< task::XInteractionHandler > xIH(
+ css::task::InteractionHandler::createWithParent( comphelper::getProcessComponentContext(), nullptr ) );
+
+ css::uno::Reference< css::ucb::XProgressHandler > xProgress;
+ rtl::Reference<::ucbhelper::CommandEnvironment> pCommandEnv = new ::ucbhelper::CommandEnvironment( new comphelper::SimpleFileAccessInteraction( xIH ), xProgress );
+
+ const SfxUnoAnyItem* pItem = SfxItemSet::GetItem<SfxUnoAnyItem>(pImpl->m_pSet.get(), SID_CONTENT, false);
+ if ( pItem )
+ pItem->GetValue() >>= xContent;
+
+ if ( xContent.is() )
+ {
+ try
+ {
+ pImpl->aContent = ::ucbhelper::Content( xContent, pCommandEnv, comphelper::getProcessComponentContext() );
+ }
+ catch ( const Exception& )
+ {
+ }
+ }
+ else
+ {
+ // TODO: SAL_WARN( "sfx.doc", "SfxMedium::GetContent()\nCreate Content? This code exists as fallback only. Please clarify, why it's used.");
+ OUString aURL;
+ if ( !pImpl->m_aName.isEmpty() )
+ osl::FileBase::getFileURLFromSystemPath( pImpl->m_aName, aURL );
+ else if ( !pImpl->m_aLogicName.isEmpty() )
+ aURL = GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE );
+ if (!aURL.isEmpty() )
+ (void)::ucbhelper::Content::create( aURL, pCommandEnv, comphelper::getProcessComponentContext(), pImpl->aContent );
+ }
+ }
+
+ return pImpl->aContent.get();
+}
+
+OUString SfxMedium::GetBaseURL( bool bForSaving )
+{
+ OUString aBaseURL;
+ const SfxStringItem* pBaseURLItem = GetItemSet()->GetItem<SfxStringItem>(SID_DOC_BASEURL);
+ if ( pBaseURLItem )
+ aBaseURL = pBaseURLItem->GetValue();
+ else if (!utl::ConfigManager::IsFuzzing() && GetContent().is())
+ {
+ try
+ {
+ Any aAny = pImpl->aContent.getPropertyValue("BaseURI");
+ aAny >>= aBaseURL;
+ }
+ catch ( const css::uno::Exception& )
+ {
+ }
+
+ if ( aBaseURL.isEmpty() )
+ aBaseURL = GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE );
+ }
+
+ if ( bForSaving )
+ {
+ bool bIsRemote = IsRemote();
+ if( (bIsRemote && !officecfg::Office::Common::Save::URL::Internet::get())
+ || (!pImpl->m_bRemote && !officecfg::Office::Common::Save::URL::FileSystem::get()) )
+ return OUString();
+ }
+
+ return aBaseURL;
+}
+
+bool SfxMedium::IsSkipImages() const
+{
+ const SfxStringItem* pSkipImagesItem = GetItemSet()->GetItem<SfxStringItem>(SID_FILE_FILTEROPTIONS);
+ return pSkipImagesItem && pSkipImagesItem->GetValue() == "SkipImages";
+}
+
+SvStream* SfxMedium::GetInStream()
+{
+ if ( pImpl->m_pInStream )
+ return pImpl->m_pInStream.get();
+
+ if ( pImpl->pTempFile )
+ {
+ pImpl->m_pInStream.reset( new SvFileStream(pImpl->m_aName, pImpl->m_nStorOpenMode) );
+
+ pImpl->m_eError = pImpl->m_pInStream->GetError();
+
+ if (!pImpl->m_eError && (pImpl->m_nStorOpenMode & StreamMode::WRITE)
+ && ! pImpl->m_pInStream->IsWritable() )
+ {
+ pImpl->m_eError = ERRCODE_IO_ACCESSDENIED;
+ pImpl->m_pInStream.reset();
+ }
+ else
+ return pImpl->m_pInStream.get();
+ }
+
+ GetMedium_Impl();
+
+ if ( GetError() )
+ return nullptr;
+
+ return pImpl->m_pInStream.get();
+}
+
+
+void SfxMedium::CloseInStream()
+{
+ CloseInStream_Impl();
+}
+
+void SfxMedium::CloseInStream_Impl(bool bInDestruction)
+{
+ // if there is a storage based on the InStream, we have to
+ // close the storage, too, because otherwise the storage
+ // would use an invalid ( deleted ) stream.
+ if ( pImpl->m_pInStream && pImpl->xStorage.is() )
+ {
+ if ( pImpl->bStorageBasedOnInStream )
+ CloseStorage();
+ }
+
+ if ( pImpl->m_pInStream && !GetContent().is() && !bInDestruction )
+ {
+ CreateTempFile();
+ return;
+ }
+
+ pImpl->m_pInStream.reset();
+ if ( pImpl->m_pSet )
+ pImpl->m_pSet->ClearItem( SID_INPUTSTREAM );
+
+ CloseZipStorage_Impl();
+ pImpl->xInputStream.clear();
+
+ if ( !pImpl->m_pOutStream )
+ {
+ // output part of the stream is not used so the whole stream can be closed
+ // TODO/LATER: is it correct?
+ pImpl->xStream.clear();
+ if ( pImpl->m_pSet )
+ pImpl->m_pSet->ClearItem( SID_STREAM );
+ }
+}
+
+
+SvStream* SfxMedium::GetOutStream()
+{
+ if ( !pImpl->m_pOutStream )
+ {
+ // Create a temp. file if there is none because we always
+ // need one.
+ CreateTempFile( false );
+
+ if ( pImpl->pTempFile )
+ {
+ // On windows we try to re-use XOutStream from xStream if that exists;
+ // because opening new SvFileStream in this situation may fail with ERROR_SHARING_VIOLATION
+ // TODO: this is a horrible hack that should probably be removed,
+ // somebody needs to investigate this more thoroughly...
+ if (getenv("SFX_MEDIUM_REUSE_STREAM") && pImpl->xStream.is())
+ {
+ assert(pImpl->xStream->getOutputStream().is()); // need that...
+ pImpl->m_pOutStream = utl::UcbStreamHelper::CreateStream(
+ pImpl->xStream, false);
+ }
+ else
+ {
+ // On Unix don't try to re-use XOutStream from xStream if that exists;
+ // it causes fdo#59022 (fails opening files via SMB on Linux)
+ pImpl->m_pOutStream.reset( new SvFileStream(
+ pImpl->m_aName, StreamMode::STD_READWRITE) );
+ }
+ CloseStorage();
+ }
+ }
+
+ return pImpl->m_pOutStream.get();
+}
+
+
+void SfxMedium::CloseOutStream()
+{
+ CloseOutStream_Impl();
+}
+
+void SfxMedium::CloseOutStream_Impl()
+{
+ if ( pImpl->m_pOutStream )
+ {
+ // if there is a storage based on the OutStream, we have to
+ // close the storage, too, because otherwise the storage
+ // would use an invalid ( deleted ) stream.
+ //TODO/MBA: how to deal with this?!
+ //maybe we need a new flag when the storage was created from the outstream
+ if ( pImpl->xStorage.is() )
+ {
+ CloseStorage();
+ }
+
+ pImpl->m_pOutStream.reset();
+ }
+
+ if ( !pImpl->m_pInStream )
+ {
+ // input part of the stream is not used so the whole stream can be closed
+ // TODO/LATER: is it correct?
+ pImpl->xStream.clear();
+ if ( pImpl->m_pSet )
+ pImpl->m_pSet->ClearItem( SID_STREAM );
+ }
+}
+
+
+const OUString& SfxMedium::GetPhysicalName() const
+{
+ if ( pImpl->m_aName.isEmpty() && !pImpl->m_aLogicName.isEmpty() )
+ const_cast<SfxMedium*>(this)->CreateFileStream();
+
+ // return the name then
+ return pImpl->m_aName;
+}
+
+
+void SfxMedium::CreateFileStream()
+{
+ // force synchron
+ if( pImpl->m_pInStream )
+ {
+ SvLockBytes* pBytes = pImpl->m_pInStream->GetLockBytes();
+ if( pBytes )
+ pBytes->SetSynchronMode();
+ }
+
+ GetInStream();
+ if( pImpl->m_pInStream )
+ {
+ CreateTempFile( false );
+ pImpl->bIsTemp = true;
+ CloseInStream_Impl();
+ }
+}
+
+
+bool SfxMedium::Commit()
+{
+ if( pImpl->xStorage.is() )
+ StorageCommit_Impl();
+ else if( pImpl->m_pOutStream )
+ pImpl->m_pOutStream->FlushBuffer();
+ else if( pImpl->m_pInStream )
+ pImpl->m_pInStream->FlushBuffer();
+
+ if ( GetError() == ERRCODE_NONE )
+ {
+ // does something only in case there is a temporary file ( means aName points to different location than aLogicName )
+ Transfer_Impl();
+ }
+
+ bool bResult = ( GetError() == ERRCODE_NONE );
+
+ if ( bResult && DocNeedsFileDateCheck() )
+ GetInitFileDate( true );
+
+ // remove truncation mode from the flags
+ pImpl->m_nStorOpenMode &= ~StreamMode::TRUNC;
+ return bResult;
+}
+
+
+bool SfxMedium::IsStorage()
+{
+ if ( pImpl->xStorage.is() )
+ return true;
+
+ if ( pImpl->m_bTriedStorage )
+ return pImpl->bIsStorage;
+
+ if ( pImpl->pTempFile )
+ {
+ OUString aURL;
+ if ( osl::FileBase::getFileURLFromSystemPath( pImpl->m_aName, aURL )
+ != osl::FileBase::E_None )
+ {
+ SAL_WARN( "sfx.doc", "Physical name '" << pImpl->m_aName << "' not convertible to file URL");
+ }
+ pImpl->bIsStorage = SotStorage::IsStorageFile( aURL ) && !SotStorage::IsOLEStorage( aURL);
+ if ( !pImpl->bIsStorage )
+ pImpl->m_bTriedStorage = true;
+ }
+ else if ( GetInStream() )
+ {
+ pImpl->bIsStorage = SotStorage::IsStorageFile( pImpl->m_pInStream.get() ) && !SotStorage::IsOLEStorage( pImpl->m_pInStream.get() );
+ if ( !pImpl->m_pInStream->GetError() && !pImpl->bIsStorage )
+ pImpl->m_bTriedStorage = true;
+ }
+
+ return pImpl->bIsStorage;
+}
+
+
+bool SfxMedium::IsPreview_Impl() const
+{
+ bool bPreview = false;
+ const SfxBoolItem* pPreview = SfxItemSet::GetItem<SfxBoolItem>(GetItemSet(), SID_PREVIEW, false);
+ if ( pPreview )
+ bPreview = pPreview->GetValue();
+ else
+ {
+ const SfxStringItem* pFlags = SfxItemSet::GetItem<SfxStringItem>(GetItemSet(), SID_OPTIONS, false);
+ if ( pFlags )
+ {
+ OUString aFileFlags = pFlags->GetValue();
+ aFileFlags = aFileFlags.toAsciiUpperCase();
+ if ( -1 != aFileFlags.indexOf( 'B' ) )
+ bPreview = true;
+ }
+ }
+
+ return bPreview;
+}
+
+
+void SfxMedium::StorageBackup_Impl()
+{
+ ::ucbhelper::Content aOriginalContent;
+ Reference< css::ucb::XCommandEnvironment > xDummyEnv;
+
+ bool bBasedOnOriginalFile =
+ !pImpl->pTempFile
+ && ( pImpl->m_aLogicName.isEmpty() || !pImpl->m_bSalvageMode )
+ && !GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE ).isEmpty()
+ && GetURLObject().GetProtocol() == INetProtocol::File
+ && ::utl::UCBContentHelper::IsDocument( GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
+
+ if ( bBasedOnOriginalFile && pImpl->m_aBackupURL.isEmpty()
+ && ::ucbhelper::Content::create( GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE ), xDummyEnv, comphelper::getProcessComponentContext(), aOriginalContent ) )
+ {
+ DoInternalBackup_Impl( aOriginalContent );
+ if( pImpl->m_aBackupURL.isEmpty() )
+ SetError(ERRCODE_SFX_CANTCREATEBACKUP);
+ }
+}
+
+
+OUString const & SfxMedium::GetBackup_Impl()
+{
+ if ( pImpl->m_aBackupURL.isEmpty() )
+ StorageBackup_Impl();
+
+ return pImpl->m_aBackupURL;
+}
+
+
+uno::Reference < embed::XStorage > SfxMedium::GetOutputStorage()
+{
+ if ( GetError() )
+ return uno::Reference< embed::XStorage >();
+
+ // if the medium was constructed with a Storage: use this one, not a temp. storage
+ // if a temporary storage already exists: use it
+ if ( pImpl->xStorage.is() && ( pImpl->m_aLogicName.isEmpty() || pImpl->pTempFile ) )
+ return pImpl->xStorage;
+
+ // if necessary close stream that was used for reading
+ if ( pImpl->m_pInStream && !pImpl->m_pInStream->IsWritable() )
+ CloseInStream();
+
+ DBG_ASSERT( !pImpl->m_pOutStream, "OutStream in a readonly Medium?!" );
+
+ // TODO/LATER: The current solution is to store the document temporary and then copy it to the target location;
+ // in future it should be stored directly and then copied to the temporary location, since in this case no
+ // file attributes have to be preserved and system copying mechanics could be used instead of streaming.
+ CreateTempFileNoCopy();
+
+ return GetStorage();
+}
+
+
+void SfxMedium::SetEncryptionDataToStorage_Impl()
+{
+ // in case media-descriptor contains password it should be used on opening
+ if ( !pImpl->xStorage.is() || !pImpl->m_pSet )
+ return;
+
+ uno::Sequence< beans::NamedValue > aEncryptionData;
+ if ( !GetEncryptionData_Impl( pImpl->m_pSet.get(), aEncryptionData ) )
+ return;
+
+ // replace the password with encryption data
+ pImpl->m_pSet->ClearItem( SID_PASSWORD );
+ pImpl->m_pSet->Put( SfxUnoAnyItem( SID_ENCRYPTIONDATA, uno::Any( aEncryptionData ) ) );
+
+ try
+ {
+ ::comphelper::OStorageHelper::SetCommonStorageEncryptionData( pImpl->xStorage, aEncryptionData );
+ }
+ catch( const uno::Exception& )
+ {
+ SAL_WARN( "sfx.doc", "It must be possible to set a common password for the storage" );
+ // TODO/LATER: set the error code in case of problem
+ // SetError(ERRCODE_IO_GENERAL);
+ }
+}
+
+#if HAVE_FEATURE_MULTIUSER_ENVIRONMENT
+
+// FIXME: Hmm actually lock files should be used for sftp: documents
+// even if !HAVE_FEATURE_MULTIUSER_ENVIRONMENT. Only the use of lock
+// files for *local* documents is unnecessary in that case. But
+// actually, the checks for sftp: here are just wishful thinking; I
+// don't this there is any support for actually editing documents
+// behind sftp: URLs anyway.
+
+// Sure, there could perhaps be a 3rd-party extension that brings UCB
+// the potential to handle files behind sftp:. But there could also be
+// an extension that handles some arbitrary foobar: scheme *and* it
+// could be that lock files would be the correct thing to use for
+// foobar: documents, too. But the hardcoded test below won't know
+// that. Clearly the knowledge whether lock files should be used or
+// not for some URL scheme belongs in UCB, not here.
+
+namespace
+{
+
+OUString tryMSOwnerFiles(std::u16string_view sDocURL)
+{
+ svt::MSODocumentLockFile aMSOLockFile(sDocURL);
+ LockFileEntry aData;
+ try
+ {
+ aData = aMSOLockFile.GetLockData();
+ }
+ catch( const uno::Exception& )
+ {
+ return OUString();
+ }
+
+ OUString sUserData = aData[LockFileComponent::OOOUSERNAME];
+
+ if (!sUserData.isEmpty())
+ sUserData += " (MS Office)"; // Mention the used office suite
+
+ return sUserData;
+}
+
+OUString tryForeignLockfiles(std::u16string_view sDocURL)
+{
+ OUString sUserData = tryMSOwnerFiles(sDocURL);
+ // here we can test for empty result, and add other known applications' lockfile testing
+ return sUserData.trim();
+}
+}
+
+SfxMedium::ShowLockResult SfxMedium::ShowLockedDocumentDialog(const LockFileEntry& aData,
+ bool bIsLoading, bool bOwnLock,
+ bool bHandleSysLocked)
+{
+ ShowLockResult nResult = ShowLockResult::NoLock;
+
+ // tdf#92817: Simple check for empty lock file that needs to be deleted, when system locking is enabled
+ if( aData[LockFileComponent::OOOUSERNAME].isEmpty() && aData[LockFileComponent::SYSUSERNAME].isEmpty() && !bHandleSysLocked )
+ bOwnLock=true;
+
+ // show the interaction regarding the document opening
+ uno::Reference< task::XInteractionHandler > xHandler = GetInteractionHandler();
+
+ if ( xHandler.is() && ( bIsLoading || !bHandleSysLocked || bOwnLock ) )
+ {
+ OUString aDocumentURL
+ = GetURLObject().GetLastName(INetURLObject::DecodeMechanism::WithCharset);
+ OUString aInfo;
+ ::rtl::Reference< ::ucbhelper::InteractionRequest > xInteractionRequestImpl;
+
+ sal_Int32 nContinuations = 3;
+
+ if ( bOwnLock )
+ {
+ aInfo = aData[LockFileComponent::EDITTIME];
+
+ xInteractionRequestImpl = new ::ucbhelper::InteractionRequest( uno::Any(
+ document::OwnLockOnDocumentRequest( OUString(), uno::Reference< uno::XInterface >(), aDocumentURL, aInfo, !bIsLoading ) ) );
+ }
+ else
+ {
+ // Use a fourth continuation in case there's no filesystem lock:
+ // "Ignore lock file and open/replace the document"
+ if (!bHandleSysLocked)
+ nContinuations = 4;
+
+ if ( !aData[LockFileComponent::OOOUSERNAME].isEmpty() )
+ aInfo = aData[LockFileComponent::OOOUSERNAME];
+ else
+ aInfo = aData[LockFileComponent::SYSUSERNAME];
+
+ if (aInfo.isEmpty() && !GetURLObject().isAnyKnownWebDAVScheme())
+ // Try to get name of user who has locked the file using other applications
+ aInfo = tryForeignLockfiles(
+ GetURLObject().GetMainURL(INetURLObject::DecodeMechanism::NONE));
+
+ if ( !aInfo.isEmpty() && !aData[LockFileComponent::EDITTIME].isEmpty() )
+ aInfo += " ( " + aData[LockFileComponent::EDITTIME] + " )";
+
+ if (!bIsLoading) // so, !bHandleSysLocked
+ {
+ xInteractionRequestImpl = new ::ucbhelper::InteractionRequest(uno::Any(
+ document::LockedOnSavingRequest(OUString(), uno::Reference< uno::XInterface >(), aDocumentURL, aInfo)));
+ // Currently, only the last "Retry" continuation (meaning ignore the lock and try overwriting) can be returned.
+ }
+ else /*logically therefore bIsLoading is set */
+ {
+ xInteractionRequestImpl = new ::ucbhelper::InteractionRequest( uno::Any(
+ document::LockedDocumentRequest( OUString(), uno::Reference< uno::XInterface >(), aDocumentURL, aInfo ) ) );
+ }
+ }
+
+ uno::Sequence< uno::Reference< task::XInteractionContinuation > > aContinuations(nContinuations);
+ auto pContinuations = aContinuations.getArray();
+ pContinuations[0] = new ::ucbhelper::InteractionAbort( xInteractionRequestImpl.get() );
+ pContinuations[1] = new ::ucbhelper::InteractionApprove( xInteractionRequestImpl.get() );
+ pContinuations[2] = new ::ucbhelper::InteractionDisapprove( xInteractionRequestImpl.get() );
+ if (nContinuations > 3)
+ {
+ // We use InteractionRetry to reflect that user wants to
+ // ignore the (stale?) alien lock file and open/overwrite the document
+ pContinuations[3] = new ::ucbhelper::InteractionRetry(xInteractionRequestImpl.get());
+ }
+ xInteractionRequestImpl->setContinuations( aContinuations );
+
+ xHandler->handle( xInteractionRequestImpl );
+
+ bool bOpenReadOnly = false;
+ ::rtl::Reference< ::ucbhelper::InteractionContinuation > xSelected = xInteractionRequestImpl->getSelection();
+ if ( uno::Reference< task::XInteractionAbort >( xSelected.get(), uno::UNO_QUERY ).is() )
+ {
+ SetError(ERRCODE_ABORT);
+ }
+ else if ( uno::Reference< task::XInteractionDisapprove >( xSelected.get(), uno::UNO_QUERY ).is() )
+ {
+ // own lock on loading, user has selected to ignore the lock
+ // own lock on saving, user has selected to ignore the lock
+ // alien lock on loading, user has selected to edit a copy of document
+ // TODO/LATER: alien lock on saving, user has selected to do SaveAs to different location
+ if ( !bOwnLock ) // bIsLoading implied from outermost condition
+ {
+ // means that a copy of the document should be opened
+ GetItemSet()->Put( SfxBoolItem( SID_TEMPLATE, true ) );
+ }
+ else
+ nResult = ShowLockResult::Succeeded;
+ }
+ else if (uno::Reference< task::XInteractionRetry >(xSelected.get(), uno::UNO_QUERY).is())
+ {
+ // User decided to ignore the alien (stale?) lock file without filesystem lock
+ nResult = ShowLockResult::Succeeded;
+ }
+ else if (uno::Reference< task::XInteractionApprove >( xSelected.get(), uno::UNO_QUERY ).is())
+ {
+ bOpenReadOnly = true;
+ }
+ else // user selected "Notify"
+ {
+ pImpl->m_bNotifyWhenEditable = true;
+ AddToCheckEditableWorkerList();
+ bOpenReadOnly = true;
+ }
+
+ if (bOpenReadOnly)
+ {
+ // own lock on loading, user has selected to open readonly
+ // own lock on saving, user has selected to open readonly
+ // alien lock on loading, user has selected to retry saving
+ // TODO/LATER: alien lock on saving, user has selected to retry saving
+
+ if (bIsLoading)
+ GetItemSet()->Put(SfxBoolItem(SID_DOC_READONLY, true));
+ else
+ nResult = ShowLockResult::Try;
+ }
+ }
+ else
+ {
+ if ( bIsLoading )
+ {
+ // if no interaction handler is provided the default answer is open readonly
+ // that usually happens in case the document is loaded per API
+ // so the document must be opened readonly for backward compatibility
+ GetItemSet()->Put( SfxBoolItem( SID_DOC_READONLY, true ) );
+ }
+ else
+ SetError(ERRCODE_IO_ACCESSDENIED);
+
+ }
+
+ return nResult;
+}
+
+bool SfxMedium::ShowLockFileProblemDialog(MessageDlg nWhichDlg)
+{
+ // system file locking is not active, ask user whether he wants to open the document without any locking
+ uno::Reference< task::XInteractionHandler > xHandler = GetInteractionHandler();
+
+ if (xHandler.is())
+ {
+ ::rtl::Reference< ::ucbhelper::InteractionRequest > xIgnoreRequestImpl;
+
+ switch (nWhichDlg)
+ {
+ case MessageDlg::LockFileIgnore:
+ xIgnoreRequestImpl = new ::ucbhelper::InteractionRequest(uno::Any( document::LockFileIgnoreRequest() ));
+ break;
+ case MessageDlg::LockFileCorrupt:
+ xIgnoreRequestImpl = new ::ucbhelper::InteractionRequest(uno::Any( document::LockFileCorruptRequest() ));
+ break;
+ }
+
+ uno::Sequence< uno::Reference< task::XInteractionContinuation > > aContinuations{
+ new ::ucbhelper::InteractionAbort(xIgnoreRequestImpl.get()),
+ new ::ucbhelper::InteractionApprove(xIgnoreRequestImpl.get())
+ };
+ xIgnoreRequestImpl->setContinuations(aContinuations);
+
+ xHandler->handle(xIgnoreRequestImpl);
+
+ ::rtl::Reference< ::ucbhelper::InteractionContinuation > xSelected = xIgnoreRequestImpl->getSelection();
+ bool bReadOnly = true;
+
+ if (uno::Reference<task::XInteractionAbort>(xSelected.get(), uno::UNO_QUERY).is())
+ {
+ SetError(ERRCODE_ABORT);
+ bReadOnly = false;
+ }
+ else if (!uno::Reference<task::XInteractionApprove>(xSelected.get(), uno::UNO_QUERY).is())
+ {
+ // user selected "Notify"
+ pImpl->m_bNotifyWhenEditable = true;
+ AddToCheckEditableWorkerList();
+ }
+
+ if (bReadOnly)
+ GetItemSet()->Put(SfxBoolItem(SID_DOC_READONLY, true));
+
+ return bReadOnly;
+ }
+
+ return false;
+}
+
+namespace
+{
+ bool isSuitableProtocolForLocking(const OUString & rLogicName)
+ {
+ INetURLObject aUrl( rLogicName );
+ INetProtocol eProt = aUrl.GetProtocol();
+#if !HAVE_FEATURE_MACOSX_SANDBOX
+ if (eProt == INetProtocol::File) {
+ return true;
+ }
+#endif
+ return eProt == INetProtocol::Smb || eProt == INetProtocol::Sftp;
+ }
+}
+
+#endif // HAVE_FEATURE_MULTIUSER_ENVIRONMENT
+
+// sets SID_DOC_READONLY if the document cannot be opened for editing
+// if user cancel the loading the ERROR_ABORT is set
+SfxMedium::LockFileResult SfxMedium::LockOrigFileOnDemand(bool bLoading, bool bNoUI,
+ bool bTryIgnoreLockFile,
+ LockFileEntry* pLockData)
+{
+#if !HAVE_FEATURE_MULTIUSER_ENVIRONMENT
+ (void) bLoading;
+ (void) bNoUI;
+ (void) bTryIgnoreLockFile;
+ (void) pLockData;
+ return LockFileResult::Succeeded;
+#else
+ LockFileResult eResult = LockFileResult::Failed;
+
+ // check if path scheme is http:// or https://
+ // may be this is better if used always, in Android and iOS as well?
+ // if this code should be always there, remember to move the relevant code in UnlockFile method as well !
+
+ if ( GetURLObject().isAnyKnownWebDAVScheme() )
+ {
+ // do nothing if WebDAV locking is disabled
+ if (!IsWebDAVLockingUsed())
+ return LockFileResult::Succeeded;
+
+ {
+ bool bResult = pImpl->m_bLocked;
+ bool bIsTemplate = false;
+ // so, this is webdav stuff...
+ if ( !bResult )
+ {
+ // no read-write access is necessary on loading if the document is explicitly opened as copy
+ const SfxBoolItem* pTemplateItem = SfxItemSet::GetItem<SfxBoolItem>(GetItemSet(), SID_TEMPLATE, false);
+ bIsTemplate = ( bLoading && pTemplateItem && pTemplateItem->GetValue() );
+ }
+
+ if ( !bIsTemplate && !bResult && !IsReadOnly() )
+ {
+ ShowLockResult bUIStatus = ShowLockResult::NoLock;
+ do
+ {
+ if( !bResult )
+ {
+ uno::Reference< task::XInteractionHandler > xCHandler = GetInteractionHandler( true );
+ Reference< css::ucb::XCommandEnvironment > xComEnv = new ::ucbhelper::CommandEnvironment(
+ xCHandler, Reference< css::ucb::XProgressHandler >() );
+
+ ucbhelper::Content aContentToLock(
+ GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE ),
+ xComEnv, comphelper::getProcessComponentContext() );
+
+ try
+ {
+ aContentToLock.lock();
+ bResult = true;
+ }
+ catch ( ucb::InteractiveLockingLockedException& )
+ {
+ // received when the resource is already locked
+ if (!bNoUI || pLockData)
+ {
+ // get the lock owner, using a special ucb.webdav property
+ // the owner property retrieved here is what the other principal send the server
+ // when activating the lock.
+ // See http://tools.ietf.org/html/rfc4918#section-14.17 for details
+ LockFileEntry aLockData;
+ aLockData[LockFileComponent::OOOUSERNAME] = "Unknown user";
+ // This solution works right when the LO user name and the WebDAV user
+ // name are the same.
+ // A better thing to do would be to obtain the 'real' WebDAV user name,
+ // but that's not possible from a WebDAV UCP provider client.
+ LockFileEntry aOwnData = svt::LockFileCommon::GenerateOwnEntry();
+ // use the current LO user name as the system name
+ aLockData[LockFileComponent::SYSUSERNAME]
+ = aOwnData[LockFileComponent::SYSUSERNAME];
+
+ uno::Sequence<css::ucb::Lock> aLocks;
+ // getting the property, send a PROPFIND to the server over the net
+ if ((aContentToLock.getPropertyValue("DAV:lockdiscovery") >>= aLocks) && aLocks.hasElements())
+ {
+ // got at least a lock, show the owner of the first lock returned
+ css::ucb::Lock aLock = aLocks[0];
+ OUString aOwner;
+ if (aLock.Owner >>= aOwner)
+ {
+ // we need to display the WebDAV user name owning the lock, not the local one
+ aLockData[LockFileComponent::OOOUSERNAME] = aOwner;
+ }
+ }
+
+ if (!bNoUI)
+ {
+ bUIStatus = ShowLockedDocumentDialog(aLockData, bLoading, false,
+ true);
+ }
+
+ if (pLockData)
+ {
+ std::copy(aLockData.begin(), aLockData.end(), pLockData->begin());
+ }
+ }
+ }
+ catch( ucb::InteractiveNetworkWriteException& )
+ {
+ // This catch it's not really needed, here just for the sake of documentation on the behaviour.
+ // This is the most likely reason:
+ // - the remote site is a WebDAV with special configuration: read/only for read operations
+ // and read/write for write operations, the user is not allowed to lock/write and
+ // she cancelled the credentials request.
+ // this is not actually an error, but the exception is sent directly from ucb, avoiding the automatic
+ // management that takes part in cancelCommandExecution()
+ // Unfortunately there is no InteractiveNetwork*Exception available to signal this more correctly
+ // since it mostly happens on read/only part of webdav, this can be the most correct
+ // exception available
+ }
+ catch( uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "sfx.doc", "Locking exception: WebDAV while trying to lock the file" );
+ }
+ }
+ } while( !bResult && bUIStatus == ShowLockResult::Try );
+ }
+
+ pImpl->m_bLocked = bResult;
+
+ if ( !bResult && GetError() == ERRCODE_NONE )
+ {
+ // the error should be set in case it is storing process
+ // or the document has been opened for editing explicitly
+ const SfxBoolItem* pReadOnlyItem = SfxItemSet::GetItem<SfxBoolItem>(pImpl->m_pSet.get(), SID_DOC_READONLY, false);
+
+ if ( !bLoading || (pReadOnlyItem && !pReadOnlyItem->GetValue()) )
+ SetError(ERRCODE_IO_ACCESSDENIED);
+ else
+ GetItemSet()->Put( SfxBoolItem( SID_DOC_READONLY, true ) );
+ }
+
+ // when the file is locked, get the current file date
+ if ( bResult && DocNeedsFileDateCheck() )
+ GetInitFileDate( true );
+
+ if ( bResult )
+ eResult = LockFileResult::Succeeded;
+ }
+ return eResult;
+ }
+
+ if (!IsLockingUsed())
+ return LockFileResult::Succeeded;
+ if (GetURLObject().HasError())
+ return eResult;
+
+ try
+ {
+ if ( pImpl->m_bLocked && bLoading
+ && GetURLObject().GetProtocol() == INetProtocol::File )
+ {
+ // if the document is already locked the system locking might be temporarily off after storing
+ // check whether the system file locking should be taken again
+ GetLockingStream_Impl();
+ }
+
+ bool bResult = pImpl->m_bLocked;
+
+ if ( !bResult )
+ {
+ // no read-write access is necessary on loading if the document is explicitly opened as copy
+ const SfxBoolItem* pTemplateItem = SfxItemSet::GetItem<SfxBoolItem>(GetItemSet(), SID_TEMPLATE, false);
+ bResult = ( bLoading && pTemplateItem && pTemplateItem->GetValue() );
+ }
+
+ if ( !bResult && !IsReadOnly() )
+ {
+ bool bContentReadonly = false;
+ if ( bLoading && GetURLObject().GetProtocol() == INetProtocol::File )
+ {
+ // let the original document be opened to check the possibility to open it for editing
+ // and to let the writable stream stay open to hold the lock on the document
+ GetLockingStream_Impl();
+ }
+
+ // "IsReadOnly" property does not allow to detect whether the file is readonly always
+ // so we try always to open the file for editing
+ // the file is readonly only in case the read-write stream can not be opened
+ if ( bLoading && !pImpl->m_xLockingStream.is() )
+ {
+ try
+ {
+ // MediaDescriptor does this check also, the duplication should be avoided in future
+ Reference< css::ucb::XCommandEnvironment > xDummyEnv;
+ ::ucbhelper::Content aContent( GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE ), xDummyEnv, comphelper::getProcessComponentContext() );
+ aContent.getPropertyValue("IsReadOnly") >>= bContentReadonly;
+ }
+ catch( const uno::Exception& ) {}
+ }
+
+ // do further checks only if the file not readonly in fs
+ if ( !bContentReadonly )
+ {
+ // the special file locking should be used only for suitable URLs
+ if ( isSuitableProtocolForLocking( pImpl->m_aLogicName ) )
+ {
+
+ // in case of storing the document should request the output before locking
+ if ( bLoading )
+ {
+ // let the stream be opened to check the system file locking
+ GetMedium_Impl();
+ if (GetError() != ERRCODE_NONE) {
+ return eResult;
+ }
+ }
+
+ ShowLockResult bUIStatus = ShowLockResult::NoLock;
+
+ // check whether system file locking has been used, the default value is false
+ bool bUseSystemLock = comphelper::isFileUrl( pImpl->m_aLogicName ) && IsSystemFileLockingUsed();
+
+ // TODO/LATER: This implementation does not allow to detect the system lock on saving here, actually this is no big problem
+ // if system lock is used the writeable stream should be available
+ bool bHandleSysLocked = ( bLoading && bUseSystemLock && !pImpl->xStream.is() && !pImpl->m_pOutStream );
+
+ // The file is attempted to get locked for the duration of lockfile creation on save
+ std::unique_ptr<osl::File> pFileLock;
+ if (!bLoading && bUseSystemLock && pImpl->pTempFile)
+ {
+ INetURLObject aDest(GetURLObject());
+ OUString aDestURL(aDest.GetMainURL(INetURLObject::DecodeMechanism::NONE));
+
+ if (comphelper::isFileUrl(aDestURL) || !aDest.removeSegment())
+ {
+ pFileLock = std::make_unique<osl::File>(aDestURL);
+ auto rc = pFileLock->open(osl_File_OpenFlag_Write);
+ if (rc == osl::FileBase::E_ACCES)
+ bHandleSysLocked = true;
+ }
+ }
+
+ do
+ {
+ try
+ {
+ ::svt::DocumentLockFile aLockFile( pImpl->m_aLogicName );
+
+ std::unique_ptr<svt::MSODocumentLockFile> pMSOLockFile;
+ const SvtFilterOptions& rOpt = SvtFilterOptions::Get();
+ if (rOpt.IsMSOLockFileCreationIsEnabled() && svt::MSODocumentLockFile::IsMSOSupportedFileFormat(pImpl->m_aLogicName))
+ {
+ pMSOLockFile.reset(new svt::MSODocumentLockFile(pImpl->m_aLogicName));
+ pImpl->m_bMSOLockFileCreated = true;
+ }
+
+ bool bIoErr = false;
+
+ if (!bHandleSysLocked)
+ {
+ try
+ {
+ bResult = aLockFile.CreateOwnLockFile();
+ if(pMSOLockFile)
+ bResult &= pMSOLockFile->CreateOwnLockFile();
+ }
+ catch (const uno::Exception&)
+ {
+ if (tools::IsMappedWebDAVPath(GetURLObject().GetMainURL(
+ INetURLObject::DecodeMechanism::NONE)))
+ {
+ // This is a path that redirects to a WebDAV resource;
+ // so failure creating lockfile is not an error here.
+ bResult = true;
+ }
+ else if (bLoading && !bNoUI)
+ {
+ bIoErr = true;
+ ShowLockFileProblemDialog(MessageDlg::LockFileIgnore);
+ bResult = true; // always delete the defect lock-file
+ }
+ }
+
+ // in case OOo locking is turned off the lock file is still written if possible
+ // but it is ignored while deciding whether the document should be opened for editing or not
+ if (!bResult && !IsOOoLockFileUsed() && !bIoErr)
+ {
+ bResult = true;
+ // take the ownership over the lock file
+ aLockFile.OverwriteOwnLockFile();
+
+ if(pMSOLockFile)
+ pMSOLockFile->OverwriteOwnLockFile();
+ }
+ }
+
+ if ( !bResult )
+ {
+ LockFileEntry aData;
+ try
+ {
+ aData = aLockFile.GetLockData();
+ }
+ catch (const io::WrongFormatException&)
+ {
+ // we get empty or corrupt data
+ // info to the user
+ if (!bIoErr && bLoading && !bNoUI )
+ bResult = ShowLockFileProblemDialog(MessageDlg::LockFileCorrupt);
+
+ // not show the Lock Document Dialog
+ bIoErr = true;
+ }
+ catch( const uno::Exception& )
+ {
+ // show the Lock Document Dialog, when locked from other app
+ bIoErr = !bHandleSysLocked;
+ }
+
+ bool bOwnLock = false;
+
+ if (!bHandleSysLocked)
+ {
+ LockFileEntry aOwnData = svt::LockFileCommon::GenerateOwnEntry();
+ bOwnLock = aOwnData[LockFileComponent::SYSUSERNAME] == aData[LockFileComponent::SYSUSERNAME];
+
+ if (bOwnLock
+ && aOwnData[LockFileComponent::LOCALHOST] == aData[LockFileComponent::LOCALHOST]
+ && aOwnData[LockFileComponent::USERURL] == aData[LockFileComponent::USERURL])
+ {
+ // this is own lock from the same installation, it could remain because of crash
+ bResult = true;
+ }
+ }
+
+ if ( !bResult && !bIoErr)
+ {
+ if (!bNoUI)
+ bUIStatus = ShowLockedDocumentDialog(
+ aData, bLoading, bOwnLock, bHandleSysLocked);
+ else if (bLoading && bTryIgnoreLockFile && !bHandleSysLocked)
+ bUIStatus = ShowLockResult::Succeeded;
+
+ if ( bUIStatus == ShowLockResult::Succeeded )
+ {
+ // take the ownership over the lock file
+ bResult = aLockFile.OverwriteOwnLockFile();
+
+ if(pMSOLockFile)
+ pMSOLockFile->OverwriteOwnLockFile();
+ }
+ else if (bLoading && !bHandleSysLocked)
+ eResult = LockFileResult::FailedLockFile;
+
+ if (!bResult && pLockData)
+ {
+ std::copy(aData.begin(), aData.end(), pLockData->begin());
+ }
+ }
+ }
+ }
+ catch( const uno::Exception& )
+ {
+ }
+ } while( !bResult && bUIStatus == ShowLockResult::Try );
+
+ pImpl->m_bLocked = bResult;
+ }
+ else
+ {
+ // this is no file URL, check whether the file is readonly
+ bResult = !bContentReadonly;
+ }
+ }
+ else // read-only
+ {
+ AddToCheckEditableWorkerList();
+ }
+ }
+
+ if ( !bResult && GetError() == ERRCODE_NONE )
+ {
+ // the error should be set in case it is storing process
+ // or the document has been opened for editing explicitly
+ const SfxBoolItem* pReadOnlyItem = SfxItemSet::GetItem<SfxBoolItem>(pImpl->m_pSet.get(), SID_DOC_READONLY, false);
+
+ if ( !bLoading || (pReadOnlyItem && !pReadOnlyItem->GetValue()) )
+ SetError(ERRCODE_IO_ACCESSDENIED);
+ else
+ GetItemSet()->Put( SfxBoolItem( SID_DOC_READONLY, true ) );
+ }
+
+ // when the file is locked, get the current file date
+ if ( bResult && DocNeedsFileDateCheck() )
+ GetInitFileDate( true );
+
+ if ( bResult )
+ eResult = LockFileResult::Succeeded;
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "sfx.doc", "Locking exception: high probability, that the content has not been created" );
+ }
+
+ return eResult;
+#endif
+}
+
+
+uno::Reference < embed::XStorage > SfxMedium::GetStorage( bool bCreateTempFile )
+{
+ if ( pImpl->xStorage.is() || pImpl->m_bTriedStorage )
+ return pImpl->xStorage;
+
+ uno::Sequence< uno::Any > aArgs( 2 );
+ auto pArgs = aArgs.getArray();
+
+ // the medium should be retrieved before temporary file creation
+ // to let the MediaDescriptor be filled with the streams
+ GetMedium_Impl();
+
+ if ( bCreateTempFile )
+ CreateTempFile( false );
+
+ GetMedium_Impl();
+
+ if ( GetError() )
+ return pImpl->xStorage;
+
+ const SfxBoolItem* pRepairItem = SfxItemSet::GetItem<SfxBoolItem>(GetItemSet(), SID_REPAIRPACKAGE, false);
+ if ( pRepairItem && pRepairItem->GetValue() )
+ {
+ // the storage should be created for repairing mode
+ CreateTempFile( false );
+ GetMedium_Impl();
+
+ Reference< css::ucb::XProgressHandler > xProgressHandler;
+ Reference< css::task::XStatusIndicator > xStatusIndicator;
+
+ const SfxUnoAnyItem* pxProgressItem = SfxItemSet::GetItem<SfxUnoAnyItem>(GetItemSet(), SID_PROGRESS_STATUSBAR_CONTROL, false);
+ if( pxProgressItem && ( pxProgressItem->GetValue() >>= xStatusIndicator ) )
+ xProgressHandler.set( new utl::ProgressHandlerWrap( xStatusIndicator ) );
+
+ uno::Sequence< beans::PropertyValue > aAddProps{
+ comphelper::makePropertyValue("RepairPackage", true),
+ comphelper::makePropertyValue("StatusIndicator", xProgressHandler)
+ };
+
+ // the first arguments will be filled later
+ aArgs.realloc( 3 );
+ pArgs = aArgs.getArray();
+ pArgs[2] <<= aAddProps;
+ }
+
+ if ( pImpl->xStream.is() )
+ {
+ // since the storage is based on temporary stream we open it always read-write
+ pArgs[0] <<= pImpl->xStream;
+ pArgs[1] <<= embed::ElementModes::READWRITE;
+ pImpl->bStorageBasedOnInStream = true;
+ if (pImpl->m_bDisableFileSync)
+ {
+ // Forward NoFileSync to the storage factory.
+ aArgs.realloc(3); // ??? this may re-write the data added above for pRepairItem
+ pArgs = aArgs.getArray();
+ uno::Sequence<beans::PropertyValue> aProperties(
+ comphelper::InitPropertySequence({ { "NoFileSync", uno::Any(true) } }));
+ pArgs[2] <<= aProperties;
+ }
+ }
+ else if ( pImpl->xInputStream.is() )
+ {
+ // since the storage is based on temporary stream we open it always read-write
+ pArgs[0] <<= pImpl->xInputStream;
+ pArgs[1] <<= embed::ElementModes::READ;
+ pImpl->bStorageBasedOnInStream = true;
+ }
+ else
+ {
+ CloseStreams_Impl();
+ pArgs[0] <<= pImpl->m_aName;
+ pArgs[1] <<= embed::ElementModes::READ;
+ pImpl->bStorageBasedOnInStream = false;
+ }
+
+ try
+ {
+ pImpl->xStorage.set( ::comphelper::OStorageHelper::GetStorageFactory()->createInstanceWithArguments( aArgs ),
+ uno::UNO_QUERY );
+ }
+ catch( const uno::Exception& )
+ {
+ // impossibility to create the storage is no error
+ }
+
+ if( ( pImpl->nLastStorageError = GetError() ) != ERRCODE_NONE )
+ {
+ pImpl->xStorage = nullptr;
+ if ( pImpl->m_pInStream )
+ pImpl->m_pInStream->Seek(0);
+ return uno::Reference< embed::XStorage >();
+ }
+
+ pImpl->m_bTriedStorage = true;
+
+ // TODO/LATER: Get versionlist on demand
+ if ( pImpl->xStorage.is() )
+ {
+ SetEncryptionDataToStorage_Impl();
+ GetVersionList();
+ }
+
+ const SfxInt16Item* pVersion = SfxItemSet::GetItem<SfxInt16Item>(pImpl->m_pSet.get(), SID_VERSION, false);
+
+ bool bResetStorage = false;
+ if ( pVersion && pVersion->GetValue() )
+ {
+ // Read all available versions
+ if ( pImpl->aVersions.hasElements() )
+ {
+ // Search for the version fits the comment
+ // The versions are numbered starting with 1, versions with
+ // negative versions numbers are counted backwards from the
+ // current version
+ short nVersion = pVersion->GetValue();
+ if ( nVersion<0 )
+ nVersion = static_cast<short>(pImpl->aVersions.getLength()) + nVersion;
+ else // nVersion > 0; pVersion->GetValue() != 0 was the condition to this block
+ nVersion--;
+
+ const util::RevisionTag& rTag = pImpl->aVersions[nVersion];
+ {
+ // Open SubStorage for all versions
+ uno::Reference < embed::XStorage > xSub = pImpl->xStorage->openStorageElement( "Versions",
+ embed::ElementModes::READ );
+
+ DBG_ASSERT( xSub.is(), "Version list, but no Versions!" );
+
+ // There the version is stored as packed Stream
+ uno::Reference < io::XStream > xStr = xSub->openStreamElement( rTag.Identifier, embed::ElementModes::READ );
+ std::unique_ptr<SvStream> pStream(utl::UcbStreamHelper::CreateStream( xStr ));
+ if ( pStream && pStream->GetError() == ERRCODE_NONE )
+ {
+ // Unpack Stream in TempDir
+ ::utl::TempFile aTempFile;
+ const OUString& aTmpName = aTempFile.GetURL();
+ SvFileStream aTmpStream( aTmpName, SFX_STREAM_READWRITE );
+
+ pStream->ReadStream( aTmpStream );
+ pStream.reset();
+ aTmpStream.Close();
+
+ // Open data as Storage
+ pImpl->m_nStorOpenMode = SFX_STREAM_READONLY;
+ pImpl->xStorage = comphelper::OStorageHelper::GetStorageFromURL( aTmpName, embed::ElementModes::READ );
+ pImpl->bStorageBasedOnInStream = false;
+ OUString aTemp;
+ osl::FileBase::getSystemPathFromFileURL( aTmpName, aTemp );
+ SetPhysicalName_Impl( aTemp );
+
+ pImpl->bIsTemp = true;
+ GetItemSet()->Put( SfxBoolItem( SID_DOC_READONLY, true ) );
+ // TODO/MBA
+ pImpl->aVersions.realloc(0);
+ }
+ else
+ bResetStorage = true;
+ }
+ }
+ else
+ bResetStorage = true;
+ }
+
+ if ( bResetStorage )
+ {
+ pImpl->xStorage.clear();
+ if ( pImpl->m_pInStream )
+ pImpl->m_pInStream->Seek( 0 );
+ }
+
+ pImpl->bIsStorage = pImpl->xStorage.is();
+ return pImpl->xStorage;
+}
+
+
+uno::Reference< embed::XStorage > const & SfxMedium::GetZipStorageToSign_Impl( bool bReadOnly )
+{
+ if ( !GetError() && !pImpl->m_xZipStorage.is() )
+ {
+ GetMedium_Impl();
+
+ try
+ {
+ // we can not sign document if there is no stream
+ // should it be possible at all?
+ if ( !bReadOnly && pImpl->xStream.is() )
+ {
+ pImpl->m_xZipStorage = ::comphelper::OStorageHelper::GetStorageOfFormatFromStream( ZIP_STORAGE_FORMAT_STRING, pImpl->xStream );
+ }
+ else if ( pImpl->xInputStream.is() )
+ {
+ pImpl->m_xZipStorage = ::comphelper::OStorageHelper::GetStorageOfFormatFromInputStream( ZIP_STORAGE_FORMAT_STRING, pImpl->xInputStream );
+ }
+ }
+ catch( const uno::Exception& )
+ {
+ SAL_WARN( "sfx.doc", "No possibility to get readonly version of storage from medium!" );
+ }
+
+ if ( GetError() ) // do not remove warnings
+ ResetError();
+ }
+
+ return pImpl->m_xZipStorage;
+}
+
+
+void SfxMedium::CloseZipStorage_Impl()
+{
+ if ( pImpl->m_xZipStorage.is() )
+ {
+ try {
+ pImpl->m_xZipStorage->dispose();
+ } catch( const uno::Exception& )
+ {}
+
+ pImpl->m_xZipStorage.clear();
+ }
+}
+
+void SfxMedium::CloseStorage()
+{
+ if ( pImpl->xStorage.is() )
+ {
+ uno::Reference < lang::XComponent > xComp = pImpl->xStorage;
+ // in the salvage mode the medium does not own the storage
+ if ( pImpl->bDisposeStorage && !pImpl->m_bSalvageMode )
+ {
+ try {
+ xComp->dispose();
+ } catch( const uno::Exception& )
+ {
+ SAL_WARN( "sfx.doc", "Medium's storage is already disposed!" );
+ }
+ }
+
+ pImpl->xStorage.clear();
+ pImpl->bStorageBasedOnInStream = false;
+ }
+
+ pImpl->m_bTriedStorage = false;
+ pImpl->bIsStorage = false;
+}
+
+void SfxMedium::CanDisposeStorage_Impl( bool bDisposeStorage )
+{
+ pImpl->bDisposeStorage = bDisposeStorage;
+}
+
+bool SfxMedium::WillDisposeStorageOnClose_Impl()
+{
+ return pImpl->bDisposeStorage;
+}
+
+StreamMode SfxMedium::GetOpenMode() const
+{
+ return pImpl->m_nStorOpenMode;
+}
+
+void SfxMedium::SetOpenMode( StreamMode nStorOpen,
+ bool bDontClose )
+{
+ if ( pImpl->m_nStorOpenMode != nStorOpen )
+ {
+ pImpl->m_nStorOpenMode = nStorOpen;
+
+ if( !bDontClose )
+ {
+ if ( pImpl->xStorage.is() )
+ CloseStorage();
+
+ CloseStreams_Impl();
+ }
+ }
+}
+
+
+bool SfxMedium::UseBackupToRestore_Impl( ::ucbhelper::Content& aOriginalContent,
+ const Reference< css::ucb::XCommandEnvironment >& xComEnv )
+{
+ try
+ {
+ ::ucbhelper::Content aTransactCont( pImpl->m_aBackupURL, xComEnv, comphelper::getProcessComponentContext() );
+
+ Reference< XInputStream > aOrigInput = aTransactCont.openStream();
+ aOriginalContent.writeStream( aOrigInput, true );
+ return true;
+ }
+ catch( const Exception& )
+ {
+ // in case of failure here the backup file should not be removed
+ // TODO/LATER: a message should be used to let user know about the backup
+ pImpl->m_bRemoveBackup = false;
+ // TODO/LATER: needs a specific error code
+ pImpl->m_eError = ERRCODE_IO_GENERAL;
+ }
+
+ return false;
+}
+
+
+bool SfxMedium::StorageCommit_Impl()
+{
+ bool bResult = false;
+ Reference< css::ucb::XCommandEnvironment > xDummyEnv;
+ ::ucbhelper::Content aOriginalContent;
+
+ if ( pImpl->xStorage.is() )
+ {
+ if ( !GetError() )
+ {
+ uno::Reference < embed::XTransactedObject > xTrans( pImpl->xStorage, uno::UNO_QUERY );
+ if ( xTrans.is() )
+ {
+ try
+ {
+ xTrans->commit();
+ CloseZipStorage_Impl();
+ bResult = true;
+ }
+ catch ( const embed::UseBackupException& aBackupExc )
+ {
+ // since the temporary file is created always now, the scenario is close to be impossible
+ if ( !pImpl->pTempFile )
+ {
+ OSL_ENSURE( !pImpl->m_aBackupURL.isEmpty(), "No backup on storage commit!" );
+ if ( !pImpl->m_aBackupURL.isEmpty()
+ && ::ucbhelper::Content::create( GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE ),
+ xDummyEnv, comphelper::getProcessComponentContext(),
+ aOriginalContent ) )
+ {
+ // use backup to restore the file
+ // the storage has already disconnected from original location
+ CloseAndReleaseStreams_Impl();
+ if ( !UseBackupToRestore_Impl( aOriginalContent, xDummyEnv ) )
+ {
+ // connect the medium to the temporary file of the storage
+ pImpl->aContent = ::ucbhelper::Content();
+ pImpl->m_aName = aBackupExc.TemporaryFileURL;
+ OSL_ENSURE( !pImpl->m_aName.isEmpty(), "The exception _must_ contain the temporary URL!" );
+ }
+ }
+ }
+
+ if (!GetError())
+ SetError(ERRCODE_IO_GENERAL);
+ }
+ catch ( const uno::Exception& )
+ {
+ //TODO/LATER: improve error handling
+ SetError(ERRCODE_IO_GENERAL);
+ }
+ }
+ }
+ }
+
+ return bResult;
+}
+
+
+void SfxMedium::TransactedTransferForFS_Impl( const INetURLObject& aSource,
+ const INetURLObject& aDest,
+ const Reference< css::ucb::XCommandEnvironment >& xComEnv )
+{
+ Reference< css::ucb::XCommandEnvironment > xDummyEnv;
+ ::ucbhelper::Content aOriginalContent;
+
+ try
+ {
+ aOriginalContent = ::ucbhelper::Content( aDest.GetMainURL( INetURLObject::DecodeMechanism::NONE ), xComEnv, comphelper::getProcessComponentContext() );
+ }
+ catch ( const css::ucb::CommandAbortedException& )
+ {
+ pImpl->m_eError = ERRCODE_ABORT;
+ }
+ catch ( const css::ucb::CommandFailedException& )
+ {
+ pImpl->m_eError = ERRCODE_ABORT;
+ }
+ catch (const css::ucb::ContentCreationException& ex)
+ {
+ pImpl->m_eError = ERRCODE_IO_GENERAL;
+ if (
+ (ex.eError == css::ucb::ContentCreationError_NO_CONTENT_PROVIDER ) ||
+ (ex.eError == css::ucb::ContentCreationError_CONTENT_CREATION_FAILED)
+ )
+ {
+ pImpl->m_eError = ERRCODE_IO_NOTEXISTSPATH;
+ }
+ }
+ catch (const css::uno::Exception&)
+ {
+ pImpl->m_eError = ERRCODE_IO_GENERAL;
+ }
+
+ if( pImpl->m_eError && !pImpl->m_eError.IsWarning() )
+ return;
+
+ if ( pImpl->xStorage.is() )
+ CloseStorage();
+
+ CloseStreams_Impl();
+
+ ::ucbhelper::Content aTempCont;
+ if( ::ucbhelper::Content::create( aSource.GetMainURL( INetURLObject::DecodeMechanism::NONE ), xDummyEnv, comphelper::getProcessComponentContext(), aTempCont ) )
+ {
+ bool bTransactStarted = false;
+ const SfxBoolItem* pOverWrite = SfxItemSet::GetItem<SfxBoolItem>(GetItemSet(), SID_OVERWRITE, false);
+ bool bOverWrite = !pOverWrite || pOverWrite->GetValue();
+ bool bResult = false;
+
+ try
+ {
+ // tdf#60237 - if the OverWrite property of the MediaDescriptor is set to false,
+ // try to write the file before trying to rename or copy it
+ if (!(bOverWrite
+ && ::utl::UCBContentHelper::IsDocument(
+ aDest.GetMainURL(INetURLObject::DecodeMechanism::NONE))))
+ {
+ Reference< XInputStream > aTempInput = aTempCont.openStream();
+ aOriginalContent.writeStream( aTempInput, bOverWrite );
+ bResult = true;
+ } else {
+ OUString aSourceMainURL = aSource.GetMainURL(INetURLObject::DecodeMechanism::NONE);
+ OUString aDestMainURL = aDest.GetMainURL(INetURLObject::DecodeMechanism::NONE);
+
+ sal_uInt64 nAttributes = GetDefaultFileAttributes(aDestMainURL);
+ if (IsFileMovable(aDest)
+ && osl::File::replace(aSourceMainURL, aDestMainURL) == osl::FileBase::E_None)
+ {
+ if (nAttributes)
+ // Adjust attributes, source might be created with
+ // the osl_File_OpenFlag_Private flag.
+ osl::File::setAttributes(aDestMainURL, nAttributes);
+ bResult = true;
+ }
+ else
+ {
+ if( pImpl->m_aBackupURL.isEmpty() )
+ DoInternalBackup_Impl( aOriginalContent );
+
+ if( !pImpl->m_aBackupURL.isEmpty() )
+ {
+ Reference< XInputStream > aTempInput = aTempCont.openStream();
+ bTransactStarted = true;
+ aOriginalContent.setPropertyValue( "Size", uno::Any( sal_Int64(0) ) );
+ aOriginalContent.writeStream( aTempInput, bOverWrite );
+ bResult = true;
+ }
+ else
+ {
+ pImpl->m_eError = ERRCODE_SFX_CANTCREATEBACKUP;
+ }
+ }
+ }
+ }
+ catch ( const css::ucb::CommandAbortedException& )
+ {
+ pImpl->m_eError = ERRCODE_ABORT;
+ }
+ catch ( const css::ucb::CommandFailedException& )
+ {
+ pImpl->m_eError = ERRCODE_ABORT;
+ }
+ catch ( const css::ucb::InteractiveIOException& r )
+ {
+ if ( r.Code == IOErrorCode_ACCESS_DENIED )
+ pImpl->m_eError = ERRCODE_IO_ACCESSDENIED;
+ else if ( r.Code == IOErrorCode_NOT_EXISTING )
+ pImpl->m_eError = ERRCODE_IO_NOTEXISTS;
+ else if ( r.Code == IOErrorCode_CANT_READ )
+ pImpl->m_eError = ERRCODE_IO_CANTREAD;
+ else
+ pImpl->m_eError = ERRCODE_IO_GENERAL;
+ }
+ // tdf#60237 - if the file is already present, raise the appropriate error
+ catch (const css::ucb::NameClashException& )
+ {
+ pImpl->m_eError = ERRCODE_IO_ALREADYEXISTS;
+ }
+ catch ( const css::uno::Exception& )
+ {
+ pImpl->m_eError = ERRCODE_IO_GENERAL;
+ }
+
+ if ( bResult )
+ {
+ if ( pImpl->pTempFile )
+ {
+ pImpl->pTempFile->EnableKillingFile();
+ pImpl->pTempFile.reset();
+ }
+ }
+ else if ( bTransactStarted )
+ {
+ UseBackupToRestore_Impl( aOriginalContent, xDummyEnv );
+ }
+ }
+ else
+ pImpl->m_eError = ERRCODE_IO_CANTREAD;
+}
+
+
+bool SfxMedium::TryDirectTransfer( const OUString& aURL, SfxItemSet const & aTargetSet )
+{
+ if ( GetError() )
+ return false;
+
+ // if the document had no password it should be stored without password
+ // if the document had password it should be stored with the same password
+ // otherwise the stream copying can not be done
+ const SfxStringItem* pNewPassItem = aTargetSet.GetItem<SfxStringItem>(SID_PASSWORD, false);
+ const SfxStringItem* pOldPassItem = SfxItemSet::GetItem<SfxStringItem>(GetItemSet(), SID_PASSWORD, false);
+ if ( ( !pNewPassItem && !pOldPassItem )
+ || ( pNewPassItem && pOldPassItem && pNewPassItem->GetValue() == pOldPassItem->GetValue() ) )
+ {
+ // the filter must be the same
+ const SfxStringItem* pNewFilterItem = aTargetSet.GetItem<SfxStringItem>(SID_FILTER_NAME, false);
+ const SfxStringItem* pOldFilterItem = SfxItemSet::GetItem<SfxStringItem>(GetItemSet(), SID_FILTER_NAME, false);
+ if ( pNewFilterItem && pOldFilterItem && pNewFilterItem->GetValue() == pOldFilterItem->GetValue() )
+ {
+ // get the input stream and copy it
+ // in case of success return true
+ uno::Reference< io::XInputStream > xInStream = GetInputStream();
+
+ ResetError();
+ if ( xInStream.is() )
+ {
+ try
+ {
+ uno::Reference< io::XSeekable > xSeek( xInStream, uno::UNO_QUERY );
+ sal_Int64 nPos = 0;
+ if ( xSeek.is() )
+ {
+ nPos = xSeek->getPosition();
+ xSeek->seek( 0 );
+ }
+
+ uno::Reference < css::ucb::XCommandEnvironment > xEnv;
+ ::ucbhelper::Content aTargetContent( aURL, xEnv, comphelper::getProcessComponentContext() );
+
+ InsertCommandArgument aInsertArg;
+ aInsertArg.Data = xInStream;
+ const SfxBoolItem* pOverWrite = aTargetSet.GetItem<SfxBoolItem>(SID_OVERWRITE, false);
+ if ( pOverWrite && !pOverWrite->GetValue() ) // argument says: never overwrite
+ aInsertArg.ReplaceExisting = false;
+ else
+ aInsertArg.ReplaceExisting = true; // default is overwrite existing files
+
+ Any aCmdArg;
+ aCmdArg <<= aInsertArg;
+ aTargetContent.executeCommand( "insert",
+ aCmdArg );
+
+ if ( xSeek.is() )
+ xSeek->seek( nPos );
+
+ return true;
+ }
+ catch( const uno::Exception& )
+ {}
+ }
+ }
+ }
+
+ return false;
+}
+
+
+void SfxMedium::Transfer_Impl()
+{
+ // The transfer is required only in two cases: either if there is a temporary file or if there is a salvage item
+ OUString aNameURL;
+ if ( pImpl->pTempFile )
+ aNameURL = pImpl->pTempFile->GetURL();
+ else if ( !pImpl->m_aLogicName.isEmpty() && pImpl->m_bSalvageMode )
+ {
+ // makes sense only in case logic name is set
+ if ( osl::FileBase::getFileURLFromSystemPath( pImpl->m_aName, aNameURL )
+ != osl::FileBase::E_None )
+ SAL_WARN( "sfx.doc", "The medium name is not convertible!" );
+ }
+
+ if ( aNameURL.isEmpty() || ( pImpl->m_eError && !pImpl->m_eError.IsWarning() ) )
+ return;
+
+ SAL_INFO( "sfx.doc", "SfxMedium::Transfer_Impl, copying to target" );
+
+ Reference < css::ucb::XCommandEnvironment > xEnv;
+ Reference< XOutputStream > rOutStream;
+
+ // in case an output stream is provided from outside and the URL is correct
+ // commit to the stream
+ if (pImpl->m_aLogicName.startsWith("private:stream"))
+ {
+ // TODO/LATER: support storing to SID_STREAM
+ const SfxUnoAnyItem* pOutStreamItem = SfxItemSet::GetItem<SfxUnoAnyItem>(pImpl->m_pSet.get(), SID_OUTPUTSTREAM, false);
+ if( pOutStreamItem && ( pOutStreamItem->GetValue() >>= rOutStream ) )
+ {
+ if ( pImpl->xStorage.is() )
+ CloseStorage();
+
+ CloseStreams_Impl();
+
+ INetURLObject aSource( aNameURL );
+ ::ucbhelper::Content aTempCont;
+ if( ::ucbhelper::Content::create( aSource.GetMainURL( INetURLObject::DecodeMechanism::NONE ), xEnv, comphelper::getProcessComponentContext(), aTempCont ) )
+ {
+ try
+ {
+ sal_Int32 nRead;
+ sal_Int32 nBufferSize = 32767;
+ Sequence < sal_Int8 > aSequence ( nBufferSize );
+ Reference< XInputStream > aTempInput = aTempCont.openStream();
+
+ do
+ {
+ nRead = aTempInput->readBytes ( aSequence, nBufferSize );
+ if ( nRead < nBufferSize )
+ {
+ Sequence < sal_Int8 > aTempBuf ( aSequence.getConstArray(), nRead );
+ rOutStream->writeBytes ( aTempBuf );
+ }
+ else
+ rOutStream->writeBytes ( aSequence );
+ }
+ while ( nRead == nBufferSize );
+
+ // remove temporary file
+ if ( pImpl->pTempFile )
+ {
+ pImpl->pTempFile->EnableKillingFile();
+ pImpl->pTempFile.reset();
+ }
+ }
+ catch( const Exception& )
+ {}
+ }
+ }
+ else
+ {
+ SAL_WARN( "sfx.doc", "Illegal Output stream parameter!" );
+ SetError(ERRCODE_IO_GENERAL);
+ }
+
+ // free the reference
+ if ( pImpl->m_pSet )
+ pImpl->m_pSet->ClearItem( SID_OUTPUTSTREAM );
+
+ return;
+ }
+
+ GetContent();
+ if ( !pImpl->aContent.get().is() )
+ {
+ pImpl->m_eError = ERRCODE_IO_NOTEXISTS;
+ return;
+ }
+
+ INetURLObject aDest( GetURLObject() );
+
+ // source is the temp file written so far
+ INetURLObject aSource( aNameURL );
+
+ // a special case, an interaction handler should be used for
+ // authentication in case it is available
+ Reference< css::ucb::XCommandEnvironment > xComEnv;
+ bool bForceInteractionHandler = GetURLObject().isAnyKnownWebDAVScheme();
+ Reference< css::task::XInteractionHandler > xInteractionHandler = GetInteractionHandler(bForceInteractionHandler);
+ if (xInteractionHandler.is())
+ xComEnv = new ::ucbhelper::CommandEnvironment( xInteractionHandler,
+ Reference< css::ucb::XProgressHandler >() );
+
+ OUString aDestURL( aDest.GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
+
+ if ( comphelper::isFileUrl( aDestURL ) || !aDest.removeSegment() )
+ {
+ TransactedTransferForFS_Impl( aSource, aDest, xComEnv );
+
+ if (!pImpl->m_bDisableFileSync)
+ {
+ // Hideous - no clean way to do this, so we re-open the file just to fsync it
+ osl::File aFile( aDestURL );
+ if ( aFile.open( osl_File_OpenFlag_Write ) == osl::FileBase::E_None )
+ {
+ aFile.sync();
+ SAL_INFO( "sfx.doc", "fsync'd saved file '" << aDestURL << "'" );
+ aFile.close();
+ }
+ }
+ }
+ else
+ {
+ // create content for the parent folder and call transfer on that content with the source content
+ // and the destination file name as parameters
+ ::ucbhelper::Content aSourceContent;
+ ::ucbhelper::Content aTransferContent;
+
+ ::ucbhelper::Content aDestContent;
+ (void)::ucbhelper::Content::create( aDestURL, xComEnv, comphelper::getProcessComponentContext(), aDestContent );
+ // For checkin, we need the object URL, not the parent folder:
+ if ( !IsInCheckIn( ) )
+ {
+ // Get the parent URL from the XChild if possible: why would the URL necessarily have
+ // a hierarchical path? It's not always the case for CMIS.
+ Reference< css::container::XChild> xChild( aDestContent.get(), uno::UNO_QUERY );
+ OUString sParentUrl;
+ if ( xChild.is( ) )
+ {
+ Reference< css::ucb::XContent > xParent( xChild->getParent( ), uno::UNO_QUERY );
+ if ( xParent.is( ) )
+ {
+ sParentUrl = xParent->getIdentifier( )->getContentIdentifier();
+ }
+ }
+
+ if ( sParentUrl.isEmpty() )
+ aDestURL = aDest.GetMainURL( INetURLObject::DecodeMechanism::NONE );
+ // adjust to above aDest.removeSegment()
+ else
+ aDestURL = sParentUrl;
+ }
+
+ // LongName wasn't defined anywhere, only used here... get the Title instead
+ // as it's less probably empty
+ OUString aFileName;
+ Any aAny = aDestContent.getPropertyValue("Title");
+ aAny >>= aFileName;
+ aAny = aDestContent.getPropertyValue( "ObjectId" );
+ OUString sObjectId;
+ aAny >>= sObjectId;
+ if ( aFileName.isEmpty() )
+ aFileName = GetURLObject().getName( INetURLObject::LAST_SEGMENT, true, INetURLObject::DecodeMechanism::WithCharset );
+
+ try
+ {
+ aTransferContent = ::ucbhelper::Content( aDestURL, xComEnv, comphelper::getProcessComponentContext() );
+ }
+ catch (const css::ucb::ContentCreationException& ex)
+ {
+ pImpl->m_eError = ERRCODE_IO_GENERAL;
+ if (
+ (ex.eError == css::ucb::ContentCreationError_NO_CONTENT_PROVIDER ) ||
+ (ex.eError == css::ucb::ContentCreationError_CONTENT_CREATION_FAILED)
+ )
+ {
+ pImpl->m_eError = ERRCODE_IO_NOTEXISTSPATH;
+ }
+ }
+ catch (const css::uno::Exception&)
+ {
+ pImpl->m_eError = ERRCODE_IO_GENERAL;
+ }
+
+ if ( !pImpl->m_eError || pImpl->m_eError.IsWarning() )
+ {
+ // free resources, otherwise the transfer may fail
+ if ( pImpl->xStorage.is() )
+ CloseStorage();
+
+ CloseStreams_Impl();
+
+ (void)::ucbhelper::Content::create( aSource.GetMainURL( INetURLObject::DecodeMechanism::NONE ), xEnv, comphelper::getProcessComponentContext(), aSourceContent );
+
+ // check for external parameters that may customize the handling of NameClash situations
+ const SfxBoolItem* pOverWrite = SfxItemSet::GetItem<SfxBoolItem>(GetItemSet(), SID_OVERWRITE, false);
+ sal_Int32 nNameClash;
+ if ( pOverWrite && !pOverWrite->GetValue() )
+ // argument says: never overwrite
+ nNameClash = NameClash::ERROR;
+ else
+ // default is overwrite existing files
+ nNameClash = NameClash::OVERWRITE;
+
+ try
+ {
+ OUString aMimeType = pImpl->getFilterMimeType();
+ ::ucbhelper::InsertOperation eOperation = ::ucbhelper::InsertOperation::Copy;
+ bool bMajor = false;
+ OUString sComment;
+ if ( IsInCheckIn( ) )
+ {
+ eOperation = ::ucbhelper::InsertOperation::Checkin;
+ const SfxBoolItem* pMajor = SfxItemSet::GetItem<SfxBoolItem>(GetItemSet(), SID_DOCINFO_MAJOR, false);
+ bMajor = pMajor && pMajor->GetValue( );
+ const SfxStringItem* pComments = SfxItemSet::GetItem<SfxStringItem>(GetItemSet(), SID_DOCINFO_COMMENTS, false);
+ if ( pComments )
+ sComment = pComments->GetValue( );
+ }
+ OUString sResultURL;
+ aTransferContent.transferContent(
+ aSourceContent, eOperation,
+ aFileName, nNameClash, aMimeType, bMajor, sComment,
+ &sResultURL, sObjectId );
+
+ if ( !sResultURL.isEmpty( ) ) // Likely to happen only for checkin
+ SwitchDocumentToFile( sResultURL );
+ try
+ {
+ if ( GetURLObject().isAnyKnownWebDAVScheme() &&
+ eOperation == ::ucbhelper::InsertOperation::Copy )
+ {
+ // tdf#95272 try to re-issue a lock command when a new file is created.
+ // This may be needed because some WebDAV servers fail to implement the
+ // 'LOCK on unallocated reference', see issue comment:
+ // <https://bugs.documentfoundation.org/show_bug.cgi?id=95792#c8>
+ // and specification at:
+ // <http://tools.ietf.org/html/rfc4918#section-7.3>
+ // If the WebDAV resource is already locked by this LO instance, nothing will
+ // happen, e.g. the LOCK method will not be sent to the server.
+ ::ucbhelper::Content aLockContent( GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE ), xComEnv, comphelper::getProcessComponentContext() );
+ aLockContent.lock();
+ }
+ }
+ catch ( css::uno::Exception & )
+ {
+ TOOLS_WARN_EXCEPTION( "sfx.doc", "LOCK not working while re-issuing it" );
+ }
+ }
+ catch ( const css::ucb::CommandAbortedException& )
+ {
+ pImpl->m_eError = ERRCODE_ABORT;
+ }
+ catch ( const css::ucb::CommandFailedException& )
+ {
+ pImpl->m_eError = ERRCODE_ABORT;
+ }
+ catch ( const css::ucb::InteractiveIOException& r )
+ {
+ if ( r.Code == IOErrorCode_ACCESS_DENIED )
+ pImpl->m_eError = ERRCODE_IO_ACCESSDENIED;
+ else if ( r.Code == IOErrorCode_NOT_EXISTING )
+ pImpl->m_eError = ERRCODE_IO_NOTEXISTS;
+ else if ( r.Code == IOErrorCode_CANT_READ )
+ pImpl->m_eError = ERRCODE_IO_CANTREAD;
+ else
+ pImpl->m_eError = ERRCODE_IO_GENERAL;
+ }
+ catch ( const css::uno::Exception& )
+ {
+ pImpl->m_eError = ERRCODE_IO_GENERAL;
+ }
+
+ // do not switch from temporary file in case of nonfile protocol
+ }
+ }
+
+ if ( ( !pImpl->m_eError || pImpl->m_eError.IsWarning() ) && !pImpl->pTempFile )
+ {
+ // without a TempFile the physical and logical name should be the same after successful transfer
+ if (osl::FileBase::getSystemPathFromFileURL(
+ GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE ), pImpl->m_aName )
+ != osl::FileBase::E_None)
+ {
+ pImpl->m_aName.clear();
+ }
+ pImpl->m_bSalvageMode = false;
+ }
+}
+
+
+void SfxMedium::DoInternalBackup_Impl( const ::ucbhelper::Content& aOriginalContent,
+ std::u16string_view aPrefix,
+ const OUString& aExtension,
+ const OUString& aDestDir )
+{
+ if ( !pImpl->m_aBackupURL.isEmpty() )
+ return; // the backup was done already
+
+ ::utl::TempFile aTransactTemp( aPrefix, true, &aExtension, &aDestDir );
+
+ INetURLObject aBackObj( aTransactTemp.GetURL() );
+ OUString aBackupName = aBackObj.getName( INetURLObject::LAST_SEGMENT, true, INetURLObject::DecodeMechanism::WithCharset );
+
+ Reference < css::ucb::XCommandEnvironment > xDummyEnv;
+ ::ucbhelper::Content aBackupCont;
+ if( ::ucbhelper::Content::create( aDestDir, xDummyEnv, comphelper::getProcessComponentContext(), aBackupCont ) )
+ {
+ try
+ {
+ OUString sMimeType = pImpl->getFilterMimeType();
+ aBackupCont.transferContent( aOriginalContent,
+ ::ucbhelper::InsertOperation::Copy,
+ aBackupName,
+ NameClash::OVERWRITE,
+ sMimeType );
+ pImpl->m_aBackupURL = aBackObj.GetMainURL( INetURLObject::DecodeMechanism::NONE );
+ pImpl->m_bRemoveBackup = true;
+ }
+ catch( const Exception& )
+ {}
+ }
+
+ if ( pImpl->m_aBackupURL.isEmpty() )
+ aTransactTemp.EnableKillingFile();
+}
+
+
+void SfxMedium::DoInternalBackup_Impl( const ::ucbhelper::Content& aOriginalContent )
+{
+ if ( !pImpl->m_aBackupURL.isEmpty() )
+ return; // the backup was done already
+
+ OUString aFileName = GetURLObject().getName( INetURLObject::LAST_SEGMENT,
+ true,
+ INetURLObject::DecodeMechanism::NONE );
+
+ sal_Int32 nPrefixLen = aFileName.lastIndexOf( '.' );
+ OUString aPrefix = ( nPrefixLen == -1 ) ? aFileName : aFileName.copy( 0, nPrefixLen );
+ OUString aExtension = ( nPrefixLen == -1 ) ? OUString() : aFileName.copy( nPrefixLen );
+ OUString aBakDir = SvtPathOptions().GetBackupPath();
+
+ // create content for the parent folder ( = backup folder )
+ ::ucbhelper::Content aContent;
+ Reference < css::ucb::XCommandEnvironment > xEnv;
+ if( ::utl::UCBContentHelper::ensureFolder(comphelper::getProcessComponentContext(), xEnv, aBakDir, aContent) )
+ DoInternalBackup_Impl( aOriginalContent, aPrefix, aExtension, aBakDir );
+
+ if ( !pImpl->m_aBackupURL.isEmpty() )
+ return;
+
+ // the copying to the backup catalog failed ( for example because
+ // of using an encrypted partition as target catalog )
+ // since the user did not specify to make backup explicitly
+ // office should try to make backup in another place,
+ // target catalog does not look bad for this case ( and looks
+ // to be the only way for encrypted partitions )
+
+ INetURLObject aDest = GetURLObject();
+ if ( aDest.removeSegment() )
+ DoInternalBackup_Impl( aOriginalContent, aPrefix, aExtension, aDest.GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
+}
+
+
+void SfxMedium::DoBackup_Impl()
+{
+ // source file name is the logical name of this medium
+ INetURLObject aSource( GetURLObject() );
+
+ // there is nothing to backup in case source file does not exist
+ if ( !::utl::UCBContentHelper::IsDocument( aSource.GetMainURL( INetURLObject::DecodeMechanism::NONE ) ) )
+ return;
+
+ bool bSuccess = false;
+
+ // get path for backups
+ OUString aBakDir = SvtPathOptions().GetBackupPath();
+ if( !aBakDir.isEmpty() )
+ {
+ // create content for the parent folder ( = backup folder )
+ ::ucbhelper::Content aContent;
+ Reference < css::ucb::XCommandEnvironment > xEnv;
+ if( ::utl::UCBContentHelper::ensureFolder(comphelper::getProcessComponentContext(), xEnv, aBakDir, aContent) )
+ {
+ // save as ".bak" file
+ INetURLObject aDest( aBakDir );
+ aDest.insertName( aSource.getName() );
+ aDest.setExtension( u"bak" );
+ OUString aFileName = aDest.getName( INetURLObject::LAST_SEGMENT, true, INetURLObject::DecodeMechanism::WithCharset );
+
+ // create a content for the source file
+ ::ucbhelper::Content aSourceContent;
+ if ( ::ucbhelper::Content::create( aSource.GetMainURL( INetURLObject::DecodeMechanism::NONE ), xEnv, comphelper::getProcessComponentContext(), aSourceContent ) )
+ {
+ try
+ {
+ // do the transfer ( copy source file to backup dir )
+ OUString sMimeType = pImpl->getFilterMimeType();
+ aContent.transferContent( aSourceContent,
+ ::ucbhelper::InsertOperation::Copy,
+ aFileName,
+ NameClash::OVERWRITE,
+ sMimeType );
+ pImpl->m_aBackupURL = aDest.GetMainURL( INetURLObject::DecodeMechanism::NONE );
+ pImpl->m_bRemoveBackup = false;
+ bSuccess = true;
+ }
+ catch ( const css::uno::Exception& )
+ {
+ }
+ }
+ }
+ }
+
+ if ( !bSuccess )
+ {
+ pImpl->m_eError = ERRCODE_SFX_CANTCREATEBACKUP;
+ }
+}
+
+
+void SfxMedium::ClearBackup_Impl()
+{
+ if( pImpl->m_bRemoveBackup )
+ {
+ // currently a document is always stored in a new medium,
+ // thus if a backup can not be removed the backup URL should not be cleaned
+ if ( !pImpl->m_aBackupURL.isEmpty() )
+ {
+ if ( ::utl::UCBContentHelper::Kill( pImpl->m_aBackupURL ) )
+ {
+ pImpl->m_bRemoveBackup = false;
+ pImpl->m_aBackupURL.clear();
+ }
+ else
+ {
+
+ SAL_WARN( "sfx.doc", "Couldn't remove backup file!");
+ }
+ }
+ }
+ else
+ pImpl->m_aBackupURL.clear();
+}
+
+
+void SfxMedium::GetLockingStream_Impl()
+{
+ if ( GetURLObject().GetProtocol() != INetProtocol::File
+ || pImpl->m_xLockingStream.is() )
+ return;
+
+ const SfxUnoAnyItem* pWriteStreamItem = SfxItemSet::GetItem<SfxUnoAnyItem>(pImpl->m_pSet.get(), SID_STREAM, false);
+ if ( pWriteStreamItem )
+ pWriteStreamItem->GetValue() >>= pImpl->m_xLockingStream;
+
+ if ( pImpl->m_xLockingStream.is() )
+ return;
+
+ // open the original document
+ uno::Sequence< beans::PropertyValue > xProps;
+ TransformItems( SID_OPENDOC, *GetItemSet(), xProps );
+ utl::MediaDescriptor aMedium( xProps );
+
+ aMedium.addInputStreamOwnLock();
+
+ uno::Reference< io::XInputStream > xInputStream;
+ aMedium[utl::MediaDescriptor::PROP_STREAM] >>= pImpl->m_xLockingStream;
+ aMedium[utl::MediaDescriptor::PROP_INPUTSTREAM] >>= xInputStream;
+
+ if ( !pImpl->pTempFile && pImpl->m_aName.isEmpty() )
+ {
+ // the medium is still based on the original file, it makes sense to initialize the streams
+ if ( pImpl->m_xLockingStream.is() )
+ pImpl->xStream = pImpl->m_xLockingStream;
+
+ if ( xInputStream.is() )
+ pImpl->xInputStream = xInputStream;
+
+ if ( !pImpl->xInputStream.is() && pImpl->xStream.is() )
+ pImpl->xInputStream = pImpl->xStream->getInputStream();
+ }
+}
+
+
+void SfxMedium::GetMedium_Impl()
+{
+ if ( pImpl->m_pInStream
+ && (!pImpl->bIsTemp || pImpl->xInputStream.is() || pImpl->m_xInputStreamToLoadFrom.is() || pImpl->xStream.is() || pImpl->m_xLockingStream.is() ) )
+ return;
+
+ pImpl->bDownloadDone = false;
+ Reference< css::task::XInteractionHandler > xInteractionHandler = GetInteractionHandler();
+
+ //TODO/MBA: need support for SID_STREAM
+ const SfxUnoAnyItem* pWriteStreamItem = SfxItemSet::GetItem<SfxUnoAnyItem>(pImpl->m_pSet.get(), SID_STREAM, false);
+ const SfxUnoAnyItem* pInStreamItem = SfxItemSet::GetItem<SfxUnoAnyItem>(pImpl->m_pSet.get(), SID_INPUTSTREAM, false);
+ if ( pWriteStreamItem )
+ {
+ pWriteStreamItem->GetValue() >>= pImpl->xStream;
+
+ if ( pInStreamItem )
+ pInStreamItem->GetValue() >>= pImpl->xInputStream;
+
+ if ( !pImpl->xInputStream.is() && pImpl->xStream.is() )
+ pImpl->xInputStream = pImpl->xStream->getInputStream();
+ }
+ else if ( pInStreamItem )
+ {
+ pInStreamItem->GetValue() >>= pImpl->xInputStream;
+ }
+ else
+ {
+ uno::Sequence < beans::PropertyValue > xProps;
+ OUString aFileName;
+ if (!pImpl->m_aName.isEmpty())
+ {
+ if ( osl::FileBase::getFileURLFromSystemPath( pImpl->m_aName, aFileName )
+ != osl::FileBase::E_None )
+ {
+ SAL_WARN( "sfx.doc", "Physical name not convertible!");
+ }
+ }
+ else
+ aFileName = GetName();
+
+ // in case the temporary file exists the streams should be initialized from it,
+ // but the original MediaDescriptor should not be changed
+ bool bFromTempFile = ( pImpl->pTempFile != nullptr );
+
+ if ( !bFromTempFile )
+ {
+ GetItemSet()->Put( SfxStringItem( SID_FILE_NAME, aFileName ) );
+ if( !(pImpl->m_nStorOpenMode & StreamMode::WRITE) )
+ GetItemSet()->Put( SfxBoolItem( SID_DOC_READONLY, true ) );
+ if (xInteractionHandler.is())
+ GetItemSet()->Put( SfxUnoAnyItem( SID_INTERACTIONHANDLER, Any(xInteractionHandler) ) );
+ }
+
+ if ( pImpl->m_xInputStreamToLoadFrom.is() )
+ {
+ pImpl->xInputStream = pImpl->m_xInputStreamToLoadFrom;
+ if (pImpl->m_bInputStreamIsReadOnly)
+ GetItemSet()->Put( SfxBoolItem( SID_DOC_READONLY, true ) );
+ }
+ else
+ {
+ TransformItems( SID_OPENDOC, *GetItemSet(), xProps );
+ utl::MediaDescriptor aMedium( xProps );
+
+ if ( pImpl->m_xLockingStream.is() && !bFromTempFile )
+ {
+ // the medium is not based on the temporary file, so the original stream can be used
+ pImpl->xStream = pImpl->m_xLockingStream;
+ }
+ else
+ {
+ if ( bFromTempFile )
+ {
+ aMedium[utl::MediaDescriptor::PROP_URL] <<= aFileName;
+ aMedium.erase( utl::MediaDescriptor::PROP_READONLY );
+ aMedium.addInputStream();
+ }
+ else if ( GetURLObject().GetProtocol() == INetProtocol::File )
+ {
+ // use the special locking approach only for file URLs
+ aMedium.addInputStreamOwnLock();
+ }
+ else
+ {
+ // add a check for protocol, if it's http or https or provide webdav then add
+ // the interaction handler to be used by the authentication dialog
+ if ( GetURLObject().isAnyKnownWebDAVScheme() )
+ {
+ aMedium[utl::MediaDescriptor::PROP_AUTHENTICATIONHANDLER] <<= GetInteractionHandler( true );
+ }
+ aMedium.addInputStream();
+ }
+ // the ReadOnly property set in aMedium is ignored
+ // the check is done in LockOrigFileOnDemand() for file and non-file URLs
+
+ //TODO/MBA: what happens if property is not there?!
+ aMedium[utl::MediaDescriptor::PROP_STREAM] >>= pImpl->xStream;
+ aMedium[utl::MediaDescriptor::PROP_INPUTSTREAM] >>= pImpl->xInputStream;
+ }
+
+ GetContent();
+ if ( !pImpl->xInputStream.is() && pImpl->xStream.is() )
+ pImpl->xInputStream = pImpl->xStream->getInputStream();
+ }
+
+ if ( !bFromTempFile )
+ {
+ //TODO/MBA: need support for SID_STREAM
+ if ( pImpl->xStream.is() )
+ GetItemSet()->Put( SfxUnoAnyItem( SID_STREAM, Any( pImpl->xStream ) ) );
+
+ GetItemSet()->Put( SfxUnoAnyItem( SID_INPUTSTREAM, Any( pImpl->xInputStream ) ) );
+ }
+ }
+
+ //TODO/MBA: ErrorHandling - how to transport error from MediaDescriptor
+ if ( !GetError() && !pImpl->xStream.is() && !pImpl->xInputStream.is() )
+ SetError(ERRCODE_IO_ACCESSDENIED);
+
+ if ( !GetError() && !pImpl->m_pInStream )
+ {
+ if ( pImpl->xStream.is() )
+ pImpl->m_pInStream = utl::UcbStreamHelper::CreateStream( pImpl->xStream );
+ else if ( pImpl->xInputStream.is() )
+ pImpl->m_pInStream = utl::UcbStreamHelper::CreateStream( pImpl->xInputStream );
+ }
+
+ pImpl->bDownloadDone = true;
+ pImpl->aDoneLink.ClearPendingCall();
+ ErrCode nError = GetError();
+ pImpl->aDoneLink.Call( reinterpret_cast<void*>(sal_uInt32(nError)) );
+}
+
+bool SfxMedium::IsRemote() const
+{
+ return pImpl->m_bRemote;
+}
+
+void SfxMedium::SetUpdatePickList(bool bVal)
+{
+ pImpl->bUpdatePickList = bVal;
+}
+
+bool SfxMedium::IsUpdatePickList() const
+{
+ return pImpl->bUpdatePickList;
+}
+
+void SfxMedium::SetLongName(const OUString &rName)
+{
+ pImpl->m_aLongName = rName;
+}
+
+const OUString& SfxMedium::GetLongName() const
+{
+ return pImpl->m_aLongName;
+}
+
+void SfxMedium::SetDoneLink( const Link<void*,void>& rLink )
+{
+ pImpl->aDoneLink = rLink;
+}
+
+void SfxMedium::Download( const Link<void*,void>& aLink )
+{
+ SetDoneLink( aLink );
+ GetInStream();
+ if ( pImpl->m_pInStream && !aLink.IsSet() )
+ {
+ while( !pImpl->bDownloadDone && !Application::IsQuit())
+ Application::Yield();
+ }
+}
+
+
+void SfxMedium::Init_Impl()
+/* [Description]
+ Includes a valid:: sun:: com:: star:: util:: URL (If a file name was
+ previously in there) in the logical name and if available sets the
+ physical name as the file name.
+ */
+
+{
+ Reference< XOutputStream > rOutStream;
+
+ // TODO/LATER: handle lifetime of storages
+ pImpl->bDisposeStorage = false;
+
+ const SfxStringItem* pSalvageItem = SfxItemSet::GetItem<SfxStringItem>(pImpl->m_pSet.get(), SID_DOC_SALVAGE, false);
+ if ( pSalvageItem && pSalvageItem->GetValue().isEmpty() )
+ {
+ pSalvageItem = nullptr;
+ pImpl->m_pSet->ClearItem( SID_DOC_SALVAGE );
+ }
+
+ if (!pImpl->m_aLogicName.isEmpty())
+ {
+ INetURLObject aUrl( pImpl->m_aLogicName );
+ INetProtocol eProt = aUrl.GetProtocol();
+ if ( eProt == INetProtocol::NotValid )
+ {
+ SAL_WARN( "sfx.doc", "URL <" << pImpl->m_aLogicName << "> with unknown protocol" );
+ }
+ else
+ {
+ if ( aUrl.HasMark() )
+ {
+ std::unique_lock<std::recursive_mutex> chkEditLock;
+ if (pImpl->m_pCheckEditableWorkerMutex != nullptr)
+ chkEditLock = std::unique_lock<std::recursive_mutex>(
+ *(pImpl->m_pCheckEditableWorkerMutex));
+ pImpl->m_aLogicName = aUrl.GetURLNoMark( INetURLObject::DecodeMechanism::NONE );
+ if (chkEditLock.owns_lock())
+ chkEditLock.unlock();
+ GetItemSet()->Put( SfxStringItem( SID_JUMPMARK, aUrl.GetMark() ) );
+ }
+
+ // try to convert the URL into a physical name - but never change a physical name
+ // physical name may be set if the logical name is changed after construction
+ if ( pImpl->m_aName.isEmpty() )
+ osl::FileBase::getSystemPathFromFileURL( GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE ), pImpl->m_aName );
+ else
+ {
+ DBG_ASSERT( pSalvageItem, "Suspicious change of logical name!" );
+ }
+ }
+ }
+
+ if ( pSalvageItem && !pSalvageItem->GetValue().isEmpty() )
+ {
+ std::unique_lock<std::recursive_mutex> chkEditLock;
+ if (pImpl->m_pCheckEditableWorkerMutex != nullptr)
+ chkEditLock
+ = std::unique_lock<std::recursive_mutex>(*(pImpl->m_pCheckEditableWorkerMutex));
+ pImpl->m_aLogicName = pSalvageItem->GetValue();
+ pImpl->m_pURLObj.reset();
+ if (chkEditLock.owns_lock())
+ chkEditLock.unlock();
+ pImpl->m_bSalvageMode = true;
+ }
+
+ // in case output stream is by mistake here
+ // clear the reference
+ const SfxUnoAnyItem* pOutStreamItem = SfxItemSet::GetItem<SfxUnoAnyItem>(pImpl->m_pSet.get(), SID_OUTPUTSTREAM, false);
+ if( pOutStreamItem
+ && ( !( pOutStreamItem->GetValue() >>= rOutStream )
+ || !pImpl->m_aLogicName.startsWith("private:stream")) )
+ {
+ pImpl->m_pSet->ClearItem( SID_OUTPUTSTREAM );
+ SAL_WARN( "sfx.doc", "Unexpected Output stream parameter!" );
+ }
+
+ if (!pImpl->m_aLogicName.isEmpty())
+ {
+ // if the logic name is set it should be set in MediaDescriptor as well
+ const SfxStringItem* pFileNameItem = SfxItemSet::GetItem<SfxStringItem>(pImpl->m_pSet.get(), SID_FILE_NAME, false);
+ if ( !pFileNameItem )
+ {
+ // let the ItemSet be created if necessary
+ GetItemSet()->Put(
+ SfxStringItem(
+ SID_FILE_NAME, INetURLObject( pImpl->m_aLogicName ).GetMainURL( INetURLObject::DecodeMechanism::NONE ) ) );
+ }
+ }
+
+ SetIsRemote_Impl();
+
+ osl::DirectoryItem item;
+ if (osl::DirectoryItem::get(GetName(), item) == osl::FileBase::E_None) {
+ osl::FileStatus stat(osl_FileStatus_Mask_Attributes);
+ if (item.getFileStatus(stat) == osl::FileBase::E_None
+ && stat.isValid(osl_FileStatus_Mask_Attributes))
+ {
+ if ((stat.getAttributes() & osl_File_Attribute_ReadOnly) != 0)
+ {
+ pImpl->m_bOriginallyReadOnly = true;
+ }
+ }
+ }
+}
+
+
+SfxMedium::SfxMedium() : pImpl(new SfxMedium_Impl)
+{
+ Init_Impl();
+}
+
+
+void SfxMedium::UseInteractionHandler( bool bUse )
+{
+ pImpl->bAllowDefaultIntHdl = bUse;
+}
+
+
+css::uno::Reference< css::task::XInteractionHandler >
+SfxMedium::GetInteractionHandler( bool bGetAlways )
+{
+ // if interaction isn't allowed explicitly ... return empty reference!
+ if ( !bGetAlways && !pImpl->bUseInteractionHandler )
+ return css::uno::Reference< css::task::XInteractionHandler >();
+
+ // search a possible existing handler inside cached item set
+ if ( pImpl->m_pSet )
+ {
+ css::uno::Reference< css::task::XInteractionHandler > xHandler;
+ const SfxUnoAnyItem* pHandler = SfxItemSet::GetItem<SfxUnoAnyItem>(pImpl->m_pSet.get(), SID_INTERACTIONHANDLER, false);
+ if ( pHandler && (pHandler->GetValue() >>= xHandler) && xHandler.is() )
+ return xHandler;
+ }
+
+ // if default interaction isn't allowed explicitly ... return empty reference!
+ if ( !bGetAlways && !pImpl->bAllowDefaultIntHdl )
+ return css::uno::Reference< css::task::XInteractionHandler >();
+
+ // otherwise return cached default handler ... if it exist.
+ if ( pImpl->xInteraction.is() )
+ return pImpl->xInteraction;
+
+ // create default handler and cache it!
+ Reference< uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext();
+ pImpl->xInteraction.set(
+ task::InteractionHandler::createWithParent(xContext, nullptr), UNO_QUERY_THROW );
+ return pImpl->xInteraction;
+}
+
+void SfxMedium::SetFilter( const std::shared_ptr<const SfxFilter>& pFilter )
+{
+ pImpl->m_pFilter = pFilter;
+}
+
+const std::shared_ptr<const SfxFilter>& SfxMedium::GetFilter() const
+{
+ return pImpl->m_pFilter;
+}
+
+sal_uInt32 SfxMedium::CreatePasswordToModifyHash( std::u16string_view aPasswd, bool bWriter )
+{
+ sal_uInt32 nHash = 0;
+
+ if ( !aPasswd.empty() )
+ {
+ if ( bWriter )
+ {
+ nHash = ::comphelper::DocPasswordHelper::GetWordHashAsUINT32( aPasswd );
+ }
+ else
+ {
+ rtl_TextEncoding nEncoding = osl_getThreadTextEncoding();
+ nHash = ::comphelper::DocPasswordHelper::GetXLHashAsUINT16( aPasswd, nEncoding );
+ }
+ }
+
+ return nHash;
+}
+
+
+void SfxMedium::Close(bool bInDestruction)
+{
+ if ( pImpl->xStorage.is() )
+ {
+ CloseStorage();
+ }
+
+ CloseStreams_Impl(bInDestruction);
+
+ UnlockFile( false );
+}
+
+void SfxMedium::CloseAndRelease()
+{
+ if ( pImpl->xStorage.is() )
+ {
+ CloseStorage();
+ }
+
+ CloseAndReleaseStreams_Impl();
+
+ UnlockFile( true );
+}
+
+void SfxMedium::DisableUnlockWebDAV( bool bDisableUnlockWebDAV )
+{
+ pImpl->m_bDisableUnlockWebDAV = bDisableUnlockWebDAV;
+}
+
+void SfxMedium::DisableFileSync(bool bDisableFileSync)
+{
+ pImpl->m_bDisableFileSync = bDisableFileSync;
+}
+
+void SfxMedium::UnlockFile( bool bReleaseLockStream )
+{
+#if !HAVE_FEATURE_MULTIUSER_ENVIRONMENT
+ (void) bReleaseLockStream;
+#else
+ // check if webdav
+ if ( GetURLObject().isAnyKnownWebDAVScheme() )
+ {
+ // do nothing if WebDAV locking if disabled
+ // (shouldn't happen because we already skipped locking,
+ // see LockOrigFileOnDemand, but just in case ...)
+ if (!IsWebDAVLockingUsed())
+ return;
+
+ if ( pImpl->m_bLocked )
+ {
+ // an interaction handler should be used for authentication, if needed
+ try {
+ uno::Reference< css::task::XInteractionHandler > xHandler = GetInteractionHandler( true );
+ uno::Reference< css::ucb::XCommandEnvironment > xComEnv = new ::ucbhelper::CommandEnvironment( xHandler,
+ Reference< css::ucb::XProgressHandler >() );
+ ucbhelper::Content aContentToUnlock( GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE ), xComEnv, comphelper::getProcessComponentContext());
+ pImpl->m_bLocked = false;
+ //check if WebDAV unlock was explicitly disabled
+ if ( !pImpl->m_bDisableUnlockWebDAV )
+ aContentToUnlock.unlock();
+ }
+ catch ( uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "sfx.doc", "Locking exception: WebDAV while trying to lock the file" );
+ }
+ }
+ return;
+ }
+
+ if ( pImpl->m_xLockingStream.is() )
+ {
+ if ( bReleaseLockStream )
+ {
+ try
+ {
+ uno::Reference< io::XInputStream > xInStream = pImpl->m_xLockingStream->getInputStream();
+ uno::Reference< io::XOutputStream > xOutStream = pImpl->m_xLockingStream->getOutputStream();
+ if ( xInStream.is() )
+ xInStream->closeInput();
+ if ( xOutStream.is() )
+ xOutStream->closeOutput();
+ }
+ catch( const uno::Exception& )
+ {}
+ }
+
+ pImpl->m_xLockingStream.clear();
+ }
+
+ if ( !pImpl->m_bLocked )
+ return;
+
+ ::svt::DocumentLockFile aLockFile( pImpl->m_aLogicName );
+
+ try
+ {
+ pImpl->m_bLocked = false;
+ // TODO/LATER: A warning could be shown in case the file is not the own one
+ aLockFile.RemoveFile();
+ }
+ catch( const io::WrongFormatException& )
+ {
+ try
+ {
+ // erase the empty or corrupt file
+ aLockFile.RemoveFileDirectly();
+ }
+ catch( const uno::Exception& )
+ {}
+ }
+ catch( const uno::Exception& )
+ {}
+
+ if(!pImpl->m_bMSOLockFileCreated)
+ return;
+
+ ::svt::MSODocumentLockFile aMSOLockFile( pImpl->m_aLogicName );
+
+ try
+ {
+ pImpl->m_bLocked = false;
+ // TODO/LATER: A warning could be shown in case the file is not the own one
+ aMSOLockFile.RemoveFile();
+ }
+ catch( const io::WrongFormatException& )
+ {
+ try
+ {
+ // erase the empty or corrupt file
+ aMSOLockFile.RemoveFileDirectly();
+ }
+ catch( const uno::Exception& )
+ {}
+ }
+ catch( const uno::Exception& )
+ {}
+ pImpl->m_bMSOLockFileCreated = false;
+#endif
+}
+
+void SfxMedium::CloseAndReleaseStreams_Impl()
+{
+ CloseZipStorage_Impl();
+
+ uno::Reference< io::XInputStream > xInToClose = pImpl->xInputStream;
+ uno::Reference< io::XOutputStream > xOutToClose;
+ if ( pImpl->xStream.is() )
+ {
+ xOutToClose = pImpl->xStream->getOutputStream();
+
+ // if the locking stream is closed here the related member should be cleaned
+ if ( pImpl->xStream == pImpl->m_xLockingStream )
+ pImpl->m_xLockingStream.clear();
+ }
+
+ // The probably existing SvStream wrappers should be closed first
+ CloseStreams_Impl();
+
+ // in case of salvage mode the storage is based on the streams
+ if ( pImpl->m_bSalvageMode )
+ return;
+
+ try
+ {
+ if ( xInToClose.is() )
+ xInToClose->closeInput();
+ if ( xOutToClose.is() )
+ xOutToClose->closeOutput();
+ }
+ catch ( const uno::Exception& )
+ {
+ }
+}
+
+
+void SfxMedium::CloseStreams_Impl(bool bInDestruction)
+{
+ CloseInStream_Impl(bInDestruction);
+ CloseOutStream_Impl();
+
+ if ( pImpl->m_pSet )
+ pImpl->m_pSet->ClearItem( SID_CONTENT );
+
+ pImpl->aContent = ::ucbhelper::Content();
+}
+
+
+void SfxMedium::SetIsRemote_Impl()
+{
+ INetURLObject aObj( GetName() );
+ switch( aObj.GetProtocol() )
+ {
+ case INetProtocol::Ftp:
+ case INetProtocol::Http:
+ case INetProtocol::Https:
+ pImpl->m_bRemote = true;
+ break;
+ default:
+ pImpl->m_bRemote = GetName().startsWith("private:msgid");
+ break;
+ }
+
+ // As files that are written to the remote transmission must also be able
+ // to be read.
+ if (pImpl->m_bRemote)
+ pImpl->m_nStorOpenMode |= StreamMode::READ;
+}
+
+
+void SfxMedium::SetName( const OUString& aNameP, bool bSetOrigURL )
+{
+ if (pImpl->aOrigURL.isEmpty())
+ pImpl->aOrigURL = pImpl->m_aLogicName;
+ if( bSetOrigURL )
+ pImpl->aOrigURL = aNameP;
+ std::unique_lock<std::recursive_mutex> chkEditLock;
+ if (pImpl->m_pCheckEditableWorkerMutex != nullptr)
+ chkEditLock = std::unique_lock<std::recursive_mutex>(*(pImpl->m_pCheckEditableWorkerMutex));
+ pImpl->m_aLogicName = aNameP;
+ pImpl->m_pURLObj.reset();
+ if (chkEditLock.owns_lock())
+ chkEditLock.unlock();
+ pImpl->aContent = ::ucbhelper::Content();
+ Init_Impl();
+}
+
+
+const OUString& SfxMedium::GetOrigURL() const
+{
+ return pImpl->aOrigURL.isEmpty() ? pImpl->m_aLogicName : pImpl->aOrigURL;
+}
+
+
+void SfxMedium::SetPhysicalName_Impl( const OUString& rNameP )
+{
+ if ( rNameP != pImpl->m_aName )
+ {
+ pImpl->pTempFile.reset();
+
+ if ( !pImpl->m_aName.isEmpty() || !rNameP.isEmpty() )
+ pImpl->aContent = ::ucbhelper::Content();
+
+ pImpl->m_aName = rNameP;
+ pImpl->m_bTriedStorage = false;
+ pImpl->bIsStorage = false;
+ }
+}
+
+void SfxMedium::ReOpen()
+{
+ bool bUseInteractionHandler = pImpl->bUseInteractionHandler;
+ pImpl->bUseInteractionHandler = false;
+ GetMedium_Impl();
+ pImpl->bUseInteractionHandler = bUseInteractionHandler;
+}
+
+void SfxMedium::CompleteReOpen()
+{
+ // do not use temporary file for reopen and in case of success throw the temporary file away
+ bool bUseInteractionHandler = pImpl->bUseInteractionHandler;
+ pImpl->bUseInteractionHandler = false;
+
+ std::unique_ptr<::utl::TempFile> pTmpFile;
+ if ( pImpl->pTempFile )
+ {
+ pTmpFile = std::move(pImpl->pTempFile);
+ pImpl->m_aName.clear();
+ }
+
+ GetMedium_Impl();
+
+ if ( GetError() )
+ {
+ if ( pImpl->pTempFile )
+ {
+ pImpl->pTempFile->EnableKillingFile();
+ pImpl->pTempFile.reset();
+ }
+ pImpl->pTempFile = std::move( pTmpFile );
+ if ( pImpl->pTempFile )
+ pImpl->m_aName = pImpl->pTempFile->GetFileName();
+ }
+ else if (pTmpFile)
+ {
+ pTmpFile->EnableKillingFile();
+ pTmpFile.reset();
+ }
+
+ pImpl->bUseInteractionHandler = bUseInteractionHandler;
+}
+
+SfxMedium::SfxMedium(const OUString &rName, StreamMode nOpenMode, std::shared_ptr<const SfxFilter> pFilter, const std::shared_ptr<SfxItemSet>& pInSet) :
+ pImpl(new SfxMedium_Impl)
+{
+ pImpl->m_pSet = pInSet;
+ pImpl->m_pFilter = std::move(pFilter);
+ pImpl->m_aLogicName = rName;
+ pImpl->m_nStorOpenMode = nOpenMode;
+ Init_Impl();
+}
+
+SfxMedium::SfxMedium(const OUString &rName, const OUString &rReferer, StreamMode nOpenMode, std::shared_ptr<const SfxFilter> pFilter, const std::shared_ptr<SfxItemSet>& pInSet) :
+ pImpl(new SfxMedium_Impl)
+{
+ pImpl->m_pSet = pInSet;
+ SfxItemSet * s = GetItemSet();
+ if (s->GetItem(SID_REFERER) == nullptr) {
+ s->Put(SfxStringItem(SID_REFERER, rReferer));
+ }
+ pImpl->m_pFilter = std::move(pFilter);
+ pImpl->m_aLogicName = rName;
+ pImpl->m_nStorOpenMode = nOpenMode;
+ Init_Impl();
+}
+
+SfxMedium::SfxMedium( const uno::Sequence<beans::PropertyValue>& aArgs ) :
+ pImpl(new SfxMedium_Impl)
+{
+ SfxAllItemSet *pParams = new SfxAllItemSet( SfxGetpApp()->GetPool() );
+ pImpl->m_pSet.reset( pParams );
+ TransformParameters( SID_OPENDOC, aArgs, *pParams );
+ SetArgs(aArgs);
+
+ OUString aFilterProvider, aFilterName;
+ {
+ const SfxStringItem* pItem = nullptr;
+ if ((pItem = pImpl->m_pSet->GetItemIfSet(SID_FILTER_PROVIDER)))
+ aFilterProvider = pItem->GetValue();
+
+ if ((pItem = pImpl->m_pSet->GetItemIfSet(SID_FILTER_NAME)))
+ aFilterName = pItem->GetValue();
+ }
+
+ if (aFilterProvider.isEmpty())
+ {
+ // This is a conventional filter type.
+ pImpl->m_pFilter = SfxGetpApp()->GetFilterMatcher().GetFilter4FilterName( aFilterName );
+ }
+ else
+ {
+ // This filter is from an external provider such as orcus.
+ pImpl->m_pCustomFilter = std::make_shared<SfxFilter>(aFilterProvider, aFilterName);
+ pImpl->m_pFilter = pImpl->m_pCustomFilter;
+ }
+
+ const SfxStringItem* pSalvageItem = SfxItemSet::GetItem<SfxStringItem>(pImpl->m_pSet.get(), SID_DOC_SALVAGE, false);
+ if( pSalvageItem )
+ {
+ // QUESTION: there is some treatment of Salvage in Init_Impl; align!
+ if ( !pSalvageItem->GetValue().isEmpty() )
+ {
+ // if a URL is provided in SalvageItem that means that the FileName refers to a temporary file
+ // that must be copied here
+
+ const SfxStringItem* pFileNameItem = SfxItemSet::GetItem<SfxStringItem>(pImpl->m_pSet.get(), SID_FILE_NAME, false);
+ if (!pFileNameItem) throw uno::RuntimeException();
+ OUString aNewTempFileURL = SfxMedium::CreateTempCopyWithExt( pFileNameItem->GetValue() );
+ if ( !aNewTempFileURL.isEmpty() )
+ {
+ pImpl->m_pSet->Put( SfxStringItem( SID_FILE_NAME, aNewTempFileURL ) );
+ pImpl->m_pSet->ClearItem( SID_INPUTSTREAM );
+ pImpl->m_pSet->ClearItem( SID_STREAM );
+ pImpl->m_pSet->ClearItem( SID_CONTENT );
+ }
+ else
+ {
+ SAL_WARN( "sfx.doc", "Can not create a new temporary file for crash recovery!" );
+ }
+ }
+ }
+
+ const SfxBoolItem* pReadOnlyItem = SfxItemSet::GetItem<SfxBoolItem>(pImpl->m_pSet.get(), SID_DOC_READONLY, false);
+ if ( pReadOnlyItem && pReadOnlyItem->GetValue() )
+ pImpl->m_bOriginallyLoadedReadOnly = true;
+
+ const SfxStringItem* pFileNameItem = SfxItemSet::GetItem<SfxStringItem>(pImpl->m_pSet.get(), SID_FILE_NAME, false);
+ if (!pFileNameItem) throw uno::RuntimeException();
+ pImpl->m_aLogicName = pFileNameItem->GetValue();
+ pImpl->m_nStorOpenMode = pImpl->m_bOriginallyLoadedReadOnly
+ ? SFX_STREAM_READONLY : SFX_STREAM_READWRITE;
+ Init_Impl();
+}
+
+void SfxMedium::SetArgs(const uno::Sequence<beans::PropertyValue>& rArgs)
+{
+ static constexpr OUStringLiteral sStream(u"Stream");
+ static constexpr OUStringLiteral sInputStream(u"InputStream");
+ comphelper::SequenceAsHashMap aArgsMap(rArgs);
+ aArgsMap.erase(sStream);
+ aArgsMap.erase(sInputStream);
+ pImpl->m_aArgs = aArgsMap.getAsConstPropertyValueList();
+}
+
+const uno::Sequence<beans::PropertyValue> & SfxMedium::GetArgs() const { return pImpl->m_aArgs; }
+
+SfxMedium::SfxMedium( const uno::Reference < embed::XStorage >& rStor, const OUString& rBaseURL, const std::shared_ptr<SfxItemSet>& p ) :
+ pImpl(new SfxMedium_Impl)
+{
+ OUString aType = SfxFilter::GetTypeFromStorage(rStor);
+ pImpl->m_pFilter = SfxGetpApp()->GetFilterMatcher().GetFilter4EA( aType );
+ DBG_ASSERT( pImpl->m_pFilter, "No Filter for storage found!" );
+
+ Init_Impl();
+ pImpl->xStorage = rStor;
+ pImpl->bDisposeStorage = false;
+
+ // always take BaseURL first, could be overwritten by ItemSet
+ GetItemSet()->Put( SfxStringItem( SID_DOC_BASEURL, rBaseURL ) );
+ if ( p )
+ GetItemSet()->Put( *p );
+}
+
+
+SfxMedium::SfxMedium( const uno::Reference < embed::XStorage >& rStor, const OUString& rBaseURL, const OUString &rTypeName, const std::shared_ptr<SfxItemSet>& p ) :
+ pImpl(new SfxMedium_Impl)
+{
+ pImpl->m_pFilter = SfxGetpApp()->GetFilterMatcher().GetFilter4EA( rTypeName );
+ DBG_ASSERT( pImpl->m_pFilter, "No Filter for storage found!" );
+
+ Init_Impl();
+ pImpl->xStorage = rStor;
+ pImpl->bDisposeStorage = false;
+
+ // always take BaseURL first, could be overwritten by ItemSet
+ GetItemSet()->Put( SfxStringItem( SID_DOC_BASEURL, rBaseURL ) );
+ if ( p )
+ GetItemSet()->Put( *p );
+}
+
+// NOTE: should only be called on main thread
+SfxMedium::~SfxMedium()
+{
+ CancelCheckEditableEntry();
+
+ // if there is a requirement to clean the backup this is the last possibility to do it
+ ClearBackup_Impl();
+
+ Close(/*bInDestruction*/true);
+
+ if( !pImpl->bIsTemp || pImpl->m_aName.isEmpty() )
+ return;
+
+ OUString aTemp;
+ if ( osl::FileBase::getFileURLFromSystemPath( pImpl->m_aName, aTemp )
+ != osl::FileBase::E_None )
+ {
+ SAL_WARN( "sfx.doc", "Physical name not convertible!");
+ }
+
+ if ( !::utl::UCBContentHelper::Kill( aTemp ) )
+ {
+ SAL_WARN( "sfx.doc", "Couldn't remove temporary file!");
+ }
+}
+
+const OUString& SfxMedium::GetName() const
+{
+ return pImpl->m_aLogicName;
+}
+
+const INetURLObject& SfxMedium::GetURLObject() const
+{
+ std::unique_lock<std::recursive_mutex> chkEditLock;
+ if (pImpl->m_pCheckEditableWorkerMutex != nullptr)
+ chkEditLock = std::unique_lock<std::recursive_mutex>(*(pImpl->m_pCheckEditableWorkerMutex));
+
+ if (!pImpl->m_pURLObj)
+ {
+ pImpl->m_pURLObj.reset( new INetURLObject( pImpl->m_aLogicName ) );
+ pImpl->m_pURLObj->SetMark(u"");
+ }
+
+ return *pImpl->m_pURLObj;
+}
+
+void SfxMedium::SetExpired_Impl( const DateTime& rDateTime )
+{
+ pImpl->aExpireTime = rDateTime;
+}
+
+
+bool SfxMedium::IsExpired() const
+{
+ return pImpl->aExpireTime.IsValidAndGregorian() && pImpl->aExpireTime < DateTime( DateTime::SYSTEM );
+}
+
+
+SfxFrame* SfxMedium::GetLoadTargetFrame() const
+{
+ return pImpl->wLoadTargetFrame;
+}
+
+void SfxMedium::setStreamToLoadFrom(const css::uno::Reference<css::io::XInputStream>& xInputStream, bool bIsReadOnly )
+{
+ pImpl->m_xInputStreamToLoadFrom = xInputStream;
+ pImpl->m_bInputStreamIsReadOnly = bIsReadOnly;
+}
+
+void SfxMedium::SetLoadTargetFrame(SfxFrame* pFrame )
+{
+ pImpl->wLoadTargetFrame = pFrame;
+}
+
+
+void SfxMedium::SetStorage_Impl( const uno::Reference < embed::XStorage >& rStor )
+{
+ pImpl->xStorage = rStor;
+}
+
+
+SfxItemSet* SfxMedium::GetItemSet() const
+{
+ // this method *must* return an ItemSet, returning NULL can cause crashes
+ if (!pImpl->m_pSet)
+ pImpl->m_pSet = std::make_shared<SfxAllItemSet>( SfxGetpApp()->GetPool() );
+ return pImpl->m_pSet.get();
+}
+
+
+SvKeyValueIterator* SfxMedium::GetHeaderAttributes_Impl()
+{
+ if( !pImpl->xAttributes.is() )
+ {
+ pImpl->xAttributes = SvKeyValueIteratorRef( new SvKeyValueIterator );
+
+ if ( GetContent().is() )
+ {
+ try
+ {
+ Any aAny = pImpl->aContent.getPropertyValue("MediaType");
+ OUString aContentType;
+ aAny >>= aContentType;
+
+ pImpl->xAttributes->Append( SvKeyValue( "content-type", aContentType ) );
+ }
+ catch ( const css::uno::Exception& )
+ {
+ }
+ }
+ }
+
+ return pImpl->xAttributes.get();
+}
+
+css::uno::Reference< css::io::XInputStream > const & SfxMedium::GetInputStream()
+{
+ if ( !pImpl->xInputStream.is() )
+ GetMedium_Impl();
+ return pImpl->xInputStream;
+}
+
+const uno::Sequence < util::RevisionTag >& SfxMedium::GetVersionList( bool _bNoReload )
+{
+ // if the medium has no name, then this medium should represent a new document and can have no version info
+ if ( ( !_bNoReload || !pImpl->m_bVersionsAlreadyLoaded ) && !pImpl->aVersions.hasElements() &&
+ ( !pImpl->m_aName.isEmpty() || !pImpl->m_aLogicName.isEmpty() ) && GetStorage().is() )
+ {
+ uno::Reference < document::XDocumentRevisionListPersistence > xReader =
+ document::DocumentRevisionListPersistence::create( comphelper::getProcessComponentContext() );
+ try
+ {
+ pImpl->aVersions = xReader->load( GetStorage() );
+ }
+ catch ( const uno::Exception& )
+ {
+ }
+ }
+
+ if ( !pImpl->m_bVersionsAlreadyLoaded )
+ pImpl->m_bVersionsAlreadyLoaded = true;
+
+ return pImpl->aVersions;
+}
+
+uno::Sequence < util::RevisionTag > SfxMedium::GetVersionList( const uno::Reference < embed::XStorage >& xStorage )
+{
+ uno::Reference < document::XDocumentRevisionListPersistence > xReader =
+ document::DocumentRevisionListPersistence::create( comphelper::getProcessComponentContext() );
+ try
+ {
+ return xReader->load( xStorage );
+ }
+ catch ( const uno::Exception& )
+ {
+ }
+
+ return uno::Sequence < util::RevisionTag >();
+}
+
+void SfxMedium::AddVersion_Impl( util::RevisionTag& rRevision )
+{
+ if ( !GetStorage().is() )
+ return;
+
+ // To determine a unique name for the stream
+ std::vector<sal_uInt32> aLongs;
+ sal_Int32 nLength = pImpl->aVersions.getLength();
+ for ( const auto& rVersion : std::as_const(pImpl->aVersions) )
+ {
+ sal_uInt32 nVer = static_cast<sal_uInt32>( o3tl::toInt32(rVersion.Identifier.subView(7)));
+ size_t n;
+ for ( n=0; n<aLongs.size(); ++n )
+ if ( nVer<aLongs[n] )
+ break;
+
+ aLongs.insert( aLongs.begin()+n, nVer );
+ }
+
+ std::vector<sal_uInt32>::size_type nKey;
+ for ( nKey=0; nKey<aLongs.size(); ++nKey )
+ if ( aLongs[nKey] > nKey+1 )
+ break;
+
+ OUString aRevName = "Version" + OUString::number( nKey + 1 );
+ pImpl->aVersions.realloc( nLength+1 );
+ rRevision.Identifier = aRevName;
+ pImpl->aVersions.getArray()[nLength] = rRevision;
+}
+
+void SfxMedium::RemoveVersion_Impl( const OUString& rName )
+{
+ if ( !pImpl->aVersions.hasElements() )
+ return;
+
+ auto pVersion = std::find_if(std::cbegin(pImpl->aVersions), std::cend(pImpl->aVersions),
+ [&rName](const auto& rVersion) { return rVersion.Identifier == rName; });
+ if (pVersion != std::cend(pImpl->aVersions))
+ {
+ auto nIndex = static_cast<sal_Int32>(std::distance(std::cbegin(pImpl->aVersions), pVersion));
+ comphelper::removeElementAt(pImpl->aVersions, nIndex);
+ }
+}
+
+bool SfxMedium::TransferVersionList_Impl( SfxMedium const & rMedium )
+{
+ if ( rMedium.pImpl->aVersions.hasElements() )
+ {
+ pImpl->aVersions = rMedium.pImpl->aVersions;
+ return true;
+ }
+
+ return false;
+}
+
+void SfxMedium::SaveVersionList_Impl()
+{
+ if ( !GetStorage().is() )
+ return;
+
+ if ( !pImpl->aVersions.hasElements() )
+ return;
+
+ uno::Reference < document::XDocumentRevisionListPersistence > xWriter =
+ document::DocumentRevisionListPersistence::create( comphelper::getProcessComponentContext() );
+ try
+ {
+ xWriter->store( GetStorage(), pImpl->aVersions );
+ }
+ catch ( const uno::Exception& )
+ {
+ }
+}
+
+bool SfxMedium::IsReadOnly() const
+{
+ // a) ReadOnly filter can't produce read/write contents!
+ bool bReadOnly = pImpl->m_pFilter && (pImpl->m_pFilter->GetFilterFlags() & SfxFilterFlags::OPENREADONLY);
+
+ // b) if filter allow read/write contents .. check open mode of the storage
+ if (!bReadOnly)
+ bReadOnly = !( GetOpenMode() & StreamMode::WRITE );
+
+ // c) the API can force the readonly state!
+ if (!bReadOnly)
+ {
+ const SfxBoolItem* pItem = SfxItemSet::GetItem<SfxBoolItem>(GetItemSet(), SID_DOC_READONLY, false);
+ if (pItem)
+ bReadOnly = pItem->GetValue();
+ }
+
+ return bReadOnly;
+}
+
+bool SfxMedium::IsOriginallyReadOnly() const
+{
+ return pImpl->m_bOriginallyReadOnly;
+}
+
+void SfxMedium::SetOriginallyReadOnly(bool val)
+{
+ pImpl->m_bOriginallyReadOnly = val;
+}
+
+bool SfxMedium::IsOriginallyLoadedReadOnly() const
+{
+ return pImpl->m_bOriginallyLoadedReadOnly;
+}
+
+bool SfxMedium::SetWritableForUserOnly( const OUString& aURL )
+{
+ // UCB does not allow to allow write access only for the user,
+ // use osl API
+ bool bResult = false;
+
+ ::osl::DirectoryItem aDirItem;
+ if ( ::osl::DirectoryItem::get( aURL, aDirItem ) == ::osl::FileBase::E_None )
+ {
+ ::osl::FileStatus aFileStatus( osl_FileStatus_Mask_Attributes );
+ if ( aDirItem.getFileStatus( aFileStatus ) == osl::FileBase::E_None
+ && aFileStatus.isValid( osl_FileStatus_Mask_Attributes ) )
+ {
+ sal_uInt64 nAttributes = aFileStatus.getAttributes();
+
+ nAttributes &= ~(osl_File_Attribute_OwnWrite |
+ osl_File_Attribute_GrpWrite |
+ osl_File_Attribute_OthWrite |
+ osl_File_Attribute_ReadOnly);
+ nAttributes |= (osl_File_Attribute_OwnWrite |
+ osl_File_Attribute_OwnRead);
+
+ bResult = ( osl::File::setAttributes( aURL, nAttributes ) == ::osl::FileBase::E_None );
+ }
+ }
+
+ return bResult;
+}
+
+namespace
+{
+/// Get the parent directory of a temporary file for output purposes.
+OUString GetLogicBase(const INetURLObject& rURL, std::unique_ptr<SfxMedium_Impl> const & pImpl)
+{
+ OUString aLogicBase;
+
+#if HAVE_FEATURE_MACOSX_SANDBOX
+ // In a sandboxed environment we don't want to attempt to create temporary files in the same
+ // directory where the user has selected an output file to be stored. The sandboxed process has
+ // permission only to create the specifically named output file in that directory.
+ (void) rURL;
+ (void) pImpl;
+#else
+
+ if (!pImpl->m_bHasEmbeddedObjects // Embedded objects would mean a special base, ignore that.
+ && rURL.GetProtocol() == INetProtocol::File && !pImpl->m_pInStream)
+ {
+ // Try to create the temp file in the same directory when storing.
+ INetURLObject aURL(rURL);
+ aURL.removeSegment();
+ aLogicBase = aURL.GetMainURL(INetURLObject::DecodeMechanism::WithCharset);
+ }
+
+#endif // !HAVE_FEATURE_MACOSX_SANDBOX
+
+ return aLogicBase;
+}
+}
+
+void SfxMedium::CreateTempFile( bool bReplace )
+{
+ if ( pImpl->pTempFile )
+ {
+ if ( !bReplace )
+ return;
+
+ pImpl->pTempFile.reset();
+ pImpl->m_aName.clear();
+ }
+
+ OUString aLogicBase = GetLogicBase(GetURLObject(), pImpl);
+ pImpl->pTempFile.reset(new ::utl::TempFile(&aLogicBase));
+ pImpl->pTempFile->EnableKillingFile();
+ pImpl->m_aName = pImpl->pTempFile->GetFileName();
+ OUString aTmpURL = pImpl->pTempFile->GetURL();
+ if ( pImpl->m_aName.isEmpty() || aTmpURL.isEmpty() )
+ {
+ SetError(ERRCODE_IO_CANTWRITE);
+ return;
+ }
+
+ if ( !(pImpl->m_nStorOpenMode & StreamMode::TRUNC) )
+ {
+ bool bTransferSuccess = false;
+
+ if ( GetContent().is()
+ && GetURLObject().GetProtocol() == INetProtocol::File
+ && ::utl::UCBContentHelper::IsDocument( GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE ) ) )
+ {
+ // if there is already such a document, we should copy it
+ // if it is a file system use OS copy process
+ try
+ {
+ uno::Reference< css::ucb::XCommandEnvironment > xComEnv;
+ INetURLObject aTmpURLObj( aTmpURL );
+ OUString aFileName = aTmpURLObj.getName( INetURLObject::LAST_SEGMENT,
+ true,
+ INetURLObject::DecodeMechanism::WithCharset );
+ if ( !aFileName.isEmpty() && aTmpURLObj.removeSegment() )
+ {
+ ::ucbhelper::Content aTargetContent( aTmpURLObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ), xComEnv, comphelper::getProcessComponentContext() );
+ OUString sMimeType = pImpl->getFilterMimeType();
+ aTargetContent.transferContent( pImpl->aContent, ::ucbhelper::InsertOperation::Copy, aFileName, NameClash::OVERWRITE, sMimeType );
+ SetWritableForUserOnly( aTmpURL );
+ bTransferSuccess = true;
+ }
+ }
+ catch( const uno::Exception& )
+ {}
+
+ if ( bTransferSuccess )
+ {
+ CloseOutStream();
+ CloseInStream();
+ }
+ }
+
+ if ( !bTransferSuccess && pImpl->m_pInStream )
+ {
+ // the case when there is no URL-access available or this is a remote protocol
+ // but there is an input stream
+ GetOutStream();
+ if ( pImpl->m_pOutStream )
+ {
+ std::unique_ptr<char[]> pBuf(new char [8192]);
+ ErrCode nErr = ERRCODE_NONE;
+
+ pImpl->m_pInStream->Seek(0);
+ pImpl->m_pOutStream->Seek(0);
+
+ while( !pImpl->m_pInStream->eof() && nErr == ERRCODE_NONE )
+ {
+ sal_uInt32 nRead = pImpl->m_pInStream->ReadBytes(pBuf.get(), 8192);
+ nErr = pImpl->m_pInStream->GetError();
+ pImpl->m_pOutStream->WriteBytes( pBuf.get(), nRead );
+ }
+
+ bTransferSuccess = true;
+ CloseInStream();
+ }
+ CloseOutStream_Impl();
+ }
+ else
+ {
+ // Quite strange design, but currently it is expected that in this case no transfer happens
+ // TODO/LATER: get rid of this inconsistent part of the call design
+ bTransferSuccess = true;
+ CloseInStream();
+ }
+
+ if ( !bTransferSuccess )
+ {
+ SetError(ERRCODE_IO_CANTWRITE);
+ return;
+ }
+ }
+
+ CloseStorage();
+}
+
+
+void SfxMedium::CreateTempFileNoCopy()
+{
+ // this call always replaces the existing temporary file
+ pImpl->pTempFile.reset();
+
+ OUString aLogicBase = GetLogicBase(GetURLObject(), pImpl);
+ pImpl->pTempFile.reset(new ::utl::TempFile(&aLogicBase));
+ pImpl->pTempFile->EnableKillingFile();
+ pImpl->m_aName = pImpl->pTempFile->GetFileName();
+ if ( pImpl->m_aName.isEmpty() )
+ {
+ SetError(ERRCODE_IO_CANTWRITE);
+ return;
+ }
+
+ CloseOutStream_Impl();
+ CloseStorage();
+}
+
+bool SfxMedium::SignDocumentContentUsingCertificate(
+ const css::uno::Reference<css::frame::XModel>& xModel, bool bHasValidDocumentSignature,
+ const Reference<XCertificate>& xCertificate)
+{
+ bool bChanges = false;
+
+ if (IsOpen() || GetError())
+ {
+ SAL_WARN("sfx.doc", "The medium must be closed by the signer!");
+ return bChanges;
+ }
+
+ // The component should know if there was a valid document signature, since
+ // it should show a warning in this case
+ OUString aODFVersion(comphelper::OStorageHelper::GetODFVersionFromStorage(GetStorage()));
+ uno::Reference< security::XDocumentDigitalSignatures > xSigner(
+ security::DocumentDigitalSignatures::createWithVersionAndValidSignature(
+ comphelper::getProcessComponentContext(), aODFVersion, bHasValidDocumentSignature ) );
+ auto xModelSigner = dynamic_cast<sfx2::DigitalSignatures*>(xSigner.get());
+ if (!xModelSigner)
+ {
+ return bChanges;
+ }
+
+ uno::Reference< embed::XStorage > xWriteableZipStor;
+
+ // we can reuse the temporary file if there is one already
+ CreateTempFile( false );
+ GetMedium_Impl();
+
+ try
+ {
+ if ( !pImpl->xStream.is() )
+ throw uno::RuntimeException();
+
+ bool bODF = GetFilter()->IsOwnFormat();
+ try
+ {
+ xWriteableZipStor = ::comphelper::OStorageHelper::GetStorageOfFormatFromStream( ZIP_STORAGE_FORMAT_STRING, pImpl->xStream );
+ }
+ catch (const io::IOException&)
+ {
+ if (bODF)
+ {
+ TOOLS_WARN_EXCEPTION("sfx.doc", "ODF stream is not a zip storage");
+ }
+ }
+
+ if ( !xWriteableZipStor.is() && bODF )
+ throw uno::RuntimeException();
+
+ uno::Reference< embed::XStorage > xMetaInf;
+ if (xWriteableZipStor.is() && xWriteableZipStor->hasByName("META-INF"))
+ {
+ xMetaInf = xWriteableZipStor->openStorageElement(
+ "META-INF",
+ embed::ElementModes::READWRITE );
+ if ( !xMetaInf.is() )
+ throw uno::RuntimeException();
+ }
+
+ {
+ if (xMetaInf.is())
+ {
+ // ODF.
+ uno::Reference< io::XStream > xStream;
+ if (GetFilter() && GetFilter()->IsOwnFormat())
+ xStream.set(xMetaInf->openStreamElement(xSigner->getDocumentContentSignatureDefaultStreamName(), embed::ElementModes::READWRITE), uno::UNO_SET_THROW);
+
+ bool bSuccess = xModelSigner->SignModelWithCertificate(
+ xModel, xCertificate, GetZipStorageToSign_Impl(), xStream);
+
+ if (bSuccess)
+ {
+ uno::Reference< embed::XTransactedObject > xTransact( xMetaInf, uno::UNO_QUERY_THROW );
+ xTransact->commit();
+ xTransact.set( xWriteableZipStor, uno::UNO_QUERY_THROW );
+ xTransact->commit();
+
+ // the temporary file has been written, commit it to the original file
+ Commit();
+ bChanges = true;
+ }
+ }
+ else if (xWriteableZipStor.is())
+ {
+ // OOXML.
+ uno::Reference<io::XStream> xStream;
+
+ // We need read-write to be able to add the signature relation.
+ bool bSuccess = xModelSigner->SignModelWithCertificate(
+ xModel, xCertificate, GetZipStorageToSign_Impl(/*bReadOnly=*/false), xStream);
+
+ if (bSuccess)
+ {
+ uno::Reference<embed::XTransactedObject> xTransact(xWriteableZipStor, uno::UNO_QUERY_THROW);
+ xTransact->commit();
+
+ // the temporary file has been written, commit it to the original file
+ Commit();
+ bChanges = true;
+ }
+ }
+ else
+ {
+ // Something not ZIP based: e.g. PDF.
+ std::unique_ptr<SvStream> pStream(utl::UcbStreamHelper::CreateStream(GetName(), StreamMode::READ | StreamMode::WRITE));
+ uno::Reference<io::XStream> xStream(new utl::OStreamWrapper(*pStream));
+ if (xModelSigner->SignModelWithCertificate(
+ xModel, xCertificate, uno::Reference<embed::XStorage>(), xStream))
+ bChanges = true;
+ }
+ }
+ }
+ catch ( const uno::Exception& )
+ {
+ SAL_WARN( "sfx.doc", "Couldn't use signing functionality!" );
+ }
+
+ CloseAndRelease();
+
+ ResetError();
+
+ return bChanges;
+}
+
+bool SfxMedium::SignContents_Impl(weld::Window* pDialogParent,
+ bool bSignScriptingContent,
+ bool bHasValidDocumentSignature,
+ const OUString& aSignatureLineId,
+ const Reference<XCertificate>& xCert,
+ const Reference<XGraphic>& xValidGraphic,
+ const Reference<XGraphic>& xInvalidGraphic,
+ const OUString& aComment)
+{
+ bool bChanges = false;
+
+ if (IsOpen() || GetError())
+ {
+ SAL_WARN("sfx.doc", "The medium must be closed by the signer!");
+ return bChanges;
+ }
+
+ // The component should know if there was a valid document signature, since
+ // it should show a warning in this case
+ OUString aODFVersion(comphelper::OStorageHelper::GetODFVersionFromStorage(GetStorage()));
+ uno::Reference< security::XDocumentDigitalSignatures > xSigner(
+ security::DocumentDigitalSignatures::createWithVersionAndValidSignature(
+ comphelper::getProcessComponentContext(), aODFVersion, bHasValidDocumentSignature ) );
+ if (pDialogParent)
+ xSigner->setParentWindow(pDialogParent->GetXWindow());
+
+ uno::Reference< embed::XStorage > xWriteableZipStor;
+
+ // we can reuse the temporary file if there is one already
+ CreateTempFile( false );
+ GetMedium_Impl();
+
+ try
+ {
+ if ( !pImpl->xStream.is() )
+ throw uno::RuntimeException();
+
+ bool bODF = GetFilter()->IsOwnFormat();
+ try
+ {
+ xWriteableZipStor = ::comphelper::OStorageHelper::GetStorageOfFormatFromStream( ZIP_STORAGE_FORMAT_STRING, pImpl->xStream );
+ }
+ catch (const io::IOException&)
+ {
+ if (bODF)
+ {
+ TOOLS_WARN_EXCEPTION("sfx.doc", "ODF stream is not a zip storage");
+ }
+ }
+
+ if ( !xWriteableZipStor.is() && bODF )
+ throw uno::RuntimeException();
+
+ uno::Reference< embed::XStorage > xMetaInf;
+ if (xWriteableZipStor.is() && xWriteableZipStor->hasByName("META-INF"))
+ {
+ xMetaInf = xWriteableZipStor->openStorageElement(
+ "META-INF",
+ embed::ElementModes::READWRITE );
+ if ( !xMetaInf.is() )
+ throw uno::RuntimeException();
+ }
+
+ if ( bSignScriptingContent )
+ {
+ // If the signature has already the document signature it will be removed
+ // after the scripting signature is inserted.
+ uno::Reference< io::XStream > xStream(
+ xMetaInf->openStreamElement( xSigner->getScriptingContentSignatureDefaultStreamName(),
+ embed::ElementModes::READWRITE ),
+ uno::UNO_SET_THROW );
+
+ if ( xSigner->signScriptingContent( GetZipStorageToSign_Impl(), xStream ) )
+ {
+ // remove the document signature if any
+ OUString aDocSigName = xSigner->getDocumentContentSignatureDefaultStreamName();
+ if ( !aDocSigName.isEmpty() && xMetaInf->hasByName( aDocSigName ) )
+ xMetaInf->removeElement( aDocSigName );
+
+ uno::Reference< embed::XTransactedObject > xTransact( xMetaInf, uno::UNO_QUERY_THROW );
+ xTransact->commit();
+ xTransact.set( xWriteableZipStor, uno::UNO_QUERY_THROW );
+ xTransact->commit();
+
+ // the temporary file has been written, commit it to the original file
+ Commit();
+ bChanges = true;
+ }
+ }
+ else
+ {
+ if (xMetaInf.is())
+ {
+ // ODF.
+ uno::Reference< io::XStream > xStream;
+ if (GetFilter() && GetFilter()->IsOwnFormat())
+ xStream.set(xMetaInf->openStreamElement(xSigner->getDocumentContentSignatureDefaultStreamName(), embed::ElementModes::READWRITE), uno::UNO_SET_THROW);
+
+ bool bSuccess = false;
+ if (xCert.is())
+ bSuccess = xSigner->signSignatureLine(
+ GetZipStorageToSign_Impl(), xStream, aSignatureLineId, xCert,
+ xValidGraphic, xInvalidGraphic, aComment);
+ else
+ bSuccess = xSigner->signDocumentContent(GetZipStorageToSign_Impl(),
+ xStream);
+
+ if (bSuccess)
+ {
+ uno::Reference< embed::XTransactedObject > xTransact( xMetaInf, uno::UNO_QUERY_THROW );
+ xTransact->commit();
+ xTransact.set( xWriteableZipStor, uno::UNO_QUERY_THROW );
+ xTransact->commit();
+
+ // the temporary file has been written, commit it to the original file
+ Commit();
+ bChanges = true;
+ }
+ }
+ else if (xWriteableZipStor.is())
+ {
+ // OOXML.
+ uno::Reference<io::XStream> xStream;
+
+ bool bSuccess = false;
+ if (xCert.is())
+ {
+ bSuccess = xSigner->signSignatureLine(
+ GetZipStorageToSign_Impl(/*bReadOnly=*/false), xStream, aSignatureLineId,
+ xCert, xValidGraphic, xInvalidGraphic, aComment);
+ }
+ else
+ {
+ // We need read-write to be able to add the signature relation.
+ bSuccess =xSigner->signDocumentContent(
+ GetZipStorageToSign_Impl(/*bReadOnly=*/false), xStream);
+ }
+
+ if (bSuccess)
+ {
+ uno::Reference<embed::XTransactedObject> xTransact(xWriteableZipStor, uno::UNO_QUERY_THROW);
+ xTransact->commit();
+
+ // the temporary file has been written, commit it to the original file
+ Commit();
+ bChanges = true;
+ }
+ }
+ else
+ {
+ // Something not ZIP based: e.g. PDF.
+ std::unique_ptr<SvStream> pStream(utl::UcbStreamHelper::CreateStream(GetName(), StreamMode::READ | StreamMode::WRITE));
+ uno::Reference<io::XStream> xStream(new utl::OStreamWrapper(*pStream));
+ if (xSigner->signDocumentContent(uno::Reference<embed::XStorage>(), xStream))
+ bChanges = true;
+ }
+ }
+ }
+ catch ( const uno::Exception& )
+ {
+ SAL_WARN( "sfx.doc", "Couldn't use signing functionality!" );
+ }
+
+ CloseAndRelease();
+
+ ResetError();
+
+ return bChanges;
+}
+
+
+SignatureState SfxMedium::GetCachedSignatureState_Impl() const
+{
+ return pImpl->m_nSignatureState;
+}
+
+
+void SfxMedium::SetCachedSignatureState_Impl( SignatureState nState )
+{
+ pImpl->m_nSignatureState = nState;
+}
+
+void SfxMedium::SetHasEmbeddedObjects(bool bHasEmbeddedObjects)
+{
+ pImpl->m_bHasEmbeddedObjects = bHasEmbeddedObjects;
+}
+
+bool SfxMedium::HasStorage_Impl() const
+{
+ return pImpl->xStorage.is();
+}
+
+bool SfxMedium::IsOpen() const
+{
+ return pImpl->m_pInStream || pImpl->m_pOutStream || pImpl->xStorage.is();
+}
+
+OUString SfxMedium::CreateTempCopyWithExt( std::u16string_view aURL )
+{
+ OUString aResult;
+
+ if ( !aURL.empty() )
+ {
+ size_t nPrefixLen = aURL.rfind( '.' );
+ OUString aExt = ( nPrefixLen == std::u16string_view::npos ) ? OUString() : OUString(aURL.substr( nPrefixLen ));
+
+ OUString aNewTempFileURL = ::utl::TempFile( u"", true, &aExt ).GetURL();
+ if ( !aNewTempFileURL.isEmpty() )
+ {
+ INetURLObject aSource( aURL );
+ INetURLObject aDest( aNewTempFileURL );
+ OUString aFileName = aDest.getName( INetURLObject::LAST_SEGMENT,
+ true,
+ INetURLObject::DecodeMechanism::WithCharset );
+ if ( !aFileName.isEmpty() && aDest.removeSegment() )
+ {
+ try
+ {
+ uno::Reference< css::ucb::XCommandEnvironment > xComEnv;
+ ::ucbhelper::Content aTargetContent( aDest.GetMainURL( INetURLObject::DecodeMechanism::NONE ), xComEnv, comphelper::getProcessComponentContext() );
+ ::ucbhelper::Content aSourceContent( aSource.GetMainURL( INetURLObject::DecodeMechanism::NONE ), xComEnv, comphelper::getProcessComponentContext() );
+ aTargetContent.transferContent( aSourceContent,
+ ::ucbhelper::InsertOperation::Copy,
+ aFileName,
+ NameClash::OVERWRITE );
+ aResult = aNewTempFileURL;
+ }
+ catch( const uno::Exception& )
+ {}
+ }
+ }
+ }
+
+ return aResult;
+}
+
+bool SfxMedium::CallApproveHandler(const uno::Reference< task::XInteractionHandler >& xHandler, const uno::Any& rRequest, bool bAllowAbort)
+{
+ bool bResult = false;
+
+ if ( xHandler.is() )
+ {
+ try
+ {
+ uno::Sequence< uno::Reference< task::XInteractionContinuation > > aContinuations( bAllowAbort ? 2 : 1 );
+ auto pContinuations = aContinuations.getArray();
+
+ ::rtl::Reference< ::comphelper::OInteractionApprove > pApprove( new ::comphelper::OInteractionApprove );
+ pContinuations[ 0 ] = pApprove.get();
+
+ if ( bAllowAbort )
+ {
+ ::rtl::Reference< ::comphelper::OInteractionAbort > pAbort( new ::comphelper::OInteractionAbort );
+ pContinuations[ 1 ] = pAbort.get();
+ }
+
+ xHandler->handle(::framework::InteractionRequest::CreateRequest(rRequest, aContinuations));
+ bResult = pApprove->wasSelected();
+ }
+ catch( const Exception& )
+ {
+ }
+ }
+
+ return bResult;
+}
+
+OUString SfxMedium::SwitchDocumentToTempFile()
+{
+ // the method returns empty string in case of failure
+ OUString aResult;
+ OUString aOrigURL = pImpl->m_aLogicName;
+
+ if ( !aOrigURL.isEmpty() )
+ {
+ sal_Int32 nPrefixLen = aOrigURL.lastIndexOf( '.' );
+ OUString const aExt = (nPrefixLen == -1)
+ ? OUString()
+ : aOrigURL.copy(nPrefixLen);
+ OUString aNewURL = ::utl::TempFile( u"", true, &aExt ).GetURL();
+
+ // TODO/LATER: In future the aLogicName should be set to shared folder URL
+ // and a temporary file should be created. Transport_Impl should be impossible then.
+ if ( !aNewURL.isEmpty() )
+ {
+ uno::Reference< embed::XStorage > xStorage = GetStorage();
+ uno::Reference< embed::XOptimizedStorage > xOptStorage( xStorage, uno::UNO_QUERY );
+
+ if ( xOptStorage.is() )
+ {
+ // TODO/LATER: reuse the pImpl->pTempFile if it already exists
+ CanDisposeStorage_Impl( false );
+ Close();
+ SetPhysicalName_Impl( OUString() );
+ SetName( aNewURL );
+
+ // remove the readonly state
+ bool bWasReadonly = false;
+ pImpl->m_nStorOpenMode = SFX_STREAM_READWRITE;
+ const SfxBoolItem* pReadOnlyItem = SfxItemSet::GetItem<SfxBoolItem>(pImpl->m_pSet.get(), SID_DOC_READONLY, false);
+ if ( pReadOnlyItem && pReadOnlyItem->GetValue() )
+ bWasReadonly = true;
+ GetItemSet()->ClearItem( SID_DOC_READONLY );
+
+ GetMedium_Impl();
+ LockOrigFileOnDemand( false, false );
+ CreateTempFile();
+ GetMedium_Impl();
+
+ if ( pImpl->xStream.is() )
+ {
+ try
+ {
+ xOptStorage->writeAndAttachToStream( pImpl->xStream );
+ pImpl->xStorage = xStorage;
+ aResult = aNewURL;
+ }
+ catch( const uno::Exception& )
+ {}
+ }
+
+ if (bWasReadonly)
+ {
+ // set the readonly state back
+ pImpl->m_nStorOpenMode = SFX_STREAM_READONLY;
+ GetItemSet()->Put(SfxBoolItem(SID_DOC_READONLY, true));
+ }
+
+ if ( aResult.isEmpty() )
+ {
+ Close();
+ SetPhysicalName_Impl( OUString() );
+ SetName( aOrigURL );
+ GetMedium_Impl();
+ pImpl->xStorage = xStorage;
+ }
+ }
+ }
+ }
+
+ return aResult;
+}
+
+bool SfxMedium::SwitchDocumentToFile( const OUString& aURL )
+{
+ // the method is only for storage based documents
+ bool bResult = false;
+ OUString aOrigURL = pImpl->m_aLogicName;
+
+ if ( !aURL.isEmpty() && !aOrigURL.isEmpty() )
+ {
+ uno::Reference< embed::XStorage > xStorage = GetStorage();
+ uno::Reference< embed::XOptimizedStorage > xOptStorage( xStorage, uno::UNO_QUERY );
+
+ // TODO/LATER: reuse the pImpl->pTempFile if it already exists
+ CanDisposeStorage_Impl( false );
+ Close();
+ SetPhysicalName_Impl( OUString() );
+ SetName( aURL );
+
+ // open the temporary file based document
+ GetMedium_Impl();
+ LockOrigFileOnDemand( false, false );
+ CreateTempFile();
+ GetMedium_Impl();
+
+ if ( pImpl->xStream.is() )
+ {
+ try
+ {
+ uno::Reference< io::XTruncate > xTruncate( pImpl->xStream, uno::UNO_QUERY_THROW );
+ xTruncate->truncate();
+ if ( xOptStorage.is() )
+ xOptStorage->writeAndAttachToStream( pImpl->xStream );
+ pImpl->xStorage = xStorage;
+ bResult = true;
+ }
+ catch( const uno::Exception& )
+ {}
+ }
+
+ if ( !bResult )
+ {
+ Close();
+ SetPhysicalName_Impl( OUString() );
+ SetName( aOrigURL );
+ GetMedium_Impl();
+ pImpl->xStorage = xStorage;
+ }
+ }
+
+ return bResult;
+}
+
+void SfxMedium::SetInCheckIn( bool bInCheckIn )
+{
+ pImpl->m_bInCheckIn = bInCheckIn;
+}
+
+bool SfxMedium::IsInCheckIn( ) const
+{
+ return pImpl->m_bInCheckIn;
+}
+
+// should only be called on main thread
+const std::shared_ptr<std::recursive_mutex>& SfxMedium::GetCheckEditableMutex() const
+{
+ return pImpl->m_pCheckEditableWorkerMutex;
+}
+
+// should only be called while holding pImpl->m_pCheckEditableWorkerMutex
+void SfxMedium::SetWorkerReloadEvent(ImplSVEvent* pEvent)
+{
+ pImpl->m_pReloadEvent = pEvent;
+}
+
+// should only be called while holding pImpl->m_pCheckEditableWorkerMutex
+ImplSVEvent* SfxMedium::GetWorkerReloadEvent() const
+{
+ return pImpl->m_pReloadEvent;
+}
+
+// should only be called on main thread
+void SfxMedium::AddToCheckEditableWorkerList()
+{
+ if (!pImpl->m_bNotifyWhenEditable)
+ return;
+
+ CancelCheckEditableEntry();
+
+ if (pImpl->m_pCheckEditableWorkerMutex == nullptr)
+ {
+ pImpl->m_pCheckEditableWorkerMutex = std::make_shared<std::recursive_mutex>();
+ if (pImpl->m_pCheckEditableWorkerMutex == nullptr)
+ return;
+ }
+
+ pImpl->m_pIsDestructed = std::make_shared<bool>(false);
+ if (pImpl->m_pIsDestructed == nullptr)
+ return;
+
+ std::unique_lock<std::mutex> globalLock(g_chkReadOnlyGlobalMutex);
+ if (g_newReadOnlyDocs.find(this) == g_newReadOnlyDocs.end())
+ {
+ bool bAddNewEntry = false;
+ if (!g_bChkReadOnlyTaskRunning)
+ {
+ std::shared_ptr<comphelper::ThreadTaskTag> pTag
+ = comphelper::ThreadPool::createThreadTaskTag();
+ if (pTag != nullptr)
+ {
+ g_bChkReadOnlyTaskRunning = true;
+ bAddNewEntry = true;
+ comphelper::ThreadPool::getSharedOptimalPool().pushTask(
+ std::make_unique<CheckReadOnlyTask>(pTag));
+ }
+ }
+ else
+ bAddNewEntry = true;
+
+ if (bAddNewEntry)
+ {
+ std::shared_ptr<ReadOnlyMediumEntry> newEntry = std::make_shared<ReadOnlyMediumEntry>(
+ pImpl->m_pCheckEditableWorkerMutex, pImpl->m_pIsDestructed);
+
+ if (newEntry != nullptr)
+ {
+ g_newReadOnlyDocs[this] = newEntry;
+ }
+ }
+ }
+}
+
+// should only be called on main thread
+void SfxMedium::CancelCheckEditableEntry(bool bRemoveEvent)
+{
+ if (pImpl->m_pCheckEditableWorkerMutex != nullptr)
+ {
+ std::unique_lock<std::recursive_mutex> lock(*(pImpl->m_pCheckEditableWorkerMutex));
+
+ if (pImpl->m_pReloadEvent != nullptr)
+ {
+ if (bRemoveEvent)
+ Application::RemoveUserEvent(pImpl->m_pReloadEvent);
+ // make sure destructor doesn't use a freed reference
+ // and reset the event so we can check again
+ pImpl->m_pReloadEvent = nullptr;
+ }
+
+ if (pImpl->m_pIsDestructed != nullptr)
+ {
+ *(pImpl->m_pIsDestructed) = true;
+ pImpl->m_pIsDestructed = nullptr;
+ }
+ }
+}
+
+/** callback function, which is triggered by worker thread after successfully checking if the file
+ is editable. Sent from <Application::PostUserEvent(..)>
+ Note: This method has to be run in the main thread.
+*/
+IMPL_STATIC_LINK(SfxMedium, ShowReloadEditableDialog, void*, p, void)
+{
+ SfxMedium* pMed = static_cast<SfxMedium*>(p);
+ if (pMed == nullptr)
+ return;
+
+ pMed->CancelCheckEditableEntry(false);
+
+ uno::Reference<task::XInteractionHandler> xHandler = pMed->GetInteractionHandler();
+ if (xHandler.is())
+ {
+ OUString aDocumentURL
+ = pMed->GetURLObject().GetLastName(INetURLObject::DecodeMechanism::WithCharset);
+ ::rtl::Reference<::ucbhelper::InteractionRequest> xInteractionRequestImpl
+ = new ::ucbhelper::InteractionRequest(uno::Any(document::ReloadEditableRequest(
+ OUString(), uno::Reference<uno::XInterface>(), aDocumentURL)));
+ if (xInteractionRequestImpl != nullptr)
+ {
+ uno::Sequence<uno::Reference<task::XInteractionContinuation>> aContinuations{
+ new ::ucbhelper::InteractionAbort(xInteractionRequestImpl.get()),
+ new ::ucbhelper::InteractionApprove(xInteractionRequestImpl.get())
+ };
+ xInteractionRequestImpl->setContinuations(aContinuations);
+ xHandler->handle(xInteractionRequestImpl);
+ ::rtl::Reference<::ucbhelper::InteractionContinuation> xSelected
+ = xInteractionRequestImpl->getSelection();
+ if (uno::Reference<task::XInteractionApprove>(xSelected.get(), uno::UNO_QUERY).is())
+ {
+ for (SfxViewFrame* pFrame = SfxViewFrame::GetFirst(); pFrame;
+ pFrame = SfxViewFrame::GetNext(*pFrame))
+ {
+ if (pFrame->GetObjectShell()->GetMedium() == pMed)
+ {
+ // special case to ensure view isn't set to read-only in
+ // SfxViewFrame::ExecReload_Impl after reloading
+ pMed->SetOriginallyReadOnly(false);
+ pFrame->GetDispatcher()->Execute(SID_RELOAD);
+ break;
+ }
+ }
+ }
+ }
+ }
+}
+
+bool SfxMedium::CheckCanGetLockfile() const
+{
+#if !HAVE_FEATURE_MULTIUSER_ENVIRONMENT
+ bool bCanReload = true;
+#else
+ bool bCanReload = false;
+ ::svt::DocumentLockFile aLockFile(GetName());
+ LockFileEntry aData;
+ osl::DirectoryItem rItem;
+ auto nError1 = osl::DirectoryItem::get(aLockFile.GetURL(), rItem);
+ if (nError1 == osl::FileBase::E_None)
+ {
+ try
+ {
+ aData = aLockFile.GetLockData();
+ }
+ catch (const io::WrongFormatException&)
+ {
+ // we get empty or corrupt data
+ return false;
+ }
+ catch (const uno::Exception&)
+ {
+ // locked from other app
+ return false;
+ }
+ LockFileEntry aOwnData = svt::LockFileCommon::GenerateOwnEntry();
+ bool bOwnLock
+ = aOwnData[LockFileComponent::SYSUSERNAME] == aData[LockFileComponent::SYSUSERNAME];
+ if (bOwnLock
+ && aOwnData[LockFileComponent::LOCALHOST] == aData[LockFileComponent::LOCALHOST]
+ && aOwnData[LockFileComponent::USERURL] == aData[LockFileComponent::USERURL])
+ {
+ // this is own lock from the same installation, it could remain because of crash
+ bCanReload = true;
+ }
+ }
+ else if (nError1 == osl::FileBase::E_NOENT) // file doesn't exist
+ {
+ try
+ {
+ aLockFile.CreateOwnLockFile();
+ try
+ {
+ // TODO/LATER: A warning could be shown in case the file is not the own one
+ aLockFile.RemoveFile();
+ }
+ catch (const io::WrongFormatException&)
+ {
+ try
+ {
+ // erase the empty or corrupt file
+ aLockFile.RemoveFileDirectly();
+ }
+ catch (const uno::Exception&)
+ {
+ }
+ }
+ bCanReload = true;
+ }
+ catch (const uno::Exception&)
+ {
+ }
+ }
+#endif
+ return bCanReload;
+}
+
+// worker thread method, should only be one thread globally
+void CheckReadOnlyTask::doWork()
+{
+ if (m_xListener == nullptr)
+ return;
+
+ while (true)
+ {
+ std::unique_lock<std::mutex> termLock(m_xListener->mMutex);
+ if (m_xListener->mCond.wait_for(termLock, std::chrono::seconds(60),
+ [this] { return m_xListener->bIsTerminated; }))
+ // signalled, spurious wakeups should not be possible
+ return;
+
+ // must have timed-out
+ termLock.unlock();
+ std::unique_lock<std::mutex> globalLock(g_chkReadOnlyGlobalMutex);
+ for (auto it = g_newReadOnlyDocs.begin(); it != g_newReadOnlyDocs.end(); )
+ {
+ auto [pMed, roEntry] = *it;
+ g_existingReadOnlyDocs[pMed] = roEntry;
+ it = g_newReadOnlyDocs.erase(it);
+ }
+ if (g_existingReadOnlyDocs.size() == 0)
+ {
+ g_bChkReadOnlyTaskRunning = false;
+ return;
+ }
+ globalLock.unlock();
+
+ auto checkForErase = [](SfxMedium* pMed, const std::shared_ptr<ReadOnlyMediumEntry>& roEntry) -> bool
+ {
+ if (pMed == nullptr || roEntry == nullptr || roEntry->_pMutex == nullptr
+ || roEntry->_pIsDestructed == nullptr)
+ return true;
+
+ std::unique_lock<std::recursive_mutex> medLock(*(roEntry->_pMutex));
+ if (*(roEntry->_pIsDestructed) || pMed->GetWorkerReloadEvent() != nullptr)
+ return true;
+
+ osl::File aFile(
+ pMed->GetURLObject().GetMainURL(INetURLObject::DecodeMechanism::WithCharset));
+ if (aFile.open(osl_File_OpenFlag_Write) != osl::FileBase::E_None)
+ return false;
+
+ if (!pMed->CheckCanGetLockfile())
+ return false;
+
+ if (aFile.close() != osl::FileBase::E_None)
+ return true;
+
+ // we can load, ask user
+ ImplSVEvent* pEvent = Application::PostUserEvent(
+ LINK(nullptr, SfxMedium, ShowReloadEditableDialog), pMed);
+ pMed->SetWorkerReloadEvent(pEvent);
+ return true;
+ };
+
+ for (auto it = g_existingReadOnlyDocs.begin(); it != g_existingReadOnlyDocs.end(); )
+ {
+ if (checkForErase(it->first, it->second))
+ it = g_existingReadOnlyDocs.erase(it);
+ else
+ ++it;
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/doc/docfilt.cxx b/sfx2/source/doc/docfilt.cxx
new file mode 100644
index 000000000..7a23a2842
--- /dev/null
+++ b/sfx2/source/doc/docfilt.cxx
@@ -0,0 +1,199 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifdef __sun
+#include <ctime>
+#endif
+
+#include <sot/exchange.hxx>
+#include <sot/storage.hxx>
+#include <comphelper/fileformat.h>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/embed/XStorage.hpp>
+#include <sfx2/docfilt.hxx>
+#include <sfx2/fcontnr.hxx>
+#include <sfx2/objsh.hxx>
+
+using namespace ::com::sun::star;
+
+SfxFilter::SfxFilter( const OUString& rProvider, const OUString &rFilterName ) :
+ maFilterName(rFilterName),
+ maProvider(rProvider),
+ nFormatType(SfxFilterFlags::NONE),
+ nVersion(0),
+ lFormat(SotClipboardFormatId::NONE),
+ mbEnabled(true)
+{
+}
+
+SfxFilter::SfxFilter( const OUString &rName,
+ std::u16string_view rWildCard,
+ SfxFilterFlags nType,
+ SotClipboardFormatId lFmt,
+ const OUString &rTypNm,
+ const OUString &rMimeType,
+ const OUString &rUsrDat,
+ const OUString &rServiceName,
+ bool bEnabled ):
+ aWildCard(rWildCard, ';'),
+ aTypeName(rTypNm),
+ aUserData(rUsrDat),
+ aServiceName(rServiceName),
+ aMimeType(rMimeType),
+ maFilterName(rName),
+ aUIName(maFilterName),
+ nFormatType(nType),
+ nVersion(SOFFICE_FILEFORMAT_50),
+ lFormat(lFmt),
+ mbEnabled(bEnabled)
+{
+ const OUString aExts = GetWildcard().getGlob();
+ sal_Int32 nLen{ aExts.getLength() };
+ if (nLen<=0)
+ return;
+
+ // truncate to first empty extension
+ if (aExts[0]==';')
+ {
+ aWildCard.setGlob(u"");
+ return;
+ }
+ const sal_Int32 nIdx{ aExts.indexOf(";;") };
+ if (nIdx>0)
+ nLen = nIdx;
+ else if (aExts[nLen-1]==';')
+ --nLen;
+ if (nLen<aExts.getLength())
+ aWildCard.setGlob(aExts.subView(0, nLen));
+}
+
+SfxFilter::~SfxFilter()
+{
+}
+
+OUString SfxFilter::GetDefaultExtension() const
+{
+ return GetWildcard().getGlob().getToken(0, ';');
+}
+
+
+OUString SfxFilter::GetSuffixes() const
+{
+ OUString aRet = GetWildcard().getGlob();
+ aRet = aRet.replaceAll( "*.", "" );
+ aRet = aRet.replaceAll( ";", "," );
+ return aRet;
+}
+
+std::shared_ptr<const SfxFilter> SfxFilter::GetDefaultFilter( std::u16string_view rName )
+{
+ return SfxFilterContainer::GetDefaultFilter_Impl( rName );
+}
+
+std::shared_ptr<const SfxFilter> SfxFilter::GetDefaultFilterFromFactory( const OUString& rFact )
+{
+ return GetDefaultFilter( SfxObjectShell::GetServiceNameFromFactory( rFact ) );
+}
+
+std::shared_ptr<const SfxFilter> SfxFilter::GetFilterByName( const OUString& rName )
+{
+ SfxFilterMatcher aMatch;
+ return aMatch.GetFilter4FilterName( rName, SfxFilterFlags::NONE, SfxFilterFlags::NONE );
+}
+
+OUString SfxFilter::GetTypeFromStorage( const SotStorage& rStg )
+{
+ const char* pType=nullptr;
+ if ( rStg.IsStream( "WordDocument" ) )
+ {
+ if ( rStg.IsStream( "0Table" ) || rStg.IsStream( "1Table" ) )
+ pType = "writer_MS_Word_97";
+ else
+ pType = "writer_MS_Word_95";
+ }
+ else if ( rStg.IsStream( "Book" ) )
+ {
+ pType = "calc_MS_Excel_95";
+ }
+ else if ( rStg.IsStream( "Workbook" ) )
+ {
+ pType = "calc_MS_Excel_97";
+ }
+ else if ( rStg.IsStream( "PowerPoint Document" ) )
+ {
+ pType = "impress_MS_PowerPoint_97";
+ }
+ else if ( rStg.IsStream( "Equation Native" ) )
+ {
+ pType = "math_MathType_3x";
+ }
+ else
+ {
+ SotClipboardFormatId nClipId = const_cast<SotStorage&>(rStg).GetFormat();
+ if ( nClipId != SotClipboardFormatId::NONE )
+ {
+ std::shared_ptr<const SfxFilter> pFilter = SfxFilterMatcher().GetFilter4ClipBoardId( nClipId );
+ if ( pFilter )
+ return pFilter->GetTypeName();
+ }
+ }
+
+ return pType ? OUString::createFromAscii(pType) : OUString();
+}
+
+OUString SfxFilter::GetTypeFromStorage(
+ const uno::Reference<embed::XStorage>& xStorage )
+{
+ SfxFilterMatcher aMatcher;
+
+ css::uno::Reference< css::beans::XPropertySet > xProps( xStorage, css::uno::UNO_QUERY );
+ if ( xProps.is() )
+ {
+ OUString aMediaType;
+ xProps->getPropertyValue("MediaType") >>= aMediaType;
+ if ( !aMediaType.isEmpty() )
+ {
+ css::datatransfer::DataFlavor aDataFlavor;
+ aDataFlavor.MimeType = aMediaType;
+ SotClipboardFormatId nClipId = SotExchange::GetFormat( aDataFlavor );
+ if ( nClipId != SotClipboardFormatId::NONE )
+ {
+ SfxFilterFlags const nMust = SfxFilterFlags::IMPORT;
+ // template filters shouldn't be detected if not explicitly asked for
+ SfxFilterFlags const nDont = SFX_FILTER_NOTINSTALLED | SfxFilterFlags::TEMPLATEPATH;
+
+ // get filter from storage MediaType
+ std::shared_ptr<const SfxFilter> pFilter = aMatcher.GetFilter4ClipBoardId( nClipId, nMust, nDont );
+ if ( !pFilter )
+ // template filter is asked for , but there isn't one; so at least the "normal" format should be detected
+ // or storage *is* a template, but bTemplate is not set
+ pFilter = aMatcher.GetFilter4ClipBoardId( nClipId );
+
+ if ( pFilter )
+ {
+ return pFilter->GetTypeName();
+ }
+ }
+ }
+ }
+
+ return OUString();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/doc/docinf.cxx b/sfx2/source/doc/docinf.cxx
new file mode 100644
index 000000000..949e1f19f
--- /dev/null
+++ b/sfx2/source/doc/docinf.cxx
@@ -0,0 +1,324 @@
+/* -*- 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/docinf.hxx>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/beans/XPropertyContainer.hpp>
+#include <com/sun/star/document/XDocumentProperties.hpp>
+#include <com/sun/star/document/XCompatWriterDocProperties.hpp>
+#include <com/sun/star/uno/Exception.hpp>
+#include <rtl/ustring.hxx>
+#include <sal/log.hxx>
+#include <tools/debug.hxx>
+#include <comphelper/string.hxx>
+#include <sot/storage.hxx>
+#include <vcl/bitmapex.hxx>
+#include <vcl/gdimtf.hxx>
+#include <vcl/dibtools.hxx>
+#include "oleprops.hxx"
+
+
+// stream names
+constexpr OUStringLiteral STREAM_SUMMARYINFO = u"\005SummaryInformation";
+constexpr OUStringLiteral STREAM_DOCSUMMARYINFO = u"\005DocumentSummaryInformation";
+
+// usings
+using namespace ::com::sun::star;
+
+
+namespace sfx2 {
+
+ErrCode LoadOlePropertySet(
+ const uno::Reference< document::XDocumentProperties>& i_xDocProps,
+ SotStorage* i_pStorage )
+{
+ // *** global properties from stream "005SummaryInformation" ***
+
+ // load the property set
+ SfxOlePropertySet aGlobSet;
+ ErrCode nGlobError = aGlobSet.LoadPropertySet(i_pStorage,
+ STREAM_SUMMARYINFO );
+
+ // global section
+ SfxOleSectionRef xGlobSect = aGlobSet.GetSection( SECTION_GLOBAL );
+ if( xGlobSect )
+ {
+ // set supported properties
+ OUString aStrValue;
+ util::DateTime aDateTime;
+
+ if( xGlobSect->GetStringValue( aStrValue, PROPID_TITLE ) )
+ i_xDocProps->setTitle( aStrValue );
+ if( xGlobSect->GetStringValue( aStrValue, PROPID_SUBJECT ) )
+ i_xDocProps->setSubject( aStrValue );
+ if( xGlobSect->GetStringValue( aStrValue, PROPID_KEYWORDS ) ) {
+ i_xDocProps->setKeywords(
+ ::comphelper::string::convertCommaSeparated(aStrValue) );
+ }
+ if( xGlobSect->GetStringValue( aStrValue, PROPID_TEMPLATE ) )
+ i_xDocProps->setTemplateName( aStrValue );
+ if( xGlobSect->GetStringValue( aStrValue, PROPID_COMMENTS ) )
+ i_xDocProps->setDescription( aStrValue );
+
+ util::DateTime aInvalid;
+ if( xGlobSect->GetStringValue( aStrValue, PROPID_AUTHOR) )
+ i_xDocProps->setAuthor( aStrValue );
+ else
+ i_xDocProps->setAuthor( OUString() );
+ if( xGlobSect->GetFileTimeValue( aDateTime, PROPID_CREATED ) )
+ i_xDocProps->setCreationDate( aDateTime );
+ else
+ i_xDocProps->setCreationDate( aInvalid );
+
+ if( xGlobSect->GetStringValue( aStrValue, PROPID_LASTAUTHOR) )
+ i_xDocProps->setModifiedBy( aStrValue );
+ else
+ i_xDocProps->setModifiedBy( OUString() );
+ if( xGlobSect->GetFileTimeValue( aDateTime, PROPID_LASTSAVED ) )
+ i_xDocProps->setModificationDate( aDateTime );
+ else
+ i_xDocProps->setModificationDate( aInvalid );
+
+ i_xDocProps->setPrintedBy( OUString() );
+ if( xGlobSect->GetFileTimeValue( aDateTime, PROPID_LASTPRINTED ) )
+ i_xDocProps->setPrintDate( aDateTime );
+ else
+ i_xDocProps->setPrintDate( aInvalid );
+
+ if( xGlobSect->GetStringValue( aStrValue, PROPID_REVNUMBER ) )
+ {
+ sal_Int16 nRevision = static_cast< sal_Int16 >( aStrValue.toInt32() );
+ if ( nRevision > 0 )
+ i_xDocProps->setEditingCycles( nRevision );
+ }
+
+ if( xGlobSect->GetFileTimeValue( aDateTime, PROPID_EDITTIME )
+ && !(aDateTime.NanoSeconds == 0 && aDateTime.Seconds == 0
+ && aDateTime.Minutes == 0 && aDateTime.Hours == 0
+ && aDateTime.Day == 0 && aDateTime.Month == 0
+ && aDateTime.Year == 0) )
+ {
+ // subtract offset 1601-01-01
+ aDateTime.Year -= 1601;
+ aDateTime.Month -= 1;
+ aDateTime.Day -= 1;
+ try
+ {
+ i_xDocProps->setEditingDuration(
+ aDateTime.Day * 60*60*24 +
+ aDateTime.Hours * 60*60 +
+ aDateTime.Minutes * 60 +
+ aDateTime.Seconds );
+ }
+ catch (const lang::IllegalArgumentException &)
+ {
+ // ignore
+ }
+ }
+ }
+
+ // *** custom properties from stream "005DocumentSummaryInformation" ***
+
+ // load the property set
+ SfxOlePropertySet aDocSet;
+ ErrCode nDocError = aDocSet.LoadPropertySet(i_pStorage,
+ STREAM_DOCSUMMARYINFO );
+
+ // custom properties
+ SfxOleSectionRef xCustomSect = aDocSet.GetSection( SECTION_CUSTOM );
+ if( xCustomSect )
+ {
+ uno::Reference < beans::XPropertyContainer > xUserDefined(
+ i_xDocProps->getUserDefinedProperties(), uno::UNO_SET_THROW);
+ ::std::vector< sal_Int32 > aPropIds;
+ xCustomSect->GetPropertyIds( aPropIds );
+ for( const auto& rPropId : aPropIds )
+ {
+ const OUString aPropName = xCustomSect->GetPropertyName( rPropId );
+ uno::Any aPropValue = xCustomSect->GetAnyValue( rPropId );
+ if( !aPropName.isEmpty() && aPropValue.hasValue() )
+ {
+ try
+ {
+ xUserDefined->addProperty( aPropName,
+ beans::PropertyAttribute::REMOVABLE, aPropValue );
+ }
+ catch (const uno::Exception&)
+ {
+ //ignore
+ }
+ }
+ }
+ }
+
+ uno::Reference< document::XCompatWriterDocProperties > xWriterProps( i_xDocProps, uno::UNO_QUERY );
+ if ( xWriterProps.is() )
+ {
+ SfxOleSectionRef xBuiltin = aDocSet.GetSection( SECTION_BUILTIN );
+ if ( xBuiltin )
+ {
+ try
+ {
+ OUString aStrValue;
+ if ( xBuiltin->GetStringValue( aStrValue, PROPID_MANAGER ) )
+ xWriterProps->setManager( aStrValue );
+ if ( xBuiltin->GetStringValue( aStrValue, PROPID_CATEGORY ) )
+ xWriterProps->setCategory( aStrValue );
+ if ( xBuiltin->GetStringValue( aStrValue, PROPID_COMPANY ) )
+ xWriterProps->setCompany( aStrValue );
+ }
+ catch (const uno::Exception&)
+ {
+ }
+ }
+ }
+
+ // return code
+ return (nGlobError != ERRCODE_NONE) ? nGlobError : nDocError;
+}
+
+bool SaveOlePropertySet(
+ const uno::Reference< document::XDocumentProperties>& i_xDocProps,
+ SotStorage* i_pStorage,
+ const uno::Sequence<sal_Int8> * i_pThumb,
+ const uno::Sequence<sal_Int8> * i_pGuid,
+ const uno::Sequence<sal_Int8> * i_pHyperlinks)
+{
+ // *** global properties into stream "005SummaryInformation" ***
+
+ SfxOlePropertySet aGlobSet;
+
+ // set supported properties
+ SfxOleSection& rGlobSect = aGlobSet.AddSection( SECTION_GLOBAL );
+ rGlobSect.SetStringValue( PROPID_TITLE, i_xDocProps->getTitle() );
+ rGlobSect.SetStringValue( PROPID_SUBJECT, i_xDocProps->getSubject() );
+ const OUString aStr = ::comphelper::string::convertCommaSeparated(
+ i_xDocProps->getKeywords() );
+ rGlobSect.SetStringValue( PROPID_KEYWORDS, aStr );
+ rGlobSect.SetStringValue( PROPID_TEMPLATE, i_xDocProps->getTemplateName() );
+ rGlobSect.SetStringValue( PROPID_COMMENTS, i_xDocProps->getDescription() );
+ rGlobSect.SetStringValue( PROPID_AUTHOR, i_xDocProps->getAuthor() );
+ rGlobSect.SetFileTimeValue(PROPID_CREATED, i_xDocProps->getCreationDate());
+ rGlobSect.SetStringValue( PROPID_LASTAUTHOR, i_xDocProps->getModifiedBy() );
+ rGlobSect.SetFileTimeValue(PROPID_LASTSAVED,
+ i_xDocProps->getModificationDate() );
+ // note: apparently PrintedBy is not supported in file format
+ rGlobSect.SetFileTimeValue(PROPID_LASTPRINTED, i_xDocProps->getPrintDate());
+
+ sal_Int32 dur = i_xDocProps->getEditingDuration();
+ util::DateTime aEditTime;
+ // add offset 1601-01-01
+ aEditTime.Year = 1601;
+ aEditTime.Month = 1;
+ aEditTime.Day = 1;
+ aEditTime.Hours = static_cast<sal_Int16>(dur / 3600);
+ aEditTime.Minutes = static_cast<sal_Int16>((dur % 3600) / 60);
+ aEditTime.Seconds = static_cast<sal_Int16>(dur % 60);
+ rGlobSect.SetFileTimeValue( PROPID_EDITTIME, aEditTime );
+
+ rGlobSect.SetStringValue( PROPID_REVNUMBER,
+ OUString::number( i_xDocProps->getEditingCycles() ) );
+ if ( i_pThumb && i_pThumb->hasElements() )
+ rGlobSect.SetThumbnailValue( PROPID_THUMBNAIL, *i_pThumb );
+
+ // save the property set
+ ErrCode nGlobError = aGlobSet.SavePropertySet(i_pStorage,
+ STREAM_SUMMARYINFO);
+
+ // *** custom properties into stream "005DocumentSummaryInformation" ***
+
+ SfxOlePropertySet aDocSet;
+
+ // set builtin properties
+ aDocSet.AddSection( SECTION_BUILTIN );
+
+ // set custom properties
+ SfxOleSection& rCustomSect = aDocSet.AddSection( SECTION_CUSTOM );
+
+ // write GUID
+ if (i_pGuid) {
+ const sal_Int32 nPropId = rCustomSect.GetFreePropertyId();
+ rCustomSect.SetBlobValue( nPropId, *i_pGuid );
+ rCustomSect.SetPropertyName( nPropId,
+ "_PID_GUID" );
+ }
+
+ // write hyperlinks
+ if (i_pHyperlinks) {
+ const sal_Int32 nPropId = rCustomSect.GetFreePropertyId();
+ rCustomSect.SetBlobValue( nPropId, *i_pHyperlinks );
+ rCustomSect.SetPropertyName( nPropId,
+ "_PID_HLINKS" );
+ }
+
+ uno::Reference<beans::XPropertySet> xUserDefinedProps(
+ i_xDocProps->getUserDefinedProperties(), uno::UNO_QUERY_THROW);
+ uno::Reference<beans::XPropertySetInfo> xPropInfo =
+ xUserDefinedProps->getPropertySetInfo();
+ DBG_ASSERT(xPropInfo.is(), "UserDefinedProperties Info is null");
+ const uno::Sequence<beans::Property> props = xPropInfo->getProperties();
+ for (const auto& rProp : props)
+ {
+ try
+ {
+ // skip transient properties
+ if (~rProp.Attributes & beans::PropertyAttribute::TRANSIENT)
+ {
+ const OUString name = rProp.Name;
+ const sal_Int32 nPropId = rCustomSect.GetFreePropertyId();
+ if (rCustomSect.SetAnyValue( nPropId,
+ xUserDefinedProps->getPropertyValue(name))) {
+ rCustomSect.SetPropertyName( nPropId, name );
+ }
+ }
+ }
+ catch (const uno::Exception &)
+ {
+ // may happen with concurrent modification...
+ SAL_INFO("sfx", "SavePropertySet: exception");
+ }
+ }
+
+ // save the property set
+ ErrCode nDocError = aDocSet.SavePropertySet(i_pStorage,
+ STREAM_DOCSUMMARYINFO );
+
+ // return code
+ return (nGlobError == ERRCODE_NONE) && (nDocError == ERRCODE_NONE);
+}
+
+uno::Sequence<sal_Int8> convertMetaFile(GDIMetaFile const * i_pThumb)
+{
+ if (i_pThumb) {
+ BitmapEx aBitmap;
+ SvMemoryStream aStream;
+ if (i_pThumb->CreateThumbnail(aBitmap))
+ {
+ WriteDIB(aBitmap.GetBitmap(), aStream, false, false);
+ return uno::Sequence<sal_Int8>(static_cast< const sal_Int8* >( aStream.GetData() ), aStream.TellEnd());
+ }
+ }
+ return uno::Sequence<sal_Int8>();
+}
+
+} // namespace sfx2
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/doc/docinsert.cxx b/sfx2/source/doc/docinsert.cxx
new file mode 100644
index 000000000..d8fe42621
--- /dev/null
+++ b/sfx2/source/doc/docinsert.cxx
@@ -0,0 +1,292 @@
+/* -*- 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/app.hxx>
+#include <sfx2/docinsert.hxx>
+#include <sfx2/docfile.hxx>
+#include <sfx2/fcontnr.hxx>
+#include <sfx2/filedlghelper.hxx>
+#include <appopen.hxx>
+#include <openflag.hxx>
+#include <sfx2/passwd.hxx>
+
+#include <sfx2/sfxsids.hrc>
+#include <com/sun/star/ui/dialogs/ControlActions.hpp>
+#include <com/sun/star/ui/dialogs/ExtendedFilePickerElementIds.hpp>
+#include <com/sun/star/ui/dialogs/TemplateDescription.hpp>
+#include <com/sun/star/ui/dialogs/XFilePicker3.hpp>
+#include <com/sun/star/ui/dialogs/XFilePickerControlAccess.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <tools/urlobj.hxx>
+#include <svl/itemset.hxx>
+#include <svl/eitem.hxx>
+#include <svl/intitem.hxx>
+#include <svl/stritem.hxx>
+#include <memory>
+#include <tools/diagnose_ex.h>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::ui::dialogs;
+using namespace ::com::sun::star::uno;
+
+namespace
+{
+
+FileDialogFlags lcl_map_mode_to_flags(const sfx2::DocumentInserter::Mode mode)
+{
+ FileDialogFlags f {FileDialogFlags::NONE};
+ switch (mode)
+ {
+ case sfx2::DocumentInserter::Mode::Insert:
+ f = FileDialogFlags::Insert;
+ break;
+ case sfx2::DocumentInserter::Mode::InsertMulti:
+ f = FileDialogFlags::Insert|FileDialogFlags::MultiSelection;
+ break;
+ case sfx2::DocumentInserter::Mode::Compare:
+ f = FileDialogFlags::InsertCompare;
+ break;
+ case sfx2::DocumentInserter::Mode::Merge:
+ f = FileDialogFlags::InsertMerge;
+ break;
+ }
+ return f;
+}
+
+}
+
+namespace sfx2 {
+
+DocumentInserter::DocumentInserter(weld::Window* pParent, const OUString& rFactory, const Mode mode)
+ : m_pParent ( pParent )
+ , m_sDocFactory ( rFactory )
+ , m_nDlgFlags ( lcl_map_mode_to_flags(mode) )
+ , m_nError ( ERRCODE_NONE )
+{
+}
+
+DocumentInserter::~DocumentInserter()
+{
+}
+
+void DocumentInserter::StartExecuteModal( const Link<sfx2::FileDialogHelper*,void>& _rDialogClosedLink )
+{
+ m_aDialogClosedLink = _rDialogClosedLink;
+ m_nError = ERRCODE_NONE;
+ if ( !m_pFileDlg )
+ {
+ m_pFileDlg.reset( new FileDialogHelper(
+ ui::dialogs::TemplateDescription::FILEOPEN_SIMPLE,
+ m_nDlgFlags, m_sDocFactory, SfxFilterFlags::NONE, SfxFilterFlags::NONE, m_pParent ) );
+ }
+ m_pFileDlg->SetContext(FileDialogHelper::InsertDoc);
+ m_pFileDlg->StartExecuteModal( LINK( this, DocumentInserter, DialogClosedHdl ) );
+}
+
+std::unique_ptr<SfxMedium> DocumentInserter::CreateMedium(char const*const pFallbackHack)
+{
+ std::unique_ptr<SfxMedium> pMedium;
+ if (!m_nError && m_xItemSet && !m_pURLList.empty())
+ {
+ DBG_ASSERT( m_pURLList.size() == 1, "DocumentInserter::CreateMedium(): invalid URL list count" );
+ pMedium.reset(new SfxMedium(
+ m_pURLList[0], SFX_STREAM_READONLY,
+ SfxGetpApp()->GetFilterMatcher().GetFilter4FilterName( m_sFilter ), m_xItemSet ));
+ pMedium->UseInteractionHandler( true );
+ std::optional<SfxFilterMatcher> pMatcher;
+ if ( !m_sDocFactory.isEmpty() )
+ pMatcher.emplace(m_sDocFactory);
+ else
+ pMatcher.emplace();
+
+ std::shared_ptr<const SfxFilter> pFilter;
+ ErrCode nError = pMatcher->DetectFilter( *pMedium, pFilter );
+ // tdf#101813 hack: check again if it's a global document
+ if (ERRCODE_NONE != nError && pFallbackHack)
+ {
+ pMatcher.emplace(OUString::createFromAscii(pFallbackHack));
+ nError = pMatcher->DetectFilter( *pMedium, pFilter );
+ }
+ if ( nError == ERRCODE_NONE && pFilter )
+ pMedium->SetFilter( pFilter );
+ else
+ pMedium.reset();
+
+ if ( pMedium && CheckPasswd_Impl( nullptr, pMedium.get() ) == ERRCODE_ABORT )
+ pMedium.reset();
+ }
+
+ return pMedium;
+}
+
+SfxMediumList DocumentInserter::CreateMediumList()
+{
+ SfxMediumList aMediumList;
+ if (!m_nError && m_xItemSet && !m_pURLList.empty())
+ {
+ for (auto const& url : m_pURLList)
+ {
+ std::unique_ptr<SfxMedium> pMedium(new SfxMedium(
+ url, SFX_STREAM_READONLY,
+ SfxGetpApp()->GetFilterMatcher().GetFilter4FilterName( m_sFilter ), m_xItemSet ));
+
+ pMedium->UseInteractionHandler( true );
+
+ SfxFilterMatcher aMatcher( m_sDocFactory );
+ std::shared_ptr<const SfxFilter> pFilter;
+ ErrCode nError = aMatcher.DetectFilter( *pMedium, pFilter );
+ if ( nError == ERRCODE_NONE && pFilter )
+ pMedium->SetFilter( pFilter );
+ else
+ pMedium.reset();
+
+ if( pMedium && CheckPasswd_Impl( nullptr, pMedium.get() ) != ERRCODE_ABORT )
+ aMediumList.push_back( std::move(pMedium) );
+ }
+ }
+
+ return aMediumList;
+}
+
+static void impl_FillURLList( sfx2::FileDialogHelper const * _pFileDlg, std::vector<OUString>& _rpURLList )
+{
+ DBG_ASSERT( _pFileDlg, "DocumentInserter::fillURLList(): invalid file dialog" );
+
+ const Sequence < OUString > aPathSeq = _pFileDlg->GetSelectedFiles();
+
+ if ( aPathSeq.hasElements() )
+ {
+ _rpURLList.clear();
+
+ std::transform(aPathSeq.begin(), aPathSeq.end(), std::back_inserter(_rpURLList),
+ [](const OUString& rPath) -> OUString {
+ INetURLObject aPathObj( rPath );
+ return aPathObj.GetMainURL(INetURLObject::DecodeMechanism::NONE);
+ });
+ }
+}
+
+IMPL_LINK_NOARG(DocumentInserter, DialogClosedHdl, sfx2::FileDialogHelper*, void)
+{
+ DBG_ASSERT( m_pFileDlg, "DocumentInserter::DialogClosedHdl(): no file dialog" );
+
+ m_nError = m_pFileDlg->GetError();
+ if ( ERRCODE_NONE == m_nError )
+ impl_FillURLList( m_pFileDlg.get(), m_pURLList );
+
+ Reference < XFilePicker3 > xFP = m_pFileDlg->GetFilePicker();
+ Reference < XFilePickerControlAccess > xCtrlAccess( xFP, UNO_QUERY );
+ if ( xCtrlAccess.is() )
+ {
+ // always create a new itemset
+ m_xItemSet = std::make_shared<SfxAllItemSet>( SfxGetpApp()->GetPool() );
+
+ short nDlgType = m_pFileDlg->GetDialogType();
+ bool bHasPassword = (
+ TemplateDescription::FILESAVE_AUTOEXTENSION_PASSWORD == nDlgType
+ || TemplateDescription::FILESAVE_AUTOEXTENSION_PASSWORD_FILTEROPTIONS == nDlgType );
+
+ // check, whether or not we have to display a password box
+ if ( bHasPassword && m_pFileDlg->IsPasswordEnabled() )
+ {
+ try
+ {
+ Any aValue = xCtrlAccess->getValue( ExtendedFilePickerElementIds::CHECKBOX_PASSWORD, 0 );
+ bool bPassWord = false;
+ if ( ( aValue >>= bPassWord ) && bPassWord )
+ {
+ // ask for the password
+ SfxPasswordDialog aPasswordDlg(m_pParent);
+ aPasswordDlg.ShowExtras( SfxShowExtras::CONFIRM );
+ short nRet = aPasswordDlg.run();
+ if ( RET_OK == nRet )
+ {
+ m_xItemSet->Put( SfxStringItem( SID_PASSWORD, aPasswordDlg.GetPassword() ) );
+ }
+ else
+ {
+ m_xItemSet.reset();
+ return;
+ }
+ }
+ }
+ catch( const IllegalArgumentException& ){}
+ }
+
+ if ( m_nDlgFlags & FileDialogFlags::Export )
+ {
+ try
+ {
+ Any aValue = xCtrlAccess->getValue( ExtendedFilePickerElementIds::CHECKBOX_SELECTION, 0 );
+ bool bSelection = false;
+ if ( aValue >>= bSelection )
+ m_xItemSet->Put( SfxBoolItem( SID_SELECTION, bSelection ) );
+ }
+ catch( const IllegalArgumentException& )
+ {
+ TOOLS_WARN_EXCEPTION( "sfx.doc", "FileDialogHelper_Impl::execute: caught an IllegalArgumentException!" );
+ }
+ }
+
+
+ // set the read-only flag. When inserting a file, this flag is always set
+ if ( m_nDlgFlags & FileDialogFlags::Insert )
+ m_xItemSet->Put( SfxBoolItem( SID_DOC_READONLY, true ) );
+ else
+ {
+ if ( TemplateDescription::FILEOPEN_READONLY_VERSION == nDlgType )
+ {
+ try
+ {
+ Any aValue = xCtrlAccess->getValue( ExtendedFilePickerElementIds::CHECKBOX_READONLY, 0 );
+ bool bReadOnly = false;
+ if ( ( aValue >>= bReadOnly ) && bReadOnly )
+ m_xItemSet->Put( SfxBoolItem( SID_DOC_READONLY, bReadOnly ) );
+ }
+ catch( const IllegalArgumentException& )
+ {
+ TOOLS_WARN_EXCEPTION( "sfx.doc", "FileDialogHelper_Impl::execute: caught an IllegalArgumentException!" );
+ }
+ }
+ }
+
+ if ( TemplateDescription::FILEOPEN_READONLY_VERSION == nDlgType )
+ {
+ 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
+ m_xItemSet->Put( SfxInt16Item( SID_VERSION, static_cast<short>(nVersion) ) );
+ }
+ catch( const IllegalArgumentException& ){}
+ }
+ }
+
+ m_sFilter = m_pFileDlg->GetRealFilter();
+
+ m_aDialogClosedLink.Call( m_pFileDlg.get() );
+}
+
+} // namespace sfx2
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/doc/docmacromode.cxx b/sfx2/source/doc/docmacromode.cxx
new file mode 100644
index 000000000..3a8684113
--- /dev/null
+++ b/sfx2/source/doc/docmacromode.cxx
@@ -0,0 +1,431 @@
+/* -*- 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 <config_features.h>
+
+#include <sfx2/docmacromode.hxx>
+#include <sfx2/signaturestate.hxx>
+#include <sfx2/docfile.hxx>
+
+#include <com/sun/star/document/MacroExecMode.hpp>
+#include <com/sun/star/task/ErrorCodeRequest.hpp>
+#include <com/sun/star/task/DocumentMacroConfirmationRequest.hpp>
+#include <com/sun/star/security/DocumentDigitalSignatures.hpp>
+#include <com/sun/star/script/XLibraryContainer.hpp>
+#include <com/sun/star/document/XEmbeddedScripts.hpp>
+
+#include <comphelper/processfactory.hxx>
+#include <framework/interaction.hxx>
+#include <osl/file.hxx>
+#include <unotools/securityoptions.hxx>
+#include <svtools/sfxecode.hxx>
+#include <tools/diagnose_ex.h>
+#include <tools/urlobj.hxx>
+
+
+namespace sfx2
+{
+
+
+ using ::com::sun::star::uno::Reference;
+ using ::com::sun::star::task::XInteractionHandler;
+ using ::com::sun::star::uno::Any;
+ using ::com::sun::star::uno::Sequence;
+ using ::com::sun::star::task::DocumentMacroConfirmationRequest;
+ using ::com::sun::star::task::ErrorCodeRequest;
+ using ::com::sun::star::uno::Exception;
+ using ::com::sun::star::security::DocumentDigitalSignatures;
+ using ::com::sun::star::security::XDocumentDigitalSignatures;
+ using ::com::sun::star::embed::XStorage;
+ using ::com::sun::star::document::XEmbeddedScripts;
+ using ::com::sun::star::script::XLibraryContainer;
+ using ::com::sun::star::container::XNameAccess;
+ using ::com::sun::star::uno::UNO_QUERY_THROW;
+
+ namespace MacroExecMode = ::com::sun::star::document::MacroExecMode;
+
+
+ //= DocumentMacroMode_Data
+
+ struct DocumentMacroMode_Data
+ {
+ IMacroDocumentAccess& m_rDocumentAccess;
+ bool m_bMacroDisabledMessageShown;
+ bool m_bDocMacroDisabledMessageShown;
+
+ explicit DocumentMacroMode_Data( IMacroDocumentAccess& rDocumentAccess )
+ :m_rDocumentAccess( rDocumentAccess )
+ ,m_bMacroDisabledMessageShown( false )
+ ,m_bDocMacroDisabledMessageShown( false )
+ {
+ }
+ };
+
+
+ //= helper
+
+ namespace
+ {
+
+ void lcl_showGeneralSfxErrorOnce( const Reference< XInteractionHandler >& rxHandler, ErrCode nSfxErrorCode, bool& rbAlreadyShown )
+ {
+ if ( rbAlreadyShown )
+ return;
+
+ ErrorCodeRequest aErrorCodeRequest;
+ aErrorCodeRequest.ErrCode = sal_uInt32(nSfxErrorCode);
+
+ SfxMedium::CallApproveHandler( rxHandler, Any( aErrorCodeRequest ), false );
+ rbAlreadyShown = true;
+ }
+
+
+ void lcl_showMacrosDisabledError( const Reference< XInteractionHandler >& rxHandler, bool& rbAlreadyShown )
+ {
+ lcl_showGeneralSfxErrorOnce( rxHandler, ERRCODE_SFX_MACROS_SUPPORT_DISABLED, rbAlreadyShown );
+ }
+
+
+ void lcl_showDocumentMacrosDisabledError( const Reference< XInteractionHandler >& rxHandler, bool& rbAlreadyShown )
+ {
+#ifdef MACOSX
+ lcl_showGeneralSfxErrorOnce( rxHandler, ERRCODE_SFX_DOCUMENT_MACRO_DISABLED_MAC, rbAlreadyShown );
+#else
+ lcl_showGeneralSfxErrorOnce( rxHandler, ERRCODE_SFX_DOCUMENT_MACRO_DISABLED, rbAlreadyShown );
+#endif
+ }
+
+ void lcl_showMacrosDisabledUnsignedContentError( const Reference< XInteractionHandler >& rxHandler, bool& rbAlreadyShown )
+ {
+ lcl_showGeneralSfxErrorOnce( rxHandler, ERRCODE_SFX_DOCUMENT_MACRO_DISABLED_CONTENT_UNSIGNED, rbAlreadyShown );
+ }
+
+ bool lcl_showMacroWarning( const Reference< XInteractionHandler >& rxHandler,
+ const OUString& rDocumentLocation )
+ {
+ DocumentMacroConfirmationRequest aRequest;
+ aRequest.DocumentURL = rDocumentLocation;
+ return SfxMedium::CallApproveHandler( rxHandler, Any( aRequest ), true );
+ }
+ }
+
+ //= DocumentMacroMode
+ DocumentMacroMode::DocumentMacroMode( IMacroDocumentAccess& rDocumentAccess )
+ :m_xData( std::make_shared<DocumentMacroMode_Data>( rDocumentAccess ) )
+ {
+ }
+
+ bool DocumentMacroMode::allowMacroExecution()
+ {
+ m_xData->m_rDocumentAccess.setCurrentMacroExecMode( MacroExecMode::ALWAYS_EXECUTE_NO_WARN );
+ return true;
+ }
+
+ bool DocumentMacroMode::disallowMacroExecution()
+ {
+ m_xData->m_rDocumentAccess.setCurrentMacroExecMode( MacroExecMode::NEVER_EXECUTE );
+ return false;
+ }
+
+ bool DocumentMacroMode::adjustMacroMode( const Reference< XInteractionHandler >& rxInteraction, bool bHasValidContentSignature )
+ {
+ sal_uInt16 nMacroExecutionMode = m_xData->m_rDocumentAccess.getCurrentMacroExecMode();
+
+ if ( SvtSecurityOptions::IsMacroDisabled() )
+ {
+ // no macro should be executed at all
+ lcl_showMacrosDisabledError( rxInteraction, m_xData->m_bMacroDisabledMessageShown );
+ return disallowMacroExecution();
+ }
+
+ // get setting from configuration if required
+ enum AutoConfirmation
+ {
+ eNoAutoConfirm,
+ eAutoConfirmApprove,
+ eAutoConfirmReject
+ };
+ AutoConfirmation eAutoConfirm( eNoAutoConfirm );
+
+ if ( ( nMacroExecutionMode == MacroExecMode::USE_CONFIG )
+ || ( nMacroExecutionMode == MacroExecMode::USE_CONFIG_REJECT_CONFIRMATION )
+ || ( nMacroExecutionMode == MacroExecMode::USE_CONFIG_APPROVE_CONFIRMATION )
+ )
+ {
+ // check confirm first, as nMacroExecutionMode is always overwritten by the GetMacroSecurityLevel() switch
+ if (nMacroExecutionMode == MacroExecMode::USE_CONFIG_REJECT_CONFIRMATION)
+ eAutoConfirm = eAutoConfirmReject;
+ else if (nMacroExecutionMode == MacroExecMode::USE_CONFIG_APPROVE_CONFIRMATION)
+ eAutoConfirm = eAutoConfirmApprove;
+
+ switch ( SvtSecurityOptions::GetMacroSecurityLevel() )
+ {
+ case 3:
+ nMacroExecutionMode = MacroExecMode::FROM_LIST_NO_WARN;
+ break;
+ case 2:
+ nMacroExecutionMode = MacroExecMode::FROM_LIST_AND_SIGNED_WARN;
+ break;
+ case 1:
+ nMacroExecutionMode = MacroExecMode::ALWAYS_EXECUTE;
+ break;
+ case 0:
+ nMacroExecutionMode = MacroExecMode::ALWAYS_EXECUTE_NO_WARN;
+ break;
+ default:
+ OSL_FAIL( "DocumentMacroMode::adjustMacroMode: unexpected macro security level!" );
+ nMacroExecutionMode = MacroExecMode::NEVER_EXECUTE;
+ }
+ }
+
+ if ( nMacroExecutionMode == MacroExecMode::NEVER_EXECUTE )
+ return false;
+
+ if ( nMacroExecutionMode == MacroExecMode::ALWAYS_EXECUTE_NO_WARN )
+ return true;
+
+ try
+ {
+ // get document location from medium name and check whether it is a trusted one
+ // the service is created without document version, since it is not of interest here
+ Reference< XDocumentDigitalSignatures > xSignatures(DocumentDigitalSignatures::createDefault(::comphelper::getProcessComponentContext()));
+ INetURLObject aURLReferer( m_xData->m_rDocumentAccess.getDocumentLocation() );
+
+ OUString aLocation;
+ if ( aURLReferer.removeSegment() )
+ aLocation = aURLReferer.GetMainURL( INetURLObject::DecodeMechanism::NONE );
+
+ if ( !aLocation.isEmpty() && xSignatures->isLocationTrusted( aLocation ) )
+ {
+ return allowMacroExecution();
+ }
+
+ // at this point it is clear that the document is not in the secure location
+ if ( nMacroExecutionMode == MacroExecMode::FROM_LIST_NO_WARN )
+ {
+ lcl_showDocumentMacrosDisabledError( rxInteraction, m_xData->m_bDocMacroDisabledMessageShown );
+ return disallowMacroExecution();
+ }
+
+ // check whether the document is signed with trusted certificate
+ if ( nMacroExecutionMode != MacroExecMode::FROM_LIST )
+ {
+ // the trusted macro check will also retrieve the signature state ( small optimization )
+ const bool bAllowUIToAddAuthor = nMacroExecutionMode != MacroExecMode::FROM_LIST_AND_SIGNED_NO_WARN
+ && (nMacroExecutionMode == MacroExecMode::ALWAYS_EXECUTE
+ || !SvtSecurityOptions::IsReadOnly(SvtSecurityOptions::EOption::MacroTrustedAuthors));
+ const bool bHasTrustedMacroSignature = m_xData->m_rDocumentAccess.hasTrustedScriptingSignature(bAllowUIToAddAuthor);
+
+ SignatureState nSignatureState = m_xData->m_rDocumentAccess.getScriptingSignatureState();
+ if ( nSignatureState == SignatureState::BROKEN )
+ {
+ if (!bAllowUIToAddAuthor)
+ lcl_showDocumentMacrosDisabledError(rxInteraction, m_xData->m_bDocMacroDisabledMessageShown);
+ return disallowMacroExecution();
+ }
+ else if ( m_xData->m_rDocumentAccess.macroCallsSeenWhileLoading() &&
+ bHasTrustedMacroSignature &&
+ !bHasValidContentSignature)
+ {
+ // When macros are signed, and the document has events which call macros, the document content needs to be signed too.
+ lcl_showMacrosDisabledUnsignedContentError(rxInteraction, m_xData->m_bDocMacroDisabledMessageShown);
+ return disallowMacroExecution();
+ }
+ else if ( bHasTrustedMacroSignature )
+ {
+ // there is trusted macro signature, allow macro execution
+ return allowMacroExecution();
+ }
+ else if ( nSignatureState == SignatureState::OK
+ || nSignatureState == SignatureState::NOTVALIDATED )
+ {
+ // there is valid signature, but it is not from the trusted author
+ if (!bAllowUIToAddAuthor)
+ lcl_showDocumentMacrosDisabledError(rxInteraction, m_xData->m_bDocMacroDisabledMessageShown);
+ return disallowMacroExecution();
+ }
+ }
+
+ // at this point it is clear that the document is neither in secure location nor signed with trusted certificate
+ if ( ( nMacroExecutionMode == MacroExecMode::FROM_LIST_AND_SIGNED_NO_WARN )
+ || ( nMacroExecutionMode == MacroExecMode::FROM_LIST_AND_SIGNED_WARN )
+ )
+ {
+ if ( nMacroExecutionMode == MacroExecMode::FROM_LIST_AND_SIGNED_WARN )
+ lcl_showDocumentMacrosDisabledError( rxInteraction, m_xData->m_bDocMacroDisabledMessageShown );
+
+ return disallowMacroExecution();
+ }
+ }
+ catch ( const Exception& )
+ {
+ if ( ( nMacroExecutionMode == MacroExecMode::FROM_LIST_NO_WARN )
+ || ( nMacroExecutionMode == MacroExecMode::FROM_LIST_AND_SIGNED_WARN )
+ || ( nMacroExecutionMode == MacroExecMode::FROM_LIST_AND_SIGNED_NO_WARN )
+ )
+ {
+ return disallowMacroExecution();
+ }
+ }
+
+ // confirmation is required
+ bool bSecure = false;
+
+ if ( eAutoConfirm == eNoAutoConfirm )
+ {
+ OUString sReferrer( m_xData->m_rDocumentAccess.getDocumentLocation() );
+
+ OUString aSystemFileURL;
+ if ( osl::FileBase::getSystemPathFromFileURL( sReferrer, aSystemFileURL ) == osl::FileBase::E_None )
+ sReferrer = aSystemFileURL;
+
+ bSecure = lcl_showMacroWarning( rxInteraction, sReferrer );
+ }
+ else
+ bSecure = ( eAutoConfirm == eAutoConfirmApprove );
+
+ return ( bSecure ? allowMacroExecution() : disallowMacroExecution() );
+ }
+
+
+ bool DocumentMacroMode::isMacroExecutionDisallowed() const
+ {
+ return m_xData->m_rDocumentAccess.getCurrentMacroExecMode() == MacroExecMode::NEVER_EXECUTE;
+ }
+
+
+ bool DocumentMacroMode::containerHasBasicMacros( const Reference< XLibraryContainer >& xContainer )
+ {
+ bool bHasMacroLib = false;
+ try
+ {
+ if ( xContainer.is() )
+ {
+ // a library container exists; check if it's empty
+
+ // if there are libraries except the "Standard" library
+ // we assume that they are not empty (because they have been created by the user)
+ if ( !xContainer->hasElements() )
+ bHasMacroLib = false;
+ else
+ {
+ static const OUStringLiteral aStdLibName( u"Standard" );
+ static const OUStringLiteral aVBAProject( u"VBAProject" );
+ const Sequence< OUString > aElements = xContainer->getElementNames();
+ for( const OUString& aElement : aElements )
+ {
+ if( aElement == aStdLibName || aElement == aVBAProject )
+ {
+ Reference < XNameAccess > xLib;
+ Any aAny = xContainer->getByName( aElement );
+ aAny >>= xLib;
+ if ( xLib.is() && xLib->hasElements() )
+ return true;
+ }
+ else
+ return true;
+ }
+ }
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("sfx.doc");
+ }
+ return bHasMacroLib;
+ }
+
+
+ bool DocumentMacroMode::hasMacroLibrary() const
+ {
+ bool bHasMacroLib = false;
+#if HAVE_FEATURE_SCRIPTING
+ try
+ {
+ Reference< XEmbeddedScripts > xScripts( m_xData->m_rDocumentAccess.getEmbeddedDocumentScripts() );
+ Reference< XLibraryContainer > xContainer;
+ if ( xScripts.is() )
+ xContainer.set( xScripts->getBasicLibraries(), UNO_QUERY_THROW );
+ bHasMacroLib = containerHasBasicMacros( xContainer );
+
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("sfx.doc");
+ }
+#endif
+ return bHasMacroLib;
+ }
+
+
+ bool DocumentMacroMode::storageHasMacros( const Reference< XStorage >& rxStorage )
+ {
+ bool bHasMacros = false;
+ if ( rxStorage.is() )
+ {
+ try
+ {
+ static constexpr OUStringLiteral s_sBasicStorageName( u"Basic" );
+ static constexpr OUStringLiteral s_sScriptsStorageName( u"Scripts" );
+
+ bHasMacros =( ( rxStorage->hasByName( s_sBasicStorageName )
+ && rxStorage->isStorageElement( s_sBasicStorageName )
+ )
+ || ( rxStorage->hasByName( s_sScriptsStorageName )
+ && rxStorage->isStorageElement( s_sScriptsStorageName )
+ )
+ );
+ }
+ catch ( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("sfx.doc");
+ }
+ }
+ return bHasMacros;
+ }
+
+
+ bool DocumentMacroMode::checkMacrosOnLoading( const Reference< XInteractionHandler >& rxInteraction, bool bHasValidContentSignature )
+ {
+ bool bAllow = false;
+ if ( SvtSecurityOptions::IsMacroDisabled() )
+ {
+ // no macro should be executed at all
+ bAllow = disallowMacroExecution();
+ }
+ else
+ {
+ if (m_xData->m_rDocumentAccess.documentStorageHasMacros() || hasMacroLibrary() || m_xData->m_rDocumentAccess.macroCallsSeenWhileLoading())
+ {
+ bAllow = adjustMacroMode( rxInteraction, bHasValidContentSignature );
+ }
+ else if ( !isMacroExecutionDisallowed() )
+ {
+ // if macros will be added by the user later, the security check is obsolete
+ bAllow = allowMacroExecution();
+ }
+ }
+ return bAllow;
+ }
+
+
+} // namespace sfx2
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/doc/docstoragemodifylistener.cxx b/sfx2/source/doc/docstoragemodifylistener.cxx
new file mode 100644
index 000000000..5c68c2bb8
--- /dev/null
+++ b/sfx2/source/doc/docstoragemodifylistener.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 <sfx2/docstoragemodifylistener.hxx>
+#include <comphelper/solarmutex.hxx>
+
+
+namespace sfx2
+{
+
+
+ using ::com::sun::star::lang::EventObject;
+
+
+ //=
+
+
+ DocumentStorageModifyListener::DocumentStorageModifyListener( IModifiableDocument& _rDocument, comphelper::SolarMutex& _rMutex )
+ :m_pDocument( &_rDocument )
+ ,m_rMutex( _rMutex )
+ {
+ }
+
+
+ DocumentStorageModifyListener::~DocumentStorageModifyListener()
+ {
+ }
+
+
+ void DocumentStorageModifyListener::dispose()
+ {
+ ::osl::Guard< comphelper::SolarMutex > aGuard( m_rMutex );
+ m_pDocument = nullptr;
+ }
+
+
+ void SAL_CALL DocumentStorageModifyListener::modified( const EventObject& /*aEvent*/ )
+ {
+ ::osl::Guard< comphelper::SolarMutex > aGuard( m_rMutex );
+ // storageIsModified must not contain any locking!
+ if ( m_pDocument )
+ m_pDocument->storageIsModified();
+ }
+
+
+ void SAL_CALL DocumentStorageModifyListener::disposing( const EventObject& /*Source*/ )
+ {
+ // not interested in. In particular, we do *not* dispose ourself when a storage we're
+ // listening at is disposed. The reason here is that this listener instance is *reused*
+ // in case the document is re-based to another storage.
+ }
+
+
+} // namespace sfx2
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/doc/doctempl.cxx b/sfx2/source/doc/doctempl.cxx
new file mode 100644
index 000000000..ebf2ddce4
--- /dev/null
+++ b/sfx2/source/doc/doctempl.cxx
@@ -0,0 +1,1745 @@
+/* -*- 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 <limits.h>
+#include <string_view>
+
+#include <com/sun/star/uno/Any.h>
+#include <osl/mutex.hxx>
+#include <sal/log.hxx>
+
+#include <unotools/pathoptions.hxx>
+#include <tools/urlobj.hxx>
+#include <tools/debug.hxx>
+#include <tools/diagnose_ex.h>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <ucbhelper/content.hxx>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/beans/XPropertySetInfo.hpp>
+#include <com/sun/star/document/XTypeDetection.hpp>
+#include <com/sun/star/document/DocumentProperties.hpp>
+#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
+#include <com/sun/star/frame/Desktop.hpp>
+#include <com/sun/star/frame/DocumentTemplates.hpp>
+#include <com/sun/star/frame/XDocumentTemplates.hpp>
+#include <com/sun/star/io/IOException.hpp>
+#include <com/sun/star/io/XPersist.hpp>
+#include <com/sun/star/lang/XLocalizable.hpp>
+#include <com/sun/star/sdbc/XResultSet.hpp>
+#include <com/sun/star/sdbc/XRow.hpp>
+#include <com/sun/star/ucb/ContentCreationException.hpp>
+#include <com/sun/star/ucb/NameClash.hpp>
+#include <com/sun/star/ucb/TransferInfo.hpp>
+#include <com/sun/star/ucb/XContent.hpp>
+#include <com/sun/star/ucb/XContentAccess.hpp>
+#include <com/sun/star/ucb/AnyCompareFactory.hpp>
+#include <com/sun/star/ucb/NumberedSortingInfo.hpp>
+
+#include "doctemplateslocal.hxx"
+#include <sfxurlrelocator.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::sdbc;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::ucb;
+using namespace ::com::sun::star::document;
+using namespace ::rtl;
+using namespace ::ucbhelper;
+
+
+#include <sfx2/doctempl.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/sfxresid.hxx>
+#include <sfx2/strings.hrc>
+#include <strings.hxx>
+#include <svtools/templatefoldercache.hxx>
+
+#include <memory>
+#include <vector>
+
+
+constexpr OUStringLiteral TITLE = u"Title";
+constexpr OUStringLiteral TARGET_URL = u"TargetURL";
+
+constexpr OUStringLiteral COMMAND_TRANSFER = u"transfer";
+
+namespace {
+
+class RegionData_Impl;
+
+}
+
+namespace DocTempl {
+
+namespace {
+
+class DocTempl_EntryData_Impl
+{
+ RegionData_Impl* mpParent;
+
+ // the following member must be SfxObjectShellLock since it controls that SfxObjectShell lifetime by design
+ // and users of this class expect it to be so.
+ SfxObjectShellLock mxObjShell;
+
+ OUString maTitle;
+ OUString maOwnURL;
+ OUString maTargetURL;
+
+public:
+ DocTempl_EntryData_Impl( RegionData_Impl* pParent,
+ const OUString& rTitle );
+
+ const OUString& GetTitle() const { return maTitle; }
+ const OUString& GetTargetURL();
+ const OUString& GetHierarchyURL();
+
+ void SetTitle( const OUString& rTitle ) { maTitle = rTitle; }
+ void SetTargetURL( const OUString& rURL ) { maTargetURL = rURL; }
+ void SetHierarchyURL( const OUString& rURL) { maOwnURL = rURL; }
+
+ int Compare( std::u16string_view rTitle ) const;
+};
+
+}
+
+}
+
+using namespace ::DocTempl;
+
+namespace {
+
+class RegionData_Impl
+{
+ const SfxDocTemplate_Impl* mpParent;
+ std::vector<std::unique_ptr<DocTempl_EntryData_Impl>> maEntries;
+ OUString maTitle;
+ OUString maOwnURL;
+
+private:
+ size_t GetEntryPos( std::u16string_view rTitle,
+ bool& rFound ) const;
+
+public:
+ RegionData_Impl( const SfxDocTemplate_Impl* pParent,
+ const OUString& rTitle );
+
+ void SetHierarchyURL( const OUString& rURL) { maOwnURL = rURL; }
+
+ DocTempl_EntryData_Impl* GetEntry( size_t nIndex ) const;
+ DocTempl_EntryData_Impl* GetEntry( std::u16string_view rName ) const;
+
+ const OUString& GetTitle() const { return maTitle; }
+ const OUString& GetHierarchyURL();
+
+ size_t GetCount() const;
+
+ void SetTitle( const OUString& rTitle ) { maTitle = rTitle; }
+
+ void AddEntry( const OUString& rTitle,
+ const OUString& rTargetURL,
+ const size_t *pPos );
+ void DeleteEntry( size_t nIndex );
+
+ int Compare( RegionData_Impl const * pCompareWith ) const;
+};
+
+}
+
+class SfxDocTemplate_Impl : public SvRefBase
+{
+ uno::Reference< XPersist > mxInfo;
+ uno::Reference< XDocumentTemplates > mxTemplates;
+
+ ::osl::Mutex maMutex;
+ OUString maRootURL;
+ OUString maStandardGroup;
+ std::vector<std::unique_ptr<RegionData_Impl>> maRegions;
+ bool mbConstructed;
+
+ uno::Reference< XAnyCompareFactory > m_rCompareFactory;
+
+ // the following member is intended to prevent clearing of the global data when it is in use
+ // TODO/LATER: it still does not make the implementation complete thread-safe
+ sal_Int32 mnLockCounter;
+
+private:
+ void Clear();
+
+public:
+ SfxDocTemplate_Impl();
+ virtual ~SfxDocTemplate_Impl() override;
+
+ void IncrementLock();
+ void DecrementLock();
+
+ bool Construct( );
+ void CreateFromHierarchy( Content &rTemplRoot );
+ void ReInitFromComponent();
+ void AddRegion( const OUString& rTitle,
+ Content& rContent );
+
+ void Rescan();
+
+ void DeleteRegion( size_t nIndex );
+
+ size_t GetRegionCount() const
+ { return maRegions.size(); }
+ RegionData_Impl* GetRegion( std::u16string_view rName ) const;
+ RegionData_Impl* GetRegion( size_t nIndex ) const;
+
+ bool GetTitleFromURL( const OUString& rURL, OUString& aTitle );
+ bool InsertRegion( std::unique_ptr<RegionData_Impl> pData, size_t nPos );
+ const OUString& GetRootURL() const { return maRootURL; }
+
+ const uno::Reference< XDocumentTemplates >& getDocTemplates() const { return mxTemplates; }
+};
+
+namespace {
+
+class DocTemplLocker_Impl
+{
+ SfxDocTemplate_Impl& m_aDocTempl;
+public:
+ explicit DocTemplLocker_Impl( SfxDocTemplate_Impl& aDocTempl )
+ : m_aDocTempl( aDocTempl )
+ {
+ m_aDocTempl.IncrementLock();
+ }
+
+ ~DocTemplLocker_Impl()
+ {
+ m_aDocTempl.DecrementLock();
+ }
+};
+
+}
+
+static SfxDocTemplate_Impl *gpTemplateData = nullptr;
+
+
+static bool getTextProperty_Impl( Content& rContent,
+ const OUString& rPropName,
+ OUString& rPropValue );
+
+
+OUString SfxDocumentTemplates::GetFullRegionName
+(
+ sal_uInt16 nIdx // Region Index
+) const
+
+/* [Description]
+
+ Returns the logical name of a region and its path
+
+ [Return value] Reference to the Region name
+
+*/
+
+{
+ // First: find the RegionData for the index
+
+ DocTemplLocker_Impl aLocker( *pImp );
+
+ if ( pImp->Construct() )
+ {
+ RegionData_Impl *pData1 = pImp->GetRegion( nIdx );
+
+ if ( pData1 )
+ return pData1->GetTitle();
+
+ // --**-- here was some code which appended the path to the
+ // group if there was more than one with the same name.
+ // this should not happen anymore
+ }
+
+ return OUString();
+}
+
+
+OUString SfxDocumentTemplates::GetRegionName
+(
+ sal_uInt16 nIdx // Region Index
+) const
+
+/* [Description]
+
+ Returns the logical name of a region
+
+ [Return value]
+
+ const String& Reference to the Region name
+
+*/
+{
+ DocTemplLocker_Impl aLocker( *pImp );
+
+ if ( pImp->Construct() )
+ {
+ RegionData_Impl *pData = pImp->GetRegion( nIdx );
+
+ if ( pData )
+ return pData->GetTitle();
+ }
+
+ return OUString();
+}
+
+
+sal_uInt16 SfxDocumentTemplates::GetRegionCount() const
+
+/* [Description]
+
+ Returns the number of Regions
+
+ [Return value]
+
+ sal_uInt16 Number of Regions
+*/
+{
+ DocTemplLocker_Impl aLocker( *pImp );
+
+ if ( !pImp->Construct() )
+ return 0;
+
+ return pImp->GetRegionCount();
+}
+
+
+sal_uInt16 SfxDocumentTemplates::GetCount
+(
+ sal_uInt16 nRegion /* Region index whose number is
+ to be determined */
+
+) const
+
+/* [Description]
+
+ Number of entries in Region
+
+ [Return value] Number of entries
+*/
+
+{
+ DocTemplLocker_Impl aLocker( *pImp );
+
+ if ( !pImp->Construct() )
+ return 0;
+
+ RegionData_Impl *pData = pImp->GetRegion( nRegion );
+
+ if ( !pData )
+ return 0;
+
+ return pData->GetCount();
+}
+
+
+OUString SfxDocumentTemplates::GetName
+(
+ sal_uInt16 nRegion, // Region Index, in which the entry lies
+ sal_uInt16 nIdx // Index of the entry
+) const
+
+/* [Description]
+
+ Returns the logical name of an entry in Region
+
+ [Return value]
+
+ const String& Entry Name
+*/
+
+{
+ DocTemplLocker_Impl aLocker( *pImp );
+
+ if ( pImp->Construct() )
+ {
+ RegionData_Impl *pRegion = pImp->GetRegion( nRegion );
+
+ if ( pRegion )
+ {
+ DocTempl_EntryData_Impl *pEntry = pRegion->GetEntry( nIdx );
+ if ( pEntry )
+ return pEntry->GetTitle();
+ }
+ }
+
+ return OUString();
+}
+
+
+OUString SfxDocumentTemplates::GetPath
+(
+ sal_uInt16 nRegion, // Region Index, in which the entry lies
+ sal_uInt16 nIdx // Index of the entry
+) const
+
+/* [Description]
+
+ Returns the file name with full path to the file assigned to an entry
+
+ [Return value]
+
+ String File name with full path
+*/
+{
+ DocTemplLocker_Impl aLocker( *pImp );
+
+ if ( !pImp->Construct() )
+ return OUString();
+
+ RegionData_Impl *pRegion = pImp->GetRegion( nRegion );
+
+ if ( pRegion )
+ {
+ DocTempl_EntryData_Impl *pEntry = pRegion->GetEntry( nIdx );
+ if ( pEntry )
+ return pEntry->GetTargetURL();
+ }
+
+ return OUString();
+}
+
+
+OUString SfxDocumentTemplates::GetTemplateTargetURLFromComponent( std::u16string_view aGroupName,
+ std::u16string_view aTitle )
+{
+ DocTemplLocker_Impl aLocker( *pImp );
+
+ INetURLObject aTemplateObj( pImp->GetRootURL() );
+
+ aTemplateObj.insertName( aGroupName, false,
+ INetURLObject::LAST_SEGMENT,
+ INetURLObject::EncodeMechanism::All );
+
+ aTemplateObj.insertName( aTitle, false,
+ INetURLObject::LAST_SEGMENT,
+ INetURLObject::EncodeMechanism::All );
+
+
+ Content aTemplate;
+ uno::Reference< XCommandEnvironment > aCmdEnv;
+ if ( Content::create( aTemplateObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ), aCmdEnv, comphelper::getProcessComponentContext(), aTemplate ) )
+ {
+ OUString aResult;
+ getTextProperty_Impl( aTemplate, TARGET_URL, aResult );
+ return SvtPathOptions().SubstituteVariable( aResult );
+ }
+
+ return OUString();
+}
+
+
+/** Convert a template name to its localised pair if it exists.
+ @param rString
+ Name to be translated.
+ @return
+ The localised pair of rString or rString if the former does not exist.
+*/
+OUString SfxDocumentTemplates::ConvertResourceString(const OUString& rString)
+{
+ static constexpr rtl::OUStringConstExpr aTemplateNames[] =
+ {
+ STR_TEMPLATE_NAME1_DEF,
+ STR_TEMPLATE_NAME2_DEF,
+ STR_TEMPLATE_NAME3_DEF,
+ STR_TEMPLATE_NAME4_DEF,
+ STR_TEMPLATE_NAME5_DEF,
+ STR_TEMPLATE_NAME6_DEF,
+ STR_TEMPLATE_NAME7_DEF,
+ STR_TEMPLATE_NAME8_DEF,
+ STR_TEMPLATE_NAME9_DEF,
+ STR_TEMPLATE_NAME10_DEF,
+ STR_TEMPLATE_NAME11_DEF,
+ STR_TEMPLATE_NAME12_DEF,
+ STR_TEMPLATE_NAME13_DEF,
+ STR_TEMPLATE_NAME14_DEF,
+ STR_TEMPLATE_NAME15_DEF,
+ STR_TEMPLATE_NAME16_DEF,
+ STR_TEMPLATE_NAME17_DEF,
+ STR_TEMPLATE_NAME18_DEF,
+ STR_TEMPLATE_NAME19_DEF,
+ STR_TEMPLATE_NAME20_DEF,
+ STR_TEMPLATE_NAME21_DEF,
+ STR_TEMPLATE_NAME22_DEF,
+ STR_TEMPLATE_NAME23_DEF,
+ STR_TEMPLATE_NAME24_DEF,
+ STR_TEMPLATE_NAME25_DEF,
+ STR_TEMPLATE_NAME26_DEF,
+ STR_TEMPLATE_NAME27_DEF,
+ STR_TEMPLATE_NAME28_DEF,
+ STR_TEMPLATE_NAME29_DEF,
+ STR_TEMPLATE_NAME30_DEF,
+ STR_TEMPLATE_NAME31_DEF,
+ STR_TEMPLATE_NAME32_DEF,
+ };
+
+ TranslateId STR_TEMPLATE_NAME[] =
+ {
+ STR_TEMPLATE_NAME1,
+ STR_TEMPLATE_NAME2,
+ STR_TEMPLATE_NAME3,
+ STR_TEMPLATE_NAME4,
+ STR_TEMPLATE_NAME5,
+ STR_TEMPLATE_NAME6,
+ STR_TEMPLATE_NAME7,
+ STR_TEMPLATE_NAME8,
+ STR_TEMPLATE_NAME9,
+ STR_TEMPLATE_NAME10,
+ STR_TEMPLATE_NAME11,
+ STR_TEMPLATE_NAME12,
+ STR_TEMPLATE_NAME13,
+ STR_TEMPLATE_NAME14,
+ STR_TEMPLATE_NAME15,
+ STR_TEMPLATE_NAME16,
+ STR_TEMPLATE_NAME17,
+ STR_TEMPLATE_NAME18,
+ STR_TEMPLATE_NAME19,
+ STR_TEMPLATE_NAME20,
+ STR_TEMPLATE_NAME21,
+ STR_TEMPLATE_NAME22,
+ STR_TEMPLATE_NAME23,
+ STR_TEMPLATE_NAME24,
+ STR_TEMPLATE_NAME25,
+ STR_TEMPLATE_NAME26,
+ STR_TEMPLATE_NAME27,
+ STR_TEMPLATE_NAME28,
+ STR_TEMPLATE_NAME29,
+ STR_TEMPLATE_NAME30,
+ STR_TEMPLATE_NAME31,
+ STR_TEMPLATE_NAME32,
+ };
+
+ static_assert(SAL_N_ELEMENTS(aTemplateNames) == SAL_N_ELEMENTS(STR_TEMPLATE_NAME));
+
+ for (size_t i = 0; i < SAL_N_ELEMENTS(STR_TEMPLATE_NAME); ++i)
+ {
+ if (rString == aTemplateNames[i])
+ return SfxResId(STR_TEMPLATE_NAME[i]);
+ }
+ return rString;
+}
+
+
+bool SfxDocumentTemplates::CopyOrMove
+(
+ sal_uInt16 nTargetRegion, // Target Region Index
+ sal_uInt16 nTargetIdx, // Target position Index
+ sal_uInt16 nSourceRegion, // Source Region Index
+ sal_uInt16 nSourceIdx, /* Index to be copied / to moved template */
+ bool bMove // Copy / Move
+)
+
+/* [Description]
+
+ Copy or move a document template
+
+ [Return value]
+
+ sal_Bool sal_True, Action could be performed
+ sal_False, Action could not be performed
+
+ [Cross-references]
+
+ <SfxDocumentTemplates::Move(sal_uInt16,sal_uInt16,sal_uInt16,sal_uInt16)>
+ <SfxDocumentTemplates::Copy(sal_uInt16,sal_uInt16,sal_uInt16,sal_uInt16)>
+*/
+
+{
+ /* to perform a copy or move, we need to send a transfer command to
+ the destination folder with the URL of the source as parameter.
+ ( If the destination content doesn't support the transfer command,
+ we could try a copy ( and delete ) instead. )
+ We need two transfers ( one for the real template and one for its
+ representation in the hierarchy )
+ ...
+ */
+
+ DocTemplLocker_Impl aLocker( *pImp );
+
+ if ( !pImp->Construct() )
+ return false;
+
+ // Don't copy or move any folders
+ if( nSourceIdx == USHRT_MAX )
+ return false ;
+
+ if ( nSourceRegion == nTargetRegion )
+ {
+ SAL_WARN( "sfx.doc", "Don't know, what to do!" );
+ return false;
+ }
+
+ RegionData_Impl *pSourceRgn = pImp->GetRegion( nSourceRegion );
+ if ( !pSourceRgn )
+ return false;
+
+ DocTempl_EntryData_Impl *pSource = pSourceRgn->GetEntry( nSourceIdx );
+ if ( !pSource )
+ return false;
+
+ RegionData_Impl *pTargetRgn = pImp->GetRegion( nTargetRegion );
+ if ( !pTargetRgn )
+ return false;
+
+ const OUString aTitle = pSource->GetTitle();
+
+ uno::Reference< XDocumentTemplates > xTemplates = pImp->getDocTemplates();
+
+ if ( xTemplates->addTemplate( pTargetRgn->GetTitle(),
+ aTitle,
+ pSource->GetTargetURL() ) )
+ {
+ const OUString aNewTargetURL = GetTemplateTargetURLFromComponent( pTargetRgn->GetTitle(), aTitle );
+ if ( aNewTargetURL.isEmpty() )
+ return false;
+
+ if ( bMove )
+ {
+ // --**-- delete the original file
+ bool bDeleted = xTemplates->removeTemplate( pSourceRgn->GetTitle(),
+ pSource->GetTitle() );
+ if ( bDeleted )
+ pSourceRgn->DeleteEntry( nSourceIdx );
+ else
+ {
+ if ( xTemplates->removeTemplate( pTargetRgn->GetTitle(), aTitle ) )
+ return false; // will trigger retry with copy instead of move
+
+ // if it is not possible to remove just created template ( must be possible! )
+ // it is better to report success here, since at least the copy has succeeded
+ // TODO/LATER: solve it more gracefully in future
+ }
+ }
+
+ // todo: fix SfxDocumentTemplates to handle size_t instead of sal_uInt16
+ size_t temp_nTargetIdx = nTargetIdx;
+ pTargetRgn->AddEntry( aTitle, aNewTargetURL, &temp_nTargetIdx );
+
+ return true;
+ }
+
+ // --**-- if the current file is opened,
+ // it must be re-opened afterwards.
+
+ return false;
+}
+
+
+bool SfxDocumentTemplates::Move
+(
+ sal_uInt16 nTargetRegion, // Target Region Index
+ sal_uInt16 nTargetIdx, // Target position Index
+ sal_uInt16 nSourceRegion, // Source Region Index
+ sal_uInt16 nSourceIdx /* Index to be copied / to moved template */
+)
+
+/* [Description]
+
+ Moving a template
+
+ [Return value]
+
+ sal_Bool sal_True, Action could be performed
+ sal_False, Action could not be performed
+
+ [Cross-references]
+
+ <SfxDocumentTemplates::CopyOrMove(sal_uInt16,sal_uInt16,sal_uInt16,sal_uInt16,sal_Bool)>
+*/
+{
+ DocTemplLocker_Impl aLocker( *pImp );
+
+ return CopyOrMove( nTargetRegion, nTargetIdx,
+ nSourceRegion, nSourceIdx, true );
+}
+
+
+bool SfxDocumentTemplates::Copy
+(
+ sal_uInt16 nTargetRegion, // Target Region Index
+ sal_uInt16 nTargetIdx, // Target position Index
+ sal_uInt16 nSourceRegion, // Source Region Index
+ sal_uInt16 nSourceIdx /* Index to be copied / to moved template */
+)
+
+/* [Description]
+
+ Copying a template
+
+ [Return value]
+
+ sal_Bool sal_True, Action could be performed
+ sal_False, Action could not be performed
+
+ [Cross-references]
+
+ <SfxDocumentTemplates::CopyOrMove(sal_uInt16,sal_uInt16,sal_uInt16,sal_uInt16,sal_Bool)>
+*/
+
+{
+ DocTemplLocker_Impl aLocker( *pImp );
+
+ return CopyOrMove( nTargetRegion, nTargetIdx,
+ nSourceRegion, nSourceIdx, false );
+}
+
+
+bool SfxDocumentTemplates::CopyTo
+(
+ sal_uInt16 nRegion, // Region of the template to be exported
+ sal_uInt16 nIdx, // Index of the template to be exported
+ std::u16string_view rName /* File name under which the template is to
+ be created */
+) const
+
+/* [Description]
+
+ Exporting a template into the file system
+
+ [Return value]
+
+ sal_Bool sal_True, Action could be performed
+ sal_False, Action could not be performed
+
+ [Cross-references]
+
+ <SfxDocumentTemplates::CopyFrom(sal_uInt16,sal_uInt16,String&)>
+*/
+
+{
+ DocTemplLocker_Impl aLocker( *pImp );
+
+ if ( ! pImp->Construct() )
+ return false;
+
+ RegionData_Impl *pSourceRgn = pImp->GetRegion( nRegion );
+ if ( !pSourceRgn )
+ return false;
+
+ DocTempl_EntryData_Impl *pSource = pSourceRgn->GetEntry( nIdx );
+ if ( !pSource )
+ return false;
+
+ INetURLObject aTargetURL( rName );
+
+ const OUString aTitle( aTargetURL.getName( INetURLObject::LAST_SEGMENT, true,
+ INetURLObject::DecodeMechanism::WithCharset ) );
+ aTargetURL.removeSegment();
+
+ const OUString aParentURL = aTargetURL.GetMainURL( INetURLObject::DecodeMechanism::NONE );
+
+ uno::Reference< XCommandEnvironment > aCmdEnv;
+ Content aTarget;
+
+ try
+ {
+ aTarget = Content( aParentURL, aCmdEnv, comphelper::getProcessComponentContext() );
+
+ TransferInfo aTransferInfo;
+ aTransferInfo.MoveData = false;
+ aTransferInfo.SourceURL = pSource->GetTargetURL();
+ aTransferInfo.NewTitle = aTitle;
+ aTransferInfo.NameClash = NameClash::RENAME;
+
+ Any aArg( aTransferInfo );
+ aTarget.executeCommand( COMMAND_TRANSFER, aArg );
+ }
+ catch ( ContentCreationException& )
+ { return false; }
+ catch ( Exception& )
+ { return false; }
+
+ return true;
+}
+
+
+bool SfxDocumentTemplates::CopyFrom
+(
+ sal_uInt16 nRegion, /* Region in which the template is to be
+ imported */
+ sal_uInt16 nIdx, // Index of the new template in this Region
+ OUString& rName /* File name of the template to be imported
+ as an out parameter of the (automatically
+ generated from the file name) logical name
+ of the template */
+)
+
+/* [Description]
+
+ Import a template from the file system
+
+ [Return value] Success (sal_True) or serfpTargetDirectory->GetContent());
+
+ sal_Bool sal_True, Action could be performed
+ sal_False, Action could not be performed
+
+ [Cross-references]
+
+ <SfxDocumentTemplates::CopyTo(sal_uInt16,sal_uInt16,const String&)>
+*/
+
+{
+ DocTemplLocker_Impl aLocker( *pImp );
+
+ if ( ! pImp->Construct() )
+ return false;
+
+ RegionData_Impl *pTargetRgn = pImp->GetRegion( nRegion );
+
+ if ( !pTargetRgn )
+ return false;
+
+ uno::Reference< XDocumentTemplates > xTemplates = pImp->getDocTemplates();
+ if ( !xTemplates.is() )
+ return false;
+
+ OUString aTitle;
+ bool bTemplateAdded = false;
+
+ if( pImp->GetTitleFromURL( rName, aTitle ) )
+ {
+ bTemplateAdded = xTemplates->addTemplate( pTargetRgn->GetTitle(), aTitle, rName );
+ }
+ else
+ {
+ uno::Reference< XDesktop2 > xDesktop = Desktop::create( ::comphelper::getProcessComponentContext() );
+
+ Sequence< PropertyValue > aArgs{ comphelper::makePropertyValue("Hidden", true) };
+
+ INetURLObject aTemplURL( rName );
+ uno::Reference< XDocumentPropertiesSupplier > xDocPropsSupplier;
+ uno::Reference< XStorable > xStorable;
+ try
+ {
+ xStorable.set(
+ xDesktop->loadComponentFromURL( aTemplURL.GetMainURL(INetURLObject::DecodeMechanism::NONE),
+ "_blank",
+ 0,
+ aArgs ),
+ UNO_QUERY );
+
+ xDocPropsSupplier.set( xStorable, UNO_QUERY );
+ }
+ catch( Exception& )
+ {
+ }
+
+ if( xStorable.is() )
+ {
+ // get Title from XDocumentPropertiesSupplier
+ if( xDocPropsSupplier.is() )
+ {
+ uno::Reference< XDocumentProperties > xDocProps
+ = xDocPropsSupplier->getDocumentProperties();
+ if (xDocProps.is() ) {
+ aTitle = xDocProps->getTitle();
+ }
+ }
+
+ if( aTitle.isEmpty() )
+ {
+ INetURLObject aURL( aTemplURL );
+ aURL.CutExtension();
+ aTitle = aURL.getName( INetURLObject::LAST_SEGMENT, true,
+ INetURLObject::DecodeMechanism::WithCharset );
+ }
+
+ // write a template using XStorable interface
+ bTemplateAdded = xTemplates->storeTemplate( pTargetRgn->GetTitle(), aTitle, xStorable );
+ }
+ }
+
+
+ if( bTemplateAdded )
+ {
+ INetURLObject aTemplObj( pTargetRgn->GetHierarchyURL() );
+ aTemplObj.insertName( aTitle, false,
+ INetURLObject::LAST_SEGMENT,
+ INetURLObject::EncodeMechanism::All );
+ const OUString aTemplURL = aTemplObj.GetMainURL( INetURLObject::DecodeMechanism::NONE );
+
+ uno::Reference< XCommandEnvironment > aCmdEnv;
+ Content aTemplCont;
+
+ if( Content::create( aTemplURL, aCmdEnv, comphelper::getProcessComponentContext(), aTemplCont ) )
+ {
+ OUString aTemplName;
+ if( getTextProperty_Impl( aTemplCont, TARGET_URL, aTemplName ) )
+ {
+ if ( nIdx == USHRT_MAX )
+ nIdx = 0;
+ else
+ ++nIdx;
+
+ // todo: fix SfxDocumentTemplates to handle size_t instead of sal_uInt16
+ size_t temp_nIdx = nIdx;
+ pTargetRgn->AddEntry( aTitle, aTemplName, &temp_nIdx );
+ rName = aTitle;
+ return true;
+ }
+ else
+ {
+ SAL_WARN( "sfx.doc", "CopyFrom(): The content should contain target URL!" );
+ }
+ }
+ else
+ {
+ SAL_WARN( "sfx.doc", "CopyFrom(): The content just was created!" );
+ }
+ }
+
+ return false;
+}
+
+
+bool SfxDocumentTemplates::Delete
+(
+ sal_uInt16 nRegion, // Region Index
+ sal_uInt16 nIdx /* Index of the entry or USHRT_MAX,
+ if a directory is meant. */
+)
+
+/* [Description]
+
+ Deleting an entry or a directory
+
+ [Return value]
+
+ sal_Bool sal_True, Action could be performed
+ sal_False, Action could not be performed
+
+ [Cross-references]
+
+ <SfxDocumentTemplates::InsertDir(const String&,sal_uInt16)>
+ <SfxDocumentTemplates::KillDir(SfxTemplateDir&)>
+*/
+
+{
+ DocTemplLocker_Impl aLocker( *pImp );
+
+ /* delete the template or folder in the hierarchy and in the
+ template folder by sending a delete command to the content.
+ Then remove the data from the lists
+ */
+ if ( ! pImp->Construct() )
+ return false;
+
+ RegionData_Impl *pRegion = pImp->GetRegion( nRegion );
+
+ if ( !pRegion )
+ return false;
+
+ bool bRet;
+ uno::Reference< XDocumentTemplates > xTemplates = pImp->getDocTemplates();
+
+ if ( nIdx == USHRT_MAX )
+ {
+ bRet = xTemplates->removeGroup( pRegion->GetTitle() );
+ if ( bRet )
+ pImp->DeleteRegion( nRegion );
+ }
+ else
+ {
+ DocTempl_EntryData_Impl *pEntry = pRegion->GetEntry( nIdx );
+
+ if ( !pEntry )
+ return false;
+
+ bRet = xTemplates->removeTemplate( pRegion->GetTitle(),
+ pEntry->GetTitle() );
+ if( bRet )
+ pRegion->DeleteEntry( nIdx );
+ }
+
+ return bRet;
+}
+
+
+bool SfxDocumentTemplates::InsertDir
+(
+ const OUString& rText, // the logical name of the new Region
+ sal_uInt16 nRegion // Region Index
+)
+
+/* [Description]
+
+ Insert an index
+
+ [Return value]
+
+ sal_Bool sal_True, Action could be performed
+ sal_False, Action could not be performed
+
+ [Cross-references]
+
+ <SfxDocumentTemplates::KillDir(SfxTemplateDir&)>
+*/
+{
+ DocTemplLocker_Impl aLocker( *pImp );
+
+ if ( ! pImp->Construct() )
+ return false;
+
+ RegionData_Impl *pRegion = pImp->GetRegion( rText );
+
+ if ( pRegion )
+ return false;
+
+ uno::Reference< XDocumentTemplates > xTemplates = pImp->getDocTemplates();
+
+ if ( xTemplates->addGroup( rText ) )
+ {
+ return pImp->InsertRegion( std::make_unique<RegionData_Impl>( pImp.get(), rText ), nRegion );
+ }
+
+ return false;
+}
+
+bool SfxDocumentTemplates::InsertTemplate(sal_uInt16 nSourceRegion, sal_uInt16 nIdx, const OUString &rName, const OUString &rPath)
+{
+ DocTemplLocker_Impl aLocker( *pImp );
+
+ if ( ! pImp->Construct() )
+ return false;
+
+ RegionData_Impl *pRegion = pImp->GetRegion( nSourceRegion );
+
+ if ( !pRegion )
+ return false;
+
+ size_t pos = nIdx;
+ pRegion->AddEntry( rName, rPath, &pos );
+
+ return true;
+}
+
+bool SfxDocumentTemplates::SetName( const OUString& rName, sal_uInt16 nRegion, sal_uInt16 nIdx )
+
+{
+ DocTemplLocker_Impl aLocker( *pImp );
+
+ if ( ! pImp->Construct() )
+ return false;
+
+ RegionData_Impl *pRegion = pImp->GetRegion( nRegion );
+
+ if ( !pRegion )
+ return false;
+
+ uno::Reference< XDocumentTemplates > xTemplates = pImp->getDocTemplates();
+
+ if ( nIdx == USHRT_MAX )
+ {
+ if ( pRegion->GetTitle() == rName )
+ return true;
+
+ // we have to rename a region
+ if ( xTemplates->renameGroup( pRegion->GetTitle(), rName ) )
+ {
+ pRegion->SetTitle( rName );
+ pRegion->SetHierarchyURL( "" );
+ return true;
+ }
+ }
+ else
+ {
+ DocTempl_EntryData_Impl *pEntry = pRegion->GetEntry( nIdx );
+
+ if ( !pEntry )
+ return false;
+
+ if ( pEntry->GetTitle() == rName )
+ return true;
+
+ if ( xTemplates->renameTemplate( pRegion->GetTitle(),
+ pEntry->GetTitle(),
+ rName ) )
+ {
+ pEntry->SetTitle( rName );
+ pEntry->SetTargetURL( "" );
+ pEntry->SetHierarchyURL( "" );
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+bool SfxDocumentTemplates::GetFull
+(
+ std::u16string_view rRegion, // Region Name
+ std::u16string_view rName, // Template Name
+ OUString &rPath // Out: Path + File name
+)
+
+/* [Description]
+
+ Returns Path + File name of the template specified by rRegion and rName.
+
+ [Return value]
+
+ sal_Bool sal_True, Action could be performed
+ sal_False, Action could not be performed
+
+ [Cross-references]
+
+ <SfxDocumentTemplates::GetLogicNames(const String&,String&,String&)>
+*/
+
+{
+ DocTemplLocker_Impl aLocker( *pImp );
+
+ // We don't search for empty names!
+ if ( rName.empty() )
+ return false;
+
+ if ( ! pImp->Construct() )
+ return false;
+
+ DocTempl_EntryData_Impl* pEntry = nullptr;
+ const sal_uInt16 nCount = GetRegionCount();
+
+ for ( sal_uInt16 i = 0; i < nCount; ++i )
+ {
+ RegionData_Impl *pRegion = pImp->GetRegion( i );
+
+ if( pRegion &&
+ ( rRegion.empty() || ( rRegion == pRegion->GetTitle() ) ) )
+ {
+ pEntry = pRegion->GetEntry( rName );
+
+ if ( pEntry )
+ {
+ rPath = pEntry->GetTargetURL();
+ break;
+ }
+ }
+ }
+
+ return ( pEntry != nullptr );
+}
+
+
+bool SfxDocumentTemplates::GetLogicNames
+(
+ std::u16string_view rPath, // Full Path to the template
+ OUString &rRegion, // Out: Region name
+ OUString &rName // Out: Template name
+) const
+
+/* [Description]
+
+ Returns and logical path name to the template specified by rPath
+
+ [Return value]
+
+ sal_Bool sal_True, Action could be performed
+ sal_False, Action could not be performed
+
+ [Cross-references]
+
+ <SfxDocumentTemplates::GetFull(const String&,const String&,DirEntry&)>
+*/
+
+{
+ DocTemplLocker_Impl aLocker( *pImp );
+
+ if ( ! pImp->Construct() )
+ return false;
+
+ INetURLObject aFullPath;
+
+ aFullPath.SetSmartProtocol( INetProtocol::File );
+ aFullPath.SetURL( rPath );
+ const OUString aPath( aFullPath.GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
+
+ const sal_uInt16 nCount = GetRegionCount();
+
+ for ( sal_uInt16 i=0; i<nCount; ++i )
+ {
+ RegionData_Impl *pData = pImp->GetRegion( i );
+ if ( pData )
+ {
+ const sal_uInt16 nChildCount = pData->GetCount();
+
+ for ( sal_uInt16 j=0; j<nChildCount; ++j )
+ {
+ DocTempl_EntryData_Impl *pEntry = pData->GetEntry( j );
+ if ( pEntry && pEntry->GetTargetURL() == aPath )
+ {
+ rRegion = pData->GetTitle();
+ rName = pEntry->GetTitle();
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
+
+SfxDocumentTemplates::SfxDocumentTemplates()
+
+/* [Description]
+
+ Constructor
+*/
+{
+ if ( !gpTemplateData )
+ gpTemplateData = new SfxDocTemplate_Impl;
+
+ pImp = gpTemplateData;
+}
+
+
+SfxDocumentTemplates::~SfxDocumentTemplates()
+
+/* [Description]
+
+ Destructor
+ Release of administrative data
+*/
+
+{
+ pImp = nullptr;
+}
+
+void SfxDocumentTemplates::Update( )
+{
+ if ( ::svt::TemplateFolderCache( true ).needsUpdate() ) // update is really necessary
+ {
+ if ( pImp->Construct() )
+ pImp->Rescan();
+ }
+}
+
+void SfxDocumentTemplates::ReInitFromComponent()
+{
+ pImp->ReInitFromComponent();
+}
+
+DocTempl_EntryData_Impl::DocTempl_EntryData_Impl( RegionData_Impl* pParent,
+ const OUString& rTitle )
+{
+ mpParent = pParent;
+ maTitle = SfxDocumentTemplates::ConvertResourceString(rTitle);
+}
+
+
+int DocTempl_EntryData_Impl::Compare( std::u16string_view rTitle ) const
+{
+ return maTitle.compareTo( rTitle );
+}
+
+
+const OUString& DocTempl_EntryData_Impl::GetHierarchyURL()
+{
+ if ( maOwnURL.isEmpty() )
+ {
+ INetURLObject aTemplateObj( mpParent->GetHierarchyURL() );
+
+ aTemplateObj.insertName( GetTitle(), false,
+ INetURLObject::LAST_SEGMENT,
+ INetURLObject::EncodeMechanism::All );
+
+ maOwnURL = aTemplateObj.GetMainURL( INetURLObject::DecodeMechanism::NONE );
+ DBG_ASSERT( !maOwnURL.isEmpty(), "GetHierarchyURL(): Could not create URL!" );
+ }
+
+ return maOwnURL;
+}
+
+
+const OUString& DocTempl_EntryData_Impl::GetTargetURL()
+{
+ if ( maTargetURL.isEmpty() )
+ {
+ uno::Reference< XCommandEnvironment > aCmdEnv;
+ Content aRegion;
+
+ if ( Content::create( GetHierarchyURL(), aCmdEnv, comphelper::getProcessComponentContext(), aRegion ) )
+ {
+ getTextProperty_Impl( aRegion, TARGET_URL, maTargetURL );
+ }
+ else
+ {
+ SAL_WARN( "sfx.doc", "GetTargetURL(): Could not create hierarchy content!" );
+ }
+ }
+
+ return maTargetURL;
+}
+
+
+RegionData_Impl::RegionData_Impl( const SfxDocTemplate_Impl* pParent,
+ const OUString& rTitle )
+ : mpParent(pParent), maTitle(rTitle)
+{
+}
+
+
+size_t RegionData_Impl::GetEntryPos( std::u16string_view rTitle, bool& rFound ) const
+{
+ const size_t nCount = maEntries.size();
+
+ for ( size_t i=0; i<nCount; ++i )
+ {
+ auto &pData = maEntries[ i ];
+
+ if ( pData->Compare( rTitle ) == 0 )
+ {
+ rFound = true;
+ return i;
+ }
+ }
+
+ rFound = false;
+ return nCount;
+}
+
+
+void RegionData_Impl::AddEntry( const OUString& rTitle,
+ const OUString& rTargetURL,
+ const size_t *pPos )
+{
+ INetURLObject aLinkObj( GetHierarchyURL() );
+ aLinkObj.insertName( rTitle, false,
+ INetURLObject::LAST_SEGMENT,
+ INetURLObject::EncodeMechanism::All );
+ const OUString aLinkURL = aLinkObj.GetMainURL( INetURLObject::DecodeMechanism::NONE );
+
+ bool bFound = false;
+ size_t nPos = GetEntryPos( rTitle, bFound );
+
+ if ( bFound )
+ return;
+
+ if ( pPos )
+ nPos = *pPos;
+
+ auto pEntry = std::make_unique<DocTempl_EntryData_Impl>(
+ this, rTitle );
+ pEntry->SetTargetURL( rTargetURL );
+ pEntry->SetHierarchyURL( aLinkURL );
+ if ( nPos < maEntries.size() ) {
+ auto it = maEntries.begin();
+ std::advance( it, nPos );
+ maEntries.insert( it, std::move(pEntry) );
+ }
+ else
+ maEntries.push_back( std::move(pEntry) );
+}
+
+
+size_t RegionData_Impl::GetCount() const
+{
+ return maEntries.size();
+}
+
+
+const OUString& RegionData_Impl::GetHierarchyURL()
+{
+ if ( maOwnURL.isEmpty() )
+ {
+ INetURLObject aRegionObj( mpParent->GetRootURL() );
+
+ aRegionObj.insertName( GetTitle(), false,
+ INetURLObject::LAST_SEGMENT,
+ INetURLObject::EncodeMechanism::All );
+
+ maOwnURL = aRegionObj.GetMainURL( INetURLObject::DecodeMechanism::NONE );
+ DBG_ASSERT( !maOwnURL.isEmpty(), "GetHierarchyURL(): Could not create URL!" );
+ }
+
+ return maOwnURL;
+}
+
+
+DocTempl_EntryData_Impl* RegionData_Impl::GetEntry( std::u16string_view rName ) const
+{
+ bool bFound = false;
+ tools::Long nPos = GetEntryPos( rName, bFound );
+
+ if ( bFound )
+ return maEntries[ nPos ].get();
+ return nullptr;
+}
+
+
+DocTempl_EntryData_Impl* RegionData_Impl::GetEntry( size_t nIndex ) const
+{
+ if ( nIndex < maEntries.size() )
+ return maEntries[ nIndex ].get();
+ return nullptr;
+}
+
+
+void RegionData_Impl::DeleteEntry( size_t nIndex )
+{
+ if ( nIndex < maEntries.size() )
+ {
+ auto it = maEntries.begin();
+ std::advance( it, nIndex );
+ maEntries.erase( it );
+ }
+}
+
+
+int RegionData_Impl::Compare( RegionData_Impl const * pCompare ) const
+{
+ return maTitle.compareTo( pCompare->maTitle );
+}
+
+
+SfxDocTemplate_Impl::SfxDocTemplate_Impl()
+: mbConstructed( false )
+, mnLockCounter( 0 )
+{
+}
+
+
+SfxDocTemplate_Impl::~SfxDocTemplate_Impl()
+{
+ gpTemplateData = nullptr;
+}
+
+
+void SfxDocTemplate_Impl::IncrementLock()
+{
+ ::osl::MutexGuard aGuard( maMutex );
+ mnLockCounter++;
+}
+
+
+void SfxDocTemplate_Impl::DecrementLock()
+{
+ ::osl::MutexGuard aGuard( maMutex );
+ if ( mnLockCounter )
+ mnLockCounter--;
+}
+
+
+RegionData_Impl* SfxDocTemplate_Impl::GetRegion( size_t nIndex ) const
+{
+ if ( nIndex < maRegions.size() )
+ return maRegions[ nIndex ].get();
+ return nullptr;
+}
+
+
+RegionData_Impl* SfxDocTemplate_Impl::GetRegion( std::u16string_view rName )
+ const
+{
+ for (auto& pData : maRegions)
+ {
+ if( pData->GetTitle() == rName )
+ return pData.get();
+ }
+ return nullptr;
+}
+
+
+void SfxDocTemplate_Impl::DeleteRegion( size_t nIndex )
+{
+ if ( nIndex < maRegions.size() )
+ {
+ auto it = maRegions.begin();
+ std::advance( it, nIndex );
+ maRegions.erase( it );
+ }
+}
+
+
+/* AddRegion adds a Region to the RegionList
+*/
+void SfxDocTemplate_Impl::AddRegion( const OUString& rTitle,
+ Content& rContent )
+{
+ auto pRegion = std::make_unique<RegionData_Impl>( this, rTitle );
+ auto pRegionTmp = pRegion.get();
+
+ if ( ! InsertRegion( std::move(pRegion), size_t(-1) ) )
+ {
+ return;
+ }
+
+ // now get the content of the region
+ uno::Reference< XResultSet > xResultSet;
+
+ try
+ {
+ xResultSet = rContent.createSortedCursor( { TITLE, TARGET_URL }, { { 1, true } }, m_rCompareFactory, INCLUDE_DOCUMENTS_ONLY );
+ }
+ catch ( Exception& ) {}
+
+ if ( !xResultSet.is() )
+ return;
+
+ uno::Reference< XRow > xRow( xResultSet, UNO_QUERY );
+
+ try
+ {
+ while ( xResultSet->next() )
+ {
+ pRegionTmp->AddEntry( xRow->getString( 1 ), xRow->getString( 2 ), nullptr );
+ }
+ }
+ catch ( Exception& ) {}
+}
+
+
+void SfxDocTemplate_Impl::CreateFromHierarchy( Content &rTemplRoot )
+{
+ uno::Reference< XResultSet > xResultSet;
+ Sequence< OUString > aProps { TITLE };
+
+ try
+ {
+ xResultSet = rTemplRoot.createSortedCursor(
+ aProps,
+ { // Sequence
+ { // NumberedSortingInfo
+ /* ColumnIndex */ 1, /* Ascending */ true
+ }
+ },
+ m_rCompareFactory,
+ INCLUDE_FOLDERS_ONLY
+ );
+ }
+ catch ( Exception& ) {}
+
+ if ( !xResultSet.is() )
+ return;
+
+ uno::Reference< XCommandEnvironment > aCmdEnv;
+ uno::Reference< XContentAccess > xContentAccess( xResultSet, UNO_QUERY );
+ uno::Reference< XRow > xRow( xResultSet, UNO_QUERY );
+
+ try
+ {
+ while ( xResultSet->next() )
+ {
+ const OUString aId = xContentAccess->queryContentIdentifierString();
+ Content aContent( aId, aCmdEnv, comphelper::getProcessComponentContext() );
+
+ AddRegion( xRow->getString( 1 ), aContent );
+ }
+ }
+ catch ( Exception& ) {}
+}
+
+
+bool SfxDocTemplate_Impl::Construct( )
+{
+ ::osl::MutexGuard aGuard( maMutex );
+
+ if ( mbConstructed )
+ return true;
+
+ uno::Reference< XComponentContext > xContext = ::comphelper::getProcessComponentContext();
+
+ uno::Reference< XPersist > xInfo( document::DocumentProperties::create(xContext), UNO_QUERY );
+ mxInfo = xInfo;
+
+ mxTemplates = frame::DocumentTemplates::create(xContext);
+
+ uno::Reference< XLocalizable > xLocalizable( mxTemplates, UNO_QUERY );
+
+ m_rCompareFactory = AnyCompareFactory::createWithLocale(xContext, xLocalizable->getLocale());
+
+ uno::Reference < XContent > aRootContent = mxTemplates->getContent();
+ uno::Reference < XCommandEnvironment > aCmdEnv;
+
+ if ( ! aRootContent.is() )
+ return false;
+
+ mbConstructed = true;
+ maRootURL = aRootContent->getIdentifier()->getContentIdentifier();
+
+ maStandardGroup = DocTemplLocaleHelper::GetStandardGroupString();
+ Content aTemplRoot( aRootContent, aCmdEnv, xContext );
+ CreateFromHierarchy( aTemplRoot );
+
+ return true;
+}
+
+
+void SfxDocTemplate_Impl::ReInitFromComponent()
+{
+ uno::Reference< XDocumentTemplates > xTemplates = getDocTemplates();
+ if ( xTemplates.is() )
+ {
+ uno::Reference < XContent > aRootContent = xTemplates->getContent();
+ uno::Reference < XCommandEnvironment > aCmdEnv;
+ Content aTemplRoot( aRootContent, aCmdEnv, comphelper::getProcessComponentContext() );
+ Clear();
+ CreateFromHierarchy( aTemplRoot );
+ }
+}
+
+
+bool SfxDocTemplate_Impl::InsertRegion( std::unique_ptr<RegionData_Impl> pNew, size_t nPos )
+{
+ ::osl::MutexGuard aGuard( maMutex );
+
+ // return false (not inserted) if the entry already exists
+ for (auto const& pRegion : maRegions)
+ if ( pRegion->Compare( pNew.get() ) == 0 )
+ return false;
+
+ size_t newPos = nPos;
+ if ( pNew->GetTitle() == maStandardGroup )
+ newPos = 0;
+
+ if ( newPos < maRegions.size() )
+ {
+ auto it = maRegions.begin();
+ std::advance( it, newPos );
+ maRegions.emplace( it, std::move(pNew) );
+ }
+ else
+ maRegions.emplace_back( std::move(pNew) );
+
+ return true;
+}
+
+
+void SfxDocTemplate_Impl::Rescan()
+{
+ Clear();
+
+ try
+ {
+ uno::Reference< XDocumentTemplates > xTemplates = getDocTemplates();
+ DBG_ASSERT( xTemplates.is(), "SfxDocTemplate_Impl::Rescan:invalid template instance!" );
+ if ( xTemplates.is() )
+ {
+ xTemplates->update();
+
+ uno::Reference < XContent > aRootContent = xTemplates->getContent();
+ uno::Reference < XCommandEnvironment > aCmdEnv;
+
+ Content aTemplRoot( aRootContent, aCmdEnv, comphelper::getProcessComponentContext() );
+ CreateFromHierarchy( aTemplRoot );
+ }
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "sfx.doc", "SfxDocTemplate_Impl::Rescan: caught an exception while doing the update" );
+ }
+}
+
+
+bool SfxDocTemplate_Impl::GetTitleFromURL( const OUString& rURL,
+ OUString& aTitle )
+{
+ if ( mxInfo.is() )
+ {
+ try
+ {
+ mxInfo->read( rURL );
+ }
+ catch ( Exception& )
+ {
+ // the document is not a StarOffice document
+ return false;
+ }
+
+
+ try
+ {
+ uno::Reference< XPropertySet > aPropSet( mxInfo, UNO_QUERY );
+ if ( aPropSet.is() )
+ {
+ Any aValue = aPropSet->getPropertyValue( TITLE );
+ aValue >>= aTitle;
+ }
+ }
+ catch ( IOException& ) {}
+ catch ( UnknownPropertyException& ) {}
+ catch ( Exception& ) {}
+ }
+
+ if ( aTitle.isEmpty() )
+ {
+ INetURLObject aURL( rURL );
+ aURL.CutExtension();
+ aTitle = aURL.getName( INetURLObject::LAST_SEGMENT, true,
+ INetURLObject::DecodeMechanism::WithCharset );
+ }
+
+ return true;
+}
+
+
+void SfxDocTemplate_Impl::Clear()
+{
+ ::osl::MutexGuard aGuard( maMutex );
+ if ( mnLockCounter )
+ return;
+ maRegions.clear();
+}
+
+
+bool getTextProperty_Impl( Content& rContent,
+ const OUString& rPropName,
+ OUString& rPropValue )
+{
+ bool bGotProperty = false;
+
+ // Get the property
+ try
+ {
+ uno::Reference< XPropertySetInfo > aPropInfo = rContent.getProperties();
+
+ // check, whether or not the property exists
+ if ( !aPropInfo.is() || !aPropInfo->hasPropertyByName( rPropName ) )
+ {
+ return false;
+ }
+
+ // now get the property
+ Any aAnyValue = rContent.getPropertyValue( rPropName );
+ aAnyValue >>= rPropValue;
+
+ if ( SfxURLRelocator_Impl::propertyCanContainOfficeDir( rPropName ) )
+ {
+ SfxURLRelocator_Impl aRelocImpl( ::comphelper::getProcessComponentContext() );
+ aRelocImpl.makeAbsoluteURL( rPropValue );
+ }
+
+ bGotProperty = true;
+ }
+ catch ( RuntimeException& ) {}
+ catch ( Exception& ) {}
+
+ return bGotProperty;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/doc/doctemplates.cxx b/sfx2/source/doc/doctemplates.cxx
new file mode 100644
index 000000000..6005aa5fd
--- /dev/null
+++ b/sfx2/source/doc/doctemplates.cxx
@@ -0,0 +1,2734 @@
+/* -*- 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 <osl/mutex.hxx>
+#include <tools/diagnose_ex.h>
+#include <tools/urlobj.hxx>
+#include <rtl/ustring.hxx>
+#include <sal/log.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/wrkwin.hxx>
+#include <unotools/pathoptions.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/propertysequence.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <comphelper/sequenceashashmap.hxx>
+#include <comphelper/storagehelper.hxx>
+#include <comphelper/string.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <com/sun/star/beans/IllegalTypeException.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/beans/PropertyExistException.hpp>
+#include <com/sun/star/beans/XPropertySetInfo.hpp>
+#include <com/sun/star/beans/XPropertyContainer.hpp>
+#include <com/sun/star/beans/StringPair.hpp>
+#include <com/sun/star/ucb/SimpleFileAccess.hpp>
+#include <com/sun/star/util/theMacroExpander.hpp>
+#include <com/sun/star/util/theOfficeInstallationDirectories.hpp>
+#include <com/sun/star/configuration/theDefaultProvider.hpp>
+#include <com/sun/star/document/XTypeDetection.hpp>
+#include <com/sun/star/document/DocumentProperties.hpp>
+#include <com/sun/star/io/TempFile.hpp>
+#include <com/sun/star/sdbc/XResultSet.hpp>
+#include <com/sun/star/sdbc/XRow.hpp>
+#include <com/sun/star/ucb/ContentCreationException.hpp>
+#include <com/sun/star/ucb/NameClash.hpp>
+#include <com/sun/star/ucb/NameClashException.hpp>
+#include <com/sun/star/ucb/XCommandEnvironment.hpp>
+#include <com/sun/star/ucb/XContentAccess.hpp>
+#include <com/sun/star/frame/ModuleManager.hpp>
+#include <com/sun/star/uno/Exception.hpp>
+#include <com/sun/star/task/InteractionHandler.hpp>
+#include <com/sun/star/ucb/XProgressHandler.hpp>
+#include <com/sun/star/container/XNameAccess.hpp>
+#include <com/sun/star/frame/XDocumentTemplates.hpp>
+#include <com/sun/star/frame/XStorable.hpp>
+#include <com/sun/star/lang/Locale.hpp>
+#include <com/sun/star/lang/XLocalizable.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/ucb/XContent.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/uno/RuntimeException.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/util/thePathSettings.hpp>
+
+#include <svtools/templatefoldercache.hxx>
+#include <unotools/configmgr.hxx>
+#include <unotools/ucbhelper.hxx>
+#include <i18nlangtag/languagetag.hxx>
+#include <ucbhelper/content.hxx>
+#include <o3tl/string_view.hxx>
+
+#include <sfx2/sfxresid.hxx>
+#include <sfxurlrelocator.hxx>
+#include "doctemplateslocal.hxx"
+#include <sfx2/docfac.hxx>
+#include <sfx2/strings.hrc>
+#include <doctempl.hrc>
+
+#include <memory>
+#include <vector>
+
+constexpr OUStringLiteral SERVICENAME_TYPEDETECTION = u"com.sun.star.document.TypeDetection";
+
+constexpr OUStringLiteral TEMPLATE_ROOT_URL = u"vnd.sun.star.hier:/templates";
+constexpr OUStringLiteral TITLE = u"Title";
+constexpr OUStringLiteral IS_FOLDER = u"IsFolder";
+constexpr OUStringLiteral IS_DOCUMENT = u"IsDocument";
+constexpr OUStringLiteral TARGET_URL = u"TargetURL";
+constexpr OUStringLiteral TEMPLATE_VERSION = u"TemplateComponentVersion";
+constexpr OUStringLiteral TEMPLATE_VERSION_VALUE = u"2";
+constexpr OUStringLiteral TYPE_FOLDER = u"application/vnd.sun.star.hier-folder";
+constexpr OUStringLiteral TYPE_LINK = u"application/vnd.sun.star.hier-link";
+constexpr OUStringLiteral TYPE_FSYS_FOLDER = u"application/vnd.sun.staroffice.fsys-folder";
+constexpr OUStringLiteral TYPE_FSYS_FILE = u"application/vnd.sun.staroffice.fsys-file";
+
+constexpr OUStringLiteral PROPERTY_DIRLIST = u"DirectoryList";
+constexpr OUStringLiteral PROPERTY_NEEDSUPDATE = u"NeedsUpdate";
+constexpr OUStringLiteral PROPERTY_TYPE = u"TypeDescription";
+
+constexpr OUStringLiteral TARGET_DIR_URL = u"TargetDirURL";
+constexpr OUStringLiteral COMMAND_DELETE = u"delete";
+
+constexpr OUStringLiteral STANDARD_FOLDER = u"standard";
+
+#define C_DELIM ';'
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::document;
+using namespace ::com::sun::star::io;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::sdbc;
+using namespace ::com::sun::star::ucb;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::util;
+
+using namespace ::ucbhelper;
+using namespace ::comphelper;
+
+using ::std::vector;
+
+namespace {
+
+class WaitWindow_Impl : public WorkWindow
+{
+ tools::Rectangle maRect;
+ OUString maText;
+ static constexpr DrawTextFlags gnTextStyle = DrawTextFlags::Center | DrawTextFlags::VCenter | DrawTextFlags::WordBreak | DrawTextFlags::MultiLine;
+
+public:
+ WaitWindow_Impl();
+ virtual ~WaitWindow_Impl() override;
+ virtual void dispose() override;
+ virtual void Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect) override;
+};
+
+#define X_OFFSET 15
+#define Y_OFFSET 15
+
+
+struct NamePair_Impl
+{
+ OUString maShortName;
+ OUString maLongName;
+};
+
+class DocTemplates_EntryData_Impl;
+class GroupData_Impl;
+
+typedef vector< std::unique_ptr<GroupData_Impl> > GroupList_Impl;
+
+
+class TplTaskEnvironment : public ::cppu::WeakImplHelper< ucb::XCommandEnvironment >
+{
+ uno::Reference< task::XInteractionHandler > m_xInteractionHandler;
+
+public:
+ explicit TplTaskEnvironment( const uno::Reference< task::XInteractionHandler>& rxInteractionHandler )
+ : m_xInteractionHandler( rxInteractionHandler )
+ {}
+
+ virtual uno::Reference<task::XInteractionHandler> SAL_CALL getInteractionHandler() override
+ { return m_xInteractionHandler; }
+
+ virtual uno::Reference<ucb::XProgressHandler> SAL_CALL getProgressHandler() override
+ { return uno::Reference<ucb::XProgressHandler>(); }
+};
+
+class SfxDocTplService_Impl
+{
+ uno::Reference< XComponentContext > mxContext;
+ uno::Reference< XCommandEnvironment > maCmdEnv;
+ uno::Reference< XDocumentProperties> m_xDocProps;
+ uno::Reference< XTypeDetection > mxType;
+
+ ::osl::Mutex maMutex;
+ Sequence< OUString > maTemplateDirs;
+ Sequence< OUString > maInternalTemplateDirs;
+ OUString maRootURL;
+ std::vector< NamePair_Impl > maNames;
+ lang::Locale maLocale;
+ Content maRootContent;
+ bool mbIsInitialized : 1;
+ bool mbLocaleSet : 1;
+
+ SfxURLRelocator_Impl maRelocator;
+
+ void init_Impl();
+ void getDefaultLocale();
+ void getDirList();
+ void readFolderList();
+ bool needsUpdate();
+ OUString getLongName( const OUString& rShortName );
+ bool setTitleForURL( const OUString& rURL, const OUString& aTitle );
+ void getTitleFromURL( const OUString& rURL, OUString& aTitle, OUString& aType, bool& bDocHasTitle );
+
+ bool addEntry( Content& rParentFolder,
+ const OUString& rTitle,
+ const OUString& rTargetURL,
+ const OUString& rType );
+
+ bool createFolder( const OUString& rNewFolderURL,
+ bool bCreateParent,
+ bool bFsysFolder,
+ Content &rNewFolder );
+
+ static bool CreateNewUniqueFolderWithPrefix( std::u16string_view aPath,
+ const OUString& aPrefix,
+ OUString& aNewFolderName,
+ OUString& aNewFolderURL,
+ Content& aNewFolder );
+ static OUString CreateNewUniqueFileWithPrefix( std::u16string_view aPath,
+ const OUString& aPrefix,
+ const OUString& aExt );
+
+ std::vector< beans::StringPair > ReadUINamesForTemplateDir_Impl( std::u16string_view aUserPath );
+ bool UpdateUINamesForTemplateDir_Impl( std::u16string_view aUserPath,
+ const OUString& aGroupName,
+ const OUString& aNewFolderName );
+ bool ReplaceUINamesForTemplateDir_Impl( std::u16string_view aUserPath,
+ const OUString& aFsysGroupName,
+ std::u16string_view aOldGroupName,
+ const OUString& aNewGroupName );
+ void RemoveUINamesForTemplateDir_Impl( std::u16string_view aUserPath,
+ std::u16string_view aGroupName );
+ bool WriteUINamesForTemplateDir_Impl( std::u16string_view aUserPath,
+ const std::vector< beans::StringPair >& aUINames );
+
+ OUString CreateNewGroupFsys( const OUString& rGroupName, Content& aGroup );
+
+ static bool removeContent( Content& rContent );
+ bool removeContent( const OUString& rContentURL );
+
+ bool setProperty( Content& rContent,
+ const OUString& rPropName,
+ const Any& rPropValue );
+ bool getProperty( Content& rContent,
+ const OUString& rPropName,
+ Any& rPropValue );
+
+ void createFromContent( GroupList_Impl& rList,
+ Content &rContent,
+ bool bHierarchy,
+ bool bWriteableContent );
+ void addHierGroup( GroupList_Impl& rList,
+ const OUString& rTitle,
+ const OUString& rOwnURL );
+ void addFsysGroup( GroupList_Impl& rList,
+ const OUString& rTitle,
+ const OUString& rUITitle,
+ const OUString& rOwnURL,
+ bool bWriteableGroup );
+ void removeFromHierarchy( DocTemplates_EntryData_Impl const *pData );
+ void addToHierarchy( GroupData_Impl const *pGroup,
+ DocTemplates_EntryData_Impl const *pData );
+
+ void removeFromHierarchy( GroupData_Impl const *pGroup );
+ void addGroupToHierarchy( GroupData_Impl *pGroup );
+
+ void updateData( DocTemplates_EntryData_Impl const *pData );
+
+ //See: #i66157# and rhbz#1065807
+ //return which template dir the rURL is a subpath of
+ OUString findParentTemplateDir(const OUString& rURL) const;
+
+ //See: #i66157# and rhbz#1065807
+ //return true if rURL is a path (or subpath of) a dir which is not a user path
+ //which implies neither it or its contents can be removed
+ bool isInternalTemplateDir(const OUString& rURL) const;
+public:
+ explicit SfxDocTplService_Impl( const uno::Reference< XComponentContext > & xContext );
+ ~SfxDocTplService_Impl();
+
+ bool init() { if ( !mbIsInitialized ) init_Impl(); return mbIsInitialized; }
+ const Content& getContent() const { return maRootContent; }
+
+ void setLocale( const lang::Locale & rLocale );
+ lang::Locale getLocale();
+
+ bool storeTemplate( const OUString& rGroupName,
+ const OUString& rTemplateName,
+ const uno::Reference< frame::XStorable >& rStorable );
+
+ bool addTemplate( const OUString& rGroupName,
+ const OUString& rTemplateName,
+ const OUString& rSourceURL );
+ bool removeTemplate( std::u16string_view rGroupName,
+ std::u16string_view rTemplateName );
+ bool renameTemplate( std::u16string_view rGroupName,
+ std::u16string_view rOldName,
+ const OUString& rNewName );
+
+ bool addGroup( const OUString& rGroupName );
+ bool removeGroup( std::u16string_view rGroupName );
+ bool renameGroup( std::u16string_view rOldName,
+ const OUString& rNewName );
+
+ void update();
+ void doUpdate();
+};
+
+
+class DocTemplates_EntryData_Impl
+{
+ OUString maTitle;
+ OUString maType;
+ OUString maTargetURL;
+ OUString maHierarchyURL;
+
+ bool mbInHierarchy : 1;
+ bool mbInUse : 1;
+ bool mbUpdateType : 1;
+ bool mbUpdateLink : 1;
+
+public:
+ explicit DocTemplates_EntryData_Impl( const OUString& rTitle );
+
+ void setInUse() { mbInUse = true; }
+ void setHierarchy( bool bInHierarchy ) { mbInHierarchy = bInHierarchy; }
+ void setUpdateLink( bool bUpdateLink ) { mbUpdateLink = bUpdateLink; }
+ void setUpdateType( bool bUpdateType ) { mbUpdateType = bUpdateType; }
+
+ bool getInUse() const { return mbInUse; }
+ bool getInHierarchy() const { return mbInHierarchy; }
+ bool getUpdateLink() const { return mbUpdateLink; }
+ bool getUpdateType() const { return mbUpdateType; }
+
+ const OUString& getHierarchyURL() const { return maHierarchyURL; }
+ const OUString& getTargetURL() const { return maTargetURL; }
+ const OUString& getTitle() const { return maTitle; }
+ const OUString& getType() const { return maType; }
+
+ void setHierarchyURL( const OUString& rURL ) { maHierarchyURL = rURL; }
+ void setTargetURL( const OUString& rURL ) { maTargetURL = rURL; }
+ void setType( const OUString& rType ) { maType = rType; }
+};
+
+
+class GroupData_Impl
+{
+ std::vector< std::unique_ptr<DocTemplates_EntryData_Impl> > maEntries;
+ OUString maTitle;
+ OUString maHierarchyURL;
+ OUString maTargetURL;
+ bool mbInUse : 1;
+ bool mbInHierarchy : 1;
+
+public:
+ explicit GroupData_Impl( const OUString& rTitle );
+
+ void setInUse() { mbInUse = true; }
+ void setHierarchy( bool bInHierarchy ) { mbInHierarchy = bInHierarchy; }
+ void setHierarchyURL( const OUString& rURL ) { maHierarchyURL = rURL; }
+ void setTargetURL( const OUString& rURL ) { maTargetURL = rURL; }
+
+ bool getInUse() const { return mbInUse; }
+ bool getInHierarchy() const { return mbInHierarchy; }
+ const OUString& getHierarchyURL() const { return maHierarchyURL; }
+ const OUString& getTargetURL() const { return maTargetURL; }
+ const OUString& getTitle() const { return maTitle; }
+
+ DocTemplates_EntryData_Impl* addEntry( const OUString& rTitle,
+ const OUString& rTargetURL,
+ const OUString& rType,
+ const OUString& rHierURL );
+ size_t count() { return maEntries.size(); }
+ DocTemplates_EntryData_Impl* getEntry( size_t nPos ) { return maEntries[ nPos ].get(); }
+};
+
+
+// private SfxDocTplService_Impl
+
+void SfxDocTplService_Impl::init_Impl()
+{
+ uno::Reference< uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext();
+ uno::Reference < task::XInteractionHandler > xInteractionHandler(
+ task::InteractionHandler::createWithParent(xContext, nullptr), uno::UNO_QUERY_THROW );
+ maCmdEnv = new TplTaskEnvironment( xInteractionHandler );
+
+ ::osl::ClearableMutexGuard aGuard( maMutex );
+ bool bIsInitialized = false;
+ bool bNeedsUpdate = false;
+
+ if ( !mbLocaleSet )
+ getDefaultLocale();
+
+ // convert locale to string
+ // set maRootContent to the root of the templates hierarchy. Create the
+ // entry if necessary
+
+ maRootURL = TEMPLATE_ROOT_URL + "/" + LanguageTag::convertToBcp47(maLocale);
+
+ const OUString aTemplVersPropName( TEMPLATE_VERSION );
+ const OUString aTemplVers( TEMPLATE_VERSION_VALUE );
+ if ( Content::create( maRootURL, maCmdEnv, comphelper::getProcessComponentContext(), maRootContent ) )
+ {
+ uno::Any aValue;
+ OUString aPropValue;
+ if ( getProperty( maRootContent, aTemplVersPropName, aValue )
+ && ( aValue >>= aPropValue )
+ && aPropValue == aTemplVers )
+ {
+ bIsInitialized = true;
+ }
+ else
+ removeContent( maRootContent );
+ }
+
+ if ( !bIsInitialized )
+ {
+ if ( createFolder( maRootURL, true, false, maRootContent )
+ && setProperty( maRootContent, aTemplVersPropName, uno::Any( aTemplVers ) ) )
+ bIsInitialized = true;
+
+ bNeedsUpdate = true;
+ }
+
+ if ( bIsInitialized )
+ {
+ try {
+ m_xDocProps.set(document::DocumentProperties::create(
+ ::comphelper::getProcessComponentContext()));
+ } catch (uno::RuntimeException const&) {
+ TOOLS_WARN_EXCEPTION("sfx.doc", "SfxDocTplService_Impl::init_Impl: cannot create DocumentProperties service:");
+ }
+
+ mxType.set( mxContext->getServiceManager()->createInstanceWithContext(SERVICENAME_TYPEDETECTION, mxContext), UNO_QUERY );
+
+ getDirList();
+ readFolderList();
+
+ if ( bNeedsUpdate )
+ {
+ aGuard.clear();
+ SolarMutexClearableGuard aSolarGuard;
+
+ VclPtrInstance< WaitWindow_Impl > pWin;
+ aSolarGuard.clear();
+ {
+ osl::MutexGuard anotherGuard(maMutex);
+ update();
+ }
+ SolarMutexGuard aSecondSolarGuard;
+
+ pWin.disposeAndClear();
+ }
+ else if ( needsUpdate() )
+ // the UI should be shown only on the first update
+ update();
+ }
+ else
+ {
+ SAL_WARN( "sfx.doc", "init_Impl(): Could not create root" );
+ }
+
+ mbIsInitialized = bIsInitialized;
+}
+
+
+void SfxDocTplService_Impl::getDefaultLocale()
+{
+ if ( !mbLocaleSet )
+ {
+ ::osl::MutexGuard aGuard( maMutex );
+ if ( !mbLocaleSet )
+ {
+ maLocale = LanguageTag::convertToLocale( utl::ConfigManager::getUILocale(), false);
+ mbLocaleSet = true;
+ }
+ }
+}
+
+const char* TEMPLATE_SHORT_NAMES_ARY[] =
+{
+ "standard",
+ "styles",
+ "officorr",
+ "offimisc",
+ "personal",
+ "forms",
+ "finance",
+ "educate",
+ "layout",
+ "presnt",
+ "misc",
+ "labels"
+};
+
+void SfxDocTplService_Impl::readFolderList()
+{
+ SolarMutexGuard aGuard;
+
+ static_assert( SAL_N_ELEMENTS(TEMPLATE_SHORT_NAMES_ARY) == SAL_N_ELEMENTS(TEMPLATE_LONG_NAMES_ARY), "mismatch array lengths" );
+ const size_t nCount = std::min(SAL_N_ELEMENTS(TEMPLATE_SHORT_NAMES_ARY), SAL_N_ELEMENTS(TEMPLATE_LONG_NAMES_ARY));
+ for (size_t i = 0; i < nCount; ++i)
+ {
+ NamePair_Impl aPair;
+ aPair.maShortName = OUString::createFromAscii(TEMPLATE_SHORT_NAMES_ARY[i]);
+ aPair.maLongName = SfxResId(TEMPLATE_LONG_NAMES_ARY[i]);
+
+ maNames.push_back( aPair );
+ }
+}
+
+
+OUString SfxDocTplService_Impl::getLongName( const OUString& rShortName )
+{
+ OUString aRet;
+
+ for (auto const & rPair : maNames)
+ {
+ if ( rPair.maShortName == rShortName )
+ {
+ aRet = rPair.maLongName;
+ break;
+ }
+ }
+
+ if ( aRet.isEmpty() )
+ aRet = rShortName;
+
+ return aRet;
+}
+
+
+void SfxDocTplService_Impl::getDirList()
+{
+ Any aValue;
+
+ // Get the template dir list
+ // TODO/LATER: let use service, register listener
+ INetURLObject aURL;
+ OUString aDirs = SvtPathOptions().GetTemplatePath();
+ sal_Int32 nCount = comphelper::string::getTokenCount(aDirs, C_DELIM);
+
+ maTemplateDirs = Sequence< OUString >( nCount );
+
+ uno::Reference< util::XMacroExpander > xExpander = util::theMacroExpander::get(mxContext);
+ static const OUStringLiteral aPrefix(
+ u"vnd.sun.star.expand:" );
+
+ sal_Int32 nIdx{ 0 };
+ for (auto& rTemplateDir : asNonConstRange(maTemplateDirs))
+ {
+ aURL.SetSmartProtocol( INetProtocol::File );
+ aURL.SetURL( o3tl::getToken(aDirs, 0, C_DELIM, nIdx ) );
+ rTemplateDir = aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE );
+
+ if ( xExpander.is() )
+ {
+ const sal_Int32 nIndex{ rTemplateDir.indexOf( aPrefix ) };
+ if (nIndex<0)
+ continue;
+
+ rTemplateDir = rTemplateDir.replaceAt(nIndex, aPrefix.getLength(), u"");
+ rTemplateDir = xExpander->expandMacros( rTemplateDir );
+ }
+ }
+
+ aValue <<= maTemplateDirs;
+
+ css::uno::Reference< css::util::XPathSettings > xPathSettings =
+ css::util::thePathSettings::get(mxContext);
+
+ // load internal paths
+ Any aAny = xPathSettings->getPropertyValue( "Template_internal" );
+ aAny >>= maInternalTemplateDirs;
+
+ for (auto& rInternalTemplateDir : asNonConstRange(maInternalTemplateDirs))
+ {
+ //expand vnd.sun.star.expand: and remove "..." from them
+ //to normalize into the expected url patterns
+ maRelocator.makeRelocatableURL(rInternalTemplateDir);
+ maRelocator.makeAbsoluteURL(rInternalTemplateDir);
+ }
+
+ // Store the template dir list
+ setProperty( maRootContent, PROPERTY_DIRLIST, aValue );
+}
+
+
+bool SfxDocTplService_Impl::needsUpdate()
+{
+ bool bNeedsUpdate = true;
+ Any aValue;
+
+ // Get the template dir list
+ bool bHasProperty = getProperty( maRootContent, PROPERTY_NEEDSUPDATE, aValue );
+
+ if ( bHasProperty )
+ aValue >>= bNeedsUpdate;
+
+ // the old template component also checks this state, but it is initialized from this component
+ // so if this component was already updated the old component does not need such an update
+ ::svt::TemplateFolderCache aTempCache;
+ if ( !bNeedsUpdate )
+ bNeedsUpdate = aTempCache.needsUpdate();
+
+ if ( bNeedsUpdate )
+ aTempCache.storeState();
+
+ return bNeedsUpdate;
+}
+
+
+bool SfxDocTplService_Impl::setTitleForURL( const OUString& rURL, const OUString& aTitle )
+{
+ if (m_xDocProps.is())
+ {
+ try
+ {
+ m_xDocProps->loadFromMedium(rURL, Sequence<PropertyValue>());
+ m_xDocProps->setTitle(aTitle);
+
+ uno::Reference< embed::XStorage > xStorage = ::comphelper::OStorageHelper::GetStorageFromURL(
+ rURL, embed::ElementModes::READWRITE);
+
+ uno::Sequence<beans::PropertyValue> medium( comphelper::InitPropertySequence({
+ { "DocumentBaseURL", Any(rURL) },
+ { "URL", Any(rURL) }
+ }));
+
+ m_xDocProps->storeToStorage(xStorage, medium);
+ return true;
+ }
+ catch ( Exception& )
+ {
+ }
+ }
+ return false;
+}
+
+
+void SfxDocTplService_Impl::getTitleFromURL( const OUString& rURL, OUString& aTitle, OUString& aType, bool& bDocHasTitle )
+{
+ bDocHasTitle = false;
+
+ if (m_xDocProps.is())
+ {
+ try
+ {
+ m_xDocProps->loadFromMedium(rURL, Sequence<PropertyValue>());
+ aTitle = m_xDocProps->getTitle();
+ }
+ catch ( Exception& )
+ {
+ }
+ }
+
+ if ( aType.isEmpty() && mxType.is() )
+ {
+ const OUString aDocType {mxType->queryTypeByURL( rURL )};
+ if ( !aDocType.isEmpty() )
+ try
+ {
+ uno::Reference< container::XNameAccess > xTypeDetection( mxType, uno::UNO_QUERY_THROW );
+ SequenceAsHashMap aTypeProps( xTypeDetection->getByName( aDocType ) );
+ aType = aTypeProps.getUnpackedValueOrDefault(
+ "MediaType",
+ OUString() );
+ }
+ catch( uno::Exception& )
+ {}
+ }
+
+ if ( aTitle.isEmpty() )
+ {
+ INetURLObject aURL( rURL );
+ aURL.CutExtension();
+ aTitle = aURL.getName( INetURLObject::LAST_SEGMENT, true,
+ INetURLObject::DecodeMechanism::WithCharset );
+ }
+ else
+ bDocHasTitle = true;
+}
+
+
+bool SfxDocTplService_Impl::addEntry( Content& rParentFolder,
+ const OUString& rTitle,
+ const OUString& rTargetURL,
+ const OUString& rType )
+{
+ bool bAddedEntry = false;
+
+ INetURLObject aLinkObj( rParentFolder.getURL() );
+ aLinkObj.insertName( rTitle, false,
+ INetURLObject::LAST_SEGMENT,
+ INetURLObject::EncodeMechanism::All );
+ const OUString aLinkURL {aLinkObj.GetMainURL( INetURLObject::DecodeMechanism::NONE )};
+
+ Content aLink;
+
+ if ( ! Content::create( aLinkURL, maCmdEnv, comphelper::getProcessComponentContext(), aLink ) )
+ {
+ Sequence< Any > aValues{ Any(rTitle), Any(false), Any(rTargetURL) };
+
+ try
+ {
+ rParentFolder.insertNewContent( TYPE_LINK, { TITLE, IS_FOLDER, TARGET_URL }, aValues, aLink );
+ setProperty( aLink, PROPERTY_TYPE, Any( rType ) );
+ bAddedEntry = true;
+ }
+ catch( Exception& )
+ {}
+ }
+ return bAddedEntry;
+}
+
+
+bool SfxDocTplService_Impl::createFolder( const OUString& rNewFolderURL,
+ bool bCreateParent,
+ bool bFsysFolder,
+ Content &rNewFolder )
+{
+ Content aParent;
+ bool bCreatedFolder = false;
+ INetURLObject aParentURL( rNewFolderURL );
+ const OUString aFolderName {aParentURL.getName( INetURLObject::LAST_SEGMENT, true,
+ INetURLObject::DecodeMechanism::WithCharset )};
+
+ // compute the parent folder url from the new folder url
+ // and remove the final slash, because Content::create doesn't
+ // like it
+ aParentURL.removeSegment();
+ if ( aParentURL.getSegmentCount() >= 1 )
+ aParentURL.removeFinalSlash();
+
+ // if the parent exists, we can continue with the creation of the
+ // new folder, we have to create the parent otherwise ( as long as
+ // bCreateParent is set to true )
+ if ( Content::create( aParentURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ), maCmdEnv, comphelper::getProcessComponentContext(), aParent ) )
+ {
+ try
+ {
+ Sequence< Any > aValues{ Any(aFolderName), Any(true) };
+ OUString aType;
+
+ if ( bFsysFolder )
+ aType = TYPE_FSYS_FOLDER;
+ else
+ aType = TYPE_FOLDER;
+
+ aParent.insertNewContent( aType, { TITLE, IS_FOLDER }, aValues, rNewFolder );
+ bCreatedFolder = true;
+ }
+ catch( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "sfx.doc", "createFolder(): Could not create new folder" );
+ }
+ }
+ else if ( bCreateParent )
+ {
+ // if the parent doesn't exists and bCreateParent is set to true,
+ // we try to create the parent and if this was successful, we
+ // try to create the new folder again ( but this time, we set
+ // bCreateParent to false to avoid endless recursions )
+ if ( ( aParentURL.getSegmentCount() >= 1 ) &&
+ createFolder( aParentURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ), bCreateParent, bFsysFolder, aParent ) )
+ {
+ bCreatedFolder = createFolder( rNewFolderURL, false, bFsysFolder, rNewFolder );
+ }
+ }
+
+ return bCreatedFolder;
+}
+
+
+bool SfxDocTplService_Impl::CreateNewUniqueFolderWithPrefix( std::u16string_view aPath,
+ const OUString& aPrefix,
+ OUString& aNewFolderName,
+ OUString& aNewFolderURL,
+ Content& aNewFolder )
+{
+ bool bCreated = false;
+ INetURLObject aDirPath( aPath );
+
+ Content aParent;
+ uno::Reference< XCommandEnvironment > aQuietEnv;
+ if ( Content::create( aDirPath.GetMainURL( INetURLObject::DecodeMechanism::NONE ), aQuietEnv, comphelper::getProcessComponentContext(), aParent ) )
+ {
+ for ( sal_Int32 nInd = 0; nInd < 32000; nInd++ )
+ {
+ OUString aTryName = aPrefix;
+ if ( nInd )
+ aTryName += OUString::number( nInd );
+
+ try
+ {
+ Sequence< Any > aValues{ Any(aTryName), Any(true) };
+ bCreated = aParent.insertNewContent( TYPE_FSYS_FOLDER, { TITLE, IS_FOLDER }, aValues, aNewFolder );
+ }
+ catch( ucb::NameClashException& )
+ {
+ // if there is already an element, retry
+ }
+ catch( Exception& )
+ {
+ INetURLObject aObjPath( aDirPath );
+ aObjPath.insertName( aTryName, false,
+ INetURLObject::LAST_SEGMENT,
+ INetURLObject::EncodeMechanism::All );
+ // if there is already an element, retry
+ // if there was another error, do not try any more
+ if ( !::utl::UCBContentHelper::Exists( aObjPath.GetMainURL( INetURLObject::DecodeMechanism::NONE ) ) )
+ break;
+ }
+
+ if ( bCreated )
+ {
+ aNewFolderName = aTryName;
+ aNewFolderURL = aNewFolder.get()->getIdentifier()->getContentIdentifier();
+ break;
+ }
+ }
+ }
+
+ return bCreated;
+}
+
+
+OUString SfxDocTplService_Impl::CreateNewUniqueFileWithPrefix( std::u16string_view aPath,
+ const OUString& aPrefix,
+ const OUString& aExt )
+{
+ OUString aNewFileURL;
+ INetURLObject aDirPath( aPath );
+
+ Content aParent;
+
+ uno::Reference< XCommandEnvironment > aQuietEnv;
+ if ( Content::create( aDirPath.GetMainURL( INetURLObject::DecodeMechanism::NONE ), aQuietEnv, comphelper::getProcessComponentContext(), aParent ) )
+ {
+ for ( sal_Int32 nInd = 0; nInd < 32000; nInd++ )
+ {
+ Content aNewFile;
+ bool bCreated = false;
+ OUString aTryName = aPrefix;
+ if ( nInd )
+ aTryName += OUString::number( nInd );
+ if ( aExt.toChar() != '.' )
+ aTryName += ".";
+ aTryName += aExt;
+
+ try
+ {
+ Sequence< Any > aValues{ Any(aTryName), Any(true) };
+ bCreated = aParent.insertNewContent( TYPE_FSYS_FILE, { TITLE, IS_DOCUMENT }, aValues, aNewFile );
+ }
+ catch( ucb::NameClashException& )
+ {
+ // if there is already an element, retry
+ }
+ catch( Exception& )
+ {
+ INetURLObject aObjPath( aPath );
+ aObjPath.insertName( aTryName, false,
+ INetURLObject::LAST_SEGMENT,
+ INetURLObject::EncodeMechanism::All );
+ // if there is already an element, retry
+ // if there was another error, do not try any more
+ if ( !::utl::UCBContentHelper::Exists( aObjPath.GetMainURL( INetURLObject::DecodeMechanism::NONE ) ) )
+ break;
+ }
+
+ if ( bCreated )
+ {
+ aNewFileURL = aNewFile.get()->getIdentifier()->getContentIdentifier();
+ break;
+ }
+ }
+ }
+
+ return aNewFileURL;
+}
+
+
+bool SfxDocTplService_Impl::removeContent( Content& rContent )
+{
+ bool bRemoved = false;
+ try
+ {
+ Any aArg( true );
+
+ rContent.executeCommand( COMMAND_DELETE, aArg );
+ bRemoved = true;
+ }
+ catch ( RuntimeException& ) {}
+ catch ( Exception& ) {}
+
+ return bRemoved;
+}
+
+
+bool SfxDocTplService_Impl::removeContent( const OUString& rContentURL )
+{
+ Content aContent;
+
+ if ( Content::create( rContentURL, maCmdEnv, comphelper::getProcessComponentContext(), aContent ) )
+ return removeContent( aContent );
+ return false;
+}
+
+
+bool SfxDocTplService_Impl::setProperty( Content& rContent,
+ const OUString& rPropName,
+ const Any& rPropValue )
+{
+ bool bPropertySet = false;
+
+ // Store the property
+ try
+ {
+ Any aPropValue( rPropValue );
+ uno::Reference< XPropertySetInfo > aPropInfo = rContent.getProperties();
+
+ // check, whether or not the property exists, create it, when not
+ if ( !aPropInfo.is() || !aPropInfo->hasPropertyByName( rPropName ) )
+ {
+ uno::Reference< XPropertyContainer > xProperties( rContent.get(), UNO_QUERY );
+ if ( xProperties.is() )
+ {
+ try
+ {
+ xProperties->addProperty( rPropName, PropertyAttribute::MAYBEVOID, rPropValue );
+ }
+ catch( PropertyExistException& ) {}
+ catch( IllegalTypeException& ) {
+ TOOLS_WARN_EXCEPTION( "sfx.doc", "" );
+ }
+ catch( IllegalArgumentException& ) {
+ TOOLS_WARN_EXCEPTION( "sfx.doc", "" );
+ }
+ }
+ }
+
+ // To ensure a reloctable office installation, the path to the
+ // office installation directory must never be stored directly.
+ if ( SfxURLRelocator_Impl::propertyCanContainOfficeDir( rPropName ) )
+ {
+ OUString aValue;
+ if ( rPropValue >>= aValue )
+ {
+ maRelocator.makeRelocatableURL( aValue );
+ aPropValue <<= aValue;
+ }
+ else
+ {
+ Sequence< OUString > aValues;
+ if ( rPropValue >>= aValues )
+ {
+ for ( auto& rValue : asNonConstRange(aValues) )
+ {
+ maRelocator.makeRelocatableURL( rValue );
+ }
+ aPropValue <<= aValues;
+ }
+ else
+ {
+ OSL_FAIL( "Unsupported property value type" );
+ }
+ }
+ }
+
+ // now set the property
+
+ rContent.setPropertyValue( rPropName, aPropValue );
+ bPropertySet = true;
+ }
+ catch ( RuntimeException& ) {}
+ catch ( Exception& ) {}
+
+ return bPropertySet;
+}
+
+
+bool SfxDocTplService_Impl::getProperty(Content& rContent, const OUString& rPropName, Any& rPropValue)
+{
+ bool bGotProperty = false;
+
+ // Get the property
+ try
+ {
+ uno::Reference< XPropertySetInfo > aPropInfo = rContent.getProperties();
+
+ // check, whether or not the property exists
+ if ( !aPropInfo.is() || !aPropInfo->hasPropertyByName( rPropName ) )
+ {
+ return false;
+ }
+
+ // now get the property
+
+ rPropValue = rContent.getPropertyValue( rPropName );
+
+ // To ensure a reloctable office installation, the path to the
+ // office installation directory must never be stored directly.
+ if ( SfxURLRelocator_Impl::propertyCanContainOfficeDir( rPropName ) )
+ {
+ OUString aValue;
+ if ( rPropValue >>= aValue )
+ {
+ maRelocator.makeAbsoluteURL( aValue );
+ rPropValue <<= aValue;
+ }
+ else
+ {
+ Sequence< OUString > aValues;
+ if ( rPropValue >>= aValues )
+ {
+ for ( auto& rValue : asNonConstRange(aValues) )
+ {
+ maRelocator.makeAbsoluteURL( rValue );
+ }
+ rPropValue <<= aValues;
+ }
+ else
+ {
+ OSL_FAIL( "Unsupported property value type" );
+ }
+ }
+ }
+
+ bGotProperty = true;
+ }
+ catch ( RuntimeException& ) {}
+ catch ( Exception& ) {}
+
+ return bGotProperty;
+}
+
+SfxDocTplService_Impl::SfxDocTplService_Impl( const uno::Reference< XComponentContext > & xContext )
+ : mxContext(xContext), mbIsInitialized(false), mbLocaleSet(false), maRelocator(xContext)
+{
+}
+
+
+SfxDocTplService_Impl::~SfxDocTplService_Impl()
+{
+ ::osl::MutexGuard aGuard( maMutex );
+ maNames.clear();
+}
+
+
+lang::Locale SfxDocTplService_Impl::getLocale()
+{
+ ::osl::MutexGuard aGuard( maMutex );
+
+ if ( !mbLocaleSet )
+ getDefaultLocale();
+
+ return maLocale;
+}
+
+
+void SfxDocTplService_Impl::setLocale( const lang::Locale &rLocale )
+{
+ ::osl::MutexGuard aGuard( maMutex );
+
+ if ( mbLocaleSet && (
+ ( maLocale.Language != rLocale.Language ) ||
+ ( maLocale.Country != rLocale.Country ) ||
+ ( maLocale.Variant != rLocale.Variant ) ) )
+ mbIsInitialized = false;
+
+ maLocale = rLocale;
+ mbLocaleSet = true;
+}
+
+
+void SfxDocTplService_Impl::update()
+{
+ ::osl::MutexGuard aGuard( maMutex );
+
+ doUpdate();
+}
+
+
+void SfxDocTplService_Impl::doUpdate()
+{
+ ::osl::MutexGuard aGuard( maMutex );
+
+ const OUString aPropName( PROPERTY_NEEDSUPDATE );
+ Any aValue;
+
+ aValue <<= true;
+ setProperty( maRootContent, aPropName, aValue );
+
+ GroupList_Impl aGroupList;
+
+ // get the entries from the hierarchy
+ createFromContent( aGroupList, maRootContent, true, false );
+
+ // get the entries from the template directories
+ sal_Int32 nCountDir = maTemplateDirs.getLength();
+ const OUString* pDirs = maTemplateDirs.getConstArray();
+ Content aDirContent;
+
+ // the last directory in the list must be writable
+ bool bWriteableDirectory = true;
+
+ // the target folder might not exist, for this reason no interaction handler should be used
+ uno::Reference< XCommandEnvironment > aQuietEnv;
+
+ while ( nCountDir )
+ {
+ nCountDir--;
+ if ( Content::create( pDirs[ nCountDir ], aQuietEnv, comphelper::getProcessComponentContext(), aDirContent ) )
+ {
+ createFromContent( aGroupList, aDirContent, false, bWriteableDirectory );
+ }
+
+ bWriteableDirectory = false;
+ }
+
+ // now check the list
+ for(std::unique_ptr<GroupData_Impl>& pGroup : aGroupList)
+ {
+ if ( pGroup->getInUse() )
+ {
+ if ( pGroup->getInHierarchy() )
+ {
+ Content aGroup;
+ if ( Content::create( pGroup->getHierarchyURL(), maCmdEnv, comphelper::getProcessComponentContext(), aGroup ) )
+ setProperty( aGroup,
+ TARGET_DIR_URL,
+ Any( pGroup->getTargetURL() ) );
+
+ size_t nCount = pGroup->count();
+ for ( size_t i=0; i<nCount; i++ )
+ {
+ DocTemplates_EntryData_Impl *pData = pGroup->getEntry( i );
+ if ( ! pData->getInUse() )
+ {
+ if ( pData->getInHierarchy() )
+ removeFromHierarchy( pData ); // delete entry in hierarchy
+ else
+ addToHierarchy( pGroup.get(), pData ); // add entry to hierarchy
+ }
+ else if ( pData->getUpdateType() ||
+ pData->getUpdateLink() )
+ {
+ updateData( pData );
+ }
+ }
+ }
+ else
+ {
+ addGroupToHierarchy( pGroup.get() ); // add group to hierarchy
+ }
+ }
+ else
+ removeFromHierarchy( pGroup.get() ); // delete group from hierarchy
+ }
+ aGroupList.clear();
+
+ aValue <<= false;
+ setProperty( maRootContent, aPropName, aValue );
+}
+
+
+std::vector< beans::StringPair > SfxDocTplService_Impl::ReadUINamesForTemplateDir_Impl( std::u16string_view aUserPath )
+{
+ INetURLObject aLocObj( aUserPath );
+ aLocObj.insertName( u"groupuinames.xml", false,
+ INetURLObject::LAST_SEGMENT,
+ INetURLObject::EncodeMechanism::All );
+ Content aLocContent;
+
+ // TODO/LATER: Use hashmap in future
+ std::vector< beans::StringPair > aUINames;
+ if ( Content::create( aLocObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ), uno::Reference < ucb::XCommandEnvironment >(), comphelper::getProcessComponentContext(), aLocContent ) )
+ {
+ try
+ {
+ uno::Reference< io::XInputStream > xLocStream = aLocContent.openStream();
+ if ( xLocStream.is() )
+ aUINames = DocTemplLocaleHelper::ReadGroupLocalizationSequence( xLocStream, mxContext );
+ }
+ catch( uno::Exception& )
+ {}
+ }
+
+ return aUINames;
+}
+
+
+bool SfxDocTplService_Impl::UpdateUINamesForTemplateDir_Impl( std::u16string_view aUserPath,
+ const OUString& aGroupName,
+ const OUString& aNewFolderName )
+{
+ std::vector< beans::StringPair > aUINames = ReadUINamesForTemplateDir_Impl( aUserPath );
+ sal_Int32 nLen = aUINames.size();
+
+ // it is possible that the name is used already, but it should be checked before
+ for ( sal_Int32 nInd = 0; nInd < nLen; nInd++ )
+ if ( aUINames[nInd].First == aNewFolderName )
+ return false;
+
+ aUINames.resize( ++nLen );
+ aUINames[nLen-1].First = aNewFolderName;
+ aUINames[nLen-1].Second = aGroupName;
+
+ return WriteUINamesForTemplateDir_Impl( aUserPath, aUINames );
+}
+
+
+bool SfxDocTplService_Impl::ReplaceUINamesForTemplateDir_Impl( std::u16string_view aUserPath,
+ const OUString& aDefaultFsysGroupName,
+ std::u16string_view aOldGroupName,
+ const OUString& aNewGroupName )
+{
+ std::vector< beans::StringPair > aUINames = ReadUINamesForTemplateDir_Impl( aUserPath );
+ sal_Int32 nLen = aUINames.size();
+
+ bool bChanged = false;
+ for ( sal_Int32 nInd = 0; nInd < nLen; nInd++ )
+ if ( aUINames[nInd].Second == aOldGroupName )
+ {
+ aUINames[nInd].Second = aNewGroupName;
+ bChanged = true;
+ }
+
+ if ( !bChanged )
+ {
+ aUINames.resize( ++nLen );
+ aUINames[nLen-1].First = aDefaultFsysGroupName;
+ aUINames[nLen-1].Second = aNewGroupName;
+ }
+ return WriteUINamesForTemplateDir_Impl( aUserPath, aUINames );
+}
+
+
+void SfxDocTplService_Impl::RemoveUINamesForTemplateDir_Impl( std::u16string_view aUserPath,
+ std::u16string_view aGroupName )
+{
+ std::vector< beans::StringPair > aUINames = ReadUINamesForTemplateDir_Impl( aUserPath );
+ sal_Int32 nLen = aUINames.size();
+ std::vector< beans::StringPair > aNewUINames( nLen );
+ sal_Int32 nNewLen = 0;
+
+ bool bChanged = false;
+ for ( sal_Int32 nInd = 0; nInd < nLen; nInd++ )
+ if ( aUINames[nInd].Second == aGroupName )
+ bChanged = true;
+ else
+ {
+ nNewLen++;
+ aNewUINames[nNewLen-1].First = aUINames[nInd].First;
+ aNewUINames[nNewLen-1].Second = aUINames[nInd].Second;
+ }
+
+ aNewUINames.resize( nNewLen );
+
+ if (bChanged)
+ WriteUINamesForTemplateDir_Impl( aUserPath, aNewUINames );
+}
+
+
+bool SfxDocTplService_Impl::WriteUINamesForTemplateDir_Impl( std::u16string_view aUserPath,
+ const std::vector< beans::StringPair >& aUINames )
+{
+ bool bResult = false;
+ try {
+ uno::Reference< io::XTempFile > xTempFile(
+ io::TempFile::create(mxContext),
+ uno::UNO_SET_THROW );
+
+ uno::Reference< io::XOutputStream > xOutStream = xTempFile->getOutputStream();
+ if ( !xOutStream.is() )
+ throw uno::RuntimeException();
+
+ DocTemplLocaleHelper::WriteGroupLocalizationSequence( xOutStream, aUINames, mxContext);
+ try {
+ // the SAX writer might close the stream
+ xOutStream->closeOutput();
+ } catch( uno::Exception& )
+ {}
+
+ Content aTargetContent( OUString(aUserPath), maCmdEnv, comphelper::getProcessComponentContext() );
+ Content aSourceContent( xTempFile->getUri(), maCmdEnv, comphelper::getProcessComponentContext() );
+ aTargetContent.transferContent( aSourceContent,
+ InsertOperation::Copy,
+ "groupuinames.xml",
+ ucb::NameClash::OVERWRITE,
+ "text/xml" );
+
+ bResult = true;
+ }
+ catch ( uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("sfx.doc", "");
+ }
+
+ return bResult;
+}
+
+
+OUString SfxDocTplService_Impl::CreateNewGroupFsys( const OUString& rGroupName, Content& aGroup )
+{
+ OUString aResultURL;
+
+ if ( maTemplateDirs.hasElements() )
+ {
+ OUString aTargetPath = maTemplateDirs[ maTemplateDirs.getLength() - 1 ];
+
+ // create a new folder with the given name
+ Content aNewFolder;
+ OUString aNewFolderName;
+
+ // the Fsys name instead of GroupName should be used, the groupuinames must be added also
+ if ( !CreateNewUniqueFolderWithPrefix( aTargetPath,
+ rGroupName,
+ aNewFolderName,
+ aResultURL,
+ aNewFolder )
+ && !CreateNewUniqueFolderWithPrefix( aTargetPath,
+ "UserGroup",
+ aNewFolderName,
+ aResultURL,
+ aNewFolder ) )
+
+ return OUString();
+
+ if ( !UpdateUINamesForTemplateDir_Impl( aTargetPath, rGroupName, aNewFolderName ) )
+ {
+ // we could not create the groupuinames for the folder, so we delete the group in
+ // the folder and return
+ removeContent( aNewFolder );
+ return OUString();
+ }
+
+ // Now set the target url for this group and we are done
+ Any aValue( aResultURL );
+
+ if ( ! setProperty( aGroup, TARGET_DIR_URL, aValue ) )
+ {
+ removeContent( aNewFolder );
+ return OUString();
+ }
+ }
+
+ return aResultURL;
+}
+
+
+bool SfxDocTplService_Impl::addGroup( const OUString& rGroupName )
+{
+ ::osl::MutexGuard aGuard( maMutex );
+
+ // Check, whether or not there is a group with this name
+ Content aNewGroup;
+ OUString aNewGroupURL;
+ INetURLObject aNewGroupObj( maRootURL );
+
+ aNewGroupObj.insertName( rGroupName, false,
+ INetURLObject::LAST_SEGMENT,
+ INetURLObject::EncodeMechanism::All );
+
+ aNewGroupURL = aNewGroupObj.GetMainURL( INetURLObject::DecodeMechanism::NONE );
+
+ if ( Content::create( aNewGroupURL, maCmdEnv, comphelper::getProcessComponentContext(), aNewGroup ) ||
+ ! createFolder( aNewGroupURL, false, false, aNewGroup ) )
+ {
+ // if there already was a group with this name or the new group
+ // could not be created, we return here
+ return false;
+ }
+
+ // Get the user template path entry ( new group will always
+ // be added in the user template path )
+ sal_Int32 nIndex;
+ OUString aUserPath;
+
+ nIndex = maTemplateDirs.getLength();
+ if ( nIndex )
+ nIndex--;
+ else
+ return false; // We don't know where to add the group
+
+ aUserPath = maTemplateDirs[ nIndex ];
+
+ // create a new folder with the given name
+ Content aNewFolder;
+ OUString aNewFolderName;
+ OUString aNewFolderURL;
+
+ // the Fsys name instead of GroupName should be used, the groupuinames must be added also
+ if ( !CreateNewUniqueFolderWithPrefix( aUserPath,
+ rGroupName,
+ aNewFolderName,
+ aNewFolderURL,
+ aNewFolder )
+ && !CreateNewUniqueFolderWithPrefix( aUserPath,
+ "UserGroup",
+ aNewFolderName,
+ aNewFolderURL,
+ aNewFolder ) )
+ {
+ // we could not create the folder, so we delete the group in the
+ // hierarchy and return
+ removeContent( aNewGroup );
+ return false;
+ }
+
+ if ( !UpdateUINamesForTemplateDir_Impl( aUserPath, rGroupName, aNewFolderName ) )
+ {
+ // we could not create the groupuinames for the folder, so we delete the group in the
+ // hierarchy, the folder and return
+ removeContent( aNewGroup );
+ removeContent( aNewFolder );
+ return false;
+ }
+
+ // Now set the target url for this group and we are done
+ Any aValue( aNewFolderURL );
+
+ if ( ! setProperty( aNewGroup, TARGET_DIR_URL, aValue ) )
+ {
+ removeContent( aNewGroup );
+ removeContent( aNewFolder );
+ return false;
+ }
+
+ return true;
+}
+
+
+bool SfxDocTplService_Impl::removeGroup( std::u16string_view rGroupName )
+{
+ // remove all the elements that have the prefix aTargetURL
+ // if the group does not have other elements remove it
+
+ ::osl::MutexGuard aGuard( maMutex );
+
+ bool bResult = false;
+
+ // create the group url
+ INetURLObject aGroupObj( maRootURL );
+ aGroupObj.insertName( rGroupName, false,
+ INetURLObject::LAST_SEGMENT,
+ INetURLObject::EncodeMechanism::All );
+
+ // Get the target url
+ Content aGroup;
+ const OUString aGroupURL = aGroupObj.GetMainURL( INetURLObject::DecodeMechanism::NONE );
+
+ if ( Content::create( aGroupURL, maCmdEnv, comphelper::getProcessComponentContext(), aGroup ) )
+ {
+ const OUString aPropName( TARGET_DIR_URL );
+ Any aValue;
+
+ OUString aGroupTargetURL;
+ if ( getProperty( aGroup, aPropName, aValue ) )
+ aValue >>= aGroupTargetURL;
+
+ if ( aGroupTargetURL.isEmpty() )
+ return false; // nothing is allowed to be removed
+
+ if ( !maTemplateDirs.hasElements() )
+ return false;
+
+ // check that the fs location is in writable folder and this is not a "My templates" folder
+ INetURLObject aGroupParentFolder( aGroupTargetURL );
+ if (!aGroupParentFolder.removeSegment())
+ return false;
+
+ OUString aGeneralTempPath = findParentTemplateDir(
+ aGroupParentFolder.GetMainURL(INetURLObject::DecodeMechanism::NONE));
+
+ if (aGeneralTempPath.isEmpty())
+ return false;
+
+ // now get the content of the Group
+ uno::Reference< XResultSet > xResultSet;
+ Sequence< OUString > aProps { TARGET_URL };
+
+ try
+ {
+ xResultSet = aGroup.createCursor( aProps, INCLUDE_DOCUMENTS_ONLY );
+
+ if ( xResultSet.is() )
+ {
+ bool bHasNonRemovable = false;
+ bool bHasShared = false;
+
+ uno::Reference< XContentAccess > xContentAccess( xResultSet, UNO_QUERY_THROW );
+ uno::Reference< XRow > xRow( xResultSet, UNO_QUERY_THROW );
+
+ while ( xResultSet->next() )
+ {
+ OUString aTemplTargetURL( xRow->getString( 1 ) );
+ OUString aHierURL = xContentAccess->queryContentIdentifierString();
+
+ if ( ::utl::UCBContentHelper::IsSubPath( aGroupTargetURL, aTemplTargetURL ) )
+ {
+ // this is a user template, and it can be removed
+ if ( removeContent( aTemplTargetURL ) )
+ removeContent( aHierURL );
+ else
+ bHasNonRemovable = true;
+ }
+ else
+ bHasShared = true;
+ }
+
+ if ( !bHasNonRemovable && !bHasShared )
+ {
+ if ( removeContent( aGroupTargetURL )
+ || !::utl::UCBContentHelper::Exists( aGroupTargetURL ) )
+ {
+ removeContent( aGroupURL );
+ RemoveUINamesForTemplateDir_Impl( aGeneralTempPath, rGroupName );
+ bResult = true; // the operation is successful only if the whole group is removed
+ }
+ }
+ else if ( !bHasNonRemovable )
+ {
+ if ( removeContent( aGroupTargetURL )
+ || !::utl::UCBContentHelper::Exists( aGroupTargetURL ) )
+ {
+ RemoveUINamesForTemplateDir_Impl( aGeneralTempPath, rGroupName );
+ setProperty( aGroup, aPropName, uno::Any( OUString() ) );
+ }
+ }
+ }
+ }
+ catch ( Exception& ) {}
+ }
+
+ return bResult;
+}
+
+
+bool SfxDocTplService_Impl::renameGroup( std::u16string_view rOldName,
+ const OUString& rNewName )
+{
+ ::osl::MutexGuard aGuard( maMutex );
+
+ // create the group url
+ Content aGroup;
+ INetURLObject aGroupObj( maRootURL );
+ aGroupObj.insertName( rNewName, false,
+ INetURLObject::LAST_SEGMENT,
+ INetURLObject::EncodeMechanism::All );
+ OUString aGroupURL = aGroupObj.GetMainURL( INetURLObject::DecodeMechanism::NONE );
+
+ // Check, if there is a group with the new name, return false
+ // if there is one.
+ if ( Content::create( aGroupURL, maCmdEnv, comphelper::getProcessComponentContext(), aGroup ) )
+ return false;
+
+ aGroupObj.removeSegment();
+ aGroupObj.insertName( rOldName, false,
+ INetURLObject::LAST_SEGMENT,
+ INetURLObject::EncodeMechanism::All );
+ aGroupURL = aGroupObj.GetMainURL( INetURLObject::DecodeMechanism::NONE );
+
+ // When there is no group with the old name, we can't rename it
+ if ( ! Content::create( aGroupURL, maCmdEnv, comphelper::getProcessComponentContext(), aGroup ) )
+ return false;
+
+ OUString aGroupTargetURL;
+ // there is no need to check whether target dir url is in target path, since if the target path is changed
+ // the target dir url should be already generated new
+ Any aValue;
+ if ( getProperty( aGroup, TARGET_DIR_URL, aValue ) )
+ aValue >>= aGroupTargetURL;
+
+ if ( aGroupTargetURL.isEmpty() )
+ return false;
+
+ if ( !maTemplateDirs.hasElements() )
+ return false;
+
+ // check that the fs location is in writable folder and this is not a "My templates" folder
+ INetURLObject aGroupParentFolder( aGroupTargetURL );
+ if (!aGroupParentFolder.removeSegment() ||
+ isInternalTemplateDir(aGroupParentFolder.GetMainURL(INetURLObject::DecodeMechanism::NONE)))
+ {
+ return false;
+ }
+
+ // check that the group can be renamed ( all the contents must be in target location )
+ bool bCanBeRenamed = false;
+ try
+ {
+ uno::Reference< XResultSet > xResultSet;
+ Sequence< OUString > aProps { TARGET_URL };
+ xResultSet = aGroup.createCursor( aProps, INCLUDE_DOCUMENTS_ONLY );
+
+ if ( xResultSet.is() )
+ {
+ uno::Reference< XContentAccess > xContentAccess( xResultSet, UNO_QUERY_THROW );
+ uno::Reference< XRow > xRow( xResultSet, UNO_QUERY_THROW );
+
+ while ( xResultSet->next() )
+ {
+ if ( !::utl::UCBContentHelper::IsSubPath( aGroupTargetURL, xRow->getString( 1 ) ) )
+ throw uno::Exception("not sub path", nullptr);
+ }
+
+ bCanBeRenamed = true;
+ }
+ }
+ catch ( Exception& ) {}
+
+ if ( bCanBeRenamed )
+ {
+ INetURLObject aGroupTargetObj( aGroupTargetURL );
+ const OUString aFsysName = aGroupTargetObj.getName( INetURLObject::LAST_SEGMENT, true, INetURLObject::DecodeMechanism::WithCharset );
+
+ if ( aGroupTargetObj.removeSegment()
+ && ReplaceUINamesForTemplateDir_Impl( aGroupTargetObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ),
+ aFsysName,
+ rOldName,
+ rNewName ) )
+ {
+ // rename the group in the hierarchy
+ Any aTitleValue;
+ aTitleValue <<= rNewName;
+
+ return setProperty( aGroup, TITLE, aTitleValue );
+ }
+ }
+
+ return false;
+}
+
+
+bool SfxDocTplService_Impl::storeTemplate( const OUString& rGroupName,
+ const OUString& rTemplateName,
+ const uno::Reference< frame::XStorable >& rStorable )
+{
+ ::osl::MutexGuard aGuard( maMutex );
+
+ // Check, whether or not there is a group with this name
+ // Return false, if there is no group with the given name
+ Content aGroup, aTemplateToRemove;
+ INetURLObject aGroupObj( maRootURL );
+ bool bRemoveOldTemplateContent = false;
+
+ aGroupObj.insertName( rGroupName, false,
+ INetURLObject::LAST_SEGMENT,
+ INetURLObject::EncodeMechanism::All );
+ const OUString aGroupURL {aGroupObj.GetMainURL( INetURLObject::DecodeMechanism::NONE )};
+
+ if ( ! Content::create( aGroupURL, maCmdEnv, comphelper::getProcessComponentContext(), aGroup ) )
+ return false;
+
+ OUString aGroupTargetURL;
+ Any aValue;
+ if ( getProperty( aGroup, TARGET_DIR_URL, aValue ) )
+ aValue >>= aGroupTargetURL;
+
+
+ // Check, if there's a template with the given name in this group
+ // the target template should be overwritten if it is imported by user
+ // in case the template is installed by office installation of by an add-in
+ // it can not be replaced
+ aGroupObj.insertName( rTemplateName, false,
+ INetURLObject::LAST_SEGMENT,
+ INetURLObject::EncodeMechanism::All );
+ const OUString aTemplateURL {aGroupObj.GetMainURL( INetURLObject::DecodeMechanism::NONE )};
+
+ OUString aTemplateToRemoveTargetURL;
+
+ if ( Content::create( aTemplateURL, maCmdEnv, comphelper::getProcessComponentContext(), aTemplateToRemove ) )
+ {
+ bRemoveOldTemplateContent = true;
+ if ( getProperty( aTemplateToRemove, TARGET_URL, aValue ) )
+ aValue >>= aTemplateToRemoveTargetURL;
+
+ if ( aGroupTargetURL.isEmpty() || !maTemplateDirs.hasElements()
+ || (!aTemplateToRemoveTargetURL.isEmpty() && isInternalTemplateDir(aTemplateToRemoveTargetURL)) )
+ return false; // it is not allowed to remove the template
+ }
+
+ try
+ {
+ uno::Reference< uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext();
+
+ // get document service name
+ uno::Reference< frame::XModuleManager2 > xModuleManager( frame::ModuleManager::create(xContext) );
+ const OUString sDocServiceName {xModuleManager->identify( uno::Reference< uno::XInterface >( rStorable, uno::UNO_QUERY ) )};
+ if ( sDocServiceName.isEmpty() )
+ throw uno::RuntimeException();
+
+ // get the actual filter name
+ uno::Reference< lang::XMultiServiceFactory > xConfigProvider =
+ configuration::theDefaultProvider::get( xContext );
+
+ uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
+ {
+ {"nodepath", uno::Any(OUString( "/org.openoffice.Setup/Office/Factories/" ))}
+ }));
+ uno::Reference< container::XNameAccess > xSOFConfig(
+ xConfigProvider->createInstanceWithArguments(
+ "com.sun.star.configuration.ConfigurationAccess",
+ aArgs ),
+ uno::UNO_QUERY_THROW );
+
+ uno::Reference< container::XNameAccess > xApplConfig;
+ xSOFConfig->getByName( sDocServiceName ) >>= xApplConfig;
+ if ( !xApplConfig.is() )
+ throw uno::RuntimeException();
+
+ OUString aFilterName;
+ xApplConfig->getByName("ooSetupFactoryActualTemplateFilter") >>= aFilterName;
+ if ( aFilterName.isEmpty() )
+ throw uno::RuntimeException();
+
+ // find the related type name
+ uno::Reference< container::XNameAccess > xFilterFactory(
+ mxContext->getServiceManager()->createInstanceWithContext("com.sun.star.document.FilterFactory", mxContext),
+ uno::UNO_QUERY_THROW );
+
+ uno::Sequence< beans::PropertyValue > aFilterData;
+ xFilterFactory->getByName( aFilterName ) >>= aFilterData;
+ OUString aTypeName;
+ for ( const auto& rProp : std::as_const(aFilterData) )
+ if ( rProp.Name == "Type" )
+ rProp.Value >>= aTypeName;
+
+ if ( aTypeName.isEmpty() )
+ throw uno::RuntimeException();
+
+ // find the mediatype and extension
+ uno::Reference< container::XNameAccess > xTypeDetection =
+ mxType.is() ?
+ uno::Reference< container::XNameAccess >( mxType, uno::UNO_QUERY_THROW ) :
+ uno::Reference< container::XNameAccess >(
+ mxContext->getServiceManager()->createInstanceWithContext("com.sun.star.document.TypeDetection", mxContext),
+ uno::UNO_QUERY_THROW );
+
+ SequenceAsHashMap aTypeProps( xTypeDetection->getByName( aTypeName ) );
+ uno::Sequence< OUString > aAllExt =
+ aTypeProps.getUnpackedValueOrDefault("Extensions", Sequence< OUString >() );
+ if ( !aAllExt.hasElements() )
+ throw uno::RuntimeException();
+
+ const OUString aMediaType {aTypeProps.getUnpackedValueOrDefault("MediaType", OUString() )};
+ const OUString aExt {aAllExt[0]};
+
+ if ( aMediaType.isEmpty() || aExt.isEmpty() )
+ throw uno::RuntimeException();
+
+ // construct destination url
+ if ( aGroupTargetURL.isEmpty() )
+ {
+ aGroupTargetURL = CreateNewGroupFsys( rGroupName, aGroup );
+
+ if ( aGroupTargetURL.isEmpty() )
+ throw uno::RuntimeException();
+ }
+
+ OUString aNewTemplateTargetURL = CreateNewUniqueFileWithPrefix( aGroupTargetURL, rTemplateName, aExt );
+ if ( aNewTemplateTargetURL.isEmpty() )
+ {
+ aNewTemplateTargetURL = CreateNewUniqueFileWithPrefix( aGroupTargetURL, "UserTemplate", aExt );
+
+ if ( aNewTemplateTargetURL.isEmpty() )
+ throw uno::RuntimeException();
+ }
+
+ // store template
+ uno::Sequence< PropertyValue > aStoreArgs{
+ comphelper::makePropertyValue("FilterName", aFilterName),
+ comphelper::makePropertyValue("DocumentTitle", rTemplateName)
+ };
+
+ if( !::utl::UCBContentHelper::EqualURLs( aNewTemplateTargetURL, rStorable->getLocation() ))
+ rStorable->storeToURL( aNewTemplateTargetURL, aStoreArgs );
+ else
+ rStorable->store();
+
+ // the storing was successful, now the old template with the same name can be removed if it existed
+ if ( !aTemplateToRemoveTargetURL.isEmpty() )
+ {
+ removeContent( aTemplateToRemoveTargetURL );
+
+ /*
+ * pb: #i79496#
+ * if the old template was the standard template
+ * it is necessary to change the standard template with the new file name
+ */
+ const OUString sStdTmplFile = SfxObjectFactory::GetStandardTemplate( sDocServiceName );
+ if ( INetURLObject( sStdTmplFile ) == INetURLObject( aTemplateToRemoveTargetURL ) )
+ {
+ SfxObjectFactory::SetStandardTemplate( sDocServiceName, aNewTemplateTargetURL );
+ }
+ }
+
+ if ( bRemoveOldTemplateContent )
+ removeContent( aTemplateToRemove );
+
+ // add the template to hierarchy
+ return addEntry( aGroup, rTemplateName, aNewTemplateTargetURL, aMediaType );
+ }
+ catch( Exception& )
+ {
+ // the template was not stored
+ return false;
+ }
+}
+
+
+bool SfxDocTplService_Impl::addTemplate( const OUString& rGroupName,
+ const OUString& rTemplateName,
+ const OUString& rSourceURL )
+{
+ ::osl::MutexGuard aGuard( maMutex );
+
+ // Check, whether or not there is a group with this name
+ // Return false, if there is no group with the given name
+ Content aGroup, aTemplate, aTargetGroup;
+ INetURLObject aGroupObj( maRootURL );
+
+ aGroupObj.insertName( rGroupName, false,
+ INetURLObject::LAST_SEGMENT,
+ INetURLObject::EncodeMechanism::All );
+ const OUString aGroupURL = aGroupObj.GetMainURL( INetURLObject::DecodeMechanism::NONE );
+
+ if ( ! Content::create( aGroupURL, maCmdEnv, comphelper::getProcessComponentContext(), aGroup ) )
+ return false;
+
+ // Check, if there's a template with the given name in this group
+ // Return false, if there already is a template
+ aGroupObj.insertName( rTemplateName, false,
+ INetURLObject::LAST_SEGMENT,
+ INetURLObject::EncodeMechanism::All );
+ const OUString aTemplateURL {aGroupObj.GetMainURL( INetURLObject::DecodeMechanism::NONE )};
+
+ if ( Content::create( aTemplateURL, maCmdEnv, comphelper::getProcessComponentContext(), aTemplate ) )
+ return false;
+
+ // get the target url of the group
+ OUString aTargetURL;
+ Any aValue;
+
+ if ( getProperty( aGroup, TARGET_DIR_URL, aValue ) )
+ aValue >>= aTargetURL;
+
+ if ( aTargetURL.isEmpty() )
+ {
+ aTargetURL = CreateNewGroupFsys( rGroupName, aGroup );
+
+ if ( aTargetURL.isEmpty() )
+ return false;
+ }
+
+ // Get the content type
+ OUString aTitle, aType;
+
+ bool bDocHasTitle = false;
+ getTitleFromURL( rSourceURL, aTitle, aType, bDocHasTitle );
+
+ INetURLObject aSourceObj( rSourceURL );
+ if ( rTemplateName == aTitle )
+ {
+ // addTemplate will sometimes be called just to add an entry in the
+ // hierarchy; the target URL and the source URL will be the same in
+ // this scenario
+ // TODO/LATER: get rid of this old hack
+
+ INetURLObject aTargetObj( aTargetURL );
+
+ aTargetObj.insertName( rTemplateName, false,
+ INetURLObject::LAST_SEGMENT,
+ INetURLObject::EncodeMechanism::All );
+ aTargetObj.setExtension( aSourceObj.getExtension() );
+
+ const OUString aTargetURL2 = aTargetObj.GetMainURL( INetURLObject::DecodeMechanism::NONE );
+
+ if ( aTargetURL2 == rSourceURL )
+ return addEntry( aGroup, rTemplateName, aTargetURL2, aType );
+ }
+
+ // copy the template into the new group (targeturl)
+
+ INetURLObject aTmpURL( aSourceObj );
+ aTmpURL.CutExtension();
+ const OUString aPattern {aTmpURL.getName( INetURLObject::LAST_SEGMENT, true, INetURLObject::DecodeMechanism::WithCharset )};
+
+ const OUString aNewTemplateTargetURL {CreateNewUniqueFileWithPrefix( aTargetURL, aPattern, aSourceObj.getExtension() )};
+ INetURLObject aNewTemplateTargetObj( aNewTemplateTargetURL );
+ const OUString aNewTemplateTargetName {aNewTemplateTargetObj.getName( INetURLObject::LAST_SEGMENT, true, INetURLObject::DecodeMechanism::WithCharset )};
+ if ( aNewTemplateTargetURL.isEmpty() || aNewTemplateTargetName.isEmpty() )
+ return false;
+
+ // get access to source file
+ Content aSourceContent;
+ uno::Reference < ucb::XCommandEnvironment > xEnv;
+ INetURLObject aSourceURL( rSourceURL );
+ if( ! Content::create( aSourceURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ), xEnv, comphelper::getProcessComponentContext(), aSourceContent ) )
+ return false;
+
+ if( ! Content::create( aTargetURL, xEnv, comphelper::getProcessComponentContext(), aTargetGroup ) )
+ return false;
+
+ // transfer source file
+ try
+ {
+ aTargetGroup.transferContent( aSourceContent,
+ InsertOperation::Copy,
+ aNewTemplateTargetName,
+ NameClash::OVERWRITE,
+ aType );
+
+ // allow to edit the added template
+ Content aResultContent;
+ if ( Content::create( aNewTemplateTargetURL, xEnv, comphelper::getProcessComponentContext(), aResultContent ) )
+ {
+ static const OUStringLiteral aPropertyName( u"IsReadOnly" );
+ uno::Any aProperty;
+ bool bReadOnly = false;
+ if ( getProperty( aResultContent, aPropertyName, aProperty ) && ( aProperty >>= bReadOnly ) && bReadOnly )
+ setProperty( aResultContent, aPropertyName, uno::Any( false ) );
+ }
+ }
+ catch ( ContentCreationException& )
+ { return false; }
+ catch ( Exception& )
+ { return false; }
+
+
+ // either the document has title and it is the same as requested, or we have to set it
+ bool bCorrectTitle = ( bDocHasTitle && aTitle == rTemplateName );
+ if ( !bCorrectTitle )
+ {
+ if ( !bDocHasTitle )
+ {
+ INetURLObject aNewTmpObj( aNewTemplateTargetObj );
+ aNewTmpObj.CutExtension();
+ bCorrectTitle = ( aNewTmpObj.getName( INetURLObject::LAST_SEGMENT, true, INetURLObject::DecodeMechanism::WithCharset ) == rTemplateName );
+ }
+
+ if ( !bCorrectTitle )
+ bCorrectTitle = setTitleForURL( aNewTemplateTargetURL, rTemplateName );
+ }
+
+ if ( bCorrectTitle )
+ {
+ // create a new entry in the hierarchy
+ return addEntry( aGroup, rTemplateName, aNewTemplateTargetURL, aType );
+ }
+
+ // TODO/LATER: The user could be notified here that the renaming has failed
+ // create a new entry in the hierarchy
+ addEntry( aGroup, aTitle, aNewTemplateTargetURL, aType );
+ return false;
+}
+
+bool SfxDocTplService_Impl::isInternalTemplateDir(const OUString& rURL) const
+{
+ return std::any_of(maInternalTemplateDirs.begin(), maInternalTemplateDirs.end(),
+ [&rURL](const OUString& rDir) { return ::utl::UCBContentHelper::IsSubPath(rDir, rURL); });
+}
+
+OUString SfxDocTplService_Impl::findParentTemplateDir(const OUString& rURL) const
+{
+ const OUString* pDirs = std::find_if(maTemplateDirs.begin(), maTemplateDirs.end(),
+ [&rURL](const OUString& rDir) { return ::utl::UCBContentHelper::IsSubPath(rDir, rURL); });
+ if (pDirs != maTemplateDirs.end())
+ return *pDirs;
+ return OUString();
+}
+
+bool SfxDocTplService_Impl::removeTemplate( std::u16string_view rGroupName,
+ std::u16string_view rTemplateName )
+{
+ ::osl::MutexGuard aGuard( maMutex );
+
+ // Check, whether or not there is a group with this name
+ // Return false, if there is no group with the given name
+ Content aGroup, aTemplate;
+ INetURLObject aGroupObj( maRootURL );
+
+ aGroupObj.insertName( rGroupName, false,
+ INetURLObject::LAST_SEGMENT,
+ INetURLObject::EncodeMechanism::All );
+ const OUString aGroupURL {aGroupObj.GetMainURL( INetURLObject::DecodeMechanism::NONE )};
+
+ if ( ! Content::create( aGroupURL, maCmdEnv, comphelper::getProcessComponentContext(), aGroup ) )
+ return false;
+
+ // Check, if there's a template with the given name in this group
+ // Return false, if there is no template
+ aGroupObj.insertName( rTemplateName, false,
+ INetURLObject::LAST_SEGMENT,
+ INetURLObject::EncodeMechanism::All );
+ const OUString aTemplateURL {aGroupObj.GetMainURL( INetURLObject::DecodeMechanism::NONE )};
+
+ if ( !Content::create( aTemplateURL, maCmdEnv, comphelper::getProcessComponentContext(), aTemplate ) )
+ return false;
+
+ // get the target URL from the template
+ OUString aTargetURL;
+ Any aValue;
+
+ if ( getProperty( aTemplate, TARGET_URL, aValue ) )
+ aValue >>= aTargetURL;
+
+ // delete the target template
+ if ( !aTargetURL.isEmpty() )
+ {
+ if (isInternalTemplateDir(aTargetURL))
+ return false;
+
+ removeContent( aTargetURL );
+ }
+
+ // delete the template entry
+ return removeContent( aTemplate );
+}
+
+
+bool SfxDocTplService_Impl::renameTemplate( std::u16string_view rGroupName,
+ std::u16string_view rOldName,
+ const OUString& rNewName )
+{
+ ::osl::MutexGuard aGuard( maMutex );
+
+ // Check, whether or not there is a group with this name
+ // Return false, if there is no group with the given name
+ Content aGroup, aTemplate;
+ INetURLObject aGroupObj( maRootURL );
+
+ aGroupObj.insertName( rGroupName, false,
+ INetURLObject::LAST_SEGMENT,
+ INetURLObject::EncodeMechanism::All );
+ const OUString aGroupURL {aGroupObj.GetMainURL( INetURLObject::DecodeMechanism::NONE )};
+
+ if ( ! Content::create( aGroupURL, maCmdEnv, comphelper::getProcessComponentContext(), aGroup ) )
+ return false;
+
+ // Check, if there's a template with the new name in this group
+ // Return false, if there is one
+ aGroupObj.insertName( rNewName, false,
+ INetURLObject::LAST_SEGMENT,
+ INetURLObject::EncodeMechanism::All );
+ OUString aTemplateURL {aGroupObj.GetMainURL( INetURLObject::DecodeMechanism::NONE )};
+
+ if ( Content::create( aTemplateURL, maCmdEnv, comphelper::getProcessComponentContext(), aTemplate ) )
+ return false;
+
+ // Check, if there's a template with the old name in this group
+ // Return false, if there is no template
+ aGroupObj.removeSegment();
+ aGroupObj.insertName( rOldName, false,
+ INetURLObject::LAST_SEGMENT,
+ INetURLObject::EncodeMechanism::All );
+ aTemplateURL = aGroupObj.GetMainURL( INetURLObject::DecodeMechanism::NONE );
+
+ if ( !Content::create( aTemplateURL, maCmdEnv, comphelper::getProcessComponentContext(), aTemplate ) )
+ return false;
+
+ OUString aTemplateTargetURL;
+ Any aTargetValue;
+
+ if ( getProperty( aTemplate, TARGET_URL, aTargetValue ) )
+ aTargetValue >>= aTemplateTargetURL;
+
+ if ( !setTitleForURL( aTemplateTargetURL, rNewName ) )
+ return false;
+
+ // rename the template entry in the cache
+ Any aTitleValue;
+ aTitleValue <<= rNewName;
+
+ return setProperty( aTemplate, TITLE, aTitleValue );
+}
+
+
+class SfxDocTplService: public ::cppu::WeakImplHelper< css::lang::XLocalizable, css::frame::XDocumentTemplates, css::lang::XServiceInfo >
+{
+ std::unique_ptr<SfxDocTplService_Impl> pImp;
+
+public:
+ explicit SfxDocTplService( const css::uno::Reference < uno::XComponentContext >& xContext );
+
+ virtual OUString SAL_CALL getImplementationName() override
+ {
+ return "com.sun.star.comp.sfx2.DocumentTemplates";
+ }
+
+ virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override
+ {
+ return cppu::supportsService(this, ServiceName);
+ }
+
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override
+ {
+ css::uno::Sequence< OUString > aSeq { "com.sun.star.frame.DocumentTemplates" };
+ return aSeq;
+ }
+
+
+ // --- XLocalizable ---
+ void SAL_CALL setLocale( const css::lang::Locale & eLocale ) override;
+ css::lang::Locale SAL_CALL getLocale() override;
+
+ // --- XDocumentTemplates ---
+ css::uno::Reference< css::ucb::XContent > SAL_CALL getContent() override;
+ sal_Bool SAL_CALL storeTemplate( const OUString& GroupName,
+ const OUString& TemplateName,
+ const css::uno::Reference< css::frame::XStorable >& Storable ) override;
+ sal_Bool SAL_CALL addTemplate( const OUString& GroupName,
+ const OUString& TemplateName,
+ const OUString& SourceURL ) override;
+ sal_Bool SAL_CALL removeTemplate( const OUString& GroupName,
+ const OUString& TemplateName ) override;
+ sal_Bool SAL_CALL renameTemplate( const OUString& GroupName,
+ const OUString& OldTemplateName,
+ const OUString& NewTemplateName ) override;
+ sal_Bool SAL_CALL addGroup( const OUString& GroupName ) override;
+ sal_Bool SAL_CALL removeGroup( const OUString& GroupName ) override;
+ sal_Bool SAL_CALL renameGroup( const OUString& OldGroupName,
+ const OUString& NewGroupName ) override;
+ void SAL_CALL update() override;
+};
+
+
+SfxDocTplService::SfxDocTplService( const uno::Reference< XComponentContext >& xContext )
+{
+ pImp.reset( new SfxDocTplService_Impl(xContext) );
+}
+
+
+
+//--- XLocalizable ---
+
+
+lang::Locale SAL_CALL SfxDocTplService::getLocale()
+{
+ return pImp->getLocale();
+}
+
+
+void SAL_CALL SfxDocTplService::setLocale( const lang::Locale & rLocale )
+{
+ pImp->setLocale( rLocale );
+}
+
+
+//--- XDocumentTemplates ---
+
+uno::Reference< ucb::XContent > SAL_CALL SfxDocTplService::getContent()
+{
+ if ( pImp->init() )
+ return pImp->getContent().get();
+ return nullptr;
+}
+
+
+sal_Bool SAL_CALL SfxDocTplService::storeTemplate( const OUString& GroupName,
+ const OUString& TemplateName,
+ const uno::Reference< frame::XStorable >& Storable )
+{
+ return pImp->init() && pImp->storeTemplate( GroupName, TemplateName, Storable );
+}
+
+
+sal_Bool SAL_CALL SfxDocTplService::addTemplate( const OUString& rGroupName,
+ const OUString& rTemplateName,
+ const OUString& rSourceURL )
+{
+ return pImp->init() && pImp->addTemplate( rGroupName, rTemplateName, rSourceURL );
+}
+
+
+sal_Bool SAL_CALL SfxDocTplService::removeTemplate( const OUString& rGroupName,
+ const OUString& rTemplateName )
+{
+ return pImp->init() && pImp->removeTemplate( rGroupName, rTemplateName );
+}
+
+
+sal_Bool SAL_CALL SfxDocTplService::renameTemplate( const OUString& rGroupName,
+ const OUString& rOldName,
+ const OUString& rNewName )
+{
+ if ( rOldName == rNewName )
+ return true;
+
+ return pImp->init() && pImp->renameTemplate( rGroupName, rOldName, rNewName );
+}
+
+
+sal_Bool SAL_CALL SfxDocTplService::addGroup( const OUString& rGroupName )
+{
+ return pImp->init() && pImp->addGroup( rGroupName );
+}
+
+
+sal_Bool SAL_CALL SfxDocTplService::removeGroup( const OUString& rGroupName )
+{
+ return pImp->init() && pImp->removeGroup( rGroupName );
+}
+
+
+sal_Bool SAL_CALL SfxDocTplService::renameGroup( const OUString& rOldName,
+ const OUString& rNewName )
+{
+ if ( rOldName == rNewName )
+ return true;
+
+ return pImp->init() && pImp->renameGroup( rOldName, rNewName );
+}
+
+
+void SAL_CALL SfxDocTplService::update()
+{
+ if ( pImp->init() )
+ pImp->update();
+}
+
+WaitWindow_Impl::WaitWindow_Impl() : WorkWindow(nullptr, WB_BORDER | WB_3DLOOK)
+{
+ tools::Rectangle aRect(0, 0, 300, 30000);
+ maText = SfxResId(RID_CNT_STR_WAITING);
+ maRect = GetTextRect(aRect, maText, gnTextStyle);
+ aRect = maRect;
+ aRect.AdjustRight(2 * X_OFFSET );
+ aRect.AdjustBottom(2 * Y_OFFSET );
+ maRect.SetPos(Point(X_OFFSET, Y_OFFSET));
+ SetOutputSizePixel(aRect.GetSize());
+
+ Show();
+ PaintImmediately();
+ GetOutDev()->Flush();
+}
+
+
+WaitWindow_Impl::~WaitWindow_Impl()
+{
+ disposeOnce();
+}
+
+void WaitWindow_Impl::dispose()
+{
+ Hide();
+ WorkWindow::dispose();
+}
+
+
+void WaitWindow_Impl::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& /*rRect*/)
+{
+ rRenderContext.DrawText(maRect, maText, gnTextStyle);
+}
+
+void SfxDocTplService_Impl::addHierGroup( GroupList_Impl& rList,
+ const OUString& rTitle,
+ const OUString& rOwnURL )
+{
+ // now get the content of the Group
+ Content aContent;
+ uno::Reference<XResultSet> xResultSet;
+
+ try
+ {
+ aContent = Content(rOwnURL, maCmdEnv, comphelper::getProcessComponentContext());
+ xResultSet = aContent.createCursor( { TITLE, TARGET_URL, PROPERTY_TYPE }, INCLUDE_DOCUMENTS_ONLY );
+ }
+ catch (ContentCreationException&)
+ {
+ TOOLS_WARN_EXCEPTION( "sfx.doc", "" );
+ }
+ catch (Exception&) {}
+
+ if ( !xResultSet.is() )
+ return;
+
+ GroupData_Impl *pGroup = new GroupData_Impl( rTitle );
+ pGroup->setHierarchy( true );
+ pGroup->setHierarchyURL( rOwnURL );
+ rList.push_back( std::unique_ptr<GroupData_Impl>(pGroup) );
+
+ uno::Reference< XContentAccess > xContentAccess( xResultSet, UNO_QUERY );
+ uno::Reference< XRow > xRow( xResultSet, UNO_QUERY );
+
+ try
+ {
+ while ( xResultSet->next() )
+ {
+ bool bUpdateType = false;
+ DocTemplates_EntryData_Impl *pData;
+
+ const OUString aTitle( xRow->getString( 1 ) );
+ const OUString aTargetDir( xRow->getString( 2 ) );
+ OUString aType( xRow->getString( 3 ) );
+ const OUString aHierURL {xContentAccess->queryContentIdentifierString()};
+
+ if ( aType.isEmpty() )
+ {
+ OUString aTmpTitle;
+
+ bool bDocHasTitle = false;
+ getTitleFromURL( aTargetDir, aTmpTitle, aType, bDocHasTitle );
+
+ if ( !aType.isEmpty() )
+ bUpdateType = true;
+ }
+
+ pData = pGroup->addEntry( aTitle, aTargetDir, aType, aHierURL );
+ pData->setUpdateType( bUpdateType );
+ }
+ }
+ catch ( Exception& ) {}
+}
+
+
+void SfxDocTplService_Impl::addFsysGroup( GroupList_Impl& rList,
+ const OUString& rTitle,
+ const OUString& rUITitle,
+ const OUString& rOwnURL,
+ bool bWriteableGroup )
+{
+ OUString aTitle;
+
+ if ( rUITitle.isEmpty() )
+ {
+ // reserved FS names that should not be used
+ if ( rTitle == "wizard" )
+ return;
+ else if ( rTitle == "internal" )
+ return;
+
+ aTitle = getLongName( rTitle );
+ }
+ else
+ aTitle = rUITitle;
+
+ if ( aTitle.isEmpty() )
+ return;
+
+ GroupData_Impl* pGroup = nullptr;
+ for (const std::unique_ptr<GroupData_Impl>& i : rList)
+ {
+ if ( i->getTitle() == aTitle )
+ {
+ pGroup = i.get();
+ break;
+ }
+ }
+
+ if ( !pGroup )
+ {
+ pGroup = new GroupData_Impl( aTitle );
+ rList.push_back( std::unique_ptr<GroupData_Impl>(pGroup) );
+ }
+
+ if ( bWriteableGroup )
+ pGroup->setTargetURL( rOwnURL );
+
+ pGroup->setInUse();
+
+ // now get the content of the Group
+ Content aContent;
+ uno::Reference< XResultSet > xResultSet;
+ Sequence< OUString > aProps { TITLE };
+
+ try
+ {
+ // this method is only used during checking of the available template-folders
+ // that should happen quietly
+ uno::Reference< XCommandEnvironment > aQuietEnv;
+ aContent = Content( rOwnURL, aQuietEnv, comphelper::getProcessComponentContext() );
+ xResultSet = aContent.createCursor( aProps, INCLUDE_DOCUMENTS_ONLY );
+ }
+ catch ( Exception& ) {}
+
+ if ( !xResultSet.is() )
+ return;
+
+ uno::Reference< XContentAccess > xContentAccess( xResultSet, UNO_QUERY );
+ uno::Reference< XRow > xRow( xResultSet, UNO_QUERY );
+
+ try
+ {
+ while ( xResultSet->next() )
+ {
+ OUString aChildTitle( xRow->getString( 1 ) );
+ const OUString aTargetURL {xContentAccess->queryContentIdentifierString()};
+ OUString aType;
+
+ if ( aChildTitle == "sfx.tlx" || aChildTitle == "groupuinames.xml" )
+ continue;
+
+ bool bDocHasTitle = false;
+ getTitleFromURL( aTargetURL, aChildTitle, aType, bDocHasTitle );
+
+ pGroup->addEntry( aChildTitle, aTargetURL, aType, OUString() );
+ }
+ }
+ catch ( Exception& ) {}
+}
+
+
+void SfxDocTplService_Impl::createFromContent( GroupList_Impl& rList,
+ Content &rContent,
+ bool bHierarchy,
+ bool bWriteableContent )
+{
+ const OUString aTargetURL {rContent.get()->getIdentifier()->getContentIdentifier()};
+
+ // when scanning the file system, we have to add the 'standard' group, too
+ if ( ! bHierarchy )
+ {
+ const OUString aUIStdTitle {getLongName( STANDARD_FOLDER )};
+ addFsysGroup( rList, OUString(), aUIStdTitle, aTargetURL, bWriteableContent );
+ }
+
+ // search for predefined UI names
+ INetURLObject aLayerObj( aTargetURL );
+
+ // TODO/LATER: Use hashmap in future
+ std::vector< beans::StringPair > aUINames;
+ if ( !bHierarchy )
+ aUINames = ReadUINamesForTemplateDir_Impl( aLayerObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
+
+ uno::Reference< XResultSet > xResultSet;
+ Sequence< OUString > aProps { TITLE };
+
+ try
+ {
+ xResultSet = rContent.createCursor( aProps, INCLUDE_FOLDERS_ONLY );
+ }
+ catch ( Exception& ) {}
+
+ if ( !xResultSet.is() )
+ return;
+
+ uno::Reference< XContentAccess > xContentAccess( xResultSet, UNO_QUERY );
+ uno::Reference< XRow > xRow( xResultSet, UNO_QUERY );
+
+ try
+ {
+ while ( xResultSet->next() )
+ {
+ // TODO/LATER: clarify the encoding of the Title
+ const OUString aTitle( xRow->getString( 1 ) );
+ const OUString aTargetSubfolderURL( xContentAccess->queryContentIdentifierString() );
+
+ if ( bHierarchy )
+ addHierGroup( rList, aTitle, aTargetSubfolderURL );
+ else
+ {
+ OUString aUITitle;
+ for (const beans::StringPair & rUIName : aUINames)
+ if ( rUIName.First == aTitle )
+ {
+ aUITitle = rUIName.Second;
+ break;
+ }
+
+ addFsysGroup( rList, aTitle, aUITitle, aTargetSubfolderURL, bWriteableContent );
+ }
+ }
+ }
+ catch ( Exception& ) {}
+}
+
+
+void SfxDocTplService_Impl::removeFromHierarchy( DocTemplates_EntryData_Impl const *pData )
+{
+ Content aTemplate;
+
+ if ( Content::create( pData->getHierarchyURL(), maCmdEnv, comphelper::getProcessComponentContext(), aTemplate ) )
+ {
+ removeContent( aTemplate );
+ }
+}
+
+
+void SfxDocTplService_Impl::addToHierarchy( GroupData_Impl const *pGroup,
+ DocTemplates_EntryData_Impl const *pData )
+{
+ Content aGroup, aTemplate;
+
+ if ( ! Content::create( pGroup->getHierarchyURL(), maCmdEnv, comphelper::getProcessComponentContext(), aGroup ) )
+ return;
+
+ // Check, if there's a template with the given name in this group
+ // Return if there is already a template
+ INetURLObject aGroupObj( pGroup->getHierarchyURL() );
+
+ aGroupObj.insertName( pData->getTitle(), false,
+ INetURLObject::LAST_SEGMENT,
+ INetURLObject::EncodeMechanism::All );
+
+ const OUString aTemplateURL {aGroupObj.GetMainURL( INetURLObject::DecodeMechanism::NONE )};
+
+ if ( Content::create( aTemplateURL, maCmdEnv, comphelper::getProcessComponentContext(), aTemplate ) )
+ return;
+
+ addEntry( aGroup, pData->getTitle(),
+ pData->getTargetURL(),
+ pData->getType() );
+}
+
+
+void SfxDocTplService_Impl::updateData( DocTemplates_EntryData_Impl const *pData )
+{
+ Content aTemplate;
+
+ if ( ! Content::create( pData->getHierarchyURL(), maCmdEnv, comphelper::getProcessComponentContext(), aTemplate ) )
+ return;
+
+ if ( pData->getUpdateType() )
+ {
+ setProperty( aTemplate, PROPERTY_TYPE, Any( pData->getType() ) );
+ }
+
+ if ( pData->getUpdateLink() )
+ {
+ setProperty( aTemplate, TARGET_URL, Any( pData->getTargetURL() ) );
+ }
+}
+
+
+void SfxDocTplService_Impl::addGroupToHierarchy( GroupData_Impl *pGroup )
+{
+ Content aGroup;
+
+ INetURLObject aNewGroupObj( maRootURL );
+ aNewGroupObj.insertName( pGroup->getTitle(), false,
+ INetURLObject::LAST_SEGMENT,
+ INetURLObject::EncodeMechanism::All );
+
+ const OUString aNewGroupURL {aNewGroupObj.GetMainURL( INetURLObject::DecodeMechanism::NONE )};
+
+ if ( createFolder( aNewGroupURL, false, false, aGroup ) )
+ {
+ setProperty( aGroup, TARGET_DIR_URL, Any( pGroup->getTargetURL() ) );
+ pGroup->setHierarchyURL( aNewGroupURL );
+
+ size_t nCount = pGroup->count();
+ for ( size_t i = 0; i < nCount; i++ )
+ {
+ DocTemplates_EntryData_Impl *pData = pGroup->getEntry( i );
+ addToHierarchy( pGroup, pData ); // add entry to hierarchy
+ }
+ }
+}
+
+
+void SfxDocTplService_Impl::removeFromHierarchy( GroupData_Impl const *pGroup )
+{
+ Content aGroup;
+
+ if ( Content::create( pGroup->getHierarchyURL(), maCmdEnv, comphelper::getProcessComponentContext(), aGroup ) )
+ {
+ removeContent( aGroup );
+ }
+}
+
+
+GroupData_Impl::GroupData_Impl( const OUString& rTitle )
+ : maTitle(rTitle), mbInUse(false), mbInHierarchy(false)
+{
+}
+
+
+DocTemplates_EntryData_Impl* GroupData_Impl::addEntry( const OUString& rTitle,
+ const OUString& rTargetURL,
+ const OUString& rType,
+ const OUString& rHierURL )
+{
+ DocTemplates_EntryData_Impl* pData = nullptr;
+ bool EntryFound = false;
+
+ for (auto const & p : maEntries)
+ {
+ pData = p.get();
+ if ( pData->getTitle() == rTitle )
+ {
+ EntryFound = true;
+ break;
+ }
+ }
+
+ if ( !EntryFound )
+ {
+ pData = new DocTemplates_EntryData_Impl( rTitle );
+ pData->setTargetURL( rTargetURL );
+ pData->setType( rType );
+ if ( !rHierURL.isEmpty() )
+ {
+ pData->setHierarchyURL( rHierURL );
+ pData->setHierarchy( true );
+ }
+ maEntries.emplace_back( pData );
+ }
+ else
+ {
+ if ( !rHierURL.isEmpty() )
+ {
+ pData->setHierarchyURL( rHierURL );
+ pData->setHierarchy( true );
+ }
+
+ if ( pData->getInHierarchy() )
+ pData->setInUse();
+
+ if ( rTargetURL != pData->getTargetURL() )
+ {
+ pData->setTargetURL( rTargetURL );
+ pData->setUpdateLink( true );
+ }
+ }
+
+ return pData;
+}
+
+
+DocTemplates_EntryData_Impl::DocTemplates_EntryData_Impl( const OUString& rTitle )
+ : maTitle(rTitle), mbInHierarchy(false), mbInUse(false), mbUpdateType(false), mbUpdateLink(false)
+{
+}
+
+}
+
+// static
+bool SfxURLRelocator_Impl::propertyCanContainOfficeDir(
+ std::u16string_view rPropName )
+{
+ // Note: TargetURL is handled by UCB itself (because it is a property
+ // with a predefined semantic). Additional Core properties introduced
+ // be a client app must be handled by the client app itself, because
+ // the UCB does not know the semantics of those properties.
+ return ( rPropName == TARGET_DIR_URL || rPropName == PROPERTY_DIRLIST );
+}
+
+
+SfxURLRelocator_Impl::SfxURLRelocator_Impl( const uno::Reference< XComponentContext > & xContext )
+: mxContext( xContext )
+{
+}
+
+
+SfxURLRelocator_Impl::~SfxURLRelocator_Impl()
+{
+}
+
+
+void SfxURLRelocator_Impl::initOfficeInstDirs()
+{
+ if ( !mxOfficeInstDirs.is() )
+ {
+ std::scoped_lock aGuard( maMutex );
+ if ( !mxOfficeInstDirs.is() )
+ {
+ OSL_ENSURE( mxContext.is(), "No service manager!" );
+
+ mxOfficeInstDirs = theOfficeInstallationDirectories::get(mxContext);
+ }
+ }
+}
+
+
+void SfxURLRelocator_Impl::implExpandURL( OUString& io_url )
+{
+ const INetURLObject aParser( io_url );
+ if ( aParser.GetProtocol() != INetProtocol::VndSunStarExpand )
+ return;
+
+ io_url = aParser.GetURLPath( INetURLObject::DecodeMechanism::WithCharset );
+ try
+ {
+ if ( !mxMacroExpander.is() )
+ {
+ mxMacroExpander.set( theMacroExpander::get(mxContext), UNO_SET_THROW );
+ }
+ io_url = mxMacroExpander->expandMacros( io_url );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("sfx.doc");
+ }
+}
+
+
+void SfxURLRelocator_Impl::makeRelocatableURL( OUString & rURL )
+{
+ if ( !rURL.isEmpty() )
+ {
+ initOfficeInstDirs();
+ implExpandURL( rURL );
+ rURL = mxOfficeInstDirs->makeRelocatableURL( rURL );
+ }
+}
+
+
+void SfxURLRelocator_Impl::makeAbsoluteURL( OUString & rURL )
+{
+ if ( !rURL.isEmpty() )
+ {
+ initOfficeInstDirs();
+ implExpandURL( rURL );
+ rURL = mxOfficeInstDirs->makeAbsoluteURL( rURL );
+ }
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_sfx2_DocumentTemplates_get_implementation(
+ css::uno::XComponentContext *context,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new SfxDocTplService(context));
+}
+
+OUString DocTemplLocaleHelper::GetStandardGroupString()
+{
+ return SfxResId(TEMPLATE_LONG_NAMES_ARY[0]);
+}
+
+std::vector<OUString> DocTemplLocaleHelper::GetBuiltInGroupNames()
+{
+ std::vector<OUString> aGroups;
+ for(auto const & aGroupName : TEMPLATE_LONG_NAMES_ARY)
+ aGroups.push_back(SfxResId(aGroupName));
+ return aGroups;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/doc/doctemplateslocal.cxx b/sfx2/source/doc/doctemplateslocal.cxx
new file mode 100644
index 000000000..5a55df72a
--- /dev/null
+++ b/sfx2/source/doc/doctemplateslocal.cxx
@@ -0,0 +1,213 @@
+/* -*- 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/StringPair.hpp>
+#include <com/sun/star/xml/sax/Parser.hpp>
+#include <com/sun/star/xml/sax/SAXException.hpp>
+#include <com/sun/star/xml/sax/Writer.hpp>
+#include <com/sun/star/xml/sax/XDocumentHandler.hpp>
+
+#include <comphelper/attributelist.hxx>
+#include <rtl/ref.hxx>
+
+#include "doctemplateslocal.hxx"
+
+using namespace ::com::sun::star;
+
+namespace
+{
+
+// Relations info related strings
+constexpr OUStringLiteral g_sGroupListElement(u"groupuinames:template-group-list");
+constexpr OUStringLiteral g_sGroupElement(u"groupuinames:template-group");
+constexpr OUStringLiteral g_sNameAttr(u"groupuinames:name");
+constexpr OUStringLiteral g_sUINameAttr(u"groupuinames:default-ui-name");
+
+}
+
+std::vector< beans::StringPair > DocTemplLocaleHelper::ReadGroupLocalizationSequence( const uno::Reference< io::XInputStream >& xInStream, const uno::Reference< uno::XComponentContext >& xContext )
+{
+ return ReadLocalizationSequence_Impl( xInStream, "groupuinames.xml", xContext );
+}
+
+
+void DocTemplLocaleHelper::WriteGroupLocalizationSequence( const uno::Reference< io::XOutputStream >& xOutStream, const std::vector< beans::StringPair >& aSequence, const uno::Reference< uno::XComponentContext >& xContext )
+{
+ if ( !xOutStream.is() )
+ throw uno::RuntimeException();
+
+ uno::Reference< xml::sax::XWriter > xWriterHandler(
+ xml::sax::Writer::create(xContext) );
+
+ xWriterHandler->setOutputStream( xOutStream );
+
+ static const OUStringLiteral aCDATAString( u"CDATA" );
+ static const OUStringLiteral aWhiteSpace( u" " );
+
+ // write the namespace
+ rtl::Reference<::comphelper::AttributeList> pRootAttrList = new ::comphelper::AttributeList;
+ pRootAttrList->AddAttribute(
+ "xmlns:groupuinames",
+ aCDATAString,
+ "http://openoffice.org/2006/groupuinames" );
+
+ xWriterHandler->startDocument();
+ xWriterHandler->startElement( g_sGroupListElement, pRootAttrList );
+
+ for (const auto & i : aSequence)
+ {
+ rtl::Reference<::comphelper::AttributeList> pAttrList = new ::comphelper::AttributeList;
+ pAttrList->AddAttribute( g_sNameAttr, aCDATAString, i.First );
+ pAttrList->AddAttribute( g_sUINameAttr, aCDATAString, i.Second );
+
+ xWriterHandler->startElement( g_sGroupElement, pAttrList );
+ xWriterHandler->ignorableWhitespace( aWhiteSpace );
+ xWriterHandler->endElement( g_sGroupElement );
+ }
+
+ xWriterHandler->ignorableWhitespace( aWhiteSpace );
+ xWriterHandler->endElement( g_sGroupListElement );
+ xWriterHandler->endDocument();
+}
+
+
+std::vector< beans::StringPair > DocTemplLocaleHelper::ReadLocalizationSequence_Impl( const uno::Reference< io::XInputStream >& xInStream, const OUString& aStringID, const uno::Reference< uno::XComponentContext >& xContext )
+{
+ if ( !xContext.is() || !xInStream.is() )
+ throw uno::RuntimeException();
+
+ uno::Reference< xml::sax::XParser > xParser = xml::sax::Parser::create( xContext );
+
+ rtl::Reference<DocTemplLocaleHelper> pHelper = new DocTemplLocaleHelper();
+ xml::sax::InputSource aParserInput;
+ aParserInput.aInputStream = xInStream;
+ aParserInput.sSystemId = aStringID;
+ xParser->setDocumentHandler( pHelper );
+ xParser->parseStream( aParserInput );
+ xParser->setDocumentHandler( uno::Reference < xml::sax::XDocumentHandler > () );
+
+ return pHelper->GetParsingResult();
+}
+
+
+DocTemplLocaleHelper::DocTemplLocaleHelper()
+{
+}
+
+
+DocTemplLocaleHelper::~DocTemplLocaleHelper()
+{
+}
+
+
+std::vector< beans::StringPair > const & DocTemplLocaleHelper::GetParsingResult() const
+{
+ if ( !m_aElementsSeq.empty() )
+ throw uno::RuntimeException("The parsing has still not finished!");
+
+ return m_aResultSeq;
+}
+
+
+void SAL_CALL DocTemplLocaleHelper::startDocument()
+{
+}
+
+
+void SAL_CALL DocTemplLocaleHelper::endDocument()
+{
+}
+
+
+void SAL_CALL DocTemplLocaleHelper::startElement( const OUString& aName, const uno::Reference< xml::sax::XAttributeList >& xAttribs )
+{
+ if ( aName == g_sGroupListElement )
+ {
+ if ( !m_aElementsSeq.empty() )
+ throw xml::sax::SAXException(); // TODO: this element must be the first level element
+
+ m_aElementsSeq.push_back( aName );
+
+ return; // nothing to do
+ }
+ else if ( aName == g_sGroupElement )
+ {
+ if ( m_aElementsSeq.size() != 1 )
+ throw xml::sax::SAXException(); // TODO: this element must be the second level element
+
+ m_aElementsSeq.push_back( aName );
+
+ const auto nNewEntryNum = m_aResultSeq.size();
+ m_aResultSeq.resize( nNewEntryNum+1 );
+
+ const OUString aNameValue = xAttribs->getValueByName( g_sNameAttr );
+ if ( aNameValue.isEmpty() )
+ throw xml::sax::SAXException(); // TODO: the ID value must present
+
+ const OUString aUINameValue = xAttribs->getValueByName( g_sUINameAttr );
+ if ( aUINameValue.isEmpty() )
+ throw xml::sax::SAXException(); // TODO: the ID value must present
+
+ m_aResultSeq[nNewEntryNum].First = aNameValue;
+ m_aResultSeq[nNewEntryNum].Second = aUINameValue;
+ }
+ else
+ {
+ // accept future extensions
+ if ( m_aElementsSeq.empty() )
+ throw xml::sax::SAXException(); // TODO: the extension element must not be the first level element
+
+ m_aElementsSeq.push_back( aName );
+ }
+}
+
+
+void SAL_CALL DocTemplLocaleHelper::endElement( const OUString& aName )
+{
+ if ( m_aElementsSeq.empty() )
+ throw xml::sax::SAXException(); // TODO: no other end elements expected!
+
+ if ( m_aElementsSeq.back() != aName )
+ throw xml::sax::SAXException(); // TODO: unexpected element ended
+
+ m_aElementsSeq.pop_back();
+}
+
+
+void SAL_CALL DocTemplLocaleHelper::characters( const OUString& /*aChars*/ )
+{
+}
+
+
+void SAL_CALL DocTemplLocaleHelper::ignorableWhitespace( const OUString& /*aWhitespaces*/ )
+{
+}
+
+
+void SAL_CALL DocTemplLocaleHelper::processingInstruction( const OUString& /*aTarget*/, const OUString& /*aData*/ )
+{
+}
+
+
+void SAL_CALL DocTemplLocaleHelper::setDocumentLocator( const uno::Reference< xml::sax::XLocator >& /*xLocator*/ )
+{
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/doc/doctemplateslocal.hxx b/sfx2/source/doc/doctemplateslocal.hxx
new file mode 100644
index 000000000..466c847db
--- /dev/null
+++ b/sfx2/source/doc/doctemplateslocal.hxx
@@ -0,0 +1,78 @@
+/* -*- 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_DOC_DOCTEMPLATESLOCAL_HXX
+#define INCLUDED_SFX2_SOURCE_DOC_DOCTEMPLATESLOCAL_HXX
+
+#include <com/sun/star/xml/sax/XDocumentHandler.hpp>
+#include <com/sun/star/beans/StringPair.hpp>
+#include <com/sun/star/io/XInputStream.hpp>
+#include <com/sun/star/io/XOutputStream.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <cppuhelper/implbase.hxx>
+#include <vector>
+
+
+class DocTemplLocaleHelper : public cppu::WeakImplHelper < css::xml::sax::XDocumentHandler >
+{
+ std::vector< css::beans::StringPair > m_aResultSeq;
+ std::vector< OUString > m_aElementsSeq; // stack of elements being parsed
+
+ DocTemplLocaleHelper();
+ std::vector< css::beans::StringPair > const & GetParsingResult() const;
+
+ /// @throws css::uno::Exception
+ static std::vector< css::beans::StringPair > ReadLocalizationSequence_Impl( const css::uno::Reference< css::io::XInputStream >& xInStream, const OUString& aStringID, const css::uno::Reference< css::uno::XComponentContext >& xContext );
+
+public:
+ virtual ~DocTemplLocaleHelper() override;
+
+ // returns sequence of pairs ( GroupName, GroupUIName )
+ /// @throws css::uno::Exception
+ static
+ std::vector< css::beans::StringPair >
+ ReadGroupLocalizationSequence(
+ const css::uno::Reference< css::io::XInputStream >& xInStream,
+ const css::uno::Reference< css::uno::XComponentContext >& xContext );
+
+ // writes sequence of elements ( GroupName, GroupUIName )
+ /// @throws css::uno::Exception
+ static
+ void WriteGroupLocalizationSequence(
+ const css::uno::Reference< css::io::XOutputStream >& xOutStream,
+ const std::vector< css::beans::StringPair >& aSequence,
+ const css::uno::Reference< css::uno::XComponentContext >& xContext );
+
+ static OUString GetStandardGroupString();
+ static std::vector<OUString> GetBuiltInGroupNames();
+
+ // XDocumentHandler
+ virtual void SAL_CALL startDocument() override;
+ virtual void SAL_CALL endDocument() override;
+ virtual void SAL_CALL startElement( const OUString& aName, const css::uno::Reference< css::xml::sax::XAttributeList >& xAttribs ) override;
+ virtual void SAL_CALL endElement( const OUString& aName ) override;
+ virtual void SAL_CALL characters( const OUString& aChars ) override;
+ virtual void SAL_CALL ignorableWhitespace( const OUString& aWhitespaces ) override;
+ virtual void SAL_CALL processingInstruction( const OUString& aTarget, const OUString& aData ) override;
+ virtual void SAL_CALL setDocumentLocator( const css::uno::Reference< css::xml::sax::XLocator >& xLocator ) override;
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/doc/docundomanager.cxx b/sfx2/source/doc/docundomanager.cxx
new file mode 100644
index 000000000..ab7438a7b
--- /dev/null
+++ b/sfx2/source/doc/docundomanager.cxx
@@ -0,0 +1,423 @@
+/* -*- 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 <docundomanager.hxx>
+#include <sfx2/sfxbasemodel.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/viewsh.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/sfxsids.hrc>
+#include <com/sun/star/lang/NoSupportException.hpp>
+#include <com/sun/star/lang/NotInitializedException.hpp>
+#include <svl/undo.hxx>
+#include <tools/diagnose_ex.h>
+#include <framework/undomanagerhelper.hxx>
+#include <framework/imutex.hxx>
+
+
+namespace sfx2
+{
+
+
+ using ::com::sun::star::uno::Reference;
+ using ::com::sun::star::uno::XInterface;
+ using ::com::sun::star::uno::RuntimeException;
+ using ::com::sun::star::uno::Sequence;
+ using ::com::sun::star::document::XUndoAction;
+ using ::com::sun::star::lang::NotInitializedException;
+ using ::com::sun::star::document::XUndoManagerListener;
+ using ::com::sun::star::document::XUndoManager;
+ using ::com::sun::star::lang::NoSupportException;
+ using ::com::sun::star::frame::XModel;
+
+ //= DocumentUndoManager_Impl
+
+ struct DocumentUndoManager_Impl : public ::framework::IUndoManagerImplementation
+ {
+ DocumentUndoManager& rAntiImpl;
+ SfxUndoManager* pUndoManager;
+ ::framework::UndoManagerHelper aUndoHelper;
+
+ explicit DocumentUndoManager_Impl( DocumentUndoManager& i_antiImpl )
+ :rAntiImpl( i_antiImpl )
+ ,pUndoManager( impl_retrieveUndoManager( i_antiImpl.getBaseModel() ) )
+ // do this *before* the construction of aUndoHelper (which actually means: put pUndoManager before
+ // aUndoHelper in the member list)!
+ ,aUndoHelper( *this )
+ {
+ }
+
+ virtual ~DocumentUndoManager_Impl()
+ {
+ };
+
+ // IUndoManagerImplementation
+ virtual SfxUndoManager& getImplUndoManager() override;
+ virtual Reference< XUndoManager > getThis() override;
+
+ void disposing()
+ {
+ aUndoHelper.disposing();
+ ENSURE_OR_RETURN_VOID( pUndoManager, "DocumentUndoManager_Impl::disposing: already disposed!" );
+ pUndoManager = nullptr;
+ }
+
+ void invalidateXDo_nolck();
+
+ private:
+ static SfxUndoManager* impl_retrieveUndoManager( SfxBaseModel& i_baseModel )
+ {
+ SfxUndoManager* pUndoManager( nullptr );
+ SfxObjectShell* pObjectShell = i_baseModel.GetObjectShell();
+ if ( pObjectShell != nullptr )
+ pUndoManager = pObjectShell->GetUndoManager();
+ if ( !pUndoManager )
+ throw NotInitializedException( OUString(), i_baseModel );
+ return pUndoManager;
+ }
+ };
+
+
+ SfxUndoManager& DocumentUndoManager_Impl::getImplUndoManager()
+ {
+ ENSURE_OR_THROW( pUndoManager != nullptr, "DocumentUndoManager_Impl::getImplUndoManager: no access to the doc's UndoManager implementation!" );
+
+#if OSL_DEBUG_LEVEL > 0
+ // in a non-product build, assert if the current UndoManager at the shell is not the same we obtained
+ // (and cached) at construction time
+ SfxObjectShell* pObjectShell = rAntiImpl.getBaseModel().GetObjectShell();
+ OSL_ENSURE( ( pObjectShell != nullptr ) && ( pUndoManager == pObjectShell->GetUndoManager() ),
+ "DocumentUndoManager_Impl::getImplUndoManager: the UndoManager changed meanwhile - what about our listener?" );
+#endif
+
+ return *pUndoManager;
+ }
+
+
+ Reference< XUndoManager > DocumentUndoManager_Impl::getThis()
+ {
+ return static_cast< XUndoManager* >( &rAntiImpl );
+ }
+
+
+ void DocumentUndoManager_Impl::invalidateXDo_nolck()
+ {
+ SfxModelGuard aGuard( rAntiImpl );
+
+ const SfxObjectShell* pDocShell = rAntiImpl.getBaseModel().GetObjectShell();
+ ENSURE_OR_THROW( pDocShell != nullptr, "lcl_invalidateUndo: no access to the doc shell!" );
+ SfxViewFrame* pViewFrame = SfxViewFrame::GetFirst( pDocShell );
+ while ( pViewFrame )
+ {
+ pViewFrame->GetBindings().Invalidate( SID_UNDO );
+ pViewFrame->GetBindings().Invalidate( SID_REDO );
+ pViewFrame = SfxViewFrame::GetNext( *pViewFrame, pDocShell );
+ }
+ }
+
+
+ //= SolarMutexFacade
+
+ namespace {
+
+ /** a facade for the SolarMutex, implementing ::framework::IMutex
+ */
+ class SolarMutexFacade : public ::framework::IMutex
+ {
+ public:
+ SolarMutexFacade()
+ {
+ }
+
+ virtual ~SolarMutexFacade() {}
+
+ virtual void acquire() override
+ {
+ Application::GetSolarMutex().acquire();
+ }
+
+ virtual void release() override
+ {
+ Application::GetSolarMutex().release();
+ }
+ };
+
+
+ //= UndoManagerGuard
+
+ class UndoManagerGuard :public ::framework::IMutexGuard
+ {
+ public:
+ explicit UndoManagerGuard( DocumentUndoManager& i_undoManager )
+ :m_guard( i_undoManager )
+ {
+ }
+
+ virtual ~UndoManagerGuard()
+ {
+ }
+
+ UndoManagerGuard(const UndoManagerGuard&) = delete;
+ UndoManagerGuard& operator=(const UndoManagerGuard&) = delete;
+
+ virtual void clear() override
+ {
+ m_guard.clear();
+ }
+
+ virtual ::framework::IMutex& getGuardedMutex() override
+ {
+ // note that this means that we *know* that SfxModelGuard also locks the SolarMutex (nothing more, nothing less).
+ // If this ever changes, we need to adjust this code here, too.
+ return m_solarMutexFacade;
+ }
+
+ private:
+ SfxModelGuard m_guard;
+ SolarMutexFacade m_solarMutexFacade;
+ };
+
+ }
+
+ //= DocumentUndoManager
+
+
+ DocumentUndoManager::DocumentUndoManager( SfxBaseModel& i_document )
+ :SfxModelSubComponent( i_document )
+ ,m_pImpl( new DocumentUndoManager_Impl( *this ) )
+ {
+ }
+
+ DocumentUndoManager::~DocumentUndoManager()
+ {
+ }
+
+ void DocumentUndoManager::disposing()
+ {
+ m_pImpl->disposing();
+ }
+
+
+ bool DocumentUndoManager::isInContext() const
+ {
+ // No mutex locking within this method, no disposal check - this is the responsibility of the owner.
+ return m_pImpl->getImplUndoManager().IsInListAction();
+ }
+
+
+ void SAL_CALL DocumentUndoManager::acquire() noexcept
+ {
+ OWeakObject::acquire();
+ SfxModelSubComponent::acquireModel();
+ }
+
+
+ void SAL_CALL DocumentUndoManager::release() noexcept
+ {
+ SfxModelSubComponent::releaseModel();
+ OWeakObject::release();
+ }
+
+
+ void SAL_CALL DocumentUndoManager::enterUndoContext( const OUString& i_title )
+ {
+ // SYNCHRONIZED --->
+ UndoManagerGuard aGuard( *this );
+ m_pImpl->aUndoHelper.enterUndoContext( i_title, aGuard );
+ // <--- SYNCHRONIZED
+ m_pImpl->invalidateXDo_nolck();
+ }
+
+
+ void SAL_CALL DocumentUndoManager::enterHiddenUndoContext( )
+ {
+ // SYNCHRONIZED --->
+ UndoManagerGuard aGuard( *this );
+ m_pImpl->aUndoHelper.enterHiddenUndoContext( aGuard );
+ // <--- SYNCHRONIZED
+ m_pImpl->invalidateXDo_nolck();
+ }
+
+
+ void SAL_CALL DocumentUndoManager::leaveUndoContext( )
+ {
+ // SYNCHRONIZED --->
+ UndoManagerGuard aGuard( *this );
+ m_pImpl->aUndoHelper.leaveUndoContext( aGuard );
+ // <--- SYNCHRONIZED
+ m_pImpl->invalidateXDo_nolck();
+ }
+
+
+ void SAL_CALL DocumentUndoManager::addUndoAction( const Reference< XUndoAction >& i_action )
+ {
+ // SYNCHRONIZED --->
+ UndoManagerGuard aGuard( *this );
+ m_pImpl->aUndoHelper.addUndoAction( i_action, aGuard );
+ // <--- SYNCHRONIZED
+ m_pImpl->invalidateXDo_nolck();
+ }
+
+
+ void SAL_CALL DocumentUndoManager::undo( )
+ {
+ // SYNCHRONIZED --->
+ UndoManagerGuard aGuard( *this );
+ m_pImpl->aUndoHelper.undo( aGuard );
+ // <--- SYNCHRONIZED
+ m_pImpl->invalidateXDo_nolck();
+ }
+
+
+ void SAL_CALL DocumentUndoManager::redo( )
+ {
+ // SYNCHRONIZED --->
+ UndoManagerGuard aGuard( *this );
+ m_pImpl->aUndoHelper.redo( aGuard );
+ // <--- SYNCHRONIZED
+ m_pImpl->invalidateXDo_nolck();
+ }
+
+
+ sal_Bool SAL_CALL DocumentUndoManager::isUndoPossible( )
+ {
+ UndoManagerGuard aGuard( *this );
+ return m_pImpl->aUndoHelper.isUndoPossible();
+ }
+
+
+ sal_Bool SAL_CALL DocumentUndoManager::isRedoPossible( )
+ {
+ UndoManagerGuard aGuard( *this );
+ return m_pImpl->aUndoHelper.isRedoPossible();
+ }
+
+
+ OUString SAL_CALL DocumentUndoManager::getCurrentUndoActionTitle( )
+ {
+ UndoManagerGuard aGuard( *this );
+ return m_pImpl->aUndoHelper.getCurrentUndoActionTitle();
+ }
+
+
+ OUString SAL_CALL DocumentUndoManager::getCurrentRedoActionTitle( )
+ {
+ UndoManagerGuard aGuard( *this );
+ return m_pImpl->aUndoHelper.getCurrentRedoActionTitle();
+ }
+
+
+ Sequence< OUString > SAL_CALL DocumentUndoManager::getAllUndoActionTitles( )
+ {
+ UndoManagerGuard aGuard( *this );
+ return m_pImpl->aUndoHelper.getAllUndoActionTitles();
+ }
+
+
+ Sequence< OUString > SAL_CALL DocumentUndoManager::getAllRedoActionTitles( )
+ {
+ UndoManagerGuard aGuard( *this );
+ return m_pImpl->aUndoHelper.getAllRedoActionTitles();
+ }
+
+
+ void SAL_CALL DocumentUndoManager::clear( )
+ {
+ // SYNCHRONIZED --->
+ UndoManagerGuard aGuard( *this );
+ m_pImpl->aUndoHelper.clear( aGuard );
+ // <--- SYNCHRONIZED
+ m_pImpl->invalidateXDo_nolck();
+ }
+
+
+ void SAL_CALL DocumentUndoManager::clearRedo( )
+ {
+ // SYNCHRONIZED --->
+ UndoManagerGuard aGuard( *this );
+ m_pImpl->aUndoHelper.clearRedo( aGuard );
+ // <--- SYNCHRONIZED
+ m_pImpl->invalidateXDo_nolck();
+ }
+
+
+ void SAL_CALL DocumentUndoManager::reset()
+ {
+ // SYNCHRONIZED --->
+ UndoManagerGuard aGuard( *this );
+ m_pImpl->aUndoHelper.reset( aGuard );
+ // <--- SYNCHRONIZED
+ m_pImpl->invalidateXDo_nolck();
+ }
+
+
+ void SAL_CALL DocumentUndoManager::lock( )
+ {
+ UndoManagerGuard aGuard( *this );
+ m_pImpl->aUndoHelper.lock();
+ }
+
+
+ void SAL_CALL DocumentUndoManager::unlock( )
+ {
+ UndoManagerGuard aGuard( *this );
+ m_pImpl->aUndoHelper.unlock();
+ }
+
+
+ sal_Bool SAL_CALL DocumentUndoManager::isLocked( )
+ {
+ UndoManagerGuard aGuard( *this );
+ return m_pImpl->aUndoHelper.isLocked();
+ }
+
+
+ void SAL_CALL DocumentUndoManager::addUndoManagerListener( const Reference< XUndoManagerListener >& i_listener )
+ {
+ UndoManagerGuard aGuard( *this );
+ return m_pImpl->aUndoHelper.addUndoManagerListener( i_listener );
+ }
+
+
+ void SAL_CALL DocumentUndoManager::removeUndoManagerListener( const Reference< XUndoManagerListener >& i_listener )
+ {
+ UndoManagerGuard aGuard( *this );
+ return m_pImpl->aUndoHelper.removeUndoManagerListener( i_listener );
+ }
+
+
+ Reference< XInterface > SAL_CALL DocumentUndoManager::getParent( )
+ {
+ UndoManagerGuard aGuard( *this );
+ return static_cast< XModel* >( &getBaseModel() );
+ }
+
+
+ void SAL_CALL DocumentUndoManager::setParent( const Reference< XInterface >& )
+ {
+ throw NoSupportException( OUString(), m_pImpl->getThis() );
+ }
+
+
+} // namespace sfx2
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/doc/exoticfileloadexception.cxx b/sfx2/source/doc/exoticfileloadexception.cxx
new file mode 100644
index 000000000..91dc9c273
--- /dev/null
+++ b/sfx2/source/doc/exoticfileloadexception.cxx
@@ -0,0 +1,37 @@
+/* -*- 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 "exoticfileloadexception.hxx"
+
+#include <comphelper/interaction.hxx>
+#include <com/sun/star/document/ExoticFileLoadException.hpp>
+
+using namespace com::sun::star;
+
+ExoticFileLoadException::ExoticFileLoadException(const OUString& rURL,
+ const OUString& rFilterUIName)
+ : m_xAbort(new comphelper::OInteractionAbort)
+ , m_xApprove(new comphelper::OInteractionApprove)
+ , m_lContinuations{ m_xApprove, m_xAbort }
+{
+ document::ExoticFileLoadException aReq;
+ aReq.URL = rURL;
+ aReq.FilterUIName = rFilterUIName;
+
+ m_aRequest <<= aReq;
+}
+
+bool ExoticFileLoadException::isApprove() const
+{
+ comphelper::OInteractionApprove* pBase
+ = static_cast<comphelper::OInteractionApprove*>(m_xApprove.get());
+ return pBase->wasSelected();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/doc/exoticfileloadexception.hxx b/sfx2/source/doc/exoticfileloadexception.hxx
new file mode 100644
index 000000000..8204d6f55
--- /dev/null
+++ b/sfx2/source/doc/exoticfileloadexception.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/.
+ */
+
+#ifndef INCLUDED_SFX2_EXOTICFILELOADEXCEPTION_HXX
+#define INCLUDED_SFX2_EXOTICFILELOADEXCEPTION_HXX
+
+#include <com/sun/star/task/XInteractionContinuation.hpp>
+#include <com/sun/star/task/XInteractionRequest.hpp>
+#include <cppuhelper/implbase.hxx>
+
+class ExoticFileLoadException : public cppu::WeakImplHelper<css::task::XInteractionRequest>
+{
+ // C++ interface
+public:
+ ExoticFileLoadException(const OUString& rURL, const OUString& rFilterUIName);
+ bool isApprove() const;
+
+ // UNO interface
+public:
+ virtual css::uno::Sequence<css::uno::Reference<css::task::XInteractionContinuation>>
+ SAL_CALL getContinuations() override
+ {
+ return m_lContinuations;
+ }
+ css::uno::Any SAL_CALL getRequest() override { return m_aRequest; }
+
+ // member
+private:
+ css::uno::Any m_aRequest;
+ css::uno::Reference<css::task::XInteractionContinuation> m_xAbort;
+ css::uno::Reference<css::task::XInteractionContinuation> m_xApprove;
+ css::uno::Sequence<css::uno::Reference<css::task::XInteractionContinuation>> m_lContinuations;
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/doc/frmdescr.cxx b/sfx2/source/doc/frmdescr.cxx
new file mode 100644
index 000000000..43183986d
--- /dev/null
+++ b/sfx2/source/doc/frmdescr.cxx
@@ -0,0 +1,57 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <svl/itemset.hxx>
+
+#include <sfx2/frmdescr.hxx>
+#include <sfx2/app.hxx>
+#include <memory>
+
+SfxFrameDescriptor::SfxFrameDescriptor() :
+ aMargin( -1, -1 ),
+ eScroll( ScrollingMode::Auto ),
+ bHasBorder( true ),
+ bHasBorderSet( false )
+{
+}
+
+SfxFrameDescriptor::~SfxFrameDescriptor()
+{
+}
+
+SfxItemSet* SfxFrameDescriptor::GetArgs()
+{
+ if( !m_pArgs )
+ m_pArgs.reset( new SfxAllItemSet( SfxGetpApp()->GetPool() ) );
+ return m_pArgs.get();
+}
+
+void SfxFrameDescriptor::SetURL( std::u16string_view rURL )
+{
+ aURL = INetURLObject(rURL);
+ SetActualURL();
+}
+
+void SfxFrameDescriptor::SetActualURL()
+{
+ if ( m_pArgs )
+ m_pArgs->ClearItem();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/doc/graphhelp.cxx b/sfx2/source/doc/graphhelp.cxx
new file mode 100644
index 000000000..136e3ac9a
--- /dev/null
+++ b/sfx2/source/doc/graphhelp.cxx
@@ -0,0 +1,260 @@
+/* -*- 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 _WIN32
+#include <prewin.h>
+#include <postwin.h>
+#endif
+
+#include <com/sun/star/uno/Exception.hpp>
+#include <com/sun/star/graphic/GraphicProvider.hpp>
+#include <com/sun/star/graphic/XGraphicProvider.hpp>
+#include <com/sun/star/graphic/XGraphic.hpp>
+
+
+#include <vcl/gdimtf.hxx>
+#include <vcl/graph.hxx>
+#include <vcl/cvtgrf.hxx>
+#include <vcl/bitmapex.hxx>
+#include <vcl/graphicfilter.hxx>
+
+#include <tools/stream.hxx>
+#include <unotools/ucbstreamhelper.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <o3tl/char16_t2wchar_t.hxx>
+#include <o3tl/string_view.hxx>
+
+#include "graphhelp.hxx"
+#include <bitmaps.hlst>
+
+#include <memory>
+
+#if defined _WIN32
+#include <unotools/tempfile.hxx>
+#include <vcl/outdev.hxx>
+#endif
+
+using namespace css;
+
+std::unique_ptr<SvMemoryStream> GraphicHelper::getFormatStrFromGDI_Impl( const GDIMetaFile* pGDIMeta, ConvertDataFormat nFormat )
+{
+ std::unique_ptr<SvMemoryStream> pResult;
+ if ( pGDIMeta )
+ {
+ std::unique_ptr<SvMemoryStream> pStream(new SvMemoryStream( 65535, 65535 ));
+ Graphic aGraph( *pGDIMeta );
+ if ( GraphicConverter::Export( *pStream, aGraph, nFormat ) == ERRCODE_NONE )
+ pResult = std::move(pStream);
+ }
+
+ return pResult;
+}
+
+
+// static
+void* GraphicHelper::getEnhMetaFileFromGDI_Impl( const GDIMetaFile* pGDIMeta )
+{
+ void* pResult = nullptr;
+
+#ifdef _WIN32
+ if ( pGDIMeta )
+ {
+ OUString const aStr(".emf");
+ ::utl::TempFile aTempFile( u"", true, &aStr );
+
+ OUString aMetaFile = aTempFile.GetFileName();
+ OUString aMetaURL = aTempFile.GetURL();
+
+ std::unique_ptr<SvStream> pStream = ::utl::UcbStreamHelper::CreateStream( aMetaURL, StreamMode::STD_READWRITE );
+ if ( pStream )
+ {
+ Graphic aGraph( *pGDIMeta );
+ ErrCode nFailed = GraphicConverter::Export( *pStream, aGraph, ConvertDataFormat::EMF );
+ pStream->Flush();
+ pStream.reset();
+
+ if ( !nFailed )
+ pResult = GetEnhMetaFileW( o3tl::toW(aMetaFile.getStr()) );
+ }
+ }
+#else
+ (void)pGDIMeta; // unused
+#endif
+
+ return pResult;
+}
+
+
+// static
+void* GraphicHelper::getWinMetaFileFromGDI_Impl( const GDIMetaFile* pGDIMeta, const Size& aMetaSize )
+{
+ void* pResult = nullptr;
+
+#ifdef _WIN32
+ if ( pGDIMeta )
+ {
+ SvMemoryStream pStream( 65535, 65535 );
+ Graphic aGraph( *pGDIMeta );
+ ErrCode nFailed = GraphicConverter::Export( pStream, aGraph, ConvertDataFormat::WMF );
+ pStream.Flush();
+ if ( !nFailed )
+ {
+ sal_Int32 nLength = pStream.TellEnd();
+ if ( nLength > 22 )
+ {
+ HMETAFILE hMeta = SetMetaFileBitsEx( nLength - 22,
+ static_cast< const unsigned char*>( pStream.GetData() ) + 22 );
+
+ if ( hMeta )
+ {
+ HGLOBAL hMemory = GlobalAlloc( GMEM_DDESHARE | GMEM_MOVEABLE, sizeof( METAFILEPICT ) );
+
+ if ( hMemory )
+ {
+ METAFILEPICT* pMF = static_cast<METAFILEPICT*>(GlobalLock( hMemory ));
+
+ pMF->hMF = hMeta;
+ pMF->mm = MM_ANISOTROPIC;
+
+ MapMode aWinMode( MapUnit::Map100thMM );
+
+ if ( aWinMode == pGDIMeta->GetPrefMapMode() )
+ {
+ pMF->xExt = aMetaSize.Width();
+ pMF->yExt = aMetaSize.Height();
+ }
+ else
+ {
+ Size aWinSize = OutputDevice::LogicToLogic( Size( aMetaSize.Width(), aMetaSize.Height() ),
+ pGDIMeta->GetPrefMapMode(),
+ aWinMode );
+ pMF->xExt = aWinSize.Width();
+ pMF->yExt = aWinSize.Height();
+ }
+
+ GlobalUnlock( hMemory );
+ pResult = static_cast<void*>(hMemory);
+ }
+ else
+ DeleteMetaFile( hMeta );
+ }
+ }
+ }
+ }
+#else
+ (void)pGDIMeta; // unused
+ (void)aMetaSize; // unused
+#endif
+
+
+ return pResult;
+}
+
+
+// static
+bool GraphicHelper::getThumbnailFormatFromBitmap_Impl(const BitmapEx& rBitmap, const uno::Reference<io::XStream>& xStream)
+{
+ if (rBitmap.IsEmpty() || !xStream.is())
+ return false;
+
+ std::unique_ptr<SvStream> pStream(utl::UcbStreamHelper::CreateStream(xStream));
+
+ if (pStream->GetError())
+ return false;
+
+ BitmapEx bitmap(rBitmap);
+ bitmap.Convert(BmpConversion::N8BitColors);
+
+ GraphicFilter& rFilter = GraphicFilter::GetGraphicFilter();
+
+ if (rFilter.compressAsPNG(bitmap, *pStream) != ERRCODE_NONE)
+ return false;
+
+ pStream->FlushBuffer();
+
+ return !pStream->GetError();
+}
+
+// static
+bool GraphicHelper::getThumbnailReplacement_Impl(std::u16string_view rResID, const uno::Reference< io::XStream >& xStream )
+{
+ bool bResult = false;
+ if (!rResID.empty() && xStream.is())
+ {
+ uno::Reference< uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext();
+ try
+ {
+ uno::Reference< graphic::XGraphicProvider > xGraphProvider(graphic::GraphicProvider::create(xContext));
+ const OUString aURL{OUString::Concat("private:graphicrepository/") + rResID};
+
+ uno::Sequence< beans::PropertyValue > aMediaProps{ comphelper::makePropertyValue("URL",
+ aURL) };
+
+ uno::Reference< graphic::XGraphic > xGraphic = xGraphProvider->queryGraphic( aMediaProps );
+ if ( xGraphic.is() )
+ {
+ uno::Sequence< beans::PropertyValue > aStoreProps{
+ comphelper::makePropertyValue("OutputStream", xStream),
+ comphelper::makePropertyValue("MimeType", OUString("image/png"))
+ };
+
+ xGraphProvider->storeGraphic( xGraphic, aStoreProps );
+ bResult = true;
+ }
+ }
+ catch(const uno::Exception&)
+ {
+ }
+ }
+
+ return bResult;
+}
+
+// static
+OUString GraphicHelper::getThumbnailReplacementIDByFactoryName_Impl( std::u16string_view aFactoryShortName )
+{
+ OUString sResult;
+
+ if ( aFactoryShortName == u"scalc" )
+ {
+ sResult = BMP_128X128_CALC_DOC;
+ }
+ else if ( aFactoryShortName == u"sdraw" )
+ {
+ sResult = BMP_128X128_DRAW_DOC;
+ }
+ else if ( aFactoryShortName == u"simpress" )
+ {
+ sResult = BMP_128X128_IMPRESS_DOC;
+ }
+ else if ( aFactoryShortName == u"smath" )
+ {
+ sResult = BMP_128X128_MATH_DOC;
+ }
+ else if ( aFactoryShortName == u"swriter" || o3tl::starts_with(aFactoryShortName, u"swriter/") )
+ {
+ sResult = BMP_128X128_WRITER_DOC;
+ }
+
+ return sResult;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/doc/graphhelp.hxx b/sfx2/source/doc/graphhelp.hxx
new file mode 100644
index 000000000..1945661fc
--- /dev/null
+++ b/sfx2/source/doc/graphhelp.hxx
@@ -0,0 +1,71 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SFX2_SOURCE_DOC_GRAPHHELP_HXX
+#define INCLUDED_SFX2_SOURCE_DOC_GRAPHHELP_HXX
+
+#include <sal/config.h>
+
+#include <string_view>
+
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/io/XStream.hpp>
+#include <rtl/ustring.hxx>
+#include <tools/gen.hxx>
+
+class SvMemoryStream;
+class GDIMetaFile;
+class BitmapEx;
+enum class ConvertDataFormat;
+
+class GraphicHelper
+{
+public:
+
+ static std::unique_ptr<SvMemoryStream> getFormatStrFromGDI_Impl( const GDIMetaFile* pGDIMeta, ConvertDataFormat nFormat );
+
+ static void* getEnhMetaFileFromGDI_Impl( const GDIMetaFile* pGDIMeta );
+
+ static void* getWinMetaFileFromGDI_Impl( const GDIMetaFile* pGDIMeta, const Size& aMetaSize );
+
+ static bool supportsMetaFileHandle_Impl()
+ {
+#ifdef _WIN32
+ return true;
+#else
+ return false;
+#endif
+ }
+
+ static bool getThumbnailFormatFromBitmap_Impl(
+ const BitmapEx& rBitmap,
+ const css::uno::Reference< css::io::XStream >& xStream );
+
+ static OUString getThumbnailReplacementIDByFactoryName_Impl(
+ std::u16string_view aFactoryShortName);
+
+ static bool getThumbnailReplacement_Impl(
+ std::u16string_view rResID,
+ const css::uno::Reference< css::io::XStream >& xStream );
+
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/doc/guisaveas.cxx b/sfx2/source/doc/guisaveas.cxx
new file mode 100644
index 000000000..23c75f296
--- /dev/null
+++ b/sfx2/source/doc/guisaveas.cxx
@@ -0,0 +1,1857 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/ui/dialogs/XExecutableDialog.hpp>
+#include <com/sun/star/ui/dialogs/XFilePicker3.hpp>
+#include <com/sun/star/ui/dialogs/XFilePickerControlAccess.hpp>
+#include <com/sun/star/ui/dialogs/CommonFilePickerElementIds.hpp>
+#include <com/sun/star/ui/dialogs/ExtendedFilePickerElementIds.hpp>
+#include <com/sun/star/ui/dialogs/TemplateDescription.hpp>
+#include <com/sun/star/view/XSelectionSupplier.hpp>
+#include <com/sun/star/beans/PropertyExistException.hpp>
+#include <com/sun/star/beans/XPropertyAccess.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/beans/XPropertyContainer.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/document/XExporter.hpp>
+#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
+#include <com/sun/star/document/XDocumentProperties.hpp>
+#include <com/sun/star/task/ErrorCodeIOException.hpp>
+#include <com/sun/star/task/InteractionHandler.hpp>
+#include <com/sun/star/util/URLTransformer.hpp>
+#include <com/sun/star/util/XURLTransformer.hpp>
+#include <com/sun/star/frame/ModuleManager.hpp>
+#include <com/sun/star/frame/XStorable.hpp>
+#include <com/sun/star/frame/XStorable2.hpp>
+#include <com/sun/star/frame/XDispatchProvider.hpp>
+#include <com/sun/star/frame/XDispatch.hpp>
+#include <com/sun/star/frame/XTitle.hpp>
+#include <com/sun/star/util/XModifiable.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+
+#include <com/sun/star/util/XCloneable.hpp>
+
+#include <guisaveas.hxx>
+
+#include <sal/log.hxx>
+#include <svl/itemset.hxx>
+#include <svl/eitem.hxx>
+#include <tools/debug.hxx>
+#include <tools/diagnose_ex.h>
+#include <tools/urlobj.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/propertysequence.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <comphelper/sequenceashashmap.hxx>
+#include <comphelper/mimeconfighelper.hxx>
+#include <comphelper/lok.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+#include <o3tl/char16_t2wchar_t.hxx>
+
+#include <sfx2/sfxsids.hrc>
+#include <sfx2/strings.hrc>
+#include <sfx2/sfxresid.hxx>
+#include <sfx2/filedlghelper.hxx>
+#include <sfx2/app.hxx>
+#include <sfx2/sfxuno.hxx>
+#include <alienwarn.hxx>
+
+#include <memory>
+#include <string_view>
+
+#include <officecfg/Office/Common.hxx>
+
+#include <vcl/FilterConfigItem.hxx>
+#include <com/sun/star/system/SystemShellExecute.hpp>
+#include <com/sun/star/system/SystemShellExecuteFlags.hpp>
+
+#include <osl/file.hxx>
+
+#ifdef _WIN32
+#include <Shlobj.h>
+#ifdef GetTempPath
+#undef GetTempPath
+#endif
+#endif
+
+// flags that specify requested operation
+#define EXPORT_REQUESTED 1
+#define PDFEXPORT_REQUESTED 2
+#define PDFDIRECTEXPORT_REQUESTED 4
+#define WIDEEXPORT_REQUESTED 8
+#define SAVE_REQUESTED 16
+#define SAVEAS_REQUESTED 32
+#define SAVEACOPY_REQUESTED 64
+#define EPUBEXPORT_REQUESTED 128
+#define EPUBDIRECTEXPORT_REQUESTED 256
+#define SAVEASREMOTE_REQUESTED -1
+
+// possible statuses of save operation
+#define STATUS_NO_ACTION 0
+#define STATUS_SAVE 1
+#define STATUS_SAVEAS 2
+#define STATUS_SAVEAS_STANDARDNAME 3
+
+constexpr OUStringLiteral aFilterNameString = u"FilterName";
+constexpr OUStringLiteral aFilterOptionsString = u"FilterOptions";
+constexpr OUStringLiteral aFilterDataString = u"FilterData";
+
+using namespace ::com::sun::star;
+using namespace css::system;
+
+namespace {
+
+sal_uInt16 getSlotIDFromMode( sal_Int16 nStoreMode )
+{
+ // This is a temporary hardcoded solution must be removed when
+ // dialogs do not need parameters in SidSet representation any more
+
+ sal_uInt16 nResult = 0;
+ if ( nStoreMode == EXPORT_REQUESTED || nStoreMode == ( EXPORT_REQUESTED | SAVEACOPY_REQUESTED | WIDEEXPORT_REQUESTED ) )
+ nResult = SID_EXPORTDOC;
+ else if ( nStoreMode == ( EXPORT_REQUESTED | PDFEXPORT_REQUESTED ) )
+ nResult = SID_EXPORTDOCASPDF;
+ else if ( nStoreMode == ( EXPORT_REQUESTED | EPUBEXPORT_REQUESTED ) )
+ nResult = SID_EXPORTDOCASEPUB;
+ else if ( nStoreMode == ( EXPORT_REQUESTED | PDFEXPORT_REQUESTED | PDFDIRECTEXPORT_REQUESTED ) )
+ nResult = SID_DIRECTEXPORTDOCASPDF;
+ else if ( nStoreMode == ( EXPORT_REQUESTED | EPUBEXPORT_REQUESTED | EPUBDIRECTEXPORT_REQUESTED ) )
+ nResult = SID_DIRECTEXPORTDOCASEPUB;
+ else if ( nStoreMode == SAVEAS_REQUESTED || nStoreMode == ( EXPORT_REQUESTED | WIDEEXPORT_REQUESTED ) )
+ nResult = SID_SAVEASDOC;
+ else if ( nStoreMode == SAVEASREMOTE_REQUESTED )
+ nResult = SID_SAVEASREMOTE;
+ else {
+ SAL_WARN( "sfx.doc", "Unacceptable slot name is provided!" );
+ }
+
+ return nResult;
+}
+
+
+sal_Int16 getStoreModeFromSlotName( std::u16string_view aSlotName )
+{
+ sal_Int16 nResult = 0;
+ if ( aSlotName == u"ExportTo" )
+ nResult = EXPORT_REQUESTED;
+ else if ( aSlotName == u"ExportToPDF" )
+ nResult = EXPORT_REQUESTED | PDFEXPORT_REQUESTED;
+ else if ( aSlotName == u"ExportDirectToPDF" )
+ nResult = EXPORT_REQUESTED | PDFEXPORT_REQUESTED | PDFDIRECTEXPORT_REQUESTED;
+ else if ( aSlotName == u"ExportToEPUB" )
+ nResult = EXPORT_REQUESTED | EPUBEXPORT_REQUESTED;
+ else if ( aSlotName == u"ExportDirectToEPUB" )
+ nResult = EXPORT_REQUESTED | EPUBEXPORT_REQUESTED | EPUBDIRECTEXPORT_REQUESTED;
+ else if ( aSlotName == u"Save" )
+ nResult = SAVE_REQUESTED;
+ else if ( aSlotName == u"SaveAs" )
+ nResult = SAVEAS_REQUESTED;
+ else if ( aSlotName == u"SaveAsRemote" )
+ nResult = SAVEASREMOTE_REQUESTED;
+ else
+ throw task::ErrorCodeIOException(
+ (OUString::Concat("getStoreModeFromSlotName(\"") + aSlotName
+ + "): ERRCODE_IO_INVALIDPARAMETER"),
+ uno::Reference< uno::XInterface >(), sal_uInt32(ERRCODE_IO_INVALIDPARAMETER) );
+
+ return nResult;
+}
+
+
+SfxFilterFlags getMustFlags( sal_Int16 nStoreMode )
+{
+ return ( SfxFilterFlags::EXPORT
+ | ( ( ( nStoreMode & EXPORT_REQUESTED ) && !( nStoreMode & WIDEEXPORT_REQUESTED ) ) ? SfxFilterFlags::NONE : SfxFilterFlags::IMPORT ) );
+}
+
+
+SfxFilterFlags getDontFlags( sal_Int16 nStoreMode )
+{
+ return ( SfxFilterFlags::INTERNAL
+ | SfxFilterFlags::NOTINFILEDLG
+ | ( ( ( nStoreMode & EXPORT_REQUESTED ) && !( nStoreMode & WIDEEXPORT_REQUESTED ) ) ? SfxFilterFlags::IMPORT : SfxFilterFlags::NONE ) );
+}
+
+
+
+
+class DocumentSettingsGuard
+{
+ uno::Reference< beans::XPropertySet > m_xDocumentSettings;
+ bool m_bPreserveReadOnly;
+ bool m_bReadOnlySupported;
+
+ bool m_bRestoreSettings;
+public:
+ DocumentSettingsGuard( const uno::Reference< frame::XModel >& xModel, bool bReadOnly, bool bRestore )
+ : m_bPreserveReadOnly( false )
+ , m_bReadOnlySupported( false )
+ , m_bRestoreSettings( bRestore )
+ {
+ try
+ {
+ uno::Reference< lang::XMultiServiceFactory > xDocSettingsSupplier( xModel, uno::UNO_QUERY_THROW );
+ m_xDocumentSettings.set(
+ xDocSettingsSupplier->createInstance( "com.sun.star.document.Settings" ),
+ uno::UNO_QUERY_THROW );
+
+ try
+ {
+ OUString aLoadReadonlyString( "LoadReadonly" );
+ m_xDocumentSettings->getPropertyValue( aLoadReadonlyString ) >>= m_bPreserveReadOnly;
+ m_xDocumentSettings->setPropertyValue( aLoadReadonlyString, uno::Any( bReadOnly ) );
+ m_bReadOnlySupported = true;
+ }
+ catch( const uno::Exception& )
+ {}
+ }
+ catch( const uno::Exception& )
+ {}
+
+ if ( bReadOnly && !m_bReadOnlySupported )
+ throw uno::RuntimeException(); // the user could provide the data, so it must be stored
+ }
+
+ ~DocumentSettingsGuard()
+ {
+ if ( m_bRestoreSettings )
+ {
+ try
+ {
+ if ( m_bReadOnlySupported )
+ m_xDocumentSettings->setPropertyValue( "LoadReadonly", uno::Any( m_bPreserveReadOnly ) );
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "sfx.doc", "" );
+ }
+ }
+ }
+};
+} // anonymous namespace
+
+
+
+class ModelData_Impl
+{
+ SfxStoringHelper* m_pOwner;
+ uno::Reference< frame::XModel > m_xModel;
+ uno::Reference< frame::XStorable > m_xStorable;
+ uno::Reference< frame::XStorable2 > m_xStorable2;
+
+ OUString m_aModuleName;
+ std::unique_ptr<::comphelper::SequenceAsHashMap> m_pDocumentPropsHM;
+ std::unique_ptr<::comphelper::SequenceAsHashMap> m_pModulePropsHM;
+
+ ::comphelper::SequenceAsHashMap m_aMediaDescrHM;
+
+ bool m_bRecommendReadOnly;
+
+public:
+ ModelData_Impl( SfxStoringHelper& aOwner,
+ const uno::Reference< frame::XModel >& xModel,
+ const uno::Sequence< beans::PropertyValue >& aMediaDescr );
+
+ ~ModelData_Impl();
+
+ void FreeDocumentProps();
+
+ uno::Reference< frame::XModel > const & GetModel() const;
+ uno::Reference< frame::XStorable > const & GetStorable();
+ uno::Reference< frame::XStorable2 > const & GetStorable2();
+
+ ::comphelper::SequenceAsHashMap& GetMediaDescr() { return m_aMediaDescrHM; }
+
+ bool IsRecommendReadOnly() const { return m_bRecommendReadOnly; }
+
+ const ::comphelper::SequenceAsHashMap& GetDocProps();
+
+ OUString const & GetModuleName();
+ const ::comphelper::SequenceAsHashMap& GetModuleProps();
+
+ void CheckInteractionHandler();
+
+
+ OUString GetDocServiceName();
+ uno::Sequence< beans::PropertyValue > GetDocServiceDefaultFilterCheckFlags( SfxFilterFlags nMust, SfxFilterFlags nDont );
+ uno::Sequence< beans::PropertyValue > GetDocServiceAnyFilter( SfxFilterFlags nMust, SfxFilterFlags nDont );
+ uno::Sequence< beans::PropertyValue > GetPreselectedFilter_Impl( sal_Int16 nStoreMode );
+ uno::Sequence< beans::PropertyValue > GetDocServiceDefaultFilter();
+
+ bool ExecuteFilterDialog_Impl( const OUString& aFilterName );
+
+ sal_Int8 CheckSaveAcceptable( sal_Int8 nCurStatus );
+ sal_Int8 CheckStateForSave();
+
+ sal_Int8 CheckFilter( const OUString& );
+
+ bool CheckFilterOptionsDialogExistence();
+
+ bool OutputFileDialog( sal_Int16 nStoreMode,
+ const ::comphelper::SequenceAsHashMap& aPreselectedFilterPropsHM,
+ bool bSetStandardName,
+ OUString& aSuggestedName,
+ bool bPreselectPassword,
+ OUString& aSuggestedDir,
+ sal_Int16 nDialog,
+ const OUString& rStandardDir,
+ const css::uno::Sequence< OUString >& rDenyList
+ );
+
+ bool ShowDocumentInfoDialog();
+
+ static OUString GetRecommendedExtension( const OUString& aTypeName );
+ OUString GetRecommendedDir( const OUString& aSuggestedDir );
+ OUString GetRecommendedName( const OUString& aSuggestedName,
+ const OUString& aTypeName );
+
+};
+
+
+ModelData_Impl::ModelData_Impl( SfxStoringHelper& aOwner,
+ const uno::Reference< frame::XModel >& xModel,
+ const uno::Sequence< beans::PropertyValue >& aMediaDescr )
+: m_pOwner( &aOwner )
+, m_xModel( xModel )
+, m_aMediaDescrHM( aMediaDescr )
+, m_bRecommendReadOnly( false )
+{
+ CheckInteractionHandler();
+}
+
+
+ModelData_Impl::~ModelData_Impl()
+{
+ FreeDocumentProps();
+ m_pDocumentPropsHM.reset();
+ m_pModulePropsHM.reset();
+}
+
+
+void ModelData_Impl::FreeDocumentProps()
+{
+ m_pDocumentPropsHM.reset();
+}
+
+
+uno::Reference< frame::XModel > const & ModelData_Impl::GetModel() const
+{
+ if ( !m_xModel.is() )
+ throw uno::RuntimeException();
+
+ return m_xModel;
+}
+
+
+uno::Reference< frame::XStorable > const & ModelData_Impl::GetStorable()
+{
+ if ( !m_xStorable.is() )
+ {
+ m_xStorable.set( m_xModel, uno::UNO_QUERY_THROW );
+ }
+
+ return m_xStorable;
+}
+
+
+uno::Reference< frame::XStorable2 > const & ModelData_Impl::GetStorable2()
+{
+ if ( !m_xStorable2.is() )
+ {
+ m_xStorable2.set( m_xModel, uno::UNO_QUERY_THROW );
+ }
+
+ return m_xStorable2;
+}
+
+
+const ::comphelper::SequenceAsHashMap& ModelData_Impl::GetDocProps()
+{
+ if ( !m_pDocumentPropsHM )
+ m_pDocumentPropsHM.reset( new ::comphelper::SequenceAsHashMap( GetModel()->getArgs() ) );
+
+ return *m_pDocumentPropsHM;
+}
+
+
+OUString const & ModelData_Impl::GetModuleName()
+{
+ if ( m_aModuleName.isEmpty() )
+ {
+ m_aModuleName = m_pOwner->GetModuleManager()->identify(
+ uno::Reference< uno::XInterface >( m_xModel, uno::UNO_QUERY ) );
+ if ( m_aModuleName.isEmpty() )
+ throw uno::RuntimeException(); // TODO:
+ }
+ return m_aModuleName;
+}
+
+
+const ::comphelper::SequenceAsHashMap& ModelData_Impl::GetModuleProps()
+{
+ if ( !m_pModulePropsHM )
+ {
+ uno::Sequence< beans::PropertyValue > aModuleProps;
+ m_pOwner->GetModuleManager()->getByName( GetModuleName() ) >>= aModuleProps;
+ if ( !aModuleProps.hasElements() )
+ throw uno::RuntimeException(); // TODO;
+ m_pModulePropsHM.reset( new ::comphelper::SequenceAsHashMap( aModuleProps ) );
+ }
+
+ return *m_pModulePropsHM;
+}
+
+
+OUString ModelData_Impl::GetDocServiceName()
+{
+ return GetModuleProps().getUnpackedValueOrDefault("ooSetupFactoryDocumentService", OUString());
+}
+
+
+void ModelData_Impl::CheckInteractionHandler()
+{
+ const OUString sInteractionHandler {"InteractionHandler"};
+ ::comphelper::SequenceAsHashMap::const_iterator aInteractIter =
+ m_aMediaDescrHM.find( sInteractionHandler );
+
+ if ( aInteractIter == m_aMediaDescrHM.end() )
+ {
+ try {
+ m_aMediaDescrHM[ sInteractionHandler ]
+ <<= task::InteractionHandler::createWithParent( comphelper::getProcessComponentContext(), nullptr);
+ }
+ catch( const uno::Exception& )
+ {
+ }
+ }
+ else
+ {
+ uno::Reference< task::XInteractionHandler > xInteract;
+ DBG_ASSERT( ( aInteractIter->second >>= xInteract ) && xInteract.is(), "Broken interaction handler is provided!\n" );
+ }
+}
+
+
+uno::Sequence< beans::PropertyValue > ModelData_Impl::GetDocServiceDefaultFilter()
+{
+ uno::Sequence< beans::PropertyValue > aProps;
+
+ const OUString aFilterName = GetModuleProps().getUnpackedValueOrDefault( "ooSetupFactoryDefaultFilter", OUString() );
+
+ m_pOwner->GetFilterConfiguration()->getByName( aFilterName ) >>= aProps;
+
+ return aProps;
+}
+
+
+uno::Sequence< beans::PropertyValue > ModelData_Impl::GetDocServiceDefaultFilterCheckFlags( SfxFilterFlags nMust,
+ SfxFilterFlags nDont )
+{
+ uno::Sequence< beans::PropertyValue > aFilterProps;
+ uno::Sequence< beans::PropertyValue > aProps = GetDocServiceDefaultFilter();
+ if ( aProps.hasElements() )
+ {
+ ::comphelper::SequenceAsHashMap aFiltHM( aProps );
+ SfxFilterFlags nFlags = static_cast<SfxFilterFlags>(aFiltHM.getUnpackedValueOrDefault("Flags",
+ sal_Int32(0) ));
+ if ( ( ( nFlags & nMust ) == nMust ) && !( nFlags & nDont ) )
+ aFilterProps = aProps;
+ }
+
+ return aFilterProps;
+}
+
+
+uno::Sequence< beans::PropertyValue > ModelData_Impl::GetDocServiceAnyFilter( SfxFilterFlags nMust, SfxFilterFlags nDont )
+{
+ uno::Sequence< beans::NamedValue > aSearchRequest { { "DocumentService", css::uno::Any(GetDocServiceName()) } };
+
+ return ::comphelper::MimeConfigurationHelper::SearchForFilter( m_pOwner->GetFilterQuery(), aSearchRequest, nMust, nDont );
+}
+
+
+uno::Sequence< beans::PropertyValue > ModelData_Impl::GetPreselectedFilter_Impl( sal_Int16 nStoreMode )
+{
+ if ( nStoreMode == SAVEASREMOTE_REQUESTED )
+ nStoreMode = SAVEAS_REQUESTED;
+
+ uno::Sequence< beans::PropertyValue > aFilterProps;
+
+ SfxFilterFlags nMust = getMustFlags( nStoreMode );
+ SfxFilterFlags nDont = getDontFlags( nStoreMode );
+
+ if ( ( nStoreMode != SAVEASREMOTE_REQUESTED ) && ( nStoreMode & PDFEXPORT_REQUESTED ) )
+ {
+ // Preselect PDF-Filter for EXPORT
+ uno::Sequence< beans::NamedValue > aSearchRequest
+ {
+ { "Type", css::uno::Any(OUString("pdf_Portable_Document_Format")) },
+ { "DocumentService", css::uno::Any(GetDocServiceName()) }
+ };
+
+ aFilterProps = ::comphelper::MimeConfigurationHelper::SearchForFilter( m_pOwner->GetFilterQuery(), aSearchRequest, nMust, nDont );
+ }
+ else if ( ( nStoreMode != SAVEASREMOTE_REQUESTED ) && ( nStoreMode & EPUBEXPORT_REQUESTED ) )
+ {
+ // Preselect EPUB filter for export.
+ uno::Sequence<beans::NamedValue> aSearchRequest
+ {
+ { "Type", css::uno::Any(OUString("writer_EPUB_Document")) },
+ { "DocumentService", css::uno::Any(GetDocServiceName()) }
+ };
+
+ aFilterProps = ::comphelper::MimeConfigurationHelper::SearchForFilter( m_pOwner->GetFilterQuery(), aSearchRequest, nMust, nDont );
+ }
+ else
+ {
+ aFilterProps = GetDocServiceDefaultFilterCheckFlags( nMust, nDont );
+
+ if ( !aFilterProps.hasElements() )
+ {
+ // the default filter was not found, use just the first acceptable one
+ aFilterProps = GetDocServiceAnyFilter( nMust, nDont );
+ }
+ }
+
+ return aFilterProps;
+}
+
+
+bool ModelData_Impl::ExecuteFilterDialog_Impl( const OUString& aFilterName )
+{
+ bool bDialogUsed = false;
+
+ try {
+ uno::Sequence < beans::PropertyValue > aProps;
+ uno::Any aAny = m_pOwner->GetFilterConfiguration()->getByName( aFilterName );
+ if ( aAny >>= aProps )
+ {
+ auto pProp = std::find_if(std::cbegin(aProps), std::cend(aProps),
+ [](const beans::PropertyValue& rProp) { return rProp.Name == "UIComponent"; });
+ if (pProp != std::cend(aProps))
+ {
+ OUString aServiceName;
+ pProp->Value >>= aServiceName;
+ if( !aServiceName.isEmpty() )
+ {
+ uno::Sequence<uno::Any> aDialogArgs(comphelper::InitAnyPropertySequence(
+ {
+ {"ParentWindow", uno::Any(SfxStoringHelper::GetModelXWindow(m_xModel))},
+ }));
+
+ uno::Reference< ui::dialogs::XExecutableDialog > xFilterDialog(
+ comphelper::getProcessServiceFactory()->createInstanceWithArguments(aServiceName, aDialogArgs), uno::UNO_QUERY );
+ uno::Reference< beans::XPropertyAccess > xFilterProperties( xFilterDialog, uno::UNO_QUERY );
+
+ if( xFilterDialog.is() && xFilterProperties.is() )
+ {
+ bDialogUsed = true;
+
+ uno::Reference< document::XExporter > xExporter( xFilterDialog, uno::UNO_QUERY );
+ if( xExporter.is() )
+ xExporter->setSourceDocument( GetModel() );
+
+ uno::Sequence< beans::PropertyValue > aPropsForDialog;
+ GetMediaDescr() >> aPropsForDialog;
+ xFilterProperties->setPropertyValues( aPropsForDialog );
+
+ if( !xFilterDialog->execute() )
+ {
+ throw task::ErrorCodeIOException(
+ ("ModelData_Impl::ExecuteFilterDialog_Impl:"
+ " ERRCODE_IO_ABORT"),
+ uno::Reference< uno::XInterface >(),
+ sal_uInt32(ERRCODE_IO_ABORT));
+ }
+
+ const uno::Sequence< beans::PropertyValue > aPropsFromDialog =
+ xFilterProperties->getPropertyValues();
+ for ( const auto& rProp : aPropsFromDialog )
+ GetMediaDescr()[rProp.Name] = rProp.Value;
+ }
+ }
+ }
+ }
+ }
+ catch( const container::NoSuchElementException& e )
+ {
+ // the filter name is unknown
+ throw task::ErrorCodeIOException(
+ ("ModelData_Impl::ExecuteFilterDialog_Impl: NoSuchElementException"
+ " \"" + e.Message + "\": ERRCODE_IO_ABORT"),
+ uno::Reference< uno::XInterface >(), sal_uInt32(ERRCODE_IO_INVALIDPARAMETER));
+ }
+ catch( const task::ErrorCodeIOException& )
+ {
+ throw;
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("sfx.doc", "ignoring");
+ }
+
+ return bDialogUsed;
+}
+
+
+sal_Int8 ModelData_Impl::CheckSaveAcceptable( sal_Int8 nCurStatus )
+{
+ sal_Int8 nResult = nCurStatus;
+
+ if ( nResult != STATUS_NO_ACTION && GetStorable()->hasLocation() )
+ {
+ // the saving is acceptable
+ // in case the configuration entry is not set or set to false
+ // or in case of version creation
+ if ( officecfg::Office::Common::Save::Document::AlwaysSaveAs::get()
+ && GetMediaDescr().find( OUString("VersionComment") ) == GetMediaDescr().end() )
+ {
+ // notify the user that SaveAs is going to be done
+ std::unique_ptr<weld::MessageDialog> xMessageBox(Application::CreateMessageDialog(SfxStoringHelper::GetModelWindow(m_xModel),
+ VclMessageType::Question, VclButtonsType::OkCancel, SfxResId(STR_NEW_FILENAME_SAVE)));
+ if (xMessageBox->run() == RET_OK)
+ nResult = STATUS_SAVEAS;
+ else
+ nResult = STATUS_NO_ACTION;
+ }
+ }
+
+ return nResult;
+}
+
+
+sal_Int8 ModelData_Impl::CheckStateForSave()
+{
+ // if the document is readonly or a new one a SaveAs operation must be used
+ if ( !GetStorable()->hasLocation() || GetStorable()->isReadonly() )
+ return STATUS_SAVEAS;
+
+ // check acceptable entries for media descriptor
+ ::comphelper::SequenceAsHashMap aAcceptedArgs;
+
+ static const OUStringLiteral aVersionCommentString(u"VersionComment");
+ static const OUStringLiteral aAuthorString(u"Author");
+ static const OUStringLiteral aDontTerminateEdit(u"DontTerminateEdit");
+ static const OUStringLiteral aInteractionHandlerString(u"InteractionHandler");
+ static const OUStringLiteral aStatusIndicatorString(u"StatusIndicator");
+ static const OUStringLiteral aFailOnWarningString(u"FailOnWarning");
+ static const OUStringLiteral aNoFileSync(u"NoFileSync");
+
+ if ( GetMediaDescr().find( aVersionCommentString ) != GetMediaDescr().end() )
+ aAcceptedArgs[ aVersionCommentString ] = GetMediaDescr()[ aVersionCommentString ];
+ if ( GetMediaDescr().find( aAuthorString ) != GetMediaDescr().end() )
+ aAcceptedArgs[ aAuthorString ] = GetMediaDescr()[ aAuthorString ];
+ if ( GetMediaDescr().find( aDontTerminateEdit ) != GetMediaDescr().end() )
+ aAcceptedArgs[ aDontTerminateEdit ] = GetMediaDescr()[ aDontTerminateEdit ];
+ if ( GetMediaDescr().find( aInteractionHandlerString ) != GetMediaDescr().end() )
+ aAcceptedArgs[ aInteractionHandlerString ] = GetMediaDescr()[ aInteractionHandlerString ];
+ if ( GetMediaDescr().find( aStatusIndicatorString ) != GetMediaDescr().end() )
+ aAcceptedArgs[ aStatusIndicatorString ] = GetMediaDescr()[ aStatusIndicatorString ];
+ if ( GetMediaDescr().find( aFailOnWarningString ) != GetMediaDescr().end() )
+ aAcceptedArgs[ aFailOnWarningString ] = GetMediaDescr()[ aFailOnWarningString ];
+ if (GetMediaDescr().find(aNoFileSync) != GetMediaDescr().end())
+ aAcceptedArgs[aNoFileSync] = GetMediaDescr()[aNoFileSync];
+
+ // remove unacceptable entry if there is any
+ DBG_ASSERT( GetMediaDescr().size() == aAcceptedArgs.size(),
+ "Unacceptable parameters are provided in Save request!\n" );
+ if ( GetMediaDescr().size() != aAcceptedArgs.size() )
+ GetMediaDescr() = aAcceptedArgs;
+
+ // check that the old filter is acceptable
+ return CheckFilter( GetDocProps().getUnpackedValueOrDefault(aFilterNameString, OUString()) );
+}
+
+sal_Int8 ModelData_Impl::CheckFilter( const OUString& aFilterName )
+{
+ ::comphelper::SequenceAsHashMap aFiltPropsHM;
+ SfxFilterFlags nFiltFlags = SfxFilterFlags::NONE;
+ if ( !aFilterName.isEmpty() )
+ {
+ // get properties of filter
+ uno::Sequence< beans::PropertyValue > aFilterProps;
+ m_pOwner->GetFilterConfiguration()->getByName( aFilterName ) >>= aFilterProps;
+
+ aFiltPropsHM = ::comphelper::SequenceAsHashMap( aFilterProps );
+ nFiltFlags = static_cast<SfxFilterFlags>(aFiltPropsHM.getUnpackedValueOrDefault("Flags", sal_Int32(0) ));
+ }
+
+ // only a temporary solution until default filter retrieving feature is implemented
+ // then GetDocServiceDefaultFilter() must be used
+ ::comphelper::SequenceAsHashMap aDefFiltPropsHM = GetDocServiceDefaultFilterCheckFlags( SfxFilterFlags::IMPORT | SfxFilterFlags::EXPORT, SfxFilterFlags::NONE );
+ SfxFilterFlags nDefFiltFlags = static_cast<SfxFilterFlags>(aDefFiltPropsHM.getUnpackedValueOrDefault("Flags", sal_Int32(0) ));
+
+ bool bAsk = false;
+
+ // if the old filter is not acceptable
+ // and there is no default filter or it is not acceptable for requested parameters then proceed with saveAs
+ if ( ( aFiltPropsHM.empty() || !( nFiltFlags & SfxFilterFlags::EXPORT ) )
+ && ( aDefFiltPropsHM.empty() || !( nDefFiltFlags & SfxFilterFlags::EXPORT ) || nDefFiltFlags & SfxFilterFlags::INTERNAL ) )
+ return STATUS_SAVEAS;
+
+ // so at this point there is either an acceptable old filter or default one
+ if ( aFiltPropsHM.empty() || !( nFiltFlags & SfxFilterFlags::EXPORT ) )
+ {
+ // so the default filter must be acceptable
+ return STATUS_SAVEAS_STANDARDNAME;
+ }
+ else if ( ( !( nFiltFlags & SfxFilterFlags::OWN ) || ( nFiltFlags & SfxFilterFlags::ALIEN ) )
+ && !aDefFiltPropsHM.empty()
+ && ( nDefFiltFlags & SfxFilterFlags::EXPORT ) && !( nDefFiltFlags & SfxFilterFlags::INTERNAL ))
+ {
+ bAsk = true;
+ }
+
+ // check if EncryptionData supports this output format
+ {
+ OUString aSupportedFilters;
+ const ::comphelper::SequenceAsHashMap& rDocumentProperties = GetDocProps();
+ const css::uno::Sequence<css::beans::NamedValue> aEncryptionData = rDocumentProperties.getUnpackedValueOrDefault("EncryptionData", css::uno::Sequence<css::beans::NamedValue>());
+ if (aEncryptionData != css::uno::Sequence<css::beans::NamedValue>())
+ {
+ for (const css::beans::NamedValue& aNamedValue : aEncryptionData)
+ {
+ if (aNamedValue.Name == "SupportedFilters")
+ {
+ aNamedValue.Value >>= aSupportedFilters;
+ }
+ }
+ }
+
+ // if 'SupportedFilters' is empty assume that all filters are supported.
+ if (!aSupportedFilters.isEmpty())
+ {
+ const OUString aSelectedFilter = aFiltPropsHM.getUnpackedValueOrDefault("UIName", OUString());
+
+ aSupportedFilters = ";" + aSupportedFilters + ";";
+ const OUString aSearchToken = ";" + aSelectedFilter + ";";
+ bAsk = (aSupportedFilters.indexOf(aSearchToken) < 0);
+ }
+ }
+
+ if (bAsk)
+ {
+ // the default filter is acceptable and the old filter is alien one
+ // so ask to make a saveAs operation
+ const OUString aUIName = aFiltPropsHM.getUnpackedValueOrDefault("UIName", OUString() );
+ const OUString aDefUIName = aDefFiltPropsHM.getUnpackedValueOrDefault("UIName", OUString() );
+ const OUString aPreusedFilterName = GetDocProps().getUnpackedValueOrDefault("PreusedFilterName", OUString() );
+ const OUString aDefType = aDefFiltPropsHM.getUnpackedValueOrDefault( "Type", OUString() );
+ const OUString aDefExtension = GetRecommendedExtension( aDefType );
+
+ if ( aPreusedFilterName != aFilterName && aUIName != aDefUIName )
+ {
+ if ( !SfxStoringHelper::WarnUnacceptableFormat( GetModel(), aUIName, aDefExtension,
+ static_cast<bool>( nDefFiltFlags & SfxFilterFlags::ALIEN ) ) )
+ return STATUS_SAVEAS_STANDARDNAME;
+ }
+ }
+
+ return STATUS_SAVE;
+}
+
+
+bool ModelData_Impl::CheckFilterOptionsDialogExistence()
+{
+ uno::Sequence< beans::NamedValue > aSearchRequest { { "DocumentService", css::uno::Any(GetDocServiceName()) } };
+
+ uno::Reference< container::XEnumeration > xFilterEnum =
+ m_pOwner->GetFilterQuery()->createSubSetEnumerationByProperties( aSearchRequest );
+
+ while ( xFilterEnum->hasMoreElements() )
+ {
+ uno::Sequence< beans::PropertyValue > aProps;
+ if ( xFilterEnum->nextElement() >>= aProps )
+ {
+ ::comphelper::SequenceAsHashMap aPropsHM( aProps );
+ if ( !aPropsHM.getUnpackedValueOrDefault("UIComponent", OUString()).isEmpty() )
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+bool ModelData_Impl::OutputFileDialog( sal_Int16 nStoreMode,
+ const ::comphelper::SequenceAsHashMap& aPreselectedFilterPropsHM,
+ bool bSetStandardName,
+ OUString& aSuggestedName,
+ bool bPreselectPassword,
+ OUString& aSuggestedDir,
+ sal_Int16 nDialog,
+ const OUString& rStandardDir,
+ const css::uno::Sequence< OUString >& rDenyList)
+{
+ if ( nStoreMode == SAVEASREMOTE_REQUESTED )
+ nStoreMode = SAVEAS_REQUESTED;
+
+ bool bUseFilterOptions = false;
+
+ ::comphelper::SequenceAsHashMap::const_iterator aOverwriteIter =
+ GetMediaDescr().find( OUString("Overwrite") );
+
+ // the file name must be specified if overwrite option is set
+ if ( aOverwriteIter != GetMediaDescr().end() )
+ throw task::ErrorCodeIOException(
+ "ModelData_Impl::OutputFileDialog: ERRCODE_IO_INVALIDPARAMETER",
+ uno::Reference< uno::XInterface >(),
+ sal_uInt32(ERRCODE_IO_INVALIDPARAMETER));
+
+ // no target file name is specified
+ // we need to show the file dialog
+
+ // check if we have a filter which allows for filter options, so we need a corresponding checkbox in the dialog
+ bool bAllowOptions = false;
+
+ // in case of Export, filter options dialog is used if available
+ if( !( nStoreMode & EXPORT_REQUESTED ) || ( nStoreMode & WIDEEXPORT_REQUESTED ) )
+ bAllowOptions = CheckFilterOptionsDialogExistence();
+
+ // get the filename by dialog ...
+ // create the file dialog
+ sal_Int16 aDialogMode = bAllowOptions
+ ? css::ui::dialogs::TemplateDescription::FILESAVE_AUTOEXTENSION_PASSWORD_FILTEROPTIONS
+ : css::ui::dialogs::TemplateDescription::FILESAVE_AUTOEXTENSION_PASSWORD;
+ FileDialogFlags aDialogFlags = FileDialogFlags::NONE;
+
+ if( ( nStoreMode & EXPORT_REQUESTED ) && !( nStoreMode & WIDEEXPORT_REQUESTED ) )
+ {
+ if ( (nStoreMode & PDFEXPORT_REQUESTED) || (nStoreMode & EPUBEXPORT_REQUESTED) )
+ aDialogMode = css::ui::dialogs::TemplateDescription::
+ FILESAVE_AUTOEXTENSION;
+ else
+ aDialogMode = css::ui::dialogs::TemplateDescription::
+ FILESAVE_AUTOEXTENSION_SELECTION;
+ aDialogFlags = FileDialogFlags::Export;
+ }
+
+ if( ( nStoreMode & EXPORT_REQUESTED ) && ( nStoreMode & SAVEACOPY_REQUESTED ) && ( nStoreMode & WIDEEXPORT_REQUESTED ) )
+ {
+ aDialogFlags = FileDialogFlags::SaveACopy;
+ }
+
+ std::unique_ptr<sfx2::FileDialogHelper> pFileDlg;
+
+ const OUString aDocServiceName {GetDocServiceName()};
+ DBG_ASSERT( !aDocServiceName.isEmpty(), "No document service for this module set!" );
+
+ SfxFilterFlags nMust = getMustFlags( nStoreMode );
+ SfxFilterFlags nDont = getDontFlags( nStoreMode );
+ weld::Window* pFrameWin = SfxStoringHelper::GetModelWindow(m_xModel);
+ if ( ( nStoreMode & EXPORT_REQUESTED ) && !( nStoreMode & WIDEEXPORT_REQUESTED ) )
+ {
+ if ( ( nStoreMode & PDFEXPORT_REQUESTED ) && !aPreselectedFilterPropsHM.empty() )
+ {
+ // this is a PDF export
+ // the filter options has been shown already
+ const OUString aFilterUIName = aPreselectedFilterPropsHM.getUnpackedValueOrDefault( "UIName", OUString() );
+ pFileDlg.reset(new sfx2::FileDialogHelper( aDialogMode, aDialogFlags, aFilterUIName, u"pdf", rStandardDir, rDenyList, pFrameWin ));
+ pFileDlg->SetCurrentFilter( aFilterUIName );
+ }
+ else if ((nStoreMode & EPUBEXPORT_REQUESTED) && !aPreselectedFilterPropsHM.empty())
+ {
+ // This is an EPUB export, the filter options has been shown already.
+ const OUString aFilterUIName = aPreselectedFilterPropsHM.getUnpackedValueOrDefault( "UIName", OUString() );
+ pFileDlg.reset(new sfx2::FileDialogHelper(aDialogMode, aDialogFlags, aFilterUIName, u"epub", rStandardDir, rDenyList, pFrameWin));
+ pFileDlg->SetCurrentFilter(aFilterUIName);
+ }
+ else
+ {
+ // This is the normal dialog
+ pFileDlg.reset(new sfx2::FileDialogHelper( aDialogMode, aDialogFlags, aDocServiceName, nDialog, nMust, nDont, rStandardDir, rDenyList, pFrameWin ));
+ }
+
+ sfx2::FileDialogHelper::Context eCtxt = sfx2::FileDialogHelper::UnknownContext;
+ if ( aDocServiceName == "com.sun.star.drawing.DrawingDocument" )
+ eCtxt = sfx2::FileDialogHelper::DrawExport;
+ else if ( aDocServiceName == "com.sun.star.presentation.PresentationDocument" )
+ eCtxt = sfx2::FileDialogHelper::ImpressExport;
+ else if ( aDocServiceName == "com.sun.star.text.TextDocument" )
+ eCtxt = sfx2::FileDialogHelper::WriterExport;
+ else if ( aDocServiceName == "com.sun.star.sheet.SpreadsheetDocument" )
+ eCtxt = sfx2::FileDialogHelper::CalcExport;
+
+ if ( eCtxt != sfx2::FileDialogHelper::UnknownContext )
+ pFileDlg->SetContext( eCtxt );
+
+ pFileDlg->CreateMatcher( aDocServiceName );
+
+ uno::Reference< ui::dialogs::XFilePicker3 > xFilePicker = pFileDlg->GetFilePicker();
+ uno::Reference< ui::dialogs::XFilePickerControlAccess > xControlAccess( xFilePicker, uno::UNO_QUERY );
+
+ if ( xControlAccess.is() )
+ {
+ xControlAccess->setLabel( ui::dialogs::CommonFilePickerElementIds::PUSHBUTTON_OK, SfxResId(STR_EXPORTBUTTON) );
+ xControlAccess->setLabel( ui::dialogs::CommonFilePickerElementIds::LISTBOX_FILTER_LABEL, SfxResId(STR_LABEL_FILEFORMAT) );
+ }
+ }
+ else
+ {
+ // This is the normal save as dialog
+ pFileDlg.reset(new sfx2::FileDialogHelper( aDialogMode, aDialogFlags, aDocServiceName, nDialog,
+ nMust, nDont, rStandardDir, rDenyList, pFrameWin ));
+ pFileDlg->CreateMatcher( aDocServiceName );
+
+ sfx2::FileDialogHelper::Context eCtxt = sfx2::FileDialogHelper::UnknownContext;
+ if ( aDocServiceName == "com.sun.star.drawing.DrawingDocument" )
+ eCtxt = sfx2::FileDialogHelper::DrawSaveAs;
+ else if ( aDocServiceName == "com.sun.star.presentation.PresentationDocument" )
+ eCtxt = sfx2::FileDialogHelper::ImpressSaveAs;
+ else if ( aDocServiceName == "com.sun.star.text.TextDocument" )
+ eCtxt = sfx2::FileDialogHelper::WriterSaveAs;
+ else if ( aDocServiceName == "com.sun.star.sheet.SpreadsheetDocument" )
+ eCtxt = sfx2::FileDialogHelper::CalcSaveAs;
+
+ if ( eCtxt != sfx2::FileDialogHelper::UnknownContext )
+ pFileDlg->SetContext( eCtxt );
+ }
+
+ OUString aAdjustToType;
+
+ const OUString sFilterNameString(aFilterNameString);
+
+ if ( ( nStoreMode & EXPORT_REQUESTED ) && !( nStoreMode & WIDEEXPORT_REQUESTED ) )
+ {
+ // it is export, set the preselected filter
+ pFileDlg->SetCurrentFilter( aPreselectedFilterPropsHM.getUnpackedValueOrDefault( "UIName", OUString() ) );
+ aAdjustToType = aPreselectedFilterPropsHM.getUnpackedValueOrDefault( "Type", OUString() );
+ }
+ // it is no export, bSetStandardName == true means that user agreed to store document in the default (default default ;-)) format
+ else if ( bSetStandardName || GetStorable()->hasLocation() )
+ {
+ uno::Sequence< beans::PropertyValue > aOldFilterProps;
+ const OUString aOldFilterName = GetDocProps().getUnpackedValueOrDefault( sFilterNameString, OUString() );
+
+ if ( !aOldFilterName.isEmpty() )
+ m_pOwner->GetFilterConfiguration()->getByName( aOldFilterName ) >>= aOldFilterProps;
+
+ ::comphelper::SequenceAsHashMap aOldFiltPropsHM( aOldFilterProps );
+ SfxFilterFlags nOldFiltFlags = static_cast<SfxFilterFlags>(aOldFiltPropsHM.getUnpackedValueOrDefault("Flags", sal_Int32(0) ));
+
+ if ( bSetStandardName || ( nOldFiltFlags & nMust ) != nMust || bool(nOldFiltFlags & nDont) )
+ {
+ // the suggested type will be changed, the extension should be adjusted
+ aAdjustToType = aPreselectedFilterPropsHM.getUnpackedValueOrDefault( "Type", OUString() );
+ pFileDlg->SetCurrentFilter( aPreselectedFilterPropsHM.getUnpackedValueOrDefault( "UIName", OUString() ) );
+ }
+ else
+ {
+ pFileDlg->SetCurrentFilter( aOldFiltPropsHM.getUnpackedValueOrDefault(
+ "UIName",
+ OUString() ) );
+ }
+ }
+
+ const OUString aRecommendedDir {GetRecommendedDir( aSuggestedDir )};
+ if ( !aRecommendedDir.isEmpty() )
+ pFileDlg->SetDisplayFolder( aRecommendedDir );
+ const OUString aRecommendedName {GetRecommendedName( aSuggestedName, aAdjustToType )};
+ if ( !aRecommendedName.isEmpty() )
+ pFileDlg->SetFileName( aRecommendedName );
+
+ uno::Reference < view::XSelectionSupplier > xSel( GetModel()->getCurrentController(), uno::UNO_QUERY );
+ if ( xSel.is() && xSel->getSelection().hasValue() )
+ GetMediaDescr()[OUString("SelectionOnly")] <<= true;
+
+ // This is a temporary hardcoded solution must be removed when
+ // dialogs do not need parameters in SidSet representation any more
+ sal_uInt16 nSlotID = getSlotIDFromMode( nStoreMode );
+ if ( !nSlotID )
+ throw lang::IllegalArgumentException(); // TODO:
+
+ // generate SidSet from MediaDescriptor and provide it into FileDialog
+ // than merge changed SidSet back
+ std::optional<SfxAllItemSet> pDialogParams( SfxGetpApp()->GetPool() );
+ TransformParameters( nSlotID,
+ GetMediaDescr().getAsConstPropertyValueList(),
+ *pDialogParams );
+
+ if ( bPreselectPassword && !pDialogParams->HasItem( SID_ENCRYPTIONDATA ) )
+ {
+ // the file dialog preselects the password checkbox if the provided mediadescriptor has encryption data entry
+ // after dialog execution the password interaction flag will be either removed or not
+ pDialogParams->Put( SfxBoolItem( SID_PASSWORDINTERACTION, true ) );
+ }
+
+ // aFilterName is a pure output parameter, pDialogParams is an in/out parameter
+ OUString aFilterName;
+ if ( pFileDlg->Execute( pDialogParams, aFilterName ) != ERRCODE_NONE )
+ {
+ throw task::ErrorCodeIOException(
+ "ModelData_Impl::OutputFileDialog: ERRCODE_IO_ABORT",
+ uno::Reference< uno::XInterface >(), sal_uInt32(ERRCODE_IO_ABORT));
+ }
+
+ // the following two arguments can not be converted in MediaDescriptor,
+ // so they should be removed from the ItemSet after retrieving
+ const SfxBoolItem* pRecommendReadOnly = SfxItemSet::GetItem<SfxBoolItem>(&*pDialogParams, SID_RECOMMENDREADONLY, false);
+ m_bRecommendReadOnly = ( pRecommendReadOnly && pRecommendReadOnly->GetValue() );
+ pDialogParams->ClearItem( SID_RECOMMENDREADONLY );
+
+ uno::Sequence< beans::PropertyValue > aPropsFromDialog;
+ TransformItems( nSlotID, *pDialogParams, aPropsFromDialog );
+ GetMediaDescr() << aPropsFromDialog;
+
+ // get the path from the dialog
+ INetURLObject aURL( pFileDlg->GetPath() );
+ // the path should be provided outside since it might be used for further calls to the dialog
+ aSuggestedName = aURL.GetLastName(INetURLObject::DecodeMechanism::WithCharset);
+ aSuggestedDir = pFileDlg->GetDisplayDirectory();
+
+ // old filter options should be cleared in case different filter is used
+
+ const OUString aFilterFromMediaDescr = GetMediaDescr().getUnpackedValueOrDefault( sFilterNameString, OUString() );
+ const OUString aOldFilterName = GetDocProps().getUnpackedValueOrDefault( sFilterNameString, OUString() );
+
+ const OUString sFilterOptionsString(aFilterOptionsString);
+ const OUString sFilterDataString(aFilterDataString);
+
+ if ( aFilterName == aFilterFromMediaDescr )
+ {
+ // preserve current settings if any
+ // if there no current settings and the name is the same
+ // as old filter name use old filter settings
+
+ if ( aFilterFromMediaDescr == aOldFilterName )
+ {
+ ::comphelper::SequenceAsHashMap::const_iterator aIter =
+ GetDocProps().find( sFilterOptionsString );
+ if ( aIter != GetDocProps().end()
+ && GetMediaDescr().find( sFilterOptionsString ) == GetMediaDescr().end() )
+ GetMediaDescr()[aIter->first] = aIter->second;
+
+ aIter = GetDocProps().find( sFilterDataString );
+ if ( aIter != GetDocProps().end()
+ && GetMediaDescr().find( sFilterDataString ) == GetMediaDescr().end() )
+ GetMediaDescr()[aIter->first] = aIter->second;
+ }
+ }
+ else
+ {
+ GetMediaDescr().erase( sFilterDataString );
+ GetMediaDescr().erase( sFilterOptionsString );
+
+ if ( aFilterName == aOldFilterName )
+ {
+ // merge filter option of the document filter
+
+ ::comphelper::SequenceAsHashMap::const_iterator aIter =
+ GetDocProps().find( sFilterOptionsString );
+ if ( aIter != GetDocProps().end() )
+ GetMediaDescr()[aIter->first] = aIter->second;
+
+ aIter = GetDocProps().find( sFilterDataString );
+ if ( aIter != GetDocProps().end() )
+ GetMediaDescr()[aIter->first] = aIter->second;
+ }
+ }
+
+ uno::Reference< ui::dialogs::XFilePickerControlAccess > xExtFileDlg( pFileDlg->GetFilePicker(), uno::UNO_QUERY );
+ if ( xExtFileDlg.is() )
+ {
+ if ( SfxStoringHelper::CheckFilterOptionsAppearance( m_pOwner->GetFilterConfiguration(), aFilterName ) )
+ bUseFilterOptions = true;
+
+ if ( ( !( nStoreMode & EXPORT_REQUESTED ) || ( nStoreMode & WIDEEXPORT_REQUESTED ) ) && bUseFilterOptions )
+ {
+ try
+ {
+ // for exporters: always show dialog if format uses options
+ // for save: show dialog if format uses options and no options given or if forced by user
+ uno::Any aVal =
+ xExtFileDlg->getValue( ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_FILTEROPTIONS, 0 );
+
+ aVal >>= bUseFilterOptions;
+ if ( !bUseFilterOptions )
+ bUseFilterOptions =
+ ( GetMediaDescr().find( sFilterDataString ) == GetMediaDescr().end()
+ && GetMediaDescr().find( sFilterOptionsString ) == GetMediaDescr().end() );
+ }
+ catch( const lang::IllegalArgumentException& )
+ {}
+ }
+ }
+
+ // merge in results of the dialog execution
+ GetMediaDescr()[OUString("URL")] <<= aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE );
+ GetMediaDescr()[sFilterNameString] <<= aFilterName;
+
+ return bUseFilterOptions;
+}
+
+
+bool ModelData_Impl::ShowDocumentInfoDialog()
+{
+ bool bDialogUsed = false;
+
+ try {
+ uno::Reference< frame::XController > xController = GetModel()->getCurrentController();
+ if ( xController.is() )
+ {
+ uno::Reference< frame::XDispatchProvider > xFrameDispatch( xController->getFrame(), uno::UNO_QUERY );
+ if ( xFrameDispatch.is() )
+ {
+ util::URL aURL;
+ aURL.Complete = ".uno:SetDocumentProperties";
+
+ uno::Reference < util::XURLTransformer > xTransformer( util::URLTransformer::create( comphelper::getProcessComponentContext() ) );
+ if ( xTransformer->parseStrict( aURL ) )
+ {
+ uno::Reference< frame::XDispatch > xDispatch = xFrameDispatch->queryDispatch(
+ aURL,
+ "_self",
+ 0 );
+ if ( xDispatch.is() )
+ {
+ // tdf#119206 use (abuse?) a SynchronMode of true,
+ // which will become SfxRequest::IsSynchronCall of true
+ // in SfxObjectShell::ExecFile_Impl to request that we
+ // do not want the properties dialog to be run async
+ uno::Sequence< beans::PropertyValue > aProperties{
+ comphelper::makePropertyValue("SynchronMode", true)
+ };
+ xDispatch->dispatch(aURL, aProperties);
+ bDialogUsed = true;
+ }
+ }
+ }
+ }
+ }
+ catch ( const uno::Exception& )
+ {
+ }
+
+ return bDialogUsed;
+}
+
+
+OUString ModelData_Impl::GetRecommendedExtension( const OUString& aTypeName )
+{
+ if ( aTypeName.isEmpty() )
+ return OUString();
+
+ uno::Reference< container::XNameAccess > xTypeDetection(
+ comphelper::getProcessServiceFactory()->createInstance("com.sun.star.document.TypeDetection"),
+ uno::UNO_QUERY );
+ if ( xTypeDetection.is() )
+ {
+ uno::Sequence< beans::PropertyValue > aTypeNameProps;
+ if ( ( xTypeDetection->getByName( aTypeName ) >>= aTypeNameProps ) && aTypeNameProps.hasElements() )
+ {
+ ::comphelper::SequenceAsHashMap aTypeNamePropsHM( aTypeNameProps );
+ uno::Sequence< OUString > aExtensions = aTypeNamePropsHM.getUnpackedValueOrDefault(
+ "Extensions",
+ ::uno::Sequence< OUString >() );
+ if ( aExtensions.hasElements() )
+ return aExtensions[0];
+ }
+ }
+
+ return OUString();
+}
+
+
+OUString ModelData_Impl::GetRecommendedDir( const OUString& aSuggestedDir )
+{
+ if ( ( !aSuggestedDir.isEmpty() || GetStorable()->hasLocation() )
+ && !GetMediaDescr().getUnpackedValueOrDefault("RepairPackage", false ) )
+ {
+ INetURLObject aLocation;
+ if ( !aSuggestedDir.isEmpty() )
+ aLocation = INetURLObject( aSuggestedDir );
+ else
+ {
+ const OUString aOldURL = GetStorable()->getLocation();
+ if ( !aOldURL.isEmpty() )
+ {
+ INetURLObject aTmp( aOldURL );
+ if ( aTmp.removeSegment() )
+ aLocation = aTmp;
+ }
+
+ if ( aLocation.HasError() )
+ aLocation = INetURLObject();
+ }
+
+ OUString sLocationURL( aLocation.GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
+ bool bIsInTempPath( false );
+ OUString sSysTempPath;
+ if( osl::FileBase::getTempDirURL( sSysTempPath ) == osl::FileBase::E_None )
+ bIsInTempPath = !sSysTempPath.isEmpty() && sLocationURL.startsWith( sSysTempPath );
+#ifdef _WIN32
+ if( !bIsInTempPath )
+ {
+ wchar_t sPath[MAX_PATH+1];
+ HRESULT hRes = SHGetFolderPathW( nullptr, CSIDL_INTERNET_CACHE, nullptr, SHGFP_TYPE_CURRENT, sPath );
+ if( SUCCEEDED(hRes) )
+ {
+ OUString sTempINetFiles;
+ if( osl::FileBase::getFileURLFromSystemPath(OUString(o3tl::toU(sPath)), sTempINetFiles) == osl::FileBase::E_None )
+ bIsInTempPath = !sTempINetFiles.isEmpty() && sLocationURL.startsWith( sTempINetFiles );
+ }
+ }
+#endif
+ // Suggest somewhere other than the system's temp directory
+ if( bIsInTempPath )
+ aLocation = INetURLObject();
+
+ aLocation.setFinalSlash();
+ if ( !aLocation.HasError() )
+ return aLocation.GetMainURL( INetURLObject::DecodeMechanism::NONE );
+
+ return OUString();
+ }
+
+ return OUString();
+}
+
+
+OUString ModelData_Impl::GetRecommendedName( const OUString& aSuggestedName, const OUString& aTypeName )
+{
+ // the last used name might be provided by aSuggestedName from the old selection, or from the MediaDescriptor
+ if ( !aSuggestedName.isEmpty() )
+ return aSuggestedName;
+
+ OUString aRecommendedName{ INetURLObject(GetStorable()->getLocation())
+ .GetLastName(INetURLObject::DecodeMechanism::WithCharset) };
+ if ( aRecommendedName.isEmpty() )
+ {
+ try {
+ uno::Reference< frame::XTitle > xTitle( GetModel(), uno::UNO_QUERY_THROW );
+ aRecommendedName = xTitle->getTitle();
+ } catch( const uno::Exception& ) {}
+ }
+
+ if ( !aRecommendedName.isEmpty() && !aTypeName.isEmpty() )
+ {
+ // adjust the extension to the type
+ uno::Reference< container::XNameAccess > xTypeDetection(
+ comphelper::getProcessServiceFactory()->createInstance("com.sun.star.document.TypeDetection"),
+ uno::UNO_QUERY );
+ if ( xTypeDetection.is() )
+ {
+ INetURLObject aObj( rtl::OUStringConcatenation("c:/" + aRecommendedName), INetProtocol::File,
+ INetURLObject::EncodeMechanism::All, RTL_TEXTENCODING_UTF8, FSysStyle::Dos );
+
+ const OUString aExtension = GetRecommendedExtension( aTypeName );
+ if ( !aExtension.isEmpty() )
+ aObj.SetExtension( aExtension );
+
+ aRecommendedName = aObj.GetLastName(INetURLObject::DecodeMechanism::WithCharset);
+ }
+ }
+
+ return aRecommendedName;
+}
+
+
+
+
+SfxStoringHelper::SfxStoringHelper()
+{
+}
+
+
+uno::Reference< container::XNameAccess > const & SfxStoringHelper::GetFilterConfiguration()
+{
+ if ( !m_xFilterCFG.is() )
+ {
+ m_xFilterCFG.set( comphelper::getProcessServiceFactory()->createInstance("com.sun.star.document.FilterFactory"),
+ uno::UNO_QUERY_THROW );
+ }
+
+ return m_xFilterCFG;
+}
+
+
+uno::Reference< container::XContainerQuery > const & SfxStoringHelper::GetFilterQuery()
+{
+ if ( !m_xFilterQuery.is() )
+ {
+ m_xFilterQuery.set( GetFilterConfiguration(), uno::UNO_QUERY_THROW );
+ }
+
+ return m_xFilterQuery;
+}
+
+
+uno::Reference< css::frame::XModuleManager2 > const & SfxStoringHelper::GetModuleManager()
+{
+ if ( !m_xModuleManager.is() )
+ {
+ m_xModuleManager = frame::ModuleManager::create(
+ comphelper::getProcessComponentContext() );
+ }
+
+ return m_xModuleManager;
+}
+
+bool SfxStoringHelper::GUIStoreModel( const uno::Reference< frame::XModel >& xModel,
+ std::u16string_view aSlotName,
+ uno::Sequence< beans::PropertyValue >& aArgsSequence,
+ bool bPreselectPassword,
+ SignatureState nDocumentSignatureState )
+{
+ ModelData_Impl aModelData( *this, xModel, aArgsSequence );
+
+ bool bDialogUsed = false;
+
+ INetURLObject aURL;
+
+ bool bSetStandardName = false; // can be set only for SaveAs
+
+ // parse the slot name
+ bool bRemote = false;
+ sal_Int16 nStoreMode = getStoreModeFromSlotName( aSlotName );
+
+ if ( nStoreMode == SAVEASREMOTE_REQUESTED )
+ {
+ nStoreMode = SAVEAS_REQUESTED;
+ bRemote = true;
+ }
+
+ sal_Int8 nStatusSave = STATUS_NO_ACTION;
+
+ ::comphelper::SequenceAsHashMap::const_iterator aSaveACopyIter =
+ aModelData.GetMediaDescr().find( OUString("SaveACopy") );
+ if ( aSaveACopyIter != aModelData.GetMediaDescr().end() )
+ {
+ bool bSaveACopy = false;
+ aSaveACopyIter->second >>= bSaveACopy;
+ if ( bSaveACopy )
+ nStoreMode = EXPORT_REQUESTED | SAVEACOPY_REQUESTED | WIDEEXPORT_REQUESTED;
+ }
+ // handle the special cases
+ if ( nStoreMode & SAVEAS_REQUESTED )
+ {
+ ::comphelper::SequenceAsHashMap::const_iterator aSaveToIter =
+ aModelData.GetMediaDescr().find( OUString("SaveTo") );
+ if ( aSaveToIter != aModelData.GetMediaDescr().end() )
+ {
+ bool bWideExport = false;
+ aSaveToIter->second >>= bWideExport;
+ if ( bWideExport )
+ nStoreMode = EXPORT_REQUESTED | WIDEEXPORT_REQUESTED;
+ }
+
+ // if saving is not acceptable the warning must be shown even in case of SaveAs operation
+ if ( ( nStoreMode & SAVEAS_REQUESTED ) && aModelData.CheckSaveAcceptable( STATUS_SAVEAS ) == STATUS_NO_ACTION )
+ throw task::ErrorCodeIOException(
+ "SfxStoringHelper::GUIStoreModel: ERRCODE_IO_ABORT",
+ uno::Reference< uno::XInterface >(), sal_uInt32(ERRCODE_IO_ABORT));
+ }
+ else if ( nStoreMode & SAVE_REQUESTED )
+ {
+ // if saving is not acceptable by the configuration the warning must be shown
+ nStatusSave = aModelData.CheckSaveAcceptable( STATUS_SAVE );
+
+ if ( nStatusSave == STATUS_NO_ACTION )
+ throw task::ErrorCodeIOException(
+ "SfxStoringHelper::GUIStoreModel: ERRCODE_IO_ABORT",
+ uno::Reference< uno::XInterface >(), sal_uInt32(ERRCODE_IO_ABORT));
+ else if ( nStatusSave == STATUS_SAVE )
+ {
+ // check whether it is possible to use save operation
+ nStatusSave = aModelData.CheckStateForSave();
+ }
+
+ if ( nStatusSave == STATUS_NO_ACTION )
+ {
+ throw task::ErrorCodeIOException(
+ "SfxStoringHelper::GUIStoreModel: ERRCODE_IO_ABORT",
+ uno::Reference< uno::XInterface >(), sal_uInt32(ERRCODE_IO_ABORT));
+ }
+ else if ( nStatusSave != STATUS_SAVE )
+ {
+ // this should be a usual SaveAs operation
+ nStoreMode = SAVEAS_REQUESTED;
+ if ( nStatusSave == STATUS_SAVEAS_STANDARDNAME )
+ bSetStandardName = true;
+ }
+ }
+
+ if (!comphelper::LibreOfficeKit::isActive() && !( nStoreMode & EXPORT_REQUESTED ) )
+ {
+ // if it is no export, warn user that the signature will be removed
+ if ( SignatureState::OK == nDocumentSignatureState
+ || SignatureState::INVALID == nDocumentSignatureState
+ || SignatureState::NOTVALIDATED == nDocumentSignatureState
+ || SignatureState::PARTIAL_OK == nDocumentSignatureState)
+ {
+ std::unique_ptr<weld::MessageDialog> xMessageBox(Application::CreateMessageDialog(SfxStoringHelper::GetModelWindow(xModel),
+ VclMessageType::Question, VclButtonsType::YesNo, SfxResId(RID_SVXSTR_XMLSEC_QUERY_LOSINGSIGNATURE)));
+ if (xMessageBox->run() != RET_YES)
+ {
+ // the user has decided not to store the document
+ throw task::ErrorCodeIOException(
+ "SfxStoringHelper::GUIStoreModel: ERRCODE_IO_ABORT (Preserve Signature)",
+ uno::Reference< uno::XInterface >(), sal_uInt32(ERRCODE_IO_ABORT));
+ }
+ }
+ }
+
+ if ( nStoreMode & SAVE_REQUESTED && nStatusSave == STATUS_SAVE )
+ {
+ // Document properties can contain streams that should be freed before storing
+ aModelData.FreeDocumentProps();
+
+ if ( aModelData.GetStorable2().is() )
+ {
+ try
+ {
+ aModelData.GetStorable2()->storeSelf( aModelData.GetMediaDescr().getAsConstPropertyValueList() );
+ }
+ catch (const lang::IllegalArgumentException&)
+ {
+ TOOLS_WARN_EXCEPTION("sfx.doc", "Ignoring parameters! ModelData considers this illegal");
+ aModelData.GetStorable()->store();
+ }
+ }
+ else
+ {
+ OSL_FAIL( "XStorable2 is not supported by the model!" );
+ aModelData.GetStorable()->store();
+ }
+
+ return false;
+ }
+
+ // preselect a filter for the storing process
+ uno::Sequence< beans::PropertyValue > aFilterProps = aModelData.GetPreselectedFilter_Impl( nStoreMode );
+
+ DBG_ASSERT( aFilterProps.hasElements(), "No filter for storing!\n" );
+ if ( !aFilterProps.hasElements() )
+ throw task::ErrorCodeIOException(
+ "SfxStoringHelper::GUIStoreModel: ERRCODE_IO_INVALIDPARAMETER",
+ uno::Reference< uno::XInterface >(), sal_uInt32(ERRCODE_IO_INVALIDPARAMETER));
+
+ ::comphelper::SequenceAsHashMap aFilterPropsHM( aFilterProps );
+ OUString aFilterName = aFilterPropsHM.getUnpackedValueOrDefault( "Name", OUString() );
+
+ const OUString sFilterNameString(aFilterNameString);
+
+ const OUString aFilterFromMediaDescr = aModelData.GetMediaDescr().getUnpackedValueOrDefault( sFilterNameString, OUString() );
+ const OUString aOldFilterName = aModelData.GetDocProps().getUnpackedValueOrDefault( sFilterNameString, OUString() );
+
+ bool bUseFilterOptions = false;
+ ::comphelper::SequenceAsHashMap::const_iterator aFileNameIter = aModelData.GetMediaDescr().find( OUString("URL") );
+
+ const OUString sFilterOptionsString(aFilterOptionsString);
+ const OUString sFilterDataString(aFilterDataString);
+
+ bool bPDFOptions = (nStoreMode & PDFEXPORT_REQUESTED) && !(nStoreMode & PDFDIRECTEXPORT_REQUESTED);
+ bool bEPUBOptions = (nStoreMode & EPUBEXPORT_REQUESTED) && !(nStoreMode & EPUBDIRECTEXPORT_REQUESTED);
+ if ( ( nStoreMode & EXPORT_REQUESTED ) && (bPDFOptions || bEPUBOptions) )
+ {
+ // this is PDF or EPUB export, the filter options dialog should be shown before the export
+ aModelData.GetMediaDescr()[sFilterNameString] <<= aFilterName;
+ if ( aModelData.GetMediaDescr().find( "FilterFlags" ) == aModelData.GetMediaDescr().end()
+ && aModelData.GetMediaDescr().find( sFilterOptionsString ) == aModelData.GetMediaDescr().end()
+ && aModelData.GetMediaDescr().find( sFilterDataString ) == aModelData.GetMediaDescr().end() )
+ {
+ // execute filter options dialog since no options are set in the media descriptor
+ if ( aModelData.ExecuteFilterDialog_Impl( aFilterName ) )
+ bDialogUsed = true;
+ }
+ }
+
+ if ( aFileNameIter == aModelData.GetMediaDescr().end() )
+ {
+ sal_Int16 nDialog = SFX2_IMPL_DIALOG_CONFIG;
+
+ if( bRemote )
+ {
+ nDialog = SFX2_IMPL_DIALOG_REMOTE;
+ }
+ else
+ {
+ ::comphelper::SequenceAsHashMap::const_iterator aDlgIter =
+ aModelData.GetMediaDescr().find( OUString("UseSystemDialog") );
+ if ( aDlgIter != aModelData.GetMediaDescr().end() )
+ {
+ bool bUseSystemDialog = true;
+ if ( aDlgIter->second >>= bUseSystemDialog )
+ {
+ if ( bUseSystemDialog )
+ nDialog = SFX2_IMPL_DIALOG_SYSTEM;
+ else
+ nDialog = SFX2_IMPL_DIALOG_OOO;
+ }
+ }
+ }
+
+ // The Dispatch supports parameter FolderName that overwrites SuggestedSaveAsDir
+ OUString aSuggestedDir = aModelData.GetMediaDescr().getUnpackedValueOrDefault("FolderName", OUString() );
+ if ( aSuggestedDir.isEmpty() )
+ {
+ aSuggestedDir = aModelData.GetMediaDescr().getUnpackedValueOrDefault("SuggestedSaveAsDir", OUString() );
+ if ( aSuggestedDir.isEmpty() )
+ aSuggestedDir = aModelData.GetDocProps().getUnpackedValueOrDefault("SuggestedSaveAsDir", OUString() );
+ }
+
+ OUString aSuggestedName = aModelData.GetMediaDescr().getUnpackedValueOrDefault("SuggestedSaveAsName", OUString() );
+ if ( aSuggestedName.isEmpty() )
+ aSuggestedName = aModelData.GetDocProps().getUnpackedValueOrDefault("SuggestedSaveAsName", OUString() );
+
+ OUString sStandardDir;
+ ::comphelper::SequenceAsHashMap::const_iterator aStdDirIter =
+ aModelData.GetMediaDescr().find( OUString("StandardDir") );
+ if ( aStdDirIter != aModelData.GetMediaDescr().end() )
+ aStdDirIter->second >>= sStandardDir;
+
+ css::uno::Sequence< OUString > aDenyList;
+
+ ::comphelper::SequenceAsHashMap::const_iterator aDenyListIter =
+ aModelData.GetMediaDescr().find( OUString("DenyList") );
+ if ( aDenyListIter != aModelData.GetMediaDescr().end() )
+ aDenyListIter->second >>= aDenyList;
+
+ for (;;)
+ {
+ // in case the dialog is opened a second time the folder should be the same as previously navigated to by the user, not what was handed over by initial parameters
+ bUseFilterOptions = aModelData.OutputFileDialog( nStoreMode, aFilterProps, bSetStandardName, aSuggestedName, bPreselectPassword, aSuggestedDir, nDialog, sStandardDir, aDenyList );
+ if ( nStoreMode == SAVEAS_REQUESTED )
+ {
+ // in case of saving check filter for possible alien warning
+ const OUString aSelFilterName = aModelData.GetMediaDescr().getUnpackedValueOrDefault( sFilterNameString, OUString() );
+ sal_Int8 nStatusFilterSave = aModelData.CheckFilter( aSelFilterName );
+ if ( nStatusFilterSave == STATUS_SAVEAS_STANDARDNAME )
+ {
+ // switch to best filter
+ bSetStandardName = true;
+ }
+ else if ( nStatusFilterSave == STATUS_SAVE )
+ {
+ // user confirmed alien filter or "good" filter is used
+ break;
+ }
+ }
+ else
+ break;
+ }
+
+ bDialogUsed = true;
+ aFileNameIter = aModelData.GetMediaDescr().find( OUString("URL") );
+ }
+ else
+ {
+ // the target file name is provided so check if new filter options
+ // are provided or old options can be used
+ if ( aFilterFromMediaDescr == aOldFilterName )
+ {
+ ::comphelper::SequenceAsHashMap::const_iterator aIter =
+ aModelData.GetDocProps().find( sFilterOptionsString );
+ if ( aIter != aModelData.GetDocProps().end()
+ && aModelData.GetMediaDescr().find( sFilterOptionsString ) == aModelData.GetMediaDescr().end() )
+ aModelData.GetMediaDescr()[aIter->first] = aIter->second;
+
+ aIter = aModelData.GetDocProps().find( sFilterDataString );
+ if ( aIter != aModelData.GetDocProps().end()
+ && aModelData.GetMediaDescr().find( sFilterDataString ) == aModelData.GetMediaDescr().end() )
+ aModelData.GetMediaDescr()[aIter->first] = aIter->second;
+ }
+ }
+
+ if ( aFileNameIter != aModelData.GetMediaDescr().end() )
+ {
+ OUString aFileName;
+ aFileNameIter->second >>= aFileName;
+ aURL.SetURL( aFileName );
+ DBG_ASSERT( aURL.GetProtocol() != INetProtocol::NotValid, "Illegal URL!" );
+
+ ::comphelper::SequenceAsHashMap::const_iterator aIter =
+ aModelData.GetMediaDescr().find( sFilterNameString );
+
+ if ( aIter != aModelData.GetMediaDescr().end() )
+ aIter->second >>= aFilterName;
+ else
+ aModelData.GetMediaDescr()[sFilterNameString] <<= aFilterName;
+
+ DBG_ASSERT( !aFilterName.isEmpty(), "Illegal filter!" );
+ }
+ else
+ {
+ SAL_WARN( "sfx.doc", "This code must be unreachable!" );
+ throw task::ErrorCodeIOException(
+ "SfxStoringHelper::GUIStoreModel: ERRCODE_IO_INVALIDPARAMETER",
+ uno::Reference< uno::XInterface >(), sal_uInt32(ERRCODE_IO_INVALIDPARAMETER));
+ }
+
+ ::comphelper::SequenceAsHashMap::const_iterator aIter =
+ aModelData.GetMediaDescr().find( OUString("FilterFlags") );
+ bool bFilterFlagsSet = ( aIter != aModelData.GetMediaDescr().end() );
+
+ // check if the filter Dialog has not been called before
+ if( !( nStoreMode & PDFEXPORT_REQUESTED ) && !( nStoreMode & EPUBEXPORT_REQUESTED ) && !bFilterFlagsSet
+ && ( ( nStoreMode & EXPORT_REQUESTED ) || bUseFilterOptions ) )
+ {
+ // execute filter options dialog
+ if ( aModelData.ExecuteFilterDialog_Impl( aFilterName ) )
+ {
+ bDialogUsed = true;
+ // check if the file is a pdf or not and change the storing mode at convenience
+ if (aFilterName.endsWith("pdf_Export"))
+ nStoreMode = EXPORT_REQUESTED | PDFEXPORT_REQUESTED;
+ }
+ }
+
+ // so the arguments will not change any more and can be stored to the main location
+ aArgsSequence = aModelData.GetMediaDescr().getAsConstPropertyValueList();
+
+ // store the document and handle it's docinfo
+
+ DocumentSettingsGuard aSettingsGuard( aModelData.GetModel(), aModelData.IsRecommendReadOnly(), nStoreMode & EXPORT_REQUESTED );
+
+ OSL_ENSURE( aModelData.GetMediaDescr().find( OUString( "Password" ) ) == aModelData.GetMediaDescr().end(), "The Password property of MediaDescriptor should not be used here!" );
+ if ( officecfg::Office::Common::Save::Document::EditProperty::get()
+ && ( !aModelData.GetStorable()->hasLocation()
+ || INetURLObject( aModelData.GetStorable()->getLocation() ) != aURL ) )
+ {
+ // this is definitely not a Save operation
+ // so the document info can be updated
+
+ // on export document info must be preserved
+ uno::Reference<document::XDocumentPropertiesSupplier> xDPS(
+ aModelData.GetModel(), uno::UNO_QUERY_THROW);
+ uno::Reference<util::XCloneable> xCloneable(
+ xDPS->getDocumentProperties(), uno::UNO_QUERY_THROW);
+ uno::Reference<document::XDocumentProperties> xOldDocProps(
+ xCloneable->createClone(), uno::UNO_QUERY_THROW);
+
+ // use dispatch API to show document info dialog
+ if ( aModelData.ShowDocumentInfoDialog() )
+ bDialogUsed = true;
+ else
+ {
+ OSL_FAIL( "Can't execute document info dialog!" );
+ }
+
+ try {
+ // Document properties can contain streams that should be freed before storing
+ aModelData.FreeDocumentProps();
+ if ( nStoreMode & EXPORT_REQUESTED )
+ aModelData.GetStorable()->storeToURL( aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ), aArgsSequence );
+ else
+ aModelData.GetStorable()->storeAsURL( aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ), aArgsSequence );
+ }
+ catch( const uno::Exception& )
+ {
+ if ( nStoreMode & EXPORT_REQUESTED )
+ {
+ SetDocInfoState(aModelData.GetModel(), xOldDocProps);
+ }
+ throw;
+ }
+
+ if ( nStoreMode & EXPORT_REQUESTED )
+ {
+ SetDocInfoState(aModelData.GetModel(), xOldDocProps);
+ }
+ }
+ else
+ {
+ // Document properties can contain streams that should be freed before storing
+ aModelData.FreeDocumentProps();
+
+ // this is actually a save operation with different parameters
+ // so storeTo or storeAs without DocInfo operations are used
+ if ( nStoreMode & EXPORT_REQUESTED )
+ aModelData.GetStorable()->storeToURL( aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ), aArgsSequence );
+ else
+ aModelData.GetStorable()->storeAsURL( aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ), aArgsSequence );
+ }
+
+ // Launch PDF viewer
+ if ( nStoreMode & PDFEXPORT_REQUESTED )
+ {
+ FilterConfigItem aItem(u"Office.Common/Filter/PDF/Export/");
+ bool aViewPDF = aItem.ReadBool( "ViewPDFAfterExport", false );
+
+ if ( aViewPDF )
+ {
+ uno::Reference<XSystemShellExecute> xSystemShellExecute(SystemShellExecute::create( ::comphelper::getProcessComponentContext() ) );
+ xSystemShellExecute->execute( aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ), "", SystemShellExecuteFlags::URIS_ONLY );
+ }
+ }
+
+ return bDialogUsed;
+}
+
+
+// static
+bool SfxStoringHelper::CheckFilterOptionsAppearance(
+ const uno::Reference< container::XNameAccess >& xFilterCFG,
+ const OUString& aFilterName )
+{
+ bool bUseFilterOptions = false;
+
+ DBG_ASSERT( xFilterCFG.is(), "No filter configuration!\n" );
+ if( xFilterCFG.is() )
+ {
+ try {
+ uno::Sequence < beans::PropertyValue > aProps;
+ uno::Any aAny = xFilterCFG->getByName( aFilterName );
+ if ( aAny >>= aProps )
+ {
+ ::comphelper::SequenceAsHashMap aPropsHM( aProps );
+ if( !aPropsHM.getUnpackedValueOrDefault( "UIComponent", OUString() ).isEmpty() )
+ bUseFilterOptions = true;
+ }
+ }
+ catch( const uno::Exception& )
+ {
+ }
+ }
+
+ return bUseFilterOptions;
+}
+
+
+// static
+void SfxStoringHelper::SetDocInfoState(
+ const uno::Reference< frame::XModel >& xModel,
+ const uno::Reference< document::XDocumentProperties>& i_xOldDocProps )
+{
+ uno::Reference<document::XDocumentPropertiesSupplier> const
+ xModelDocPropsSupplier(xModel, uno::UNO_QUERY_THROW);
+ uno::Reference<document::XDocumentProperties> const xDocPropsToFill =
+ xModelDocPropsSupplier->getDocumentProperties();
+ uno::Reference< beans::XPropertySet > const xPropSet(
+ i_xOldDocProps->getUserDefinedProperties(), uno::UNO_QUERY_THROW);
+
+ uno::Reference< util::XModifiable > xModifiable( xModel, uno::UNO_QUERY );
+ if ( !xModifiable.is() )
+ throw uno::RuntimeException();
+
+ bool bIsModified = xModifiable->isModified();
+
+ try
+ {
+ uno::Reference< beans::XPropertySet > const xSet(
+ xDocPropsToFill->getUserDefinedProperties(), uno::UNO_QUERY);
+ uno::Reference< beans::XPropertyContainer > xContainer( xSet, uno::UNO_QUERY );
+ uno::Reference< beans::XPropertySetInfo > xSetInfo = xSet->getPropertySetInfo();
+ const uno::Sequence< beans::Property > lProps = xSetInfo->getProperties();
+ for (const beans::Property& rProp : lProps)
+ {
+ uno::Any aValue = xPropSet->getPropertyValue( rProp.Name );
+ if ( rProp.Attributes & css::beans::PropertyAttribute::REMOVABLE )
+ {
+ try
+ {
+ // QUESTION: DefaultValue?!
+ xContainer->addProperty( rProp.Name, rProp.Attributes, aValue );
+ }
+ catch (beans::PropertyExistException const&) {}
+ try
+ {
+ // it is possible that the propertysets from XML and binary files differ; we shouldn't break then
+ xSet->setPropertyValue( rProp.Name, aValue );
+ }
+ catch ( const uno::Exception& ) {}
+ }
+ }
+
+ // sigh... have to set these manually I'm afraid... wonder why
+ // SfxObjectShell doesn't handle this internally, should be easier
+ xDocPropsToFill->setAuthor(i_xOldDocProps->getAuthor());
+ xDocPropsToFill->setGenerator(i_xOldDocProps->getGenerator());
+ xDocPropsToFill->setCreationDate(i_xOldDocProps->getCreationDate());
+ xDocPropsToFill->setTitle(i_xOldDocProps->getTitle());
+ xDocPropsToFill->setSubject(i_xOldDocProps->getSubject());
+ xDocPropsToFill->setDescription(i_xOldDocProps->getDescription());
+ xDocPropsToFill->setKeywords(i_xOldDocProps->getKeywords());
+ xDocPropsToFill->setModifiedBy(i_xOldDocProps->getModifiedBy());
+ xDocPropsToFill->setModificationDate(i_xOldDocProps->getModificationDate());
+ xDocPropsToFill->setPrintedBy(i_xOldDocProps->getPrintedBy());
+ xDocPropsToFill->setPrintDate(i_xOldDocProps->getPrintDate());
+ xDocPropsToFill->setAutoloadURL(i_xOldDocProps->getAutoloadURL());
+ xDocPropsToFill->setAutoloadSecs(i_xOldDocProps->getAutoloadSecs());
+ xDocPropsToFill->setDefaultTarget(i_xOldDocProps->getDefaultTarget());
+ xDocPropsToFill->setEditingCycles(i_xOldDocProps->getEditingCycles());
+ xDocPropsToFill->setEditingDuration(i_xOldDocProps->getEditingDuration());
+ // other attributes e.g. DocumentStatistics are not editable from dialog
+ }
+ catch (const uno::Exception&)
+ {
+ TOOLS_INFO_EXCEPTION("sfx.doc", "SetDocInfoState");
+ }
+
+ // set the modified flag back if required
+ if ( bIsModified != bool(xModifiable->isModified()) )
+ xModifiable->setModified( bIsModified );
+}
+
+
+// static
+bool SfxStoringHelper::WarnUnacceptableFormat( const uno::Reference< frame::XModel >& xModel,
+ std::u16string_view aOldUIName,
+ const OUString& aDefExtension,
+ bool bDefIsAlien )
+{
+ if ( !officecfg::Office::Common::Save::Document::WarnAlienFormat::get() )
+ return true;
+
+ weld::Window* pWin = SfxStoringHelper::GetModelWindow(xModel);
+ SfxAlienWarningDialog aDlg(pWin, aOldUIName, aDefExtension, bDefIsAlien);
+
+ return aDlg.run() == RET_OK;
+}
+
+uno::Reference<awt::XWindow> SfxStoringHelper::GetModelXWindow(const uno::Reference<frame::XModel>& xModel)
+{
+ try {
+ if ( xModel.is() )
+ {
+ uno::Reference< frame::XController > xController = xModel->getCurrentController();
+ if ( xController.is() )
+ {
+ uno::Reference< frame::XFrame > xFrame = xController->getFrame();
+ if ( xFrame.is() )
+ {
+ return xFrame->getContainerWindow();
+ }
+ }
+ }
+ }
+ catch ( const uno::Exception& )
+ {
+ }
+
+ return uno::Reference<awt::XWindow>();
+}
+
+weld::Window* SfxStoringHelper::GetModelWindow( const uno::Reference< frame::XModel >& xModel )
+{
+ weld::Window* pWin = nullptr;
+
+ try
+ {
+ pWin = Application::GetFrameWeld(GetModelXWindow(xModel));
+ }
+ catch (const uno::Exception&)
+ {
+ }
+
+ return pWin;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/doc/iframe.cxx b/sfx2/source/doc/iframe.cxx
new file mode 100644
index 000000000..52962c4be
--- /dev/null
+++ b/sfx2/source/doc/iframe.cxx
@@ -0,0 +1,444 @@
+/* -*- 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/awt/XWindowPeer.hpp>
+#include <com/sun/star/frame/XDispatch.hpp>
+#include <com/sun/star/frame/Frame.hpp>
+#include <com/sun/star/frame/XFrame2.hpp>
+#include <com/sun/star/frame/XSynchronousFrameLoader.hpp>
+#include <com/sun/star/util/URLTransformer.hpp>
+#include <com/sun/star/util/XURLTransformer.hpp>
+#include <com/sun/star/util/XCloseable.hpp>
+#include <com/sun/star/lang/XEventListener.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/ui/dialogs/XExecutableDialog.hpp>
+#include <com/sun/star/embed/XEmbeddedObject.hpp>
+
+#include <comphelper/propertyvalue.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <officecfg/Office/Common.hxx>
+#include <svl/itemprop.hxx>
+#include <sfx2/docfile.hxx>
+#include <sfx2/frmdescr.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/sfxdlg.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+#include <vcl/window.hxx>
+#include <tools/debug.hxx>
+#include <macroloader.hxx>
+#include <eventsupplier.hxx>
+
+using namespace ::com::sun::star;
+
+namespace {
+
+class IFrameObject : public ::cppu::WeakImplHelper <
+ css::util::XCloseable,
+ css::lang::XEventListener,
+ css::frame::XSynchronousFrameLoader,
+ css::ui::dialogs::XExecutableDialog,
+ css::lang::XServiceInfo,
+ css::beans::XPropertySet >
+{
+ css::uno::Reference < css::uno::XComponentContext > mxContext;
+ css::uno::Reference < css::frame::XFrame2 > mxFrame;
+ css::uno::Reference < css::embed::XEmbeddedObject > mxObj;
+ SfxItemPropertyMap maPropMap;
+ SfxFrameDescriptor maFrmDescr;
+
+public:
+ /// @throws css::uno::Exception
+ /// @throws css::uno::RuntimeException
+ IFrameObject(const css::uno::Reference < css::uno::XComponentContext>& rxContext, const css::uno::Sequence< css::uno::Any >& aArguments);
+
+ virtual OUString SAL_CALL getImplementationName() override
+ {
+ return "com.sun.star.comp.sfx2.IFrameObject";
+ }
+
+ virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override
+ {
+ return cppu::supportsService(this, ServiceName);
+ }
+
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override
+ {
+ css::uno::Sequence< OUString > aSeq { "com.sun.star.frame.SpecialEmbeddedObject" };
+ return aSeq;
+ }
+
+ virtual sal_Bool SAL_CALL load( const css::uno::Sequence < css::beans::PropertyValue >& lDescriptor,
+ const css::uno::Reference < css::frame::XFrame >& xFrame ) override;
+ virtual void SAL_CALL cancel() override;
+ virtual void SAL_CALL close( sal_Bool bDeliverOwnership ) override;
+ virtual void SAL_CALL addCloseListener( const css::uno::Reference < css::util::XCloseListener >& xListener ) override;
+ virtual void SAL_CALL removeCloseListener( const css::uno::Reference < css::util::XCloseListener >& xListener ) override;
+ virtual void SAL_CALL disposing( const css::lang::EventObject& aEvent ) override ;
+ virtual void SAL_CALL setTitle( const OUString& aTitle ) override;
+ virtual ::sal_Int16 SAL_CALL execute( ) override;
+ virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo() override;
+ virtual void SAL_CALL addPropertyChangeListener(const OUString& aPropertyName, const css::uno::Reference< css::beans::XPropertyChangeListener > & aListener) override;
+ virtual void SAL_CALL removePropertyChangeListener(const OUString& aPropertyName, const css::uno::Reference< css::beans::XPropertyChangeListener > & aListener) override;
+ virtual void SAL_CALL addVetoableChangeListener(const OUString& aPropertyName, const css::uno::Reference< css::beans::XVetoableChangeListener > & aListener) override;
+ virtual void SAL_CALL removeVetoableChangeListener(const OUString& aPropertyName, const css::uno::Reference< css::beans::XVetoableChangeListener > & aListener) override;
+ virtual void SAL_CALL setPropertyValue( const OUString& aPropertyName, const css::uno::Any& aValue ) override;
+ virtual css::uno::Any SAL_CALL getPropertyValue( const OUString& PropertyName ) override;
+};
+
+class IFrameWindow_Impl : public vcl::Window
+{
+public:
+ IFrameWindow_Impl( vcl::Window *pParent, bool bHasBorder );
+};
+
+IFrameWindow_Impl::IFrameWindow_Impl( vcl::Window *pParent, bool bHasBorder )
+ : Window( pParent, WB_CLIPCHILDREN | WB_NODIALOGCONTROL )
+{
+ if ( !bHasBorder )
+ SetBorderStyle( WindowBorderStyle::NOBORDER );
+ else
+ SetBorderStyle( WindowBorderStyle::NORMAL );
+}
+
+#define PROPERTY_UNBOUND 0
+
+#define WID_FRAME_URL 1
+#define WID_FRAME_NAME 2
+#define WID_FRAME_IS_AUTO_SCROLL 3
+#define WID_FRAME_IS_SCROLLING_MODE 4
+#define WID_FRAME_IS_BORDER 5
+#define WID_FRAME_IS_AUTO_BORDER 6
+#define WID_FRAME_MARGIN_WIDTH 7
+#define WID_FRAME_MARGIN_HEIGHT 8
+
+const SfxItemPropertyMapEntry* lcl_GetIFramePropertyMap_Impl()
+{
+ static const SfxItemPropertyMapEntry aIFramePropertyMap_Impl[] =
+ {
+ { u"FrameIsAutoBorder", WID_FRAME_IS_AUTO_BORDER, cppu::UnoType<bool>::get(), PROPERTY_UNBOUND, 0 },
+ { u"FrameIsAutoScroll", WID_FRAME_IS_AUTO_SCROLL, cppu::UnoType<bool>::get(), PROPERTY_UNBOUND, 0 },
+ { u"FrameIsBorder", WID_FRAME_IS_BORDER, cppu::UnoType<bool>::get(), PROPERTY_UNBOUND, 0 },
+ { u"FrameIsScrollingMode", WID_FRAME_IS_SCROLLING_MODE,cppu::UnoType<bool>::get(), PROPERTY_UNBOUND, 0 },
+ { u"FrameMarginHeight", WID_FRAME_MARGIN_HEIGHT, cppu::UnoType<sal_Int32>::get(), PROPERTY_UNBOUND, 0 },
+ { u"FrameMarginWidth", WID_FRAME_MARGIN_WIDTH, cppu::UnoType<sal_Int32>::get(), PROPERTY_UNBOUND, 0 },
+ { u"FrameName", WID_FRAME_NAME, cppu::UnoType<OUString>::get(), PROPERTY_UNBOUND, 0 },
+ { u"FrameURL", WID_FRAME_URL, cppu::UnoType<OUString>::get(), PROPERTY_UNBOUND, 0 },
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+ return aIFramePropertyMap_Impl;
+}
+
+IFrameObject::IFrameObject(const uno::Reference < uno::XComponentContext >& rxContext, const css::uno::Sequence< css::uno::Any >& aArguments)
+ : mxContext( rxContext )
+ , maPropMap( lcl_GetIFramePropertyMap_Impl() )
+{
+ if ( aArguments.hasElements() )
+ aArguments[0] >>= mxObj;
+}
+
+sal_Bool SAL_CALL IFrameObject::load(
+ const uno::Sequence < css::beans::PropertyValue >& /*lDescriptor*/,
+ const uno::Reference < frame::XFrame >& xFrame )
+{
+ if ( officecfg::Office::Common::Misc::PluginsEnabled::get() )
+ {
+ util::URL aTargetURL;
+ aTargetURL.Complete = maFrmDescr.GetURL().GetMainURL( INetURLObject::DecodeMechanism::NONE );
+ uno::Reference < util::XURLTransformer > xTrans( util::URLTransformer::create( mxContext ) );
+ xTrans->parseStrict( aTargetURL );
+
+ INetURLObject aURLObject(aTargetURL.Complete);
+ if (aURLObject.GetProtocol() == INetProtocol::Macro || aURLObject.isSchemeEqualTo(u"vnd.sun.star.script"))
+ return false;
+
+ uno::Reference<frame::XFramesSupplier> xParentFrame = xFrame->getCreator();
+ SfxObjectShell* pDoc = SfxMacroLoader::GetObjectShell(xParentFrame);
+
+ bool bUpdateAllowed(true);
+ if (pDoc)
+ {
+ comphelper::EmbeddedObjectContainer& rEmbeddedObjectContainer = pDoc->getEmbeddedObjectContainer();
+ bUpdateAllowed = rEmbeddedObjectContainer.getUserAllowsLinkUpdate();
+ }
+ if (!bUpdateAllowed)
+ return false;
+
+ OUString sReferer;
+ if (pDoc && pDoc->HasName())
+ sReferer = pDoc->GetMedium()->GetName();
+
+ uno::Reference<css::awt::XWindow> xParentWindow(xFrame->getContainerWindow());
+
+ if (!mxFrame.is())
+ {
+ VclPtr<vcl::Window> pParent = VCLUnoHelper::GetWindow(xParentWindow);
+ VclPtr<IFrameWindow_Impl> pWin = VclPtr<IFrameWindow_Impl>::Create( pParent, maFrmDescr.IsFrameBorderOn() );
+ pWin->SetSizePixel( pParent->GetOutputSizePixel() );
+ pWin->SetBackground();
+ pWin->Show();
+
+ uno::Reference < awt::XWindow > xWindow( pWin->GetComponentInterface(), uno::UNO_QUERY );
+ xFrame->setComponent( xWindow, uno::Reference < frame::XController >() );
+
+ // we must destroy the IFrame before the parent is destroyed
+ xWindow->addEventListener( this );
+
+ mxFrame = frame::Frame::create( mxContext );
+ uno::Reference < awt::XWindow > xWin( pWin->GetComponentInterface(), uno::UNO_QUERY );
+ mxFrame->initialize( xWin );
+ mxFrame->setName( maFrmDescr.GetName() );
+
+ uno::Reference < frame::XFramesSupplier > xFramesSupplier( xFrame, uno::UNO_QUERY );
+ if ( xFramesSupplier.is() )
+ mxFrame->setCreator( xFramesSupplier );
+ }
+
+ uno::Sequence < beans::PropertyValue > aProps{
+ comphelper::makePropertyValue("PluginMode", sal_Int16(2)),
+ comphelper::makePropertyValue("ReadOnly", true),
+ comphelper::makePropertyValue("Referer", sReferer)
+ };
+ uno::Reference < frame::XDispatch > xDisp = mxFrame->queryDispatch( aTargetURL, "_self", 0 );
+ if ( xDisp.is() )
+ xDisp->dispatch( aTargetURL, aProps );
+
+ return true;
+ }
+
+ return false;
+}
+
+void SAL_CALL IFrameObject::cancel()
+{
+ try
+ {
+ uno::Reference < util::XCloseable > xClose( mxFrame, uno::UNO_QUERY );
+ if ( xClose.is() )
+ xClose->close( true );
+ mxFrame = nullptr;
+ }
+ catch (const uno::Exception&)
+ {
+ }
+}
+
+void SAL_CALL IFrameObject::close( sal_Bool /*bDeliverOwnership*/ )
+{
+}
+
+void SAL_CALL IFrameObject::addCloseListener( const css::uno::Reference < css::util::XCloseListener >& )
+{
+}
+
+void SAL_CALL IFrameObject::removeCloseListener( const css::uno::Reference < css::util::XCloseListener >& )
+{
+}
+
+void SAL_CALL IFrameObject::disposing( const css::lang::EventObject& )
+{
+ cancel();
+}
+
+uno::Reference< beans::XPropertySetInfo > SAL_CALL IFrameObject::getPropertySetInfo()
+{
+ static uno::Reference< beans::XPropertySetInfo > xInfo = new SfxItemPropertySetInfo( maPropMap );
+ return xInfo;
+}
+
+void SAL_CALL IFrameObject::setPropertyValue(const OUString& aPropertyName, const uno::Any& aAny)
+{
+ const SfxItemPropertyMapEntry* pEntry = maPropMap.getByName( aPropertyName );
+ if( !pEntry )
+ throw beans::UnknownPropertyException(aPropertyName);
+ switch( pEntry->nWID )
+ {
+ case WID_FRAME_URL:
+ {
+ OUString aURL;
+ aAny >>= aURL;
+ maFrmDescr.SetURL( aURL );
+ }
+ break;
+ case WID_FRAME_NAME:
+ {
+ OUString aName;
+ if ( aAny >>= aName )
+ maFrmDescr.SetName( aName );
+ }
+ break;
+ case WID_FRAME_IS_AUTO_SCROLL:
+ {
+ bool bIsAutoScroll;
+ if ( (aAny >>= bIsAutoScroll) && bIsAutoScroll )
+ maFrmDescr.SetScrollingMode( ScrollingMode::Auto );
+ }
+ break;
+ case WID_FRAME_IS_SCROLLING_MODE:
+ {
+ bool bIsScroll;
+ if ( aAny >>= bIsScroll )
+ maFrmDescr.SetScrollingMode( bIsScroll ? ScrollingMode::Yes : ScrollingMode::No );
+ }
+ break;
+ case WID_FRAME_IS_BORDER:
+ {
+ bool bIsBorder;
+ if ( aAny >>= bIsBorder )
+ maFrmDescr.SetFrameBorder( bIsBorder );
+ }
+ break;
+ case WID_FRAME_IS_AUTO_BORDER:
+ {
+ bool bIsAutoBorder;
+ if ( aAny >>= bIsAutoBorder )
+ {
+ bool bBorder = maFrmDescr.IsFrameBorderOn();
+ maFrmDescr.ResetBorder();
+ if ( bIsAutoBorder )
+ maFrmDescr.SetFrameBorder( bBorder );
+ }
+ }
+ break;
+ case WID_FRAME_MARGIN_WIDTH:
+ {
+ sal_Int32 nMargin = 0;
+ Size aSize = maFrmDescr.GetMargin();
+ if ( aAny >>= nMargin )
+ {
+ aSize.setWidth( nMargin );
+ maFrmDescr.SetMargin( aSize );
+ }
+ }
+ break;
+ case WID_FRAME_MARGIN_HEIGHT:
+ {
+ sal_Int32 nMargin = 0;
+ Size aSize = maFrmDescr.GetMargin();
+ if ( aAny >>= nMargin )
+ {
+ aSize.setHeight( nMargin );
+ maFrmDescr.SetMargin( aSize );
+ }
+ }
+ break;
+ default: ;
+ }
+}
+
+uno::Any SAL_CALL IFrameObject::getPropertyValue(const OUString& aPropertyName)
+{
+ const SfxItemPropertyMapEntry* pEntry = maPropMap.getByName( aPropertyName );
+ if( !pEntry )
+ throw beans::UnknownPropertyException(aPropertyName);
+ uno::Any aAny;
+ switch( pEntry->nWID )
+ {
+ case WID_FRAME_URL:
+ {
+ aAny <<= maFrmDescr.GetURL().GetMainURL( INetURLObject::DecodeMechanism::NONE );
+ }
+ break;
+ case WID_FRAME_NAME:
+ {
+ aAny <<= maFrmDescr.GetName();
+ }
+ break;
+ case WID_FRAME_IS_AUTO_SCROLL:
+ {
+ bool bIsAutoScroll = ( maFrmDescr.GetScrollingMode() == ScrollingMode::Auto );
+ aAny <<= bIsAutoScroll;
+ }
+ break;
+ case WID_FRAME_IS_SCROLLING_MODE:
+ {
+ bool bIsScroll = ( maFrmDescr.GetScrollingMode() == ScrollingMode::Yes );
+ aAny <<= bIsScroll;
+ }
+ break;
+ case WID_FRAME_IS_BORDER:
+ {
+ bool bIsBorder = maFrmDescr.IsFrameBorderOn();
+ aAny <<= bIsBorder;
+ }
+ break;
+ case WID_FRAME_IS_AUTO_BORDER:
+ {
+ bool bIsAutoBorder = !maFrmDescr.IsFrameBorderSet();
+ aAny <<= bIsAutoBorder;
+ }
+ break;
+ case WID_FRAME_MARGIN_WIDTH:
+ {
+ aAny <<= static_cast<sal_Int32>(maFrmDescr.GetMargin().Width());
+ }
+ break;
+ case WID_FRAME_MARGIN_HEIGHT:
+ {
+ aAny <<= static_cast<sal_Int32>(maFrmDescr.GetMargin().Height());
+ }
+ break;
+ default: ;
+ }
+ return aAny;
+}
+
+void SAL_CALL IFrameObject::addPropertyChangeListener(const OUString&, const css::uno::Reference< css::beans::XPropertyChangeListener > & )
+{
+}
+
+void SAL_CALL IFrameObject::removePropertyChangeListener(const OUString&, const css::uno::Reference< css::beans::XPropertyChangeListener > & )
+{
+}
+
+void SAL_CALL IFrameObject::addVetoableChangeListener(const OUString&, const css::uno::Reference< css::beans::XVetoableChangeListener > & )
+{
+}
+
+void SAL_CALL IFrameObject::removeVetoableChangeListener(const OUString&, const css::uno::Reference< css::beans::XVetoableChangeListener > & )
+{
+}
+
+::sal_Int16 SAL_CALL IFrameObject::execute()
+{
+ SfxAbstractDialogFactory* pFact = SfxAbstractDialogFactory::Create();
+ //we really should set a parent here
+ ScopedVclPtr<VclAbstractDialog> pDlg(pFact->CreateEditObjectDialog(nullptr, ".uno:InsertObjectFloatingFrame", mxObj));
+ pDlg->Execute();
+ return 0;
+}
+
+void SAL_CALL IFrameObject::setTitle( const OUString& )
+{
+}
+
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_sfx2_IFrameObject_get_implementation(
+ css::uno::XComponentContext *context,
+ css::uno::Sequence<css::uno::Any> const &arguments)
+{
+ return cppu::acquire(new IFrameObject(context, arguments));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/doc/new.cxx b/sfx2/source/doc/new.cxx
new file mode 100644
index 000000000..a6922befc
--- /dev/null
+++ b/sfx2/source/doc/new.cxx
@@ -0,0 +1,350 @@
+/* -*- 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 <osl/file.hxx>
+#include <sfx2/new.hxx>
+#include <vcl/idle.hxx>
+#include <vcl/gdimtf.hxx>
+#include <vcl/outdev.hxx>
+#include <svl/itemset.hxx>
+#include <svl/eitem.hxx>
+#include <svtools/sfxecode.hxx>
+#include <svtools/ehdl.hxx>
+#include <tools/urlobj.hxx>
+#include <tools/debug.hxx>
+
+#include <sfx2/strings.hrc>
+#include <sfx2/sfxsids.hrc>
+#include <sfx2/app.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/sfxresid.hxx>
+#include <sfx2/docfile.hxx>
+#include <preview.hxx>
+#include <unotools/viewoptions.hxx>
+
+void SfxPreviewWin_Impl::SetObjectShell(SfxObjectShell const * pObj)
+{
+ std::shared_ptr<GDIMetaFile> xFile = pObj
+ ? pObj->GetPreviewMetaFile()
+ : std::shared_ptr<GDIMetaFile>();
+ xMetaFile = xFile;
+ Invalidate();
+}
+
+SfxPreviewWin_Impl::SfxPreviewWin_Impl()
+{
+}
+
+void SfxPreviewWin_Impl::ImpPaint(vcl::RenderContext& rRenderContext, GDIMetaFile* pFile)
+{
+ rRenderContext.SetLineColor();
+ rRenderContext.SetFillColor(COL_LIGHTGRAY);
+ rRenderContext.DrawRect(tools::Rectangle(Point(0,0), rRenderContext.GetOutputSize()));
+
+ Size aTmpSize = pFile ? pFile->GetPrefSize() : Size(1, 1);
+ DBG_ASSERT(!aTmpSize.IsEmpty(), "size of first page is 0, override GetFirstPageSize or set visible-area!");
+
+#define FRAME 4
+
+ tools::Long nWidth = rRenderContext.GetOutputSize().Width() - 2 * FRAME;
+ tools::Long nHeight = rRenderContext.GetOutputSize().Height() - 2 * FRAME;
+ if (nWidth <= 0 || nHeight <= 0)
+ return;
+
+ double dRatio = aTmpSize.Height() ? (double(aTmpSize.Width()) / aTmpSize.Height()) : 1;
+ double dRatioPreV = double(nWidth) / nHeight;
+ Size aSize;
+ Point aPoint;
+ if (dRatio > dRatioPreV)
+ {
+ aSize = Size(nWidth, sal_uInt16(nWidth / dRatio));
+ aPoint = Point(0, sal_uInt16((nHeight - aSize.Height()) / 2));
+ }
+ else
+ {
+ aSize = Size(sal_uInt16(nHeight * dRatio), nHeight);
+ aPoint = Point(sal_uInt16((nWidth - aSize.Width()) / 2), 0);
+ }
+ Point bPoint = Point(nWidth, nHeight) - aPoint;
+
+ if (pFile)
+ {
+ rRenderContext.SetLineColor(COL_BLACK);
+ rRenderContext.SetFillColor(COL_WHITE);
+ rRenderContext.DrawRect(tools::Rectangle(aPoint + Point(FRAME, FRAME), bPoint + Point(FRAME, FRAME)));
+ pFile->WindStart();
+ pFile->Play(rRenderContext, aPoint + Point(FRAME, FRAME), aSize);
+ }
+}
+
+void SfxPreviewWin_Impl::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&)
+{
+ ImpPaint(rRenderContext, xMetaFile.get());
+}
+
+IMPL_LINK_NOARG(SfxNewFileDialog, Update, Timer*, void)
+{
+ if (m_xDocShell.Is())
+ {
+ if (m_xDocShell->GetProgress())
+ return;
+ m_xDocShell.Clear();
+ }
+
+ const sal_uInt16 nEntry = GetSelectedTemplatePos();
+ if (!nEntry)
+ {
+ m_xPreviewController->Invalidate();
+ m_xPreviewController->SetObjectShell(nullptr);
+ return;
+ }
+
+ if (!m_xMoreBt->get_expanded() || (m_nFlags != SfxNewFileDialogMode::Preview))
+ return;
+
+ OUString aFileName = m_aTemplates.GetPath(m_xRegionLb->get_selected_index(), nEntry - 1);
+ INetURLObject aTestObj(aFileName);
+ if (aTestObj.GetProtocol() == INetProtocol::NotValid)
+ {
+ // temp. fix until Templates are managed by UCB compatible service
+ // does NOT work with locally cached components !
+ OUString aTemp;
+ osl::FileBase::getFileURLFromSystemPath( aFileName, aTemp );
+ aFileName = aTemp;
+ }
+
+ INetURLObject aObj(aFileName);
+ for (SfxObjectShell* pTmp = SfxObjectShell::GetFirst(); pTmp; pTmp = SfxObjectShell::GetNext(*pTmp))
+ {
+ //! fsys bug op==
+ if (pTmp->GetMedium())
+ // ??? HasName() MM
+ if (INetURLObject( pTmp->GetMedium()->GetName() ) == aObj)
+ {
+ m_xDocShell = pTmp;
+ break;
+ }
+ }
+
+ if (!m_xDocShell.Is())
+ {
+ SfxErrorContext eEC(ERRCTX_SFX_LOADTEMPLATE, m_xDialog.get());
+ SfxApplication *pSfxApp = SfxGetpApp();
+ std::unique_ptr<SfxItemSet> pSet(new SfxAllItemSet(pSfxApp->GetPool()));
+ pSet->Put(SfxBoolItem(SID_TEMPLATE, true));
+ pSet->Put(SfxBoolItem(SID_PREVIEW, true));
+ ErrCode lErr = pSfxApp->LoadTemplate(m_xDocShell, aFileName, std::move(pSet));
+ if (lErr)
+ ErrorHandler::HandleError(lErr);
+ if (!m_xDocShell.Is())
+ {
+ m_xPreviewController->SetObjectShell(nullptr);
+ return;
+ }
+ }
+
+ m_xPreviewController->SetObjectShell(m_xDocShell);
+}
+
+IMPL_LINK( SfxNewFileDialog, RegionSelect, weld::TreeView&, rBox, void )
+{
+ if (m_xDocShell.Is() && m_xDocShell->GetProgress())
+ return;
+
+ const sal_uInt16 nRegion = rBox.get_selected_index();
+ const sal_uInt16 nCount = m_aTemplates.GetRegionCount() ? m_aTemplates.GetCount(nRegion): 0;
+ m_xTemplateLb->freeze();
+ m_xTemplateLb->clear();
+ OUString aSel = m_xRegionLb->get_selected_text();
+ sal_Int32 nc = aSel.indexOf('(');
+ if (nc != -1 && nc != 0)
+ aSel = aSel.replaceAt(nc-1, 1, u"");
+ if ( aSel.compareToIgnoreAsciiCase( SfxResId(STR_STANDARD) ) == 0 )
+ m_xTemplateLb->append_text(SfxResId(STR_NONE));
+ for (sal_uInt16 i = 0; i < nCount; ++i)
+ m_xTemplateLb->append_text(m_aTemplates.GetName(nRegion, i));
+ m_xTemplateLb->thaw();
+ if (nCount)
+ m_xTemplateLb->select(0);
+ TemplateSelect(*m_xTemplateLb);
+}
+
+IMPL_LINK_NOARG(SfxNewFileDialog, Expand, weld::Expander&, void)
+{
+ TemplateSelect(*m_xTemplateLb);
+}
+
+IMPL_LINK_NOARG(SfxNewFileDialog, TemplateSelect, weld::TreeView&, void)
+{
+ // Still loading
+ if (m_xDocShell && m_xDocShell->GetProgress())
+ return;
+
+ if (!m_xMoreBt->get_expanded())
+ {
+ // Dialog is not opened
+ return;
+ }
+
+ m_aPrevIdle.Start();
+}
+
+IMPL_LINK_NOARG( SfxNewFileDialog, DoubleClick, weld::TreeView&, bool )
+{
+ // Still loading
+ if (!m_xDocShell.Is() || !m_xDocShell->GetProgress())
+ m_xDialog->response(RET_OK);
+ return true;
+}
+
+sal_uInt16 SfxNewFileDialog::GetSelectedTemplatePos() const
+{
+ int nEntry = m_xTemplateLb->get_selected_index();
+ if (nEntry == -1)
+ return 0;
+ OUString aSel = m_xRegionLb->get_selected_text();
+ sal_Int32 nc = aSel.indexOf('(');
+ if (nc != -1 && nc != 0)
+ aSel = aSel.replaceAt(nc-1, 1, u"");
+ if ( aSel.compareToIgnoreAsciiCase(SfxResId(STR_STANDARD)) != 0 )
+ nEntry++;
+ return nEntry;
+}
+
+SfxNewFileDialog::SfxNewFileDialog(weld::Window *pParent, SfxNewFileDialogMode nFlags)
+ : SfxDialogController(pParent, "sfx/ui/loadtemplatedialog.ui", "LoadTemplateDialog")
+ , m_aPrevIdle("SfxNewFileDialog m_aPrevIdle")
+ , m_nFlags(nFlags)
+ , m_xPreviewController(new SfxPreviewWin_Impl)
+ , m_xRegionLb(m_xBuilder->weld_tree_view("categories"))
+ , m_xTemplateLb(m_xBuilder->weld_tree_view("templates"))
+ , m_xTextStyleCB(m_xBuilder->weld_check_button("text"))
+ , m_xFrameStyleCB(m_xBuilder->weld_check_button("frame"))
+ , m_xPageStyleCB(m_xBuilder->weld_check_button("pages"))
+ , m_xNumStyleCB(m_xBuilder->weld_check_button("numbering"))
+ , m_xMergeStyleCB(m_xBuilder->weld_check_button("overwrite"))
+ , m_xLoadFilePB(m_xBuilder->weld_button("fromfile"))
+ , m_xMoreBt(m_xBuilder->weld_expander("expander"))
+ , m_xPreviewWin(new weld::CustomWeld(*m_xBuilder, "image", *m_xPreviewController))
+ , m_xAltTitleFt(m_xBuilder->weld_label("alttitle"))
+{
+ const int nWidth = m_xRegionLb->get_approximate_digit_width() * 32;
+ const int nHeight = m_xRegionLb->get_height_rows(8);
+ m_xRegionLb->set_size_request(nWidth, nHeight);
+ m_xTemplateLb->set_size_request(nWidth, nHeight);
+ m_xPreviewWin->set_size_request(nWidth, nWidth);
+
+ if (nFlags == SfxNewFileDialogMode::NONE)
+ m_xMoreBt->hide();
+ else if(SfxNewFileDialogMode::LoadTemplate == nFlags)
+ {
+ m_xLoadFilePB->show();
+ m_xTextStyleCB->show();
+ m_xFrameStyleCB->show();
+ m_xPageStyleCB->show();
+ m_xNumStyleCB->show();
+ m_xMergeStyleCB->show();
+ m_xMoreBt->hide();
+ m_xTextStyleCB->set_active(true);
+ m_xDialog->set_title(m_xAltTitleFt->get_label());
+ }
+ else
+ {
+ m_xMoreBt->connect_expanded(LINK(this, SfxNewFileDialog, Expand));
+ m_xPreviewWin->show();
+ }
+
+ OUString sExtraData;
+ SvtViewOptions aDlgOpt(EViewType::Dialog, OStringToOUString(m_xDialog->get_help_id(), RTL_TEXTENCODING_UTF8));
+ if (aDlgOpt.Exists())
+ {
+ css::uno::Any aUserItem = aDlgOpt.GetUserItem("UserItem");
+ aUserItem >>= sExtraData;
+ }
+
+ bool bExpand = !sExtraData.isEmpty() && sExtraData[0] == 'Y';
+ m_xMoreBt->set_expanded(bExpand && (nFlags != SfxNewFileDialogMode::NONE));
+
+ m_xTemplateLb->connect_changed(LINK(this, SfxNewFileDialog, TemplateSelect));
+ m_xTemplateLb->connect_row_activated(LINK(this, SfxNewFileDialog, DoubleClick));
+
+ // update the template configuration if necessary
+ {
+ weld::WaitObject aWaitCursor(m_xDialog.get());
+ m_aTemplates.Update();
+ }
+ // fill the list boxes
+ const sal_uInt16 nCount = m_aTemplates.GetRegionCount();
+ if (nCount)
+ {
+ for(sal_uInt16 i = 0; i < nCount; ++i)
+ m_xRegionLb->append_text(m_aTemplates.GetFullRegionName(i));
+ m_xRegionLb->connect_changed(LINK(this, SfxNewFileDialog, RegionSelect));
+ }
+
+ m_aPrevIdle.SetPriority( TaskPriority::LOWEST );
+ m_aPrevIdle.SetInvokeHandler( LINK( this, SfxNewFileDialog, Update));
+
+ m_xRegionLb->select(0);
+ RegionSelect(*m_xRegionLb);
+}
+
+SfxNewFileDialog::~SfxNewFileDialog()
+{
+ SvtViewOptions aDlgOpt(EViewType::Dialog, OStringToOUString(m_xDialog->get_help_id(), RTL_TEXTENCODING_UTF8));
+ aDlgOpt.SetUserItem("UserItem", css::uno::Any(m_xMoreBt->get_expanded() ? OUString("Y") : OUString("N")));
+}
+
+bool SfxNewFileDialog::IsTemplate() const
+{
+ return GetSelectedTemplatePos()!=0;
+}
+
+OUString SfxNewFileDialog::GetTemplateFileName() const
+{
+ if (!IsTemplate() || !m_aTemplates.GetRegionCount())
+ return OUString();
+ return m_aTemplates.GetPath(m_xRegionLb->get_selected_index(),
+ GetSelectedTemplatePos()-1);
+}
+
+SfxTemplateFlags SfxNewFileDialog::GetTemplateFlags()const
+{
+ SfxTemplateFlags nRet = m_xTextStyleCB->get_active() ? SfxTemplateFlags::LOAD_TEXT_STYLES : SfxTemplateFlags::NONE;
+ if(m_xFrameStyleCB->get_active())
+ nRet |= SfxTemplateFlags::LOAD_FRAME_STYLES;
+ if(m_xPageStyleCB->get_active())
+ nRet |= SfxTemplateFlags::LOAD_PAGE_STYLES;
+ if(m_xNumStyleCB->get_active())
+ nRet |= SfxTemplateFlags::LOAD_NUM_STYLES;
+ if(m_xMergeStyleCB->get_active())
+ nRet |= SfxTemplateFlags::MERGE_STYLES;
+ return nRet;
+}
+
+void SfxNewFileDialog::SetTemplateFlags(SfxTemplateFlags nSet)
+{
+ m_xTextStyleCB->set_active( bool(nSet & SfxTemplateFlags::LOAD_TEXT_STYLES ));
+ m_xFrameStyleCB->set_active( bool(nSet & SfxTemplateFlags::LOAD_FRAME_STYLES));
+ m_xPageStyleCB->set_active( bool(nSet & SfxTemplateFlags::LOAD_PAGE_STYLES ));
+ m_xNumStyleCB->set_active( bool(nSet & SfxTemplateFlags::LOAD_NUM_STYLES ));
+ m_xMergeStyleCB->set_active( bool(nSet & SfxTemplateFlags::MERGE_STYLES ));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/doc/objcont.cxx b/sfx2/source/doc/objcont.cxx
new file mode 100644
index 000000000..8313cfc3d
--- /dev/null
+++ b/sfx2/source/doc/objcont.cxx
@@ -0,0 +1,712 @@
+/* -*- 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/uno/Reference.hxx>
+
+#include <com/sun/star/document/DocumentProperties.hpp>
+#include <com/sun/star/document/XDocumentProperties.hpp>
+#include <com/sun/star/document/UpdateDocMode.hpp>
+#include <comphelper/fileurl.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+#include <svl/style.hxx>
+
+#include <svl/intitem.hxx>
+#include <svl/ctloptions.hxx>
+#include <comphelper/processfactory.hxx>
+#include <unotools/securityoptions.hxx>
+#include <tools/datetime.hxx>
+#include <tools/diagnose_ex.h>
+#include <tools/helpers.hxx>
+#include <rtl/uri.hxx>
+
+#include <unotools/useroptions.hxx>
+#include <vcl/virdev.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/gdimtf.hxx>
+
+#include <sfx2/app.hxx>
+#include <sfx2/dinfdlg.hxx>
+#include <sfx2/sfxresid.hxx>
+#include <appdata.hxx>
+#include <sfx2/docfac.hxx>
+#include <sfx2/viewsh.hxx>
+#include <sfx2/objsh.hxx>
+#include <objshimp.hxx>
+#include <sfx2/printer.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/doctempl.hxx>
+#include <sfx2/sfxsids.hrc>
+#include <sfx2/strings.hrc>
+#include <sfx2/docfile.hxx>
+#include <sfx2/docfilt.hxx>
+#include <memory>
+#include <helpids.h>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+
+
+static
+bool operator> (const util::DateTime& i_rLeft, const util::DateTime& i_rRight)
+{
+ if ( i_rLeft.Year != i_rRight.Year )
+ return i_rLeft.Year > i_rRight.Year;
+
+ if ( i_rLeft.Month != i_rRight.Month )
+ return i_rLeft.Month > i_rRight.Month;
+
+ if ( i_rLeft.Day != i_rRight.Day )
+ return i_rLeft.Day > i_rRight.Day;
+
+ if ( i_rLeft.Hours != i_rRight.Hours )
+ return i_rLeft.Hours > i_rRight.Hours;
+
+ if ( i_rLeft.Minutes != i_rRight.Minutes )
+ return i_rLeft.Minutes > i_rRight.Minutes;
+
+ if ( i_rLeft.Seconds != i_rRight.Seconds )
+ return i_rLeft.Seconds > i_rRight.Seconds;
+
+ if ( i_rLeft.NanoSeconds != i_rRight.NanoSeconds )
+ return i_rLeft.NanoSeconds > i_rRight.NanoSeconds;
+
+ return false;
+}
+
+std::shared_ptr<GDIMetaFile>
+SfxObjectShell::GetPreviewMetaFile( bool bFullContent ) const
+{
+ auto xFile = std::make_shared<GDIMetaFile>();
+ ScopedVclPtrInstance< VirtualDevice > pDevice;
+ pDevice->EnableOutput( false );
+ if(!CreatePreview_Impl(bFullContent, pDevice, xFile.get()))
+ return std::shared_ptr<GDIMetaFile>();
+ return xFile;
+}
+
+BitmapEx SfxObjectShell::GetPreviewBitmap() const
+{
+ ScopedVclPtrInstance< VirtualDevice > pDevice;
+ pDevice->SetAntialiasing(AntialiasingFlags::Enable | pDevice->GetAntialiasing());
+ if(!CreatePreview_Impl(/*bFullContent*/false, pDevice, nullptr))
+ return BitmapEx();
+ Size size = pDevice->GetOutputSizePixel();
+ BitmapEx aBitmap = pDevice->GetBitmapEx( Point(), size);
+ // Scale down the image to the desired size from the 4*size from CreatePreview_Impl().
+ size = Size( size.Width() / 4, size.Height() / 4 );
+ aBitmap.Scale(size, BmpScaleFlag::BestQuality);
+ if (!aBitmap.IsEmpty())
+ aBitmap.Convert(BmpConversion::N24Bit);
+ return aBitmap;
+}
+
+bool SfxObjectShell::CreatePreview_Impl( bool bFullContent, VirtualDevice* pDevice, GDIMetaFile* pFile) const
+{
+ // DoDraw can only be called when no printing is done, otherwise
+ // the printer may be turned off
+ SfxViewFrame *pFrame = SfxViewFrame::GetFirst( this );
+ if ( pFrame && pFrame->GetViewShell() &&
+ pFrame->GetViewShell()->GetPrinter() &&
+ pFrame->GetViewShell()->GetPrinter()->IsPrinting() )
+ return false;
+
+ MapMode aMode( GetMapUnit() );
+ Size aTmpSize;
+ sal_Int8 nAspect;
+ if ( bFullContent )
+ {
+ nAspect = ASPECT_CONTENT;
+ aTmpSize = GetVisArea( nAspect ).GetSize();
+ }
+ else
+ {
+ nAspect = ASPECT_THUMBNAIL;
+ aTmpSize = GetFirstPageSize();
+ }
+
+ DBG_ASSERT( !aTmpSize.IsEmpty(),
+ "size of first page is 0, override GetFirstPageSize or set visible-area!" );
+
+ if(pFile)
+ {
+ pDevice->SetMapMode( aMode );
+ pFile->SetPrefMapMode( aMode );
+ pFile->SetPrefSize( aTmpSize );
+ pFile->Record( pDevice );
+ }
+ else
+ {
+ // Use pixel size, that's also what DoDraw() requires in this case,
+ // despite the metafile case (needlessly?) setting mapmode.
+ Size aSizePix = pDevice->LogicToPixel( aTmpSize, aMode );
+ // Code based on GDIMetaFile::CreateThumbnail().
+ sal_uInt32 nMaximumExtent = 512;
+ // determine size that has the same aspect ratio as image size and
+ // fits into the rectangle determined by nMaximumExtent
+ if ( aSizePix.Width() && aSizePix.Height()
+ && ( sal::static_int_cast< tools::ULong >(aSizePix.Width()) >
+ nMaximumExtent ||
+ sal::static_int_cast< tools::ULong >(aSizePix.Height()) >
+ nMaximumExtent ) )
+ {
+ double fWH = static_cast< double >( aSizePix.Width() ) / aSizePix.Height();
+ if ( fWH <= 1.0 )
+ {
+ aSizePix.setWidth( FRound( nMaximumExtent * fWH ) );
+ aSizePix.setHeight( nMaximumExtent );
+ }
+ else
+ {
+ aSizePix.setWidth( nMaximumExtent );
+ aSizePix.setHeight( FRound( nMaximumExtent / fWH ) );
+ }
+ }
+ // do it 4x larger to be able to scale it down & get beautiful antialias
+ aTmpSize = Size( aSizePix.Width() * 4, aSizePix.Height() * 4 );
+ pDevice->SetOutputSizePixel( aTmpSize );
+ }
+
+ LanguageType eLang;
+ SvtCTLOptions aCTLOptions;
+ if ( SvtCTLOptions::NUMERALS_HINDI == aCTLOptions.GetCTLTextNumerals() )
+ eLang = LANGUAGE_ARABIC_SAUDI_ARABIA;
+ else if ( SvtCTLOptions::NUMERALS_ARABIC == aCTLOptions.GetCTLTextNumerals() )
+ eLang = LANGUAGE_ENGLISH;
+ else
+ eLang = Application::GetSettings().GetLanguageTag().getLanguageType();
+
+ pDevice->SetDigitLanguage( eLang );
+
+ const_cast<SfxObjectShell*>(this)->DoDraw( pDevice, Point(0,0), aTmpSize, JobSetup(), nAspect );
+
+ if(pFile)
+ pFile->Stop();
+
+ return true;
+}
+
+
+void SfxObjectShell::UpdateDocInfoForSave()
+{
+ uno::Reference<document::XDocumentProperties> xDocProps(getDocProperties());
+
+ // clear user data if recommend (see 'Tools - Options - LibreOffice - Security')
+ if ( SvtSecurityOptions::IsOptionSet(
+ SvtSecurityOptions::EOption::DocWarnRemovePersonalInfo ) )
+ {
+ xDocProps->resetUserData( OUString() );
+ }
+ else if ( IsModified() )
+ {
+ const OUString aUserName = SvtUserOptions().GetFullName();
+ if ( !IsUseUserData() )
+ {
+ // remove all data pointing to the current user
+ if (xDocProps->getAuthor() == aUserName) {
+ xDocProps->setAuthor( OUString() );
+ }
+ xDocProps->setModifiedBy( OUString() );
+ if (xDocProps->getPrintedBy() == aUserName) {
+ xDocProps->setPrintedBy( OUString() );
+ }
+ }
+ else
+ {
+ // update ModificationAuthor, revision and editing time
+ ::DateTime now( ::DateTime::SYSTEM );
+ xDocProps->setModificationDate( now.GetUNODateTime() );
+ xDocProps->setModifiedBy( aUserName );
+ UpdateTime_Impl( xDocProps );
+ }
+ }
+}
+
+
+static void
+lcl_add(util::Duration & rDur, tools::Time const& rTime)
+{
+ // here we don't care about overflow: rDur is converted back to seconds
+ // anyway, and tools::Time cannot store more than ~4000 hours
+ rDur.Hours += rTime.GetHour();
+ rDur.Minutes += rTime.GetMin();
+ rDur.Seconds += rTime.GetSec();
+}
+
+// Update the processing time
+void SfxObjectShell::UpdateTime_Impl(
+ const uno::Reference<document::XDocumentProperties> & i_xDocProps)
+{
+ // Get old time from documentinfo
+ const sal_Int32 secs = i_xDocProps->getEditingDuration();
+ util::Duration editDuration(false, 0, 0, 0,
+ secs/3600, (secs%3600)/60, secs%60, 0);
+
+ // Initialize some local member! It's necessary for follow operations!
+ DateTime aNow( DateTime::SYSTEM ); // Date and time at current moment
+ tools::Time n24Time (24,0,0,0) ; // Time-value for 24 hours - see follow calculation
+ tools::Time nAddTime (0) ; // Value to add on aOldTime
+
+ // Save impossible cases!
+ // User has changed time to the past between last editing and now... it's not possible!!!
+ DBG_ASSERT( !(aNow.GetDate()<pImpl->nTime.GetDate()), "Timestamp of last change is in the past!?..." );
+
+ // Do the follow only, if user has NOT changed time to the past.
+ // Else add a time of 0 to aOldTime... !!!
+ if (aNow.GetDate()>=pImpl->nTime.GetDate())
+ {
+ // Count of days between now and last editing
+ sal_Int32 nDays = aNow.GetSecFromDateTime(Date(pImpl->nTime.GetDate()))/86400 ;
+
+ if (nDays==0)
+ {
+ // If no day between now and last editing - calculate time directly.
+ nAddTime = static_cast<const tools::Time&>(aNow) - static_cast<const tools::Time&>(pImpl->nTime);
+ }
+ else if (nDays<=31)
+ {
+ // If time of working without save greater than 1 month (!)...
+ // we add 0 to aOldTime!
+
+ // If 1 or up to 31 days between now and last editing - calculate time indirectly.
+ // nAddTime = (24h - nTime) + (nDays * 24h) + aNow
+ --nDays;
+ nAddTime = tools::Time( nDays * n24Time.GetTime());
+ nAddTime += n24Time-static_cast<const tools::Time&>(pImpl->nTime);
+ nAddTime += aNow ;
+ }
+
+ lcl_add(editDuration, nAddTime);
+ }
+
+ pImpl->nTime = aNow;
+ try {
+ const sal_Int32 newSecs( (editDuration.Hours*3600)
+ + (editDuration.Minutes*60) + editDuration.Seconds);
+ i_xDocProps->setEditingDuration(newSecs);
+ i_xDocProps->setEditingCycles(i_xDocProps->getEditingCycles() + 1);
+ }
+ catch (const lang::IllegalArgumentException &)
+ {
+ // ignore overflow
+ }
+}
+
+std::shared_ptr<SfxDocumentInfoDialog> SfxObjectShell::CreateDocumentInfoDialog(weld::Window* pParent,
+ const SfxItemSet& rSet)
+{
+ return std::make_shared<SfxDocumentInfoDialog>(pParent, rSet);
+}
+
+std::set<Color> SfxObjectShell::GetDocColors()
+{
+ std::set<Color> empty;
+ return empty;
+}
+
+std::vector<Color> SfxObjectShell::GetThemeColors() { return std::vector<Color>(); }
+
+sfx::AccessibilityIssueCollection SfxObjectShell::runAccessibilityCheck()
+{
+ sfx::AccessibilityIssueCollection aCollection;
+ return aCollection;
+}
+
+SfxStyleSheetBasePool* SfxObjectShell::GetStyleSheetPool()
+{
+ return nullptr;
+}
+
+namespace {
+
+struct Styles_Impl
+{
+ SfxStyleSheetBase *pSource;
+ SfxStyleSheetBase *pDest;
+};
+
+}
+
+void SfxObjectShell::LoadStyles
+(
+ SfxObjectShell &rSource /* the document template from which
+ the styles are to be loaded */
+)
+
+/* [Description]
+
+ This method is called by the SFx if styles are to be loaded from a template.
+ Existing styles are in this case overwritten. The document must then be
+ re-formatted. Therefore, applications usually override this method
+ and call the implementation in the base class.
+*/
+
+{
+ SfxStyleSheetBasePool *pSourcePool = rSource.GetStyleSheetPool();
+ DBG_ASSERT(pSourcePool, "Source-DocumentShell without StyleSheetPool");
+ SfxStyleSheetBasePool *pMyPool = GetStyleSheetPool();
+ DBG_ASSERT(pMyPool, "Dest-DocumentShell without StyleSheetPool");
+ auto xIter = pSourcePool->CreateIterator(SfxStyleFamily::All);
+ std::unique_ptr<Styles_Impl[]> pFound(new Styles_Impl[xIter->Count()]);
+ sal_uInt16 nFound = 0;
+
+ SfxStyleSheetBase *pSource = xIter->First();
+ while ( pSource )
+ {
+ SfxStyleSheetBase *pDest =
+ pMyPool->Find( pSource->GetName(), pSource->GetFamily() );
+ if ( !pDest )
+ {
+ pDest = &pMyPool->Make( pSource->GetName(),
+ pSource->GetFamily(), pSource->GetMask());
+ // Setting of parents, the next style
+ }
+ pFound[nFound].pSource = pSource;
+ pFound[nFound].pDest = pDest;
+ ++nFound;
+ pSource = xIter->Next();
+ }
+
+ for ( sal_uInt16 i = 0; i < nFound; ++i )
+ {
+ pFound[i].pDest->GetItemSet().PutExtended(pFound[i].pSource->GetItemSet(), SfxItemState::DONTCARE, SfxItemState::DEFAULT);
+ if(pFound[i].pSource->HasParentSupport())
+ pFound[i].pDest->SetParent(pFound[i].pSource->GetParent());
+ if(pFound[i].pSource->HasFollowSupport())
+ pFound[i].pDest->SetFollow(pFound[i].pSource->GetParent());
+ }
+}
+
+sfx2::StyleManager* SfxObjectShell::GetStyleManager()
+{
+ return nullptr;
+}
+
+namespace
+{
+ class QueryTemplateBox
+ {
+ private:
+ std::unique_ptr<weld::MessageDialog> m_xQueryBox;
+ public:
+ QueryTemplateBox(weld::Window* pParent, const OUString& rMessage)
+ : m_xQueryBox(Application::CreateMessageDialog(pParent, VclMessageType::Question, VclButtonsType::NONE, rMessage))
+ {
+ m_xQueryBox->add_button(SfxResId(STR_QRYTEMPL_UPDATE_BTN), RET_YES);
+ m_xQueryBox->add_button(SfxResId(STR_QRYTEMPL_KEEP_BTN), RET_NO);
+ m_xQueryBox->set_default_response(RET_YES);
+ m_xQueryBox->set_help_id(HID_QUERY_LOAD_TEMPLATE);
+ }
+ short run() { return m_xQueryBox->run(); }
+ };
+}
+
+void SfxObjectShell::UpdateFromTemplate_Impl( )
+
+/* [Description]
+
+ This internal method checks whether the document was created from a
+ template, and if this is newer than the document. If this is the case,
+ the user is asked if the Templates (StyleSheets) should be updated.
+ If this is answered positively, the StyleSheets are updated.
+*/
+
+{
+ // Storage-medium?
+ SfxMedium *pFile = GetMedium();
+ DBG_ASSERT( pFile, "cannot UpdateFromTemplate without medium" );
+ if ( !pFile )
+ return;
+
+ if ( !comphelper::isFileUrl( pFile->GetName() ) )
+ // update only for documents loaded from the local file system
+ return;
+
+ // tdf#113935 - do not remove this line - somehow, it makes the process
+ // of switching from viewing a read-only document to opening it in writable
+ // mode much faster.
+ uno::Reference< embed::XStorage > xDocStor = pFile->GetStorage(false);
+
+ // only for own storage formats
+ if ( !pFile->GetFilter() || !pFile->GetFilter()->IsOwnFormat() )
+ return;
+
+ const SfxUInt16Item* pUpdateDocItem = SfxItemSet::GetItem<SfxUInt16Item>(pFile->GetItemSet(), SID_UPDATEDOCMODE, false);
+ sal_Int16 bCanUpdateFromTemplate = pUpdateDocItem ? pUpdateDocItem->GetValue() : document::UpdateDocMode::NO_UPDATE;
+
+ // created from template?
+ uno::Reference<document::XDocumentProperties> xDocProps(getDocProperties());
+ const OUString aTemplName( xDocProps->getTemplateName() );
+ OUString aTemplURL( xDocProps->getTemplateURL() );
+ OUString aFoundName;
+
+ if ( !aTemplName.isEmpty() || (!aTemplURL.isEmpty() && !IsReadOnly()) )
+ {
+ // try to locate template, first using filename this must be done
+ // because writer global document uses this "great" idea to manage
+ // the templates of all parts in the master document but it is NOT
+ // an error if the template filename points not to a valid file
+ SfxDocumentTemplates aTempl;
+ if (!aTemplURL.isEmpty())
+ {
+ try {
+ aFoundName = ::rtl::Uri::convertRelToAbs(GetMedium()->GetName(),
+ aTemplURL);
+ } catch (::rtl::MalformedUriException const&) {
+ assert(false); // don't think that's supposed to happen?
+ }
+ }
+
+ if( aFoundName.isEmpty() && !aTemplName.isEmpty() )
+ // if the template filename did not lead to success,
+ // try to get a file name for the logical template name
+ aTempl.GetFull( u"", aTemplName, aFoundName );
+ }
+
+ if ( aFoundName.isEmpty() )
+ return;
+
+ // check existence of template storage
+ aTemplURL = aFoundName;
+
+ // should the document checked against changes in the template ?
+ if ( !IsQueryLoadTemplate() )
+ return;
+
+ bool bLoad = false;
+
+ // load document properties of template
+ bool bOK = false;
+ util::DateTime aTemplDate;
+ try
+ {
+ Reference<document::XDocumentProperties> const
+ xTemplateDocProps( document::DocumentProperties::create(
+ ::comphelper::getProcessComponentContext()));
+ xTemplateDocProps->loadFromMedium(aTemplURL,
+ Sequence<beans::PropertyValue>());
+ aTemplDate = xTemplateDocProps->getModificationDate();
+ bOK = true;
+ }
+ catch (const Exception&)
+ {
+ TOOLS_INFO_EXCEPTION("sfx.doc", "");
+ }
+
+ // if modify date was read successfully
+ if ( bOK )
+ {
+ // compare modify data of template with the last check date of the document
+ const util::DateTime aInfoDate( xDocProps->getTemplateDate() );
+ if ( aTemplDate > aInfoDate )
+ {
+ // ask user
+ if( bCanUpdateFromTemplate == document::UpdateDocMode::QUIET_UPDATE
+ || bCanUpdateFromTemplate == document::UpdateDocMode::FULL_UPDATE )
+ bLoad = true;
+ else if ( bCanUpdateFromTemplate == document::UpdateDocMode::ACCORDING_TO_CONFIG )
+ {
+ const OUString sMessage( SfxResId(STR_QRYTEMPL_MESSAGE).replaceAll( "$(ARG1)", aTemplName ) );
+ QueryTemplateBox aBox(Application::GetFrameWeld(GetDialogParent()), sMessage);
+ if (RET_YES == aBox.run())
+ bLoad = true;
+ }
+
+ if( !bLoad )
+ {
+ // user refuses, so don't ask again for this document
+ SetQueryLoadTemplate(false);
+ SetModified();
+ }
+ }
+ }
+
+ if ( !bLoad )
+ return;
+
+ // styles should be updated, create document in organizer mode to read in the styles
+ //TODO: testen!
+ SfxObjectShellLock xTemplDoc = CreateObjectByFactoryName( GetFactory().GetFactoryName(), SfxObjectCreateMode::ORGANIZER );
+ xTemplDoc->DoInitNew();
+
+ // TODO/MBA: do we need a BaseURL? Then LoadFrom must be extended!
+ //xTemplDoc->SetBaseURL( aFoundName );
+
+ // TODO/LATER: make sure that we don't use binary templates!
+ SfxMedium aMedium( aFoundName, StreamMode::STD_READ );
+ if ( xTemplDoc->LoadFrom( aMedium ) )
+ {
+ // transfer styles from xTemplDoc to this document
+ // TODO/MBA: make sure that no BaseURL is needed in *this* document
+ LoadStyles(*xTemplDoc);
+
+ // remember date/time of check
+ xDocProps->setTemplateDate(aTemplDate);
+ // TODO/LATER: new functionality to store document info is required ( didn't work for SO7 XML format )
+ }
+}
+
+bool SfxObjectShell::IsHelpDocument() const
+{
+ std::shared_ptr<const SfxFilter> pFilter = GetMedium()->GetFilter();
+ return (pFilter && pFilter->GetFilterName() == "writer_web_HTML_help");
+}
+
+void SfxObjectShell::ResetFromTemplate( const OUString& rTemplateName, const OUString& rFileName )
+{
+ // only care about resetting this data for LibreOffice formats otherwise
+ if ( !IsOwnStorageFormat( *GetMedium()) )
+ return;
+
+ uno::Reference<document::XDocumentProperties> xDocProps(getDocProperties());
+ xDocProps->setTemplateURL( OUString() );
+ xDocProps->setTemplateName( OUString() );
+ xDocProps->setTemplateDate( util::DateTime() );
+ xDocProps->resetUserData( OUString() );
+
+ // TODO/REFACTOR:
+ // Title?
+
+ if( !comphelper::isFileUrl( rFileName ) )
+ return;
+
+ OUString aFoundName;
+ if( SfxGetpApp()->Get_Impl()->GetDocumentTemplates()->GetFull( u"", rTemplateName, aFoundName ) )
+ {
+ INetURLObject aObj( rFileName );
+ xDocProps->setTemplateURL( aObj.GetMainURL(INetURLObject::DecodeMechanism::ToIUri) );
+ xDocProps->setTemplateName( rTemplateName );
+
+ ::DateTime now( ::DateTime::SYSTEM );
+ xDocProps->setTemplateDate( now.GetUNODateTime() );
+
+ SetQueryLoadTemplate( true );
+ }
+}
+
+bool SfxObjectShell::IsQueryLoadTemplate() const
+{
+ return pImpl->bQueryLoadTemplate;
+}
+
+bool SfxObjectShell::IsUseUserData() const
+{
+ return pImpl->bUseUserData;
+}
+
+bool SfxObjectShell::IsUseThumbnailSave() const
+{
+ return pImpl->bUseThumbnailSave;
+}
+
+void SfxObjectShell::SetQueryLoadTemplate( bool bNew )
+{
+ if ( pImpl->bQueryLoadTemplate != bNew )
+ SetModified();
+ pImpl->bQueryLoadTemplate = bNew;
+}
+
+void SfxObjectShell::SetUseUserData( bool bNew )
+{
+ if ( pImpl->bUseUserData != bNew )
+ SetModified();
+ pImpl->bUseUserData = bNew;
+}
+
+void SfxObjectShell::SetUseThumbnailSave( bool _bNew )
+{
+ if ( pImpl->bUseThumbnailSave != _bNew )
+ SetModified();
+ pImpl->bUseThumbnailSave = _bNew;
+}
+
+bool SfxObjectShell::IsLoadReadonly() const
+{
+ return pImpl->bLoadReadonly;
+}
+
+bool SfxObjectShell::IsSaveVersionOnClose() const
+{
+ return pImpl->bSaveVersionOnClose;
+}
+
+void SfxObjectShell::SetLoadReadonly( bool bNew )
+{
+ if ( pImpl->bLoadReadonly != bNew )
+ SetModified();
+ pImpl->bLoadReadonly = bNew;
+}
+
+void SfxObjectShell::SetSaveVersionOnClose( bool bNew )
+{
+ if ( pImpl->bSaveVersionOnClose != bNew )
+ SetModified();
+ pImpl->bSaveVersionOnClose = bNew;
+}
+
+sal_uInt32 SfxObjectShell::GetModifyPasswordHash() const
+{
+ return pImpl->m_nModifyPasswordHash;
+}
+
+bool SfxObjectShell::SetModifyPasswordHash( sal_uInt32 nHash )
+{
+ if ( ( !IsReadOnly() && !IsReadOnlyUI() )
+ || !(pImpl->nFlagsInProgress & SfxLoadedFlags::MAINDOCUMENT ) )
+ {
+ // the hash can be changed only in editable documents,
+ // or during loading of document
+ pImpl->m_nModifyPasswordHash = nHash;
+ return true;
+ }
+
+ return false;
+}
+
+const uno::Sequence< beans::PropertyValue >& SfxObjectShell::GetModifyPasswordInfo() const
+{
+ return pImpl->m_aModifyPasswordInfo;
+}
+
+bool SfxObjectShell::SetModifyPasswordInfo( const uno::Sequence< beans::PropertyValue >& aInfo )
+{
+ if ( ( !IsReadOnly() && !IsReadOnlyUI() )
+ || !(pImpl->nFlagsInProgress & SfxLoadedFlags::MAINDOCUMENT ) )
+ {
+ // the hash can be changed only in editable documents,
+ // or during loading of document
+ pImpl->m_aModifyPasswordInfo = aInfo;
+ return true;
+ }
+
+ return false;
+}
+
+void SfxObjectShell::SetModifyPasswordEntered( bool bEntered )
+{
+ pImpl->m_bModifyPasswordEntered = bEntered;
+}
+
+bool SfxObjectShell::IsModifyPasswordEntered() const
+{
+ return pImpl->m_bModifyPasswordEntered;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/doc/objembed.cxx b/sfx2/source/doc/objembed.cxx
new file mode 100644
index 000000000..91e886c8a
--- /dev/null
+++ b/sfx2/source/doc/objembed.cxx
@@ -0,0 +1,217 @@
+/* -*- 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/objsh.hxx>
+#include <sfx2/app.hxx>
+#include <objshimp.hxx>
+#include <sfx2/event.hxx>
+
+#include <comphelper/fileformat.h>
+#include <tools/fract.hxx>
+#include <vcl/transfer.hxx>
+#include <vcl/outdev.hxx>
+#include <vcl/gdimtf.hxx>
+
+using namespace ::com::sun::star;
+
+
+Printer* SfxObjectShell::GetDocumentPrinter()
+{
+ SfxObjectShell* pParent = GetParentShell(GetModel());
+ if ( pParent )
+ return pParent->GetDocumentPrinter();
+ return nullptr;
+}
+
+
+OutputDevice* SfxObjectShell::GetDocumentRefDev()
+{
+ SfxObjectShell* pParent = GetParentShell(GetModel());
+ if ( pParent )
+ return pParent->GetDocumentRefDev();
+ return nullptr;
+}
+
+
+void SfxObjectShell::OnDocumentPrinterChanged( Printer* /*pNewPrinter*/ )
+{
+ // virtual method
+}
+
+
+tools::Rectangle SfxObjectShell::GetVisArea( sal_uInt16 nAspect ) const
+{
+ if( nAspect == ASPECT_CONTENT )
+ return pImpl->m_aVisArea;
+ else if( nAspect == ASPECT_THUMBNAIL )
+ {
+ tools::Rectangle aRect;
+ aRect.SetSize( OutputDevice::LogicToLogic( Size( 5000, 5000 ),
+ MapMode(MapUnit::Map100thMM), MapMode(GetMapUnit())));
+ return aRect;
+ }
+ return tools::Rectangle();
+}
+
+
+const tools::Rectangle& SfxObjectShell::GetVisArea() const
+{
+ pImpl->m_aVisArea = GetVisArea( ASPECT_CONTENT );
+ return pImpl->m_aVisArea;
+}
+
+
+void SfxObjectShell::SetVisArea( const tools::Rectangle & rVisArea )
+{
+ if( pImpl->m_aVisArea != rVisArea )
+ {
+ pImpl->m_aVisArea = rVisArea;
+ if ( GetCreateMode() == SfxObjectCreateMode::EMBEDDED )
+ {
+ if ( IsEnableSetModified() )
+ SetModified();
+
+ SfxGetpApp()->NotifyEvent(SfxEventHint( SfxEventHintId::VisAreaChanged, GlobalEventConfig::GetEventName(GlobalEventId::VISAREACHANGED), this));
+ }
+ }
+}
+
+
+void SfxObjectShell::SetVisAreaSize( const Size & rVisSize )
+{
+ SetVisArea( tools::Rectangle( GetVisArea().TopLeft(), rVisSize ) );
+}
+
+
+MapUnit SfxObjectShell::GetMapUnit() const
+{
+ return pImpl->m_nMapUnit;
+}
+
+
+void SfxObjectShell::SetMapUnit( MapUnit nMapUnit )
+{
+ pImpl->m_nMapUnit = nMapUnit;
+}
+
+
+void SfxObjectShell::FillTransferableObjectDescriptor( TransferableObjectDescriptor& rDesc ) const
+{
+ SotClipboardFormatId nClipFormat;
+ FillClass( &rDesc.maClassName, &nClipFormat, &rDesc.maTypeName, SOFFICE_FILEFORMAT_CURRENT );
+
+ rDesc.mnViewAspect = ASPECT_CONTENT;
+ rDesc.maSize = OutputDevice::LogicToLogic(GetVisArea().GetSize(), MapMode(GetMapUnit()), MapMode(MapUnit::Map100thMM));
+ rDesc.maDragStartPos = Point();
+ rDesc.maDisplayName.clear();
+}
+
+
+void SfxObjectShell::DoDraw( OutputDevice* pDev,
+ const Point & rObjPos,
+ const Size & rSize,
+ const JobSetup & rSetup,
+ sal_uInt16 nAspect )
+{
+ if (!rSize.Width() || !rSize.Height())
+ return;
+
+ MapMode aMod = pDev->GetMapMode();
+ Size aSize = GetVisArea( nAspect ).GetSize();
+ MapMode aWilliMode( GetMapUnit() );
+ aSize = pDev->LogicToLogic( aSize, &aWilliMode, &aMod );
+ if( aSize.Width() && aSize.Height() )
+ {
+ Fraction aXF( rSize.Width(), aSize.Width() );
+ Fraction aYF( rSize.Height(), aSize.Height() );
+
+ DoDraw_Impl( pDev, rObjPos, aXF, aYF, rSetup, nAspect );
+ }
+}
+
+
+void SfxObjectShell::DoDraw_Impl( OutputDevice* pDev,
+ const Point & rViewPos,
+ const Fraction & rScaleX,
+ const Fraction & rScaleY,
+ const JobSetup & rSetup,
+ sal_uInt16 nAspect )
+{
+ tools::Rectangle aVisArea = GetVisArea( nAspect );
+ // MapUnit of the target
+ MapMode aMapMode( GetMapUnit() );
+ aMapMode.SetScaleX( rScaleX );
+ aMapMode.SetScaleY( rScaleY );
+
+ // Target in Pixels
+ Point aOrg = pDev->LogicToLogic( rViewPos, nullptr, &aMapMode );
+ Point aDelta = aOrg - aVisArea.TopLeft();
+
+ // Origin moved according to the viewable area
+ // Origin set with Scale
+ aMapMode.SetOrigin( aDelta );
+
+ // Secure the Device settings
+ pDev->Push();
+
+ vcl::Region aRegion;
+ if( pDev->IsClipRegion() && pDev->GetOutDevType() != OUTDEV_PRINTER )
+ {
+ aRegion = pDev->GetClipRegion();
+ aRegion = pDev->LogicToPixel( aRegion );
+ }
+ pDev->SetRelativeMapMode( aMapMode );
+
+ GDIMetaFile * pMtf = pDev->GetConnectMetaFile();
+ if( pMtf )
+ {
+ if( pMtf->IsRecord() && pDev->GetOutDevType() != OUTDEV_PRINTER )
+ pMtf->Stop();
+ else
+ pMtf = nullptr;
+ }
+ if( pDev->IsClipRegion() && pDev->GetOutDevType() != OUTDEV_PRINTER )
+ {
+ aRegion = pDev->PixelToLogic( aRegion );
+ pDev->SetClipRegion( aRegion );
+ }
+ if( pMtf )
+ pMtf->Record( pDev );
+
+ Draw( pDev, rSetup, nAspect );
+
+ // Restore Device settings
+ pDev->Pop();
+
+}
+
+comphelper::EmbeddedObjectContainer& SfxObjectShell::GetEmbeddedObjectContainer() const
+{
+ if ( !pImpl->mxObjectContainer )
+ pImpl->mxObjectContainer.reset(new comphelper::EmbeddedObjectContainer( const_cast<SfxObjectShell*>(this)->GetStorage(), GetModel() ));
+ return *pImpl->mxObjectContainer;
+}
+
+void SfxObjectShell::ClearEmbeddedObjects()
+{
+ // frees all space taken by embedded objects
+ pImpl->mxObjectContainer.reset();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/doc/objitem.cxx b/sfx2/source/doc/objitem.cxx
new file mode 100644
index 000000000..e776e6608
--- /dev/null
+++ b/sfx2/source/doc/objitem.cxx
@@ -0,0 +1,97 @@
+/* -*- 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/objsh.hxx>
+#include <sfx2/objitem.hxx>
+#include <com/sun/star/frame/XModel.hpp>
+
+
+SfxPoolItem* SfxObjectShellItem::CreateDefault() { return new SfxObjectShellItem; }
+
+SfxPoolItem* SfxObjectItem::CreateDefault() { return new SfxObjectItem; }
+
+bool SfxObjectShellItem::operator==( const SfxPoolItem &rItem ) const
+{
+ return SfxPoolItem::operator==(rItem) &&
+ static_cast<const SfxObjectShellItem&>(rItem).pObjSh == pObjSh;
+}
+
+SfxObjectShellItem* SfxObjectShellItem::Clone( SfxItemPool *) const
+{
+ return new SfxObjectShellItem( *this );
+}
+
+bool SfxObjectShellItem::QueryValue( css::uno::Any& rVal, sal_uInt8 /*nMemberId*/ ) const
+{
+ if ( pObjSh )
+ {
+ // This item MUST provide a model. Please don't change this, there are UNO-based
+ // implementations which need it!!
+ rVal <<= pObjSh->GetModel();
+ }
+ else
+ {
+ rVal <<= css::uno::Reference< css::frame::XModel >();
+ }
+ return true;
+}
+
+bool SfxObjectShellItem::PutValue( const css::uno::Any& rVal, sal_uInt8 /*nMemberId*/ )
+{
+ // This item MUST have a model. Please don't change this, there are UNO-based
+ // implementations which need it!!
+ css::uno::Reference< css::frame::XModel > xModel;
+
+ if ( rVal >>= xModel )
+ {
+ pObjSh = SfxObjectShell::GetShellFromComponent(xModel);
+ return true;
+ }
+
+ return true;
+}
+
+SfxObjectItem::SfxObjectItem( sal_uInt16 nWhichId, SfxShell *pSh )
+: SfxPoolItem( nWhichId ),
+ _pSh( pSh )
+{}
+
+bool SfxObjectItem::operator==( const SfxPoolItem &rItem ) const
+{
+ return SfxPoolItem::operator==(rItem) &&
+ static_cast<const SfxObjectItem&>(rItem)._pSh == _pSh;
+}
+
+SfxObjectItem* SfxObjectItem::Clone( SfxItemPool *) const
+{
+ return new SfxObjectItem( *this );
+}
+
+bool SfxObjectItem::QueryValue(css::uno::Any&, sal_uInt8) const
+{
+ return false;
+}
+
+bool SfxObjectItem::PutValue(const css::uno::Any&, sal_uInt8)
+{
+ return false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/doc/objmisc.cxx b/sfx2/source/doc/objmisc.cxx
new file mode 100644
index 000000000..873ab8b9b
--- /dev/null
+++ b/sfx2/source/doc/objmisc.cxx
@@ -0,0 +1,1920 @@
+/* -*- 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 <config_features.h>
+
+#include <tools/inetmsg.hxx>
+#include <tools/diagnose_ex.h>
+#include <svl/eitem.hxx>
+#include <svl/stritem.hxx>
+#include <svl/intitem.hxx>
+#include <svtools/svparser.hxx>
+#include <cppuhelper/exc_hlp.hxx>
+#include <sal/log.hxx>
+#include <o3tl/string_view.hxx>
+
+#include <com/sun/star/awt/XTopWindow.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/container/XChild.hpp>
+#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
+#include <com/sun/star/document/XDocumentProperties.hpp>
+#include <com/sun/star/document/MacroExecMode.hpp>
+#include <com/sun/star/document/XScriptInvocationContext.hpp>
+#include <com/sun/star/embed/EmbedStates.hpp>
+#include <com/sun/star/embed/XEmbeddedObject.hpp>
+#include <com/sun/star/script/provider/theMasterScriptProviderFactory.hpp>
+#include <com/sun/star/script/provider/XScript.hpp>
+#include <com/sun/star/script/provider/XScriptProvider.hpp>
+#include <com/sun/star/script/provider/XScriptProviderSupplier.hpp>
+#include <com/sun/star/uri/UriReferenceFactory.hpp>
+#include <com/sun/star/uri/XVndSunStarScriptUrl.hpp>
+#include <com/sun/star/util/XModifiable.hpp>
+
+#include <com/sun/star/uno/Reference.h>
+#include <com/sun/star/uno/Any.h>
+#include <com/sun/star/task/ErrorCodeRequest.hpp>
+
+#include <comphelper/processfactory.hxx>
+#include <comphelper/string.hxx>
+
+#include <com/sun/star/security/DocumentDigitalSignatures.hpp>
+#include <com/sun/star/task/DocumentMacroConfirmationRequest.hpp>
+#include <com/sun/star/task/InteractionClassification.hpp>
+#include <com/sun/star/task/XInteractionHandler.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+
+#include <basic/basmgr.hxx>
+#include <basic/sberrors.hxx>
+#include <vcl/weld.hxx>
+#include <basic/sbx.hxx>
+#include <svtools/sfxecode.hxx>
+
+#include <unotools/ucbhelper.hxx>
+#include <tools/urlobj.hxx>
+#include <svl/sharecontrolfile.hxx>
+#include <rtl/uri.hxx>
+#include <vcl/svapp.hxx>
+#include <framework/interaction.hxx>
+#include <framework/documentundoguard.hxx>
+#include <comphelper/interaction.hxx>
+#include <comphelper/storagehelper.hxx>
+#include <comphelper/documentconstants.hxx>
+#include <comphelper/namedvaluecollection.hxx>
+#include <officecfg/Office/Common.hxx>
+
+#include <sfx2/signaturestate.hxx>
+#include <sfx2/app.hxx>
+#include <appdata.hxx>
+#include <sfx2/request.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/sfxresid.hxx>
+#include <sfx2/docfile.hxx>
+#include <sfx2/objsh.hxx>
+#include <objshimp.hxx>
+#include <sfx2/event.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/sfxuno.hxx>
+#include <sfx2/module.hxx>
+#include <sfx2/docfac.hxx>
+#include <sfx2/sfxsids.hrc>
+#include <sfx2/strings.hrc>
+#include <workwin.hxx>
+#include <sfx2/sfxdlg.hxx>
+#include <sfx2/infobar.hxx>
+#include <sfx2/sfxbasemodel.hxx>
+#include <openflag.hxx>
+#include "objstor.hxx"
+#include <appopen.hxx>
+
+#include <memory>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::ucb;
+using namespace ::com::sun::star::document;
+using namespace ::com::sun::star::frame;
+using namespace ::com::sun::star::script;
+using namespace ::com::sun::star::script::provider;
+using namespace ::com::sun::star::container;
+
+// class SfxHeaderAttributes_Impl ----------------------------------------
+
+namespace {
+
+class SfxHeaderAttributes_Impl : public SvKeyValueIterator
+{
+private:
+ SfxObjectShell* pDoc;
+ SvKeyValueIteratorRef xIter;
+ bool bAlert;
+
+public:
+ explicit SfxHeaderAttributes_Impl( SfxObjectShell* pSh ) :
+ pDoc( pSh ),
+ xIter( pSh->GetMedium()->GetHeaderAttributes_Impl() ),
+ bAlert( false ) {}
+
+ virtual bool GetFirst( SvKeyValue& rKV ) override { return xIter->GetFirst( rKV ); }
+ virtual bool GetNext( SvKeyValue& rKV ) override { return xIter->GetNext( rKV ); }
+ virtual void Append( const SvKeyValue& rKV ) override;
+
+ void ClearForSourceView() { xIter = new SvKeyValueIterator; bAlert = false; }
+ void SetAttributes();
+ void SetAttribute( const SvKeyValue& rKV );
+};
+
+}
+
+sal_uInt16 const aTitleMap_Impl[3][2] =
+{
+ // local remote
+ /* SFX_TITLE_CAPTION */ { SFX_TITLE_FILENAME, SFX_TITLE_TITLE },
+ /* SFX_TITLE_PICKLIST */ { 32, SFX_TITLE_FULLNAME },
+ /* SFX_TITLE_HISTORY */ { 32, SFX_TITLE_FULLNAME }
+};
+
+
+bool SfxObjectShell::IsAbortingImport() const
+{
+ return pImpl->bIsAbortingImport;
+}
+
+
+uno::Reference<document::XDocumentProperties>
+SfxObjectShell::getDocProperties() const
+{
+ uno::Reference<document::XDocumentPropertiesSupplier> xDPS(
+ GetModel(), uno::UNO_QUERY_THROW);
+ uno::Reference<document::XDocumentProperties> xDocProps(
+ xDPS->getDocumentProperties());
+ DBG_ASSERT(xDocProps.is(),
+ "SfxObjectShell: model has no DocumentProperties");
+ return xDocProps;
+}
+
+
+void SfxObjectShell::DoFlushDocInfo()
+{
+}
+
+
+// Note: the only thing that calls this is the modification event handler
+// that is installed at the XDocumentProperties
+void SfxObjectShell::FlushDocInfo()
+{
+ if ( IsLoading() )
+ return;
+
+ SetModified();
+ uno::Reference<document::XDocumentProperties> xDocProps(getDocProperties());
+ DoFlushDocInfo(); // call template method
+ const OUString url(xDocProps->getAutoloadURL());
+ sal_Int32 delay(xDocProps->getAutoloadSecs());
+ SetAutoLoad( INetURLObject(url), delay * 1000,
+ (delay > 0) || !url.isEmpty() );
+}
+
+void SfxObjectShell::AppendInfoBarWhenReady(const OUString& sId, const OUString& sPrimaryMessage,
+ const OUString& sSecondaryMessage,
+ InfobarType aInfobarType, bool bShowCloseButton)
+{
+ InfobarData aInfobarData;
+ aInfobarData.msId = sId;
+ aInfobarData.msPrimaryMessage = sPrimaryMessage;
+ aInfobarData.msSecondaryMessage = sSecondaryMessage;
+ aInfobarData.maInfobarType = aInfobarType;
+ aInfobarData.mbShowCloseButton = bShowCloseButton;
+ Get_Impl()->m_aPendingInfobars.emplace_back(aInfobarData);
+}
+
+std::vector<InfobarData>& SfxObjectShell::getPendingInfobars()
+{
+ return Get_Impl()->m_aPendingInfobars;
+}
+
+void SfxObjectShell::SetError(ErrCode lErr)
+{
+ if (pImpl->lErr==ERRCODE_NONE)
+ {
+ pImpl->lErr=lErr;
+ }
+}
+
+ErrCode SfxObjectShell::GetError() const
+{
+ return GetErrorCode().IgnoreWarning();
+}
+
+ErrCode SfxObjectShell::GetErrorCode() const
+{
+ ErrCode lError=pImpl->lErr;
+ if(!lError && GetMedium())
+ lError=GetMedium()->GetErrorCode();
+ return lError;
+}
+
+void SfxObjectShell::ResetError()
+{
+ pImpl->lErr=ERRCODE_NONE;
+ SfxMedium * pMed = GetMedium();
+ if( pMed )
+ pMed->ResetError();
+}
+
+void SfxObjectShell::EnableSetModified( bool bEnable )
+{
+ SAL_INFO_IF( bEnable == pImpl->m_bEnableSetModified, "sfx", "SFX_PERSIST: EnableSetModified 2x called with the same value" );
+ pImpl->m_bEnableSetModified = bEnable;
+}
+
+
+bool SfxObjectShell::IsEnableSetModified() const
+{
+ return pImpl->m_bEnableSetModified && !IsReadOnly();
+}
+
+
+bool SfxObjectShell::IsModified() const
+{
+ if ( pImpl->m_bIsModified )
+ return true;
+
+ if ( !pImpl->m_xDocStorage.is() || IsReadOnly() )
+ {
+ // if the document still has no storage and is not set to be modified explicitly it is not modified
+ // a readonly document is also not modified
+
+ return false;
+ }
+
+ if (pImpl->mxObjectContainer)
+ {
+ const uno::Sequence < OUString > aNames = GetEmbeddedObjectContainer().GetObjectNames();
+ for ( const auto& rName : aNames )
+ {
+ uno::Reference < embed::XEmbeddedObject > xObj = GetEmbeddedObjectContainer().GetEmbeddedObject( rName );
+ OSL_ENSURE( xObj.is(), "An empty entry in the embedded objects list!" );
+ if ( xObj.is() )
+ {
+ try
+ {
+ sal_Int32 nState = xObj->getCurrentState();
+ if ( nState != embed::EmbedStates::LOADED )
+ {
+ uno::Reference< util::XModifiable > xModifiable( xObj->getComponent(), uno::UNO_QUERY );
+ if ( xModifiable.is() && xModifiable->isModified() )
+ return true;
+ }
+ }
+ catch( uno::Exception& )
+ {}
+ }
+ }
+ }
+
+ return false;
+}
+
+
+void SfxObjectShell::SetModified( bool bModifiedP )
+{
+ SAL_INFO_IF( !bModifiedP && !IsEnableSetModified(), "sfx",
+ "SFX_PERSIST: SetModified( sal_False ), although IsEnableSetModified() == sal_False" );
+
+ if( !IsEnableSetModified() )
+ return;
+
+ if( pImpl->m_bIsModified != bModifiedP )
+ {
+ pImpl->m_bIsModified = bModifiedP;
+ ModifyChanged();
+ }
+}
+
+
+void SfxObjectShell::ModifyChanged()
+{
+ if ( pImpl->bClosing )
+ // SetModified dispose of the models!
+ return;
+
+
+ SfxViewFrame* pViewFrame = SfxViewFrame::Current();
+ if ( pViewFrame )
+ pViewFrame->GetBindings().Invalidate( SID_SAVEDOCS );
+
+ Invalidate( SID_SIGNATURE );
+ Invalidate( SID_MACRO_SIGNATURE );
+ Broadcast( SfxHint( SfxHintId::TitleChanged ) ); // xmlsec05, signed state might change in title...
+
+ SfxGetpApp()->NotifyEvent( SfxEventHint( SfxEventHintId::ModifyChanged, GlobalEventConfig::GetEventName(GlobalEventId::MODIFYCHANGED), this ) );
+}
+
+
+bool SfxObjectShell::IsReadOnlyUI() const
+
+/* [Description]
+
+ Returns sal_True if the document for the UI is treated as r/o. This is
+ regardless of the actual r/o, which can be checked with <IsReadOnly()>.
+*/
+
+{
+ return pImpl->bReadOnlyUI;
+}
+
+
+bool SfxObjectShell::IsReadOnlyMedium() const
+
+/* [Description]
+
+ Returns sal_True when the medium is r/o, for instance when opened as r/o.
+*/
+
+{
+ if ( !pMedium )
+ return true;
+ return pMedium->IsReadOnly();
+}
+
+bool SfxObjectShell::IsOriginallyReadOnlyMedium() const
+{
+ return pMedium == nullptr || pMedium->IsOriginallyReadOnly();
+}
+
+bool SfxObjectShell::IsOriginallyLoadedReadOnlyMedium() const
+{
+ return pMedium != nullptr && pMedium->IsOriginallyLoadedReadOnly();
+}
+
+
+void SfxObjectShell::SetReadOnlyUI( bool bReadOnly )
+
+/* [Description]
+
+ Turns the document in a r/o and r/w state respectively without reloading
+ it and without changing the open mode of the medium.
+*/
+
+{
+ if ( bReadOnly != pImpl->bReadOnlyUI )
+ {
+ pImpl->bReadOnlyUI = bReadOnly;
+ Broadcast( SfxHint(SfxHintId::ModeChanged) );
+ }
+}
+
+
+void SfxObjectShell::SetReadOnly()
+{
+ // Let the document be completely readonly, means that the
+ // medium open mode is adjusted accordingly, and the write lock
+ // on the file is removed.
+
+ if ( !pMedium || IsReadOnlyMedium() )
+ return;
+
+ bool bWasROUI = IsReadOnly();
+
+ pMedium->UnlockFile( false );
+
+ // the storage-based mediums are already based on the temporary file
+ // so UnlockFile has already closed the locking stream
+ if ( !pMedium->HasStorage_Impl() && IsLoadingFinished() )
+ pMedium->CloseInStream();
+
+ pMedium->SetOpenMode( SFX_STREAM_READONLY, true );
+ pMedium->GetItemSet()->Put( SfxBoolItem( SID_DOC_READONLY, true ) );
+
+ if ( !bWasROUI )
+ Broadcast( SfxHint(SfxHintId::ModeChanged) );
+}
+
+
+bool SfxObjectShell::IsReadOnly() const
+{
+ return pImpl->bReadOnlyUI || pMedium == nullptr;
+}
+
+
+bool SfxObjectShell::IsInModalMode() const
+{
+ return pImpl->bModalMode || pImpl->bRunningMacro;
+}
+
+bool SfxObjectShell::AcceptStateUpdate() const
+{
+ return !IsInModalMode();
+}
+
+
+void SfxObjectShell::SetMacroMode_Impl( bool bModal )
+{
+ if ( !pImpl->bRunningMacro != !bModal )
+ {
+ pImpl->bRunningMacro = bModal;
+ Broadcast( SfxHint( SfxHintId::ModeChanged ) );
+ }
+}
+
+
+void SfxObjectShell::SetModalMode_Impl( bool bModal )
+{
+ // Broadcast only if modified, or otherwise it will possibly go into
+ // an endless loop
+ if ( pImpl->bModalMode == bModal )
+ return;
+
+ // Central count
+ sal_uInt16 &rDocModalCount = SfxGetpApp()->Get_Impl()->nDocModalMode;
+ if ( bModal )
+ ++rDocModalCount;
+ else
+ --rDocModalCount;
+
+ // Switch
+ pImpl->bModalMode = bModal;
+ Broadcast( SfxHint( SfxHintId::ModeChanged ) );
+}
+
+#if HAVE_FEATURE_MULTIUSER_ENVIRONMENT
+
+bool SfxObjectShell::SwitchToShared( bool bShared, bool bSave )
+{
+ bool bResult = true;
+
+ if ( bShared != IsDocShared() )
+ {
+ OUString aOrigURL = GetMedium()->GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE );
+
+ if ( aOrigURL.isEmpty() && bSave )
+ {
+ // this is a new document, let it be stored before switching to the shared mode;
+ // the storing should be done without shared flag, since it is possible that the
+ // target location does not allow to create sharing control file;
+ // the shared flag will be set later after creation of sharing control file
+ SfxViewFrame* pViewFrame = SfxViewFrame::GetFirst( this );
+
+ if ( pViewFrame )
+ {
+ // TODO/LATER: currently the application guards against the reentrance problem
+ const SfxPoolItem* pItem = pViewFrame->GetBindings().ExecuteSynchron( HasName() ? SID_SAVEDOC : SID_SAVEASDOC );
+ const SfxBoolItem* pResult = dynamic_cast<const SfxBoolItem*>( pItem );
+ bResult = ( pResult && pResult->GetValue() );
+ if ( bResult )
+ aOrigURL = GetMedium()->GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE );
+ }
+ }
+
+ bool bOldValue = HasSharedXMLFlagSet();
+ SetSharedXMLFlag( bShared );
+
+ bool bRemoveEntryOnError = false;
+ if ( bResult && bShared )
+ {
+ try
+ {
+ ::svt::ShareControlFile aControlFile( aOrigURL );
+ aControlFile.InsertOwnEntry();
+ bRemoveEntryOnError = true;
+ }
+ catch( uno::Exception& )
+ {
+ bResult = false;
+ }
+ }
+
+ if ( bResult && bSave )
+ {
+ SfxViewFrame* pViewFrame = SfxViewFrame::GetFirst( this );
+
+ if ( pViewFrame )
+ {
+ // TODO/LATER: currently the application guards against the reentrance problem
+ SetModified(); // the modified flag has to be set to let the document be stored with the shared flag
+ try
+ {
+ // Do *not* use dispatch mechanism in this place - we don't want others (extensions etc.) to intercept this.
+ pImpl->pBaseModel->store();
+ bResult = true;
+ }
+ catch (...)
+ {
+ bResult = false;
+ }
+ }
+ }
+
+ if ( bResult )
+ {
+ // TODO/LATER: Is it possible that the following calls fail?
+ if ( bShared )
+ {
+ pImpl->m_aSharedFileURL = aOrigURL;
+ GetMedium()->SwitchDocumentToTempFile();
+ }
+ else
+ {
+ const OUString aTempFileURL = pMedium->GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE );
+ GetMedium()->SwitchDocumentToFile( GetSharedFileURL() );
+ pImpl->m_aSharedFileURL.clear();
+
+ // now remove the temporary file the document was based on
+ ::utl::UCBContentHelper::Kill( aTempFileURL );
+
+ try
+ {
+ // aOrigURL can not be used since it contains an old value
+ ::svt::ShareControlFile aControlFile( GetMedium()->GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
+ aControlFile.RemoveFile();
+ }
+ catch( uno::Exception& )
+ {
+ }
+ }
+ }
+ else
+ {
+ // the saving has failed!
+ if ( bRemoveEntryOnError )
+ {
+ try
+ {
+ ::svt::ShareControlFile aControlFile( aOrigURL );
+ aControlFile.RemoveEntry();
+ }
+ catch( uno::Exception& )
+ {}
+ }
+
+ SetSharedXMLFlag( bOldValue );
+ }
+ }
+ else
+ bResult = false; // the second switch to the same mode
+
+ if ( bResult )
+ SetTitle( "" );
+
+ return bResult;
+}
+
+
+void SfxObjectShell::FreeSharedFile( const OUString& aTempFileURL )
+{
+ SetSharedXMLFlag( false );
+
+ if ( !IsDocShared() || aTempFileURL.isEmpty()
+ || ::utl::UCBContentHelper::EqualURLs( aTempFileURL, GetSharedFileURL() ) )
+ return;
+
+ if ( pImpl->m_bAllowShareControlFileClean )
+ {
+ try
+ {
+ ::svt::ShareControlFile aControlFile( GetSharedFileURL() );
+ aControlFile.RemoveEntry();
+ }
+ catch( uno::Exception& )
+ {
+ }
+ }
+
+ // the cleaning is forbidden only once
+ pImpl->m_bAllowShareControlFileClean = true;
+
+ // now remove the temporary file the document is based currently on
+ ::utl::UCBContentHelper::Kill( aTempFileURL );
+
+ pImpl->m_aSharedFileURL.clear();
+}
+
+
+void SfxObjectShell::DoNotCleanShareControlFile()
+{
+ pImpl->m_bAllowShareControlFileClean = false;
+}
+
+
+void SfxObjectShell::SetSharedXMLFlag( bool bFlag ) const
+{
+ pImpl->m_bSharedXMLFlag = bFlag;
+}
+
+
+bool SfxObjectShell::HasSharedXMLFlagSet() const
+{
+ return pImpl->m_bSharedXMLFlag;
+}
+
+#endif
+
+bool SfxObjectShell::IsDocShared() const
+{
+#if HAVE_FEATURE_MULTIUSER_ENVIRONMENT
+ return ( !pImpl->m_aSharedFileURL.isEmpty() );
+#else
+ return false;
+#endif
+}
+
+
+OUString SfxObjectShell::GetSharedFileURL() const
+{
+#if HAVE_FEATURE_MULTIUSER_ENVIRONMENT
+ return pImpl->m_aSharedFileURL;
+#else
+ return OUString();
+#endif
+}
+
+Size SfxObjectShell::GetFirstPageSize() const
+{
+ return GetVisArea(ASPECT_THUMBNAIL).GetSize();
+}
+
+
+IndexBitSet& SfxObjectShell::GetNoSet_Impl()
+{
+ return pImpl->aBitSet;
+}
+
+
+// changes the title of the document
+
+void SfxObjectShell::SetTitle
+(
+ const OUString& rTitle // the new Document Title
+)
+
+/* [Description]
+
+ With this method, the title of the document can be set.
+ This corresponds initially to the full file name. A setting of the
+ title does not affect the file name, but it will be shown in the
+ Caption-Bars of the MDI-window.
+*/
+
+{
+
+ // Nothing to do?
+ if ( ( ( HasName() && pImpl->aTitle == rTitle )
+ || ( !HasName() && GetTitle() == rTitle ) )
+ && !IsDocShared() )
+ return;
+
+ SfxApplication *pSfxApp = SfxGetpApp();
+
+ // If possible release the unnamed number.
+ if ( pImpl->bIsNamedVisible && USHRT_MAX != pImpl->nVisualDocumentNumber )
+ {
+ pSfxApp->ReleaseIndex(pImpl->nVisualDocumentNumber);
+ pImpl->bIsNamedVisible = false;
+ }
+
+ // Set Title
+ pImpl->aTitle = rTitle;
+
+ // Notification
+ if ( GetMedium() )
+ {
+ SfxShell::SetName( GetTitle(SFX_TITLE_APINAME) );
+ Broadcast( SfxHint(SfxHintId::TitleChanged) );
+ }
+}
+
+
+
+OUString SfxObjectShell::GetTitle( sal_uInt16 nMaxLength ) const
+
+/* [Description]
+
+ Returns the title or logical file name of the document, depending on the
+ 'nMaxLength'.
+
+ If the file name with path is used, the Name shortened by replacing one or
+ more directory names with "...", URLs are currently always returned
+ in complete form.
+*/
+
+{
+ SfxMedium *pMed = GetMedium();
+ if ( IsLoading() )
+ return OUString();
+
+ // Create Title?
+ if ( SFX_TITLE_DETECT == nMaxLength && pImpl->aTitle.isEmpty() )
+ {
+ static bool bRecur = false;
+ if ( bRecur )
+ return "-not available-";
+ bRecur = true;
+
+ OUString aTitle;
+
+ if ( pMed )
+ {
+ const SfxStringItem* pNameItem = SfxItemSet::GetItem<SfxStringItem>(pMed->GetItemSet(), SID_DOCINFO_TITLE, false);
+ if ( pNameItem )
+ aTitle = pNameItem->GetValue();
+ }
+
+ if ( aTitle.isEmpty() )
+ aTitle = GetTitle( SFX_TITLE_FILENAME );
+
+ bRecur = false;
+ return aTitle;
+ }
+
+ if (SFX_TITLE_APINAME == nMaxLength )
+ return GetAPIName();
+
+ // Picklist/Caption is mapped
+ if ( pMed && ( nMaxLength == SFX_TITLE_CAPTION || nMaxLength == SFX_TITLE_PICKLIST ) )
+ {
+ // If a specific title was given at open:
+ // important for URLs: use INetProtocol::File for which the set title is not
+ // considered. (See below, analysis of aTitleMap_Impl)
+ const SfxStringItem* pNameItem = SfxItemSet::GetItem<SfxStringItem>(pMed->GetItemSet(), SID_DOCINFO_TITLE, false);
+ if ( pNameItem )
+ return pNameItem->GetValue();
+ }
+
+ // Still unnamed?
+ DBG_ASSERT( !HasName() || pMed, "HasName() but no Medium?!?" );
+ if ( !HasName() || !pMed )
+ {
+ // Title already set?
+ if ( !pImpl->aTitle.isEmpty() )
+ return pImpl->aTitle;
+
+ // must it be numbered?
+ const OUString aNoName(SfxResId(STR_NONAME));
+ if (pImpl->bIsNamedVisible)
+ {
+ // Append number
+ return aNoName + " " + OUString::number(pImpl->nVisualDocumentNumber);
+ }
+
+ // Document called "Untitled" for the time being
+ return aNoName;
+ }
+ assert(pMed);
+
+ const INetURLObject aURL( IsDocShared() ? GetSharedFileURL() : GetMedium()->GetName() );
+ if ( nMaxLength > SFX_TITLE_CAPTION && nMaxLength <= SFX_TITLE_HISTORY )
+ {
+ sal_uInt16 nRemote;
+ if (aURL.GetProtocol() == INetProtocol::File)
+ nRemote = 0;
+ else
+ nRemote = 1;
+ nMaxLength = aTitleMap_Impl[nMaxLength-SFX_TITLE_CAPTION][nRemote];
+ }
+
+ // Local file?
+ if ( aURL.GetProtocol() == INetProtocol::File )
+ {
+ if ( nMaxLength == SFX_TITLE_FULLNAME )
+ return aURL.HasMark() ? INetURLObject( aURL.GetURLNoMark() ).PathToFileName() : aURL.PathToFileName();
+ if ( nMaxLength == SFX_TITLE_FILENAME )
+ return aURL.getName(INetURLObject::LAST_SEGMENT, true, INetURLObject::DecodeMechanism::WithCharset);
+ if ( pImpl->aTitle.isEmpty() )
+ pImpl->aTitle = aURL.getBase( INetURLObject::LAST_SEGMENT,
+ true, INetURLObject::DecodeMechanism::WithCharset );
+ }
+ else
+ {
+ if ( nMaxLength >= SFX_TITLE_MAXLEN )
+ {
+ const OUString aComplete( aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
+ if( aComplete.getLength() > nMaxLength )
+ return OUString::Concat("...") + aComplete.subView( aComplete.getLength() - nMaxLength + 3, nMaxLength - 3 );
+ return aComplete;
+ }
+ if ( nMaxLength == SFX_TITLE_FILENAME )
+ {
+ const OUString aName = INetURLObject::decode( aURL.GetBase(), INetURLObject::DecodeMechanism::WithCharset );
+ return aName.isEmpty() ? aURL.GetURLNoPass() : aName;
+ }
+ if ( nMaxLength == SFX_TITLE_FULLNAME )
+ return aURL.GetMainURL( INetURLObject::DecodeMechanism::ToIUri );
+
+ // Generate Title from file name if possible
+ if ( pImpl->aTitle.isEmpty() )
+ pImpl->aTitle = aURL.GetBase();
+
+ // workaround for the case when the name can not be retrieved from URL by INetURLObject
+ if ( pImpl->aTitle.isEmpty() )
+ pImpl->aTitle = aURL.GetMainURL( INetURLObject::DecodeMechanism::WithCharset );
+ }
+
+ // Complete Title
+ return pImpl->aTitle;
+}
+
+
+void SfxObjectShell::InvalidateName()
+
+/* [Description]
+
+ Returns the title of the new document, DocInfo-Title or
+ File name. Is required for loading from template or SaveAs.
+*/
+
+{
+ pImpl->aTitle.clear();
+ SetName( GetTitle( SFX_TITLE_APINAME ) );
+
+ Broadcast( SfxHint(SfxHintId::TitleChanged) );
+}
+
+
+void SfxObjectShell::SetNamedVisibility_Impl()
+{
+ if ( !pImpl->bIsNamedVisible )
+ {
+ pImpl->bIsNamedVisible = true;
+ if ( !HasName() && USHRT_MAX == pImpl->nVisualDocumentNumber && pImpl->aTitle.isEmpty() )
+ {
+ pImpl->nVisualDocumentNumber = SfxGetpApp()->GetFreeIndex();
+ Broadcast( SfxHint(SfxHintId::TitleChanged) );
+ }
+ }
+
+ SetName( GetTitle(SFX_TITLE_APINAME) );
+}
+
+void SfxObjectShell::SetNoName()
+{
+ bHasName = false;
+ GetModel()->attachResource( OUString(), GetModel()->getArgs() );
+}
+
+
+SfxProgress* SfxObjectShell::GetProgress() const
+{
+ return pImpl->pProgress;
+}
+
+
+void SfxObjectShell::SetProgress_Impl
+(
+ SfxProgress *pProgress /* to started <SfxProgress> or 0,
+ if the progress is to be reset */
+)
+
+/* [Description]
+
+ Internal method to set or reset the Progress modes for
+ SfxObjectShell.
+*/
+
+{
+ DBG_ASSERT( ( !pImpl->pProgress && pProgress ) ||
+ ( pImpl->pProgress && !pProgress ),
+ "Progress activation/deactivation mismatch" );
+ pImpl->pProgress = pProgress;
+}
+
+
+void SfxObjectShell::PostActivateEvent_Impl( SfxViewFrame const * pFrame )
+{
+ SfxApplication* pSfxApp = SfxGetpApp();
+ if ( pSfxApp->IsDowning() || IsLoading() || !pFrame || pFrame->GetFrame().IsClosing_Impl() )
+ return;
+
+ const SfxBoolItem* pHiddenItem = SfxItemSet::GetItem<SfxBoolItem>(pMedium->GetItemSet(), SID_HIDDEN, false);
+ if ( !pHiddenItem || !pHiddenItem->GetValue() )
+ {
+ SfxEventHintId nId = pImpl->nEventId;
+ pImpl->nEventId = SfxEventHintId::NONE;
+ if ( nId == SfxEventHintId::OpenDoc )
+ pSfxApp->NotifyEvent(SfxViewEventHint( nId, GlobalEventConfig::GetEventName(GlobalEventId::OPENDOC), this, pFrame->GetFrame().GetController() ), false);
+ else if (nId == SfxEventHintId::CreateDoc )
+ pSfxApp->NotifyEvent(SfxViewEventHint( nId, GlobalEventConfig::GetEventName(GlobalEventId::CREATEDOC), this, pFrame->GetFrame().GetController() ), false);
+ }
+}
+
+
+void SfxObjectShell::SetActivateEvent_Impl(SfxEventHintId nId )
+{
+ pImpl->nEventId = nId;
+}
+
+bool SfxObjectShell::IsAutoLoadLocked() const
+
+/* Returns whether an Autoload is allowed to be executed. Before the
+ surrounding FrameSet of the AutoLoad is also taken into account as well.
+*/
+
+{
+ return !IsReadOnly();
+}
+
+
+void SfxObjectShell::BreakMacroSign_Impl( bool bBreakMacroSign )
+{
+ pImpl->m_bMacroSignBroken = bBreakMacroSign;
+}
+
+
+void SfxObjectShell::CheckSecurityOnLoading_Impl()
+{
+ // make sure LO evaluates the macro signatures, so it can be preserved
+ GetScriptingSignatureState();
+
+ uno::Reference< task::XInteractionHandler > xInteraction;
+ if ( GetMedium() )
+ xInteraction = GetMedium()->GetInteractionHandler();
+
+ // check if there is a broken signature...
+ CheckForBrokenDocSignatures_Impl();
+
+ CheckEncryption_Impl( xInteraction );
+
+ // check macro security
+ const bool bHasValidContentSignature = HasValidSignatures();
+ pImpl->aMacroMode.checkMacrosOnLoading( xInteraction, bHasValidContentSignature );
+}
+
+
+void SfxObjectShell::CheckEncryption_Impl( const uno::Reference< task::XInteractionHandler >& xHandler )
+{
+ OUString aVersion;
+ bool bIsEncrypted = false;
+ bool bHasNonEncrypted = false;
+
+ try
+ {
+ uno::Reference < beans::XPropertySet > xPropSet( GetStorage(), uno::UNO_QUERY_THROW );
+ xPropSet->getPropertyValue("Version") >>= aVersion;
+ xPropSet->getPropertyValue("HasEncryptedEntries") >>= bIsEncrypted;
+ xPropSet->getPropertyValue("HasNonEncryptedEntries") >>= bHasNonEncrypted;
+ }
+ catch( uno::Exception& )
+ {
+ }
+
+ if ( aVersion.compareTo( ODFVER_012_TEXT ) < 0 )
+ return;
+
+ // this is ODF1.2 or later
+ if ( !(bIsEncrypted && bHasNonEncrypted) )
+ return;
+
+ if ( !pImpl->m_bIncomplEncrWarnShown )
+ {
+ // this is an encrypted document with nonencrypted streams inside, show the warning
+ css::task::ErrorCodeRequest aErrorCode;
+ aErrorCode.ErrCode = sal_uInt32(ERRCODE_SFX_INCOMPLETE_ENCRYPTION);
+
+ SfxMedium::CallApproveHandler( xHandler, uno::Any( aErrorCode ), false );
+ pImpl->m_bIncomplEncrWarnShown = true;
+ }
+
+ // broken signatures imply no macro execution at all
+ pImpl->aMacroMode.disallowMacroExecution();
+}
+
+
+void SfxObjectShell::CheckForBrokenDocSignatures_Impl()
+{
+ SignatureState nSignatureState = GetDocumentSignatureState();
+ bool bSignatureBroken = ( nSignatureState == SignatureState::BROKEN );
+ if ( !bSignatureBroken )
+ return;
+
+ // broken signatures imply no macro execution at all
+ pImpl->aMacroMode.disallowMacroExecution();
+}
+
+
+void SfxObjectShell::SetAutoLoad(
+ const INetURLObject& rUrl, sal_uInt32 nTime, bool bReload )
+{
+ pImpl->pReloadTimer.reset();
+ if ( bReload )
+ {
+ pImpl->pReloadTimer.reset(new AutoReloadTimer_Impl(
+ rUrl.GetMainURL( INetURLObject::DecodeMechanism::ToIUri ),
+ nTime, this ));
+ pImpl->pReloadTimer->Start();
+ }
+}
+
+void SfxObjectShell::SetLoading(SfxLoadedFlags nFlags)
+{
+ pImpl->nLoadedFlags = nFlags;
+}
+
+bool SfxObjectShell::IsLoadingFinished() const
+{
+ return ( pImpl->nLoadedFlags == SfxLoadedFlags::ALL );
+}
+
+void SfxObjectShell::InitOwnModel_Impl()
+{
+ if ( pImpl->bModelInitialized )
+ return;
+
+ const SfxStringItem* pSalvageItem = SfxItemSet::GetItem<SfxStringItem>(pMedium->GetItemSet(), SID_DOC_SALVAGE, false);
+ if ( pSalvageItem )
+ {
+ pImpl->aTempName = pMedium->GetPhysicalName();
+ pMedium->GetItemSet()->ClearItem( SID_DOC_SALVAGE );
+ pMedium->GetItemSet()->ClearItem( SID_FILE_NAME );
+ pMedium->GetItemSet()->Put( SfxStringItem( SID_FILE_NAME, pMedium->GetOrigURL() ) );
+ }
+ else
+ {
+ pMedium->GetItemSet()->ClearItem( SID_PROGRESS_STATUSBAR_CONTROL );
+ pMedium->GetItemSet()->ClearItem( SID_DOCUMENT );
+ }
+
+ pMedium->GetItemSet()->ClearItem( SID_REFERER );
+ uno::Reference< frame::XModel > xModel = GetModel();
+ if ( xModel.is() )
+ {
+ SfxItemSet *pSet = GetMedium()->GetItemSet();
+ if ( !GetMedium()->IsReadOnly() )
+ pSet->ClearItem( SID_INPUTSTREAM );
+ uno::Sequence< beans::PropertyValue > aArgs;
+ TransformItems( SID_OPENDOC, *pSet, aArgs );
+ xModel->attachResource( GetMedium()->GetOrigURL(), aArgs );
+ impl_addToModelCollection(xModel);
+ }
+
+ pImpl->bModelInitialized = true;
+}
+
+void SfxObjectShell::FinishedLoading( SfxLoadedFlags nFlags )
+{
+ bool bSetModifiedTRUE = false;
+ const SfxStringItem* pSalvageItem = SfxItemSet::GetItem<SfxStringItem>(pMedium->GetItemSet(), SID_DOC_SALVAGE, false);
+ if( ( nFlags & SfxLoadedFlags::MAINDOCUMENT ) && !(pImpl->nLoadedFlags & SfxLoadedFlags::MAINDOCUMENT )
+ && !(pImpl->nFlagsInProgress & SfxLoadedFlags::MAINDOCUMENT ))
+ {
+ pImpl->nFlagsInProgress |= SfxLoadedFlags::MAINDOCUMENT;
+ static_cast<SfxHeaderAttributes_Impl*>(GetHeaderAttributes())->SetAttributes();
+
+ if ( ( GetModifyPasswordHash() || GetModifyPasswordInfo().hasElements() ) && !IsModifyPasswordEntered() )
+ SetReadOnly();
+
+ // Salvage
+ if ( pSalvageItem )
+ bSetModifiedTRUE = true;
+
+ if ( !IsEnableSetModified() )
+ EnableSetModified();
+
+ if( !bSetModifiedTRUE && IsEnableSetModified() )
+ SetModified( false );
+
+ CheckSecurityOnLoading_Impl();
+
+ bHasName = true; // the document is loaded, so the name should already available
+ GetTitle( SFX_TITLE_DETECT );
+ InitOwnModel_Impl();
+ pImpl->nFlagsInProgress &= ~SfxLoadedFlags::MAINDOCUMENT;
+ }
+
+ if( ( nFlags & SfxLoadedFlags::IMAGES ) && !(pImpl->nLoadedFlags & SfxLoadedFlags::IMAGES )
+ && !(pImpl->nFlagsInProgress & SfxLoadedFlags::IMAGES ))
+ {
+ pImpl->nFlagsInProgress |= SfxLoadedFlags::IMAGES;
+ uno::Reference<document::XDocumentProperties> xDocProps(
+ getDocProperties());
+ const OUString url(xDocProps->getAutoloadURL());
+ sal_Int32 delay(xDocProps->getAutoloadSecs());
+ SetAutoLoad( INetURLObject(url), delay * 1000,
+ (delay > 0) || !url.isEmpty() );
+ if( !bSetModifiedTRUE && IsEnableSetModified() )
+ SetModified( false );
+ Invalidate( SID_SAVEASDOC );
+ pImpl->nFlagsInProgress &= ~SfxLoadedFlags::IMAGES;
+ }
+
+ pImpl->nLoadedFlags |= nFlags;
+
+ if ( pImpl->nFlagsInProgress != SfxLoadedFlags::NONE )
+ return;
+
+ // in case of reentrance calls the first called FinishedLoading() call on the stack
+ // should do the notification, in result the notification is done when all the FinishedLoading() calls are finished
+
+ if ( bSetModifiedTRUE )
+ SetModified();
+ else
+ SetModified( false );
+
+ if ( (pImpl->nLoadedFlags & SfxLoadedFlags::MAINDOCUMENT ) && (pImpl->nLoadedFlags & SfxLoadedFlags::IMAGES ) )
+ {
+ const SfxBoolItem* pTemplateItem = SfxItemSet::GetItem<SfxBoolItem>(pMedium->GetItemSet(), SID_TEMPLATE, false);
+ bool bTemplate = pTemplateItem && pTemplateItem->GetValue();
+
+ // closing the streams on loading should be under control of SFX!
+ DBG_ASSERT( pMedium->IsOpen(), "Don't close the medium when loading documents!" );
+
+ if ( bTemplate )
+ {
+ TemplateDisconnectionAfterLoad();
+ }
+ else
+ {
+ // if a readonly medium has storage then it's stream is already based on temporary file
+ if( !(pMedium->GetOpenMode() & StreamMode::WRITE) && !pMedium->HasStorage_Impl() )
+ // don't lock file opened read only
+ pMedium->CloseInStream();
+ }
+ }
+
+ SetInitialized_Impl( false );
+
+ // Title is not available until loading has finished
+ Broadcast( SfxHint( SfxHintId::TitleChanged ) );
+ if ( pImpl->nEventId != SfxEventHintId::NONE )
+ PostActivateEvent_Impl(SfxViewFrame::GetFirst(this));
+}
+
+void SfxObjectShell::TemplateDisconnectionAfterLoad()
+{
+ // document is created from a template
+ //TODO/LATER: should the templates always be XML docs!
+
+ SfxMedium* pTmpMedium = pMedium;
+ if ( !pTmpMedium )
+ return;
+
+ const OUString aName( pTmpMedium->GetName() );
+ const SfxStringItem* pTemplNamItem = SfxItemSet::GetItem<SfxStringItem>(pTmpMedium->GetItemSet(), SID_TEMPLATE_NAME, false);
+ OUString aTemplateName;
+ if ( pTemplNamItem )
+ aTemplateName = pTemplNamItem->GetValue();
+ else
+ {
+ // !TODO/LATER: what's this?!
+ // Interactive ( DClick, Contextmenu ) no long name is included
+ aTemplateName = getDocProperties()->getTitle();
+ if ( aTemplateName.isEmpty() )
+ {
+ INetURLObject aURL( aName );
+ aURL.CutExtension();
+ aTemplateName = aURL.getName( INetURLObject::LAST_SEGMENT, true, INetURLObject::DecodeMechanism::WithCharset );
+ }
+ }
+
+ // set medium to noname
+ pTmpMedium->SetName( OUString(), true );
+ pTmpMedium->Init_Impl();
+
+ // drop resource
+ SetNoName();
+ InvalidateName();
+
+ if( IsPackageStorageFormat_Impl( *pTmpMedium ) )
+ {
+ // untitled document must be based on temporary storage
+ // the medium should not dispose the storage in this case
+ uno::Reference < embed::XStorage > xTmpStor = ::comphelper::OStorageHelper::GetTemporaryStorage();
+ GetStorage()->copyToStorage( xTmpStor );
+
+ // the medium should disconnect from the original location
+ // the storage should not be disposed since the document is still
+ // based on it, but in DoSaveCompleted it will be disposed
+ pTmpMedium->CanDisposeStorage_Impl( false );
+ pTmpMedium->Close();
+
+ // setting the new storage the medium will be based on
+ pTmpMedium->SetStorage_Impl( xTmpStor );
+
+ pMedium = nullptr;
+ bool ok = DoSaveCompleted( pTmpMedium );
+ assert(pMedium != nullptr);
+ if( ok )
+ {
+ const SfxStringItem* pSalvageItem = SfxItemSet::GetItem<SfxStringItem>(pMedium->GetItemSet(), SID_DOC_SALVAGE, false);
+ bool bSalvage = pSalvageItem != nullptr;
+
+ if ( !bSalvage )
+ {
+ // some further initializations for templates
+ SetTemplate_Impl( aName, aTemplateName, this );
+ }
+
+ // the medium should not dispose the storage, DoSaveCompleted() has let it to do so
+ pTmpMedium->CanDisposeStorage_Impl( false );
+ }
+ else
+ {
+ SetError(ERRCODE_IO_GENERAL);
+ }
+ }
+ else
+ {
+ // some further initializations for templates
+ SetTemplate_Impl( aName, aTemplateName, this );
+ pTmpMedium->CreateTempFile();
+ }
+
+ // templates are never readonly
+ pTmpMedium->GetItemSet()->ClearItem( SID_DOC_READONLY );
+ pTmpMedium->SetOpenMode( SFX_STREAM_READWRITE, true );
+
+ // notifications about possible changes in readonly state and document info
+ Broadcast( SfxHint(SfxHintId::ModeChanged) );
+
+ // created untitled document can't be modified
+ SetModified( false );
+}
+
+
+bool SfxObjectShell::IsLoading() const
+/* [Description]
+
+ Has FinishedLoading been called?
+*/
+{
+ return !( pImpl->nLoadedFlags & SfxLoadedFlags::MAINDOCUMENT );
+}
+
+
+void SfxObjectShell::CancelTransfers()
+/* [Description]
+
+ Here can Transfers get canceled, which were not registered
+ by RegisterTransfer.
+*/
+{
+ if( ( pImpl->nLoadedFlags & SfxLoadedFlags::ALL ) != SfxLoadedFlags::ALL )
+ {
+ pImpl->bIsAbortingImport = true;
+ if( IsLoading() )
+ FinishedLoading();
+ }
+}
+
+
+AutoReloadTimer_Impl::AutoReloadTimer_Impl(
+ const OUString& rURL, sal_uInt32 nTime, SfxObjectShell* pSh )
+ : Timer("sfx2 AutoReloadTimer_Impl"), aUrl( rURL ), pObjSh( pSh )
+{
+ SetTimeout( nTime );
+}
+
+
+void AutoReloadTimer_Impl::Invoke()
+{
+ SfxViewFrame *pFrame = SfxViewFrame::GetFirst( pObjSh );
+
+ if ( pFrame )
+ {
+ // Not possible/meaningful at the moment?
+ if ( !pObjSh->CanReload_Impl() || pObjSh->IsAutoLoadLocked() || Application::IsUICaptured() )
+ {
+ // Allow a retry
+ Start();
+ return;
+ }
+
+ SfxAllItemSet aSet( SfxGetpApp()->GetPool() );
+ aSet.Put( SfxBoolItem( SID_AUTOLOAD, true ) );
+ if ( !aUrl.isEmpty() )
+ aSet.Put( SfxStringItem( SID_FILE_NAME, aUrl ) );
+ if (pObjSh->HasName()) {
+ aSet.Put(
+ SfxStringItem(SID_REFERER, pObjSh->GetMedium()->GetName()));
+ }
+ SfxRequest aReq( SID_RELOAD, SfxCallMode::SLOT, aSet );
+ // this will delete this
+ pObjSh->Get_Impl()->pReloadTimer.reset();
+ pFrame->ExecReload_Impl( aReq );
+ return;
+ }
+
+ // this will delete this
+ pObjSh->Get_Impl()->pReloadTimer.reset();
+}
+
+SfxModule* SfxObjectShell::GetModule() const
+{
+ return GetFactory().GetModule();
+}
+
+ErrCode SfxObjectShell::CallBasic( std::u16string_view rMacro,
+ std::u16string_view rBasic, SbxArray* pArgs,
+ SbxValue* pRet )
+{
+ SfxApplication* pApp = SfxGetpApp();
+ if( pApp->GetName() != rBasic )
+ {
+ if ( !AdjustMacroMode() )
+ return ERRCODE_IO_ACCESSDENIED;
+ }
+
+ BasicManager *pMgr = GetBasicManager();
+ if( pApp->GetName() == rBasic )
+ pMgr = SfxApplication::GetBasicManager();
+ ErrCode nRet = SfxApplication::CallBasic( OUString(rMacro), pMgr, pArgs, pRet );
+ return nRet;
+}
+
+bool SfxObjectShell::isScriptAccessAllowed( const Reference< XInterface >& _rxScriptContext )
+{
+ try
+ {
+ Reference< XEmbeddedScripts > xScripts( _rxScriptContext, UNO_QUERY );
+ if ( !xScripts.is() )
+ {
+ Reference< XScriptInvocationContext > xContext( _rxScriptContext, UNO_QUERY_THROW );
+ xScripts.set( xContext->getScriptContainer(), UNO_SET_THROW );
+ }
+
+ return xScripts->getAllowMacroExecution();
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("sfx.doc");
+ }
+ return false;
+}
+
+// don't allow LibreLogo to be used with our mouseover/etc dom-alike events
+bool SfxObjectShell::UnTrustedScript(const OUString& rScriptURL)
+{
+ if (!rScriptURL.startsWith("vnd.sun.star.script:"))
+ return false;
+
+ // ensure URL Escape Codes are decoded
+ css::uno::Reference<css::uri::XUriReference> uri(
+ css::uri::UriReferenceFactory::create(comphelper::getProcessComponentContext())->parse(rScriptURL));
+ css::uno::Reference<css::uri::XVndSunStarScriptUrl> sfUri(uri, css::uno::UNO_QUERY);
+
+ if (!sfUri.is())
+ return false;
+
+ // pyuno encodes path separator as |
+ OUString sScript = sfUri->getName().replace('|', '/');
+
+ // check if any path portion matches LibreLogo and ban it if it does
+ sal_Int32 nIndex = 0;
+ do
+ {
+ OUString aToken = sScript.getToken(0, '/', nIndex);
+ if (aToken.startsWithIgnoreAsciiCase("LibreLogo") || aToken.indexOf('~') != -1)
+ {
+ return true;
+ }
+ }
+ while (nIndex >= 0);
+
+ return false;
+}
+
+ErrCode SfxObjectShell::CallXScript( const Reference< XInterface >& _rxScriptContext, const OUString& _rScriptURL,
+ const Sequence< Any >& aParams, Any& aRet, Sequence< sal_Int16 >& aOutParamIndex, Sequence< Any >& aOutParam, bool bRaiseError, const css::uno::Any* pCaller )
+{
+ SAL_INFO("sfx", "in CallXScript" );
+ ErrCode nErr = ERRCODE_NONE;
+
+ bool bCaughtException = false;
+ Any aException;
+ try
+ {
+ if (!isScriptAccessAllowed(_rxScriptContext))
+ return ERRCODE_IO_ACCESSDENIED;
+
+ if ( UnTrustedScript(_rScriptURL) )
+ return ERRCODE_IO_ACCESSDENIED;
+
+ // obtain/create a script provider
+ Reference< provider::XScriptProvider > xScriptProvider;
+ Reference< provider::XScriptProviderSupplier > xSPS( _rxScriptContext, UNO_QUERY );
+ if ( xSPS.is() )
+ xScriptProvider.set( xSPS->getScriptProvider() );
+
+ if ( !xScriptProvider.is() )
+ {
+ Reference< provider::XScriptProviderFactory > xScriptProviderFactory =
+ provider::theMasterScriptProviderFactory::get( ::comphelper::getProcessComponentContext() );
+ xScriptProvider.set( xScriptProviderFactory->createScriptProvider( Any( _rxScriptContext ) ), UNO_SET_THROW );
+ }
+
+ // ry to protect the invocation context's undo manager (if present), just in case the script tampers with it
+ ::framework::DocumentUndoGuard aUndoGuard( _rxScriptContext );
+
+ // obtain the script, and execute it
+ Reference< provider::XScript > xScript( xScriptProvider->getScript( _rScriptURL ), UNO_SET_THROW );
+ if ( pCaller && pCaller->hasValue() )
+ {
+ Reference< beans::XPropertySet > xProps( xScript, uno::UNO_QUERY );
+ if ( xProps.is() )
+ {
+ Sequence< uno::Any > aArgs{ *pCaller };
+ xProps->setPropertyValue("Caller", uno::Any( aArgs ) );
+ }
+ }
+ aRet = xScript->invoke( aParams, aOutParamIndex, aOutParam );
+ }
+ catch ( const uno::Exception& )
+ {
+ aException = ::cppu::getCaughtException();
+ bCaughtException = true;
+ nErr = ERRCODE_BASIC_INTERNAL_ERROR;
+ }
+
+ if ( bCaughtException && bRaiseError )
+ {
+ SfxAbstractDialogFactory* pFact = SfxAbstractDialogFactory::Create();
+ pFact->ShowAsyncScriptErrorDialog( nullptr, aException );
+ }
+
+ SAL_INFO("sfx", "leaving CallXScript" );
+ return nErr;
+}
+
+// perhaps rename to CallScript once we get rid of the existing CallScript
+// and Call, CallBasic, CallStarBasic methods
+ErrCode SfxObjectShell::CallXScript( const OUString& rScriptURL,
+ const css::uno::Sequence< css::uno::Any >& aParams,
+ css::uno::Any& aRet,
+ css::uno::Sequence< sal_Int16 >& aOutParamIndex,
+ css::uno::Sequence< css::uno::Any >& aOutParam,
+ bool bRaiseError,
+ const css::uno::Any* pCaller )
+{
+ return CallXScript( GetModel(), rScriptURL, aParams, aRet, aOutParamIndex, aOutParam, bRaiseError, pCaller );
+}
+
+void SfxHeaderAttributes_Impl::SetAttributes()
+{
+ bAlert = true;
+ SvKeyValue aPair;
+ for( bool bCont = xIter->GetFirst( aPair ); bCont;
+ bCont = xIter->GetNext( aPair ) )
+ SetAttribute( aPair );
+}
+
+void SfxHeaderAttributes_Impl::SetAttribute( const SvKeyValue& rKV )
+{
+ const OUString& aValue = rKV.GetValue();
+ if( rKV.GetKey().equalsIgnoreAsciiCase("refresh") && !rKV.GetValue().isEmpty() )
+ {
+ sal_Int32 nIdx{ 0 };
+ const sal_Int32 nTime{ o3tl::toInt32(o3tl::getToken(aValue, 0, ';', nIdx )) };
+ const OUString aURL{ comphelper::string::strip(o3tl::getToken(aValue, 0, ';', nIdx ), ' ') };
+ uno::Reference<document::XDocumentProperties> xDocProps(
+ pDoc->getDocProperties());
+ if( aURL.startsWithIgnoreAsciiCase( "url=" ) )
+ {
+ try {
+ xDocProps->setAutoloadURL(
+ rtl::Uri::convertRelToAbs(pDoc->GetMedium()->GetName(), aURL.copy( 4 )) );
+ } catch (rtl::MalformedUriException &) {
+ TOOLS_WARN_EXCEPTION("sfx", "");
+ }
+ }
+ try
+ {
+ xDocProps->setAutoloadSecs( nTime );
+ }
+ catch (lang::IllegalArgumentException &)
+ {
+ // ignore
+ }
+ }
+ else if( rKV.GetKey().equalsIgnoreAsciiCase( "expires" ) )
+ {
+ DateTime aDateTime( DateTime::EMPTY );
+ if( INetMIMEMessage::ParseDateField( rKV.GetValue(), aDateTime ) )
+ {
+ aDateTime.ConvertToLocalTime();
+ pDoc->GetMedium()->SetExpired_Impl( aDateTime );
+ }
+ else
+ {
+ pDoc->GetMedium()->SetExpired_Impl( Date( 1, 1, 1970 ) );
+ }
+ }
+}
+
+void SfxHeaderAttributes_Impl::Append( const SvKeyValue& rKV )
+{
+ xIter->Append( rKV );
+ if( bAlert ) SetAttribute( rKV );
+}
+
+SvKeyValueIterator* SfxObjectShell::GetHeaderAttributes()
+{
+ if( !pImpl->xHeaderAttributes.is() )
+ {
+ DBG_ASSERT( pMedium, "No Medium" );
+ pImpl->xHeaderAttributes = new SfxHeaderAttributes_Impl( this );
+ }
+ return static_cast<SvKeyValueIterator*>( pImpl->xHeaderAttributes.get() );
+}
+
+void SfxObjectShell::ClearHeaderAttributesForSourceViewHack()
+{
+ static_cast<SfxHeaderAttributes_Impl*>(GetHeaderAttributes())
+ ->ClearForSourceView();
+}
+
+
+void SfxObjectShell::SetHeaderAttributesForSourceViewHack()
+{
+ static_cast<SfxHeaderAttributes_Impl*>(GetHeaderAttributes())
+ ->SetAttributes();
+}
+
+bool SfxObjectShell::IsPreview() const
+{
+ if ( !pMedium )
+ return false;
+
+ bool bPreview = false;
+ const SfxStringItem* pFlags = SfxItemSet::GetItem<SfxStringItem>(pMedium->GetItemSet(), SID_OPTIONS, false);
+ if ( pFlags )
+ {
+ // Distributed values among individual items
+ const OUString aFileFlags = pFlags->GetValue().toAsciiUpperCase();
+ if ( -1 != aFileFlags.indexOf( 'B' ) )
+ bPreview = true;
+ }
+
+ if ( !bPreview )
+ {
+ const SfxBoolItem* pItem = SfxItemSet::GetItem<SfxBoolItem>(pMedium->GetItemSet(), SID_PREVIEW, false);
+ if ( pItem )
+ bPreview = pItem->GetValue();
+ }
+
+ return bPreview;
+}
+
+void SfxObjectShell::SetWaitCursor( bool bSet ) const
+{
+ for( SfxViewFrame* pFrame = SfxViewFrame::GetFirst( this ); pFrame; pFrame = SfxViewFrame::GetNext( *pFrame, this ) )
+ {
+ if ( bSet )
+ pFrame->GetFrame().GetWindow().EnterWait();
+ else
+ pFrame->GetFrame().GetWindow().LeaveWait();
+ }
+}
+
+OUString SfxObjectShell::GetAPIName() const
+{
+ INetURLObject aURL( IsDocShared() ? GetSharedFileURL() : GetMedium()->GetName() );
+ OUString aName( aURL.GetBase() );
+ if( aName.isEmpty() )
+ aName = aURL.GetURLNoPass();
+ if ( aName.isEmpty() )
+ aName = GetTitle( SFX_TITLE_DETECT );
+ return aName;
+}
+
+void SfxObjectShell::Invalidate( sal_uInt16 nId )
+{
+ for( SfxViewFrame* pFrame = SfxViewFrame::GetFirst( this ); pFrame; pFrame = SfxViewFrame::GetNext( *pFrame, this ) )
+ Invalidate_Impl( pFrame->GetBindings(), nId );
+}
+
+bool SfxObjectShell::AdjustMacroMode()
+{
+ uno::Reference< task::XInteractionHandler > xInteraction;
+ if ( pMedium )
+ xInteraction = pMedium->GetInteractionHandler();
+
+ CheckForBrokenDocSignatures_Impl();
+
+ CheckEncryption_Impl( xInteraction );
+
+ return pImpl->aMacroMode.adjustMacroMode( xInteraction );
+}
+
+css::uno::Reference<css::awt::XWindow> SfxObjectShell::GetDialogParent( SfxMedium const * pLoadingMedium )
+{
+ css::uno::Reference<css::awt::XWindow> xWindow;
+ SfxItemSet* pSet = pLoadingMedium ? pLoadingMedium->GetItemSet() : GetMedium()->GetItemSet();
+ const SfxUnoFrameItem* pUnoItem = SfxItemSet::GetItem<SfxUnoFrameItem>(pSet, SID_FILLFRAME, false);
+ if ( pUnoItem )
+ {
+ const uno::Reference < frame::XFrame >& xFrame( pUnoItem->GetFrame() );
+ xWindow = xFrame->getContainerWindow();
+ }
+
+ if (!xWindow)
+ {
+ SfxFrame* pFrame = nullptr;
+ const SfxFrameItem* pFrameItem = SfxItemSet::GetItem<SfxFrameItem>(pSet, SID_DOCFRAME, false);
+ if( pFrameItem && pFrameItem->GetFrame() )
+ // get target frame from ItemSet
+ pFrame = pFrameItem->GetFrame();
+ else
+ {
+ // try the current frame
+ SfxViewFrame* pView = SfxViewFrame::Current();
+ if ( !pView || pView->GetObjectShell() != this )
+ // get any visible frame
+ pView = SfxViewFrame::GetFirst(this);
+ if ( pView )
+ pFrame = &pView->GetFrame();
+ }
+
+ if ( pFrame )
+ {
+ // get topmost window
+ xWindow = pFrame->GetFrameInterface()->getContainerWindow();
+ }
+ }
+
+ if (xWindow)
+ {
+ // this frame may be invisible, show it if it is allowed
+ const SfxBoolItem* pHiddenItem = SfxItemSet::GetItem<SfxBoolItem>(pSet, SID_HIDDEN, false);
+ if ( !pHiddenItem || !pHiddenItem->GetValue() )
+ {
+ xWindow->setVisible(true);
+ css::uno::Reference<css::awt::XTopWindow> xTopWindow(xWindow, uno::UNO_QUERY);
+ SAL_WARN_IF(!xTopWindow, "sfx.appl", "XTopWindow not available from XWindow");
+ if (xTopWindow)
+ xTopWindow->toFront();
+ }
+ }
+
+ return xWindow;
+}
+
+void SfxObjectShell::SetCreateMode_Impl( SfxObjectCreateMode nMode )
+{
+ eCreateMode = nMode;
+}
+
+bool SfxObjectShell::IsInPlaceActive() const
+{
+ if ( eCreateMode != SfxObjectCreateMode::EMBEDDED )
+ return false;
+
+ SfxViewFrame* pFrame = SfxViewFrame::GetFirst( this );
+ return pFrame && pFrame->GetFrame().IsInPlace();
+}
+
+bool SfxObjectShell::IsUIActive() const
+{
+ if ( eCreateMode != SfxObjectCreateMode::EMBEDDED )
+ return false;
+
+ SfxViewFrame* pFrame = SfxViewFrame::GetFirst( this );
+ return pFrame && pFrame->GetFrame().IsInPlace() && pFrame->GetFrame().GetWorkWindow_Impl()->IsVisible_Impl();
+}
+
+bool SfxObjectShell::UseInteractionToHandleError(
+ const uno::Reference< task::XInteractionHandler >& xHandler,
+ ErrCode nError )
+{
+ bool bResult = false;
+
+ if ( xHandler.is() )
+ {
+ try
+ {
+ uno::Any aInteraction;
+ rtl::Reference<::comphelper::OInteractionAbort> pAbort = new ::comphelper::OInteractionAbort();
+ rtl::Reference<::comphelper::OInteractionApprove> pApprove = new ::comphelper::OInteractionApprove();
+ uno::Sequence< uno::Reference< task::XInteractionContinuation > > lContinuations{
+ pAbort, pApprove
+ };
+
+ task::ErrorCodeRequest aErrorCode;
+ aErrorCode.ErrCode = sal_uInt32(nError);
+ aInteraction <<= aErrorCode;
+ xHandler->handle(::framework::InteractionRequest::CreateRequest (aInteraction,lContinuations));
+ bResult = pAbort->wasSelected();
+ }
+ catch( uno::Exception& )
+ {}
+ }
+
+ return bResult;
+}
+
+sal_Int16 SfxObjectShell_Impl::getCurrentMacroExecMode() const
+{
+ sal_Int16 nImposedExecMode( MacroExecMode::NEVER_EXECUTE );
+
+ const SfxMedium* pMedium( rDocShell.GetMedium() );
+ OSL_PRECOND( pMedium, "SfxObjectShell_Impl::getCurrentMacroExecMode: no medium!" );
+ if ( pMedium )
+ {
+ const SfxUInt16Item* pMacroModeItem = SfxItemSet::GetItem<SfxUInt16Item>(pMedium->GetItemSet(), SID_MACROEXECMODE, false);
+ if ( pMacroModeItem )
+ nImposedExecMode = pMacroModeItem->GetValue();
+ }
+ return nImposedExecMode;
+}
+
+void SfxObjectShell_Impl::setCurrentMacroExecMode( sal_uInt16 nMacroMode )
+{
+ const SfxMedium* pMedium( rDocShell.GetMedium() );
+ OSL_PRECOND( pMedium, "SfxObjectShell_Impl::getCurrentMacroExecMode: no medium!" );
+ if ( pMedium )
+ {
+ pMedium->GetItemSet()->Put( SfxUInt16Item( SID_MACROEXECMODE, nMacroMode ) );
+ }
+}
+
+OUString SfxObjectShell_Impl::getDocumentLocation() const
+{
+ OUString sLocation;
+
+ const SfxMedium* pMedium( rDocShell.GetMedium() );
+ OSL_PRECOND( pMedium, "SfxObjectShell_Impl::getDocumentLocation: no medium!" );
+ if ( pMedium )
+ {
+ sLocation = pMedium->GetName();
+ if ( sLocation.isEmpty() )
+ {
+ // for documents made from a template: get the name of the template
+ sLocation = rDocShell.getDocProperties()->getTemplateURL();
+ }
+
+ // tdf#128006 take document base url as location
+ if (sLocation.isEmpty())
+ sLocation = rDocShell.getDocumentBaseURL();
+ }
+
+ return sLocation;
+}
+
+bool SfxObjectShell_Impl::documentStorageHasMacros() const
+{
+ return ::sfx2::DocumentMacroMode::storageHasMacros( m_xDocStorage );
+}
+
+bool SfxObjectShell_Impl::macroCallsSeenWhileLoading() const
+{
+ return rDocShell.GetMacroCallsSeenWhileLoading();
+}
+
+Reference< XEmbeddedScripts > SfxObjectShell_Impl::getEmbeddedDocumentScripts() const
+{
+ return Reference< XEmbeddedScripts >( rDocShell.GetModel(), UNO_QUERY );
+}
+
+SignatureState SfxObjectShell_Impl::getScriptingSignatureState()
+{
+ SignatureState nSignatureState( rDocShell.GetScriptingSignatureState() );
+
+ if ( nSignatureState != SignatureState::NOSIGNATURES && m_bMacroSignBroken )
+ {
+ // if there is a macro signature it must be handled as broken
+ nSignatureState = SignatureState::BROKEN;
+ }
+
+ return nSignatureState;
+}
+
+bool SfxObjectShell_Impl::hasTrustedScriptingSignature( bool bAllowUIToAddAuthor )
+{
+ bool bResult = false;
+
+ try
+ {
+ OUString aVersion;
+ try
+ {
+ uno::Reference < beans::XPropertySet > xPropSet( rDocShell.GetStorage(), uno::UNO_QUERY_THROW );
+ xPropSet->getPropertyValue("Version") >>= aVersion;
+ }
+ catch( uno::Exception& )
+ {
+ }
+
+ uno::Reference< security::XDocumentDigitalSignatures > xSigner( security::DocumentDigitalSignatures::createWithVersion(comphelper::getProcessComponentContext(), aVersion) );
+
+ if ( nScriptingSignatureState == SignatureState::UNKNOWN
+ || nScriptingSignatureState == SignatureState::OK
+ || nScriptingSignatureState == SignatureState::NOTVALIDATED )
+ {
+ const uno::Sequence< security::DocumentSignatureInformation > aInfo = rDocShell.GetDocumentSignatureInformation( true, xSigner );
+
+ if ( aInfo.hasElements() )
+ {
+ if ( nScriptingSignatureState == SignatureState::UNKNOWN )
+ nScriptingSignatureState = DocumentSignatures::getSignatureState(aInfo);
+
+ if ( nScriptingSignatureState == SignatureState::OK
+ || nScriptingSignatureState == SignatureState::NOTVALIDATED )
+ {
+ bResult = std::any_of(aInfo.begin(), aInfo.end(),
+ [&xSigner](const security::DocumentSignatureInformation& rInfo) {
+ return xSigner->isAuthorTrusted( rInfo.Signer ); });
+
+ if ( !bResult && bAllowUIToAddAuthor )
+ {
+ uno::Reference< task::XInteractionHandler > xInteraction;
+ if ( rDocShell.GetMedium() )
+ xInteraction = rDocShell.GetMedium()->GetInteractionHandler();
+
+ if ( xInteraction.is() )
+ {
+ task::DocumentMacroConfirmationRequest aRequest;
+ aRequest.DocumentURL = getDocumentLocation();
+ aRequest.DocumentStorage = rDocShell.GetMedium()->GetZipStorageToSign_Impl();
+ aRequest.DocumentSignatureInformation = aInfo;
+ aRequest.DocumentVersion = aVersion;
+ aRequest.Classification = task::InteractionClassification_QUERY;
+ bResult = SfxMedium::CallApproveHandler( xInteraction, uno::Any( aRequest ), true );
+ }
+ }
+ }
+ }
+ }
+ }
+ catch( uno::Exception& )
+ {}
+
+ return bResult;
+}
+
+bool SfxObjectShell::IsContinueImportOnFilterExceptions(std::u16string_view aErrMessage)
+{
+ if (mbContinueImportOnFilterExceptions == undefined)
+ {
+ if (Application::GetDialogCancelMode() == DialogCancelMode::Off)
+ {
+ // Ask the user to try to continue or abort loading
+ OUString aMessage = SfxResId(STR_QMSG_ERROR_OPENING_FILE);
+ if (!aErrMessage.empty())
+ aMessage += SfxResId(STR_QMSG_ERROR_OPENING_FILE_DETAILS) + aErrMessage;
+ aMessage += SfxResId(STR_QMSG_ERROR_OPENING_FILE_CONTINUE);
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(nullptr,
+ VclMessageType::Question, VclButtonsType::YesNo, aMessage));
+ mbContinueImportOnFilterExceptions = (xBox->run() == RET_YES) ? yes : no;
+ }
+ else
+ mbContinueImportOnFilterExceptions = no;
+ }
+ return mbContinueImportOnFilterExceptions == yes;
+}
+
+bool SfxObjectShell::isEditDocLocked() const
+{
+ Reference<XModel3> xModel = GetModel();
+ if (!xModel.is())
+ return false;
+ if (!officecfg::Office::Common::Misc::AllowEditReadonlyDocs::get())
+ return true;
+ return comphelper::NamedValueCollection::getOrDefault(xModel->getArgs2( { "LockEditDoc" } ), u"LockEditDoc", false);
+}
+
+bool SfxObjectShell::isContentExtractionLocked() const
+{
+ Reference<XModel3> xModel = GetModel();
+ if (!xModel.is())
+ return false;
+ return comphelper::NamedValueCollection::getOrDefault(xModel->getArgs2( { "LockContentExtraction" } ), u"LockContentExtraction", false);
+}
+
+bool SfxObjectShell::isExportLocked() const
+{
+ Reference<XModel3> xModel = GetModel();
+ if (!xModel.is())
+ return false;
+ return comphelper::NamedValueCollection::getOrDefault(xModel->getArgs2( { "LockExport" } ), u"LockExport", false);
+}
+
+bool SfxObjectShell::isPrintLocked() const
+{
+ Reference<XModel3> xModel = GetModel();
+ if (!xModel.is())
+ return false;
+ return comphelper::NamedValueCollection::getOrDefault(xModel->getArgs2( { "LockPrint" } ), u"LockPrint", false);
+}
+
+bool SfxObjectShell::isSaveLocked() const
+{
+ Reference<XModel3> xModel = GetModel();
+ if (!xModel.is())
+ return false;
+ return comphelper::NamedValueCollection::getOrDefault(xModel->getArgs2( { "LockSave" } ), u"LockSave", false);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/doc/objserv.cxx b/sfx2/source/doc/objserv.cxx
new file mode 100644
index 000000000..3a6a0ceea
--- /dev/null
+++ b/sfx2/source/doc/objserv.cxx
@@ -0,0 +1,2131 @@
+/* -*- 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 <config_features.h>
+
+#include <com/sun/star/style/XStyleFamiliesSupplier.hpp>
+#include <com/sun/star/ui/dialogs/ExtendedFilePickerElementIds.hpp>
+#include <com/sun/star/util/CloseVetoException.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/document/XCmisDocument.hpp>
+#include <com/sun/star/drawing/LineStyle.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/security/XCertificate.hpp>
+#include <com/sun/star/task/ErrorCodeIOException.hpp>
+#include <com/sun/star/task/InteractionHandler.hpp>
+#include <com/sun/star/task/XStatusIndicator.hpp>
+#include <com/sun/star/task/XStatusIndicatorFactory.hpp>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/servicehelper.hxx>
+#include <com/sun/star/drawing/XDrawView.hpp>
+
+#include <com/sun/star/security/DocumentSignatureInformation.hpp>
+#include <com/sun/star/security/DocumentDigitalSignatures.hpp>
+#include <tools/diagnose_ex.h>
+#include <tools/urlobj.hxx>
+#include <svl/whiter.hxx>
+#include <svl/intitem.hxx>
+#include <svl/eitem.hxx>
+#include <svl/visitem.hxx>
+#include <svtools/sfxecode.hxx>
+#include <svtools/ehdl.hxx>
+#include <sal/log.hxx>
+#include <sfx2/app.hxx>
+
+#include <comphelper/string.hxx>
+#include <basic/sbxcore.hxx>
+#include <basic/sberrors.hxx>
+#include <unotools/moduleoptions.hxx>
+#include <unotools/saveopt.hxx>
+#include <svtools/DocumentToGraphicRenderer.hxx>
+#include <vcl/gdimtf.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+#include <comphelper/documentconstants.hxx>
+#include <comphelper/storagehelper.hxx>
+#include <comphelper/lok.hxx>
+#include <LibreOfficeKit/LibreOfficeKitEnums.h>
+#include <tools/link.hxx>
+
+#include <sfx2/signaturestate.hxx>
+#include <sfx2/sfxresid.hxx>
+#include <sfx2/request.hxx>
+#include <sfx2/printer.hxx>
+#include <sfx2/viewsh.hxx>
+#include <sfx2/dinfdlg.hxx>
+#include <sfx2/docfilt.hxx>
+#include <sfx2/docfile.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/objitem.hxx>
+#include <sfx2/objsh.hxx>
+#include <objshimp.hxx>
+#include <sfx2/module.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <versdlg.hxx>
+#include <sfx2/strings.hrc>
+#include <sfx2/docfac.hxx>
+#include <sfx2/fcontnr.hxx>
+#include <sfx2/msgpool.hxx>
+#include <sfx2/objface.hxx>
+#include <checkin.hxx>
+#include <sfx2/infobar.hxx>
+#include <sfx2/sfxuno.hxx>
+#include <sfx2/sfxsids.hrc>
+#include <SfxRedactionHelper.hxx>
+
+#include <com/sun/star/util/XCloseable.hpp>
+#include <com/sun/star/document/XDocumentProperties.hpp>
+
+#include <com/sun/star/drawing/XDrawPagesSupplier.hpp>
+#include <com/sun/star/frame/XDesktop2.hpp>
+#include <com/sun/star/frame/Desktop.hpp>
+
+#include <guisaveas.hxx>
+#include <saveastemplatedlg.hxx>
+#include <memory>
+#include <cppuhelper/implbase.hxx>
+#include <unotools/ucbstreamhelper.hxx>
+#include <unotools/streamwrap.hxx>
+#include <comphelper/sequenceashashmap.hxx>
+#include <editeng/unoprnms.hxx>
+
+#include <autoredactdialog.hxx>
+
+#include <boost/property_tree/json_parser.hpp>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::ui::dialogs;
+using namespace ::com::sun::star::awt;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::document;
+using namespace ::com::sun::star::security;
+using namespace ::com::sun::star::task;
+using namespace ::com::sun::star::graphic;
+
+#define ShellClass_SfxObjectShell
+#include <sfxslots.hxx>
+
+SFX_IMPL_SUPERCLASS_INTERFACE(SfxObjectShell, SfxShell)
+
+void SfxObjectShell::InitInterface_Impl()
+{
+}
+
+namespace {
+
+class SfxClosePreventer_Impl : public ::cppu::WeakImplHelper< css::util::XCloseListener >
+{
+ bool m_bGotOwnership;
+ bool m_bPreventClose;
+
+public:
+ SfxClosePreventer_Impl();
+
+ bool HasOwnership() const { return m_bGotOwnership; }
+
+ void SetPreventClose( bool bPrevent ) { m_bPreventClose = bPrevent; }
+
+ virtual void SAL_CALL queryClosing( const lang::EventObject& aEvent, sal_Bool bDeliverOwnership ) override;
+
+ virtual void SAL_CALL notifyClosing( const lang::EventObject& aEvent ) override ;
+
+ virtual void SAL_CALL disposing( const lang::EventObject& aEvent ) override ;
+
+} ;
+
+}
+
+SfxClosePreventer_Impl::SfxClosePreventer_Impl()
+: m_bGotOwnership( false )
+, m_bPreventClose( true )
+{
+}
+
+void SAL_CALL SfxClosePreventer_Impl::queryClosing( const lang::EventObject&, sal_Bool bDeliverOwnership )
+{
+ if ( m_bPreventClose )
+ {
+ if ( !m_bGotOwnership )
+ m_bGotOwnership = bDeliverOwnership;
+
+ throw util::CloseVetoException();
+ }
+}
+
+void SAL_CALL SfxClosePreventer_Impl::notifyClosing( const lang::EventObject& )
+{}
+
+void SAL_CALL SfxClosePreventer_Impl::disposing( const lang::EventObject& )
+{}
+
+namespace {
+
+class SfxInstanceCloseGuard_Impl
+{
+ rtl::Reference<SfxClosePreventer_Impl> m_xPreventer;
+ uno::Reference< util::XCloseable > m_xCloseable;
+
+public:
+ SfxInstanceCloseGuard_Impl() {}
+
+ ~SfxInstanceCloseGuard_Impl();
+
+ bool Init_Impl( const uno::Reference< util::XCloseable >& xCloseable );
+};
+
+}
+
+bool SfxInstanceCloseGuard_Impl::Init_Impl( const uno::Reference< util::XCloseable >& xCloseable )
+{
+ bool bResult = false;
+
+ // do not allow reinit after the successful init
+ if ( xCloseable.is() && !m_xCloseable.is() )
+ {
+ try
+ {
+ m_xPreventer = new SfxClosePreventer_Impl();
+ xCloseable->addCloseListener( m_xPreventer );
+ m_xCloseable = xCloseable;
+ bResult = true;
+ }
+ catch( uno::Exception& )
+ {
+ OSL_FAIL( "Could not register close listener!" );
+ }
+ }
+
+ return bResult;
+}
+
+SfxInstanceCloseGuard_Impl::~SfxInstanceCloseGuard_Impl()
+{
+ if ( !m_xCloseable.is() || !m_xPreventer.is() )
+ return;
+
+ try
+ {
+ m_xCloseable->removeCloseListener( m_xPreventer );
+ }
+ catch( uno::Exception& )
+ {
+ }
+
+ try
+ {
+ if ( m_xPreventer.is() )
+ {
+ m_xPreventer->SetPreventClose( false );
+
+ if ( m_xPreventer->HasOwnership() )
+ m_xCloseable->close( true ); // TODO: do it asynchronously
+ }
+ }
+ catch( uno::Exception& )
+ {
+ }
+}
+
+
+void SfxObjectShell::PrintExec_Impl(SfxRequest &rReq)
+{
+ SfxViewFrame *pFrame = SfxViewFrame::GetFirst(this);
+ if ( pFrame )
+ {
+ rReq.SetSlot( SID_PRINTDOC );
+ pFrame->GetViewShell()->ExecuteSlot(rReq);
+ }
+}
+
+
+void SfxObjectShell::PrintState_Impl(SfxItemSet &rSet)
+{
+ bool bPrinting = false;
+ SfxViewFrame* pFrame = SfxViewFrame::GetFirst( this );
+ if ( pFrame )
+ {
+ SfxPrinter *pPrinter = pFrame->GetViewShell()->GetPrinter();
+ bPrinting = pPrinter && pPrinter->IsPrinting();
+ }
+ rSet.Put( SfxBoolItem( SID_PRINTOUT, bPrinting ) );
+}
+
+bool SfxObjectShell::APISaveAs_Impl(std::u16string_view aFileName, SfxItemSet& rItemSet,
+ const css::uno::Sequence<css::beans::PropertyValue>& rArgs)
+{
+ bool bOk = false;
+
+ if ( GetMedium() )
+ {
+ OUString aFilterName;
+ const SfxStringItem* pFilterNameItem = rItemSet.GetItem<SfxStringItem>(SID_FILTER_NAME, false);
+ if( pFilterNameItem )
+ {
+ aFilterName = pFilterNameItem->GetValue();
+ }
+ else
+ {
+ const SfxStringItem* pContentTypeItem = rItemSet.GetItem<SfxStringItem>(SID_CONTENTTYPE, false);
+ if ( pContentTypeItem )
+ {
+ std::shared_ptr<const SfxFilter> pFilter = SfxFilterMatcher( GetFactory().GetFactoryName() ).GetFilter4Mime( pContentTypeItem->GetValue(), SfxFilterFlags::EXPORT );
+ if ( pFilter )
+ aFilterName = pFilter->GetName();
+ }
+ }
+
+ // in case no filter defined use default one
+ if( aFilterName.isEmpty() )
+ {
+ std::shared_ptr<const SfxFilter> pFilt = SfxFilter::GetDefaultFilterFromFactory(GetFactory().GetFactoryName());
+
+ DBG_ASSERT( pFilt, "No default filter!\n" );
+ if( pFilt )
+ aFilterName = pFilt->GetFilterName();
+
+ rItemSet.Put(SfxStringItem(SID_FILTER_NAME, aFilterName));
+ }
+
+
+ {
+ SfxObjectShellRef xLock( this ); // ???
+
+ // use the title that is provided in the media descriptor
+ const SfxStringItem* pDocTitleItem = rItemSet.GetItem<SfxStringItem>(SID_DOCINFO_TITLE, false);
+ if ( pDocTitleItem )
+ getDocProperties()->setTitle( pDocTitleItem->GetValue() );
+
+ bOk = CommonSaveAs_Impl(INetURLObject(aFileName), aFilterName, rItemSet, rArgs);
+ }
+ }
+
+ return bOk;
+}
+
+void SfxObjectShell::CheckOut( )
+{
+ try
+ {
+ uno::Reference< document::XCmisDocument > xCmisDoc( GetModel(), uno::UNO_QUERY_THROW );
+ xCmisDoc->checkOut( );
+
+ // Remove the info bar
+ SfxViewFrame* pViewFrame = GetFrame();
+ pViewFrame->RemoveInfoBar( u"checkout" );
+ }
+ catch ( const uno::RuntimeException& e )
+ {
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(GetFrame()->GetFrameWeld(),
+ VclMessageType::Warning, VclButtonsType::Ok, e.Message));
+ xBox->run();
+ }
+}
+
+void SfxObjectShell::CancelCheckOut( )
+{
+ try
+ {
+ uno::Reference< document::XCmisDocument > xCmisDoc( GetModel(), uno::UNO_QUERY_THROW );
+ xCmisDoc->cancelCheckOut( );
+
+ uno::Reference< util::XModifiable > xModifiable( GetModel( ), uno::UNO_QUERY );
+ if ( xModifiable.is( ) )
+ xModifiable->setModified( false );
+ }
+ catch ( const uno::RuntimeException& e )
+ {
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(GetFrame()->GetFrameWeld(),
+ VclMessageType::Warning, VclButtonsType::Ok, e.Message));
+ xBox->run();
+ }
+}
+
+void SfxObjectShell::CheckIn( )
+{
+ try
+ {
+ uno::Reference< document::XCmisDocument > xCmisDoc( GetModel(), uno::UNO_QUERY_THROW );
+ // Pop up dialog to ask for comment and major
+ SfxCheckinDialog checkinDlg(GetFrame()->GetFrameWeld());
+ if (checkinDlg.run() == RET_OK)
+ {
+ xCmisDoc->checkIn(checkinDlg.IsMajor(), checkinDlg.GetComment());
+ uno::Reference< util::XModifiable > xModifiable( GetModel( ), uno::UNO_QUERY );
+ if ( xModifiable.is( ) )
+ xModifiable->setModified( false );
+ }
+ }
+ catch ( const uno::RuntimeException& e )
+ {
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(GetFrame()->GetFrameWeld(),
+ VclMessageType::Warning, VclButtonsType::Ok, e.Message));
+ xBox->run();
+ }
+}
+
+uno::Sequence< document::CmisVersion > SfxObjectShell::GetCmisVersions( ) const
+{
+ try
+ {
+ uno::Reference< document::XCmisDocument > xCmisDoc( GetModel(), uno::UNO_QUERY_THROW );
+ return xCmisDoc->getAllVersions( );
+ }
+ catch ( const uno::RuntimeException& e )
+ {
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(GetFrame()->GetFrameWeld(),
+ VclMessageType::Warning, VclButtonsType::Ok, e.Message));
+ xBox->run();
+ }
+ return uno::Sequence< document::CmisVersion > ( );
+}
+
+bool SfxObjectShell::IsSignPDF() const
+{
+ if (pMedium && !pMedium->IsOriginallyReadOnly())
+ {
+ const std::shared_ptr<const SfxFilter>& pFilter = pMedium->GetFilter();
+ if (pFilter && pFilter->GetName() == "draw_pdf_import")
+ return true;
+ }
+
+ return false;
+}
+
+uno::Reference<security::XCertificate> SfxObjectShell::GetSignPDFCertificate() const
+{
+ uno::Reference<frame::XModel> xModel = GetBaseModel();
+ if (!xModel.is())
+ {
+ return uno::Reference<security::XCertificate>();
+ }
+
+ uno::Reference<drawing::XShapes> xShapes(xModel->getCurrentSelection(), uno::UNO_QUERY);
+ if (!xShapes.is() || xShapes->getCount() < 1)
+ {
+ return uno::Reference<security::XCertificate>();
+ }
+
+ uno::Reference<beans::XPropertySet> xShapeProps(xShapes->getByIndex(0), uno::UNO_QUERY);
+ if (!xShapeProps.is())
+ {
+ return uno::Reference<security::XCertificate>();
+ }
+
+ if (!xShapeProps->getPropertySetInfo()->hasPropertyByName("InteropGrabBag"))
+ {
+ return uno::Reference<security::XCertificate>();
+ }
+
+ comphelper::SequenceAsHashMap aMap(xShapeProps->getPropertyValue("InteropGrabBag"));
+ auto it = aMap.find("SignatureCertificate");
+ if (it == aMap.end())
+ {
+ return uno::Reference<security::XCertificate>();
+ }
+
+ return uno::Reference<security::XCertificate>(it->second, uno::UNO_QUERY);
+}
+
+static void sendErrorToLOK(ErrCode error)
+{
+ if (error.GetClass() == ErrCodeClass::NONE)
+ return;
+
+ boost::property_tree::ptree aTree;
+ aTree.put("code", error);
+ aTree.put("kind", "");
+ aTree.put("cmd", "");
+
+ std::unique_ptr<ErrorInfo> pInfo = ErrorInfo::GetErrorInfo(error);
+ OUString aErr;
+ if (ErrorStringFactory::CreateString(pInfo.get(), aErr))
+ aTree.put("message", aErr.toUtf8());
+
+ std::stringstream aStream;
+ boost::property_tree::write_json(aStream, aTree);
+
+ SfxViewShell::Current()->libreOfficeKitViewCallback(LOK_CALLBACK_ERROR, aStream.str().c_str());
+}
+
+void SfxObjectShell::ExecFile_Impl(SfxRequest &rReq)
+{
+ weld::Window* pDialogParent = rReq.GetFrameWeld();
+ if (!pDialogParent)
+ {
+ SfxViewFrame* pFrame = GetFrame();
+ if (!pFrame)
+ pFrame = SfxViewFrame::GetFirst(this);
+ if (pFrame)
+ pDialogParent = pFrame->GetFrameWeld();
+ }
+
+ sal_uInt16 nId = rReq.GetSlot();
+
+ if( SID_SIGNATURE == nId || SID_MACRO_SIGNATURE == nId )
+ {
+ if ( QueryHiddenInformation( HiddenWarningFact::WhenSigning, nullptr ) == RET_YES )
+ {
+ if (SID_SIGNATURE == nId)
+ {
+ uno::Reference<security::XCertificate> xCertificate = GetSignPDFCertificate();
+ if (xCertificate.is())
+ {
+ SignDocumentContentUsingCertificate(xCertificate);
+
+ // Reload to show how the PDF actually looks like after signing. This also
+ // changes "finish signing" on the infobar back to "sign document" as a side
+ // effect.
+ SfxViewFrame* pFrame = GetFrame();
+ if (pFrame)
+ {
+ // Store current page before reload.
+ SfxAllItemSet aSet(SfxGetpApp()->GetPool());
+ uno::Reference<drawing::XDrawView> xController(
+ GetBaseModel()->getCurrentController(), uno::UNO_QUERY);
+ uno::Reference<beans::XPropertySet> xPage(xController->getCurrentPage(),
+ uno::UNO_QUERY);
+ sal_Int32 nPage{};
+ xPage->getPropertyValue("Number") >>= nPage;
+ if (nPage > 0)
+ {
+ // nPage is 1-based.
+ aSet.Put(SfxInt32Item(SID_PAGE_NUMBER, nPage - 1));
+ }
+ SfxRequest aReq(SID_RELOAD, SfxCallMode::SLOT, aSet);
+ pFrame->ExecReload_Impl(aReq);
+ }
+ }
+ else
+ {
+ SignDocumentContent(pDialogParent);
+ }
+ }
+ else
+ {
+ SignScriptingContent(pDialogParent);
+ }
+ }
+ return;
+ }
+
+ if ( !GetMedium() && nId != SID_CLOSEDOC )
+ {
+ rReq.Ignore();
+ return;
+ }
+
+ // this guard is created here to have it destruction at the end of the method
+ SfxInstanceCloseGuard_Impl aModelGuard;
+
+ bool bIsPDFExport = false;
+ bool bIsAutoRedact = false;
+ std::vector<std::pair<RedactionTarget, OUString>> aRedactionTargets;
+ switch(nId)
+ {
+ case SID_VERSION:
+ {
+ SfxViewFrame* pFrame = GetFrame();
+ if ( !pFrame )
+ pFrame = SfxViewFrame::GetFirst( this );
+ if ( !pFrame )
+ return;
+
+ if ( !IsOwnStorageFormat( *GetMedium() ) )
+ return;
+
+ SfxVersionDialog aDlg(pDialogParent, pFrame, IsSaveVersionOnClose());
+ aDlg.run();
+ SetSaveVersionOnClose(aDlg.IsSaveVersionOnClose());
+ rReq.Done();
+ return;
+ }
+
+ // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ case SID_DOCINFO:
+ {
+ const SfxDocumentInfoItem* pDocInfItem = rReq.GetArg<SfxDocumentInfoItem>(SID_DOCINFO);
+ if ( pDocInfItem )
+ {
+ // parameter, e.g. from replayed macro
+ pDocInfItem->UpdateDocumentInfo(getDocProperties(), true);
+ SetUseUserData( pDocInfItem->IsUseUserData() );
+ SetUseThumbnailSave( pDocInfItem->IsUseThumbnailSave() );
+ }
+ else
+ {
+ // no argument containing DocInfo; check optional arguments
+ bool bReadOnly = IsReadOnly();
+ const SfxBoolItem* pROItem = rReq.GetArg<SfxBoolItem>(SID_DOC_READONLY);
+ if ( pROItem )
+ // override readonly attribute of document
+ // e.g. if a readonly document is saved elsewhere and user asks for editing DocInfo before
+ bReadOnly = pROItem->GetValue();
+
+ // URL for dialog
+ const OUString aURL( HasName() ? GetMedium()->GetName() : GetFactory().GetFactoryURL() );
+
+ Reference< XCmisDocument > xCmisDoc( GetModel(), uno::UNO_QUERY );
+ uno::Sequence< document::CmisProperty> aCmisProperties = xCmisDoc->getCmisProperties();
+
+ SfxDocumentInfoItem aDocInfoItem( aURL, getDocProperties(), aCmisProperties,
+ IsUseUserData(), IsUseThumbnailSave() );
+ if ( !GetSlotState( SID_DOCTEMPLATE ) )
+ // templates not supported
+ aDocInfoItem.SetTemplate(false);
+
+ SfxItemSetFixed<SID_DOCINFO, SID_DOCINFO, SID_DOC_READONLY, SID_DOC_READONLY,
+ SID_EXPLORER_PROPS_START, SID_EXPLORER_PROPS_START, SID_BASEURL, SID_BASEURL>
+ aSet(GetPool());
+ aSet.Put( aDocInfoItem );
+ aSet.Put( SfxBoolItem( SID_DOC_READONLY, bReadOnly ) );
+ aSet.Put( SfxStringItem( SID_EXPLORER_PROPS_START, GetTitle() ) );
+ aSet.Put( SfxStringItem( SID_BASEURL, GetMedium()->GetBaseURL() ) );
+
+ // creating dialog is done via virtual method; application will
+ // add its own statistics page
+ std::shared_ptr<SfxDocumentInfoDialog> xDlg(CreateDocumentInfoDialog(rReq.GetFrameWeld(), aSet));
+ auto aFunc = [this, xDlg, xCmisDoc](sal_Int32 nResult, SfxRequest& rRequest)
+ {
+ if (RET_OK == nResult)
+ {
+ const SfxDocumentInfoItem* pDocInfoItem = SfxItemSet::GetItem(xDlg->GetOutputItemSet(), SID_DOCINFO, false);
+ if ( pDocInfoItem )
+ {
+ // user has done some changes to DocumentInfo
+ pDocInfoItem->UpdateDocumentInfo(getDocProperties());
+ const uno::Sequence< document::CmisProperty >& aNewCmisProperties =
+ pDocInfoItem->GetCmisProperties( );
+ if ( aNewCmisProperties.hasElements( ) )
+ xCmisDoc->updateCmisProperties( aNewCmisProperties );
+ SetUseUserData( pDocInfoItem->IsUseUserData() );
+ SetUseThumbnailSave( pDocInfoItem-> IsUseThumbnailSave() );
+ // add data from dialog for possible recording purpose
+ rRequest.AppendItem( SfxDocumentInfoItem( GetTitle(),
+ getDocProperties(), aNewCmisProperties, IsUseUserData(), IsUseThumbnailSave() ) );
+ }
+ rRequest.Done();
+ }
+ else
+ {
+ // nothing done; no recording
+ rRequest.Ignore();
+ }
+ };
+
+ if (!rReq.IsSynchronCall())
+ {
+ std::shared_ptr<SfxRequest> pReq = std::make_shared<SfxRequest>(rReq);
+ SfxTabDialogController::runAsync(xDlg, [pReq, aFunc](sal_Int32 nResult)
+ {
+ aFunc(nResult, *pReq);
+ });
+ rReq.Ignore();
+ }
+ else
+ {
+ aFunc(xDlg->run(), rReq);
+ }
+ }
+
+ return;
+ }
+
+ case SID_AUTOREDACTDOC:
+ {
+ // Actual redaction takes place on a newly generated Draw document
+ if (!SvtModuleOptions().IsModuleInstalled(SvtModuleOptions::EModule::DRAW))
+ {
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(
+ pDialogParent, VclMessageType::Warning, VclButtonsType::Ok,
+ SfxResId(STR_REDACTION_NO_DRAW_WARNING)));
+
+ xBox->run();
+
+ return;
+ }
+
+ SfxAutoRedactDialog aDlg(pDialogParent);
+ sal_Int16 nResult = aDlg.run();
+
+ if (nResult != RET_OK || !aDlg.hasTargets() || !aDlg.isValidState())
+ {
+ //Do nothing
+ return;
+ }
+
+ // else continue with normal redaction
+ bIsAutoRedact = true;
+ aDlg.getTargets(aRedactionTargets);
+
+ [[fallthrough]];
+ }
+
+ case SID_REDACTDOC:
+ {
+ css::uno::Reference<css::frame::XModel> xModel = GetModel();
+ if(!xModel.is())
+ return;
+
+ uno::Reference< lang::XComponent > xSourceDoc( xModel );
+
+ // Actual redaction takes place on a newly generated Draw document
+ if (!SvtModuleOptions().IsModuleInstalled(SvtModuleOptions::EModule::DRAW))
+ {
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(
+ pDialogParent, VclMessageType::Warning, VclButtonsType::Ok,
+ SfxResId(STR_REDACTION_NO_DRAW_WARNING)));
+
+ xBox->run();
+
+ return;
+ }
+
+ DocumentToGraphicRenderer aRenderer(xSourceDoc, false);
+
+ // Get the page margins of the original doc
+ PageMargins aPageMargins = {-1, -1, -1, -1};
+ if (aRenderer.isWriter())
+ aPageMargins = SfxRedactionHelper::getPageMarginsForWriter(xModel);
+ else if (aRenderer.isCalc())
+ aPageMargins = SfxRedactionHelper::getPageMarginsForCalc(xModel);
+
+ sal_Int32 nPages = aRenderer.getPageCount();
+ std::vector< GDIMetaFile > aMetaFiles;
+ std::vector< ::Size > aPageSizes;
+
+ // Convert the pages of the document to gdimetafiles
+ SfxRedactionHelper::getPageMetaFilesFromDoc(aMetaFiles, aPageSizes, nPages, aRenderer);
+
+ // Create an empty Draw component.
+ uno::Reference<frame::XDesktop2> xDesktop = css::frame::Desktop::create(comphelper::getProcessComponentContext());
+ uno::Reference<lang::XComponent> xComponent = xDesktop->loadComponentFromURL("private:factory/sdraw", "_default", 0, {});
+
+ if (!xComponent.is())
+ {
+ SAL_WARN("sfx.doc", "SID_REDACTDOC: Failed to load new draw component. loadComponentFromURL returned an empty reference.");
+
+ return;
+ }
+
+ // Add the doc pages to the new draw document
+ SfxRedactionHelper::addPagesToDraw(xComponent, nPages, aMetaFiles, aPageSizes, aPageMargins, aRedactionTargets, bIsAutoRedact);
+
+ // Show the Redaction toolbar
+ SfxViewFrame* pViewFrame = SfxViewFrame::Current();
+ if (!pViewFrame)
+ return;
+ SfxRedactionHelper::showRedactionToolbar(pViewFrame);
+
+ return;
+ }
+
+ // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ case SID_DIRECTEXPORTDOCASPDF:
+ {
+ uno::Reference< lang::XComponent > xComponent( GetCurrentComponent(), uno::UNO_QUERY );
+ if (!xComponent.is())
+ return;
+
+ uno::Reference< lang::XServiceInfo > xServiceInfo( xComponent, uno::UNO_QUERY);
+
+ // Redaction finalization takes place in Draw
+ if ( xServiceInfo.is() && xServiceInfo->supportsService("com.sun.star.drawing.DrawingDocument")
+ && SfxRedactionHelper::isRedactMode(rReq) )
+ {
+ OUString sRedactionStyle(SfxRedactionHelper::getStringParam(rReq, SID_REDACTION_STYLE));
+
+ // Access the draw pages
+ uno::Reference<drawing::XDrawPagesSupplier> xDrawPagesSupplier(xComponent, uno::UNO_QUERY);
+ uno::Reference<drawing::XDrawPages> xDrawPages = xDrawPagesSupplier->getDrawPages();
+
+ sal_Int32 nPageCount = xDrawPages->getCount();
+ for (sal_Int32 nPageNum = 0; nPageNum < nPageCount; ++nPageNum)
+ {
+ // Get the page
+ uno::Reference< drawing::XDrawPage > xPage( xDrawPages->getByIndex( nPageNum ), uno::UNO_QUERY );
+
+ if (!xPage.is())
+ continue;
+
+ // Go through all shapes
+ sal_Int32 nShapeCount = xPage->getCount();
+ for (sal_Int32 nShapeNum = 0; nShapeNum < nShapeCount; ++nShapeNum)
+ {
+ uno::Reference< drawing::XShape > xCurrShape(xPage->getByIndex(nShapeNum), uno::UNO_QUERY);
+ if (!xCurrShape.is())
+ continue;
+
+ uno::Reference< beans::XPropertySet > xPropSet(xCurrShape, uno::UNO_QUERY);
+ if (!xPropSet.is())
+ continue;
+
+ uno::Reference< beans::XPropertySetInfo> xInfo = xPropSet->getPropertySetInfo();
+ if (!xInfo.is())
+ continue;
+
+ OUString sShapeName;
+ if (xInfo->hasPropertyByName("Name"))
+ {
+ uno::Any aAnyShapeName = xPropSet->getPropertyValue("Name");
+ aAnyShapeName >>= sShapeName;
+ }
+ else
+ continue;
+
+ // Rectangle redaction
+ if (sShapeName == "RectangleRedactionShape"
+ && xInfo->hasPropertyByName("FillTransparence") && xInfo->hasPropertyByName("FillColor"))
+ {
+ xPropSet->setPropertyValue("FillTransparence", css::uno::Any(static_cast<sal_Int16>(0)));
+ if (sRedactionStyle == "White")
+ {
+ xPropSet->setPropertyValue("FillColor", css::uno::Any(COL_WHITE));
+ xPropSet->setPropertyValue("LineStyle", css::uno::Any(css::drawing::LineStyle::LineStyle_SOLID));
+ xPropSet->setPropertyValue("LineColor", css::uno::Any(COL_BLACK));
+ }
+ else
+ {
+ xPropSet->setPropertyValue("FillColor", css::uno::Any(COL_BLACK));
+ xPropSet->setPropertyValue("LineStyle", css::uno::Any(css::drawing::LineStyle::LineStyle_NONE));
+ }
+ }
+ // Freeform redaction
+ else if (sShapeName == "FreeformRedactionShape"
+ && xInfo->hasPropertyByName("LineTransparence") && xInfo->hasPropertyByName("LineColor"))
+ {
+ xPropSet->setPropertyValue("LineTransparence", css::uno::Any(static_cast<sal_Int16>(0)));
+
+ if (sRedactionStyle == "White")
+ {
+ xPropSet->setPropertyValue("LineColor", css::uno::Any(COL_WHITE));
+ }
+ else
+ {
+ xPropSet->setPropertyValue("LineColor", css::uno::Any(COL_BLACK));
+ }
+ }
+ }
+ }
+ }
+ }
+ [[fallthrough]];
+ case SID_EXPORTDOCASPDF:
+ bIsPDFExport = true;
+ [[fallthrough]];
+ case SID_EXPORTDOCASEPUB:
+ case SID_DIRECTEXPORTDOCASEPUB:
+ case SID_EXPORTDOC:
+ case SID_SAVEASDOC:
+ case SID_SAVEASREMOTE:
+ case SID_SAVEDOC:
+ {
+ // derived class may decide to abort this
+ if( !QuerySlotExecutable( nId ) )
+ {
+ rReq.SetReturnValue( SfxBoolItem( 0, false ) );
+ return;
+ }
+
+ //!! detailed analysis of an error code
+ SfxObjectShellRef xLock( this );
+
+ // the model can not be closed till the end of this method
+ // if somebody tries to close it during this time the model will be closed
+ // at the end of the method
+ aModelGuard.Init_Impl( uno::Reference< util::XCloseable >( GetModel(), uno::UNO_QUERY ) );
+
+ ErrCode nErrorCode = ERRCODE_NONE;
+
+ // by default versions should be preserved always except in case of an explicit
+ // SaveAs via GUI, so the flag must be set accordingly
+ pImpl->bPreserveVersions = (nId == SID_SAVEDOC);
+ try
+ {
+ SfxErrorContext aEc( ERRCTX_SFX_SAVEASDOC, GetTitle() ); // ???
+
+ if ( nId == SID_SAVEASDOC || nId == SID_SAVEASREMOTE )
+ {
+ // in case of plugin mode the SaveAs operation means SaveTo
+ const SfxBoolItem* pViewOnlyItem = SfxItemSet::GetItem<SfxBoolItem>(GetMedium()->GetItemSet(), SID_VIEWONLY, false);
+ if ( pViewOnlyItem && pViewOnlyItem->GetValue() )
+ rReq.AppendItem( SfxBoolItem( SID_SAVETO, true ) );
+ }
+
+ // TODO/LATER: do the following GUI related actions in standalone method
+
+ // Introduce a status indicator for GUI operation
+ const SfxUnoAnyItem* pStatusIndicatorItem = rReq.GetArg<SfxUnoAnyItem>(SID_PROGRESS_STATUSBAR_CONTROL);
+ if ( !pStatusIndicatorItem )
+ {
+ // get statusindicator
+ uno::Reference< task::XStatusIndicator > xStatusIndicator;
+ uno::Reference < frame::XController > xCtrl( GetModel()->getCurrentController() );
+ if ( xCtrl.is() )
+ {
+ uno::Reference< task::XStatusIndicatorFactory > xStatFactory( xCtrl->getFrame(), uno::UNO_QUERY );
+ if( xStatFactory.is() )
+ xStatusIndicator = xStatFactory->createStatusIndicator();
+ }
+
+ OSL_ENSURE( xStatusIndicator.is(), "Can not retrieve default status indicator!" );
+
+ if ( xStatusIndicator.is() )
+ {
+ SfxUnoAnyItem aStatIndItem( SID_PROGRESS_STATUSBAR_CONTROL, uno::Any( xStatusIndicator ) );
+
+ if ( nId == SID_SAVEDOC )
+ {
+ // in case of saving it is not possible to transport the parameters from here
+ // but it is not clear here whether the saving will be done or saveAs operation
+ GetMedium()->GetItemSet()->Put( aStatIndItem );
+ }
+
+ rReq.AppendItem( aStatIndItem );
+ }
+ }
+ else if ( nId == SID_SAVEDOC )
+ {
+ // in case of saving it is not possible to transport the parameters from here
+ // but it is not clear here whether the saving will be done or saveAs operation
+ GetMedium()->GetItemSet()->Put( *pStatusIndicatorItem );
+ }
+
+ // Introduce an interaction handler for GUI operation
+ const SfxUnoAnyItem* pInteractionHandlerItem = rReq.GetArg<SfxUnoAnyItem>(SID_INTERACTIONHANDLER);
+ if ( !pInteractionHandlerItem )
+ {
+ uno::Reference<css::awt::XWindow> xParentWindow;
+ uno::Reference<frame::XController> xCtrl(GetModel()->getCurrentController());
+ if (xCtrl.is())
+ xParentWindow = xCtrl->getFrame()->getContainerWindow();
+
+ uno::Reference< uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext();
+
+ uno::Reference< task::XInteractionHandler2 > xInteract(
+ task::InteractionHandler::createWithParent(xContext, xParentWindow) );
+
+ SfxUnoAnyItem aInteractionItem( SID_INTERACTIONHANDLER, uno::Any( xInteract ) );
+ if ( nId == SID_SAVEDOC )
+ {
+ // in case of saving it is not possible to transport the parameters from here
+ // but it is not clear here whether the saving will be done or saveAs operation
+ GetMedium()->GetItemSet()->Put( aInteractionItem );
+ }
+
+ rReq.AppendItem( aInteractionItem );
+ }
+ else if ( nId == SID_SAVEDOC )
+ {
+ // in case of saving it is not possible to transport the parameters from here
+ // but it is not clear here whether the saving will be done or saveAs operation
+ GetMedium()->GetItemSet()->Put( *pInteractionHandlerItem );
+ }
+
+
+ const SfxStringItem* pOldPasswordItem = SfxItemSet::GetItem<SfxStringItem>(GetMedium()->GetItemSet(), SID_PASSWORD, false);
+ const SfxUnoAnyItem* pOldEncryptionDataItem = SfxItemSet::GetItem<SfxUnoAnyItem>(GetMedium()->GetItemSet(), SID_ENCRYPTIONDATA, false);
+ bool bPreselectPassword = (pOldPasswordItem && pOldEncryptionDataItem);
+
+ uno::Sequence< beans::PropertyValue > aDispatchArgs;
+ if ( rReq.GetArgs() )
+ TransformItems( nId,
+ *rReq.GetArgs(),
+ aDispatchArgs );
+
+ bool bForceSaveAs = nId == SID_SAVEDOC && IsReadOnlyMedium();
+ const SfxSlot* pSlot = GetModule()->GetSlotPool()->GetSlot( bForceSaveAs ? SID_SAVEASDOC : nId );
+ if ( !pSlot )
+ throw uno::Exception("no slot", nullptr);
+
+ SfxStoringHelper aHelper;
+
+ if ( QueryHiddenInformation( bIsPDFExport ? HiddenWarningFact::WhenCreatingPDF : HiddenWarningFact::WhenSaving, nullptr ) != RET_YES )
+ {
+ // the user has decided not to store the document
+ throw task::ErrorCodeIOException(
+ "SfxObjectShell::ExecFile_Impl: ERRCODE_IO_ABORT",
+ uno::Reference< uno::XInterface >(), sal_uInt32(ERRCODE_IO_ABORT));
+ }
+
+ aHelper.GUIStoreModel( GetModel(),
+ OUString::createFromAscii( pSlot->GetUnoName() ),
+ aDispatchArgs,
+ bPreselectPassword,
+ GetDocumentSignatureState() );
+
+
+ // merge aDispatchArgs to the request
+ SfxAllItemSet aResultParams( GetPool() );
+ TransformParameters( nId,
+ aDispatchArgs,
+ aResultParams );
+ rReq.SetArgs( aResultParams );
+
+ // the StoreAsURL/StoreToURL method have called this method with false
+ // so it has to be restored to true here since it is a call from GUI
+ GetMedium()->SetUpdatePickList( true );
+
+ // TODO: in future it must be done in following way
+ // if document is opened from GUI, it immediately appears in the picklist
+ // if the document is a new one then it appears in the picklist immediately
+ // after SaveAs operation triggered from GUI
+ }
+ catch( const task::ErrorCodeIOException& aErrorEx )
+ {
+ TOOLS_WARN_EXCEPTION( "sfx.doc", "Fatal IO error during save");
+ nErrorCode = ErrCode(aErrorEx.ErrCode);
+ }
+ catch( Exception& )
+ {
+ nErrorCode = ERRCODE_IO_GENERAL;
+ }
+
+ // by default versions should be preserved always except in case of an explicit
+ // SaveAs via GUI, so the flag must be reset to guarantee this
+ pImpl->bPreserveVersions = true;
+ ErrCode lErr=GetErrorCode();
+
+ if ( !lErr && nErrorCode )
+ lErr = nErrorCode;
+
+ if ( lErr && nErrorCode == ERRCODE_NONE )
+ {
+ const SfxBoolItem* pWarnItem = rReq.GetArg<SfxBoolItem>(SID_FAIL_ON_WARNING);
+ if ( pWarnItem && pWarnItem->GetValue() )
+ nErrorCode = lErr;
+ }
+
+ // may be nErrorCode should be shown in future
+ if ( lErr != ERRCODE_IO_ABORT )
+ {
+ if (comphelper::LibreOfficeKit::isActive())
+ sendErrorToLOK(lErr);
+ else
+ {
+ SfxErrorContext aEc(ERRCTX_SFX_SAVEASDOC,GetTitle());
+ ErrorHandler::HandleError(lErr, pDialogParent);
+ }
+ }
+
+ if (nId == SID_DIRECTEXPORTDOCASPDF &&
+ SfxRedactionHelper::isRedactMode(rReq))
+ {
+ // Return the finalized redaction shapes back to normal (gray & transparent)
+ uno::Reference< lang::XComponent > xComponent( GetCurrentComponent(), uno::UNO_QUERY );
+ if (!xComponent.is())
+ return;
+
+ uno::Reference< lang::XServiceInfo > xServiceInfo( xComponent, uno::UNO_QUERY);
+
+ // Redaction finalization takes place in Draw
+ if ( xServiceInfo.is() && xServiceInfo->supportsService("com.sun.star.drawing.DrawingDocument") )
+ {
+ // Access the draw pages
+ uno::Reference<drawing::XDrawPagesSupplier> xDrawPagesSupplier(xComponent, uno::UNO_QUERY);
+ uno::Reference<drawing::XDrawPages> xDrawPages = xDrawPagesSupplier->getDrawPages();
+
+ sal_Int32 nPageCount = xDrawPages->getCount();
+ for (sal_Int32 nPageNum = 0; nPageNum < nPageCount; ++nPageNum)
+ {
+ // Get the page
+ uno::Reference< drawing::XDrawPage > xPage( xDrawPages->getByIndex( nPageNum ), uno::UNO_QUERY );
+
+ if (!xPage.is())
+ continue;
+
+ // Go through all shapes
+ sal_Int32 nShapeCount = xPage->getCount();
+ for (sal_Int32 nShapeNum = 0; nShapeNum < nShapeCount; ++nShapeNum)
+ {
+ uno::Reference< drawing::XShape > xCurrShape(xPage->getByIndex(nShapeNum), uno::UNO_QUERY);
+ if (!xCurrShape.is())
+ continue;
+
+ uno::Reference< beans::XPropertySet > xPropSet(xCurrShape, uno::UNO_QUERY);
+ if (!xPropSet.is())
+ continue;
+
+ uno::Reference< beans::XPropertySetInfo> xInfo = xPropSet->getPropertySetInfo();
+ if (!xInfo.is())
+ continue;
+
+ // Not a shape we converted?
+ if (!xInfo->hasPropertyByName("Name"))
+ continue;
+
+ OUString sShapeName;
+ if (xInfo->hasPropertyByName("Name"))
+ {
+ uno::Any aAnyShapeName = xPropSet->getPropertyValue("Name");
+ aAnyShapeName >>= sShapeName;
+ }
+ else
+ continue;
+
+ // Rectangle redaction
+ if (sShapeName == "RectangleRedactionShape"
+ && xInfo->hasPropertyByName("FillTransparence") && xInfo->hasPropertyByName("FillColor"))
+ {
+ xPropSet->setPropertyValue("FillTransparence", css::uno::Any(static_cast<sal_Int16>(50)));
+ xPropSet->setPropertyValue("FillColor", css::uno::Any(COL_GRAY7));
+ xPropSet->setPropertyValue("LineStyle", css::uno::Any(css::drawing::LineStyle::LineStyle_NONE));
+
+ }
+ // Freeform redaction
+ else if (sShapeName == "FreeformRedactionShape")
+ {
+ xPropSet->setPropertyValue("LineTransparence", css::uno::Any(static_cast<sal_Int16>(50)));
+ xPropSet->setPropertyValue("LineColor", css::uno::Any(COL_GRAY7));
+ }
+ }
+ }
+
+
+ }
+ }
+
+ if ( nId == SID_EXPORTDOCASPDF )
+ {
+ // This function is used by the SendMail function that needs information if an export
+ // file was written or not. This could be due to cancellation of the export
+ // or due to an error. So IO abort must be handled like an error!
+ nErrorCode = ( lErr != ERRCODE_IO_ABORT ) && ( nErrorCode == ERRCODE_NONE ) ? nErrorCode : lErr;
+ }
+
+ if ( ( nId == SID_SAVEASDOC || nId == SID_SAVEASREMOTE ) && nErrorCode == ERRCODE_NONE )
+ {
+ const SfxBoolItem* saveTo = rReq.GetArg<SfxBoolItem>(SID_SAVETO);
+ if (saveTo == nullptr || !saveTo->GetValue())
+ {
+ SfxViewFrame *pFrame = GetFrame();
+ if (pFrame)
+ pFrame->RemoveInfoBar(u"readonly");
+ SetReadOnlyUI(false);
+ }
+ }
+
+ rReq.SetReturnValue( SfxBoolItem(0, nErrorCode == ERRCODE_NONE ) );
+
+ ResetError();
+
+ Invalidate();
+ break;
+ }
+
+ case SID_SAVEACOPY:
+ {
+ SfxAllItemSet aArgs( GetPool() );
+ aArgs.Put( SfxBoolItem( SID_SAVEACOPYITEM, true ) );
+ SfxRequest aSaveACopyReq( SID_EXPORTDOC, SfxCallMode::API, aArgs );
+ ExecFile_Impl( aSaveACopyReq );
+ if ( !aSaveACopyReq.IsDone() )
+ {
+ rReq.Ignore();
+ return;
+ }
+ break;
+ }
+
+ // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+ case SID_CLOSEDOC:
+ {
+ // Evaluate Parameter
+ const SfxBoolItem* pSaveItem = rReq.GetArg<SfxBoolItem>(SID_CLOSEDOC_SAVE);
+ const SfxStringItem* pNameItem = rReq.GetArg<SfxStringItem>(SID_CLOSEDOC_FILENAME);
+ if ( pSaveItem )
+ {
+ if ( pSaveItem->GetValue() )
+ {
+ if ( !pNameItem )
+ {
+#if HAVE_FEATURE_SCRIPTING
+ SbxBase::SetError( ERRCODE_BASIC_WRONG_ARGS );
+#endif
+ rReq.Ignore();
+ return;
+ }
+ SfxAllItemSet aArgs( GetPool() );
+ SfxStringItem aTmpItem( SID_FILE_NAME, pNameItem->GetValue() );
+ aArgs.Put( aTmpItem, aTmpItem.Which() );
+ SfxRequest aSaveAsReq( SID_SAVEASDOC, SfxCallMode::API, aArgs );
+ ExecFile_Impl( aSaveAsReq );
+ if ( !aSaveAsReq.IsDone() )
+ {
+ rReq.Ignore();
+ return;
+ }
+ }
+ else
+ SetModified(false);
+ }
+
+ // Cancelled by the user?
+ if (!PrepareClose())
+ {
+ rReq.SetReturnValue( SfxBoolItem(0, false) );
+ rReq.Done();
+ return;
+ }
+
+ SetModified( false );
+ ErrCode lErr = GetErrorCode();
+
+ if (comphelper::LibreOfficeKit::isActive())
+ sendErrorToLOK(lErr);
+ else
+ ErrorHandler::HandleError(lErr, pDialogParent);
+
+ rReq.SetReturnValue( SfxBoolItem(0, true) );
+ rReq.Done();
+ rReq.ReleaseArgs(); // because the pool is destroyed in Close
+ DoClose();
+ return;
+ }
+
+ // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ case SID_DOCTEMPLATE:
+ {
+ // save as document templates
+ SfxSaveAsTemplateDialog aDlg(pDialogParent, GetModel());
+ (void)aDlg.run();
+ break;
+ }
+
+ case SID_CHECKOUT:
+ {
+ CheckOut( );
+ break;
+ }
+ case SID_CANCELCHECKOUT:
+ {
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(nullptr,
+ VclMessageType::Question, VclButtonsType::YesNo, SfxResId(STR_QUERY_CANCELCHECKOUT)));
+ if (xBox->run() == RET_YES)
+ {
+ CancelCheckOut( );
+
+ // Reload the document as we may still have local changes
+ SfxViewFrame *pFrame = GetFrame();
+ if ( pFrame )
+ pFrame->GetDispatcher()->Execute(SID_RELOAD);
+ }
+ break;
+ }
+ case SID_CHECKIN:
+ {
+ CheckIn( );
+ break;
+ }
+ }
+
+ // Prevent entry in the Pick-lists
+ if ( rReq.IsAPI() )
+ GetMedium()->SetUpdatePickList( false );
+
+ // Ignore()-branches have already returned
+ rReq.Done();
+}
+
+
+void SfxObjectShell::GetState_Impl(SfxItemSet &rSet)
+{
+ SfxWhichIter aIter( rSet );
+
+ for ( sal_uInt16 nWhich = aIter.FirstWhich(); nWhich; nWhich = aIter.NextWhich() )
+ {
+ switch ( nWhich )
+ {
+ case SID_DOCTEMPLATE :
+ {
+ if ( isExportLocked())
+ rSet.DisableItem( nWhich );
+ break;
+ }
+
+ case SID_CHECKOUT:
+ {
+ bool bShow = false;
+ Reference< XCmisDocument > xCmisDoc( GetModel(), uno::UNO_QUERY );
+ const uno::Sequence< document::CmisProperty> aCmisProperties = xCmisDoc->getCmisProperties();
+
+ if ( xCmisDoc->isVersionable( ) && aCmisProperties.hasElements( ) )
+ {
+ // Loop over the CMIS Properties to find cmis:isVersionSeriesCheckedOut
+ bool bIsGoogleFile = false;
+ bool bCheckedOut = false;
+ for ( const auto& rCmisProperty : aCmisProperties )
+ {
+ if ( rCmisProperty.Id == "cmis:isVersionSeriesCheckedOut" )
+ {
+ uno::Sequence< sal_Bool > bTmp;
+ rCmisProperty.Value >>= bTmp;
+ bCheckedOut = bTmp[0];
+ }
+ // using title to know if it's a Google Drive file
+ // maybe there's a safer way.
+ if ( rCmisProperty.Name == "title" )
+ bIsGoogleFile = true;
+ }
+ bShow = !bCheckedOut && !bIsGoogleFile;
+ }
+
+ if ( !bShow )
+ {
+ rSet.DisableItem( nWhich );
+ rSet.Put( SfxVisibilityItem( nWhich, false ) );
+ }
+ }
+ break;
+
+ case SID_CANCELCHECKOUT:
+ case SID_CHECKIN:
+ {
+ bool bShow = false;
+ Reference< XCmisDocument > xCmisDoc( GetModel(), uno::UNO_QUERY );
+ const uno::Sequence< document::CmisProperty> aCmisProperties = xCmisDoc->getCmisProperties( );
+
+ if ( xCmisDoc->isVersionable( ) && aCmisProperties.hasElements( ) )
+ {
+ // Loop over the CMIS Properties to find cmis:isVersionSeriesCheckedOut
+ bool bCheckedOut = false;
+ auto pProp = std::find_if(aCmisProperties.begin(), aCmisProperties.end(),
+ [](const document::CmisProperty& rProp) { return rProp.Id == "cmis:isVersionSeriesCheckedOut"; });
+ if (pProp != aCmisProperties.end())
+ {
+ uno::Sequence< sal_Bool > bTmp;
+ pProp->Value >>= bTmp;
+ bCheckedOut = bTmp[0];
+ }
+ bShow = bCheckedOut;
+ }
+
+ if ( !bShow )
+ {
+ rSet.DisableItem( nWhich );
+ rSet.Put( SfxVisibilityItem( nWhich, false ) );
+ }
+ }
+ break;
+
+ case SID_VERSION:
+ {
+ SfxObjectShell *pDoc = this;
+ SfxViewFrame* pFrame = GetFrame();
+ if ( !pFrame )
+ pFrame = SfxViewFrame::GetFirst( this );
+
+ if ( !pFrame || !pDoc->HasName() ||
+ !IsOwnStorageFormat( *pDoc->GetMedium() ) )
+ rSet.DisableItem( nWhich );
+ break;
+ }
+ case SID_SAVEDOC:
+ {
+ if ( IsReadOnly() || isSaveLocked())
+ {
+ rSet.DisableItem(nWhich);
+ break;
+ }
+ rSet.Put(SfxStringItem(nWhich, SfxResId(STR_SAVEDOC)));
+ }
+ break;
+
+ case SID_DOCINFO:
+ break;
+
+ case SID_CLOSEDOC:
+ {
+ rSet.Put(SfxStringItem(nWhich, SfxResId(STR_CLOSEDOC)));
+ break;
+ }
+
+ case SID_SAVEASDOC:
+ {
+ if (!(pImpl->nLoadedFlags & SfxLoadedFlags::MAINDOCUMENT)
+ || isExportLocked())
+ {
+ rSet.DisableItem( nWhich );
+ break;
+ }
+ if ( /*!pCombinedFilters ||*/ !GetMedium() )
+ rSet.DisableItem( nWhich );
+ else
+ rSet.Put( SfxStringItem( nWhich, SfxResId(STR_SAVEASDOC) ) );
+ break;
+ }
+
+ case SID_SAVEACOPY:
+ {
+ if (!(pImpl->nLoadedFlags & SfxLoadedFlags::MAINDOCUMENT) || isExportLocked())
+ {
+ rSet.DisableItem( nWhich );
+ break;
+ }
+ if ( /*!pCombinedFilters ||*/ !GetMedium() )
+ rSet.DisableItem( nWhich );
+ else
+ rSet.Put( SfxStringItem( nWhich, SfxResId(STR_SAVEACOPY) ) );
+ break;
+ }
+
+ case SID_EXPORTDOC:
+ case SID_EXPORTDOCASPDF:
+ case SID_DIRECTEXPORTDOCASPDF:
+ case SID_EXPORTDOCASEPUB:
+ case SID_DIRECTEXPORTDOCASEPUB:
+ case SID_REDACTDOC:
+ case SID_AUTOREDACTDOC:
+ case SID_SAVEASREMOTE:
+ {
+ if (isExportLocked())
+ rSet.DisableItem( nWhich );
+ break;
+ }
+
+ case SID_DOC_MODIFIED:
+ {
+ rSet.Put( SfxBoolItem( SID_DOC_MODIFIED, IsModified() ) );
+ break;
+ }
+
+ case SID_MODIFIED:
+ {
+ rSet.Put( SfxBoolItem( SID_MODIFIED, IsModified() ) );
+ break;
+ }
+
+ case SID_DOCINFO_TITLE:
+ {
+ rSet.Put( SfxStringItem(
+ SID_DOCINFO_TITLE, getDocProperties()->getTitle() ) );
+ break;
+ }
+ case SID_FILE_NAME:
+ {
+ if( GetMedium() && HasName() )
+ rSet.Put( SfxStringItem(
+ SID_FILE_NAME, GetMedium()->GetName() ) );
+ break;
+ }
+ case SID_SIGNATURE:
+ {
+ SfxViewFrame *pFrame = SfxViewFrame::GetFirst(this);
+ if ( pFrame )
+ {
+ SignatureState eState = GetDocumentSignatureState();
+ InfobarType aInfobarType(InfobarType::INFO);
+ OUString sMessage("");
+
+ switch (eState)
+ {
+ case SignatureState::BROKEN:
+ sMessage = SfxResId(STR_SIGNATURE_BROKEN);
+ aInfobarType = InfobarType::DANGER;
+ break;
+ case SignatureState::INVALID:
+ sMessage = SfxResId(STR_SIGNATURE_INVALID);
+ // Warning only, I've tried Danger and it looked too scary
+ aInfobarType = InfobarType::WARNING;
+ break;
+ case SignatureState::NOTVALIDATED:
+ sMessage = SfxResId(STR_SIGNATURE_NOTVALIDATED);
+ aInfobarType = InfobarType::WARNING;
+ break;
+ case SignatureState::PARTIAL_OK:
+ sMessage = SfxResId(STR_SIGNATURE_PARTIAL_OK);
+ aInfobarType = InfobarType::WARNING;
+ break;
+ case SignatureState::OK:
+ sMessage = SfxResId(STR_SIGNATURE_OK);
+ aInfobarType = InfobarType::INFO;
+ break;
+ case SignatureState::NOTVALIDATED_PARTIAL_OK:
+ sMessage = SfxResId(STR_SIGNATURE_NOTVALIDATED_PARTIAL_OK);
+ aInfobarType = InfobarType::WARNING;
+ break;
+ //FIXME SignatureState::Unknown, own message?
+ default:
+ break;
+ }
+
+ // new info bar
+ if ( !pFrame->HasInfoBarWithID(u"signature") )
+ {
+ if ( !sMessage.isEmpty() )
+ {
+ auto pInfoBar = pFrame->AppendInfoBar("signature", "", sMessage, aInfobarType);
+ if (pInfoBar == nullptr || pInfoBar->isDisposed())
+ return;
+ weld::Button& rBtn = pInfoBar->addButton();
+ rBtn.set_label(SfxResId(STR_SIGNATURE_SHOW));
+ rBtn.connect_clicked(LINK(this, SfxObjectShell, SignDocumentHandler));
+ }
+ }
+ else // info bar exists already
+ {
+ if ( eState == SignatureState::NOSIGNATURES )
+ pFrame->RemoveInfoBar(u"signature");
+ else
+ pFrame->UpdateInfoBar(u"signature", "", sMessage, aInfobarType);
+ }
+ }
+
+ rSet.Put( SfxUInt16Item( SID_SIGNATURE, static_cast<sal_uInt16>(GetDocumentSignatureState()) ) );
+ break;
+ }
+ case SID_MACRO_SIGNATURE:
+ {
+ // the slot makes sense only if there is a macro in the document
+ if ( pImpl->documentStorageHasMacros() || pImpl->aMacroMode.hasMacroLibrary() )
+ rSet.Put( SfxUInt16Item( SID_MACRO_SIGNATURE, static_cast<sal_uInt16>(GetScriptingSignatureState()) ) );
+ else
+ rSet.DisableItem( nWhich );
+ break;
+ }
+ case SID_DOC_REPAIR:
+ {
+ SfxUndoManager* pIUndoMgr = GetUndoManager();
+ if (pIUndoMgr)
+ rSet.Put( SfxBoolItem(nWhich, pIUndoMgr->IsEmptyActions()) );
+ else
+ rSet.DisableItem( nWhich );
+ break;
+ }
+ }
+ }
+}
+
+IMPL_LINK_NOARG(SfxObjectShell, SignDocumentHandler, weld::Button&, void)
+{
+ SfxViewFrame* pViewFrm = SfxViewFrame::GetFirst(this);
+ if (!pViewFrm)
+ {
+ SAL_WARN("sfx.appl", "There should be some SfxViewFrame associated here");
+ return;
+ }
+ SfxUnoFrameItem aDocFrame(SID_FILLFRAME, pViewFrm->GetFrame().GetFrameInterface());
+ pViewFrm->GetDispatcher()->ExecuteList(SID_SIGNATURE, SfxCallMode::SLOT, {}, { &aDocFrame });
+}
+
+void SfxObjectShell::ExecProps_Impl(SfxRequest &rReq)
+{
+ switch ( rReq.GetSlot() )
+ {
+ case SID_MODIFIED:
+ {
+ SetModified( rReq.GetArgs()->Get(SID_MODIFIED).GetValue() );
+ rReq.Done();
+ break;
+ }
+
+ case SID_DOCTITLE:
+ SetTitle( rReq.GetArgs()->Get(SID_DOCTITLE).GetValue() );
+ rReq.Done();
+ break;
+
+ case SID_DOCINFO_AUTHOR :
+ getDocProperties()->setAuthor( static_cast<const SfxStringItem&>(rReq.GetArgs()->Get(rReq.GetSlot())).GetValue() );
+ break;
+
+ case SID_DOCINFO_COMMENTS :
+ getDocProperties()->setDescription( static_cast<const SfxStringItem&>(rReq.GetArgs()->Get(rReq.GetSlot())).GetValue() );
+ break;
+
+ case SID_DOCINFO_KEYWORDS :
+ {
+ const OUString aStr = static_cast<const SfxStringItem&>(rReq.GetArgs()->Get(rReq.GetSlot())).GetValue();
+ getDocProperties()->setKeywords(
+ ::comphelper::string::convertCommaSeparated(aStr) );
+ break;
+ }
+ }
+}
+
+
+void SfxObjectShell::StateProps_Impl(SfxItemSet &rSet)
+{
+ SfxWhichIter aIter(rSet);
+ for ( sal_uInt16 nSID = aIter.FirstWhich(); nSID; nSID = aIter.NextWhich() )
+ {
+ switch ( nSID )
+ {
+ case SID_DOCINFO_AUTHOR :
+ {
+ rSet.Put( SfxStringItem( nSID,
+ getDocProperties()->getAuthor() ) );
+ break;
+ }
+
+ case SID_DOCINFO_COMMENTS :
+ {
+ rSet.Put( SfxStringItem( nSID,
+ getDocProperties()->getDescription()) );
+ break;
+ }
+
+ case SID_DOCINFO_KEYWORDS :
+ {
+ rSet.Put( SfxStringItem( nSID, ::comphelper::string::
+ convertCommaSeparated(getDocProperties()->getKeywords())) );
+ break;
+ }
+
+ case SID_DOCPATH:
+ {
+ OSL_FAIL( "Not supported anymore!" );
+ break;
+ }
+
+ case SID_DOCFULLNAME:
+ {
+ rSet.Put( SfxStringItem( SID_DOCFULLNAME, GetTitle(SFX_TITLE_FULLNAME) ) );
+ break;
+ }
+
+ case SID_DOCTITLE:
+ {
+ rSet.Put( SfxStringItem( SID_DOCTITLE, GetTitle() ) );
+ break;
+ }
+
+ case SID_DOC_READONLY:
+ {
+ rSet.Put( SfxBoolItem( SID_DOC_READONLY, IsReadOnly() ) );
+ break;
+ }
+
+ case SID_DOC_SAVED:
+ {
+ rSet.Put( SfxBoolItem( SID_DOC_SAVED, !IsModified() ) );
+ break;
+ }
+
+ case SID_CLOSING:
+ {
+ rSet.Put( SfxBoolItem( SID_CLOSING, false ) );
+ break;
+ }
+
+ case SID_DOC_LOADING:
+ rSet.Put( SfxBoolItem( nSID, ! ( pImpl->nLoadedFlags & SfxLoadedFlags::MAINDOCUMENT ) ) );
+ break;
+
+ case SID_IMG_LOADING:
+ rSet.Put( SfxBoolItem( nSID, ! ( pImpl->nLoadedFlags & SfxLoadedFlags::IMAGES ) ) );
+ break;
+ }
+ }
+}
+
+
+void SfxObjectShell::ExecView_Impl(SfxRequest &rReq)
+{
+ switch ( rReq.GetSlot() )
+ {
+ case SID_ACTIVATE:
+ {
+ SfxViewFrame *pFrame = SfxViewFrame::GetFirst( this );
+ if ( pFrame )
+ pFrame->GetFrame().Appear();
+ rReq.SetReturnValue( SfxObjectItem( 0, pFrame ) );
+ rReq.Done();
+ break;
+ }
+ }
+}
+
+
+void SfxObjectShell::StateView_Impl(SfxItemSet& /*rSet*/)
+{
+}
+
+/// Does this ZIP storage have a signature stream?
+static bool HasSignatureStream(const uno::Reference<embed::XStorage>& xStorage)
+{
+ if (!xStorage.is())
+ return false;
+
+ if (xStorage->hasByName("META-INF"))
+ {
+ // ODF case.
+ try
+ {
+ uno::Reference<embed::XStorage> xMetaInf
+ = xStorage->openStorageElement("META-INF", embed::ElementModes::READ);
+ if (xMetaInf.is())
+ {
+ return xMetaInf->hasByName("documentsignatures.xml")
+ || xMetaInf->hasByName("macrosignatures.xml")
+ || xMetaInf->hasByName("packagesignatures.xml");
+ }
+ }
+ catch (const css::io::IOException&)
+ {
+ TOOLS_WARN_EXCEPTION("sfx.doc", "HasSignatureStream: failed to open META-INF");
+ }
+ }
+
+ // OOXML case.
+ return xStorage->hasByName("_xmlsignatures");
+}
+
+uno::Sequence< security::DocumentSignatureInformation > SfxObjectShell::GetDocumentSignatureInformation( bool bScriptingContent, const uno::Reference< security::XDocumentDigitalSignatures >& xSigner )
+{
+ uno::Sequence< security::DocumentSignatureInformation > aResult;
+ uno::Reference< security::XDocumentDigitalSignatures > xLocSigner = xSigner;
+
+ bool bSupportsSigning = GetMedium() && GetMedium()->GetFilter() && GetMedium()->GetFilter()->GetSupportsSigning();
+ if (GetMedium() && !GetMedium()->GetName().isEmpty() && ((IsOwnStorageFormat(*GetMedium()) && GetMedium()->GetStorage().is()) || bSupportsSigning))
+ {
+ try
+ {
+ if ( !xLocSigner.is() )
+ {
+ OUString aVersion;
+ try
+ {
+ uno::Reference < beans::XPropertySet > xPropSet( GetStorage(), uno::UNO_QUERY_THROW );
+ xPropSet->getPropertyValue("Version") >>= aVersion;
+ }
+ catch( uno::Exception& )
+ {
+ }
+
+ xLocSigner.set( security::DocumentDigitalSignatures::createWithVersion(comphelper::getProcessComponentContext(), aVersion) );
+
+ }
+
+ if ( bScriptingContent )
+ aResult = xLocSigner->verifyScriptingContentSignatures( GetMedium()->GetZipStorageToSign_Impl(),
+ uno::Reference< io::XInputStream >() );
+ else
+ {
+ if (GetMedium()->GetStorage(false).is())
+ {
+ // Something ZIP-based.
+ // Only call into xmlsecurity if we see a signature stream,
+ // as libxmlsec init is expensive.
+ if (HasSignatureStream(GetMedium()->GetZipStorageToSign_Impl()))
+ aResult = xLocSigner->verifyDocumentContentSignatures( GetMedium()->GetZipStorageToSign_Impl(),
+ uno::Reference< io::XInputStream >() );
+ }
+ else
+ {
+ // Not ZIP-based, e.g. PDF.
+
+ // Create temp file if needed.
+ GetMedium()->CreateTempFile(/*bReplace=*/false);
+
+ std::unique_ptr<SvStream> pStream(utl::UcbStreamHelper::CreateStream(GetMedium()->GetName(), StreamMode::READ));
+ uno::Reference<io::XStream> xStream(new utl::OStreamWrapper(*pStream));
+ uno::Reference<io::XInputStream> xInputStream(xStream, uno::UNO_QUERY);
+ aResult = xLocSigner->verifyDocumentContentSignatures(uno::Reference<embed::XStorage>(), xInputStream);
+ }
+ }
+ }
+ catch( css::uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("sfx.doc", "Failed to get document signature information");
+ }
+ }
+
+ return aResult;
+}
+
+SignatureState SfxObjectShell::ImplGetSignatureState( bool bScriptingContent )
+{
+ SignatureState* pState = bScriptingContent ? &pImpl->nScriptingSignatureState : &pImpl->nDocumentSignatureState;
+
+ if ( *pState == SignatureState::UNKNOWN )
+ {
+ *pState = SignatureState::NOSIGNATURES;
+
+ uno::Sequence< security::DocumentSignatureInformation > aInfos = GetDocumentSignatureInformation( bScriptingContent );
+ *pState = DocumentSignatures::getSignatureState(aInfos);
+ }
+
+ if ( *pState == SignatureState::OK || *pState == SignatureState::NOTVALIDATED
+ || *pState == SignatureState::PARTIAL_OK)
+ {
+ if ( IsModified() )
+ *pState = SignatureState::INVALID;
+ }
+
+ return *pState;
+}
+
+bool SfxObjectShell::PrepareForSigning(weld::Window* pDialogParent)
+{
+ // check whether the document is signed
+ ImplGetSignatureState(); // document signature
+ if (GetMedium() && GetMedium()->GetFilter() && GetMedium()->GetFilter()->IsOwnFormat())
+ ImplGetSignatureState( true ); // script signature
+ bool bHasSign = ( pImpl->nScriptingSignatureState != SignatureState::NOSIGNATURES || pImpl->nDocumentSignatureState != SignatureState::NOSIGNATURES );
+
+ // the target ODF version on saving (only valid when signing ODF of course)
+ SvtSaveOptions::ODFSaneDefaultVersion nVersion = GetODFSaneDefaultVersion();
+
+ // the document is not new and is not modified
+ OUString aODFVersion(comphelper::OStorageHelper::GetODFVersionFromStorage(GetStorage()));
+
+ if ( IsModified() || !GetMedium() || GetMedium()->GetName().isEmpty()
+ || (GetMedium()->GetFilter()->IsOwnFormat() && aODFVersion.compareTo(ODFVER_012_TEXT) < 0 && !bHasSign))
+ {
+ // the document might need saving ( new, modified or in ODF1.1 format without signature )
+
+ if (nVersion >= SvtSaveOptions::ODFSVER_012)
+ {
+ OUString sQuestion(bHasSign ? SfxResId(STR_XMLSEC_QUERY_SAVESIGNEDBEFORESIGN) : SfxResId(RID_SVXSTR_XMLSEC_QUERY_SAVEBEFORESIGN));
+ std::unique_ptr<weld::MessageDialog> xQuestion(Application::CreateMessageDialog(pDialogParent,
+ VclMessageType::Question, VclButtonsType::YesNo, sQuestion));
+
+
+ if (xQuestion->run() == RET_YES)
+ {
+ sal_uInt16 nId = SID_SAVEDOC;
+ if ( !GetMedium() || GetMedium()->GetName().isEmpty() )
+ nId = SID_SAVEASDOC;
+ SfxRequest aSaveRequest( nId, SfxCallMode::SLOT, GetPool() );
+ //ToDo: Review. We needed to call SetModified, otherwise the document would not be saved.
+ SetModified();
+ ExecFile_Impl( aSaveRequest );
+
+ // Check if it is stored a format which supports signing
+ if (GetMedium() && GetMedium()->GetFilter() && !GetMedium()->GetName().isEmpty()
+ && ((!GetMedium()->GetFilter()->IsOwnFormat()
+ && !GetMedium()->GetFilter()->GetSupportsSigning())
+ || (GetMedium()->GetFilter()->IsOwnFormat()
+ && !GetMedium()->HasStorage_Impl())))
+ {
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(
+ pDialogParent, VclMessageType::Info, VclButtonsType::Ok,
+ SfxResId(STR_INFO_WRONGDOCFORMAT)));
+
+ xBox->run();
+ return false;
+ }
+ }
+ else
+ {
+ // When the document is modified then we must not show the
+ // digital signatures dialog
+ // If we have come here then the user denied to save.
+ if (!bHasSign)
+ return false;
+ }
+ }
+ else
+ {
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(pDialogParent,
+ VclMessageType::Warning, VclButtonsType::Ok, SfxResId(STR_XMLSEC_ODF12_EXPECTED)));
+ xBox->run();
+ return false;
+ }
+
+ if ( IsModified() || !GetMedium() || GetMedium()->GetName().isEmpty() )
+ return false;
+ }
+
+ // the document is not modified currently, so it can not become modified after signing
+ pImpl->m_bAllowModifiedBackAfterSigning = false;
+ if ( IsEnableSetModified() )
+ {
+ EnableSetModified( false );
+ pImpl->m_bAllowModifiedBackAfterSigning = true;
+ }
+
+ // we have to store to the original document, the original medium should be closed for this time
+ if ( ConnectTmpStorage_Impl( pMedium->GetStorage(), pMedium ) )
+ {
+ GetMedium()->CloseAndRelease();
+ return true;
+ }
+ return false;
+}
+
+void SfxObjectShell::RecheckSignature(bool bAlsoRecheckScriptingSignature)
+{
+ if (bAlsoRecheckScriptingSignature)
+ pImpl->nScriptingSignatureState = SignatureState::UNKNOWN; // Re-Check
+
+ pImpl->nDocumentSignatureState = SignatureState::UNKNOWN; // Re-Check
+
+ Invalidate(SID_SIGNATURE);
+ Invalidate(SID_MACRO_SIGNATURE);
+ Broadcast(SfxHint(SfxHintId::TitleChanged));
+}
+
+void SfxObjectShell::AfterSigning(bool bSignSuccess, bool bSignScriptingContent)
+{
+ pImpl->m_bSavingForSigning = true;
+ DoSaveCompleted( GetMedium() );
+ pImpl->m_bSavingForSigning = false;
+
+ if ( bSignSuccess )
+ RecheckSignature(bSignScriptingContent);
+
+ if ( pImpl->m_bAllowModifiedBackAfterSigning )
+ EnableSetModified();
+}
+
+bool SfxObjectShell::CheckIsReadonly(bool bSignScriptingContent, weld::Window* pDialogParent)
+{
+ // in LOK case we support only viewer / readonly mode so far
+ if (GetMedium()->IsOriginallyReadOnly() || comphelper::LibreOfficeKit::isActive())
+ {
+ // If the file is physically read-only, we just show the existing signatures
+ try
+ {
+ OUString aODFVersion(
+ comphelper::OStorageHelper::GetODFVersionFromStorage(GetStorage()));
+ uno::Reference<security::XDocumentDigitalSignatures> xSigner(
+ security::DocumentDigitalSignatures::createWithVersionAndValidSignature(
+ comphelper::getProcessComponentContext(), aODFVersion, HasValidSignatures()));
+
+ if (pDialogParent)
+ xSigner->setParentWindow(pDialogParent->GetXWindow());
+
+ if (bSignScriptingContent)
+ xSigner->showScriptingContentSignatures(GetMedium()->GetZipStorageToSign_Impl(),
+ uno::Reference<io::XInputStream>());
+ else
+ {
+ uno::Reference<embed::XStorage> xStorage = GetMedium()->GetZipStorageToSign_Impl();
+ if (xStorage.is())
+ xSigner->showDocumentContentSignatures(xStorage,
+ uno::Reference<io::XInputStream>());
+ else
+ {
+ std::unique_ptr<SvStream> pStream(
+ utl::UcbStreamHelper::CreateStream(GetName(), StreamMode::READ));
+
+ if (!pStream)
+ {
+ pStream = utl::UcbStreamHelper::CreateStream(GetMedium()->GetName(), StreamMode::READ);
+
+ if (!pStream)
+ {
+ SAL_WARN( "sfx.doc", "Couldn't use signing functionality!" );
+ return true;
+ }
+ }
+
+ uno::Reference<io::XInputStream> xStream(new utl::OStreamWrapper(*pStream));
+ xSigner->showDocumentContentSignatures(uno::Reference<embed::XStorage>(),
+ xStream);
+ }
+ }
+ }
+ catch (const uno::Exception&)
+ {
+ SAL_WARN("sfx.doc", "Couldn't use signing functionality!");
+ }
+ return true;
+ }
+ return false;
+}
+
+bool SfxObjectShell::HasValidSignatures() const
+{
+ return pImpl->nDocumentSignatureState == SignatureState::OK
+ || pImpl->nDocumentSignatureState == SignatureState::NOTVALIDATED
+ || pImpl->nDocumentSignatureState == SignatureState::PARTIAL_OK;
+}
+
+SignatureState SfxObjectShell::GetDocumentSignatureState()
+{
+ return ImplGetSignatureState();
+}
+
+void SfxObjectShell::SignDocumentContent(weld::Window* pDialogParent)
+{
+ if (!PrepareForSigning(pDialogParent))
+ return;
+
+ if (CheckIsReadonly(false, pDialogParent))
+ return;
+
+ bool bSignSuccess = GetMedium()->SignContents_Impl(pDialogParent, false, HasValidSignatures());
+
+ AfterSigning(bSignSuccess, false);
+}
+
+bool SfxObjectShell::SignDocumentContentUsingCertificate(const Reference<XCertificate>& xCertificate)
+{
+ // 1. PrepareForSigning
+
+ // check whether the document is signed
+ ImplGetSignatureState(false); // document signature
+ if (GetMedium() && GetMedium()->GetFilter() && GetMedium()->GetFilter()->IsOwnFormat())
+ ImplGetSignatureState( true ); // script signature
+ bool bHasSign = ( pImpl->nScriptingSignatureState != SignatureState::NOSIGNATURES || pImpl->nDocumentSignatureState != SignatureState::NOSIGNATURES );
+
+ // the target ODF version on saving (only valid when signing ODF of course)
+ SvtSaveOptions::ODFSaneDefaultVersion nVersion = GetODFSaneDefaultVersion();
+
+ // the document is not new and is not modified
+ OUString aODFVersion(comphelper::OStorageHelper::GetODFVersionFromStorage(GetStorage()));
+
+ if (IsModified() || !GetMedium() || GetMedium()->GetName().isEmpty()
+ || (GetMedium()->GetFilter()->IsOwnFormat() && aODFVersion.compareTo(ODFVER_012_TEXT) < 0 && !bHasSign))
+ {
+ if (nVersion >= SvtSaveOptions::ODFSVER_012)
+ {
+ sal_uInt16 nId = SID_SAVEDOC;
+ if ( !GetMedium() || GetMedium()->GetName().isEmpty() )
+ nId = SID_SAVEASDOC;
+ SfxRequest aSaveRequest( nId, SfxCallMode::SLOT, GetPool() );
+ //ToDo: Review. We needed to call SetModified, otherwise the document would not be saved.
+ SetModified();
+ ExecFile_Impl( aSaveRequest );
+
+ // Check if it is stored a format which supports signing
+ if (GetMedium() && GetMedium()->GetFilter() && !GetMedium()->GetName().isEmpty()
+ && ((!GetMedium()->GetFilter()->IsOwnFormat()
+ && !GetMedium()->GetFilter()->GetSupportsSigning())
+ || (GetMedium()->GetFilter()->IsOwnFormat()
+ && !GetMedium()->HasStorage_Impl())))
+ {
+ return false;
+ }
+ }
+ else
+ {
+ return false;
+ }
+
+ if ( IsModified() || !GetMedium() || GetMedium()->GetName().isEmpty() )
+ return false;
+ }
+
+ // the document is not modified currently, so it can not become modified after signing
+ pImpl->m_bAllowModifiedBackAfterSigning = false;
+ if ( IsEnableSetModified() )
+ {
+ EnableSetModified( false );
+ pImpl->m_bAllowModifiedBackAfterSigning = true;
+ }
+
+ // we have to store to the original document, the original medium should be closed for this time
+ bool bResult = ConnectTmpStorage_Impl( pMedium->GetStorage(), pMedium);
+
+ if (!bResult)
+ return false;
+
+ GetMedium()->CloseAndRelease();
+
+ // 2. Check Read-Only
+ if (GetMedium()->IsOriginallyReadOnly())
+ return false;
+
+ // 3. Sign
+ bool bSignSuccess = GetMedium()->SignDocumentContentUsingCertificate(
+ GetBaseModel(), HasValidSignatures(), xCertificate);
+
+ // 4. AfterSigning
+ AfterSigning(bSignSuccess, false);
+
+ return true;
+}
+
+void SfxObjectShell::SignSignatureLine(weld::Window* pDialogParent,
+ const OUString& aSignatureLineId,
+ const Reference<XCertificate>& xCert,
+ const Reference<XGraphic>& xValidGraphic,
+ const Reference<XGraphic>& xInvalidGraphic,
+ const OUString& aComment)
+{
+ if (!PrepareForSigning(pDialogParent))
+ return;
+
+ if (CheckIsReadonly(false, pDialogParent))
+ return;
+
+ bool bSignSuccess = GetMedium()->SignContents_Impl(pDialogParent,
+ false, HasValidSignatures(), aSignatureLineId, xCert, xValidGraphic, xInvalidGraphic, aComment);
+
+ AfterSigning(bSignSuccess, false);
+
+ // Reload the document to get the updated graphic
+ // FIXME: Update just the signature line graphic instead of reloading the document
+ SfxViewFrame *pFrame = GetFrame();
+ if (pFrame)
+ pFrame->GetDispatcher()->Execute(SID_RELOAD);
+}
+
+SignatureState SfxObjectShell::GetScriptingSignatureState()
+{
+ return ImplGetSignatureState( true );
+}
+
+void SfxObjectShell::SignScriptingContent(weld::Window* pDialogParent)
+{
+ if (!PrepareForSigning(pDialogParent))
+ return;
+
+ if (CheckIsReadonly(true, pDialogParent))
+ return;
+
+ bool bSignSuccess = GetMedium()->SignContents_Impl(pDialogParent, true, HasValidSignatures());
+
+ AfterSigning(bSignSuccess, true);
+}
+
+const uno::Sequence<sal_Int8>& SfxObjectShell::getUnoTunnelId()
+{
+ static const comphelper::UnoIdInit theSfxObjectShellUnoTunnelId;
+ return theSfxObjectShellUnoTunnelId.getSeq();
+}
+
+uno::Sequence< beans::PropertyValue > SfxObjectShell::GetDocumentProtectionFromGrabBag() const
+{
+ uno::Reference<frame::XModel> xModel = GetBaseModel();
+
+ if (!xModel.is())
+ {
+ return uno::Sequence< beans::PropertyValue>();
+ }
+
+ uno::Reference< beans::XPropertySet > xPropSet( xModel, uno::UNO_QUERY_THROW );
+ uno::Reference< beans::XPropertySetInfo > xPropSetInfo = xPropSet->getPropertySetInfo();
+ const OUString aGrabBagName = UNO_NAME_MISC_OBJ_INTEROPGRABBAG;
+ if ( xPropSetInfo->hasPropertyByName( aGrabBagName ) )
+ {
+ uno::Sequence< beans::PropertyValue > propList;
+ xPropSet->getPropertyValue( aGrabBagName ) >>= propList;
+ for( const auto& rProp : std::as_const(propList) )
+ {
+ if (rProp.Name == "DocumentProtection")
+ {
+ uno::Sequence< beans::PropertyValue > rAttributeList;
+ rProp.Value >>= rAttributeList;
+ return rAttributeList;
+ }
+ }
+ }
+
+ return uno::Sequence< beans::PropertyValue>();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/doc/objstor.cxx b/sfx2/source/doc/objstor.cxx
new file mode 100644
index 000000000..288b6a7ae
--- /dev/null
+++ b/sfx2/source/doc/objstor.cxx
@@ -0,0 +1,3791 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * 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 <config_features.h>
+
+#include <sal/config.h>
+#include <sal/log.hxx>
+
+#include <cassert>
+
+#include <svl/eitem.hxx>
+#include <svl/stritem.hxx>
+#include <svl/intitem.hxx>
+#include <com/sun/star/frame/theGlobalEventBroadcaster.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/frame/XModule.hpp>
+#include <com/sun/star/document/XFilter.hpp>
+#include <com/sun/star/document/XImporter.hpp>
+#include <com/sun/star/document/XExporter.hpp>
+#include <com/sun/star/packages/zip/ZipIOException.hpp>
+#include <com/sun/star/task/XInteractionHandler.hpp>
+#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
+#include <com/sun/star/document/MacroExecMode.hpp>
+#include <com/sun/star/ui/dialogs/ExtendedFilePickerElementIds.hpp>
+#include <com/sun/star/beans/XPropertySetInfo.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/container/XNameAccess.hpp>
+#include <com/sun/star/embed/ElementModes.hpp>
+#include <com/sun/star/embed/EmbedStates.hpp>
+#include <com/sun/star/embed/XTransactedObject.hpp>
+#include <com/sun/star/embed/XEmbeddedObject.hpp>
+#include <com/sun/star/embed/XEmbedPersist.hpp>
+#include <com/sun/star/embed/XOptimizedStorage.hpp>
+#include <com/sun/star/embed/XEncryptionProtectedStorage.hpp>
+#include <com/sun/star/io/WrongFormatException.hpp>
+#include <com/sun/star/io/XTruncate.hpp>
+#include <com/sun/star/util/XModifiable.hpp>
+#include <com/sun/star/util/RevisionTag.hpp>
+#include <com/sun/star/security/DocumentDigitalSignatures.hpp>
+#include <com/sun/star/text/XTextRange.hpp>
+#include <com/sun/star/xml/crypto/CipherID.hpp>
+#include <com/sun/star/xml/crypto/DigestID.hpp>
+
+#include <com/sun/star/document/XDocumentProperties.hpp>
+#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
+#include <comphelper/fileformat.h>
+#include <comphelper/processfactory.hxx>
+#include <svtools/langtab.hxx>
+#include <svtools/sfxecode.hxx>
+#include <unotools/configmgr.hxx>
+#include <unotools/streamwrap.hxx>
+
+#include <unotools/saveopt.hxx>
+#include <unotools/useroptions.hxx>
+#include <unotools/securityoptions.hxx>
+#include <tools/urlobj.hxx>
+#include <tools/diagnose_ex.h>
+#include <unotools/ucbhelper.hxx>
+#include <unotools/tempfile.hxx>
+#include <unotools/docinfohelper.hxx>
+#include <unotools/mediadescriptor.hxx>
+#include <ucbhelper/content.hxx>
+#include <sot/storage.hxx>
+#include <sot/exchange.hxx>
+#include <sot/formats.hxx>
+#include <comphelper/storagehelper.hxx>
+#include <comphelper/documentconstants.hxx>
+#include <comphelper/string.hxx>
+#include <vcl/errinf.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+#include <basic/modsizeexceeded.hxx>
+#include <officecfg/Office/Common.hxx>
+#include <osl/file.hxx>
+#include <comphelper/scopeguard.hxx>
+#include <comphelper/lok.hxx>
+
+#include <sfx2/signaturestate.hxx>
+#include <sfx2/app.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/sfxresid.hxx>
+#include <sfx2/docfile.hxx>
+#include <sfx2/fcontnr.hxx>
+#include <sfx2/docfilt.hxx>
+#include <sfx2/docfac.hxx>
+#include <appopen.hxx>
+#include <objshimp.hxx>
+#include <sfx2/strings.hrc>
+#include <sfx2/sfxsids.hrc>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/sfxuno.hxx>
+#include <sfx2/event.hxx>
+#include <fltoptint.hxx>
+#include <sfx2/viewfrm.hxx>
+#include "graphhelp.hxx"
+#include <appbaslib.hxx>
+#include "objstor.hxx"
+#include "exoticfileloadexception.hxx"
+
+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::uno;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::ucb;
+using namespace ::com::sun::star::task;
+using namespace ::com::sun::star::document;
+using namespace ::cppu;
+
+
+void impl_addToModelCollection(const css::uno::Reference< css::frame::XModel >& xModel)
+{
+ if (!xModel.is())
+ return;
+
+ css::uno::Reference< css::uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext();
+ css::uno::Reference< css::frame::XGlobalEventBroadcaster > xModelCollection =
+ css::frame::theGlobalEventBroadcaster::get(xContext);
+ try
+ {
+ xModelCollection->insert(css::uno::Any(xModel));
+ }
+ catch ( uno::Exception& )
+ {
+ SAL_WARN( "sfx.doc", "The document seems to be in the collection already!" );
+ }
+}
+
+
+bool SfxObjectShell::Save()
+{
+ SaveChildren();
+ return true;
+}
+
+
+bool SfxObjectShell::SaveAs( SfxMedium& rMedium )
+{
+ return SaveAsChildren( rMedium );
+}
+
+
+bool SfxObjectShell::QuerySlotExecutable( sal_uInt16 /*nSlotId*/ )
+{
+ return true;
+}
+
+
+bool GetEncryptionData_Impl( const SfxItemSet* pSet, uno::Sequence< beans::NamedValue >& o_rEncryptionData )
+{
+ bool bResult = false;
+ if ( pSet )
+ {
+ const SfxUnoAnyItem* pEncryptionDataItem = SfxItemSet::GetItem<SfxUnoAnyItem>(pSet, SID_ENCRYPTIONDATA, false);
+ if ( pEncryptionDataItem )
+ {
+ pEncryptionDataItem->GetValue() >>= o_rEncryptionData;
+ bResult = true;
+ }
+ else
+ {
+ const SfxStringItem* pPasswordItem = SfxItemSet::GetItem<SfxStringItem>(pSet, SID_PASSWORD, false);
+ if ( pPasswordItem )
+ {
+ o_rEncryptionData = ::comphelper::OStorageHelper::CreatePackageEncryptionData( pPasswordItem->GetValue() );
+ bResult = true;
+ }
+ }
+ }
+
+ return bResult;
+}
+
+
+bool SfxObjectShell::PutURLContentsToVersionStream_Impl(
+ const OUString& aURL,
+ const uno::Reference< embed::XStorage >& xDocStorage,
+ const OUString& aStreamName )
+{
+ bool bResult = false;
+ try
+ {
+ uno::Reference< embed::XStorage > xVersion = xDocStorage->openStorageElement(
+ "Versions",
+ embed::ElementModes::READWRITE );
+
+ DBG_ASSERT( xVersion.is(),
+ "The method must throw an exception if the storage can not be opened!" );
+ if ( !xVersion.is() )
+ throw uno::RuntimeException();
+
+ uno::Reference< io::XStream > xVerStream = xVersion->openStreamElement(
+ aStreamName,
+ embed::ElementModes::READWRITE );
+ DBG_ASSERT( xVerStream.is(), "The method must throw an exception if the storage can not be opened!" );
+ if ( !xVerStream.is() )
+ throw uno::RuntimeException();
+
+ uno::Reference< io::XOutputStream > xOutStream = xVerStream->getOutputStream();
+ uno::Reference< io::XTruncate > xTrunc( xOutStream, uno::UNO_QUERY_THROW );
+
+ uno::Reference< io::XInputStream > xTmpInStream =
+ ::comphelper::OStorageHelper::GetInputStreamFromURL(
+ aURL, comphelper::getProcessComponentContext() );
+ assert( xTmpInStream.is() );
+
+ xTrunc->truncate();
+ ::comphelper::OStorageHelper::CopyInputToOutput( xTmpInStream, xOutStream );
+ xOutStream->closeOutput();
+
+ uno::Reference< embed::XTransactedObject > xTransact( xVersion, uno::UNO_QUERY );
+ DBG_ASSERT( xTransact.is(), "The storage must implement XTransacted interface!\n" );
+ if ( xTransact.is() )
+ xTransact->commit();
+
+ bResult = true;
+ }
+ catch( uno::Exception& )
+ {
+ // TODO/LATER: handle the error depending on exception
+ SetError(ERRCODE_IO_GENERAL);
+ }
+
+ return bResult;
+}
+
+
+OUString SfxObjectShell::CreateTempCopyOfStorage_Impl( const uno::Reference< embed::XStorage >& xStorage )
+{
+ OUString aTempURL = ::utl::TempFile().GetURL();
+
+ DBG_ASSERT( !aTempURL.isEmpty(), "Can't create a temporary file!\n" );
+ if ( !aTempURL.isEmpty() )
+ {
+ try
+ {
+ uno::Reference< embed::XStorage > xTempStorage =
+ ::comphelper::OStorageHelper::GetStorageFromURL( aTempURL, embed::ElementModes::READWRITE );
+
+ // the password will be transferred from the xStorage to xTempStorage by storage implementation
+ xStorage->copyToStorage( xTempStorage );
+
+ // the temporary storage was committed by the previous method and it will die by refcount
+ }
+ catch ( uno::Exception& )
+ {
+ SAL_WARN( "sfx.doc", "Creation of a storage copy is failed!" );
+ ::utl::UCBContentHelper::Kill( aTempURL );
+
+ aTempURL.clear();
+
+ // TODO/LATER: may need error code setting based on exception
+ SetError(ERRCODE_IO_GENERAL);
+ }
+ }
+
+ return aTempURL;
+}
+
+
+SvGlobalName const & SfxObjectShell::GetClassName() const
+{
+ return GetFactory().GetClassId();
+}
+
+
+void SfxObjectShell::SetupStorage( const uno::Reference< embed::XStorage >& xStorage,
+ sal_Int32 nVersion, bool bTemplate ) const
+{
+ uno::Reference< beans::XPropertySet > xProps( xStorage, uno::UNO_QUERY );
+
+ if ( !xProps.is() )
+ return;
+
+ SotClipboardFormatId nClipFormat = SotClipboardFormatId::NONE;
+
+ SvGlobalName aName;
+ OUString aFullTypeName;
+ FillClass( &aName, &nClipFormat, &aFullTypeName, nVersion, bTemplate );
+
+ if ( nClipFormat == SotClipboardFormatId::NONE )
+ return;
+
+ // basic doesn't have a ClipFormat
+ // without MediaType the storage is not really usable, but currently the BasicIDE still
+ // is an SfxObjectShell and so we can't take this as an error
+ datatransfer::DataFlavor aDataFlavor;
+ SotExchange::GetFormatDataFlavor( nClipFormat, aDataFlavor );
+ if ( aDataFlavor.MimeType.isEmpty() )
+ return;
+
+ try
+ {
+ xProps->setPropertyValue("MediaType", uno::Any( aDataFlavor.MimeType ) );
+ }
+ catch( uno::Exception& )
+ {
+ const_cast<SfxObjectShell*>( this )->SetError(ERRCODE_IO_GENERAL);
+ }
+
+ SvtSaveOptions::ODFSaneDefaultVersion nDefVersion = SvtSaveOptions::ODFSVER_013;
+ if (!utl::ConfigManager::IsFuzzing())
+ {
+ nDefVersion = GetODFSaneDefaultVersion();
+ }
+
+ // the default values, that should be used for ODF1.1 and older formats
+ uno::Sequence< beans::NamedValue > aEncryptionAlgs
+ {
+ { "StartKeyGenerationAlgorithm", css::uno::Any(xml::crypto::DigestID::SHA1) },
+ { "EncryptionAlgorithm", css::uno::Any(xml::crypto::CipherID::BLOWFISH_CFB_8) },
+ { "ChecksumAlgorithm", css::uno::Any(xml::crypto::DigestID::SHA1_1K) }
+ };
+
+ if (nDefVersion >= SvtSaveOptions::ODFSVER_012)
+ {
+ try
+ {
+ // older versions can not have this property set, it exists only starting from ODF1.2
+ uno::Reference<frame::XModule> const xModule(GetModel(), uno::UNO_QUERY);
+ bool const isBaseForm(xModule.is() &&
+ xModule->getIdentifier() == "com.sun.star.sdb.FormDesign");
+ SAL_INFO_IF(isBaseForm, "sfx.doc", "tdf#138209 force form export to ODF 1.2");
+ if (!isBaseForm && SvtSaveOptions::ODFSVER_013 <= nDefVersion)
+ {
+ xProps->setPropertyValue("Version", uno::Any(OUString(ODFVER_013_TEXT)));
+ }
+ else
+ {
+ xProps->setPropertyValue("Version", uno::Any(OUString(ODFVER_012_TEXT)));
+ }
+ }
+ catch( uno::Exception& )
+ {
+ }
+
+ auto pEncryptionAlgs = aEncryptionAlgs.getArray();
+ pEncryptionAlgs[0].Value <<= xml::crypto::DigestID::SHA256;
+ pEncryptionAlgs[2].Value <<= xml::crypto::DigestID::SHA256_1K;
+ pEncryptionAlgs[1].Value <<= xml::crypto::CipherID::AES_CBC_W3C_PADDING;
+ }
+
+ try
+ {
+ // set the encryption algorithms accordingly;
+ // the setting does not trigger encryption,
+ // it just provides the format for the case that contents should be encrypted
+ uno::Reference< embed::XEncryptionProtectedStorage > xEncr( xStorage, uno::UNO_QUERY_THROW );
+ xEncr->setEncryptionAlgorithms( aEncryptionAlgs );
+ }
+ catch( uno::Exception& )
+ {
+ const_cast<SfxObjectShell*>( this )->SetError(ERRCODE_IO_GENERAL);
+ }
+}
+
+
+void SfxObjectShell::PrepareSecondTryLoad_Impl()
+{
+ // only for internal use
+ pImpl->m_xDocStorage.clear();
+ pImpl->m_bIsInit = false;
+ ResetError();
+}
+
+
+bool SfxObjectShell::GeneralInit_Impl( const uno::Reference< embed::XStorage >& xStorage,
+ bool bTypeMustBeSetAlready )
+{
+ if ( pImpl->m_bIsInit )
+ return false;
+
+ pImpl->m_bIsInit = true;
+ if ( xStorage.is() )
+ {
+ // no notification is required the storage is set the first time
+ pImpl->m_xDocStorage = xStorage;
+
+ try {
+ uno::Reference < beans::XPropertySet > xPropSet( xStorage, uno::UNO_QUERY_THROW );
+ Any a = xPropSet->getPropertyValue("MediaType");
+ OUString aMediaType;
+ if ( !(a>>=aMediaType) || aMediaType.isEmpty() )
+ {
+ if ( bTypeMustBeSetAlready )
+ {
+ SetError(ERRCODE_IO_BROKENPACKAGE);
+ return false;
+ }
+
+ SetupStorage( xStorage, SOFFICE_FILEFORMAT_CURRENT, false );
+ }
+ }
+ catch ( uno::Exception& )
+ {
+ SAL_WARN( "sfx.doc", "Can't check storage's mediatype!" );
+ }
+ }
+ else
+ pImpl->m_bCreateTempStor = true;
+
+ return true;
+}
+
+
+bool SfxObjectShell::InitNew( const uno::Reference< embed::XStorage >& xStorage )
+{
+ return GeneralInit_Impl( xStorage, false );
+}
+
+
+bool SfxObjectShell::Load( SfxMedium& rMedium )
+{
+ return GeneralInit_Impl(rMedium.GetStorage(), true);
+}
+
+void SfxObjectShell::DoInitUnitTest()
+{
+ pMedium = new SfxMedium;
+}
+
+bool SfxObjectShell::DoInitNew()
+/* [Description]
+
+ This from SvPersist inherited virtual method is called to initialize
+ the SfxObjectShell instance from a storage (PStore! = 0) or (PStore == 0)
+
+ Like with all Do...-methods there is a from a control, the actual
+ implementation is done by the virtual method in which also the
+ InitNew(SvStorate *) from the SfxObjectShell-Subclass is implemented.
+
+ For pStore == 0 the SfxObjectShell-instance is connected to an empty
+ SfxMedium, otherwise a SfxMedium, which refers to the SotStorage
+ passed as a parameter.
+
+ The object is only initialized correctly after InitNew() or Load().
+
+ [Return value]
+ true The object has been initialized.
+ false The object could not be initialized
+*/
+
+{
+ ModifyBlocker_Impl aBlock( this );
+ pMedium = new SfxMedium;
+
+ pMedium->CanDisposeStorage_Impl( true );
+
+ if ( InitNew( nullptr ) )
+ {
+ // empty documents always get their macros from the user, so there is no reason to restrict access
+ pImpl->aMacroMode.allowMacroExecution();
+ if ( SfxObjectCreateMode::EMBEDDED == eCreateMode )
+ SetTitle(SfxResId(STR_NONAME));
+
+ uno::Reference< frame::XModel > xModel = GetModel();
+ if ( xModel.is() )
+ {
+ SfxItemSet *pSet = GetMedium()->GetItemSet();
+ uno::Sequence< beans::PropertyValue > aArgs;
+ TransformItems( SID_OPENDOC, *pSet, aArgs );
+ sal_Int32 nLength = aArgs.getLength();
+ aArgs.realloc( nLength + 1 );
+ auto pArgs = aArgs.getArray();
+ pArgs[nLength].Name = "Title";
+ pArgs[nLength].Value <<= GetTitle( SFX_TITLE_DETECT );
+ xModel->attachResource( OUString(), aArgs );
+ if (!utl::ConfigManager::IsFuzzing())
+ impl_addToModelCollection(xModel);
+ }
+
+ SetInitialized_Impl( true );
+ return true;
+ }
+
+ return false;
+}
+
+bool SfxObjectShell::ImportFromGeneratedStream_Impl(
+ const uno::Reference< io::XStream >& xStream,
+ const uno::Sequence< beans::PropertyValue >& rMediaDescr )
+{
+ if ( !xStream.is() )
+ return false;
+
+ if ( pMedium && pMedium->HasStorage_Impl() )
+ pMedium->CloseStorage();
+
+ bool bResult = false;
+
+ try
+ {
+ uno::Reference< embed::XStorage > xStorage =
+ ::comphelper::OStorageHelper::GetStorageFromStream( xStream );
+
+ if ( !xStorage.is() )
+ throw uno::RuntimeException();
+
+ if ( !pMedium )
+ pMedium = new SfxMedium( xStorage, OUString() );
+ else
+ pMedium->SetStorage_Impl( xStorage );
+
+ SfxAllItemSet aSet( SfxGetpApp()->GetPool() );
+ TransformParameters( SID_OPENDOC, rMediaDescr, aSet );
+ pMedium->GetItemSet()->Put( aSet );
+ pMedium->CanDisposeStorage_Impl( false );
+ uno::Reference<text::XTextRange> xInsertTextRange;
+ for (const auto& rProp : rMediaDescr)
+ {
+ if (rProp.Name == "TextInsertModeRange")
+ {
+ rProp.Value >>= xInsertTextRange;
+ }
+ }
+
+ if (xInsertTextRange.is())
+ {
+ bResult = InsertGeneratedStream(*pMedium, xInsertTextRange);
+ }
+ else
+ {
+
+ // allow the subfilter to reinit the model
+ if ( pImpl->m_bIsInit )
+ pImpl->m_bIsInit = false;
+
+ if ( LoadOwnFormat( *pMedium ) )
+ {
+ bHasName = true;
+ if ( !IsReadOnly() && IsLoadReadonly() )
+ SetReadOnlyUI();
+
+ bResult = true;
+ OSL_ENSURE( pImpl->m_xDocStorage == xStorage, "Wrong storage is used!" );
+ }
+ }
+
+ // now the medium can be disconnected from the storage
+ // the medium is not allowed to dispose the storage so CloseStorage() can be used
+ pMedium->CloseStorage();
+ }
+ catch( uno::Exception& )
+ {
+ }
+
+ return bResult;
+}
+
+
+bool SfxObjectShell::DoLoad( SfxMedium *pMed )
+{
+ ModifyBlocker_Impl aBlock( this );
+
+ pMedium = pMed;
+ pMedium->CanDisposeStorage_Impl( true );
+
+ bool bOk = false;
+ std::shared_ptr<const SfxFilter> pFilter = pMed->GetFilter();
+ SfxItemSet* pSet = pMedium->GetItemSet();
+ if( pImpl->nEventId == SfxEventHintId::NONE )
+ {
+ const SfxBoolItem* pTemplateItem = SfxItemSet::GetItem<SfxBoolItem>(pSet, SID_TEMPLATE, false);
+ SetActivateEvent_Impl(
+ ( pTemplateItem && pTemplateItem->GetValue() )
+ ? SfxEventHintId::CreateDoc : SfxEventHintId::OpenDoc );
+ }
+
+ const SfxStringItem* pBaseItem = SfxItemSet::GetItem<SfxStringItem>(pSet, SID_BASEURL, false);
+ OUString aBaseURL;
+ const SfxStringItem* pSalvageItem = SfxItemSet::GetItem<SfxStringItem>(pMedium->GetItemSet(), SID_DOC_SALVAGE, false);
+ if( pBaseItem )
+ aBaseURL = pBaseItem->GetValue();
+ else
+ {
+ if ( pSalvageItem )
+ {
+ osl::FileBase::getFileURLFromSystemPath( pMed->GetPhysicalName(), aBaseURL );
+ }
+ else
+ aBaseURL = pMed->GetBaseURL();
+ }
+ pMed->GetItemSet()->Put( SfxStringItem( SID_DOC_BASEURL, aBaseURL ) );
+
+ pImpl->nLoadedFlags = SfxLoadedFlags::NONE;
+ pImpl->bModelInitialized = false;
+
+ if (pFilter && !pFilter->IsEnabled())
+ {
+ SetError( ERRCODE_IO_FILTERDISABLED );
+ }
+
+ if ( pFilter && pFilter->IsExoticFormat() && !QueryAllowExoticFormat_Impl( getInteractionHandler(), aBaseURL, pMed->GetFilter()->GetUIName() ) )
+ {
+ SetError( ERRCODE_IO_ABORT );
+ }
+
+ // initialize static language table so language-related extensions are learned before the document loads
+ (void)SvtLanguageTable::GetLanguageEntryCount();
+
+ //TODO/LATER: make a clear strategy how to handle "UsesStorage" etc.
+ bool bOwnStorageFormat = IsOwnStorageFormat( *pMedium );
+ bool bHasStorage = IsPackageStorageFormat_Impl( *pMedium );
+ if ( pMedium->GetFilter() )
+ {
+ ErrCode nError = HandleFilter( pMedium, this );
+ if ( nError != ERRCODE_NONE )
+ SetError(nError);
+
+ if (pMedium->GetFilter()->GetFilterFlags() & SfxFilterFlags::STARTPRESENTATION)
+ pSet->Put( SfxBoolItem( SID_DOC_STARTPRESENTATION, true) );
+ }
+
+ EnableSetModified( false );
+
+ pMedium->LockOrigFileOnDemand( true, false );
+ if ( GetError() == ERRCODE_NONE && bOwnStorageFormat && ( !pFilter || !( pFilter->GetFilterFlags() & SfxFilterFlags::STARONEFILTER ) ) )
+ {
+ uno::Reference< embed::XStorage > xStorage;
+ if ( pMedium->GetError() == ERRCODE_NONE )
+ xStorage = pMedium->GetStorage();
+
+ if( xStorage.is() && pMedium->GetLastStorageCreationState() == ERRCODE_NONE )
+ {
+ DBG_ASSERT( pFilter, "No filter for storage found!" );
+
+ try
+ {
+ bool bWarnMediaTypeFallback = false;
+ const SfxBoolItem* pRepairPackageItem = SfxItemSet::GetItem<SfxBoolItem>(pMedium->GetItemSet(), SID_REPAIRPACKAGE, false);
+
+ // treat the package as broken if the mediatype was retrieved as a fallback
+ uno::Reference< beans::XPropertySet > xStorProps( xStorage, uno::UNO_QUERY_THROW );
+ xStorProps->getPropertyValue("MediaTypeFallbackUsed")
+ >>= bWarnMediaTypeFallback;
+
+ if ( pRepairPackageItem && pRepairPackageItem->GetValue() )
+ {
+ // the macros in repaired documents should be disabled
+ pMedium->GetItemSet()->Put( SfxUInt16Item( SID_MACROEXECMODE, document::MacroExecMode::NEVER_EXECUTE ) );
+
+ // the mediatype was retrieved by using fallback solution but this is a repairing mode
+ // so it is acceptable to open the document if there is no contents that required manifest.xml
+ bWarnMediaTypeFallback = false;
+ }
+
+ if (bWarnMediaTypeFallback || !xStorage->getElementNames().hasElements())
+ SetError(ERRCODE_IO_BROKENPACKAGE);
+ }
+ catch( uno::Exception& )
+ {
+ // TODO/LATER: may need error code setting based on exception
+ SetError(ERRCODE_IO_GENERAL);
+ }
+
+ // Load
+ if ( !GetError() )
+ {
+ pImpl->nLoadedFlags = SfxLoadedFlags::NONE;
+ pImpl->bModelInitialized = false;
+ bOk = xStorage.is() && LoadOwnFormat( *pMed );
+ if ( bOk )
+ {
+ // the document loaded from template has no name
+ const SfxBoolItem* pTemplateItem = SfxItemSet::GetItem<SfxBoolItem>(pMedium->GetItemSet(), SID_TEMPLATE, false);
+ if ( !pTemplateItem || !pTemplateItem->GetValue() )
+ bHasName = true;
+ }
+ else
+ SetError(ERRCODE_ABORT);
+ }
+ }
+ else
+ SetError(pMed->GetLastStorageCreationState());
+ }
+ else if ( GetError() == ERRCODE_NONE && InitNew(nullptr) )
+ {
+ // set name before ConvertFrom, so that GetSbxObject() already works
+ bHasName = true;
+ SetName( SfxResId(STR_NONAME) );
+
+ if( !bHasStorage )
+ pMedium->GetInStream();
+ else
+ pMedium->GetStorage();
+
+ if ( GetError() == ERRCODE_NONE )
+ {
+ // Experimental PDF importing using PDFium. This is currently enabled for LOK only and
+ // we handle it not via XmlFilterAdaptor but a new SdPdfFiler.
+#if !HAVE_FEATURE_POPPLER
+ constexpr bool bUsePdfium = true;
+#else
+ const bool bUsePdfium
+ = comphelper::LibreOfficeKit::isActive() || getenv("LO_IMPORT_USE_PDFIUM");
+#endif
+ const bool bPdfiumImport
+ = bUsePdfium && pMedium->GetFilter()
+ && (pMedium->GetFilter()->GetFilterName() == "draw_pdf_import");
+
+ pImpl->nLoadedFlags = SfxLoadedFlags::NONE;
+ pImpl->bModelInitialized = false;
+ if (pMedium->GetFilter()
+ && (pMedium->GetFilter()->GetFilterFlags() & SfxFilterFlags::STARONEFILTER)
+ && !bPdfiumImport)
+ {
+ uno::Reference < beans::XPropertySet > xSet( GetModel(), uno::UNO_QUERY );
+ static const OUStringLiteral sLockUpdates(u"LockUpdates");
+ bool bSetProperty = true;
+ try
+ {
+ xSet->setPropertyValue( sLockUpdates, Any( true ) );
+ }
+ catch(const beans::UnknownPropertyException& )
+ {
+ bSetProperty = false;
+ }
+ bOk = ImportFrom(*pMedium, nullptr);
+ if(bSetProperty)
+ {
+ try
+ {
+ xSet->setPropertyValue( sLockUpdates, Any( false ) );
+ }
+ catch(const beans::UnknownPropertyException& )
+ {}
+ }
+ UpdateLinks();
+ FinishedLoading();
+ }
+ else
+ {
+ if (tools::isEmptyFileUrl(pMedium->GetName()))
+ {
+ // The import filter would fail with empty input.
+ bOk = true;
+ }
+ else
+ {
+ bOk = ConvertFrom(*pMedium);
+ }
+ InitOwnModel_Impl();
+ }
+ }
+ }
+
+ if ( bOk )
+ {
+ if ( IsReadOnlyMedium() || IsLoadReadonly() )
+ SetReadOnlyUI();
+
+ try
+ {
+ ::ucbhelper::Content aContent( pMedium->GetName(), utl::UCBContentHelper::getDefaultCommandEnvironment(), comphelper::getProcessComponentContext() );
+ css::uno::Reference < XPropertySetInfo > xProps = aContent.getProperties();
+ if ( xProps.is() )
+ {
+ static const OUStringLiteral aAuthor( u"Author" );
+ static const OUStringLiteral aKeywords( u"Keywords" );
+ static const OUStringLiteral aSubject( u"Subject" );
+ Any aAny;
+ OUString aValue;
+ uno::Reference<document::XDocumentPropertiesSupplier> xDPS(
+ GetModel(), uno::UNO_QUERY_THROW);
+ uno::Reference<document::XDocumentProperties> xDocProps
+ = xDPS->getDocumentProperties();
+ if ( xProps->hasPropertyByName( aAuthor ) )
+ {
+ aAny = aContent.getPropertyValue( aAuthor );
+ if ( aAny >>= aValue )
+ xDocProps->setAuthor(aValue);
+ }
+ if ( xProps->hasPropertyByName( aKeywords ) )
+ {
+ aAny = aContent.getPropertyValue( aKeywords );
+ if ( aAny >>= aValue )
+ xDocProps->setKeywords(
+ ::comphelper::string::convertCommaSeparated(aValue));
+;
+ }
+ if ( xProps->hasPropertyByName( aSubject ) )
+ {
+ aAny = aContent.getPropertyValue( aSubject );
+ if ( aAny >>= aValue ) {
+ xDocProps->setSubject(aValue);
+ }
+ }
+ }
+ }
+ catch( Exception& )
+ {
+ }
+
+ // If not loaded asynchronously call FinishedLoading
+ if ( !( pImpl->nLoadedFlags & SfxLoadedFlags::MAINDOCUMENT ) &&
+ ( !pMedium->GetFilter() || pMedium->GetFilter()->UsesStorage() )
+ )
+ FinishedLoading( SfxLoadedFlags::MAINDOCUMENT );
+
+ Broadcast( SfxHint(SfxHintId::NameChanged) );
+
+ if ( SfxObjectCreateMode::EMBEDDED != eCreateMode )
+ {
+ const SfxBoolItem* pAsTempItem = SfxItemSet::GetItem<SfxBoolItem>(pMedium->GetItemSet(), SID_TEMPLATE, false);
+ const SfxBoolItem* pPreviewItem = SfxItemSet::GetItem<SfxBoolItem>(pMedium->GetItemSet(), SID_PREVIEW, false);
+ const SfxBoolItem* pHiddenItem = SfxItemSet::GetItem<SfxBoolItem>(pMedium->GetItemSet(), SID_HIDDEN, false);
+ if( bOk && !pMedium->GetOrigURL().isEmpty()
+ && !( pAsTempItem && pAsTempItem->GetValue() )
+ && !( pPreviewItem && pPreviewItem->GetValue() )
+ && !( pHiddenItem && pHiddenItem->GetValue() ) )
+ {
+ AddToRecentlyUsedList();
+ }
+ }
+
+ const SfxBoolItem* pDdeReconnectItem = SfxItemSet::GetItem<SfxBoolItem>(pMedium->GetItemSet(), SID_DDE_RECONNECT_ONLOAD, false);
+
+ bool bReconnectDde = true; // by default, we try to auto-connect DDE connections.
+ if (pDdeReconnectItem)
+ bReconnectDde = pDdeReconnectItem->GetValue();
+
+ if (bReconnectDde)
+ ReconnectDdeLinks(*this);
+ }
+
+ return bOk;
+}
+
+bool SfxObjectShell::DoLoadExternal( SfxMedium *pMed )
+{
+ pMedium = pMed;
+ return LoadExternal(*pMedium);
+}
+
+ErrCode SfxObjectShell::HandleFilter( SfxMedium* pMedium, SfxObjectShell const * pDoc )
+{
+ ErrCode nError = ERRCODE_NONE;
+ SfxItemSet* pSet = pMedium->GetItemSet();
+ const SfxStringItem* pOptions = SfxItemSet::GetItem<SfxStringItem>(pSet, SID_FILE_FILTEROPTIONS, false);
+ const SfxUnoAnyItem* pData = SfxItemSet::GetItem<SfxUnoAnyItem>(pSet, SID_FILTER_DATA, false);
+ const bool bTiledRendering = comphelper::LibreOfficeKit::isActive();
+ if ( !pData && (bTiledRendering || !pOptions) )
+ {
+ css::uno::Reference< XMultiServiceFactory > xServiceManager = ::comphelper::getProcessServiceFactory();
+ css::uno::Reference< XNameAccess > xFilterCFG;
+ if( xServiceManager.is() )
+ {
+ xFilterCFG.set( xServiceManager->createInstance("com.sun.star.document.FilterFactory"),
+ UNO_QUERY );
+ }
+
+ if( xFilterCFG.is() )
+ {
+ try {
+ bool bAbort = false;
+ std::shared_ptr<const SfxFilter> pFilter = pMedium->GetFilter();
+ Sequence < PropertyValue > aProps;
+ Any aAny = xFilterCFG->getByName( pFilter->GetName() );
+ if ( aAny >>= aProps )
+ {
+ auto pProp = std::find_if(std::cbegin(aProps), std::cend(aProps),
+ [](const PropertyValue& rProp) { return rProp.Name == "UIComponent"; });
+ if (pProp != std::cend(aProps))
+ {
+ OUString aServiceName;
+ pProp->Value >>= aServiceName;
+ if( !aServiceName.isEmpty() )
+ {
+ css::uno::Reference< XInteractionHandler > rHandler = pMedium->GetInteractionHandler();
+ if( rHandler.is() )
+ {
+ // we need some properties in the media descriptor, so we have to make sure that they are in
+ Any aStreamAny;
+ aStreamAny <<= pMedium->GetInputStream();
+ if ( pSet->GetItemState( SID_INPUTSTREAM ) < SfxItemState::SET )
+ pSet->Put( SfxUnoAnyItem( SID_INPUTSTREAM, aStreamAny ) );
+ if ( pSet->GetItemState( SID_FILE_NAME ) < SfxItemState::SET )
+ pSet->Put( SfxStringItem( SID_FILE_NAME, pMedium->GetName() ) );
+ if ( pSet->GetItemState( SID_FILTER_NAME ) < SfxItemState::SET )
+ pSet->Put( SfxStringItem( SID_FILTER_NAME, pFilter->GetName() ) );
+
+ Sequence< PropertyValue > rProperties;
+ TransformItems( SID_OPENDOC, *pSet, rProperties );
+ rtl::Reference<RequestFilterOptions> pFORequest = new RequestFilterOptions( pDoc->GetModel(), rProperties );
+
+ rHandler->handle( pFORequest );
+
+ if ( !pFORequest->isAbort() )
+ {
+ SfxAllItemSet aNewParams( pDoc->GetPool() );
+ TransformParameters( SID_OPENDOC,
+ pFORequest->getFilterOptions(),
+ aNewParams );
+
+ const SfxStringItem* pFilterOptions = aNewParams.GetItem<SfxStringItem>(SID_FILE_FILTEROPTIONS, false);
+ if ( pFilterOptions )
+ pSet->Put( *pFilterOptions );
+
+ const SfxUnoAnyItem* pFilterData = aNewParams.GetItem<SfxUnoAnyItem>(SID_FILTER_DATA, false);
+ if ( pFilterData )
+ pSet->Put( *pFilterData );
+ }
+ else
+ bAbort = true;
+ }
+ }
+ }
+ }
+
+ if( bAbort )
+ {
+ // filter options were not entered
+ nError = ERRCODE_ABORT;
+ }
+ }
+ catch( NoSuchElementException& )
+ {
+ // the filter name is unknown
+ nError = ERRCODE_IO_INVALIDPARAMETER;
+ }
+ catch( Exception& )
+ {
+ nError = ERRCODE_ABORT;
+ }
+ }
+ }
+
+ return nError;
+}
+
+
+bool SfxObjectShell::IsOwnStorageFormat(const SfxMedium &rMedium)
+{
+ return !rMedium.GetFilter() || // Embedded
+ ( rMedium.GetFilter()->IsOwnFormat() &&
+ rMedium.GetFilter()->UsesStorage() &&
+ rMedium.GetFilter()->GetVersion() >= SOFFICE_FILEFORMAT_60 );
+}
+
+
+bool SfxObjectShell::IsPackageStorageFormat_Impl(const SfxMedium &rMedium)
+{
+ return !rMedium.GetFilter() || // Embedded
+ ( rMedium.GetFilter()->UsesStorage() &&
+ rMedium.GetFilter()->GetVersion() >= SOFFICE_FILEFORMAT_60 );
+}
+
+
+bool SfxObjectShell::DoSave()
+// DoSave is only invoked for OLE. Save your own documents in the SFX through
+// DoSave_Impl order to allow for the creation of backups.
+// Save in your own format again.
+{
+ bool bOk = false ;
+ {
+ ModifyBlocker_Impl aBlock( this );
+
+ pImpl->bIsSaving = true;
+
+ if (IsOwnStorageFormat(*GetMedium()))
+ {
+ SvtSaveOptions::ODFSaneDefaultVersion nDefVersion = SvtSaveOptions::ODFSVER_013;
+ if (!utl::ConfigManager::IsFuzzing())
+ {
+ nDefVersion = GetODFSaneDefaultVersion();
+ }
+ uno::Reference<beans::XPropertySet> const xProps(GetMedium()->GetStorage(), uno::UNO_QUERY);
+ assert(xProps.is());
+ if (nDefVersion >= SvtSaveOptions::ODFSVER_012) // property exists only since ODF 1.2
+ {
+ try // tdf#134582 set Version on embedded objects as they
+ { // could have been loaded with a different/old version
+ uno::Reference<frame::XModule> const xModule(GetModel(), uno::UNO_QUERY);
+ bool const isBaseForm(xModule.is() &&
+ xModule->getIdentifier() == "com.sun.star.sdb.FormDesign");
+ SAL_INFO_IF(isBaseForm, "sfx.doc", "tdf#138209 force form export to ODF 1.2");
+ if (!isBaseForm && SvtSaveOptions::ODFSVER_013 <= nDefVersion)
+ {
+ xProps->setPropertyValue("Version", uno::Any(OUString(ODFVER_013_TEXT)));
+ }
+ else
+ {
+ xProps->setPropertyValue("Version", uno::Any(OUString(ODFVER_012_TEXT)));
+ }
+ }
+ catch (uno::Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("sfx.doc", "SfxObjectShell::DoSave");
+ }
+ }
+ }
+
+ uno::Sequence< beans::NamedValue > aEncryptionData;
+ if ( IsPackageStorageFormat_Impl( *GetMedium() ) )
+ {
+ if ( GetEncryptionData_Impl( GetMedium()->GetItemSet(), aEncryptionData ) )
+ {
+ try
+ {
+ //TODO/MBA: GetOutputStorage?! Special mode, because it's "Save"?!
+ ::comphelper::OStorageHelper::SetCommonStorageEncryptionData( GetMedium()->GetStorage(), aEncryptionData );
+ bOk = true;
+ }
+ catch( uno::Exception& )
+ {
+ SetError(ERRCODE_IO_GENERAL);
+ }
+
+ DBG_ASSERT( bOk, "The root storage must allow to set common password!\n" );
+ }
+ else
+ bOk = true;
+#if HAVE_FEATURE_SCRIPTING
+ if ( HasBasic() )
+ {
+ try
+ {
+ // The basic and dialogs related contents are still not able to proceed with save operation ( saveTo only )
+ // so since the document storage is locked a workaround has to be used
+
+ uno::Reference< embed::XStorage > xTmpStorage = ::comphelper::OStorageHelper::GetTemporaryStorage();
+ DBG_ASSERT( xTmpStorage.is(), "If a storage can not be created an exception must be thrown!\n" );
+ if ( !xTmpStorage.is() )
+ throw uno::RuntimeException();
+
+ static const OUStringLiteral aBasicStorageName( u"Basic" );
+ static const OUStringLiteral aDialogsStorageName( u"Dialogs" );
+ if ( GetMedium()->GetStorage()->hasByName( aBasicStorageName ) )
+ GetMedium()->GetStorage()->copyElementTo( aBasicStorageName, xTmpStorage, aBasicStorageName );
+ if ( GetMedium()->GetStorage()->hasByName( aDialogsStorageName ) )
+ GetMedium()->GetStorage()->copyElementTo( aDialogsStorageName, xTmpStorage, aDialogsStorageName );
+
+ GetBasicManager();
+
+ // disconnect from the current storage
+ pImpl->aBasicManager.setStorage( xTmpStorage );
+
+ // store to the current storage
+ pImpl->aBasicManager.storeLibrariesToStorage( GetMedium()->GetStorage() );
+
+ // connect to the current storage back
+ pImpl->aBasicManager.setStorage( GetMedium()->GetStorage() );
+ }
+ catch( uno::Exception& )
+ {
+ SetError(ERRCODE_IO_GENERAL);
+ bOk = false;
+ }
+ }
+#endif
+ }
+
+ if (bOk)
+ bOk = Save();
+
+ if (bOk)
+ bOk = pMedium->Commit();
+ }
+
+ return bOk;
+}
+
+namespace
+{
+class LockUIGuard
+{
+public:
+ LockUIGuard(SfxObjectShell const* pDoc)
+ : m_pDoc(pDoc)
+ {
+ Lock_Impl();
+ }
+ ~LockUIGuard() { Unlock(); }
+
+ void Unlock()
+ {
+ if (m_bUnlock)
+ Lock_Impl();
+ }
+
+private:
+ void Lock_Impl()
+ {
+ SfxViewFrame* pFrame = SfxViewFrame::GetFirst(m_pDoc);
+ while (pFrame)
+ {
+ pFrame->GetDispatcher()->Lock(!m_bUnlock);
+ pFrame->Enable(m_bUnlock);
+ pFrame = SfxViewFrame::GetNext(*pFrame, m_pDoc);
+ }
+ m_bUnlock = !m_bUnlock;
+ }
+ SfxObjectShell const* m_pDoc;
+ bool m_bUnlock = false;
+};
+}
+
+static OUString lcl_strip_template(const OUString &aString)
+{
+ static constexpr OUStringLiteral sPostfix(u"_template");
+ OUString sRes(aString);
+ if (sRes.endsWith(sPostfix))
+ sRes = sRes.copy(0, sRes.getLength() - sPostfix.getLength());
+ return sRes;
+}
+
+bool SfxObjectShell::SaveTo_Impl
+(
+ SfxMedium &rMedium, // Medium, in which it will be stored
+ const SfxItemSet* pSet
+)
+
+/* [Description]
+
+ Writes the current contents to the medium rMedium. If the target medium is
+ no storage, then saving to a temporary storage, or directly if the medium
+ is transacted, if we ourselves have opened it, and if we are a server
+ either the container a transacted storage provides or created a
+ temporary storage by one self.
+*/
+
+{
+ SAL_INFO( "sfx.doc", "saving \"" << rMedium.GetName() << "\"" );
+
+ UpdateDocInfoForSave();
+
+ ModifyBlocker_Impl aMod(this);
+ // tdf#41063, tdf#135244: prevent jumping to cursor at any temporary modification
+ auto aViewGuard(LockAllViews());
+
+ std::shared_ptr<const SfxFilter> pFilter = rMedium.GetFilter();
+ if ( !pFilter )
+ {
+ // if no filter was set, use the default filter
+ // this should be changed in the feature, it should be an error!
+ SAL_WARN( "sfx.doc","No filter set!");
+ pFilter = GetFactory().GetFilterContainer()->GetAnyFilter( SfxFilterFlags::IMPORT | SfxFilterFlags::EXPORT );
+ rMedium.SetFilter(pFilter);
+ }
+
+ bool bStorageBasedSource = IsPackageStorageFormat_Impl( *pMedium );
+ bool bStorageBasedTarget = IsPackageStorageFormat_Impl( rMedium );
+ bool bOwnSource = IsOwnStorageFormat( *pMedium );
+ bool bOwnTarget = IsOwnStorageFormat( rMedium );
+
+ // Examine target format to determine whether to query if any password
+ // protected libraries exceed the size we can handler
+ if ( bOwnTarget && !QuerySaveSizeExceededModules_Impl( rMedium.GetInteractionHandler() ) )
+ {
+ SetError(ERRCODE_IO_ABORT);
+ return false;
+ }
+
+ bool bNeedsDisconnectionOnFail = false;
+
+ bool bStoreToSameLocation = false;
+
+ // the detection whether the script is changed should be done before saving
+ bool bTryToPreserveScriptSignature = false;
+ // no way to detect whether a filter is oasis format, have to wait for saving process
+ bool bNoPreserveForOasis = false;
+ if ( bOwnSource && bOwnTarget
+ && ( pImpl->nScriptingSignatureState == SignatureState::OK
+ || pImpl->nScriptingSignatureState == SignatureState::NOTVALIDATED
+ || pImpl->nScriptingSignatureState == SignatureState::INVALID ) )
+ {
+ // the checking of the library modified state iterates over the libraries, should be done only when required
+ // currently the check is commented out since it is broken, we have to check the signature every time we save
+ // TODO/LATER: let isAnyContainerModified() work!
+ bTryToPreserveScriptSignature = true; // !pImpl->pBasicManager->isAnyContainerModified();
+ if ( bTryToPreserveScriptSignature )
+ {
+ // check that the storage format stays the same
+ SvtSaveOptions::ODFSaneDefaultVersion nVersion = GetODFSaneDefaultVersion();
+
+ OUString aODFVersion;
+ try
+ {
+ uno::Reference < beans::XPropertySet > xPropSet( GetStorage(), uno::UNO_QUERY_THROW );
+ xPropSet->getPropertyValue("Version") >>= aODFVersion;
+ }
+ catch( uno::Exception& )
+ {}
+
+ // preserve only if the same filter has been used
+ // for templates, strip the _template from the filter name for comparison
+ const OUString aMediumFilter = lcl_strip_template(pMedium->GetFilter()->GetFilterName());
+ bTryToPreserveScriptSignature = pMedium->GetFilter() && pFilter && aMediumFilter == lcl_strip_template(pFilter->GetFilterName());
+
+ // signatures were specified in ODF 1.2 but were used since much longer.
+ // LO will still correctly validate an old style signature on an ODF 1.2
+ // document, but technically this is not correct, so this prevents old
+ // signatures to be copied over to a version 1.2 document
+ bNoPreserveForOasis = (
+ (0 <= aODFVersion.compareTo(ODFVER_012_TEXT) && nVersion < SvtSaveOptions::ODFSVER_012) ||
+ (aODFVersion.isEmpty() && nVersion >= SvtSaveOptions::ODFSVER_012)
+ );
+ }
+ }
+
+ // use UCB for case sensitive/insensitive file name comparison
+ if ( !pMedium->GetName().equalsIgnoreAsciiCase("private:stream")
+ && !rMedium.GetName().equalsIgnoreAsciiCase("private:stream")
+ && ::utl::UCBContentHelper::EqualURLs( pMedium->GetName(), rMedium.GetName() ) )
+ {
+ // Do not unlock the file during saving.
+ // need to modify this for WebDAV if this method is called outside of
+ // the process of saving a file
+ pMedium->DisableUnlockWebDAV();
+ bStoreToSameLocation = true;
+
+ if ( pMedium->DocNeedsFileDateCheck() )
+ rMedium.CheckFileDate( pMedium->GetInitFileDate( false ) );
+
+ // before we overwrite the original file, we will make a backup if there is a demand for that
+ // if the backup is not created here it will be created internally and will be removed in case of successful saving
+ const bool bDoBackup = officecfg::Office::Common::Save::Document::CreateBackup::get();
+ if ( bDoBackup )
+ {
+ rMedium.DoBackup_Impl();
+ if ( rMedium.GetError() )
+ {
+ SetError(rMedium.GetErrorCode());
+ rMedium.ResetError();
+ }
+ }
+
+ if ( bStorageBasedSource && bStorageBasedTarget )
+ {
+ // The active storage must be switched. The simple saving is not enough.
+ // The problem is that the target medium contains target MediaDescriptor.
+
+ // In future the switch of the persistence could be done on stream level:
+ // a new wrapper service will be implemented that allows to exchange
+ // persistence on the fly. So the real persistence will be set
+ // to that stream only after successful commit of the storage.
+ // TODO/LATER:
+ // create wrapper stream based on the URL
+ // create a new storage based on this stream
+ // store to this new storage
+ // commit the new storage
+ // call saveCompleted based with this new storage ( get rid of old storage and "frees" URL )
+ // commit the wrapper stream ( the stream will connect the URL only on commit, after that it will hold it )
+ // if the last step is failed the stream should stay to be transacted and should be committed on any flush
+ // so we can forget the stream in any way and the next storage commit will flush it
+
+ bNeedsDisconnectionOnFail = DisconnectStorage_Impl(
+ *pMedium, rMedium );
+ if ( bNeedsDisconnectionOnFail
+ || ConnectTmpStorage_Impl( pMedium->GetStorage(), pMedium ) )
+ {
+ pMedium->CloseAndRelease();
+
+ // TODO/LATER: for now the medium must be closed since it can already contain streams from old medium
+ // in future those streams should not be copied in case a valid target url is provided,
+ // if the url is not provided ( means the document is based on a stream ) this code is not
+ // reachable.
+ rMedium.CloseAndRelease();
+ rMedium.SetHasEmbeddedObjects(GetEmbeddedObjectContainer().HasEmbeddedObjects());
+ rMedium.GetOutputStorage();
+ rMedium.SetHasEmbeddedObjects(false);
+ }
+ }
+ else if ( !bStorageBasedSource && !bStorageBasedTarget )
+ {
+ // the source and the target formats are alien
+ // just disconnect the stream from the source format
+ // so that the target medium can use it
+
+ pMedium->CloseAndRelease();
+ rMedium.CloseAndRelease();
+ rMedium.CreateTempFileNoCopy();
+ rMedium.GetOutStream();
+ }
+ else if ( !bStorageBasedSource && bStorageBasedTarget )
+ {
+ // the source format is an alien one but the target
+ // format is an own one so just disconnect the source
+ // medium
+
+ pMedium->CloseAndRelease();
+ rMedium.CloseAndRelease();
+ rMedium.GetOutputStorage();
+ }
+ else // means if ( bStorageBasedSource && !bStorageBasedTarget )
+ {
+ // the source format is an own one but the target is
+ // an alien format, just connect the source to temporary
+ // storage
+
+ bNeedsDisconnectionOnFail = DisconnectStorage_Impl(
+ *pMedium, rMedium );
+ if ( bNeedsDisconnectionOnFail
+ || ConnectTmpStorage_Impl( pMedium->GetStorage(), pMedium ) )
+ {
+ pMedium->CloseAndRelease();
+ rMedium.CloseAndRelease();
+ rMedium.CreateTempFileNoCopy();
+ rMedium.GetOutStream();
+ }
+ }
+ pMedium->DisableUnlockWebDAV(false);
+ }
+ else
+ {
+ // This is SaveAs or export action, prepare the target medium
+ // the alien filters still might write directly to the file, that is of course a bug,
+ // but for now the framework has to be ready for it
+ // TODO/LATER: let the medium be prepared for alien formats as well
+
+ rMedium.CloseAndRelease();
+ if ( bStorageBasedTarget )
+ {
+ rMedium.SetHasEmbeddedObjects(GetEmbeddedObjectContainer().HasEmbeddedObjects());
+ rMedium.GetOutputStorage();
+ rMedium.SetHasEmbeddedObjects(false);
+ }
+ }
+
+ // TODO/LATER: error handling
+ if( rMedium.GetErrorCode() || pMedium->GetErrorCode() || GetErrorCode() )
+ {
+ SAL_WARN("sfx.doc", "SfxObjectShell::SaveTo_Impl: very early error return");
+ return false;
+ }
+
+ rMedium.LockOrigFileOnDemand( false, false );
+
+ if ( bStorageBasedTarget )
+ {
+ if ( rMedium.GetErrorCode() )
+ return false;
+
+ // If the filter is a "cross export" filter ( f.e. a filter for exporting an impress document from
+ // a draw document ), the ClassId of the destination storage is different from the ClassId of this
+ // document. It can be retrieved from the default filter for the desired target format
+ SotClipboardFormatId nFormat = rMedium.GetFilter()->GetFormat();
+ SfxFilterMatcher& rMatcher = SfxGetpApp()->GetFilterMatcher();
+ std::shared_ptr<const SfxFilter> pFilt = rMatcher.GetFilter4ClipBoardId( nFormat );
+ if ( pFilt )
+ {
+ if ( pFilt->GetServiceName() != rMedium.GetFilter()->GetServiceName() )
+ {
+ datatransfer::DataFlavor aDataFlavor;
+ SotExchange::GetFormatDataFlavor( nFormat, aDataFlavor );
+
+ try
+ {
+ uno::Reference< beans::XPropertySet > xProps( rMedium.GetStorage(), uno::UNO_QUERY_THROW );
+ xProps->setPropertyValue("MediaType",
+ uno::Any( aDataFlavor.MimeType ) );
+ }
+ catch( uno::Exception& )
+ {
+ }
+ }
+ }
+ }
+
+ // TODO/LATER: error handling
+ if( rMedium.GetErrorCode() || pMedium->GetErrorCode() || GetErrorCode() )
+ return false;
+
+ bool bOldStat = pImpl->bForbidReload;
+ pImpl->bForbidReload = true;
+
+ // lock user interface while saving the document
+ LockUIGuard aLockUIGuard(this);
+
+ bool bCopyTo = false;
+ SfxItemSet *pMedSet = rMedium.GetItemSet();
+ if( pMedSet )
+ {
+ const SfxBoolItem* pSaveToItem = SfxItemSet::GetItem<SfxBoolItem>(pMedSet, SID_SAVETO, false);
+ bCopyTo = GetCreateMode() == SfxObjectCreateMode::EMBEDDED ||
+ (pSaveToItem && pSaveToItem->GetValue());
+ }
+
+ bool bOk = false;
+ // TODO/LATER: get rid of bOk
+ if (bOwnTarget && pFilter && !(pFilter->GetFilterFlags() & SfxFilterFlags::STARONEFILTER))
+ {
+ uno::Reference< embed::XStorage > xMedStorage = rMedium.GetStorage();
+ if ( !xMedStorage.is() )
+ {
+ // no saving without storage
+ pImpl->bForbidReload = bOldStat;
+ return false;
+ }
+
+ // transfer password from the parameters to the storage
+ uno::Sequence< beans::NamedValue > aEncryptionData;
+ bool bPasswdProvided = false;
+ if ( GetEncryptionData_Impl( rMedium.GetItemSet(), aEncryptionData ) )
+ {
+ bPasswdProvided = true;
+ try {
+ ::comphelper::OStorageHelper::SetCommonStorageEncryptionData( xMedStorage, aEncryptionData );
+ bOk = true;
+ }
+ catch( uno::Exception& )
+ {
+ SAL_WARN( "sfx.doc", "Setting of common encryption key failed!" );
+ SetError(ERRCODE_IO_GENERAL);
+ }
+ }
+ else
+ bOk = true;
+
+ pFilter = rMedium.GetFilter();
+
+ const SfxStringItem *pVersionItem = !rMedium.IsInCheckIn()? SfxItemSet::GetItem<SfxStringItem>(pSet, SID_DOCINFO_COMMENTS, false): nullptr;
+ OUString aTmpVersionURL;
+
+ if ( bOk )
+ {
+ bOk = false;
+ // currently the case that the storage is the same should be impossible
+ if ( xMedStorage == GetStorage() )
+ {
+ OSL_ENSURE( !pVersionItem, "This scenario is impossible currently!" );
+ // usual save procedure
+ bOk = Save();
+ }
+ else
+ {
+ // save to target
+ bOk = SaveAsOwnFormat( rMedium );
+ if ( bOk && pVersionItem )
+ {
+ aTmpVersionURL = CreateTempCopyOfStorage_Impl( xMedStorage );
+ bOk = !aTmpVersionURL.isEmpty();
+ }
+ }
+ }
+
+ //fdo#61320: only store thumbnail image if the corresponding option is enabled in the configuration
+ if ( bOk && officecfg::Office::Common::Save::Document::GenerateThumbnail::get()
+ && GetCreateMode() != SfxObjectCreateMode::EMBEDDED && !bPasswdProvided && IsUseThumbnailSave() )
+ {
+ // store the thumbnail representation image
+ // the thumbnail is not stored in case of encrypted document
+ if ( !GenerateAndStoreThumbnail( bPasswdProvided, xMedStorage ) )
+ {
+ // TODO: error handling
+ SAL_WARN( "sfx.doc", "Couldn't store thumbnail representation!" );
+ }
+ }
+
+ if ( bOk )
+ {
+ if ( pImpl->bIsSaving || pImpl->bPreserveVersions )
+ {
+ try
+ {
+ const Sequence < util::RevisionTag > aVersions = rMedium.GetVersionList();
+ if ( aVersions.hasElements() )
+ {
+ // copy the version streams
+ static const OUStringLiteral aVersionsName( u"Versions" );
+ uno::Reference< embed::XStorage > xNewVerStor = xMedStorage->openStorageElement(
+ aVersionsName,
+ embed::ElementModes::READWRITE );
+ uno::Reference< embed::XStorage > xOldVerStor = GetStorage()->openStorageElement(
+ aVersionsName,
+ embed::ElementModes::READ );
+ if ( !xNewVerStor.is() || !xOldVerStor.is() )
+ throw uno::RuntimeException();
+
+ for ( const auto& rVersion : aVersions )
+ {
+ if ( xOldVerStor->hasByName( rVersion.Identifier ) )
+ xOldVerStor->copyElementTo( rVersion.Identifier, xNewVerStor, rVersion.Identifier );
+ }
+
+ uno::Reference< embed::XTransactedObject > xTransact( xNewVerStor, uno::UNO_QUERY );
+ if ( xTransact.is() )
+ xTransact->commit();
+ }
+ }
+ catch( uno::Exception& )
+ {
+ SAL_WARN( "sfx.doc", "Couldn't copy versions!" );
+ bOk = false;
+ // TODO/LATER: a specific error could be set
+ }
+ }
+
+ if ( bOk && pVersionItem && !rMedium.IsInCheckIn() )
+ {
+ // store a version also
+ const SfxStringItem *pAuthorItem = SfxItemSet::GetItem<SfxStringItem>(pSet, SID_DOCINFO_AUTHOR, false);
+
+ // version comment
+ util::RevisionTag aInfo;
+ aInfo.Comment = pVersionItem->GetValue();
+
+ // version author
+ if ( pAuthorItem )
+ aInfo.Author = pAuthorItem->GetValue();
+ else
+ // if not transferred as a parameter, get it from user settings
+ aInfo.Author = SvtUserOptions().GetFullName();
+
+ DateTime aTime( DateTime::SYSTEM );
+ aInfo.TimeStamp.Day = aTime.GetDay();
+ aInfo.TimeStamp.Month = aTime.GetMonth();
+ aInfo.TimeStamp.Year = aTime.GetYear();
+ aInfo.TimeStamp.Hours = aTime.GetHour();
+ aInfo.TimeStamp.Minutes = aTime.GetMin();
+ aInfo.TimeStamp.Seconds = aTime.GetSec();
+
+ // add new version information into the versionlist and save the versionlist
+ // the version list must have been transferred from the "old" medium before
+ rMedium.AddVersion_Impl(aInfo);
+ rMedium.SaveVersionList_Impl();
+ bOk = PutURLContentsToVersionStream_Impl(aTmpVersionURL, xMedStorage,
+ aInfo.Identifier);
+ }
+ else if ( bOk && ( pImpl->bIsSaving || pImpl->bPreserveVersions ) )
+ {
+ rMedium.SaveVersionList_Impl();
+ }
+ }
+
+ if ( !aTmpVersionURL.isEmpty() )
+ ::utl::UCBContentHelper::Kill( aTmpVersionURL );
+ }
+ else
+ {
+ // it's a "SaveAs" in an alien format
+ if ( rMedium.GetFilter() && ( rMedium.GetFilter()->GetFilterFlags() & SfxFilterFlags::STARONEFILTER ) )
+ bOk = ExportTo( rMedium );
+ else
+ bOk = ConvertTo( rMedium );
+
+ // after saving the document, the temporary object storage must be updated
+ // if the old object storage was not a temporary one, it will be updated also, because it will be used
+ // as a source for copying the objects into the new temporary storage that will be created below
+ // updating means: all child objects must be stored into it
+ // ( same as on loading, where these objects are copied to the temporary storage )
+ // but don't commit these changes, because in the case when the old object storage is not a temporary one,
+ // all changes will be written into the original file !
+
+ if( bOk && !bCopyTo )
+ // we also don't touch any graphical replacements here
+ SaveChildren( true );
+ }
+
+ if ( bOk )
+ {
+ // if ODF version of oasis format changes on saving the signature should not be preserved
+ if ( bTryToPreserveScriptSignature && bNoPreserveForOasis )
+ bTryToPreserveScriptSignature = ( SotStorage::GetVersion( rMedium.GetStorage() ) == SOFFICE_FILEFORMAT_60 );
+
+ uno::Reference< security::XDocumentDigitalSignatures > xDDSigns;
+ if (bTryToPreserveScriptSignature)
+ {
+ // if the scripting code was not changed and it is signed the signature should be preserved
+ // unfortunately at this point we have only information whether the basic code has changed or not
+ // so the only way is to check the signature if the basic was not changed
+ try
+ {
+ // get the ODF version of the new medium
+ OUString aVersion;
+ try
+ {
+ uno::Reference < beans::XPropertySet > xPropSet( rMedium.GetStorage(), uno::UNO_QUERY_THROW );
+ xPropSet->getPropertyValue("Version") >>= aVersion;
+ }
+ catch( uno::Exception& )
+ {
+ }
+
+ xDDSigns = security::DocumentDigitalSignatures::createWithVersion(comphelper::getProcessComponentContext(), aVersion);
+
+ const OUString aScriptSignName = xDDSigns->getScriptingContentSignatureDefaultStreamName();
+
+ if ( !aScriptSignName.isEmpty() )
+ {
+ // target medium is still not committed, it should not be closed
+ // commit the package storage and close it, but leave the streams open
+ rMedium.StorageCommit_Impl();
+ rMedium.CloseStorage();
+
+ uno::Reference< embed::XStorage > xReadOrig = pMedium->GetZipStorageToSign_Impl();
+ if ( !xReadOrig.is() )
+ throw uno::RuntimeException();
+ uno::Reference< embed::XStorage > xMetaInf = xReadOrig->openStorageElement(
+ "META-INF",
+ embed::ElementModes::READ );
+
+ uno::Reference< embed::XStorage > xTarget = rMedium.GetZipStorageToSign_Impl( false );
+ if ( !xTarget.is() )
+ throw uno::RuntimeException();
+ uno::Reference< embed::XStorage > xTargetMetaInf = xTarget->openStorageElement(
+ "META-INF",
+ embed::ElementModes::READWRITE );
+
+ if ( xMetaInf.is() && xTargetMetaInf.is() )
+ {
+ xMetaInf->copyElementTo( aScriptSignName, xTargetMetaInf, aScriptSignName );
+
+ uno::Reference< embed::XTransactedObject > xTransact( xTargetMetaInf, uno::UNO_QUERY );
+ if ( xTransact.is() )
+ xTransact->commit();
+
+ xTargetMetaInf->dispose();
+
+ // now check the copied signature
+ uno::Sequence< security::DocumentSignatureInformation > aInfos =
+ xDDSigns->verifyScriptingContentSignatures( xTarget,
+ uno::Reference< io::XInputStream >() );
+ SignatureState nState = DocumentSignatures::getSignatureState(aInfos);
+ if ( nState == SignatureState::OK || nState == SignatureState::NOTVALIDATED
+ || nState == SignatureState::PARTIAL_OK)
+ {
+ rMedium.SetCachedSignatureState_Impl( nState );
+
+ // commit the ZipStorage from target medium
+ xTransact.set( xTarget, uno::UNO_QUERY );
+ if ( xTransact.is() )
+ xTransact->commit();
+ }
+ else
+ {
+ // it should not happen, the copies signature is invalid!
+ // throw the changes away
+ SAL_WARN( "sfx.doc", "An invalid signature was copied!" );
+ }
+ }
+ }
+ }
+ catch( uno::Exception& )
+ {
+ }
+
+ rMedium.CloseZipStorage_Impl();
+ }
+
+ const OUString sName( rMedium.GetName( ) );
+ bOk = rMedium.Commit();
+ const OUString sNewName( rMedium.GetName( ) );
+
+ if ( sName != sNewName )
+ GetMedium( )->SwitchDocumentToFile( sNewName );
+
+ if ( bOk )
+ {
+ // if the target medium is an alien format and the "old" medium was an own format and the "old" medium
+ // has a name, the object storage must be exchanged, because now we need a new temporary storage
+ // as object storage
+ if ( !bCopyTo && bStorageBasedSource && !bStorageBasedTarget )
+ {
+ if ( bStoreToSameLocation )
+ {
+ // if the old medium already disconnected from document storage, the storage still must
+ // be switched if backup file is used
+ if ( bNeedsDisconnectionOnFail )
+ ConnectTmpStorage_Impl( pImpl->m_xDocStorage, nullptr );
+ }
+ else if (!pMedium->GetName().isEmpty()
+ || ( pMedium->HasStorage_Impl() && pMedium->WillDisposeStorageOnClose_Impl() ) )
+ {
+ OSL_ENSURE(!pMedium->GetName().isEmpty(), "Fallback is used, the medium without name should not dispose the storage!");
+ // copy storage of old medium to new temporary storage and take this over
+ if( !ConnectTmpStorage_Impl( pMedium->GetStorage(), pMedium ) )
+ {
+ SAL_WARN( "sfx.doc", "Process after storing has failed." );
+ bOk = false;
+ }
+ }
+ }
+ }
+ else
+ {
+ SAL_WARN( "sfx.doc", "Storing has failed." );
+
+ // in case the document storage was connected to backup temporarily it must be disconnected now
+ if ( bNeedsDisconnectionOnFail )
+ ConnectTmpStorage_Impl( pImpl->m_xDocStorage, nullptr );
+ }
+ }
+
+ // unlock user interface
+ aLockUIGuard.Unlock();
+ pImpl->bForbidReload = bOldStat;
+
+ if ( bOk )
+ {
+ try
+ {
+ ::ucbhelper::Content aContent( rMedium.GetName(), utl::UCBContentHelper::getDefaultCommandEnvironment(), comphelper::getProcessComponentContext() );
+ css::uno::Reference < XPropertySetInfo > xProps = aContent.getProperties();
+ if ( xProps.is() )
+ {
+ static const OUStringLiteral aAuthor( u"Author" );
+ static const OUStringLiteral aKeywords( u"Keywords" );
+ static const OUStringLiteral aSubject( u"Subject" );
+
+ uno::Reference<document::XDocumentPropertiesSupplier> xDPS(
+ GetModel(), uno::UNO_QUERY_THROW);
+ uno::Reference<document::XDocumentProperties> xDocProps
+ = xDPS->getDocumentProperties();
+
+ if ( xProps->hasPropertyByName( aAuthor ) )
+ {
+ aContent.setPropertyValue( aAuthor, Any(xDocProps->getAuthor()) );
+ }
+ if ( xProps->hasPropertyByName( aKeywords ) )
+ {
+ Any aAny;
+ aAny <<= ::comphelper::string::convertCommaSeparated(
+ xDocProps->getKeywords());
+ aContent.setPropertyValue( aKeywords, aAny );
+ }
+ if ( xProps->hasPropertyByName( aSubject ) )
+ {
+ aContent.setPropertyValue( aSubject, Any(xDocProps->getSubject()) );
+ }
+ }
+ }
+ catch( Exception& )
+ {
+ }
+ }
+
+ return bOk;
+}
+
+bool SfxObjectShell::DisconnectStorage_Impl( SfxMedium& rSrcMedium, SfxMedium& rTargetMedium )
+{
+ // this method disconnects the storage from source medium, and attaches it to the backup created by the target medium
+
+ uno::Reference< embed::XStorage > xStorage = rSrcMedium.GetStorage();
+
+ bool bResult = false;
+ if ( xStorage == pImpl->m_xDocStorage )
+ {
+ try
+ {
+ uno::Reference< embed::XOptimizedStorage > xOptStorage( xStorage, uno::UNO_QUERY_THROW );
+ const OUString aBackupURL = rTargetMedium.GetBackup_Impl();
+ if ( aBackupURL.isEmpty() )
+ {
+ // the backup could not be created, try to disconnect the storage and close the source SfxMedium
+ // in this case the optimization is not possible, connect storage to a temporary file
+ rTargetMedium.ResetError();
+ xOptStorage->writeAndAttachToStream( uno::Reference< io::XStream >() );
+ rSrcMedium.CanDisposeStorage_Impl( false );
+ rSrcMedium.Close();
+
+ // now try to create the backup
+ rTargetMedium.GetBackup_Impl();
+ }
+ else
+ {
+ // the following call will only compare stream sizes
+ // TODO/LATER: this is a very risky part, since if the URL contents are different from the storage
+ // contents, the storage will be broken
+ xOptStorage->attachToURL( aBackupURL, true );
+
+ // the storage is successfully attached to backup, thus it is owned by the document not by the medium
+ rSrcMedium.CanDisposeStorage_Impl( false );
+ bResult = true;
+ }
+ }
+ catch ( uno::Exception& )
+ {}
+ }
+ return bResult;
+}
+
+
+bool SfxObjectShell::ConnectTmpStorage_Impl(
+ const uno::Reference< embed::XStorage >& xStorage,
+ SfxMedium* pMediumArg )
+
+/* [Description]
+
+ If the application operates on a temporary storage, then it may not take
+ the temporary storage from the SaveCompleted. Therefore the new storage
+ is connected already here in this case and SaveCompleted then does nothing.
+*/
+
+{
+ bool bResult = false;
+
+ if ( xStorage.is() )
+ {
+ try
+ {
+ // the empty argument means that the storage will create temporary stream itself
+ uno::Reference< embed::XOptimizedStorage > xOptStorage( xStorage, uno::UNO_QUERY_THROW );
+ xOptStorage->writeAndAttachToStream( uno::Reference< io::XStream >() );
+
+ // the storage is successfully disconnected from the original sources, thus the medium must not dispose it
+ if ( pMediumArg )
+ pMediumArg->CanDisposeStorage_Impl( false );
+
+ bResult = true;
+ }
+ catch( uno::Exception& )
+ {
+ }
+
+ // if switching of the storage does not work for any reason ( nonroot storage for example ) use the old method
+ if ( !bResult ) try
+ {
+ uno::Reference< embed::XStorage > xTmpStorage = ::comphelper::OStorageHelper::GetTemporaryStorage();
+
+ DBG_ASSERT( xTmpStorage.is(), "If a storage can not be created an exception must be thrown!\n" );
+ if ( !xTmpStorage.is() )
+ throw uno::RuntimeException();
+
+ // TODO/LATER: may be it should be done in SwitchPersistence also
+ // TODO/LATER: find faster way to copy storage; perhaps sharing with backup?!
+ xStorage->copyToStorage( xTmpStorage );
+ bResult = SaveCompleted( xTmpStorage );
+
+ if ( bResult )
+ {
+ pImpl->aBasicManager.setStorage( xTmpStorage );
+
+ // Get rid of this workaround after issue i113914 is fixed
+ try
+ {
+ uno::Reference< script::XStorageBasedLibraryContainer > xBasicLibraries( pImpl->xBasicLibraries, uno::UNO_QUERY_THROW );
+ xBasicLibraries->setRootStorage( xTmpStorage );
+ }
+ catch( uno::Exception& )
+ {}
+ try
+ {
+ uno::Reference< script::XStorageBasedLibraryContainer > xDialogLibraries( pImpl->xDialogLibraries, uno::UNO_QUERY_THROW );
+ xDialogLibraries->setRootStorage( xTmpStorage );
+ }
+ catch( uno::Exception& )
+ {}
+ }
+ }
+ catch( uno::Exception& )
+ {}
+
+ if ( !bResult )
+ {
+ // TODO/LATER: may need error code setting based on exception
+ SetError(ERRCODE_IO_GENERAL);
+ }
+ }
+ else if (!GetMedium()->GetFilter()->IsOwnFormat())
+ bResult = true;
+
+ return bResult;
+}
+
+
+bool SfxObjectShell::DoSaveObjectAs( SfxMedium& rMedium, bool bCommit )
+{
+ bool bOk = false;
+
+ ModifyBlocker_Impl aBlock( this );
+
+ uno::Reference < embed::XStorage > xNewStor = rMedium.GetStorage();
+ if ( !xNewStor.is() )
+ return false;
+
+ uno::Reference < beans::XPropertySet > xPropSet( xNewStor, uno::UNO_QUERY );
+ if ( !xPropSet.is() )
+ return false;
+
+ Any a = xPropSet->getPropertyValue("MediaType");
+ OUString aMediaType;
+ if ( !(a>>=aMediaType) || aMediaType.isEmpty() )
+ {
+ SAL_WARN( "sfx.doc", "The mediatype must be set already!" );
+ SetupStorage( xNewStor, SOFFICE_FILEFORMAT_CURRENT, false );
+ }
+
+ pImpl->bIsSaving = false;
+ bOk = SaveAsOwnFormat( rMedium );
+
+ if ( bCommit )
+ {
+ try {
+ uno::Reference< embed::XTransactedObject > xTransact( xNewStor, uno::UNO_QUERY_THROW );
+ xTransact->commit();
+ }
+ catch( uno::Exception& )
+ {
+ SAL_WARN( "sfx.doc", "The storage was not committed on DoSaveAs!" );
+ }
+ }
+
+ return bOk;
+}
+
+
+// TODO/LATER: may be the call must be removed completely
+bool SfxObjectShell::DoSaveAs( SfxMedium& rMedium )
+{
+ // here only root storages are included, which are stored via temp file
+ rMedium.CreateTempFileNoCopy();
+ SetError(rMedium.GetErrorCode());
+ if ( GetError() )
+ return false;
+
+ // copy version list from "old" medium to target medium, so it can be used on saving
+ if ( pImpl->bPreserveVersions )
+ rMedium.TransferVersionList_Impl( *pMedium );
+
+ bool bRet = SaveTo_Impl( rMedium, nullptr );
+ if ( !bRet )
+ SetError(rMedium.GetErrorCode());
+ return bRet;
+}
+
+
+bool SfxObjectShell::DoSaveCompleted( SfxMedium* pNewMed, bool bRegisterRecent )
+{
+ bool bOk = true;
+ bool bMedChanged = pNewMed && pNewMed!=pMedium;
+
+ DBG_ASSERT( !pNewMed || pNewMed->GetError() == ERRCODE_NONE, "DoSaveCompleted: Medium has error!" );
+
+ // delete Medium (and Storage!) after all notifications
+ SfxMedium* pOld = pMedium;
+ if ( bMedChanged )
+ {
+ pMedium = pNewMed;
+ pMedium->CanDisposeStorage_Impl( true );
+ }
+
+ std::shared_ptr<const SfxFilter> pFilter = pMedium ? pMedium->GetFilter() : nullptr;
+ if ( pNewMed )
+ {
+ if( bMedChanged )
+ {
+ if (!pNewMed->GetName().isEmpty())
+ bHasName = true;
+ Broadcast( SfxHint(SfxHintId::NameChanged) );
+ EnableSetModified(false);
+ getDocProperties()->setGenerator(
+ ::utl::DocInfoHelper::GetGeneratorString() );
+ EnableSetModified();
+ }
+
+ uno::Reference< embed::XStorage > xStorage;
+ if ( !pFilter || IsPackageStorageFormat_Impl( *pMedium ) )
+ {
+ uno::Reference < embed::XStorage > xOld = GetStorage();
+
+ // when the package based medium is broken and has no storage or if the storage
+ // is the same as the document storage the current document storage should be preserved
+ xStorage = pMedium->GetStorage();
+ bOk = SaveCompleted( xStorage );
+ if ( bOk && xStorage.is() && xOld != xStorage
+ && (!pOld || !pOld->HasStorage_Impl() || xOld != pOld->GetStorage() ) )
+ {
+ // old own storage was not controlled by old Medium -> dispose it
+ try {
+ xOld->dispose();
+ } catch( uno::Exception& )
+ {
+ // the storage is disposed already
+ // can happen during reload scenario when the medium has
+ // disposed it during the closing
+ // will be fixed in one of the next milestones
+ }
+ }
+ }
+ else
+ {
+ if (pImpl->m_bSavingForSigning && pFilter && pFilter->GetSupportsSigning())
+ // So that pMedium->pImpl->xStream becomes a non-empty
+ // reference, and at the end we attempt locking again in
+ // SfxMedium::LockOrigFileOnDemand().
+ pMedium->GetMedium_Impl();
+
+ if( pMedium->GetOpenMode() & StreamMode::WRITE )
+ pMedium->GetInStream();
+ xStorage = GetStorage();
+ }
+
+ // TODO/LATER: may be this code will be replaced, but not sure
+ // Set storage in document library containers
+ pImpl->aBasicManager.setStorage( xStorage );
+
+ // Get rid of this workaround after issue i113914 is fixed
+ try
+ {
+ uno::Reference< script::XStorageBasedLibraryContainer > xBasicLibraries( pImpl->xBasicLibraries, uno::UNO_QUERY_THROW );
+ xBasicLibraries->setRootStorage( xStorage );
+ }
+ catch( uno::Exception& )
+ {}
+ try
+ {
+ uno::Reference< script::XStorageBasedLibraryContainer > xDialogLibraries( pImpl->xDialogLibraries, uno::UNO_QUERY_THROW );
+ xDialogLibraries->setRootStorage( xStorage );
+ }
+ catch( uno::Exception& )
+ {}
+ }
+ else
+ {
+ if( pMedium )
+ {
+ if( pFilter && !IsPackageStorageFormat_Impl( *pMedium ) && (pMedium->GetOpenMode() & StreamMode::WRITE ))
+ {
+ pMedium->ReOpen();
+ bOk = SaveCompletedChildren();
+ }
+ else
+ bOk = SaveCompleted( nullptr );
+ }
+ // either Save or ConvertTo
+ else
+ bOk = SaveCompleted( nullptr );
+ }
+
+ if ( bOk && pNewMed )
+ {
+ if( bMedChanged )
+ {
+ delete pOld;
+
+ uno::Reference< frame::XModel > xModel = GetModel();
+ if ( xModel.is() )
+ {
+ const OUString& aURL {pNewMed->GetOrigURL()};
+ uno::Sequence< beans::PropertyValue > aMediaDescr;
+ TransformItems( SID_OPENDOC, *pNewMed->GetItemSet(), aMediaDescr );
+ try
+ {
+ xModel->attachResource( aURL, aMediaDescr );
+ }
+ catch( uno::Exception& )
+ {}
+ }
+
+ const SfxBoolItem* pTemplateItem = SfxItemSet::GetItem<SfxBoolItem>(pMedium->GetItemSet(), SID_TEMPLATE, false);
+ bool bTemplate = pTemplateItem && pTemplateItem->GetValue();
+
+ // before the title regenerated the document must lose the signatures
+ pImpl->nDocumentSignatureState = SignatureState::NOSIGNATURES;
+ if (!bTemplate)
+ {
+ pImpl->nScriptingSignatureState = pNewMed->GetCachedSignatureState_Impl();
+ OSL_ENSURE( pImpl->nScriptingSignatureState != SignatureState::BROKEN, "The signature must not be broken at this place" );
+
+ // TODO/LATER: in future the medium must control own signature state, not the document
+ pNewMed->SetCachedSignatureState_Impl( SignatureState::NOSIGNATURES ); // set the default value back
+ }
+ else
+ pNewMed->SetCachedSignatureState_Impl( pImpl->nScriptingSignatureState );
+
+ // Set new title
+ if (!pNewMed->GetName().isEmpty() && SfxObjectCreateMode::EMBEDDED != eCreateMode)
+ InvalidateName();
+ SetModified(false); // reset only by set medium
+ Broadcast( SfxHint(SfxHintId::ModeChanged) );
+
+ // this is the end of the saving process, it is possible that
+ // the file was changed
+ // between medium commit and this step (attributes change and so on)
+ // so get the file date again
+ if ( pNewMed->DocNeedsFileDateCheck() )
+ pNewMed->GetInitFileDate( true );
+ }
+ }
+
+ pMedium->ClearBackup_Impl();
+ pMedium->LockOrigFileOnDemand( true, false );
+
+ if (bRegisterRecent)
+ AddToRecentlyUsedList();
+
+ return bOk;
+}
+
+void SfxObjectShell::AddToRecentlyUsedList()
+{
+ INetURLObject aUrl( pMedium->GetOrigURL() );
+
+ if ( aUrl.GetProtocol() == INetProtocol::File )
+ {
+ std::shared_ptr<const SfxFilter> pOrgFilter = pMedium->GetFilter();
+ Application::AddToRecentDocumentList( aUrl.GetURLNoPass( INetURLObject::DecodeMechanism::NONE ),
+ pOrgFilter ? pOrgFilter->GetMimeType() : OUString(),
+ pOrgFilter ? pOrgFilter->GetServiceName() : OUString() );
+ }
+}
+
+
+bool SfxObjectShell::ConvertFrom
+(
+ SfxMedium& /*rMedium*/ /* <SfxMedium>, which describes the source file
+ (for example file name, <SfxFilter>,
+ Open-Modi and so on) */
+)
+
+/* [Description]
+
+ This method is called for loading of documents over all filters which are
+ not SfxFilterFlags::OWN or for which no clipboard format has been registered
+ (thus no storage format that is used). In other words, with this method
+ it is imported.
+
+ Files which are to be opened here should be opened through 'rMedium'
+ to guarantee the right open modes. Especially if the format is retained
+ (only possible with SfxFilterFlags::SIMULATE or SfxFilterFlags::OWN) file which must
+ be opened STREAM_SHARE_DENYWRITE.
+
+ [Return value]
+
+ bool true
+ The document could be loaded.
+
+ false
+ The document could not be loaded, an error code
+ received through <SvMedium::GetError()const>
+
+ [Example]
+
+ bool DocSh::ConvertFrom( SfxMedium &rMedium )
+ {
+ SvStreamRef xStream = rMedium.GetInStream();
+ if( xStream.is() )
+ {
+ xStream->SetBufferSize(4096);
+ *xStream >> ...;
+
+ // Do not call 'rMedium.CloseInStream()'! Keep File locked!
+ return ERRCODE_NONE == rMedium.GetError();
+ }
+
+ return false;
+ }
+
+ [Cross-references]
+
+ <SfxObjectShell::ConvertTo(SfxMedium&)>
+ <SfxFilterFlags::REGISTRATION>
+*/
+{
+ return false;
+}
+
+bool SfxObjectShell::ImportFrom(SfxMedium& rMedium,
+ css::uno::Reference<css::text::XTextRange> const& xInsertPosition)
+{
+ const OUString aFilterName( rMedium.GetFilter()->GetFilterName() );
+
+ uno::Reference< lang::XMultiServiceFactory > xMan = ::comphelper::getProcessServiceFactory();
+ uno::Reference < lang::XMultiServiceFactory > xFilterFact (
+ xMan->createInstance( "com.sun.star.document.FilterFactory" ), uno::UNO_QUERY );
+
+ uno::Sequence < beans::PropertyValue > aProps;
+ uno::Reference < container::XNameAccess > xFilters ( xFilterFact, uno::UNO_QUERY );
+ if ( xFilters->hasByName( aFilterName ) )
+ {
+ xFilters->getByName( aFilterName ) >>= aProps;
+ rMedium.GetItemSet()->Put( SfxStringItem( SID_FILTER_NAME, aFilterName ) );
+ }
+
+ OUString aFilterImplName;
+ auto pProp = std::find_if(std::cbegin(aProps), std::cend(aProps),
+ [](const beans::PropertyValue& rFilterProp) { return rFilterProp.Name == "FilterService"; });
+ if (pProp != std::cend(aProps))
+ pProp->Value >>= aFilterImplName;
+
+ uno::Reference< document::XFilter > xLoader;
+ if ( !aFilterImplName.isEmpty() )
+ {
+ try
+ {
+ xLoader.set( xFilterFact->createInstanceWithArguments( aFilterName, uno::Sequence < uno::Any >() ), uno::UNO_QUERY );
+ }
+ catch(const uno::Exception&)
+ {
+ xLoader.clear();
+ }
+ }
+ if ( xLoader.is() )
+ {
+ // it happens that xLoader does not support xImporter!
+ try
+ {
+ uno::Reference< lang::XComponent > xComp( GetModel(), uno::UNO_QUERY_THROW );
+ uno::Reference< document::XImporter > xImporter( xLoader, uno::UNO_QUERY_THROW );
+ xImporter->setTargetDocument( xComp );
+
+ uno::Sequence < beans::PropertyValue > lDescriptor;
+ rMedium.GetItemSet()->Put( SfxStringItem( SID_FILE_NAME, rMedium.GetName() ) );
+ TransformItems( SID_OPENDOC, *rMedium.GetItemSet(), lDescriptor );
+
+ css::uno::Sequence < css::beans::PropertyValue > aArgs ( lDescriptor.getLength() );
+ css::beans::PropertyValue * pNewValue = aArgs.getArray();
+ const css::beans::PropertyValue * pOldValue = lDescriptor.getConstArray();
+ static const OUStringLiteral sInputStream ( u"InputStream" );
+
+ bool bHasInputStream = false;
+ bool bHasBaseURL = false;
+ sal_Int32 nEnd = lDescriptor.getLength();
+
+ for ( sal_Int32 i = 0; i < nEnd; i++ )
+ {
+ pNewValue[i] = pOldValue[i];
+ if ( pOldValue [i].Name == sInputStream )
+ bHasInputStream = true;
+ else if ( pOldValue[i].Name == "DocumentBaseURL" )
+ bHasBaseURL = true;
+ }
+
+ if ( !bHasInputStream )
+ {
+ aArgs.realloc ( ++nEnd );
+ auto pArgs = aArgs.getArray();
+ pArgs[nEnd-1].Name = sInputStream;
+ pArgs[nEnd-1].Value <<= css::uno::Reference < css::io::XInputStream > ( new utl::OSeekableInputStreamWrapper ( *rMedium.GetInStream() ) );
+ }
+
+ if ( !bHasBaseURL )
+ {
+ aArgs.realloc ( ++nEnd );
+ auto pArgs = aArgs.getArray();
+ pArgs[nEnd-1].Name = "DocumentBaseURL";
+ pArgs[nEnd-1].Value <<= rMedium.GetBaseURL();
+ }
+
+ if (xInsertPosition.is()) {
+ aArgs.realloc( nEnd += 2 );
+ auto pArgs = aArgs.getArray();
+ pArgs[nEnd-2].Name = "InsertMode";
+ pArgs[nEnd-2].Value <<= true;
+ pArgs[nEnd-1].Name = "TextInsertModeRange";
+ pArgs[nEnd-1].Value <<= xInsertPosition;
+ }
+
+ // #i119492# During loading, some OLE objects like chart will be set
+ // modified flag, so needs to reset the flag to false after loading
+ bool bRtn = xLoader->filter(aArgs);
+ const uno::Sequence < OUString > aNames = GetEmbeddedObjectContainer().GetObjectNames();
+ for ( const auto& rName : aNames )
+ {
+ uno::Reference < embed::XEmbeddedObject > xObj = GetEmbeddedObjectContainer().GetEmbeddedObject( rName );
+ OSL_ENSURE( xObj.is(), "An empty entry in the embedded objects list!" );
+ if ( xObj.is() )
+ {
+ sal_Int32 nState = xObj->getCurrentState();
+ if ( nState == embed::EmbedStates::LOADED || nState == embed::EmbedStates::RUNNING ) // means that the object is not active
+ {
+ uno::Reference< util::XModifiable > xModifiable( xObj->getComponent(), uno::UNO_QUERY );
+ if (xModifiable.is() && xModifiable->isModified())
+ {
+ uno::Reference<embed::XEmbedPersist> const xPers(xObj, uno::UNO_QUERY);
+ assert(xPers.is() && "Modified object without persistence!");
+ // store it before resetting modified!
+ xPers->storeOwn();
+ xModifiable->setModified(false);
+ }
+ }
+ }
+ }
+
+ // tdf#107690 import custom document property _MarkAsFinal as SecurityOptOpenReadonly
+ // (before this fix, LibreOffice opened read-only OOXML documents as editable,
+ // also saved and exported _MarkAsFinal=true silently, resulting unintended read-only
+ // warning info bar in MSO)
+ uno::Reference< document::XDocumentPropertiesSupplier > xPropSupplier(GetModel(), uno::UNO_QUERY_THROW);
+ uno::Reference<document::XDocumentProperties> xDocProps = xPropSupplier->getDocumentProperties() ;
+ uno::Reference<beans::XPropertyContainer> xPropertyContainer = xDocProps->getUserDefinedProperties();
+ if (xPropertyContainer.is())
+ {
+ uno::Reference<beans::XPropertySet> xPropertySet(xPropertyContainer, uno::UNO_QUERY);
+ if (xPropertySet.is())
+ {
+ uno::Reference<beans::XPropertySetInfo> xPropertySetInfo = xPropertySet->getPropertySetInfo();
+ if (xPropertySetInfo.is() && xPropertySetInfo->hasPropertyByName("_MarkAsFinal"))
+ {
+ if (xPropertySet->getPropertyValue("_MarkAsFinal").get<bool>())
+ {
+ uno::Reference< lang::XMultiServiceFactory > xFactory(GetModel(), uno::UNO_QUERY);
+ uno::Reference< beans::XPropertySet > xSettings(xFactory->createInstance("com.sun.star.document.Settings"), uno::UNO_QUERY);
+ xSettings->setPropertyValue("LoadReadonly", uno::Any(true));
+ }
+ xPropertyContainer->removeProperty("_MarkAsFinal");
+ }
+ }
+ }
+
+ return bRtn;
+ }
+ catch (const packages::zip::ZipIOException&)
+ {
+ SetError(ERRCODE_IO_BROKENPACKAGE);
+ }
+ catch (const lang::WrappedTargetRuntimeException& rWrapped)
+ {
+ io::WrongFormatException e;
+ if (rWrapped.TargetException >>= e)
+ {
+ SetError(*new StringErrorInfo(ERRCODE_SFX_FORMAT_ROWCOL,
+ e.Message, DialogMask::ButtonsOk | DialogMask::MessageError ));
+ }
+ }
+ catch (const css::io::IOException& e)
+ {
+ SetError(*new StringErrorInfo(ERRCODE_SFX_FORMAT_ROWCOL,
+ e.Message, DialogMask::ButtonsOk | DialogMask::MessageError ));
+ }
+ catch (const std::exception& e)
+ {
+ const char *msg = e.what();
+ const OUString sError(msg, strlen(msg), RTL_TEXTENCODING_ASCII_US);
+ SetError(*new StringErrorInfo(ERRCODE_SFX_DOLOADFAILED,
+ sError, DialogMask::ButtonsOk | DialogMask::MessageError));
+ }
+ catch (...)
+ {
+ std::abort(); // cannot happen
+ }
+ }
+
+ return false;
+}
+
+bool SfxObjectShell::ExportTo( SfxMedium& rMedium )
+{
+ const OUString aFilterName( rMedium.GetFilter()->GetFilterName() );
+ uno::Reference< document::XExporter > xExporter;
+
+ {
+ uno::Reference< lang::XMultiServiceFactory > xMan = ::comphelper::getProcessServiceFactory();
+ uno::Reference < lang::XMultiServiceFactory > xFilterFact (
+ xMan->createInstance( "com.sun.star.document.FilterFactory" ), uno::UNO_QUERY );
+
+ uno::Sequence < beans::PropertyValue > aProps;
+ uno::Reference < container::XNameAccess > xFilters ( xFilterFact, uno::UNO_QUERY );
+ if ( xFilters->hasByName( aFilterName ) )
+ xFilters->getByName( aFilterName ) >>= aProps;
+
+ OUString aFilterImplName;
+ auto pProp = std::find_if(std::cbegin(aProps), std::cend(aProps),
+ [](const beans::PropertyValue& rFilterProp) { return rFilterProp.Name == "FilterService"; });
+ if (pProp != std::cend(aProps))
+ pProp->Value >>= aFilterImplName;
+
+ if ( !aFilterImplName.isEmpty() )
+ {
+ try
+ {
+ xExporter.set( xFilterFact->createInstanceWithArguments( aFilterName, uno::Sequence < uno::Any >() ), uno::UNO_QUERY );
+ }
+ catch(const uno::Exception&)
+ {
+ xExporter.clear();
+ }
+ }
+ }
+
+ if ( xExporter.is() )
+ {
+ try{
+ uno::Reference< lang::XComponent > xComp( GetModel(), uno::UNO_QUERY_THROW );
+ uno::Reference< document::XFilter > xFilter( xExporter, uno::UNO_QUERY_THROW );
+ xExporter->setSourceDocument( xComp );
+
+ css::uno::Sequence < css::beans::PropertyValue > aOldArgs;
+ SfxItemSet* pItems = rMedium.GetItemSet();
+ TransformItems( SID_SAVEASDOC, *pItems, aOldArgs );
+
+ const css::beans::PropertyValue * pOldValue = aOldArgs.getConstArray();
+ css::uno::Sequence < css::beans::PropertyValue > aArgs ( aOldArgs.getLength() );
+ css::beans::PropertyValue * pNewValue = aArgs.getArray();
+
+ // put in the REAL file name, and copy all PropertyValues
+ static const OUStringLiteral sOutputStream ( u"OutputStream" );
+ static const OUStringLiteral sStream ( u"StreamForOutput" );
+ bool bHasOutputStream = false;
+ bool bHasStream = false;
+ bool bHasBaseURL = false;
+ bool bHasFilterName = false;
+ bool bIsRedactMode = false;
+ bool bIsPreview = false;
+ sal_Int32 nEnd = aOldArgs.getLength();
+
+ for ( sal_Int32 i = 0; i < nEnd; i++ )
+ {
+ pNewValue[i] = pOldValue[i];
+ if ( pOldValue[i].Name == "FileName" )
+ pNewValue[i].Value <<= rMedium.GetName();
+ else if ( pOldValue[i].Name == sOutputStream )
+ bHasOutputStream = true;
+ else if ( pOldValue[i].Name == sStream )
+ bHasStream = true;
+ else if ( pOldValue[i].Name == "DocumentBaseURL" )
+ bHasBaseURL = true;
+ else if( pOldValue[i].Name == "FilterName" )
+ bHasFilterName = true;
+ }
+
+ const css::uno::Sequence<css::beans::PropertyValue>& rMediumArgs = rMedium.GetArgs();
+ for ( sal_Int32 i = 0; i < rMediumArgs.getLength(); i++ )
+ {
+ if( rMediumArgs[i].Name == "IsPreview" )
+ rMediumArgs[i].Value >>= bIsPreview;
+ }
+
+ // FIXME: Handle this inside TransformItems()
+ if (pItems->GetItemState(SID_IS_REDACT_MODE) == SfxItemState::SET)
+ bIsRedactMode = true;
+
+ if ( !bHasOutputStream )
+ {
+ aArgs.realloc ( ++nEnd );
+ auto pArgs = aArgs.getArray();
+ pArgs[nEnd-1].Name = sOutputStream;
+ pArgs[nEnd-1].Value <<= css::uno::Reference < css::io::XOutputStream > ( new utl::OOutputStreamWrapper ( *rMedium.GetOutStream() ) );
+ }
+
+ // add stream as well, for OOX export and maybe others
+ if ( !bHasStream )
+ {
+ aArgs.realloc ( ++nEnd );
+ auto pArgs = aArgs.getArray();
+ pArgs[nEnd-1].Name = sStream;
+ pArgs[nEnd-1].Value <<= css::uno::Reference < css::io::XStream > ( new utl::OStreamWrapper ( *rMedium.GetOutStream() ) );
+ }
+
+ if ( !bHasBaseURL )
+ {
+ aArgs.realloc ( ++nEnd );
+ auto pArgs = aArgs.getArray();
+ pArgs[nEnd-1].Name = "DocumentBaseURL";
+ pArgs[nEnd-1].Value <<= rMedium.GetBaseURL( true );
+ }
+
+ if( !bHasFilterName )
+ {
+ aArgs.realloc( ++nEnd );
+ auto pArgs = aArgs.getArray();
+ pArgs[nEnd-1].Name = "FilterName";
+ pArgs[nEnd-1].Value <<= aFilterName;
+ }
+
+ if (bIsRedactMode)
+ {
+ aArgs.realloc( ++nEnd );
+ auto pArgs = aArgs.getArray();
+ pArgs[nEnd-1].Name = "IsRedactMode";
+ pArgs[nEnd-1].Value <<= bIsRedactMode;
+ }
+
+ if (bIsPreview)
+ {
+ aArgs.realloc( ++nEnd );
+ auto pArgs = aArgs.getArray();
+ pArgs[nEnd-1].Name = "IsPreview";
+ pArgs[nEnd-1].Value <<= bIsPreview;
+ }
+
+ return xFilter->filter( aArgs );
+ }catch(...)
+ {}
+ }
+
+ return false;
+}
+
+
+bool SfxObjectShell::ConvertTo
+(
+ SfxMedium& /*rMedium*/ /* <SfxMedium>, which describes the target file
+ (for example file name, <SfxFilter>,
+ Open-Modi and so on) */
+)
+
+/* [Description]
+
+ This method is called for saving of documents over all filters which are
+ not SfxFilterFlags::OWN or for which no clipboard format has been registered
+ (thus no storage format that is used). In other words, with this method
+ it is exported.
+
+ Files which are to be opened here should be opened through 'rMedium'
+ to guarantee the right open modes. Especially if the format is retained
+ (only possible with SfxFilterFlags::SIMULATE or SfxFilterFlags::OWN) file which must
+ be opened STREAM_SHARE_DENYWRITE.
+
+ [Return value]
+
+ bool true
+ The document could be saved.
+
+ false
+ The document could not be saved, an error code is
+ received by <SvMedium::GetError()const>
+
+
+ [Example]
+
+ bool DocSh::ConvertTo( SfxMedium &rMedium )
+ {
+ SvStreamRef xStream = rMedium.GetOutStream();
+ if ( xStream.is() )
+ {
+ xStream->SetBufferSize(4096);
+ *xStream << ...;
+
+ rMedium.CloseOutStream(); // opens the InStream automatically
+ return ERRCODE_NONE == rMedium.GetError();
+ }
+ return false ;
+ }
+
+ [Cross-references]
+
+ <SfxObjectShell::ConvertFrom(SfxMedium&)>
+ <SfxFilterFlags::REGISTRATION>
+*/
+
+{
+ return false;
+}
+
+
+bool SfxObjectShell::DoSave_Impl( const SfxItemSet* pArgs )
+{
+ SfxMedium* pRetrMedium = GetMedium();
+ std::shared_ptr<const SfxFilter> pFilter = pRetrMedium->GetFilter();
+
+ // copy the original itemset, but remove the "version" item, because pMediumTmp
+ // is a new medium "from scratch", so no version should be stored into it
+ std::shared_ptr<SfxItemSet> pSet = std::make_shared<SfxAllItemSet>(*pRetrMedium->GetItemSet());
+ pSet->ClearItem( SID_VERSION );
+ pSet->ClearItem( SID_DOC_BASEURL );
+
+ // copy the version comment and major items for the checkin only
+ if ( pRetrMedium->IsInCheckIn( ) )
+ {
+ const SfxPoolItem* pMajor = pArgs->GetItem( SID_DOCINFO_MAJOR );
+ if ( pMajor )
+ pSet->Put( *pMajor );
+
+ const SfxPoolItem* pComments = pArgs->GetItem( SID_DOCINFO_COMMENTS );
+ if ( pComments )
+ pSet->Put( *pComments );
+ }
+
+ // create a medium as a copy; this medium is only for writing, because it
+ // uses the same name as the original one writing is done through a copy,
+ // that will be transferred to the target (of course after calling HandsOff)
+ SfxMedium* pMediumTmp = new SfxMedium( pRetrMedium->GetName(), pRetrMedium->GetOpenMode(), pFilter, std::move(pSet) );
+ pMediumTmp->SetInCheckIn( pRetrMedium->IsInCheckIn( ) );
+ pMediumTmp->SetLongName( pRetrMedium->GetLongName() );
+ if ( pMediumTmp->GetErrorCode() != ERRCODE_NONE )
+ {
+ SetError(pMediumTmp->GetError());
+ delete pMediumTmp;
+ return false;
+ }
+
+ // copy version list from "old" medium to target medium, so it can be used on saving
+ pMediumTmp->TransferVersionList_Impl( *pRetrMedium );
+
+ // an interaction handler here can acquire only in case of GUI Saving
+ // and should be removed after the saving is done
+ css::uno::Reference< XInteractionHandler > xInteract;
+ const SfxUnoAnyItem* pxInteractionItem = SfxItemSet::GetItem<SfxUnoAnyItem>(pArgs, SID_INTERACTIONHANDLER, false);
+ if ( pxInteractionItem && ( pxInteractionItem->GetValue() >>= xInteract ) && xInteract.is() )
+ pMediumTmp->GetItemSet()->Put( SfxUnoAnyItem( SID_INTERACTIONHANDLER, Any( xInteract ) ) );
+
+ const SfxBoolItem* pNoFileSync = pArgs->GetItem<SfxBoolItem>(SID_NO_FILE_SYNC, false);
+ if (pNoFileSync && pNoFileSync->GetValue())
+ pMediumTmp->DisableFileSync(true);
+
+ bool bSaved = false;
+ if( !GetError() && SaveTo_Impl( *pMediumTmp, pArgs ) )
+ {
+ bSaved = true;
+
+ if( pMediumTmp->GetItemSet() )
+ {
+ pMediumTmp->GetItemSet()->ClearItem( SID_INTERACTIONHANDLER );
+ pMediumTmp->GetItemSet()->ClearItem( SID_PROGRESS_STATUSBAR_CONTROL );
+ }
+
+ SetError(pMediumTmp->GetErrorCode());
+
+ bool bOpen = DoSaveCompleted( pMediumTmp );
+
+ DBG_ASSERT(bOpen,"Error handling for DoSaveCompleted not implemented");
+ }
+ else
+ {
+ // transfer error code from medium to objectshell
+ SetError(pMediumTmp->GetError());
+
+ // reconnect to object storage
+ DoSaveCompleted();
+
+ if( pRetrMedium->GetItemSet() )
+ {
+ pRetrMedium->GetItemSet()->ClearItem( SID_INTERACTIONHANDLER );
+ pRetrMedium->GetItemSet()->ClearItem( SID_PROGRESS_STATUSBAR_CONTROL );
+ }
+
+ delete pMediumTmp;
+ }
+
+ SetModified( !bSaved );
+ return bSaved;
+}
+
+
+bool SfxObjectShell::Save_Impl( const SfxItemSet* pSet )
+{
+ if ( IsReadOnly() )
+ {
+ SetError(ERRCODE_SFX_DOCUMENTREADONLY);
+ return false;
+ }
+
+ pImpl->bIsSaving = true;
+ bool bSaved = false;
+ const SfxStringItem* pSalvageItem = SfxItemSet::GetItem<SfxStringItem>(GetMedium()->GetItemSet(), SID_DOC_SALVAGE, false);
+ if ( pSalvageItem )
+ {
+ const SfxStringItem* pFilterItem = SfxItemSet::GetItem<SfxStringItem>(GetMedium()->GetItemSet(), SID_FILTER_NAME, false);
+ std::shared_ptr<const SfxFilter> pFilter;
+ if ( pFilterItem )
+ pFilter = SfxFilterMatcher( GetFactory().GetFactoryName() ).GetFilter4FilterName( OUString() );
+
+ SfxMedium *pMed = new SfxMedium(
+ pSalvageItem->GetValue(), StreamMode::READWRITE | StreamMode::SHARE_DENYWRITE | StreamMode::TRUNC, pFilter );
+
+ const SfxStringItem* pPasswordItem = SfxItemSet::GetItem<SfxStringItem>(GetMedium()->GetItemSet(), SID_PASSWORD, false);
+ if ( pPasswordItem )
+ pMed->GetItemSet()->Put( *pPasswordItem );
+
+ bSaved = DoSaveAs( *pMed );
+ if ( bSaved )
+ bSaved = DoSaveCompleted( pMed );
+ else
+ delete pMed;
+ }
+ else
+ bSaved = DoSave_Impl( pSet );
+ return bSaved;
+}
+
+bool SfxObjectShell::CommonSaveAs_Impl(const INetURLObject& aURL, const OUString& aFilterName,
+ SfxItemSet& rItemSet,
+ const uno::Sequence<beans::PropertyValue>& rArgs)
+{
+ if( aURL.HasError() )
+ {
+ SetError(ERRCODE_IO_INVALIDPARAMETER);
+ return false;
+ }
+
+ if ( aURL != INetURLObject( u"private:stream" ) )
+ {
+ // Is there already a Document with this name?
+ SfxObjectShell* pDoc = nullptr;
+ for ( SfxObjectShell* pTmp = SfxObjectShell::GetFirst();
+ pTmp && !pDoc;
+ pTmp = SfxObjectShell::GetNext(*pTmp) )
+ {
+ if( ( pTmp != this ) && pTmp->GetMedium() )
+ {
+ INetURLObject aCompare( pTmp->GetMedium()->GetName() );
+ if ( aCompare == aURL )
+ pDoc = pTmp;
+ }
+ }
+ if ( pDoc )
+ {
+ // Then error message: "already opened"
+ SetError(ERRCODE_SFX_ALREADYOPEN);
+ return false;
+ }
+ }
+
+ DBG_ASSERT( aURL.GetProtocol() != INetProtocol::NotValid, "Illegal URL!" );
+ DBG_ASSERT( rItemSet.Count() != 0, "Incorrect Parameter");
+
+ const SfxBoolItem* pSaveToItem = rItemSet.GetItem<SfxBoolItem>(SID_SAVETO, false);
+ bool bSaveTo = pSaveToItem && pSaveToItem->GetValue();
+
+ std::shared_ptr<const SfxFilter> pFilter = GetFactory().GetFilterContainer()->GetFilter4FilterName( aFilterName );
+ if ( !pFilter
+ || !pFilter->CanExport()
+ || (!bSaveTo && !pFilter->CanImport()) )
+ {
+ SetError(ERRCODE_IO_INVALIDPARAMETER);
+ return false;
+ }
+
+
+ const SfxBoolItem* pCopyStreamItem = rItemSet.GetItem(SID_COPY_STREAM_IF_POSSIBLE, false);
+ if ( bSaveTo && pCopyStreamItem && pCopyStreamItem->GetValue() && !IsModified() )
+ {
+ if (pMedium->TryDirectTransfer(aURL.GetMainURL(INetURLObject::DecodeMechanism::NONE), rItemSet))
+ return true;
+ }
+ rItemSet.ClearItem( SID_COPY_STREAM_IF_POSSIBLE );
+
+ SfxMedium *pActMed = GetMedium();
+ const INetURLObject aActName(pActMed->GetName());
+
+ bool bWasReadonly = IsReadOnly();
+
+ if ( aURL == aActName && aURL != INetURLObject( u"private:stream" )
+ && IsReadOnly() )
+ {
+ SetError(ERRCODE_SFX_DOCUMENTREADONLY);
+ return false;
+ }
+
+ if (SfxItemState::SET != rItemSet.GetItemState(SID_UNPACK) && officecfg::Office::Common::Save::Document::Unpacked::get())
+ rItemSet.Put(SfxBoolItem(SID_UNPACK, false));
+
+#if HAVE_FEATURE_MULTIUSER_ENVIRONMENT
+ OUString aTempFileURL;
+ if ( IsDocShared() )
+ aTempFileURL = pMedium->GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE );
+#endif
+
+ if (PreDoSaveAs_Impl(aURL.GetMainURL(INetURLObject::DecodeMechanism::NONE), aFilterName,
+ rItemSet, rArgs))
+ {
+ // Update Data on media
+ SfxItemSet *pSet = GetMedium()->GetItemSet();
+ pSet->ClearItem( SID_INTERACTIONHANDLER );
+ pSet->ClearItem( SID_PROGRESS_STATUSBAR_CONTROL );
+ pSet->ClearItem( SID_STANDARD_DIR );
+ pSet->ClearItem( SID_PATH );
+
+ if ( !bSaveTo )
+ {
+ pSet->ClearItem( SID_REFERER );
+ pSet->ClearItem( SID_POSTDATA );
+ pSet->ClearItem( SID_TEMPLATE );
+ pSet->ClearItem( SID_DOC_READONLY );
+ pSet->ClearItem( SID_CONTENTTYPE );
+ pSet->ClearItem( SID_CHARSET );
+ pSet->ClearItem( SID_FILTER_NAME );
+ pSet->ClearItem( SID_OPTIONS );
+ pSet->ClearItem( SID_VERSION );
+ pSet->ClearItem( SID_EDITDOC );
+ pSet->ClearItem( SID_OVERWRITE );
+ pSet->ClearItem( SID_DEFAULTFILEPATH );
+ pSet->ClearItem( SID_DEFAULTFILENAME );
+
+ const SfxStringItem* pFilterItem = rItemSet.GetItem<SfxStringItem>(SID_FILTER_NAME, false);
+ if ( pFilterItem )
+ pSet->Put( *pFilterItem );
+
+ const SfxStringItem* pOptionsItem = rItemSet.GetItem<SfxStringItem>(SID_OPTIONS, false);
+ if ( pOptionsItem )
+ pSet->Put( *pOptionsItem );
+
+ const SfxStringItem* pFilterOptItem = rItemSet.GetItem<SfxStringItem>(SID_FILE_FILTEROPTIONS, false);
+ if ( pFilterOptItem )
+ pSet->Put( *pFilterOptItem );
+
+#if HAVE_FEATURE_MULTIUSER_ENVIRONMENT
+ if ( IsDocShared() && !aTempFileURL.isEmpty() )
+ {
+ // this is a shared document that has to be disconnected from the old location
+ FreeSharedFile( aTempFileURL );
+
+ if ( pFilter->IsOwnFormat()
+ && pFilter->UsesStorage()
+ && pFilter->GetVersion() >= SOFFICE_FILEFORMAT_60 )
+ {
+ // the target format is the own format
+ // the target document must be shared
+ SwitchToShared( true, false );
+ }
+ }
+#endif
+ }
+
+ if ( bWasReadonly && !bSaveTo )
+ Broadcast( SfxHint(SfxHintId::ModeChanged) );
+
+ return true;
+ }
+ else
+ return false;
+}
+
+bool SfxObjectShell::PreDoSaveAs_Impl(const OUString& rFileName, const OUString& aFilterName,
+ SfxItemSet const& rItemSet,
+ const uno::Sequence<beans::PropertyValue>& rArgs)
+{
+ // copy all items stored in the itemset of the current medium
+ std::shared_ptr<SfxAllItemSet> xMergedParams = std::make_shared<SfxAllItemSet>( *pMedium->GetItemSet() );
+
+ // in "SaveAs" title and password will be cleared ( maybe the new itemset contains new values, otherwise they will be empty )
+ // #i119366# - As the SID_ENCRYPTIONDATA and SID_PASSWORD are using for setting password together, we need to clear them both.
+ // Also, ( maybe the new itemset contains new values, otherwise they will be empty )
+ if (xMergedParams->HasItem(SID_ENCRYPTIONDATA))
+ {
+ bool bPasswordProtected = true;
+ const SfxUnoAnyItem* pEncryptionDataItem
+ = xMergedParams->GetItem<SfxUnoAnyItem>(SID_ENCRYPTIONDATA, false);
+ if (pEncryptionDataItem)
+ {
+ uno::Sequence<beans::NamedValue> aEncryptionData;
+ pEncryptionDataItem->GetValue() >>= aEncryptionData;
+ for (const auto& rItem : std::as_const(aEncryptionData))
+ {
+ if (rItem.Name == "CryptoType")
+ {
+ OUString aValue;
+ rItem.Value >>= aValue;
+ if (aValue != "StrongEncryptionDataSpace")
+ {
+ // This is not just a password protected document. Let's keep encryption data as is.
+ bPasswordProtected = false;
+ }
+ break;
+ }
+ }
+ }
+ if (bPasswordProtected)
+ {
+ // For password protected documents remove encryption data during "Save as..."
+ xMergedParams->ClearItem(SID_PASSWORD);
+ xMergedParams->ClearItem(SID_ENCRYPTIONDATA);
+ }
+ }
+
+ xMergedParams->ClearItem( SID_DOCINFO_TITLE );
+
+ xMergedParams->ClearItem( SID_INPUTSTREAM );
+ xMergedParams->ClearItem( SID_STREAM );
+ xMergedParams->ClearItem( SID_CONTENT );
+ xMergedParams->ClearItem( SID_DOC_READONLY );
+ xMergedParams->ClearItem( SID_DOC_BASEURL );
+
+ xMergedParams->ClearItem( SID_REPAIRPACKAGE );
+
+ // "SaveAs" will never store any version information - it's a complete new file !
+ xMergedParams->ClearItem( SID_VERSION );
+
+ // merge the new parameters into the copy
+ // all values present in both itemsets will be overwritten by the new parameters
+ xMergedParams->Put(rItemSet);
+
+ SAL_WARN_IF( xMergedParams->GetItemState( SID_DOC_SALVAGE) >= SfxItemState::SET,
+ "sfx.doc","Salvage item present in Itemset, check the parameters!");
+
+ // should be unnecessary - too hot to handle!
+ xMergedParams->ClearItem( SID_DOC_SALVAGE );
+
+ // create a medium for the target URL
+ SfxMedium *pNewFile = new SfxMedium( rFileName, StreamMode::READWRITE | StreamMode::SHARE_DENYWRITE | StreamMode::TRUNC, nullptr, xMergedParams );
+ pNewFile->SetArgs(rArgs);
+
+ const SfxBoolItem* pNoFileSync = xMergedParams->GetItem<SfxBoolItem>(SID_NO_FILE_SYNC, false);
+ if (pNoFileSync && pNoFileSync->GetValue())
+ pNewFile->DisableFileSync(true);
+
+ bool bUseThumbnailSave = IsUseThumbnailSave();
+ comphelper::ScopeGuard aThumbnailGuard(
+ [this, bUseThumbnailSave] { this->SetUseThumbnailSave(bUseThumbnailSave); });
+ const SfxBoolItem* pNoThumbnail = xMergedParams->GetItem<SfxBoolItem>(SID_NO_THUMBNAIL, false);
+ if (pNoThumbnail)
+ // Thumbnail generation should be avoided just for this save.
+ SetUseThumbnailSave(!pNoThumbnail->GetValue());
+ else
+ aThumbnailGuard.dismiss();
+
+ // set filter; if no filter is given, take the default filter of the factory
+ if ( !aFilterName.isEmpty() )
+ {
+ pNewFile->SetFilter( GetFactory().GetFilterContainer()->GetFilter4FilterName( aFilterName ) );
+
+ if(aFilterName == "writer_pdf_Export" && pNewFile->GetItemSet())
+ {
+ uno::Sequence< beans::PropertyValue > aSaveToFilterDataOptions(2);
+ auto pSaveToFilterDataOptions = aSaveToFilterDataOptions.getArray();
+ bool bRet = false;
+
+ for(int i = 0 ; i< rArgs.getLength() ; ++i)
+ {
+ auto aProp = rArgs[i];
+ if(aProp.Name == "EncryptFile")
+ {
+ pSaveToFilterDataOptions[0].Name = aProp.Name;
+ pSaveToFilterDataOptions[0].Value = aProp.Value;
+ bRet = true;
+ }
+ if(aProp.Name == "DocumentOpenPassword")
+ {
+ pSaveToFilterDataOptions[1].Name = aProp.Name;
+ pSaveToFilterDataOptions[1].Value = aProp.Value;
+ bRet = true;
+ }
+ }
+
+ if( bRet )
+ pNewFile->GetItemSet()->Put( SfxUnoAnyItem(SID_FILTER_DATA, uno::Any(aSaveToFilterDataOptions)));
+ }
+ }
+ else
+ pNewFile->SetFilter( GetFactory().GetFilterContainer()->GetAnyFilter( SfxFilterFlags::IMPORT | SfxFilterFlags::EXPORT ) );
+
+ if ( pNewFile->GetErrorCode() != ERRCODE_NONE )
+ {
+ // creating temporary file failed ( f.e. floppy disk not inserted! )
+ SetError(pNewFile->GetError());
+ delete pNewFile;
+ return false;
+ }
+
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ // Before saving, commit in-flight changes.
+ TerminateEditing();
+ }
+
+ // check if a "SaveTo" is wanted, no "SaveAs"
+ const SfxBoolItem* pSaveToItem = xMergedParams->GetItem<SfxBoolItem>(SID_SAVETO, false);
+ bool bCopyTo = GetCreateMode() == SfxObjectCreateMode::EMBEDDED || (pSaveToItem && pSaveToItem->GetValue());
+
+ // distinguish between "Save" and "SaveAs"
+ pImpl->bIsSaving = false;
+
+ // copy version list from "old" medium to target medium, so it can be used on saving
+ if ( pImpl->bPreserveVersions )
+ pNewFile->TransferVersionList_Impl( *pMedium );
+
+ // Save the document ( first as temporary file, then transfer to the target URL by committing the medium )
+ bool bOk = false;
+ if ( !pNewFile->GetErrorCode() && SaveTo_Impl( *pNewFile, nullptr ) )
+ {
+ // transfer a possible error from the medium to the document
+ SetError(pNewFile->GetErrorCode());
+
+ // notify the document that saving was done successfully
+ if ( !bCopyTo )
+ {
+ bOk = DoSaveCompleted( pNewFile );
+ }
+ else
+ bOk = DoSaveCompleted();
+
+ if( bOk )
+ {
+ if( !bCopyTo )
+ SetModified( false );
+ }
+ else
+ {
+ // TODO/LATER: the code below must be dead since the storage commit makes all the stuff
+ // and the DoSaveCompleted call should not be able to fail in general
+
+ DBG_ASSERT( !bCopyTo, "Error while reconnecting to medium, can't be handled!");
+ SetError(pNewFile->GetErrorCode());
+
+ if ( !bCopyTo )
+ {
+ // reconnect to the old medium
+ bool bRet = DoSaveCompleted( pMedium );
+ DBG_ASSERT( bRet, "Error in DoSaveCompleted, can't be handled!");
+ }
+
+ // TODO/LATER: disconnect the new file from the storage for the case when pure saving is done
+ // if storing has corrupted the file, probably it must be restored either here or
+ // by the storage
+ delete pNewFile;
+ pNewFile = nullptr;
+ }
+ }
+ else
+ {
+ SetError(pNewFile->GetErrorCode());
+
+ // reconnect to the old storage
+ DoSaveCompleted();
+
+ delete pNewFile;
+ pNewFile = nullptr;
+ }
+
+ if ( bCopyTo )
+ delete pNewFile;
+ else if( !bOk )
+ SetModified();
+
+ return bOk;
+}
+
+
+bool SfxObjectShell::LoadFrom( SfxMedium& /*rMedium*/ )
+{
+ SAL_WARN( "sfx.doc", "Base implementation, must not be called in general!" );
+ return true;
+}
+
+
+bool SfxObjectShell::CanReload_Impl()
+
+/* [Description]
+
+ Internal method for determining whether a reload of the document
+ (as RevertToSaved or last known version) is possible.
+*/
+
+{
+ return pMedium && HasName() && !IsInModalMode() && !pImpl->bForbidReload;
+}
+
+
+HiddenInformation SfxObjectShell::GetHiddenInformationState( HiddenInformation nStates )
+{
+ HiddenInformation nState = HiddenInformation::NONE;
+ if ( nStates & HiddenInformation::DOCUMENTVERSIONS )
+ {
+ if ( GetMedium()->GetVersionList().hasElements() )
+ nState |= HiddenInformation::DOCUMENTVERSIONS;
+ }
+
+ return nState;
+}
+
+sal_Int16 SfxObjectShell::QueryHiddenInformation(HiddenWarningFact eFact, weld::Window* pParent)
+{
+ sal_Int16 nRet = RET_YES;
+ TranslateId pResId;
+ SvtSecurityOptions::EOption eOption = SvtSecurityOptions::EOption();
+
+ switch ( eFact )
+ {
+ case HiddenWarningFact::WhenSaving :
+ {
+ pResId = STR_HIDDENINFO_CONTINUE_SAVING;
+ eOption = SvtSecurityOptions::EOption::DocWarnSaveOrSend;
+ break;
+ }
+ case HiddenWarningFact::WhenPrinting :
+ {
+ pResId = STR_HIDDENINFO_CONTINUE_PRINTING;
+ eOption = SvtSecurityOptions::EOption::DocWarnPrint;
+ break;
+ }
+ case HiddenWarningFact::WhenSigning :
+ {
+ pResId = STR_HIDDENINFO_CONTINUE_SIGNING;
+ eOption = SvtSecurityOptions::EOption::DocWarnSigning;
+ break;
+ }
+ case HiddenWarningFact::WhenCreatingPDF :
+ {
+ pResId = STR_HIDDENINFO_CONTINUE_CREATEPDF;
+ eOption = SvtSecurityOptions::EOption::DocWarnCreatePdf;
+ break;
+ }
+ default:
+ assert(false); // this cannot happen
+ }
+
+ if ( SvtSecurityOptions::IsOptionSet( eOption ) )
+ {
+ OUString sMessage( SfxResId(STR_HIDDENINFO_CONTAINS) );
+ HiddenInformation nWantedStates = HiddenInformation::RECORDEDCHANGES | HiddenInformation::NOTES;
+ if ( eFact != HiddenWarningFact::WhenPrinting )
+ nWantedStates |= HiddenInformation::DOCUMENTVERSIONS;
+ HiddenInformation nStates = GetHiddenInformationState( nWantedStates );
+ bool bWarning = false;
+
+ if ( nStates & HiddenInformation::RECORDEDCHANGES )
+ {
+ sMessage += SfxResId(STR_HIDDENINFO_RECORDCHANGES) + "\n";
+ bWarning = true;
+ }
+ if ( nStates & HiddenInformation::NOTES )
+ {
+ sMessage += SfxResId(STR_HIDDENINFO_NOTES) + "\n";
+ bWarning = true;
+ }
+ if ( nStates & HiddenInformation::DOCUMENTVERSIONS )
+ {
+ sMessage += SfxResId(STR_HIDDENINFO_DOCVERSIONS) + "\n";
+ bWarning = true;
+ }
+
+ if ( bWarning )
+ {
+ sMessage += "\n" + SfxResId(pResId);
+ std::unique_ptr<weld::MessageDialog> xWarn(Application::CreateMessageDialog(pParent,
+ VclMessageType::Warning, VclButtonsType::YesNo, sMessage));
+ xWarn->set_default_response(RET_NO);
+ nRet = xWarn->run();
+ }
+ }
+
+ return nRet;
+}
+
+bool SfxObjectShell::IsSecurityOptOpenReadOnly() const
+{
+ return IsLoadReadonly();
+}
+
+void SfxObjectShell::SetSecurityOptOpenReadOnly( bool _b )
+{
+ SetLoadReadonly( _b );
+}
+
+bool SfxObjectShell::LoadOwnFormat( SfxMedium& rMedium )
+{
+ SAL_INFO( "sfx.doc", "loading \" " << rMedium.GetName() << "\"" );
+
+ uno::Reference< embed::XStorage > xStorage = rMedium.GetStorage();
+ if ( xStorage.is() )
+ {
+ // Password
+ const SfxStringItem* pPasswdItem = SfxItemSet::GetItem<SfxStringItem>(rMedium.GetItemSet(), SID_PASSWORD, false);
+ if ( pPasswdItem || ERRCODE_IO_ABORT != CheckPasswd_Impl( this, pMedium ) )
+ {
+ uno::Sequence< beans::NamedValue > aEncryptionData;
+ if ( GetEncryptionData_Impl(pMedium->GetItemSet(), aEncryptionData) )
+ {
+ try
+ {
+ // the following code must throw an exception in case of failure
+ ::comphelper::OStorageHelper::SetCommonStorageEncryptionData( xStorage, aEncryptionData );
+ }
+ catch( uno::Exception& )
+ {
+ // TODO/LATER: handle the error code
+ }
+ }
+
+ // load document
+ return Load( rMedium );
+ }
+ return false;
+ }
+ else
+ return false;
+}
+
+bool SfxObjectShell::SaveAsOwnFormat( SfxMedium& rMedium )
+{
+ uno::Reference< embed::XStorage > xStorage = rMedium.GetStorage();
+ if( xStorage.is() )
+ {
+ sal_Int32 nVersion = rMedium.GetFilter()->GetVersion();
+
+ // OASIS templates have own mediatypes (SO7 also actually, but it is too late to use them here)
+ const bool bTemplate = rMedium.GetFilter()->IsOwnTemplateFormat()
+ && nVersion > SOFFICE_FILEFORMAT_60;
+
+ SetupStorage( xStorage, nVersion, bTemplate );
+#if HAVE_FEATURE_SCRIPTING
+ if ( HasBasic() )
+ {
+ // Initialize Basic
+ GetBasicManager();
+
+ // Save dialog/script container
+ pImpl->aBasicManager.storeLibrariesToStorage( xStorage );
+ }
+#endif
+ return SaveAs( rMedium );
+ }
+ else return false;
+}
+
+uno::Reference< embed::XStorage > const & SfxObjectShell::GetStorage()
+{
+ if ( !pImpl->m_xDocStorage.is() )
+ {
+ OSL_ENSURE( pImpl->m_bCreateTempStor, "The storage must exist already!" );
+ try {
+ // no notification is required the storage is set the first time
+ pImpl->m_xDocStorage = ::comphelper::OStorageHelper::GetTemporaryStorage();
+ OSL_ENSURE( pImpl->m_xDocStorage.is(), "The method must either return storage or throw exception!" );
+
+ SetupStorage( pImpl->m_xDocStorage, SOFFICE_FILEFORMAT_CURRENT, false );
+ pImpl->m_bCreateTempStor = false;
+ if (!utl::ConfigManager::IsFuzzing())
+ SfxGetpApp()->NotifyEvent( SfxEventHint( SfxEventHintId::StorageChanged, GlobalEventConfig::GetEventName(GlobalEventId::STORAGECHANGED), this ) );
+ }
+ catch( uno::Exception& )
+ {
+ // TODO/LATER: error handling?
+ TOOLS_WARN_EXCEPTION("sfx.doc", "SfxObjectShell::GetStorage");
+ }
+ }
+
+ OSL_ENSURE( pImpl->m_xDocStorage.is(), "The document storage must be created!" );
+ return pImpl->m_xDocStorage;
+}
+
+
+void SfxObjectShell::SaveChildren( bool bObjectsOnly )
+{
+ if ( pImpl->mxObjectContainer )
+ {
+ bool bOasis = ( SotStorage::GetVersion( GetStorage() ) > SOFFICE_FILEFORMAT_60 );
+ GetEmbeddedObjectContainer().StoreChildren(bOasis,bObjectsOnly);
+ }
+}
+
+bool SfxObjectShell::SaveAsChildren( SfxMedium& rMedium )
+{
+ uno::Reference < embed::XStorage > xStorage = rMedium.GetStorage();
+ if ( !xStorage.is() )
+ return false;
+
+ if ( xStorage == GetStorage() )
+ {
+ SaveChildren();
+ return true;
+ }
+
+ bool AutoSaveEvent = false;
+ utl::MediaDescriptor lArgs(rMedium.GetArgs());
+ lArgs[utl::MediaDescriptor::PROP_AUTOSAVEEVENT] >>= AutoSaveEvent;
+
+ if ( pImpl->mxObjectContainer )
+ {
+ bool bOasis = ( SotStorage::GetVersion( xStorage ) > SOFFICE_FILEFORMAT_60 );
+ GetEmbeddedObjectContainer().StoreAsChildren(bOasis,SfxObjectCreateMode::EMBEDDED == eCreateMode,AutoSaveEvent,xStorage);
+ }
+
+ uno::Sequence<OUString> aExceptions;
+ if (const SfxBoolItem* pNoEmbDS
+ = SfxItemSet::GetItem(rMedium.GetItemSet(), SID_NO_EMBEDDED_DS, false))
+ {
+ // Don't save data source in case a temporary is being saved for preview in MM wizard
+ if (pNoEmbDS->GetValue())
+ aExceptions = uno::Sequence<OUString>{ "EmbeddedDatabase" };
+ }
+
+ return CopyStoragesOfUnknownMediaType(GetStorage(), xStorage, aExceptions);
+}
+
+bool SfxObjectShell::SaveCompletedChildren()
+{
+ bool bResult = true;
+
+ if ( pImpl->mxObjectContainer )
+ {
+ const uno::Sequence < OUString > aNames = GetEmbeddedObjectContainer().GetObjectNames();
+ for ( const auto& rName : aNames )
+ {
+ uno::Reference < embed::XEmbeddedObject > xObj = GetEmbeddedObjectContainer().GetEmbeddedObject( rName );
+ OSL_ENSURE( xObj.is(), "An empty entry in the embedded objects list!" );
+ if ( xObj.is() )
+ {
+ uno::Reference< embed::XEmbedPersist > xPersist( xObj, uno::UNO_QUERY );
+ if ( xPersist.is() )
+ {
+ try
+ {
+ xPersist->saveCompleted( false/*bSuccess*/ );
+ }
+ catch( uno::Exception& )
+ {
+ // TODO/LATER: error handling
+ bResult = false;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ return bResult;
+}
+
+bool SfxObjectShell::SwitchChildrenPersistence( const uno::Reference< embed::XStorage >& xStorage,
+ bool bForceNonModified )
+{
+ if ( !xStorage.is() )
+ {
+ // TODO/LATER: error handling
+ return false;
+ }
+
+ if ( pImpl->mxObjectContainer )
+ pImpl->mxObjectContainer->SetPersistentEntries(xStorage,bForceNonModified);
+
+ return true;
+}
+
+// Never call this method directly, always use the DoSaveCompleted call
+bool SfxObjectShell::SaveCompleted( const uno::Reference< embed::XStorage >& xStorage )
+{
+ bool bResult = false;
+ bool bSendNotification = false;
+ uno::Reference< embed::XStorage > xOldStorageHolder;
+
+ // check for wrong creation of object container
+ bool bHasContainer( pImpl->mxObjectContainer );
+
+ if ( !xStorage.is() || xStorage == GetStorage() )
+ {
+ // no persistence change
+ bResult = SaveCompletedChildren();
+ }
+ else
+ {
+ if ( pImpl->mxObjectContainer )
+ GetEmbeddedObjectContainer().SwitchPersistence( xStorage );
+
+ bResult = SwitchChildrenPersistence( xStorage, true );
+ }
+
+ if ( bResult )
+ {
+ if ( xStorage.is() && pImpl->m_xDocStorage != xStorage )
+ {
+ // make sure that until the storage is assigned the object
+ // container is not created by accident!
+ DBG_ASSERT( bHasContainer == (pImpl->mxObjectContainer != nullptr), "Wrong storage in object container!" );
+ xOldStorageHolder = pImpl->m_xDocStorage;
+ pImpl->m_xDocStorage = xStorage;
+ bSendNotification = true;
+
+ if ( IsEnableSetModified() )
+ SetModified( false );
+ }
+ }
+ else
+ {
+ if ( pImpl->mxObjectContainer )
+ GetEmbeddedObjectContainer().SwitchPersistence( pImpl->m_xDocStorage );
+
+ // let already successfully connected objects be switched back
+ SwitchChildrenPersistence( pImpl->m_xDocStorage, true );
+ }
+
+ if ( bSendNotification )
+ {
+ SfxGetpApp()->NotifyEvent( SfxEventHint( SfxEventHintId::StorageChanged, GlobalEventConfig::GetEventName(GlobalEventId::STORAGECHANGED), this ) );
+ }
+
+ return bResult;
+}
+
+static bool StoragesOfUnknownMediaTypeAreCopied_Impl( const uno::Reference< embed::XStorage >& xSource,
+ const uno::Reference< embed::XStorage >& xTarget )
+{
+ OSL_ENSURE( xSource.is() && xTarget.is(), "Source and/or target storages are not available!" );
+ if ( !xSource.is() || !xTarget.is() || xSource == xTarget )
+ return true;
+
+ try
+ {
+ const uno::Sequence< OUString > aSubElements = xSource->getElementNames();
+ for ( const auto& rSubElement : aSubElements )
+ {
+ if ( xSource->isStorageElement( rSubElement ) )
+ {
+ OUString aMediaType;
+ static const OUStringLiteral aMediaTypePropName( u"MediaType" );
+ bool bGotMediaType = false;
+
+ try
+ {
+ uno::Reference< embed::XOptimizedStorage > xOptStorage( xSource, uno::UNO_QUERY_THROW );
+ bGotMediaType =
+ ( xOptStorage->getElementPropertyValue( rSubElement, aMediaTypePropName ) >>= aMediaType );
+ }
+ catch( uno::Exception& )
+ {}
+
+ if ( !bGotMediaType )
+ {
+ uno::Reference< embed::XStorage > xSubStorage;
+ try {
+ xSubStorage = xSource->openStorageElement( rSubElement, embed::ElementModes::READ );
+ } catch( uno::Exception& )
+ {}
+
+ if ( !xSubStorage.is() )
+ {
+ xSubStorage = ::comphelper::OStorageHelper::GetTemporaryStorage();
+ xSource->copyStorageElementLastCommitTo( rSubElement, xSubStorage );
+ }
+
+ uno::Reference< beans::XPropertySet > xProps( xSubStorage, uno::UNO_QUERY_THROW );
+ xProps->getPropertyValue( aMediaTypePropName ) >>= aMediaType;
+ }
+
+ // TODO/LATER: there should be a way to detect whether an object with such a MediaType can exist
+ // probably it should be placed in the MimeType-ClassID table or in standalone table
+ if ( !aMediaType.isEmpty()
+ && aMediaType != "application/vnd.sun.star.oleobject" )
+ {
+ css::datatransfer::DataFlavor aDataFlavor;
+ aDataFlavor.MimeType = aMediaType;
+ SotClipboardFormatId nFormat = SotExchange::GetFormat( aDataFlavor );
+
+ switch ( nFormat )
+ {
+ case SotClipboardFormatId::STARWRITER_60 :
+ case SotClipboardFormatId::STARWRITERWEB_60 :
+ case SotClipboardFormatId::STARWRITERGLOB_60 :
+ case SotClipboardFormatId::STARDRAW_60 :
+ case SotClipboardFormatId::STARIMPRESS_60 :
+ case SotClipboardFormatId::STARCALC_60 :
+ case SotClipboardFormatId::STARCHART_60 :
+ case SotClipboardFormatId::STARMATH_60 :
+ case SotClipboardFormatId::STARWRITER_8:
+ case SotClipboardFormatId::STARWRITERWEB_8:
+ case SotClipboardFormatId::STARWRITERGLOB_8:
+ case SotClipboardFormatId::STARDRAW_8:
+ case SotClipboardFormatId::STARIMPRESS_8:
+ case SotClipboardFormatId::STARCALC_8:
+ case SotClipboardFormatId::STARCHART_8:
+ case SotClipboardFormatId::STARMATH_8:
+ break;
+
+ default:
+ {
+ if ( !xTarget->hasByName( rSubElement ) )
+ return false;
+ }
+ }
+ }
+ }
+ }
+ }
+ catch( uno::Exception& )
+ {
+ SAL_WARN( "sfx.doc", "Can not check storage consistency!" );
+ }
+
+ return true;
+}
+
+bool SfxObjectShell::SwitchPersistence( const uno::Reference< embed::XStorage >& xStorage )
+{
+ bool bResult = false;
+ // check for wrong creation of object container
+ bool bHasContainer( pImpl->mxObjectContainer );
+ if ( xStorage.is() )
+ {
+ if ( pImpl->mxObjectContainer )
+ GetEmbeddedObjectContainer().SwitchPersistence( xStorage );
+ bResult = SwitchChildrenPersistence( xStorage );
+
+ // TODO/LATER: substorages that have unknown mimetypes probably should be copied to the target storage here
+ OSL_ENSURE( StoragesOfUnknownMediaTypeAreCopied_Impl( pImpl->m_xDocStorage, xStorage ),
+ "Some of substorages with unknown mimetypes is lost!" );
+ }
+
+ if ( bResult )
+ {
+ // make sure that until the storage is assigned the object container is not created by accident!
+ DBG_ASSERT( bHasContainer == (pImpl->mxObjectContainer != nullptr), "Wrong storage in object container!" );
+ if ( pImpl->m_xDocStorage != xStorage )
+ DoSaveCompleted( new SfxMedium( xStorage, GetMedium()->GetBaseURL() ) );
+
+ if ( IsEnableSetModified() )
+ SetModified(); // ???
+ }
+
+ return bResult;
+}
+
+bool SfxObjectShell::CopyStoragesOfUnknownMediaType(const uno::Reference< embed::XStorage >& xSource,
+ const uno::Reference< embed::XStorage >& xTarget,
+ const uno::Sequence<OUString>& rExceptions)
+{
+ // This method does not commit the target storage and should not do it
+ bool bResult = true;
+
+ try
+ {
+ const css::uno::Sequence<OUString> aSubElementNames = xSource->getElementNames();
+ for (const OUString& rSubElement : aSubElementNames)
+ {
+ if (std::find(rExceptions.begin(), rExceptions.end(), rSubElement) != rExceptions.end())
+ continue;
+
+ if (rSubElement == "Configurations")
+ {
+ // The workaround for compatibility with SO7, "Configurations" substorage must be preserved
+ if (xSource->isStorageElement(rSubElement))
+ {
+ OSL_ENSURE(!xTarget->hasByName(rSubElement), "The target storage is an output "
+ "storage, the element should not "
+ "exist in the target!");
+
+ xSource->copyElementTo(rSubElement, xTarget, rSubElement);
+ }
+ }
+ else if (xSource->isStorageElement(rSubElement))
+ {
+ OUString aMediaType;
+ static const OUStringLiteral aMediaTypePropName( u"MediaType" );
+ bool bGotMediaType = false;
+
+ try
+ {
+ uno::Reference< embed::XOptimizedStorage > xOptStorage( xSource, uno::UNO_QUERY_THROW );
+ bGotMediaType = (xOptStorage->getElementPropertyValue(rSubElement, aMediaTypePropName)
+ >>= aMediaType);
+ }
+ catch( uno::Exception& )
+ {}
+
+ if ( !bGotMediaType )
+ {
+ uno::Reference< embed::XStorage > xSubStorage;
+ try {
+ xSubStorage
+ = xSource->openStorageElement(rSubElement, embed::ElementModes::READ);
+ } catch( uno::Exception& )
+ {}
+
+ if ( !xSubStorage.is() )
+ {
+ // TODO/LATER: as optimization in future a substorage of target storage could be used
+ // instead of the temporary storage; this substorage should be removed later
+ // if the MimeType is wrong
+ xSubStorage = ::comphelper::OStorageHelper::GetTemporaryStorage();
+ xSource->copyStorageElementLastCommitTo(rSubElement, xSubStorage);
+ }
+
+ uno::Reference< beans::XPropertySet > xProps( xSubStorage, uno::UNO_QUERY_THROW );
+ xProps->getPropertyValue( aMediaTypePropName ) >>= aMediaType;
+ }
+
+ // TODO/LATER: there should be a way to detect whether an object with such a MediaType can exist
+ // probably it should be placed in the MimeType-ClassID table or in standalone table
+ if ( !aMediaType.isEmpty()
+ && aMediaType != "application/vnd.sun.star.oleobject" )
+ {
+ css::datatransfer::DataFlavor aDataFlavor;
+ aDataFlavor.MimeType = aMediaType;
+ SotClipboardFormatId nFormat = SotExchange::GetFormat( aDataFlavor );
+
+ switch ( nFormat )
+ {
+ case SotClipboardFormatId::STARWRITER_60 :
+ case SotClipboardFormatId::STARWRITERWEB_60 :
+ case SotClipboardFormatId::STARWRITERGLOB_60 :
+ case SotClipboardFormatId::STARDRAW_60 :
+ case SotClipboardFormatId::STARIMPRESS_60 :
+ case SotClipboardFormatId::STARCALC_60 :
+ case SotClipboardFormatId::STARCHART_60 :
+ case SotClipboardFormatId::STARMATH_60 :
+ case SotClipboardFormatId::STARWRITER_8:
+ case SotClipboardFormatId::STARWRITERWEB_8:
+ case SotClipboardFormatId::STARWRITERGLOB_8:
+ case SotClipboardFormatId::STARDRAW_8:
+ case SotClipboardFormatId::STARIMPRESS_8:
+ case SotClipboardFormatId::STARCALC_8:
+ case SotClipboardFormatId::STARCHART_8:
+ case SotClipboardFormatId::STARMATH_8:
+ break;
+
+ default:
+ {
+ OSL_ENSURE(rSubElement == "Configurations2"
+ || nFormat == SotClipboardFormatId::STARBASE_8
+ || !xTarget->hasByName(rSubElement),
+ "The target storage is an output storage, the element "
+ "should not exist in the target!");
+
+ if (!xTarget->hasByName(rSubElement))
+ {
+ xSource->copyElementTo(rSubElement, xTarget, rSubElement);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ catch( uno::Exception& )
+ {
+ bResult = false;
+ // TODO/LATER: a specific error could be provided
+ }
+
+ return bResult;
+}
+
+bool SfxObjectShell::GenerateAndStoreThumbnail(bool bEncrypted, const uno::Reference<embed::XStorage>& xStorage)
+{
+ //optimize thumbnail generate and store procedure to improve odt saving performance, i120030
+ bIsInGenerateThumbnail = true;
+
+ bool bResult = false;
+
+ try
+ {
+ uno::Reference<embed::XStorage> xThumbnailStorage = xStorage->openStorageElement("Thumbnails", embed::ElementModes::READWRITE);
+
+ if (xThumbnailStorage.is())
+ {
+ uno::Reference<io::XStream> xStream = xThumbnailStorage->openStreamElement("thumbnail.png", embed::ElementModes::READWRITE);
+
+ if (xStream.is() && WriteThumbnail(bEncrypted, xStream))
+ {
+ uno::Reference<embed::XTransactedObject> xTransactedObject(xThumbnailStorage, uno::UNO_QUERY_THROW);
+ xTransactedObject->commit();
+ bResult = true;
+ }
+ }
+ }
+ catch( uno::Exception& )
+ {
+ }
+
+ //optimize thumbnail generate and store procedure to improve odt saving performance, i120030
+ bIsInGenerateThumbnail = false;
+
+ return bResult;
+}
+
+bool SfxObjectShell::WriteThumbnail(bool bEncrypted, const uno::Reference<io::XStream>& xStream)
+{
+ bool bResult = false;
+
+ if (!xStream.is())
+ return false;
+
+ try
+ {
+ uno::Reference<io::XTruncate> xTruncate(xStream->getOutputStream(), uno::UNO_QUERY_THROW);
+ xTruncate->truncate();
+
+ uno::Reference <beans::XPropertySet> xSet(xStream, uno::UNO_QUERY);
+ if (xSet.is())
+ xSet->setPropertyValue("MediaType", uno::Any(OUString("image/png")));
+ if (bEncrypted)
+ {
+ const OUString sResID = GraphicHelper::getThumbnailReplacementIDByFactoryName_Impl(
+ GetFactory().GetFactoryName());
+ if (!sResID.isEmpty())
+ bResult = GraphicHelper::getThumbnailReplacement_Impl(sResID, xStream);
+ }
+ else
+ {
+ BitmapEx bitmap = GetPreviewBitmap();
+ if (!bitmap.IsEmpty())
+ {
+ bResult = GraphicHelper::getThumbnailFormatFromBitmap_Impl(bitmap, xStream);
+ }
+ }
+ }
+ catch(uno::Exception&)
+ {}
+
+ return bResult;
+}
+
+void SfxObjectShell::UpdateLinks()
+{
+}
+
+bool SfxObjectShell::LoadExternal( SfxMedium& )
+{
+ // Not implemented. It's an error if the code path ever comes here.
+ assert(false);
+ return false;
+}
+
+bool SfxObjectShell::InsertGeneratedStream(SfxMedium&,
+ uno::Reference<text::XTextRange> const&)
+{
+ // Not implemented. It's an error if the code path ever comes here.
+ assert(false);
+ return false;
+}
+
+bool SfxObjectShell::IsConfigOptionsChecked() const
+{
+ return pImpl->m_bConfigOptionsChecked;
+}
+
+void SfxObjectShell::SetConfigOptionsChecked( bool bChecked )
+{
+ pImpl->m_bConfigOptionsChecked = bChecked;
+}
+
+void SfxObjectShell::SetMacroCallsSeenWhileLoading()
+{
+ pImpl->m_bMacroCallsSeenWhileLoading = true;
+}
+
+bool SfxObjectShell::GetMacroCallsSeenWhileLoading() const
+{
+ if (officecfg::Office::Common::Security::Scripting::CheckDocumentEvents::get())
+ return pImpl->m_bMacroCallsSeenWhileLoading;
+ return false;
+}
+
+bool SfxObjectShell::QuerySaveSizeExceededModules_Impl( const uno::Reference< task::XInteractionHandler >& xHandler )
+{
+#if !HAVE_FEATURE_SCRIPTING
+ (void) xHandler;
+#else
+ if ( !HasBasic() )
+ return true;
+
+ if ( !pImpl->aBasicManager.isValid() )
+ GetBasicManager();
+ std::vector< OUString > sModules;
+ if ( xHandler.is() )
+ {
+ if( pImpl->aBasicManager.LegacyPsswdBinaryLimitExceeded( sModules ) )
+ {
+ rtl::Reference<ModuleSizeExceeded> pReq = new ModuleSizeExceeded( sModules );
+ xHandler->handle( pReq );
+ return pReq->isApprove();
+ }
+ }
+#endif
+ // No interaction handler, default is to continue to save
+ return true;
+}
+
+bool SfxObjectShell::QueryAllowExoticFormat_Impl( const uno::Reference< task::XInteractionHandler >& xHandler, const OUString& rURL, const OUString& rFilterUIName )
+{
+ if ( SvtSecurityOptions::isTrustedLocationUri( rURL ) )
+ {
+ // Always load from trusted location
+ return true;
+ }
+ if ( officecfg::Office::Common::Security::LoadExoticFileFormats::get() == 0 )
+ {
+ // Refuse loading without question
+ return false;
+ }
+ else if ( officecfg::Office::Common::Security::LoadExoticFileFormats::get() == 2 )
+ {
+ // Always load without question
+ return true;
+ }
+ else if ( officecfg::Office::Common::Security::LoadExoticFileFormats::get() == 1 && xHandler.is() )
+ {
+ // Display a warning and let the user decide
+ rtl::Reference<ExoticFileLoadException> xException(new ExoticFileLoadException( rURL, rFilterUIName ));
+ xHandler->handle( xException );
+ return xException->isApprove();
+ }
+ // No interaction handler, default is to continue to load
+ return true;
+}
+
+uno::Reference< task::XInteractionHandler > SfxObjectShell::getInteractionHandler() const
+{
+ uno::Reference< task::XInteractionHandler > xRet;
+ if ( GetMedium() )
+ xRet = GetMedium()->GetInteractionHandler();
+ return xRet;
+}
+
+OUString SfxObjectShell::getDocumentBaseURL() const
+{
+ return GetMedium()->GetBaseURL();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/doc/objstor.hxx b/sfx2/source/doc/objstor.hxx
new file mode 100644
index 000000000..4692dbf71
--- /dev/null
+++ b/sfx2/source/doc/objstor.hxx
@@ -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 .
+ */
+
+#ifndef INCLUDED_SFX2_SOURCE_DOC_OBJSTOR_HXX
+#define INCLUDED_SFX2_SOURCE_DOC_OBJSTOR_HXX
+
+#include <com/sun/star/frame/XModel.hpp>
+
+void impl_addToModelCollection(const css::uno::Reference<css::frame::XModel>& xModel);
+
+#endif // INCLUDED_SFX2_SOURCE_DOC_OBJSTOR_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/doc/objxtor.cxx b/sfx2/source/doc/objxtor.cxx
new file mode 100644
index 000000000..c83a446a8
--- /dev/null
+++ b/sfx2/source/doc/objxtor.cxx
@@ -0,0 +1,1118 @@
+/* -*- 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 <config_features.h>
+#include <config_fuzzers.h>
+
+#include <map>
+
+#include <cppuhelper/implbase.hxx>
+#include <cppuhelper/weakref.hxx>
+
+#include <com/sun/star/util/XCloseable.hpp>
+#include <com/sun/star/frame/XComponentLoader.hpp>
+#include <com/sun/star/frame/Desktop.hpp>
+#include <com/sun/star/util/XCloseListener.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/frame/XTitle.hpp>
+#include <osl/file.hxx>
+#include <sal/log.hxx>
+#include <vcl/weld.hxx>
+#include <vcl/svapp.hxx>
+#include <svl/eitem.hxx>
+#include <basic/sbstar.hxx>
+#include <svl/stritem.hxx>
+#include <unotools/configmgr.hxx>
+#include <unotools/eventcfg.hxx>
+
+#include <sfx2/objsh.hxx>
+#include <sfx2/signaturestate.hxx>
+#include <sfx2/sfxmodelfactory.hxx>
+
+#include <comphelper/processfactory.hxx>
+#include <comphelper/servicehelper.hxx>
+
+#include <com/sun/star/document/XStorageBasedDocument.hpp>
+#include <com/sun/star/script/DocumentDialogLibraryContainer.hpp>
+#include <com/sun/star/script/DocumentScriptLibraryContainer.hpp>
+#include <com/sun/star/document/XEmbeddedScripts.hpp>
+#include <com/sun/star/document/XScriptInvocationContext.hpp>
+#include <com/sun/star/ucb/ContentCreationException.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+
+#include <unotools/ucbhelper.hxx>
+#include <tools/diagnose_ex.h>
+#include <tools/globname.hxx>
+#include <tools/debug.hxx>
+
+#include <sfx2/app.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/docfile.hxx>
+#include <sfx2/event.hxx>
+#include <sfx2/viewsh.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/sfxresid.hxx>
+#include <objshimp.hxx>
+#include <sfx2/strings.hrc>
+#include <sfx2/sfxsids.hrc>
+#include <basic/basmgr.hxx>
+#include <sfx2/QuerySaveDocument.hxx>
+#include <appbaslib.hxx>
+#include <sfx2/sfxbasemodel.hxx>
+#include <sfx2/sfxuno.hxx>
+#include <sfx2/notebookbar/SfxNotebookBar.hxx>
+#include <sfx2/infobar.hxx>
+
+#include <basic/basicmanagerrepository.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::script;
+using namespace ::com::sun::star::frame;
+using namespace ::com::sun::star::document;
+
+using ::basic::BasicManagerRepository;
+
+namespace {
+
+WeakReference< XInterface > theCurrentComponent;
+
+#if HAVE_FEATURE_SCRIPTING
+
+// remember all registered components for VBA compatibility, to be able to remove them on disposing the model
+typedef ::std::map< XInterface*, OUString > VBAConstantNameMap;
+VBAConstantNameMap s_aRegisteredVBAConstants;
+
+OUString lclGetVBAGlobalConstName( const Reference< XInterface >& rxComponent )
+{
+ OSL_ENSURE( rxComponent.is(), "lclGetVBAGlobalConstName - missing component" );
+
+ VBAConstantNameMap::iterator aIt = s_aRegisteredVBAConstants.find( rxComponent.get() );
+ if( aIt != s_aRegisteredVBAConstants.end() )
+ return aIt->second;
+
+ uno::Reference< beans::XPropertySet > xProps( rxComponent, uno::UNO_QUERY );
+ if( xProps.is() ) try
+ {
+ OUString aConstName;
+ xProps->getPropertyValue("VBAGlobalConstantName") >>= aConstName;
+ return aConstName;
+ }
+ catch (const uno::Exception&) // not supported
+ {
+ }
+ return OUString();
+}
+
+#endif
+
+class SfxModelListener_Impl : public ::cppu::WeakImplHelper< css::util::XCloseListener >
+{
+ SfxObjectShell* mpDoc;
+public:
+ explicit SfxModelListener_Impl( SfxObjectShell* pDoc ) : mpDoc(pDoc) {};
+ virtual void SAL_CALL queryClosing( const css::lang::EventObject& aEvent, sal_Bool bDeliverOwnership ) override ;
+ virtual void SAL_CALL notifyClosing( const css::lang::EventObject& aEvent ) override ;
+ virtual void SAL_CALL disposing( const css::lang::EventObject& aEvent ) override ;
+
+};
+
+} // namespace
+
+void SAL_CALL SfxModelListener_Impl::queryClosing( const css::lang::EventObject& , sal_Bool )
+{
+}
+
+void SAL_CALL SfxModelListener_Impl::notifyClosing( const css::lang::EventObject& )
+{
+ SolarMutexGuard aSolarGuard;
+ mpDoc->Broadcast( SfxHint(SfxHintId::Deinitializing) );
+}
+
+void SAL_CALL SfxModelListener_Impl::disposing( const css::lang::EventObject& _rEvent )
+{
+ // am I ThisComponent in AppBasic?
+ SolarMutexGuard aSolarGuard;
+ if ( SfxObjectShell::GetCurrentComponent() == _rEvent.Source )
+ {
+ // remove ThisComponent reference from AppBasic
+ SfxObjectShell::SetCurrentComponent( Reference< XInterface >() );
+ }
+
+#if HAVE_FEATURE_SCRIPTING
+ /* Remove VBA component from AppBasic. As every application registers its
+ own current component, the disposed component may not be the "current
+ component" of the SfxObjectShell. */
+ if ( _rEvent.Source.is() )
+ {
+ VBAConstantNameMap::iterator aIt = s_aRegisteredVBAConstants.find( _rEvent.Source.get() );
+ if ( aIt != s_aRegisteredVBAConstants.end() )
+ {
+ if ( BasicManager* pAppMgr = SfxApplication::GetBasicManager() )
+ pAppMgr->SetGlobalUNOConstant( aIt->second, Any( Reference< XInterface >() ) );
+ s_aRegisteredVBAConstants.erase( aIt );
+ }
+ }
+#endif
+
+ if ( !mpDoc->Get_Impl()->bClosing )
+ // GCC crashes when already in the destructor, so first query the Flag
+ mpDoc->DoClose();
+}
+
+
+SfxObjectShell_Impl::SfxObjectShell_Impl( SfxObjectShell& _rDocShell )
+ :rDocShell( _rDocShell )
+ ,aMacroMode( *this )
+ ,pProgress( nullptr)
+ ,nTime( DateTime::SYSTEM )
+ ,nVisualDocumentNumber( USHRT_MAX)
+ ,nDocumentSignatureState( SignatureState::UNKNOWN )
+ ,nScriptingSignatureState( SignatureState::UNKNOWN )
+ ,bClosing( false)
+ ,bIsSaving( false)
+ ,bIsNamedVisible( false)
+ ,bIsAbortingImport ( false)
+ ,bInPrepareClose( false )
+ ,bPreparedForClose( false )
+ ,bForbidReload( false )
+ ,bBasicInitialized( false )
+ ,bIsPrintJobCancelable( true )
+ ,bOwnsStorage( true )
+ ,bInitialized( false )
+ ,bModelInitialized( false )
+ ,bPreserveVersions( true )
+ ,m_bMacroSignBroken( false )
+ ,m_bNoBasicCapabilities( false )
+ ,m_bDocRecoverySupport( true )
+ ,bQueryLoadTemplate( true )
+ ,bLoadReadonly( false )
+ ,bUseUserData( true )
+ ,bUseThumbnailSave( true )
+ ,bSaveVersionOnClose( false )
+ ,m_bSharedXMLFlag( false )
+ ,m_bAllowShareControlFileClean( true )
+ ,m_bConfigOptionsChecked( false )
+ ,m_bMacroCallsSeenWhileLoading( false )
+ ,lErr(ERRCODE_NONE)
+ ,nEventId ( SfxEventHintId::NONE )
+ ,nLoadedFlags ( SfxLoadedFlags::ALL )
+ ,nFlagsInProgress( SfxLoadedFlags::NONE )
+ ,bModalMode( false )
+ ,bRunningMacro( false )
+ ,bReadOnlyUI( false )
+ ,nStyleFilter( 0 )
+ ,m_bEnableSetModified( true )
+ ,m_bIsModified( false )
+ ,m_nMapUnit( MapUnit::Map100thMM )
+ ,m_bCreateTempStor( false )
+ ,m_bIsInit( false )
+ ,m_bIncomplEncrWarnShown( false )
+ ,m_nModifyPasswordHash( 0 )
+ ,m_bModifyPasswordEntered( false )
+ ,m_bSavingForSigning( false )
+ ,m_bAllowModifiedBackAfterSigning( false )
+{
+ SfxObjectShell* pDoc = &_rDocShell;
+ std::vector<SfxObjectShell*> &rArr = SfxGetpApp()->GetObjectShells_Impl();
+ rArr.push_back( pDoc );
+}
+
+
+SfxObjectShell_Impl::~SfxObjectShell_Impl()
+{
+}
+
+
+SfxObjectShell::SfxObjectShell( const SfxModelFlags i_nCreationFlags )
+ : pImpl(new SfxObjectShell_Impl(*this))
+ , pMedium(nullptr)
+ , eCreateMode(SfxObjectCreateMode::STANDARD)
+ , bHasName(false)
+ , bIsInGenerateThumbnail (false)
+ , mbAvoidRecentDocs(false)
+{
+ if (i_nCreationFlags & SfxModelFlags::EMBEDDED_OBJECT)
+ eCreateMode = SfxObjectCreateMode::EMBEDDED;
+ else if (i_nCreationFlags & SfxModelFlags::EXTERNAL_LINK)
+ eCreateMode = SfxObjectCreateMode::INTERNAL;
+
+ const bool bScriptSupport = ( i_nCreationFlags & SfxModelFlags::DISABLE_EMBEDDED_SCRIPTS ) == SfxModelFlags::NONE;
+ if ( !bScriptSupport )
+ pImpl->m_bNoBasicCapabilities = true;
+
+ const bool bDocRecovery = ( i_nCreationFlags & SfxModelFlags::DISABLE_DOCUMENT_RECOVERY ) == SfxModelFlags::NONE;
+ if ( !bDocRecovery )
+ pImpl->m_bDocRecoverySupport = false;
+}
+
+/** Constructor of the class SfxObjectShell.
+
+ @param eMode Purpose, to which the SfxObjectShell is created:
+ SfxObjectCreateMode::EMBEDDED (default) as SO-Server from within another Document
+ SfxObjectCreateMode::STANDARD, as a normal Document open stand-alone
+ SfxObjectCreateMode::ORGANIZER to be displayed in the Organizer, here nothing of the contents is used
+*/
+SfxObjectShell::SfxObjectShell(SfxObjectCreateMode eMode)
+ : pImpl(new SfxObjectShell_Impl(*this))
+ , pMedium(nullptr)
+ , eCreateMode(eMode)
+ , bHasName(false)
+ , bIsInGenerateThumbnail(false)
+ , mbAvoidRecentDocs(false)
+{
+}
+
+SfxObjectShell::~SfxObjectShell()
+{
+
+ if ( IsEnableSetModified() )
+ EnableSetModified( false );
+
+ SfxObjectShell::CloseInternal();
+ pImpl->pBaseModel.set( nullptr );
+
+ pImpl->pReloadTimer.reset();
+
+ SfxApplication *pSfxApp = SfxGetpApp();
+ if ( USHRT_MAX != pImpl->nVisualDocumentNumber && pSfxApp )
+ pSfxApp->ReleaseIndex(pImpl->nVisualDocumentNumber);
+
+ // Destroy Basic-Manager
+ pImpl->aBasicManager.reset(nullptr);
+
+ if ( pSfxApp && pSfxApp->GetDdeService() )
+ pSfxApp->RemoveDdeTopic( this );
+
+ pImpl->pBaseModel.set( nullptr );
+
+ // don't call GetStorage() here, in case of Load Failure it's possible that a storage was never assigned!
+ if ( pMedium && pMedium->HasStorage_Impl() && pMedium->GetStorage( false ) == pImpl->m_xDocStorage )
+ pMedium->CanDisposeStorage_Impl( false );
+
+ if ( pImpl->mxObjectContainer )
+ {
+ pImpl->mxObjectContainer->CloseEmbeddedObjects();
+ pImpl->mxObjectContainer.reset();
+ }
+
+ if ( pImpl->bOwnsStorage && pImpl->m_xDocStorage.is() )
+ pImpl->m_xDocStorage->dispose();
+
+ if ( pMedium )
+ {
+ pMedium->CloseAndReleaseStreams_Impl();
+
+#if HAVE_FEATURE_MULTIUSER_ENVIRONMENT
+ if (IsDocShared())
+ FreeSharedFile( pMedium->GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
+#endif
+ delete pMedium;
+ pMedium = nullptr;
+ }
+
+ // The removing of the temporary file must be done as the latest step in the document destruction
+ if ( !pImpl->aTempName.isEmpty() )
+ {
+ OUString aTmp;
+ osl::FileBase::getFileURLFromSystemPath( pImpl->aTempName, aTmp );
+ ::utl::UCBContentHelper::Kill( aTmp );
+ }
+}
+
+
+void SfxObjectShell::Stamp_SetPrintCancelState(bool bState)
+{
+ pImpl->bIsPrintJobCancelable = bState;
+}
+
+
+bool SfxObjectShell::Stamp_GetPrintCancelState() const
+{
+ return pImpl->bIsPrintJobCancelable;
+}
+
+
+// closes the Object and all its views
+
+bool SfxObjectShell::Close()
+{
+ SfxObjectShellRef xKeepAlive(this);
+ return CloseInternal();
+}
+
+// variant that does not take a reference to itself, so we can call it during object destruction
+bool SfxObjectShell::CloseInternal()
+{
+ if ( !pImpl->bClosing )
+ {
+ // Do not close if a progress is still running
+ if ( GetProgress() )
+ return false;
+
+ pImpl->bClosing = true;
+ Reference< util::XCloseable > xCloseable( GetBaseModel(), UNO_QUERY );
+
+ if ( xCloseable.is() )
+ {
+ try
+ {
+ xCloseable->close( true );
+ }
+ catch (const Exception&)
+ {
+ pImpl->bClosing = false;
+ }
+ }
+
+ if ( pImpl->bClosing )
+ {
+ // remove from Document list
+ // If there is no App, there is no document to remove
+ // no need to call GetOrCreate here
+ SfxApplication *pSfxApp = SfxApplication::Get();
+ if(pSfxApp)
+ {
+ std::vector<SfxObjectShell*> &rDocs = pSfxApp->GetObjectShells_Impl();
+ auto it = std::find( rDocs.begin(), rDocs.end(), this );
+ if ( it != rDocs.end() )
+ rDocs.erase( it );
+ }
+ }
+ }
+
+ return true;
+}
+
+OUString SfxObjectShell::CreateShellID( const SfxObjectShell* pShell )
+{
+ if (!pShell)
+ return OUString();
+
+ OUString aShellID;
+
+ SfxMedium* pMedium = pShell->GetMedium();
+ if (pMedium)
+ aShellID = pMedium->GetBaseURL();
+
+ if (!aShellID.isEmpty())
+ return aShellID;
+
+ sal_Int64 nShellID = reinterpret_cast<sal_Int64>(pShell);
+ aShellID = "0x" + OUString::number(nShellID, 16);
+ return aShellID;
+}
+
+// returns a pointer the first SfxDocument of specified type
+
+SfxObjectShell* SfxObjectShell::GetFirst
+(
+ const std::function<bool ( const SfxObjectShell* )>& isObjectShell,
+ bool bOnlyVisible
+)
+{
+ std::vector<SfxObjectShell*> &rDocs = SfxGetpApp()->GetObjectShells_Impl();
+
+ // search for a SfxDocument of the specified type
+ for (SfxObjectShell* pSh : rDocs)
+ {
+ if ( bOnlyVisible && pSh->IsPreview() && pSh->IsReadOnly() )
+ continue;
+
+ if ( (!isObjectShell || isObjectShell( pSh)) &&
+ ( !bOnlyVisible || SfxViewFrame::GetFirst( pSh )))
+ return pSh;
+ }
+
+ return nullptr;
+}
+
+
+// returns a pointer to the next SfxDocument of specified type behind *pDoc
+
+SfxObjectShell* SfxObjectShell::GetNext
+(
+ const SfxObjectShell& rPrev,
+ const std::function<bool ( const SfxObjectShell* )>& isObjectShell,
+ bool bOnlyVisible
+)
+{
+ std::vector<SfxObjectShell*> &rDocs = SfxGetpApp()->GetObjectShells_Impl();
+
+ // refind the specified predecessor
+ size_t nPos;
+ for ( nPos = 0; nPos < rDocs.size(); ++nPos )
+ if ( rDocs[nPos] == &rPrev )
+ break;
+
+ // search for the next SfxDocument of the specified type
+ for ( ++nPos; nPos < rDocs.size(); ++nPos )
+ {
+ SfxObjectShell* pSh = rDocs[ nPos ];
+ if ( bOnlyVisible && pSh->IsPreview() && pSh->IsReadOnly() )
+ continue;
+
+ if ( (!isObjectShell || isObjectShell( pSh)) &&
+ ( !bOnlyVisible || SfxViewFrame::GetFirst( pSh )))
+ return pSh;
+ }
+ return nullptr;
+}
+
+
+SfxObjectShell* SfxObjectShell::Current()
+{
+ SfxViewFrame *pFrame = SfxViewFrame::Current();
+ return pFrame ? pFrame->GetObjectShell() : nullptr;
+}
+
+
+bool SfxObjectShell::IsInPrepareClose() const
+{
+ return pImpl->bInPrepareClose;
+}
+
+namespace {
+
+struct BoolEnv_Impl
+{
+ SfxObjectShell_Impl& rImpl;
+ explicit BoolEnv_Impl( SfxObjectShell_Impl& rImplP) : rImpl( rImplP )
+ { rImplP.bInPrepareClose = true; }
+ ~BoolEnv_Impl() { rImpl.bInPrepareClose = false; }
+};
+
+}
+
+bool SfxObjectShell::PrepareClose
+(
+ bool bUI // true: Dialog and so on is allowed
+ // false: silent-mode
+)
+{
+ if( pImpl->bInPrepareClose || pImpl->bPreparedForClose )
+ return true;
+ BoolEnv_Impl aBoolEnv( *pImpl );
+
+ // DocModalDialog?
+ if ( IsInModalMode() )
+ return false;
+
+ SfxViewFrame* pFirst = SfxViewFrame::GetFirst( this );
+ if( pFirst && !pFirst->GetFrame().PrepareClose_Impl( bUI ) )
+ return false;
+
+ // prepare views for closing
+ for ( SfxViewFrame* pFrm = SfxViewFrame::GetFirst( this );
+ pFrm; pFrm = SfxViewFrame::GetNext( *pFrm, this ) )
+ {
+ DBG_ASSERT(pFrm->GetViewShell(),"No Shell");
+ if ( pFrm->GetViewShell() )
+ {
+ bool bRet = pFrm->GetViewShell()->PrepareClose( bUI );
+ if ( !bRet )
+ return bRet;
+ }
+ }
+
+ SfxApplication *pSfxApp = SfxGetpApp();
+ pSfxApp->NotifyEvent( SfxEventHint(SfxEventHintId::PrepareCloseDoc, GlobalEventConfig::GetEventName(GlobalEventId::PREPARECLOSEDOC), this) );
+
+ if( GetCreateMode() == SfxObjectCreateMode::EMBEDDED )
+ {
+ pImpl->bPreparedForClose = true;
+ return true;
+ }
+
+ // Ask if possible if it should be saved
+ // only ask for the Document in the visible window
+ SfxViewFrame *pFrame = SfxObjectShell::Current() == this
+ ? SfxViewFrame::Current() : SfxViewFrame::GetFirst( this );
+
+ if ( bUI && IsModified() && pFrame )
+ {
+ // restore minimized
+ SfxFrame& rTop = pFrame->GetFrame();
+ SfxViewFrame::SetViewFrame( rTop.GetCurrentViewFrame() );
+ pFrame->GetFrame().Appear();
+
+ // Ask if to save
+ short nRet = RET_YES;
+ {
+ const Reference<XTitle> xTitle(*pImpl->pBaseModel, UNO_QUERY_THROW);
+ const OUString sTitle = xTitle->getTitle ();
+ nRet = ExecuteQuerySaveDocument(pFrame->GetFrameWeld(), sTitle);
+ }
+ /*HACK for plugin::destroy()*/
+
+ if ( RET_YES == nRet )
+ {
+ // Save by each Dispatcher
+ const SfxPoolItem *pPoolItem;
+ if ( IsSaveVersionOnClose() )
+ {
+ SfxStringItem aItem( SID_DOCINFO_COMMENTS, SfxResId(STR_AUTOMATICVERSION) );
+ SfxBoolItem aWarnItem( SID_FAIL_ON_WARNING, bUI );
+ const SfxPoolItem* ppArgs[] = { &aItem, &aWarnItem, nullptr };
+ pPoolItem = pFrame->GetBindings().ExecuteSynchron( SID_SAVEDOC, ppArgs );
+ }
+ else
+ {
+ SfxBoolItem aWarnItem( SID_FAIL_ON_WARNING, bUI );
+ const SfxPoolItem* ppArgs[] = { &aWarnItem, nullptr };
+ pPoolItem = pFrame->GetBindings().ExecuteSynchron( SID_SAVEDOC, ppArgs );
+ }
+
+ if ( !pPoolItem || pPoolItem->IsVoidItem() )
+ return false;
+ if ( auto pBoolItem = dynamic_cast< const SfxBoolItem *>( pPoolItem ) )
+ if ( !pBoolItem->GetValue() )
+ return false;
+ }
+ else if ( RET_CANCEL == nRet )
+ // Cancelled
+ return false;
+ }
+
+ if ( pFrame )
+ sfx2::SfxNotebookBar::CloseMethod(pFrame->GetBindings());
+ pImpl->bPreparedForClose = true;
+ return true;
+}
+
+
+#if HAVE_FEATURE_SCRIPTING
+namespace
+{
+ BasicManager* lcl_getBasicManagerForDocument( const SfxObjectShell& _rDocument )
+ {
+ if ( !_rDocument.Get_Impl()->m_bNoBasicCapabilities )
+ {
+ if ( !_rDocument.Get_Impl()->bBasicInitialized )
+ const_cast< SfxObjectShell& >( _rDocument ).InitBasicManager_Impl();
+ return _rDocument.Get_Impl()->aBasicManager.get();
+ }
+
+ // assume we do not have Basic ourself, but we can refer to another
+ // document which does (by our model's XScriptInvocationContext::getScriptContainer).
+ // In this case, we return the BasicManager of this other document.
+
+ OSL_ENSURE( !Reference< XEmbeddedScripts >( _rDocument.GetModel(), UNO_QUERY ).is(),
+ "lcl_getBasicManagerForDocument: inconsistency: no Basic, but an XEmbeddedScripts?" );
+ Reference< XModel > xForeignDocument;
+ Reference< XScriptInvocationContext > xContext( _rDocument.GetModel(), UNO_QUERY );
+ if ( xContext.is() )
+ {
+ xForeignDocument.set( xContext->getScriptContainer(), UNO_QUERY );
+ OSL_ENSURE( xForeignDocument.is() && xForeignDocument != _rDocument.GetModel(),
+ "lcl_getBasicManagerForDocument: no Basic, but providing ourself as script container?" );
+ }
+
+ BasicManager* pBasMgr = nullptr;
+ if ( xForeignDocument.is() )
+ pBasMgr = ::basic::BasicManagerRepository::getDocumentBasicManager( xForeignDocument );
+
+ return pBasMgr;
+ }
+}
+#endif
+
+BasicManager* SfxObjectShell::GetBasicManager() const
+{
+ BasicManager* pBasMgr = nullptr;
+#if HAVE_FEATURE_SCRIPTING
+ try
+ {
+ pBasMgr = lcl_getBasicManagerForDocument( *this );
+ if ( !pBasMgr )
+ pBasMgr = SfxApplication::GetBasicManager();
+ }
+ catch (const css::ucb::ContentCreationException&)
+ {
+ TOOLS_WARN_EXCEPTION("sfx.doc", "");
+ }
+#endif
+ return pBasMgr;
+}
+
+bool SfxObjectShell::HasBasic() const
+{
+#if !HAVE_FEATURE_SCRIPTING
+ return false;
+#else
+ if ( pImpl->m_bNoBasicCapabilities )
+ return false;
+
+ if ( !pImpl->bBasicInitialized )
+ const_cast< SfxObjectShell* >( this )->InitBasicManager_Impl();
+
+ return pImpl->aBasicManager.isValid();
+#endif
+}
+
+
+#if HAVE_FEATURE_SCRIPTING
+namespace
+{
+ const Reference< XLibraryContainer >&
+ lcl_getOrCreateLibraryContainer( bool _bScript, Reference< XLibraryContainer >& _rxContainer,
+ const Reference< XModel >& _rxDocument )
+ {
+ if ( !_rxContainer.is() )
+ {
+ try
+ {
+ Reference< XStorageBasedDocument > xStorageDoc( _rxDocument, UNO_QUERY );
+ const Reference< XComponentContext > xContext(
+ ::comphelper::getProcessComponentContext() );
+ _rxContainer.set ( _bScript
+ ? DocumentScriptLibraryContainer::create(
+ xContext, xStorageDoc )
+ : DocumentDialogLibraryContainer::create(
+ xContext, xStorageDoc )
+ , UNO_QUERY_THROW );
+ }
+ catch (const Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("sfx.doc");
+ }
+ }
+ return _rxContainer;
+ }
+}
+#endif
+
+Reference< XLibraryContainer > SfxObjectShell::GetDialogContainer()
+{
+#if HAVE_FEATURE_SCRIPTING
+ try
+ {
+ if ( !pImpl->m_bNoBasicCapabilities )
+ return lcl_getOrCreateLibraryContainer( false, pImpl->xDialogLibraries, GetModel() );
+
+ BasicManager* pBasMgr = lcl_getBasicManagerForDocument( *this );
+ if ( pBasMgr )
+ return pBasMgr->GetDialogLibraryContainer();
+ }
+ catch (const css::ucb::ContentCreationException&)
+ {
+ TOOLS_WARN_EXCEPTION("sfx.doc", "");
+ }
+
+ SAL_WARN("sfx.doc", "SfxObjectShell::GetDialogContainer: falling back to the application - is this really expected here?");
+#endif
+ return SfxGetpApp()->GetDialogContainer();
+}
+
+Reference< XLibraryContainer > SfxObjectShell::GetBasicContainer()
+{
+#if HAVE_FEATURE_SCRIPTING
+ if (!utl::ConfigManager::IsFuzzing())
+ {
+ try
+ {
+ if ( !pImpl->m_bNoBasicCapabilities )
+ return lcl_getOrCreateLibraryContainer( true, pImpl->xBasicLibraries, GetModel() );
+
+ BasicManager* pBasMgr = lcl_getBasicManagerForDocument( *this );
+ if ( pBasMgr )
+ return pBasMgr->GetScriptLibraryContainer();
+ }
+ catch (const css::ucb::ContentCreationException&)
+ {
+ TOOLS_WARN_EXCEPTION("sfx.doc", "");
+ }
+ }
+ SAL_WARN("sfx.doc", "SfxObjectShell::GetBasicContainer: falling back to the application - is this really expected here?");
+#endif
+ return SfxGetpApp()->GetBasicContainer();
+}
+
+StarBASIC* SfxObjectShell::GetBasic() const
+{
+#if !HAVE_FEATURE_SCRIPTING
+ return nullptr;
+#else
+ BasicManager * pMan = GetBasicManager();
+ return pMan ? pMan->GetLib(0) : nullptr;
+#endif
+}
+
+void SfxObjectShell::InitBasicManager_Impl()
+/* [Description]
+
+ Creates a document's BasicManager and loads it, if we are already based on
+ a storage.
+
+ [Note]
+
+ This method has to be called by implementations of <SvPersist::Load()>
+ (with its pStor parameter) and by implementations of <SvPersist::InitNew()>
+ (with pStor = 0).
+*/
+
+{
+ /* #163556# (DR) - Handling of recursive calls while creating the Basic
+ manager.
+
+ It is possible that (while creating the Basic manager) the code that
+ imports the Basic storage wants to access the Basic manager again.
+ Especially in VBA compatibility mode, there is code that wants to
+ access the "VBA Globals" object which is stored as global UNO constant
+ in the Basic manager.
+
+ To achieve correct handling of the recursive calls of this function
+ from lcl_getBasicManagerForDocument(), the implementation of the
+ function BasicManagerRepository::getDocumentBasicManager() has been
+ changed to return the Basic manager currently under construction, when
+ called repeatedly.
+
+ The variable pImpl->bBasicInitialized will be set to sal_True after
+ construction now, to ensure that the recursive call of the function
+ lcl_getBasicManagerForDocument() will be routed into this function too.
+
+ Calling BasicManagerHolder::reset() twice is not a big problem, as it
+ does not take ownership but stores only the raw pointer. Owner of all
+ Basic managers is the global BasicManagerRepository instance.
+ */
+#if HAVE_FEATURE_SCRIPTING
+ DBG_ASSERT( !pImpl->bBasicInitialized && !pImpl->aBasicManager.isValid(), "Local BasicManager already exists");
+ try
+ {
+ pImpl->aBasicManager.reset( BasicManagerRepository::getDocumentBasicManager( GetModel() ) );
+ }
+ catch (const css::ucb::ContentCreationException&)
+ {
+ TOOLS_WARN_EXCEPTION("sfx.doc", "");
+ }
+ DBG_ASSERT( pImpl->aBasicManager.isValid(), "SfxObjectShell::InitBasicManager_Impl: did not get a BasicManager!" );
+ pImpl->bBasicInitialized = true;
+#endif
+}
+
+
+bool SfxObjectShell::DoClose()
+{
+ return Close();
+}
+
+
+SfxObjectShell* SfxObjectShell::GetObjectShell()
+{
+ return this;
+}
+
+
+uno::Sequence< OUString > SfxObjectShell::GetEventNames()
+{
+ static uno::Sequence< OUString > s_EventNameContainer(rtl::Reference<GlobalEventConfig>(new GlobalEventConfig)->getElementNames());
+
+ return s_EventNameContainer;
+}
+
+
+css::uno::Reference< css::frame::XModel3 > SfxObjectShell::GetModel() const
+{
+ return GetBaseModel();
+}
+
+void SfxObjectShell::SetBaseModel( SfxBaseModel* pModel )
+{
+ OSL_ENSURE( !pImpl->pBaseModel.is() || pModel == nullptr, "Model already set!" );
+ pImpl->pBaseModel.set( pModel );
+ if ( pImpl->pBaseModel.is() )
+ {
+ pImpl->pBaseModel->addCloseListener( new SfxModelListener_Impl(this) );
+ }
+}
+
+
+css::uno::Reference< css::frame::XModel3 > SfxObjectShell::GetBaseModel() const
+{
+ return pImpl->pBaseModel;
+}
+
+void SfxObjectShell::SetAutoStyleFilterIndex(sal_uInt16 nSet)
+{
+ pImpl->nStyleFilter = nSet;
+}
+
+sal_uInt16 SfxObjectShell::GetAutoStyleFilterIndex() const
+{
+ return pImpl->nStyleFilter;
+}
+
+
+void SfxObjectShell::SetCurrentComponent( const Reference< XInterface >& _rxComponent )
+{
+ WeakReference< XInterface >& rTheCurrentComponent = theCurrentComponent;
+
+ Reference< XInterface > xOldCurrentComp(rTheCurrentComponent);
+ if ( _rxComponent == xOldCurrentComp )
+ // nothing to do
+ return;
+ // note that "_rxComponent.get() == s_xCurrentComponent.get().get()" is /sufficient/, but not
+ // /required/ for "_rxComponent == s_xCurrentComponent.get()".
+ // In other words, it's still possible that we here do something which is not necessary,
+ // but we should have filtered quite some unnecessary calls already.
+
+#if HAVE_FEATURE_SCRIPTING
+ BasicManager* pAppMgr = SfxApplication::GetBasicManager();
+ rTheCurrentComponent = _rxComponent;
+ if ( !pAppMgr )
+ return;
+
+ // set "ThisComponent" for Basic
+ pAppMgr->SetGlobalUNOConstant( "ThisComponent", Any( _rxComponent ) );
+
+ // set new current component for VBA compatibility
+ if ( _rxComponent.is() )
+ {
+ OUString aVBAConstName = lclGetVBAGlobalConstName( _rxComponent );
+ if ( !aVBAConstName.isEmpty() )
+ {
+ pAppMgr->SetGlobalUNOConstant( aVBAConstName, Any( _rxComponent ) );
+ s_aRegisteredVBAConstants[ _rxComponent.get() ] = aVBAConstName;
+ }
+ }
+ // no new component passed -> remove last registered VBA component
+ else if ( xOldCurrentComp.is() )
+ {
+ OUString aVBAConstName = lclGetVBAGlobalConstName( xOldCurrentComp );
+ if ( !aVBAConstName.isEmpty() )
+ {
+ pAppMgr->SetGlobalUNOConstant( aVBAConstName, Any( Reference< XInterface >() ) );
+ s_aRegisteredVBAConstants.erase( xOldCurrentComp.get() );
+ }
+ }
+#endif
+}
+
+Reference< XInterface > SfxObjectShell::GetCurrentComponent()
+{
+ return theCurrentComponent;
+}
+
+
+OUString SfxObjectShell::GetServiceNameFromFactory( const OUString& rFact )
+{
+ //! Remove everything behind name!
+ OUString aFact( rFact );
+ OUString aPrefix("private:factory/");
+ if ( aFact.startsWith( aPrefix ) )
+ aFact = aFact.copy( aPrefix.getLength() );
+ sal_Int32 nPos = aFact.indexOf( '?' );
+ if ( nPos != -1 )
+ {
+ aFact = aFact.copy( 0, nPos );
+ }
+ aFact = aFact.replaceAll("4", "");
+ aFact = aFact.toAsciiLowerCase();
+
+ // HACK: sometimes a real document service name is given here instead of
+ // a factory short name. Set return value directly to this service name as fallback
+ // in case next lines of code does nothing ...
+ // use rFact instead of normed aFact value !
+ OUString aServiceName = rFact;
+
+ if ( aFact == "swriter" )
+ {
+ aServiceName = "com.sun.star.text.TextDocument";
+ }
+ else if ( aFact == "sweb" || aFact == "swriter/web" )
+ {
+ aServiceName = "com.sun.star.text.WebDocument";
+ }
+ else if ( aFact == "sglobal" || aFact == "swriter/globaldocument" )
+ {
+ aServiceName = "com.sun.star.text.GlobalDocument";
+ }
+ else if ( aFact == "scalc" )
+ {
+ aServiceName = "com.sun.star.sheet.SpreadsheetDocument";
+ }
+ else if ( aFact == "sdraw" )
+ {
+ aServiceName = "com.sun.star.drawing.DrawingDocument";
+ }
+ else if ( aFact == "simpress" )
+ {
+ aServiceName = "com.sun.star.presentation.PresentationDocument";
+ }
+ else if ( aFact == "schart" )
+ {
+ aServiceName = "com.sun.star.chart.ChartDocument";
+ }
+ else if ( aFact == "smath" )
+ {
+ aServiceName = "com.sun.star.formula.FormulaProperties";
+ }
+#if HAVE_FEATURE_SCRIPTING
+ else if ( aFact == "sbasic" )
+ {
+ aServiceName = "com.sun.star.script.BasicIDE";
+ }
+#endif
+#if HAVE_FEATURE_DBCONNECTIVITY && !ENABLE_FUZZERS
+ else if ( aFact == "sdatabase" )
+ {
+ aServiceName = "com.sun.star.sdb.OfficeDatabaseDocument";
+ }
+#endif
+
+ return aServiceName;
+}
+
+SfxObjectShell* SfxObjectShell::CreateObjectByFactoryName( const OUString& rFact, SfxObjectCreateMode eMode )
+{
+ return CreateObject( GetServiceNameFromFactory( rFact ), eMode );
+}
+
+
+SfxObjectShell* SfxObjectShell::CreateObject( const OUString& rServiceName, SfxObjectCreateMode eCreateMode )
+{
+ if ( !rServiceName.isEmpty() )
+ {
+ uno::Reference < frame::XModel > xDoc( ::comphelper::getProcessServiceFactory()->createInstance( rServiceName ), UNO_QUERY );
+ if (SfxObjectShell* pRet = SfxObjectShell::GetShellFromComponent(xDoc))
+ {
+ pRet->SetCreateMode_Impl(eCreateMode);
+ return pRet;
+ }
+ }
+
+ return nullptr;
+}
+
+Reference<lang::XComponent> SfxObjectShell::CreateAndLoadComponent( const SfxItemSet& rSet )
+{
+ uno::Sequence < beans::PropertyValue > aProps;
+ TransformItems( SID_OPENDOC, rSet, aProps );
+ const SfxStringItem* pFileNameItem = rSet.GetItem<SfxStringItem>(SID_FILE_NAME, false);
+ const SfxStringItem* pTargetItem = rSet.GetItem<SfxStringItem>(SID_TARGETNAME, false);
+ OUString aURL;
+ OUString aTarget("_blank");
+ if ( pFileNameItem )
+ aURL = pFileNameItem->GetValue();
+ if ( pTargetItem )
+ aTarget = pTargetItem->GetValue();
+
+ uno::Reference < frame::XComponentLoader > xLoader =
+ frame::Desktop::create(comphelper::getProcessComponentContext());
+
+ Reference <lang::XComponent> xComp;
+ try
+ {
+ xComp = xLoader->loadComponentFromURL(aURL, aTarget, 0, aProps);
+ }
+ catch (const uno::Exception&)
+ {
+ }
+
+ return xComp;
+}
+
+SfxObjectShell* SfxObjectShell::GetShellFromComponent(const Reference<uno::XInterface>& xComp)
+{
+ try
+ {
+ Reference<lang::XUnoTunnel> xTunnel(xComp, UNO_QUERY);
+ if (!xTunnel)
+ return nullptr;
+ static const Sequence <sal_Int8> aSeq( SvGlobalName( SFX_GLOBAL_CLASSID ).GetByteSequence() );
+ return comphelper::getSomething_cast<SfxObjectShell>(xTunnel->getSomething(aSeq));
+ }
+ catch (const Exception&)
+ {
+ }
+
+ return nullptr;
+}
+
+SfxObjectShell* SfxObjectShell::GetParentShell(const css::uno::Reference<css::uno::XInterface>& xChild)
+{
+ SfxObjectShell* pResult = nullptr;
+
+ try
+ {
+ if (css::uno::Reference<css::container::XChild> xChildModel{ xChild, css::uno::UNO_QUERY })
+ pResult = GetShellFromComponent(xChildModel->getParent());
+ }
+ catch (const Exception&)
+ {
+ }
+
+ return pResult;
+}
+
+void SfxObjectShell::SetInitialized_Impl( const bool i_fromInitNew )
+{
+ pImpl->bInitialized = true;
+ if (utl::ConfigManager::IsFuzzing())
+ return;
+ if ( i_fromInitNew )
+ {
+ SetActivateEvent_Impl( SfxEventHintId::CreateDoc );
+ SfxGetpApp()->NotifyEvent( SfxEventHint( SfxEventHintId::DocCreated, GlobalEventConfig::GetEventName(GlobalEventId::DOCCREATED), this ) );
+ }
+ else
+ {
+ SfxGetpApp()->NotifyEvent( SfxEventHint( SfxEventHintId::LoadFinished, GlobalEventConfig::GetEventName(GlobalEventId::LOADFINISHED), this ) );
+ }
+}
+
+
+bool SfxObjectShell::IsChangeRecording() const
+{
+ // currently this function needs to be overwritten by Writer and Calc only
+ SAL_WARN( "sfx.doc", "function not implemented" );
+ return false;
+}
+
+
+bool SfxObjectShell::HasChangeRecordProtection() const
+{
+ // currently this function needs to be overwritten by Writer and Calc only
+ SAL_WARN( "sfx.doc", "function not implemented" );
+ return false;
+}
+
+
+void SfxObjectShell::SetChangeRecording( bool /*bActivate*/, bool /*bLockAllViews*/ )
+{
+ // currently this function needs to be overwritten by Writer and Calc only
+ SAL_WARN( "sfx.doc", "function not implemented" );
+}
+
+
+void SfxObjectShell::SetProtectionPassword( const OUString & /*rPassword*/ )
+{
+ // currently this function needs to be overwritten by Writer and Calc only
+ SAL_WARN( "sfx.doc", "function not implemented" );
+}
+
+
+bool SfxObjectShell::GetProtectionHash( /*out*/ css::uno::Sequence< sal_Int8 > & /*rPasswordHash*/ )
+{
+ // currently this function needs to be overwritten by Writer and Calc only
+ SAL_WARN( "sfx.doc", "function not implemented" );
+ return false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/doc/oleprops.cxx b/sfx2/source/doc/oleprops.cxx
new file mode 100644
index 000000000..6de4aace5
--- /dev/null
+++ b/sfx2/source/doc/oleprops.cxx
@@ -0,0 +1,1240 @@
+/* -*- 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 "oleprops.hxx"
+
+#include <comphelper/types.hxx>
+#include <o3tl/safeint.hxx>
+#include <tools/datetime.hxx>
+#include <rtl/tencinfo.h>
+#include <sal/log.hxx>
+
+
+#define STREAM_BUFFER_SIZE 2048
+
+// usings
+using ::com::sun::star::uno::Any;
+
+using namespace ::com::sun::star;
+
+#define TIMESTAMP_INVALID_DATETIME ( DateTime ( Date ( 1, 1, 1601 ), tools::Time ( 0, 0, 0 ) ) ) /// Invalid value for date and time to create invalid instance of TimeStamp.
+/// Invalid value for date and time to create invalid instance of TimeStamp.
+#define TIMESTAMP_INVALID_UTILDATETIME (util::DateTime(0, 0, 0, 0, 1, 1, 1601, false))
+/// Invalid value for date to create invalid instance of TimeStamp.
+#define TIMESTAMP_INVALID_UTILDATE (util::Date(1, 1, 1601))
+
+namespace {
+
+/** Property representing a signed 32-bit integer value. */
+class SfxOleInt32Property : public SfxOlePropertyBase
+{
+public:
+ explicit SfxOleInt32Property( sal_Int32 nPropId, sal_Int32 nValue = 0 );
+
+ sal_Int32 GetValue() const { return mnValue; }
+
+private:
+ virtual void ImplLoad( SvStream& rStrm ) override;
+ virtual void ImplSave( SvStream& rStrm ) override;
+
+private:
+ sal_Int32 mnValue;
+};
+
+
+/** Property representing a floating-point value. */
+class SfxOleDoubleProperty : public SfxOlePropertyBase
+{
+public:
+ explicit SfxOleDoubleProperty( sal_Int32 nPropId, double fValue = 0.0 );
+
+ double GetValue() const { return mfValue; }
+
+private:
+ virtual void ImplLoad( SvStream& rStrm ) override;
+ virtual void ImplSave( SvStream& rStrm ) override;
+
+private:
+ double mfValue;
+};
+
+
+/** Property representing a boolean value. */
+class SfxOleBoolProperty : public SfxOlePropertyBase
+{
+public:
+ explicit SfxOleBoolProperty( sal_Int32 nPropId, bool bValue = false );
+
+ bool GetValue() const { return mbValue; }
+
+private:
+ virtual void ImplLoad( SvStream& rStrm ) override;
+ virtual void ImplSave( SvStream& rStrm ) override;
+
+private:
+ bool mbValue;
+};
+
+
+/** Base class for properties that contain a single string value. */
+class SfxOleStringPropertyBase : public SfxOlePropertyBase, public SfxOleStringHelper
+{
+public:
+ explicit SfxOleStringPropertyBase(
+ sal_Int32 nPropId, sal_Int32 nPropType,
+ const SfxOleTextEncoding& rTextEnc );
+ explicit SfxOleStringPropertyBase(
+ sal_Int32 nPropId, sal_Int32 nPropType,
+ const SfxOleTextEncoding& rTextEnc, const OUString& rValue );
+ explicit SfxOleStringPropertyBase(
+ sal_Int32 nPropId, sal_Int32 nPropType,
+ rtl_TextEncoding eTextEnc );
+
+ const OUString& GetValue() const { return maValue; }
+ void SetValue( const OUString& rValue ) { maValue = rValue; }
+
+private:
+ OUString maValue;
+};
+
+
+/** Property representing a bytestring value. */
+class SfxOleString8Property : public SfxOleStringPropertyBase
+{
+public:
+ explicit SfxOleString8Property(
+ sal_Int32 nPropId, const SfxOleTextEncoding& rTextEnc );
+ explicit SfxOleString8Property(
+ sal_Int32 nPropId, const SfxOleTextEncoding& rTextEnc,
+ const OUString& rValue );
+
+private:
+ virtual void ImplLoad( SvStream& rStrm ) override;
+ virtual void ImplSave( SvStream& rStrm ) override;
+};
+
+
+/** Property representing a Unicode string value. */
+class SfxOleString16Property : public SfxOleStringPropertyBase
+{
+public:
+ explicit SfxOleString16Property( sal_Int32 nPropId );
+
+private:
+ virtual void ImplLoad( SvStream& rStrm ) override;
+ virtual void ImplSave( SvStream& rStrm ) override;
+};
+
+
+/** Property representing a filetime value as defined by the Windows API. */
+class SfxOleFileTimeProperty : public SfxOlePropertyBase
+{
+public:
+ explicit SfxOleFileTimeProperty( sal_Int32 nPropId );
+ /** @param rDateTime Date and time as LOCAL time. */
+ explicit SfxOleFileTimeProperty( sal_Int32 nPropId, const util::DateTime& rDateTime );
+
+ /** Returns the time value as LOCAL time. */
+ const util::DateTime& GetValue() const { return maDateTime; }
+
+private:
+ virtual void ImplLoad( SvStream& rStrm ) override;
+ virtual void ImplSave( SvStream& rStrm ) override;
+
+private:
+ util::DateTime maDateTime;
+};
+
+/** Property representing a filetime value as defined by the Windows API. */
+class SfxOleDateProperty : public SfxOlePropertyBase
+{
+public:
+ explicit SfxOleDateProperty( sal_Int32 nPropId );
+
+ /** Returns the date value as LOCAL time. */
+ const util::Date& GetValue() const { return maDate; }
+
+private:
+ virtual void ImplLoad( SvStream& rStrm ) override;
+ virtual void ImplSave( SvStream& rStrm ) override;
+
+private:
+ util::Date maDate;
+};
+
+
+/** Property representing a thumbnail picture.
+
+ Currently, only saving this property is implemented.
+ */
+class SfxOleThumbnailProperty : public SfxOlePropertyBase
+{
+public:
+ explicit SfxOleThumbnailProperty( sal_Int32 nPropId,
+ const uno::Sequence<sal_Int8> & i_rData);
+
+ bool IsValid() const { return mData.hasElements(); }
+
+private:
+ virtual void ImplLoad( SvStream& rStrm ) override;
+ virtual void ImplSave( SvStream& rStrm ) override;
+
+private:
+ uno::Sequence<sal_Int8> mData;
+};
+
+
+/** Property representing a BLOB (which presumably stands for binary large
+ object).
+
+ Currently, only saving this property is implemented.
+ */
+class SfxOleBlobProperty : public SfxOlePropertyBase
+{
+public:
+ explicit SfxOleBlobProperty( sal_Int32 nPropId,
+ const uno::Sequence<sal_Int8> & i_rData);
+ bool IsValid() const { return mData.hasElements(); }
+
+private:
+ virtual void ImplLoad( SvStream& rStrm ) override;
+ virtual void ImplSave( SvStream& rStrm ) override;
+
+private:
+ uno::Sequence<sal_Int8> mData;
+};
+
+}
+
+sal_uInt16 SfxOleTextEncoding::GetCodePage() const
+{
+ sal_uInt16 nCodePage = IsUnicode() ? CODEPAGE_UNICODE :
+ static_cast< sal_uInt16 >( rtl_getWindowsCodePageFromTextEncoding( *mxTextEnc ) );
+ return (nCodePage == CODEPAGE_UNKNOWN) ? CODEPAGE_UTF8 : nCodePage;
+}
+
+void SfxOleTextEncoding::SetCodePage( sal_uInt16 nCodePage )
+{
+ if( nCodePage == CODEPAGE_UNICODE )
+ SetUnicode();
+ else
+ {
+ rtl_TextEncoding eTextEnc = rtl_getTextEncodingFromWindowsCodePage( nCodePage );
+ if( eTextEnc != RTL_TEXTENCODING_DONTKNOW )
+ *mxTextEnc = eTextEnc;
+ }
+}
+
+
+OUString SfxOleStringHelper::LoadString8( SvStream& rStrm ) const
+{
+ return IsUnicode() ? ImplLoadString16( rStrm ) : ImplLoadString8( rStrm );
+}
+
+void SfxOleStringHelper::SaveString8( SvStream& rStrm, const OUString& rValue ) const
+{
+ if( IsUnicode() )
+ ImplSaveString16( rStrm, rValue );
+ else
+ ImplSaveString8( rStrm, rValue );
+}
+
+OUString SfxOleStringHelper::LoadString16( SvStream& rStrm )
+{
+ return ImplLoadString16( rStrm );
+}
+
+void SfxOleStringHelper::SaveString16( SvStream& rStrm, const OUString& rValue )
+{
+ ImplSaveString16( rStrm, rValue );
+}
+
+OUString SfxOleStringHelper::ImplLoadString8( SvStream& rStrm ) const
+{
+ // read size field (signed 32-bit)
+ sal_Int32 nSize(0);
+ rStrm.ReadInt32( nSize );
+ // size field includes trailing NUL character
+ SAL_WARN_IF(nSize < 1 || nSize > 0xFFFF, "sfx.doc", "SfxOleStringHelper::ImplLoadString8 - invalid string of len " << nSize);
+ if (nSize < 1 || nSize > 0xFFFF)
+ return OUString();
+ // load character buffer
+ OString sValue(read_uInt8s_ToOString(rStrm, nSize - 1));
+ if (rStrm.good() && rStrm.remainingSize())
+ rStrm.SeekRel(1); // skip null-byte at end
+ return OStringToOUString(sValue, GetTextEncoding());
+}
+
+OUString SfxOleStringHelper::ImplLoadString16( SvStream& rStrm )
+{
+ // read size field (signed 32-bit), may be buffer size or character count
+ sal_Int32 nSize(0);
+ rStrm.ReadInt32(nSize);
+ SAL_WARN_IF(nSize < 1 || nSize > 0xFFFF, "sfx.doc", "SfxOleStringHelper::ImplLoadString16 - invalid string of len " << nSize);
+ // size field includes trailing NUL character
+ if (nSize < 1 || nSize > 0xFFFF)
+ return OUString();
+ // load character buffer
+ OUString aValue = read_uInt16s_ToOUString(rStrm, nSize - 1);
+ sal_Int32 nSkip(2); // skip null-byte at end
+ // stream is always padded to 32-bit boundary, skip 2 bytes on odd character count
+ if ((nSize & 1) == 1)
+ nSkip += 2;
+ nSkip = std::min<sal_uInt32>(nSkip, rStrm.remainingSize());
+ if (rStrm.good() && nSkip)
+ rStrm.SeekRel(nSkip);
+ return aValue;
+}
+
+void SfxOleStringHelper::ImplSaveString8( SvStream& rStrm, std::u16string_view rValue ) const
+{
+ // encode to byte string
+ OString aEncoded(OUStringToOString(rValue, GetTextEncoding()));
+ // write size field (including trailing NUL character)
+ sal_Int32 nSize = aEncoded.getLength() + 1;
+ rStrm.WriteInt32( nSize );
+ // write character array with trailing NUL character
+ rStrm.WriteBytes(aEncoded.getStr(), aEncoded.getLength());
+ rStrm.WriteUChar( 0 );
+}
+
+void SfxOleStringHelper::ImplSaveString16( SvStream& rStrm, const OUString& rValue )
+{
+ // write size field (including trailing NUL character)
+ sal_Int32 nSize = static_cast< sal_Int32 >( rValue.getLength() + 1 );
+ rStrm.WriteInt32( nSize );
+ // write character array with trailing NUL character
+ for( sal_Int32 nIdx = 0; nIdx < rValue.getLength(); ++nIdx )
+ rStrm.WriteUInt16( rValue[ nIdx ] );
+ rStrm.WriteUInt16( 0 );
+ // stream is always padded to 32-bit boundary, add 2 bytes on odd character count
+ if( (nSize & 1) == 1 )
+ rStrm.WriteUInt16( 0 );
+}
+
+
+SfxOleObjectBase::~SfxOleObjectBase()
+{
+}
+
+ErrCode const & SfxOleObjectBase::Load( SvStream& rStrm )
+{
+ mnErrCode = ERRCODE_NONE;
+ ImplLoad( rStrm );
+ SetError( rStrm.GetErrorCode() );
+ return GetError();
+}
+
+ErrCode const & SfxOleObjectBase::Save( SvStream& rStrm )
+{
+ mnErrCode = ERRCODE_NONE;
+ ImplSave( rStrm );
+ SetError( rStrm.GetErrorCode() );
+ return GetError();
+}
+
+void SfxOleObjectBase::LoadObject( SvStream& rStrm, SfxOleObjectBase& rObj )
+{
+ SetError( rObj.Load( rStrm ) );
+}
+
+void SfxOleObjectBase::SaveObject( SvStream& rStrm, SfxOleObjectBase& rObj )
+{
+ SetError( rObj.Save( rStrm ) );
+}
+
+
+SfxOleCodePageProperty::SfxOleCodePageProperty() :
+ SfxOlePropertyBase( PROPID_CODEPAGE, PROPTYPE_INT16 )
+{
+}
+
+void SfxOleCodePageProperty::ImplLoad(SvStream& rStrm)
+{
+ // property type is signed int16, but we use always unsigned int16 for codepages
+ sal_uInt16 nCodePage(0);
+ rStrm.ReadUInt16(nCodePage);
+ SetCodePage(nCodePage);
+}
+
+void SfxOleCodePageProperty::ImplSave( SvStream& rStrm )
+{
+ // property type is signed int16, but we use always unsigned int16 for codepages
+ rStrm.WriteUInt16( GetCodePage() );
+}
+
+
+SfxOleInt32Property::SfxOleInt32Property( sal_Int32 nPropId, sal_Int32 nValue ) :
+ SfxOlePropertyBase( nPropId, PROPTYPE_INT32 ),
+ mnValue( nValue )
+{
+}
+
+void SfxOleInt32Property::ImplLoad( SvStream& rStrm )
+{
+ rStrm.ReadInt32( mnValue );
+}
+
+void SfxOleInt32Property::ImplSave( SvStream& rStrm )
+{
+ rStrm.WriteInt32( mnValue );
+}
+
+
+SfxOleDoubleProperty::SfxOleDoubleProperty( sal_Int32 nPropId, double fValue ) :
+ SfxOlePropertyBase( nPropId, PROPTYPE_DOUBLE ),
+ mfValue( fValue )
+{
+}
+
+void SfxOleDoubleProperty::ImplLoad( SvStream& rStrm )
+{
+ rStrm.ReadDouble( mfValue );
+}
+
+void SfxOleDoubleProperty::ImplSave( SvStream& rStrm )
+{
+ rStrm.WriteDouble( mfValue );
+}
+
+
+SfxOleBoolProperty::SfxOleBoolProperty( sal_Int32 nPropId, bool bValue ) :
+ SfxOlePropertyBase( nPropId, PROPTYPE_BOOL ),
+ mbValue( bValue )
+{
+}
+
+void SfxOleBoolProperty::ImplLoad( SvStream& rStrm )
+{
+ sal_Int16 nValue(0);
+ rStrm.ReadInt16( nValue );
+ mbValue = nValue != 0;
+}
+
+void SfxOleBoolProperty::ImplSave( SvStream& rStrm )
+{
+ rStrm.WriteInt16( mbValue ? -1 : 0 );
+}
+
+
+SfxOleStringPropertyBase::SfxOleStringPropertyBase(
+ sal_Int32 nPropId, sal_Int32 nPropType, const SfxOleTextEncoding& rTextEnc ) :
+ SfxOlePropertyBase( nPropId, nPropType ),
+ SfxOleStringHelper( rTextEnc )
+{
+}
+
+SfxOleStringPropertyBase::SfxOleStringPropertyBase(
+ sal_Int32 nPropId, sal_Int32 nPropType, const SfxOleTextEncoding& rTextEnc, const OUString& rValue ) :
+ SfxOlePropertyBase( nPropId, nPropType ),
+ SfxOleStringHelper( rTextEnc ),
+ maValue( rValue )
+{
+}
+
+SfxOleStringPropertyBase::SfxOleStringPropertyBase(
+ sal_Int32 nPropId, sal_Int32 nPropType, rtl_TextEncoding eTextEnc ) :
+ SfxOlePropertyBase( nPropId, nPropType ),
+ SfxOleStringHelper( eTextEnc )
+{
+}
+
+
+SfxOleString8Property::SfxOleString8Property(
+ sal_Int32 nPropId, const SfxOleTextEncoding& rTextEnc ) :
+ SfxOleStringPropertyBase( nPropId, PROPTYPE_STRING8, rTextEnc )
+{
+}
+
+SfxOleString8Property::SfxOleString8Property(
+ sal_Int32 nPropId, const SfxOleTextEncoding& rTextEnc, const OUString& rValue ) :
+ SfxOleStringPropertyBase( nPropId, PROPTYPE_STRING8, rTextEnc, rValue )
+{
+}
+
+void SfxOleString8Property::ImplLoad( SvStream& rStrm )
+{
+ SetValue( LoadString8( rStrm ) );
+}
+
+void SfxOleString8Property::ImplSave( SvStream& rStrm )
+{
+ SaveString8( rStrm, GetValue() );
+}
+
+
+SfxOleString16Property::SfxOleString16Property( sal_Int32 nPropId ) :
+ SfxOleStringPropertyBase( nPropId, PROPTYPE_STRING16, RTL_TEXTENCODING_UCS2 )
+{
+}
+
+void SfxOleString16Property::ImplLoad( SvStream& rStrm )
+{
+ SetValue( LoadString16( rStrm ) );
+}
+
+void SfxOleString16Property::ImplSave( SvStream& rStrm )
+{
+ SaveString16( rStrm, GetValue() );
+}
+
+
+SfxOleFileTimeProperty::SfxOleFileTimeProperty( sal_Int32 nPropId ) :
+ SfxOlePropertyBase( nPropId, PROPTYPE_FILETIME )
+{
+}
+
+SfxOleFileTimeProperty::SfxOleFileTimeProperty( sal_Int32 nPropId, const util::DateTime& rDateTime ) :
+ SfxOlePropertyBase( nPropId, PROPTYPE_FILETIME ),
+ maDateTime( rDateTime )
+{
+}
+
+void SfxOleFileTimeProperty::ImplLoad( SvStream& rStrm )
+{
+ sal_uInt32 nLower(0), nUpper(0);
+ rStrm.ReadUInt32( nLower ).ReadUInt32( nUpper );
+ ::DateTime aDateTime = DateTime::CreateFromWin32FileDateTime( nLower, nUpper );
+ // note: editing duration is stored as offset to TIMESTAMP_INVALID_DATETIME
+ // of course we should not convert the time zone of a duration!
+ // heuristic to detect editing durations (which we assume to be < 1 year):
+ // check only the year, not the entire date
+ if ( aDateTime.GetYear() != TIMESTAMP_INVALID_DATETIME.GetYear() )
+ aDateTime.ConvertToLocalTime();
+ maDateTime.Year = aDateTime.GetYear();
+ maDateTime.Month = aDateTime.GetMonth();
+ maDateTime.Day = aDateTime.GetDay();
+ maDateTime.Hours = aDateTime.GetHour();
+ maDateTime.Minutes = aDateTime.GetMin();
+ maDateTime.Seconds = aDateTime.GetSec();
+ maDateTime.NanoSeconds = aDateTime.GetNanoSec();
+ maDateTime.IsUTC = false;
+}
+
+void SfxOleFileTimeProperty::ImplSave( SvStream& rStrm )
+{
+ DateTime aDateTimeUtc(
+ Date(
+ maDateTime.Day,
+ maDateTime.Month,
+ static_cast< sal_uInt16 >( maDateTime.Year ) ),
+ tools::Time(
+ maDateTime.Hours,
+ maDateTime.Minutes,
+ maDateTime.Seconds,
+ maDateTime.NanoSeconds ) );
+ // invalid time stamp is not converted to UTC
+ // heuristic to detect editing durations (which we assume to be < 1 year):
+ // check only the year, not the entire date
+ if( aDateTimeUtc.IsValidAndGregorian()
+ && aDateTimeUtc.GetYear() != TIMESTAMP_INVALID_DATETIME.GetYear() ) {
+ aDateTimeUtc.ConvertToUTC();
+ }
+ sal_uInt32 nLower, nUpper;
+ aDateTimeUtc.GetWin32FileDateTime( nLower, nUpper );
+ rStrm.WriteUInt32( nLower ).WriteUInt32( nUpper );
+}
+
+SfxOleDateProperty::SfxOleDateProperty( sal_Int32 nPropId ) :
+ SfxOlePropertyBase( nPropId, PROPTYPE_DATE )
+{
+}
+
+void SfxOleDateProperty::ImplLoad( SvStream& rStrm )
+{
+ double fValue(0.0);
+ rStrm.ReadDouble( fValue );
+ //stored as number of days (not seconds) since December 31, 1899
+ sal_Int32 nDays = fValue;
+ sal_Int32 nStartDays = ::Date::DateToDays(31, 12, 1899);
+ if (o3tl::checked_add(nStartDays, nDays, nStartDays))
+ SAL_WARN("sfx.doc", "SfxOleDateProperty::ImplLoad bad date, ignored");
+ else
+ {
+ ::Date aDate(31, 12, 1899);
+ aDate.AddDays(nDays);
+ maDate.Day = aDate.GetDay();
+ maDate.Month = aDate.GetMonth();
+ maDate.Year = aDate.GetYear();
+ }
+}
+
+void SfxOleDateProperty::ImplSave( SvStream& rStrm )
+{
+ sal_Int32 nDays = ::Date::DateToDays(maDate.Day, maDate.Month, maDate.Year);
+ //number of days (not seconds) since December 31, 1899
+ sal_Int32 nStartDays = ::Date::DateToDays(31, 12, 1899);
+ double fValue = nDays-nStartDays;
+ rStrm.WriteDouble( fValue );
+}
+
+
+SfxOleThumbnailProperty::SfxOleThumbnailProperty(
+ sal_Int32 nPropId, const uno::Sequence<sal_Int8> & i_rData) :
+ SfxOlePropertyBase( nPropId, PROPTYPE_CLIPFMT ),
+ mData(i_rData)
+{
+}
+
+void SfxOleThumbnailProperty::ImplLoad( SvStream& )
+{
+ SAL_WARN( "sfx.doc", "SfxOleThumbnailProperty::ImplLoad - not implemented" );
+ SetError( SVSTREAM_INVALID_ACCESS );
+}
+
+void SfxOleThumbnailProperty::ImplSave( SvStream& rStrm )
+{
+ /* Type Contents
+ -----------------------------------------------------------------------
+ int32 size of following data
+ int32 clipboard format tag (see below)
+ byte[] clipboard data (see below)
+
+ Clipboard format tag:
+ -1 = Windows clipboard format
+ -2 = Macintosh clipboard format
+ -3 = GUID that contains a format identifier (FMTID)
+ >0 = custom clipboard format name plus data (see msdn site below)
+ 0 = no data
+
+ References:
+ http://msdn.microsoft.com/library/default.asp?url=/library/en-us/stg/stg/propvariant.asp
+ http://jakarta.apache.org/poi/hpsf/thumbnails.html
+ http://linux.com.hk/docs/poi/org/apache/poi/hpsf/Thumbnail.html
+ https://web.archive.org/web/20060126202945/http://sparks.discreet.com/knowledgebase/public/solutions/ExtractThumbnailImg.htm
+ */
+ if( IsValid() )
+ {
+ // clipboard size: clip_format_tag + data_format_tag + bitmap_len
+ sal_Int32 nClipSize = static_cast< sal_Int32 >( 4 + 4 + mData.getLength() );
+ rStrm.WriteInt32( nClipSize ).WriteInt32( CLIPFMT_WIN ).WriteInt32( CLIPDATAFMT_DIB );
+ rStrm.WriteBytes(mData.getConstArray(), mData.getLength());
+ }
+ else
+ {
+ SAL_WARN( "sfx.doc", "SfxOleThumbnailProperty::ImplSave - invalid thumbnail property" );
+ SetError( SVSTREAM_INVALID_ACCESS );
+ }
+}
+
+
+SfxOleBlobProperty::SfxOleBlobProperty( sal_Int32 nPropId,
+ const uno::Sequence<sal_Int8> & i_rData) :
+ SfxOlePropertyBase( nPropId, PROPTYPE_BLOB ),
+ mData(i_rData)
+{
+}
+
+void SfxOleBlobProperty::ImplLoad( SvStream& )
+{
+ SAL_WARN( "sfx.doc", "SfxOleBlobProperty::ImplLoad - not implemented" );
+ SetError( SVSTREAM_INVALID_ACCESS );
+}
+
+void SfxOleBlobProperty::ImplSave( SvStream& rStrm )
+{
+ if (IsValid()) {
+ rStrm.WriteBytes(mData.getConstArray(), mData.getLength());
+ } else {
+ SAL_WARN( "sfx.doc", "SfxOleBlobProperty::ImplSave - invalid BLOB property" );
+ SetError( SVSTREAM_INVALID_ACCESS );
+ }
+}
+
+
+SfxOleDictionaryProperty::SfxOleDictionaryProperty( const SfxOleTextEncoding& rTextEnc ) :
+ SfxOlePropertyBase( PROPID_DICTIONARY, 0 ),
+ SfxOleStringHelper( rTextEnc )
+{
+}
+
+OUString SfxOleDictionaryProperty::GetPropertyName( sal_Int32 nPropId ) const
+{
+ SfxOlePropNameMap::const_iterator aIt = maPropNameMap.find( nPropId );
+ return (aIt == maPropNameMap.end()) ? OUString() : aIt->second;
+}
+
+void SfxOleDictionaryProperty::SetPropertyName( sal_Int32 nPropId, const OUString& rPropName )
+{
+ maPropNameMap[ nPropId ] = rPropName;
+ // dictionary property contains number of pairs in property type field
+ SetPropType( static_cast< sal_Int32 >( maPropNameMap.size() ) );
+}
+
+void SfxOleDictionaryProperty::ImplLoad( SvStream& rStrm )
+{
+ // dictionary property contains number of pairs in property type field
+ sal_Int32 nNameCount = GetPropType();
+ // read property ID/name pairs
+ maPropNameMap.clear();
+ for (sal_Int32 nIdx = 0; nIdx < nNameCount && rStrm.good() && rStrm.remainingSize() >= 4; ++nIdx)
+ {
+ sal_Int32 nPropId(0);
+ rStrm.ReadInt32(nPropId);
+ // name always stored as byte string
+ maPropNameMap[nPropId] = LoadString8(rStrm);
+ }
+}
+
+void SfxOleDictionaryProperty::ImplSave( SvStream& rStrm )
+{
+ // write property ID/name pairs
+ for (auto const& propName : maPropNameMap)
+ {
+ rStrm.WriteInt32( propName.first );
+ // name always stored as byte string
+ SaveString8( rStrm, propName.second );
+ }
+}
+
+
+SfxOleSection::SfxOleSection( bool bSupportsDict ) :
+ maDictProp( maCodePageProp ),
+ mnStartPos( 0 ),
+ mbSupportsDict( bSupportsDict )
+{
+}
+
+SfxOlePropertyRef SfxOleSection::GetProperty( sal_Int32 nPropId ) const
+{
+ SfxOlePropertyRef xProp;
+ SfxOlePropMap::const_iterator aIt = maPropMap.find( nPropId );
+ if( aIt != maPropMap.end() )
+ xProp = aIt->second;
+ return xProp;
+}
+
+bool SfxOleSection::GetInt32Value( sal_Int32& rnValue, sal_Int32 nPropId ) const
+{
+ SfxOlePropertyRef xProp = GetProperty( nPropId );
+ const SfxOleInt32Property* pProp =
+ dynamic_cast< const SfxOleInt32Property* >( xProp.get() );
+ if( pProp )
+ rnValue = pProp->GetValue();
+ return pProp != nullptr;
+}
+
+bool SfxOleSection::GetDoubleValue( double& rfValue, sal_Int32 nPropId ) const
+{
+ SfxOlePropertyRef xProp = GetProperty( nPropId );
+ const SfxOleDoubleProperty* pProp =
+ dynamic_cast< const SfxOleDoubleProperty* >( xProp.get() );
+ if( pProp )
+ rfValue = pProp->GetValue();
+ return pProp != nullptr;
+}
+
+bool SfxOleSection::GetBoolValue( bool& rbValue, sal_Int32 nPropId ) const
+{
+ SfxOlePropertyRef xProp = GetProperty( nPropId );
+ const SfxOleBoolProperty* pProp =
+ dynamic_cast< const SfxOleBoolProperty* >( xProp.get() );
+ if( pProp )
+ rbValue = pProp->GetValue();
+ return pProp != nullptr;
+}
+
+bool SfxOleSection::GetStringValue( OUString& rValue, sal_Int32 nPropId ) const
+{
+ SfxOlePropertyRef xProp = GetProperty( nPropId );
+ const SfxOleStringPropertyBase* pProp =
+ dynamic_cast< const SfxOleStringPropertyBase* >( xProp.get() );
+ if( pProp )
+ rValue = pProp->GetValue();
+ return pProp != nullptr;
+}
+
+bool SfxOleSection::GetFileTimeValue( util::DateTime& rValue, sal_Int32 nPropId ) const
+{
+ SfxOlePropertyRef xProp = GetProperty( nPropId );
+ const SfxOleFileTimeProperty* pProp =
+ dynamic_cast< const SfxOleFileTimeProperty* >( xProp.get() );
+ if( pProp )
+ {
+ if ( pProp->GetValue() == TIMESTAMP_INVALID_UTILDATETIME )
+ rValue = util::DateTime();
+ else
+ rValue = pProp->GetValue();
+ }
+ return pProp != nullptr;
+}
+
+bool SfxOleSection::GetDateValue( util::Date& rValue, sal_Int32 nPropId ) const
+{
+ SfxOlePropertyRef xProp = GetProperty( nPropId );
+ const SfxOleDateProperty* pProp =
+ dynamic_cast< const SfxOleDateProperty* >( xProp.get() );
+ if( pProp )
+ {
+ if ( pProp->GetValue() == TIMESTAMP_INVALID_UTILDATE )
+ rValue = util::Date();
+ else
+ rValue = pProp->GetValue();
+ }
+ return pProp != nullptr;
+}
+
+void SfxOleSection::SetProperty( const SfxOlePropertyRef& xProp )
+{
+ if( xProp )
+ maPropMap[ xProp->GetPropId() ] = xProp;
+}
+
+void SfxOleSection::SetInt32Value( sal_Int32 nPropId, sal_Int32 nValue )
+{
+ SetProperty( std::make_shared<SfxOleInt32Property>( nPropId, nValue ) );
+}
+
+void SfxOleSection::SetDoubleValue( sal_Int32 nPropId, double fValue )
+{
+ SetProperty( std::make_shared<SfxOleDoubleProperty>( nPropId, fValue ) );
+}
+
+void SfxOleSection::SetBoolValue( sal_Int32 nPropId, bool bValue )
+{
+ SetProperty( std::make_shared<SfxOleBoolProperty>( nPropId, bValue ) );
+}
+
+bool SfxOleSection::SetStringValue( sal_Int32 nPropId, const OUString& rValue )
+{
+ bool bInserted = !rValue.isEmpty();
+ if( bInserted )
+ SetProperty( std::make_shared<SfxOleString8Property>( nPropId, maCodePageProp, rValue ) );
+ return bInserted;
+}
+
+void SfxOleSection::SetFileTimeValue( sal_Int32 nPropId, const util::DateTime& rValue )
+{
+ if ( rValue.Year == 0 || rValue.Month == 0 || rValue.Day == 0 )
+ SetProperty( std::make_shared<SfxOleFileTimeProperty>( nPropId, TIMESTAMP_INVALID_UTILDATETIME ) );
+ else
+ SetProperty( std::make_shared<SfxOleFileTimeProperty>( nPropId, rValue ) );
+}
+
+void SfxOleSection::SetDateValue( sal_Int32 nPropId, const util::Date& rValue )
+{
+ //Annoyingly MS2010 considers VT_DATE apparently as an invalid possibility, so here we use VT_FILETIME
+ //instead :-(
+ if ( rValue.Year == 0 || rValue.Month == 0 || rValue.Day == 0 )
+ SetProperty( std::make_shared<SfxOleFileTimeProperty>( nPropId, TIMESTAMP_INVALID_UTILDATETIME ) );
+ else
+ {
+ const util::DateTime aValue(0, 0, 0, 0, rValue.Day, rValue.Month,
+ rValue.Year, false );
+ SetProperty( std::make_shared<SfxOleFileTimeProperty>( nPropId, aValue ) );
+ }
+}
+
+void SfxOleSection::SetThumbnailValue( sal_Int32 nPropId,
+ const uno::Sequence<sal_Int8> & i_rData)
+{
+ auto pThumbnail = std::make_shared<SfxOleThumbnailProperty>( nPropId, i_rData );
+ if( pThumbnail->IsValid() )
+ SetProperty( pThumbnail );
+}
+
+void SfxOleSection::SetBlobValue( sal_Int32 nPropId,
+ const uno::Sequence<sal_Int8> & i_rData)
+{
+ auto pBlob = std::make_shared<SfxOleBlobProperty>( nPropId, i_rData );
+ if( pBlob->IsValid() )
+ SetProperty( pBlob );
+}
+
+Any SfxOleSection::GetAnyValue( sal_Int32 nPropId ) const
+{
+ Any aValue;
+ sal_Int32 nInt32 = 0;
+ double fDouble = 0.0;
+ bool bBool = false;
+ OUString aString;
+ css::util::DateTime aApiDateTime;
+ css::util::Date aApiDate;
+
+ if( GetInt32Value( nInt32, nPropId ) )
+ aValue <<= nInt32;
+ else if( GetDoubleValue( fDouble, nPropId ) )
+ aValue <<= fDouble;
+ else if( GetBoolValue( bBool, nPropId ) )
+ aValue <<= bBool;
+ else if( GetStringValue( aString, nPropId ) )
+ aValue <<= aString;
+ else if( GetFileTimeValue( aApiDateTime, nPropId ) )
+ {
+ aValue <<= aApiDateTime;
+ }
+ else if( GetDateValue( aApiDate, nPropId ) )
+ {
+ aValue <<= aApiDate;
+ }
+ return aValue;
+}
+
+bool SfxOleSection::SetAnyValue( sal_Int32 nPropId, const Any& rValue )
+{
+ bool bInserted = true;
+ sal_Int32 nInt32 = 0;
+ double fDouble = 0.0;
+ OUString aString;
+ css::util::DateTime aApiDateTime;
+ css::util::Date aApiDate;
+
+ if( rValue.getValueType() == cppu::UnoType<bool>::get() )
+ SetBoolValue( nPropId, ::comphelper::getBOOL( rValue ) );
+ else if( rValue >>= nInt32 )
+ SetInt32Value( nPropId, nInt32 );
+ else if( rValue >>= fDouble )
+ SetDoubleValue( nPropId, fDouble );
+ else if( rValue >>= aString )
+ bInserted = SetStringValue( nPropId, aString );
+ else if( rValue >>= aApiDateTime )
+ SetFileTimeValue( nPropId, aApiDateTime );
+ else if( rValue >>= aApiDate )
+ SetDateValue( nPropId, aApiDate );
+ else
+ bInserted = false;
+ return bInserted;
+}
+
+OUString SfxOleSection::GetPropertyName( sal_Int32 nPropId ) const
+{
+ return maDictProp.GetPropertyName( nPropId );
+}
+
+void SfxOleSection::SetPropertyName( sal_Int32 nPropId, const OUString& rPropName )
+{
+ maDictProp.SetPropertyName( nPropId, rPropName );
+}
+
+void SfxOleSection::GetPropertyIds( ::std::vector< sal_Int32 >& rPropIds ) const
+{
+ rPropIds.clear();
+ for (auto const& prop : maPropMap)
+ rPropIds.push_back(prop.first);
+}
+
+sal_Int32 SfxOleSection::GetFreePropertyId() const
+{
+ return maPropMap.empty() ? PROPID_FIRSTCUSTOM : (maPropMap.rbegin()->first + 1);
+}
+
+void SfxOleSection::ImplLoad( SvStream& rStrm )
+{
+ // read section header
+ mnStartPos = rStrm.Tell();
+ sal_uInt32 nSize(0);
+ sal_Int32 nPropCount(0);
+ rStrm.ReadUInt32( nSize ).ReadInt32( nPropCount );
+
+ // read property ID/position pairs
+ typedef ::std::map< sal_Int32, sal_uInt32 > SfxOlePropPosMap;
+ SfxOlePropPosMap aPropPosMap;
+ for (sal_Int32 nPropIdx = 0; nPropIdx < nPropCount && rStrm.good(); ++nPropIdx)
+ {
+ sal_Int32 nPropId(0);
+ sal_uInt32 nPropPos(0);
+ rStrm.ReadInt32( nPropId ).ReadUInt32( nPropPos );
+ aPropPosMap[ nPropId ] = nPropPos;
+ }
+
+ // read codepage property
+ SfxOlePropPosMap::iterator aCodePageIt = aPropPosMap.find( PROPID_CODEPAGE );
+ if( (aCodePageIt != aPropPosMap.end()) && SeekToPropertyPos( rStrm, aCodePageIt->second ) )
+ {
+ // codepage property must be of type signed int-16
+ sal_Int32 nPropType(0);
+ rStrm.ReadInt32( nPropType );
+ if( nPropType == PROPTYPE_INT16 )
+ LoadObject( rStrm, maCodePageProp );
+ // remove property position
+ aPropPosMap.erase( aCodePageIt );
+ }
+
+ // read dictionary property
+ SfxOlePropPosMap::iterator aDictIt = aPropPosMap.find( PROPID_DICTIONARY );
+ if( (aDictIt != aPropPosMap.end()) && SeekToPropertyPos( rStrm, aDictIt->second ) )
+ {
+ // #i66214# #i66428# applications may write broken dictionary properties in wrong sections
+ if( mbSupportsDict )
+ {
+ // dictionary property contains number of pairs in property type field
+ sal_Int32 nNameCount(0);
+ rStrm.ReadInt32( nNameCount );
+ maDictProp.SetNameCount( nNameCount );
+ LoadObject( rStrm, maDictProp );
+ }
+ // always remove position of dictionary property (do not try to read it again below)
+ aPropPosMap.erase( aDictIt );
+ }
+
+ // read other properties
+ maPropMap.clear();
+ for (auto const& propPos : aPropPosMap)
+ if( SeekToPropertyPos( rStrm, propPos.second ) )
+ LoadProperty( rStrm, propPos.first );
+}
+
+void SfxOleSection::ImplSave( SvStream& rStrm )
+{
+ /* Always export with UTF-8 encoding. All dependent properties (bytestring
+ and dictionary) will be updated automatically. */
+ maCodePageProp.SetTextEncoding( RTL_TEXTENCODING_UTF8 );
+
+ // write section header
+ mnStartPos = rStrm.Tell();
+ sal_Int32 nPropCount = static_cast< sal_Int32 >( maPropMap.size() + 1 );
+ if( maDictProp.HasPropertyNames() )
+ ++nPropCount;
+ rStrm.WriteUInt32( 0 ).WriteInt32( nPropCount );
+
+ // write placeholders for property ID/position pairs
+ sal_uInt64 nPropPosPos = rStrm.Tell();
+ rStrm.SeekRel( static_cast< sal_sSize >( 8 * nPropCount ) );
+
+ // write dictionary property
+ if( maDictProp.HasPropertyNames() )
+ SaveProperty( rStrm, maDictProp, nPropPosPos );
+ // write codepage property
+ SaveProperty( rStrm, maCodePageProp, nPropPosPos );
+ // write other properties
+ for (auto const& prop : maPropMap)
+ SaveProperty( rStrm, *prop.second, nPropPosPos );
+
+ // write section size (first field in section header)
+ sal_uInt32 nSectSize = static_cast< sal_uInt32 >( rStrm.TellEnd() - mnStartPos );
+ rStrm.Seek( mnStartPos );
+ rStrm.WriteUInt32( nSectSize );
+}
+
+bool SfxOleSection::SeekToPropertyPos( SvStream& rStrm, sal_uInt32 nPropPos ) const
+{
+ return checkSeek(rStrm, static_cast<std::size_t>(mnStartPos + nPropPos)) &&
+ rStrm.GetErrorCode() == ERRCODE_NONE;
+}
+
+void SfxOleSection::LoadProperty( SvStream& rStrm, sal_Int32 nPropId )
+{
+ // property data type
+ sal_Int32 nPropType(0);
+ rStrm.ReadInt32( nPropType );
+ // create empty property object
+ SfxOlePropertyRef xProp;
+ switch( nPropType )
+ {
+ case PROPTYPE_INT32:
+ xProp = std::make_shared<SfxOleInt32Property>( nPropId );
+ break;
+ case PROPTYPE_DOUBLE:
+ xProp = std::make_shared<SfxOleDoubleProperty>( nPropId );
+ break;
+ case PROPTYPE_BOOL:
+ xProp = std::make_shared<SfxOleBoolProperty>( nPropId );
+ break;
+ case PROPTYPE_STRING8:
+ xProp = std::make_shared<SfxOleString8Property>( nPropId, maCodePageProp );
+ break;
+ case PROPTYPE_STRING16:
+ xProp = std::make_shared<SfxOleString16Property>( nPropId );
+ break;
+ case PROPTYPE_FILETIME:
+ xProp = std::make_shared<SfxOleFileTimeProperty>( nPropId );
+ break;
+ case PROPTYPE_DATE:
+ xProp = std::make_shared<SfxOleDateProperty>( nPropId );
+ break;
+ }
+ // load property contents
+ if( xProp )
+ {
+ SetError( xProp->Load( rStrm ) );
+ maPropMap[ nPropId ] = xProp;
+ }
+}
+
+void SfxOleSection::SaveProperty( SvStream& rStrm, SfxOlePropertyBase& rProp, sal_uInt64 & rnPropPosPos )
+{
+ rStrm.Seek( STREAM_SEEK_TO_END );
+ sal_uInt32 nPropPos = static_cast< sal_uInt32 >( rStrm.Tell() - mnStartPos );
+ // property data type
+ rStrm.WriteInt32( rProp.GetPropType() );
+ // write property contents
+ SaveObject( rStrm, rProp );
+ // align to 32-bit
+ while( (rStrm.Tell() & 3) != 0 )
+ rStrm.WriteUChar( 0 );
+ // write property ID/position pair
+ rStrm.Seek( rnPropPosPos );
+ rStrm.WriteInt32( rProp.GetPropId() ).WriteUInt32( nPropPos );
+ rnPropPosPos = rStrm.Tell();
+}
+
+
+ErrCode const & SfxOlePropertySet::LoadPropertySet( SotStorage* pStrg, const OUString& rStrmName )
+{
+ if( pStrg )
+ {
+ tools::SvRef<SotStorageStream> xStrm = pStrg->OpenSotStream( rStrmName, StreamMode::STD_READ );
+ if( xStrm.is() && (xStrm->GetError() == ERRCODE_NONE) )
+ {
+ xStrm->SetBufferSize( STREAM_BUFFER_SIZE );
+ Load( *xStrm );
+ }
+ else
+ SetError( ERRCODE_IO_ACCESSDENIED );
+ }
+ else
+ SetError( ERRCODE_IO_ACCESSDENIED );
+ return GetError();
+}
+
+ErrCode const & SfxOlePropertySet::SavePropertySet( SotStorage* pStrg, const OUString& rStrmName )
+{
+ if( pStrg )
+ {
+ tools::SvRef<SotStorageStream> xStrm = pStrg->OpenSotStream( rStrmName, StreamMode::TRUNC | StreamMode::STD_WRITE );
+ if( xStrm.is() )
+ Save( *xStrm );
+ else
+ SetError( ERRCODE_IO_ACCESSDENIED );
+ }
+ else
+ SetError( ERRCODE_IO_ACCESSDENIED );
+ return GetError();
+}
+
+SfxOleSectionRef SfxOlePropertySet::GetSection( SfxOleSectionType eSection ) const
+{
+ return GetSection( GetSectionGuid( eSection ) );
+}
+
+SfxOleSectionRef SfxOlePropertySet::GetSection( const SvGlobalName& rSectionGuid ) const
+{
+ SfxOleSectionRef xSection;
+ SfxOleSectionMap::const_iterator aIt = maSectionMap.find( rSectionGuid );
+ if( aIt != maSectionMap.end() )
+ xSection = aIt->second;
+ return xSection;
+}
+
+SfxOleSection& SfxOlePropertySet::AddSection( SfxOleSectionType eSection )
+{
+ return AddSection( GetSectionGuid( eSection ) );
+}
+
+SfxOleSection& SfxOlePropertySet::AddSection( const SvGlobalName& rSectionGuid )
+{
+ SfxOleSectionRef xSection = GetSection( rSectionGuid );
+ if( !xSection )
+ {
+ // #i66214# #i66428# applications may write broken dictionary properties in wrong sections
+ bool bSupportsDict = rSectionGuid == GetSectionGuid( SECTION_CUSTOM );
+ xSection = std::make_shared<SfxOleSection>( bSupportsDict );
+ maSectionMap[ rSectionGuid ] = xSection;
+ }
+ return *xSection;
+}
+
+void SfxOlePropertySet::ImplLoad( SvStream& rStrm )
+{
+ // read property set header
+ sal_uInt16 nByteOrder;
+ sal_uInt16 nVersion;
+ sal_uInt16 nOsMinor;
+ sal_uInt16 nOsType;
+ SvGlobalName aGuid;
+ sal_Int32 nSectCount(0);
+ rStrm.ReadUInt16( nByteOrder ).ReadUInt16( nVersion ).ReadUInt16( nOsMinor ).ReadUInt16( nOsType );
+ rStrm >> aGuid;
+ rStrm.ReadInt32( nSectCount );
+
+ // read sections
+ sal_uInt64 nSectPosPos = rStrm.Tell();
+ for (sal_Int32 nSectIdx = 0; nSectIdx < nSectCount; ++nSectIdx)
+ {
+ // read section guid/position pair
+ rStrm.Seek(nSectPosPos);
+ SvGlobalName aSectGuid;
+ rStrm >> aSectGuid;
+ sal_uInt32 nSectPos(0);
+ rStrm.ReadUInt32(nSectPos);
+ if (!rStrm.good())
+ break;
+ nSectPosPos = rStrm.Tell();
+ // read section
+ if (!checkSeek(rStrm, nSectPos))
+ break;
+ LoadObject(rStrm, AddSection(aSectGuid));
+ if (!rStrm.good())
+ break;
+ }
+}
+
+void SfxOlePropertySet::ImplSave( SvStream& rStrm )
+{
+ // write property set header
+ SvGlobalName aGuid;
+ sal_Int32 nSectCount = static_cast< sal_Int32 >( maSectionMap.size() );
+ rStrm .WriteUInt16( 0xFFFE ) // byte order
+ .WriteUInt16( 0 ) // version
+ .WriteUInt16( 1 ) // OS minor version
+ .WriteUInt16( 2 ); // OS type always windows for text encoding
+ WriteSvGlobalName( rStrm, aGuid ); // unused guid
+ rStrm .WriteInt32( nSectCount ); // number of sections
+
+ // write placeholders for section guid/position pairs
+ sal_uInt64 nSectPosPos = rStrm.Tell();
+ rStrm.SeekRel( static_cast< sal_sSize >( 20 * nSectCount ) );
+
+ // write sections
+ for (auto const& section : maSectionMap)
+ {
+ SfxOleSection& rSection = *section.second;
+ rStrm.Seek( STREAM_SEEK_TO_END );
+ sal_uInt32 nSectPos = static_cast< sal_uInt32 >( rStrm.Tell() );
+ // write the section
+ SaveObject( rStrm, rSection );
+ // write section guid/position pair
+ rStrm.Seek( nSectPosPos );
+ WriteSvGlobalName( rStrm, section.first );
+ rStrm.WriteUInt32( nSectPos );
+ nSectPosPos = rStrm.Tell();
+ }
+}
+
+const SvGlobalName& SfxOlePropertySet::GetSectionGuid( SfxOleSectionType eSection )
+{
+ static const SvGlobalName saGlobalGuid( 0xF29F85E0, 0x4FF9, 0x1068, 0xAB, 0x91, 0x08, 0x00, 0x2B, 0x27, 0xB3, 0xD9 );
+ static const SvGlobalName saBuiltInGuid( 0xD5CDD502, 0x2E9C, 0x101B, 0x93, 0x97, 0x08, 0x00, 0x2B, 0x2C, 0xF9, 0xAE );
+ static const SvGlobalName saCustomGuid( 0xD5CDD505, 0x2E9C, 0x101B, 0x93, 0x97, 0x08, 0x00, 0x2B, 0x2C, 0xF9, 0xAE );
+ static const SvGlobalName saEmptyGuid;
+ switch( eSection )
+ {
+ case SECTION_GLOBAL: return saGlobalGuid;
+ case SECTION_BUILTIN: return saBuiltInGuid;
+ case SECTION_CUSTOM: return saCustomGuid;
+ default: SAL_WARN( "sfx.doc", "SfxOlePropertySet::GetSectionGuid - unknown section type" );
+ }
+ return saEmptyGuid;
+}
+
+
+//} // namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/doc/oleprops.hxx b/sfx2/source/doc/oleprops.hxx
new file mode 100644
index 000000000..81eb744ea
--- /dev/null
+++ b/sfx2/source/doc/oleprops.hxx
@@ -0,0 +1,391 @@
+/* -*- 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_DOC_OLEPROPS_HXX
+#define INCLUDED_SFX2_SOURCE_DOC_OLEPROPS_HXX
+
+#include <map>
+#include <memory>
+#include <string_view>
+
+#include <osl/thread.h>
+#include <rtl/ustring.hxx>
+#include <sot/storage.hxx>
+
+#include <com/sun/star/util/DateTime.hpp>
+#include <com/sun/star/util/Date.hpp>
+
+
+//namespace {
+
+
+// property type IDs
+const sal_Int32 PROPTYPE_INT16 = 2;
+const sal_Int32 PROPTYPE_INT32 = 3;
+const sal_Int32 PROPTYPE_FLOAT = 4;
+const sal_Int32 PROPTYPE_DOUBLE = 5;
+const sal_Int32 PROPTYPE_DATE = 7;
+const sal_Int32 PROPTYPE_STRING = 8;
+const sal_Int32 PROPTYPE_STATUS = 10;
+const sal_Int32 PROPTYPE_BOOL = 11;
+const sal_Int32 PROPTYPE_VARIANT = 12;
+const sal_Int32 PROPTYPE_INT8 = 16;
+const sal_Int32 PROPTYPE_UINT8 = 17;
+const sal_Int32 PROPTYPE_UINT16 = 18;
+const sal_Int32 PROPTYPE_UINT32 = 19;
+const sal_Int32 PROPTYPE_INT64 = 20;
+const sal_Int32 PROPTYPE_UINT64 = 21;
+const sal_Int32 PROPTYPE_STRING8 = 30;
+const sal_Int32 PROPTYPE_STRING16 = 31;
+const sal_Int32 PROPTYPE_FILETIME = 64;
+const sal_Int32 PROPTYPE_BLOB = 65;
+const sal_Int32 PROPTYPE_CLIPFMT = 71;
+
+// static property IDs
+const sal_Int32 PROPID_DICTIONARY = 0;
+const sal_Int32 PROPID_CODEPAGE = 1;
+const sal_Int32 PROPID_FIRSTCUSTOM = 2;
+
+// property IDs for GlobalDocPropertySet
+const sal_Int32 PROPID_TITLE = 2;
+const sal_Int32 PROPID_SUBJECT = 3;
+const sal_Int32 PROPID_AUTHOR = 4;
+const sal_Int32 PROPID_KEYWORDS = 5;
+const sal_Int32 PROPID_COMMENTS = 6;
+const sal_Int32 PROPID_TEMPLATE = 7;
+const sal_Int32 PROPID_LASTAUTHOR = 8;
+const sal_Int32 PROPID_REVNUMBER = 9;
+const sal_Int32 PROPID_EDITTIME = 10;
+const sal_Int32 PROPID_LASTPRINTED = 11;
+const sal_Int32 PROPID_CREATED = 12;
+const sal_Int32 PROPID_LASTSAVED = 13;
+const sal_Int32 PROPID_THUMBNAIL = 17;
+
+// some Builtin properties
+const sal_Int32 PROPID_CATEGORY = 0x2;
+const sal_Int32 PROPID_COMPANY = 0xf;
+const sal_Int32 PROPID_MANAGER = 0xe;
+// predefined codepages
+const sal_uInt16 CODEPAGE_UNKNOWN = 0;
+const sal_uInt16 CODEPAGE_UNICODE = 1200;
+const sal_uInt16 CODEPAGE_UTF8 = 65001;
+
+// predefined clipboard format IDs
+const sal_Int32 CLIPFMT_WIN = -1;
+
+// predefined clipboard data format IDs
+const sal_Int32 CLIPDATAFMT_DIB = 8;
+
+
+/** Helper for classes that need text encoding settings.
+
+ Classes derived from this class will include functions to store and use
+ text encoding settings and to convert Windows codepage constants.
+ */
+class SfxOleTextEncoding
+{
+public:
+ explicit SfxOleTextEncoding() :
+ mxTextEnc( std::make_shared<rtl_TextEncoding>( osl_getThreadTextEncoding() ) ) {}
+ explicit SfxOleTextEncoding( rtl_TextEncoding eTextEnc ) :
+ mxTextEnc( std::make_shared<rtl_TextEncoding>( eTextEnc ) ) {}
+
+ /** Returns the current text encoding identifier. */
+ rtl_TextEncoding GetTextEncoding() const { return *mxTextEnc; }
+ /** Sets the passed text encoding. */
+ void SetTextEncoding( rtl_TextEncoding eTextEnc ) { *mxTextEnc = eTextEnc; }
+
+ /** Returns true, if this object contains Unicode text encoding. */
+ bool IsUnicode() const { return GetTextEncoding() == RTL_TEXTENCODING_UCS2; }
+ /** Sets Unicode text encoding to this object. */
+ void SetUnicode() { SetTextEncoding( RTL_TEXTENCODING_UCS2 ); }
+
+ /** Converts the current settings to a Windows codepage identifier. */
+ sal_uInt16 GetCodePage() const;
+ /** Sets the current text encoding from a Windows codepage identifier. */
+ void SetCodePage( sal_uInt16 nCodePage );
+
+private:
+ std::shared_ptr< rtl_TextEncoding > mxTextEnc;
+};
+
+
+/** Helper for classes that need to load or save string values.
+
+ Classes derived from this class contain functions to load and save string
+ values with the text encoding passed in the constructor.
+ */
+class SfxOleStringHelper : public SfxOleTextEncoding
+{
+public:
+ /** Creates a string helper object depending on an external text encoding. */
+ explicit SfxOleStringHelper( const SfxOleTextEncoding& rTextEnc ) :
+ SfxOleTextEncoding( rTextEnc ) {}
+ /** Creates a string helper object with own text encoding. */
+ explicit SfxOleStringHelper( rtl_TextEncoding eTextEnc ) :
+ SfxOleTextEncoding( eTextEnc ) {}
+
+ /** Loads a string from the passed stream with current encoding (maybe Unicode). */
+ OUString LoadString8( SvStream& rStrm ) const;
+ /** Saves a string to the passed stream with current encoding (maybe Unicode). */
+ void SaveString8( SvStream& rStrm, const OUString& rValue ) const;
+
+ /** Loads a Unicode string from the passed stream, ignores own encoding. */
+ static OUString LoadString16( SvStream& rStrm );
+ /** Saves a Unicode string to the passed stream, ignores own encoding. */
+ static void SaveString16( SvStream& rStrm, const OUString& rValue );
+
+private:
+ OUString ImplLoadString8( SvStream& rStrm ) const;
+ static OUString ImplLoadString16( SvStream& rStrm );
+ void ImplSaveString8( SvStream& rStrm, std::u16string_view rValue ) const;
+ static void ImplSaveString16( SvStream& rStrm, const OUString& rValue );
+};
+
+
+/** Base class for all classes related to OLE property sets.
+
+ Derived classes have to implement the pure virtual functions ImplLoad() and
+ ImplSave().
+ */
+class SfxOleObjectBase
+{
+public:
+ explicit SfxOleObjectBase() : mnErrCode( ERRCODE_NONE ) {}
+ virtual ~SfxOleObjectBase();
+
+ /** Returns the current error code. */
+ ErrCode const & GetError() const { return mnErrCode; }
+
+ /** Loads this object from the passed stream. Calls virtual ImplLoad(). */
+ ErrCode const & Load( SvStream& rStrm );
+ /** Saves this object to the passed stream. Calls virtual ImplSave(). */
+ ErrCode const & Save( SvStream& rStrm );
+
+protected:
+ /** Sets the passed error code. Will be returned by Load() and Save() functions.
+ Always the first error code is stored. Multiple calls have no effect. */
+ void SetError( ErrCode nErrCode ) { if( mnErrCode == ERRCODE_NONE ) mnErrCode = nErrCode; }
+ /** Loads the passed object from the stream. Sets returned error code as own error. */
+ void LoadObject( SvStream& rStrm, SfxOleObjectBase& rObj );
+ /** Saves the passed object to the stream. Sets returned error code as own error. */
+ void SaveObject( SvStream& rStrm, SfxOleObjectBase& rObj );
+
+private:
+ /** Derived classes implement loading the object from the passed steam. */
+ virtual void ImplLoad( SvStream& rStrm ) = 0;
+ /** Derived classes implement saving the object to the passed steam. */
+ virtual void ImplSave( SvStream& rStrm ) = 0;
+
+private:
+ ErrCode mnErrCode; /// Current error code.
+};
+
+
+/** Base class for all OLE property objects. */
+class SfxOlePropertyBase : public SfxOleObjectBase
+{
+public:
+ explicit SfxOlePropertyBase( sal_Int32 nPropId, sal_Int32 nPropType ) :
+ mnPropId( nPropId ), mnPropType( nPropType ) {}
+
+ sal_Int32 GetPropId() const { return mnPropId; }
+ sal_Int32 GetPropType() const { return mnPropType; }
+
+protected:
+ void SetPropType( sal_Int32 nPropType ) { mnPropType = nPropType; }
+
+private:
+ sal_Int32 mnPropId;
+ sal_Int32 mnPropType;
+};
+
+typedef std::shared_ptr< SfxOlePropertyBase > SfxOlePropertyRef;
+
+
+/** Property representing the codepage used to encode bytestrings in the entire property set. */
+class SfxOleCodePageProperty : public SfxOlePropertyBase, public SfxOleTextEncoding
+{
+public:
+ explicit SfxOleCodePageProperty();
+
+private:
+ virtual void ImplLoad( SvStream& rStrm ) override;
+ virtual void ImplSave( SvStream& rStrm ) override;
+};
+
+
+/** Property containing custom names for other properties in the property set. */
+class SfxOleDictionaryProperty : public SfxOlePropertyBase, public SfxOleStringHelper
+{
+public:
+ explicit SfxOleDictionaryProperty( const SfxOleTextEncoding& rTextEnc );
+
+ /** Returns true, if the property contains at least one custom property name. */
+ bool HasPropertyNames() const { return !maPropNameMap.empty(); }
+ /** Prepares the property for loading. Does not affect contained names for its own. */
+ void SetNameCount( sal_Int32 nNameCount ) { SetPropType( nNameCount ); }
+
+ /** Returns the custom name for the passed property ID, or an empty string, if name not found. */
+ OUString GetPropertyName( sal_Int32 nPropId ) const;
+ /** Sets a custom name for the passed property ID. */
+ void SetPropertyName( sal_Int32 nPropId, const OUString& rPropName );
+
+private:
+ virtual void ImplLoad( SvStream& rStrm ) override;
+ virtual void ImplSave( SvStream& rStrm ) override;
+
+private:
+ typedef ::std::map< sal_Int32, OUString > SfxOlePropNameMap;
+ SfxOlePropNameMap maPropNameMap;
+};
+
+
+/** A section in a property set. Contains properties with unique identifiers. */
+class SfxOleSection : public SfxOleObjectBase
+{
+private:
+ typedef ::std::map< sal_Int32, SfxOlePropertyRef > SfxOlePropMap;
+
+public:
+ explicit SfxOleSection( bool bSupportsDict );
+
+ /** Returns the property with the passed ID, or an empty reference, if nothing found. */
+ SfxOlePropertyRef GetProperty( sal_Int32 nPropId ) const;
+ /** Returns the value of a signed int32 property with the passed ID in rnValue.
+ @return true = Property found, rnValue is valid; false = Property not found. */
+ bool GetInt32Value( sal_Int32& rnValue, sal_Int32 nPropId ) const;
+ /** Returns the value of a floating-point property with the passed ID in rfValue.
+ @return true = Property found, rfValue is valid; false = Property not found. */
+ bool GetDoubleValue( double& rfValue, sal_Int32 nPropId ) const;
+ /** Returns the value of a boolean property with the passed ID in rbValue.
+ @return true = Property found, rbValue is valid; false = Property not found. */
+ bool GetBoolValue( bool& rbValue, sal_Int32 nPropId ) const;
+ /** Returns the value of a string property with the passed ID in rValue.
+ @return true = Property found, rValue is valid; false = Property not found. */
+ bool GetStringValue( OUString& rValue, sal_Int32 nPropId ) const;
+ /** Returns the value of a time stamp property with the passed ID in rValue.
+ @return true = Property found, rValue is valid; false = Property not found. */
+ bool GetFileTimeValue( css::util::DateTime& rValue, sal_Int32 nPropId ) const;
+ /** Returns the value of a date property with the passed ID in rValue.
+ @return true = Property found, rValue is valid; false = Property not found. */
+ bool GetDateValue( css::util::Date& rValue, sal_Int32 nPropId ) const;
+
+ /** Adds the passed property to the property set. Drops an existing old property. */
+ void SetProperty( const SfxOlePropertyRef& xProp );
+ /** Inserts a signed int32 property with the passed value. */
+ void SetInt32Value( sal_Int32 nPropId, sal_Int32 nValue );
+ /** Inserts a floating-point property with the passed value. */
+ void SetDoubleValue( sal_Int32 nPropId, double fValue );
+ /** Inserts a boolean property with the passed value. */
+ void SetBoolValue( sal_Int32 nPropId, bool bValue );
+ /** Inserts a string property with the passed value.
+ @return true = Property inserted; false = String was empty, property not inserted. */
+ bool SetStringValue( sal_Int32 nPropId, const OUString& rValue );
+ /** Inserts a time stamp property with the passed value. */
+ void SetFileTimeValue( sal_Int32 nPropId, const css::util::DateTime& rValue );
+ /** Inserts a date property with the passed value. */
+ void SetDateValue( sal_Int32 nPropId, const css::util::Date& rValue );
+ /** Inserts a thumbnail property from the passed meta file. */
+ void SetThumbnailValue( sal_Int32 nPropId,
+ const css::uno::Sequence<sal_Int8> & i_rData);
+ /** Inserts a BLOB property with the passed data. */
+ void SetBlobValue( sal_Int32 nPropId,
+ const css::uno::Sequence<sal_Int8> & i_rData);
+
+ /** Returns the value of the property with the passed ID in a UNO any. */
+ css::uno::Any GetAnyValue( sal_Int32 nPropId ) const;
+ /** Inserts a property created from the passed any.
+ @return true = Property converted and inserted; false = Property type not supported. */
+ bool SetAnyValue( sal_Int32 nPropId, const css::uno::Any& rValue );
+
+ /** Returns the custom name for the passed property ID, or an empty string, if name not found. */
+ OUString GetPropertyName( sal_Int32 nPropId ) const;
+ /** Sets a custom name for the passed property ID. */
+ void SetPropertyName( sal_Int32 nPropId, const OUString& rPropName );
+
+ /** Returns the identifiers of all existing properties in the passed vector. */
+ void GetPropertyIds( ::std::vector< sal_Int32 >& rPropIds ) const;
+ /** Returns a property identifier not used in this section. */
+ sal_Int32 GetFreePropertyId() const;
+
+private:
+ virtual void ImplLoad( SvStream& rStrm ) override;
+ virtual void ImplSave( SvStream& rStrm ) override;
+
+ bool SeekToPropertyPos( SvStream& rStrm, sal_uInt32 nPropPos ) const;
+ void LoadProperty( SvStream& rStrm, sal_Int32 nPropId );
+ void SaveProperty( SvStream& rStrm, SfxOlePropertyBase& rProp, sal_uInt64 & rnPropPosPos );
+
+private:
+ SfxOlePropMap maPropMap; /// All properties in this section, by identifier.
+ SfxOleCodePageProperty maCodePageProp; /// The codepage property.
+ SfxOleDictionaryProperty maDictProp; /// The dictionary property.
+ sal_uInt64 mnStartPos; /// Start stream position of the section.
+ bool mbSupportsDict; /// true = section supports dictionary.
+};
+
+typedef std::shared_ptr< SfxOleSection > SfxOleSectionRef;
+
+
+/** Enumerates different section types in OLE property sets. */
+enum SfxOleSectionType
+{
+ SECTION_GLOBAL, /// Globally defined properties.
+ SECTION_BUILTIN, /// Properties built into MS Office.
+ SECTION_CUSTOM /// Custom properties.
+};
+
+
+/** Represents a complete property set, may consist of several property sections. */
+class SfxOlePropertySet : public SfxOleObjectBase
+{
+public:
+ explicit SfxOlePropertySet() {}
+
+ /** Loads this object from the passed storage. */
+ ErrCode const & LoadPropertySet( SotStorage* pStrg, const OUString& rStrmName );
+ /** Saves this object to the passed storage. */
+ ErrCode const & SavePropertySet( SotStorage* pStrg, const OUString& rStrmName );
+
+ /** Returns the specified section, or an empty reference, if nothing found. */
+ SfxOleSectionRef GetSection( SfxOleSectionType eSection ) const;
+ /** Returns the specified section, or an empty reference, if nothing found. */
+ SfxOleSectionRef GetSection( const SvGlobalName& rSectionGuid ) const;
+
+ /** Creates and returns the specified section, or just returns it if it already exists. */
+ SfxOleSection& AddSection( SfxOleSectionType eSection );
+ /** Creates and returns the specified section, or just returns it if it already exists. */
+ SfxOleSection& AddSection( const SvGlobalName& rSectionGuid );
+
+private:
+ virtual void ImplLoad( SvStream& rStrm ) override;
+ virtual void ImplSave( SvStream& rStrm ) override;
+
+ /** Returns the GUID for the specified section. */
+ static const SvGlobalName& GetSectionGuid( SfxOleSectionType eSection );
+
+private:
+ typedef ::std::map< SvGlobalName, SfxOleSectionRef > SfxOleSectionMap;
+ SfxOleSectionMap maSectionMap;
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/doc/ownsubfilterservice.cxx b/sfx2/source/doc/ownsubfilterservice.cxx
new file mode 100644
index 000000000..7e317cc29
--- /dev/null
+++ b/sfx2/source/doc/ownsubfilterservice.cxx
@@ -0,0 +1,115 @@
+/* -*- 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/frame/DoubleInitializationException.hpp>
+#include <com/sun/star/document/XFilter.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/io/XStream.hpp>
+
+#include <cppuhelper/implbase.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <sfx2/objsh.hxx>
+
+using namespace css;
+
+namespace {
+
+class OwnSubFilterService : public cppu::WeakImplHelper < document::XFilter
+ ,lang::XServiceInfo >
+{
+ uno::Reference< frame::XModel > m_xModel;
+ uno::Reference< io::XStream > m_xStream;
+ SfxObjectShell* m_pObjectShell;
+
+public:
+ /// @throws css::uno::Exception
+ /// @throws css::uno::RuntimeException
+ explicit OwnSubFilterService(const css::uno::Sequence< css::uno::Any >& aArguments);
+
+ // XFilter
+ virtual sal_Bool SAL_CALL filter( const uno::Sequence< beans::PropertyValue >& aDescriptor ) override;
+ virtual void SAL_CALL cancel() override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName( ) override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+ virtual uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override;
+};
+
+OwnSubFilterService::OwnSubFilterService(const css::uno::Sequence< css::uno::Any >& aArguments)
+ : m_pObjectShell( nullptr )
+{
+ if ( aArguments.getLength() != 2 )
+ throw lang::IllegalArgumentException();
+
+ if ( m_pObjectShell )
+ throw frame::DoubleInitializationException();
+
+ if ( ( aArguments[1] >>= m_xStream ) && m_xStream.is()
+ && ( aArguments[0] >>= m_xModel ) && m_xModel.is() )
+ {
+ m_pObjectShell = SfxObjectShell::GetShellFromComponent(m_xModel);
+ }
+
+ if ( !m_pObjectShell )
+ throw lang::IllegalArgumentException();
+}
+
+sal_Bool SAL_CALL OwnSubFilterService::filter( const uno::Sequence< beans::PropertyValue >& aDescriptor )
+{
+ if ( !m_pObjectShell )
+ throw uno::RuntimeException();
+
+ return m_pObjectShell->ImportFromGeneratedStream_Impl( m_xStream, aDescriptor );
+}
+
+void SAL_CALL OwnSubFilterService::cancel()
+{
+ // not implemented
+}
+
+OUString SAL_CALL OwnSubFilterService::getImplementationName()
+{
+ return "com.sun.star.comp.document.OwnSubFilter";
+}
+
+sal_Bool SAL_CALL OwnSubFilterService::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+uno::Sequence< OUString > SAL_CALL OwnSubFilterService::getSupportedServiceNames()
+{
+ return { "com.sun.star.document.OwnSubFilter", "com.sun.star.comp.document.OwnSubFilter" };
+}
+
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_document_OwnSubFilter_get_implementation(
+ css::uno::XComponentContext *,
+ css::uno::Sequence<css::uno::Any> const &arguments)
+{
+ return cppu::acquire(new OwnSubFilterService(arguments));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/doc/printhelper.cxx b/sfx2/source/doc/printhelper.cxx
new file mode 100644
index 000000000..7a1891037
--- /dev/null
+++ b/sfx2/source/doc/printhelper.cxx
@@ -0,0 +1,807 @@
+/* -*- 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 "printhelper.hxx"
+
+#include <com/sun/star/view/XPrintJob.hpp>
+#include <com/sun/star/awt/Size.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <com/sun/star/view/PaperFormat.hpp>
+#include <com/sun/star/view/PaperOrientation.hpp>
+#include <com/sun/star/ucb/NameClash.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/view/DuplexMode.hpp>
+#include <comphelper/processfactory.hxx>
+#include <svl/itemset.hxx>
+#include <svl/lstner.hxx>
+#include <unotools/tempfile.hxx>
+#include <osl/file.hxx>
+#include <osl/thread.hxx>
+#include <tools/urlobj.hxx>
+#include <tools/diagnose_ex.h>
+#include <ucbhelper/content.hxx>
+#include <comphelper/interfacecontainer4.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/svapp.hxx>
+
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/viewsh.hxx>
+#include <sfx2/printer.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/event.hxx>
+
+#define SFX_PRINTABLESTATE_CANCELJOB css::view::PrintableState(-2)
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+
+struct IMPL_PrintListener_DataContainer : public SfxListener
+{
+ SfxObjectShellRef m_pObjectShell;
+ std::mutex m_aMutex;
+ comphelper::OInterfaceContainerHelper4<view::XPrintJobListener> m_aJobListeners;
+ uno::Reference< css::view::XPrintJob> m_xPrintJob;
+ css::uno::Sequence< css::beans::PropertyValue > m_aPrintOptions;
+
+ explicit IMPL_PrintListener_DataContainer()
+ {
+ }
+
+
+ void Notify( SfxBroadcaster& aBC ,
+ const SfxHint& aHint ) override ;
+};
+
+static awt::Size impl_Size_Object2Struct( const Size& aSize )
+{
+ awt::Size aReturnValue;
+ aReturnValue.Width = aSize.Width() ;
+ aReturnValue.Height = aSize.Height() ;
+ return aReturnValue ;
+}
+
+static Size impl_Size_Struct2Object( const awt::Size& aSize )
+{
+ Size aReturnValue;
+ aReturnValue.setWidth( aSize.Width ) ;
+ aReturnValue.setHeight( aSize.Height ) ;
+ return aReturnValue ;
+}
+
+namespace {
+
+class SfxPrintJob_Impl : public cppu::WeakImplHelper
+<
+ css::view::XPrintJob
+>
+{
+ IMPL_PrintListener_DataContainer* m_pData;
+
+public:
+ explicit SfxPrintJob_Impl( IMPL_PrintListener_DataContainer* pData );
+ virtual Sequence< css::beans::PropertyValue > SAL_CALL getPrintOptions( ) override;
+ virtual Sequence< css::beans::PropertyValue > SAL_CALL getPrinter( ) override;
+ virtual Reference< css::view::XPrintable > SAL_CALL getPrintable( ) override;
+ virtual void SAL_CALL cancelJob() override;
+};
+
+}
+
+SfxPrintJob_Impl::SfxPrintJob_Impl( IMPL_PrintListener_DataContainer* pData )
+ : m_pData( pData )
+{
+}
+
+Sequence< css::beans::PropertyValue > SAL_CALL SfxPrintJob_Impl::getPrintOptions()
+{
+ return m_pData->m_aPrintOptions;
+}
+
+Sequence< css::beans::PropertyValue > SAL_CALL SfxPrintJob_Impl::getPrinter()
+{
+ if( m_pData->m_pObjectShell.is() )
+ {
+ Reference < view::XPrintable > xPrintable( m_pData->m_pObjectShell->GetModel(), UNO_QUERY );
+ if ( xPrintable.is() )
+ return xPrintable->getPrinter();
+ }
+ return Sequence< css::beans::PropertyValue >();
+}
+
+Reference< css::view::XPrintable > SAL_CALL SfxPrintJob_Impl::getPrintable()
+{
+ Reference < view::XPrintable > xPrintable( m_pData->m_pObjectShell.is() ? m_pData->m_pObjectShell->GetModel() : nullptr, UNO_QUERY );
+ return xPrintable;
+}
+
+void SAL_CALL SfxPrintJob_Impl::cancelJob()
+{
+ // FIXME: how to cancel PrintJob via API?!
+ if( m_pData->m_pObjectShell.is() )
+ m_pData->m_pObjectShell->Broadcast( SfxPrintingHint( SFX_PRINTABLESTATE_CANCELJOB ) );
+}
+
+SfxPrintHelper::SfxPrintHelper()
+{
+ m_pData.reset(new IMPL_PrintListener_DataContainer());
+}
+
+void SAL_CALL SfxPrintHelper::initialize( const css::uno::Sequence< css::uno::Any >& aArguments )
+{
+ if ( !aArguments.hasElements() )
+ return;
+
+ css::uno::Reference < css::frame::XModel > xModel;
+ aArguments[0] >>= xModel;
+ m_pData->m_pObjectShell = SfxObjectShell::GetShellFromComponent(xModel);
+ if (m_pData->m_pObjectShell)
+ m_pData->StartListening(*m_pData->m_pObjectShell);
+}
+
+SfxPrintHelper::~SfxPrintHelper()
+{
+}
+
+namespace
+{
+ view::PaperFormat convertToPaperFormat(Paper eFormat)
+ {
+ view::PaperFormat eRet;
+ switch (eFormat)
+ {
+ case PAPER_A3:
+ eRet = view::PaperFormat_A3;
+ break;
+ case PAPER_A4:
+ eRet = view::PaperFormat_A4;
+ break;
+ case PAPER_A5:
+ eRet = view::PaperFormat_A5;
+ break;
+ case PAPER_B4_ISO:
+ eRet = view::PaperFormat_B4;
+ break;
+ case PAPER_B5_ISO:
+ eRet = view::PaperFormat_B5;
+ break;
+ case PAPER_LETTER:
+ eRet = view::PaperFormat_LETTER;
+ break;
+ case PAPER_LEGAL:
+ eRet = view::PaperFormat_LEGAL;
+ break;
+ case PAPER_TABLOID:
+ eRet = view::PaperFormat_TABLOID;
+ break;
+ case PAPER_USER:
+ default:
+ eRet = view::PaperFormat_USER;
+ break;
+ }
+ return eRet;
+ }
+
+ Paper convertToPaper(view::PaperFormat eFormat)
+ {
+ Paper eRet(PAPER_USER);
+ switch (eFormat)
+ {
+ case view::PaperFormat_A3:
+ eRet = PAPER_A3;
+ break;
+ case view::PaperFormat_A4:
+ eRet = PAPER_A4;
+ break;
+ case view::PaperFormat_A5:
+ eRet = PAPER_A5;
+ break;
+ case view::PaperFormat_B4:
+ eRet = PAPER_B4_ISO;
+ break;
+ case view::PaperFormat_B5:
+ eRet = PAPER_B5_ISO;
+ break;
+ case view::PaperFormat_LETTER:
+ eRet = PAPER_LETTER;
+ break;
+ case view::PaperFormat_LEGAL:
+ eRet = PAPER_LEGAL;
+ break;
+ case view::PaperFormat_TABLOID:
+ eRet = PAPER_TABLOID;
+ break;
+ case view::PaperFormat_USER:
+ eRet = PAPER_USER;
+ break;
+ case view::PaperFormat::PaperFormat_MAKE_FIXED_SIZE:
+ break;
+ //deliberate no default to force warn on a new papersize
+ }
+ return eRet;
+ }
+}
+
+
+// XPrintable
+
+
+uno::Sequence< beans::PropertyValue > SAL_CALL SfxPrintHelper::getPrinter()
+{
+ // object already disposed?
+ SolarMutexGuard aGuard;
+
+ // search for any view of this document that is currently printing
+ const Printer *pPrinter = nullptr;
+ SfxViewFrame *pViewFrm = m_pData->m_pObjectShell.is() ? SfxViewFrame::GetFirst( m_pData->m_pObjectShell.get(), false ) : nullptr;
+ SfxViewFrame* pFirst = pViewFrm;
+ while ( pViewFrm && !pPrinter )
+ {
+ pPrinter = pViewFrm->GetViewShell()->GetActivePrinter();
+ pViewFrm = SfxViewFrame::GetNext( *pViewFrm, m_pData->m_pObjectShell.get(), false );
+ }
+
+ // if no view is printing currently, use the permanent SfxPrinter instance
+ if ( !pPrinter && pFirst )
+ pPrinter = pFirst->GetViewShell()->GetPrinter(true);
+
+ if ( !pPrinter )
+ return uno::Sequence< beans::PropertyValue >();
+
+ uno::Sequence< beans::PropertyValue > aPrinter(8);
+
+ aPrinter.getArray()[7].Name = "CanSetPaperSize";
+ aPrinter.getArray()[7].Value <<= pPrinter->HasSupport( PrinterSupport::SetPaperSize );
+
+ aPrinter.getArray()[6].Name = "CanSetPaperFormat";
+ aPrinter.getArray()[6].Value <<= pPrinter->HasSupport( PrinterSupport::SetPaper );
+
+ aPrinter.getArray()[5].Name = "CanSetPaperOrientation";
+ aPrinter.getArray()[5].Value <<= pPrinter->HasSupport( PrinterSupport::SetOrientation );
+
+ aPrinter.getArray()[4].Name = "IsBusy";
+ aPrinter.getArray()[4].Value <<= pPrinter->IsPrinting();
+
+ aPrinter.getArray()[3].Name = "PaperSize";
+ awt::Size aSize = impl_Size_Object2Struct(pPrinter->GetPaperSize() );
+ aPrinter.getArray()[3].Value <<= aSize;
+
+ aPrinter.getArray()[2].Name = "PaperFormat";
+ view::PaperFormat eFormat = convertToPaperFormat(pPrinter->GetPaper());
+ aPrinter.getArray()[2].Value <<= eFormat;
+
+ aPrinter.getArray()[1].Name = "PaperOrientation";
+ view::PaperOrientation eOrient = static_cast<view::PaperOrientation>(pPrinter->GetOrientation());
+ aPrinter.getArray()[1].Value <<= eOrient;
+
+ aPrinter.getArray()[0].Name = "Name";
+ OUString sStringTemp = pPrinter->GetName() ;
+ aPrinter.getArray()[0].Value <<= sStringTemp;
+
+ return aPrinter;
+}
+
+
+// XPrintable
+
+
+void SfxPrintHelper::impl_setPrinter(const uno::Sequence< beans::PropertyValue >& rPrinter,
+ VclPtr<SfxPrinter>& pPrinter,
+ SfxPrinterChangeFlags& nChangeFlags,
+ SfxViewShell*& pViewSh)
+
+{
+ // Get old Printer
+ SfxViewFrame *pViewFrm = m_pData->m_pObjectShell.is() ?
+ SfxViewFrame::GetFirst( m_pData->m_pObjectShell.get(), false ) : nullptr;
+ if ( !pViewFrm )
+ return;
+
+ pViewSh = pViewFrm->GetViewShell();
+ pPrinter = pViewSh->GetPrinter(true);
+ if ( !pPrinter )
+ return;
+
+ // new Printer-Name available?
+ nChangeFlags = SfxPrinterChangeFlags::NONE;
+ sal_Int32 lDummy = 0;
+ auto pProp = std::find_if(rPrinter.begin(), rPrinter.end(),
+ [](const beans::PropertyValue &rProp) { return rProp.Name == "Name"; });
+ if (pProp != rPrinter.end())
+ {
+ OUString aPrinterName;
+ if ( ! ( pProp->Value >>= aPrinterName ) )
+ throw css::lang::IllegalArgumentException();
+
+ if ( aPrinterName != pPrinter->GetName() )
+ {
+ pPrinter = VclPtr<SfxPrinter>::Create( pPrinter->GetOptions().Clone(), aPrinterName );
+ nChangeFlags = SfxPrinterChangeFlags::PRINTER;
+ }
+ }
+
+ Size aSetPaperSize( 0, 0);
+ view::PaperFormat nPaperFormat = view::PaperFormat_USER;
+
+ // other properties
+ for ( const beans::PropertyValue &rProp : rPrinter )
+ {
+ // get Property-Value from printer description
+ // PaperOrientation-Property?
+ if ( rProp.Name == "PaperOrientation" )
+ {
+ view::PaperOrientation eOrient;
+ if ( !( rProp.Value >>= eOrient ) )
+ {
+ if ( !( rProp.Value >>= lDummy ) )
+ throw css::lang::IllegalArgumentException();
+ eOrient = static_cast<view::PaperOrientation>(lDummy);
+ }
+
+ if ( static_cast<Orientation>(eOrient) != pPrinter->GetOrientation() )
+ {
+ pPrinter->SetOrientation( static_cast<Orientation>(eOrient) );
+ nChangeFlags |= SfxPrinterChangeFlags::CHG_ORIENTATION;
+ }
+ }
+
+ // PaperFormat-Property?
+ else if ( rProp.Name == "PaperFormat" )
+ {
+ if ( !( rProp.Value >>= nPaperFormat ) )
+ {
+ if ( !( rProp.Value >>= lDummy ) )
+ throw css::lang::IllegalArgumentException();
+ nPaperFormat = static_cast<view::PaperFormat>(lDummy);
+ }
+
+ if ( convertToPaper(nPaperFormat) != pPrinter->GetPaper() )
+ {
+ pPrinter->SetPaper( convertToPaper(nPaperFormat) );
+ nChangeFlags |= SfxPrinterChangeFlags::CHG_SIZE;
+ }
+ }
+
+ // PaperSize-Property?
+ else if ( rProp.Name == "PaperSize" )
+ {
+ awt::Size aTempSize ;
+ if ( !( rProp.Value >>= aTempSize ) )
+ {
+ throw css::lang::IllegalArgumentException();
+ }
+ aSetPaperSize = impl_Size_Struct2Object(aTempSize);
+ }
+
+ // PrinterTray-Property
+ else if ( rProp.Name == "PrinterPaperTray" )
+ {
+ OUString aTmp;
+ if ( !( rProp.Value >>= aTmp ) )
+ throw css::lang::IllegalArgumentException();
+ const sal_uInt16 nCount = pPrinter->GetPaperBinCount();
+ for (sal_uInt16 nBin=0; nBin<nCount; nBin++)
+ {
+ OUString aName( pPrinter->GetPaperBinName(nBin) );
+ if ( aName == aTmp )
+ {
+ pPrinter->SetPaperBin(nBin);
+ break;
+ }
+ }
+ }
+ }
+
+ // The PaperSize may be set only when actually PAPER_USER
+ // applies, otherwise the driver could choose an invalid format.
+ if(nPaperFormat == view::PaperFormat_USER && aSetPaperSize.Width())
+ {
+ // Bug 56929 - MapMode of 100mm which recalculated when
+ // the device is set. Additionally only set if they were really changed.
+ aSetPaperSize = pPrinter->LogicToPixel(aSetPaperSize, MapMode(MapUnit::Map100thMM));
+ if( aSetPaperSize != pPrinter->GetPaperSizePixel() )
+ {
+ pPrinter->SetPaperSizeUser( pPrinter->PixelToLogic( aSetPaperSize ) );
+ nChangeFlags |= SfxPrinterChangeFlags::CHG_SIZE;
+ }
+ }
+
+ //wait until printing is done
+ SfxPrinter* pDocPrinter = pViewSh->GetPrinter();
+ while ( pDocPrinter->IsPrinting() && !Application::IsQuit())
+ Application::Yield();
+}
+
+void SAL_CALL SfxPrintHelper::setPrinter(const uno::Sequence< beans::PropertyValue >& rPrinter)
+{
+ // object already disposed?
+ SolarMutexGuard aGuard;
+
+ SfxViewShell* pViewSh = nullptr;
+ VclPtr<SfxPrinter> pPrinter;
+ SfxPrinterChangeFlags nChangeFlags = SfxPrinterChangeFlags::NONE;
+ impl_setPrinter(rPrinter,pPrinter,nChangeFlags,pViewSh);
+ // set new printer
+ if ( pViewSh && pPrinter )
+ pViewSh->SetPrinter( pPrinter, nChangeFlags );
+}
+
+
+// ImplPrintWatch thread for asynchronous printing with moving temp. file to ucb location
+
+namespace {
+
+/* This implements a thread which will be started to wait for asynchronous
+ print jobs to temp. locally files. If they finish we move the temp. files
+ to their right locations by using the ucb.
+ */
+class ImplUCBPrintWatcher : public ::osl::Thread
+{
+ private:
+ /// of course we must know the printer which execute the job
+ VclPtr<SfxPrinter> m_pPrinter;
+ /// this describes the target location for the printed temp file
+ OUString m_sTargetURL;
+ /// it holds the temp file alive, till the print job will finish and remove it from disk automatically if the object die
+ ::utl::TempFile* m_pTempFile;
+
+ public:
+ /* initialize this watcher but don't start it */
+ ImplUCBPrintWatcher( SfxPrinter* pPrinter, ::utl::TempFile* pTempFile, const OUString& sTargetURL )
+ : m_pPrinter ( pPrinter )
+ , m_sTargetURL( sTargetURL )
+ , m_pTempFile ( pTempFile )
+ {}
+
+ /* waits for finishing of the print job and moves the temp file afterwards
+ Note: Starting of the job is done outside this thread!
+ But we have to free some of the given resources on heap!
+ */
+ void SAL_CALL run() override
+ {
+ osl_setThreadName("ImplUCBPrintWatcher");
+
+ /* SAFE { */
+ {
+ SolarMutexGuard aGuard;
+ while( m_pPrinter->IsPrinting() && !Application::IsQuit())
+ Application::Yield();
+ m_pPrinter.clear(); // don't delete it! It's borrowed only :-)
+ }
+ /* } SAFE */
+
+ // lock for further using of our member isn't necessary - because
+ // we run alone by definition. Nobody join for us nor use us...
+ moveAndDeleteTemp(&m_pTempFile,m_sTargetURL);
+
+ // finishing of this run() method will call onTerminate() automatically
+ // kill this thread there!
+ }
+
+ /* nobody wait for this thread. We must kill ourself ...
+ */
+ void SAL_CALL onTerminated() override
+ {
+ delete this;
+ }
+
+ /* static helper to move the temp. file to the target location by using the ucb
+ It's static to be usable from outside too. So it's not really necessary to start
+ the thread, if finishing of the job was detected outside this thread.
+ But it must be called without using a corresponding thread for the given parameter!
+ */
+ static void moveAndDeleteTemp( ::utl::TempFile** ppTempFile, std::u16string_view sTargetURL )
+ {
+ // move the file
+ try
+ {
+ INetURLObject aSplitter(sTargetURL);
+ OUString sFileName = aSplitter.getName(
+ INetURLObject::LAST_SEGMENT,
+ true,
+ INetURLObject::DecodeMechanism::WithCharset);
+ if (aSplitter.removeSegment() && !sFileName.isEmpty())
+ {
+ ::ucbhelper::Content aSource(
+ (*ppTempFile)->GetURL(),
+ css::uno::Reference< css::ucb::XCommandEnvironment >(),
+ comphelper::getProcessComponentContext());
+
+ ::ucbhelper::Content aTarget(
+ aSplitter.GetMainURL(INetURLObject::DecodeMechanism::NONE),
+ css::uno::Reference< css::ucb::XCommandEnvironment >(),
+ comphelper::getProcessComponentContext());
+
+ aTarget.transferContent(
+ aSource,
+ ::ucbhelper::InsertOperation::Copy,
+ sFileName,
+ css::ucb::NameClash::OVERWRITE);
+ }
+ }
+ catch (const css::uno::Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "sfx.doc", "");
+ }
+
+ // kill the temp file!
+ delete *ppTempFile;
+ *ppTempFile = nullptr;
+ }
+};
+
+}
+
+// XPrintable
+
+void SAL_CALL SfxPrintHelper::print(const uno::Sequence< beans::PropertyValue >& rOptions)
+{
+ if( Application::GetSettings().GetMiscSettings().GetDisablePrinting() )
+ return;
+
+ // object already disposed?
+ // object already disposed?
+ SolarMutexGuard aGuard;
+
+ // get view for sfx printing capabilities
+ SfxViewFrame *pViewFrm = m_pData->m_pObjectShell.is() ?
+ SfxViewFrame::GetFirst( m_pData->m_pObjectShell.get(), false ) : nullptr;
+ if ( !pViewFrm )
+ return;
+ SfxViewShell* pView = pViewFrm->GetViewShell();
+ if ( !pView )
+ return;
+ bool bMonitor = false;
+ // We need this information at the end of this method, if we start the vcl printer
+ // by executing the slot. Because if it is a ucb relevant URL we must wait for
+ // finishing the print job and move the temporary local file by using the ucb
+ // to the right location. But in case of no file name is given or it is already
+ // a local one we can suppress this special handling. Because then vcl makes all
+ // right for us.
+ OUString sUcbUrl;
+ ::utl::TempFile* pUCBPrintTempFile = nullptr;
+
+ uno::Sequence < beans::PropertyValue > aCheckedArgs( rOptions.getLength() );
+ auto pCheckedArgs = aCheckedArgs.getArray();
+ sal_Int32 nProps = 0;
+ bool bWaitUntilEnd = false;
+ sal_Int16 nDuplexMode = css::view::DuplexMode::UNKNOWN;
+ for ( const beans::PropertyValue &rProp : rOptions )
+ {
+ // get Property-Value from options
+ // FileName-Property?
+ if ( rProp.Name == "FileName" )
+ {
+ // unpack th URL and check for a valid and well known protocol
+ OUString sTemp;
+ if (
+ ( rProp.Value.getValueType()!=cppu::UnoType<OUString>::get()) ||
+ (!(rProp.Value>>=sTemp))
+ )
+ {
+ throw css::lang::IllegalArgumentException();
+ }
+
+ OUString sPath;
+ OUString sURL (sTemp);
+ INetURLObject aCheck(sURL );
+ if (aCheck.GetProtocol()==INetProtocol::NotValid)
+ {
+ // OK - it's not a valid URL. But may it's a simple
+ // system path directly. It will be supported for historical
+ // reasons. Otherwise we break too much external code...
+ // We try to convert it to a file URL. If it's possible
+ // we put the system path to the item set and let vcl work with it.
+ // No ucb or thread will be necessary then. In case it couldn't be
+ // converted it's not a URL nor a system path. Then we can't accept
+ // this parameter and have to throw an exception.
+ const OUString& sSystemPath(sTemp);
+ OUString sFileURL;
+ if (::osl::FileBase::getFileURLFromSystemPath(sSystemPath,sFileURL)!=::osl::FileBase::E_None)
+ throw css::lang::IllegalArgumentException();
+ pCheckedArgs[nProps].Name = rProp.Name;
+ pCheckedArgs[nProps++].Value <<= sFileURL;
+ // and append the local filename
+ aCheckedArgs.realloc( aCheckedArgs.getLength()+1 );
+ pCheckedArgs = aCheckedArgs.getArray();
+ pCheckedArgs[nProps].Name = "LocalFileName";
+ pCheckedArgs[nProps++].Value <<= sTemp;
+ }
+ else
+ // It's a valid URL. but now we must know, if it is a local one or not.
+ // It's a question of using ucb or not!
+ if (osl::FileBase::getSystemPathFromFileURL(sURL, sPath) == osl::FileBase::E_None)
+ {
+ // it's a local file, we can use vcl without special handling
+ // And we have to use the system notation of the incoming URL.
+ // But it into the descriptor and let the slot be executed at
+ // the end of this method.
+ pCheckedArgs[nProps].Name = rProp.Name;
+ pCheckedArgs[nProps++].Value <<= sTemp;
+ // and append the local filename
+ aCheckedArgs.realloc( aCheckedArgs.getLength()+1 );
+ pCheckedArgs = aCheckedArgs.getArray();
+ pCheckedArgs[nProps].Name = "LocalFileName";
+ pCheckedArgs[nProps++].Value <<= sPath;
+ }
+ else
+ {
+ // it's a ucb target. So we must use a temp. file for vcl
+ // and move it after printing by using the ucb.
+ // Create a temp file on the heap (because it must delete the
+ // real file on disk automatically if it die - bt we have to share it with
+ // some other sources ... e.g. the ImplUCBPrintWatcher).
+ // And we put the name of this temp file to the descriptor instead
+ // of the URL. The URL we save for later using separately.
+ // Execution of the print job will be done later by executing
+ // a slot ...
+ if(!pUCBPrintTempFile)
+ pUCBPrintTempFile = new ::utl::TempFile();
+ pUCBPrintTempFile->EnableKillingFile();
+
+ //FIXME: does it work?
+ pCheckedArgs[nProps].Name = "LocalFileName";
+ pCheckedArgs[nProps++].Value <<= pUCBPrintTempFile->GetFileName();
+ sUcbUrl = sURL;
+ }
+ }
+
+ // CopyCount-Property
+ else if ( rProp.Name == "CopyCount" )
+ {
+ sal_Int32 nCopies = 0;
+ if ( !( rProp.Value >>= nCopies ) )
+ throw css::lang::IllegalArgumentException();
+ pCheckedArgs[nProps].Name = rProp.Name;
+ pCheckedArgs[nProps++].Value <<= nCopies;
+ }
+
+ // Collate-Property
+ // Sort-Property (deprecated)
+ else if ( rProp.Name == "Collate" || rProp.Name == "Sort" )
+ {
+ bool bTemp;
+ if ( !(rProp.Value >>= bTemp) )
+ throw css::lang::IllegalArgumentException();
+ pCheckedArgs[nProps].Name = "Collate";
+ pCheckedArgs[nProps++].Value <<= bTemp;
+ }
+
+ else if ( rProp.Name == "SinglePrintJobs" )
+ {
+ bool bTemp;
+ if ( !(rProp.Value >>= bTemp) )
+ throw css::lang::IllegalArgumentException();
+ pCheckedArgs[nProps].Name = "SinglePrintJobs";
+ pCheckedArgs[nProps++].Value <<= bTemp;
+ }
+
+ // Pages-Property
+ else if ( rProp.Name == "Pages" )
+ {
+ OUString sTemp;
+ if( !(rProp.Value >>= sTemp) )
+ throw css::lang::IllegalArgumentException();
+ pCheckedArgs[nProps].Name = rProp.Name;
+ pCheckedArgs[nProps++].Value <<= sTemp;
+ }
+
+ // MonitorVisible
+ else if ( rProp.Name == "MonitorVisible" )
+ {
+ if( !(rProp.Value >>= bMonitor) )
+ throw css::lang::IllegalArgumentException();
+ pCheckedArgs[nProps].Name = rProp.Name;
+ pCheckedArgs[nProps++].Value <<= bMonitor;
+ }
+
+ // Wait
+ else if ( rProp.Name == "Wait" )
+ {
+ if ( !(rProp.Value >>= bWaitUntilEnd) )
+ throw css::lang::IllegalArgumentException();
+ pCheckedArgs[nProps].Name = rProp.Name;
+ pCheckedArgs[nProps++].Value <<= bWaitUntilEnd;
+ }
+
+ else if ( rProp.Name == "DuplexMode" )
+ {
+ if ( !(rProp.Value >>= nDuplexMode ) )
+ throw css::lang::IllegalArgumentException();
+ pCheckedArgs[nProps].Name = rProp.Name;
+ pCheckedArgs[nProps++].Value <<= nDuplexMode;
+ }
+ }
+
+ if ( nProps != aCheckedArgs.getLength() )
+ aCheckedArgs.realloc(nProps);
+
+ // Execute the print request every time.
+ // It doesn't matter if it is a real printer used or we print to a local file
+ // nor if we print to a temp file and move it afterwards by using the ucb.
+ // That will be handled later. see pUCBPrintFile below!
+ pView->ExecPrint( aCheckedArgs, true, false );
+
+ // Ok - may be execution before has finished (or started!) printing.
+ // And may it was a printing to a file.
+ // Now we have to check if we can move the file (if necessary) via UCB to its right location.
+ // Cases:
+ // a) printing finished => move the file directly and forget the watcher thread
+ // b) printing is asynchron and runs currently => start watcher thread and exit this method
+ // This thread make all necessary things by itself.
+ if (!pUCBPrintTempFile)
+ return;
+
+ // a)
+ SfxPrinter* pPrinter = pView->GetPrinter();
+ if ( ! pPrinter->IsPrinting() )
+ ImplUCBPrintWatcher::moveAndDeleteTemp(&pUCBPrintTempFile,sUcbUrl);
+ // b)
+ else
+ {
+ // Note: we create(d) some resource on the heap (thread and temp file).
+ // They will be deleted by the thread automatically if it finishes its run() method.
+ ImplUCBPrintWatcher* pWatcher = new ImplUCBPrintWatcher( pPrinter, pUCBPrintTempFile, sUcbUrl );
+ pWatcher->create();
+ }
+}
+
+void IMPL_PrintListener_DataContainer::Notify( SfxBroadcaster& rBC, const SfxHint& rHint )
+{
+ const SfxPrintingHint* pPrintHint = dynamic_cast<const SfxPrintingHint*>(&rHint);
+ if ( &rBC != m_pObjectShell.get()
+ || !pPrintHint
+ || pPrintHint->GetWhich() == SFX_PRINTABLESTATE_CANCELJOB )
+ return;
+
+ if ( pPrintHint->GetWhich() == css::view::PrintableState_JOB_STARTED )
+ {
+ if ( !m_xPrintJob.is() )
+ m_xPrintJob = new SfxPrintJob_Impl( this );
+ m_aPrintOptions = pPrintHint->GetOptions();
+ }
+
+ std::unique_lock aGuard(m_aMutex);
+ if (!m_aJobListeners.getLength(aGuard))
+ return;
+ view::PrintJobEvent aEvent;
+ aEvent.Source = m_xPrintJob;
+ aEvent.State = pPrintHint->GetWhich();
+
+ comphelper::OInterfaceIteratorHelper4 pIterator(aGuard, m_aJobListeners);
+ aGuard.unlock();
+ while (pIterator.hasMoreElements())
+ pIterator.next()->printJobEvent( aEvent );
+}
+
+void SAL_CALL SfxPrintHelper::addPrintJobListener( const css::uno::Reference< css::view::XPrintJobListener >& xListener )
+{
+ std::unique_lock aGuard(m_pData->m_aMutex);
+ m_pData->m_aJobListeners.addInterface( aGuard, xListener );
+}
+
+void SAL_CALL SfxPrintHelper::removePrintJobListener( const css::uno::Reference< css::view::XPrintJobListener >& xListener )
+{
+ std::unique_lock aGuard(m_pData->m_aMutex);
+ m_pData->m_aJobListeners.removeInterface( aGuard, xListener );
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/doc/printhelper.hxx b/sfx2/source/doc/printhelper.hxx
new file mode 100644
index 000000000..a38e3d7a4
--- /dev/null
+++ b/sfx2/source/doc/printhelper.hxx
@@ -0,0 +1,68 @@
+/* -*- 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_DOC_PRINTHELPER_HXX
+#define INCLUDED_SFX2_SOURCE_DOC_PRINTHELPER_HXX
+
+#include <memory>
+#include <sal/config.h>
+#include <sfx2/viewsh.hxx>
+#include <sal/types.h>
+
+#include <com/sun/star/view/XPrintable.hpp>
+#include <com/sun/star/view/XPrintJobBroadcaster.hpp>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/uno/Any.hxx>
+#include <com/sun/star/lang/XInitialization.hpp>
+#include <cppuhelper/implbase.hxx>
+
+struct IMPL_PrintListener_DataContainer;
+class SfxViewShell;
+class SfxPrinter;
+
+class SfxPrintHelper : public cppu::WeakImplHelper
+ < css::view::XPrintable
+ , css::view::XPrintJobBroadcaster
+ , css::lang::XInitialization >
+{
+public:
+
+ SfxPrintHelper() ;
+ virtual ~SfxPrintHelper() override ;
+
+ void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override;
+ virtual void SAL_CALL addPrintJobListener( const css::uno::Reference< css::view::XPrintJobListener >& xListener ) override;
+ virtual void SAL_CALL removePrintJobListener( const css::uno::Reference< css::view::XPrintJobListener >& xListener ) override;
+ virtual css::uno::Sequence< css::beans::PropertyValue > SAL_CALL getPrinter() override;
+ virtual void SAL_CALL setPrinter( const css::uno::Sequence< css::beans::PropertyValue >& seqPrinter ) override;
+ virtual void SAL_CALL print( const css::uno::Sequence< css::beans::PropertyValue >& seqOptions ) override;
+
+private:
+
+ std::unique_ptr<IMPL_PrintListener_DataContainer> m_pData ;
+ void impl_setPrinter(const css::uno::Sequence< css::beans::PropertyValue >& rPrinter,
+ VclPtr<SfxPrinter>& pPrinter,
+ SfxPrinterChangeFlags& nChangeFlags,
+ SfxViewShell*& pViewSh);
+} ;
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/doc/saveastemplatedlg.cxx b/sfx2/source/doc/saveastemplatedlg.cxx
new file mode 100644
index 000000000..6f9fd1b47
--- /dev/null
+++ b/sfx2/source/doc/saveastemplatedlg.cxx
@@ -0,0 +1,178 @@
+/* -*- 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 <comphelper/processfactory.hxx>
+#include <comphelper/string.hxx>
+#include <comphelper/storagehelper.hxx>
+#include <sfx2/sfxresid.hxx>
+#include <sfx2/app.hxx>
+#include <sfx2/fcontnr.hxx>
+#include <sfx2/docfac.hxx>
+#include <sfx2/doctempl.hxx>
+#include <sfx2/docfilt.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+#include <sot/storage.hxx>
+
+#include <com/sun/star/frame/DocumentTemplates.hpp>
+#include <com/sun/star/frame/XStorable.hpp>
+
+#include <sfx2/strings.hrc>
+
+#include <saveastemplatedlg.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::frame;
+
+// Class SfxSaveAsTemplateDialog --------------------------------------------------
+
+SfxSaveAsTemplateDialog::SfxSaveAsTemplateDialog(weld::Window* pParent, const uno::Reference<frame::XModel> &rModel)
+ : GenericDialogController(pParent, "sfx/ui/saveastemplatedlg.ui", "SaveAsTemplateDialog")
+ , m_xLBCategory(m_xBuilder->weld_tree_view("categorylb"))
+ , m_xCBXDefault(m_xBuilder->weld_check_button("defaultcb"))
+ , m_xTemplateNameEdit(m_xBuilder->weld_entry("name_entry"))
+ , m_xOKButton(m_xBuilder->weld_button("ok"))
+ , mnRegionPos(0)
+ , m_xModel(rModel)
+{
+ m_xLBCategory->append_text(SfxResId(STR_CATEGORY_NONE));
+ initialize();
+ SetCategoryLBEntries(msCategories);
+
+ m_xTemplateNameEdit->connect_changed(LINK(this, SfxSaveAsTemplateDialog, TemplateNameEditHdl));
+ m_xLBCategory->connect_changed(LINK(this, SfxSaveAsTemplateDialog, SelectCategoryHdl));
+ m_xLBCategory->set_size_request(m_xLBCategory->get_approximate_digit_width() * 32,
+ m_xLBCategory->get_height_rows(8));
+ m_xOKButton->connect_clicked(LINK(this, SfxSaveAsTemplateDialog, OkClickHdl));
+
+ m_xOKButton->set_sensitive(false);
+ m_xOKButton->set_label(SfxResId(STR_SAVEDOC));
+}
+
+IMPL_LINK_NOARG(SfxSaveAsTemplateDialog, OkClickHdl, weld::Button&, void)
+{
+ std::unique_ptr<weld::MessageDialog> xQueryDlg(Application::CreateMessageDialog(m_xDialog.get(), VclMessageType::Question,
+ VclButtonsType::YesNo, OUString()));
+ if(!IsTemplateNameUnique())
+ {
+ OUString sQueryMsg(SfxResId(STR_QMSG_TEMPLATE_OVERWRITE));
+ sQueryMsg = sQueryMsg.replaceFirst("$1",msTemplateName);
+ xQueryDlg->set_primary_text(sQueryMsg.replaceFirst("$2", msSelectedCategory));
+
+ if (xQueryDlg->run() == RET_NO)
+ return;
+ }
+
+ if (SaveTemplate())
+ m_xDialog->response(RET_OK);
+ else
+ {
+ OUString sText( SfxResId(STR_ERROR_SAVEAS) );
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(m_xDialog.get(), VclMessageType::Warning,
+ VclButtonsType::Ok, sText.replaceFirst("$1", msTemplateName)));
+ xBox->run();
+ }
+}
+
+IMPL_LINK_NOARG(SfxSaveAsTemplateDialog, TemplateNameEditHdl, weld::Entry&, void)
+{
+ msTemplateName = comphelper::string::strip(m_xTemplateNameEdit->get_text(), ' ');
+ SelectCategoryHdl(*m_xLBCategory);
+}
+
+IMPL_LINK_NOARG(SfxSaveAsTemplateDialog, SelectCategoryHdl, weld::TreeView&, void)
+{
+ if (m_xLBCategory->get_selected_index() == 0)
+ {
+ msSelectedCategory = OUString();
+ m_xOKButton->set_sensitive(false);
+ }
+ else
+ {
+ msSelectedCategory = m_xLBCategory->get_selected_text();
+ m_xOKButton->set_sensitive(!msTemplateName.isEmpty());
+ }
+}
+
+void SfxSaveAsTemplateDialog::initialize()
+{
+ sal_uInt16 nCount = maDocTemplates.GetRegionCount();
+ for (sal_uInt16 i = 0; i < nCount; ++i)
+ {
+ OUString sCategoryName(maDocTemplates.GetFullRegionName(i));
+ msCategories.push_back(sCategoryName);
+ }
+}
+
+void SfxSaveAsTemplateDialog::SetCategoryLBEntries(const std::vector<OUString>& rFolderNames)
+{
+ for (size_t i = 0, n = rFolderNames.size(); i < n; ++i)
+ m_xLBCategory->insert_text(i+1, rFolderNames[i]);
+ m_xLBCategory->select(0);
+}
+
+bool SfxSaveAsTemplateDialog::IsTemplateNameUnique()
+{
+ std::vector<OUString>::iterator it=find(msCategories.begin(), msCategories.end(), msSelectedCategory);
+ mnRegionPos = std::distance(msCategories.begin(), it);
+
+ sal_uInt16 nEntries = maDocTemplates.GetCount(mnRegionPos);
+ for(sal_uInt16 i = 0; i < nEntries; i++)
+ {
+ OUString aName = maDocTemplates.GetName(mnRegionPos, i);
+ if(aName == msTemplateName)
+ return false;
+ }
+
+ return true;
+}
+
+bool SfxSaveAsTemplateDialog::SaveTemplate()
+{
+ uno::Reference< frame::XStorable > xStorable(m_xModel, uno::UNO_QUERY_THROW );
+
+ uno::Reference< frame::XDocumentTemplates > xTemplates(frame::DocumentTemplates::create(comphelper::getProcessComponentContext()) );
+
+ if (!xTemplates->storeTemplate( msSelectedCategory, msTemplateName, xStorable ))
+ return false;
+
+ sal_uInt16 nDocId = maDocTemplates.GetCount(mnRegionPos);
+ OUString sURL = maDocTemplates.GetTemplateTargetURLFromComponent(msSelectedCategory, msTemplateName);
+ bool bIsSaved = maDocTemplates.InsertTemplate( mnRegionPos, nDocId, msTemplateName, sURL);
+
+ if (!bIsSaved)
+ return false;
+
+ if (!sURL.isEmpty() && m_xCBXDefault->get_active())
+ {
+ OUString aServiceName;
+ try
+ {
+ uno::Reference< embed::XStorage > xStorage =
+ comphelper::OStorageHelper::GetStorageFromURL( sURL, embed::ElementModes::READ );
+
+ SotClipboardFormatId nFormat = SotStorage::GetFormatID( xStorage );
+
+ std::shared_ptr<const SfxFilter> pFilter = SfxGetpApp()->GetFilterMatcher().GetFilter4ClipBoardId( nFormat );
+
+ if ( pFilter )
+ aServiceName = pFilter->GetServiceName();
+ }
+ catch( uno::Exception& )
+ {}
+
+ if(!aServiceName.isEmpty())
+ SfxObjectFactory::SetStandardTemplate(aServiceName, sURL);
+ }
+
+ maDocTemplates.Update();
+ return true;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/doc/sfxbasemodel.cxx b/sfx2/source/doc/sfxbasemodel.cxx
new file mode 100644
index 000000000..dc9a056e3
--- /dev/null
+++ b/sfx2/source/doc/sfxbasemodel.cxx
@@ -0,0 +1,4561 @@
+/* -*- 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 <algorithm>
+#include <memory>
+#include <config_features.h>
+
+#include <sfx2/sfxbasemodel.hxx>
+
+#include <com/sun/star/datatransfer/UnsupportedFlavorException.hpp>
+#include <com/sun/star/task/XInteractionHandler.hpp>
+#include <com/sun/star/task/ErrorCodeIOException.hpp>
+#include <com/sun/star/task/ErrorCodeRequest.hpp>
+#include <com/sun/star/view/XSelectionSupplier.hpp>
+#include <com/sun/star/view/XPrintJobListener.hpp>
+#include <com/sun/star/lang/DisposedException.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <com/sun/star/lang/NoSupportException.hpp>
+#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
+#include <com/sun/star/lang/NotInitializedException.hpp>
+#include <com/sun/star/frame/Desktop.hpp>
+#include <com/sun/star/frame/IllegalArgumentIOException.hpp>
+#include <com/sun/star/frame/XUntitledNumbers.hpp>
+#include <com/sun/star/frame/DoubleInitializationException.hpp>
+#include <com/sun/star/embed/XStorage.hpp>
+#include <com/sun/star/document/XStorageChangeListener.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/beans/XPropertySetInfo.hpp>
+#include <com/sun/star/container/XIndexContainer.hpp>
+#include <com/sun/star/script/provider/theMasterScriptProviderFactory.hpp>
+#include <com/sun/star/script/provider/XScriptProvider.hpp>
+#include <com/sun/star/ui/UIConfigurationManager.hpp>
+#include <com/sun/star/embed/ElementModes.hpp>
+#include <com/sun/star/embed/Aspects.hpp>
+#include <com/sun/star/document/DocumentProperties.hpp>
+#include <com/sun/star/frame/XTransientDocumentsDocumentContentFactory.hpp>
+#include <com/sun/star/ucb/XCommandEnvironment.hpp>
+#include <com/sun/star/ucb/ContentCreationException.hpp>
+#include <com/sun/star/ucb/CommandAbortedException.hpp>
+#include <com/sun/star/util/XCloneable.hpp>
+#include <com/sun/star/util/InvalidStateException.hpp>
+#include <com/sun/star/util/CloseVetoException.hpp>
+#include <comphelper/enumhelper.hxx>
+#include <comphelper/indexedpropertyvalues.hxx>
+#include <comphelper/string.hxx>
+
+#include <cppuhelper/implbase.hxx>
+#include <comphelper/multicontainer2.hxx>
+#include <cppuhelper/exc_hlp.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <comphelper/sequenceashashmap.hxx>
+#include <comphelper/namedvaluecollection.hxx>
+#include <o3tl/safeint.hxx>
+#include <o3tl/string_view.hxx>
+#include <svl/itemset.hxx>
+#include <svl/stritem.hxx>
+#include <svl/eitem.hxx>
+#include <svl/grabbagitem.hxx>
+#include <tools/urlobj.hxx>
+#include <tools/debug.hxx>
+#include <tools/diagnose_ex.h>
+#include <tools/svborder.hxx>
+#include <unotools/tempfile.hxx>
+#include <osl/mutex.hxx>
+#include <vcl/errcode.hxx>
+#include <vcl/filter/SvmWriter.hxx>
+#include <vcl/salctype.hxx>
+#include <vcl/gdimtf.hxx>
+#include <comphelper/fileformat.h>
+#include <comphelper/servicehelper.hxx>
+#include <comphelper/storagehelper.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+#include <vcl/transfer.hxx>
+#include <svtools/ehdl.hxx>
+#include <svtools/sfxecode.hxx>
+#include <sal/log.hxx>
+#include <framework/configimporter.hxx>
+#include <framework/titlehelper.hxx>
+#include <comphelper/numberedcollection.hxx>
+#include <unotools/ucbhelper.hxx>
+#include <ucbhelper/content.hxx>
+
+#include <sfx2/sfxbasecontroller.hxx>
+#include <sfx2/viewfac.hxx>
+#include <workwin.hxx>
+#include <sfx2/signaturestate.hxx>
+#include <sfx2/sfxuno.hxx>
+#include <objshimp.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/viewsh.hxx>
+#include <sfx2/docfile.hxx>
+#include <sfx2/docfilt.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/module.hxx>
+#include <basic/basmgr.hxx>
+#include <sfx2/event.hxx>
+#include <eventsupplier.hxx>
+#include <sfx2/sfxsids.hrc>
+#include <sfx2/strings.hrc>
+#include <sfx2/app.hxx>
+#include <sfx2/docfac.hxx>
+#include <sfx2/fcontnr.hxx>
+#include <sfx2/docstoragemodifylistener.hxx>
+#include <sfx2/brokenpackageint.hxx>
+#include "graphhelp.hxx"
+#include <docundomanager.hxx>
+#include <openurlhint.hxx>
+#include <sfx2/msgpool.hxx>
+#include <sfx2/DocumentMetadataAccess.hxx>
+#include "printhelper.hxx"
+#include <sfx2/sfxresid.hxx>
+#include <comphelper/profilezone.hxx>
+#include <vcl/threadex.hxx>
+#include <unotools/mediadescriptor.hxx>
+
+// namespaces
+
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using ::com::sun::star::beans::PropertyValue;
+using ::com::sun::star::document::CmisProperty;
+using ::com::sun::star::frame::XFrame;
+using ::com::sun::star::frame::XController;
+using ::com::sun::star::frame::XController2;
+using ::com::sun::star::lang::IllegalArgumentException;
+using ::com::sun::star::io::IOException;
+using ::com::sun::star::uno::Sequence;
+using ::com::sun::star::document::XDocumentRecovery;
+using ::com::sun::star::document::XUndoManager;
+using ::com::sun::star::document::XUndoAction;
+using ::com::sun::star::frame::XModel;
+
+namespace {
+
+/** This Listener is used to get notified when the XDocumentProperties of the
+ XModel change.
+ */
+class SfxDocInfoListener_Impl : public ::cppu::WeakImplHelper<
+ util::XModifyListener >
+{
+
+public:
+ SfxObjectShell& m_rShell;
+
+ explicit SfxDocInfoListener_Impl( SfxObjectShell& i_rDoc )
+ : m_rShell(i_rDoc)
+ { };
+
+ virtual void SAL_CALL disposing( const lang::EventObject& ) override;
+ virtual void SAL_CALL modified( const lang::EventObject& ) override;
+};
+
+}
+
+void SAL_CALL SfxDocInfoListener_Impl::modified( const lang::EventObject& )
+{
+ SolarMutexGuard aSolarGuard;
+
+ // notify changes to the SfxObjectShell
+ m_rShell.FlushDocInfo();
+}
+
+void SAL_CALL SfxDocInfoListener_Impl::disposing( const lang::EventObject& )
+{
+}
+
+
+// impl. declarations
+
+
+struct IMPL_SfxBaseModel_DataContainer : public ::sfx2::IModifiableDocument
+{
+ // counter for SfxBaseModel instances created.
+ static sal_Int64 g_nInstanceCounter ;
+ SfxObjectShellRef m_pObjectShell ;
+ OUString m_sURL ;
+ OUString m_sRuntimeUID ;
+ OUString m_aPreusedFilterName ;
+ comphelper::OMultiTypeInterfaceContainerHelper2 m_aInterfaceContainer ;
+ std::unordered_map<css::uno::Reference< css::drawing::XShape >,
+ std::vector<css::uno::Reference< css::document::XShapeEventListener >>> maShapeListeners;
+ Reference< XInterface > m_xParent ;
+ Reference< frame::XController > m_xCurrent ;
+ Reference< document::XDocumentProperties > m_xDocumentProperties ;
+ Reference< script::XStarBasicAccess > m_xStarBasicAccess ;
+ Reference< container::XNameReplace > m_xEvents ;
+ Sequence< beans::PropertyValue> m_seqArguments ;
+ std::vector< Reference< frame::XController > > m_seqControllers ;
+ Reference< container::XIndexAccess > m_contViewData ;
+ sal_uInt16 m_nControllerLockCount ;
+ bool m_bClosed ;
+ bool m_bClosing ;
+ bool m_bSaving ;
+ bool m_bSuicide ;
+ bool m_bExternalTitle ;
+ bool m_bModifiedSinceLastSave ;
+ Reference< view::XPrintable> m_xPrintable ;
+ Reference< ui::XUIConfigurationManager2 > m_xUIConfigurationManager;
+ ::rtl::Reference< ::sfx2::DocumentStorageModifyListener > m_pStorageModifyListen ;
+ OUString m_sModuleIdentifier ;
+ Reference< frame::XTitle > m_xTitleHelper ;
+ Reference< frame::XUntitledNumbers > m_xNumberedControllers ;
+ Reference< rdf::XDocumentMetadataAccess> m_xDocumentMetadata ;
+ ::rtl::Reference< ::sfx2::DocumentUndoManager > m_pDocumentUndoManager ;
+ Sequence< document::CmisProperty> m_cmisProperties ;
+ std::shared_ptr<SfxGrabBagItem> m_xGrabBagItem ;
+
+ IMPL_SfxBaseModel_DataContainer( ::osl::Mutex& rMutex, SfxObjectShell* pObjectShell )
+ : m_pObjectShell ( pObjectShell )
+ , m_aInterfaceContainer ( rMutex )
+ , m_nControllerLockCount ( 0 )
+ , m_bClosed ( false )
+ , m_bClosing ( false )
+ , m_bSaving ( false )
+ , m_bSuicide ( false )
+ , m_bExternalTitle ( false )
+ , m_bModifiedSinceLastSave( false )
+ {
+ // increase global instance counter.
+ ++g_nInstanceCounter;
+ // set own Runtime UID
+ m_sRuntimeUID = OUString::number( g_nInstanceCounter );
+ }
+
+ virtual ~IMPL_SfxBaseModel_DataContainer()
+ {
+ }
+
+ // ::sfx2::IModifiableDocument
+ virtual void storageIsModified() override
+ {
+ if ( m_pObjectShell.is() && !m_pObjectShell->IsModified() )
+ m_pObjectShell->SetModified();
+ }
+
+ void impl_setDocumentProperties(
+ const Reference< document::XDocumentProperties >& );
+
+ Reference<rdf::XDocumentMetadataAccess> GetDMA()
+ {
+ if (!m_xDocumentMetadata.is())
+ {
+ OSL_ENSURE(m_pObjectShell.is(), "GetDMA: no object shell?");
+ if (!m_pObjectShell.is())
+ {
+ return nullptr;
+ }
+
+ const Reference<XComponentContext> xContext(
+ ::comphelper::getProcessComponentContext());
+ const Reference<frame::XModel> xModel(
+ m_pObjectShell->GetModel());
+ const Reference<lang::XMultiComponentFactory> xMsf(
+ xContext->getServiceManager());
+ const Reference<frame::
+ XTransientDocumentsDocumentContentFactory> xTDDCF(
+ xMsf->createInstanceWithContext(
+ "com.sun.star.frame.TransientDocumentsDocumentContentFactory",
+ xContext),
+ UNO_QUERY_THROW);
+ const Reference<ucb::XContent> xContent(
+ xTDDCF->createDocumentContent(xModel) );
+ OSL_ENSURE(xContent.is(), "GetDMA: cannot create DocumentContent");
+ if (!xContent.is())
+ {
+ return nullptr;
+ }
+ OUString uri = xContent->getIdentifier()->getContentIdentifier();
+ OSL_ENSURE(!uri.isEmpty(), "GetDMA: empty uri?");
+ if (!uri.isEmpty() && !uri.endsWith("/"))
+ {
+ uri += "/";
+ }
+
+ m_xDocumentMetadata = new ::sfx2::DocumentMetadataAccess(
+ xContext, *m_pObjectShell, uri);
+ }
+ return m_xDocumentMetadata;
+ }
+
+ Reference<rdf::XDocumentMetadataAccess> CreateDMAUninitialized()
+ {
+ return (m_pObjectShell.is())
+ ? new ::sfx2::DocumentMetadataAccess(
+ ::comphelper::getProcessComponentContext(), *m_pObjectShell)
+ : nullptr;
+ }
+};
+
+// static member initialization.
+sal_Int64 IMPL_SfxBaseModel_DataContainer::g_nInstanceCounter = 0;
+
+namespace {
+
+// Listener that forwards notifications from the PrintHelper to the "real" listeners
+class SfxPrintHelperListener_Impl : public ::cppu::WeakImplHelper< view::XPrintJobListener >
+{
+public:
+ IMPL_SfxBaseModel_DataContainer* m_pData;
+ explicit SfxPrintHelperListener_Impl( IMPL_SfxBaseModel_DataContainer* pData )
+ : m_pData( pData )
+ {}
+
+ virtual void SAL_CALL disposing( const lang::EventObject& aEvent ) override ;
+ virtual void SAL_CALL printJobEvent( const view::PrintJobEvent& rEvent ) override;
+};
+
+}
+
+void SAL_CALL SfxPrintHelperListener_Impl::disposing( const lang::EventObject& )
+{
+ m_pData->m_xPrintable = nullptr;
+}
+
+void SAL_CALL SfxPrintHelperListener_Impl::printJobEvent( const view::PrintJobEvent& rEvent )
+{
+ ::comphelper::OInterfaceContainerHelper2* pContainer = m_pData->m_aInterfaceContainer.getContainer( cppu::UnoType<view::XPrintJobListener>::get());
+ if ( pContainer!=nullptr )
+ {
+ ::comphelper::OInterfaceIteratorHelper2 pIterator(*pContainer);
+ while (pIterator.hasMoreElements())
+ static_cast<view::XPrintJobListener*>(pIterator.next())->printJobEvent( rEvent );
+ }
+}
+
+namespace {
+
+// SfxOwnFramesLocker ====================================================================================
+// allows to lock all the frames related to the provided SfxObjectShell
+class SfxOwnFramesLocker
+{
+ Sequence< Reference< frame::XFrame > > m_aLockedFrames;
+
+ static vcl::Window* GetVCLWindow( const Reference< frame::XFrame >& xFrame );
+public:
+ explicit SfxOwnFramesLocker( SfxObjectShell const * ObjechShell );
+ ~SfxOwnFramesLocker();
+};
+
+}
+
+SfxOwnFramesLocker::SfxOwnFramesLocker( SfxObjectShell const * pObjectShell )
+{
+ if ( !pObjectShell )
+ return;
+
+ for ( SfxViewFrame *pFrame = SfxViewFrame::GetFirst( pObjectShell );
+ pFrame;
+ pFrame = SfxViewFrame::GetNext( *pFrame, pObjectShell )
+ )
+ {
+ SfxFrame& rSfxFrame = pFrame->GetFrame();
+ try
+ {
+ // get vcl window related to the frame and lock it if it is still not locked
+ const Reference< frame::XFrame >& xFrame = rSfxFrame.GetFrameInterface();
+ vcl::Window* pWindow = GetVCLWindow( xFrame );
+ if ( !pWindow )
+ throw RuntimeException();
+
+ if ( pWindow->IsEnabled() )
+ {
+ pWindow->Disable();
+
+ try
+ {
+ sal_Int32 nLen = m_aLockedFrames.getLength();
+ m_aLockedFrames.realloc( nLen + 1 );
+ m_aLockedFrames.getArray()[nLen] = xFrame;
+ }
+ catch( Exception& )
+ {
+ pWindow->Enable();
+ throw;
+ }
+ }
+ }
+ catch( Exception& )
+ {
+ OSL_FAIL( "Not possible to lock the frame window!" );
+ }
+ }
+}
+
+SfxOwnFramesLocker::~SfxOwnFramesLocker()
+{
+ for ( auto& rFrame : asNonConstRange(m_aLockedFrames) )
+ {
+ try
+ {
+ if ( rFrame.is() )
+ {
+ // get vcl window related to the frame and unlock it
+ vcl::Window* pWindow = GetVCLWindow( rFrame );
+ if ( !pWindow )
+ throw RuntimeException();
+
+ pWindow->Enable();
+
+ rFrame.clear();
+ }
+ }
+ catch( Exception& )
+ {
+ OSL_FAIL( "Can't unlock the frame window!" );
+ }
+ }
+}
+
+vcl::Window* SfxOwnFramesLocker::GetVCLWindow( const Reference< frame::XFrame >& xFrame )
+{
+ VclPtr<vcl::Window> pWindow;
+
+ if ( xFrame.is() )
+ {
+ Reference< awt::XWindow > xWindow = xFrame->getContainerWindow();
+ if ( xWindow.is() )
+ pWindow = VCLUnoHelper::GetWindow( xWindow );
+ }
+
+ return pWindow;
+}
+
+namespace {
+
+// SfxSaveGuard ====================================================================================
+class SfxSaveGuard
+{
+ private:
+ Reference< frame::XModel > m_xModel;
+ IMPL_SfxBaseModel_DataContainer* m_pData;
+ std::unique_ptr<SfxOwnFramesLocker> m_pFramesLock;
+
+ SfxSaveGuard(SfxSaveGuard const &) = delete;
+ void operator =(const SfxSaveGuard&) = delete;
+
+ public:
+ SfxSaveGuard(const Reference< frame::XModel >& xModel ,
+ IMPL_SfxBaseModel_DataContainer* pData);
+ ~SfxSaveGuard();
+};
+
+}
+
+SfxSaveGuard::SfxSaveGuard(const Reference< frame::XModel >& xModel ,
+ IMPL_SfxBaseModel_DataContainer* pData)
+ : m_xModel ( xModel )
+ , m_pData ( pData )
+{
+ if ( m_pData->m_bClosed )
+ throw lang::DisposedException("Object already disposed.");
+
+ m_pData->m_bSaving = true;
+ m_pFramesLock.reset(new SfxOwnFramesLocker( m_pData->m_pObjectShell.get() ));
+}
+
+SfxSaveGuard::~SfxSaveGuard()
+{
+ m_pFramesLock.reset();
+
+ m_pData->m_bSaving = false;
+
+ // m_bSuicide was set e.g. in case someone tried to close a document, while it was used for
+ // storing at the same time. Further m_bSuicide was set to sal_True only if close(sal_True) was called.
+ // So the ownership was delegated to the place where a veto exception was thrown.
+ // Now we have to call close() again and delegate the ownership to the next one, which
+ // can't accept that. Close(sal_False) can't work in this case. Because then the document will may be never closed...
+
+ if ( !m_pData->m_bSuicide )
+ return;
+
+ // Reset this state. In case the new close() request is not accepted by someone else...
+ // it's not a good idea to have two "owners" for close.-)
+ m_pData->m_bSuicide = false;
+ try
+ {
+ Reference< util::XCloseable > xClose(m_xModel, UNO_QUERY);
+ if (xClose.is())
+ xClose->close(true);
+ }
+ catch(const util::CloseVetoException&)
+ {}
+}
+
+SfxBaseModel::SfxBaseModel( SfxObjectShell *pObjectShell )
+: BaseMutex()
+, m_pData( std::make_shared<IMPL_SfxBaseModel_DataContainer>( m_aMutex, pObjectShell ) )
+, m_bSupportEmbeddedScripts( pObjectShell && pObjectShell->Get_Impl() && !pObjectShell->Get_Impl()->m_bNoBasicCapabilities )
+, m_bSupportDocRecovery( pObjectShell && pObjectShell->Get_Impl() && pObjectShell->Get_Impl()->m_bDocRecoverySupport )
+{
+ if ( pObjectShell != nullptr )
+ {
+ StartListening( *pObjectShell ) ;
+ }
+}
+
+// destructor
+SfxBaseModel::~SfxBaseModel()
+{
+}
+
+// XInterface
+Any SAL_CALL SfxBaseModel::queryInterface( const uno::Type& rType )
+{
+ if ( ( !m_bSupportEmbeddedScripts && rType.equals( cppu::UnoType<document::XEmbeddedScripts>::get() ) )
+ || ( !m_bSupportDocRecovery && rType.equals( cppu::UnoType<XDocumentRecovery>::get() ) )
+ )
+ return Any();
+
+ return SfxBaseModel_Base::queryInterface( rType );
+}
+
+
+// XTypeProvider
+
+
+namespace
+{
+ void lcl_stripType( Sequence< uno::Type >& io_rTypes, const uno::Type& i_rTypeToStrip )
+ {
+ Sequence< uno::Type > aStrippedTypes( io_rTypes.getLength() - 1 );
+ ::std::remove_copy_if(
+ std::cbegin(io_rTypes),
+ std::cend(io_rTypes),
+ aStrippedTypes.getArray(),
+ [&i_rTypeToStrip](const uno::Type& aType) { return aType == i_rTypeToStrip; }
+ );
+ io_rTypes = aStrippedTypes;
+ }
+}
+
+Sequence< uno::Type > SAL_CALL SfxBaseModel::getTypes()
+{
+ Sequence< uno::Type > aTypes( SfxBaseModel_Base::getTypes() );
+
+ if ( !m_bSupportEmbeddedScripts )
+ lcl_stripType( aTypes, cppu::UnoType<document::XEmbeddedScripts>::get() );
+
+ if ( !m_bSupportDocRecovery )
+ lcl_stripType( aTypes, cppu::UnoType<XDocumentRecovery>::get() );
+
+ return aTypes;
+}
+
+
+// XTypeProvider
+
+
+Sequence< sal_Int8 > SAL_CALL SfxBaseModel::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+
+// XStarBasicAccess
+
+#if HAVE_FEATURE_SCRIPTING
+
+static Reference< script::XStarBasicAccess > implGetStarBasicAccess( SfxObjectShell const * pObjectShell )
+{
+ Reference< script::XStarBasicAccess > xRet;
+
+#if !HAVE_FEATURE_SCRIPTING
+ (void) pObjectShell;
+#else
+ if( pObjectShell )
+ {
+ BasicManager* pMgr = pObjectShell->GetBasicManager();
+ xRet = getStarBasicAccess( pMgr );
+ }
+#endif
+ return xRet;
+}
+
+#endif
+
+Reference< container::XNameContainer > SAL_CALL SfxBaseModel::getLibraryContainer()
+{
+#if !HAVE_FEATURE_SCRIPTING
+ Reference< container::XNameContainer > dummy;
+
+ return dummy;
+#else
+ SfxModelGuard aGuard( *this );
+
+ Reference< script::XStarBasicAccess >& rxAccess = m_pData->m_xStarBasicAccess;
+ if( !rxAccess.is() && m_pData->m_pObjectShell.is() )
+ rxAccess = implGetStarBasicAccess( m_pData->m_pObjectShell.get() );
+
+ Reference< container::XNameContainer > xRet;
+ if( rxAccess.is() )
+ xRet = rxAccess->getLibraryContainer();
+ return xRet;
+#endif
+}
+
+/**___________________________________________________________________________________________________
+ @seealso XStarBasicAccess
+*/
+void SAL_CALL SfxBaseModel::createLibrary( const OUString& LibName, const OUString& Password,
+ const OUString& ExternalSourceURL, const OUString& LinkTargetURL )
+{
+#if !HAVE_FEATURE_SCRIPTING
+ (void) LibName;
+ (void) Password;
+ (void) ExternalSourceURL;
+ (void) LinkTargetURL;
+#else
+ SfxModelGuard aGuard( *this );
+
+ Reference< script::XStarBasicAccess >& rxAccess = m_pData->m_xStarBasicAccess;
+ if( !rxAccess.is() && m_pData->m_pObjectShell.is() )
+ rxAccess = implGetStarBasicAccess( m_pData->m_pObjectShell.get() );
+
+ if( rxAccess.is() )
+ rxAccess->createLibrary( LibName, Password, ExternalSourceURL, LinkTargetURL );
+#endif
+}
+
+/**___________________________________________________________________________________________________
+ @seealso XStarBasicAccess
+*/
+void SAL_CALL SfxBaseModel::addModule( const OUString& LibraryName, const OUString& ModuleName,
+ const OUString& Language, const OUString& Source )
+{
+#if !HAVE_FEATURE_SCRIPTING
+ (void) LibraryName;
+ (void) ModuleName;
+ (void) Language;
+ (void) Source;
+#else
+ SfxModelGuard aGuard( *this );
+
+ Reference< script::XStarBasicAccess >& rxAccess = m_pData->m_xStarBasicAccess;
+ if( !rxAccess.is() && m_pData->m_pObjectShell.is() )
+ rxAccess = implGetStarBasicAccess( m_pData->m_pObjectShell.get() );
+
+ if( rxAccess.is() )
+ rxAccess->addModule( LibraryName, ModuleName, Language, Source );
+#endif
+}
+
+/**___________________________________________________________________________________________________
+ @seealso XStarBasicAccess
+*/
+void SAL_CALL SfxBaseModel::addDialog( const OUString& LibraryName, const OUString& DialogName,
+ const Sequence< sal_Int8 >& Data )
+{
+#if !HAVE_FEATURE_SCRIPTING
+ (void) LibraryName;
+ (void) DialogName;
+ (void) Data;
+#else
+ SfxModelGuard aGuard( *this );
+
+ Reference< script::XStarBasicAccess >& rxAccess = m_pData->m_xStarBasicAccess;
+ if( !rxAccess.is() && m_pData->m_pObjectShell.is() )
+ rxAccess = implGetStarBasicAccess( m_pData->m_pObjectShell.get() );
+
+ if( rxAccess.is() )
+ rxAccess->addDialog( LibraryName, DialogName, Data );
+#endif
+}
+
+
+// XChild
+
+
+Reference< XInterface > SAL_CALL SfxBaseModel::getParent()
+{
+ SfxModelGuard aGuard( *this );
+
+ return m_pData->m_xParent;
+}
+
+
+// XChild
+
+
+void SAL_CALL SfxBaseModel::setParent(const Reference< XInterface >& Parent)
+{
+ SfxModelGuard aGuard( *this, SfxModelGuard::E_INITIALIZING );
+ m_pData->m_xParent = Parent;
+}
+
+
+// XChild
+
+
+void SAL_CALL SfxBaseModel::dispose()
+{
+ SfxModelGuard aGuard( *this, SfxModelGuard::E_INITIALIZING );
+
+ if ( !m_pData->m_bClosed )
+ {
+ // gracefully accept wrong dispose calls instead of close call
+ // and try to make it work (may be really disposed later!)
+ try
+ {
+ close( true );
+ }
+ catch ( util::CloseVetoException& )
+ {
+ }
+
+ return;
+ }
+
+ if ( m_pData->m_pStorageModifyListen.is() )
+ {
+ m_pData->m_pStorageModifyListen->dispose();
+ m_pData->m_pStorageModifyListen = nullptr;
+ }
+
+ if ( m_pData->m_pDocumentUndoManager.is() )
+ {
+ m_pData->m_pDocumentUndoManager->disposing();
+ m_pData->m_pDocumentUndoManager = nullptr;
+ }
+
+ lang::EventObject aEvent( static_cast<frame::XModel *>(this) );
+ m_pData->m_aInterfaceContainer.disposeAndClear( aEvent );
+
+ m_pData->m_xDocumentProperties.clear();
+
+ m_pData->m_xDocumentMetadata.clear();
+
+ if ( m_pData->m_pObjectShell.is() )
+ {
+ EndListening( *m_pData->m_pObjectShell );
+ }
+
+ m_pData->m_xCurrent.clear();
+ m_pData->m_seqControllers.clear();
+
+ // m_pData member must be set to zero before delete is called to
+ // force disposed exception whenever someone tries to access our
+ // instance while in the dtor.
+ m_pData.reset();
+}
+
+
+// XChild
+
+
+void SAL_CALL SfxBaseModel::addEventListener( const Reference< lang::XEventListener >& aListener )
+{
+ SfxModelGuard aGuard( *this, SfxModelGuard::E_INITIALIZING );
+ m_pData->m_aInterfaceContainer.addInterface( cppu::UnoType<lang::XEventListener>::get(), aListener );
+}
+
+
+// XChild
+
+
+void SAL_CALL SfxBaseModel::removeEventListener( const Reference< lang::XEventListener >& aListener )
+{
+ SfxModelGuard aGuard( *this, SfxModelGuard::E_INITIALIZING );
+ m_pData->m_aInterfaceContainer.removeInterface( cppu::UnoType<lang::XEventListener>::get(), aListener );
+}
+
+void
+IMPL_SfxBaseModel_DataContainer::impl_setDocumentProperties(
+ const Reference< document::XDocumentProperties >& rxNewDocProps)
+{
+ m_xDocumentProperties.set(rxNewDocProps, UNO_SET_THROW);
+ if (m_pObjectShell.is())
+ {
+ Reference<util::XModifyBroadcaster> const xMB(
+ m_xDocumentProperties, UNO_QUERY_THROW);
+ xMB->addModifyListener(new SfxDocInfoListener_Impl(*m_pObjectShell));
+ }
+}
+
+// document::XDocumentPropertiesSupplier:
+Reference< document::XDocumentProperties > SAL_CALL
+SfxBaseModel::getDocumentProperties()
+{
+ SfxModelGuard aGuard( *this, SfxModelGuard::E_INITIALIZING );
+ if ( !m_pData->m_xDocumentProperties.is() )
+ {
+ Reference< document::XDocumentProperties > xDocProps(
+ document::DocumentProperties::create( ::comphelper::getProcessComponentContext() ) );
+ m_pData->impl_setDocumentProperties(xDocProps);
+ }
+
+ return m_pData->m_xDocumentProperties;
+}
+
+
+// lang::XEventListener
+
+
+void SAL_CALL SfxBaseModel::disposing( const lang::EventObject& aObject )
+{
+ SolarMutexGuard aGuard;
+ if ( impl_isDisposed() )
+ return;
+
+ Reference< util::XModifyListener > xMod( aObject.Source, UNO_QUERY );
+ Reference< lang::XEventListener > xListener( aObject.Source, UNO_QUERY );
+ Reference< document::XEventListener > xDocListener( aObject.Source, UNO_QUERY );
+
+ if ( xMod.is() )
+ m_pData->m_aInterfaceContainer.removeInterface( cppu::UnoType<util::XModifyListener>::get(), xMod );
+ else if ( xListener.is() )
+ m_pData->m_aInterfaceContainer.removeInterface( cppu::UnoType<lang::XEventListener>::get(), xListener );
+ else if ( xDocListener.is() )
+ m_pData->m_aInterfaceContainer.removeInterface( cppu::UnoType<document::XEventListener>::get(), xListener );
+}
+
+
+// frame::XModel
+
+
+sal_Bool SAL_CALL SfxBaseModel::attachResource( const OUString& rURL ,
+ const Sequence< beans::PropertyValue >& rArgs )
+{
+ SfxModelGuard aGuard( *this, SfxModelGuard::E_INITIALIZING );
+ if ( rURL.isEmpty() && rArgs.getLength() == 1 && rArgs[0].Name == "SetEmbedded" )
+ {
+ // allows to set a windowless document to EMBEDDED state
+ // but _only_ before load() or initNew() methods
+ if ( m_pData->m_pObjectShell.is() && !m_pData->m_pObjectShell->GetMedium() )
+ {
+ bool bEmb(false);
+ if ( ( rArgs[0].Value >>= bEmb ) && bEmb )
+ m_pData->m_pObjectShell->SetCreateMode_Impl( SfxObjectCreateMode::EMBEDDED );
+ }
+
+ return true;
+ }
+
+ if ( m_pData->m_pObjectShell.is() )
+ {
+ m_pData->m_sURL = rURL;
+
+ SfxObjectShell* pObjectShell = m_pData->m_pObjectShell.get();
+
+ Sequence< sal_Int32 > aWinExtent;
+ for (const beans::PropertyValue & rProp : rArgs)
+ {
+ if (rProp.Name == "WinExtent" && (rProp.Value >>= aWinExtent) && ( aWinExtent.getLength() == 4 ) )
+ {
+ tools::Rectangle aVisArea( aWinExtent[0], aWinExtent[1], aWinExtent[2], aWinExtent[3] );
+ aVisArea = OutputDevice::LogicToLogic(aVisArea, MapMode(MapUnit::Map100thMM), MapMode(pObjectShell->GetMapUnit()));
+ pObjectShell->SetVisArea( aVisArea );
+ }
+ bool bBreakMacroSign = false;
+ if ( rProp.Name == "BreakMacroSignature" && (rProp.Value >>= bBreakMacroSign) )
+ {
+ pObjectShell->BreakMacroSign_Impl( bBreakMacroSign );
+ }
+ bool bMacroEventRead = false;
+ if ( rProp.Name == "MacroEventRead" && (rProp.Value >>= bMacroEventRead) && bMacroEventRead)
+ {
+ pObjectShell->SetMacroCallsSeenWhileLoading();
+ }
+ }
+ Sequence<beans::PropertyValue> aStrippedArgs(rArgs.getLength());
+ beans::PropertyValue* pStripped = aStrippedArgs.getArray();
+ for (const beans::PropertyValue & rProp : rArgs)
+ {
+ if (rProp.Name == "WinExtent"
+ || rProp.Name == "BreakMacroSignature"
+ || rProp.Name == "MacroEventRead"
+ || rProp.Name == "Stream"
+ || rProp.Name == "InputStream"
+ || rProp.Name == "URL"
+ || rProp.Name == "Frame"
+ || rProp.Name == "Password"
+ || rProp.Name == "EncryptionData")
+ continue;
+ *pStripped++ = rProp;
+ }
+ aStrippedArgs.realloc(pStripped - aStrippedArgs.getArray());
+
+ // TODO/LATER: all the parameters that are accepted by ItemSet of the DocShell must be removed here
+
+ m_pData->m_seqArguments = aStrippedArgs;
+
+ SfxMedium* pMedium = pObjectShell->GetMedium();
+ if ( pMedium )
+ {
+ SfxAllItemSet aSet( pObjectShell->GetPool() );
+ TransformParameters( SID_OPENDOC, rArgs, aSet );
+
+ // the arguments are not allowed to reach the medium
+ aSet.ClearItem( SID_FILE_NAME );
+ aSet.ClearItem( SID_FILLFRAME );
+
+ pMedium->GetItemSet()->Put( aSet );
+ const SfxStringItem* pItem = aSet.GetItem<SfxStringItem>(SID_FILTER_NAME, false);
+ if ( pItem )
+ pMedium->SetFilter(
+ pObjectShell->GetFactory().GetFilterContainer()->GetFilter4FilterName( pItem->GetValue() ) );
+
+ const SfxStringItem* pTitleItem = aSet.GetItem<SfxStringItem>(SID_DOCINFO_TITLE, false);
+ if ( pTitleItem )
+ {
+ SfxViewFrame* pFrame = SfxViewFrame::GetFirst( pObjectShell );
+ if ( pFrame )
+ pFrame->UpdateTitle();
+ }
+ }
+ }
+
+ return true ;
+}
+
+
+// frame::XModel
+
+
+OUString SAL_CALL SfxBaseModel::getURL()
+{
+ SfxModelGuard aGuard( *this );
+ return m_pData->m_sURL ;
+}
+
+
+// frame::XModel
+
+Sequence< beans::PropertyValue > SAL_CALL SfxBaseModel::getArgs()
+{
+ return getArgs2({});
+}
+
+// frame::XModel3
+
+Sequence< beans::PropertyValue > SAL_CALL SfxBaseModel::getArgs2(const Sequence<OUString> & requestedArgsSeq )
+{
+ SfxModelGuard aGuard( *this );
+
+ if (!SfxApplication::Get()) // tdf#113755
+ {
+ SAL_WARN("sfx.appl", "Unexpected operations on model");
+ return m_pData->m_seqArguments;
+ }
+
+ std::set<std::u16string_view> requestedArgs;
+ for (OUString const & s : requestedArgsSeq)
+ requestedArgs.insert(s);
+
+ if ( m_pData->m_pObjectShell.is() )
+ {
+ Sequence< beans::PropertyValue > seqArgsNew;
+ Sequence< beans::PropertyValue > seqArgsOld;
+ SfxAllItemSet aSet( m_pData->m_pObjectShell->GetPool() );
+
+ // we need to know which properties are supported by the transformer
+ // hopefully it is a temporary solution, I guess nonconvertable properties
+ // should not be supported so then there will be only ItemSet from medium
+
+ TransformItems( SID_OPENDOC, *(m_pData->m_pObjectShell->GetMedium()->GetItemSet()), seqArgsNew );
+ TransformParameters( SID_OPENDOC, m_pData->m_seqArguments, aSet );
+ TransformItems( SID_OPENDOC, aSet, seqArgsOld );
+
+ sal_Int32 nNewLength = seqArgsNew.getLength();
+
+ if (requestedArgs.empty() || requestedArgs.count(u"WinExtent"))
+ {
+ // "WinExtent" property should be updated always.
+ // We can store it now to overwrite an old value
+ // since it is not from ItemSet
+ tools::Rectangle aTmpRect = m_pData->m_pObjectShell->GetVisArea( ASPECT_CONTENT );
+ aTmpRect = OutputDevice::LogicToLogic(aTmpRect, MapMode(m_pData->m_pObjectShell->GetMapUnit()), MapMode(MapUnit::Map100thMM));
+
+ Sequence< sal_Int32 > aRectSeq
+ {
+ o3tl::narrowing<int>(aTmpRect.Left()),
+ o3tl::narrowing<int>(aTmpRect.Top()),
+ o3tl::narrowing<int>(aTmpRect.IsWidthEmpty() ? aTmpRect.Left() : aTmpRect.Right()),
+ o3tl::narrowing<int>(aTmpRect.IsHeightEmpty() ? aTmpRect.Top() : aTmpRect.Bottom())
+ };
+
+ seqArgsNew.realloc( ++nNewLength );
+ auto pseqArgsNew = seqArgsNew.getArray();
+ pseqArgsNew[ nNewLength - 1 ].Name = "WinExtent";
+ pseqArgsNew[ nNewLength - 1 ].Value <<= aRectSeq;
+ }
+
+ if (requestedArgs.empty() || requestedArgs.count(u"PreusedFilterName"))
+ {
+ if ( !m_pData->m_aPreusedFilterName.isEmpty() )
+ {
+ seqArgsNew.realloc( ++nNewLength );
+ auto pseqArgsNew = seqArgsNew.getArray();
+ pseqArgsNew[ nNewLength - 1 ].Name = "PreusedFilterName";
+ pseqArgsNew[ nNewLength - 1 ].Value <<= m_pData->m_aPreusedFilterName;
+ }
+ }
+
+ if (requestedArgs.empty() || requestedArgs.count(u"DocumentBorder"))
+ {
+ SfxViewFrame* pFrame = SfxViewFrame::GetFirst( m_pData->m_pObjectShell.get() );
+ if ( pFrame )
+ {
+ SvBorder aBorder = pFrame->GetBorderPixelImpl();
+
+ Sequence< sal_Int32 > aBorderSeq
+ {
+ o3tl::narrowing<int>(aBorder.Left()),
+ o3tl::narrowing<int>(aBorder.Top()),
+ o3tl::narrowing<int>(aBorder.Right()),
+ o3tl::narrowing<int>(aBorder.Bottom())
+ };
+
+ seqArgsNew.realloc( ++nNewLength );
+ auto pseqArgsNew = seqArgsNew.getArray();
+ pseqArgsNew[ nNewLength - 1 ].Name = "DocumentBorder";
+ pseqArgsNew[ nNewLength - 1 ].Value <<= aBorderSeq;
+ }
+ }
+
+ if (requestedArgs.empty())
+ {
+ // only the values that are not supported by the ItemSet must be cached here
+ Sequence< beans::PropertyValue > aFinalCache;
+ sal_Int32 nFinalLength = 0;
+
+ for ( const auto& rOrg : std::as_const(m_pData->m_seqArguments) )
+ {
+ auto bNew = std::none_of(std::cbegin(seqArgsOld), std::cend(seqArgsOld),
+ [&rOrg](const beans::PropertyValue& rOld){ return rOld.Name == rOrg.Name; });
+ if ( bNew )
+ {
+ // the entity with this name should be new for seqArgsNew
+ // since it is not supported by transformer
+
+ seqArgsNew.realloc( ++nNewLength );
+ seqArgsNew.getArray()[ nNewLength - 1 ] = rOrg;
+
+ aFinalCache.realloc( ++nFinalLength );
+ aFinalCache.getArray()[ nFinalLength - 1 ] = rOrg;
+ }
+ }
+
+ m_pData->m_seqArguments = aFinalCache;
+ }
+
+ return seqArgsNew;
+ }
+
+ return m_pData->m_seqArguments;
+}
+
+void SAL_CALL SfxBaseModel::setArgs(const Sequence<beans::PropertyValue>& aArgs)
+{
+ SfxModelGuard aGuard( *this );
+
+ SfxMedium* pMedium = m_pData->m_pObjectShell->GetMedium();
+ if (!pMedium)
+ {
+ throw util::InvalidStateException(
+ "Medium could not be retrieved, unable to execute setArgs");
+ }
+
+ for (const auto& rArg : aArgs)
+ {
+ OUString sValue;
+ bool bValue;
+ bool ok = false;
+ if (rArg.Name == "SuggestedSaveAsName")
+ {
+ if (rArg.Value >>= sValue)
+ {
+ pMedium->GetItemSet()->Put(SfxStringItem(SID_SUGGESTEDSAVEASNAME, sValue));
+ ok = true;
+ }
+ }
+ else if (rArg.Name == "SuggestedSaveAsDir")
+ {
+ if (rArg.Value >>= sValue)
+ {
+ pMedium->GetItemSet()->Put(SfxStringItem(SID_SUGGESTEDSAVEASDIR, sValue));
+ ok = true;
+ }
+ }
+ else if (rArg.Name == "LockContentExtraction")
+ {
+ if (rArg.Value >>= bValue)
+ {
+ pMedium->GetItemSet()->Put(SfxBoolItem(SID_LOCK_CONTENT_EXTRACTION, bValue));
+ ok = true;
+ }
+ }
+ else if (rArg.Name == "LockExport")
+ {
+ if (rArg.Value >>= bValue)
+ {
+ pMedium->GetItemSet()->Put(SfxBoolItem(SID_LOCK_EXPORT, bValue));
+ ok = true;
+ }
+ }
+ else if (rArg.Name == "LockPrint")
+ {
+ if (rArg.Value >>= bValue)
+ {
+ pMedium->GetItemSet()->Put(SfxBoolItem(SID_LOCK_PRINT, bValue));
+ ok = true;
+ }
+ }
+ else if (rArg.Name == "LockSave")
+ {
+ if (rArg.Value >>= bValue)
+ {
+ pMedium->GetItemSet()->Put(SfxBoolItem(SID_LOCK_SAVE, bValue));
+ ok = true;
+ }
+ }
+ else if (rArg.Name == "LockEditDoc")
+ {
+ if (rArg.Value >>= bValue)
+ {
+ pMedium->GetItemSet()->Put(SfxBoolItem(SID_LOCK_EDITDOC, bValue));
+ ok = true;
+ }
+ }
+ else if (rArg.Name == "Replaceable")
+ {
+ if (rArg.Value >>= bValue)
+ {
+ pMedium->GetItemSet()->Put(SfxBoolItem(SID_REPLACEABLE, bValue));
+ ok = true;
+ }
+ }
+ else if (rArg.Name == "EncryptionData")
+ {
+ pMedium->GetItemSet()->Put(SfxUnoAnyItem(SID_ENCRYPTIONDATA, rArg.Value));
+ ok = true;
+ }
+ if (!ok)
+ {
+ throw lang::IllegalArgumentException("Setting property not supported: " + rArg.Name,
+ comphelper::getProcessComponentContext(), 0);
+ }
+ }
+}
+
+// frame::XModel
+
+
+void SAL_CALL SfxBaseModel::connectController( const Reference< frame::XController >& xController )
+{
+ SfxModelGuard aGuard( *this );
+ OSL_PRECOND( xController.is(), "SfxBaseModel::connectController: invalid controller!" );
+ if ( !xController.is() )
+ return;
+
+ m_pData->m_seqControllers.push_back(xController);
+
+ if ( m_pData->m_seqControllers.size() == 1 )
+ {
+ SfxViewFrame* pViewFrame = SfxViewFrame::Get( xController, GetObjectShell() );
+ ENSURE_OR_THROW( pViewFrame, "SFX document without SFX view!?" );
+ pViewFrame->UpdateDocument_Impl();
+ const OUString sDocumentURL = GetObjectShell()->GetMedium()->GetName();
+ if ( !sDocumentURL.isEmpty() )
+ SfxGetpApp()->Broadcast( SfxOpenUrlHint( sDocumentURL ) );
+ }
+}
+
+
+// frame::XModel
+
+
+void SAL_CALL SfxBaseModel::disconnectController( const Reference< frame::XController >& xController )
+{
+ SfxModelGuard aGuard( *this );
+
+ if ( m_pData->m_seqControllers.empty() )
+ return;
+
+ auto& vec = m_pData->m_seqControllers;
+ vec.erase(std::remove(vec.begin(), vec.end(), xController), vec.end());
+
+ if ( xController == m_pData->m_xCurrent )
+ m_pData->m_xCurrent.clear();
+}
+
+namespace
+{
+ class ControllerLockUndoAction : public ::cppu::WeakImplHelper< XUndoAction >
+ {
+ public:
+ ControllerLockUndoAction( const Reference< XModel >& i_model, const bool i_undoIsUnlock )
+ :m_xModel( i_model )
+ ,m_bUndoIsUnlock( i_undoIsUnlock )
+ {
+ }
+
+ // XUndoAction
+ virtual OUString SAL_CALL getTitle() override;
+ virtual void SAL_CALL undo( ) override;
+ virtual void SAL_CALL redo( ) override;
+
+ private:
+ const Reference< XModel > m_xModel;
+ const bool m_bUndoIsUnlock;
+ };
+
+ OUString SAL_CALL ControllerLockUndoAction::getTitle()
+ {
+ // this action is intended to be used within an UndoContext only, so nobody will ever see this title ...
+ return OUString();
+ }
+
+ void SAL_CALL ControllerLockUndoAction::undo( )
+ {
+ if ( m_bUndoIsUnlock )
+ m_xModel->unlockControllers();
+ else
+ m_xModel->lockControllers();
+ }
+
+ void SAL_CALL ControllerLockUndoAction::redo( )
+ {
+ if ( m_bUndoIsUnlock )
+ m_xModel->lockControllers();
+ else
+ m_xModel->unlockControllers();
+ }
+}
+
+
+// frame::XModel
+
+
+void SAL_CALL SfxBaseModel::lockControllers()
+{
+ SfxModelGuard aGuard( *this );
+
+ ++m_pData->m_nControllerLockCount ;
+
+ if ( m_pData->m_pDocumentUndoManager.is()
+ && m_pData->m_pDocumentUndoManager->isInContext()
+ && !m_pData->m_pDocumentUndoManager->isLocked()
+ )
+ {
+ m_pData->m_pDocumentUndoManager->addUndoAction( new ControllerLockUndoAction( this, true ) );
+ }
+}
+
+
+// frame::XModel
+
+
+void SAL_CALL SfxBaseModel::unlockControllers()
+{
+ SfxModelGuard aGuard( *this );
+
+ --m_pData->m_nControllerLockCount ;
+
+ if ( m_pData->m_pDocumentUndoManager.is()
+ && m_pData->m_pDocumentUndoManager->isInContext()
+ && !m_pData->m_pDocumentUndoManager->isLocked()
+ )
+ {
+ m_pData->m_pDocumentUndoManager->addUndoAction( new ControllerLockUndoAction( this, false ) );
+ }
+}
+
+
+// frame::XModel
+
+
+sal_Bool SAL_CALL SfxBaseModel::hasControllersLocked()
+{
+ SfxModelGuard aGuard( *this );
+ return ( m_pData->m_nControllerLockCount != 0 ) ;
+}
+
+
+// frame::XModel
+
+
+Reference< frame::XController > SAL_CALL SfxBaseModel::getCurrentController()
+{
+ SfxModelGuard aGuard( *this );
+
+ // get the last active controller of this model
+ if ( m_pData->m_xCurrent.is() )
+ return m_pData->m_xCurrent;
+
+ // get the first controller of this model
+ return !m_pData->m_seqControllers.empty() ? m_pData->m_seqControllers.front() : m_pData->m_xCurrent;
+}
+
+
+// frame::XModel
+
+
+void SAL_CALL SfxBaseModel::setCurrentController( const Reference< frame::XController >& xCurrentController )
+{
+ SfxModelGuard aGuard( *this );
+
+ m_pData->m_xCurrent = xCurrentController;
+}
+
+
+// frame::XModel
+
+
+Reference< XInterface > SAL_CALL SfxBaseModel::getCurrentSelection()
+{
+ SfxModelGuard aGuard( *this );
+
+ Reference< XInterface > xReturn;
+ Reference< frame::XController > xController = getCurrentController() ;
+
+ if ( xController.is() )
+ {
+ Reference< view::XSelectionSupplier > xDocView( xController, UNO_QUERY );
+ if ( xDocView.is() )
+ {
+ Any aSel = xDocView->getSelection();
+ aSel >>= xReturn ;
+ }
+ }
+
+ return xReturn ;
+}
+
+
+// XModifiable2
+
+
+sal_Bool SAL_CALL SfxBaseModel::disableSetModified()
+{
+ SfxModelGuard aGuard( *this );
+
+ if ( !m_pData->m_pObjectShell.is() )
+ throw RuntimeException();
+
+ bool bResult = m_pData->m_pObjectShell->IsEnableSetModified();
+ m_pData->m_pObjectShell->EnableSetModified( false );
+
+ return bResult;
+}
+
+sal_Bool SAL_CALL SfxBaseModel::enableSetModified()
+{
+ SfxModelGuard aGuard( *this );
+
+ if ( !m_pData->m_pObjectShell.is() )
+ throw RuntimeException();
+
+ bool bResult = m_pData->m_pObjectShell->IsEnableSetModified();
+ m_pData->m_pObjectShell->EnableSetModified();
+
+ return bResult;
+}
+
+sal_Bool SAL_CALL SfxBaseModel::isSetModifiedEnabled()
+{
+ SfxModelGuard aGuard( *this );
+
+ if ( !m_pData->m_pObjectShell.is() )
+ throw RuntimeException();
+
+ return m_pData->m_pObjectShell->IsEnableSetModified();
+}
+
+
+// XModifiable
+
+
+sal_Bool SAL_CALL SfxBaseModel::isModified()
+{
+ SfxModelGuard aGuard( *this );
+
+ return m_pData->m_pObjectShell.is() && m_pData->m_pObjectShell->IsModified();
+}
+
+
+// XModifiable
+
+
+void SAL_CALL SfxBaseModel::setModified( sal_Bool bModified )
+{
+ SfxModelGuard aGuard( *this );
+
+ if ( m_pData->m_pObjectShell.is() )
+ m_pData->m_pObjectShell->SetModified(bModified);
+}
+
+
+// XModifiable
+
+
+void SAL_CALL SfxBaseModel::addModifyListener(const Reference< util::XModifyListener >& xListener)
+{
+ SfxModelGuard aGuard( *this, SfxModelGuard::E_INITIALIZING );
+
+ m_pData->m_aInterfaceContainer.addInterface( cppu::UnoType<util::XModifyListener>::get(),xListener );
+}
+
+
+// XModifiable
+
+
+void SAL_CALL SfxBaseModel::removeModifyListener(const Reference< util::XModifyListener >& xListener)
+{
+ SfxModelGuard aGuard( *this );
+
+ m_pData->m_aInterfaceContainer.removeInterface( cppu::UnoType<util::XModifyListener>::get(), xListener );
+}
+
+
+// XCloseable
+
+
+void SAL_CALL SfxBaseModel::close( sal_Bool bDeliverOwnership )
+{
+ SolarMutexGuard aGuard;
+ if ( impl_isDisposed() || m_pData->m_bClosed || m_pData->m_bClosing )
+ return;
+
+ Reference< XInterface > xSelfHold( static_cast< ::cppu::OWeakObject* >(this) );
+ lang::EventObject aSource ( static_cast< ::cppu::OWeakObject* >(this) );
+ comphelper::OInterfaceContainerHelper2* pContainer = m_pData->m_aInterfaceContainer.getContainer( cppu::UnoType<util::XCloseListener>::get());
+ if (pContainer!=nullptr)
+ {
+ comphelper::OInterfaceIteratorHelper2 pIterator(*pContainer);
+ while (pIterator.hasMoreElements())
+ {
+ try
+ {
+ static_cast<util::XCloseListener*>(pIterator.next())->queryClosing( aSource, bDeliverOwnership );
+ }
+ catch( RuntimeException& )
+ {
+ pIterator.remove();
+ }
+ }
+ }
+
+ if ( m_pData->m_bSaving )
+ {
+ if (bDeliverOwnership)
+ m_pData->m_bSuicide = true;
+ throw util::CloseVetoException(
+ "Can not close while saving.",
+ static_cast< util::XCloseable* >(this));
+ }
+
+ // no own objections against closing!
+ m_pData->m_bClosing = true;
+ pContainer = m_pData->m_aInterfaceContainer.getContainer( cppu::UnoType<util::XCloseListener>::get());
+ if (pContainer!=nullptr)
+ {
+ comphelper::OInterfaceIteratorHelper2 pCloseIterator(*pContainer);
+ while (pCloseIterator.hasMoreElements())
+ {
+ try
+ {
+ static_cast<util::XCloseListener*>(pCloseIterator.next())->notifyClosing( aSource );
+ }
+ catch( RuntimeException& )
+ {
+ pCloseIterator.remove();
+ }
+ }
+ }
+
+ m_pData->m_bClosed = true;
+ m_pData->m_bClosing = false;
+
+ dispose();
+}
+
+
+// XCloseBroadcaster
+
+
+void SAL_CALL SfxBaseModel::addCloseListener( const Reference< util::XCloseListener >& xListener )
+{
+ SfxModelGuard aGuard( *this, SfxModelGuard::E_INITIALIZING );
+
+ m_pData->m_aInterfaceContainer.addInterface( cppu::UnoType<util::XCloseListener>::get(), xListener );
+}
+
+
+// XCloseBroadcaster
+
+
+void SAL_CALL SfxBaseModel::removeCloseListener( const Reference< util::XCloseListener >& xListener )
+{
+ SfxModelGuard aGuard( *this );
+
+ m_pData->m_aInterfaceContainer.removeInterface( cppu::UnoType<util::XCloseListener>::get(), xListener );
+}
+
+
+// XPrintable
+
+
+Sequence< beans::PropertyValue > SAL_CALL SfxBaseModel::getPrinter()
+{
+ SfxModelGuard aGuard( *this );
+
+ impl_getPrintHelper();
+ return m_pData->m_xPrintable->getPrinter();
+}
+
+void SAL_CALL SfxBaseModel::setPrinter(const Sequence< beans::PropertyValue >& rPrinter)
+{
+ SfxModelGuard aGuard( *this );
+
+ impl_getPrintHelper();
+ m_pData->m_xPrintable->setPrinter( rPrinter );
+}
+
+void SAL_CALL SfxBaseModel::print(const Sequence< beans::PropertyValue >& rOptions)
+{
+ SfxModelGuard aGuard( *this );
+
+ impl_getPrintHelper();
+
+ // tdf#123728 Always print on main thread to avoid deadlocks
+ vcl::solarthread::syncExecute([this, &rOptions]() { m_pData->m_xPrintable->print(rOptions); });
+}
+
+// XStorable
+
+
+sal_Bool SAL_CALL SfxBaseModel::hasLocation()
+{
+ SfxModelGuard aGuard( *this );
+
+ return m_pData->m_pObjectShell.is() && m_pData->m_pObjectShell->HasName();
+}
+
+
+// XStorable
+
+
+OUString SAL_CALL SfxBaseModel::getLocation()
+{
+ SfxModelGuard aGuard( *this );
+
+ if ( m_pData->m_pObjectShell.is() )
+ {
+ // TODO/LATER: is it correct that the shared document returns shared file location?
+ if ( m_pData->m_pObjectShell->IsDocShared() )
+ return m_pData->m_pObjectShell->GetSharedFileURL();
+ else
+ return m_pData->m_pObjectShell->GetMedium()->GetName();
+ }
+
+ return m_pData->m_sURL;
+}
+
+
+// XStorable
+
+
+sal_Bool SAL_CALL SfxBaseModel::isReadonly()
+{
+ SfxModelGuard aGuard( *this );
+
+ return !m_pData->m_pObjectShell.is() || m_pData->m_pObjectShell->IsReadOnly();
+}
+
+// XStorable2
+
+
+void SAL_CALL SfxBaseModel::storeSelf( const Sequence< beans::PropertyValue >& aSeqArgs )
+{
+ SfxModelGuard aGuard( *this );
+
+ if ( !m_pData->m_pObjectShell.is() )
+ return;
+
+ SfxSaveGuard aSaveGuard(this, m_pData.get());
+
+ bool bCheckIn = false;
+ bool bOnMainThread = false;
+ for ( const auto& rArg : aSeqArgs )
+ {
+ // check that only acceptable parameters are provided here
+ if ( rArg.Name != "VersionComment" && rArg.Name != "Author"
+ && rArg.Name != "DontTerminateEdit"
+ && rArg.Name != "InteractionHandler" && rArg.Name != "StatusIndicator"
+ && rArg.Name != "VersionMajor"
+ && rArg.Name != "FailOnWarning"
+ && rArg.Name != "CheckIn"
+ && rArg.Name != "NoFileSync"
+ && rArg.Name != "OnMainThread" )
+ {
+ const OUString aMessage( "Unexpected MediaDescriptor parameter: " + rArg.Name );
+ throw lang::IllegalArgumentException( aMessage, Reference< XInterface >(), 1 );
+ }
+ else if ( rArg.Name == "CheckIn" )
+ {
+ rArg.Value >>= bCheckIn;
+ }
+ else if (rArg.Name == "OnMainThread")
+ {
+ rArg.Value >>= bOnMainThread;
+ }
+ }
+
+ // Remove CheckIn property if needed
+ sal_uInt16 nSlotId = SID_SAVEDOC;
+ Sequence< beans::PropertyValue > aArgs = aSeqArgs;
+ if ( bCheckIn )
+ {
+ nSlotId = SID_CHECKIN;
+ sal_Int32 nLength = aSeqArgs.getLength( );
+ aArgs = Sequence< beans::PropertyValue >( nLength - 1 );
+ std::copy_if(aSeqArgs.begin(), aSeqArgs.end(), aArgs.getArray(),
+ [](const beans::PropertyValue& rProp) { return rProp.Name != "CheckIn"; });
+ }
+
+ std::optional<SfxAllItemSet> pParams(SfxGetpApp()->GetPool() );
+ TransformParameters( nSlotId, aArgs, *pParams );
+
+ SfxGetpApp()->NotifyEvent( SfxEventHint( SfxEventHintId::SaveDoc, GlobalEventConfig::GetEventName(GlobalEventId::SAVEDOC), m_pData->m_pObjectShell.get() ) );
+
+ bool bRet = false;
+
+ // TODO/LATER: let the embedded case of saving be handled more careful
+ if ( m_pData->m_pObjectShell->GetCreateMode() == SfxObjectCreateMode::EMBEDDED )
+ {
+ // If this is an embedded object that has no URL based location it should be stored to own storage.
+ // An embedded object can have a location based on URL in case it is a link, then it should be
+ // stored in normal way.
+ if ( !hasLocation() || getLocation().startsWith("private:") )
+ {
+ // actually in this very rare case only UI parameters have sense
+ // TODO/LATER: should be done later, after integration of sb19
+ bRet = m_pData->m_pObjectShell->DoSave()
+ && m_pData->m_pObjectShell->DoSaveCompleted();
+ }
+ else
+ {
+ bRet = m_pData->m_pObjectShell->Save_Impl( &*pParams );
+ }
+ }
+ else
+ {
+ // Tell the SfxMedium if we are in checkin instead of normal save
+ m_pData->m_pObjectShell->GetMedium( )->SetInCheckIn( nSlotId == SID_CHECKIN );
+ if (bOnMainThread)
+ bRet = vcl::solarthread::syncExecute(
+ [this, &pParams] { return m_pData->m_pObjectShell->Save_Impl(&*pParams); });
+ else
+ bRet = m_pData->m_pObjectShell->Save_Impl(&*pParams);
+ m_pData->m_pObjectShell->GetMedium( )->SetInCheckIn( nSlotId != SID_CHECKIN );
+ }
+
+ pParams.reset();
+
+ ErrCode nErrCode = m_pData->m_pObjectShell->GetError() ? m_pData->m_pObjectShell->GetError()
+ : ERRCODE_IO_CANTWRITE;
+ m_pData->m_pObjectShell->ResetError();
+
+ if ( bRet )
+ {
+ m_pData->m_aPreusedFilterName = GetMediumFilterName_Impl();
+
+ SfxGetpApp()->NotifyEvent( SfxEventHint( SfxEventHintId::SaveDocDone, GlobalEventConfig::GetEventName(GlobalEventId::SAVEDOCDONE), m_pData->m_pObjectShell.get() ) );
+ }
+ else
+ {
+ // write the contents of the logger to the file
+ SfxGetpApp()->NotifyEvent( SfxEventHint( SfxEventHintId::SaveDocFailed, GlobalEventConfig::GetEventName(GlobalEventId::SAVEDOCFAILED), m_pData->m_pObjectShell.get() ) );
+
+ throw task::ErrorCodeIOException(
+ "SfxBaseModel::storeSelf: " + nErrCode.toHexString(),
+ Reference< XInterface >(), sal_uInt32(nErrCode));
+ }
+}
+
+
+// XStorable
+
+
+void SAL_CALL SfxBaseModel::store()
+{
+ comphelper::ProfileZone aZone("store");
+ storeSelf( Sequence< beans::PropertyValue >() );
+}
+
+
+// XStorable
+
+
+void SAL_CALL SfxBaseModel::storeAsURL( const OUString& rURL ,
+ const Sequence< beans::PropertyValue >& rArgs )
+{
+ SfxModelGuard aGuard( *this );
+ comphelper::ProfileZone aZone("storeAs");
+
+ if ( !m_pData->m_pObjectShell.is() )
+ return;
+
+ SfxSaveGuard aSaveGuard(this, m_pData.get());
+
+ utl::MediaDescriptor aDescriptor(rArgs);
+ bool bOnMainThread = aDescriptor.getUnpackedValueOrDefault("OnMainThread", false);
+ if (bOnMainThread)
+ {
+ vcl::solarthread::syncExecute([this, rURL, rArgs]() { impl_store(rURL, rArgs, false); });
+ }
+ else
+ {
+ impl_store(rURL, rArgs, false);
+ }
+
+ Sequence< beans::PropertyValue > aSequence ;
+ TransformItems( SID_OPENDOC, *m_pData->m_pObjectShell->GetMedium()->GetItemSet(), aSequence );
+ attachResource( rURL, aSequence );
+
+ loadCmisProperties( );
+
+#if OSL_DEBUG_LEVEL > 0
+ const SfxStringItem* pPasswdItem = SfxItemSet::GetItem<SfxStringItem>(m_pData->m_pObjectShell->GetMedium()->GetItemSet(), SID_PASSWORD, false);
+ OSL_ENSURE( !pPasswdItem, "There should be no Password property in the document MediaDescriptor!" );
+#endif
+}
+
+
+// XUndoManagerSupplier
+
+Reference< XUndoManager > SAL_CALL SfxBaseModel::getUndoManager( )
+{
+ SfxModelGuard aGuard( *this );
+ if ( !m_pData->m_pDocumentUndoManager.is() )
+ m_pData->m_pDocumentUndoManager.set( new ::sfx2::DocumentUndoManager( *this ) );
+ return m_pData->m_pDocumentUndoManager;
+}
+
+
+// XStorable
+
+
+void SAL_CALL SfxBaseModel::storeToURL( const OUString& rURL ,
+ const Sequence< beans::PropertyValue >& rArgs )
+{
+ SfxModelGuard aGuard( *this );
+ comphelper::ProfileZone aZone("storeToURL");
+
+ if ( !m_pData->m_pObjectShell.is() )
+ return;
+
+ SfxSaveGuard aSaveGuard(this, m_pData.get());
+ try {
+ utl::MediaDescriptor aDescriptor(rArgs);
+ bool bOnMainThread = aDescriptor.getUnpackedValueOrDefault("OnMainThread", false);
+ if (bOnMainThread)
+ vcl::solarthread::syncExecute([this, rURL, rArgs]() { impl_store(rURL, rArgs, true); });
+ else
+ impl_store(rURL, rArgs, true);
+ }
+ catch (const uno::Exception &e)
+ {
+ // convert to the exception we announce in the throw
+ // (eg. neon likes to throw InteractiveAugmentedIOException which
+ // is not an io::IOException)
+ throw io::IOException(e.Message, e.Context);
+ }
+}
+
+sal_Bool SAL_CALL SfxBaseModel::wasModifiedSinceLastSave()
+{
+ SfxModelGuard aGuard( *this );
+ return m_pData->m_bModifiedSinceLastSave;
+}
+
+void SAL_CALL SfxBaseModel::storeToRecoveryFile( const OUString& i_TargetLocation, const Sequence< PropertyValue >& i_MediaDescriptor )
+{
+ SfxModelGuard aGuard( *this );
+
+ // delegate
+ SfxSaveGuard aSaveGuard( this, m_pData.get() );
+ impl_store( i_TargetLocation, i_MediaDescriptor, true );
+
+ // no need for subsequent calls to storeToRecoveryFile, unless we're modified, again
+ m_pData->m_bModifiedSinceLastSave = false;
+}
+
+void SAL_CALL SfxBaseModel::recoverFromFile( const OUString& i_SourceLocation, const OUString& i_SalvagedFile, const Sequence< PropertyValue >& i_MediaDescriptor )
+{
+ SfxModelGuard aGuard( *this, SfxModelGuard::E_INITIALIZING );
+
+ // delegate to our "load" method
+ ::comphelper::NamedValueCollection aMediaDescriptor( i_MediaDescriptor );
+
+ // our load implementation expects the SalvagedFile to be in the media descriptor
+ OSL_ENSURE( !aMediaDescriptor.has( "SalvagedFile" ) || ( aMediaDescriptor.getOrDefault( "SalvagedFile", OUString() ) == i_SalvagedFile ),
+ "SfxBaseModel::recoverFromFile: inconsistent information!" );
+ aMediaDescriptor.put( "SalvagedFile", i_SalvagedFile );
+
+ // similar for the to-be-loaded file
+ OSL_ENSURE( !aMediaDescriptor.has( "URL" ) || ( aMediaDescriptor.getOrDefault( "URL", OUString() ) == i_SourceLocation ),
+ "SfxBaseModel::recoverFromFile: inconsistent information!" );
+ aMediaDescriptor.put( "URL", i_SourceLocation );
+
+ load( aMediaDescriptor.getPropertyValues() );
+
+ // Note: The XDocumentRecovery interface specification requires us to do an attachResource after loading.
+ // However, we will not do this here, as we know that our load implementation (respectively some method
+ // called from there) already did so.
+ // In particular, the load process might already have modified some elements of the media
+ // descriptor, for instance the MacroExecMode (in case the user was involved to decide about it), and we do
+ // not want to overwrite it with the "old" elements passed to this method here.
+}
+
+
+// XLoadable
+
+
+void SAL_CALL SfxBaseModel::initNew()
+{
+ SfxModelGuard aGuard( *this, SfxModelGuard::E_INITIALIZING );
+ if ( IsInitialized() )
+ throw frame::DoubleInitializationException( OUString(), *this );
+
+ // the object shell should exist always
+ DBG_ASSERT( m_pData->m_pObjectShell.is(), "Model is useless without an ObjectShell" );
+ if ( !m_pData->m_pObjectShell.is() )
+ return;
+
+ if( m_pData->m_pObjectShell->GetMedium() )
+ throw frame::DoubleInitializationException();
+
+ bool bRes = m_pData->m_pObjectShell->DoInitNew();
+ ErrCode nErrCode = m_pData->m_pObjectShell->GetError() ?
+ m_pData->m_pObjectShell->GetError() : ERRCODE_IO_CANTCREATE;
+ m_pData->m_pObjectShell->ResetError();
+
+ if ( !bRes )
+ throw task::ErrorCodeIOException(
+ "SfxBaseModel::initNew: " + nErrCode.toHexString(),
+ Reference< XInterface >(), sal_uInt32(nErrCode));
+}
+
+namespace {
+
+OUString getFilterProvider( SfxMedium const & rMedium )
+{
+ const std::shared_ptr<const SfxFilter>& pFilter = rMedium.GetFilter();
+ if (!pFilter)
+ return OUString();
+
+ return pFilter->GetProviderName();
+}
+
+void setUpdatePickList( SfxMedium* pMedium )
+{
+ if (!pMedium)
+ return;
+
+ bool bHidden = false;
+ const SfxBoolItem* pHidItem = SfxItemSet::GetItem<SfxBoolItem>(pMedium->GetItemSet(), SID_HIDDEN, false);
+ if (pHidItem)
+ bHidden = pHidItem->GetValue();
+
+ pMedium->SetUpdatePickList(!bHidden);
+}
+
+}
+
+void SAL_CALL SfxBaseModel::load( const Sequence< beans::PropertyValue >& seqArguments )
+{
+ SfxModelGuard aGuard( *this, SfxModelGuard::E_INITIALIZING );
+ if ( IsInitialized() )
+ throw frame::DoubleInitializationException( OUString(), *this );
+
+ // the object shell should exist always
+ DBG_ASSERT( m_pData->m_pObjectShell.is(), "Model is useless without an ObjectShell" );
+
+ if (!m_pData->m_pObjectShell.is())
+ return;
+
+ if( m_pData->m_pObjectShell->GetMedium() )
+ // if a Medium is present, the document is already initialized
+ throw frame::DoubleInitializationException();
+
+ SfxMedium* pMedium = new SfxMedium( seqArguments );
+
+ ErrCode nError = ERRCODE_NONE;
+ if (!getFilterProvider(*pMedium).isEmpty())
+ {
+ if (!m_pData->m_pObjectShell->DoLoadExternal(pMedium))
+ nError = ERRCODE_IO_GENERAL;
+
+ pMedium = handleLoadError(nError, pMedium);
+ setUpdatePickList(pMedium);
+ return;
+ }
+
+ OUString aFilterName;
+ const SfxStringItem* pFilterNameItem = SfxItemSet::GetItem<SfxStringItem>(pMedium->GetItemSet(), SID_FILTER_NAME, false);
+ if( pFilterNameItem )
+ aFilterName = pFilterNameItem->GetValue();
+ if( !m_pData->m_pObjectShell->GetFactory().GetFilterContainer()->GetFilter4FilterName( aFilterName ) )
+ {
+ // filtername is not valid
+ delete pMedium;
+ throw frame::IllegalArgumentIOException();
+ }
+
+ const SfxStringItem* pSalvageItem = SfxItemSet::GetItem<SfxStringItem>(pMedium->GetItemSet(), SID_DOC_SALVAGE, false);
+ bool bSalvage = pSalvageItem != nullptr;
+
+ // load document
+ if ( !m_pData->m_pObjectShell->DoLoad(pMedium) )
+ nError=ERRCODE_IO_GENERAL;
+
+ // QUESTION: if the following happens outside of DoLoad, something important is missing there!
+ Reference< task::XInteractionHandler > xHandler = pMedium->GetInteractionHandler();
+ if( m_pData->m_pObjectShell->GetErrorCode() )
+ {
+ nError = m_pData->m_pObjectShell->GetErrorCode();
+ if ( nError == ERRCODE_IO_BROKENPACKAGE && xHandler.is() )
+ {
+ const OUString aDocName( pMedium->GetURLObject().getName( INetURLObject::LAST_SEGMENT, true, INetURLObject::DecodeMechanism::WithCharset ) );
+ const SfxBoolItem* pRepairItem = SfxItemSet::GetItem<SfxBoolItem>(pMedium->GetItemSet(), SID_REPAIRPACKAGE, false);
+ if ( !pRepairItem || !pRepairItem->GetValue() )
+ {
+ RequestPackageReparation aRequest( aDocName );
+ xHandler->handle( aRequest.GetRequest() );
+ if( aRequest.isApproved() )
+ {
+ // broken package: try second loading and allow repair
+ pMedium->GetItemSet()->Put( SfxBoolItem( SID_REPAIRPACKAGE, true ) );
+ pMedium->GetItemSet()->Put( SfxBoolItem( SID_TEMPLATE, true ) );
+ pMedium->GetItemSet()->Put( SfxStringItem( SID_DOCINFO_TITLE, aDocName ) );
+
+ // the error must be reset and the storage must be reopened in new mode
+ pMedium->ResetError();
+ pMedium->CloseStorage();
+ m_pData->m_pObjectShell->PrepareSecondTryLoad_Impl();
+ nError = ERRCODE_NONE;
+ if ( !m_pData->m_pObjectShell->DoLoad(pMedium) )
+ nError=ERRCODE_IO_GENERAL;
+ if (m_pData->m_pObjectShell->GetErrorCode())
+ nError = m_pData->m_pObjectShell->GetErrorCode();
+ }
+ }
+
+ if ( nError == ERRCODE_IO_BROKENPACKAGE )
+ {
+ // repair either not allowed or not successful
+ NotifyBrokenPackage aRequest( aDocName );
+ xHandler->handle( aRequest.GetRequest() );
+ }
+ }
+ }
+
+ if( m_pData->m_pObjectShell->IsAbortingImport() )
+ nError = ERRCODE_ABORT;
+
+ if( bSalvage )
+ {
+ // file recovery: restore original filter
+ const SfxStringItem* pFilterItem = SfxItemSet::GetItem<SfxStringItem>(pMedium->GetItemSet(), SID_FILTER_NAME, false);
+ SfxFilterMatcher& rMatcher = SfxGetpApp()->GetFilterMatcher();
+ std::shared_ptr<const SfxFilter> pSetFilter = rMatcher.GetFilter4FilterName( pFilterItem->GetValue() );
+ pMedium->SetFilter( pSetFilter );
+ m_pData->m_pObjectShell->SetModified();
+ }
+
+ // TODO/LATER: maybe the mode should be retrieved from outside and the preused filter should not be set
+ if ( m_pData->m_pObjectShell->GetCreateMode() == SfxObjectCreateMode::EMBEDDED )
+ {
+ const SfxStringItem* pFilterItem = SfxItemSet::GetItem<SfxStringItem>(pMedium->GetItemSet(), SID_FILTER_NAME, false);
+ if ( pFilterItem )
+ m_pData->m_aPreusedFilterName = pFilterItem->GetValue();
+ }
+
+ if ( !nError )
+ nError = pMedium->GetError();
+
+ m_pData->m_pObjectShell->ResetError();
+
+ pMedium = handleLoadError(nError, pMedium);
+ loadCmisProperties();
+ setUpdatePickList(pMedium);
+
+#if OSL_DEBUG_LEVEL > 0
+ const SfxStringItem* pPasswdItem = SfxItemSet::GetItem<SfxStringItem>(pMedium->GetItemSet(), SID_PASSWORD, false);
+ OSL_ENSURE( !pPasswdItem, "There should be no Password property in the document MediaDescriptor!" );
+#endif
+}
+
+
+// XTransferable
+
+
+Any SAL_CALL SfxBaseModel::getTransferData( const datatransfer::DataFlavor& aFlavor )
+{
+ SfxModelGuard aGuard( *this );
+
+ Any aAny;
+
+ if ( m_pData->m_pObjectShell.is() )
+ {
+ if ( aFlavor.MimeType == "application/x-openoffice-objectdescriptor-xml;windows_formatname=\"Star Object Descriptor (XML)\"" )
+ {
+ if ( aFlavor.DataType != cppu::UnoType<Sequence< sal_Int8 >>::get() )
+ throw datatransfer::UnsupportedFlavorException();
+
+ TransferableObjectDescriptor aDesc;
+
+ aDesc.maClassName = m_pData->m_pObjectShell->GetClassName();
+ aDesc.maTypeName = aFlavor.HumanPresentableName;
+
+ // TODO/LATER: ViewAspect needs to be sal_Int64
+ aDesc.mnViewAspect = sal::static_int_cast< sal_uInt16 >( embed::Aspects::MSOLE_CONTENT );
+
+ Size aSize = m_pData->m_pObjectShell->GetVisArea().GetSize();
+
+ MapUnit aMapUnit = m_pData->m_pObjectShell->GetMapUnit();
+ aDesc.maSize = OutputDevice::LogicToLogic(aSize, MapMode(aMapUnit), MapMode(MapUnit::Map100thMM));
+ aDesc.maDragStartPos = Point();
+ aDesc.maDisplayName.clear();
+
+ SvMemoryStream aMemStm( 1024, 1024 );
+ WriteTransferableObjectDescriptor( aMemStm, aDesc );
+ aAny <<= Sequence< sal_Int8 >( static_cast< const sal_Int8* >( aMemStm.GetData() ), aMemStm.Tell() );
+ }
+ else if ( aFlavor.MimeType == "application/x-openoffice-embed-source;windows_formatname=\"Star EMBS\"" )
+ {
+ if ( aFlavor.DataType != cppu::UnoType<Sequence< sal_Int8 >>::get() )
+ throw datatransfer::UnsupportedFlavorException();
+
+ try
+ {
+ utl::TempFile aTmp;
+ aTmp.EnableKillingFile();
+ storeToURL( aTmp.GetURL(), Sequence < beans::PropertyValue >() );
+ std::unique_ptr<SvStream> pStream(aTmp.GetStream( StreamMode::READ ));
+ const sal_uInt32 nLen = pStream->TellEnd();
+ Sequence< sal_Int8 > aSeq( nLen );
+ pStream->ReadBytes(aSeq.getArray(), nLen);
+ if( aSeq.hasElements() )
+ aAny <<= aSeq;
+ }
+ catch ( Exception& )
+ {
+ }
+ }
+ else if ( aFlavor.MimeType == "application/x-openoffice-gdimetafile;windows_formatname=\"GDIMetaFile\"" )
+ {
+ if ( aFlavor.DataType != cppu::UnoType<Sequence< sal_Int8 >>::get() )
+ throw datatransfer::UnsupportedFlavorException();
+
+
+ std::shared_ptr<GDIMetaFile> xMetaFile =
+ m_pData->m_pObjectShell->GetPreviewMetaFile( true );
+
+ if (xMetaFile)
+ {
+ SvMemoryStream aMemStm( 65535, 65535 );
+ aMemStm.SetVersion( SOFFICE_FILEFORMAT_CURRENT );
+
+ SvmWriter aWriter( aMemStm );
+ aWriter.Write( *xMetaFile );
+ aAny <<= Sequence< sal_Int8 >( static_cast< const sal_Int8* >( aMemStm.GetData() ),
+ aMemStm.TellEnd() );
+ }
+ }
+ else if ( aFlavor.MimeType == "application/x-openoffice-highcontrast-gdimetafile;windows_formatname=\"GDIMetaFile\"" )
+ {
+ if ( aFlavor.DataType != cppu::UnoType<Sequence< sal_Int8 >>::get() )
+ throw datatransfer::UnsupportedFlavorException();
+
+ std::shared_ptr<GDIMetaFile> xMetaFile =
+ m_pData->m_pObjectShell->GetPreviewMetaFile( true );
+
+ if (xMetaFile)
+ {
+ SvMemoryStream aMemStm( 65535, 65535 );
+ aMemStm.SetVersion( SOFFICE_FILEFORMAT_CURRENT );
+
+ SvmWriter aWriter( aMemStm );
+ aWriter.Write( *xMetaFile );
+ aAny <<= Sequence< sal_Int8 >( static_cast< const sal_Int8* >( aMemStm.GetData() ),
+ aMemStm.TellEnd() );
+ }
+ }
+ else if ( aFlavor.MimeType == "application/x-openoffice-emf;windows_formatname=\"Image EMF\"" )
+ {
+ if ( aFlavor.DataType == cppu::UnoType<Sequence< sal_Int8 >>::get() )
+ {
+ std::shared_ptr<GDIMetaFile> xMetaFile =
+ m_pData->m_pObjectShell->GetPreviewMetaFile( true );
+
+ if (xMetaFile)
+ {
+ std::unique_ptr<SvMemoryStream> xStream(
+ GraphicHelper::getFormatStrFromGDI_Impl(
+ xMetaFile.get(), ConvertDataFormat::EMF ) );
+ if (xStream)
+ {
+ xStream->SetVersion( SOFFICE_FILEFORMAT_CURRENT );
+ aAny <<= Sequence< sal_Int8 >( static_cast< const sal_Int8* >( xStream->GetData() ),
+ xStream->TellEnd() );
+ }
+ }
+ }
+ else if ( GraphicHelper::supportsMetaFileHandle_Impl()
+ && aFlavor.DataType == cppu::UnoType<sal_uInt64>::get())
+ {
+ std::shared_ptr<GDIMetaFile> xMetaFile =
+ m_pData->m_pObjectShell->GetPreviewMetaFile( true );
+
+ if (xMetaFile)
+ {
+ aAny <<= reinterpret_cast< sal_uInt64 >(
+ GraphicHelper::getEnhMetaFileFromGDI_Impl( xMetaFile.get() ) );
+ }
+ }
+ else
+ throw datatransfer::UnsupportedFlavorException();
+ }
+ else if ( aFlavor.MimeType == "application/x-openoffice-wmf;windows_formatname=\"Image WMF\"" )
+ {
+ if ( aFlavor.DataType == cppu::UnoType<Sequence< sal_Int8 >>::get() )
+ {
+ std::shared_ptr<GDIMetaFile> xMetaFile =
+ m_pData->m_pObjectShell->GetPreviewMetaFile( true );
+
+ if (xMetaFile)
+ {
+ std::unique_ptr<SvMemoryStream> xStream(
+ GraphicHelper::getFormatStrFromGDI_Impl(
+ xMetaFile.get(), ConvertDataFormat::WMF ) );
+
+ if (xStream)
+ {
+ xStream->SetVersion( SOFFICE_FILEFORMAT_CURRENT );
+ aAny <<= Sequence< sal_Int8 >( static_cast< const sal_Int8* >( xStream->GetData() ),
+ xStream->TellEnd() );
+ }
+ }
+ }
+ else if ( GraphicHelper::supportsMetaFileHandle_Impl()
+ && aFlavor.DataType == cppu::UnoType<sal_uInt64>::get())
+ {
+ // means HGLOBAL handler to memory storage containing METAFILEPICT structure
+
+ std::shared_ptr<GDIMetaFile> xMetaFile =
+ m_pData->m_pObjectShell->GetPreviewMetaFile( true );
+
+ if (xMetaFile)
+ {
+ Size aMetaSize = xMetaFile->GetPrefSize();
+ aAny <<= reinterpret_cast< sal_uInt64 >(
+ GraphicHelper::getWinMetaFileFromGDI_Impl(
+ xMetaFile.get(), aMetaSize ) );
+ }
+ }
+ else
+ throw datatransfer::UnsupportedFlavorException();
+ }
+ else if ( aFlavor.MimeType == "application/x-openoffice-bitmap;windows_formatname=\"Bitmap\"" )
+ {
+ if ( aFlavor.DataType != cppu::UnoType<Sequence< sal_Int8 >>::get() )
+ throw datatransfer::UnsupportedFlavorException();
+
+ std::shared_ptr<GDIMetaFile> xMetaFile =
+ m_pData->m_pObjectShell->GetPreviewMetaFile( true );
+
+ if (xMetaFile)
+ {
+ std::unique_ptr<SvMemoryStream> xStream(
+ GraphicHelper::getFormatStrFromGDI_Impl(
+ xMetaFile.get(), ConvertDataFormat::BMP ) );
+
+ if (xStream)
+ {
+ xStream->SetVersion( SOFFICE_FILEFORMAT_CURRENT );
+ aAny <<= Sequence< sal_Int8 >( static_cast< const sal_Int8* >( xStream->GetData() ),
+ xStream->TellEnd() );
+ }
+ }
+ }
+ else if ( aFlavor.MimeType == "image/png" )
+ {
+ if ( aFlavor.DataType != cppu::UnoType<Sequence< sal_Int8 >>::get() )
+ throw datatransfer::UnsupportedFlavorException();
+
+ std::shared_ptr<GDIMetaFile> xMetaFile =
+ m_pData->m_pObjectShell->GetPreviewMetaFile( true );
+
+ if (xMetaFile)
+ {
+ std::unique_ptr<SvMemoryStream> xStream(
+ GraphicHelper::getFormatStrFromGDI_Impl(
+ xMetaFile.get(), ConvertDataFormat::PNG ) );
+
+ if (xStream)
+ {
+ xStream->SetVersion( SOFFICE_FILEFORMAT_CURRENT );
+ aAny <<= Sequence< sal_Int8 >( static_cast< const sal_Int8* >( xStream->GetData() ),
+ xStream->TellEnd() );
+ }
+ }
+ }
+ else
+ throw datatransfer::UnsupportedFlavorException();
+ }
+
+ return aAny;
+}
+
+
+// XTransferable
+
+
+Sequence< datatransfer::DataFlavor > SAL_CALL SfxBaseModel::getTransferDataFlavors()
+{
+ SfxModelGuard aGuard( *this );
+
+ const sal_Int32 nSuppFlavors = GraphicHelper::supportsMetaFileHandle_Impl() ? 10 : 8;
+ Sequence< datatransfer::DataFlavor > aFlavorSeq( nSuppFlavors );
+ auto pFlavorSeq = aFlavorSeq.getArray();
+
+ pFlavorSeq[0].MimeType =
+ "application/x-openoffice-gdimetafile;windows_formatname=\"GDIMetaFile\"";
+ pFlavorSeq[0].HumanPresentableName = "GDIMetaFile";
+ pFlavorSeq[0].DataType = cppu::UnoType<Sequence< sal_Int8 >>::get();
+
+ pFlavorSeq[1].MimeType =
+ "application/x-openoffice-highcontrast-gdimetafile;windows_formatname=\"GDIMetaFile\"";
+ pFlavorSeq[1].HumanPresentableName = "GDIMetaFile";
+ pFlavorSeq[1].DataType = cppu::UnoType<Sequence< sal_Int8 >>::get();
+
+ pFlavorSeq[2].MimeType =
+ "application/x-openoffice-emf;windows_formatname=\"Image EMF\"" ;
+ pFlavorSeq[2].HumanPresentableName = "Enhanced Windows MetaFile";
+ pFlavorSeq[2].DataType = cppu::UnoType<Sequence< sal_Int8 >>::get();
+
+ pFlavorSeq[3].MimeType =
+ "application/x-openoffice-wmf;windows_formatname=\"Image WMF\"";
+ pFlavorSeq[3].HumanPresentableName = "Windows MetaFile";
+ pFlavorSeq[3].DataType = cppu::UnoType<Sequence< sal_Int8 >>::get();
+
+ pFlavorSeq[4].MimeType =
+ "application/x-openoffice-objectdescriptor-xml;windows_formatname=\"Star Object Descriptor (XML)\"";
+ pFlavorSeq[4].HumanPresentableName = "Star Object Descriptor (XML)";
+ pFlavorSeq[4].DataType = cppu::UnoType<Sequence< sal_Int8 >>::get();
+
+ pFlavorSeq[5].MimeType =
+ "application/x-openoffice-embed-source-xml;windows_formatname=\"Star Embed Source (XML)\"";
+ pFlavorSeq[5].HumanPresentableName = "Star Embed Source (XML)";
+ pFlavorSeq[5].DataType = cppu::UnoType<Sequence< sal_Int8 >>::get();
+
+ pFlavorSeq[6].MimeType =
+ "application/x-openoffice-bitmap;windows_formatname=\"Bitmap\"";
+ pFlavorSeq[6].HumanPresentableName = "Bitmap";
+ pFlavorSeq[6].DataType = cppu::UnoType<Sequence< sal_Int8 >>::get();
+
+ pFlavorSeq[7].MimeType = "image/png";
+ pFlavorSeq[7].HumanPresentableName = "PNG";
+ pFlavorSeq[7].DataType = cppu::UnoType<Sequence< sal_Int8 >>::get();
+
+ if ( nSuppFlavors == 10 )
+ {
+ pFlavorSeq[8].MimeType =
+ "application/x-openoffice-emf;windows_formatname=\"Image EMF\"";
+ pFlavorSeq[8].HumanPresentableName = "Enhanced Windows MetaFile";
+ pFlavorSeq[8].DataType = cppu::UnoType<sal_uInt64>::get();
+
+ pFlavorSeq[9].MimeType =
+ "application/x-openoffice-wmf;windows_formatname=\"Image WMF\"";
+ pFlavorSeq[9].HumanPresentableName = "Windows MetaFile";
+ pFlavorSeq[9].DataType = cppu::UnoType<sal_uInt64>::get();
+ }
+
+ return aFlavorSeq;
+}
+
+
+// XTransferable
+
+
+sal_Bool SAL_CALL SfxBaseModel::isDataFlavorSupported( const datatransfer::DataFlavor& aFlavor )
+{
+ SfxModelGuard aGuard( *this );
+
+ if ( aFlavor.MimeType == "application/x-openoffice-gdimetafile;windows_formatname=\"GDIMetaFile\"" )
+ {
+ if ( aFlavor.DataType == cppu::UnoType<Sequence< sal_Int8 >>::get() )
+ return true;
+ }
+ else if ( aFlavor.MimeType == "application/x-openoffice-highcontrast-gdimetafile;windows_formatname=\"GDIMetaFile\"" )
+ {
+ if ( aFlavor.DataType == cppu::UnoType<Sequence< sal_Int8 >>::get() )
+ return true;
+ }
+ else if ( aFlavor.MimeType == "application/x-openoffice-emf;windows_formatname=\"Image EMF\"" )
+ {
+ if ( aFlavor.DataType == cppu::UnoType<Sequence< sal_Int8 >>::get() )
+ return true;
+ else if ( GraphicHelper::supportsMetaFileHandle_Impl()
+ && aFlavor.DataType == cppu::UnoType<sal_uInt64>::get())
+ return true;
+ }
+ else if ( aFlavor.MimeType == "application/x-openoffice-wmf;windows_formatname=\"Image WMF\"" )
+ {
+ if ( aFlavor.DataType == cppu::UnoType<Sequence< sal_Int8 >>::get() )
+ return true;
+ else if ( GraphicHelper::supportsMetaFileHandle_Impl()
+ && aFlavor.DataType == cppu::UnoType<sal_uInt64>::get())
+ return true;
+ }
+ else if ( aFlavor.MimeType == "application/x-openoffice-objectdescriptor-xml;windows_formatname=\"Star Object Descriptor (XML)\"" )
+ {
+ if ( aFlavor.DataType == cppu::UnoType<Sequence< sal_Int8 >>::get() )
+ return true;
+ }
+ else if ( aFlavor.MimeType == "application/x-openoffice-embed-source;windows_formatname=\"Star EMBS\"" )
+ {
+ if ( aFlavor.DataType == cppu::UnoType<Sequence< sal_Int8 >>::get() )
+ return true;
+ }
+ else if ( aFlavor.MimeType == "application/x-openoffice-bitmap;windows_formatname=\"Bitmap\"" )
+ {
+ if ( aFlavor.DataType == cppu::UnoType<Sequence< sal_Int8 >>::get() )
+ return true;
+ }
+ else if ( aFlavor.MimeType == "image/png" )
+ {
+ if ( aFlavor.DataType == cppu::UnoType<Sequence< sal_Int8 >>::get() )
+ return true;
+ }
+
+ return false;
+}
+
+
+// XEventsSupplier
+
+
+Reference< container::XNameReplace > SAL_CALL SfxBaseModel::getEvents()
+{
+ SfxModelGuard aGuard( *this );
+
+ if ( ! m_pData->m_xEvents.is() )
+ {
+ m_pData->m_xEvents = new SfxEvents_Impl( m_pData->m_pObjectShell.get(), this );
+ }
+
+ return m_pData->m_xEvents;
+}
+
+
+// XEmbeddedScripts
+
+
+Reference< script::XStorageBasedLibraryContainer > SAL_CALL SfxBaseModel::getBasicLibraries()
+{
+ SfxModelGuard aGuard( *this );
+
+ Reference< script::XStorageBasedLibraryContainer > xBasicLibraries;
+ if ( m_pData->m_pObjectShell.is() )
+ xBasicLibraries.set(m_pData->m_pObjectShell->GetBasicContainer(), UNO_QUERY);
+ return xBasicLibraries;
+}
+
+Reference< script::XStorageBasedLibraryContainer > SAL_CALL SfxBaseModel::getDialogLibraries()
+{
+ SfxModelGuard aGuard( *this );
+
+ Reference< script::XStorageBasedLibraryContainer > xDialogLibraries;
+ if ( m_pData->m_pObjectShell.is() )
+ xDialogLibraries.set(m_pData->m_pObjectShell->GetDialogContainer(), UNO_QUERY);
+ return xDialogLibraries;
+}
+
+sal_Bool SAL_CALL SfxBaseModel::getAllowMacroExecution()
+{
+ SfxModelGuard aGuard( *this );
+
+ if ( m_pData->m_pObjectShell.is() )
+ return m_pData->m_pObjectShell->AdjustMacroMode();
+ return false;
+}
+
+
+// XScriptInvocationContext
+
+
+Reference< document::XEmbeddedScripts > SAL_CALL SfxBaseModel::getScriptContainer()
+{
+ SfxModelGuard aGuard( *this );
+
+ Reference< document::XEmbeddedScripts > xDocumentScripts;
+
+ try
+ {
+ Reference< frame::XModel > xDocument( this );
+ xDocumentScripts.set( xDocument, UNO_QUERY );
+ while ( !xDocumentScripts.is() && xDocument.is() )
+ {
+ Reference< container::XChild > xDocAsChild( xDocument, UNO_QUERY );
+ if ( !xDocAsChild.is() )
+ {
+ xDocument = nullptr;
+ break;
+ }
+
+ xDocument.set( xDocAsChild->getParent(), UNO_QUERY );
+ xDocumentScripts.set( xDocument, UNO_QUERY );
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("sfx.doc");
+ xDocumentScripts = nullptr;
+ }
+
+ return xDocumentScripts;
+}
+
+
+// XEventBroadcaster
+
+
+void SAL_CALL SfxBaseModel::addEventListener( const Reference< document::XEventListener >& aListener )
+{
+ SfxModelGuard aGuard( *this, SfxModelGuard::E_INITIALIZING );
+
+ m_pData->m_aInterfaceContainer.addInterface( cppu::UnoType<document::XEventListener>::get(), aListener );
+}
+
+
+// XEventBroadcaster
+
+
+void SAL_CALL SfxBaseModel::removeEventListener( const Reference< document::XEventListener >& aListener )
+{
+ SfxModelGuard aGuard( *this );
+
+ m_pData->m_aInterfaceContainer.removeInterface( cppu::UnoType<document::XEventListener>::get(), aListener );
+}
+
+// XShapeEventBroadcaster
+
+void SAL_CALL SfxBaseModel::addShapeEventListener( const css::uno::Reference< css::drawing::XShape >& xShape, const Reference< document::XShapeEventListener >& xListener )
+{
+ assert(xShape.is() && "no shape?");
+ SfxModelGuard aGuard( *this, SfxModelGuard::E_INITIALIZING );
+
+ m_pData->maShapeListeners[xShape].push_back(xListener);
+}
+
+
+// XShapeEventBroadcaster
+
+
+void SAL_CALL SfxBaseModel::removeShapeEventListener( const css::uno::Reference< css::drawing::XShape >& xShape, const Reference< document::XShapeEventListener >& xListener )
+{
+ SfxModelGuard aGuard( *this );
+
+ auto it = m_pData->maShapeListeners.find(xShape);
+ if (it != m_pData->maShapeListeners.end())
+ {
+ auto rVec = it->second;
+ auto it2 = std::find(rVec.begin(), rVec.end(), xListener);
+ if (it2 != rVec.end())
+ {
+ rVec.erase(it2);
+ if (rVec.empty())
+ m_pData->maShapeListeners.erase(it);
+ }
+ }
+}
+
+// XDocumentEventBroadcaster
+
+
+void SAL_CALL SfxBaseModel::addDocumentEventListener( const Reference< document::XDocumentEventListener >& aListener )
+{
+ SfxModelGuard aGuard( *this, SfxModelGuard::E_INITIALIZING );
+ m_pData->m_aInterfaceContainer.addInterface( cppu::UnoType<document::XDocumentEventListener>::get(), aListener );
+}
+
+
+void SAL_CALL SfxBaseModel::removeDocumentEventListener( const Reference< document::XDocumentEventListener >& aListener )
+{
+ SfxModelGuard aGuard( *this );
+ m_pData->m_aInterfaceContainer.removeInterface( cppu::UnoType<document::XDocumentEventListener>::get(), aListener );
+}
+
+
+void SAL_CALL SfxBaseModel::notifyDocumentEvent( const OUString&, const Reference< frame::XController2 >&, const Any& )
+{
+ throw lang::NoSupportException("SfxBaseModel controls all the sent notifications itself!" );
+}
+
+Sequence<document::CmisProperty> SAL_CALL SfxBaseModel::getCmisProperties()
+{
+ if (impl_isDisposed())
+ return Sequence<document::CmisProperty>();
+ return m_pData->m_cmisProperties;
+}
+
+void SAL_CALL SfxBaseModel::setCmisProperties( const Sequence< document::CmisProperty >& _cmisproperties )
+{
+ m_pData->m_cmisProperties = _cmisproperties;
+}
+
+void SAL_CALL SfxBaseModel::updateCmisProperties( const Sequence< document::CmisProperty >& aProperties )
+{
+ SfxMedium* pMedium = m_pData->m_pObjectShell->GetMedium();
+ if ( !pMedium )
+ return;
+
+ try
+ {
+ ::ucbhelper::Content aContent( pMedium->GetName( ),
+ Reference<ucb::XCommandEnvironment>(),
+ comphelper::getProcessComponentContext() );
+
+ aContent.executeCommand( "updateProperties", uno::Any( aProperties ) );
+ loadCmisProperties( );
+ }
+ catch (const Exception & e)
+ {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw lang::WrappedTargetRuntimeException( e.Message,
+ e.Context, anyEx );
+ }
+
+}
+
+void SAL_CALL SfxBaseModel::checkOut( )
+{
+ SfxMedium* pMedium = m_pData->m_pObjectShell->GetMedium();
+ if ( !pMedium )
+ return;
+
+ try
+ {
+ ::ucbhelper::Content aContent( pMedium->GetName(),
+ Reference<ucb::XCommandEnvironment>(),
+ comphelper::getProcessComponentContext() );
+
+ Any aResult = aContent.executeCommand( "checkout", Any( ) );
+ OUString sURL;
+ aResult >>= sURL;
+
+ m_pData->m_pObjectShell->GetMedium( )->SetName( sURL );
+ m_pData->m_pObjectShell->GetMedium( )->GetMedium_Impl( );
+ m_pData->m_xDocumentProperties->setTitle( getTitle( ) );
+ Sequence< beans::PropertyValue > aSequence ;
+ TransformItems( SID_OPENDOC, *pMedium->GetItemSet(), aSequence );
+ attachResource( sURL, aSequence );
+
+ // Reload the CMIS properties
+ loadCmisProperties( );
+ }
+ catch ( const Exception & e )
+ {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw lang::WrappedTargetRuntimeException( e.Message,
+ e.Context, anyEx );
+ }
+}
+
+void SAL_CALL SfxBaseModel::cancelCheckOut( )
+{
+ SfxMedium* pMedium = m_pData->m_pObjectShell->GetMedium();
+ if ( !pMedium )
+ return;
+
+ try
+ {
+ ::ucbhelper::Content aContent( pMedium->GetName(),
+ Reference<ucb::XCommandEnvironment>(),
+ comphelper::getProcessComponentContext() );
+
+ Any aResult = aContent.executeCommand( "cancelCheckout", Any( ) );
+ OUString sURL;
+ aResult >>= sURL;
+
+ m_pData->m_pObjectShell->GetMedium( )->SetName( sURL );
+ }
+ catch ( const Exception & e )
+ {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw lang::WrappedTargetRuntimeException( e.Message,
+ e.Context, anyEx );
+ }
+}
+
+void SAL_CALL SfxBaseModel::checkIn( sal_Bool bIsMajor, const OUString& rMessage )
+{
+ SfxMedium* pMedium = m_pData->m_pObjectShell->GetMedium();
+ if ( !pMedium )
+ return;
+
+ try
+ {
+ Sequence< beans::PropertyValue > aProps{
+ comphelper::makePropertyValue("VersionMajor", bIsMajor),
+ comphelper::makePropertyValue("VersionComment", rMessage),
+ comphelper::makePropertyValue("CheckIn", true)
+ };
+
+ const OUString sName( pMedium->GetName( ) );
+ storeSelf( aProps );
+
+ // Refresh pMedium as it has probably changed during the storeSelf call
+ pMedium = m_pData->m_pObjectShell->GetMedium( );
+ const OUString sNewName( pMedium->GetName( ) );
+
+ // URL has changed, update the document
+ if ( sName != sNewName )
+ {
+ m_pData->m_xDocumentProperties->setTitle( getTitle( ) );
+ Sequence< beans::PropertyValue > aSequence ;
+ TransformItems( SID_OPENDOC, *pMedium->GetItemSet(), aSequence );
+ attachResource( sNewName, aSequence );
+
+ // Reload the CMIS properties
+ loadCmisProperties( );
+ }
+ }
+ catch ( const Exception & e )
+ {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw lang::WrappedTargetRuntimeException( e.Message,
+ e.Context, anyEx );
+ }
+}
+
+uno::Sequence< document::CmisVersion > SAL_CALL SfxBaseModel::getAllVersions( )
+{
+ uno::Sequence<document::CmisVersion> aVersions;
+ if (impl_isDisposed())
+ return aVersions;
+ SfxMedium* pMedium = m_pData->m_pObjectShell->GetMedium();
+ if ( pMedium )
+ {
+ try
+ {
+ ::ucbhelper::Content aContent( pMedium->GetName(),
+ Reference<ucb::XCommandEnvironment>(),
+ comphelper::getProcessComponentContext() );
+
+ Any aResult = aContent.executeCommand( "getAllVersions", Any( ) );
+ aResult >>= aVersions;
+ }
+ catch ( const Exception & e )
+ {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw lang::WrappedTargetRuntimeException( e.Message,
+ e.Context, anyEx );
+ }
+ }
+ return aVersions;
+}
+
+bool SfxBaseModel::getBoolPropertyValue( const OUString& rName )
+{
+ bool bValue = false;
+ if ( m_pData->m_pObjectShell.is() )
+ {
+ SfxMedium* pMedium = m_pData->m_pObjectShell->GetMedium();
+ if ( pMedium )
+ {
+ try
+ {
+ ::ucbhelper::Content aContent( pMedium->GetName( ),
+ utl::UCBContentHelper::getDefaultCommandEnvironment(),
+ comphelper::getProcessComponentContext() );
+ Reference < beans::XPropertySetInfo > xProps = aContent.getProperties();
+ if ( xProps->hasPropertyByName( rName ) )
+ {
+ aContent.getPropertyValue( rName ) >>= bValue;
+ }
+ }
+ catch ( const Exception & )
+ {
+ // Simply ignore it: it's likely the document isn't versionable in that case
+ bValue = false;
+ }
+ }
+ }
+ return bValue;
+}
+
+sal_Bool SAL_CALL SfxBaseModel::isVersionable( )
+{
+ return getBoolPropertyValue( "IsVersionable" );
+}
+
+sal_Bool SAL_CALL SfxBaseModel::canCheckOut( )
+{
+ return getBoolPropertyValue( "CanCheckOut" );
+}
+
+sal_Bool SAL_CALL SfxBaseModel::canCancelCheckOut( )
+{
+ return getBoolPropertyValue( "CanCancelCheckOut" );
+}
+
+sal_Bool SAL_CALL SfxBaseModel::canCheckIn( )
+{
+ return getBoolPropertyValue( "CanCheckIn" );
+}
+
+void SfxBaseModel::loadCmisProperties( )
+{
+ SfxMedium* pMedium = m_pData->m_pObjectShell->GetMedium();
+ if ( !pMedium )
+ return;
+
+ try
+ {
+ ::ucbhelper::Content aContent( pMedium->GetName( ),
+ utl::UCBContentHelper::getDefaultCommandEnvironment(),
+ comphelper::getProcessComponentContext() );
+ Reference < beans::XPropertySetInfo > xProps = aContent.getProperties();
+ static const OUStringLiteral aCmisProps( u"CmisProperties" );
+ if ( xProps->hasPropertyByName( aCmisProps ) )
+ {
+ Sequence< document::CmisProperty> aCmisProperties;
+ aContent.getPropertyValue( aCmisProps ) >>= aCmisProperties;
+ setCmisProperties( aCmisProperties );
+ }
+ }
+ catch (const ucb::ContentCreationException &)
+ {
+ }
+ catch (const ucb::CommandAbortedException &)
+ {
+ }
+}
+
+SfxMedium* SfxBaseModel::handleLoadError( ErrCode nError, SfxMedium* pMedium )
+{
+ if (!nError)
+ {
+ // No error condition.
+ return pMedium;
+ }
+
+ bool bSilent = false;
+ const SfxBoolItem* pSilentItem = SfxItemSet::GetItem<SfxBoolItem>(pMedium->GetItemSet(), SID_SILENT, false);
+ if( pSilentItem )
+ bSilent = pSilentItem->GetValue();
+
+ bool bWarning = nError.IsWarning();
+ if ( nError != ERRCODE_IO_BROKENPACKAGE && !bSilent )
+ {
+ // broken package was handled already
+ if ( SfxObjectShell::UseInteractionToHandleError(pMedium->GetInteractionHandler(), nError) && !bWarning)
+ {
+ // abort loading (except for warnings)
+ nError = ERRCODE_IO_ABORT;
+ }
+ }
+
+ if ( m_pData->m_pObjectShell->GetMedium() != pMedium )
+ {
+ // for whatever reason document now has another medium
+ OSL_FAIL("Document has rejected the medium?!");
+ delete pMedium;
+ pMedium = nullptr;
+ }
+
+ if ( !bWarning ) // #i30711# don't abort loading if it's only a warning
+ {
+ nError = nError ? nError : ERRCODE_IO_CANTREAD;
+ throw task::ErrorCodeIOException(
+ "SfxBaseModel::handleLoadError: 0x" + nError.toHexString(),
+ Reference< XInterface >(), sal_uInt32(nError));
+ }
+
+ return pMedium;
+}
+
+
+// SfxListener
+
+
+static void addTitle_Impl( Sequence < beans::PropertyValue >& rSeq, const OUString& rTitle )
+{
+ auto [begin, end] = asNonConstRange(rSeq);
+ auto pProp = std::find_if(begin, end,
+ [](const beans::PropertyValue& rProp) { return rProp.Name == "Title"; });
+ if (pProp != end)
+ {
+ pProp->Value <<= rTitle;
+ }
+ else
+ {
+ sal_Int32 nCount = rSeq.getLength();
+ rSeq.realloc( nCount+1 );
+ auto& el = rSeq.getArray()[nCount];
+ el.Name = "Title";
+ el.Value <<= rTitle;
+ }
+}
+
+void SfxBaseModel::Notify( SfxBroadcaster& rBC ,
+ const SfxHint& rHint )
+{
+ if ( !m_pData )
+ return;
+
+ if ( &rBC != m_pData->m_pObjectShell.get() )
+ return;
+
+ if ( rHint.GetId() == SfxHintId::DocChanged )
+ changing();
+
+ const SfxEventHint* pNamedHint = dynamic_cast<const SfxEventHint*>(&rHint);
+ if ( pNamedHint )
+ {
+
+ switch ( pNamedHint->GetEventId() )
+ {
+ case SfxEventHintId::StorageChanged:
+ {
+ if ( m_pData->m_xUIConfigurationManager.is()
+ && m_pData->m_pObjectShell->GetCreateMode() != SfxObjectCreateMode::EMBEDDED )
+ {
+ Reference< embed::XStorage > xConfigStorage;
+ static const OUStringLiteral aUIConfigFolderName( u"Configurations2" );
+
+ xConfigStorage = getDocumentSubStorage( aUIConfigFolderName, embed::ElementModes::READWRITE );
+ if ( !xConfigStorage.is() )
+ xConfigStorage = getDocumentSubStorage( aUIConfigFolderName, embed::ElementModes::READ );
+
+ if ( xConfigStorage.is() || !m_pData->m_pObjectShell->GetStorage()->hasByName( aUIConfigFolderName ) )
+ {
+ // the storage is different, since otherwise it could not be opened, so it must be exchanged
+ m_pData->m_xUIConfigurationManager->setStorage( xConfigStorage );
+ }
+ else
+ {
+ OSL_FAIL( "Unexpected scenario!" );
+ }
+ }
+
+ ListenForStorage_Impl( m_pData->m_pObjectShell->GetStorage() );
+ }
+ break;
+
+ case SfxEventHintId::LoadFinished:
+ {
+ impl_getPrintHelper();
+ ListenForStorage_Impl( m_pData->m_pObjectShell->GetStorage() );
+ m_pData->m_bModifiedSinceLastSave = false;
+ }
+ break;
+
+ case SfxEventHintId::SaveAsDocDone:
+ {
+ m_pData->m_sURL = m_pData->m_pObjectShell->GetMedium()->GetName();
+
+ SfxItemSet *pSet = m_pData->m_pObjectShell->GetMedium()->GetItemSet();
+ Sequence< beans::PropertyValue > aArgs;
+ TransformItems( SID_SAVEASDOC, *pSet, aArgs );
+ addTitle_Impl( aArgs, m_pData->m_pObjectShell->GetTitle() );
+ attachResource( m_pData->m_pObjectShell->GetMedium()->GetName(), aArgs );
+ }
+ break;
+
+ case SfxEventHintId::DocCreated:
+ {
+ impl_getPrintHelper();
+ m_pData->m_bModifiedSinceLastSave = false;
+ }
+ break;
+
+ case SfxEventHintId::ModifyChanged:
+ {
+ m_pData->m_bModifiedSinceLastSave = isModified();
+ }
+ break;
+ default: break;
+ }
+
+ Any aSupplement;
+ if (const SfxPrintingHint* pPrintingHint = dynamic_cast<const SfxPrintingHint*>(&rHint))
+ aSupplement <<= pPrintingHint->GetWhich();
+ const SfxViewEventHint* pViewHint = dynamic_cast<const SfxViewEventHint*>(&rHint);
+ postEvent_Impl( pNamedHint->GetEventName(), pViewHint ? pViewHint->GetController() : Reference< frame::XController2 >(), aSupplement );
+ }
+
+ if ( rHint.GetId() == SfxHintId::TitleChanged )
+ {
+ addTitle_Impl( m_pData->m_seqArguments, m_pData->m_pObjectShell->GetTitle() );
+ postEvent_Impl( GlobalEventConfig::GetEventName( GlobalEventId::TITLECHANGED ) );
+ }
+ else if ( rHint.GetId() == SfxHintId::ModeChanged )
+ {
+ postEvent_Impl( GlobalEventConfig::GetEventName( GlobalEventId::MODECHANGED ) );
+ }
+}
+
+
+// public impl.
+
+
+void SfxBaseModel::NotifyModifyListeners_Impl() const
+{
+ comphelper::OInterfaceContainerHelper2* pIC = m_pData->m_aInterfaceContainer.getContainer( cppu::UnoType<util::XModifyListener>::get());
+ if ( pIC )
+ {
+ lang::EventObject aEvent( static_cast<frame::XModel *>(const_cast<SfxBaseModel *>(this)) );
+ pIC->notifyEach( &util::XModifyListener::modified, aEvent );
+ }
+
+ // this notification here is done too generously, we cannot simply assume that we're really modified
+ // now, but we need to check it ...
+ m_pData->m_bModifiedSinceLastSave = const_cast< SfxBaseModel* >( this )->isModified();
+}
+
+void SfxBaseModel::changing()
+{
+ SfxModelGuard aGuard( *this );
+
+ // the notification should not be sent if the document can not be modified
+ if ( !m_pData->m_pObjectShell.is() || !m_pData->m_pObjectShell->IsEnableSetModified() )
+ return;
+
+ NotifyModifyListeners_Impl();
+}
+
+
+// public impl.
+
+
+SfxObjectShell* SfxBaseModel::GetObjectShell() const
+{
+ return m_pData ? m_pData->m_pObjectShell.get() : nullptr;
+}
+
+
+// public impl.
+
+
+bool SfxBaseModel::IsInitialized() const
+{
+ if ( !m_pData || !m_pData->m_pObjectShell.is() )
+ {
+ OSL_FAIL( "SfxBaseModel::IsInitialized: this should have been caught earlier!" );
+ return false;
+ }
+
+ return m_pData->m_pObjectShell->GetMedium() != nullptr;
+}
+
+void SfxBaseModel::MethodEntryCheck( const bool i_mustBeInitialized ) const
+{
+ if ( impl_isDisposed() )
+ throw lang::DisposedException( OUString(), *const_cast< SfxBaseModel* >( this ) );
+ if ( i_mustBeInitialized && !IsInitialized() )
+ throw lang::NotInitializedException( OUString(), *const_cast< SfxBaseModel* >( this ) );
+}
+
+bool SfxBaseModel::impl_isDisposed() const
+{
+ return ( m_pData == nullptr ) ;
+}
+
+
+// private impl.
+
+
+OUString SfxBaseModel::GetMediumFilterName_Impl() const
+{
+ std::shared_ptr<const SfxFilter> pFilter;
+ SfxMedium* pMedium = m_pData->m_pObjectShell->GetMedium();
+ if ( pMedium )
+ pFilter = pMedium->GetFilter();
+
+ if ( pFilter )
+ return pFilter->GetName();
+
+ return OUString();
+}
+
+void SfxBaseModel::impl_store( const OUString& sURL ,
+ const Sequence< beans::PropertyValue >& seqArguments ,
+ bool bSaveTo )
+{
+ if( sURL.isEmpty() )
+ throw frame::IllegalArgumentIOException();
+
+ bool bSaved = false;
+ if ( !bSaveTo && m_pData->m_pObjectShell.is() && !sURL.isEmpty()
+ && !sURL.startsWith( "private:stream" )
+ && ::utl::UCBContentHelper::EqualURLs( getLocation(), sURL ) )
+ {
+ // this is the same file URL as the current document location, try to use storeOwn if possible
+
+ ::comphelper::SequenceAsHashMap aArgHash( seqArguments );
+ static const OUStringLiteral aFilterString( u"FilterName" );
+ const OUString aFilterName( aArgHash.getUnpackedValueOrDefault( aFilterString, OUString() ) );
+ if ( !aFilterName.isEmpty() )
+ {
+ SfxMedium* pMedium = m_pData->m_pObjectShell->GetMedium();
+ if ( pMedium )
+ {
+ const std::shared_ptr<const SfxFilter>& pFilter = pMedium->GetFilter();
+ if ( pFilter && aFilterName == pFilter->GetFilterName() )
+ {
+ // #i119366# - If the former file saving with password, do not trying in StoreSelf anyway...
+ bool bFormerPassword = false;
+ {
+ uno::Sequence< beans::NamedValue > aOldEncryptionData;
+ if (GetEncryptionData_Impl( pMedium->GetItemSet(), aOldEncryptionData ))
+ {
+ bFormerPassword = true;
+ }
+ }
+ if ( !bFormerPassword )
+ {
+ aArgHash.erase( aFilterString );
+ aArgHash.erase( "URL" );
+
+ try
+ {
+ storeSelf( aArgHash.getAsConstPropertyValueList() );
+ bSaved = true;
+ }
+ catch( const lang::IllegalArgumentException& )
+ {
+#if HAVE_FEATURE_MULTIUSER_ENVIRONMENT
+ // some additional arguments do not allow to use saving, SaveAs should be done
+ // but only for normal documents, the shared documents would be overwritten in this case
+ // that would mean an information loss
+ // TODO/LATER: need a new interaction for this case
+ if ( m_pData->m_pObjectShell->IsDocShared() )
+ {
+ uno::Sequence< beans::NamedValue > aNewEncryptionData = aArgHash.getUnpackedValueOrDefault("EncryptionData", uno::Sequence< beans::NamedValue >() );
+ if ( !aNewEncryptionData.hasElements() )
+ {
+ aNewEncryptionData = ::comphelper::OStorageHelper::CreatePackageEncryptionData( aArgHash.getUnpackedValueOrDefault("Password", OUString()) );
+ }
+
+ uno::Sequence< beans::NamedValue > aOldEncryptionData;
+ (void)GetEncryptionData_Impl( pMedium->GetItemSet(), aOldEncryptionData );
+
+ if ( !aOldEncryptionData.hasElements() && !aNewEncryptionData.hasElements() )
+ throw;
+ else
+ {
+ // if the password is changed a special error should be used in case of shared document
+ throw task::ErrorCodeIOException("Can not change password for shared document.", uno::Reference< uno::XInterface >(), sal_uInt32(ERRCODE_SFX_SHARED_NOPASSWORDCHANGE) );
+ }
+ }
+#endif
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if ( bSaved || !m_pData->m_pObjectShell.is() )
+ return;
+
+ SfxGetpApp()->NotifyEvent( SfxEventHint( bSaveTo ? SfxEventHintId::SaveToDoc : SfxEventHintId::SaveAsDoc, GlobalEventConfig::GetEventName( bSaveTo ? GlobalEventId::SAVETODOC : GlobalEventId::SAVEASDOC ),
+ m_pData->m_pObjectShell.get() ) );
+
+ std::optional<SfxAllItemSet> pItemSet(SfxGetpApp()->GetPool());
+ pItemSet->Put(SfxStringItem(SID_FILE_NAME, sURL));
+ if ( bSaveTo )
+ pItemSet->Put(SfxBoolItem(SID_SAVETO, true));
+
+ TransformParameters(SID_SAVEASDOC, seqArguments, *pItemSet);
+
+ const SfxBoolItem* pCopyStreamItem = pItemSet->GetItem<SfxBoolItem>(SID_COPY_STREAM_IF_POSSIBLE, false);
+
+ if ( pCopyStreamItem && pCopyStreamItem->GetValue() && !bSaveTo )
+ {
+ throw frame::IllegalArgumentIOException(
+ "CopyStreamIfPossible parameter is not acceptable for storeAsURL() call!" );
+ }
+
+ sal_uInt32 nModifyPasswordHash = 0;
+ Sequence< beans::PropertyValue > aModifyPasswordInfo;
+ const SfxUnoAnyItem* pModifyPasswordInfoItem = pItemSet->GetItem<SfxUnoAnyItem>(SID_MODIFYPASSWORDINFO, false);
+ if ( pModifyPasswordInfoItem )
+ {
+ // it contains either a simple hash or a set of PropertyValues
+ // TODO/LATER: the sequence of PropertyValue should replace the hash completely in future
+ sal_Int32 nMPHTmp = 0;
+ pModifyPasswordInfoItem->GetValue() >>= nMPHTmp;
+ nModifyPasswordHash = static_cast<sal_uInt32>(nMPHTmp);
+ pModifyPasswordInfoItem->GetValue() >>= aModifyPasswordInfo;
+ }
+ pItemSet->ClearItem(SID_MODIFYPASSWORDINFO);
+ sal_uInt32 nOldModifyPasswordHash = m_pData->m_pObjectShell->GetModifyPasswordHash();
+ m_pData->m_pObjectShell->SetModifyPasswordHash( nModifyPasswordHash );
+ Sequence< beans::PropertyValue > aOldModifyPasswordInfo = m_pData->m_pObjectShell->GetModifyPasswordInfo();
+ m_pData->m_pObjectShell->SetModifyPasswordInfo( aModifyPasswordInfo );
+
+ // since saving a document modifies its DocumentProperties, the current
+ // DocumentProperties must be saved on "SaveTo", so it can be restored
+ // after saving
+ bool bCopyTo = bSaveTo ||
+ m_pData->m_pObjectShell->GetCreateMode() == SfxObjectCreateMode::EMBEDDED;
+ Reference<document::XDocumentProperties> xOldDocProps;
+ if ( bCopyTo )
+ {
+ xOldDocProps = getDocumentProperties();
+ const Reference<util::XCloneable> xCloneable(xOldDocProps,
+ UNO_QUERY_THROW);
+ const Reference<document::XDocumentProperties> xNewDocProps(
+ xCloneable->createClone(), UNO_QUERY_THROW);
+ m_pData->m_xDocumentProperties = xNewDocProps;
+ }
+
+ bool bRet = m_pData->m_pObjectShell->APISaveAs_Impl(sURL, *pItemSet, seqArguments);
+
+ if ( bCopyTo )
+ {
+ // restore DocumentProperties if a copy was created
+ m_pData->m_xDocumentProperties = xOldDocProps;
+ }
+
+ Reference < task::XInteractionHandler > xHandler;
+ const SfxUnoAnyItem* pItem = pItemSet->GetItem<SfxUnoAnyItem>(SID_INTERACTIONHANDLER, false);
+ if ( pItem )
+ pItem->GetValue() >>= xHandler;
+
+ pItemSet.reset();
+
+ ErrCode nErrCode = m_pData->m_pObjectShell->GetErrorCode();
+ if ( !bRet && !nErrCode )
+ {
+ SAL_WARN("sfx.doc", "Storing has failed, no error is set!");
+ nErrCode = ERRCODE_IO_CANTWRITE;
+ }
+ m_pData->m_pObjectShell->ResetError();
+
+ if ( bRet )
+ {
+ if ( nErrCode )
+ {
+ // must be a warning - use Interactionhandler if possible or abandon
+ if ( xHandler.is() )
+ {
+ // TODO/LATER: a general way to set the error context should be available
+ SfxErrorContext aEc( ERRCTX_SFX_SAVEASDOC, m_pData->m_pObjectShell->GetTitle() );
+
+ task::ErrorCodeRequest aErrorCode;
+ aErrorCode.ErrCode = sal_uInt32(nErrCode);
+ SfxMedium::CallApproveHandler( xHandler, Any( aErrorCode ), false );
+ }
+ }
+
+ if ( !bSaveTo )
+ {
+ m_pData->m_aPreusedFilterName = GetMediumFilterName_Impl();
+ m_pData->m_pObjectShell->SetModifyPasswordEntered();
+
+ SfxGetpApp()->NotifyEvent( SfxEventHint( SfxEventHintId::SaveAsDocDone, GlobalEventConfig::GetEventName(GlobalEventId::SAVEASDOCDONE), m_pData->m_pObjectShell.get() ) );
+ }
+ else
+ {
+ m_pData->m_pObjectShell->SetModifyPasswordHash( nOldModifyPasswordHash );
+ m_pData->m_pObjectShell->SetModifyPasswordInfo( aOldModifyPasswordInfo );
+
+ SfxGetpApp()->NotifyEvent( SfxEventHint( SfxEventHintId::SaveToDocDone, GlobalEventConfig::GetEventName(GlobalEventId::SAVETODOCDONE), m_pData->m_pObjectShell.get() ) );
+ }
+ }
+ else
+ {
+ m_pData->m_pObjectShell->SetModifyPasswordHash( nOldModifyPasswordHash );
+ m_pData->m_pObjectShell->SetModifyPasswordInfo( aOldModifyPasswordInfo );
+
+
+ SfxGetpApp()->NotifyEvent( SfxEventHint( bSaveTo ? SfxEventHintId::SaveToDocFailed : SfxEventHintId::SaveAsDocFailed, GlobalEventConfig::GetEventName( bSaveTo ? GlobalEventId::SAVETODOCFAILED : GlobalEventId::SAVEASDOCFAILED),
+ m_pData->m_pObjectShell.get() ) );
+
+ std::stringstream aErrCode;
+ aErrCode << nErrCode;
+ throw task::ErrorCodeIOException(
+ "SfxBaseModel::impl_store <" + sURL + "> failed: " + OUString::fromUtf8(aErrCode.str().c_str()),
+ Reference< XInterface >(), sal_uInt32(nErrCode));
+ }
+}
+
+
+namespace {
+template< typename ListenerT, typename EventT >
+class NotifySingleListenerIgnoreRE
+{
+private:
+ typedef void ( SAL_CALL ListenerT::*NotificationMethod )( const EventT& );
+ NotificationMethod m_pMethod;
+ const EventT& m_rEvent;
+public:
+ NotifySingleListenerIgnoreRE( NotificationMethod method, const EventT& event ) : m_pMethod( method ), m_rEvent( event ) { }
+
+ void operator()( const Reference<ListenerT>& listener ) const
+ {
+ try
+ {
+ (listener.get()->*m_pMethod)( m_rEvent );
+ }
+ catch( RuntimeException& )
+ {
+ // this exception is ignored to avoid problems with invalid listeners, the listener should be probably thrown away in future
+ }
+ }
+};
+} // anonymous namespace
+
+void SfxBaseModel::postEvent_Impl( const OUString& aName, const Reference< frame::XController2 >& xController, const Any& supplement )
+{
+ // object already disposed?
+ if ( impl_isDisposed() )
+ return;
+
+ // keep m_pData alive, if notified target would dispose the document
+ std::shared_ptr<IMPL_SfxBaseModel_DataContainer> xKeepAlive(m_pData);
+
+ // also make sure this object doesn't self-destruct while notifying
+ rtl::Reference<SfxBaseModel> xHoldAlive(this);
+
+ DBG_ASSERT( !aName.isEmpty(), "Empty event name!" );
+ if (aName.isEmpty())
+ return;
+
+ comphelper::OInterfaceContainerHelper2* pIC =
+ m_pData->m_aInterfaceContainer.getContainer( cppu::UnoType<document::XDocumentEventListener>::get());
+ if ( pIC )
+ {
+ SAL_INFO("sfx.doc", "SfxDocumentEvent: " + aName);
+
+ document::DocumentEvent aDocumentEvent( static_cast<frame::XModel*>(this), aName, xController, supplement );
+
+ pIC->forEach< document::XDocumentEventListener, NotifySingleListenerIgnoreRE< document::XDocumentEventListener, document::DocumentEvent > >(
+ NotifySingleListenerIgnoreRE< document::XDocumentEventListener, document::DocumentEvent >(
+ &document::XDocumentEventListener::documentEventOccured,
+ aDocumentEvent ) );
+ }
+
+ pIC = m_pData->m_aInterfaceContainer.getContainer( cppu::UnoType<document::XEventListener>::get());
+ if ( pIC )
+ {
+ SAL_INFO("sfx.doc", "SfxEvent: " + aName);
+
+ document::EventObject aEvent( static_cast<frame::XModel*>(this), aName );
+
+ pIC->forEach< document::XEventListener, NotifySingleListenerIgnoreRE< document::XEventListener, document::EventObject > >(
+ NotifySingleListenerIgnoreRE< document::XEventListener, document::EventObject >(
+ &document::XEventListener::notifyEvent,
+ aEvent ) );
+ }
+
+}
+
+Reference < container::XIndexAccess > SAL_CALL SfxBaseModel::getViewData()
+{
+ SfxModelGuard aGuard( *this );
+
+ if ( m_pData->m_pObjectShell.is() && !m_pData->m_contViewData.is() )
+ {
+ SfxViewFrame *pActFrame = SfxViewFrame::Current();
+ if ( !pActFrame || pActFrame->GetObjectShell() != m_pData->m_pObjectShell.get() )
+ pActFrame = SfxViewFrame::GetFirst( m_pData->m_pObjectShell.get() );
+
+ if ( !pActFrame || !pActFrame->GetViewShell() )
+ // currently no frame for this document at all or View is under construction
+ return Reference < container::XIndexAccess >();
+
+ m_pData->m_contViewData = new comphelper::IndexedPropertyValuesContainer();
+
+ if ( !m_pData->m_contViewData.is() )
+ {
+ // error: no container class available!
+ return Reference < container::XIndexAccess >();
+ }
+
+ Reference < container::XIndexContainer > xCont( m_pData->m_contViewData, UNO_QUERY );
+ sal_Int32 nCount = 0;
+ Sequence < beans::PropertyValue > aSeq;
+ for ( SfxViewFrame *pFrame = SfxViewFrame::GetFirst( m_pData->m_pObjectShell.get() ); pFrame;
+ pFrame = SfxViewFrame::GetNext( *pFrame, m_pData->m_pObjectShell.get() ) )
+ {
+ bool bIsActive = ( pFrame == pActFrame );
+ pFrame->GetViewShell()->WriteUserDataSequence( aSeq );
+ xCont->insertByIndex( bIsActive ? 0 : nCount, Any(aSeq) );
+ nCount++;
+ }
+ }
+
+ return m_pData->m_contViewData;
+}
+
+void SAL_CALL SfxBaseModel::setViewData( const Reference < container::XIndexAccess >& aData )
+{
+ SfxModelGuard aGuard( *this );
+
+ m_pData->m_contViewData = aData;
+}
+
+/** calls all XEventListeners */
+void SfxBaseModel::notifyEvent( const document::EventObject& aEvent ) const
+{
+ // object already disposed?
+ if ( impl_isDisposed() )
+ return;
+
+ comphelper::OInterfaceContainerHelper2* pIC = m_pData->m_aInterfaceContainer.getContainer(
+ cppu::UnoType<document::XEventListener>::get());
+ if( !pIC )
+
+ return;
+
+ comphelper::OInterfaceIteratorHelper2 aIt( *pIC );
+ while( aIt.hasMoreElements() )
+ {
+ try
+ {
+ static_cast<document::XEventListener *>(aIt.next())->notifyEvent( aEvent );
+ }
+ catch( RuntimeException& )
+ {
+ aIt.remove();
+ }
+ }
+ // for right now, we're only doing the event that this particular performance problem needed
+ if (aEvent.EventName == "ShapeModified")
+ {
+ uno::Reference<drawing::XShape> xShape(aEvent.Source, uno::UNO_QUERY);
+ if (xShape.is())
+ {
+ auto it = m_pData->maShapeListeners.find(xShape);
+ if (it != m_pData->maShapeListeners.end())
+ for (auto const & rListenerUnoRef : it->second)
+ rListenerUnoRef->notifyShapeEvent(aEvent);
+ }
+ }
+}
+
+/** returns true if someone added a XEventListener to this XEventBroadcaster */
+bool SfxBaseModel::hasEventListeners() const
+{
+ return !impl_isDisposed()
+ && ( (nullptr != m_pData->m_aInterfaceContainer.getContainer( cppu::UnoType<document::XEventListener>::get()) )
+ || !m_pData->maShapeListeners.empty());
+}
+
+void SAL_CALL SfxBaseModel::addPrintJobListener( const Reference< view::XPrintJobListener >& xListener )
+{
+ SfxModelGuard aGuard( *this, SfxModelGuard::E_INITIALIZING );
+
+ impl_getPrintHelper();
+ Reference < view::XPrintJobBroadcaster > xPJB( m_pData->m_xPrintable, UNO_QUERY );
+ if ( xPJB.is() )
+ xPJB->addPrintJobListener( xListener );
+}
+
+void SAL_CALL SfxBaseModel::removePrintJobListener( const Reference< view::XPrintJobListener >& xListener )
+{
+ SfxModelGuard aGuard( *this );
+
+ impl_getPrintHelper();
+ Reference < view::XPrintJobBroadcaster > xPJB( m_pData->m_xPrintable, UNO_QUERY );
+ if ( xPJB.is() )
+ xPJB->removePrintJobListener( xListener );
+}
+
+sal_Int64 SAL_CALL SfxBaseModel::getSomething( const Sequence< sal_Int8 >& aIdentifier )
+{
+ SvGlobalName aName( aIdentifier );
+ if (aName == SvGlobalName( SFX_GLOBAL_CLASSID ))
+ {
+ SolarMutexGuard aGuard;
+ SfxObjectShell *const pObjectShell(GetObjectShell());
+ if (pObjectShell)
+ {
+ return comphelper::getSomething_cast(pObjectShell);
+ }
+ }
+
+ return 0;
+}
+
+
+// XDocumentSubStorageSupplier
+
+
+void SfxBaseModel::ListenForStorage_Impl( const Reference< embed::XStorage >& xStorage )
+{
+ Reference< util::XModifiable > xModifiable( xStorage, UNO_QUERY );
+ if ( xModifiable.is() )
+ {
+ if ( !m_pData->m_pStorageModifyListen.is() )
+ {
+ m_pData->m_pStorageModifyListen = new ::sfx2::DocumentStorageModifyListener( *m_pData, Application::GetSolarMutex() );
+ }
+
+ // no need to deregister the listening for old storage since it should be disposed automatically
+ xModifiable->addModifyListener( m_pData->m_pStorageModifyListen );
+ }
+}
+
+Reference< embed::XStorage > SAL_CALL SfxBaseModel::getDocumentSubStorage( const OUString& aStorageName, sal_Int32 nMode )
+{
+ SfxModelGuard aGuard( *this );
+
+ Reference< embed::XStorage > xResult;
+ if ( m_pData->m_pObjectShell.is() )
+ {
+ Reference< embed::XStorage > xStorage = m_pData->m_pObjectShell->GetStorage();
+ if ( xStorage.is() )
+ {
+ try
+ {
+ xResult = xStorage->openStorageElement( aStorageName, nMode );
+ }
+ catch ( Exception& )
+ {
+ }
+ }
+ }
+
+ return xResult;
+}
+
+Sequence< OUString > SAL_CALL SfxBaseModel::getDocumentSubStoragesNames()
+{
+ SfxModelGuard aGuard( *this );
+
+ Sequence< OUString > aResult;
+ bool bSuccess = false;
+ if ( m_pData->m_pObjectShell.is() )
+ {
+ Reference < embed::XStorage > xStorage = m_pData->m_pObjectShell->GetStorage();
+ if ( xStorage.is() )
+ {
+ const Sequence< OUString > aTemp = xStorage->getElementNames();
+ sal_Int32 nResultSize = 0;
+ for ( const auto& rName : aTemp )
+ {
+ if ( xStorage->isStorageElement( rName ) )
+ {
+ aResult.realloc( ++nResultSize );
+ aResult.getArray()[ nResultSize - 1 ] = rName;
+ }
+ }
+
+ bSuccess = true;
+ }
+ }
+
+ if ( !bSuccess )
+ throw io::IOException();
+
+ return aResult;
+}
+
+
+// XScriptProviderSupplier
+
+
+Reference< script::provider::XScriptProvider > SAL_CALL SfxBaseModel::getScriptProvider()
+{
+ SfxModelGuard aGuard( *this );
+
+ Reference< script::provider::XScriptProviderFactory > xScriptProviderFactory =
+ script::provider::theMasterScriptProviderFactory::get( ::comphelper::getProcessComponentContext() );
+
+ Reference< XScriptInvocationContext > xScriptContext( this );
+
+ Reference< script::provider::XScriptProvider > xScriptProvider(
+ xScriptProviderFactory->createScriptProvider( Any( xScriptContext ) ),
+ UNO_SET_THROW );
+
+ return xScriptProvider;
+}
+
+
+// XUIConfigurationManagerSupplier
+
+
+OUString const & SfxBaseModel::getRuntimeUID() const
+{
+ OSL_ENSURE( !m_pData->m_sRuntimeUID.isEmpty(),
+ "SfxBaseModel::getRuntimeUID - ID is empty!" );
+ return m_pData->m_sRuntimeUID;
+}
+
+bool SfxBaseModel::hasValidSignatures() const
+{
+ SolarMutexGuard aGuard;
+ if ( m_pData->m_pObjectShell.is() )
+ return ( m_pData->m_pObjectShell->ImplGetSignatureState() == SignatureState::OK );
+ return false;
+}
+
+void SfxBaseModel::getGrabBagItem(css::uno::Any& rVal) const
+{
+ if (m_pData->m_xGrabBagItem)
+ m_pData->m_xGrabBagItem->QueryValue(rVal);
+ else
+ rVal <<= uno::Sequence<beans::PropertyValue>();
+}
+
+void SfxBaseModel::setGrabBagItem(const css::uno::Any& rVal)
+{
+ if (!m_pData->m_xGrabBagItem)
+ m_pData->m_xGrabBagItem = std::make_shared<SfxGrabBagItem>();
+
+ m_pData->m_xGrabBagItem->PutValue(rVal, 0);
+}
+
+static void GetCommandFromSequence( OUString& rCommand, sal_Int32& nIndex, const Sequence< beans::PropertyValue >& rSeqPropValue )
+{
+ nIndex = -1;
+
+ auto pPropValue = std::find_if(rSeqPropValue.begin(), rSeqPropValue.end(),
+ [](const beans::PropertyValue& rPropValue) { return rPropValue.Name == "Command"; });
+ if (pPropValue != rSeqPropValue.end())
+ {
+ pPropValue->Value >>= rCommand;
+ nIndex = static_cast<sal_Int32>(std::distance(rSeqPropValue.begin(), pPropValue));
+ }
+}
+
+static void ConvertSlotsToCommands( SfxObjectShell const * pDoc, Reference< container::XIndexContainer > const & rToolbarDefinition )
+{
+ if ( !pDoc )
+ return;
+
+ SfxModule* pModule( pDoc->GetFactory().GetModule() );
+ Sequence< beans::PropertyValue > aSeqPropValue;
+
+ for ( sal_Int32 i = 0; i < rToolbarDefinition->getCount(); i++ )
+ {
+ if ( rToolbarDefinition->getByIndex( i ) >>= aSeqPropValue )
+ {
+ OUString aCommand;
+ sal_Int32 nIndex( -1 );
+ GetCommandFromSequence( aCommand, nIndex, aSeqPropValue );
+ if ( nIndex >= 0 && aCommand.startsWith( "slot:" ) )
+ {
+ const sal_uInt16 nSlot = o3tl::toInt32(aCommand.subView( 5 ));
+
+ // We have to replace the old "slot-Command" with our new ".uno:-Command"
+ const SfxSlot* pSlot = pModule->GetSlotPool()->GetSlot( nSlot );
+ if ( pSlot )
+ {
+ OUStringBuffer aStrBuf( ".uno:" );
+ aStrBuf.appendAscii( pSlot->GetUnoName() );
+
+ aCommand = aStrBuf.makeStringAndClear();
+ aSeqPropValue.getArray()[nIndex].Value <<= aCommand;
+ rToolbarDefinition->replaceByIndex( i, Any( aSeqPropValue ));
+ }
+ }
+ }
+ }
+}
+
+Reference< ui::XUIConfigurationManager > SAL_CALL SfxBaseModel::getUIConfigurationManager()
+{
+ return Reference< ui::XUIConfigurationManager >( getUIConfigurationManager2(), UNO_QUERY_THROW );
+}
+
+Reference< ui::XUIConfigurationManager2 > SfxBaseModel::getUIConfigurationManager2()
+{
+ SfxModelGuard aGuard( *this );
+
+ if ( !m_pData->m_xUIConfigurationManager.is() )
+ {
+ Reference< ui::XUIConfigurationManager2 > xNewUIConfMan =
+ ui::UIConfigurationManager::create( comphelper::getProcessComponentContext() );
+
+ Reference< embed::XStorage > xConfigStorage;
+
+ OUString aUIConfigFolderName( "Configurations2" );
+ // First try to open with READWRITE and then READ
+ xConfigStorage = getDocumentSubStorage( aUIConfigFolderName, embed::ElementModes::READWRITE );
+ if ( xConfigStorage.is() )
+ {
+ static const OUStringLiteral aMediaTypeProp( u"MediaType" );
+ OUString aMediaType;
+ Reference< beans::XPropertySet > xPropSet( xConfigStorage, UNO_QUERY );
+ Any a = xPropSet->getPropertyValue( aMediaTypeProp );
+ if ( !( a >>= aMediaType ) || aMediaType.isEmpty())
+ {
+ xPropSet->setPropertyValue( aMediaTypeProp, Any(OUString("application/vnd.sun.xml.ui.configuration")) );
+ }
+ }
+ else
+ xConfigStorage = getDocumentSubStorage( aUIConfigFolderName, embed::ElementModes::READ );
+
+ // initialize ui configuration manager with document substorage
+ xNewUIConfMan->setStorage( xConfigStorage );
+
+ // embedded objects did not support local configuration data until OOo 3.0, so there's nothing to
+ // migrate
+ if ( m_pData->m_pObjectShell->GetCreateMode() != SfxObjectCreateMode::EMBEDDED )
+ {
+ // Import old UI configuration from OOo 1.x
+
+ // Try to open with READ
+ Reference< embed::XStorage > xOOo1ConfigStorage = getDocumentSubStorage( "Configurations", embed::ElementModes::READ );
+ if ( xOOo1ConfigStorage.is() )
+ {
+ Reference< XComponentContext > xContext( ::comphelper::getProcessComponentContext() );
+ std::vector< Reference< container::XIndexContainer > > rToolbars;
+
+ bool bImported = framework::UIConfigurationImporterOOo1x::ImportCustomToolbars(
+ xNewUIConfMan, rToolbars, xContext, xOOo1ConfigStorage );
+ if ( bImported )
+ {
+ SfxObjectShell* pObjShell = SfxBaseModel::GetObjectShell();
+
+ for ( size_t i = 0; i < rToolbars.size(); i++ )
+ {
+ const OUString sId(OUString::number( i + 1 ));
+ const OUString aCustomTbxName = "private:resource/toolbar/custom_OOo1x_" + sId;
+
+ Reference< container::XIndexContainer > xToolbar = rToolbars[i];
+ ConvertSlotsToCommands( pObjShell, xToolbar );
+ if ( !xNewUIConfMan->hasSettings( aCustomTbxName ))
+ {
+ // Set UIName for the toolbar with container property
+ Reference< beans::XPropertySet > xPropSet( xToolbar, UNO_QUERY );
+ if ( xPropSet.is() )
+ {
+ try
+ {
+ xPropSet->setPropertyValue( "UIName", Any( "Toolbar " + sId ) );
+ }
+ catch ( beans::UnknownPropertyException& )
+ {
+ }
+ }
+
+ xNewUIConfMan->insertSettings( aCustomTbxName, xToolbar );
+ xNewUIConfMan->store();
+ }
+ }
+ }
+ }
+ }
+
+ m_pData->m_xUIConfigurationManager = xNewUIConfMan;
+ }
+
+ return m_pData->m_xUIConfigurationManager;
+}
+
+
+// XVisualObject
+
+
+void SAL_CALL SfxBaseModel::setVisualAreaSize( sal_Int64 nAspect, const awt::Size& aSize )
+{
+ SfxModelGuard aGuard( *this );
+
+ if ( !m_pData->m_pObjectShell.is() )
+ throw Exception("no object shell", nullptr); // TODO: error handling
+
+ SfxViewFrame* pViewFrm = SfxViewFrame::GetFirst( m_pData->m_pObjectShell.get(), false );
+ if ( pViewFrm && m_pData->m_pObjectShell->GetCreateMode() == SfxObjectCreateMode::EMBEDDED && !pViewFrm->GetFrame().IsInPlace() )
+ {
+ VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( pViewFrm->GetFrame().GetFrameInterface()->getContainerWindow() );
+ Size aWinSize = pWindow->GetSizePixel();
+ awt::Size aCurrent = getVisualAreaSize( nAspect );
+ Size aDiff( aSize.Width-aCurrent.Width, aSize.Height-aCurrent.Height );
+ aDiff = pViewFrm->GetViewShell()->GetWindow()->LogicToPixel( aDiff );
+ aWinSize.AdjustWidth(aDiff.Width() );
+ aWinSize.AdjustHeight(aDiff.Height() );
+ pWindow->SetSizePixel( aWinSize );
+ }
+ else
+ {
+ tools::Rectangle aTmpRect = m_pData->m_pObjectShell->GetVisArea( ASPECT_CONTENT );
+ aTmpRect.SetSize( Size( aSize.Width, aSize.Height ) );
+ m_pData->m_pObjectShell->SetVisArea( aTmpRect );
+ }
+}
+
+awt::Size SAL_CALL SfxBaseModel::getVisualAreaSize( sal_Int64 /*nAspect*/ )
+{
+ SfxModelGuard aGuard( *this );
+
+ if ( !m_pData->m_pObjectShell.is() )
+ throw Exception("no object shell", nullptr); // TODO: error handling
+
+ tools::Rectangle aTmpRect = m_pData->m_pObjectShell->GetVisArea( ASPECT_CONTENT );
+
+ return awt::Size( aTmpRect.GetWidth(), aTmpRect.GetHeight() );
+}
+
+
+sal_Int32 SAL_CALL SfxBaseModel::getMapUnit( sal_Int64 /*nAspect*/ )
+{
+ SfxModelGuard aGuard( *this );
+
+ if ( !m_pData->m_pObjectShell.is() )
+ throw Exception("no object shell", nullptr); // TODO: error handling
+
+ return VCLUnoHelper::VCL2UnoEmbedMapUnit( m_pData->m_pObjectShell->GetMapUnit() );
+}
+
+embed::VisualRepresentation SAL_CALL SfxBaseModel::getPreferredVisualRepresentation( ::sal_Int64 /*nAspect*/ )
+{
+ SfxModelGuard aGuard( *this );
+
+ datatransfer::DataFlavor aDataFlavor(
+ "application/x-openoffice-gdimetafile;windows_formatname=\"GDIMetaFile\"",
+ "GDIMetaFile",
+ cppu::UnoType<Sequence< sal_Int8 >>::get() );
+
+ embed::VisualRepresentation aVisualRepresentation;
+ aVisualRepresentation.Data = getTransferData( aDataFlavor );
+ aVisualRepresentation.Flavor = aDataFlavor;
+
+ return aVisualRepresentation;
+}
+
+
+// XStorageBasedDocument
+
+
+void SAL_CALL SfxBaseModel::loadFromStorage( const Reference< embed::XStorage >& xStorage,
+ const Sequence< beans::PropertyValue >& aMediaDescriptor )
+{
+ SfxModelGuard aGuard( *this, SfxModelGuard::E_INITIALIZING );
+ if ( IsInitialized() )
+ throw frame::DoubleInitializationException( OUString(), *this );
+
+ // after i36090 is fixed the pool from object shell can be used
+ // SfxAllItemSet aSet( m_pData->m_pObjectShell->GetPool() );
+ SfxAllItemSet aSet( SfxGetpApp()->GetPool() );
+
+ // the BaseURL is part of the ItemSet
+ SfxMedium* pMedium = new SfxMedium( xStorage, OUString() );
+ TransformParameters( SID_OPENDOC, aMediaDescriptor, aSet );
+ pMedium->GetItemSet()->Put( aSet );
+
+ // allow to use an interactionhandler (if there is one)
+ pMedium->UseInteractionHandler( true );
+
+ const SfxBoolItem* pTemplateItem = aSet.GetItem<SfxBoolItem>(SID_TEMPLATE, false);
+ bool bTemplate = pTemplateItem && pTemplateItem->GetValue();
+ m_pData->m_pObjectShell->SetActivateEvent_Impl( bTemplate ? SfxEventHintId::CreateDoc : SfxEventHintId::OpenDoc );
+ m_pData->m_pObjectShell->Get_Impl()->bOwnsStorage = false;
+
+ // load document
+ if ( !m_pData->m_pObjectShell->DoLoad(pMedium) )
+ {
+ ErrCode nError = m_pData->m_pObjectShell->GetErrorCode();
+ nError = nError ? nError : ERRCODE_IO_CANTREAD;
+ throw task::ErrorCodeIOException(
+ "SfxBaseModel::loadFromStorage: " + nError.toHexString(),
+ Reference< XInterface >(), sal_uInt32(nError));
+ }
+ loadCmisProperties( );
+}
+
+void SAL_CALL SfxBaseModel::storeToStorage( const Reference< embed::XStorage >& xStorage,
+ const Sequence< beans::PropertyValue >& aMediaDescriptor )
+{
+ SfxModelGuard aGuard( *this );
+
+ if ( !m_pData->m_pObjectShell.is() )
+ throw io::IOException(); // TODO:
+
+ auto xSet = std::make_shared<SfxAllItemSet>(m_pData->m_pObjectShell->GetPool());
+ TransformParameters( SID_SAVEASDOC, aMediaDescriptor, *xSet );
+
+ // TODO/LATER: maybe a special URL "private:storage" should be used
+ const SfxStringItem* pItem = xSet->GetItem<SfxStringItem>(SID_FILTER_NAME, false);
+ sal_Int32 nVersion = SOFFICE_FILEFORMAT_CURRENT;
+ if( pItem )
+ {
+ std::shared_ptr<const SfxFilter> pFilter = SfxGetpApp()->GetFilterMatcher().GetFilter4FilterName( pItem->GetValue() );
+ if ( pFilter && pFilter->UsesStorage() )
+ nVersion = pFilter->GetVersion();
+ }
+
+ bool bSuccess = false;
+ if ( xStorage == m_pData->m_pObjectShell->GetStorage() )
+ {
+ // storing to the own storage
+ bSuccess = m_pData->m_pObjectShell->DoSave();
+ }
+ else
+ {
+ // TODO/LATER: if the provided storage has some data inside the storing might fail, probably the storage must be truncated
+ // TODO/LATER: is it possible to have a template here?
+ m_pData->m_pObjectShell->SetupStorage( xStorage, nVersion, false );
+
+ // BaseURL is part of the ItemSet
+ SfxMedium aMedium( xStorage, OUString(), xSet );
+ aMedium.CanDisposeStorage_Impl( false );
+ if ( aMedium.GetFilter() )
+ {
+ // storing without a valid filter will often crash
+ bSuccess = m_pData->m_pObjectShell->DoSaveObjectAs( aMedium, true );
+ m_pData->m_pObjectShell->DoSaveCompleted();
+ }
+ }
+
+ ErrCode nError = m_pData->m_pObjectShell->GetErrorCode();
+ m_pData->m_pObjectShell->ResetError();
+
+ // the warnings are currently not transported
+ if ( !bSuccess )
+ {
+ nError = nError ? nError : ERRCODE_IO_GENERAL;
+ throw task::ErrorCodeIOException(
+ "SfxBaseModel::storeToStorage: " + nError.toHexString(),
+ Reference< XInterface >(), sal_uInt32(nError));
+ }
+}
+
+void SAL_CALL SfxBaseModel::switchToStorage( const Reference< embed::XStorage >& xStorage )
+{
+ SfxModelGuard aGuard( *this );
+
+ if ( !m_pData->m_pObjectShell.is() )
+ throw io::IOException(); // TODO:
+
+ // the persistence should be switched only if the storage is different
+ if ( xStorage != m_pData->m_pObjectShell->GetStorage() )
+ {
+ if ( !m_pData->m_pObjectShell->SwitchPersistence( xStorage ) )
+ {
+ ErrCode nError = m_pData->m_pObjectShell->GetErrorCode();
+ nError = nError ? nError : ERRCODE_IO_GENERAL;
+ throw task::ErrorCodeIOException(
+ "SfxBaseModel::switchToStorage: " + nError.toHexString(),
+ Reference< XInterface >(), sal_uInt32(nError));
+ }
+ else
+ {
+ // UICfgMgr has a reference to the old storage, update it
+ getUIConfigurationManager2()->setStorage( xStorage );
+ }
+ }
+ m_pData->m_pObjectShell->Get_Impl()->bOwnsStorage = false;
+}
+
+Reference< embed::XStorage > SAL_CALL SfxBaseModel::getDocumentStorage()
+{
+ SfxModelGuard aGuard( *this );
+
+ if ( !m_pData->m_pObjectShell.is() )
+ throw io::IOException(); // TODO
+
+ return m_pData->m_pObjectShell->GetStorage();
+}
+
+void SAL_CALL SfxBaseModel::addStorageChangeListener(
+ const Reference< document::XStorageChangeListener >& xListener )
+{
+ SfxModelGuard aGuard( *this, SfxModelGuard::E_INITIALIZING );
+
+ m_pData->m_aInterfaceContainer.addInterface(
+ cppu::UnoType<document::XStorageChangeListener>::get(), xListener );
+}
+
+void SAL_CALL SfxBaseModel::removeStorageChangeListener(
+ const Reference< document::XStorageChangeListener >& xListener )
+{
+ SfxModelGuard aGuard( *this );
+
+ m_pData->m_aInterfaceContainer.removeInterface(
+ cppu::UnoType<document::XStorageChangeListener>::get(), xListener );
+}
+
+void SfxBaseModel::impl_getPrintHelper()
+{
+ if ( m_pData->m_xPrintable.is() )
+ return;
+ m_pData->m_xPrintable = new SfxPrintHelper();
+ Reference < lang::XInitialization > xInit( m_pData->m_xPrintable, UNO_QUERY );
+ xInit->initialize( { Any(Reference < frame::XModel > (this)) } );
+ Reference < view::XPrintJobBroadcaster > xBrd( m_pData->m_xPrintable, UNO_QUERY );
+ xBrd->addPrintJobListener( new SfxPrintHelperListener_Impl( m_pData.get() ) );
+}
+
+
+// css.frame.XModule
+ void SAL_CALL SfxBaseModel::setIdentifier(const OUString& Identifier)
+{
+ SfxModelGuard aGuard( *this );
+ m_pData->m_sModuleIdentifier = Identifier;
+}
+
+
+// css.frame.XModule
+ OUString SAL_CALL SfxBaseModel::getIdentifier()
+{
+ SfxModelGuard aGuard( *this );
+ if (!m_pData->m_sModuleIdentifier.isEmpty())
+ return m_pData->m_sModuleIdentifier;
+ if (m_pData->m_pObjectShell.is())
+ return m_pData->m_pObjectShell->GetFactory().GetDocumentServiceName();
+ return OUString();
+}
+
+
+Reference< frame::XTitle > SfxBaseModel::impl_getTitleHelper ()
+{
+ SfxModelGuard aGuard( *this );
+
+ if ( ! m_pData->m_xTitleHelper.is ())
+ {
+ Reference< XComponentContext > xContext = ::comphelper::getProcessComponentContext();
+ Reference< frame::XUntitledNumbers > xDesktop( frame::Desktop::create(xContext), UNO_QUERY_THROW);
+
+ m_pData->m_xTitleHelper = new ::framework::TitleHelper(xContext, Reference< frame::XModel >(this), xDesktop);
+ }
+
+ return m_pData->m_xTitleHelper;
+}
+
+
+Reference< frame::XUntitledNumbers > SfxBaseModel::impl_getUntitledHelper ()
+{
+ SfxModelGuard aGuard( *this );
+
+ if ( ! m_pData->m_xNumberedControllers.is ())
+ {
+ rtl::Reference<::comphelper::NumberedCollection> pHelper = new ::comphelper::NumberedCollection();
+ m_pData->m_xNumberedControllers = pHelper;
+ pHelper->setOwner (Reference< frame::XModel >(this));
+ pHelper->setUntitledPrefix (" : ");
+ }
+
+ return m_pData->m_xNumberedControllers;
+}
+
+
+// css.frame.XTitle
+OUString SAL_CALL SfxBaseModel::getTitle()
+{
+ // SYNCHRONIZED ->
+ SfxModelGuard aGuard( *this );
+
+ OUString aResult = impl_getTitleHelper()->getTitle ();
+ if ( !m_pData->m_bExternalTitle && m_pData->m_pObjectShell )
+ {
+ SfxMedium* pMedium = m_pData->m_pObjectShell->GetMedium();
+ if ( pMedium )
+ {
+ try {
+ ::ucbhelper::Content aContent( pMedium->GetName(),
+ utl::UCBContentHelper::getDefaultCommandEnvironment(),
+ comphelper::getProcessComponentContext() );
+ const Reference < beans::XPropertySetInfo > xProps
+ = aContent.getProperties();
+ if ( xProps.is() )
+ {
+ static const OUStringLiteral aServerTitle( u"TitleOnServer" );
+ if ( xProps->hasPropertyByName( aServerTitle ) )
+ {
+ Any aAny = aContent.getPropertyValue( aServerTitle );
+ aAny >>= aResult;
+ }
+ }
+ }
+ catch (const ucb::ContentCreationException &)
+ {
+ }
+ catch (const ucb::CommandAbortedException &)
+ {
+ }
+ const SfxBoolItem* pRepairedDocItem = SfxItemSet::GetItem<SfxBoolItem>(pMedium->GetItemSet(), SID_REPAIRPACKAGE, false);
+ if ( pRepairedDocItem && pRepairedDocItem->GetValue() )
+ aResult += SfxResId(STR_REPAIREDDOCUMENT);
+ }
+
+ if ( m_pData->m_pObjectShell->IsReadOnlyUI() || (pMedium && pMedium->IsReadOnly()) )
+ aResult += SfxResId(STR_READONLY);
+ else if ( m_pData->m_pObjectShell->IsDocShared() )
+ aResult += SfxResId(STR_SHARED);
+
+ if ( m_pData->m_pObjectShell->GetDocumentSignatureState() == SignatureState::OK )
+ aResult += SfxResId(RID_XMLSEC_DOCUMENTSIGNED);
+ }
+
+ return aResult;
+}
+
+
+// css.frame.XTitle
+void SAL_CALL SfxBaseModel::setTitle( const OUString& sTitle )
+{
+ // SYNCHRONIZED ->
+ SfxModelGuard aGuard( *this );
+
+ impl_getTitleHelper()->setTitle (sTitle);
+ m_pData->m_bExternalTitle = true;
+}
+
+
+// css.frame.XTitleChangeBroadcaster
+void SAL_CALL SfxBaseModel::addTitleChangeListener( const Reference< frame::XTitleChangeListener >& xListener )
+{
+ // SYNCHRONIZED ->
+ SfxModelGuard aGuard( *this, SfxModelGuard::E_INITIALIZING );
+
+ Reference< frame::XTitleChangeBroadcaster > xBroadcaster(impl_getTitleHelper(), UNO_QUERY);
+ if (xBroadcaster.is ())
+ xBroadcaster->addTitleChangeListener (xListener);
+}
+
+
+// css.frame.XTitleChangeBroadcaster
+void SAL_CALL SfxBaseModel::removeTitleChangeListener( const Reference< frame::XTitleChangeListener >& xListener )
+{
+ // SYNCHRONIZED ->
+ SfxModelGuard aGuard( *this );
+
+ Reference< frame::XTitleChangeBroadcaster > xBroadcaster(impl_getTitleHelper(), UNO_QUERY);
+ if (xBroadcaster.is ())
+ xBroadcaster->removeTitleChangeListener (xListener);
+}
+
+
+// css.frame.XUntitledNumbers
+::sal_Int32 SAL_CALL SfxBaseModel::leaseNumber( const Reference< XInterface >& xComponent )
+{
+ SfxModelGuard aGuard( *this );
+
+ return impl_getUntitledHelper ()->leaseNumber (xComponent);
+}
+
+
+// css.frame.XUntitledNumbers
+void SAL_CALL SfxBaseModel::releaseNumber( ::sal_Int32 nNumber )
+{
+ SfxModelGuard aGuard( *this );
+ impl_getUntitledHelper ()->releaseNumber (nNumber);
+}
+
+
+// css.frame.XUntitledNumbers
+void SAL_CALL SfxBaseModel::releaseNumberForComponent( const Reference< XInterface >& xComponent )
+{
+ SfxModelGuard aGuard( *this );
+ impl_getUntitledHelper ()->releaseNumberForComponent (xComponent);
+}
+
+
+// css.frame.XUntitledNumbers
+OUString SAL_CALL SfxBaseModel::getUntitledPrefix()
+{
+ SfxModelGuard aGuard( *this );
+ return impl_getUntitledHelper ()->getUntitledPrefix ();
+}
+
+
+// frame::XModel2
+Reference< container::XEnumeration > SAL_CALL SfxBaseModel::getControllers()
+{
+ SfxModelGuard aGuard( *this );
+
+ sal_Int32 c = m_pData->m_seqControllers.size();
+ Sequence< Any > lEnum(c);
+ std::transform(m_pData->m_seqControllers.begin(), m_pData->m_seqControllers.end(),
+ lEnum.getArray(), [](const auto& x) { return css::uno::Any(x); });
+
+ return new ::comphelper::OAnyEnumeration(lEnum);
+}
+
+
+// frame::XModel2
+Sequence< OUString > SAL_CALL SfxBaseModel::getAvailableViewControllerNames()
+{
+ SfxModelGuard aGuard( *this );
+
+ const SfxObjectFactory& rDocumentFactory = GetObjectShell()->GetFactory();
+ const sal_Int16 nViewFactoryCount = rDocumentFactory.GetViewFactoryCount();
+
+ Sequence< OUString > aViewNames( nViewFactoryCount );
+ auto aViewNamesRange = asNonConstRange(aViewNames);
+ for ( sal_Int16 nViewNo = 0; nViewNo < nViewFactoryCount; ++nViewNo )
+ aViewNamesRange[nViewNo] = rDocumentFactory.GetViewFactory( nViewNo ).GetAPIViewName();
+ return aViewNames;
+}
+
+
+// frame::XModel2
+Reference< frame::XController2 > SAL_CALL SfxBaseModel::createDefaultViewController( const Reference< frame::XFrame >& i_rFrame )
+{
+ SfxModelGuard aGuard( *this );
+
+ const SfxObjectFactory& rDocumentFactory = GetObjectShell()->GetFactory();
+ const OUString sDefaultViewName = rDocumentFactory.GetViewFactory().GetAPIViewName();
+
+ aGuard.clear();
+
+ return createViewController( sDefaultViewName, Sequence< PropertyValue >(), i_rFrame );
+}
+
+
+namespace sfx::intern {
+
+ /** a class which, in its dtor, cleans up various objects (well, at the moment only the frame) collected during
+ the creation of a document view, unless the creation was successful.
+ */
+ class ViewCreationGuard
+ {
+ public:
+ ViewCreationGuard()
+ :m_bSuccess( false )
+ {
+ }
+
+ ~ViewCreationGuard()
+ {
+ if ( !m_bSuccess && m_aWeakFrame && !m_aWeakFrame->GetCurrentDocument() )
+ {
+ m_aWeakFrame->SetFrameInterface_Impl( nullptr );
+ m_aWeakFrame->DoClose();
+ }
+ }
+
+ void takeFrameOwnership( SfxFrame* i_pFrame )
+ {
+ OSL_PRECOND( !m_aWeakFrame, "ViewCreationGuard::takeFrameOwnership: already have a frame!" );
+ OSL_PRECOND( i_pFrame != nullptr, "ViewCreationGuard::takeFrameOwnership: invalid frame!" );
+ m_aWeakFrame = i_pFrame;
+ }
+
+ void releaseAll()
+ {
+ m_bSuccess = true;
+ }
+
+ private:
+ bool m_bSuccess;
+ SfxFrameWeakRef m_aWeakFrame;
+ };
+}
+
+
+SfxViewFrame* SfxBaseModel::FindOrCreateViewFrame_Impl( const Reference< XFrame >& i_rFrame, ::sfx::intern::ViewCreationGuard& i_rGuard ) const
+{
+ SfxViewFrame* pViewFrame = nullptr;
+ for ( pViewFrame = SfxViewFrame::GetFirst( GetObjectShell(), false );
+ pViewFrame;
+ pViewFrame= SfxViewFrame::GetNext( *pViewFrame, GetObjectShell(), false )
+ )
+ {
+ if ( pViewFrame->GetFrame().GetFrameInterface() == i_rFrame )
+ break;
+ }
+ if ( !pViewFrame )
+ {
+ #if OSL_DEBUG_LEVEL > 0
+ for ( SfxFrame* pCheckFrame = SfxFrame::GetFirst();
+ pCheckFrame;
+ pCheckFrame = SfxFrame::GetNext( *pCheckFrame )
+ )
+ {
+ if ( pCheckFrame->GetFrameInterface() == i_rFrame )
+ {
+ if ( ( pCheckFrame->GetCurrentViewFrame() != nullptr )
+ || ( pCheckFrame->GetCurrentDocument() != nullptr )
+ )
+ // Note that it is perfectly legitimate that during loading into an XFrame which already contains
+ // a document, there exist two SfxFrame instances bound to this XFrame - the old one, which will be
+ // destroyed later, and the new one, which we're going to create
+ continue;
+
+ OSL_FAIL( "SfxBaseModel::FindOrCreateViewFrame_Impl: there already is an SfxFrame for the given XFrame, but no view in it!" );
+ // nowadays, we're the only instance allowed to create an SfxFrame for an XFrame, so this case here should not happen
+ break;
+ }
+ }
+ #endif
+
+ SfxFrame* pTargetFrame = SfxFrame::Create( i_rFrame );
+ ENSURE_OR_THROW( pTargetFrame, "could not create an SfxFrame" );
+ i_rGuard.takeFrameOwnership( pTargetFrame );
+
+ // prepare it
+ pTargetFrame->PrepareForDoc_Impl( *GetObjectShell() );
+
+ // create view frame
+ pViewFrame = new SfxViewFrame( *pTargetFrame, GetObjectShell() );
+ }
+ return pViewFrame;
+}
+
+
+// frame::XModel2
+Reference< frame::XController2 > SAL_CALL SfxBaseModel::createViewController(
+ const OUString& i_rViewName, const Sequence< PropertyValue >& i_rArguments, const Reference< XFrame >& i_rFrame )
+{
+ SfxModelGuard aGuard( *this );
+
+ if ( !i_rFrame.is() )
+ throw lang::IllegalArgumentException( OUString(), *this, 3 );
+
+ // find the proper SFX view factory
+ SfxViewFactory* pViewFactory = GetObjectShell()->GetFactory().GetViewFactoryByViewName( i_rViewName );
+ if ( !pViewFactory )
+ throw IllegalArgumentException( OUString(), *this, 1 );
+
+ // determine previous shell (used in some special cases)
+ Reference< XController > xPreviousController( i_rFrame->getController() );
+ const Reference< XModel > xMe( this );
+ if ( ( xPreviousController.is() )
+ && ( xMe != xPreviousController->getModel() )
+ )
+ {
+ xPreviousController.clear();
+ }
+ SfxViewShell* pOldViewShell = SfxViewShell::Get( xPreviousController );
+ OSL_ENSURE( !xPreviousController.is() || ( pOldViewShell != nullptr ),
+ "SfxBaseModel::createViewController: invalid old controller!" );
+
+ // a guard which will clean up in case of failure
+ ::sfx::intern::ViewCreationGuard aViewCreationGuard;
+
+ // determine the ViewFrame belonging to the given XFrame
+ SfxViewFrame* pViewFrame = FindOrCreateViewFrame_Impl( i_rFrame, aViewCreationGuard );
+ SAL_WARN_IF( !pViewFrame , "sfx.doc", "SfxBaseModel::createViewController: no frame?" );
+
+ // delegate to SFX' view factory
+ pViewFrame->GetBindings().ENTERREGISTRATIONS();
+ SfxViewShell* pViewShell = pViewFactory->CreateInstance( pViewFrame, pOldViewShell );
+ pViewFrame->GetBindings().LEAVEREGISTRATIONS();
+ ENSURE_OR_THROW( pViewShell, "invalid view shell provided by factory" );
+
+ // by setting the ViewShell it is prevented that disposing the Controller will destroy this ViewFrame also
+ pViewFrame->GetDispatcher()->SetDisableFlags( SfxDisableFlags::NONE );
+ pViewFrame->SetViewShell_Impl( pViewShell );
+
+ // remember ViewID
+ pViewFrame->SetCurViewId_Impl( pViewFactory->GetOrdinal() );
+
+ // ensure a default controller, if the view shell did not provide an own implementation
+ if ( !pViewShell->GetController().is() )
+ pViewShell->SetController( new SfxBaseController( pViewShell ) );
+
+ // pass the creation arguments to the controller
+ SfxBaseController* pBaseController = pViewShell->GetBaseController_Impl();
+ ENSURE_OR_THROW( pBaseController, "invalid controller implementation!" );
+ pBaseController->SetCreationArguments_Impl( i_rArguments );
+
+ // some initial view settings, coming from our most recent attachResource call
+ ::comphelper::NamedValueCollection aDocumentLoadArgs( getArgs2( { "ViewOnly", "PluginMode" } ) );
+ if ( aDocumentLoadArgs.getOrDefault( "ViewOnly", false ) )
+ pViewFrame->GetFrame().SetMenuBarOn_Impl( false );
+
+ const sal_Int16 nPluginMode = aDocumentLoadArgs.getOrDefault( "PluginMode", sal_Int16( 0 ) );
+ if ( nPluginMode == 1 )
+ {
+ pViewFrame->ForceOuterResize_Impl();
+ pViewFrame->GetBindings().HidePopups();
+
+ SfxFrame& rFrame = pViewFrame->GetFrame();
+ // MBA: layoutmanager of inplace frame starts locked and invisible
+ rFrame.GetWorkWindow_Impl()->MakeVisible_Impl( false );
+ rFrame.GetWorkWindow_Impl()->Lock_Impl( true );
+
+ rFrame.GetWindow().SetBorderStyle( WindowBorderStyle::NOBORDER );
+ pViewFrame->GetWindow().SetBorderStyle( WindowBorderStyle::NOBORDER );
+ }
+
+ // tell the guard we were successful
+ aViewCreationGuard.releaseAll();
+
+ // outta here
+ return pBaseController;
+}
+
+
+// RDF DocumentMetadataAccess
+
+// rdf::XRepositorySupplier:
+Reference< rdf::XRepository > SAL_CALL
+SfxBaseModel::getRDFRepository()
+{
+ SfxModelGuard aGuard( *this );
+
+ const Reference<rdf::XDocumentMetadataAccess> xDMA(m_pData->GetDMA());
+ if (!xDMA.is()) {
+ throw RuntimeException( "model has no document metadata", *this );
+ }
+
+ return xDMA->getRDFRepository();
+}
+
+// rdf::XNode:
+OUString SAL_CALL
+SfxBaseModel::getStringValue()
+{
+ SfxModelGuard aGuard( *this );
+
+ const Reference<rdf::XDocumentMetadataAccess> xDMA(m_pData->GetDMA());
+ if (!xDMA.is()) {
+ throw RuntimeException( "model has no document metadata", *this );
+ }
+
+ return xDMA->getStringValue();
+}
+
+// rdf::XURI:
+OUString SAL_CALL
+SfxBaseModel::getNamespace()
+{
+ SfxModelGuard aGuard( *this );
+
+ const Reference<rdf::XDocumentMetadataAccess> xDMA(m_pData->GetDMA());
+ if (!xDMA.is()) {
+ throw RuntimeException( "model has no document metadata", *this );
+ }
+
+ return xDMA->getNamespace();
+}
+
+OUString SAL_CALL
+SfxBaseModel::getLocalName()
+{
+ SfxModelGuard aGuard( *this );
+
+ const Reference<rdf::XDocumentMetadataAccess> xDMA(m_pData->GetDMA());
+ if (!xDMA.is()) {
+ throw RuntimeException( "model has no document metadata", *this );
+ }
+
+ return xDMA->getLocalName();
+}
+
+// rdf::XDocumentMetadataAccess:
+Reference< rdf::XMetadatable > SAL_CALL
+SfxBaseModel::getElementByMetadataReference(
+ const beans::StringPair & i_rReference)
+{
+ SfxModelGuard aGuard( *this );
+
+ const Reference<rdf::XDocumentMetadataAccess> xDMA(m_pData->GetDMA());
+ if (!xDMA.is()) {
+ throw RuntimeException( "model has no document metadata", *this );
+ }
+
+ return xDMA->getElementByMetadataReference(i_rReference);
+}
+
+Reference< rdf::XMetadatable > SAL_CALL
+SfxBaseModel::getElementByURI(const Reference< rdf::XURI > & i_xURI)
+{
+ SfxModelGuard aGuard( *this );
+
+ const Reference<rdf::XDocumentMetadataAccess> xDMA(m_pData->GetDMA());
+ if (!xDMA.is()) {
+ throw RuntimeException( "model has no document metadata", *this );
+ }
+
+ return xDMA->getElementByURI(i_xURI);
+}
+
+Sequence< Reference< rdf::XURI > > SAL_CALL
+SfxBaseModel::getMetadataGraphsWithType(
+ const Reference<rdf::XURI> & i_xType)
+{
+ SfxModelGuard aGuard( *this );
+
+ const Reference<rdf::XDocumentMetadataAccess> xDMA(m_pData->GetDMA());
+ if (!xDMA.is()) {
+ throw RuntimeException( "model has no document metadata", *this );
+ }
+
+ return xDMA->getMetadataGraphsWithType(i_xType);
+}
+
+Reference<rdf::XURI> SAL_CALL
+SfxBaseModel::addMetadataFile(const OUString & i_rFileName,
+ const Sequence < Reference< rdf::XURI > > & i_rTypes)
+{
+ SfxModelGuard aGuard( *this );
+
+ const Reference<rdf::XDocumentMetadataAccess> xDMA(m_pData->GetDMA());
+ if (!xDMA.is()) {
+ throw RuntimeException( "model has no document metadata", *this );
+ }
+
+ return xDMA->addMetadataFile(i_rFileName, i_rTypes);
+}
+
+Reference<rdf::XURI> SAL_CALL
+SfxBaseModel::importMetadataFile(::sal_Int16 i_Format,
+ const Reference< io::XInputStream > & i_xInStream,
+ const OUString & i_rFileName,
+ const Reference< rdf::XURI > & i_xBaseURI,
+ const Sequence < Reference< rdf::XURI > > & i_rTypes)
+{
+ SfxModelGuard aGuard( *this );
+
+ const Reference<rdf::XDocumentMetadataAccess> xDMA(m_pData->GetDMA());
+ if (!xDMA.is()) {
+ throw RuntimeException( "model has no document metadata", *this );
+ }
+
+ return xDMA->importMetadataFile(i_Format,
+ i_xInStream, i_rFileName, i_xBaseURI, i_rTypes);
+}
+
+void SAL_CALL
+SfxBaseModel::removeMetadataFile(
+ const Reference< rdf::XURI > & i_xGraphName)
+{
+ SfxModelGuard aGuard( *this );
+
+ const Reference<rdf::XDocumentMetadataAccess> xDMA(m_pData->GetDMA());
+ if (!xDMA.is()) {
+ throw RuntimeException( "model has no document metadata", *this );
+ }
+
+ return xDMA->removeMetadataFile(i_xGraphName);
+}
+
+void SAL_CALL
+SfxBaseModel::addContentOrStylesFile(const OUString & i_rFileName)
+{
+ SfxModelGuard aGuard( *this );
+
+ const Reference<rdf::XDocumentMetadataAccess> xDMA(m_pData->GetDMA());
+ if (!xDMA.is()) {
+ throw RuntimeException( "model has no document metadata", *this );
+ }
+
+ return xDMA->addContentOrStylesFile(i_rFileName);
+}
+
+void SAL_CALL
+SfxBaseModel::removeContentOrStylesFile(const OUString & i_rFileName)
+{
+ SfxModelGuard aGuard( *this );
+
+ const Reference<rdf::XDocumentMetadataAccess> xDMA(m_pData->GetDMA());
+ if (!xDMA.is()) {
+ throw RuntimeException( "model has no document metadata", *this );
+ }
+
+ return xDMA->removeContentOrStylesFile(i_rFileName);
+}
+
+void SAL_CALL
+SfxBaseModel::loadMetadataFromStorage(
+ Reference< embed::XStorage > const & i_xStorage,
+ Reference<rdf::XURI> const & i_xBaseURI,
+ Reference<task::XInteractionHandler> const & i_xHandler)
+{
+ SfxModelGuard aGuard( *this );
+
+ const Reference<rdf::XDocumentMetadataAccess> xDMA(
+ m_pData->CreateDMAUninitialized());
+ if (!xDMA.is()) {
+ throw RuntimeException( "model has no document metadata", *this );
+ }
+
+ try {
+ xDMA->loadMetadataFromStorage(i_xStorage, i_xBaseURI, i_xHandler);
+ } catch (lang::IllegalArgumentException &) {
+ throw; // not initialized
+ } catch (Exception &) {
+ // UGLY: if it's a RuntimeException, we can't be sure DMA is initialized
+ m_pData->m_xDocumentMetadata = xDMA;
+ throw;
+ }
+ m_pData->m_xDocumentMetadata = xDMA;
+
+}
+
+void SAL_CALL
+SfxBaseModel::storeMetadataToStorage(
+ Reference< embed::XStorage > const & i_xStorage)
+{
+ SfxModelGuard aGuard( *this );
+
+ const Reference<rdf::XDocumentMetadataAccess> xDMA(m_pData->GetDMA());
+ if (!xDMA.is()) {
+ throw RuntimeException( "model has no document metadata", *this );
+ }
+
+ return xDMA->storeMetadataToStorage(i_xStorage);
+}
+
+void SAL_CALL
+SfxBaseModel::loadMetadataFromMedium(
+ const Sequence< beans::PropertyValue > & i_rMedium)
+{
+ SfxModelGuard aGuard( *this );
+
+ const Reference<rdf::XDocumentMetadataAccess> xDMA(
+ m_pData->CreateDMAUninitialized());
+ if (!xDMA.is()) {
+ throw RuntimeException( "model has no document metadata", *this );
+ }
+
+ try {
+ xDMA->loadMetadataFromMedium(i_rMedium);
+ } catch (lang::IllegalArgumentException &) {
+ throw; // not initialized
+ } catch (Exception &) {
+ // UGLY: if it's a RuntimeException, we can't be sure DMA is initialized
+ m_pData->m_xDocumentMetadata = xDMA;
+ throw;
+ }
+ m_pData->m_xDocumentMetadata = xDMA;
+}
+
+void SAL_CALL
+SfxBaseModel::storeMetadataToMedium(
+ const Sequence< beans::PropertyValue > & i_rMedium)
+{
+ SfxModelGuard aGuard( *this );
+
+ const Reference<rdf::XDocumentMetadataAccess> xDMA(m_pData->GetDMA());
+ if (!xDMA.is()) {
+ throw RuntimeException( "model has no document metadata", *this );
+ }
+
+ return xDMA->storeMetadataToMedium(i_rMedium);
+}
+
+
+// = SfxModelSubComponent
+
+
+SfxModelSubComponent::~SfxModelSubComponent()
+{
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/doc/sfxmodelfactory.cxx b/sfx2/source/doc/sfxmodelfactory.cxx
new file mode 100644
index 000000000..a14ff68b1
--- /dev/null
+++ b/sfx2/source/doc/sfxmodelfactory.cxx
@@ -0,0 +1,110 @@
+/* -*- 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/sfxmodelfactory.hxx>
+
+#include <com/sun/star/beans/NamedValue.hpp>
+#include <com/sun/star/lang/XInitialization.hpp>
+
+#include <comphelper/namedvaluecollection.hxx>
+
+#include <osl/diagnose.h>
+
+#include <algorithm>
+
+
+namespace sfx2
+{
+
+
+ using ::com::sun::star::uno::Reference;
+ using ::com::sun::star::uno::XInterface;
+ using ::com::sun::star::uno::UNO_QUERY;
+ using ::com::sun::star::uno::Any;
+ using ::com::sun::star::uno::Sequence;
+ using ::com::sun::star::beans::NamedValue;
+ using ::com::sun::star::beans::PropertyValue;
+ using ::com::sun::star::lang::XInitialization;
+
+
+
+ namespace
+ {
+ struct IsSpecialArgument
+ {
+ static bool isSpecialArgumentName( std::u16string_view _rValueName )
+ {
+ return _rValueName == u"EmbeddedObject" || _rValueName == u"EmbeddedScriptSupport" || _rValueName == u"DocumentRecoverySupport";
+ }
+
+ bool operator()( const Any& _rArgument ) const
+ {
+ NamedValue aNamedValue;
+ if ( ( _rArgument >>= aNamedValue ) && isSpecialArgumentName( aNamedValue.Name ) )
+ return true;
+ PropertyValue aPropertyValue;
+ return ( _rArgument >>= aPropertyValue ) && isSpecialArgumentName( aPropertyValue.Name );
+ }
+ };
+ }
+
+
+ css::uno::Reference<css::uno::XInterface> createSfxModelInstance(
+ const css::uno::Sequence<css::uno::Any> & _rArguments,
+ std::function<css::uno::Reference<css::uno::XInterface>(SfxModelFlags)> creationFunc)
+ {
+ ::comphelper::NamedValueCollection aArgs( _rArguments );
+ const bool bEmbeddedObject = aArgs.getOrDefault( "EmbeddedObject", false );
+ const bool bScriptSupport = aArgs.getOrDefault( "EmbeddedScriptSupport", true );
+ const bool bDocRecoverySupport = aArgs.getOrDefault( "DocumentRecoverySupport", true );
+
+ SfxModelFlags nCreationFlags =
+ ( bEmbeddedObject ? SfxModelFlags::EMBEDDED_OBJECT : SfxModelFlags::NONE )
+ | ( bScriptSupport ? SfxModelFlags::NONE : SfxModelFlags::DISABLE_EMBEDDED_SCRIPTS )
+ | ( bDocRecoverySupport ? SfxModelFlags::NONE : SfxModelFlags::DISABLE_DOCUMENT_RECOVERY );
+
+ Reference< XInterface > xInstance( creationFunc(nCreationFlags ) );
+
+ // to mimic the behaviour of the default factory's createInstanceWithArguments, we initialize
+ // the object with the given arguments, stripped by the three special ones
+ Sequence< Any > aStrippedArguments( _rArguments.getLength() );
+ Any* pStrippedArgs = aStrippedArguments.getArray();
+ Any* pStrippedArgsEnd = ::std::remove_copy_if(
+ _rArguments.begin(),
+ _rArguments.end(),
+ pStrippedArgs,
+ IsSpecialArgument()
+ );
+ aStrippedArguments.realloc( pStrippedArgsEnd - pStrippedArgs );
+
+ if ( aStrippedArguments.hasElements() )
+ {
+ Reference< XInitialization > xModelInit( xInstance, UNO_QUERY );
+ OSL_ENSURE( xModelInit.is(), "SfxModelFactory::createInstanceWithArguments: no XInitialization!" );
+ if ( xModelInit.is() )
+ xModelInit->initialize( aStrippedArguments );
+ }
+
+ return xInstance;
+ }
+
+
+} // namespace sfx2
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/doc/signaturestate.cxx b/sfx2/source/doc/signaturestate.cxx
new file mode 100644
index 000000000..d511fa31a
--- /dev/null
+++ b/sfx2/source/doc/signaturestate.cxx
@@ -0,0 +1,59 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * 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/signaturestate.hxx>
+
+#include <com/sun/star/security/CertificateValidity.hpp>
+#include <com/sun/star/security/DocumentSignatureInformation.hpp>
+
+using namespace css;
+
+namespace DocumentSignatures
+{
+SignatureState
+getSignatureState(const uno::Sequence<security::DocumentSignatureInformation>& aSigInfo)
+{
+ bool bCertValid = true;
+ SignatureState nResult = SignatureState::NOSIGNATURES;
+ bool bCompleteSignature = true;
+ if (!aSigInfo.hasElements())
+ return nResult;
+
+ nResult = SignatureState::OK;
+ for (const auto& rInfo : aSigInfo)
+ {
+ if (bCertValid)
+ {
+ sal_Int32 nCertStat = rInfo.CertificateStatus;
+ bCertValid = nCertStat == security::CertificateValidity::VALID;
+ }
+
+ if (!rInfo.SignatureIsValid)
+ {
+ nResult = SignatureState::BROKEN;
+ break;
+ }
+ bCompleteSignature &= !rInfo.PartialDocumentSignature;
+ }
+
+ if (nResult == SignatureState::OK && !bCertValid && !bCompleteSignature)
+ nResult = SignatureState::NOTVALIDATED_PARTIAL_OK;
+ else if (nResult == SignatureState::OK && !bCertValid)
+ nResult = SignatureState::NOTVALIDATED;
+ else if (nResult == SignatureState::OK && bCertValid && !bCompleteSignature)
+ nResult = SignatureState::PARTIAL_OK;
+
+ // this code must not check whether the document is modified
+ // it should only check the provided info
+
+ return nResult;
+}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/sfx2/source/doc/syspath.cxx b/sfx2/source/doc/syspath.cxx
new file mode 100644
index 000000000..fb949def0
--- /dev/null
+++ b/sfx2/source/doc/syspath.cxx
@@ -0,0 +1,37 @@
+/* -*- 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 "syspath.hxx"
+#include "syspathw32.hxx"
+
+namespace SystemPath
+{
+bool GetUserTemplateLocation(sal_Unicode* pFolder, int nSize)
+{
+#ifdef _WIN32
+ return ::GetUserTemplateLocation(pFolder, nSize);
+#else
+ (void)pFolder;
+ (void)nSize;
+ return false;
+#endif
+}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/doc/syspath.hxx b/sfx2/source/doc/syspath.hxx
new file mode 100644
index 000000000..9c135993c
--- /dev/null
+++ b/sfx2/source/doc/syspath.hxx
@@ -0,0 +1,32 @@
+/* -*- 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_DOC_SYSPATH_HXX
+#define INCLUDED_SFX2_SOURCE_DOC_SYSPATH_HXX
+
+#include <sal/types.h>
+
+namespace SystemPath
+{
+bool GetUserTemplateLocation(sal_Unicode*, int nSize);
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/doc/syspathw32.cxx b/sfx2/source/doc/syspathw32.cxx
new file mode 100644
index 000000000..f60f45982
--- /dev/null
+++ b/sfx2/source/doc/syspathw32.cxx
@@ -0,0 +1,69 @@
+/* -*- 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 <sal/types.h>
+#include <o3tl/char16_t2wchar_t.hxx>
+
+#ifdef _WIN32
+
+#undef WB_LEFT
+#undef WB_RIGHT
+
+#include <shlobj.h>
+
+#include "syspathw32.hxx"
+
+static bool SHGetSpecialFolderW32( int nFolderID, WCHAR* pszFolder, int nSize )
+{
+ LPITEMIDLIST pidl;
+ HRESULT hHdl = SHGetSpecialFolderLocation( nullptr, nFolderID, &pidl );
+
+ if( hHdl == NOERROR )
+ {
+ WCHAR *lpFolder = static_cast< WCHAR* >( HeapAlloc( GetProcessHeap(), 0, 16000 ));
+
+ SHGetPathFromIDListW( pidl, lpFolder );
+ wcsncpy( pszFolder, lpFolder, nSize );
+
+ HeapFree( GetProcessHeap(), 0, lpFolder );
+ IMalloc *pMalloc;
+ if( NOERROR == SHGetMalloc(&pMalloc) )
+ {
+ pMalloc->Free( pidl );
+ pMalloc->Release();
+ }
+ }
+ return true;
+}
+
+#endif
+
+bool GetUserTemplateLocation(sal_Unicode* pFolder, int nSize)
+{
+#ifdef _WIN32
+ return SHGetSpecialFolderW32( CSIDL_TEMPLATES, o3tl::toW(pFolder), nSize );
+#else
+ (void)pFolder;
+ (void)nSize;
+ return false;
+#endif
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/doc/syspathw32.hxx b/sfx2/source/doc/syspathw32.hxx
new file mode 100644
index 000000000..7a06950c5
--- /dev/null
+++ b/sfx2/source/doc/syspathw32.hxx
@@ -0,0 +1,33 @@
+/* -*- 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_DOC_SYSPATHW32_HXX
+#define INCLUDED_SFX2_SOURCE_DOC_SYSPATHW32_HXX
+
+#include <sal/config.h>
+
+#include <sal/types.h>
+
+#if defined _WIN32
+bool GetUserTemplateLocation(sal_Unicode*, int nSize);
+#endif
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/doc/templatedlg.cxx b/sfx2/source/doc/templatedlg.cxx
new file mode 100644
index 000000000..42e570e75
--- /dev/null
+++ b/sfx2/source/doc/templatedlg.cxx
@@ -0,0 +1,1396 @@
+/* -*- 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/templatedlg.hxx>
+
+#include <sfx2/inputdlg.hxx>
+#include <sfx2/module.hxx>
+
+#include <comphelper/processfactory.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <comphelper/string.hxx>
+#include <comphelper/storagehelper.hxx>
+#include <sfx2/app.hxx>
+#include <sfx2/docfac.hxx>
+#include <sfx2/docfilt.hxx>
+#include <sfx2/fcontnr.hxx>
+#include <sfx2/filedlghelper.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/sfxresid.hxx>
+#include <sfx2/templatedlglocalview.hxx>
+#include <templatecontaineritem.hxx>
+#include <templateviewitem.hxx>
+#include <sfx2/thumbnailviewitem.hxx>
+#include <sot/storage.hxx>
+#include <tools/urlobj.hxx>
+#include <unotools/moduleoptions.hxx>
+#include <unotools/pathoptions.hxx>
+#include <unotools/viewoptions.hxx>
+#include <vcl/event.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+
+#include <com/sun/star/beans/NamedValue.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/document/MacroExecMode.hpp>
+#include <com/sun/star/document/UpdateDocMode.hpp>
+#include <com/sun/star/embed/XStorage.hpp>
+#include <com/sun/star/embed/ElementModes.hpp>
+#include <com/sun/star/frame/Desktop.hpp>
+#include <com/sun/star/frame/XStorable.hpp>
+#include <com/sun/star/ui/dialogs/ExecutableDialogResults.hpp>
+#include <com/sun/star/ui/dialogs/TemplateDescription.hpp>
+#include <com/sun/star/ui/dialogs/XFolderPicker2.hpp>
+#include <com/sun/star/task/InteractionHandler.hpp>
+#include <comphelper/dispatchcommand.hxx>
+
+#include <sfx2/strings.hrc>
+#include <bitmaps.hlst>
+
+constexpr OUStringLiteral TM_SETTING_MANAGER = u"TemplateManager";
+constexpr OUStringLiteral TM_SETTING_LASTFOLDER = u"LastFolder";
+constexpr OUStringLiteral TM_SETTING_LASTAPPLICATION = u"LastApplication";
+constexpr OUStringLiteral TM_SETTING_VIEWMODE = u"ViewMode";
+
+#define MNI_ACTION_NEW_FOLDER "new"
+#define MNI_ACTION_RENAME_FOLDER "rename"
+#define MNI_ACTION_DELETE_FOLDER "delete"
+#define MNI_ACTION_DEFAULT "default"
+#define MNI_ACTION_DEFAULT_WRITER "default_writer"
+#define MNI_ACTION_DEFAULT_CALC "default_calc"
+#define MNI_ACTION_DEFAULT_IMPRESS "default_impress"
+#define MNI_ACTION_DEFAULT_DRAW "default_draw"
+#define MNI_ACTION_IMPORT "import_template"
+#define MNI_ACTION_EXTENSIONS "extensions"
+#define MNI_ALL_APPLICATIONS 0
+#define MNI_WRITER 1
+#define MNI_CALC 2
+#define MNI_IMPRESS 3
+#define MNI_DRAW 4
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::embed;
+using namespace ::com::sun::star::frame;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::ui::dialogs;
+using namespace ::com::sun::star::document;
+
+static bool lcl_getServiceName (const OUString &rFileURL, OUString &rName );
+
+static std::vector<OUString> lcl_getAllFactoryURLs ();
+
+namespace {
+
+class SearchView_Keyword
+{
+public:
+
+ SearchView_Keyword (const OUString &rKeyword, FILTER_APPLICATION App)
+ : maKeyword(rKeyword.toAsciiLowerCase()), meApp(App)
+ {}
+
+ bool operator() (const TemplateItemProperties &rItem)
+ {
+ bool bRet = true;
+
+ INetURLObject aUrl(rItem.aPath);
+ OUString aExt = aUrl.getExtension();
+
+ if (meApp == FILTER_APPLICATION::WRITER)
+ {
+ bRet = aExt == "ott" || aExt == "stw" || aExt == "oth" || aExt == "dot" || aExt == "dotx";
+ }
+ else if (meApp == FILTER_APPLICATION::CALC)
+ {
+ bRet = aExt == "ots" || aExt == "stc" || aExt == "xlt" || aExt == "xltm" || aExt == "xltx";
+ }
+ else if (meApp == FILTER_APPLICATION::IMPRESS)
+ {
+ bRet = aExt == "otp" || aExt == "sti" || aExt == "pot" || aExt == "potm" || aExt == "potx";
+ }
+ else if (meApp == FILTER_APPLICATION::DRAW)
+ {
+ bRet = aExt == "otg" || aExt == "std";
+ }
+
+ return bRet && MatchSubstring(rItem.aName);
+ }
+
+ bool MatchSubstring( OUString const & sItemName )
+ {
+ if(maKeyword.isEmpty())
+ return false;
+ return sItemName.toAsciiLowerCase().indexOf(maKeyword) >= 0;
+ }
+
+private:
+
+ OUString maKeyword;
+ FILTER_APPLICATION meApp;
+};
+
+}
+
+/***
+ *
+ * Order items in ascending order (useful for the selection sets and move/copy operations since the associated ids
+ * change when processed by the SfxDocumentTemplates class so we want to process to ones with higher id first)
+ *
+ ***/
+
+static bool cmpSelectionItems (const ThumbnailViewItem *pItem1, const ThumbnailViewItem *pItem2)
+{
+ return pItem1->mnId > pItem2->mnId;
+}
+
+SfxTemplateManagerDlg::SfxTemplateManagerDlg(weld::Window *pParent)
+ : GenericDialogController(pParent, "sfx/ui/templatedlg.ui", "TemplateDialog")
+ , maSelTemplates(cmpSelectionItems)
+ , mxDesktop(Desktop::create(comphelper::getProcessComponentContext()))
+ , m_aUpdateDataTimer( "SfxTemplateManagerDlg UpdateDataTimer" )
+ , mxSearchFilter(m_xBuilder->weld_entry("search_filter"))
+ , mxCBApp(m_xBuilder->weld_combo_box("filter_application"))
+ , mxCBFolder(m_xBuilder->weld_combo_box("filter_folder"))
+ , mxOKButton(m_xBuilder->weld_button("ok"))
+ , mxCBXHideDlg(m_xBuilder->weld_check_button("hidedialogcb"))
+ , mxActionBar(m_xBuilder->weld_menu_button("action_menu"))
+ , mxLocalView(new TemplateDlgLocalView(m_xBuilder->weld_scrolled_window("scrolllocal", true),
+ m_xBuilder->weld_menu("contextmenu"),
+ m_xBuilder->weld_tree_view("tree_list")))
+ , mxLocalViewWeld(new weld::CustomWeld(*m_xBuilder, "template_view", *mxLocalView))
+ , mxListViewButton(m_xBuilder->weld_toggle_button("list_view_btn"))
+ , mxThumbnailViewButton(m_xBuilder->weld_toggle_button("thumbnail_view_btn"))
+ , mViewMode(TemplateViewMode::eThumbnailView)
+{
+ // Create popup menus
+ mxActionBar->append_item(MNI_ACTION_NEW_FOLDER, SfxResId(STR_CATEGORY_NEW), BMP_ACTION_NEW_CATEGORY);
+ mxActionBar->append_item(MNI_ACTION_RENAME_FOLDER, SfxResId(STR_CATEGORY_RENAME), BMP_ACTION_RENAME);
+ mxActionBar->append_item(MNI_ACTION_DELETE_FOLDER, SfxResId(STR_CATEGORY_DELETE), BMP_ACTION_DELETE_CATEGORY);
+ mxActionBar->append_separator("separator");
+ mxActionBar->append_item(MNI_ACTION_DEFAULT, SfxResId(STR_ACTION_RESET_ALL_DEFAULT_TEMPLATES));
+ mxActionBar->append_item(MNI_ACTION_DEFAULT_WRITER, SfxResId(STR_ACTION_RESET_WRITER_TEMPLATE), BMP_ACTION_DEFAULT_WRITER);
+ mxActionBar->append_item(MNI_ACTION_DEFAULT_CALC, SfxResId(STR_ACTION_RESET_CALC_TEMPLATE), BMP_ACTION_DEFAULT_CALC);
+ mxActionBar->append_item(MNI_ACTION_DEFAULT_IMPRESS, SfxResId(STR_ACTION_RESET_IMPRESS_TEMPLATE), BMP_ACTION_DEFAULT_IMPRESS);
+ mxActionBar->append_item(MNI_ACTION_DEFAULT_DRAW, SfxResId(STR_ACTION_RESET_DRAW_TEMPLATE), BMP_ACTION_DEFAULT_DRAW);
+ mxActionBar->append_separator("separator2");
+ mxActionBar->append_item(MNI_ACTION_IMPORT, SfxResId(STR_ACTION_IMPORT), BMP_ACTION_IMPORT);
+ mxActionBar->append_item(MNI_ACTION_EXTENSIONS, SfxResId(STR_ACTION_EXTENSIONS), BMP_ACTION_EXTENSIONS);
+
+ mxActionBar->connect_selected(LINK(this,SfxTemplateManagerDlg,MenuSelectHdl));
+
+ mxLocalView->setItemMaxTextLength(TEMPLATE_ITEM_MAX_TEXT_LENGTH);
+ mxLocalView->setItemDimensions(TEMPLATE_ITEM_MAX_WIDTH,TEMPLATE_ITEM_THUMBNAIL_MAX_HEIGHT,
+ TEMPLATE_ITEM_MAX_HEIGHT-TEMPLATE_ITEM_THUMBNAIL_MAX_HEIGHT,
+ TEMPLATE_ITEM_PADDING);
+
+ mxLocalView->setItemStateHdl(LINK(this,SfxTemplateManagerDlg,TVItemStateHdl));
+ mxLocalView->setCreateContextMenuHdl(LINK(this,SfxTemplateManagerDlg, CreateContextMenuHdl));
+ mxLocalView->setOpenRegionHdl(LINK(this,SfxTemplateManagerDlg, OpenRegionHdl));
+ mxLocalView->setOpenTemplateHdl(LINK(this,SfxTemplateManagerDlg, OpenTemplateHdl));
+ mxLocalView->setEditTemplateHdl(LINK(this,SfxTemplateManagerDlg, EditTemplateHdl));
+ mxLocalView->setDeleteTemplateHdl(LINK(this,SfxTemplateManagerDlg, DeleteTemplateHdl));
+ mxLocalView->setDefaultTemplateHdl(LINK(this,SfxTemplateManagerDlg, DefaultTemplateHdl));
+ mxLocalView->setMoveTemplateHdl(LINK(this,SfxTemplateManagerDlg, MoveTemplateHdl));
+ mxLocalView->setExportTemplateHdl(LINK(this,SfxTemplateManagerDlg, ExportTemplateHdl));
+
+ mxLocalView->ShowTooltips(true);
+
+ // Set width and height of the templates thumbnail viewer to acommodate 3 rows and 4 columns of items
+ mxLocalViewWeld->set_size_request(TEMPLATE_ITEM_MAX_WIDTH * 5, TEMPLATE_ITEM_MAX_HEIGHT_SUB * 3);
+
+ mxOKButton->connect_clicked(LINK(this, SfxTemplateManagerDlg, OkClickHdl));
+ // FIXME: rather than disabling make dispatchCommand(".uno:AdditionsDialog") work in start center
+ if ( !SfxModule::GetActiveModule() )
+ mxActionBar->set_item_sensitive(MNI_ACTION_EXTENSIONS, false);
+ else
+ mxActionBar->set_item_sensitive(MNI_ACTION_EXTENSIONS, true);
+ mxListViewButton->connect_toggled(LINK(this, SfxTemplateManagerDlg, ListViewHdl));
+ mxThumbnailViewButton->connect_toggled(LINK(this, SfxTemplateManagerDlg, ThumbnailViewHdl));
+
+ mxSearchFilter->connect_changed(LINK(this, SfxTemplateManagerDlg, SearchUpdateHdl));
+ mxSearchFilter->connect_focus_in(LINK( this, SfxTemplateManagerDlg, GetFocusHdl ));
+ mxSearchFilter->connect_focus_out(LINK( this, SfxTemplateManagerDlg, LoseFocusHdl ));
+ mxSearchFilter->connect_key_press(LINK( this, SfxTemplateManagerDlg, KeyInputHdl));
+
+ mxActionBar->show();
+
+ mxLocalView->Populate();
+ mxLocalView->filterItems(ViewFilter_Application(FILTER_APPLICATION::NONE));
+
+ mxCBApp->set_active(0);
+ fillFolderComboBox();
+
+ mxActionBar->set_item_visible(MNI_ACTION_EXTENSIONS, true);
+ mxActionBar->set_item_visible(MNI_ACTION_IMPORT, true);
+ mxActionBar->set_item_visible(MNI_ACTION_NEW_FOLDER, true);
+
+ mxOKButton->set_label(SfxResId(STR_OPEN));
+
+ mxCBApp->connect_changed(LINK(this, SfxTemplateManagerDlg, SelectApplicationHdl));
+ mxCBFolder->connect_changed(LINK(this, SfxTemplateManagerDlg, SelectRegionHdl));
+
+ mxLocalView->Show();
+
+ m_aUpdateDataTimer.SetInvokeHandler(LINK(this, SfxTemplateManagerDlg, ImplUpdateDataHdl));
+ m_aUpdateDataTimer.SetTimeout(EDIT_UPDATEDATA_TIMEOUT);
+
+ mxLocalView->connect_focus_rect(LINK(this, SfxTemplateManagerDlg, FocusRectLocalHdl));
+ bMakeSelItemVisible = false;
+}
+
+SfxTemplateManagerDlg::~SfxTemplateManagerDlg()
+{
+ writeSettings();
+
+ // Ignore view events since we are cleaning the object
+ mxLocalView->setItemStateHdl(Link<const ThumbnailViewItem*,void>());
+ mxLocalView->setOpenRegionHdl(Link<void*,void>());
+ mxLocalView->setOpenTemplateHdl(Link<ThumbnailViewItem*, void>());
+}
+
+short SfxTemplateManagerDlg::run()
+{
+ //use application specific settings if there's no previous setting
+ getApplicationSpecificSettings();
+ readSettings();
+ updateMenuItems();
+
+ return weld::GenericDialogController::run();
+}
+
+IMPL_LINK(SfxTemplateManagerDlg, KeyInputHdl, const KeyEvent&, rKeyEvent, bool)
+{
+ if (mxSearchFilter != nullptr && !mxSearchFilter->get_text().isEmpty())
+ {
+ vcl::KeyCode aKeyCode = rKeyEvent.GetKeyCode();
+ sal_uInt16 nKeyCode = aKeyCode.GetCode();
+
+ if ( nKeyCode == KEY_ESCAPE )
+ {
+ mxSearchFilter->set_text("");
+ SearchUpdateHdl(*mxSearchFilter);
+ return true;
+ }
+ }
+ return false;
+}
+
+void SfxTemplateManagerDlg::setDocumentModel(const uno::Reference<frame::XModel> &rModel)
+{
+ m_xModel = rModel;
+}
+
+void SfxTemplateManagerDlg::setTemplateViewMode(TemplateViewMode eViewMode)
+{
+ if(eViewMode == TemplateViewMode::eThumbnailView && mViewMode != TemplateViewMode::eThumbnailView)
+ {
+ mxThumbnailViewButton->set_state(TRISTATE_TRUE);
+ mxListViewButton->set_state(TRISTATE_FALSE);
+ mxLocalView->ThumbnailView::GrabFocus();
+ mViewMode = eViewMode;
+ mxLocalView->setTemplateViewMode(eViewMode);
+ mxLocalView->Show();
+ }
+ if(eViewMode == TemplateViewMode::eListView && mViewMode != TemplateViewMode::eListView)
+ {
+ mxListViewButton->set_state(TRISTATE_TRUE);
+ mxThumbnailViewButton->set_state(TRISTATE_FALSE);
+ mxLocalView->ListView::grab_focus();
+ mViewMode = eViewMode;
+ mxLocalView->setTemplateViewMode(eViewMode);
+ mxLocalView->Show();
+ }
+}
+
+TemplateViewMode SfxTemplateManagerDlg::getTemplateViewMode() const
+{
+ return mViewMode;
+}
+
+
+FILTER_APPLICATION SfxTemplateManagerDlg::getCurrentApplicationFilter() const
+{
+ const sal_Int16 nCurAppId = mxCBApp->get_active();
+
+ if (nCurAppId == MNI_WRITER)
+ return FILTER_APPLICATION::WRITER;
+ else if (nCurAppId == MNI_IMPRESS)
+ return FILTER_APPLICATION::IMPRESS;
+ else if (nCurAppId == MNI_CALC)
+ return FILTER_APPLICATION::CALC;
+ else if (nCurAppId == MNI_DRAW)
+ return FILTER_APPLICATION::DRAW;
+
+ return FILTER_APPLICATION::NONE;
+}
+
+void SfxTemplateManagerDlg::fillFolderComboBox()
+{
+ std::vector<OUString> aFolderNames = mxLocalView->getFolderNames();
+
+ for (size_t i = 0, n = aFolderNames.size(); i < n; ++i)
+ mxCBFolder->append_text(aFolderNames[i]);
+ mxCBFolder->set_active(0);
+ mxActionBar->set_item_sensitive(MNI_ACTION_RENAME_FOLDER, false);
+ mxActionBar->set_item_sensitive(MNI_ACTION_DELETE_FOLDER, false);
+}
+
+void SfxTemplateManagerDlg::getApplicationSpecificSettings()
+{
+ if ( ! m_xModel.is() )
+ {
+ mxCBApp->set_active(0);
+ mxCBFolder->set_active(0);
+ mxActionBar->set_item_sensitive(MNI_ACTION_RENAME_FOLDER, false);
+ mxActionBar->set_item_sensitive(MNI_ACTION_DELETE_FOLDER, false);
+ mxLocalView->filterItems(ViewFilter_Application(getCurrentApplicationFilter()));
+ mxLocalView->showAllTemplates();
+ return;
+ }
+
+ SvtModuleOptions::EFactory eFactory = SvtModuleOptions::ClassifyFactoryByModel(m_xModel);
+
+ switch(eFactory)
+ {
+ case SvtModuleOptions::EFactory::WRITER:
+ case SvtModuleOptions::EFactory::WRITERWEB:
+ case SvtModuleOptions::EFactory::WRITERGLOBAL:
+ mxCBApp->set_active(MNI_WRITER);
+ break;
+ case SvtModuleOptions::EFactory::CALC:
+ mxCBApp->set_active(MNI_CALC);
+ break;
+ case SvtModuleOptions::EFactory::IMPRESS:
+ mxCBApp->set_active(MNI_IMPRESS);
+ break;
+ case SvtModuleOptions::EFactory::DRAW:
+ mxCBApp->set_active(MNI_DRAW);
+ break;
+ default:
+ mxCBApp->set_active(0);
+ break;
+ }
+
+ mxLocalView->filterItems(ViewFilter_Application(getCurrentApplicationFilter()));
+ mxCBFolder->set_active(0);
+ mxActionBar->set_item_sensitive(MNI_ACTION_RENAME_FOLDER, false);
+ mxActionBar->set_item_sensitive(MNI_ACTION_DELETE_FOLDER, false);
+ mxLocalView->showAllTemplates();
+}
+
+void SfxTemplateManagerDlg::readSettings ()
+{
+ OUString aLastFolder;
+ SvtViewOptions aViewSettings( EViewType::Dialog, TM_SETTING_MANAGER );
+ sal_Int16 nViewMode = -1;
+
+ if ( aViewSettings.Exists() )
+ {
+ sal_uInt16 nTmp = 0;
+ aViewSettings.GetUserItem(TM_SETTING_LASTFOLDER) >>= aLastFolder;
+ aViewSettings.GetUserItem(TM_SETTING_LASTAPPLICATION) >>= nTmp;
+ aViewSettings.GetUserItem(TM_SETTING_VIEWMODE) >>= nViewMode;
+
+ //open last remembered application only when application model is not set
+ if(!m_xModel.is())
+ {
+ switch (nTmp)
+ {
+ case MNI_WRITER:
+ mxCBApp->set_active(MNI_WRITER);
+ break;
+ case MNI_CALC:
+ mxCBApp->set_active(MNI_CALC);
+ break;
+ case MNI_IMPRESS:
+ mxCBApp->set_active(MNI_IMPRESS);
+ break;
+ case MNI_DRAW:
+ mxCBApp->set_active(MNI_DRAW);
+ break;
+ default:
+ mxCBApp->set_active(0);
+ break;
+ }
+ }
+ }
+
+ mxLocalView->filterItems(ViewFilter_Application(getCurrentApplicationFilter()));
+
+ if (aLastFolder.isEmpty())
+ {
+ //show all categories
+ mxCBFolder->set_active(0);
+ mxActionBar->set_item_sensitive(MNI_ACTION_RENAME_FOLDER, false);
+ mxActionBar->set_item_sensitive(MNI_ACTION_DELETE_FOLDER, false);
+ mxLocalView->showAllTemplates();
+ }
+ else
+ {
+ mxCBFolder->set_active_text(aLastFolder);
+ mxLocalView->showRegion(aLastFolder);
+ bool bIsBuiltInRegion = mxLocalView->IsBuiltInRegion(aLastFolder);
+ mxActionBar->set_item_sensitive(MNI_ACTION_RENAME_FOLDER, !bIsBuiltInRegion);
+ mxActionBar->set_item_sensitive(MNI_ACTION_DELETE_FOLDER, !bIsBuiltInRegion);
+ }
+
+ if(nViewMode == static_cast<sal_Int16>(TemplateViewMode::eListView) ||
+ nViewMode == static_cast<sal_Int16>(TemplateViewMode::eThumbnailView))
+ {
+ TemplateViewMode eViewMode = static_cast<TemplateViewMode>(nViewMode);
+ setTemplateViewMode(eViewMode);
+ }
+ else
+ {
+ //Default ViewMode
+ setTemplateViewMode(TemplateViewMode::eThumbnailView);
+ }
+}
+
+void SfxTemplateManagerDlg::writeSettings ()
+{
+ OUString aLastFolder;
+
+ if (mxLocalView->getCurRegionId())
+ aLastFolder = mxLocalView->getRegionName(mxLocalView->getCurRegionId()-1);
+
+ // last folder
+ Sequence< NamedValue > aSettings
+ {
+ { TM_SETTING_LASTFOLDER, css::uno::Any(aLastFolder) },
+ { TM_SETTING_LASTAPPLICATION, css::uno::Any(sal_uInt16(mxCBApp->get_active())) },
+ { TM_SETTING_VIEWMODE, css::uno::Any(static_cast<sal_Int16>(getTemplateViewMode()))}
+ };
+
+ // write
+ SvtViewOptions aViewSettings(EViewType::Dialog, TM_SETTING_MANAGER);
+ aViewSettings.SetUserData(aSettings);
+}
+
+IMPL_LINK_NOARG(SfxTemplateManagerDlg, SelectApplicationHdl, weld::ComboBox&, void)
+{
+ mxLocalView->filterItems(ViewFilter_Application(getCurrentApplicationFilter()));
+ SelectRegionHdl(*mxCBFolder);
+ updateMenuItems();
+}
+
+IMPL_LINK_NOARG(SfxTemplateManagerDlg, SelectRegionHdl, weld::ComboBox&, void)
+{
+ const OUString sSelectedRegion = mxCBFolder->get_active_text();
+
+ if(mxCBFolder->get_active() == 0)
+ {
+ mxActionBar->set_item_sensitive(MNI_ACTION_RENAME_FOLDER, false);
+ mxActionBar->set_item_sensitive(MNI_ACTION_DELETE_FOLDER, false);
+ }
+ else
+ {
+ bool bIsBuiltInRegion = mxLocalView->IsBuiltInRegion(sSelectedRegion);
+ mxActionBar->set_item_sensitive(MNI_ACTION_RENAME_FOLDER, !bIsBuiltInRegion);
+ mxActionBar->set_item_sensitive(MNI_ACTION_DELETE_FOLDER, !bIsBuiltInRegion);
+ }
+ SearchUpdate();
+}
+
+IMPL_LINK(SfxTemplateManagerDlg, TVItemStateHdl, const ThumbnailViewItem*, pItem, void)
+{
+ const TemplateViewItem *pViewItem = dynamic_cast<const TemplateViewItem*>(pItem);
+
+ if (pViewItem)
+ OnTemplateState(pItem);
+}
+
+IMPL_LINK(SfxTemplateManagerDlg, MenuSelectHdl, const OString&, rIdent, void)
+{
+ if (rIdent == MNI_ACTION_NEW_FOLDER)
+ OnCategoryNew();
+ else if (rIdent == MNI_ACTION_RENAME_FOLDER)
+ OnCategoryRename();
+ else if (rIdent == MNI_ACTION_DELETE_FOLDER)
+ OnCategoryDelete();
+ else if (rIdent == MNI_ACTION_DEFAULT)
+ {
+ DefaultTemplateMenuSelectHdl(MNI_ACTION_DEFAULT_WRITER);
+ DefaultTemplateMenuSelectHdl(MNI_ACTION_DEFAULT_CALC);
+ DefaultTemplateMenuSelectHdl(MNI_ACTION_DEFAULT_IMPRESS);
+ DefaultTemplateMenuSelectHdl(MNI_ACTION_DEFAULT_DRAW);
+ }
+ else if(rIdent == MNI_ACTION_DEFAULT_WRITER || rIdent == MNI_ACTION_DEFAULT_CALC ||
+ rIdent == MNI_ACTION_DEFAULT_IMPRESS || rIdent == MNI_ACTION_DEFAULT_DRAW )
+ DefaultTemplateMenuSelectHdl(rIdent);
+ else if(rIdent == MNI_ACTION_IMPORT)
+ ImportActionHdl();
+ else if(rIdent == MNI_ACTION_EXTENSIONS)
+ ExtensionsActionHdl();
+}
+
+void SfxTemplateManagerDlg::DefaultTemplateMenuSelectHdl(std::string_view rIdent)
+{
+ SvtModuleOptions aModOpt;
+ OUString aFactoryURL;
+ if (rIdent == MNI_ACTION_DEFAULT_WRITER)
+ aFactoryURL = aModOpt.GetFactoryEmptyDocumentURL( SvtModuleOptions::EFactory::WRITER);
+ else if (rIdent == MNI_ACTION_DEFAULT_CALC)
+ aFactoryURL = aModOpt.GetFactoryEmptyDocumentURL( SvtModuleOptions::EFactory::CALC);
+ else if (rIdent == MNI_ACTION_DEFAULT_IMPRESS)
+ aFactoryURL = aModOpt.GetFactoryEmptyDocumentURL( SvtModuleOptions::EFactory::IMPRESS);
+ else if (rIdent == MNI_ACTION_DEFAULT_DRAW)
+ aFactoryURL = aModOpt.GetFactoryEmptyDocumentURL( SvtModuleOptions::EFactory::DRAW);
+ else
+ return;
+
+ OUString aServiceName = SfxObjectShell::GetServiceNameFromFactory(aFactoryURL);
+ OUString sPrevDefault = SfxObjectFactory::GetStandardTemplate( aServiceName );
+ if(!sPrevDefault.isEmpty())
+ {
+ mxLocalView->RemoveDefaultTemplateIcon(sPrevDefault);
+ }
+
+ SfxObjectFactory::SetStandardTemplate( aServiceName, OUString() );
+ mxLocalView->refreshDefaultColumn();
+ updateMenuItems();
+}
+
+IMPL_LINK_NOARG(SfxTemplateManagerDlg, OkClickHdl, weld::Button&, void)
+{
+ OnTemplateOpen();
+ m_xDialog->response(RET_OK);
+}
+
+IMPL_LINK_NOARG(SfxTemplateManagerDlg, MoveTemplateHdl, void*, void)
+{
+ // modal dialog to select templates category
+ SfxTemplateCategoryDialog aDlg(m_xDialog.get());
+ aDlg.SetCategoryLBEntries(mxLocalView->getFolderNames());
+
+ size_t nItemId = 0;
+
+ if (aDlg.run() != RET_OK)
+ return;
+
+ const OUString& sCategory = aDlg.GetSelectedCategory();
+ bool bIsNewCategory = aDlg.IsNewCategoryCreated();
+ if(bIsNewCategory)
+ {
+ if (!sCategory.isEmpty())
+ {
+ nItemId = mxLocalView->createRegion(sCategory);
+ if(nItemId)
+ mxCBFolder->append_text(sCategory);
+ }
+ }
+ else
+ nItemId = mxLocalView->getRegionId(sCategory);
+
+ if(nItemId)
+ {
+ localMoveTo(nItemId);
+ }
+
+ mxLocalView->reload();
+ SearchUpdate();
+}
+IMPL_LINK_NOARG(SfxTemplateManagerDlg, ExportTemplateHdl, void*, void)
+{
+ OnTemplateExport();
+}
+
+void SfxTemplateManagerDlg::ImportActionHdl()
+{
+ if(mxCBFolder->get_active() == 0)
+ {
+ //Modal Dialog to select Category
+ SfxTemplateCategoryDialog aDlg(m_xDialog.get());
+ aDlg.SetCategoryLBEntries(mxLocalView->getFolderNames());
+
+ if (aDlg.run() == RET_OK)
+ {
+ const OUString& sCategory = aDlg.GetSelectedCategory();
+ bool bIsNewCategory = aDlg.IsNewCategoryCreated();
+ if(bIsNewCategory)
+ {
+ if(mxLocalView->createRegion(sCategory))
+ {
+ mxCBFolder->append_text(sCategory);
+ OnTemplateImportCategory(sCategory);
+ }
+ else
+ {
+ OUString aMsg( SfxResId(STR_CREATE_ERROR) );
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(m_xDialog.get(),
+ VclMessageType::Warning, VclButtonsType::Ok,
+ aMsg.replaceFirst("$1", sCategory)));
+ xBox->run();
+ return;
+ }
+ }
+ else
+ OnTemplateImportCategory(sCategory);
+ }
+ }
+ else
+ {
+ const auto sCategory = mxCBFolder->get_active_text();
+ OnTemplateImportCategory(sCategory);
+ }
+ mxLocalView->reload();
+ SearchUpdate();
+}
+
+void SfxTemplateManagerDlg::ExtensionsActionHdl()
+{
+ uno::Sequence<beans::PropertyValue> aArgs{ comphelper::makePropertyValue(
+ "AdditionsTag", OUString("Templates")) };
+ comphelper::dispatchCommand(".uno:AdditionsDialog", aArgs);
+}
+
+IMPL_LINK_NOARG(SfxTemplateManagerDlg, OpenRegionHdl, void*, void)
+{
+ maSelTemplates.clear();
+ mxOKButton->set_sensitive(false);
+ mxActionBar->show();
+}
+
+IMPL_LINK(SfxTemplateManagerDlg, CreateContextMenuHdl, ThumbnailViewItem*, pItem, void)
+{
+ const TemplateViewItem *pViewItem = dynamic_cast<TemplateViewItem*>(pItem);
+ bool bIsDefault = false;
+ bool bIsInternal = false;
+ std::vector<const TemplateViewItem*> aSelTemplates;
+ for(const auto& aSelTmpl : maSelTemplates)
+ {
+ const TemplateViewItem *aItem = dynamic_cast<const TemplateViewItem*>(aSelTmpl);
+ aSelTemplates.push_back(aItem);
+ }
+
+ for(const auto& aSelTmpl : aSelTemplates)
+ {
+ if(aSelTmpl->IsDefaultTemplate())
+ bIsDefault = true;
+ if(TemplateLocalView::IsInternalTemplate(aSelTmpl->getPath()))
+ {
+ bIsInternal = true;
+ if(bIsDefault)
+ break;
+ }
+ }
+
+ if (!pViewItem)
+ return;
+
+ bool bIsSingleSel = maSelTemplates.size() == 1;
+ OUString aDefaultImg;
+ INetURLObject aUrl(pViewItem->getPath());
+ if (ViewFilter_Application::isFilteredExtension(FILTER_APPLICATION::WRITER, aUrl.getExtension()))
+ aDefaultImg = BMP_ACTION_DEFAULT_WRITER;
+ else if (ViewFilter_Application::isFilteredExtension(FILTER_APPLICATION::CALC, aUrl.getExtension()))
+ aDefaultImg = BMP_ACTION_DEFAULT_CALC;
+ else if (ViewFilter_Application::isFilteredExtension(FILTER_APPLICATION::IMPRESS, aUrl.getExtension()))
+ aDefaultImg = BMP_ACTION_DEFAULT_IMPRESS;
+ else if (ViewFilter_Application::isFilteredExtension(FILTER_APPLICATION::DRAW, aUrl.getExtension()))
+ aDefaultImg = BMP_ACTION_DEFAULT_DRAW;
+ mxLocalView->createContextMenu(bIsDefault, bIsInternal, bIsSingleSel, aDefaultImg);
+}
+
+IMPL_LINK(SfxTemplateManagerDlg, 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 )),
+ comphelper::makePropertyValue("ReadOnly", true)
+ };
+
+ TemplateViewItem *pTemplateItem = static_cast<TemplateViewItem*>(pItem);
+
+ try
+ {
+ mxDesktop->loadComponentFromURL(pTemplateItem->getPath(),"_default", 0, aArgs );
+ }
+ catch( const uno::Exception& )
+ {
+ }
+
+ m_xDialog->response(RET_OK);
+}
+
+IMPL_LINK(SfxTemplateManagerDlg, 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)
+ };
+
+ uno::Reference< XStorable > xStorable;
+ TemplateViewItem *pViewItem = static_cast<TemplateViewItem*>(pItem);
+
+ try
+ {
+ xStorable.set( mxDesktop->loadComponentFromURL(pViewItem->getPath(),"_default", 0, aArgs ),
+ uno::UNO_QUERY );
+ }
+ catch( const uno::Exception& )
+ {
+ }
+
+ m_xDialog->response(RET_OK);
+}
+
+IMPL_LINK_NOARG(SfxTemplateManagerDlg, DeleteTemplateHdl, void*, void)
+{
+ std::set<const ThumbnailViewItem*,selection_cmp_fn> aSelTemplates = maSelTemplates;
+ OUString aDeletedTemplate;
+
+ for (auto const& pItem : aSelTemplates)
+ {
+ const TemplateViewItem *pViewItem = static_cast<const TemplateViewItem*>(pItem);
+ sal_uInt16 nRegionItemId = mxLocalView->getRegionId(pViewItem->mnRegionId);
+
+ if (!mxLocalView->removeTemplate(pViewItem->mnDocId + 1, nRegionItemId))//mnId w.r.t. region is mnDocId + 1;
+ {
+ aDeletedTemplate += pItem->maTitle+"\n";
+ }
+ }
+
+ if (!aDeletedTemplate.isEmpty())
+ {
+ OUString aMsg( SfxResId(STR_MSG_ERROR_DELETE_TEMPLATE) );
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(m_xDialog.get(),
+ VclMessageType::Warning, VclButtonsType::Ok,
+ aMsg.replaceFirst("$1",aDeletedTemplate)));
+ xBox->run();
+ }
+}
+
+IMPL_LINK(SfxTemplateManagerDlg, DefaultTemplateHdl, ThumbnailViewItem*, pItem, void)
+{
+ TemplateViewItem *pViewItem = static_cast<TemplateViewItem*>(pItem);
+ OUString aServiceName;
+
+ if(!pViewItem->IsDefaultTemplate())
+ {
+ if (lcl_getServiceName(pViewItem->getPath(),aServiceName))
+ {
+ OUString sPrevDefault = SfxObjectFactory::GetStandardTemplate( aServiceName );
+ if(!sPrevDefault.isEmpty())
+ {
+ mxLocalView->RemoveDefaultTemplateIcon(sPrevDefault);
+ }
+ SfxObjectFactory::SetStandardTemplate(aServiceName,pViewItem->getPath());
+ pViewItem->showDefaultIcon(true);
+ }
+ }
+ else
+ {
+ if(lcl_getServiceName(pViewItem->getPath(),aServiceName))
+ {
+ SfxObjectFactory::SetStandardTemplate( aServiceName, OUString() );
+ pViewItem->showDefaultIcon(false);
+ }
+ }
+
+ updateMenuItems();
+}
+
+IMPL_LINK_NOARG(SfxTemplateManagerDlg, SearchUpdateHdl, weld::Entry&, void)
+{
+ m_aUpdateDataTimer.Start();
+}
+
+IMPL_LINK_NOARG(SfxTemplateManagerDlg, ImplUpdateDataHdl, Timer*, void)
+{
+ SearchUpdate();
+}
+
+IMPL_LINK_NOARG(SfxTemplateManagerDlg, LoseFocusHdl, weld::Widget&, void)
+{
+ if (m_aUpdateDataTimer.IsActive())
+ {
+ m_aUpdateDataTimer.Stop();
+ m_aUpdateDataTimer.Invoke();
+ }
+}
+
+IMPL_LINK_NOARG ( SfxTemplateManagerDlg, ListViewHdl, weld::Toggleable&, void )
+{
+ setTemplateViewMode(TemplateViewMode::eListView);
+}
+
+IMPL_LINK_NOARG ( SfxTemplateManagerDlg, ThumbnailViewHdl, weld::Toggleable&, void )
+{
+ setTemplateViewMode(TemplateViewMode::eThumbnailView);
+ bMakeSelItemVisible = true;
+}
+
+IMPL_LINK_NOARG(SfxTemplateManagerDlg, FocusRectLocalHdl, weld::Widget&, tools::Rectangle)
+{
+ if(bMakeSelItemVisible && !maSelTemplates.empty())
+ mxLocalView->MakeItemVisible((*maSelTemplates.begin())->mnId);
+ bMakeSelItemVisible = false;
+ return tools::Rectangle();
+}
+
+void SfxTemplateManagerDlg::SearchUpdate()
+{
+ const OUString sSelectedRegion = mxCBFolder->get_active_text();
+ mxLocalView->setCurRegionId(mxLocalView->getRegionId(sSelectedRegion));
+ OUString aKeyword = mxSearchFilter->get_text();
+ mxLocalView->Clear();
+ std::function<bool(const TemplateItemProperties &)> aFunc =
+ [&](const TemplateItemProperties &rItem)->bool
+ {
+ return aKeyword.isEmpty() || SearchView_Keyword(aKeyword, getCurrentApplicationFilter())(rItem);
+ };
+
+ std::vector<TemplateItemProperties> aItems = mxLocalView->getFilteredItems(aFunc);
+ mxLocalView->insertItems(aItems, mxCBFolder->get_active()!=0, true);
+ mxLocalView->Invalidate();
+}
+
+IMPL_LINK_NOARG(SfxTemplateManagerDlg, GetFocusHdl, weld::Widget&, void)
+{
+ mxLocalView->deselectItems();
+ maSelTemplates.clear();
+}
+
+void SfxTemplateManagerDlg::OnTemplateState (const ThumbnailViewItem *pItem)
+{
+ bool bInSelection = maSelTemplates.find(pItem) != maSelTemplates.end();
+
+ if (pItem->isSelected())
+ {
+ if (maSelTemplates.empty())
+ {
+ mxOKButton->set_sensitive(true);
+ }
+ else if (maSelTemplates.size() != 1 || !bInSelection)
+ {
+ mxOKButton->set_sensitive(false);
+ }
+
+ if (!bInSelection)
+ maSelTemplates.insert(pItem);
+ }
+ else
+ {
+ if (bInSelection)
+ {
+ maSelTemplates.erase(pItem);
+
+ if (maSelTemplates.empty())
+ {
+ mxOKButton->set_sensitive(false);
+ }
+ else if (maSelTemplates.size() == 1)
+ {
+ mxOKButton->set_sensitive(true);
+ }
+ }
+ }
+
+}
+
+void SfxTemplateManagerDlg::OnTemplateImportCategory(std::u16string_view sCategory)
+{
+ sfx2::FileDialogHelper aFileDlg(css::ui::dialogs::TemplateDescription::FILEOPEN_SIMPLE,
+ FileDialogFlags::MultiSelection, m_xDialog.get());
+ aFileDlg.SetContext(sfx2::FileDialogHelper::TemplateImport);
+
+ // add "All" filter
+ aFileDlg.AddFilter( SfxResId(STR_SFX_FILTERNAME_ALL),
+ FILEDIALOG_FILTER_ALL );
+
+ // add template filter
+ OUString sFilterExt;
+ OUString sFilterName( SfxResId( STR_TEMPLATE_FILTER ) );
+
+ // add filters of modules which are installed
+ SvtModuleOptions aModuleOpt;
+ if ( aModuleOpt.IsModuleInstalled( SvtModuleOptions::EModule::WRITER ) )
+ sFilterExt += "*.ott;*.stw;*.oth;*.dotx;*.dot";
+
+ if ( aModuleOpt.IsModuleInstalled( SvtModuleOptions::EModule::CALC ) )
+ {
+ if ( !sFilterExt.isEmpty() )
+ sFilterExt += ";";
+
+ sFilterExt += "*.ots;*.stc;*.xltx;*.xlt";
+ }
+
+ if ( aModuleOpt.IsModuleInstalled( SvtModuleOptions::EModule::IMPRESS ) )
+ {
+ if ( !sFilterExt.isEmpty() )
+ sFilterExt += ";";
+
+ sFilterExt += "*.otp;*.sti;*.pot;*.potx";
+ }
+
+ if ( aModuleOpt.IsModuleInstalled( SvtModuleOptions::EModule::DRAW ) )
+ {
+ if ( !sFilterExt.isEmpty() )
+ sFilterExt += ";";
+
+ sFilterExt += "*.otg;*.std";
+ }
+
+ if ( !sFilterExt.isEmpty() )
+ sFilterExt += ";";
+
+ sFilterExt += "*.vor";
+
+ sFilterName += " (" + sFilterExt + ")";
+
+ aFileDlg.AddFilter( sFilterName, sFilterExt );
+ aFileDlg.SetCurrentFilter( sFilterName );
+
+ ErrCode nCode = aFileDlg.Execute();
+
+ if ( nCode != ERRCODE_NONE )
+ return;
+
+ const css::uno::Sequence<OUString> aFiles = aFileDlg.GetSelectedFiles();
+
+ if (!aFiles.hasElements())
+ return;
+
+ //Import to the selected regions
+ TemplateContainerItem* pContItem = mxLocalView->getRegion(sCategory);
+ if(!pContItem)
+ return;
+
+ OUString aTemplateList;
+
+ for (const auto& rFile : aFiles)
+ {
+ if(!mxLocalView->copyFrom(pContItem, rFile))
+ {
+ if (aTemplateList.isEmpty())
+ aTemplateList = rFile;
+ else
+ aTemplateList += "\n" + rFile;
+ }
+ }
+
+ if (!aTemplateList.isEmpty())
+ {
+ OUString aMsg(SfxResId(STR_MSG_ERROR_IMPORT));
+ aMsg = aMsg.replaceFirst("$1",pContItem->maTitle);
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(m_xDialog.get(),
+ VclMessageType::Warning, VclButtonsType::Ok,
+ aMsg.replaceFirst("$2",aTemplateList)));
+ xBox->run();
+ }
+}
+
+void SfxTemplateManagerDlg::OnTemplateExport()
+{
+ uno::Reference<XComponentContext> xContext(comphelper::getProcessComponentContext());
+ uno::Reference<XFolderPicker2> xFolderPicker = sfx2::createFolderPicker(xContext, m_xDialog.get());
+
+ xFolderPicker->setDisplayDirectory(SvtPathOptions().GetWorkPath());
+
+ sal_Int16 nResult = xFolderPicker->execute();
+ sal_Int16 nCount = maSelTemplates.size();
+
+ if( nResult != ExecutableDialogResults::OK )
+ return;
+
+ OUString aTemplateList;
+ INetURLObject aPathObj(xFolderPicker->getDirectory());
+ aPathObj.setFinalSlash();
+
+ // export templates from the current view
+
+ sal_uInt16 i = 1;
+ auto aSelTemplates = maSelTemplates;
+ for (auto const& selTemplate : aSelTemplates)
+ {
+ const TemplateViewItem *pItem = static_cast<const TemplateViewItem*>(selTemplate);
+
+ INetURLObject aItemPath(pItem->getPath());
+
+ if ( 1 == i )
+ aPathObj.Append(aItemPath.getName());
+ else
+ aPathObj.setName(aItemPath.getName());
+
+ OUString aPath = aPathObj.GetMainURL( INetURLObject::DecodeMechanism::NONE );
+
+ if (!mxLocalView->exportTo(pItem->mnDocId + 1, //mnId w.r.t. region = mDocId + 1
+ mxLocalView->getRegionId(pItem->mnRegionId), //pItem->mnRegionId does not store actual region Id
+ aPath))
+ {
+ if (aTemplateList.isEmpty())
+ aTemplateList = pItem->maTitle;
+ else
+ aTemplateList += "\n" + pItem->maTitle;
+ }
+ ++i;
+ mxLocalView->deselectItems();
+ }
+
+ if (!aTemplateList.isEmpty())
+ {
+ OUString aText( SfxResId(STR_MSG_ERROR_EXPORT) );
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(m_xDialog.get(),
+ VclMessageType::Warning, VclButtonsType::Ok,
+ aText.replaceFirst("$1",aTemplateList)));
+ xBox->run();
+ }
+ else
+ {
+ OUString sText( SfxResId(STR_MSG_EXPORT_SUCCESS) );
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(m_xDialog.get(),
+ VclMessageType::Info, VclButtonsType::Ok,
+ sText.replaceFirst("$1", OUString::number(nCount))));
+ xBox->run();
+ }
+}
+
+void SfxTemplateManagerDlg::OnTemplateOpen ()
+{
+ ThumbnailViewItem *pItem = const_cast<ThumbnailViewItem*>(*maSelTemplates.begin());
+
+ OpenTemplateHdl(pItem);
+}
+
+void SfxTemplateManagerDlg::OnCategoryNew()
+{
+ InputDialog dlg(m_xDialog.get(), SfxResId(STR_INPUT_NEW));
+ dlg.set_title(SfxResId(STR_WINDOW_TITLE_RENAME_NEW_CATEGORY));
+ int ret = dlg.run();
+
+ if (!ret)
+ return;
+
+ OUString aName = dlg.GetEntryText();
+
+ if(mxLocalView->createRegion(aName))
+ mxCBFolder->append_text(aName);
+ else
+ {
+ OUString aMsg( SfxResId(STR_CREATE_ERROR) );
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(m_xDialog.get(),
+ VclMessageType::Warning, VclButtonsType::Ok,
+ aMsg.replaceFirst("$1", aName)));
+ xBox->run();
+ }
+}
+
+void SfxTemplateManagerDlg::OnCategoryRename()
+{
+ OUString sCategory = mxCBFolder->get_active_text();
+ InputDialog dlg(m_xDialog.get(), SfxResId(STR_INPUT_NEW));
+ dlg.set_title(SfxResId(STR_WINDOW_TITLE_RENAME_CATEGORY));
+ dlg.SetEntryText(sCategory);
+ int ret = dlg.run();
+
+ if (!ret)
+ return;
+
+ OUString aName = dlg.GetEntryText();
+
+ if(mxLocalView->renameRegion(sCategory, aName))
+ {
+ sal_Int32 nPos = mxCBFolder->find_text(sCategory);
+ mxCBFolder->remove(nPos);
+ mxCBFolder->insert_text(nPos, aName);
+ mxCBFolder->set_active(nPos);
+
+ mxLocalView->reload();
+ SearchUpdate();
+ }
+ else
+ {
+ OUString aMsg( SfxResId(STR_CREATE_ERROR) );
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(m_xDialog.get(),
+ VclMessageType::Warning, VclButtonsType::Ok,
+ aMsg.replaceFirst("$1", aName)));
+ xBox->run();
+ }
+}
+
+void SfxTemplateManagerDlg::OnCategoryDelete()
+{
+ const auto sCategory = mxCBFolder->get_active_text();
+ std::unique_ptr<weld::MessageDialog> popupDlg(Application::CreateMessageDialog(m_xDialog.get(),
+ VclMessageType::Question, VclButtonsType::YesNo,
+ SfxResId(STR_QMSG_SEL_FOLDER_DELETE).replaceFirst("$1",sCategory)));
+ if (popupDlg->run() != RET_YES)
+ return;
+
+ sal_Int16 nItemId = mxLocalView->getRegionId(sCategory);
+
+ if (!mxLocalView->removeRegion(nItemId))
+ {
+ OUString sMsg( SfxResId(STR_MSG_ERROR_DELETE_FOLDER) );
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(m_xDialog.get(),
+ VclMessageType::Warning, VclButtonsType::Ok,
+ sMsg.replaceFirst("$1",sCategory)));
+ xBox->run();
+ }
+ else
+ {
+ mxCBFolder->remove_text(sCategory);
+ }
+
+ mxLocalView->reload();
+ mxLocalView->showAllTemplates();
+ mxCBApp->set_active(0);
+ mxCBFolder->set_active(0);
+ SearchUpdate();
+ mxActionBar->set_item_sensitive(MNI_ACTION_RENAME_FOLDER, false);
+ mxActionBar->set_item_sensitive(MNI_ACTION_DELETE_FOLDER, false);
+ updateMenuItems();
+}
+
+void SfxTemplateManagerDlg::updateMenuItems ()
+{
+
+ mxActionBar->set_item_visible(MNI_ACTION_DEFAULT, false);
+ mxActionBar->set_item_visible(MNI_ACTION_DEFAULT_WRITER, false);
+ mxActionBar->set_item_visible(MNI_ACTION_DEFAULT_CALC, false);
+ mxActionBar->set_item_visible(MNI_ACTION_DEFAULT_IMPRESS, false);
+ mxActionBar->set_item_visible(MNI_ACTION_DEFAULT_DRAW, false);
+ mxActionBar->set_item_sensitive(MNI_ACTION_DEFAULT, false);
+ mxActionBar->set_item_sensitive(MNI_ACTION_DEFAULT_WRITER, false);
+ mxActionBar->set_item_sensitive(MNI_ACTION_DEFAULT_CALC, false);
+ mxActionBar->set_item_sensitive(MNI_ACTION_DEFAULT_IMPRESS, false);
+ mxActionBar->set_item_sensitive(MNI_ACTION_DEFAULT_DRAW, false);
+
+ SvtModuleOptions aModOpt;
+ if( mxCBApp->get_active() == MNI_WRITER)
+ {
+ mxActionBar->set_item_visible(MNI_ACTION_DEFAULT_WRITER, true);
+ if(!aModOpt.GetFactoryStandardTemplate( SvtModuleOptions::EFactory::WRITER).isEmpty())
+ mxActionBar->set_item_sensitive(MNI_ACTION_DEFAULT_WRITER, true);
+ }
+ else if( mxCBApp->get_active() == MNI_CALC )
+ {
+ mxActionBar->set_item_visible(MNI_ACTION_DEFAULT_CALC, true);
+ if(!aModOpt.GetFactoryStandardTemplate( SvtModuleOptions::EFactory::CALC).isEmpty())
+ mxActionBar->set_item_sensitive(MNI_ACTION_DEFAULT_CALC, true);
+ }
+ else if(mxCBApp->get_active() == MNI_IMPRESS)
+ {
+ mxActionBar->set_item_visible(MNI_ACTION_DEFAULT_IMPRESS, true);
+ if(!aModOpt.GetFactoryStandardTemplate( SvtModuleOptions::EFactory::IMPRESS).isEmpty())
+ mxActionBar->set_item_sensitive(MNI_ACTION_DEFAULT_IMPRESS, true);
+ }
+ else if(mxCBApp->get_active() == MNI_DRAW)
+ {
+ mxActionBar->set_item_visible(MNI_ACTION_DEFAULT_DRAW, true);
+ if(!aModOpt.GetFactoryStandardTemplate( SvtModuleOptions::EFactory::DRAW).isEmpty())
+ mxActionBar->set_item_sensitive(MNI_ACTION_DEFAULT_DRAW, true);
+ }
+ else if(mxCBApp->get_active() == MNI_ALL_APPLICATIONS)
+ {
+ mxActionBar->set_item_visible(MNI_ACTION_DEFAULT, true);
+ if(!lcl_getAllFactoryURLs().empty())
+ mxActionBar->set_item_sensitive(MNI_ACTION_DEFAULT, true);
+ }
+}
+
+void SfxTemplateManagerDlg::localMoveTo(sal_uInt16 nItemId)
+{
+ if (nItemId)
+ {
+ // Move templates to desired folder if for some reason move fails
+ // try copying them.
+ mxLocalView->moveTemplates(maSelTemplates,nItemId);
+ }
+}
+
+static bool lcl_getServiceName ( const OUString &rFileURL, OUString &rName )
+{
+ bool bRet = false;
+
+ if ( !rFileURL.isEmpty() )
+ {
+ try
+ {
+ uno::Reference< embed::XStorage > xStorage =
+ comphelper::OStorageHelper::GetStorageFromURL( rFileURL, embed::ElementModes::READ );
+
+ SotClipboardFormatId nFormat = SotStorage::GetFormatID( xStorage );
+
+ std::shared_ptr<const SfxFilter> pFilter = SfxGetpApp()->GetFilterMatcher().GetFilter4ClipBoardId( nFormat );
+
+ if ( pFilter )
+ {
+ rName = pFilter->GetServiceName();
+ bRet = true;
+ }
+ }
+ catch( uno::Exception& )
+ {}
+ }
+
+ return bRet;
+}
+
+static std::vector<OUString> lcl_getAllFactoryURLs ()
+{
+ SvtModuleOptions aModOpt;
+ std::vector<OUString> aList;
+ const css::uno::Sequence<OUString> &aServiceNames = aModOpt.GetAllServiceNames();
+
+ for( const auto& rServiceName : aServiceNames )
+ {
+ if ( ! SfxObjectFactory::GetStandardTemplate( rServiceName ).isEmpty() )
+ {
+ SvtModuleOptions::EFactory eFac = SvtModuleOptions::EFactory::WRITER;
+ SvtModuleOptions::ClassifyFactoryByName( rServiceName, eFac );
+ aList.push_back(aModOpt.GetFactoryEmptyDocumentURL(eFac));
+ }
+ }
+
+ return aList;
+}
+
+
+// Class SfxTemplateCategoryDialog --------------------------------------------------
+
+SfxTemplateCategoryDialog::SfxTemplateCategoryDialog(weld::Window* pParent)
+ : GenericDialogController(pParent, "sfx/ui/templatecategorydlg.ui", "TemplatesCategoryDialog")
+ , mbIsNewCategory(false)
+ , mxLBCategory(m_xBuilder->weld_tree_view("categorylb"))
+ , mxSelectLabel(m_xBuilder->weld_label("select_label"))
+ , mxNewCategoryEdit(m_xBuilder->weld_entry("category_entry"))
+ , mxCreateLabel(m_xBuilder->weld_label("create_label"))
+ , mxOKButton(m_xBuilder->weld_button("ok"))
+{
+ mxLBCategory->append_text(SfxResId(STR_CATEGORY_NONE));
+ mxNewCategoryEdit->connect_changed(LINK(this, SfxTemplateCategoryDialog, NewCategoryEditHdl));
+ mxLBCategory->set_size_request(mxLBCategory->get_approximate_digit_width() * 32,
+ mxLBCategory->get_height_rows(8));
+ mxLBCategory->connect_changed(LINK(this, SfxTemplateCategoryDialog, SelectCategoryHdl));
+ mxOKButton->set_sensitive(false);
+}
+
+SfxTemplateCategoryDialog::~SfxTemplateCategoryDialog()
+{
+}
+
+IMPL_LINK_NOARG(SfxTemplateCategoryDialog, NewCategoryEditHdl, weld::Entry&, void)
+{
+ OUString sParam = comphelper::string::strip(mxNewCategoryEdit->get_text(), ' ');
+ mxLBCategory->set_sensitive(sParam.isEmpty());
+ if(!sParam.isEmpty())
+ {
+ msSelectedCategory = sParam;
+ mbIsNewCategory = true;
+ mxOKButton->set_sensitive(true);
+ }
+ else
+ {
+ SelectCategoryHdl(*mxLBCategory);
+ mbIsNewCategory = false;
+ }
+}
+
+IMPL_LINK_NOARG(SfxTemplateCategoryDialog, SelectCategoryHdl, weld::TreeView&, void)
+{
+ if (mxLBCategory->get_selected_index() == 0)
+ {
+ msSelectedCategory = OUString();
+ mxOKButton->set_sensitive(false);
+ mxNewCategoryEdit->set_sensitive(true);
+ }
+ else
+ {
+ msSelectedCategory = mxLBCategory->get_selected_text();
+ mxNewCategoryEdit->set_sensitive(false);
+ mxOKButton->set_sensitive(true);
+ }
+
+ mbIsNewCategory = false;
+}
+
+void SfxTemplateCategoryDialog::SetCategoryLBEntries(std::vector<OUString> aFolderNames)
+{
+ for (size_t i = 0, n = aFolderNames.size(); i < n; ++i)
+ mxLBCategory->append_text(aFolderNames[i]);
+ mxLBCategory->select(0);
+}
+
+// SfxTemplateSelectionDialog -----------------------------------------------------------------
+
+SfxTemplateSelectionDlg::SfxTemplateSelectionDlg(weld::Window* pParent)
+ : SfxTemplateManagerDlg(pParent)
+ , maIdle("sfx2 SfxTemplateManagerDlg maIdle")
+{
+ mxCBApp->set_active(MNI_IMPRESS);
+ mxCBFolder->set_active(0);
+ m_xDialog->set_title(SfxResId(STR_TEMPLATE_SELECTION));
+
+ if (mxLocalView->IsVisible())
+ {
+ mxLocalView->filterItems(ViewFilter_Application(getCurrentApplicationFilter()));
+ mxLocalView->showAllTemplates();
+ }
+
+ mxCBApp->set_sensitive(false);
+ mxActionBar->show();
+ mxCBXHideDlg->show();
+ mxCBXHideDlg->set_active(true);
+
+ mxLocalView->setOpenTemplateHdl(LINK(this,SfxTemplateSelectionDlg, OpenTemplateHdl));
+ mxOKButton->connect_clicked(LINK(this, SfxTemplateSelectionDlg, OkClickHdl));
+ updateMenuItems();
+}
+
+SfxTemplateSelectionDlg::~SfxTemplateSelectionDlg()
+{
+ maIdle.Stop();
+}
+
+short SfxTemplateSelectionDlg::run()
+{
+ // tdf#124597 at startup this dialog is launched before its parent window
+ // has taken its final size. The parent size request is processed during
+ // the dialogs event loop so configure this dialog to center to
+ // the parents pending geometry request
+ m_xDialog->set_centered_on_parent(true);
+
+ // tdf#125079 toggle off the size tracking at some future idle point
+ maIdle.SetPriority(TaskPriority::LOWEST);
+ maIdle.SetInvokeHandler(LINK(this,SfxTemplateSelectionDlg,TimeOut));
+ maIdle.Start();
+ setTemplateViewMode(TemplateViewMode::eThumbnailView);
+
+ return weld::GenericDialogController::run();
+}
+
+IMPL_LINK_NOARG(SfxTemplateSelectionDlg, TimeOut, Timer*, void)
+{
+ m_xDialog->set_centered_on_parent(false);
+}
+
+IMPL_LINK(SfxTemplateSelectionDlg, OpenTemplateHdl, ThumbnailViewItem*, pItem, void)
+{
+ TemplateViewItem *pViewItem = static_cast<TemplateViewItem*>(pItem);
+ msTemplatePath = pViewItem->getPath();
+
+ m_xDialog->response(RET_OK);
+}
+
+IMPL_LINK_NOARG(SfxTemplateSelectionDlg, OkClickHdl, weld::Button&, void)
+{
+ TemplateViewItem *pViewItem = static_cast<TemplateViewItem*>(const_cast<ThumbnailViewItem*>(*maSelTemplates.begin()));
+ msTemplatePath = pViewItem->getPath();
+
+ m_xDialog->response(RET_OK);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/doc/watermarkitem.cxx b/sfx2/source/doc/watermarkitem.cxx
new file mode 100644
index 000000000..4e64afd7f
--- /dev/null
+++ b/sfx2/source/doc/watermarkitem.cxx
@@ -0,0 +1,82 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * 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/watermarkitem.hxx>
+#include <sfx2/sfxsids.hrc>
+#include <comphelper/propertysequence.hxx>
+
+SfxWatermarkItem::SfxWatermarkItem()
+: SfxPoolItem( SID_WATERMARK )
+, m_aText( "" )
+, m_aFont( "Liberation Sans" )
+, m_nAngle( 45 )
+, m_nTransparency( 50 )
+, m_nColor( 0xc0c0c0 )
+{
+}
+
+SfxPoolItem* SfxWatermarkItem::CreateDefault()
+{
+ return new SfxWatermarkItem();
+}
+
+bool SfxWatermarkItem::operator==( const SfxPoolItem& rCmp ) const
+{
+ return ( SfxPoolItem::operator==( rCmp ) &&
+ m_aText == static_cast<const SfxWatermarkItem&>(rCmp).m_aText &&
+ m_aFont == static_cast<const SfxWatermarkItem&>(rCmp).m_aFont &&
+ m_nAngle == static_cast<const SfxWatermarkItem&>(rCmp).m_nAngle &&
+ m_nTransparency == static_cast<const SfxWatermarkItem&>(rCmp).m_nTransparency &&
+ m_nColor == static_cast<const SfxWatermarkItem&>(rCmp).m_nColor );
+}
+
+SfxWatermarkItem* SfxWatermarkItem::Clone( SfxItemPool *) const
+{
+ return new SfxWatermarkItem(*this);
+}
+
+bool SfxWatermarkItem::QueryValue( css::uno::Any& rVal, sal_uInt8 /*nMemberId*/ ) const
+{
+ rVal <<= comphelper::InitPropertySequence( {
+ { "Text", css::uno::Any( m_aText ) },
+ { "Font", css::uno::Any( m_aFont ) },
+ { "Angle", css::uno::Any( m_nAngle ) },
+ { "Transparency", css::uno::Any( m_nTransparency ) },
+ { "Color", css::uno::Any( m_nColor ) },
+ } );
+
+ return true;
+}
+
+bool SfxWatermarkItem::PutValue( const css::uno::Any& rVal, sal_uInt8 /*nMemberId*/ )
+{
+ css::uno::Sequence<css::beans::PropertyValue> aSequence;
+
+ if ( rVal >>= aSequence )
+ {
+ for(const auto& aEntry : std::as_const(aSequence))
+ {
+ if(aEntry.Name == "Text")
+ aEntry.Value >>= m_aText;
+ if(aEntry.Name == "Font")
+ aEntry.Value >>= m_aFont;
+ if(aEntry.Name == "Angle")
+ aEntry.Value >>= m_nAngle;
+ if(aEntry.Name == "Transparency")
+ aEntry.Value >>= m_nTransparency;
+ if(aEntry.Name == "Color")
+ aEntry.Value >>= m_nColor;
+ }
+ return true;
+ }
+
+ return false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/sfx2/source/doc/zoomitem.cxx b/sfx2/source/doc/zoomitem.cxx
new file mode 100644
index 000000000..60f193e85
--- /dev/null
+++ b/sfx2/source/doc/zoomitem.cxx
@@ -0,0 +1,172 @@
+/* -*- 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/zoomitem.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/beans/PropertyValue.hpp>
+
+#include <comphelper/propertyvalue.hxx>
+#include <osl/diagnose.h>
+
+#include <cassert>
+
+
+SfxPoolItem* SvxZoomItem::CreateDefault() { return new SvxZoomItem; }
+
+constexpr OUStringLiteral ZOOM_PARAM_VALUE = u"Value";
+constexpr OUStringLiteral ZOOM_PARAM_VALUESET = u"ValueSet";
+constexpr OUStringLiteral ZOOM_PARAM_TYPE = u"Type";
+#define ZOOM_PARAMS 3
+
+
+SvxZoomItem::SvxZoomItem
+(
+ SvxZoomType eZoomType,
+ sal_uInt16 nVal,
+ sal_uInt16 _nWhich
+)
+: SfxUInt16Item( _nWhich, nVal ),
+ nValueSet( SvxZoomEnableFlags::ALL ),
+ eType( eZoomType )
+{
+}
+
+SvxZoomItem* SvxZoomItem::Clone( SfxItemPool * /*pPool*/ ) const
+{
+ return new SvxZoomItem( *this );
+}
+
+bool SvxZoomItem::operator==( const SfxPoolItem& rAttr ) const
+{
+ assert(SfxPoolItem::operator==(rAttr));
+
+ const SvxZoomItem& rItem = static_cast<const SvxZoomItem&>(rAttr);
+
+ return ( GetValue() == rItem.GetValue() &&
+ nValueSet == rItem.GetValueSet() &&
+ eType == rItem.GetType() );
+}
+
+bool SvxZoomItem::QueryValue( css::uno::Any& rVal, sal_uInt8 nMemberId ) const
+{
+ nMemberId &= ~CONVERT_TWIPS;
+ switch( nMemberId )
+ {
+ case 0:
+ {
+ css::uno::Sequence< css::beans::PropertyValue > aSeq{
+ comphelper::makePropertyValue(ZOOM_PARAM_VALUE, sal_Int32( GetValue() )),
+ comphelper::makePropertyValue(ZOOM_PARAM_VALUESET, sal_Int16( nValueSet )),
+ comphelper::makePropertyValue(ZOOM_PARAM_TYPE, sal_Int16( eType ))
+ };
+ assert(aSeq.getLength() == ZOOM_PARAMS);
+ rVal <<= aSeq;
+ break;
+ }
+
+ case MID_VALUE: rVal <<= static_cast<sal_Int32>(GetValue()); break;
+ case MID_VALUESET: rVal <<= static_cast<sal_Int16>(nValueSet); break;
+ case MID_TYPE: rVal <<= static_cast<sal_Int16>(eType); break;
+ default:
+ OSL_FAIL("sfx2::SvxZoomItem::QueryValue(), Wrong MemberId!");
+ return false;
+ }
+
+ return true;
+}
+
+bool SvxZoomItem::PutValue( const css::uno::Any& rVal, sal_uInt8 nMemberId )
+{
+ nMemberId &= ~CONVERT_TWIPS;
+ switch( nMemberId )
+ {
+ case 0:
+ {
+ css::uno::Sequence< css::beans::PropertyValue > aSeq;
+ if (( rVal >>= aSeq ) && ( aSeq.getLength() == ZOOM_PARAMS ))
+ {
+ sal_Int32 nValueTmp( 0 );
+ sal_Int16 nValueSetTmp( 0 );
+ sal_Int16 nTypeTmp( 0 );
+ bool bAllConverted( true );
+ sal_Int16 nConvertedCount( 0 );
+ for ( const auto& rProp : std::as_const(aSeq) )
+ {
+ if ( rProp.Name == ZOOM_PARAM_VALUE )
+ {
+ bAllConverted &= ( rProp.Value >>= nValueTmp );
+ ++nConvertedCount;
+ }
+ else if ( rProp.Name == ZOOM_PARAM_VALUESET )
+ {
+ bAllConverted &= ( rProp.Value >>= nValueSetTmp );
+ ++nConvertedCount;
+ }
+ else if ( rProp.Name == ZOOM_PARAM_TYPE )
+ {
+ bAllConverted &= ( rProp.Value >>= nTypeTmp );
+ ++nConvertedCount;
+ }
+ }
+
+ if ( bAllConverted && nConvertedCount == ZOOM_PARAMS )
+ {
+ SetValue( static_cast<sal_uInt16>(nValueTmp) );
+ nValueSet = static_cast<SvxZoomEnableFlags>(nValueSetTmp);
+ eType = static_cast<SvxZoomType>(nTypeTmp);
+ return true;
+ }
+ }
+ return false;
+ }
+ case MID_VALUE:
+ {
+ sal_Int32 nVal = 0;
+ if ( rVal >>= nVal )
+ {
+ SetValue( static_cast<sal_uInt16>(nVal) );
+ return true;
+ }
+ else
+ return false;
+ }
+
+ case MID_VALUESET:
+ case MID_TYPE:
+ {
+ sal_Int16 nVal;
+ if ( rVal >>= nVal )
+ {
+ if ( nMemberId == MID_VALUESET )
+ nValueSet = static_cast<SvxZoomEnableFlags>(nVal);
+ else if ( nMemberId == MID_TYPE )
+ eType = static_cast<SvxZoomType>(nVal);
+ return true;
+ }
+ else
+ return false;
+ }
+
+ default:
+ OSL_FAIL("sfx2::SvxZoomItem::PutValue(), Wrong MemberId!");
+ return false;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/explorer/nochaos.cxx b/sfx2/source/explorer/nochaos.cxx
new file mode 100644
index 000000000..ad4bd2343
--- /dev/null
+++ b/sfx2/source/explorer/nochaos.cxx
@@ -0,0 +1,187 @@
+/* -*- 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 <svl/itempool.hxx>
+#include <svl/poolitem.hxx>
+#include <svl/stritem.hxx>
+#include <nochaos.hxx>
+#include <memory>
+
+
+#define WID_CHAOS_START 500
+
+
+namespace {
+
+class CntItemPool;
+
+class CntStaticPoolDefaults_Impl
+{
+ static const sal_uInt32 m_nItems = 1;
+ std::vector<SfxPoolItem*> mvDefaults;
+ std::unique_ptr<SfxItemInfo[]> m_pItemInfos;
+
+private:
+ inline void Insert( SfxPoolItem* pItem );
+
+public:
+ explicit CntStaticPoolDefaults_Impl();
+ ~CntStaticPoolDefaults_Impl();
+ CntStaticPoolDefaults_Impl(const CntStaticPoolDefaults_Impl&) = delete;
+ CntStaticPoolDefaults_Impl& operator=(const CntStaticPoolDefaults_Impl&) = delete;
+
+ std::vector<SfxPoolItem*>* GetDefaults() { return &mvDefaults; }
+ const SfxItemInfo* GetItemInfos() const { return m_pItemInfos.get(); }
+};
+
+
+class CntItemPool: public SfxItemPool
+{
+ static CntItemPool* _pThePool;
+ sal_uInt16 _nRefs;
+
+protected:
+ CntItemPool();
+ virtual ~CntItemPool() override;
+
+public:
+ static CntItemPool* Acquire();
+ static sal_uInt16 Release();
+};
+
+}
+
+// static
+SfxItemPool* NoChaos::GetItemPool()
+{
+ // Get and hold CHAOS item pool.
+ return CntItemPool::Acquire();
+}
+
+
+// static
+sal_uInt16 NoChaos::ReleaseItemPool()
+{
+ // Release CHAOS item pool.
+ return CntItemPool::Release();
+}
+
+
+// CntItemPool implementation
+
+
+static CntStaticPoolDefaults_Impl* pPoolDefs_Impl = nullptr;
+
+// static member!
+CntItemPool* CntItemPool::_pThePool = nullptr;
+
+
+CntItemPool::CntItemPool()
+: SfxItemPool( "chaos", WID_CHAOS_START, WID_CHAOS_START, nullptr ),
+ _nRefs( 0 )
+{
+ FreezeIdRanges();
+
+ // Create static defaults.
+ pPoolDefs_Impl = new CntStaticPoolDefaults_Impl;
+
+ // Set item infos.
+ SetItemInfos( pPoolDefs_Impl->GetItemInfos() );
+
+ // Set static pool default items.
+ SetDefaults( pPoolDefs_Impl->GetDefaults() );
+}
+
+
+//virtual
+CntItemPool::~CntItemPool()
+{
+ // Release static pool default items.
+ ReleaseDefaults();
+}
+
+
+// static
+CntItemPool* CntItemPool::Acquire()
+{
+ if ( !_pThePool )
+ _pThePool = new CntItemPool;
+
+ _pThePool->_nRefs++;
+
+ return _pThePool;
+}
+
+
+// static
+sal_uInt16 CntItemPool::Release()
+{
+ if ( !_pThePool )
+ return 0;
+
+ sal_uInt16& nRefs = _pThePool->_nRefs;
+
+ if ( nRefs )
+ --nRefs;
+
+ if ( !nRefs )
+ {
+ delete _pThePool;
+ _pThePool = nullptr;
+ delete pPoolDefs_Impl;
+ pPoolDefs_Impl = nullptr;
+ return 0;
+ }
+
+ return nRefs;
+}
+
+
+// CntStaticPoolDefaults_Impl implementation.
+
+
+inline void CntStaticPoolDefaults_Impl::Insert(
+ SfxPoolItem* pItem /* Static Pool Default Item */ )
+{
+ sal_uInt16 nPos = pItem->Which() - WID_CHAOS_START;
+
+ mvDefaults[ nPos ] = pItem;
+ m_pItemInfos[ nPos ]._nSID = 0;
+ m_pItemInfos[ nPos ]._bPoolable = true;
+}
+
+
+CntStaticPoolDefaults_Impl::~CntStaticPoolDefaults_Impl()
+{
+ for ( sal_uInt32 n = 0; n < m_nItems; ++n )
+ delete mvDefaults[ n ];
+}
+
+
+CntStaticPoolDefaults_Impl::CntStaticPoolDefaults_Impl()
+: mvDefaults( m_nItems, nullptr ),
+ m_pItemInfos( new SfxItemInfo [ m_nItems ] )
+{
+ memset( m_pItemInfos.get(), 0, sizeof( SfxItemInfo ) * m_nItems );
+ Insert( new SfxStringItem( WID_CHAOS_START, OUString() ) );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/inc/StyleList.hxx b/sfx2/source/inc/StyleList.hxx
new file mode 100644
index 000000000..f6ba9f318
--- /dev/null
+++ b/sfx2/source/inc/StyleList.hxx
@@ -0,0 +1,239 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <o3tl/typed_flags_set.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/styfitem.hxx>
+#include <svl/eitem.hxx>
+#include <svl/style.hxx>
+#include <vcl/idle.hxx>
+#include <vcl/transfer.hxx>
+#include <vcl/weld.hxx>
+
+class SfxObjectShell;
+class SfxStyleFamilyItem;
+class SfxTemplateItem;
+class SfxCommonTemplateDialog_Impl;
+class SfxTemplateControllerItem;
+
+enum class StyleFlags
+{
+ NONE = 0,
+ UpdateFamilyList = 1,
+ UpdateFamily = 2
+};
+
+namespace o3tl
+{
+template <> struct typed_flags<StyleFlags> : is_typed_flags<StyleFlags, 3>
+{
+};
+}
+
+class TreeViewDropTarget;
+
+constexpr int MAX_FAMILIES = 6;
+constexpr int COUNT_BOUND_FUNC = 14;
+
+class StyleList final : public SfxListener
+{
+ friend class TreeViewDropTarget;
+ friend class SfxTemplateControllerItem;
+
+public:
+ // Constructor
+ StyleList(weld::Builder* pBuilder, SfxBindings* pBindings, SfxCommonTemplateDialog_Impl* Parent,
+ weld::Container* pC, OString treeviewname, OString flatviewname);
+
+ // Destructor
+ ~StyleList();
+
+ // This function connects m_xTreeBox, m_xFmtLb and m_pParentDialog with certain LINKs
+ void Initialize();
+
+ // 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 SelectStyle(const OUString& rStr, bool bIsCallback);
+ // Checks whether a family has a saved state
+ bool CurrentFamilyHasState() { return nullptr != m_pFamilyState[m_nActFamily - 1]; }
+
+ // This function is a subpart of Dialog's SetFamilyState
+ // When a new style is selected for use, it resets it.
+ void SetFamilyState(sal_uInt16 nSlotId, const SfxTemplateItem* pItem);
+
+ // It is used in Dialog's EnableExample_Impl
+ // When the value of m_bNewbyExampleDisabled is updated there
+ // while creating a new style by example,
+ // the corresponding value gets updated here too.
+ void EnableNewByExample(bool newByExampleDisabled);
+
+ // This function is used to set a hierarchical view.
+ void SetHierarchical();
+ // This function handles the controls while setting a filter except hierarchical
+ void SetFilterControlsHandle();
+ // Return whether treeview is visible
+ // It is used in StyleList's UpdateStyles_Hdl
+ // It is used to defaultly set the hierarchical view
+ bool IsTreeView() const { return m_xTreeBox->get_visible(); }
+
+ // Helper function: Access to the current family item
+ // Used in Dialog's updateStyleHandler, Execute_Impl etc...
+ const SfxStyleFamilyItem* GetFamilyItem() const;
+ // Used to get the current selected entry in treeview
+ // Used in Dialog's Execute_Impl, Action_Select etc...
+ OUString GetSelectedEntry() const;
+ // Returns the Family Item at ith index
+ // Used in Dialog's ReadResource_Hdl
+ const SfxStyleFamilyItem& GetFamilyItemByIndex(size_t i) const;
+ bool IsHierarchical() const { return m_bHierarchical; }
+
+ void Enabledel(bool candel) { m_bCanDel = candel; }
+ void Enablehide(bool canhide) { m_bCanHide = canhide; }
+ void Enableshow(bool canshow) { m_bCanShow = canshow; }
+ void Enablenew(bool cannew) { m_bCanNew = cannew; }
+ void Enableedit(bool canedit) { m_bCanEdit = canedit; }
+ // Handles the enabling/Disabling of Preview
+ void EnablePreview(bool bCustomPreview);
+ // Used in Dialog's Execute_Impl
+ // It is a necessary condition to execute a style
+ bool EnableExecute();
+
+ void connect_UpdateStyles(const Link<StyleFlags, void>& rLink) { m_aUpdateStyles = rLink; }
+ void connect_ReadResource(const Link<StyleList&, void>& rLink) { m_aReadResource = rLink; }
+ void connect_ClearResource(const Link<void*, void>& rLink) { m_aClearResource = rLink; }
+ void connect_LoadFactoryStyleFilter(const Link<SfxObjectShell const*, sal_Int32>& rLink);
+ void connect_SaveSelection(const Link<StyleList&, SfxObjectShell*> rLink);
+ void connect_UpdateFamily(const Link<StyleList&, void> rLink) { m_aUpdateFamily = rLink; }
+
+ void FamilySelect(sal_uInt16 nEntry);
+ void FilterSelect(sal_uInt16 nActFilter, bool bsetFilter);
+
+ DECL_LINK(NewMenuExecuteAction, void*, void);
+
+private:
+ void FillTreeBox(SfxStyleFamily eFam);
+
+ void UpdateFamily();
+ void UpdateStyles(StyleFlags nFlags);
+
+ OUString getDefaultStyleName(const SfxStyleFamily eFam);
+ SfxStyleFamily GetActualFamily() const;
+ void GetSelectedStyle() const;
+
+ sal_Int8 AcceptDrop(const AcceptDropEvent& rEvt, const DropTargetHelper& rHelper);
+ void DropHdl(const OUString& rStyle, const OUString& rParent);
+
+ void MenuSelect(const OString& rIdent);
+ void PrepareMenu(const Point& rPos);
+ void ShowMenu(const CommandEvent& rCEvt);
+ void CreateContextMenu();
+
+ void Notify(SfxBroadcaster& rBC, const SfxHint& rHint);
+
+ // In which FamilyState do I have to look, in order to get the
+ // information of the ith Family in the pStyleFamilies.
+ sal_uInt16 StyleNrToInfoOffset(sal_uInt16 i);
+
+ DECL_LINK(ReadResource, void*, size_t);
+ DECL_LINK(Clear, void*, void);
+ DECL_LINK(Cleanup, void*, void);
+ DECL_LINK(ExecuteDrop, const ExecuteDropEvent&, sal_Int8);
+ DECL_LINK(IsSafeForWaterCan, void*, bool);
+ DECL_LINK(HasSelectedStyle, void*, bool);
+ DECL_LINK(UpdateStyleDependents, void*, void);
+ DECL_LINK(TimeOut, Timer*, void);
+ DECL_LINK(EnableTreeDrag, bool, void);
+ DECL_LINK(EnableDelete, void*, void);
+ DECL_LINK(SetWaterCanState, const SfxBoolItem*, void);
+ DECL_LINK(SetFamily, sal_uInt16, void);
+
+ void InvalidateBindings();
+
+ void Update();
+ Link<StyleFlags, void> m_aUpdateStyles;
+ Link<StyleList&, void> m_aReadResource;
+ Link<void*, void> m_aClearResource;
+ Link<SfxObjectShell const*, sal_Int32> m_aLoadFactoryStyleFilter;
+ Link<StyleList&, SfxObjectShell*> m_aSaveSelection;
+ Link<StyleList&, void> m_aUpdateFamily;
+
+ void NewHdl();
+ void EditHdl();
+ void DeleteHdl();
+ void HideHdl();
+ void ShowHdl();
+
+ DECL_LINK(DragBeginHdl, bool&, bool);
+ DECL_LINK(KeyInputHdl, const KeyEvent&, bool);
+ DECL_LINK(QueryTooltipHdl, const weld::TreeIter&, OUString);
+ DECL_LINK(CustomRenderHdl, weld::TreeView::render_args, void);
+ DECL_LINK(FmtSelectHdl, weld::TreeView&, void);
+ DECL_LINK(TreeListApplyHdl, weld::TreeView&, bool);
+ DECL_LINK(MousePressHdl, const MouseEvent&, bool);
+ DECL_STATIC_LINK(StyleList, CustomGetSizeHdl, weld::TreeView::get_size_args, Size);
+ DECL_LINK(PopupFlatMenuHdl, const CommandEvent&, bool);
+ DECL_LINK(PopupTreeMenuHdl, const CommandEvent&, bool);
+ DECL_LINK(MenuSelectAsyncHdl, void*, void);
+
+ bool m_bHierarchical : 1;
+
+ bool m_bAllowReParentDrop : 1;
+ bool m_bNewByExampleDisabled : 1;
+ bool m_bDontUpdate : 1;
+ bool m_bTreeDrag : 1;
+ bool m_bCanEdit : 1;
+ bool m_bCanHide : 1;
+ bool m_bCanShow : 1;
+ bool m_bCanNew : 1;
+ bool m_bUpdateFamily : 1;
+ bool m_bCanDel : 1;
+ bool m_bBindingUpdate : 1;
+ SfxStyleSheetBasePool* m_pStyleSheetPool;
+ sal_uInt16 m_nActFilter;
+ std::unique_ptr<weld::TreeView> m_xFmtLb;
+ std::unique_ptr<weld::TreeView> m_xTreeBox;
+
+ std::unique_ptr<weld::Builder> mxMenuBuilder;
+ std::unique_ptr<weld::Menu> mxMenu;
+
+ std::optional<SfxStyleFamilies> m_xStyleFamilies;
+ std::array<std::unique_ptr<SfxTemplateItem>, MAX_FAMILIES> m_pFamilyState;
+ SfxObjectShell* m_pCurObjShell;
+ sal_uInt16 m_nActFamily;
+ SfxStyleSearchBits m_nAppFilter; // Filter, which has set the application (for automatic)
+
+ std::unique_ptr<TreeViewDropTarget> m_xTreeView1DropTargetHelper;
+ std::unique_ptr<TreeViewDropTarget> m_xTreeView2DropTargetHelper;
+
+ SfxCommonTemplateDialog_Impl* m_pParentDialog;
+ SfxBindings* m_pBindings;
+ std::array<std::unique_ptr<SfxTemplateControllerItem>, COUNT_BOUND_FUNC> pBoundItems;
+
+ std::unique_ptr<Idle> pIdle;
+
+ OString sLastItemIdent;
+ SfxModule* m_Module;
+ sal_uInt16 m_nModifier;
+ weld::Container* m_pContainer;
+};
diff --git a/sfx2/source/inc/alienwarn.hxx b/sfx2/source/inc/alienwarn.hxx
new file mode 100644
index 000000000..7c4f8cb36
--- /dev/null
+++ b/sfx2/source/inc/alienwarn.hxx
@@ -0,0 +1,39 @@
+/* -*- 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_INC_ALIENWARN_HXX
+#define INCLUDED_SFX2_SOURCE_INC_ALIENWARN_HXX
+
+#include <vcl/weld.hxx>
+
+class SfxAlienWarningDialog final : public weld::MessageDialogController
+{
+private:
+ std::unique_ptr<weld::Button> m_xKeepCurrentBtn;
+ std::unique_ptr<weld::Button> m_xUseDefaultFormatBtn;
+ std::unique_ptr<weld::CheckButton> m_xWarningOnBox;
+
+public:
+ SfxAlienWarningDialog(weld::Window* pParent, std::u16string_view _rFormatName,
+ const OUString& _rDefaultExtension, bool rDefaultIsAlien);
+ virtual ~SfxAlienWarningDialog() override;
+};
+
+#endif // INCLUDED_SFX2_SOURCE_INC_ALIENWARN_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/inc/appbaslib.hxx b/sfx2/source/inc/appbaslib.hxx
new file mode 100644
index 000000000..30473f2af
--- /dev/null
+++ b/sfx2/source/inc/appbaslib.hxx
@@ -0,0 +1,98 @@
+/* -*- 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_INC_APPBASLIB_HXX
+#define INCLUDED_SFX2_SOURCE_INC_APPBASLIB_HXX
+
+#include <svl/lstner.hxx>
+
+#include <com/sun/star/script/XStorageBasedLibraryContainer.hpp>
+#include <com/sun/star/embed/XStorage.hpp>
+#include <vector>
+
+class BasicManager;
+
+/** helper class which holds and manipulates a BasicManager
+*/
+class SfxBasicManagerHolder final
+ : public SfxListener
+{
+private:
+ BasicManager* mpBasicManager;
+ css::uno::Reference< css::script::XStorageBasedLibraryContainer >
+ mxBasicContainer;
+ css::uno::Reference< css::script::XStorageBasedLibraryContainer >
+ mxDialogContainer;
+
+public:
+ SfxBasicManagerHolder();
+
+ enum ContainerType
+ {
+ SCRIPTS, DIALOGS
+ };
+
+ /** returns <TRUE/> if and only if the instance is currently bound to a non-<NULL/>
+ BasicManager.
+ */
+ bool isValid() const { return mpBasicManager != nullptr; }
+
+ /** returns the BasicManager which this instance is currently bound to
+ */
+ BasicManager*
+ get() const { return mpBasicManager; }
+
+ /** binds the instance to the given BasicManager
+ */
+ void reset( BasicManager* _pBasicManager );
+
+ css::script::XLibraryContainer *
+ getLibraryContainer( ContainerType _eType );
+
+ /** calls the storeLibraries at both our script and basic library container
+ */
+ void storeAllLibraries();
+
+ /** calls the setStorage at all our XStorageBasedLibraryContainer.
+ */
+ void setStorage(
+ const css::uno::Reference< css::embed::XStorage >& _rxStorage
+ );
+
+ /** calls the storeLibrariesToStorage at all our XStorageBasedLibraryContainer.
+ */
+ void storeLibrariesToStorage(
+ const css::uno::Reference< css::embed::XStorage >& _rxStorage
+ );
+
+
+ /** checks if any modules in the SfxLibraryContainer exceed the binary
+ limits.
+ */
+ bool LegacyPsswdBinaryLimitExceeded( std::vector< OUString >& sModules );
+
+ virtual void Notify(SfxBroadcaster& rBC, SfxHint const& rHint) override;
+
+private:
+ void impl_releaseContainers();
+};
+
+#endif // INCLUDED_SFX2_SOURCE_INC_APPBASLIB_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/inc/appdata.hxx b/sfx2/source/inc/appdata.hxx
new file mode 100644
index 000000000..121ba43f5
--- /dev/null
+++ b/sfx2/source/inc/appdata.hxx
@@ -0,0 +1,148 @@
+/* -*- 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_INC_APPDATA_HXX
+#define INCLUDED_SFX2_SOURCE_INC_APPDATA_HXX
+
+#include <config_features.h>
+
+#include <rtl/ref.hxx>
+#include <rtl/ustring.hxx>
+#include <svl/svdde.hxx>
+#include <svtools/ehdl.hxx>
+#include <sfx2/app.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/doctempl.hxx>
+#include <sfx2/fcontnr.hxx>
+#include <sfx2/msgpool.hxx>
+#include <o3tl/enumarray.hxx>
+#include "sfxpicklist.hxx"
+
+#include <bitset.hxx>
+#include <memory>
+#include <vector>
+
+class SfxApplication;
+class SfxProgress;
+class SfxDdeDocTopic_Impl;
+class DdeService;
+class SfxItemPool;
+class SfxFilterMatcher;
+class ISfxTemplateCommon;
+class SfxStatusDispatcher;
+class SfxDdeTriggerTopic_Impl;
+class SfxFrame;
+class SfxViewFrame;
+class SfxInterface;
+class BasicManager;
+class SfxBasicManagerHolder;
+class SfxBasicManagerCreationListener;
+namespace sfx2::sidebar { class Theme; }
+
+
+
+typedef std::vector<SfxDdeDocTopic_Impl*> SfxDdeDocTopics_Impl;
+
+class SfxAppData_Impl
+{
+public:
+ IndexBitSet aIndexBitSet; // for counting noname documents
+ OUString aLastDir; // for IO dialog
+
+ // DDE stuff
+ std::unique_ptr<DdeService> pDdeService;
+ std::unique_ptr<SfxDdeDocTopics_Impl> pDocTopics;
+ std::unique_ptr<SfxDdeTriggerTopic_Impl> pTriggerTopic;
+ std::unique_ptr<DdeService> pDdeService2;
+
+ // single instance classes
+ std::vector<SfxChildWinFactory> maFactories;
+ std::vector<SfxFrame*> vTopFrames;
+
+ // application members
+ std::optional<SfxFilterMatcher> pMatcher;
+ std::optional<SfxErrorHandler> m_pToolsErrorHdl;
+ std::optional<SfxErrorHandler> m_pSoErrorHdl;
+#if HAVE_FEATURE_SCRIPTING
+ std::optional<SfxErrorHandler> m_pSbxErrorHdl;
+#endif
+ rtl::Reference<SfxStatusDispatcher> mxAppDispatch;
+ std::optional<SfxPickList> mxAppPickList;
+ std::optional<SfxDocumentTemplates> pTemplates;
+
+ // global pointers
+ SfxItemPool* pPool;
+
+ // "current" functionality
+ SfxProgress* pProgress;
+
+ sal_uInt16 nDocModalMode; // counts documents in modal mode
+ sal_uInt16 nRescheduleLocks;
+
+ std::vector<SfxTbxCtrlFactory>
+ maTbxCtrlFactories;
+ std::vector<SfxStbCtrlFactory>
+ maStbCtrlFactories;
+ std::vector<SfxViewFrame*> maViewFrames;
+ std::vector<SfxViewShell*> maViewShells;
+ std::vector<SfxObjectShell*>
+ maObjShells;
+ std::unique_ptr<SfxBasicManagerHolder>
+ pBasicManager;
+ std::unique_ptr<SfxBasicManagerCreationListener>
+ pBasMgrListener;
+ SfxViewFrame* pViewFrame;
+ std::optional<SfxSlotPool> pSlotPool;
+ std::optional<SfxDispatcher>
+ pAppDispat; // Dispatcher if no document
+ ::rtl::Reference<sfx2::sidebar::Theme> m_pSidebarTheme;
+
+ bool bDowning:1; // sal_True on Exit and afterwards
+ bool bInQuit : 1;
+
+ SfxAppData_Impl();
+ ~SfxAppData_Impl();
+
+ SfxDocumentTemplates* GetDocumentTemplates();
+ void DeInitDDE();
+
+ o3tl::enumarray<SfxToolsModule, std::unique_ptr<SfxModule>> aModules;
+
+ /** called when the Application's BasicManager has been created. This can happen
+ explicitly in SfxApplication::GetBasicManager, or implicitly if a document's
+ BasicManager is created before the application's BasicManager exists.
+ */
+ void OnApplicationBasicManagerCreated( BasicManager& _rManager );
+};
+
+class SfxDdeTriggerTopic_Impl final : public DdeTopic
+{
+#if defined(_WIN32)
+public:
+ SfxDdeTriggerTopic_Impl()
+ : DdeTopic( "TRIGGER" )
+ {}
+
+ virtual bool Execute( const OUString* ) override { return true; }
+#endif
+};
+
+#endif // INCLUDED_SFX2_SOURCE_INC_APPDATA_HXX
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/inc/appopen.hxx b/sfx2/source/inc/appopen.hxx
new file mode 100644
index 000000000..97a4d7a18
--- /dev/null
+++ b/sfx2/source/inc/appopen.hxx
@@ -0,0 +1,36 @@
+/* -*- 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_INC_APPOPEN_HXX
+#define INCLUDED_SFX2_SOURCE_INC_APPOPEN_HXX
+
+#include <sal/config.h>
+#include <vcl/errcode.hxx>
+
+class SfxItemPool;
+class SfxMedium;
+class SfxObjectShell;
+
+ErrCode CheckPasswd_Impl(SfxObjectShell* pDoc, SfxMedium* pFile);
+
+void SetTemplate_Impl(const OUString&, const OUString&, SfxObjectShell*);
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/inc/asyncfunc.hxx b/sfx2/source/inc/asyncfunc.hxx
new file mode 100644
index 000000000..708750baa
--- /dev/null
+++ b/sfx2/source/inc/asyncfunc.hxx
@@ -0,0 +1,36 @@
+/* -*- 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/.
+ */
+
+#ifndef INCLUDED_SFX2_ASYNCFUNC_HXX
+#define INCLUDED_SFX2_ASYNCFUNC_HXX
+
+#include <functional>
+
+#include <com/sun/star/lang/XUnoTunnel.hpp>
+#include <comphelper/servicehelper.hxx>
+#include <cppuhelper/implbase.hxx>
+
+class AsyncFunc final : public cppu::WeakImplHelper<css::lang::XUnoTunnel>
+{
+private:
+ std::function<void()> m_pAsyncFunc;
+
+public:
+ AsyncFunc(const std::function<void()>&);
+ virtual ~AsyncFunc() override;
+
+ void Execute();
+
+ //XUnoTunnel
+ UNO3_GETIMPLEMENTATION_DECL(AsyncFunc)
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/inc/documentfontsdialog.hxx b/sfx2/source/inc/documentfontsdialog.hxx
new file mode 100644
index 000000000..9ce447669
--- /dev/null
+++ b/sfx2/source/inc/documentfontsdialog.hxx
@@ -0,0 +1,49 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#ifndef INCLUDED_SFX2_SOURCE_INC_DOCUMENTFONTSDIALOG_HXX
+#define INCLUDED_SFX2_SOURCE_INC_DOCUMENTFONTSDIALOG_HXX
+
+#include <sfx2/tabdlg.hxx>
+
+/**
+ Tab page for document font settings in the document properties dialog.
+*/
+class SfxDocumentFontsPage final : public SfxTabPage
+{
+public:
+ SfxDocumentFontsPage(weld::Container* pPage, weld::DialogController* pController,
+ const SfxItemSet& set);
+ virtual ~SfxDocumentFontsPage() override;
+ static std::unique_ptr<SfxTabPage>
+ Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* set);
+
+private:
+ virtual bool FillItemSet(SfxItemSet* set) override;
+ virtual void Reset(const SfxItemSet* set) override;
+
+ std::unique_ptr<weld::CheckButton> embedFontsCheckbox;
+ std::unique_ptr<weld::CheckButton> embedUsedFontsCheckbox;
+ std::unique_ptr<weld::CheckButton> embedLatinScriptFontsCheckbox;
+ std::unique_ptr<weld::CheckButton> embedAsianScriptFontsCheckbox;
+ std::unique_ptr<weld::CheckButton> embedComplexScriptFontsCheckbox;
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/inc/docundomanager.hxx b/sfx2/source/inc/docundomanager.hxx
new file mode 100644
index 000000000..98bf827fb
--- /dev/null
+++ b/sfx2/source/inc/docundomanager.hxx
@@ -0,0 +1,159 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SFX2_SOURCE_INC_DOCUNDOMANAGER_HXX
+#define INCLUDED_SFX2_SOURCE_INC_DOCUNDOMANAGER_HXX
+
+#include <sfx2/sfxbasemodel.hxx>
+
+#include <com/sun/star/document/XUndoManager.hpp>
+
+#include <cppuhelper/implbase.hxx>
+#include <vcl/svapp.hxx>
+
+#include <memory>
+
+/** base class for sub components of an SfxBaseModel, which share their ref count and lifetime with the SfxBaseModel
+*/
+class SfxModelSubComponent
+{
+public:
+ /** checks whether the instance is alive, i.e. properly initialized, and not yet disposed
+ */
+ void MethodEntryCheck()
+ {
+ m_rModel.MethodEntryCheck( true );
+ }
+
+protected:
+ SfxModelSubComponent( SfxBaseModel& i_model )
+ :m_rModel( i_model )
+ {
+ }
+ virtual ~SfxModelSubComponent();
+
+ void acquireModel() { m_rModel.acquire(); }
+ void releaseModel() { m_rModel.release(); }
+
+protected:
+ const SfxBaseModel& getBaseModel() const { return m_rModel; }
+ SfxBaseModel& getBaseModel() { return m_rModel; }
+
+private:
+ SfxBaseModel& m_rModel;
+};
+
+class SfxModelGuard
+{
+public:
+ enum AllowedModelState
+ {
+ // not yet initialized
+ E_INITIALIZING,
+ // fully alive, i.e. initialized, and not yet disposed
+ E_FULLY_ALIVE
+ };
+
+ SfxModelGuard( SfxBaseModel const & i_rModel, const AllowedModelState i_eState = E_FULLY_ALIVE )
+ : m_aGuard()
+ {
+ i_rModel.MethodEntryCheck( i_eState != E_INITIALIZING );
+ }
+ SfxModelGuard( SfxModelSubComponent& i_rSubComponent )
+ :m_aGuard()
+ {
+ i_rSubComponent.MethodEntryCheck();
+ }
+
+ void clear()
+ {
+ m_aGuard.clear();
+ }
+
+private:
+ SolarMutexClearableGuard m_aGuard;
+};
+
+namespace sfx2
+{
+ //= DocumentUndoManager
+
+ struct DocumentUndoManager_Impl;
+ class DocumentUndoManager final : public ::cppu::WeakImplHelper<css::document::XUndoManager>
+ ,public SfxModelSubComponent
+ {
+ friend struct DocumentUndoManager_Impl;
+
+ public:
+ DocumentUndoManager( SfxBaseModel& i_document );
+ virtual ~DocumentUndoManager() override;
+ DocumentUndoManager(const DocumentUndoManager&) = delete;
+ DocumentUndoManager& operator=(const DocumentUndoManager&) = delete;
+
+ void disposing();
+
+ // non-UNO API for our owner
+ /** determines whether we have an open Undo context. No mutex locking within this method, no disposal check - this
+ is the responsibility of the owner.
+ */
+ bool isInContext() const;
+
+ // XInterface
+ virtual void SAL_CALL acquire( ) noexcept override;
+ virtual void SAL_CALL release( ) noexcept override;
+
+ // XUndoManager
+ virtual void SAL_CALL enterUndoContext( const OUString& i_title ) override;
+ virtual void SAL_CALL enterHiddenUndoContext( ) override;
+ virtual void SAL_CALL leaveUndoContext( ) override;
+ virtual void SAL_CALL addUndoAction( const css::uno::Reference< css::document::XUndoAction >& i_action ) override;
+ virtual void SAL_CALL undo( ) override;
+ virtual void SAL_CALL redo( ) override;
+ virtual sal_Bool SAL_CALL isUndoPossible( ) override;
+ virtual sal_Bool SAL_CALL isRedoPossible( ) override;
+ virtual OUString SAL_CALL getCurrentUndoActionTitle( ) override;
+ virtual OUString SAL_CALL getCurrentRedoActionTitle( ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getAllUndoActionTitles( ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getAllRedoActionTitles( ) override;
+ virtual void SAL_CALL clear( ) override;
+ virtual void SAL_CALL clearRedo( ) override;
+ virtual void SAL_CALL reset( ) override;
+ virtual void SAL_CALL addUndoManagerListener( const css::uno::Reference< css::document::XUndoManagerListener >& i_listener ) override;
+ virtual void SAL_CALL removeUndoManagerListener( const css::uno::Reference< css::document::XUndoManagerListener >& i_listener ) override;
+
+ // XLockable, base of XUndoManager
+ virtual void SAL_CALL lock( ) override;
+ virtual void SAL_CALL unlock( ) override;
+ virtual sal_Bool SAL_CALL isLocked( ) override;
+
+ // XChild, base of XUndoManager
+ virtual css::uno::Reference< css::uno::XInterface > SAL_CALL getParent( ) override;
+ virtual void SAL_CALL setParent( const css::uno::Reference< css::uno::XInterface >& Parent ) override;
+
+ private:
+ std::unique_ptr< DocumentUndoManager_Impl > m_pImpl;
+ };
+
+
+} // namespace sfx2
+
+
+#endif // INCLUDED_SFX2_SOURCE_INC_DOCUNDOMANAGER_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/inc/eventsupplier.hxx b/sfx2/source/inc/eventsupplier.hxx
new file mode 100644
index 000000000..56aa8f95e
--- /dev/null
+++ b/sfx2/source/inc/eventsupplier.hxx
@@ -0,0 +1,91 @@
+/* -*- 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_INC_EVENTSUPPLIER_HXX
+#define INCLUDED_SFX2_SOURCE_INC_EVENTSUPPLIER_HXX
+
+#include <sal/types.h>
+
+#include <com/sun/star/document/DocumentEvent.hpp>
+#include <com/sun/star/container/XNameReplace.hpp>
+#include <com/sun/star/document/XDocumentEventListener.hpp>
+#include <com/sun/star/document/XDocumentEventBroadcaster.hpp>
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/uno/Type.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <mutex>
+#include <vector>
+
+namespace comphelper
+{
+ class NamedValueCollection;
+}
+
+class SfxObjectShell;
+class SvxMacro;
+
+
+class SfxEvents_Impl final : public ::cppu::WeakImplHelper< css::container::XNameReplace, css::document::XDocumentEventListener >
+{
+ css::uno::Sequence< OUString > maEventNames;
+ std::vector< css::uno::Sequence < css::beans::PropertyValue > > maEventData;
+ css::uno::Reference< css::document::XDocumentEventBroadcaster > mxBroadcaster;
+ std::mutex maMutex;
+ SfxObjectShell *mpObjShell;
+
+public:
+ SfxEvents_Impl( SfxObjectShell* pShell,
+ css::uno::Reference< css::document::XDocumentEventBroadcaster > const & xBroadcaster );
+ virtual ~SfxEvents_Impl() override;
+
+ // --- XNameReplace ---
+ virtual void SAL_CALL replaceByName( const OUString & aName, const css::uno::Any & aElement ) override;
+
+ // --- XNameAccess ( parent of XNameReplace ) ---
+ virtual css::uno::Any SAL_CALL getByName( const OUString& aName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getElementNames() override;
+ virtual sal_Bool SAL_CALL hasByName( const OUString& aName ) override;
+
+ // --- XElementAccess ( parent of XNameAccess ) ---
+ virtual css::uno::Type SAL_CALL getElementType() override;
+ virtual sal_Bool SAL_CALL hasElements() override;
+
+ // --- ::document::XDocumentEventListener ---
+ virtual void SAL_CALL documentEventOccured(const css::document::DocumentEvent& aEvent) override;
+
+ // --- ::lang::XEventListener ---
+ virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override;
+
+ // convert and normalize
+ static std::unique_ptr<SvxMacro> ConvertToMacro( const css::uno::Any& rElement, SfxObjectShell* pDoc );
+ static void NormalizeMacro( const css::uno::Any& rIn, css::uno::Any& rOut, SfxObjectShell* pDoc );
+ static void NormalizeMacro(
+ const ::comphelper::NamedValueCollection& i_eventDescriptor,
+ ::comphelper::NamedValueCollection& o_normalizedDescriptor,
+ SfxObjectShell* i_document );
+ static void Execute( css::uno::Sequence < css::beans::PropertyValue > const & aEventData, const css::document::DocumentEvent& aTrigger, SfxObjectShell* pDoc );
+
+private:
+ /// Check if script URL whitelist exists, and if so, if current script url is part of it
+ static bool isScriptURLAllowed(const OUString& aScriptURL);
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/inc/fltoptint.hxx b/sfx2/source/inc/fltoptint.hxx
new file mode 100644
index 000000000..ca9537bfe
--- /dev/null
+++ b/sfx2/source/inc/fltoptint.hxx
@@ -0,0 +1,62 @@
+/* -*- 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_INC_FLTOPTINT_HXX
+#define INCLUDED_SFX2_SOURCE_INC_FLTOPTINT_HXX
+
+#include <com/sun/star/document/XInteractionFilterOptions.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+#include <comphelper/interaction.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <rtl/ref.hxx>
+
+class FilterOptionsContinuation final : public comphelper::OInteraction< css::document::XInteractionFilterOptions >
+{
+ css::uno::Sequence< css::beans::PropertyValue > rProperties;
+
+public:
+ virtual void SAL_CALL setFilterOptions( const css::uno::Sequence< css::beans::PropertyValue >& rProp ) override;
+ virtual css::uno::Sequence< css::beans::PropertyValue > SAL_CALL getFilterOptions( ) override;
+};
+
+class RequestFilterOptions final : public ::cppu::WeakImplHelper< css::task::XInteractionRequest >
+{
+ css::uno::Any m_aRequest;
+
+ rtl::Reference<comphelper::OInteractionAbort> m_xAbort;
+ rtl::Reference<FilterOptionsContinuation> m_xOptions;
+
+public:
+ RequestFilterOptions( css::uno::Reference< css::frame::XModel > const & rModel,
+ const css::uno::Sequence< css::beans::PropertyValue >& rProperties );
+
+ bool isAbort() const { return m_xAbort->wasSelected(); }
+
+ css::uno::Sequence< css::beans::PropertyValue > getFilterOptions()
+ {
+ return m_xOptions->getFilterOptions();
+ }
+
+ virtual css::uno::Any SAL_CALL getRequest() override;
+
+ virtual css::uno::Sequence< css::uno::Reference< css::task::XInteractionContinuation >
+ > SAL_CALL getContinuations() override;
+};
+
+#endif
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/inc/helper.hxx b/sfx2/source/inc/helper.hxx
new file mode 100644
index 000000000..b2787a3ad
--- /dev/null
+++ b/sfx2/source/inc/helper.hxx
@@ -0,0 +1,41 @@
+/* -*- 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_INC_HELPER_HXX
+#define INCLUDED_SFX2_SOURCE_INC_HELPER_HXX
+
+#include <rtl/ustring.hxx>
+
+#include <vector>
+
+// class SfxContentHelper ------------------------------------------------
+
+class SfxContentHelper
+{
+public:
+ static std::vector<OUString> GetResultSet(const OUString& rURL);
+ static std::vector<OUString> GetHelpTreeViewContents(const OUString& rURL);
+ static OUString GetActiveHelpString(const OUString& rURL);
+ static bool IsHelpErrorDocument(std::u16string_view rURL);
+
+ static sal_Int64 GetSize(std::u16string_view rContent);
+};
+
+#endif // INCLUDED_SFX2_SOURCE_INC_HELPER_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/inc/helpids.h b/sfx2/source/inc/helpids.h
new file mode 100644
index 000000000..91248f469
--- /dev/null
+++ b/sfx2/source/inc/helpids.h
@@ -0,0 +1,49 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#ifndef INCLUDED_SFX2_SOURCE_INC_HELPIDS_H
+#define INCLUDED_SFX2_SOURCE_INC_HELPIDS_H
+
+#include <rtl/string.hxx>
+
+inline constexpr OStringLiteral HID_TEMPLATE_FMT = "SFX2_HID_TEMPLATE_FMT";
+inline constexpr OStringLiteral HID_TEMPLATE_FILTER = "SFX2_HID_TEMPLATE_FILTER";
+inline constexpr OStringLiteral HID_TEMPLDLG_NEWBYEXAMPLE = "SFX2_HID_TEMPLDLG_NEWBYEXAMPLE";
+inline constexpr OStringLiteral HID_TEMPLDLG_UPDATEBYEXAMPLE = "SFX2_HID_TEMPLDLG_UPDATEBYEXAMPLE";
+inline constexpr OStringLiteral HID_TEMPLDLG_WATERCAN = "SFX2_HID_TEMPLDLG_WATERCAN";
+inline constexpr OStringLiteral HID_NAVIGATOR_WINDOW = "SFX2_HID_NAVIGATOR_WINDOW";
+inline constexpr OStringLiteral HID_TABDLG_RESET_BTN = "SFX2_HID_TABDLG_RESET_BTN";
+inline constexpr OStringLiteral HID_TABDLG_STANDARD_BTN = "SFX2_HID_TABDLG_STANDARD_BTN";
+inline constexpr OStringLiteral HID_TEMPLDLG_TOOLBOX_LEFT = "SFX2_HID_TEMPLDLG_TOOLBOX_LEFT";
+inline constexpr OStringLiteral HID_HELP_WINDOW = "SFX2_HID_HELP_WINDOW";
+inline constexpr OStringLiteral HID_HELP_TOOLBOX = "SFX2_HID_HELP_TOOLBOX";
+inline constexpr OStringLiteral HID_HELP_TOOLBOXITEM_INDEX = "SFX2_HID_HELP_TOOLBOXITEM_INDEX";
+inline constexpr OStringLiteral HID_HELP_TOOLBOXITEM_START = "SFX2_HID_HELP_TOOLBOXITEM_START";
+inline constexpr OStringLiteral HID_HELP_TOOLBOXITEM_BACKWARD = "SFX2_HID_HELP_TOOLBOXITEM_BACKWARD";
+inline constexpr OStringLiteral HID_HELP_TOOLBOXITEM_FORWARD = "SFX2_HID_HELP_TOOLBOXITEM_FORWARD";
+inline constexpr OStringLiteral HID_HELP_TOOLBOXITEM_PRINT = "SFX2_HID_HELP_TOOLBOXITEM_PRINT";
+inline constexpr OStringLiteral HID_HELP_TOOLBOXITEM_BOOKMARKS = "SFX2_HID_HELP_TOOLBOXITEM_BOOKMARKS";
+inline constexpr OStringLiteral HID_HELP_TOOLBOXITEM_SEARCHDIALOG = "SFX2_HID_HELP_TOOLBOXITEM_SEARCHDIALOG";
+
+inline constexpr OStringLiteral HID_QUERY_LOAD_TEMPLATE = "SFX2_HID_QUERY_LOAD_TEMPLATE";
+
+inline constexpr OStringLiteral HID_SIDEBAR_WINDOW = "SFX2_HID_SIDEBAR_WINDOW";
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/inc/hintpost.hxx b/sfx2/source/inc/hintpost.hxx
new file mode 100644
index 000000000..0a72aa661
--- /dev/null
+++ b/sfx2/source/inc/hintpost.hxx
@@ -0,0 +1,59 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#ifndef INCLUDED_SFX2_HINTPOST_HXX
+#define INCLUDED_SFX2_HINTPOST_HXX
+
+#include <tools/link.hxx>
+#include <tools/ref.hxx>
+#include <functional>
+#include <memory>
+
+
+class SfxRequest;
+
+/** [Description]
+
+ This class allows sending unique events via VCL's
+ Application::PostUserEvent(). When the User-Event is dispatched,
+ the handler <Event()> is called, which calls the Link provided with
+ SetEventHdl().
+
+ The instances are held via Ref-Count until a possibly sent
+ event has arrived. If the target dies before delivery,
+ the connection must be severed with SetEventHdl(Link()).
+*/
+class SfxHintPoster final : public SvRefBase
+{
+private:
+ std::function<void (std::unique_ptr<SfxRequest>)> m_Link;
+
+ DECL_LINK( DoEvent_Impl, void*, void );
+
+ virtual ~SfxHintPoster() override;
+
+public:
+ SfxHintPoster(const std::function<void (std::unique_ptr<SfxRequest>)>& rLink);
+
+ void Post( std::unique_ptr<SfxRequest> pHint );
+ void SetEventHdl(const std::function<void (std::unique_ptr<SfxRequest>)>& rLink);
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/inc/itemdel.hxx b/sfx2/source/inc/itemdel.hxx
new file mode 100644
index 000000000..b8ea32501
--- /dev/null
+++ b/sfx2/source/inc/itemdel.hxx
@@ -0,0 +1,30 @@
+/* -*- 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_ITEMDEL_HXX
+#define INCLUDED_SFX2_ITEMDEL_HXX
+
+#include <memory>
+
+class SfxPoolItem;
+
+void DeleteItemOnIdle(std::unique_ptr<SfxPoolItem> pItem);
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/inc/macroloader.hxx b/sfx2/source/inc/macroloader.hxx
new file mode 100644
index 000000000..250a07cd8
--- /dev/null
+++ b/sfx2/source/inc/macroloader.hxx
@@ -0,0 +1,88 @@
+/* -*- 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_INC_MACROLOADER_HXX
+#define INCLUDED_SFX2_SOURCE_INC_MACROLOADER_HXX
+
+#include <sal/config.h>
+
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/frame/DispatchDescriptor.hpp>
+#include <com/sun/star/frame/XDispatch.hpp>
+#include <com/sun/star/frame/XDispatchProvider.hpp>
+#include <com/sun/star/frame/XFrame.hpp>
+#include <com/sun/star/frame/XNotifyingDispatch.hpp>
+#include <com/sun/star/frame/XDispatchResultListener.hpp>
+#include <com/sun/star/frame/XSynchronousDispatch.hpp>
+#include <com/sun/star/util/URL.hpp>
+
+#include <cppuhelper/implbase.hxx>
+#include <cppuhelper/weakref.hxx>
+#include <vcl/errcode.hxx>
+
+class SfxObjectShell;
+
+class SfxMacroLoader final : public cppu::WeakImplHelper<
+ css::frame::XDispatchProvider,
+ css::frame::XNotifyingDispatch,
+ css::frame::XSynchronousDispatch,
+ css::lang::XServiceInfo>
+{
+ css::uno::WeakReference < css::frame::XFrame > m_xFrame;
+ SfxObjectShell* GetObjectShell_Impl();
+
+public:
+ /// @throws css::uno::Exception
+ /// @throws css::uno::RuntimeException
+ SfxMacroLoader(const css::uno::Sequence< css::uno::Any >& aArguments);
+
+ virtual OUString SAL_CALL getImplementationName() override;
+
+ virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override;
+
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+
+ /// @throws css::uno::RuntimeException
+ /// @throws css::ucb::ContentCreationException
+ static ErrCode loadMacro( const OUString& aURL, css::uno::Any& rRetval, SfxObjectShell* pDoc );
+
+ virtual css::uno::Reference < css::frame::XDispatch > SAL_CALL queryDispatch(
+ const css::util::URL& aURL, const OUString& sTargetFrameName,
+ sal_Int32 eSearchFlags ) override;
+
+ virtual css::uno::Sequence< css::uno::Reference < css::frame::XDispatch > > SAL_CALL queryDispatches(
+ const css::uno::Sequence < css::frame::DispatchDescriptor >& seqDescriptor ) override;
+
+ virtual void SAL_CALL dispatchWithNotification( const css::util::URL& aURL, const css::uno::Sequence< css::beans::PropertyValue >& lArgs, const css::uno::Reference< css::frame::XDispatchResultListener >& Listener ) override;
+
+ virtual void SAL_CALL dispatch( const css::util::URL& aURL, const css::uno::Sequence< css::beans::PropertyValue >& lArgs ) override;
+
+ virtual css::uno::Any SAL_CALL dispatchWithReturnValue( const css::util::URL& aURL, const css::uno::Sequence< css::beans::PropertyValue >& lArgs ) override;
+
+ virtual void SAL_CALL addStatusListener( const css::uno::Reference< css::frame::XStatusListener >& xControl, const css::util::URL& aURL ) override;
+
+ virtual void SAL_CALL removeStatusListener( const css::uno::Reference< css::frame::XStatusListener >& xControl, const css::util::URL& aURL ) override;
+
+ static SfxObjectShell* GetObjectShell(const css::uno::Reference<css::frame::XFrame>& xFrame);
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/inc/nochaos.hxx b/sfx2/source/inc/nochaos.hxx
new file mode 100644
index 000000000..4baee35fc
--- /dev/null
+++ b/sfx2/source/inc/nochaos.hxx
@@ -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 .
+ */
+#ifndef INCLUDED_SFX2_SOURCE_INC_NOCHAOS_HXX
+#define INCLUDED_SFX2_SOURCE_INC_NOCHAOS_HXX
+
+#include <sal/types.h>
+
+class SfxItemPool;
+
+class NoChaos
+{
+public:
+ static SfxItemPool* GetItemPool();
+ static sal_uInt16 ReleaseItemPool();
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/inc/objshimp.hxx b/sfx2/source/inc/objshimp.hxx
new file mode 100644
index 000000000..46db15898
--- /dev/null
+++ b/sfx2/source/inc/objshimp.hxx
@@ -0,0 +1,153 @@
+/* -*- 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_INC_OBJSHIMP_HXX
+#define INCLUDED_SFX2_SOURCE_INC_OBJSHIMP_HXX
+
+#include <com/sun/star/uno/Sequence.hxx>
+#include <rtl/ustring.hxx>
+#include <rtl/ref.hxx>
+#include <tools/datetime.hxx>
+
+#include <sfx2/objsh.hxx>
+#include <sfx2/docmacromode.hxx>
+#include <bitset.hxx>
+#include <vcl/timer.hxx>
+
+#include "appbaslib.hxx"
+
+namespace svtools { class AsynchronLink; }
+
+class SfxViewFrame;
+
+class SfxBasicManagerHolder;
+
+class AutoReloadTimer_Impl final : public Timer
+{
+ OUString aUrl;
+ SfxObjectShell* pObjSh;
+
+public:
+ AutoReloadTimer_Impl( const OUString& rURL, sal_uInt32 nTime,
+ SfxObjectShell* pSh );
+ virtual void Invoke() override;
+};
+
+struct SfxObjectShell_Impl final : public ::sfx2::IMacroDocumentAccess
+{
+ std::unique_ptr<::comphelper::EmbeddedObjectContainer> mxObjectContainer;
+ SfxBasicManagerHolder aBasicManager;
+ SfxObjectShell& rDocShell;
+ css::uno::Reference< css::script::XLibraryContainer >
+ xBasicLibraries;
+ css::uno::Reference< css::script::XLibraryContainer >
+ xDialogLibraries;
+ ::sfx2::DocumentMacroMode
+ aMacroMode;
+ SfxProgress* pProgress;
+ OUString aTitle;
+ OUString aTempName;
+ DateTime nTime;
+ sal_uInt16 nVisualDocumentNumber;
+ SignatureState nDocumentSignatureState;
+ SignatureState nScriptingSignatureState;
+ bool bClosing:1, // sal_True while Close(), to prevent recurrences Notification
+ bIsSaving:1,
+ bIsNamedVisible:1,
+ bIsAbortingImport:1, // Import operation should be canceled.
+ bInPrepareClose : 1,
+ bPreparedForClose : 1,
+ bForbidReload : 1,
+ bBasicInitialized :1,
+ bIsPrintJobCancelable :1, // Stampit disable/enable cancel button for print jobs ... default = true = enable!
+ bOwnsStorage:1,
+ bInitialized:1,
+ bModelInitialized:1, // whether the related model is initialized
+ bPreserveVersions:1,
+ m_bMacroSignBroken:1, // whether the macro signature was explicitly broken
+ m_bNoBasicCapabilities:1,
+ m_bDocRecoverySupport:1,
+ bQueryLoadTemplate:1,
+ bLoadReadonly:1,
+ bUseUserData:1,
+ bUseThumbnailSave:1,
+ bSaveVersionOnClose:1,
+ m_bSharedXMLFlag:1, // whether the document should be edited in shared mode
+ m_bAllowShareControlFileClean:1, // whether the flag should be stored in xml file
+ m_bConfigOptionsChecked:1, // whether or not the user options are checked after the Options dialog is closed.
+ m_bMacroCallsSeenWhileLoading:1; // whether or not the user options are checked after the Options dialog is closed.
+
+ IndexBitSet aBitSet;
+ ErrCode lErr;
+ SfxEventHintId nEventId; // If Open/Create as to be sent
+ // before Activate
+ std::unique_ptr<AutoReloadTimer_Impl> pReloadTimer;
+ SfxLoadedFlags nLoadedFlags;
+ SfxLoadedFlags nFlagsInProgress;
+ bool bModalMode;
+ bool bRunningMacro;
+ bool bReadOnlyUI;
+ tools::SvRef<SvRefBase> xHeaderAttributes;
+ ::rtl::Reference< SfxBaseModel >
+ pBaseModel;
+ sal_uInt16 nStyleFilter;
+
+ bool m_bEnableSetModified;
+ bool m_bIsModified;
+
+ tools::Rectangle m_aVisArea;
+ MapUnit m_nMapUnit;
+
+ bool m_bCreateTempStor;
+ css::uno::Reference< css::embed::XStorage > m_xDocStorage;
+
+ bool m_bIsInit;
+
+ OUString m_aSharedFileURL;
+
+ bool m_bIncomplEncrWarnShown;
+
+ // TODO/LATER: m_aModifyPasswordInfo should completely replace m_nModifyPasswordHash in future
+ sal_uInt32 m_nModifyPasswordHash;
+ css::uno::Sequence< css::beans::PropertyValue > m_aModifyPasswordInfo;
+ bool m_bModifyPasswordEntered;
+ /// If true, then this is not a real save, just the signatures change.
+ bool m_bSavingForSigning;
+ bool m_bAllowModifiedBackAfterSigning;
+
+ /// Holds Infobars until View is fully loaded
+ std::vector<InfobarData> m_aPendingInfobars;
+
+ SfxObjectShell_Impl( SfxObjectShell& _rDocShell );
+ virtual ~SfxObjectShell_Impl();
+
+ // IMacroDocumentAccess overridables
+ virtual sal_Int16 getCurrentMacroExecMode() const override;
+ virtual void setCurrentMacroExecMode( sal_uInt16 nMacroMode ) override;
+ virtual OUString getDocumentLocation() const override;
+ virtual bool documentStorageHasMacros() const override;
+ virtual bool macroCallsSeenWhileLoading() const override;
+ virtual css::uno::Reference< css::document::XEmbeddedScripts > getEmbeddedDocumentScripts() const override;
+ virtual SignatureState getScriptingSignatureState() override;
+
+ virtual bool hasTrustedScriptingSignature( bool bAllowUIToAddAuthor ) override;
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/inc/openflag.hxx b/sfx2/source/inc/openflag.hxx
new file mode 100644
index 000000000..11421d8cf
--- /dev/null
+++ b/sfx2/source/inc/openflag.hxx
@@ -0,0 +1,33 @@
+/* -*- 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_INC_OPENFLAG_HXX
+#define INCLUDED_SFX2_SOURCE_INC_OPENFLAG_HXX
+
+// Open file for editing, then only the third option (reading a copy) works
+#define SFX_STREAM_READWRITE (StreamMode::READWRITE | StreamMode::SHARE_DENYWRITE)
+// I work on the original, not a copy
+// -> file then can not be opened for editing
+#define SFX_STREAM_READONLY (StreamMode::READ | StreamMode::SHARE_DENYWRITE) // + !bDirect
+// Someone else is editing the file, a copy it created
+// -> the file can then be opened for editing
+#define SFX_STREAM_READONLY_MAKECOPY (StreamMode::READ | StreamMode::SHARE_DENYNONE)
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/inc/openuriexternally.hxx b/sfx2/source/inc/openuriexternally.hxx
new file mode 100644
index 000000000..cf78d1b71
--- /dev/null
+++ b/sfx2/source/inc/openuriexternally.hxx
@@ -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/.
+ */
+
+#ifndef INCLUDED_SFX2_SOURCE_INC_OPENURIEXTERNALLY_HXX
+#define INCLUDED_SFX2_SOURCE_INC_OPENURIEXTERNALLY_HXX
+
+#include <sal/config.h>
+
+#include <rtl/ustring.hxx>
+
+namespace weld
+{
+class Widget;
+}
+
+namespace sfx2
+{
+/** Open a URI via com.sun.star.system.SystemShellExecute
+
+ Handles XSystemShellExecute.execute's IllegalArgumentException (throwing a
+ RuntimeException if it is unexpected, i.e., not caused by the given sURI not
+ being an absolute URI reference).
+
+ Handles XSystemShellExecute.execute's SystemShellExecuteException unless the
+ given bHandleSystemShellExecuteException is false (in which case the
+ exception is re-thrown).
+*/
+void openUriExternally(const OUString& sURI, bool bHandleSystemShellExecuteException,
+ weld::Widget* pDialogParent);
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/inc/openurlhint.hxx b/sfx2/source/inc/openurlhint.hxx
new file mode 100644
index 000000000..ea52a44e4
--- /dev/null
+++ b/sfx2/source/inc/openurlhint.hxx
@@ -0,0 +1,38 @@
+/* -*- 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_STRINGHINT_HXX
+#define INCLUDED_SFX2_STRINGHINT_HXX
+
+#include <svl/hint.hxx>
+#include <rtl/ustring.hxx>
+
+class SfxOpenUrlHint final : public SfxHint
+{
+ OUString msDocumentURL;
+
+public:
+ SfxOpenUrlHint(const OUString& sDocumentURL);
+ const OUString& GetDocumentURL() const;
+ virtual ~SfxOpenUrlHint() override;
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/inc/partwnd.hxx b/sfx2/source/inc/partwnd.hxx
new file mode 100644
index 000000000..1d51329f6
--- /dev/null
+++ b/sfx2/source/inc/partwnd.hxx
@@ -0,0 +1,63 @@
+/* -*- 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_INC_PARTWND_HXX
+#define INCLUDED_SFX2_SOURCE_INC_PARTWND_HXX
+
+#include <sfx2/childwin.hxx>
+#include <sfx2/dockwin.hxx>
+
+
+namespace com::sun::star::frame { class XFrame; }
+
+// forward ---------------------------------------------------------------
+
+// class SfxPartChildWnd_Impl -----------------------------------
+
+class SfxPartChildWnd_Impl final : public SfxChildWindow
+{
+public:
+ SfxPartChildWnd_Impl( vcl::Window* pParent, sal_uInt16 nId,
+ SfxBindings* pBindings,
+ SfxChildWinInfo* pInfo );
+
+ SFX_DECL_CHILDWINDOW(SfxPartChildWnd_Impl);
+ virtual ~SfxPartChildWnd_Impl() override;
+
+ virtual bool QueryClose() override;
+};
+
+// class SfxExplorerDockWnd_Impl -----------------------------------------
+
+class SfxPartDockWnd_Impl final : public SfxDockingWindow
+{
+ virtual bool EventNotify( NotifyEvent& rNEvt ) override;
+
+public:
+ SfxPartDockWnd_Impl( SfxBindings* pBindings,
+ SfxChildWindow* pChildWin,
+ vcl::Window* pParent,
+ WinBits nBits );
+
+ bool QueryClose();
+ virtual void FillInfo(SfxChildWinInfo&) const override;
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/inc/preview.hxx b/sfx2/source/inc/preview.hxx
new file mode 100644
index 000000000..b1a5621ab
--- /dev/null
+++ b/sfx2/source/inc/preview.hxx
@@ -0,0 +1,41 @@
+/* -*- 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_INC_PREVIEW_HXX
+#define INCLUDED_SFX2_SOURCE_INC_PREVIEW_HXX
+
+#include <vcl/customweld.hxx>
+
+class SfxObjectShell;
+class GDIMetaFile;
+
+class SfxPreviewWin_Impl final : public weld::CustomWidgetController
+{
+private:
+ virtual void Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&) override;
+ std::shared_ptr<GDIMetaFile> xMetaFile;
+
+public:
+ SfxPreviewWin_Impl();
+ void SetObjectShell(SfxObjectShell const* pObj);
+ static void ImpPaint(vcl::RenderContext& rRenderContext, GDIMetaFile* pFile);
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/inc/recfloat.hxx b/sfx2/source/inc/recfloat.hxx
new file mode 100644
index 000000000..e5720e155
--- /dev/null
+++ b/sfx2/source/inc/recfloat.hxx
@@ -0,0 +1,61 @@
+/* -*- 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_INC_RECFLOAT_HXX
+#define INCLUDED_SFX2_SOURCE_INC_RECFLOAT_HXX
+
+#include <sfx2/basedlgs.hxx>
+#include <sfx2/childwin.hxx>
+#include <sfx2/weldutils.hxx>
+
+class SfxRecordingFloatWrapper_Impl final : public SfxChildWindow
+{
+ SfxBindings* pBindings;
+public:
+ SfxRecordingFloatWrapper_Impl( vcl::Window* pParent ,
+ sal_uInt16 nId ,
+ SfxBindings* pBindings ,
+ SfxChildWinInfo const * pInfo );
+ virtual ~SfxRecordingFloatWrapper_Impl() override;
+
+ SFX_DECL_CHILDWINDOW(SfxRecordingFloatWrapper_Impl);
+ virtual bool QueryClose() override;
+};
+
+class SfxRecordingFloat_Impl final : public SfxModelessDialogController
+{
+ std::unique_ptr<weld::Toolbar> m_xToolbar;
+ std::unique_ptr<ToolbarUnoDispatcher> m_xDispatcher;
+ ImplSVEvent *mnPostUserEventId;
+ bool m_bFirstActivate;
+
+ DECL_LINK(PresentParentFrame, void*, void);
+
+public:
+ SfxRecordingFloat_Impl(SfxBindings* pBindings,
+ SfxChildWindow* pChildWin,
+ weld::Window* pParent);
+ virtual ~SfxRecordingFloat_Impl() override;
+ virtual void FillInfo(SfxChildWinInfo& rInfo) const override;
+ virtual void Activate() override;
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/inc/sfxpicklist.hxx b/sfx2/source/inc/sfxpicklist.hxx
new file mode 100644
index 000000000..b8af2310d
--- /dev/null
+++ b/sfx2/source/inc/sfxpicklist.hxx
@@ -0,0 +1,42 @@
+/* -*- 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_INC_SFXPICKLIST_HXX
+#define INCLUDED_SFX2_SOURCE_INC_SFXPICKLIST_HXX
+
+#include <memory>
+
+#define PICKLIST_MAXSIZE 100
+
+class SfxApplication;
+class SfxPickListImpl;
+
+class SfxPickList
+{
+private:
+ std::unique_ptr<SfxPickListImpl> mxImpl;
+
+public:
+ SfxPickList(SfxApplication& rApp);
+ ~SfxPickList();
+};
+
+#endif // INCLUDED_SFX2_SOURCE_INC_SFXPICKLIST_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/inc/sfxtypes.hxx b/sfx2/source/inc/sfxtypes.hxx
new file mode 100644
index 000000000..acc17ff9a
--- /dev/null
+++ b/sfx2/source/inc/sfxtypes.hxx
@@ -0,0 +1,51 @@
+/* -*- 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_INC_SFXTYPES_HXX
+#define INCLUDED_SFX2_SOURCE_INC_SFXTYPES_HXX
+
+#include <sal/config.h>
+#include <sal/log.hxx>
+
+#if defined(DBG_UTIL)
+
+class SfxStack
+{
+ static unsigned nLevel;
+
+public:
+ SfxStack(const char* pName)
+ {
+ ++nLevel;
+ SAL_INFO("sfx.control", "STACK: enter " << nLevel << " " << pName);
+ }
+ ~SfxStack()
+ {
+ SAL_INFO("sfx.control", "STACK: leave " << nLevel);
+ --nLevel;
+ }
+};
+
+#define SFX_STACK(s) SfxStack aSfxStack_(#s)
+#else
+#define SFX_STACK(s)
+#endif
+
+#endif // INCLUDED_SFX2_SOURCE_INC_SFXTYPES_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/inc/sfxurlrelocator.hxx b/sfx2/source/inc/sfxurlrelocator.hxx
new file mode 100644
index 000000000..3a1fa7729
--- /dev/null
+++ b/sfx2/source/inc/sfxurlrelocator.hxx
@@ -0,0 +1,52 @@
+/* -*- 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_INC_SFXURLRELOCATOR_HXX
+#define INCLUDED_SFX2_SOURCE_INC_SFXURLRELOCATOR_HXX
+
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/util/XOfficeInstallationDirectories.hpp>
+#include <com/sun/star/util/XMacroExpander.hpp>
+
+#include <rtl/ustring.hxx>
+#include <mutex>
+
+class SfxURLRelocator_Impl
+{
+ std::mutex maMutex;
+ css::uno::Reference< css::uno::XComponentContext > mxContext;
+ css::uno::Reference< css::util::XOfficeInstallationDirectories > mxOfficeInstDirs;
+ css::uno::Reference< css::util::XMacroExpander > mxMacroExpander;
+
+public:
+ static bool propertyCanContainOfficeDir( std::u16string_view rPropName );
+ void initOfficeInstDirs();
+ void makeRelocatableURL( OUString & rURL );
+ void makeAbsoluteURL( OUString & rURL );
+
+ SfxURLRelocator_Impl( const css::uno::Reference< css::uno::XComponentContext > & xContext );
+ ~SfxURLRelocator_Impl();
+
+private:
+ void implExpandURL( OUString& io_url );
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/inc/slotserv.hxx b/sfx2/source/inc/slotserv.hxx
new file mode 100644
index 000000000..e5bd07dce
--- /dev/null
+++ b/sfx2/source/inc/slotserv.hxx
@@ -0,0 +1,59 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#ifndef INCLUDED_SFX2_SOURCE_INC_SLOTSERV_HXX
+#define INCLUDED_SFX2_SOURCE_INC_SLOTSERV_HXX
+
+#include <sal/types.h>
+
+class SfxSlot;
+
+class SfxSlotServer
+{
+private:
+ const SfxSlot* _pSlot;
+ sal_uInt16 _nShellLevel;
+
+public:
+ SfxSlotServer();
+
+ sal_uInt16 GetShellLevel() const;
+ void SetShellLevel(sal_uInt16 nLevel) { _nShellLevel = nLevel; }
+ void SetSlot(const SfxSlot* pSlot) { _pSlot = pSlot; }
+ const SfxSlot* GetSlot() const;
+};
+
+inline SfxSlotServer::SfxSlotServer():
+ _pSlot(nullptr),
+ _nShellLevel(0)
+{
+}
+
+inline sal_uInt16 SfxSlotServer::GetShellLevel() const
+{
+ return _nShellLevel;
+}
+
+inline const SfxSlot* SfxSlotServer::GetSlot() const
+{
+ return _pSlot;
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/inc/splitwin.hxx b/sfx2/source/inc/splitwin.hxx
new file mode 100644
index 000000000..4b65830b9
--- /dev/null
+++ b/sfx2/source/inc/splitwin.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_INC_SPLITWIN_HXX
+#define INCLUDED_SFX2_SOURCE_INC_SPLITWIN_HXX
+
+#include <vcl/splitwin.hxx>
+#include <sfx2/childwin.hxx>
+#include <sfx2/dockwin.hxx>
+
+#include <vector>
+#include <memory>
+
+class SfxWorkWindow;
+class SfxDockingWindow;
+class SfxEmptySplitWin_Impl;
+
+struct SfxDock_Impl
+{
+ sal_uInt16 nType;
+ VclPtr<SfxDockingWindow> pWin; // SplitWindow has this window
+ bool bNewLine;
+ bool bHide; // SplitWindow had this window
+};
+
+class SfxSplitWindow final : public SplitWindow
+{
+friend class SfxEmptySplitWin_Impl;
+
+private:
+ SfxChildAlignment eAlign;
+ SfxWorkWindow* pWorkWin;
+ std::vector<std::unique_ptr<SfxDock_Impl> >
+ maDockArr;
+ bool bPinned;
+ VclPtr<SfxEmptySplitWin_Impl> pEmptyWin;
+ VclPtr<SfxDockingWindow> pActive;
+
+ void InsertWindow_Impl( SfxDock_Impl const * pDockWin,
+ const Size& rSize,
+ sal_uInt16 nLine,
+ sal_uInt16 nPos,
+ bool bNewLine );
+
+ DECL_LINK( TimerHdl, Timer*, void );
+ bool CursorIsOverRect() const;
+ void SetPinned_Impl( bool );
+ void SetFadeIn_Impl( bool );
+ void SaveConfig_Impl();
+ void FadeOut_Impl();
+
+ virtual void StartSplit() override;
+ virtual void SplitResize() override;
+ virtual void Split() override;
+ virtual void MouseButtonDown ( const MouseEvent& ) override;
+
+public:
+ SfxSplitWindow( vcl::Window* pParent, SfxChildAlignment eAl,
+ SfxWorkWindow *pW, bool bWithButtons );
+
+ virtual ~SfxSplitWindow() override;
+ virtual void dispose() override;
+
+ void ReleaseWindow_Impl(SfxDockingWindow const *pWin, bool bSaveConfig=true);
+
+ void InsertWindow( SfxDockingWindow* pDockWin,
+ const Size& rSize);
+
+ void InsertWindow( SfxDockingWindow* pDockWin,
+ const Size& rSize,
+ sal_uInt16 nLine,
+ sal_uInt16 nPos,
+ bool bNewLine );
+
+ void MoveWindow( SfxDockingWindow* pDockWin,
+ const Size& rSize,
+ sal_uInt16 nLine,
+ sal_uInt16 nPos,
+ bool bNewLine );
+
+ void RemoveWindow( SfxDockingWindow const * pDockWin, bool bHide=true);
+
+ void Lock( bool bLock=true )
+ {
+ SetUpdateMode( !bLock );
+ }
+
+ bool GetWindowPos( const SfxDockingWindow* pWindow,
+ sal_uInt16& rLine, sal_uInt16& rPos ) const;
+ bool GetWindowPos( const Point& rTestPos,
+ sal_uInt16& rLine, sal_uInt16& rPos ) const;
+ sal_uInt16 GetLineCount() const;
+ tools::Long GetLineSize( sal_uInt16 ) const;
+ sal_uInt16 GetWindowCount(sal_uInt16 nLine) const;
+ sal_uInt16 GetWindowCount() const;
+
+ bool IsPinned() const { return bPinned; }
+ bool IsFadeIn() const;
+ bool IsAutoHide( bool bSelf ) const;
+ SplitWindow* GetSplitWindow();
+
+ virtual void FadeOut() override;
+ virtual void FadeIn() override;
+ void SetActiveWindow_Impl( SfxDockingWindow* pWin );
+};
+
+#endif // INCLUDED_SFX2_SOURCE_INC_SPLITWIN_HXX
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/inc/statcach.hxx b/sfx2/source/inc/statcach.hxx
new file mode 100644
index 000000000..4247037e2
--- /dev/null
+++ b/sfx2/source/inc/statcach.hxx
@@ -0,0 +1,154 @@
+/* -*- 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_INC_STATCACH_HXX
+#define INCLUDED_SFX2_SOURCE_INC_STATCACH_HXX
+
+#include <com/sun/star/frame/XDispatch.hpp>
+#include <com/sun/star/frame/XDispatchProvider.hpp>
+#include <com/sun/star/frame/XStatusListener.hpp>
+#include <com/sun/star/frame/FeatureStateEvent.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <cppuhelper/implbase.hxx>
+#include <tools/debug.hxx>
+#include <rtl/ref.hxx>
+
+#include <sfx2/bindings.hxx>
+
+#include "slotserv.hxx"
+
+class SfxControllerItem;
+class SfxDispatcher;
+class BindDispatch_Impl final : public ::cppu::WeakImplHelper< css::frame::XStatusListener >
+{
+friend class SfxStateCache;
+ css::uno::Reference< css::frame::XDispatch > xDisp;
+ css::util::URL aURL;
+ css::frame::FeatureStateEvent aStatus;
+ SfxStateCache* pCache;
+ const SfxSlot* pSlot;
+
+public:
+ BindDispatch_Impl(
+ const css::uno::Reference< css::frame::XDispatch > & rDisp,
+ const css::util::URL& rURL,
+ SfxStateCache* pStateCache, const SfxSlot* pSlot );
+
+ virtual void SAL_CALL statusChanged( const css::frame::FeatureStateEvent& Event ) override;
+ virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override;
+
+ const css::frame::FeatureStateEvent& GetStatus() const { return aStatus;}
+ sal_Int16 Dispatch( const css::uno::Sequence < css::beans::PropertyValue >& aProps, bool bForceSynchron );
+ void Release();
+};
+
+class SfxStateCache
+{
+friend class BindDispatch_Impl;
+ rtl::Reference<BindDispatch_Impl>
+ mxDispatch;
+ sal_uInt16 nId; // Slot-Id
+ SfxControllerItem* pInternalController;
+ css::uno::Reference < css::frame::XDispatch > xMyDispatch;
+ SfxControllerItem* pController; // Pointer to first bound Controller (interlinked with each other)
+ SfxSlotServer aSlotServ; // SlotServer, SlotPtr = 0 -> not on Stack
+ SfxPoolItem* pLastItem; // Last sent Item, never -1
+ SfxItemState eLastState; // Last sent State
+ bool bCtrlDirty:1; // Update Controller?
+ bool bSlotDirty:1; // Present Function, must be updated
+ bool bItemVisible:1; // item visibility
+ bool bItemDirty; // Validity of pLastItem
+
+private:
+ SfxStateCache( const SfxStateCache& rOrig ) = delete;
+ void SetState_Impl( SfxItemState, const SfxPoolItem*, bool bMaybeDirty );
+
+public:
+ SfxStateCache( sal_uInt16 nFuncId );
+ ~SfxStateCache();
+
+ sal_uInt16 GetId() const;
+
+ const SfxSlotServer* GetSlotServer( SfxDispatcher &rDispat, const css::uno::Reference< css::frame::XDispatchProvider > & xProv );
+ const SfxSlotServer* GetSlotServer( SfxDispatcher &rDispat )
+ { return GetSlotServer( rDispat, css::uno::Reference< css::frame::XDispatchProvider > () ); }
+ css::uno::Reference< css::frame::XDispatch > GetDispatch() const;
+ sal_Int16 Dispatch( const SfxItemSet* pSet, bool bForceSynchron );
+ bool IsControllerDirty() const
+ { return bCtrlDirty; }
+ void ClearCache();
+
+ void SetState( SfxItemState, const SfxPoolItem*, bool bMaybeDirty=false );
+ void SetCachedState(bool bAlways);
+ void Invalidate( bool bWithSlot );
+ void SetVisibleState( bool bShow );
+ void GetState( boost::property_tree::ptree& );
+
+ SfxControllerItem* ChangeItemLink( SfxControllerItem* pNewBinding );
+ SfxControllerItem* GetItemLink() const;
+ void SetInternalController( SfxControllerItem* pCtrl )
+ { DBG_ASSERT( !pInternalController, "Only one internal controller allowed!" ); pInternalController = pCtrl; }
+ void ReleaseInternalController() { pInternalController = nullptr; }
+ SfxControllerItem* GetInternalController() const { return pInternalController; }
+ const css::uno::Reference < css::frame::XDispatch >&
+ GetInternalDispatch() const
+ { return xMyDispatch; }
+ void SetInternalDispatch( const css::uno::Reference < css::frame::XDispatch >& rDisp )
+ { xMyDispatch = rDisp; }
+};
+
+
+// clears Cached-Item
+
+inline void SfxStateCache::ClearCache()
+{
+ bItemDirty = true;
+}
+
+
+// registers an item representing this function
+
+inline SfxControllerItem* SfxStateCache::ChangeItemLink( SfxControllerItem* pNewBinding )
+{
+ SfxControllerItem* pOldBinding = pController;
+ pController = pNewBinding;
+ if ( pNewBinding )
+ {
+ bCtrlDirty = true;
+ bItemDirty = true;
+ }
+ return pOldBinding;
+}
+
+
+// returns the func binding which becomes called on spreading states
+
+inline SfxControllerItem* SfxStateCache::GetItemLink() const
+{
+ return pController;
+}
+
+
+inline sal_uInt16 SfxStateCache::GetId() const
+{
+ return nId;
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/inc/templatesearchviewitem.hxx b/sfx2/source/inc/templatesearchviewitem.hxx
new file mode 100644
index 000000000..e9ba57f1c
--- /dev/null
+++ b/sfx2/source/inc/templatesearchviewitem.hxx
@@ -0,0 +1,28 @@
+/* -*- 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/.
+ */
+
+#ifndef INCLUDED_SFX2_SOURCE_INC_TEMPLATESEARCHVIEWITEM_HXX
+#define INCLUDED_SFX2_SOURCE_INC_TEMPLATESEARCHVIEWITEM_HXX
+
+#include <templateviewitem.hxx>
+
+struct TemplateSearchViewItem final : public TemplateViewItem
+{
+ TemplateSearchViewItem(ThumbnailView& rView, sal_uInt16 nId)
+ : TemplateViewItem(rView, nId)
+ , mnAssocId(0)
+ {
+ }
+
+ sal_uInt16 mnAssocId; //Associated item id to the TemplateViews
+};
+
+#endif // INCLUDED_SFX2_SOURCE_INC_TEMPLATESEARCHVIEWITEM_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/inc/templdgi.hxx b/sfx2/source/inc/templdgi.hxx
new file mode 100644
index 000000000..6c72de1ca
--- /dev/null
+++ b/sfx2/source/inc/templdgi.hxx
@@ -0,0 +1,242 @@
+/* -*- 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_INC_TEMPLDGI_HXX
+#define INCLUDED_SFX2_SOURCE_INC_TEMPLDGI_HXX
+
+#include <sal/config.h>
+
+#include <memory>
+
+#include <vcl/transfer.hxx>
+#include <vcl/weld.hxx>
+#include <svl/eitem.hxx>
+
+#include <svl/style.hxx>
+
+#include <sfx2/objsh.hxx>
+#include <sfx2/styfitem.hxx>
+#include <sfx2/templdlg.hxx>
+
+#include "StyleList.hxx"
+
+class SfxTemplateControllerItem;
+class SfxStyleFamilyItem;
+class SfxTemplateItem;
+class SfxBindings;
+class SfxStyleSheetBase;
+class SfxStyleSheetBasePool;
+class StyleTreeListBox_Impl;
+class SfxTemplateDialog_Impl;
+class SfxCommonTemplateDialog_Impl;
+namespace com::sun::star::frame {
+ class XModuleManager2;
+}
+
+class SfxCommonTemplateDialog_Impl
+{
+private:
+ class DeletionWatcher;
+ friend class DeletionWatcher;
+
+ DeletionWatcher* impl_setDeletionWatcher(DeletionWatcher* pNewWatcher);
+
+protected:
+#define MAX_FAMILIES 6
+#define COUNT_BOUND_FUNC 14
+
+ friend class SfxTemplateControllerItem;
+
+ SfxBindings* pBindings;
+
+ weld::Container* mpContainer;
+
+ css::uno::Reference<css::frame::XModuleManager2> xModuleManager;
+ DeletionWatcher* m_pDeletionWatcher;
+
+ StyleList m_aStyleList;
+ std::unique_ptr<weld::CheckButton> mxPreviewCheckbox;
+ std::unique_ptr<weld::ComboBox> mxFilterLb;
+
+ sal_uInt16 nActFamily; // Id in the ToolBox = Position - 1
+ sal_uInt16 nActFilter; // FilterIdx
+
+ bool bIsWater :1;
+ bool bUpdate :1;
+ bool bWaterDisabled :1;
+ bool bNewByExampleDisabled :1;
+ bool bUpdateByExampleDisabled :1;
+ bool m_bWantHierarchical :1;
+
+ Link<void*, size_t> m_aStyleListReadResource;
+ Link<void*, void> m_aStyleListClear;
+ Link<void*, void> m_aStyleListCleanup;
+ Link<const ExecuteDropEvent&, sal_Int8> m_aStyleListExecuteDrop;
+ Link<void*, void> m_aStyleListNewMenu;
+ Link<void*, bool> m_aStyleListWaterCan;
+ Link<void*, bool> m_aStyleListHasSelectedStyle;
+ Link<void*, void> m_aStyleListUpdateFamily;
+ Link<void*, void> m_aStyleListUpdateStyleDependents;
+ Link<bool, void> m_aStyleListEnableTreeDrag;
+ Link<void*, void> m_aStyleListEnableDelete;
+ Link<const SfxBoolItem*, void> m_aStyleListSetWaterCanState;
+ Link<sal_uInt16, void> m_aStyleListSetFamily;
+
+ DECL_LINK(FilterSelectHdl, weld::ComboBox&, void );
+ DECL_LINK(PreviewHdl, weld::Toggleable&, void);
+
+ virtual void InsertFamilyItem(sal_uInt16 nId, const SfxStyleFamilyItem& rItem) = 0;
+ virtual void EnableFamilyItem(sal_uInt16 nId, bool bEnabled) = 0;
+ virtual void ClearFamilyList() = 0;
+ virtual void ReplaceUpdateButtonByMenu();
+
+ void Initialize();
+ void EnableHierarchical(bool, StyleList& rStyleList);
+
+ void FilterSelect( sal_uInt16 nFilterIdx, bool bForce );
+ void SetFamilyState( sal_uInt16 nSlotId, const SfxTemplateItem* );
+ void SetWaterCanState( const SfxBoolItem* pItem );
+ bool IsSafeForWaterCan() const;
+
+ void SetFamily(SfxStyleFamily nFamily);
+ void ActionSelect(const OString& rId, StyleList& rStyleList);
+
+ void SaveFactoryStyleFilter(SfxObjectShell const* i_pObjSh, sal_Int32 i_nFilter);
+
+ DECL_LINK(ReadResource_Hdl, StyleList&, void);
+ DECL_LINK(ClearResource_Hdl, void*, void);
+ DECL_LINK(SaveSelection_Hdl, StyleList&, SfxObjectShell*);
+ DECL_LINK(LoadFactoryStyleFilter_Hdl, SfxObjectShell const*, sal_Int32);
+ DECL_LINK(UpdateStyles_Hdl, StyleFlags, void);
+ DECL_LINK(UpdateFamily_Hdl, StyleList&, void);
+ DECL_LINK(UpdateStyleDependents_Hdl, void*, void);
+
+public:
+ // Used in StyleList::NewMenuExecuteAction, StyleList::UpdateStyleDependents, StyleList::NewHdl, EditHdl...
+ // It comes into action whenever an existing style is selected for use, or a new style is created etc..
+ bool Execute_Impl(sal_uInt16 nId, const OUString& rStr, const OUString& rRefStr,
+ sal_uInt16 nFamily, StyleList& rStyleList, SfxStyleSearchBits nMask = SfxStyleSearchBits::Auto,
+ sal_uInt16* pIdx = nullptr, const sal_uInt16* pModifier = nullptr);
+
+ // This function handles drop of content into the treeview to create a new style
+ sal_Int8 ExecuteDrop(const ExecuteDropEvent& rEvt);
+ // This function is used when a newstyle is created
+ DECL_LINK(OnAsyncExecuteDrop, void*, void);
+
+ // Used in StyleList::UpdateStyles, StyleList::Update
+ // Whenever a new family(Eg. Character, List etc.) is selected it comes into action
+ void FamilySelect(sal_uInt16 nId, StyleList& rStyleList, bool bPreviewRefresh = false);
+
+ // Constructor
+ SfxCommonTemplateDialog_Impl(SfxBindings* pB, weld::Container*, weld::Builder* pBuilder);
+
+ // Destructor
+ virtual ~SfxCommonTemplateDialog_Impl();
+
+ // Used in StyleList::SelectStyle, StyleList::Notify, IMPL_LINK(PopupFlatMenuHdl)
+ // These functions are used when a style is edited, deleted, created etc..
+ virtual void EnableEdit(bool b, StyleList* rStyleList);
+ void EnableDel(bool b, const StyleList* rStyleList);
+ void EnableNew(bool b, const StyleList* rStyleList);
+ void EnableHide(bool b, const StyleList* rStyleList);
+ void EnableShow(bool b, const StyleList* rStyleList);
+
+ // Used in TreeDrag
+ void EnableTreeDrag(bool b);
+ // It comes into action when a style is created or updated or newmenu is created
+ void EnableExample_Impl(sal_uInt16 nId, bool bEnable);
+
+ // This comes into action when a family is selected or a style is applied for use
+ virtual void CheckItem(const OString& /*rMesId*/, bool /*bCheck*/ = true) {}
+ // This is used for watercan or when newmenu or watercan is enabled or updated
+ virtual void EnableItem(const OString& /*rMesId*/, bool /*bCheck*/ = true) {}
+ // This is used for watercan
+ virtual bool IsCheckedItem(const OString& /*rMesId*/) { return true; }
+
+ // This is used when a style is selected
+ void SelectStyle(const OUString& rStyle, bool bIsCallback, StyleList& rStyleList);
+
+ //When a new document is created, it comes into action
+ void IsUpdate(StyleList&);
+
+ // This function return the value of bUpdate in Stylelist
+ // This value is used in StyleList's Notify
+ bool GetNotifyUpdate() const { return bUpdate; }
+ // This function sets the value of bUpdate in Dialog
+ // This function is used in StyleList's Notify to update the value of bUpdate when required
+ void SetNotifyupdate(bool b) { bUpdate = b; }
+
+ void connect_stylelist_read_resource(const Link<void*, size_t>& rLink) { m_aStyleListReadResource = rLink; }
+ void connect_stylelist_clear(const Link<void*, void>& rLink) { m_aStyleListClear = rLink; }
+ void connect_stylelist_cleanup(const Link<void*, void>& rLink) { m_aStyleListCleanup = rLink; }
+ void connect_stylelist_execute_drop(const Link<const ExecuteDropEvent&, sal_Int8>& rLink);
+ void connect_stylelist_execute_new_menu(const Link<void*, void>& rLink) { m_aStyleListNewMenu = rLink; }
+ void connect_stylelist_for_watercan(const Link<void*, bool>& rLink) { m_aStyleListWaterCan = rLink; }
+ void connect_stylelist_has_selected_style(const Link<void*, bool>& rLink);
+ void connect_stylelist_update_style_dependents(const Link<void*, void>& rLink);
+ void connect_stylelist_enable_tree_drag(const Link<bool, void> rLink);
+ void connect_stylelist_enable_delete(const Link<void*, void> rLink);
+ void connect_stylelist_set_water_can_state(const Link<const SfxBoolItem*, void> rLink);
+ void connect_set_family(const Link<sal_uInt16, void> rLink) { m_aStyleListSetFamily = rLink; }
+};
+
+class ToolbarDropTarget;
+class DropTargetHelper;
+
+class SfxTemplateDialog_Impl final : public SfxCommonTemplateDialog_Impl
+{
+private:
+ friend class SfxTemplateControllerItem;
+ friend class SfxTemplatePanelControl;
+
+ std::unique_ptr<ToolbarDropTarget> m_xToolbarDropTargetHelper;
+ std::unique_ptr<weld::Toolbar> m_xActionTbL;
+ std::unique_ptr<weld::Toolbar> m_xActionTbR;
+ std::unique_ptr<weld::Menu> m_xToolMenu;
+ int m_nActionTbLVisible;
+
+ void FillToolMenu();
+
+ DECL_LINK(ToolBoxLSelect, const OString&, void);
+ DECL_LINK(ToolBoxRSelect, const OString&, void);
+ DECL_LINK(ToolMenuSelectHdl, const OString&, void);
+
+ virtual void EnableEdit( bool, StyleList* rStyleList) override;
+ virtual void EnableItem(const OString& rMesId, bool bCheck = true) override;
+ virtual void CheckItem(const OString& rMesId, bool bCheck = true) override;
+ virtual bool IsCheckedItem(const OString& rMesId) override;
+ virtual void InsertFamilyItem(sal_uInt16 nId, const SfxStyleFamilyItem& rItem) override;
+ virtual void EnableFamilyItem(sal_uInt16 nId, bool bEnabled) override;
+ virtual void ClearFamilyList() override;
+ virtual void ReplaceUpdateButtonByMenu() override;
+
+public:
+ friend class SfxTemplateDialog;
+
+ SfxTemplateDialog_Impl( SfxBindings*, SfxTemplatePanelControl* pDlgWindow );
+ virtual ~SfxTemplateDialog_Impl() override;
+
+ sal_Int8 AcceptToolbarDrop(const AcceptDropEvent& rEvt, const DropTargetHelper& rHelper);
+
+ void Initialize();
+};
+
+#endif // INCLUDED_SFX2_SOURCE_INC_TEMPLDGI_HXX
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/inc/tplcitem.hxx b/sfx2/source/inc/tplcitem.hxx
new file mode 100644
index 000000000..9c532fbc8
--- /dev/null
+++ b/sfx2/source/inc/tplcitem.hxx
@@ -0,0 +1,45 @@
+/* -*- 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_INC_TPLCITEM_HXX
+#define INCLUDED_SFX2_SOURCE_INC_TPLCITEM_HXX
+
+#include <sfx2/ctrlitem.hxx>
+#include <tools/link.hxx>
+
+struct ImplSVEvent;
+class SfxCommonTemplateDialog_Impl;
+
+class SfxTemplateControllerItem final : public SfxControllerItem
+{
+ SfxCommonTemplateDialog_Impl &rTemplateDlg;
+ sal_uInt8 nWaterCanState;
+ ImplSVEvent* nUserEventId;
+
+ DECL_LINK(SetWaterCanStateHdl_Impl, void*, void);
+
+ virtual void StateChangedAtToolBoxControl(sal_uInt16, SfxItemState, const SfxPoolItem* pState) override;
+
+public:
+ SfxTemplateControllerItem(sal_uInt16 nId, SfxCommonTemplateDialog_Impl& rDialog, SfxBindings& rBindings);
+ virtual ~SfxTemplateControllerItem() override;
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/inc/versdlg.hxx b/sfx2/source/inc/versdlg.hxx
new file mode 100644
index 000000000..22513f365
--- /dev/null
+++ b/sfx2/source/inc/versdlg.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_SOURCE_INC_VERSDLG_HXX
+#define INCLUDED_SFX2_SOURCE_INC_VERSDLG_HXX
+
+#include <sfx2/basedlgs.hxx>
+#include <vcl/weld.hxx>
+
+class SfxViewFrame;
+struct SfxVersionInfo;
+
+class SfxVersionTableDtor;
+class SfxVersionDialog final : public SfxDialogController
+{
+ SfxViewFrame* m_pViewFrame;
+ bool m_bIsSaveVersionOnClose;
+ std::unique_ptr<SfxVersionTableDtor> m_pTable;
+ std::unique_ptr<weld::Button> m_xSaveButton;
+ std::unique_ptr<weld::CheckButton> m_xSaveCheckBox;
+ std::unique_ptr<weld::Button> m_xOpenButton;
+ std::unique_ptr<weld::Button> m_xViewButton;
+ std::unique_ptr<weld::Button> m_xDeleteButton;
+ std::unique_ptr<weld::Button> m_xCompareButton;
+ std::unique_ptr<weld::Button> m_xCmisButton;
+ std::unique_ptr<weld::TreeView> m_xVersionBox;
+
+ DECL_LINK(DClickHdl_Impl, weld::TreeView&, bool);
+ DECL_LINK(SelectHdl_Impl, weld::TreeView&, void);
+ DECL_LINK(ButtonHdl_Impl, weld::Button&, void);
+ DECL_LINK(ToggleHdl_Impl, weld::Toggleable&, void);
+ void Init_Impl();
+ void Open_Impl();
+
+public:
+ SfxVersionDialog(weld::Window* pParent, SfxViewFrame* pFrame, bool);
+ virtual ~SfxVersionDialog() override;
+ bool IsSaveVersionOnClose() const { return m_bIsSaveVersionOnClose; }
+};
+
+class SfxViewVersionDialog_Impl final : public SfxDialogController
+{
+private:
+ SfxVersionInfo& m_rInfo;
+
+ std::unique_ptr<weld::Label> m_xDateTimeText;
+ std::unique_ptr<weld::Label> m_xSavedByText;
+ std::unique_ptr<weld::TextView> m_xEdit;
+ std::unique_ptr<weld::Button> m_xOKButton;
+ std::unique_ptr<weld::Button> m_xCancelButton;
+ std::unique_ptr<weld::Button> m_xCloseButton;
+
+ DECL_LINK(ButtonHdl, weld::Button&, void);
+
+public:
+ SfxViewVersionDialog_Impl(weld::Window* pParent, SfxVersionInfo& rInfo, bool bEdit);
+};
+
+class SfxCmisVersionsDialog final : public SfxDialogController
+{
+ SfxViewFrame* m_pViewFrame;
+ std::unique_ptr<SfxVersionTableDtor> m_pTable;
+
+ std::unique_ptr<weld::Button> m_xOpenButton;
+ std::unique_ptr<weld::Button> m_xViewButton;
+ std::unique_ptr<weld::Button> m_xDeleteButton;
+ std::unique_ptr<weld::Button> m_xCompareButton;
+ std::unique_ptr<weld::TreeView> m_xVersionBox;
+
+ void LoadVersions();
+
+public:
+ SfxCmisVersionsDialog(weld::Window* pParent, SfxViewFrame* pFrame);
+ virtual ~SfxCmisVersionsDialog() override;
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/inc/workwin.hxx b/sfx2/source/inc/workwin.hxx
new file mode 100644
index 000000000..350901910
--- /dev/null
+++ b/sfx2/source/inc/workwin.hxx
@@ -0,0 +1,300 @@
+/* -*- 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_INC_WORKWIN_HXX
+#define INCLUDED_SFX2_SOURCE_INC_WORKWIN_HXX
+
+#include <vector>
+#include <com/sun/star/frame/XFrame.hpp>
+#include <com/sun/star/task/XStatusIndicator.hpp>
+#include <com/sun/star/frame/XLayoutManagerListener.hpp>
+#include <cppuhelper/implbase.hxx>
+#include <cppuhelper/weakref.hxx>
+
+#include <o3tl/typed_flags_set.hxx>
+
+#include <sfx2/basedlgs.hxx>
+#include <sfx2/childwin.hxx>
+#include <sfx2/frame.hxx>
+#include <sfx2/objface.hxx>
+#include <sfx2/shell.hxx>
+#include <sfx2/toolbarids.hxx>
+
+class SfxSplitWindow;
+class SfxWorkWindow;
+
+
+// This struct makes all relevant Information available of Toolboxes
+struct SfxObjectBar_Impl
+{
+ ToolbarId eId; // ConfigId of Toolbox
+ SfxVisibilityFlags nMode; // special visibility flags
+ bool bDestroy;
+
+ SfxObjectBar_Impl() :
+ eId(ToolbarId::None),
+ nMode(SfxVisibilityFlags::Invisible),
+ bDestroy(false)
+ {}
+};
+
+// This struct makes all relevant Information available of the status bar
+
+struct SfxStatBar_Impl
+{
+ StatusBarId eId;
+
+ SfxStatBar_Impl() :
+ eId(StatusBarId::None)
+ {}
+};
+
+enum class SfxChildVisibility
+{
+ NOT_VISIBLE = 0,
+ ACTIVE = 1, // not disabled through HidePopups
+ NOT_HIDDEN = 2, // not disabled through HideChildWindow
+ FITS_IN = 4, // not too large for output size of the parent
+ VISIBLE = 7, // NOT_HIDDEN | ACTIVE | FITS_IN)
+};
+namespace o3tl
+{
+ template<> struct typed_flags<SfxChildVisibility> : is_typed_flags<SfxChildVisibility, 0x07> {};
+}
+
+
+struct SfxChild_Impl
+{
+ VclPtr<vcl::Window> pWin;
+ std::shared_ptr<SfxDialogController> xController;
+ Size aSize;
+ SfxChildAlignment eAlign;
+ SfxChildVisibility nVisible;
+ bool bResize;
+ bool bSetFocus;
+
+ SfxChild_Impl( vcl::Window& rChild, const Size& rSize,
+ SfxChildAlignment eAlignment, bool bIsVisible ):
+ pWin(&rChild), aSize(rSize), eAlign(eAlignment), bResize(false),
+ bSetFocus( false )
+ {
+ nVisible = bIsVisible ? SfxChildVisibility::VISIBLE : SfxChildVisibility::NOT_VISIBLE;
+ }
+
+ SfxChild_Impl(const std::shared_ptr<SfxDialogController>& rChild,
+ SfxChildAlignment eAlignment):
+ pWin(nullptr), xController(rChild), eAlign(eAlignment), bResize(false),
+ bSetFocus( false )
+ {
+ nVisible = xController->getDialog()->get_visible() ? SfxChildVisibility::VISIBLE : SfxChildVisibility::NOT_VISIBLE;
+ }
+};
+
+struct SfxChildWin_Impl
+{
+ sal_uInt16 nSaveId; // the ChildWindow-Id
+ sal_uInt16 nId; // current Id
+ SfxChildWindow* pWin;
+ bool bCreate;
+ SfxChildWinInfo aInfo;
+ SfxChild_Impl* pCli; // != 0 at direct Children
+ SfxVisibilityFlags nVisibility;
+ bool bEnable;
+
+ SfxChildWin_Impl( sal_uInt32 nID ) :
+ nSaveId(static_cast<sal_uInt16>(nID & 0xFFFF) ),
+ nId(nSaveId),
+ pWin(nullptr),
+ bCreate(false),
+ pCli(nullptr),
+ nVisibility( SfxVisibilityFlags::Invisible ),
+ bEnable( true )
+ {}
+};
+
+enum class SfxChildIdentifier
+{
+ DOCKINGWINDOW,
+ SPLITWINDOW
+};
+
+enum class SfxDockingConfig
+{
+ SETDOCKINGRECTS,
+ ALIGNDOCKINGWINDOW,
+ TOGGLEFLOATMODE
+};
+
+
+#define SFX_SPLITWINDOWS_LEFT 0
+#define SFX_SPLITWINDOWS_TOP 2
+#define SFX_SPLITWINDOWS_RIGHT 1
+#define SFX_SPLITWINDOWS_MAX 4
+
+
+class LayoutManagerListener final : public ::cppu::WeakImplHelper<
+ css::frame::XLayoutManagerListener,
+ css::lang::XComponent >
+{
+ public:
+ LayoutManagerListener( SfxWorkWindow* pWrkWin );
+ virtual ~LayoutManagerListener() override;
+
+ void setFrame( const css::uno::Reference< css::frame::XFrame >& rFrame );
+
+
+ // XComponent
+
+ 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 >& aListener ) override;
+ virtual void SAL_CALL dispose() override;
+
+
+ // XEventListener
+
+ virtual void SAL_CALL disposing( const css::lang::EventObject& aEvent ) override;
+
+
+ // XLayoutManagerEventListener
+
+ virtual void SAL_CALL layoutEvent( const css::lang::EventObject& aSource, ::sal_Int16 eLayoutEvent, const css::uno::Any& aInfo ) override;
+
+ private:
+ bool m_bHasFrame;
+ SfxWorkWindow* m_pWrkWin;
+ css::uno::WeakReference< css::frame::XFrame > m_xFrame;
+};
+
+class SfxWorkWindow final
+{
+ friend class LayoutManagerListener;
+
+ std::vector<sal_uInt16> aSortedList;
+ SfxStatBar_Impl aStatBar;
+ std::vector< SfxObjectBar_Impl > aObjBarList;
+ tools::Rectangle aClientArea;
+ tools::Rectangle aUpperClientArea;
+ VclPtr<SfxSplitWindow> pSplit[SFX_SPLITWINDOWS_MAX];
+ std::vector<std::unique_ptr<SfxChild_Impl>>
+ aChildren;
+ std::vector<std::unique_ptr<SfxChildWin_Impl>>
+ aChildWins;
+ SfxBindings* pBindings;
+ VclPtr<vcl::Window> pWorkWin;
+ VclPtr<vcl::Window> pActiveChild;
+ SfxVisibilityFlags nUpdateMode;
+ sal_uInt16 nChildren;
+ SfxVisibilityFlags nOrigMode;
+ bool bSorted : 1;
+ bool bDockingAllowed : 1;
+ bool bInternalDockingAllowed : 1;
+ bool bAllChildrenVisible : 1;
+ bool bIsFullScreen : 1;
+ bool bShowStatusBar : 1;
+ sal_Int32 m_nLock;
+ css::uno::Reference< css::lang::XComponent > m_xLayoutManagerListener;
+ SfxFrame* pMasterFrame;
+ SfxFrame* pFrame;
+
+ void CreateChildWin_Impl(SfxChildWin_Impl*,bool);
+ void RemoveChildWin_Impl(SfxChildWin_Impl*);
+ void Sort_Impl();
+ SfxChild_Impl* FindChild_Impl( const vcl::Window* rWindow ) const;
+ bool RequestTopToolSpacePixel_Impl( SvBorder aBorder );
+ tools::Rectangle GetTopRect_Impl() const;
+ SvBorder Arrange_Impl();
+ void SaveStatus_Impl(SfxChildWindow*, const SfxChildWinInfo&);
+ static bool IsPluginMode( SfxObjectShell const * pObjShell );
+
+ void FlushPendingChildSizes();
+
+public:
+ SfxWorkWindow( vcl::Window* pWin, SfxFrame* pFrm, SfxFrame* pMaster );
+ ~SfxWorkWindow();
+ SfxBindings& GetBindings()
+ { return *pBindings; }
+ vcl::Window* GetWindow() const
+ { return pWorkWin; }
+ tools::Rectangle GetFreeArea( bool bAutoHide ) const;
+ void SetDockingAllowed(bool bSet)
+ { bDockingAllowed = bSet; }
+ void SetInternalDockingAllowed(bool bSet)
+ { bInternalDockingAllowed = bSet; }
+ bool IsDockingAllowed() const
+ { return bDockingAllowed; }
+ bool IsInternalDockingAllowed() const
+ { return bInternalDockingAllowed; }
+
+ // Methods for all Child windows
+ void DataChanged_Impl();
+ void ReleaseChild_Impl( vcl::Window& rWindow );
+ void ReleaseChild_Impl(const SfxDialogController&);
+ SfxChild_Impl* RegisterChild_Impl( vcl::Window& rWindow, SfxChildAlignment eAlign );
+ SfxChild_Impl* RegisterChild_Impl(std::shared_ptr<SfxDialogController>& rController, SfxChildAlignment eAlign);
+ void ShowChildren_Impl();
+ void HideChildren_Impl();
+ bool PrepareClose_Impl();
+ void ArrangeChildren_Impl( bool bForce = true );
+ void DeleteControllers_Impl();
+ void HidePopups_Impl(bool bHide, sal_uInt16 nId=0);
+ void ConfigChild_Impl(SfxChildIdentifier,
+ SfxDockingConfig, sal_uInt16);
+ void MakeChildrenVisible_Impl( bool bVis );
+ void ArrangeAutoHideWindows( SfxSplitWindow *pSplit );
+ bool IsAutoHideMode( const SfxSplitWindow *pSplit );
+ void EndAutoShow_Impl( Point aPos );
+ void SetFullScreen_Impl( bool bSet ) { bIsFullScreen = bSet; }
+
+ // Methods for Objectbars
+ void UpdateObjectBars_Impl();
+ void UpdateObjectBars_Impl2();
+ void ResetObjectBars_Impl();
+ void SetObjectBar_Impl(sal_uInt16 nPos, SfxVisibilityFlags nFlags, ToolbarId eId);
+ bool IsVisible_Impl() const;
+ void MakeVisible_Impl( bool );
+ void Lock_Impl( bool );
+
+ // Methods for ChildWindows
+ void UpdateChildWindows_Impl();
+ void ResetChildWindows_Impl();
+ void SetChildWindowVisible_Impl( sal_uInt32, bool, SfxVisibilityFlags );
+ void ToggleChildWindow_Impl(sal_uInt16,bool);
+ bool HasChildWindow_Impl(sal_uInt16);
+ bool KnowsChildWindow_Impl(sal_uInt16);
+ void ShowChildWindow_Impl(sal_uInt16, bool bVisible, bool bSetFocus);
+ void SetChildWindow_Impl(sal_uInt16, bool bOn, bool bSetFocus);
+ SfxChildWindow* GetChildWindow_Impl(sal_uInt16);
+ void InitializeChild_Impl(SfxChildWin_Impl*);
+ SfxSplitWindow* GetSplitWindow_Impl(SfxChildAlignment);
+
+ bool IsVisible_Impl( SfxVisibilityFlags nMode ) const;
+ bool IsFloating( sal_uInt16 nId );
+ void SetActiveChild_Impl( vcl::Window *pChild );
+ const VclPtr<vcl::Window>& GetActiveChild_Impl() const { return pActiveChild; }
+
+ // Methods for StatusBar
+ void ResetStatusBar_Impl();
+ void SetStatusBar_Impl(StatusBarId eResId);
+ void UpdateStatusBar_Impl();
+ css::uno::Reference< css::task::XStatusIndicator > GetStatusIndicator();
+ css::uno::Reference< css::frame::XFrame > GetFrameInterface();
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/inet/inettbc.cxx b/sfx2/source/inet/inettbc.cxx
new file mode 100644
index 000000000..165dd2eeb
--- /dev/null
+++ b/sfx2/source/inet/inettbc.cxx
@@ -0,0 +1,275 @@
+/* -*- 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 <inettbc.hxx>
+
+#include <com/sun/star/awt/XTopWindow.hpp>
+#include <com/sun/star/frame/Desktop.hpp>
+#include <com/sun/star/task/XInteractionHandler.hpp>
+#include <com/sun/star/util/XURLTransformer.hpp>
+
+#include <comphelper/propertyvalue.hxx>
+#include <svl/stritem.hxx>
+#include <unotools/historyoptions.hxx>
+#include <vcl/toolbox.hxx>
+#include <vcl/svapp.hxx>
+#include <osl/file.hxx>
+#include <rtl/ustring.hxx>
+
+#include <svtools/inettbc.hxx>
+
+#include <vcl/InterimItemWindow.hxx>
+#include <sfx2/sfxsids.hrc>
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::util;
+using namespace ::com::sun::star::frame;
+using namespace ::com::sun::star::task;
+
+
+// SfxURLToolBoxControl_Impl
+
+
+SFX_IMPL_TOOLBOX_CONTROL(SfxURLToolBoxControl_Impl,SfxStringItem)
+
+SfxURLToolBoxControl_Impl::SfxURLToolBoxControl_Impl( sal_uInt16 nSlotId, ToolBoxItemId nId, ToolBox& rBox )
+ : SfxToolBoxControl( nSlotId, nId, rBox )
+ , m_bModified(false)
+{
+ addStatusListener( ".uno:CurrentURL");
+}
+
+SfxURLToolBoxControl_Impl::~SfxURLToolBoxControl_Impl()
+{
+}
+
+class URLBoxItemWindow final : public InterimItemWindow
+{
+private:
+ std::unique_ptr<SvtURLBox> m_xWidget;
+
+ DECL_LINK(KeyInputHdl, const KeyEvent&, bool);
+public:
+ URLBoxItemWindow(vcl::Window* pParent)
+ : InterimItemWindow(pParent, "sfx/ui/urlbox.ui", "URLBox")
+ , m_xWidget(new SvtURLBox(m_xBuilder->weld_combo_box("urlbox")))
+ {
+ InitControlBase(m_xWidget->getWidget());
+
+ m_xWidget->connect_key_press(LINK(this, URLBoxItemWindow, KeyInputHdl));
+
+ int nWidth = GetDesktopRectPixel().GetWidth() > 800 ? 300 : 225;
+ SetSizePixel(Size(nWidth, m_xWidget->get_preferred_size().Height()));
+ }
+
+ SvtURLBox* GetURLBox()
+ {
+ return m_xWidget.get();
+ }
+
+ virtual void dispose() override
+ {
+ m_xWidget.reset();
+ InterimItemWindow::dispose();
+ }
+
+ void set_sensitive(bool bSensitive)
+ {
+ Enable(bSensitive);
+ m_xWidget->set_sensitive(bSensitive);
+ }
+
+ virtual ~URLBoxItemWindow() override
+ {
+ disposeOnce();
+ }
+};
+
+IMPL_LINK(URLBoxItemWindow, KeyInputHdl, const KeyEvent&, rKEvt, bool)
+{
+ return ChildKeyInput(rKEvt);
+}
+
+URLBoxItemWindow* SfxURLToolBoxControl_Impl::GetURLBoxItemWindow() const
+{
+ return static_cast<URLBoxItemWindow*>(GetToolBox().GetItemWindow(GetId()));
+}
+
+SvtURLBox* SfxURLToolBoxControl_Impl::GetURLBox() const
+{
+ return GetURLBoxItemWindow()->GetURLBox();
+}
+
+void SfxURLToolBoxControl_Impl::OpenURL( const OUString& rName ) const
+{
+ OUString aName;
+
+ INetURLObject aObj( rName );
+ if ( aObj.GetProtocol() == INetProtocol::NotValid )
+ {
+ aName = SvtURLBox::ParseSmart( rName, "" );
+ }
+ else
+ aName = rName;
+
+ if ( aName.isEmpty() )
+ return;
+
+ Reference< XDispatchProvider > xDispatchProvider( getFrameInterface(), UNO_QUERY );
+ if ( !xDispatchProvider.is() )
+ return;
+
+ URL aTargetURL;
+ aTargetURL.Complete = aName;
+
+ getURLTransformer()->parseStrict( aTargetURL );
+ Reference< XDispatch > xDispatch = xDispatchProvider->queryDispatch( aTargetURL, "_default", 0 );
+ if ( !xDispatch.is() )
+ return;
+
+ Sequence< PropertyValue > aArgs{
+ comphelper::makePropertyValue("Referer", OUString( "private:user" )),
+ comphelper::makePropertyValue("FileName", aName)
+ };
+
+ SfxURLToolBoxControl_Impl::ExecuteInfo* pExecuteInfo = new SfxURLToolBoxControl_Impl::ExecuteInfo;
+ pExecuteInfo->xDispatch = xDispatch;
+ pExecuteInfo->aTargetURL = aTargetURL;
+ pExecuteInfo->aArgs = aArgs;
+ Application::PostUserEvent( LINK( nullptr, SfxURLToolBoxControl_Impl, ExecuteHdl_Impl), pExecuteInfo );
+}
+
+
+IMPL_STATIC_LINK( SfxURLToolBoxControl_Impl, ExecuteHdl_Impl, void*, p, void )
+{
+ ExecuteInfo* pExecuteInfo = static_cast<ExecuteInfo*>(p);
+ try
+ {
+ // Asynchronous execution as this can lead to our own destruction!
+ // Framework can recycle our current frame and the layout manager disposes all user interface
+ // elements if a component gets detached from its frame!
+ pExecuteInfo->xDispatch->dispatch( pExecuteInfo->aTargetURL, pExecuteInfo->aArgs );
+ }
+ catch ( Exception& )
+ {
+ }
+
+ delete pExecuteInfo;
+}
+
+VclPtr<InterimItemWindow> SfxURLToolBoxControl_Impl::CreateItemWindow( vcl::Window* pParent )
+{
+ VclPtrInstance<URLBoxItemWindow> xURLBox(pParent);
+ SvtURLBox* pURLBox = xURLBox->GetURLBox();
+ pURLBox->connect_changed(LINK(this, SfxURLToolBoxControl_Impl, SelectHdl));
+ pURLBox->connect_entry_activate(LINK(this, SfxURLToolBoxControl_Impl, OpenHdl));
+ xURLBox->Show();
+ return xURLBox;
+}
+
+IMPL_LINK(SfxURLToolBoxControl_Impl, SelectHdl, weld::ComboBox&, rComboBox, void)
+{
+ m_bModified = true;
+
+ SvtURLBox* pURLBox = GetURLBox();
+ OUString aName( pURLBox->GetURL() );
+
+ if (rComboBox.changed_by_direct_pick() && !aName.isEmpty())
+ OpenURL( aName );
+}
+
+IMPL_LINK_NOARG(SfxURLToolBoxControl_Impl, OpenHdl, weld::ComboBox&, bool)
+{
+ SvtURLBox* pURLBox = GetURLBox();
+ OpenURL( pURLBox->GetURL() );
+
+ Reference< XDesktop2 > xDesktop = Desktop::create( m_xContext );
+ Reference< XFrame > xFrame = xDesktop->getActiveFrame();
+ if (!xFrame.is())
+ return true;
+
+ auto xWin = xFrame->getContainerWindow();
+ if (!xWin)
+ return true;
+ xWin->setFocus();
+ Reference<css::awt::XTopWindow> xTop(xWin, UNO_QUERY);
+ if (!xTop)
+ return true;
+ xTop->toFront();
+ return true;
+}
+
+void SfxURLToolBoxControl_Impl::StateChangedAtToolBoxControl
+(
+ sal_uInt16 nSID,
+ SfxItemState eState,
+ const SfxPoolItem* pState
+)
+{
+ if ( nSID == SID_OPENURL )
+ {
+ // Disable URL box if command is disabled
+ GetURLBoxItemWindow()->set_sensitive( SfxItemState::DISABLED != eState );
+ }
+
+ if ( !GetURLBoxItemWindow()->IsEnabled() )
+ return;
+
+ if( nSID == SID_FOCUSURLBOX )
+ {
+ if ( GetURLBoxItemWindow()->IsVisible() )
+ GetURLBoxItemWindow()->GrabFocus();
+ }
+ else if ( !m_bModified && SfxItemState::DEFAULT == eState )
+ {
+ SvtURLBox* pURLBox = GetURLBox();
+ pURLBox->clear();
+
+ const std::vector< SvtHistoryOptions::HistoryItem > lList = SvtHistoryOptions::GetList(EHistoryType::PickList);
+ for (const SvtHistoryOptions::HistoryItem& lProps : lList)
+ {
+ if (!lProps.sURL.isEmpty())
+ {
+ INetURLObject aURL ( lProps.sURL );
+ OUString sMainURL( aURL.GetMainURL( INetURLObject::DecodeMechanism::WithCharset ) );
+ OUString sFile;
+
+ if (osl::FileBase::getSystemPathFromFileURL(sMainURL, sFile) == osl::FileBase::E_None)
+ pURLBox->append_text(sFile);
+ else
+ pURLBox->append_text(sMainURL);
+ }
+ }
+
+ const SfxStringItem *pURL = dynamic_cast< const SfxStringItem* >(pState);
+ assert(pURL);
+ INetURLObject aURL( pURL->GetValue() );
+ INetProtocol eProt = aURL.GetProtocol();
+ if ( eProt == INetProtocol::File )
+ {
+ pURLBox->set_entry_text( aURL.PathToFileName() );
+ }
+ else
+ pURLBox->set_entry_text( aURL.GetURLNoPass() );
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/notebookbar/NotebookbarTabControl.cxx b/sfx2/source/notebookbar/NotebookbarTabControl.cxx
new file mode 100644
index 000000000..78929e5b1
--- /dev/null
+++ b/sfx2/source/notebookbar/NotebookbarTabControl.cxx
@@ -0,0 +1,385 @@
+/* -*- 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/builderfactory.hxx>
+#include <vcl/layout.hxx>
+#include <vcl/notebookbar/notebookbar.hxx>
+#include <vcl/tabpage.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <notebookbar/NotebookbarTabControl.hxx>
+#include <com/sun/star/ui/theModuleUIConfigurationManagerSupplier.hpp>
+#include <com/sun/star/ui/ItemType.hpp>
+#include <com/sun/star/frame/XModuleManager.hpp>
+#include <com/sun/star/frame/ModuleManager.hpp>
+#include <com/sun/star/frame/XFrame.hpp>
+#include <com/sun/star/uno/Reference.h>
+#include <com/sun/star/awt/PopupMenuDirection.hpp>
+#include <com/sun/star/frame/XPopupMenuController.hpp>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <sidebar/SidebarToolBox.hxx>
+#include <cppuhelper/implbase.hxx>
+
+#define ICON_SIZE 25
+constexpr OUStringLiteral TOOLBAR_STR = u"private:resource/toolbar/notebookbarshortcuts";
+
+using namespace css::uno;
+using namespace css::ui;
+using namespace css::frame;
+
+class ChangedUIEventListener : public ::cppu::WeakImplHelper<XUIConfigurationListener>
+{
+ VclPtr<NotebookbarTabControl> m_pParent;
+
+public:
+ explicit ChangedUIEventListener(NotebookbarTabControl *p)
+ : m_pParent(p)
+ {
+ try
+ {
+ if( SfxViewFrame::Current() )
+ {
+ Reference<XComponentContext> xContext = comphelper::getProcessComponentContext();
+ const Reference<XModuleManager> xModuleManager = ModuleManager::create( xContext );
+ Reference<XFrame> xFrame = SfxViewFrame::Current()->GetFrame().GetFrameInterface();
+ OUString aModuleName = xModuleManager->identify( xFrame );
+
+ Reference<XUIConfigurationManager> m_xConfigManager;
+ Reference<XModuleUIConfigurationManagerSupplier > xModuleCfgMgrSupplier(
+ theModuleUIConfigurationManagerSupplier::get( xContext ) );
+ m_xConfigManager.set( xModuleCfgMgrSupplier->getUIConfigurationManager( aModuleName ) );
+ css::uno::Reference< css::ui::XUIConfiguration > xConfig( m_xConfigManager, css::uno::UNO_QUERY_THROW );
+ xConfig->addConfigurationListener( this );
+ }
+ }
+ catch( const css::uno::RuntimeException& ) {}
+ }
+
+ // XUIConfigurationListener
+ virtual void SAL_CALL elementInserted( const ConfigurationEvent& rEvent ) override
+ {
+ if( rEvent.ResourceURL == TOOLBAR_STR )
+ {
+ m_pParent->m_bInvalidate = true;
+ m_pParent->StateChanged(StateChangedType::UpdateMode);
+ }
+ }
+
+ virtual void SAL_CALL elementRemoved( const ConfigurationEvent& rEvent ) override
+ {
+ elementInserted( rEvent );
+ }
+
+ virtual void SAL_CALL elementReplaced( const ConfigurationEvent& rEvent ) override
+ {
+ elementInserted( rEvent );
+ }
+
+ virtual void SAL_CALL disposing(const ::css::lang::EventObject&) override
+ {
+ try
+ {
+ if( SfxViewFrame::Current() )
+ {
+ Reference<XComponentContext> xContext = comphelper::getProcessComponentContext();
+ const Reference<XModuleManager> xModuleManager = ModuleManager::create( xContext );
+ Reference<XFrame> xFrame = SfxViewFrame::Current()->GetFrame().GetFrameInterface();
+ OUString aModuleName = xModuleManager->identify( xFrame );
+
+ Reference<XUIConfigurationManager> m_xConfigManager;
+ Reference<XModuleUIConfigurationManagerSupplier > xModuleCfgMgrSupplier(
+ theModuleUIConfigurationManagerSupplier::get( xContext ) );
+ m_xConfigManager.set( xModuleCfgMgrSupplier->getUIConfigurationManager( aModuleName ) );
+ css::uno::Reference< css::ui::XUIConfiguration > xConfig( m_xConfigManager, css::uno::UNO_QUERY_THROW );
+ xConfig->removeConfigurationListener( this );
+ }
+ }
+ catch( const css::uno::RuntimeException& ) {}
+
+ m_pParent.clear();
+ }
+};
+
+namespace {
+
+class ShortcutsToolBox : public sfx2::sidebar::SidebarToolBox
+{
+public:
+ ShortcutsToolBox( Window* pParent )
+ : sfx2::sidebar::SidebarToolBox( pParent )
+ {
+ mbUseDefaultButtonSize = false;
+ mbSideBar = false;
+ SetToolboxButtonSize(ToolBoxButtonSize::Small);
+ }
+
+ virtual void KeyInput( const KeyEvent& rKEvt ) override
+ {
+ if ( rKEvt.GetKeyCode().IsMod1() )
+ {
+ sal_uInt16 nCode( rKEvt.GetKeyCode().GetCode() );
+ if ( nCode == KEY_RIGHT || nCode == KEY_LEFT )
+ {
+ GetParent()->KeyInput( rKEvt );
+ return;
+ }
+ }
+ return sfx2::sidebar::SidebarToolBox::KeyInput( rKEvt );
+ }
+};
+
+}
+
+NotebookbarTabControl::NotebookbarTabControl( Window* pParent )
+: NotebookbarTabControlBase( pParent )
+, m_bInitialized( false )
+, m_bInvalidate( true )
+{
+}
+
+NotebookbarTabControl::~NotebookbarTabControl()
+{
+}
+
+void NotebookbarTabControl::ArrowStops( sal_uInt16 nCode )
+{
+ ToolBox* pToolBox( GetToolBox() );
+ Control* pOpenMenu( GetOpenMenu() );
+
+ if ( nCode == KEY_LEFT )
+ {
+ if ( HasFocus() )
+ {
+ if ( pToolBox )
+ pToolBox->GrabFocus();
+ else if ( pOpenMenu )
+ pOpenMenu->GrabFocus();
+ }
+ else if ( pToolBox && pToolBox->HasFocus() )
+ {
+ if ( pOpenMenu )
+ pOpenMenu->GrabFocus();
+ else
+ GrabFocus();
+ }
+ else if ( pOpenMenu && pOpenMenu->HasFocus() )
+ {
+ GrabFocus();
+ }
+ }
+ else if ( nCode == KEY_RIGHT )
+ {
+ if ( HasFocus() )
+ {
+ if ( pOpenMenu )
+ pOpenMenu->GrabFocus();
+ else if ( pToolBox )
+ pToolBox->GrabFocus();
+ }
+ else if ( pToolBox && pToolBox->HasFocus() )
+ {
+ GrabFocus();
+ }
+ else if ( pOpenMenu && pOpenMenu->HasFocus() )
+ {
+ if ( pToolBox )
+ pToolBox->GrabFocus();
+ else
+ GrabFocus();
+ }
+ }
+}
+
+void NotebookbarTabControl::KeyInput( const KeyEvent& rKEvt )
+{
+ if ( rKEvt.GetKeyCode().IsMod1() )
+ {
+ sal_uInt16 nCode( rKEvt.GetKeyCode().GetCode() );
+ if ( nCode == KEY_RIGHT || nCode == KEY_LEFT )
+ {
+ ArrowStops( nCode );
+ return;
+ }
+ }
+ return NotebookbarTabControlBase::KeyInput( rKEvt );
+}
+
+bool NotebookbarTabControl::EventNotify( NotifyEvent& rNEvt )
+{
+ if ( rNEvt.GetType() == MouseNotifyEvent::KEYINPUT )
+ {
+ const vcl::KeyCode& rKey = rNEvt.GetKeyEvent()->GetKeyCode();
+ sal_uInt16 nCode = rKey.GetCode();
+ if ( rKey.IsMod1() && ( nCode == KEY_RIGHT || nCode == KEY_LEFT ) )
+ {
+ ArrowStops( nCode );
+ return true;
+ }
+ }
+ return NotebookbarTabControlBase::EventNotify( rNEvt );
+}
+
+void NotebookbarTabControl::StateChanged(StateChangedType nStateChange)
+{
+ if( !m_bInitialized && SfxViewFrame::Current() )
+ {
+ VclPtr<ShortcutsToolBox> pShortcuts = VclPtr<ShortcutsToolBox>::Create( this );
+ pShortcuts->Show();
+
+ SetToolBox( static_cast<ToolBox*>( pShortcuts.get() ) );
+ SetIconClickHdl( LINK( this, NotebookbarTabControl, OpenNotebookbarPopupMenu ) );
+
+ m_pListener = new ChangedUIEventListener( this );
+
+ m_bInitialized = true;
+ }
+ if( m_bInitialized && m_bInvalidate && SfxViewFrame::Current() )
+ {
+ ToolBox* pToolBox = GetToolBox();
+ if( !pToolBox )
+ return;
+
+ pToolBox->Clear();
+
+ Reference<XComponentContext> xContext = comphelper::getProcessComponentContext();
+ const Reference<XModuleManager> xModuleManager = ModuleManager::create( xContext );
+ m_xFrame = SfxViewFrame::Current()->GetFrame().GetFrameInterface();
+ OUString aModuleName = xModuleManager->identify( m_xFrame );
+
+ FillShortcutsToolBox( xContext, m_xFrame, aModuleName, pToolBox );
+
+ Size aSize( pToolBox->GetOptimalSize() );
+ Point aPos( ICON_SIZE + 10, 0 );
+ pToolBox->SetPosSizePixel( aPos, aSize );
+ ImplPlaceTabs( GetSizePixel().getWidth() );
+
+ m_bInvalidate = false;
+ }
+ NotebookbarTabControlBase::StateChanged( nStateChange );
+}
+
+void NotebookbarTabControl::FillShortcutsToolBox(Reference<XComponentContext> const & xContext,
+ const Reference<XFrame>& xFrame,
+ const OUString& aModuleName,
+ ToolBox* pShortcuts
+)
+{
+ Reference<::com::sun::star::container::XIndexAccess> xIndex;
+
+ try
+ {
+ Reference<XUIConfigurationManager> m_xConfigManager;
+ Reference<XModuleUIConfigurationManagerSupplier > xModuleCfgMgrSupplier(
+ theModuleUIConfigurationManagerSupplier::get( xContext ) );
+ m_xConfigManager.set( xModuleCfgMgrSupplier->getUIConfigurationManager( aModuleName ) );
+ xIndex = m_xConfigManager->getSettings( TOOLBAR_STR, false );
+ }
+ catch( const Exception& ) {}
+
+ if ( !xIndex.is() )
+ return;
+
+ Sequence< css::beans::PropertyValue > aPropSequence;
+ for ( sal_Int32 i = 0; i < xIndex->getCount(); ++i )
+ {
+ try
+ {
+ if ( xIndex->getByIndex( i ) >>= aPropSequence )
+ {
+ OUString aCommandURL;
+ sal_uInt16 nType = ItemType::DEFAULT;
+ bool bVisible = true;
+
+ for ( const auto& aProp: std::as_const(aPropSequence) )
+ {
+ if ( aProp.Name == "CommandURL" )
+ aProp.Value >>= aCommandURL;
+ else if ( aProp.Name == "Type" )
+ aProp.Value >>= nType;
+ else if ( aProp.Name == "IsVisible" )
+ aProp.Value >>= bVisible;
+ }
+ if ( bVisible && ( nType == ItemType::DEFAULT ) )
+ pShortcuts->InsertItem( aCommandURL, xFrame, ToolBoxItemBits::ICON_ONLY, Size( ICON_SIZE, ICON_SIZE ) );
+ }
+ }
+ catch ( const Exception& )
+ {
+ break;
+ }
+ }
+}
+
+IMPL_LINK(NotebookbarTabControl, OpenNotebookbarPopupMenu, NotebookBar*, pNotebookbar, void)
+{
+ if (!pNotebookbar || !m_xFrame.is())
+ return;
+
+ Sequence<Any> aArgs {
+ Any(comphelper::makePropertyValue("Value", OUString("notebookbar"))),
+ Any(comphelper::makePropertyValue("Frame", m_xFrame)) };
+
+ Reference<XComponentContext> xContext = comphelper::getProcessComponentContext();
+ Reference<XPopupMenuController> xPopupController(
+ xContext->getServiceManager()->createInstanceWithArgumentsAndContext(
+ "com.sun.star.comp.framework.ResourceMenuController", aArgs, xContext), UNO_QUERY);
+
+ Reference<css::awt::XPopupMenu> xPopupMenu(xContext->getServiceManager()->createInstanceWithContext(
+ "com.sun.star.awt.PopupMenu", xContext), UNO_QUERY);
+
+ if (!xPopupController.is() || !xPopupMenu.is())
+ return;
+
+ xPopupController->setPopupMenu(xPopupMenu);
+ Point aPos(pNotebookbar->GetSizePixel().getWidth(), NotebookbarTabControl::GetHeaderHeight() - ICON_SIZE + 10);
+ xPopupMenu->execute(pNotebookbar->GetComponentInterface(),
+ css::awt::Rectangle(aPos.X(), aPos.Y(), 1, 1),
+ css::awt::PopupMenuDirection::EXECUTE_DOWN);
+
+ Reference<css::lang::XComponent> xComponent(xPopupController, UNO_QUERY);
+ if (xComponent.is())
+ xComponent->dispose();
+}
+
+Size NotebookbarTabControl::calculateRequisition() const
+{
+ Size aSize = NotebookbarTabControlBase::calculateRequisition();
+
+ for (int i = 0; i < GetPageCount(); i++)
+ {
+ vcl::Window* pChild = GetTabPage(TabControl::GetPageId(i));
+
+ if (pChild)
+ {
+ Size aChildSize = VclContainer::getLayoutRequisition(*pChild);
+
+ if (aChildSize.getWidth() < aSize.getWidth())
+ aSize.setWidth( aChildSize.Width() );
+ }
+ }
+
+ if (aSize.Width() < 400)
+ aSize.setWidth( 400 );
+
+ return aSize;
+}
+
+VCL_BUILDER_FACTORY( NotebookbarTabControl )
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/notebookbar/SfxNotebookBar.cxx b/sfx2/source/notebookbar/SfxNotebookBar.cxx
new file mode 100644
index 000000000..aa602ba17
--- /dev/null
+++ b/sfx2/source/notebookbar/SfxNotebookBar.cxx
@@ -0,0 +1,584 @@
+/* -*- 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/bindings.hxx>
+#include <sfx2/viewsh.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/notebookbar/SfxNotebookBar.hxx>
+#include <vcl/notebookbar/notebookbar.hxx>
+#include <vcl/syswin.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/sfxsids.hrc>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/lok.hxx>
+#include <com/sun/star/frame/UnknownModuleException.hpp>
+#include <com/sun/star/frame/XLayoutManager.hpp>
+#include <officecfg/Office/UI/ToolbarMode.hxx>
+#include <com/sun/star/frame/XModuleManager.hpp>
+#include <com/sun/star/frame/ModuleManager.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <unotools/confignode.hxx>
+#include <comphelper/types.hxx>
+#include <framework/addonsoptions.hxx>
+#include <vcl/notebookbar/NotebookBarAddonsMerger.hxx>
+#include <vector>
+#include <map>
+#include <vcl/WeldedTabbedNotebookbar.hxx>
+
+using namespace sfx2;
+using namespace css::uno;
+using namespace css::ui;
+using namespace css;
+
+constexpr OUStringLiteral MENUBAR_STR = u"private:resource/menubar/menubar";
+
+const char MERGE_NOTEBOOKBAR_URL[] = "URL";
+
+bool SfxNotebookBar::m_bLock = false;
+bool SfxNotebookBar::m_bHide = false;
+std::map<const SfxViewShell*, std::shared_ptr<WeldedTabbedNotebookbar>> SfxNotebookBar::m_pNotebookBarWeldedWrapper;
+
+static void NotebookbarAddonValues(
+ std::vector<Image>& aImageValues,
+ std::vector<css::uno::Sequence<css::uno::Sequence<css::beans::PropertyValue>>>&
+ aExtensionValues)
+{
+ framework::AddonsOptions aAddonsItems;
+
+ for (int nIdx = 0; nIdx < aAddonsItems.GetAddonsNotebookBarCount(); nIdx++)
+ {
+ const css::uno::Sequence<css::uno::Sequence<css::beans::PropertyValue>> aExtension
+ = aAddonsItems.GetAddonsNotebookBarPart(nIdx);
+ for (const css::uno::Sequence<css::beans::PropertyValue>& rExtensionVal : aExtension)
+ {
+ Image aImage;
+ bool isBigImage = true;
+ for (const auto& rProp : rExtensionVal)
+ {
+ if (rProp.Name == MERGE_NOTEBOOKBAR_URL)
+ {
+ OUString sImage;
+ rProp.Value >>= sImage;
+ aImage = Image(aAddonsItems.GetImageFromURL(sImage, isBigImage));
+ }
+ }
+ aImageValues.push_back(aImage);
+ }
+ aExtensionValues.push_back(aExtension);
+ }
+}
+
+static Reference<frame::XLayoutManager> lcl_getLayoutManager( const Reference<frame::XFrame>& xFrame )
+{
+ css::uno::Reference<css::frame::XLayoutManager> xLayoutManager;
+
+ if (xFrame.is())
+ {
+ Reference<css::beans::XPropertySet> xPropSet(xFrame, UNO_QUERY);
+
+ if (xPropSet.is())
+ {
+ Any aValue = xPropSet->getPropertyValue("LayoutManager");
+ aValue >>= xLayoutManager;
+ }
+ }
+
+ return xLayoutManager;
+}
+
+static OUString lcl_getAppName( vcl::EnumContext::Application eApp )
+{
+ switch ( eApp )
+ {
+ case vcl::EnumContext::Application::Writer:
+ return "Writer";
+ case vcl::EnumContext::Application::Calc:
+ return "Calc";
+ case vcl::EnumContext::Application::Impress:
+ return "Impress";
+ case vcl::EnumContext::Application::Draw:
+ return "Draw";
+ case vcl::EnumContext::Application::Formula:
+ return "Formula";
+ default:
+ return OUString();
+ }
+}
+
+static void lcl_setNotebookbarFileName( vcl::EnumContext::Application eApp, const OUString& sFileName )
+{
+ std::shared_ptr<comphelper::ConfigurationChanges> aBatch(
+ comphelper::ConfigurationChanges::create() );
+ switch ( eApp )
+ {
+ case vcl::EnumContext::Application::Writer:
+ officecfg::Office::UI::ToolbarMode::ActiveWriter::set( sFileName, aBatch );
+ break;
+ case vcl::EnumContext::Application::Calc:
+ officecfg::Office::UI::ToolbarMode::ActiveCalc::set( sFileName, aBatch );
+ break;
+ case vcl::EnumContext::Application::Impress:
+ officecfg::Office::UI::ToolbarMode::ActiveImpress::set( sFileName, aBatch );
+ break;
+ case vcl::EnumContext::Application::Draw:
+ officecfg::Office::UI::ToolbarMode::ActiveDraw::set( sFileName, aBatch );
+ break;
+ default:
+ break;
+ }
+ aBatch->commit();
+}
+
+static OUString lcl_getNotebookbarFileName( vcl::EnumContext::Application eApp )
+{
+ switch ( eApp )
+ {
+ case vcl::EnumContext::Application::Writer:
+ return officecfg::Office::UI::ToolbarMode::ActiveWriter::get();
+ case vcl::EnumContext::Application::Calc:
+ return officecfg::Office::UI::ToolbarMode::ActiveCalc::get();
+ case vcl::EnumContext::Application::Impress:
+ return officecfg::Office::UI::ToolbarMode::ActiveImpress::get();
+ case vcl::EnumContext::Application::Draw:
+ return officecfg::Office::UI::ToolbarMode::ActiveDraw::get();
+
+ default:
+ break;
+ }
+ return OUString();
+}
+
+static utl::OConfigurationTreeRoot lcl_getCurrentImplConfigRoot()
+{
+ return utl::OConfigurationTreeRoot(::comphelper::getProcessComponentContext(),
+ "org.openoffice.Office.UI.ToolbarMode/",
+ true);
+}
+
+static utl::OConfigurationNode lcl_getCurrentImplConfigNode(const Reference<css::frame::XFrame>& xFrame,
+ utl::OConfigurationTreeRoot const & rNotebookbarNode )
+{
+ if (!rNotebookbarNode.isValid())
+ return utl::OConfigurationNode();
+
+ const Reference<frame::XModuleManager> xModuleManager = frame::ModuleManager::create( ::comphelper::getProcessComponentContext() );
+
+ vcl::EnumContext::Application eApp = vcl::EnumContext::GetApplicationEnum( xModuleManager->identify( xFrame ) );
+ OUString aActive = lcl_getNotebookbarFileName( eApp );
+
+ const utl::OConfigurationNode aImplsNode = rNotebookbarNode.openNode("Applications/" + lcl_getAppName( eApp) + "/Modes");
+ const Sequence<OUString> aModeNodeNames( aImplsNode.getNodeNames() );
+
+ for ( const auto& rModeNodeName : aModeNodeNames )
+ {
+ const utl::OConfigurationNode aImplNode( aImplsNode.openNode( rModeNodeName ) );
+ if ( !aImplNode.isValid() )
+ continue;
+
+ OUString aCommandArg = comphelper::getString( aImplNode.getNodeValue( "CommandArg" ) );
+
+ if ( aCommandArg == aActive )
+ {
+ return aImplNode;
+ }
+ }
+
+ return utl::OConfigurationNode();
+}
+
+void SfxNotebookBar::CloseMethod(SfxBindings& rBindings)
+{
+ SfxFrame& rFrame = rBindings.GetDispatcher_Impl()->GetFrame()->GetFrame();
+ CloseMethod(rFrame.GetSystemWindow());
+}
+
+void SfxNotebookBar::CloseMethod(SystemWindow* pSysWindow)
+{
+ if (pSysWindow)
+ {
+ RemoveListeners(pSysWindow);
+ if(pSysWindow->GetNotebookBar())
+ pSysWindow->CloseNotebookBar();
+ if (SfxViewFrame::Current())
+ SfxNotebookBar::ShowMenubar(SfxViewFrame::Current(), true);
+ }
+}
+
+void SfxNotebookBar::LockNotebookBar()
+{
+ m_bHide = true;
+ m_bLock = true;
+}
+
+void SfxNotebookBar::UnlockNotebookBar()
+{
+ m_bHide = false;
+ m_bLock = false;
+}
+
+bool SfxNotebookBar::IsActive()
+{
+ if (m_bHide)
+ return false;
+
+ vcl::EnumContext::Application eApp = vcl::EnumContext::Application::Any;
+
+ if (SfxViewFrame::Current())
+ {
+ const Reference<frame::XFrame>& xFrame = SfxViewFrame::Current()->GetFrame().GetFrameInterface();
+ if (!xFrame.is())
+ return false;
+
+ const Reference<frame::XModuleManager> xModuleManager = frame::ModuleManager::create( ::comphelper::getProcessComponentContext() );
+ try
+ {
+ eApp = vcl::EnumContext::GetApplicationEnum(xModuleManager->identify(xFrame));
+ }
+ catch (css::frame::UnknownModuleException& e)
+ {
+ SAL_WARN("sfx.appl", "SfxNotebookBar::IsActive(): " + e.Message);
+ return false;
+ }
+ }
+ else
+ return false;
+
+ OUString appName(lcl_getAppName( eApp ));
+
+ if (appName.isEmpty())
+ return false;
+
+
+ OUString aPath = "org.openoffice.Office.UI.ToolbarMode/Applications/" + appName;
+
+ const utl::OConfigurationTreeRoot aAppNode(
+ ::comphelper::getProcessComponentContext(),
+ aPath,
+ false);
+ if ( !aAppNode.isValid() )
+ return false;
+
+ OUString aActive = comphelper::getString( aAppNode.getNodeValue( "Active" ) );
+
+ if (comphelper::LibreOfficeKit::isActive() && aActive == "notebookbar_online.ui")
+ return true;
+
+ const utl::OConfigurationNode aModesNode = aAppNode.openNode("Modes");
+ const Sequence<OUString> aModeNodeNames( aModesNode.getNodeNames() );
+
+ for ( const auto& rModeNodeName : aModeNodeNames )
+ {
+ const utl::OConfigurationNode aModeNode( aModesNode.openNode( rModeNodeName ) );
+ if ( !aModeNode.isValid() )
+ continue;
+
+ OUString aCommandArg = comphelper::getString( aModeNode.getNodeValue( "CommandArg" ) );
+
+ if ( aCommandArg == aActive )
+ {
+ return comphelper::getBOOL( aModeNode.getNodeValue( "HasNotebookbar" ) );
+ }
+ }
+ return false;
+}
+
+void SfxNotebookBar::ResetActiveToolbarModeToDefault(vcl::EnumContext::Application eApp)
+{
+ const OUString appName( lcl_getAppName( eApp ) );
+
+ if ( appName.isEmpty() )
+ return;
+
+ const OUString aPath = "org.openoffice.Office.UI.ToolbarMode/Applications/" + appName;
+
+ utl::OConfigurationTreeRoot aAppNode(
+ ::comphelper::getProcessComponentContext(),
+ aPath,
+ true);
+ if ( !aAppNode.isValid() )
+ return;
+
+ aAppNode.setNodeValue( "Active", Any( OUString( "Default" ) ) );
+ aAppNode.commit();
+}
+
+void SfxNotebookBar::ExecMethod(SfxBindings& rBindings, const OUString& rUIName)
+{
+ // Save active UI file name
+ if ( !rUIName.isEmpty() && SfxViewFrame::Current() )
+ {
+ const Reference<frame::XFrame>& xFrame = SfxViewFrame::Current()->GetFrame().GetFrameInterface();
+ if (xFrame.is())
+ {
+ const Reference<frame::XModuleManager> xModuleManager = frame::ModuleManager::create( ::comphelper::getProcessComponentContext() );
+ vcl::EnumContext::Application eApp = vcl::EnumContext::GetApplicationEnum(xModuleManager->identify(xFrame));
+ lcl_setNotebookbarFileName( eApp, rUIName );
+ }
+ }
+
+ // trigger the StateMethod
+ rBindings.Invalidate(SID_NOTEBOOKBAR);
+ rBindings.Update();
+}
+
+bool SfxNotebookBar::StateMethod(SfxBindings& rBindings, std::u16string_view rUIFile,
+ bool bReloadNotebookbar)
+{
+ SfxFrame& rFrame = rBindings.GetDispatcher_Impl()->GetFrame()->GetFrame();
+ return StateMethod(rFrame.GetSystemWindow(), rFrame.GetFrameInterface(), rUIFile,
+ bReloadNotebookbar);
+}
+
+bool SfxNotebookBar::StateMethod(SystemWindow* pSysWindow,
+ const Reference<css::frame::XFrame>& xFrame,
+ std::u16string_view rUIFile, bool bReloadNotebookbar)
+{
+ if (!pSysWindow)
+ {
+ if (SfxViewFrame::Current() && SfxViewFrame::Current()->GetWindow().GetSystemWindow())
+ pSysWindow = SfxViewFrame::Current()->GetWindow().GetSystemWindow();
+ else
+ return false;
+ }
+
+ if (IsActive())
+ {
+ css::uno::Reference<css::uno::XComponentContext> xContext = comphelper::getProcessComponentContext();
+ const Reference<frame::XModuleManager> xModuleManager = frame::ModuleManager::create( xContext );
+ OUString aModuleName = xModuleManager->identify( xFrame );
+ vcl::EnumContext::Application eApp = vcl::EnumContext::GetApplicationEnum( aModuleName );
+
+ OUString sFile;
+ if (comphelper::LibreOfficeKit::isActive())
+ sFile = "notebookbar_online.ui";
+ else
+ sFile = lcl_getNotebookbarFileName( eApp );
+
+ OUString sNewFile = rUIFile + sFile;
+ OUString sCurrentFile;
+ VclPtr<NotebookBar> pNotebookBar = pSysWindow->GetNotebookBar();
+ if ( pNotebookBar )
+ sCurrentFile = pNotebookBar->GetUIFilePath();
+
+ bool bChangedFile = sNewFile != sCurrentFile;
+
+ if ((!sFile.isEmpty() && bChangedFile) || !pNotebookBar || !pNotebookBar->IsVisible()
+ || bReloadNotebookbar || comphelper::LibreOfficeKit::isActive())
+ {
+ const SfxViewShell* pViewShell = SfxViewShell::Current();
+
+ // Notebookbar was loaded too early what caused:
+ // * in LOK: Paste Special feature was incorrectly initialized
+ // Skip first request so Notebookbar will be initialized after document was loaded
+ static std::map<const void*, bool> bSkippedFirstInit;
+ if (comphelper::LibreOfficeKit::isActive() && eApp == vcl::EnumContext::Application::Writer
+ && bSkippedFirstInit.find(pViewShell) == bSkippedFirstInit.end())
+ {
+ bSkippedFirstInit[pViewShell] = true;
+ ResetActiveToolbarModeToDefault(eApp);
+ return false;
+ }
+
+ RemoveListeners(pSysWindow);
+
+ OUString aBuf = rUIFile + sFile;
+
+ //Addons For Notebookbar
+ std::vector<Image> aImageValues;
+ std::vector<css::uno::Sequence< css::uno::Sequence< css::beans::PropertyValue > > > aExtensionValues;
+ NotebookBarAddonsItem aNotebookBarAddonsItem;
+ NotebookbarAddonValues(aImageValues , aExtensionValues);
+ aNotebookBarAddonsItem.aAddonValues = aExtensionValues;
+ aNotebookBarAddonsItem.aImageValues = aImageValues;
+
+ // setup if necessary
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ // update the current LOK language and locale for the dialog tunneling
+ comphelper::LibreOfficeKit::setLanguageTag(pViewShell->GetLOKLanguageTag());
+ comphelper::LibreOfficeKit::setLocale(pViewShell->GetLOKLocale());
+ }
+
+ pSysWindow->SetNotebookBar(aBuf, xFrame, aNotebookBarAddonsItem , bReloadNotebookbar);
+ pNotebookBar = pSysWindow->GetNotebookBar();
+ pNotebookBar->Show();
+
+
+ bool hasWeldedWrapper = m_pNotebookBarWeldedWrapper.find(pViewShell) != m_pNotebookBarWeldedWrapper.end();
+ if ((!hasWeldedWrapper || bReloadNotebookbar) && pNotebookBar->IsWelded())
+ {
+ sal_uInt64 nWindowId = reinterpret_cast<sal_uInt64>(pViewShell);
+ m_pNotebookBarWeldedWrapper.emplace(std::make_pair(pViewShell,
+ new WeldedTabbedNotebookbar(pNotebookBar->GetMainContainer(),
+ pNotebookBar->GetUIFilePath(),
+ xFrame,
+ nWindowId)));
+ pNotebookBar->SetDisposeCallback(LINK(nullptr, SfxNotebookBar, VclDisposeHdl), pViewShell);
+ }
+
+ pNotebookBar->GetParent()->Resize();
+
+ utl::OConfigurationTreeRoot aRoot(lcl_getCurrentImplConfigRoot());
+ const utl::OConfigurationNode aModeNode(lcl_getCurrentImplConfigNode(xFrame, aRoot));
+ SfxNotebookBar::ShowMenubar( comphelper::getBOOL( aModeNode.getNodeValue( "HasMenubar" ) ) );
+
+ SfxViewFrame* pView = SfxViewFrame::Current();
+
+ if(pView)
+ {
+ pNotebookBar->ControlListenerForCurrentController(true);
+ }
+ }
+
+ return true;
+ }
+ else if (auto pNotebookBar = pSysWindow->GetNotebookBar())
+ {
+ vcl::Window* pParent = pNotebookBar->GetParent();
+ RemoveListeners(pSysWindow);
+ pSysWindow->CloseNotebookBar();
+ pParent->Resize();
+ SfxNotebookBar::ShowMenubar(true);
+ }
+
+ return false;
+}
+
+void SfxNotebookBar::RemoveListeners(SystemWindow const * pSysWindow)
+{
+ if (auto pNotebookBar = pSysWindow->GetNotebookBar())
+ {
+ pNotebookBar->StopListeningAllControllers();
+ }
+}
+
+void SfxNotebookBar::ShowMenubar(bool bShow)
+{
+ if (m_bLock)
+ return;
+
+ m_bLock = true;
+
+ Reference<frame::XFrame> xFrame;
+ vcl::EnumContext::Application eCurrentApp = vcl::EnumContext::Application::NONE;
+ uno::Reference< uno::XComponentContext > xContext = comphelper::getProcessComponentContext();
+ const Reference<frame::XModuleManager> xModuleManager = frame::ModuleManager::create( xContext );
+
+ if ( SfxViewFrame::Current() )
+ {
+ xFrame = SfxViewFrame::Current()->GetFrame().GetFrameInterface();
+ eCurrentApp = vcl::EnumContext::GetApplicationEnum( xModuleManager->identify( xFrame ) );
+ }
+
+ SfxViewFrame* pViewFrame = SfxViewFrame::GetFirst();
+ while( pViewFrame )
+ {
+ xFrame = pViewFrame->GetFrame().GetFrameInterface();
+ if ( xFrame.is() )
+ {
+ vcl::EnumContext::Application eApp =
+ vcl::EnumContext::GetApplicationEnum( xModuleManager->identify( xFrame ) );
+
+ if ( eApp == eCurrentApp )
+ {
+ const Reference<frame::XLayoutManager>& xLayoutManager =
+ lcl_getLayoutManager( xFrame );
+
+ if (xLayoutManager.is())
+ {
+ if (xLayoutManager->getElement(MENUBAR_STR).is())
+ {
+ if (xLayoutManager->isElementVisible(MENUBAR_STR) && !bShow)
+ xLayoutManager->hideElement(MENUBAR_STR);
+ else if(!xLayoutManager->isElementVisible(MENUBAR_STR) && bShow)
+ xLayoutManager->showElement(MENUBAR_STR);
+ }
+ }
+ }
+ }
+
+ pViewFrame = SfxViewFrame::GetNext( *pViewFrame );
+ }
+ m_bLock = false;
+}
+
+void SfxNotebookBar::ShowMenubar(SfxViewFrame const * pViewFrame, bool bShow)
+{
+ if (m_bLock)
+ return;
+
+ m_bLock = true;
+
+ Reference<frame::XFrame> xFrame = pViewFrame->GetFrame().GetFrameInterface();
+ if (xFrame.is())
+ {
+ const Reference<frame::XLayoutManager>& xLayoutManager = lcl_getLayoutManager(xFrame);
+ if (xLayoutManager.is())
+ {
+ if (xLayoutManager->getElement(MENUBAR_STR).is())
+ {
+ if (xLayoutManager->isElementVisible(MENUBAR_STR) && !bShow)
+ xLayoutManager->hideElement(MENUBAR_STR);
+ else if (!xLayoutManager->isElementVisible(MENUBAR_STR) && bShow)
+ xLayoutManager->showElement(MENUBAR_STR);
+ }
+ }
+ }
+ m_bLock = false;
+}
+
+void SfxNotebookBar::ToggleMenubar()
+{
+ if (!SfxViewFrame::Current())
+ return;
+
+ const Reference<frame::XFrame>& xFrame = SfxViewFrame::Current()->GetFrame().GetFrameInterface();
+ if (!xFrame.is())
+ return;
+
+ const Reference<frame::XLayoutManager>& xLayoutManager =
+ lcl_getLayoutManager(xFrame);
+
+ bool bShow = true;
+ if (xLayoutManager.is() && xLayoutManager->getElement(MENUBAR_STR).is())
+ {
+ if (xLayoutManager->isElementVisible(MENUBAR_STR))
+ {
+ SfxNotebookBar::ShowMenubar(false);
+ bShow = false;
+ }
+ else
+ SfxNotebookBar::ShowMenubar(true);
+ }
+
+ // Save menubar settings
+ if (IsActive())
+ {
+ utl::OConfigurationTreeRoot aRoot(lcl_getCurrentImplConfigRoot());
+ utl::OConfigurationNode aModeNode(lcl_getCurrentImplConfigNode(xFrame, aRoot));
+ aModeNode.setNodeValue( "HasMenubar", toAny<bool>( bShow ) );
+ aRoot.commit();
+ }
+}
+
+void SfxNotebookBar::ReloadNotebookBar(std::u16string_view sUIPath)
+{
+ if (SfxNotebookBar::IsActive())
+ {
+ SfxViewShell* pViewShell = SfxViewShell::Current();
+ sfx2::SfxNotebookBar::StateMethod(pViewShell->GetViewFrame()->GetBindings(), sUIPath, true);
+ }
+}
+
+IMPL_STATIC_LINK(SfxNotebookBar, VclDisposeHdl, const SfxViewShell*, pViewShell, void)
+{
+ m_pNotebookBarWeldedWrapper.erase(pViewShell);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/notify/eventsupplier.cxx b/sfx2/source/notify/eventsupplier.cxx
new file mode 100644
index 000000000..d6bff98de
--- /dev/null
+++ b/sfx2/source/notify/eventsupplier.cxx
@@ -0,0 +1,471 @@
+/* -*- 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/util/URL.hpp>
+#include <com/sun/star/frame/Desktop.hpp>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/util/URLTransformer.hpp>
+#include <com/sun/star/util/XURLTransformer.hpp>
+#include <tools/urlobj.hxx>
+#include <svl/macitem.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/evntconf.hxx>
+#include <unotools/eventcfg.hxx>
+#include <sal/log.hxx>
+
+#include <comphelper/processfactory.hxx>
+#include <comphelper/namedvaluecollection.hxx>
+#include <comphelper/sequence.hxx>
+#include <officecfg/Office/Common.hxx>
+#include <eventsupplier.hxx>
+
+#include <sfx2/app.hxx>
+
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/frame.hxx>
+#include <macroloader.hxx>
+
+#include <unicode/errorcode.h>
+#include <unicode/regex.h>
+#include <unicode/unistr.h>
+
+using namespace css;
+using namespace ::com::sun::star;
+
+
+
+ // --- XNameReplace ---
+
+void SAL_CALL SfxEvents_Impl::replaceByName( const OUString & aName, const uno::Any & rElement )
+{
+ std::unique_lock aGuard( maMutex );
+
+ // find the event in the list and replace the data
+ auto nIndex = comphelper::findValue(maEventNames, aName);
+ if (nIndex == -1)
+ throw container::NoSuchElementException();
+
+ // check for correct type of the element
+ if ( !::comphelper::NamedValueCollection::canExtractFrom( rElement ) )
+ throw lang::IllegalArgumentException();
+ ::comphelper::NamedValueCollection const aEventDescriptor( rElement );
+
+ // create Configuration at first, creation might call this method also and that would overwrite everything
+ // we might have stored before!
+ if ( mpObjShell && !mpObjShell->IsLoading() )
+ {
+ // SetModified will end up calling into our documentEventOccured method
+ aGuard.unlock();
+ mpObjShell->SetModified();
+ aGuard.lock();
+ }
+
+ ::comphelper::NamedValueCollection aNormalizedDescriptor;
+ NormalizeMacro( aEventDescriptor, aNormalizedDescriptor, mpObjShell );
+
+ OUString sType;
+ if ( ( aNormalizedDescriptor.size() == 1 )
+ && !aNormalizedDescriptor.has( PROP_EVENT_TYPE ) //TODO
+ && ( aNormalizedDescriptor.get( PROP_EVENT_TYPE ) >>= sType )
+ && ( sType.isEmpty() )
+ )
+ {
+ // An empty event type means no binding. Therefore reset data
+ // to reflect that state.
+ // (that's for compatibility only. Nowadays, the Tools/Customize dialog should
+ // set an empty sequence to indicate the request for resetting the assignment.)
+ OSL_ENSURE( false, "legacy event assignment format detected" );
+ aNormalizedDescriptor.clear();
+ }
+
+ if ( !aNormalizedDescriptor.empty() )
+ {
+ maEventData[nIndex] = aNormalizedDescriptor.getPropertyValues();
+ }
+ else
+ {
+ maEventData[nIndex] = {};
+ }
+}
+
+
+// --- XNameAccess ---
+
+uno::Any SAL_CALL SfxEvents_Impl::getByName( const OUString& aName )
+{
+ std::unique_lock aGuard( maMutex );
+
+ // find the event in the list and return the data
+
+ auto nIndex = comphelper::findValue(maEventNames, aName);
+ if (nIndex != -1)
+ return uno::Any(maEventData[nIndex]);
+
+ throw container::NoSuchElementException();
+}
+
+
+uno::Sequence< OUString > SAL_CALL SfxEvents_Impl::getElementNames()
+{
+ return maEventNames;
+}
+
+
+sal_Bool SAL_CALL SfxEvents_Impl::hasByName( const OUString& aName )
+{
+ std::unique_lock aGuard( maMutex );
+
+ // find the event in the list and return the data
+
+ return comphelper::findValue(maEventNames, aName) != -1;
+}
+
+
+// --- XElementAccess ( parent of XNameAccess ) ---
+
+uno::Type SAL_CALL SfxEvents_Impl::getElementType()
+{
+ uno::Type aElementType = cppu::UnoType<uno::Sequence < beans::PropertyValue >>::get();
+ return aElementType;
+}
+
+
+sal_Bool SAL_CALL SfxEvents_Impl::hasElements()
+{
+ std::unique_lock aGuard( maMutex );
+
+ return maEventNames.hasElements();
+}
+
+bool SfxEvents_Impl::isScriptURLAllowed(const OUString& aScriptURL)
+{
+ std::optional<css::uno::Sequence<OUString>> allowedEvents(
+ officecfg::Office::Common::Security::Scripting::AllowedDocumentEventURLs::get());
+ // When AllowedDocumentEventURLs is empty, all event URLs are allowed
+ if (!allowedEvents)
+ return true;
+
+ icu::ErrorCode status;
+ const uint32_t rMatcherFlags = UREGEX_CASE_INSENSITIVE;
+ icu::UnicodeString usInput(aScriptURL.getStr());
+ const css::uno::Sequence<OUString>& rAllowedEvents = *allowedEvents;
+ for (auto const& allowedEvent : rAllowedEvents)
+ {
+ icu::UnicodeString usRegex(allowedEvent.getStr());
+ icu::RegexMatcher rmatch1(usRegex, usInput, rMatcherFlags, status);
+ if (aScriptURL.startsWith(allowedEvent) || rmatch1.matches(status))
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void SfxEvents_Impl::Execute( css::uno::Sequence < css::beans::PropertyValue > const & aProperties, const document::DocumentEvent& aTrigger, SfxObjectShell* pDoc )
+{
+ OUString aType;
+ OUString aScript;
+ OUString aLibrary;
+ OUString aMacroName;
+
+ if ( !aProperties.hasElements() )
+ return;
+
+ for ( const auto& rProp : std::as_const(aProperties) )
+ {
+ if ( rProp.Name == PROP_EVENT_TYPE )
+ rProp.Value >>= aType;
+ else if ( rProp.Name == PROP_SCRIPT )
+ rProp.Value >>= aScript;
+ else if ( rProp.Name == PROP_LIBRARY )
+ rProp.Value >>= aLibrary;
+ else if ( rProp.Name == PROP_MACRO_NAME )
+ rProp.Value >>= aMacroName;
+ else {
+ OSL_FAIL("Unknown property value!");
+ }
+ }
+
+ if (aType.isEmpty())
+ {
+ // Empty type means no active binding for the event. Just ignore do nothing.
+ return;
+ }
+
+ if (aScript.isEmpty())
+ return;
+
+ if (!isScriptURLAllowed(aScript))
+ return;
+
+ if (!pDoc)
+ pDoc = SfxObjectShell::Current();
+
+ if (pDoc && !SfxObjectShell::isScriptAccessAllowed(pDoc->GetModel()))
+ return;
+
+ if (aType == STAR_BASIC)
+ {
+ uno::Any aAny;
+ SfxMacroLoader::loadMacro( aScript, aAny, pDoc );
+ }
+ else if (aType == "Service" || aType == "Script")
+ {
+ util::URL aURL;
+ uno::Reference < util::XURLTransformer > xTrans( util::URLTransformer::create( ::comphelper::getProcessComponentContext() ) );
+
+ aURL.Complete = aScript;
+ xTrans->parseStrict( aURL );
+
+ bool bAllowed = !SfxObjectShell::UnTrustedScript(aURL.Complete);
+
+ if (bAllowed)
+ {
+ SfxViewFrame* pView = SfxViewFrame::GetFirst(pDoc);
+
+ uno::Reference
+ < frame::XDispatchProvider > xProv;
+
+ if ( pView != nullptr )
+ {
+ xProv = uno::Reference
+ < frame::XDispatchProvider > (
+ pView->GetFrame().GetFrameInterface(), uno::UNO_QUERY );
+ }
+ else
+ {
+ xProv = frame::Desktop::create( ::comphelper::getProcessComponentContext() );
+ }
+
+ uno::Reference < frame::XDispatch > xDisp;
+ if ( xProv.is() )
+ xDisp = xProv->queryDispatch( aURL, OUString(), 0 );
+
+ if ( xDisp.is() )
+ {
+ beans::PropertyValue aEventParam;
+ aEventParam.Value <<= aTrigger;
+ uno::Sequence< beans::PropertyValue > aDispatchArgs( &aEventParam, 1 );
+ xDisp->dispatch( aURL, aDispatchArgs );
+ }
+ }
+ }
+ else
+ {
+ SAL_WARN( "sfx.notify", "notifyEvent(): Unsupported event type" );
+ }
+}
+
+
+// --- ::document::XEventListener ---
+
+void SAL_CALL SfxEvents_Impl::documentEventOccured( const document::DocumentEvent& aEvent )
+{
+ std::unique_lock aGuard( maMutex );
+
+ // get the event name, find the corresponding data, execute the data
+
+ auto nIndex = comphelper::findValue(maEventNames, aEvent.EventName);
+ if ( nIndex == -1 )
+ return;
+
+ css::uno::Sequence < css::beans::PropertyValue > aEventData = maEventData[ nIndex ];
+ aGuard.unlock();
+ Execute( aEventData, aEvent, mpObjShell );
+}
+
+
+// --- ::lang::XEventListener ---
+
+void SAL_CALL SfxEvents_Impl::disposing( const lang::EventObject& /*Source*/ )
+{
+ std::unique_lock aGuard( maMutex );
+
+ if ( mxBroadcaster.is() )
+ {
+ mxBroadcaster->removeDocumentEventListener( this );
+ mxBroadcaster = nullptr;
+ }
+}
+
+
+SfxEvents_Impl::SfxEvents_Impl( SfxObjectShell* pShell,
+ uno::Reference< document::XDocumentEventBroadcaster > const & xBroadcaster )
+{
+ // get the list of supported events and store it
+ if ( pShell )
+ maEventNames = pShell->GetEventNames();
+ else
+ maEventNames = rtl::Reference<GlobalEventConfig>(new GlobalEventConfig)->getElementNames();
+
+ maEventData.resize( maEventNames.getLength() );
+
+ mpObjShell = pShell;
+ mxBroadcaster = xBroadcaster;
+
+ if ( mxBroadcaster.is() )
+ mxBroadcaster->addDocumentEventListener( this );
+}
+
+
+SfxEvents_Impl::~SfxEvents_Impl()
+{
+}
+
+
+std::unique_ptr<SvxMacro> SfxEvents_Impl::ConvertToMacro( const uno::Any& rElement, SfxObjectShell* pObjShell )
+{
+ std::unique_ptr<SvxMacro> pMacro;
+ uno::Sequence < beans::PropertyValue > aProperties;
+ uno::Any aAny;
+ NormalizeMacro( rElement, aAny, pObjShell );
+
+ if ( aAny >>= aProperties )
+ {
+ OUString aType;
+ OUString aScriptURL;
+ OUString aLibrary;
+ OUString aMacroName;
+
+ if ( !aProperties.hasElements() )
+ return pMacro;
+
+ for ( const auto& rProp : std::as_const(aProperties) )
+ {
+ if ( rProp.Name == PROP_EVENT_TYPE )
+ rProp.Value >>= aType;
+ else if ( rProp.Name == PROP_SCRIPT )
+ rProp.Value >>= aScriptURL;
+ else if ( rProp.Name == PROP_LIBRARY )
+ rProp.Value >>= aLibrary;
+ else if ( rProp.Name == PROP_MACRO_NAME )
+ rProp.Value >>= aMacroName;
+ else {
+ OSL_FAIL("Unknown property value!");
+ }
+ }
+
+ // Get the type
+ ScriptType eType( STARBASIC );
+ if ( aType == STAR_BASIC )
+ eType = STARBASIC;
+ else if (aType == "Script" && !aScriptURL.isEmpty())
+ eType = EXTENDED_STYPE;
+ else if ( aType == SVX_MACRO_LANGUAGE_JAVASCRIPT )
+ eType = JAVASCRIPT;
+ else {
+ SAL_WARN( "sfx.notify", "ConvertToMacro: Unknown macro type" );
+ }
+
+ if ( !aMacroName.isEmpty() )
+ {
+ if ( aLibrary == "application" )
+ aLibrary = SfxGetpApp()->GetName();
+ else
+ aLibrary.clear();
+ pMacro.reset(new SvxMacro( aMacroName, aLibrary, eType ));
+ }
+ else if ( eType == EXTENDED_STYPE )
+ pMacro.reset(new SvxMacro( aScriptURL, aType ));
+ }
+
+ return pMacro;
+}
+
+void SfxEvents_Impl::NormalizeMacro( const uno::Any& rEvent, uno::Any& rRet, SfxObjectShell* pDoc )
+{
+ const ::comphelper::NamedValueCollection aEventDescriptor( rEvent );
+ ::comphelper::NamedValueCollection aEventDescriptorOut;
+
+ NormalizeMacro( aEventDescriptor, aEventDescriptorOut, pDoc );
+
+ rRet <<= aEventDescriptorOut.getPropertyValues();
+}
+
+void SfxEvents_Impl::NormalizeMacro( const ::comphelper::NamedValueCollection& i_eventDescriptor,
+ ::comphelper::NamedValueCollection& o_normalizedDescriptor, SfxObjectShell* i_document )
+{
+ SfxObjectShell* pDoc = i_document;
+ if ( !pDoc )
+ pDoc = SfxObjectShell::Current();
+
+ OUString aType = i_eventDescriptor.getOrDefault( PROP_EVENT_TYPE, OUString() );
+ OUString aScript = i_eventDescriptor.getOrDefault( PROP_SCRIPT, OUString() );
+ OUString aLibrary = i_eventDescriptor.getOrDefault( PROP_LIBRARY, OUString() );
+ OUString aMacroName = i_eventDescriptor.getOrDefault( PROP_MACRO_NAME, OUString() );
+
+ if ( !aType.isEmpty() )
+ o_normalizedDescriptor.put( PROP_EVENT_TYPE, aType );
+ if ( !aScript.isEmpty() )
+ o_normalizedDescriptor.put( PROP_SCRIPT, aScript );
+
+ if ( aType != STAR_BASIC )
+ return;
+
+ if ( !aScript.isEmpty() )
+ {
+ if ( aMacroName.isEmpty() || aLibrary.isEmpty() )
+ {
+ sal_Int32 nThirdSlashPos = aScript.indexOf( '/', 8 );
+ sal_Int32 nArgsPos = aScript.indexOf( '(' );
+ if ( ( nThirdSlashPos != -1 ) && ( nArgsPos == -1 || nThirdSlashPos < nArgsPos ) )
+ {
+ OUString aBasMgrName( INetURLObject::decode( aScript.subView( 8, nThirdSlashPos-8 ), INetURLObject::DecodeMechanism::WithCharset ) );
+ if (pDoc && aBasMgrName == ".")
+ aLibrary = pDoc->GetTitle();
+ else
+ aLibrary = SfxGetpApp()->GetName();
+
+ // Get the macro name
+ aMacroName = aScript.copy( nThirdSlashPos+1, nArgsPos - nThirdSlashPos - 1 );
+ }
+ else
+ {
+ SAL_WARN( "sfx.notify", "ConvertToMacro: Unknown macro url format" );
+ }
+ }
+ }
+ else if ( !aMacroName.isEmpty() )
+ {
+ aScript = "macro://";
+ if ( aLibrary != SfxGetpApp()->GetName() && aLibrary != "StarDesktop" && aLibrary != "application" )
+ aScript += ".";
+ aScript += "/" + aMacroName + "()";
+ }
+ else
+ // wrong properties
+ return;
+
+ if (aLibrary != "document")
+ {
+ if ( aLibrary.isEmpty() || (pDoc && ( aLibrary == pDoc->GetTitle( SFX_TITLE_APINAME ) || aLibrary == pDoc->GetTitle() )) )
+ aLibrary = "document";
+ else
+ aLibrary = "application";
+ }
+
+ o_normalizedDescriptor.put( PROP_SCRIPT, aScript );
+ o_normalizedDescriptor.put( PROP_LIBRARY, aLibrary );
+ o_normalizedDescriptor.put( PROP_MACRO_NAME, aMacroName );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/notify/globalevents.cxx b/sfx2/source/notify/globalevents.cxx
new file mode 100644
index 000000000..cd6b08007
--- /dev/null
+++ b/sfx2/source/notify/globalevents.cxx
@@ -0,0 +1,521 @@
+/* -*- 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/types.h>
+
+#include <com/sun/star/task/theJobExecutor.hpp>
+#include <com/sun/star/container/XNameReplace.hpp>
+#include <com/sun/star/container/XSet.hpp>
+#include <com/sun/star/document/XEventListener.hpp>
+#include <com/sun/star/document/XEventBroadcaster.hpp>
+#include <com/sun/star/document/XDocumentEventListener.hpp>
+#include <com/sun/star/frame/XGlobalEventBroadcaster.hpp>
+#include <com/sun/star/lang/DisposedException.hpp>
+#include <com/sun/star/lang/NoSupportException.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/uno/Type.hxx>
+
+#include <cppuhelper/implbase.hxx>
+#include <comphelper/interfacecontainer4.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <comphelper/enumhelper.hxx>
+#include <rtl/ref.hxx>
+#include <sfx2/app.hxx>
+#include <tools/diagnose_ex.h>
+#include <unotools/eventcfg.hxx>
+#include <eventsupplier.hxx>
+
+#include <mutex>
+#include <set>
+#include <vector>
+
+using namespace css;
+
+namespace {
+
+typedef ::std::vector< css::uno::Reference< css::frame::XModel > > TModelList;
+
+
+//TODO: remove support of obsolete document::XEventBroadcaster/Listener
+class SfxGlobalEvents_Impl : public ::cppu::WeakImplHelper< css::lang::XServiceInfo
+ , css::frame::XGlobalEventBroadcaster
+ , css::document::XEventBroadcaster
+ , css::document::XEventListener
+ , css::lang::XComponent
+ >
+{
+ std::mutex m_aLock;
+ rtl::Reference< GlobalEventConfig > m_xEvents;
+ css::uno::Reference< css::document::XEventListener > m_xJobExecutorListener;
+ ::comphelper::OInterfaceContainerHelper4<document::XEventListener> m_aLegacyListeners;
+ ::comphelper::OInterfaceContainerHelper4<document::XDocumentEventListener> m_aDocumentListeners;
+ std::multiset<css::uno::Reference<css::lang::XEventListener>> m_disposeListeners;
+ TModelList m_lModels;
+ bool m_disposed;
+
+public:
+ explicit SfxGlobalEvents_Impl(const css::uno::Reference < css::uno::XComponentContext >& rxContext);
+
+ virtual OUString SAL_CALL getImplementationName() override
+ {
+ return "com.sun.star.comp.sfx2.GlobalEventBroadcaster";
+ }
+
+ virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override
+ {
+ return cppu::supportsService(this, ServiceName);
+ }
+
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override
+ {
+ css::uno::Sequence< OUString > aSeq { "com.sun.star.frame.GlobalEventBroadcaster" };
+ return aSeq;
+ }
+
+ // css.document.XEventBroadcaster
+ virtual css::uno::Reference< css::container::XNameReplace > SAL_CALL getEvents() override;
+
+ virtual void SAL_CALL addEventListener(const css::uno::Reference< css::document::XEventListener >& xListener) override;
+
+ virtual void SAL_CALL removeEventListener( const css::uno::Reference< css::document::XEventListener >& xListener) override;
+
+ // css.document.XDocumentEventBroadcaster
+ virtual void SAL_CALL addDocumentEventListener( const css::uno::Reference< css::document::XDocumentEventListener >& Listener ) override;
+ virtual void SAL_CALL removeDocumentEventListener( const css::uno::Reference< css::document::XDocumentEventListener >& Listener ) override;
+ virtual void SAL_CALL notifyDocumentEvent( const OUString& EventName, const css::uno::Reference< css::frame::XController2 >& ViewController, const css::uno::Any& Supplement ) override;
+
+ // css.document.XEventListener
+ virtual void SAL_CALL notifyEvent(const css::document::EventObject& aEvent) override;
+
+ // css.document.XDocumentEventListener
+ virtual void SAL_CALL documentEventOccured( const css::document::DocumentEvent& Event ) override;
+
+ // css.container.XSet
+ virtual sal_Bool SAL_CALL has(const css::uno::Any& aElement) override;
+
+ virtual void SAL_CALL insert(const css::uno::Any& aElement) override;
+
+ virtual void SAL_CALL remove(const css::uno::Any& aElement) override;
+
+ // css.container.XEnumerationAccess
+ virtual css::uno::Reference< css::container::XEnumeration > SAL_CALL createEnumeration() override;
+
+ // css.container.XElementAccess
+ virtual css::uno::Type SAL_CALL getElementType() override;
+
+ virtual sal_Bool SAL_CALL hasElements() override;
+
+ // css.lang.XEventListener
+ virtual void SAL_CALL disposing(const css::lang::EventObject& aEvent) override;
+
+ // css.lang.XComponent
+ void SAL_CALL dispose() override;
+
+ void SAL_CALL addEventListener(css::uno::Reference<css::lang::XEventListener> const & xListener)
+ override;
+
+ void SAL_CALL removeEventListener(
+ css::uno::Reference<css::lang::XEventListener> const & aListener) override;
+
+private:
+
+ // threadsafe
+ void implts_notifyJobExecution(const css::document::EventObject& aEvent);
+ void implts_checkAndExecuteEventBindings(const css::document::DocumentEvent& aEvent);
+ void implts_notifyListener(const css::document::DocumentEvent& aEvent);
+
+ // not threadsafe
+ TModelList::iterator impl_searchDoc(const css::uno::Reference< css::frame::XModel >& xModel);
+};
+
+SfxGlobalEvents_Impl::SfxGlobalEvents_Impl( const uno::Reference < uno::XComponentContext >& rxContext)
+ : m_xJobExecutorListener( task::theJobExecutor::get( rxContext ), uno::UNO_QUERY_THROW )
+ , m_disposed(false)
+{
+ osl_atomic_increment(&m_refCount);
+ SfxApplication::GetOrCreate();
+ m_xEvents = new GlobalEventConfig();
+ osl_atomic_decrement(&m_refCount);
+}
+
+uno::Reference< container::XNameReplace > SAL_CALL SfxGlobalEvents_Impl::getEvents()
+{
+ // SAFE ->
+ std::scoped_lock aLock(m_aLock);
+ if (m_disposed) {
+ throw css::lang::DisposedException();
+ }
+ return m_xEvents;
+ // <- SAFE
+}
+
+
+void SAL_CALL SfxGlobalEvents_Impl::addEventListener(const uno::Reference< document::XEventListener >& xListener)
+{
+ std::unique_lock g(m_aLock);
+ if (m_disposed)
+ throw css::lang::DisposedException();
+ m_aLegacyListeners.addInterface(g, xListener);
+}
+
+
+void SAL_CALL SfxGlobalEvents_Impl::removeEventListener(const uno::Reference< document::XEventListener >& xListener)
+{
+ std::unique_lock g(m_aLock);
+ // The below removeInterface will silently do nothing when m_disposed
+ m_aLegacyListeners.removeInterface(g, xListener);
+}
+
+
+void SAL_CALL SfxGlobalEvents_Impl::addDocumentEventListener( const uno::Reference< document::XDocumentEventListener >& Listener )
+{
+ std::unique_lock g(m_aLock);
+ if (m_disposed)
+ throw css::lang::DisposedException();
+ m_aDocumentListeners.addInterface( g, Listener );
+}
+
+
+void SAL_CALL SfxGlobalEvents_Impl::removeDocumentEventListener( const uno::Reference< document::XDocumentEventListener >& Listener )
+{
+ std::unique_lock g(m_aLock);
+ // The below removeInterface will silently do nothing when m_disposed:
+ m_aDocumentListeners.removeInterface( g, Listener );
+}
+
+
+void SAL_CALL SfxGlobalEvents_Impl::notifyDocumentEvent( const OUString& /*_EventName*/,
+ const uno::Reference< frame::XController2 >& /*_ViewController*/, const uno::Any& /*_Supplement*/ )
+{
+ // we're a multiplexer only, no chance to generate artificial events here
+ throw lang::NoSupportException(OUString(), *this);
+}
+
+
+void SAL_CALL SfxGlobalEvents_Impl::notifyEvent(const document::EventObject& aEvent)
+{
+ // The below implts_* will silently do nothing when m_disposed:
+ document::DocumentEvent aDocEvent(aEvent.Source, aEvent.EventName, nullptr, uno::Any());
+ implts_notifyJobExecution(aEvent);
+ implts_checkAndExecuteEventBindings(aDocEvent);
+ implts_notifyListener(aDocEvent);
+}
+
+
+void SAL_CALL SfxGlobalEvents_Impl::documentEventOccured( const document::DocumentEvent& Event )
+{
+ // The below implts_* will silently do nothing when m_disposed:
+ implts_notifyJobExecution(document::EventObject(Event.Source, Event.EventName));
+ implts_checkAndExecuteEventBindings(Event);
+ implts_notifyListener(Event);
+}
+
+
+void SAL_CALL SfxGlobalEvents_Impl::disposing(const lang::EventObject& aEvent)
+{
+ uno::Reference< frame::XModel > xDoc(aEvent.Source, uno::UNO_QUERY);
+
+ // SAFE ->
+ std::scoped_lock g(m_aLock);
+ TModelList::iterator pIt = impl_searchDoc(xDoc);
+ if (pIt != m_lModels.end())
+ m_lModels.erase(pIt);
+ // <- SAFE
+}
+
+void SfxGlobalEvents_Impl::dispose() {
+ std::multiset<css::uno::Reference<css::lang::XEventListener>> listeners;
+ {
+ std::unique_lock g(m_aLock);
+ if (m_disposed)
+ return;
+ m_disposed = true;
+ auto tmpEvents = std::move(m_xEvents);
+ auto tmpModels = std::move(m_lModels);
+ m_xJobExecutorListener.clear();
+ m_disposeListeners.swap(listeners);
+ m_lModels.clear();
+ g.unlock();
+ // clear events&models outside lock because it will trigger a call back into us
+ tmpEvents.clear();
+ tmpModels.clear();
+ g.lock();
+ m_aLegacyListeners.disposeAndClear(g, {static_cast<OWeakObject *>(this)});
+ m_aDocumentListeners.disposeAndClear(g, {static_cast<OWeakObject *>(this)});
+ }
+ for (auto const & i: listeners) {
+ try {
+ i->disposing({static_cast< cppu::OWeakObject * >(this)});
+ } catch (css::lang::DisposedException &) {}
+ }
+}
+
+void SfxGlobalEvents_Impl::addEventListener(
+ css::uno::Reference<css::lang::XEventListener> const & xListener)
+{
+ if (!xListener.is()) {
+ throw css::uno::RuntimeException("null listener");
+ }
+ {
+ std::scoped_lock g(m_aLock);
+ if (!m_disposed) {
+ m_disposeListeners.insert(xListener);
+ return;
+ }
+ }
+ try {
+ xListener->disposing({static_cast< cppu::OWeakObject * >(this)});
+ } catch (css::lang::DisposedException &) {}
+}
+
+void SfxGlobalEvents_Impl::removeEventListener(
+ css::uno::Reference<css::lang::XEventListener> const & aListener)
+{
+ std::scoped_lock g(m_aLock);
+ auto const i = m_disposeListeners.find(aListener);
+ if (i != m_disposeListeners.end()) {
+ m_disposeListeners.erase(i);
+ }
+}
+
+sal_Bool SAL_CALL SfxGlobalEvents_Impl::has(const uno::Any& aElement)
+{
+ uno::Reference< frame::XModel > xDoc;
+ aElement >>= xDoc;
+
+ bool bHas = false;
+
+ // SAFE ->
+ std::scoped_lock g(m_aLock);
+ if (m_disposed) {
+ throw css::lang::DisposedException();
+ }
+ TModelList::iterator pIt = impl_searchDoc(xDoc);
+ if (pIt != m_lModels.end())
+ bHas = true;
+ // <- SAFE
+
+ return bHas;
+}
+
+
+void SAL_CALL SfxGlobalEvents_Impl::insert( const uno::Any& aElement )
+{
+ uno::Reference< frame::XModel > xDoc;
+ aElement >>= xDoc;
+ if (!xDoc.is())
+ throw lang::IllegalArgumentException(
+ "Can not locate at least the model parameter.",
+ static_cast< container::XSet* >(this),
+ 0);
+
+ // SAFE ->
+ {
+ std::scoped_lock g(m_aLock);
+ if (m_disposed) {
+ throw css::lang::DisposedException();
+ }
+ TModelList::iterator pIt = impl_searchDoc(xDoc);
+ if (pIt != m_lModels.end())
+ throw container::ElementExistException(
+ OUString(),
+ static_cast<container::XSet*>(this));
+ m_lModels.push_back(xDoc);
+ }
+ // <- SAFE
+
+ uno::Reference< document::XDocumentEventBroadcaster > xDocBroadcaster(xDoc, uno::UNO_QUERY );
+ if (xDocBroadcaster.is())
+ xDocBroadcaster->addDocumentEventListener(this);
+ else
+ {
+ // try the "legacy version" of XDocumentEventBroadcaster, which is XEventBroadcaster
+ uno::Reference< document::XEventBroadcaster > xBroadcaster(xDoc, uno::UNO_QUERY);
+ if (xBroadcaster.is())
+ xBroadcaster->addEventListener(static_cast< document::XEventListener* >(this));
+ }
+}
+
+
+void SAL_CALL SfxGlobalEvents_Impl::remove( const uno::Any& aElement )
+{
+ uno::Reference< frame::XModel > xDoc;
+ aElement >>= xDoc;
+ if (!xDoc.is())
+ throw lang::IllegalArgumentException(
+ "Can not locate at least the model parameter.",
+ static_cast< container::XSet* >(this),
+ 0);
+
+ // SAFE ->
+ {
+ std::scoped_lock g(m_aLock);
+ TModelList::iterator pIt = impl_searchDoc(xDoc);
+ if (pIt == m_lModels.end())
+ throw container::NoSuchElementException(
+ OUString(),
+ static_cast<container::XSet*>(this));
+ m_lModels.erase(pIt);
+ }
+ // <- SAFE
+
+ uno::Reference< document::XDocumentEventBroadcaster > xDocBroadcaster(xDoc, uno::UNO_QUERY );
+ if (xDocBroadcaster.is())
+ xDocBroadcaster->removeDocumentEventListener(this);
+ else
+ {
+ // try the "legacy version" of XDocumentEventBroadcaster, which is XEventBroadcaster
+ uno::Reference< document::XEventBroadcaster > xBroadcaster(xDoc, uno::UNO_QUERY);
+ if (xBroadcaster.is())
+ xBroadcaster->removeEventListener(static_cast< document::XEventListener* >(this));
+ }
+}
+
+
+uno::Reference< container::XEnumeration > SAL_CALL SfxGlobalEvents_Impl::createEnumeration()
+{
+ // SAFE ->
+ std::scoped_lock g(m_aLock);
+ if (m_disposed) {
+ throw css::lang::DisposedException();
+ }
+ uno::Sequence<uno::Any> models(m_lModels.size());
+ auto modelsRange = asNonConstRange(models);
+ for (size_t i = 0; i < m_lModels.size(); ++i)
+ {
+ modelsRange[i] <<= m_lModels[i];
+ }
+ uno::Reference<container::XEnumeration> xEnum(new ::comphelper::OAnyEnumeration(models));
+ // <- SAFE
+
+ return xEnum;
+}
+
+
+uno::Type SAL_CALL SfxGlobalEvents_Impl::getElementType()
+{
+ return cppu::UnoType<frame::XModel>::get();
+}
+
+
+sal_Bool SAL_CALL SfxGlobalEvents_Impl::hasElements()
+{
+ // SAFE ->
+ std::scoped_lock g(m_aLock);
+ if (m_disposed) {
+ throw css::lang::DisposedException();
+ }
+ return (!m_lModels.empty());
+ // <- SAFE
+}
+
+
+void SfxGlobalEvents_Impl::implts_notifyJobExecution(const document::EventObject& aEvent)
+{
+ css::uno::Reference<css::document::XEventListener> listener;
+ {
+ std::scoped_lock g(m_aLock);
+ listener = m_xJobExecutorListener;
+ }
+ if (!listener.is()) {
+ return;
+ }
+ try
+ {
+ listener->notifyEvent(aEvent);
+ }
+ catch(const uno::RuntimeException&)
+ { throw; }
+ catch(const uno::Exception&)
+ {}
+}
+
+
+void SfxGlobalEvents_Impl::implts_checkAndExecuteEventBindings(const document::DocumentEvent& aEvent)
+{
+ rtl::Reference<GlobalEventConfig> events;
+ {
+ std::scoped_lock g(m_aLock);
+ events = m_xEvents;
+ }
+ if (!events.is()) {
+ return;
+ }
+ try
+ {
+ if ( events->hasByName( aEvent.EventName ) )
+ {
+ uno::Sequence < beans::PropertyValue > aAny = events->getByName2(aEvent.EventName);
+ SfxEvents_Impl::Execute(aAny, aEvent, nullptr);
+ }
+ }
+ catch ( uno::RuntimeException const & )
+ {
+ throw;
+ }
+ catch ( uno::Exception const & )
+ {
+ DBG_UNHANDLED_EXCEPTION("sfx.notify");
+ }
+}
+
+
+void SfxGlobalEvents_Impl::implts_notifyListener(const document::DocumentEvent& aEvent)
+{
+ std::unique_lock g(m_aLock);
+
+ document::EventObject aLegacyEvent(aEvent.Source, aEvent.EventName);
+ m_aLegacyListeners.forEach(g,
+ [&aLegacyEvent](const css::uno::Reference<document::XEventListener>& xListener)
+ {
+ xListener->notifyEvent(aLegacyEvent);
+ }
+ );
+ m_aDocumentListeners.forEach(g,
+ [&aEvent](const css::uno::Reference<document::XDocumentEventListener>& xListener)
+ {
+ xListener->documentEventOccured(aEvent);
+ }
+ );
+}
+
+
+// not threadsafe ... must be locked from outside!
+TModelList::iterator SfxGlobalEvents_Impl::impl_searchDoc(const uno::Reference< frame::XModel >& xModel)
+{
+ if (!xModel.is())
+ return m_lModels.end();
+
+ return std::find_if(m_lModels.begin(), m_lModels.end(),
+ [&xModel](const uno::Reference< frame::XModel >& rxModel) {
+ return rxModel == xModel;
+ });
+}
+
+} // namespace
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_sfx2_GlobalEventBroadcaster_get_implementation(
+ css::uno::XComponentContext *context,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new SfxGlobalEvents_Impl(context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/notify/hintpost.cxx b/sfx2/source/notify/hintpost.cxx
new file mode 100644
index 000000000..446c7a954
--- /dev/null
+++ b/sfx2/source/notify/hintpost.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 <hintpost.hxx>
+
+#include <sfx2/request.hxx>
+#include <vcl/svapp.hxx>
+
+SfxHintPoster::SfxHintPoster(const std::function<void(std::unique_ptr<SfxRequest>)>& rLink)
+ : m_Link(rLink)
+{
+}
+
+SfxHintPoster::~SfxHintPoster() {}
+
+void SfxHintPoster::Post(std::unique_ptr<SfxRequest> pHintToPost)
+{
+ Application::PostUserEvent((LINK(this, SfxHintPoster, DoEvent_Impl)), pHintToPost.release());
+ AddFirstRef();
+}
+
+IMPL_LINK(SfxHintPoster, DoEvent_Impl, void*, pPostedHint, void)
+{
+ auto pRequest = static_cast<SfxRequest*>(pPostedHint);
+ if (m_Link)
+ m_Link(std::unique_ptr<SfxRequest>(pRequest));
+ else
+ delete pRequest;
+ ReleaseRef();
+}
+
+void SfxHintPoster::SetEventHdl(const std::function<void(std::unique_ptr<SfxRequest>)>& rLink)
+{
+ m_Link = rLink;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/notify/openurlhint.cxx b/sfx2/source/notify/openurlhint.cxx
new file mode 100644
index 000000000..ca831a6f4
--- /dev/null
+++ b/sfx2/source/notify/openurlhint.cxx
@@ -0,0 +1,32 @@
+/* -*- 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 <openurlhint.hxx>
+
+SfxOpenUrlHint::SfxOpenUrlHint( const OUString& sDocumentURL ) :
+ msDocumentURL(sDocumentURL) { }
+
+const OUString& SfxOpenUrlHint::GetDocumentURL() const
+{
+ return msDocumentURL;
+}
+
+SfxOpenUrlHint::~SfxOpenUrlHint() {}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/safemode/safemode.cxx b/sfx2/source/safemode/safemode.cxx
new file mode 100644
index 000000000..f61001bec
--- /dev/null
+++ b/sfx2/source/safemode/safemode.cxx
@@ -0,0 +1,81 @@
+/* -*- 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/safemode.hxx>
+
+#include <config_folders.h>
+
+#include <osl/file.hxx>
+#include <rtl/bootstrap.hxx>
+
+using namespace osl;
+
+namespace sfx2
+{
+bool SafeMode::putFlag()
+{
+ File safeModeFile(getFilePath("safemode"));
+ if (safeModeFile.open(osl_File_OpenFlag_Create) == FileBase::E_None)
+ {
+ safeModeFile.close();
+ return true;
+ }
+ return false;
+}
+bool SafeMode::hasFlag()
+{
+ File safeModeFile(getFilePath("safemode"));
+ if (safeModeFile.open(osl_File_OpenFlag_Read) == FileBase::E_None)
+ {
+ safeModeFile.close();
+ return true;
+ }
+ return false;
+}
+bool SafeMode::removeFlag() { return File::remove(getFilePath("safemode")) == FileBase::E_None; }
+
+bool SafeMode::putRestartFlag()
+{
+ File restartFile(getFilePath("safemode_restart"));
+ if (restartFile.open(osl_File_OpenFlag_Create) == FileBase::E_None)
+ {
+ restartFile.close();
+ return true;
+ }
+ return false;
+}
+bool SafeMode::hasRestartFlag()
+{
+ File restartFile(getFilePath("safemode_restart"));
+ if (restartFile.open(osl_File_OpenFlag_Read) == FileBase::E_None)
+ {
+ restartFile.close();
+ return true;
+ }
+ return false;
+}
+bool SafeMode::removeRestartFlag()
+{
+ return File::remove(getFilePath("safemode_restart")) == FileBase::E_None;
+}
+
+OUString SafeMode::getFilePath(const OUString& sFilename)
+{
+ OUString url("${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER
+ "/" SAL_CONFIGFILE("bootstrap") ":UserInstallation}/");
+ rtl::Bootstrap::expandMacros(url);
+
+ OUString aProfilePath;
+ FileBase::getSystemPathFromFileURL(url, aProfilePath);
+ (void)FileBase::getAbsoluteFileURL(url, sFilename, aProfilePath);
+ return aProfilePath;
+}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/sidebar/AsynchronousCall.cxx b/sfx2/source/sidebar/AsynchronousCall.cxx
new file mode 100644
index 000000000..5a3ce8db2
--- /dev/null
+++ b/sfx2/source/sidebar/AsynchronousCall.cxx
@@ -0,0 +1,71 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sfx2/sidebar/AsynchronousCall.hxx>
+#include <vcl/svapp.hxx>
+
+namespace sfx2::sidebar {
+
+AsynchronousCall::AsynchronousCall (const Action& rAction)
+ : maAction(rAction),
+ mnCallId(nullptr)
+{
+}
+
+AsynchronousCall::~AsynchronousCall()
+{
+ CancelRequest();
+}
+
+void AsynchronousCall::RequestCall()
+{
+ if (mnCallId == nullptr)
+ {
+ Link<void*,void> aLink (LINK(this, AsynchronousCall, HandleUserCall));
+ mnCallId = Application::PostUserEvent(aLink);
+ }
+}
+
+void AsynchronousCall::CancelRequest()
+{
+ if (mnCallId != nullptr)
+ {
+ Application::RemoveUserEvent(mnCallId);
+ mnCallId = nullptr;
+ }
+}
+
+void AsynchronousCall::Sync()
+{
+ if (mnCallId != nullptr) {
+ maAction();
+ CancelRequest();
+ }
+}
+
+IMPL_LINK_NOARG(AsynchronousCall, HandleUserCall, void*, void )
+{
+ mnCallId = nullptr;
+ if (maAction)
+ maAction();
+}
+
+} // end of namespace sfx2::sidebar
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/sidebar/Context.cxx b/sfx2/source/sidebar/Context.cxx
new file mode 100644
index 000000000..2065fbd04
--- /dev/null
+++ b/sfx2/source/sidebar/Context.cxx
@@ -0,0 +1,78 @@
+/* -*- 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/sidebar/Context.hxx>
+
+
+constexpr OUStringLiteral AnyApplicationName = u"any";
+constexpr OUStringLiteral AnyContextName = u"any";
+
+namespace sfx2::sidebar {
+
+const sal_Int32 Context::NoMatch = 4;
+const sal_Int32 Context::ApplicationWildcardMatch = 1;
+const sal_Int32 Context::ContextWildcardMatch = 2;
+const sal_Int32 Context::OptimalMatch = 0; // Neither application nor context name is "any".
+
+Context::Context()
+ : msApplication(AnyApplicationName),
+ msContext(AnyContextName)
+{
+}
+
+Context::Context (
+ const OUString& rsApplication,
+ const OUString& rsContext)
+ : msApplication(rsApplication),
+ msContext(rsContext)
+{
+}
+
+sal_Int32 Context::EvaluateMatch (
+ const Context& rOther) const
+{
+ const bool bApplicationNameIsAny (rOther.msApplication == AnyApplicationName);
+ if (rOther.msApplication == msApplication || bApplicationNameIsAny)
+ {
+ // Application name matches.
+ const bool bContextNameIsAny (rOther.msContext == AnyContextName);
+ if (rOther.msContext == msContext || bContextNameIsAny)
+ {
+ // Context name matches.
+ return (bApplicationNameIsAny ? ApplicationWildcardMatch : 0)
+ + (bContextNameIsAny ? ContextWildcardMatch : 0);
+ }
+ }
+ return NoMatch;
+}
+
+bool Context::operator== (const Context& rOther) const
+{
+ return msApplication == rOther.msApplication
+ && msContext == rOther.msContext;
+}
+
+bool Context::operator!= (const Context& rOther) const
+{
+ return ( msApplication != rOther.msApplication)
+ || ( msContext != rOther.msContext);
+}
+
+} // end of namespace sfx2::sidebar
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/sidebar/ContextChangeBroadcaster.cxx b/sfx2/source/sidebar/ContextChangeBroadcaster.cxx
new file mode 100644
index 000000000..0350929ee
--- /dev/null
+++ b/sfx2/source/sidebar/ContextChangeBroadcaster.cxx
@@ -0,0 +1,127 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#include <sidebar/ContextChangeBroadcaster.hxx>
+#include <vcl/EnumContext.hxx>
+#include <com/sun/star/ui/ContextChangeEventObject.hpp>
+#include <com/sun/star/ui/ContextChangeEventMultiplexer.hpp>
+#include <com/sun/star/frame/ModuleManager.hpp>
+#include <osl/diagnose.h>
+#include <comphelper/lok.hxx>
+#include <comphelper/processfactory.hxx>
+#include <sfx2/lokhelper.hxx>
+#include <sfx2/viewsh.hxx>
+
+using namespace css;
+using namespace css::uno;
+
+namespace sfx2::sidebar {
+
+ContextChangeBroadcaster::ContextChangeBroadcaster()
+ : mbIsBroadcasterEnabled(true)
+{
+}
+
+ContextChangeBroadcaster::~ContextChangeBroadcaster()
+{
+}
+
+void ContextChangeBroadcaster::Initialize (const OUString& rsContextName)
+{
+ msContextName = rsContextName;
+}
+
+void ContextChangeBroadcaster::Activate (const css::uno::Reference<css::frame::XFrame>& rxFrame)
+{
+ if (msContextName.getLength() > 0)
+ BroadcastContextChange(rxFrame, GetModuleName(rxFrame), msContextName);
+}
+
+void ContextChangeBroadcaster::Deactivate (const css::uno::Reference<css::frame::XFrame>& rxFrame)
+{
+ if (msContextName.getLength() > 0 && !comphelper::LibreOfficeKit::isActive())
+ {
+ BroadcastContextChange(rxFrame, GetModuleName(rxFrame),
+ vcl::EnumContext::GetContextName(vcl::EnumContext::Context::Default));
+ }
+}
+
+bool ContextChangeBroadcaster::SetBroadcasterEnabled (const bool bIsEnabled)
+{
+ const bool bWasEnabled (mbIsBroadcasterEnabled);
+ mbIsBroadcasterEnabled = bIsEnabled;
+ return bWasEnabled;
+}
+
+void ContextChangeBroadcaster::BroadcastContextChange (
+ const css::uno::Reference<css::frame::XFrame>& rxFrame,
+ const OUString& rsModuleName,
+ const OUString& rsContextName)
+{
+ if ( ! mbIsBroadcasterEnabled)
+ return;
+
+ if (rsContextName.getLength() == 0)
+ return;
+
+ if ( ! rxFrame.is() || ! rxFrame->getController().is())
+ {
+ // Frame is (probably) being deleted. Broadcasting context
+ // changes is not necessary anymore.
+ return;
+ }
+
+ // notify the LOK too
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ if (SfxViewShell* pViewShell = SfxViewShell::Get(rxFrame->getController()))
+ SfxLokHelper::notifyContextChange(pViewShell, rsModuleName, rsContextName);
+ }
+
+ const css::ui::ContextChangeEventObject aEvent(
+ rxFrame->getController(),
+ rsModuleName,
+ rsContextName);
+
+ css::uno::Reference<css::ui::XContextChangeEventMultiplexer> xMultiplexer (
+ css::ui::ContextChangeEventMultiplexer::get(
+ ::comphelper::getProcessComponentContext()));
+ if (xMultiplexer.is())
+ xMultiplexer->broadcastContextChangeEvent(aEvent, rxFrame->getController());
+}
+
+OUString ContextChangeBroadcaster::GetModuleName (const css::uno::Reference<css::frame::XFrame>& rxFrame)
+{
+ if ( ! rxFrame.is() || ! rxFrame->getController().is())
+ return OUString();
+ try
+ {
+ const Reference<XComponentContext> xContext (::comphelper::getProcessComponentContext() );
+ const Reference<frame::XModuleManager> xModuleManager = frame::ModuleManager::create( xContext );
+ return xModuleManager->identify(rxFrame);
+ }
+ catch (const Exception&)
+ {
+ OSL_ENSURE(false, "can not determine module name");
+ }
+ return OUString();
+}
+
+} // end of namespace ::sfx2::sidebar
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/sidebar/ContextList.cxx b/sfx2/source/sidebar/ContextList.cxx
new file mode 100644
index 000000000..9f1716447
--- /dev/null
+++ b/sfx2/source/sidebar/ContextList.cxx
@@ -0,0 +1,103 @@
+/* -*- 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 <sidebar/ContextList.hxx>
+#include <sfx2/sidebar/Context.hxx>
+
+namespace sfx2::sidebar {
+
+ContextList::ContextList()
+{
+}
+
+const ContextList::Entry* ContextList::GetMatch (const Context& rContext) const
+{
+ const ::std::vector<Entry>::const_iterator iEntry = FindBestMatch(rContext);
+ if (iEntry != maEntries.end())
+ return &*iEntry;
+ else
+ return nullptr;
+}
+
+ContextList::Entry* ContextList::GetMatch (const Context& rContext)
+{
+ const ::std::vector<Entry>::const_iterator iEntry = FindBestMatch(rContext);
+ if (iEntry != maEntries.end())
+ return const_cast<Entry*>(&*iEntry);
+ else
+ return nullptr;
+}
+
+::std::vector<ContextList::Entry>::const_iterator ContextList::FindBestMatch (const Context& rContext) const
+{
+ sal_Int32 nBestMatch (Context::NoMatch);
+ ::std::vector<Entry>::const_iterator iBestMatch (maEntries.end());
+
+ for (::std::vector<Entry>::const_iterator
+ iEntry(maEntries.begin()),
+ iEnd(maEntries.end());
+ iEntry!=iEnd;
+ ++iEntry)
+ {
+ const sal_Int32 nMatch (rContext.EvaluateMatch(iEntry->maContext));
+ if (nMatch < nBestMatch)
+ {
+ nBestMatch = nMatch;
+ iBestMatch = iEntry;
+ }
+ if (nBestMatch == Context::OptimalMatch)
+ return iEntry;
+ }
+
+ return iBestMatch;
+}
+
+void ContextList::AddContextDescription (
+ const Context& rContext,
+ const bool bIsInitiallyVisible,
+ const OUString& rsMenuCommand)
+{
+ maEntries.emplace_back();
+ maEntries.back().maContext = rContext;
+ maEntries.back().mbIsInitiallyVisible = bIsInitiallyVisible;
+ maEntries.back().msMenuCommand = rsMenuCommand;
+}
+
+void ContextList::ToggleVisibilityForContext( const Context &rContext, const bool bVisible)
+{
+ ContextList::Entry *pEntry = GetMatch( rContext );
+
+ if ( !pEntry )
+ return;
+
+ const sal_Int32 nMatch( rContext.EvaluateMatch( pEntry->maContext ) );
+
+ if ( nMatch & Context::ApplicationWildcardMatch )
+ {
+ // Create a separate context list entry for this app if 'any'
+ // is the only context that matches. Toggling the visibility
+ // for 'any' would change it for all apps, not just this one
+ AddContextDescription( rContext, bVisible, OUString() );
+ }
+ else if ( nMatch == Context::OptimalMatch || nMatch == Context::ContextWildcardMatch )
+ pEntry->mbIsInitiallyVisible = bVisible;
+}
+
+} // end of namespace sfx2::sidebar
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/sidebar/ControllerFactory.cxx b/sfx2/source/sidebar/ControllerFactory.cxx
new file mode 100644
index 000000000..d904b5260
--- /dev/null
+++ b/sfx2/source/sidebar/ControllerFactory.cxx
@@ -0,0 +1,239 @@
+/* -*- 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 <sidebar/ControllerFactory.hxx>
+#include <sidebar/Tools.hxx>
+
+#include <com/sun/star/frame/XToolbarController.hpp>
+#include <com/sun/star/frame/XFrame.hpp>
+#include <com/sun/star/frame/theToolbarControllerFactory.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+
+#include <framework/sfxhelperfunctions.hxx>
+#include <framework/generictoolbarcontroller.hxx>
+#include <vcl/toolbox.hxx>
+#include <vcl/commandinfoprovider.hxx>
+#include <vcl/weldutils.hxx>
+#include <comphelper/processfactory.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+
+using namespace css;
+using namespace css::uno;
+
+namespace sfx2::sidebar {
+
+Reference<frame::XToolbarController> ControllerFactory::CreateToolBoxController(
+ ToolBox* pToolBox,
+ const ToolBoxItemId nItemId,
+ const OUString& rsCommandName,
+ const Reference<frame::XFrame>& rxFrame,
+ const Reference<frame::XController>& rxController,
+ const Reference<awt::XWindow>& rxParentWindow,
+ const sal_Int32 nWidth, bool bSideBar)
+{
+ Reference<frame::XToolbarController> xController (
+ CreateToolBarController(
+ VCLUnoHelper::GetInterface(pToolBox),
+ rsCommandName,
+ rxFrame, rxController,
+ nWidth, bSideBar));
+
+ bool bFactoryHasController( xController.is() );
+
+ // Create a controller for the new item.
+ if ( !bFactoryHasController )
+ {
+ xController = ::framework::CreateToolBoxController(
+ rxFrame,
+ pToolBox,
+ nItemId,
+ rsCommandName);
+ }
+ if ( ! xController.is())
+ {
+ xController = new framework::GenericToolbarController(
+ ::comphelper::getProcessComponentContext(),
+ rxFrame,
+ pToolBox,
+ nItemId,
+ rsCommandName);
+ }
+
+ // Initialize the controller with eg a service factory.
+ Reference<lang::XInitialization> xInitialization (xController, UNO_QUERY);
+ if (!bFactoryHasController && xInitialization.is())
+ {
+ beans::PropertyValue aPropValue;
+ std::vector<Any> aPropertyVector;
+
+ aPropValue.Name = "Frame";
+ aPropValue.Value <<= rxFrame;
+ aPropertyVector.push_back(Any(aPropValue));
+
+ aPropValue.Name = "ServiceManager";
+ aPropValue.Value <<= ::comphelper::getProcessServiceFactory();
+ aPropertyVector.push_back(Any(aPropValue));
+
+ aPropValue.Name = "CommandURL";
+ aPropValue.Value <<= rsCommandName;
+ aPropertyVector.push_back(Any(aPropValue));
+
+ Sequence<Any> aArgs (comphelper::containerToSequence(aPropertyVector));
+ xInitialization->initialize(aArgs);
+ }
+
+ if (xController.is())
+ {
+ if (rxParentWindow.is())
+ {
+ Reference<awt::XWindow> xItemWindow (xController->createItemWindow(rxParentWindow));
+ VclPtr<vcl::Window> pItemWindow = VCLUnoHelper::GetWindow(xItemWindow);
+ if (pItemWindow != nullptr)
+ {
+ WindowType nType = pItemWindow->GetType();
+ if (nType == WindowType::LISTBOX || nType == WindowType::MULTILISTBOX || nType == WindowType::COMBOBOX)
+ pItemWindow->SetAccessibleName(pToolBox->GetItemText(nItemId));
+ if (nWidth > 0)
+ pItemWindow->SetSizePixel(Size(nWidth, pItemWindow->GetSizePixel().Height()));
+ pToolBox->SetItemWindow(nItemId, pItemWindow);
+ }
+ }
+
+ Reference<util::XUpdatable> xUpdatable (xController, UNO_QUERY);
+ if (xUpdatable.is())
+ xUpdatable->update();
+
+ // Add tooltip.
+ if (xController.is())
+ {
+ auto aProperties = vcl::CommandInfoProvider::GetCommandProperties(rsCommandName,
+ vcl::CommandInfoProvider::GetModuleIdentifier(rxFrame));
+ const OUString sTooltip (vcl::CommandInfoProvider::GetTooltipForCommand(
+ rsCommandName, aProperties, rxFrame));
+ if (pToolBox->GetQuickHelpText(nItemId).isEmpty())
+ pToolBox->SetQuickHelpText(nItemId, sTooltip);
+ pToolBox->EnableItem(nItemId);
+ }
+ }
+
+ return xController;
+}
+
+Reference<frame::XToolbarController> ControllerFactory::CreateToolBoxController(
+ weld::Toolbar& rToolbar, weld::Builder& rBuilder,
+ const OUString& rsCommandName,
+ const Reference<frame::XFrame>& rxFrame,
+ const Reference<frame::XController>& rxController,
+ bool bSideBar)
+{
+ css::uno::Reference<css::awt::XWindow> xWidget(new weld::TransportAsXWindow(&rToolbar, &rBuilder));
+
+ Reference<frame::XToolbarController> xController(
+ CreateToolBarController(
+ xWidget,
+ rsCommandName,
+ rxFrame, rxController,
+ -1, bSideBar));
+
+ if (!xController.is())
+ {
+ xController = new framework::GenericToolbarController(
+ ::comphelper::getProcessComponentContext(),
+ rxFrame,
+ rToolbar,
+ rsCommandName);
+ }
+
+ if (xController.is())
+ {
+ xController->createItemWindow(xWidget);
+
+ Reference<util::XUpdatable> xUpdatable(xController, UNO_QUERY);
+ if (xUpdatable.is())
+ xUpdatable->update();
+ }
+
+ return xController;
+}
+
+
+Reference<frame::XToolbarController> ControllerFactory::CreateToolBarController(
+ const Reference<awt::XWindow>& rxToolbar,
+ const OUString& rsCommandName,
+ const Reference<frame::XFrame>& rxFrame,
+ const Reference<frame::XController>& rxController,
+ const sal_Int32 nWidth, bool bSideBar)
+{
+ try
+ {
+ Reference<XComponentContext> xContext = comphelper::getProcessComponentContext();
+ Reference<frame::XUIControllerFactory> xFactory = frame::theToolbarControllerFactory::get( xContext );
+ OUString sModuleName (Tools::GetModuleName(rxController));
+
+ if (xFactory.is() && xFactory->hasController(rsCommandName, sModuleName))
+ {
+ beans::PropertyValue aPropValue;
+ std::vector<Any> aPropertyVector;
+
+ aPropValue.Name = "ModuleIdentifier";
+ aPropValue.Value <<= sModuleName;
+ aPropertyVector.push_back( Any( aPropValue ));
+
+ aPropValue.Name = "Frame";
+ aPropValue.Value <<= rxFrame;
+ aPropertyVector.push_back( Any( aPropValue ));
+
+ aPropValue.Name = "ServiceManager";
+ aPropValue.Value <<= comphelper::getProcessServiceFactory();
+ aPropertyVector.push_back( Any( aPropValue ));
+
+ aPropValue.Name = "ParentWindow";
+ aPropValue.Value <<= rxToolbar;
+ aPropertyVector.push_back( Any( aPropValue ));
+
+ aPropValue.Name = "IsSidebar";
+ aPropValue.Value <<= bSideBar;
+ aPropertyVector.push_back( Any( aPropValue ));
+
+ if (nWidth > 0)
+ {
+ aPropValue.Name = "Width";
+ aPropValue.Value <<= nWidth;
+ aPropertyVector.push_back( Any( aPropValue ));
+ }
+
+ Sequence<Any> aArgs (comphelper::containerToSequence(aPropertyVector));
+ return Reference<frame::XToolbarController>(
+ xFactory->createInstanceWithArgumentsAndContext(
+ rsCommandName,
+ aArgs,
+ xContext),
+ UNO_QUERY);
+ }
+ }
+ catch (Exception&)
+ {
+ // Ignore exception.
+ }
+ return nullptr;
+}
+
+} // end of namespace sfx2::sidebar
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/sidebar/ControllerItem.cxx b/sfx2/source/sidebar/ControllerItem.cxx
new file mode 100644
index 000000000..e02276ec0
--- /dev/null
+++ b/sfx2/source/sidebar/ControllerItem.cxx
@@ -0,0 +1,71 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#include <memory>
+#include <sfx2/sidebar/ControllerItem.hxx>
+
+#include <sfx2/bindings.hxx>
+
+using namespace css;
+using namespace css::uno;
+
+namespace sfx2::sidebar {
+
+ControllerItem::ControllerItem (
+ const sal_uInt16 nSlotId,
+ SfxBindings &rBindings,
+ ItemUpdateReceiverInterface& rItemUpdateReceiver)
+ : SfxControllerItem(nSlotId, rBindings),
+ mrItemUpdateReceiver(rItemUpdateReceiver)
+{
+}
+
+ControllerItem::~ControllerItem()
+{
+ dispose();
+}
+
+void ControllerItem::StateChangedAtToolBoxControl (
+ sal_uInt16 nSID,
+ SfxItemState eState,
+ const SfxPoolItem* pState)
+{
+ mrItemUpdateReceiver.NotifyItemUpdate(nSID, eState, pState);
+}
+
+void ControllerItem::GetControlState (
+ sal_uInt16 nSID,
+ boost::property_tree::ptree& rState)
+{
+ mrItemUpdateReceiver.GetControlState(nSID, rState);
+}
+
+void ControllerItem::RequestUpdate()
+{
+ std::unique_ptr<SfxPoolItem> pState;
+ const SfxItemState eState (GetBindings().QueryState(GetId(), pState));
+ mrItemUpdateReceiver.NotifyItemUpdate(GetId(), eState, pState.get());
+}
+
+ControllerItem::ItemUpdateReceiverInterface::~ItemUpdateReceiverInterface()
+{
+}
+
+} // end of namespace sfx2::sidebar
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/sidebar/Deck.cxx b/sfx2/source/sidebar/Deck.cxx
new file mode 100644
index 000000000..b6ddffa89
--- /dev/null
+++ b/sfx2/source/sidebar/Deck.cxx
@@ -0,0 +1,287 @@
+/* -*- 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/sidebar/Deck.hxx>
+#include <sidebar/DeckDescriptor.hxx>
+#include <sidebar/DeckLayouter.hxx>
+#include <sidebar/DeckTitleBar.hxx>
+#include <sidebar/PanelTitleBar.hxx>
+#include <sfx2/sidebar/Panel.hxx>
+#include <sfx2/sidebar/SidebarDockingWindow.hxx>
+#include <sfx2/sidebar/Theme.hxx>
+#include <sfx2/viewsh.hxx>
+
+#include <vcl/event.hxx>
+#include <comphelper/lok.hxx>
+#include <tools/json_writer.hxx>
+
+using namespace css;
+using namespace css::uno;
+
+namespace sfx2::sidebar {
+
+Deck::Deck(const DeckDescriptor& rDeckDescriptor, SidebarDockingWindow* pParentWindow,
+ const std::function<void()>& rCloserAction)
+ : InterimItemWindow(pParentWindow, "sfx/ui/deck.ui", "Deck")
+ , msId(rDeckDescriptor.msId)
+ , mnMinimalWidth(0)
+ , mnScrolledWindowExtraWidth(0)
+ , mnMinimalHeight(0)
+ , maPanels()
+ , mxParentWindow(pParentWindow)
+ , mxTitleBar(new DeckTitleBar(rDeckDescriptor.msTitle, *m_xBuilder, rCloserAction))
+ , mxVerticalScrollBar(m_xBuilder->weld_scrolled_window("scrolledwindow"))
+ , mxContents(m_xBuilder->weld_box("contents"))
+{
+ SetStyle(GetStyle() | WB_DIALOGCONTROL);
+
+ m_xContainer->set_background(Theme::GetColor(Theme::Color_DeckBackground));
+
+ mxVerticalScrollBar->vadjustment_set_step_increment(10);
+ mxVerticalScrollBar->vadjustment_set_page_increment(100);
+
+ // tdf#142458 Measure the preferred width of an empty ScrolledWindow
+ // to add to the width of the union of panel widths when calculating
+ // the minimal width of the deck
+ mxVerticalScrollBar->set_hpolicy(VclPolicyType::NEVER);
+ mxVerticalScrollBar->set_vpolicy(VclPolicyType::NEVER);
+ mnScrolledWindowExtraWidth = mxVerticalScrollBar->get_preferred_size().Width();
+ mxVerticalScrollBar->set_hpolicy(VclPolicyType::AUTOMATIC);
+ mxVerticalScrollBar->set_vpolicy(VclPolicyType::AUTOMATIC);
+}
+
+Deck::~Deck()
+{
+ disposeOnce();
+}
+
+void Deck::dispose()
+{
+ SharedPanelContainer aPanels;
+ aPanels.swap(maPanels);
+
+ // We have to explicitly trigger the destruction of panels.
+ // Otherwise that is done by one of our base class destructors
+ // without updating maPanels.
+ for (auto& rpPanel : aPanels)
+ rpPanel.reset();
+
+ maPanels.clear();
+ mxTitleBar.reset();
+ mxContents.reset();
+ mxVerticalScrollBar.reset();
+
+ mxParentWindow.clear();
+
+ InterimItemWindow::dispose();
+}
+
+DeckTitleBar* Deck::GetTitleBar() const
+{
+ return mxTitleBar.get();
+}
+
+tools::Rectangle Deck::GetContentArea() const
+{
+ const Size aWindowSize (GetSizePixel());
+ const int nBorderSize (Theme::GetInteger(Theme::Int_DeckBorderSize));
+ if (aWindowSize.IsEmpty())
+ return tools::Rectangle();
+
+ return tools::Rectangle(
+ Theme::GetInteger(Theme::Int_DeckLeftPadding) + nBorderSize,
+ Theme::GetInteger(Theme::Int_DeckTopPadding) + nBorderSize,
+ aWindowSize.Width() - 1 - Theme::GetInteger(Theme::Int_DeckRightPadding) - nBorderSize,
+ aWindowSize.Height() - 1 - Theme::GetInteger(Theme::Int_DeckBottomPadding) - nBorderSize);
+}
+
+void Deck::DataChanged(const DataChangedEvent&)
+{
+ for (auto& rpPanel : maPanels)
+ rpPanel->DataChanged();
+
+ RequestLayoutInternal();
+}
+
+/*
+ * Get the ordering as is shown in the layout, and our type as 'deck'
+ * also elide nested panel windows.
+ */
+void Deck::DumpAsPropertyTree(tools::JsonWriter& rJsonWriter)
+{
+ rJsonWriter.put("id", get_id().isEmpty() ? msId : get_id());
+ rJsonWriter.put("type", "deck");
+ rJsonWriter.put("text", GetText());
+ rJsonWriter.put("enabled", IsEnabled());
+ if (!IsVisible())
+ rJsonWriter.put("visible", false);
+
+ auto childrenNode = rJsonWriter.startArray("children");
+ for (const auto &it : maPanels)
+ {
+ // collapse the panel itself out
+ auto xContent = it->GetContents();
+ if (!xContent)
+ continue;
+
+ auto childNode = rJsonWriter.startStruct();
+ rJsonWriter.put("id", it->GetId());
+ rJsonWriter.put("type", "panel");
+ rJsonWriter.put("text", it->GetTitle());
+ rJsonWriter.put("enabled", true);
+ rJsonWriter.put("hidden", it->IsLurking());
+ rJsonWriter.put("expanded", it->IsExpanded());
+
+ if (it->GetTitleBar() && !it->GetTitleBar()->GetMoreOptionsCommand().isEmpty())
+ rJsonWriter.put("command", it->GetTitleBar()->GetMoreOptionsCommand());
+
+ {
+ auto children2Node = rJsonWriter.startArray("children");
+ {
+ auto child2Node = rJsonWriter.startStruct();
+ xContent->get_property_tree(rJsonWriter);
+ }
+ }
+ }
+}
+
+/**
+ * This container may contain existing panels that are
+ * being re-used, and new ones too.
+ */
+void Deck::ResetPanels(SharedPanelContainer&& rPanelContainer)
+{
+ SharedPanelContainer aHiddens;
+
+ // First hide old panels we don't need just now.
+ for (auto& rpPanel : maPanels)
+ {
+ bool bFound = false;
+ for (const auto & i : rPanelContainer)
+ bFound = bFound || (rpPanel.get() == i.get());
+ if (!bFound) // this one didn't survive.
+ {
+ rpPanel->SetLurkMode(true);
+ aHiddens.push_back(rpPanel);
+ }
+ }
+ maPanels = std::move(rPanelContainer);
+
+ // Hidden ones always at the end
+ maPanels.insert(std::end(maPanels), std::begin(aHiddens), std::end(aHiddens));
+
+ RequestLayoutInternal();
+}
+
+void Deck::RequestLayoutInternal()
+{
+ mnMinimalWidth = 0;
+ mnMinimalHeight = 0;
+
+ DeckLayouter::LayoutDeck(mxParentWindow.get(), GetContentArea(),
+ mnMinimalWidth, mnMinimalHeight, maPanels,
+ *GetTitleBar(), *mxVerticalScrollBar);
+
+ if (mnMinimalWidth)
+ {
+ // tdf#142458 at this point mnMinimalWidth contains the width required
+ // by the panels, but extra space may be needed by the scrolledwindow
+ // that will contain the panels
+ mnMinimalWidth += mnScrolledWindowExtraWidth;
+ }
+}
+
+void Deck::RequestLayout()
+{
+ RequestLayoutInternal();
+
+ if (!comphelper::LibreOfficeKit::isActive())
+ return;
+
+ bool bChangeNeeded = false;
+ Size aParentSize = mxParentWindow->GetSizePixel();
+
+ if (mnMinimalHeight > 0 && (mnMinimalHeight != aParentSize.Height() || GetSizePixel().Height() != mnMinimalHeight))
+ {
+ aParentSize.setHeight(mnMinimalHeight);
+ bChangeNeeded = true;
+ }
+ const SfxViewShell* pViewShell = SfxViewShell::Current();
+ if (mnMinimalWidth > 0 && (mnMinimalWidth != aParentSize.Width() || GetSizePixel().Width() != mnMinimalWidth)
+ && pViewShell && pViewShell->isLOKMobilePhone())
+ {
+ aParentSize.setWidth(mnMinimalWidth);
+ bChangeNeeded = true;
+ }
+
+ if (bChangeNeeded)
+ {
+ mxParentWindow->SetSizePixel(aParentSize);
+ setPosSizePixel(0, 0, aParentSize.Width(), aParentSize.Height());
+ }
+ else if (aParentSize != GetSizePixel()) //Sync parent & child sizes
+ setPosSizePixel(0, 0, aParentSize.Width(), aParentSize.Height());
+}
+
+weld::Widget* Deck::GetPanelParentWindow()
+{
+ return mxContents.get();
+}
+
+std::shared_ptr<Panel> Deck::GetPanel(std::u16string_view panelId)
+{
+ for (const auto& pPanel : maPanels)
+ {
+ if(pPanel->GetId() == panelId)
+ {
+ return pPanel;
+ }
+ }
+ return nullptr;
+
+}
+
+void Deck::ShowPanel(const Panel& rPanel)
+{
+ if (!mxVerticalScrollBar || mxVerticalScrollBar->get_vpolicy() == VclPolicyType::NEVER)
+ return;
+
+ // Get vertical extent of the panel.
+ tools::Rectangle aExtents;
+ if (!rPanel.get_extents(aExtents))
+ return;
+
+ auto nPanelTop = aExtents.Top();
+ auto nPanelBottom = aExtents.Bottom() - 1;
+
+ // Determine what the new thumb position should be like.
+ // When the whole panel does not fit then make its top visible
+ // and it off at the bottom.
+ sal_Int32 nNewThumbPos(mxVerticalScrollBar->vadjustment_get_value());
+ if (nPanelBottom >= nNewThumbPos + mxVerticalScrollBar->vadjustment_get_page_size())
+ nNewThumbPos = nPanelBottom - mxVerticalScrollBar->vadjustment_get_page_size();
+ if (nPanelTop < nNewThumbPos)
+ nNewThumbPos = nPanelTop;
+
+ mxVerticalScrollBar->vadjustment_set_value(nNewThumbPos);
+}
+
+} // end of namespace sfx2::sidebar
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/sidebar/DeckDescriptor.cxx b/sfx2/source/sidebar/DeckDescriptor.cxx
new file mode 100644
index 000000000..fee839f08
--- /dev/null
+++ b/sfx2/source/sidebar/DeckDescriptor.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 <sidebar/DeckDescriptor.hxx>
+
+namespace sfx2::sidebar {
+
+DeckDescriptor::DeckDescriptor()
+ : mbIsEnabled(true),
+ mnOrderIndex(10000), // Default value as defined in Sidebar.xcs
+ mbExperimental(false)
+{
+}
+
+DeckDescriptor::DeckDescriptor (const DeckDescriptor& rOther)
+ : msTitle(rOther.msTitle),
+ msId(rOther.msId),
+ msIconURL(rOther.msIconURL),
+ msHighContrastIconURL(rOther.msHighContrastIconURL),
+ msTitleBarIconURL(rOther.msTitleBarIconURL),
+ msHighContrastTitleBarIconURL(rOther.msHighContrastTitleBarIconURL),
+ msHelpText(rOther.msHelpText),
+ maContextList(rOther.maContextList),
+ mbIsEnabled(rOther.mbIsEnabled),
+ mnOrderIndex(rOther.mnOrderIndex),
+ mbExperimental(rOther.mbExperimental),
+ mpDeck(rOther.mpDeck)
+{
+}
+
+DeckDescriptor::~DeckDescriptor()
+{
+}
+
+} // end of namespace sfx2::sidebar
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/sidebar/DeckLayouter.cxx b/sfx2/source/sidebar/DeckLayouter.cxx
new file mode 100644
index 000000000..c24c93a51
--- /dev/null
+++ b/sfx2/source/sidebar/DeckLayouter.cxx
@@ -0,0 +1,555 @@
+/* -*- 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 <sidebar/DeckLayouter.hxx>
+#include <sidebar/DeckTitleBar.hxx>
+#include <sidebar/PanelTitleBar.hxx>
+#include <sfx2/sidebar/Panel.hxx>
+#include <sfx2/sidebar/Theme.hxx>
+#include <sfx2/sidebar/SidebarDockingWindow.hxx>
+#include <sfx2/sidebar/SidebarController.hxx>
+#include <sfx2/viewsh.hxx>
+#include <comphelper/lok.hxx>
+#include <osl/diagnose.h>
+
+#include <comphelper/processfactory.hxx>
+
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/frame/Desktop.hpp>
+#include <com/sun/star/frame/XDesktop2.hpp>
+#include <com/sun/star/frame/XFrame.hpp>
+#include <com/sun/star/ui/XSidebarPanel.hpp>
+
+#include <vcl/jsdialog/executor.hxx>
+
+using namespace css;
+using namespace css::uno;
+
+namespace sfx2::sidebar {
+
+namespace {
+ const sal_Int32 MinimalPanelHeight (25);
+
+ enum LayoutMode
+ {
+ MinimumOrLarger,
+ PreferredOrLarger,
+ Preferred
+ };
+ class LayoutItem
+ {
+ public:
+ std::shared_ptr<Panel> mpPanel;
+ css::ui::LayoutSize maLayoutSize;
+ sal_Int32 mnDistributedHeight;
+ sal_Int32 mnWeight;
+ bool mbShowTitleBar;
+
+ LayoutItem(const std::shared_ptr<Panel>& pPanel)
+ : mpPanel(pPanel)
+ , maLayoutSize(0, 0, 0)
+ , mnDistributedHeight(0)
+ , mnWeight(0)
+ , mbShowTitleBar(true)
+ {
+ }
+ };
+ void LayoutPanels (
+ const tools::Rectangle& rContentArea,
+ sal_Int32& rMinimalWidth,
+ sal_Int32& rMinimalHeight,
+ ::std::vector<LayoutItem>& rLayoutItems,
+ weld::ScrolledWindow& pVerticalScrollBar,
+ const bool bShowVerticalScrollBar);
+ void GetRequestedSizes (
+ ::std::vector<LayoutItem>& rLayoutItem,
+ sal_Int32& rAvailableHeight,
+ sal_Int32& rMinimalWidth,
+ const tools::Rectangle& rContentBox);
+ void DistributeHeights (
+ ::std::vector<LayoutItem>& rLayoutItems,
+ const sal_Int32 nHeightToDistribute,
+ const sal_Int32 nContainerHeight,
+ const bool bMinimumHeightIsBase);
+ sal_Int32 PlacePanels (
+ ::std::vector<LayoutItem>& rLayoutItems,
+ const LayoutMode eMode_);
+ tools::Rectangle PlaceDeckTitle (
+ const SidebarDockingWindow* pDockingWindow,
+ DeckTitleBar& rTitleBar,
+ const tools::Rectangle& rAvailableSpace);
+ tools::Rectangle PlaceVerticalScrollBar (
+ weld::ScrolledWindow& rVerticalScrollBar,
+ const tools::Rectangle& rAvailableSpace,
+ const bool bShowVerticalScrollBar);
+ void SetupVerticalScrollBar(
+ weld::ScrolledWindow& rVerticalScrollBar,
+ const sal_Int32 nContentHeight,
+ const sal_Int32 nVisibleHeight);
+}
+
+void DeckLayouter::LayoutDeck (
+ const SidebarDockingWindow* pDockingWindow,
+ const tools::Rectangle& rContentArea,
+ sal_Int32& rMinimalWidth,
+ sal_Int32& rMinimalHeight,
+ SharedPanelContainer& rPanels,
+ DeckTitleBar& rDeckTitleBar,
+ weld::ScrolledWindow& rVerticalScrollBar)
+{
+ if (rContentArea.GetWidth()<=0 || rContentArea.GetHeight()<=0)
+ return;
+ tools::Rectangle aBox(PlaceDeckTitle(pDockingWindow, rDeckTitleBar, rContentArea));
+
+ if ( rPanels.empty())
+ return;
+
+ // Prepare the layout item container.
+ ::std::vector<LayoutItem> aLayoutItems;
+ aLayoutItems.reserve(rPanels.size());
+ for (auto& rPanel : rPanels)
+ aLayoutItems.emplace_back(rPanel);
+
+ LayoutPanels(
+ aBox,
+ rMinimalWidth,
+ rMinimalHeight,
+ aLayoutItems,
+ rVerticalScrollBar,
+ false);
+}
+
+namespace {
+
+void LayoutPanels (
+ const tools::Rectangle& rContentArea,
+ sal_Int32& rMinimalWidth,
+ sal_Int32& rMinimalHeight,
+ ::std::vector<LayoutItem>& rLayoutItems,
+ weld::ScrolledWindow& rVerticalScrollBar,
+ const bool bShowVerticalScrollBar)
+{
+ tools::Rectangle aBox (PlaceVerticalScrollBar(rVerticalScrollBar, rContentArea, bShowVerticalScrollBar));
+
+ // Get the requested heights of the panels and the available
+ // height that is left when all panel titles and separators are
+ // taken into account.
+ sal_Int32 nAvailableHeight (aBox.GetHeight());
+ GetRequestedSizes(rLayoutItems, nAvailableHeight, rMinimalWidth, aBox);
+ const sal_Int32 nTotalDecorationHeight (aBox.GetHeight() - nAvailableHeight);
+
+ // Analyze the requested heights.
+ // Determine the height that is available for panel content
+ // and count the different layouts.
+ sal_Int32 nTotalPreferredHeight (0);
+ sal_Int32 nTotalMinimumHeight (0);
+
+ for (const auto& rItem : rLayoutItems)
+ {
+ nTotalMinimumHeight += rItem.maLayoutSize.Minimum;
+ nTotalPreferredHeight += rItem.maLayoutSize.Preferred;
+ }
+
+ if (nTotalMinimumHeight > nAvailableHeight && !bShowVerticalScrollBar
+ && !comphelper::LibreOfficeKit::isActive())
+ {
+ // Not enough space, even when all panels are shrunk to their
+ // minimum height.
+ // Show a vertical scrollbar.
+ LayoutPanels(
+ rContentArea,
+ rMinimalWidth,
+ rMinimalHeight,
+ rLayoutItems,
+ rVerticalScrollBar,
+ true);
+ return;
+ }
+
+ // We are now in one of three modes.
+ // - The preferred height fits into the available size:
+ // Use the preferred size, distribute the remaining height by
+ // enlarging panels.
+ // - The total minimum height fits into the available size:
+ // Use the minimum size, distribute the remaining height by
+ // enlarging panels.
+ // - The total minimum height does not fit into the available
+ // size:
+ // Use the unmodified preferred height for all panels.
+
+ LayoutMode eMode(MinimumOrLarger);
+ if (bShowVerticalScrollBar)
+ {
+ eMode = Preferred;
+
+ const sal_Int32 nContentHeight(nTotalPreferredHeight + nTotalDecorationHeight);
+ SetupVerticalScrollBar(rVerticalScrollBar, nContentHeight, aBox.GetHeight());
+ }
+ else
+ {
+ if (nTotalPreferredHeight <= nAvailableHeight)
+ eMode = PreferredOrLarger;
+ else
+ eMode = MinimumOrLarger;
+
+ const sal_Int32 nTotalHeight (eMode==MinimumOrLarger ? nTotalMinimumHeight : nTotalPreferredHeight);
+
+ DistributeHeights(
+ rLayoutItems,
+ nAvailableHeight-nTotalHeight,
+ aBox.GetHeight(),
+ eMode==MinimumOrLarger);
+ }
+
+ const sal_Int32 nUsedHeight(PlacePanels(rLayoutItems, eMode));
+ rMinimalHeight = nUsedHeight;
+}
+
+sal_Int32 PlacePanels (
+ ::std::vector<LayoutItem>& rLayoutItems,
+ const LayoutMode eMode)
+{
+ const sal_Int32 nDeckSeparatorHeight (Theme::GetInteger(Theme::Int_DeckSeparatorHeight));
+ sal_Int32 nY (0);
+
+ // Assign heights and places.
+ for(::std::vector<LayoutItem>::const_iterator iItem(rLayoutItems.begin()),
+ iEnd(rLayoutItems.end());
+ iItem!=iEnd;
+ ++iItem)
+ {
+ if (!iItem->mpPanel)
+ continue;
+
+ Panel& rPanel (*iItem->mpPanel);
+
+ rPanel.set_margin_top(nDeckSeparatorHeight);
+ rPanel.set_margin_bottom(0);
+
+ // Separator above the panel title bar.
+ if (!rPanel.IsLurking())
+ {
+ nY += nDeckSeparatorHeight;
+ }
+
+ bool bShowTitlebar = iItem->mbShowTitleBar;
+ PanelTitleBar* pTitleBar = rPanel.GetTitleBar();
+ pTitleBar->Show(bShowTitlebar);
+ rPanel.set_vexpand(!bShowTitlebar);
+ weld::Container* pContents = rPanel.GetContents();
+ pContents->set_vexpand(true);
+
+ bool bExpanded = rPanel.IsExpanded() && !rPanel.IsLurking();
+ if (bShowTitlebar || bExpanded)
+ {
+ rPanel.Show(true);
+
+ sal_Int32 nPanelHeight(0);
+ if (bExpanded)
+ {
+ // Determine the height of the panel depending on layout
+ // mode and distributed heights.
+ switch(eMode)
+ {
+ case MinimumOrLarger:
+ nPanelHeight = iItem->maLayoutSize.Minimum + iItem->mnDistributedHeight;
+ break;
+ case PreferredOrLarger:
+ nPanelHeight = iItem->maLayoutSize.Preferred + iItem->mnDistributedHeight;
+ break;
+ case Preferred:
+ nPanelHeight = iItem->maLayoutSize.Preferred;
+ break;
+ default:
+ OSL_ASSERT(false);
+ break;
+ }
+ }
+ if (bShowTitlebar)
+ nPanelHeight += pTitleBar->get_preferred_size().Height();
+
+ rPanel.SetHeightPixel(nPanelHeight);
+
+ nY += nPanelHeight;
+ }
+ else
+ {
+ rPanel.Show(false);
+ }
+
+ if (!bExpanded)
+ {
+ // Add a separator below the collapsed panel, if it is the
+ // last panel in the deck.
+ if (iItem == rLayoutItems.end()-1)
+ {
+ // Separator below the panel title bar.
+ rPanel.set_margin_bottom(nDeckSeparatorHeight);
+ nY += nDeckSeparatorHeight;
+ }
+ }
+ }
+
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ sal_uInt64 nShellId = reinterpret_cast<sal_uInt64>(SfxViewShell::Current());
+ jsdialog::SendFullUpdate(std::to_string(nShellId) + "sidebar", "Panel");
+ }
+
+ return nY;
+}
+
+void GetRequestedSizes (
+ ::std::vector<LayoutItem>& rLayoutItems,
+ sal_Int32& rAvailableHeight,
+ sal_Int32& rMinimalWidth,
+ const tools::Rectangle& rContentBox)
+{
+ rAvailableHeight = rContentBox.GetHeight();
+
+ const sal_Int32 nDeckSeparatorHeight (Theme::GetInteger(Theme::Int_DeckSeparatorHeight));
+
+ for (auto& rItem : rLayoutItems)
+ {
+ rItem.maLayoutSize = ui::LayoutSize(0,0,0);
+
+ if (rItem.mpPanel == nullptr)
+ continue;
+
+ if (rItem.mpPanel->IsLurking())
+ {
+ rItem.mbShowTitleBar = false;
+ continue;
+ }
+
+ if (rLayoutItems.size() == 1
+ && rItem.mpPanel->IsTitleBarOptional())
+ {
+ // There is only one panel and its title bar is
+ // optional => hide it.
+ rAvailableHeight -= nDeckSeparatorHeight;
+ rItem.mbShowTitleBar = false;
+ }
+ else
+ {
+ // Show the title bar and a separator above and below
+ // the title bar.
+ PanelTitleBar* pTitleBar = rItem.mpPanel->GetTitleBar();
+ const sal_Int32 nPanelTitleBarHeight = pTitleBar->get_preferred_size().Height();
+
+ rAvailableHeight -= nPanelTitleBarHeight;
+ rAvailableHeight -= nDeckSeparatorHeight;
+ }
+
+ if (rItem.mpPanel->IsExpanded() && rItem.mpPanel->GetPanelComponent().is())
+ {
+ Reference<ui::XSidebarPanel> xPanel (rItem.mpPanel->GetPanelComponent());
+
+ rItem.maLayoutSize = xPanel->getHeightForWidth(rContentBox.GetWidth());
+ if (!(0 <= rItem.maLayoutSize.Minimum && rItem.maLayoutSize.Minimum <= rItem.maLayoutSize.Preferred
+ && rItem.maLayoutSize.Preferred <= rItem.maLayoutSize.Maximum))
+ {
+ SAL_INFO("sfx.sidebar", "Please follow LayoutSize constraints: 0 ≤ "
+ "Minimum ≤ Preferred ≤ Maximum."
+ " Currently: Minimum: "
+ << rItem.maLayoutSize.Minimum
+ << " Preferred: " << rItem.maLayoutSize.Preferred
+ << " Maximum: " << rItem.maLayoutSize.Maximum);
+ }
+
+ sal_Int32 nWidth = rMinimalWidth;
+ try
+ {
+ // The demo sidebar extension "Analog Clock" fails with
+ // java.lang.AbstractMethodError here
+ nWidth = xPanel->getMinimalWidth();
+ }
+ catch (...)
+ {
+ }
+
+ uno::Reference<frame::XDesktop2> xDesktop
+ = frame::Desktop::create(comphelper::getProcessComponentContext());
+ uno::Reference<frame::XFrame> xFrame = xDesktop->getActiveFrame();
+ if (xFrame.is())
+ {
+ SidebarController* pController
+ = SidebarController::GetSidebarControllerForFrame(xFrame);
+ if (pController && pController->getMaximumWidth() < nWidth)
+ {
+ // Add 100 extra pixels to still have the sidebar resizable
+ // (See also documentation of XSidebarPanel::getMinimalWidth)
+ pController->setMaximumWidth(nWidth + 100);
+ }
+ }
+
+ if (nWidth > rMinimalWidth)
+ rMinimalWidth = nWidth;
+ }
+ else
+ rItem.maLayoutSize = ui::LayoutSize(MinimalPanelHeight, -1, 0);
+ }
+}
+
+void DistributeHeights (
+ ::std::vector<LayoutItem>& rLayoutItems,
+ const sal_Int32 nHeightToDistribute,
+ const sal_Int32 nContainerHeight,
+ const bool bMinimumHeightIsBase)
+{
+ if (nHeightToDistribute <= 0)
+ return;
+
+ sal_Int32 nRemainingHeightToDistribute (nHeightToDistribute);
+
+ // Compute the weights as difference between panel base height
+ // (either its minimum or preferred height) and the container height.
+ sal_Int32 nTotalWeight (0);
+ sal_Int32 nNoMaximumCount (0);
+
+ for (auto& rItem : rLayoutItems)
+ {
+ if (rItem.maLayoutSize.Maximum == 0)
+ continue;
+ if (rItem.maLayoutSize.Maximum < 0)
+ ++nNoMaximumCount;
+
+ const sal_Int32 nBaseHeight (
+ bMinimumHeightIsBase
+ ? rItem.maLayoutSize.Minimum
+ : rItem.maLayoutSize.Preferred);
+ if (nBaseHeight < nContainerHeight)
+ {
+ rItem.mnWeight = nContainerHeight - nBaseHeight;
+ nTotalWeight += rItem.mnWeight;
+ }
+ }
+
+ if (nTotalWeight == 0)
+ return;
+
+ // First pass of height distribution.
+ for (auto& rItem : rLayoutItems)
+ {
+ const sal_Int32 nBaseHeight (
+ bMinimumHeightIsBase
+ ? rItem.maLayoutSize.Minimum
+ : rItem.maLayoutSize.Preferred);
+ sal_Int32 nDistributedHeight (rItem.mnWeight * nHeightToDistribute / nTotalWeight);
+ if (nBaseHeight+nDistributedHeight > rItem.maLayoutSize.Maximum
+ && rItem.maLayoutSize.Maximum >= 0)
+ {
+ nDistributedHeight = ::std::max<sal_Int32>(0, rItem.maLayoutSize.Maximum - nBaseHeight);
+ }
+ rItem.mnDistributedHeight = nDistributedHeight;
+ nRemainingHeightToDistribute -= nDistributedHeight;
+ }
+
+ if (nRemainingHeightToDistribute == 0)
+ return;
+ OSL_ASSERT(nRemainingHeightToDistribute > 0);
+
+ // It is possible that not all of the height could be distributed
+ // because of Maximum heights being smaller than expected.
+ // Distribute the remaining height between the panels that have no
+ // Maximum (ie Maximum==-1).
+ if (nNoMaximumCount == 0)
+ {
+ // There are no panels with unrestricted height.
+ return;
+ }
+
+ const sal_Int32 nAdditionalHeightPerPanel(nRemainingHeightToDistribute / nNoMaximumCount);
+ // Handle rounding error.
+ sal_Int32 nAdditionalHeightForFirstPanel (nRemainingHeightToDistribute
+ - nNoMaximumCount*nAdditionalHeightPerPanel);
+
+ for (auto& rItem : rLayoutItems)
+ {
+ if (rItem.maLayoutSize.Maximum < 0)
+ {
+ rItem.mnDistributedHeight += nAdditionalHeightPerPanel + nAdditionalHeightForFirstPanel;
+ nRemainingHeightToDistribute -= nAdditionalHeightPerPanel + nAdditionalHeightForFirstPanel;
+ }
+ }
+
+ OSL_ASSERT(nRemainingHeightToDistribute==0);
+}
+
+tools::Rectangle PlaceDeckTitle(
+ const SidebarDockingWindow* pDockingWindow,
+ DeckTitleBar& rDeckTitleBar,
+ const tools::Rectangle& rAvailableSpace)
+{
+ if (pDockingWindow->IsFloatingMode())
+ {
+ // When the side bar is undocked then the outer system window displays the deck title.
+ rDeckTitleBar.Show(false);
+ return rAvailableSpace;
+ }
+ else
+ {
+ rDeckTitleBar.Show(true);
+ const sal_Int32 nDeckTitleBarHeight(rDeckTitleBar.get_preferred_size().Height());
+ return tools::Rectangle(
+ rAvailableSpace.Left(),
+ rAvailableSpace.Top() + nDeckTitleBarHeight,
+ rAvailableSpace.Right(),
+ rAvailableSpace.Bottom());
+ }
+}
+
+tools::Rectangle PlaceVerticalScrollBar (
+ weld::ScrolledWindow& rVerticalScrollBar,
+ const tools::Rectangle& rAvailableSpace,
+ const bool bShowVerticalScrollBar)
+{
+ if (bShowVerticalScrollBar)
+ {
+ const sal_Int32 nScrollBarWidth(rVerticalScrollBar.get_scroll_thickness());
+ rVerticalScrollBar.set_vpolicy(VclPolicyType::ALWAYS);
+ return tools::Rectangle(
+ rAvailableSpace.Left(),
+ rAvailableSpace.Top(),
+ rAvailableSpace.Right() - nScrollBarWidth,
+ rAvailableSpace.Bottom());
+ }
+ else
+ {
+ rVerticalScrollBar.set_vpolicy(VclPolicyType::NEVER);
+ return rAvailableSpace;
+ }
+}
+
+void SetupVerticalScrollBar(
+ weld::ScrolledWindow& rVerticalScrollBar,
+ const sal_Int32 nContentHeight,
+ const sal_Int32 nVisibleHeight)
+{
+ OSL_ASSERT(nContentHeight > nVisibleHeight);
+
+ rVerticalScrollBar.vadjustment_set_upper(nContentHeight-1);
+ rVerticalScrollBar.vadjustment_set_page_size(nVisibleHeight);
+}
+
+}
+
+} // end of namespace sfx2::sidebar
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/sidebar/DeckTitleBar.cxx b/sfx2/source/sidebar/DeckTitleBar.cxx
new file mode 100644
index 000000000..2a9bbb287
--- /dev/null
+++ b/sfx2/source/sidebar/DeckTitleBar.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 <sidebar/DeckTitleBar.hxx>
+#include <sfx2/sidebar/Theme.hxx>
+
+#include <vcl/bitmapex.hxx>
+#include <vcl/customweld.hxx>
+#include <vcl/outdev.hxx>
+#include <vcl/ptrstyle.hxx>
+
+#ifdef DEBUG
+#include <sfx2/sidebar/Tools.hxx>
+#endif
+
+namespace sfx2::sidebar {
+
+class GripWidget : public weld::CustomWidgetController
+{
+private:
+ BitmapEx maGrip;
+public:
+ virtual void SetDrawingArea(weld::DrawingArea* pDrawingArea) override
+ {
+ weld::CustomWidgetController::SetDrawingArea(pDrawingArea);
+ StyleUpdated();
+ }
+
+ virtual void StyleUpdated() override
+ {
+ maGrip = BitmapEx("sfx2/res/grip.png");
+ Size aGripSize(maGrip.GetSizePixel());
+ set_size_request(aGripSize.Width(), aGripSize.Height());
+ weld::CustomWidgetController::StyleUpdated();
+ }
+
+ virtual void Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& /*rRect*/) override
+ {
+ rRenderContext.SetBackground(Theme::GetColor(Theme::Color_DeckTitleBarBackground));
+ rRenderContext.Erase();
+ rRenderContext.DrawBitmapEx(Point(0, 0), maGrip);
+ }
+};
+
+DeckTitleBar::DeckTitleBar (const OUString& rsTitle,
+ weld::Builder& rBuilder,
+ const std::function<void()>& rCloserAction)
+ : TitleBar(rBuilder, Theme::Color_DeckTitleBarBackground)
+ , mxGripWidget(new GripWidget)
+ , mxGripWeld(new weld::CustomWeld(rBuilder, "grip", *mxGripWidget))
+ , mxLabel(rBuilder.weld_label("label"))
+ , maCloserAction(rCloserAction)
+ , mbIsCloserVisible(false)
+{
+ mxLabel->set_label(rsTitle);
+ mxGripWidget->SetPointer(PointerStyle::Move);
+
+ if (maCloserAction)
+ SetCloserVisible(true);
+}
+
+DeckTitleBar::~DeckTitleBar()
+{
+}
+
+tools::Rectangle DeckTitleBar::GetDragArea() const
+{
+ int x, y, width, height;
+ if (mxGripWidget->GetDrawingArea()->get_extents_relative_to(*mxTitlebar, x, y, width, height))
+ return tools::Rectangle(Point(x, y), Size(width, height));
+ return tools::Rectangle();
+}
+
+void DeckTitleBar::SetTitle(const OUString& rsTitle)
+{
+ mxLabel->set_label(rsTitle);
+}
+
+OUString DeckTitleBar::GetTitle() const
+{
+ return mxLabel->get_label();
+}
+
+void DeckTitleBar::SetCloserVisible (const bool bIsCloserVisible)
+{
+ if (mbIsCloserVisible == bIsCloserVisible)
+ return;
+
+ mbIsCloserVisible = bIsCloserVisible;
+
+ mxToolBox->set_visible(mbIsCloserVisible);
+}
+
+void DeckTitleBar::HandleToolBoxItemClick()
+{
+ if (maCloserAction)
+ maCloserAction();
+}
+
+void DeckTitleBar::DataChanged()
+{
+ mxToolBox->set_item_icon_name("button", "sfx2/res/closedoc.png");
+ TitleBar::DataChanged();
+}
+
+} // end of namespace sfx2::sidebar
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/sidebar/FocusManager.cxx b/sfx2/source/sidebar/FocusManager.cxx
new file mode 100644
index 000000000..0b8755810
--- /dev/null
+++ b/sfx2/source/sidebar/FocusManager.cxx
@@ -0,0 +1,505 @@
+/* -*- 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 <o3tl/safeint.hxx>
+#include <sfx2/sidebar/FocusManager.hxx>
+#include <sfx2/sidebar/Deck.hxx>
+#include <sfx2/sidebar/Panel.hxx>
+#include <sidebar/DeckTitleBar.hxx>
+#include <sidebar/PanelTitleBar.hxx>
+#include <sidebar/TitleBar.hxx>
+#include <vcl/event.hxx>
+#include <vcl/weld.hxx>
+
+namespace sfx2::sidebar {
+
+FocusManager::FocusLocation::FocusLocation (const PanelComponent eComponent, const sal_Int32 nIndex)
+ : meComponent(eComponent),
+ mnIndex(nIndex)
+{
+}
+
+FocusManager::FocusManager(const std::function<void(const Panel&)>& rShowPanelFunctor)
+ : mpDeckTitleBar(nullptr),
+ maShowPanelFunctor(rShowPanelFunctor)
+{
+}
+
+FocusManager::~FocusManager()
+{
+ Clear();
+}
+
+void FocusManager::GrabFocus()
+{
+ FocusDeckTitle();
+}
+
+void FocusManager::GrabFocusPanel()
+{
+ FocusPanel(0, false);
+}
+
+void FocusManager::Clear()
+{
+ SetDeck(nullptr);
+ ClearPanels();
+ ClearButtons();
+}
+
+void FocusManager::ClearPanels()
+{
+ SharedPanelContainer aPanels;
+ aPanels.swap(maPanels);
+ for (auto const& panel : aPanels)
+ {
+ if (panel->GetTitleBar())
+ {
+ UnregisterWindow(panel->GetTitleBar()->GetToolBox());
+ UnregisterWindow(panel->GetTitleBar()->GetExpander());
+ }
+
+ weld::Container* pContents = panel->GetContents();
+ UnregisterWindow(*pContents);
+ }
+}
+
+void FocusManager::ClearButtons()
+{
+ std::vector<weld::Widget*> aButtons;
+ aButtons.swap(maButtons);
+ for (auto const& button : aButtons)
+ {
+ UnregisterWindow(*button);
+ }
+}
+
+void FocusManager::SetDeck(Deck* pDeck)
+{
+ DeckTitleBar* pDeckTitleBar = pDeck ? pDeck->GetTitleBar() : nullptr;
+ if (mpDeckTitleBar != nullptr)
+ UnregisterWindow(mpDeckTitleBar->GetToolBox());
+ mxDeck = pDeck;
+ mpDeckTitleBar = pDeckTitleBar;
+ if (mpDeckTitleBar != nullptr)
+ RegisterWindow(mpDeckTitleBar->GetToolBox());
+}
+
+void FocusManager::SetPanels (const SharedPanelContainer& rPanels)
+{
+ ClearPanels();
+ for (auto const& panel : rPanels)
+ {
+ if (panel->GetTitleBar())
+ {
+ RegisterWindow(panel->GetTitleBar()->GetToolBox());
+ RegisterWindow(panel->GetTitleBar()->GetExpander());
+ }
+
+ // Register also as key event listener at the panel.
+ weld::Container* pContents = panel->GetContents();
+ RegisterWindow(*pContents);
+
+ maPanels.emplace_back(panel);
+ }
+}
+
+void FocusManager::SetButtons(const std::vector<weld::Widget*>& rButtons)
+{
+ ClearButtons();
+ for (auto const& button : rButtons)
+ {
+ RegisterWindow(*button);
+ maButtons.emplace_back(button);
+ }
+}
+
+void FocusManager::RegisterWindow(weld::Widget& rWidget)
+{
+ UnregisterWindow(rWidget); // explicitly unset key press handler so we can reconnect without warnings
+ rWidget.connect_key_press(LINK(this, FocusManager, KeyInputHdl));
+}
+
+void FocusManager::UnregisterWindow(weld::Widget& rWidget)
+{
+ rWidget.connect_key_press(Link<const KeyEvent&, bool>());
+}
+
+FocusManager::FocusLocation FocusManager::GetFocusLocation() const
+{
+ // Check the deck title.
+ if (mpDeckTitleBar && mpDeckTitleBar->GetToolBox().has_focus())
+ return FocusLocation(PC_DeckToolBox, -1);
+
+ // Search the panels.
+ for (size_t nIndex = 0; nIndex < maPanels.size(); ++nIndex)
+ {
+ PanelTitleBar* pTitleBar = maPanels[nIndex]->GetTitleBar();
+ if (!pTitleBar)
+ continue;
+ if (pTitleBar->GetExpander().has_focus())
+ return FocusLocation(PC_PanelTitle, nIndex);
+ if (pTitleBar->GetToolBox().has_focus())
+ return FocusLocation(PC_PanelToolBox, nIndex);
+ weld::Container* pContents = maPanels[nIndex]->GetContents();
+ if (pContents->has_child_focus())
+ return FocusLocation(PC_PanelContent, nIndex);
+ }
+
+ // Search the buttons.
+ for (size_t nIndex=0; nIndex < maButtons.size(); ++nIndex)
+ {
+ if (maButtons[nIndex]->has_focus())
+ return FocusLocation(PC_TabBar, nIndex);
+ }
+ return FocusLocation(PC_None, -1);
+}
+
+void FocusManager::FocusDeckTitle()
+{
+ if (mpDeckTitleBar != nullptr)
+ {
+ if (mpDeckTitleBar->GetToolBox().get_n_items() > 0)
+ {
+ weld::Toolbar& rToolBox = mpDeckTitleBar->GetToolBox();
+ rToolBox.grab_focus();
+ }
+ else
+ FocusPanel(0, false);
+ }
+ else
+ FocusPanel(0, false);
+}
+
+bool FocusManager::IsDeckTitleVisible() const
+{
+ return mpDeckTitleBar != nullptr && mpDeckTitleBar->GetVisible();
+}
+
+bool FocusManager::IsPanelTitleVisible (const sal_Int32 nPanelIndex) const
+{
+ if (nPanelIndex<0 || o3tl::make_unsigned(nPanelIndex)>=maPanels.size())
+ return false;
+
+ TitleBar* pTitleBar = maPanels[nPanelIndex]->GetTitleBar();
+ if (!pTitleBar)
+ return false;
+ return pTitleBar->GetVisible();
+}
+
+void FocusManager::FocusPanel (
+ const sal_Int32 nPanelIndex,
+ const bool bFallbackToDeckTitle)
+{
+ if (nPanelIndex<0 || o3tl::make_unsigned(nPanelIndex)>=maPanels.size())
+ {
+ if (bFallbackToDeckTitle)
+ FocusDeckTitle();
+ return;
+ }
+
+ Panel& rPanel (*maPanels[nPanelIndex]);
+ PanelTitleBar* pTitleBar = rPanel.GetTitleBar();
+ if (pTitleBar && pTitleBar->GetVisible())
+ {
+ rPanel.SetExpanded(true);
+ pTitleBar->GetExpander().grab_focus();
+ }
+ else if (bFallbackToDeckTitle)
+ {
+ // The panel title is not visible, fall back to the deck
+ // title.
+ // Make sure that the desk title is visible here to prevent a
+ // loop when both the title of panel 0 and the deck title are
+ // not present.
+ if (IsDeckTitleVisible())
+ FocusDeckTitle();
+ else
+ FocusPanelContent(nPanelIndex);
+ }
+ else
+ FocusPanelContent(nPanelIndex);
+
+ if (maShowPanelFunctor)
+ maShowPanelFunctor(rPanel);
+}
+
+void FocusManager::FocusPanelContent(const sal_Int32 nPanelIndex)
+{
+ if (!maPanels[nPanelIndex]->IsExpanded())
+ maPanels[nPanelIndex]->SetExpanded(true);
+
+ weld::Container* pContents = maPanels[nPanelIndex]->GetContents();
+ pContents->child_grab_focus();
+}
+
+void FocusManager::FocusButton (const sal_Int32 nButtonIndex)
+{
+ maButtons[nButtonIndex]->grab_focus();
+}
+
+void FocusManager::MoveFocusInsidePanel (
+ const FocusLocation& rFocusLocation,
+ const sal_Int32 nDirection)
+{
+ const bool bHasToolBoxItem (
+ maPanels[rFocusLocation.mnIndex]->GetTitleBar()->GetToolBox().get_n_items() > 0);
+ switch (rFocusLocation.meComponent)
+ {
+ case PC_PanelTitle:
+ if (nDirection > 0 && bHasToolBoxItem)
+ maPanels[rFocusLocation.mnIndex]->GetTitleBar()->GetToolBox().grab_focus();
+ else
+ FocusPanelContent(rFocusLocation.mnIndex);
+ break;
+
+ case PC_PanelToolBox:
+ if (nDirection < 0 && bHasToolBoxItem)
+ maPanels[rFocusLocation.mnIndex]->GetTitleBar()->GetExpander().grab_focus();
+ else
+ FocusPanelContent(rFocusLocation.mnIndex);
+ break;
+
+ default: break;
+ }
+}
+
+bool FocusManager::MoveFocusInsideDeckTitle (
+ const FocusLocation& rFocusLocation,
+ const sal_Int32 nDirection)
+{
+ bool bConsumed = false;
+ // Note that when the title bar of the first (and only) panel is
+ // not visible then the deck title takes its place and the focus
+ // is moved between a) deck closer and b) content of panel 0.
+ switch (rFocusLocation.meComponent)
+ {
+ case PC_DeckToolBox:
+ if (nDirection>0 && ! IsPanelTitleVisible(0))
+ {
+ FocusPanelContent(0);
+ bConsumed = true;
+ }
+ break;
+
+ default: break;
+ }
+ return bConsumed;
+}
+
+bool FocusManager::HandleKeyEvent(
+ const vcl::KeyCode& rKeyCode,
+ const FocusLocation& aLocation)
+{
+ bool bConsumed = false;
+
+ switch (rKeyCode.GetCode())
+ {
+ case KEY_ESCAPE:
+ switch (aLocation.meComponent)
+ {
+ case PC_TabBar:
+ case PC_DeckToolBox:
+ case PC_PanelTitle:
+ case PC_PanelToolBox:
+ {
+ if (mxDeck)
+ {
+ mxDeck->GrabFocusToDocument();
+ bConsumed = true;
+ }
+ break;
+ }
+ case PC_PanelContent:
+ // Return focus to tab bar sidebar settings button or panel title.
+ if (!IsDeckTitleVisible() && maPanels.size() == 1)
+ FocusButton(0);
+ else
+ FocusPanel(aLocation.mnIndex, true);
+ bConsumed = true;
+ break;
+ default:
+ break;
+ }
+ return bConsumed;
+
+ case KEY_RETURN:
+ switch (aLocation.meComponent)
+ {
+ case PC_DeckToolBox:
+ FocusButton(0);
+ bConsumed = true;
+ break;
+
+ case PC_PanelTitle:
+ // Enter the panel.
+ FocusPanelContent(aLocation.mnIndex);
+ bConsumed = true;
+ break;
+
+ default:
+ break;
+ }
+ return bConsumed;
+
+ case KEY_TAB:
+ {
+ const sal_Int32 nDirection (
+ rKeyCode.IsShift()
+ ? -1
+ : +1);
+ switch (aLocation.meComponent)
+ {
+ case PC_PanelTitle:
+ case PC_PanelToolBox:
+ MoveFocusInsidePanel(aLocation, nDirection);
+ bConsumed = true;
+ break;
+
+ case PC_DeckToolBox:
+ bConsumed = MoveFocusInsideDeckTitle(aLocation, nDirection);
+ break;
+
+ case PC_TabBar:
+ if (rKeyCode.IsShift())
+ FocusPanel(maPanels.size()-1, true);
+ else
+ {
+ if (IsDeckTitleVisible())
+ FocusDeckTitle();
+ else
+ FocusPanel(0, true);
+ }
+ bConsumed = true;
+ break;
+
+ default:
+ break;
+ }
+ break;
+ }
+
+ case KEY_LEFT:
+ case KEY_UP:
+ switch (aLocation.meComponent)
+ {
+ case PC_PanelTitle:
+ case PC_PanelToolBox:
+ // Go to previous panel or the deck title.
+ if (aLocation.mnIndex > 0)
+ FocusPanel(aLocation.mnIndex-1, true);
+ else if (IsDeckTitleVisible())
+ FocusDeckTitle();
+ else
+ {
+ // Focus the last button.
+ sal_Int32 nIndex(maButtons.size()-1);
+ while(!maButtons[nIndex]->get_visible() && --nIndex > 0);
+ FocusButton(nIndex);
+ }
+ bConsumed = true;
+ break;
+
+ case PC_DeckToolBox:
+ {
+ // Focus the last button.
+ sal_Int32 nIndex(maButtons.size()-1);
+ while(!maButtons[nIndex]->get_visible() && --nIndex > 0);
+ FocusButton(nIndex);
+ bConsumed = true;
+ break;
+ }
+
+ case PC_TabBar:
+ // Go to previous tab bar item.
+ if (aLocation.mnIndex == 0)
+ FocusPanel(maPanels.size()-1, true);
+ else
+ {
+ sal_Int32 nIndex((aLocation.mnIndex + maButtons.size() - 1) % maButtons.size());
+ while(!maButtons[nIndex]->get_visible() && --nIndex > 0);
+ FocusButton(nIndex);
+ }
+ bConsumed = true;
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ case KEY_RIGHT:
+ case KEY_DOWN:
+ switch(aLocation.meComponent)
+ {
+ case PC_PanelTitle:
+ case PC_PanelToolBox:
+ // Go to next panel.
+ if (aLocation.mnIndex < static_cast<sal_Int32>(maPanels.size())-1)
+ FocusPanel(aLocation.mnIndex+1, false);
+ else
+ FocusButton(0);
+ bConsumed = true;
+ break;
+
+ case PC_DeckToolBox:
+ // Focus the first panel.
+ if (IsPanelTitleVisible(0))
+ FocusPanel(0, false);
+ else
+ FocusButton(0);
+ bConsumed = true;
+ break;
+
+ case PC_TabBar:
+ // Go to next tab bar item.
+ if (aLocation.mnIndex < static_cast<sal_Int32>(maButtons.size())-1)
+ {
+ sal_Int32 nIndex(aLocation.mnIndex + 1);
+ while(!maButtons[nIndex]->get_visible() && ++nIndex < static_cast<sal_Int32>(maButtons.size()));
+ if (nIndex < static_cast<sal_Int32>(maButtons.size()))
+ {
+ FocusButton(nIndex);
+ bConsumed = true;
+ break;
+ }
+ }
+ if (IsDeckTitleVisible())
+ FocusDeckTitle();
+ else
+ FocusPanel(0, true);
+ bConsumed = true;
+ break;
+
+ default:
+ break;
+ }
+ break;
+ }
+ return bConsumed;
+}
+
+IMPL_LINK(FocusManager, KeyInputHdl, const KeyEvent&, rKeyEvent, bool)
+{
+ return HandleKeyEvent(rKeyEvent.GetKeyCode(), GetFocusLocation());
+}
+
+} // end of namespace sfx2::sidebar
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/sidebar/IContextChangeReceiver.cxx b/sfx2/source/sidebar/IContextChangeReceiver.cxx
new file mode 100644
index 000000000..6fc053921
--- /dev/null
+++ b/sfx2/source/sidebar/IContextChangeReceiver.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/sidebar/IContextChangeReceiver.hxx>
+
+namespace sfx2::sidebar {
+
+IContextChangeReceiver::~IContextChangeReceiver()
+{
+}
+
+} // end of namespace ::sfx2::sidebar
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/sidebar/ILayoutableWindow.cxx b/sfx2/source/sidebar/ILayoutableWindow.cxx
new file mode 100644
index 000000000..6a24cadbf
--- /dev/null
+++ b/sfx2/source/sidebar/ILayoutableWindow.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/sidebar/ILayoutableWindow.hxx>
+
+namespace sfx2::sidebar {
+
+ILayoutableWindow::~ILayoutableWindow()
+{
+}
+
+} // end of namespace ::sfx2::sidebar
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/sidebar/Panel.cxx b/sfx2/source/sidebar/Panel.cxx
new file mode 100644
index 000000000..3f85c8ae5
--- /dev/null
+++ b/sfx2/source/sidebar/Panel.cxx
@@ -0,0 +1,240 @@
+/* -*- 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/sidebar/Panel.hxx>
+#include <sidebar/PanelTitleBar.hxx>
+#include <sidebar/PanelDescriptor.hxx>
+#include <sfx2/sidebar/ResourceManager.hxx>
+#include <sfx2/sidebar/SidebarController.hxx>
+#include <sfx2/sidebar/SidebarPanelBase.hxx>
+#include <sfx2/viewsh.hxx>
+#include <tools/json_writer.hxx>
+
+
+#ifdef DEBUG
+#include <sfx2/sidebar/Tools.hxx>
+#include <sfx2/sidebar/Deck.hxx>
+#endif
+
+#include <com/sun/star/ui/XToolPanel.hpp>
+#include <com/sun/star/ui/XSidebarPanel.hpp>
+#include <com/sun/star/ui/XUIElement.hpp>
+
+#include <vcl/weldutils.hxx>
+
+using namespace css;
+using namespace css::uno;
+
+namespace sfx2::sidebar {
+
+Panel::Panel(const PanelDescriptor& rPanelDescriptor,
+ weld::Widget* pParentWindow,
+ const bool bIsInitiallyExpanded,
+ Deck* pDeck,
+ const std::function<Context()>& rContextAccess,
+ const css::uno::Reference<css::frame::XFrame>& rxFrame)
+ : mxBuilder(Application::CreateBuilder(pParentWindow, "sfx/ui/panel.ui", false, reinterpret_cast<sal_uInt64>(SfxViewShell::Current())))
+ , msPanelId(rPanelDescriptor.msId)
+ , msTitle(rPanelDescriptor.msTitle)
+ , mbIsTitleBarOptional(rPanelDescriptor.mbIsTitleBarOptional)
+ , mbWantsAWT(rPanelDescriptor.mbWantsAWT)
+ , mbIsExpanded(bIsInitiallyExpanded)
+ , mbLurking(false)
+ , maContextAccess(rContextAccess)
+ , mxFrame(rxFrame)
+ , mpParentWindow(pParentWindow)
+ , mxDeck(pDeck)
+ , mxContainer(mxBuilder->weld_box("Panel"))
+ , mxTitleBar(new PanelTitleBar(rPanelDescriptor.msTitle, *mxBuilder, this))
+ , mxContents(mxBuilder->weld_box("contents"))
+{
+ mxContents->set_visible(mbIsExpanded);
+ mxContainer->connect_get_property_tree(LINK(this, Panel, DumpAsPropertyTreeHdl));
+}
+
+bool Panel::get_extents(tools::Rectangle &rExtents) const
+{
+ // Get vertical extent of the panel.
+ int x, y, width, height;
+ if (mxContainer->get_extents_relative_to(*mpParentWindow, x, y, width, height))
+ {
+ rExtents = tools::Rectangle(Point(x, y), Size(width, height));
+ return true;
+ }
+ return false;
+}
+
+void Panel::SetHeightPixel(int nHeight)
+{
+ mxContainer->set_size_request(-1, nHeight);
+}
+
+void Panel::set_margin_top(int nMargin)
+{
+ mxContainer->set_margin_top(nMargin);
+}
+
+void Panel::set_margin_bottom(int nMargin)
+{
+ mxContainer->set_margin_bottom(nMargin);
+}
+
+void Panel::set_vexpand(bool bExpand)
+{
+ mxContainer->set_vexpand(bExpand);
+}
+
+void Panel::SetLurkMode(bool bLurk)
+{
+ // cf. DeckLayouter
+ mbLurking = bLurk;
+}
+
+IMPL_LINK(Panel, DumpAsPropertyTreeHdl, tools::JsonWriter&, rJsonWriter, void)
+{
+ if (!IsLurking())
+ rJsonWriter.put("type", "panel");
+}
+
+Panel::~Panel()
+{
+ mxPanelComponent = nullptr;
+
+ {
+ Reference<lang::XComponent> xComponent (mxElement, UNO_QUERY);
+ mxElement = nullptr;
+ if (xComponent.is())
+ xComponent->dispose();
+ }
+
+ {
+ Reference<lang::XComponent> xComponent = GetElementWindow();
+ if (xComponent.is())
+ xComponent->dispose();
+ }
+
+ mxTitleBar.reset();
+
+ if (mxXWindow)
+ {
+ mxXWindow->dispose();
+ mxXWindow.clear();
+ }
+ mxContents.reset();
+
+ assert(!mxTitleBar);
+}
+
+PanelTitleBar* Panel::GetTitleBar() const
+{
+ return mxTitleBar.get();
+}
+
+weld::Box* Panel::GetContents() const
+{
+ return mxContents.get();
+}
+
+void Panel::Show(bool bShow)
+{
+ mxContainer->set_visible(bShow);
+}
+
+void Panel::SetUIElement (const Reference<ui::XUIElement>& rxElement)
+{
+ mxElement = rxElement;
+ if (!mxElement.is())
+ return;
+ mxPanelComponent.set(mxElement->getRealInterface(), UNO_QUERY);
+ if (mbWantsAWT)
+ return;
+ sfx2::sidebar::SidebarPanelBase* pPanelBase = dynamic_cast<sfx2::sidebar::SidebarPanelBase*>(mxElement.get());
+ assert(pPanelBase && "internal panels are all expected to inherit from SidebarPanelBase");
+ pPanelBase->SetParentPanel(this);
+}
+
+void Panel::TriggerDeckLayouting()
+{
+ mxDeck->RequestLayout();
+}
+
+weld::Window* Panel::GetFrameWeld()
+{
+ return mxDeck->GetFrameWeld();
+}
+
+void Panel::SetExpanded (const bool bIsExpanded)
+{
+ SidebarController* pSidebarController = SidebarController::GetSidebarControllerForFrame(mxFrame);
+
+ if (mbIsExpanded == bIsExpanded)
+ return;
+
+ mbIsExpanded = bIsExpanded;
+ mxTitleBar->UpdateExpandedState();
+ TriggerDeckLayouting();
+
+ if (maContextAccess && pSidebarController)
+ {
+ pSidebarController->GetResourceManager()->StorePanelExpansionState(
+ msPanelId,
+ bIsExpanded,
+ maContextAccess());
+ }
+
+ mxContents->set_visible(mbIsExpanded);
+}
+
+bool Panel::HasIdPredicate (std::u16string_view rsId) const
+{
+ return msPanelId == rsId;
+}
+
+void Panel::DataChanged()
+{
+ mxTitleBar->DataChanged();
+}
+
+Reference<awt::XWindow> Panel::GetElementWindow()
+{
+ if (mxElement.is())
+ {
+ Reference<ui::XToolPanel> xToolPanel(mxElement->getRealInterface(), UNO_QUERY);
+ if (xToolPanel.is())
+ return xToolPanel->getWindow();
+ }
+
+ return nullptr;
+}
+
+const Reference<awt::XWindow>& Panel::GetElementParentWindow()
+{
+ if (!mxXWindow)
+ {
+ if (mbWantsAWT)
+ mxXWindow = mxContents->CreateChildFrame();
+ else
+ mxXWindow = Reference<awt::XWindow>(new weld::TransportAsXWindow(mxContents.get()));
+ }
+ return mxXWindow;
+}
+
+} // end of namespace sfx2::sidebar
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/sidebar/PanelDescriptor.cxx b/sfx2/source/sidebar/PanelDescriptor.cxx
new file mode 100644
index 000000000..6ca328de2
--- /dev/null
+++ b/sfx2/source/sidebar/PanelDescriptor.cxx
@@ -0,0 +1,57 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sidebar/PanelDescriptor.hxx>
+
+namespace sfx2::sidebar {
+
+PanelDescriptor::PanelDescriptor()
+ : mbIsTitleBarOptional(false),
+ mnOrderIndex(10000), // Default value as defined in Sidebar.xcs
+ mbShowForReadOnlyDocuments(false),
+ mbWantsCanvas(false),
+ mbWantsAWT(true),
+ mbExperimental(false)
+{
+}
+
+PanelDescriptor::PanelDescriptor (const PanelDescriptor& rOther)
+ : msTitle(rOther.msTitle),
+ mbIsTitleBarOptional(rOther.mbIsTitleBarOptional),
+ msId(rOther.msId),
+ msDeckId(rOther.msDeckId),
+ msTitleBarIconURL(rOther.msTitleBarIconURL),
+ msHighContrastTitleBarIconURL(rOther.msHighContrastTitleBarIconURL),
+ maContextList(rOther.maContextList),
+ msImplementationURL(rOther.msImplementationURL),
+ mnOrderIndex(rOther.mnOrderIndex),
+ mbShowForReadOnlyDocuments(rOther.mbShowForReadOnlyDocuments),
+ mbWantsCanvas(rOther.mbWantsCanvas),
+ mbWantsAWT(rOther.mbWantsAWT),
+ mbExperimental(rOther.mbExperimental)
+{
+}
+
+PanelDescriptor::~PanelDescriptor()
+{
+}
+
+} // end of namespace sfx2::sidebar
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/sidebar/PanelLayout.cxx b/sfx2/source/sidebar/PanelLayout.cxx
new file mode 100644
index 000000000..ef80cb3d2
--- /dev/null
+++ b/sfx2/source/sidebar/PanelLayout.cxx
@@ -0,0 +1,86 @@
+/* -*- 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 <sal/log.hxx>
+#include <sfx2/sidebar/Panel.hxx>
+#include <sfx2/sidebar/PanelLayout.hxx>
+#include <sfx2/sidebar/Theme.hxx>
+#include <sfx2/viewsh.hxx>
+#include <vcl/event.hxx>
+#include <vcl/svapp.hxx>
+
+using namespace sfx2::sidebar;
+
+PanelLayout::PanelLayout(weld::Widget* pParent, const OString& rID, const OUString& rUIXMLDescription)
+ : m_xBuilder(Application::CreateBuilder(pParent, rUIXMLDescription, false, reinterpret_cast<sal_uInt64>(SfxViewShell::Current())))
+ , m_xContainer(m_xBuilder->weld_container(rID))
+ , m_pPanel(nullptr)
+{
+ m_xContainer->set_background(Theme::GetColor(Theme::Color_PanelBackground));
+ m_xContainer->connect_get_property_tree(LINK(this, PanelLayout, DumpAsPropertyTreeHdl));
+ ::Application::AddEventListener(LINK(this, PanelLayout, DataChangedEventListener));
+}
+
+IMPL_LINK(PanelLayout, DumpAsPropertyTreeHdl, tools::JsonWriter&, rJsonWriter, void)
+{
+ DumpAsPropertyTree(rJsonWriter);
+}
+
+void PanelLayout::DumpAsPropertyTree(tools::JsonWriter&)
+{
+}
+
+IMPL_LINK(PanelLayout, DataChangedEventListener, VclSimpleEvent&, rEvent, void)
+{
+ if (rEvent.GetId() != VclEventId::ApplicationDataChanged)
+ return;
+
+ DataChangedEvent* pData = static_cast<DataChangedEvent*>(static_cast<VclWindowEvent&>(rEvent).GetData());
+ DataChanged(*pData);
+}
+
+void PanelLayout::DataChanged(const DataChangedEvent& rEvent)
+{
+ if (rEvent.GetType() != DataChangedEventType::SETTINGS)
+ return;
+ if (rEvent.GetFlags() & AllSettingsFlags::STYLE)
+ m_xContainer->set_background(Theme::GetColor(Theme::Color_PanelBackground));
+}
+
+void PanelLayout::SetPanel(sfx2::sidebar::Panel* pPanel)
+{
+ m_pPanel = pPanel;
+}
+
+weld::Window* PanelLayout::GetFrameWeld() const
+{
+ if (!m_pPanel)
+ {
+ SAL_WARN("sfx.sidebar", "Expected a toplevel Panel to exist");
+ return nullptr;
+ }
+ return m_pPanel->GetFrameWeld();
+}
+
+PanelLayout::~PanelLayout()
+{
+ ::Application::RemoveEventListener(LINK(this, PanelLayout, DataChangedEventListener));
+
+ m_xContainer.reset();
+ m_xBuilder.reset();
+}
+
+void PanelLayout::queue_resize()
+{
+ if (!m_xContainer)
+ return;
+ m_xContainer->queue_resize();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/sidebar/PanelTitleBar.cxx b/sfx2/source/sidebar/PanelTitleBar.cxx
new file mode 100644
index 000000000..50d6e6abb
--- /dev/null
+++ b/sfx2/source/sidebar/PanelTitleBar.cxx
@@ -0,0 +1,123 @@
+/* -*- 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 <sidebar/PanelTitleBar.hxx>
+#include <sfx2/sidebar/Panel.hxx>
+#include <sfx2/sidebar/Theme.hxx>
+#include <sidebar/ControllerFactory.hxx>
+
+using namespace css;
+using namespace css::uno;
+
+namespace sfx2::sidebar {
+
+PanelTitleBar::PanelTitleBar(const OUString& rsTitle,
+ weld::Builder& rBuilder,
+ Panel* pPanel)
+ : TitleBar(rBuilder, Theme::Color_PanelTitleBarBackground),
+ mxExpander(rBuilder.weld_expander("expander")),
+ mpPanel(pPanel),
+ msIdent("button")
+{
+ mxExpander->set_label(rsTitle);
+ mxExpander->connect_expanded(LINK(this, PanelTitleBar, ExpandHdl));
+
+ // tdf#145801 lock the height to the size it needs with the "toolbar" button shown
+ // so all of the titlebars are the same height if the "toolbar" is hidden in some
+ // of them
+ mxToolBox->show();
+ mxTitlebar->set_size_request(-1, mxTitlebar->get_preferred_size().Height());
+ mxToolBox->hide();
+
+ assert(mpPanel);
+
+ UpdateExpandedState();
+}
+
+void PanelTitleBar::SetTitle(const OUString& rsTitle)
+{
+ mxExpander->set_label(rsTitle);
+}
+
+OUString PanelTitleBar::GetTitle() const
+{
+ return mxExpander->get_label();
+}
+
+void PanelTitleBar::UpdateExpandedState()
+{
+ mxExpander->set_expanded(mpPanel->IsExpanded());
+}
+
+PanelTitleBar::~PanelTitleBar()
+{
+ Reference<lang::XComponent> xComponent(mxController, UNO_QUERY);
+ if (xComponent.is())
+ xComponent->dispose();
+ mxController.clear();
+ mpPanel = nullptr;
+ mxExpander.reset();
+}
+
+void PanelTitleBar::SetMoreOptionsCommand(const OUString& rsCommandName,
+ const css::uno::Reference<css::frame::XFrame>& rxFrame,
+ const css::uno::Reference<css::frame::XController>& rxController)
+{
+ if (rsCommandName == msMoreOptionsCommand)
+ return;
+
+ if (!msMoreOptionsCommand.isEmpty())
+ mxToolBox->hide();
+
+ msMoreOptionsCommand = rsCommandName;
+
+ if (msMoreOptionsCommand.isEmpty())
+ return;
+
+ msIdent = msMoreOptionsCommand.toUtf8();
+ mxToolBox->set_item_ident(0, msIdent);
+
+ Reference<lang::XComponent> xComponent(mxController, UNO_QUERY);
+ if (xComponent.is())
+ xComponent->dispose();
+ mxController =
+ ControllerFactory::CreateToolBoxController(
+ *mxToolBox, mrBuilder, msMoreOptionsCommand, rxFrame, rxController, true);
+
+ mxToolBox->show();
+}
+
+void PanelTitleBar::HandleToolBoxItemClick()
+{
+ if (!mxController)
+ return;
+ mxController->click();
+ mxController->execute(0);
+}
+
+IMPL_LINK(PanelTitleBar, ExpandHdl, weld::Expander&, rExpander, void)
+{
+ if (!mpPanel)
+ return;
+ mpPanel->SetExpanded(rExpander.get_expanded());
+}
+
+} // end of namespace sfx2::sidebar
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/sidebar/ResourceManager.cxx b/sfx2/source/sidebar/ResourceManager.cxx
new file mode 100644
index 000000000..00b59df5e
--- /dev/null
+++ b/sfx2/source/sidebar/ResourceManager.cxx
@@ -0,0 +1,802 @@
+/* -*- 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 <sidebar/DeckDescriptor.hxx>
+#include <sidebar/PanelDescriptor.hxx>
+#include <sfx2/sidebar/ResourceManager.hxx>
+#include <sidebar/Tools.hxx>
+
+#include <officecfg/Office/Common.hxx>
+#include <officecfg/Office/UI/Sidebar.hxx>
+#include <unotools/confignode.hxx>
+#include <comphelper/lok.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/namedvaluecollection.hxx>
+#include <comphelper/sequence.hxx>
+#include <comphelper/types.hxx>
+
+#include <tools/diagnose_ex.h>
+#include <sal/log.hxx>
+#include <vcl/EnumContext.hxx>
+#include <o3tl/string_view.hxx>
+
+#include <com/sun/star/frame/ModuleManager.hpp>
+#include <com/sun/star/ui/XSidebarPanel.hpp>
+#include <com/sun/star/ui/XUpdateModel.hpp>
+
+#include <map>
+
+using namespace css;
+using namespace css::uno;
+
+namespace sfx2::sidebar {
+
+namespace
+{
+
+OUString getString(utl::OConfigurationNode const & aNode, const char* pNodeName)
+{
+ return comphelper::getString(aNode.getNodeValue(pNodeName));
+}
+sal_Int32 getInt32(utl::OConfigurationNode const & aNode, const char* pNodeName)
+{
+ return comphelper::getINT32(aNode.getNodeValue(pNodeName));
+}
+bool getBool(utl::OConfigurationNode const & aNode, const char* pNodeName)
+{
+ return comphelper::getBOOL(aNode.getNodeValue(pNodeName));
+}
+
+css::uno::Sequence<OUString> BuildContextList (const ContextList& rContextList)
+{
+ const ::std::vector<ContextList::Entry>& entries = rContextList.GetEntries();
+
+ css::uno::Sequence<OUString> result(entries.size());
+ auto resultRange = asNonConstRange(result);
+ tools::Long i = 0;
+
+ for (auto const& entry : entries)
+ {
+ OUString appName = entry.maContext.msApplication;
+ OUString contextName = entry.maContext.msContext;
+ OUString menuCommand = entry.msMenuCommand;
+
+ OUString visibility;
+ if (entry.mbIsInitiallyVisible)
+ visibility = "visible";
+ else
+ visibility = "hidden";
+
+ OUString element = appName + ", " + contextName +", " + visibility;
+
+ if (!menuCommand.isEmpty())
+ element += ", "+menuCommand;
+
+ resultRange[i] = element;
+
+ ++i;
+ }
+
+ return result;
+
+}
+
+} //end anonymous namespace
+
+ResourceManager::ResourceManager()
+{
+ ReadDeckList();
+ ReadPanelList();
+ ReadLastActive();
+}
+
+ResourceManager::~ResourceManager()
+{
+}
+
+void ResourceManager::InitDeckContext(const Context& rContext)
+{
+ for (auto const& deck : maDecks)
+ {
+ const ContextList::Entry* pMatchingEntry = deck->maContextList.GetMatch(rContext);
+
+ bool bIsEnabled;
+ if (pMatchingEntry)
+ bIsEnabled = pMatchingEntry->mbIsInitiallyVisible;
+ else
+ bIsEnabled = false;
+
+ deck->mbIsEnabled = bIsEnabled;
+ }
+}
+
+std::shared_ptr<DeckDescriptor> ResourceManager::ImplGetDeckDescriptor(std::u16string_view rsDeckId) const
+{
+ for (auto const& deck : maDecks)
+ {
+ if (deck->mbExperimental && !officecfg::Office::Common::Misc::ExperimentalMode::get())
+ continue;
+ if (deck->msId == rsDeckId)
+ return deck;
+ }
+ return nullptr;
+}
+
+std::shared_ptr<DeckDescriptor> ResourceManager::GetDeckDescriptor(std::u16string_view rsDeckId) const
+{
+ return ImplGetDeckDescriptor( rsDeckId );
+}
+
+std::shared_ptr<PanelDescriptor> ResourceManager::ImplGetPanelDescriptor(std::u16string_view rsPanelId) const
+{
+ for (auto const& panel : maPanels)
+ {
+ if (panel->msId == rsPanelId)
+ return panel;
+ }
+ return nullptr;
+}
+
+std::shared_ptr<PanelDescriptor> ResourceManager::GetPanelDescriptor(std::u16string_view rsPanelId) const
+{
+ return ImplGetPanelDescriptor( rsPanelId );
+}
+
+const ResourceManager::DeckContextDescriptorContainer& ResourceManager::GetMatchingDecks (
+ DeckContextDescriptorContainer& rDecks,
+ const Context& rContext,
+ const bool bIsDocumentReadOnly,
+ const Reference<frame::XController>& rxController)
+{
+ ReadLegacyAddons(rxController);
+
+ std::multimap<sal_Int32,DeckContextDescriptor> aOrderedIds;
+ for (auto const& deck : maDecks)
+ {
+ if (deck->mbExperimental && !officecfg::Office::Common::Misc::ExperimentalMode::get())
+ continue;
+
+ const DeckDescriptor& rDeckDescriptor (*deck);
+ if (rDeckDescriptor.maContextList.GetMatch(rContext) == nullptr)
+ continue;
+
+ DeckContextDescriptor aDeckContextDescriptor;
+ aDeckContextDescriptor.msId = rDeckDescriptor.msId;
+
+ aDeckContextDescriptor.mbIsEnabled = (! bIsDocumentReadOnly || IsDeckEnabled(rDeckDescriptor.msId, rContext, rxController) )
+ && rDeckDescriptor.mbIsEnabled;
+
+
+ aOrderedIds.emplace(rDeckDescriptor.mnOrderIndex, aDeckContextDescriptor);
+ }
+
+ for (auto const& orderId : aOrderedIds)
+ {
+ rDecks.push_back(orderId.second);
+ }
+
+ return rDecks;
+}
+
+const ResourceManager::PanelContextDescriptorContainer& ResourceManager::GetMatchingPanels (
+ PanelContextDescriptorContainer& rPanelIds,
+ const Context& rContext,
+ std::u16string_view sDeckId,
+ const Reference<frame::XController>& rxController)
+{
+ ReadLegacyAddons(rxController);
+
+ std::multimap<sal_Int32, PanelContextDescriptor> aOrderedIds;
+ for (auto const& panel : maPanels)
+ {
+ const PanelDescriptor& rPanelDescriptor (*panel);
+ if (rPanelDescriptor.mbExperimental && !officecfg::Office::Common::Misc::ExperimentalMode::get())
+ continue;
+ if ( rPanelDescriptor.msDeckId != sDeckId )
+ continue;
+
+ const ContextList::Entry* pEntry = rPanelDescriptor.maContextList.GetMatch(rContext);
+ if (pEntry == nullptr)
+ continue;
+
+ PanelContextDescriptor aPanelContextDescriptor;
+ aPanelContextDescriptor.msId = rPanelDescriptor.msId;
+ aPanelContextDescriptor.msMenuCommand = pEntry->msMenuCommand;
+ aPanelContextDescriptor.mbIsInitiallyVisible = pEntry->mbIsInitiallyVisible;
+ aPanelContextDescriptor.mbShowForReadOnlyDocuments = rPanelDescriptor.mbShowForReadOnlyDocuments;
+ aOrderedIds.emplace(rPanelDescriptor.mnOrderIndex, aPanelContextDescriptor);
+ }
+
+ for (auto const& orderId : aOrderedIds)
+ {
+ rPanelIds.push_back(orderId.second);
+ }
+
+ return rPanelIds;
+}
+
+const OUString& ResourceManager::GetLastActiveDeck( const Context& rContext )
+{
+ if( maLastActiveDecks.find( rContext.msApplication ) == maLastActiveDecks.end())
+ return maLastActiveDecks["any"];
+ else
+ return maLastActiveDecks[rContext.msApplication];
+}
+
+void ResourceManager::SetLastActiveDeck( const Context& rContext, const OUString &rsDeckId )
+{
+ maLastActiveDecks[rContext.msApplication] = rsDeckId;
+}
+
+void ResourceManager::ReadDeckList()
+{
+ const utl::OConfigurationTreeRoot aDeckRootNode(
+ comphelper::getProcessComponentContext(),
+ "org.openoffice.Office.UI.Sidebar/Content/DeckList",
+ false);
+ if (!aDeckRootNode.isValid())
+ return;
+
+ const Sequence<OUString> aDeckNodeNames (aDeckRootNode.getNodeNames());
+ maDecks.clear();
+ for (const OUString& aDeckName : aDeckNodeNames)
+ {
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ // Hide these decks in LOK as they aren't fully functional.
+ if (aDeckName == "GalleryDeck" || aDeckName == "NavigatorDeck"
+ || aDeckName == "StyleListDeck")
+ continue;
+ }
+
+ const utl::OConfigurationNode aDeckNode(aDeckRootNode.openNode(aDeckName));
+ if (!aDeckNode.isValid())
+ continue;
+
+ maDecks.push_back(std::make_shared<DeckDescriptor>());
+ DeckDescriptor& rDeckDescriptor (*maDecks.back());
+
+ rDeckDescriptor.msTitle = getString(aDeckNode, "Title");
+ rDeckDescriptor.msId = getString(aDeckNode, "Id");
+ rDeckDescriptor.msIconURL = getString(aDeckNode, "IconURL");
+ rDeckDescriptor.msHighContrastIconURL = getString(aDeckNode, "HighContrastIconURL");
+ rDeckDescriptor.msTitleBarIconURL = getString(aDeckNode, "TitleBarIconURL");
+ rDeckDescriptor.msHighContrastTitleBarIconURL = getString(aDeckNode, "HighContrastTitleBarIconURL");
+ rDeckDescriptor.msHelpText = rDeckDescriptor.msTitle;
+ rDeckDescriptor.mnOrderIndex = getInt32(aDeckNode, "OrderIndex");
+ rDeckDescriptor.mbExperimental = getBool(aDeckNode, "IsExperimental");
+
+ rDeckDescriptor.msNodeName = aDeckName;
+
+ ReadContextList(
+ aDeckNode,
+ rDeckDescriptor.maContextList,
+ OUString());
+
+ }
+}
+
+void ResourceManager::SaveDecksSettings(const Context& rContext)
+{
+ for (auto const& deck : maDecks)
+ {
+ const ContextList::Entry* pMatchingEntry = deck->maContextList.GetMatch(rContext);
+ if (pMatchingEntry)
+ {
+ std::shared_ptr<DeckDescriptor> xDeckDesc = GetDeckDescriptor(deck->msId);
+ if (xDeckDesc)
+ SaveDeckSettings(xDeckDesc.get());
+ }
+
+ }
+}
+
+void ResourceManager::SaveDeckSettings(const DeckDescriptor* pDeckDesc)
+{
+ const utl::OConfigurationTreeRoot aDeckRootNode(
+ comphelper::getProcessComponentContext(),
+ "org.openoffice.Office.UI.Sidebar/Content/DeckList",
+ true);
+ if (!aDeckRootNode.isValid())
+ return;
+
+ // save deck settings
+
+ ::uno::Sequence< OUString > sContextList = BuildContextList(pDeckDesc->maContextList);
+
+ utl::OConfigurationNode aDeckNode (aDeckRootNode.openNode(pDeckDesc->msNodeName));
+
+ css::uno::Any aTitle(Any(pDeckDesc->msTitle));
+ css::uno::Any aOrder(Any(pDeckDesc->mnOrderIndex));
+ css::uno::Any aContextList(sContextList);
+
+ bool bChanged = false;
+ if (aTitle != aDeckNode.getNodeValue("Title"))
+ {
+ aDeckNode.setNodeValue("Title", aTitle);
+ bChanged = true;
+ }
+ if (aOrder != aDeckNode.getNodeValue("OrderIndex"))
+ {
+ aDeckNode.setNodeValue("OrderIndex", aOrder);
+ bChanged = true;
+ }
+ if (aContextList != aDeckNode.getNodeValue("ContextList"))
+ {
+ aDeckNode.setNodeValue("ContextList", aContextList);
+ bChanged = true;
+ }
+
+ if (bChanged)
+ aDeckRootNode.commit();
+
+ // save panel settings
+
+ const utl::OConfigurationTreeRoot aPanelRootNode(
+ comphelper::getProcessComponentContext(),
+ "org.openoffice.Office.UI.Sidebar/Content/PanelList",
+ true);
+
+ if (!aPanelRootNode.isValid())
+ return;
+
+ if (!pDeckDesc->mpDeck) // the deck has not been edited
+ return;
+
+ SharedPanelContainer rPanels = pDeckDesc->mpDeck->GetPanels();
+
+ bChanged = false;
+ for (auto const& panel : rPanels)
+ {
+ OUString panelId = panel->GetId();
+ std::shared_ptr<PanelDescriptor> xPanelDesc = GetPanelDescriptor(panelId);
+
+ ::uno::Sequence< OUString > sPanelContextList = BuildContextList(xPanelDesc->maContextList);
+
+ utl::OConfigurationNode aPanelNode (aPanelRootNode.openNode(xPanelDesc->msNodeName));
+
+ aTitle <<= xPanelDesc->msTitle;
+ aOrder <<= xPanelDesc->mnOrderIndex;
+ aContextList <<= sPanelContextList;
+
+ if (aTitle != aPanelNode.getNodeValue("Title"))
+ {
+ aPanelNode.setNodeValue("Title", aTitle);
+ bChanged = true;
+ }
+ if (aOrder != aPanelNode.getNodeValue("OrderIndex"))
+ {
+ aPanelNode.setNodeValue("OrderIndex", aOrder);
+ bChanged = true;
+ }
+ if (aContextList != aPanelNode.getNodeValue("ContextList"))
+ {
+ aPanelNode.setNodeValue("ContextList", aContextList);
+ bChanged = true;
+ }
+ }
+
+ if (bChanged)
+ aPanelRootNode.commit();
+}
+
+void ResourceManager::SaveLastActiveDeck(const Context& rContext, const OUString& rActiveDeck)
+{
+ maLastActiveDecks[rContext.msApplication] = rActiveDeck;
+
+ std::set<OUString> aLastActiveDecks;
+ for ( auto const & rEntry : maLastActiveDecks )
+ aLastActiveDecks.insert( rEntry.first + "," + rEntry.second);
+
+ std::shared_ptr<comphelper::ConfigurationChanges> cfgWriter( comphelper::ConfigurationChanges::create() );
+
+ officecfg::Office::UI::Sidebar::Content::LastActiveDeck::set(comphelper::containerToSequence(aLastActiveDecks), cfgWriter);
+ cfgWriter->commit();
+
+}
+
+void ResourceManager::ReadPanelList()
+{
+ const utl::OConfigurationTreeRoot aPanelRootNode(
+ comphelper::getProcessComponentContext(),
+ "org.openoffice.Office.UI.Sidebar/Content/PanelList",
+ false);
+ if (!aPanelRootNode.isValid())
+ return;
+
+ const Sequence<OUString> aPanelNodeNames (aPanelRootNode.getNodeNames());
+ maPanels.clear();
+ for (const auto& rPanelNodeName : aPanelNodeNames)
+ {
+ const utl::OConfigurationNode aPanelNode (aPanelRootNode.openNode(rPanelNodeName));
+ if (!aPanelNode.isValid())
+ continue;
+
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ // Hide these panels in LOK as they aren't fully functional.
+ OUString aPanelId = getString(aPanelNode, "Id");
+ if (aPanelId == "PageStylesPanel" || aPanelId == "PageHeaderPanel"
+ || aPanelId == "PageFooterPanel")
+ continue;
+ }
+
+ maPanels.push_back(std::make_shared<PanelDescriptor>());
+ PanelDescriptor& rPanelDescriptor(*maPanels.back());
+
+ rPanelDescriptor.msTitle = getString(aPanelNode, "Title");
+ rPanelDescriptor.mbIsTitleBarOptional = getBool(aPanelNode, "TitleBarIsOptional");
+ rPanelDescriptor.msId = getString(aPanelNode, "Id");
+ rPanelDescriptor.msDeckId = getString(aPanelNode, "DeckId");
+ rPanelDescriptor.msTitleBarIconURL = getString(aPanelNode, "TitleBarIconURL");
+ rPanelDescriptor.msHighContrastTitleBarIconURL = getString(aPanelNode, "HighContrastTitleBarIconURL");
+ rPanelDescriptor.msImplementationURL = getString(aPanelNode, "ImplementationURL");
+ rPanelDescriptor.mnOrderIndex = getInt32(aPanelNode, "OrderIndex");
+ rPanelDescriptor.mbShowForReadOnlyDocuments = getBool(aPanelNode, "ShowForReadOnlyDocument");
+ rPanelDescriptor.mbWantsCanvas = getBool(aPanelNode, "WantsCanvas");
+ rPanelDescriptor.mbWantsAWT = getBool(aPanelNode, "WantsAWT");
+ rPanelDescriptor.mbExperimental = getBool(aPanelNode, "IsExperimental");
+ const OUString sDefaultMenuCommand(getString(aPanelNode, "DefaultMenuCommand"));
+
+ rPanelDescriptor.msNodeName = rPanelNodeName;
+
+ ReadContextList(aPanelNode, rPanelDescriptor.maContextList, sDefaultMenuCommand);
+ }
+}
+
+void ResourceManager::ReadLastActive()
+{
+ const Sequence <OUString> aLastActive (officecfg::Office::UI::Sidebar::Content::LastActiveDeck::get());
+
+ for (const auto& rDeckInfo : aLastActive)
+ {
+ sal_Int32 nCharIdx = rDeckInfo.lastIndexOf(',');
+ if ( nCharIdx <= 0 || (nCharIdx == rDeckInfo.getLength() - 1) )
+ {
+ SAL_WARN("sfx.sidebar", "Expecting 2 values separated by comma");
+ continue;
+ }
+
+ const OUString sApplicationName = rDeckInfo.copy( 0, nCharIdx );
+ vcl::EnumContext::Application eApplication (vcl::EnumContext::GetApplicationEnum(sApplicationName));
+ const OUString sLastUsed = rDeckInfo.copy( nCharIdx + 1 );
+
+ // guard against garbage in place of application
+ if (eApplication != vcl::EnumContext::Application::NONE)
+ maLastActiveDecks.insert( std::make_pair(sApplicationName, sLastUsed ) );
+ }
+}
+
+void ResourceManager::ReadContextList (
+ const utl::OConfigurationNode& rParentNode,
+ ContextList& rContextList,
+ const OUString& rsDefaultMenuCommand)
+{
+ const Any aValue = rParentNode.getNodeValue("ContextList");
+ Sequence<OUString> aValues;
+ if (!(aValue >>= aValues))
+ return;
+
+ for (const OUString& sValue : std::as_const(aValues))
+ {
+ sal_Int32 nCharacterIndex (0);
+ const OUString sApplicationName (o3tl::trim(o3tl::getToken(sValue, 0, ',', nCharacterIndex)));
+ if (nCharacterIndex < 0)
+ {
+ if (sApplicationName.getLength() == 0)
+ {
+ // This is a valid case: in the XML file the separator
+ // was used as terminator. Using it in the last line
+ // creates an additional but empty entry.
+ break;
+ }
+ else
+ {
+ OSL_FAIL("expecting three or four values per ContextList entry, separated by comma");
+ continue;
+ }
+ }
+
+ const OUString sContextName(o3tl::trim(o3tl::getToken(sValue, 0, ',', nCharacterIndex)));
+ if (nCharacterIndex < 0)
+ {
+ OSL_FAIL("expecting three or four values per ContextList entry, separated by comma");
+ continue;
+ }
+
+ const std::u16string_view sInitialState(o3tl::trim(o3tl::getToken(sValue, 0, ',', nCharacterIndex)));
+
+ // The fourth argument is optional.
+ const OUString sMenuCommandOverride(
+ nCharacterIndex < 0
+ ? OUString()
+ : OUString(o3tl::trim(o3tl::getToken(sValue, 0, ',', nCharacterIndex))));
+
+ const OUString sMenuCommand(
+ sMenuCommandOverride.getLength() > 0
+ ? (sMenuCommandOverride == "none"
+ ? OUString()
+ : sMenuCommandOverride)
+ : rsDefaultMenuCommand);
+
+ // Setup a list of application enums. Note that the
+ // application name may result in more than one value (eg
+ // DrawImpress will result in two enums, one for Draw and one
+ // for Impress).
+ std::vector<vcl::EnumContext::Application> aApplications;
+ vcl::EnumContext::Application eApplication (vcl::EnumContext::GetApplicationEnum(sApplicationName));
+
+ if (eApplication == vcl::EnumContext::Application::NONE
+ && sApplicationName != vcl::EnumContext::GetApplicationName(vcl::EnumContext::Application::NONE))
+ {
+ // Handle some special names: abbreviations that make
+ // context descriptions more readable.
+ if (sApplicationName == "Writer")
+ aApplications.push_back(vcl::EnumContext::Application::Writer);
+ else if (sApplicationName == "Calc")
+ aApplications.push_back(vcl::EnumContext::Application::Calc);
+ else if (sApplicationName == "Draw")
+ aApplications.push_back(vcl::EnumContext::Application::Draw);
+ else if (sApplicationName == "Impress")
+ aApplications.push_back(vcl::EnumContext::Application::Impress);
+ else if (sApplicationName == "Chart")
+ aApplications.push_back(vcl::EnumContext::Application::Chart);
+ else if (sApplicationName == "Math")
+ aApplications.push_back(vcl::EnumContext::Application::Formula);
+ else if (sApplicationName == "DrawImpress")
+ {
+ // A special case among the special names: it is
+ // common to use the same context descriptions for
+ // both Draw and Impress. This special case helps to
+ // avoid duplication in the .xcu file.
+ aApplications.push_back(vcl::EnumContext::Application::Draw);
+ aApplications.push_back(vcl::EnumContext::Application::Impress);
+ }
+ else if (sApplicationName == "WriterVariants")
+ {
+ // Another special case for all Writer variants.
+ aApplications.push_back(vcl::EnumContext::Application::Writer);
+ aApplications.push_back(vcl::EnumContext::Application::WriterGlobal);
+ aApplications.push_back(vcl::EnumContext::Application::WriterWeb);
+ aApplications.push_back(vcl::EnumContext::Application::WriterXML);
+ aApplications.push_back(vcl::EnumContext::Application::WriterForm);
+ aApplications.push_back(vcl::EnumContext::Application::WriterReport);
+ }
+ else
+ {
+ SAL_WARN("sfx.sidebar", "application name " << sApplicationName << " not recognized");
+ continue;
+ }
+ }
+ else
+ {
+ // No conversion of the application name necessary.
+ aApplications.push_back(eApplication);
+ }
+
+ // Setup the actual context enum.
+ const vcl::EnumContext::Context eContext (vcl::EnumContext::GetContextEnum(sContextName));
+ if (eContext == vcl::EnumContext::Context::Unknown)
+ {
+ SAL_WARN("sfx.sidebar", "context name " << sContextName << " not recognized");
+ continue;
+ }
+
+ // Setup the flag that controls whether a deck/pane is
+ // initially visible/expanded.
+ bool bIsInitiallyVisible;
+ if (sInitialState == u"visible")
+ bIsInitiallyVisible = true;
+ else if (sInitialState == u"hidden")
+ bIsInitiallyVisible = false;
+ else
+ {
+ OSL_FAIL("unrecognized state");
+ continue;
+ }
+
+
+ // Add context descriptors.
+ for (auto const& application : aApplications)
+ {
+ if (application != vcl::EnumContext::Application::NONE)
+ {
+ rContextList.AddContextDescription(
+ Context(
+ vcl::EnumContext::GetApplicationName(application),
+ vcl::EnumContext::GetContextName(eContext)),
+ bIsInitiallyVisible,
+ sMenuCommand);
+ }
+ }
+ }
+}
+
+void ResourceManager::ReadLegacyAddons (const Reference<frame::XController>& rxController)
+{
+ // Get module name for given frame.
+ OUString sModuleName (Tools::GetModuleName(rxController));
+ if (sModuleName.getLength() == 0)
+ return;
+ if (maProcessedApplications.find(sModuleName) != maProcessedApplications.end())
+ {
+ // Addons for this application have already been read.
+ // There is nothing more to do.
+ return;
+ }
+
+ // Mark module as processed. Even when there is an error that
+ // prevents the configuration data from being read, this error
+ // will not be triggered a second time.
+ maProcessedApplications.insert(sModuleName);
+
+ // Get access to the configuration root node for the application.
+ utl::OConfigurationTreeRoot aLegacyRootNode (GetLegacyAddonRootNode(sModuleName));
+ if (!aLegacyRootNode.isValid())
+ return;
+
+ // Process child nodes.
+ std::vector<OUString> aMatchingNodeNames;
+ GetToolPanelNodeNames(aMatchingNodeNames, aLegacyRootNode);
+ const sal_Int32 nCount (aMatchingNodeNames.size());
+ for (sal_Int32 nReadIndex(0); nReadIndex<nCount; ++nReadIndex)
+ {
+ const OUString& rsNodeName (aMatchingNodeNames[nReadIndex]);
+ const utl::OConfigurationNode aChildNode (aLegacyRootNode.openNode(rsNodeName));
+ if (!aChildNode.isValid())
+ continue;
+
+ if ( rsNodeName == "private:resource/toolpanel/DrawingFramework/CustomAnimations" ||
+ rsNodeName == "private:resource/toolpanel/DrawingFramework/Layouts" ||
+ rsNodeName == "private:resource/toolpanel/DrawingFramework/MasterPages" ||
+ rsNodeName == "private:resource/toolpanel/DrawingFramework/SlideTransitions" ||
+ rsNodeName == "private:resource/toolpanel/DrawingFramework/TableDesign" )
+ continue;
+
+ maDecks.push_back(std::make_shared<DeckDescriptor>());
+ DeckDescriptor& rDeckDescriptor(*maDecks.back());
+ rDeckDescriptor.msTitle = getString(aChildNode, "UIName");
+ rDeckDescriptor.msId = rsNodeName;
+ rDeckDescriptor.msIconURL = getString(aChildNode, "ImageURL");
+ rDeckDescriptor.msHighContrastIconURL = rDeckDescriptor.msIconURL;
+ rDeckDescriptor.msTitleBarIconURL.clear();
+ rDeckDescriptor.msHighContrastTitleBarIconURL.clear();
+ rDeckDescriptor.msHelpText = rDeckDescriptor.msTitle;
+ rDeckDescriptor.mbIsEnabled = true;
+ rDeckDescriptor.mnOrderIndex = 100000 + nReadIndex;
+ rDeckDescriptor.maContextList.AddContextDescription(Context(sModuleName, "any"), true, OUString());
+
+ maPanels.push_back(std::make_shared<PanelDescriptor>());
+ PanelDescriptor& rPanelDescriptor(*maPanels.back());
+ rPanelDescriptor.msTitle = getString(aChildNode, "UIName");
+ rPanelDescriptor.mbIsTitleBarOptional = true;
+ rPanelDescriptor.msId = rsNodeName;
+ rPanelDescriptor.msDeckId = rsNodeName;
+ rPanelDescriptor.msTitleBarIconURL.clear();
+ rPanelDescriptor.msHighContrastTitleBarIconURL.clear();
+ rPanelDescriptor.msImplementationURL = rsNodeName;
+ rPanelDescriptor.mnOrderIndex = 100000 + nReadIndex;
+ rPanelDescriptor.mbShowForReadOnlyDocuments = false;
+ rPanelDescriptor.mbWantsCanvas = false;
+ rPanelDescriptor.mbWantsAWT = true;
+ fprintf(stderr, "THIS PLACE\n");
+ rPanelDescriptor.maContextList.AddContextDescription(Context(sModuleName, "any"), true, OUString());
+ }
+}
+
+void ResourceManager::StorePanelExpansionState (
+ std::u16string_view rsPanelId,
+ const bool bExpansionState,
+ const Context& rContext)
+{
+ for (auto const& panel : maPanels)
+ {
+ if (panel->msId == rsPanelId)
+ {
+ ContextList::Entry* pEntry(panel->maContextList.GetMatch(rContext));
+ if (pEntry != nullptr)
+ pEntry->mbIsInitiallyVisible = bExpansionState;
+ }
+ }
+}
+
+utl::OConfigurationTreeRoot ResourceManager::GetLegacyAddonRootNode (const OUString& rsModuleName)
+{
+ try
+ {
+ const Reference<XComponentContext> xContext(comphelper::getProcessComponentContext());
+ const Reference<frame::XModuleManager2> xModuleAccess = frame::ModuleManager::create(xContext);
+ const comphelper::NamedValueCollection aModuleProperties(xModuleAccess->getByName(rsModuleName));
+ const OUString sWindowStateRef(aModuleProperties.getOrDefault(
+ "ooSetupFactoryWindowStateConfigRef",
+ OUString()));
+
+ OUString aPathComposer = "org.openoffice.Office.UI." + sWindowStateRef +
+ "/UIElements/States";
+
+ return utl::OConfigurationTreeRoot(xContext, aPathComposer, false);
+ }
+ catch (const Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("sfx.sidebar");
+ }
+
+ return utl::OConfigurationTreeRoot();
+}
+
+void ResourceManager::GetToolPanelNodeNames (
+ std::vector<OUString>& rMatchingNames,
+ const utl::OConfigurationTreeRoot& aRoot)
+{
+ const Sequence<OUString> aChildNodeNames (aRoot.getNodeNames());
+ std::copy_if(aChildNodeNames.begin(), aChildNodeNames.end(), std::back_inserter(rMatchingNames),
+ [](const OUString& rChildNodeName) { return rChildNodeName.startsWith( "private:resource/toolpanel/" ); });
+}
+
+bool ResourceManager::IsDeckEnabled (
+ std::u16string_view rsDeckId,
+ const Context& rContext,
+ const Reference<frame::XController>& rxController)
+{
+
+ // Check if any panel that matches the current context can be
+ // displayed.
+ PanelContextDescriptorContainer aPanelContextDescriptors;
+
+ GetMatchingPanels(aPanelContextDescriptors, rContext, rsDeckId, rxController);
+
+ for (auto const& panelContextDescriptor : aPanelContextDescriptors)
+ {
+ if (panelContextDescriptor.mbShowForReadOnlyDocuments)
+ return true;
+ }
+ return false;
+}
+
+void ResourceManager::UpdateModel(const css::uno::Reference<css::frame::XModel>& xModel)
+{
+ for (auto const& deck : maDecks)
+ {
+ if (!deck->mpDeck)
+ continue;
+
+ const SharedPanelContainer& rContainer = deck->mpDeck->GetPanels();
+
+ for (auto const& elem : rContainer)
+ {
+ css::uno::Reference<css::ui::XUpdateModel> xPanel(elem->GetPanelComponent(), css::uno::UNO_QUERY);
+ if (xPanel.is()) // tdf#108814 interface is optional
+ {
+ xPanel->updateModel(xModel);
+ }
+ }
+ }
+}
+
+void ResourceManager::disposeDecks()
+{
+ for (auto const& deck : maDecks)
+ {
+ deck->mpDeck.disposeAndClear();
+ }
+}
+
+} // end of namespace sfx2::sidebar
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/sidebar/Sidebar.cxx b/sfx2/source/sidebar/Sidebar.cxx
new file mode 100644
index 000000000..ca48542d5
--- /dev/null
+++ b/sfx2/source/sidebar/Sidebar.cxx
@@ -0,0 +1,128 @@
+/* -*- 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/sidebar/Sidebar.hxx>
+#include <sfx2/sidebar/SidebarController.hxx>
+#include <sfx2/sidebar/ResourceManager.hxx>
+#include <sidebar/PanelDescriptor.hxx>
+#include <sidebar/Tools.hxx>
+#include <sfx2/sidebar/FocusManager.hxx>
+#include <sfx2/childwin.hxx>
+#include <sfx2/sfxsids.hrc>
+#include <com/sun/star/frame/XDispatch.hpp>
+
+using namespace css;
+
+namespace sfx2::sidebar {
+
+void Sidebar::ToggleDeck(std::u16string_view rsDeckId, SfxViewFrame* pViewFrame)
+{
+ if (!pViewFrame)
+ return;
+
+ SfxChildWindow* pSidebarChildWindow = pViewFrame->GetChildWindow(SID_SIDEBAR);
+ bool bInitiallyVisible = pSidebarChildWindow && pSidebarChildWindow->IsVisible();
+ if (!bInitiallyVisible)
+ pViewFrame->ShowChildWindow(SID_SIDEBAR);
+
+ SidebarController* pController =
+ SidebarController::GetSidebarControllerForFrame(pViewFrame->GetFrame().GetFrameInterface());
+ if (!pController)
+ return;
+
+ if (bInitiallyVisible && pController->IsDeckVisible(rsDeckId))
+ {
+ // close the sidebar if it was already visible and showing this sidebar deck
+ const util::URL aURL(Tools::GetURL(".uno:Sidebar"));
+ css::uno::Reference<frame::XDispatch> xDispatch(Tools::GetDispatch(pViewFrame->GetFrame().GetFrameInterface(), aURL));
+ if (xDispatch.is())
+ xDispatch->dispatch(aURL, css::uno::Sequence<beans::PropertyValue>());
+ }
+ else
+ {
+ pController->OpenThenSwitchToDeck(rsDeckId);
+ pController->GetFocusManager().GrabFocusPanel();
+ }
+}
+
+void Sidebar::ShowPanel (
+ std::u16string_view rsPanelId,
+ const css::uno::Reference<frame::XFrame>& rxFrame, bool bFocus)
+{
+ SidebarController* pController = SidebarController::GetSidebarControllerForFrame(rxFrame);
+ if (!pController)
+ return;
+
+ std::shared_ptr<PanelDescriptor> xPanelDescriptor = pController->GetResourceManager()->GetPanelDescriptor(rsPanelId);
+
+ if (!xPanelDescriptor)
+ return;
+
+ // This should be a lot more sophisticated:
+ // - Make the deck switching asynchronous
+ // - Make sure to use a context that really shows the panel
+
+ // All that is not necessary for the current use cases so lets
+ // keep it simple for the time being.
+ pController->OpenThenSwitchToDeck(xPanelDescriptor->msDeckId);
+
+ if (bFocus)
+ pController->GetFocusManager().GrabFocusPanel();
+}
+
+void Sidebar::TogglePanel (
+ std::u16string_view rsPanelId,
+ const css::uno::Reference<frame::XFrame>& rxFrame)
+{
+ SidebarController* pController = SidebarController::GetSidebarControllerForFrame(rxFrame);
+ if (!pController)
+ return;
+
+ std::shared_ptr<PanelDescriptor> xPanelDescriptor = pController->GetResourceManager()->GetPanelDescriptor(rsPanelId);
+
+ if (!xPanelDescriptor)
+ return;
+
+ // This should be a lot more sophisticated:
+ // - Make the deck switching asynchronous
+ // - Make sure to use a context that really shows the panel
+
+ // All that is not necessary for the current use cases so lets
+ // keep it simple for the time being.
+ pController->OpenThenToggleDeck(xPanelDescriptor->msDeckId);
+}
+
+bool Sidebar::IsPanelVisible(
+ std::u16string_view rsPanelId,
+ const css::uno::Reference<frame::XFrame>& rxFrame)
+{
+ SidebarController* pController = SidebarController::GetSidebarControllerForFrame(rxFrame);
+ if (!pController)
+ return false;
+
+ std::shared_ptr<PanelDescriptor> xPanelDescriptor = pController->GetResourceManager()->GetPanelDescriptor(rsPanelId);
+ if (!xPanelDescriptor)
+ return false;
+
+ return pController->IsDeckVisible(xPanelDescriptor->msDeckId);
+}
+
+} // end of namespace sfx2::sidebar
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/sidebar/SidebarChildWindow.cxx b/sfx2/source/sidebar/SidebarChildWindow.cxx
new file mode 100644
index 000000000..04d1f1037
--- /dev/null
+++ b/sfx2/source/sidebar/SidebarChildWindow.cxx
@@ -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 .
+ */
+
+#include <sfx2/sidebar/TabBar.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/sidebar/SidebarChildWindow.hxx>
+#include <sfx2/sidebar/SidebarDockingWindow.hxx>
+#include <sfx2/sfxsids.hrc>
+#include <helpids.h>
+#include <comphelper/lok.hxx>
+
+namespace sfx2::sidebar {
+
+SFX_IMPL_DOCKINGWINDOW_WITHID(SidebarChildWindow, SID_SIDEBAR);
+
+SidebarChildWindow::SidebarChildWindow(vcl::Window* pParentWindow, sal_uInt16 nId,
+ SfxBindings* pBindings, SfxChildWinInfo* pInfo)
+ : SfxChildWindow(pParentWindow, nId)
+{
+ auto pDockWin = VclPtr<SidebarDockingWindow>::Create(
+ pBindings, *this, pParentWindow, WB_STDDOCKWIN | WB_OWNERDRAWDECORATION | WB_CLIPCHILDREN
+ | WB_SIZEABLE | WB_3DLOOK);
+ SetWindow(pDockWin);
+ SetAlignment(SfxChildAlignment::RIGHT);
+
+ pDockWin->SetHelpId(HID_SIDEBAR_WINDOW);
+ pDockWin->SetOutputSizePixel(Size(GetDefaultWidth(pDockWin), 450));
+
+ if (pInfo && pInfo->aExtraString.isEmpty() && pInfo->aModule != "sdraw"
+ && pInfo->aModule != "simpress")
+ {
+ // When this is the first start (never had the sidebar open yet),
+ // default to non-expanded sidebars in Writer and Calc.
+ //
+ // HACK: unfortunately I haven't found a clean solution to do
+ // this, so do it this way:
+ //
+ if (!comphelper::LibreOfficeKit::isActive())
+ {
+ pDockWin->SetSizePixel(
+ Size(TabBar::GetDefaultWidth(),
+ pDockWin->GetSizePixel().Height()));
+ }
+ }
+
+ pDockWin->Initialize(pInfo);
+
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ // Undock sidebar in LOK to allow for resizing freely
+ // (i.e. when the client window is resized) and collapse
+ // it so the client can open it on demand.
+ pDockWin->SetFloatingSize(Size(pDockWin->GetSizePixel().Width(),
+ pDockWin->GetSizePixel().Height()));
+ pDockWin->SetFloatingMode(true);
+ }
+
+ SetHideNotDelete(true);
+
+ pDockWin->Show();
+}
+
+sal_Int32 SidebarChildWindow::GetDefaultWidth(vcl::Window const* pWindow)
+{
+ if (pWindow != nullptr)
+ {
+ // Width of the paragraph panel.
+ const static sal_Int32 nMaxPropertyPageWidth(146);
+
+ return pWindow->LogicToPixel(Point(nMaxPropertyPageWidth,1), MapMode(MapUnit::MapAppFont)).X()
+ + TabBar::GetDefaultWidth();
+ }
+ else
+ return 0;
+}
+
+} // end of namespace sfx2::sidebar
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/sidebar/SidebarController.cxx b/sfx2/source/sidebar/SidebarController.cxx
new file mode 100644
index 000000000..0bd71db02
--- /dev/null
+++ b/sfx2/source/sidebar/SidebarController.cxx
@@ -0,0 +1,1643 @@
+/* -*- 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/sidebar/SidebarController.hxx>
+#include <sfx2/sidebar/Deck.hxx>
+#include <sidebar/DeckDescriptor.hxx>
+#include <sidebar/DeckTitleBar.hxx>
+#include <sfx2/sidebar/Panel.hxx>
+#include <sidebar/PanelDescriptor.hxx>
+#include <sidebar/PanelTitleBar.hxx>
+#include <sfx2/sidebar/TabBar.hxx>
+#include <sfx2/sidebar/Theme.hxx>
+#include <sfx2/sidebar/SidebarChildWindow.hxx>
+#include <sidebar/Tools.hxx>
+#include <sfx2/sidebar/SidebarDockingWindow.hxx>
+#include <com/sun/star/ui/XSidebarProvider.hpp>
+#include <com/sun/star/frame/XController2.hpp>
+#include <sfx2/sidebar/Context.hxx>
+#include <sfx2/viewsh.hxx>
+
+
+#include <framework/ContextChangeEventMultiplexerTunnel.hxx>
+#include <vcl/EnumContext.hxx>
+#include <vcl/uitest/logger.hxx>
+#include <vcl/uitest/eventdescription.hxx>
+#include <vcl/svapp.hxx>
+#include <splitwin.hxx>
+#include <tools/diagnose_ex.h>
+#include <tools/json_writer.hxx>
+#include <tools/link.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/namedvaluecollection.hxx>
+#include <comphelper/lok.hxx>
+#include <sal/log.hxx>
+#include <officecfg/Office/UI/Sidebar.hxx>
+#include <LibreOfficeKit/LibreOfficeKitEnums.h>
+
+#include <com/sun/star/awt/XWindowPeer.hpp>
+#include <com/sun/star/frame/XDispatch.hpp>
+#include <com/sun/star/ui/ContextChangeEventMultiplexer.hpp>
+#include <com/sun/star/ui/ContextChangeEventObject.hpp>
+#include <com/sun/star/ui/theUIElementFactoryManager.hpp>
+#include <com/sun/star/util/URL.hpp>
+#include <com/sun/star/rendering/XSpriteCanvas.hpp>
+
+#include <bitmaps.hlst>
+
+using namespace css;
+using namespace css::uno;
+
+namespace
+{
+ constexpr OUStringLiteral gsReadOnlyCommandName = u".uno:EditDoc";
+ const sal_Int32 gnWidthCloseThreshold (70);
+ const sal_Int32 gnWidthOpenThreshold (40);
+
+ std::string UnoNameFromDeckId(std::u16string_view rsDeckId, bool isImpress = false)
+ {
+ if (rsDeckId == u"SdCustomAnimationDeck")
+ return ".uno:CustomAnimation";
+
+ if (rsDeckId == u"PropertyDeck")
+ return isImpress ? ".uno:ModifyPage" : ".uno:Sidebar";
+
+ if (rsDeckId == u"SdLayoutsDeck")
+ return ".uno:ModifyPage";
+
+ if (rsDeckId == u"SdSlideTransitionDeck")
+ return ".uno:SlideChangeWindow";
+
+ if (rsDeckId == u"SdAllMasterPagesDeck")
+ return ".uno:MasterSlidesPanel";
+
+ if (rsDeckId == u"SdMasterPagesDeck")
+ return ".uno:MasterSlidesPanel";
+
+ if (rsDeckId == u"GalleryDeck")
+ return ".uno:Gallery";
+
+ return "";
+ }
+}
+
+namespace sfx2::sidebar {
+
+namespace {
+
+ /** When in doubt, show this deck.
+ */
+ constexpr OUStringLiteral gsDefaultDeckId(u"PropertyDeck");
+}
+
+SidebarController::SidebarController (
+ SidebarDockingWindow* pParentWindow,
+ const SfxViewFrame* pViewFrame)
+ : mpParentWindow(pParentWindow),
+ mpViewFrame(pViewFrame),
+ mxFrame(pViewFrame->GetFrame().GetFrameInterface()),
+ mpTabBar(VclPtr<TabBar>::Create(
+ mpParentWindow,
+ mxFrame,
+ [this](const OUString& rsDeckId) { return this->OpenThenToggleDeck(rsDeckId); },
+ [this](weld::Menu& rMainMenu, weld::Menu& rSubMenu,
+ const ::std::vector<TabBar::DeckMenuData>& rMenuData) { return this->ShowPopupMenu(rMainMenu, rSubMenu, rMenuData); },
+ this)),
+ maCurrentContext(OUString(), OUString()),
+ mnRequestedForceFlags(SwitchFlag_NoForce),
+ mbMinimumSidebarWidth(officecfg::Office::UI::Sidebar::General::MinimumWidth::get()),
+ msCurrentDeckId(gsDefaultDeckId),
+ maPropertyChangeForwarder([this](){ return this->BroadcastPropertyChange(); }),
+ maContextChangeUpdate([this](){ return this->UpdateConfigurations(); }),
+ mbFloatingDeckClosed(!pParentWindow->IsFloatingMode()),
+ mnSavedSidebarWidth(pParentWindow->GetSizePixel().Width()),
+ maFocusManager([this](const Panel& rPanel){ return this->ShowPanel(rPanel); }),
+ mbIsDocumentReadOnly(false),
+ mpSplitWindow(nullptr),
+ mnWidthOnSplitterButtonDown(0)
+{
+ mnMaximumSidebarWidth = officecfg::Office::UI::Sidebar::General::MaximumWidth::get() * mpTabBar->GetDPIScaleFactor();
+ // Decks and panel collections for this sidebar
+ mpResourceManager = std::make_unique<ResourceManager>();
+}
+
+rtl::Reference<SidebarController> SidebarController::create(SidebarDockingWindow* pParentWindow,
+ const SfxViewFrame* pViewFrame)
+{
+ rtl::Reference<SidebarController> instance(new SidebarController(pParentWindow, pViewFrame));
+
+ const css::uno::Reference<css::frame::XFrame>& rxFrame = pViewFrame->GetFrame().GetFrameInterface();
+ registerSidebarForFrame(instance.get(), rxFrame->getController());
+ rxFrame->addFrameActionListener(instance);
+ // Listen for window events.
+ instance->mpParentWindow->AddEventListener(LINK(instance.get(), SidebarController, WindowEventHandler));
+
+ // Listen for theme property changes.
+ instance->mxThemePropertySet = Theme::GetPropertySet();
+ instance->mxThemePropertySet->addPropertyChangeListener(
+ "",
+ static_cast<css::beans::XPropertyChangeListener*>(instance.get()));
+
+ // Get the dispatch object as preparation to listen for changes of
+ // the read-only state.
+ const util::URL aURL (Tools::GetURL(gsReadOnlyCommandName));
+ instance->mxReadOnlyModeDispatch = Tools::GetDispatch(rxFrame, aURL);
+ if (instance->mxReadOnlyModeDispatch.is())
+ instance->mxReadOnlyModeDispatch->addStatusListener(instance, aURL);
+
+ //first UpdateConfigurations call will SwitchToDeck
+
+ return instance;
+}
+
+SidebarController::~SidebarController()
+{
+}
+
+SidebarController* SidebarController::GetSidebarControllerForFrame (
+ const css::uno::Reference<css::frame::XFrame>& rxFrame)
+{
+ uno::Reference<frame::XController> const xController(rxFrame->getController());
+ if (!xController.is()) // this may happen during dispose of Draw controller but perhaps it's a bug
+ {
+ SAL_WARN("sfx.sidebar", "GetSidebarControllerForFrame: frame has no XController");
+ return nullptr;
+ }
+ uno::Reference<ui::XContextChangeEventListener> const xListener(
+ framework::GetFirstListenerWith(
+ ::comphelper::getProcessComponentContext(),
+ xController,
+ [] (uno::Reference<uno::XInterface> const& xRef)
+ { return nullptr != dynamic_cast<SidebarController*>(xRef.get()); }
+ ));
+
+ return dynamic_cast<SidebarController*>(xListener.get());
+}
+
+void SidebarController::registerSidebarForFrame(SidebarController* pController, const css::uno::Reference<css::frame::XController>& xController)
+{
+ // Listen for context change events.
+ css::uno::Reference<css::ui::XContextChangeEventMultiplexer> xMultiplexer (
+ css::ui::ContextChangeEventMultiplexer::get(
+ ::comphelper::getProcessComponentContext()));
+ xMultiplexer->addContextChangeEventListener(
+ static_cast<css::ui::XContextChangeEventListener*>(pController),
+ xController);
+}
+
+void SidebarController::unregisterSidebarForFrame(SidebarController* pController, const css::uno::Reference<css::frame::XController>& xController)
+{
+ pController->saveDeckState();
+ pController->disposeDecks();
+
+ css::uno::Reference<css::ui::XContextChangeEventMultiplexer> xMultiplexer (
+ css::ui::ContextChangeEventMultiplexer::get(
+ ::comphelper::getProcessComponentContext()));
+ xMultiplexer->removeContextChangeEventListener(
+ static_cast<css::ui::XContextChangeEventListener*>(pController),
+ xController);
+}
+
+void SidebarController::disposeDecks()
+{
+ SolarMutexGuard aSolarMutexGuard;
+
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ if (const SfxViewShell* pViewShell = mpViewFrame->GetViewShell())
+ {
+ const std::string hide = UnoNameFromDeckId(msCurrentDeckId, vcl::EnumContext::Application::Impress == vcl::EnumContext::GetApplicationEnum(GetCurrentContext().msApplication));
+ if (!hide.empty())
+ pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_STATE_CHANGED,
+ (hide + "=false").c_str());
+ }
+
+ if (mpParentWindow)
+ mpParentWindow->ReleaseLOKNotifier();
+ }
+
+ mpCurrentDeck.clear();
+ maFocusManager.Clear();
+ mpResourceManager->disposeDecks();
+}
+
+namespace
+{
+ class CloseIndicator final : public InterimItemWindow
+ {
+ public:
+ CloseIndicator(vcl::Window* pParent)
+ : InterimItemWindow(pParent, "svt/ui/fixedimagecontrol.ui", "FixedImageControl")
+ , m_xWidget(m_xBuilder->weld_image("image"))
+ {
+ InitControlBase(m_xWidget.get());
+
+ m_xWidget->set_from_icon_name(SIDEBAR_CLOSE_INDICATOR);
+
+ SetSizePixel(get_preferred_size());
+
+ SetBackground(Theme::GetColor(Theme::Color_DeckBackground));
+ }
+
+ virtual ~CloseIndicator() override
+ {
+ disposeOnce();
+ }
+
+ virtual void dispose() override
+ {
+ m_xWidget.reset();
+ InterimItemWindow::dispose();
+ }
+
+ private:
+ std::unique_ptr<weld::Image> m_xWidget;
+ };
+}
+
+void SidebarController::disposing(std::unique_lock<std::mutex>&)
+{
+ SolarMutexGuard aSolarMutexGuard;
+
+ mpCloseIndicator.disposeAndClear();
+
+ maFocusManager.Clear();
+ mpTabBar.disposeAndClear();
+
+ saveDeckState();
+
+ // clear decks
+ ResourceManager::DeckContextDescriptorContainer aDecks;
+
+ mpResourceManager->GetMatchingDecks (
+ aDecks,
+ GetCurrentContext(),
+ IsDocumentReadOnly(),
+ mxFrame->getController());
+
+ for (const auto& rDeck : aDecks)
+ {
+ std::shared_ptr<DeckDescriptor> deckDesc = mpResourceManager->GetDeckDescriptor(rDeck.msId);
+
+ VclPtr<Deck> aDeck = deckDesc->mpDeck;
+ if (aDeck)
+ aDeck.disposeAndClear();
+ }
+
+ maContextChangeUpdate.CancelRequest();
+
+ if (mxReadOnlyModeDispatch.is())
+ mxReadOnlyModeDispatch->removeStatusListener(this, Tools::GetURL(gsReadOnlyCommandName));
+
+ if (mxThemePropertySet.is())
+ mxThemePropertySet->removePropertyChangeListener(
+ "",
+ static_cast<css::beans::XPropertyChangeListener*>(this));
+
+ if (mpParentWindow != nullptr)
+ {
+ mpParentWindow->RemoveEventListener(LINK(this, SidebarController, WindowEventHandler));
+ mpParentWindow = nullptr;
+ }
+
+ if (mpSplitWindow != nullptr)
+ {
+ mpSplitWindow->RemoveEventListener(LINK(this, SidebarController, WindowEventHandler));
+ mpSplitWindow = nullptr;
+ }
+
+ mxFrame->removeFrameActionListener(this);
+
+ uno::Reference<css::frame::XController> xController = mxFrame->getController();
+ if (!xController.is())
+ xController = mxCurrentController;
+
+ unregisterSidebarForFrame(this, xController);
+}
+
+void SAL_CALL SidebarController::notifyContextChangeEvent (const css::ui::ContextChangeEventObject& rEvent)
+{
+ SolarMutexGuard aSolarMutexGuard;
+
+ // Update to the requested new context asynchronously to avoid
+ // subtle errors caused by SFX2 which in rare cases can not
+ // properly handle a synchronous update.
+
+ maRequestedContext = Context(
+ rEvent.ApplicationName,
+ rEvent.ContextName);
+
+ if (maRequestedContext != maCurrentContext)
+ {
+ mxCurrentController.set(rEvent.Source, css::uno::UNO_QUERY);
+ maContextChangeUpdate.RequestCall(); // async call, not a prob
+ // calling with held
+ // solarmutex
+ // TODO: this call is redundant but mandatory for unit test to update context on document loading
+ if (!comphelper::LibreOfficeKit::isActive())
+ UpdateConfigurations();
+ }
+}
+
+void SAL_CALL SidebarController::disposing (const css::lang::EventObject& )
+{
+ dispose();
+}
+
+void SAL_CALL SidebarController::propertyChange (const css::beans::PropertyChangeEvent& )
+{
+ SolarMutexGuard aSolarMutexGuard;
+
+ maPropertyChangeForwarder.RequestCall(); // async call, not a prob
+ // to call with held
+ // solarmutex
+}
+
+void SAL_CALL SidebarController::statusChanged (const css::frame::FeatureStateEvent& rEvent)
+{
+ SolarMutexGuard aSolarMutexGuard;
+
+ bool bIsReadWrite (true);
+ if (rEvent.IsEnabled)
+ rEvent.State >>= bIsReadWrite;
+
+ if (mbIsDocumentReadOnly != !bIsReadWrite)
+ {
+ mbIsDocumentReadOnly = !bIsReadWrite;
+
+ // Force the current deck to update its panel list.
+ if ( ! mbIsDocumentReadOnly)
+ SwitchToDefaultDeck();
+
+ mnRequestedForceFlags |= SwitchFlag_ForceSwitch;
+ maContextChangeUpdate.RequestCall(); // async call, ok to call
+ // with held solarmutex
+ }
+}
+
+void SAL_CALL SidebarController::requestLayout()
+{
+ SolarMutexGuard aSolarMutexGuard;
+
+ sal_Int32 nMinimalWidth = 0;
+ if (mpCurrentDeck && !mpCurrentDeck->isDisposed())
+ {
+ mpCurrentDeck->RequestLayout();
+ nMinimalWidth = mbMinimumSidebarWidth ? mpCurrentDeck->GetMinimalWidth() : 0;
+ }
+ RestrictWidth(nMinimalWidth);
+}
+
+void SidebarController::BroadcastPropertyChange()
+{
+ mpParentWindow->Invalidate(InvalidateFlags::Children);
+}
+
+void SidebarController::NotifyResize()
+{
+ if (!mpTabBar)
+ {
+ OSL_ASSERT(mpTabBar!=nullptr);
+ return;
+ }
+
+ const sal_Int32 nTabBarDefaultWidth = TabBar::GetDefaultWidth();
+
+ const sal_Int32 nWidth(mpParentWindow->GetSizePixel().Width());
+ const sal_Int32 nHeight(mpParentWindow->GetSizePixel().Height());
+
+ mbIsDeckOpen = (nWidth > nTabBarDefaultWidth);
+
+ if (mnSavedSidebarWidth <= 0)
+ mnSavedSidebarWidth = nWidth;
+
+ bool bIsDeckVisible;
+ const bool bIsOpening (nWidth > mnWidthOnSplitterButtonDown);
+ if (bIsOpening)
+ bIsDeckVisible = nWidth >= nTabBarDefaultWidth + gnWidthOpenThreshold;
+ else
+ bIsDeckVisible = nWidth >= nTabBarDefaultWidth + gnWidthCloseThreshold;
+ mbIsDeckRequestedOpen = bIsDeckVisible;
+ UpdateCloseIndicator(!bIsDeckVisible);
+
+ if (mpCurrentDeck && !mpCurrentDeck->isDisposed())
+ {
+ SfxSplitWindow* pSplitWindow = GetSplitWindow();
+ WindowAlign eAlign = pSplitWindow ? pSplitWindow->GetAlign() : WindowAlign::Right;
+ tools::Long nDeckX, nTabX;
+ if (eAlign == WindowAlign::Left) // attach the Sidebar towards the left-side of screen
+ {
+ nDeckX = nTabBarDefaultWidth;
+ nTabX = 0;
+ }
+ else // attach the Sidebar towards the right-side of screen
+ {
+ nDeckX = 0;
+ nTabX = nWidth - nTabBarDefaultWidth;
+ }
+
+ // Place the deck first.
+ if (bIsDeckVisible)
+ {
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ // We want to let the layouter use up as much of the
+ // height as necessary to make sure no scrollbar is
+ // visible. This only works when there are no greedy
+ // panes that fill up all available area. So we only
+ // use this for the PropertyDeck, which has no such
+ // panes, while most other do. This is fine, since
+ // it's the PropertyDeck that really has many panes
+ // that can collapse or expand. For others, limit
+ // the height to something sensible.
+ const sal_Int32 nExtHeight = (msCurrentDeckId == "PropertyDeck" ? 2000 : 600);
+ // No TabBar in LOK (use nWidth in full).
+ mpCurrentDeck->setPosSizePixel(nDeckX, 0, nWidth, nExtHeight);
+ }
+ else
+ mpCurrentDeck->setPosSizePixel(nDeckX, 0, nWidth - nTabBarDefaultWidth, nHeight);
+ mpCurrentDeck->Show();
+ mpCurrentDeck->RequestLayout();
+ }
+ else
+ mpCurrentDeck->Hide();
+
+ // Now place the tab bar.
+ mpTabBar->setPosSizePixel(nTabX, 0, nTabBarDefaultWidth, nHeight);
+ if (!comphelper::LibreOfficeKit::isActive())
+ mpTabBar->Show(); // Don't show TabBar in LOK.
+ }
+
+ // Determine if the closer of the deck can be shown.
+ sal_Int32 nMinimalWidth = 0;
+ if (mpCurrentDeck && !mpCurrentDeck->isDisposed())
+ {
+ DeckTitleBar* pTitleBar = mpCurrentDeck->GetTitleBar();
+ if (pTitleBar && pTitleBar->GetVisible())
+ pTitleBar->SetCloserVisible(CanModifyChildWindowWidth());
+ nMinimalWidth = mbMinimumSidebarWidth ? mpCurrentDeck->GetMinimalWidth() : 0;
+ }
+
+ RestrictWidth(nMinimalWidth);
+}
+
+void SidebarController::ProcessNewWidth (const sal_Int32 nNewWidth)
+{
+ if ( ! mbIsDeckRequestedOpen)
+ return;
+
+ if (*mbIsDeckRequestedOpen)
+ {
+ // Deck became large enough to be shown. Show it.
+ mnSavedSidebarWidth = nNewWidth;
+ // Store nNewWidth to mnWidthOnSplitterButtonDown when dragging sidebar Splitter
+ mnWidthOnSplitterButtonDown = nNewWidth;
+ if (!*mbIsDeckOpen)
+ RequestOpenDeck();
+ }
+ else
+ {
+ // Deck became too small. Close it completely.
+ // If window is wider than the tab bar then mark the deck as being visible, even when it is not.
+ // This is to trigger an adjustment of the width to the width of the tab bar.
+ mbIsDeckOpen = true;
+ RequestCloseDeck();
+
+ if (mnWidthOnSplitterButtonDown > TabBar::GetDefaultWidth())
+ mnSavedSidebarWidth = mnWidthOnSplitterButtonDown;
+ }
+}
+
+void SidebarController::SyncUpdate()
+{
+ maPropertyChangeForwarder.Sync();
+ maContextChangeUpdate.Sync();
+}
+
+void SidebarController::UpdateConfigurations()
+{
+ if (maCurrentContext == maRequestedContext
+ && mnRequestedForceFlags == SwitchFlag_NoForce)
+ return;
+
+ if ((maCurrentContext.msApplication != "none") &&
+ !maCurrentContext.msApplication.isEmpty())
+ {
+ mpResourceManager->SaveDecksSettings(maCurrentContext);
+ mpResourceManager->SetLastActiveDeck(maCurrentContext, msCurrentDeckId);
+ }
+
+ // get last active deck for this application on first update
+ if (!maRequestedContext.msApplication.isEmpty() &&
+ (maCurrentContext.msApplication != maRequestedContext.msApplication))
+ {
+ OUString sLastActiveDeck = mpResourceManager->GetLastActiveDeck( maRequestedContext );
+ if (!sLastActiveDeck.isEmpty())
+ msCurrentDeckId = sLastActiveDeck;
+ }
+
+ maCurrentContext = maRequestedContext;
+
+ mpResourceManager->InitDeckContext(GetCurrentContext());
+
+ // Find the set of decks that could be displayed for the new context.
+ ResourceManager::DeckContextDescriptorContainer aDecks;
+
+ css::uno::Reference<css::frame::XController> xController = mxCurrentController.is() ? mxCurrentController : mxFrame->getController();
+
+ mpResourceManager->GetMatchingDecks (
+ aDecks,
+ maCurrentContext,
+ mbIsDocumentReadOnly,
+ xController);
+
+ maFocusManager.Clear();
+
+ // Notify the tab bar about the updated set of decks.
+ mpTabBar->SetDecks(aDecks);
+
+ // Find the new deck. By default that is the same as the old
+ // one. If that is not set or not enabled, then choose the
+ // first enabled deck (which is PropertyDeck).
+ OUString sNewDeckId;
+ for (const auto& rDeck : aDecks)
+ {
+ if (rDeck.mbIsEnabled)
+ {
+ if (rDeck.msId == msCurrentDeckId)
+ {
+ sNewDeckId = msCurrentDeckId;
+ break;
+ }
+ else if (sNewDeckId.getLength() == 0)
+ sNewDeckId = rDeck.msId;
+ }
+ }
+
+ if (sNewDeckId.getLength() == 0)
+ {
+ // We did not find a valid deck.
+ RequestCloseDeck();
+ return;
+ }
+
+ // Tell the tab bar to highlight the button associated
+ // with the deck.
+ mpTabBar->HighlightDeck(sNewDeckId);
+
+ std::shared_ptr<DeckDescriptor> xDescriptor = mpResourceManager->GetDeckDescriptor(sNewDeckId);
+
+ if (xDescriptor)
+ {
+ SwitchToDeck(*xDescriptor, maCurrentContext);
+ }
+}
+
+namespace {
+
+void collectUIInformation(const OUString& rDeckId)
+{
+ EventDescription aDescription;
+ aDescription.aAction = "SIDEBAR";
+ aDescription.aParent = "MainWindow";
+ aDescription.aParameters = {{"PANEL", rDeckId}};
+ aDescription.aKeyWord = "CurrentApp";
+
+ UITestLogger::getInstance().logEvent(aDescription);
+}
+
+}
+
+void SidebarController::OpenThenToggleDeck (
+ const OUString& rsDeckId)
+{
+ SfxSplitWindow* pSplitWindow = GetSplitWindow();
+ if ( pSplitWindow && !pSplitWindow->IsFadeIn() )
+ // tdf#83546 Collapsed sidebar should expand first
+ pSplitWindow->FadeIn();
+ else if ( IsDeckVisible( rsDeckId ) )
+ {
+ if( !WasFloatingDeckClosed() )
+ {
+ // tdf#88241 Summoning an undocked sidebar a second time should close sidebar
+ mpParentWindow->Close();
+ return;
+ }
+ else
+ {
+ // tdf#67627 Clicking a second time on a Deck icon will close the Deck
+ RequestCloseDeck();
+ return;
+ }
+ }
+ RequestOpenDeck();
+ // before SwitchToDeck which may cause the rsDeckId string to be released
+ collectUIInformation(rsDeckId);
+ SwitchToDeck(rsDeckId);
+
+ // Make sure the sidebar is wide enough to fit the requested content
+ if (mpCurrentDeck && mpTabBar)
+ {
+ sal_Int32 nRequestedWidth = mpCurrentDeck->GetMinimalWidth() + TabBar::GetDefaultWidth();
+ // if sidebar was dragged
+ if(mnWidthOnSplitterButtonDown > 0 && mnWidthOnSplitterButtonDown > nRequestedWidth){
+ SetChildWindowWidth(mnWidthOnSplitterButtonDown);
+ }else{
+ SetChildWindowWidth(nRequestedWidth);
+ }
+ }
+}
+
+void SidebarController::OpenThenSwitchToDeck (
+ std::u16string_view rsDeckId)
+{
+ RequestOpenDeck();
+ SwitchToDeck(rsDeckId);
+
+}
+
+void SidebarController::SwitchToDefaultDeck()
+{
+ SwitchToDeck(gsDefaultDeckId);
+}
+
+void SidebarController::SwitchToDeck (
+ std::u16string_view rsDeckId)
+{
+ if ( msCurrentDeckId != rsDeckId
+ || ! mbIsDeckOpen
+ || mnRequestedForceFlags!=SwitchFlag_NoForce)
+ {
+ std::shared_ptr<DeckDescriptor> xDeckDescriptor = mpResourceManager->GetDeckDescriptor(rsDeckId);
+
+ if (xDeckDescriptor)
+ SwitchToDeck(*xDeckDescriptor, maCurrentContext);
+ }
+}
+
+void SidebarController::CreateDeck(std::u16string_view rDeckId) {
+ CreateDeck(rDeckId, maCurrentContext);
+}
+
+void SidebarController::CreateDeck(std::u16string_view rDeckId, const Context& rContext, bool bForceCreate)
+{
+ std::shared_ptr<DeckDescriptor> xDeckDescriptor = mpResourceManager->GetDeckDescriptor(rDeckId);
+
+ if (!xDeckDescriptor)
+ return;
+
+ VclPtr<Deck> aDeck = xDeckDescriptor->mpDeck;
+ if (!aDeck || bForceCreate)
+ {
+ if (aDeck)
+ aDeck.disposeAndClear();
+
+ aDeck = VclPtr<Deck>::Create(
+ *xDeckDescriptor,
+ mpParentWindow,
+ [this]() { return this->RequestCloseDeck(); });
+ }
+ xDeckDescriptor->mpDeck = aDeck;
+ CreatePanels(rDeckId, rContext);
+}
+
+void SidebarController::CreatePanels(std::u16string_view rDeckId, const Context& rContext)
+{
+ std::shared_ptr<DeckDescriptor> xDeckDescriptor = mpResourceManager->GetDeckDescriptor(rDeckId);
+
+ // init panels bounded to that deck, do not wait them being displayed as may be accessed through API
+
+ VclPtr<Deck> pDeck = xDeckDescriptor->mpDeck;
+
+ ResourceManager::PanelContextDescriptorContainer aPanelContextDescriptors;
+
+ css::uno::Reference<css::frame::XController> xController = mxCurrentController.is() ? mxCurrentController : mxFrame->getController();
+
+ mpResourceManager->GetMatchingPanels(
+ aPanelContextDescriptors,
+ rContext,
+ rDeckId,
+ xController);
+
+ // Update the panel list.
+ const sal_Int32 nNewPanelCount (aPanelContextDescriptors.size());
+ SharedPanelContainer aNewPanels;
+ sal_Int32 nWriteIndex (0);
+
+ aNewPanels.resize(nNewPanelCount);
+
+ for (sal_Int32 nReadIndex=0; nReadIndex<nNewPanelCount; ++nReadIndex)
+ {
+ const ResourceManager::PanelContextDescriptor& rPanelContexDescriptor (
+ aPanelContextDescriptors[nReadIndex]);
+
+ // Determine if the panel can be displayed.
+ const bool bIsPanelVisible (!mbIsDocumentReadOnly || rPanelContexDescriptor.mbShowForReadOnlyDocuments);
+ if ( ! bIsPanelVisible)
+ continue;
+
+ auto xOldPanel(pDeck->GetPanel(rPanelContexDescriptor.msId));
+ if (xOldPanel)
+ {
+ xOldPanel->SetLurkMode(false);
+ aNewPanels[nWriteIndex] = xOldPanel;
+ xOldPanel->SetExpanded(rPanelContexDescriptor.mbIsInitiallyVisible);
+ ++nWriteIndex;
+ }
+ else
+ {
+ auto aPanel = CreatePanel(rPanelContexDescriptor.msId,
+ pDeck->GetPanelParentWindow(),
+ rPanelContexDescriptor.mbIsInitiallyVisible,
+ rContext,
+ pDeck);
+ if (aPanel)
+ {
+ aNewPanels[nWriteIndex] = std::move(aPanel);
+
+ // Depending on the context we have to change the command
+ // for the "more options" dialog.
+ PanelTitleBar* pTitleBar = aNewPanels[nWriteIndex]->GetTitleBar();
+ if (pTitleBar)
+ {
+ pTitleBar->SetMoreOptionsCommand(
+ rPanelContexDescriptor.msMenuCommand,
+ mxFrame, xController);
+ }
+ ++nWriteIndex;
+ }
+ }
+ }
+
+ // mpCurrentPanels - may miss stuff (?)
+ aNewPanels.resize(nWriteIndex);
+ pDeck->ResetPanels(std::move(aNewPanels));
+}
+
+void SidebarController::SwitchToDeck (
+ const DeckDescriptor& rDeckDescriptor,
+ const Context& rContext)
+{
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ if (const SfxViewShell* pViewShell = mpViewFrame->GetViewShell())
+ {
+ if (msCurrentDeckId != rDeckDescriptor.msId)
+ {
+ const std::string hide = UnoNameFromDeckId(msCurrentDeckId, vcl::EnumContext::Application::Impress == vcl::EnumContext::GetApplicationEnum(GetCurrentContext().msApplication));
+ if (!hide.empty())
+ pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_STATE_CHANGED,
+ (hide + "=false").c_str());
+ }
+
+ const std::string show = UnoNameFromDeckId(rDeckDescriptor.msId, vcl::EnumContext::Application::Impress == vcl::EnumContext::GetApplicationEnum(GetCurrentContext().msApplication));
+ if (!show.empty())
+ pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_STATE_CHANGED,
+ (show + "=true").c_str());
+ }
+ }
+
+ maFocusManager.Clear();
+
+ const bool bForceNewDeck ((mnRequestedForceFlags&SwitchFlag_ForceNewDeck)!=0);
+ const bool bForceNewPanels ((mnRequestedForceFlags&SwitchFlag_ForceNewPanels)!=0);
+ mnRequestedForceFlags = SwitchFlag_NoForce;
+
+ if ( msCurrentDeckId != rDeckDescriptor.msId
+ || bForceNewDeck)
+ {
+ if (mpCurrentDeck)
+ mpCurrentDeck->Hide();
+
+ msCurrentDeckId = rDeckDescriptor.msId;
+ }
+
+ mpTabBar->HighlightDeck(msCurrentDeckId);
+
+ // Determine the panels to display in the deck.
+ ResourceManager::PanelContextDescriptorContainer aPanelContextDescriptors;
+
+ css::uno::Reference<css::frame::XController> xController = mxCurrentController.is() ? mxCurrentController : mxFrame->getController();
+
+ mpResourceManager->GetMatchingPanels(
+ aPanelContextDescriptors,
+ rContext,
+ rDeckDescriptor.msId,
+ xController);
+
+ if (aPanelContextDescriptors.empty())
+ {
+ // There are no panels to be displayed in the current context.
+ if (vcl::EnumContext::GetContextEnum(rContext.msContext) != vcl::EnumContext::Context::Empty)
+ {
+ // Switch to the "empty" context and try again.
+ SwitchToDeck(
+ rDeckDescriptor,
+ Context(
+ rContext.msApplication,
+ vcl::EnumContext::GetContextName(vcl::EnumContext::Context::Empty)));
+ return;
+ }
+ else
+ {
+ // This is already the "empty" context. Looks like we have
+ // to live with an empty deck.
+ }
+ }
+
+ // Provide a configuration and Deck object.
+
+ CreateDeck(rDeckDescriptor.msId, rContext, bForceNewDeck);
+
+ if (bForceNewPanels && !bForceNewDeck) // already forced if bForceNewDeck
+ CreatePanels(rDeckDescriptor.msId, rContext);
+
+ if (mpCurrentDeck && mpCurrentDeck != rDeckDescriptor.mpDeck)
+ mpCurrentDeck->Hide();
+ mpCurrentDeck.reset(rDeckDescriptor.mpDeck);
+
+ if ( ! mpCurrentDeck)
+ return;
+
+#ifdef DEBUG
+ // Show the context name in the deck title bar.
+ DeckTitleBar* pDebugTitleBar = mpCurrentDeck->GetTitleBar();
+ if (pDebugTitleBar)
+ pDebugTitleBar->SetTitle(rDeckDescriptor.msTitle + " (" + maCurrentContext.msContext + ")");
+#endif
+
+ SfxSplitWindow* pSplitWindow = GetSplitWindow();
+ sal_Int32 nTabBarDefaultWidth = TabBar::GetDefaultWidth();
+ WindowAlign eAlign = pSplitWindow ? pSplitWindow->GetAlign() : WindowAlign::Right;
+ tools::Long nDeckX;
+ if (eAlign == WindowAlign::Left) // attach the Sidebar towards the left-side of screen
+ {
+ nDeckX = nTabBarDefaultWidth;
+ }
+ else // attach the Sidebar towards the right-side of screen
+ {
+ nDeckX = 0;
+ }
+
+ // Activate the deck and the new set of panels.
+ mpCurrentDeck->setPosSizePixel(
+ nDeckX,
+ 0,
+ mpParentWindow->GetSizePixel().Width() - nTabBarDefaultWidth,
+ mpParentWindow->GetSizePixel().Height());
+
+ mpCurrentDeck->Show();
+
+ mpParentWindow->SetText(rDeckDescriptor.msTitle);
+
+ NotifyResize();
+
+ // Tell the focus manager about the new panels and tab bar
+ // buttons.
+ maFocusManager.SetDeck(mpCurrentDeck);
+ maFocusManager.SetPanels(mpCurrentDeck->GetPanels());
+
+ mpTabBar->UpdateFocusManager(maFocusManager);
+ UpdateTitleBarIcons();
+}
+
+void SidebarController::notifyDeckTitle(std::u16string_view targetDeckId)
+{
+ if (msCurrentDeckId == targetDeckId)
+ {
+ maFocusManager.SetDeck(mpCurrentDeck);
+ mpTabBar->UpdateFocusManager(maFocusManager);
+ UpdateTitleBarIcons();
+ }
+}
+
+std::shared_ptr<Panel> SidebarController::CreatePanel (
+ std::u16string_view rsPanelId,
+ weld::Widget* pParentWindow,
+ const bool bIsInitiallyExpanded,
+ const Context& rContext,
+ const VclPtr<Deck>& pDeck)
+{
+ std::shared_ptr<PanelDescriptor> xPanelDescriptor = mpResourceManager->GetPanelDescriptor(rsPanelId);
+
+ if (!xPanelDescriptor)
+ return nullptr;
+
+ // Create the panel which is the parent window of the UIElement.
+ auto xPanel = std::make_shared<Panel>(
+ *xPanelDescriptor,
+ pParentWindow,
+ bIsInitiallyExpanded,
+ pDeck,
+ [this]() { return this->GetCurrentContext(); },
+ mxFrame);
+
+ // Create the XUIElement.
+ Reference<ui::XUIElement> xUIElement (CreateUIElement(
+ xPanel->GetElementParentWindow(),
+ xPanelDescriptor->msImplementationURL,
+ xPanelDescriptor->mbWantsCanvas,
+ rContext));
+ if (xUIElement.is())
+ {
+ // Initialize the panel and add it to the active deck.
+ xPanel->SetUIElement(xUIElement);
+ }
+ else
+ {
+ xPanel.reset();
+ }
+
+ return xPanel;
+}
+
+Reference<ui::XUIElement> SidebarController::CreateUIElement (
+ const Reference<awt::XWindow>& rxWindow,
+ const OUString& rsImplementationURL,
+ const bool bWantsCanvas,
+ const Context& rContext)
+{
+ try
+ {
+ const Reference<XComponentContext> xComponentContext (::comphelper::getProcessComponentContext() );
+ const Reference<ui::XUIElementFactory> xUIElementFactory =
+ ui::theUIElementFactoryManager::get( xComponentContext );
+
+ // Create the XUIElement.
+ ::comphelper::NamedValueCollection aCreationArguments;
+ aCreationArguments.put("Frame", Any(mxFrame));
+ aCreationArguments.put("ParentWindow", Any(rxWindow));
+ SidebarDockingWindow* pSfxDockingWindow = mpParentWindow.get();
+ if (pSfxDockingWindow != nullptr)
+ aCreationArguments.put("SfxBindings", Any(reinterpret_cast<sal_uInt64>(&pSfxDockingWindow->GetBindings())));
+ aCreationArguments.put("Theme", Theme::GetPropertySet());
+ aCreationArguments.put("Sidebar", Any(Reference<ui::XSidebar>(static_cast<ui::XSidebar*>(this))));
+ if (bWantsCanvas)
+ {
+ Reference<rendering::XSpriteCanvas> xCanvas (VCLUnoHelper::GetWindow(rxWindow)->GetOutDev()->GetSpriteCanvas());
+ aCreationArguments.put("Canvas", Any(xCanvas));
+ }
+
+ if (mxCurrentController.is())
+ {
+ OUString aModule = Tools::GetModuleName(mxCurrentController);
+ if (!aModule.isEmpty())
+ {
+ aCreationArguments.put("Module", Any(aModule));
+ }
+ aCreationArguments.put("Controller", Any(mxCurrentController));
+ }
+
+ aCreationArguments.put("ApplicationName", Any(rContext.msApplication));
+ aCreationArguments.put("ContextName", Any(rContext.msContext));
+
+ Reference<ui::XUIElement> xUIElement(
+ xUIElementFactory->createUIElement(
+ rsImplementationURL,
+ aCreationArguments.getPropertyValues()),
+ UNO_SET_THROW);
+
+ return xUIElement;
+ }
+ catch(const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("sfx.sidebar", "Cannot create panel " << rsImplementationURL);
+ return nullptr;
+ }
+}
+
+IMPL_LINK(SidebarController, WindowEventHandler, VclWindowEvent&, rEvent, void)
+{
+ if (rEvent.GetWindow() == mpParentWindow)
+ {
+ switch (rEvent.GetId())
+ {
+ case VclEventId::WindowShow:
+ case VclEventId::WindowResize:
+ NotifyResize();
+ break;
+
+ case VclEventId::WindowDataChanged:
+ // Force an update of deck and tab bar to reflect
+ // changes in theme (high contrast mode).
+ Theme::HandleDataChange();
+ UpdateTitleBarIcons();
+ mpParentWindow->Invalidate();
+ mnRequestedForceFlags |= SwitchFlag_ForceNewDeck | SwitchFlag_ForceNewPanels;
+ maContextChangeUpdate.RequestCall();
+ break;
+
+ case VclEventId::ObjectDying:
+ dispose();
+ break;
+
+ case VclEventId::WindowPaint:
+ SAL_INFO("sfx.sidebar", "Paint");
+ break;
+
+ default:
+ break;
+ }
+ }
+ else if (rEvent.GetWindow()==mpSplitWindow && mpSplitWindow!=nullptr)
+ {
+ switch (rEvent.GetId())
+ {
+ case VclEventId::WindowMouseButtonDown:
+ mnWidthOnSplitterButtonDown = mpParentWindow->GetSizePixel().Width();
+ break;
+
+ case VclEventId::WindowMouseButtonUp:
+ {
+ ProcessNewWidth(mpParentWindow->GetSizePixel().Width());
+ break;
+ }
+
+ case VclEventId::ObjectDying:
+ dispose();
+ break;
+
+ default: break;
+ }
+ }
+}
+
+void SidebarController::ShowPopupMenu(
+ weld::Menu& rMainMenu, weld::Menu& rSubMenu,
+ const ::std::vector<TabBar::DeckMenuData>& rMenuData) const
+{
+ PopulatePopupMenus(rMainMenu, rSubMenu, rMenuData);
+ rMainMenu.connect_activate(LINK(const_cast<SidebarController*>(this), SidebarController, OnMenuItemSelected));
+ rSubMenu.connect_activate(LINK(const_cast<SidebarController*>(this), SidebarController, OnSubMenuItemSelected));
+}
+
+void SidebarController::PopulatePopupMenus(weld::Menu& rMenu, weld::Menu& rCustomizationMenu,
+ const std::vector<TabBar::DeckMenuData>& rMenuData) const
+{
+ // Add one entry for every tool panel element to individually make
+ // them visible or hide them.
+ sal_Int32 nIndex (0);
+ for (const auto& rItem : rMenuData)
+ {
+ OString sIdent("select" + OString::number(nIndex));
+ rMenu.insert(nIndex, OUString::fromUtf8(sIdent), rItem.msDisplayName,
+ nullptr, nullptr, nullptr, TRISTATE_FALSE);
+ rMenu.set_active(sIdent, rItem.mbIsCurrentDeck);
+ rMenu.set_sensitive(sIdent, rItem.mbIsEnabled && rItem.mbIsActive);
+
+ if (!comphelper::LibreOfficeKit::isActive())
+ {
+ if (rItem.mbIsCurrentDeck)
+ {
+ // Don't allow the currently visible deck to be disabled.
+ OString sSubIdent("nocustomize" + OString::number(nIndex));
+ rCustomizationMenu.insert(nIndex, OUString::fromUtf8(sSubIdent), rItem.msDisplayName,
+ nullptr, nullptr, nullptr, TRISTATE_FALSE);
+ rCustomizationMenu.set_active(sSubIdent, true);
+ }
+ else
+ {
+ OString sSubIdent("customize" + OString::number(nIndex));
+ rCustomizationMenu.insert(nIndex, OUString::fromUtf8(sSubIdent), rItem.msDisplayName,
+ nullptr, nullptr, nullptr, TRISTATE_TRUE);
+ rCustomizationMenu.set_active(sSubIdent, rItem.mbIsEnabled && rItem.mbIsActive);
+ }
+ }
+
+ ++nIndex;
+ }
+
+ bool bHideLock = true;
+ bool bHideUnLock = true;
+ // LOK doesn't support docked/undocked; Sidebar is floating but rendered docked in browser.
+ if (!comphelper::LibreOfficeKit::isActive())
+ {
+ // Add entry for docking or un-docking the tool panel.
+ if (mpParentWindow->IsFloatingMode())
+ bHideLock = false;
+ else
+ bHideUnLock = false;
+ }
+ rMenu.set_visible("locktaskpanel", !bHideLock);
+ rMenu.set_visible("unlocktaskpanel", !bHideUnLock);
+
+ // No Restore or Customize options for LoKit.
+ rMenu.set_visible("customization", !comphelper::LibreOfficeKit::isActive());
+}
+
+IMPL_LINK(SidebarController, OnMenuItemSelected, const OString&, rCurItemId, void)
+{
+ if (rCurItemId == "unlocktaskpanel")
+ {
+ mpParentWindow->SetFloatingMode(true);
+ if (mpParentWindow->IsFloatingMode())
+ mpParentWindow->ToTop(ToTopFlags::GrabFocusOnly);
+ }
+ else if (rCurItemId == "locktaskpanel")
+ {
+ mpParentWindow->SetFloatingMode(false);
+ }
+ else if (rCurItemId == "hidesidebar")
+ {
+ if (!comphelper::LibreOfficeKit::isActive())
+ {
+ const util::URL aURL(Tools::GetURL(".uno:Sidebar"));
+ Reference<frame::XDispatch> xDispatch(Tools::GetDispatch(mxFrame, aURL));
+ if (xDispatch.is())
+ xDispatch->dispatch(aURL, Sequence<beans::PropertyValue>());
+ }
+ else
+ {
+ // In LOK we don't really destroy the sidebar when "closing";
+ // we simply hide it. This is because recreating it is problematic
+ // See notes in SidebarDockingWindow::NotifyResize().
+ RequestCloseDeck();
+ }
+ }
+ else
+ {
+ try
+ {
+ OString sNumber;
+ if (rCurItemId.startsWith("select", &sNumber))
+ {
+ RequestOpenDeck();
+ SwitchToDeck(mpTabBar->GetDeckIdForIndex(sNumber.toInt32()));
+ }
+ mpParentWindow->GrabFocusToDocument();
+ }
+ catch (RuntimeException&)
+ {
+ }
+ }
+}
+
+IMPL_LINK(SidebarController, OnSubMenuItemSelected, const OString&, rCurItemId, void)
+{
+ if (rCurItemId == "restoredefault")
+ mpTabBar->RestoreHideFlags();
+ else
+ {
+ try
+ {
+ OString sNumber;
+ if (rCurItemId.startsWith("customize", &sNumber))
+ {
+ mpTabBar->ToggleHideFlag(sNumber.toInt32());
+
+ // Find the set of decks that could be displayed for the new context.
+ ResourceManager::DeckContextDescriptorContainer aDecks;
+ mpResourceManager->GetMatchingDecks (
+ aDecks,
+ GetCurrentContext(),
+ IsDocumentReadOnly(),
+ mxFrame->getController());
+ // Notify the tab bar about the updated set of decks.
+ maFocusManager.Clear();
+ mpTabBar->SetDecks(aDecks);
+ mpTabBar->HighlightDeck(mpCurrentDeck->GetId());
+ mpTabBar->UpdateFocusManager(maFocusManager);
+ }
+ mpParentWindow->GrabFocusToDocument();
+ }
+ catch (RuntimeException&)
+ {
+ }
+ }
+}
+
+
+void SidebarController::RequestCloseDeck()
+{
+ if (comphelper::LibreOfficeKit::isActive() && mpCurrentDeck)
+ {
+ const SfxViewShell* pViewShell = SfxViewShell::Current();
+ if (pViewShell && pViewShell->isLOKMobilePhone())
+ {
+ // Mobile phone - TODO: unify with desktop
+ tools::JsonWriter aJsonWriter;
+ aJsonWriter.put("id", mpParentWindow->get_id());
+ aJsonWriter.put("type", "dockingwindow");
+ aJsonWriter.put("text", mpParentWindow->GetText());
+ aJsonWriter.put("enabled", false);
+ const std::string message = aJsonWriter.extractAsStdString();
+ pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_JSDIALOG, message.c_str());
+ }
+ else if (pViewShell)
+ {
+ tools::JsonWriter aJsonWriter;
+ aJsonWriter.put("id", mpParentWindow->get_id());
+ aJsonWriter.put("action", "close");
+ aJsonWriter.put("jsontype", "sidebar");
+ const std::string message = aJsonWriter.extractAsStdString();
+ pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_JSDIALOG, message.c_str());
+ }
+ }
+
+ mbIsDeckRequestedOpen = false;
+ UpdateDeckOpenState();
+
+ if (!mpCurrentDeck)
+ mpTabBar->RemoveDeckHighlight();
+}
+
+void SidebarController::RequestOpenDeck()
+{
+ SfxSplitWindow* pSplitWindow = GetSplitWindow();
+ if ( pSplitWindow && !pSplitWindow->IsFadeIn() )
+ // tdf#83546 Collapsed sidebar should expand first
+ pSplitWindow->FadeIn();
+
+ mbIsDeckRequestedOpen = true;
+ UpdateDeckOpenState();
+}
+
+bool SidebarController::IsDeckOpen(const sal_Int32 nIndex)
+{
+ if (nIndex >= 0)
+ {
+ OUString asDeckId(mpTabBar->GetDeckIdForIndex(nIndex));
+ return IsDeckVisible(asDeckId);
+ }
+ return mbIsDeckOpen && *mbIsDeckOpen;
+}
+
+bool SidebarController::IsDeckVisible(std::u16string_view rsDeckId)
+{
+ return mbIsDeckOpen && *mbIsDeckOpen && msCurrentDeckId == rsDeckId;
+}
+
+void SidebarController::UpdateDeckOpenState()
+{
+ if ( ! mbIsDeckRequestedOpen)
+ // No state requested.
+ return;
+
+ const sal_Int32 nTabBarDefaultWidth = TabBar::GetDefaultWidth();
+
+ // Update (change) the open state when it either has not yet been initialized
+ // or when its value differs from the requested state.
+ if ( mbIsDeckOpen && *mbIsDeckOpen == *mbIsDeckRequestedOpen )
+ return;
+
+ if (*mbIsDeckRequestedOpen)
+ {
+ if (!mpParentWindow->IsFloatingMode())
+ {
+ if (mnSavedSidebarWidth <= nTabBarDefaultWidth)
+ SetChildWindowWidth(SidebarChildWindow::GetDefaultWidth(mpParentWindow));
+ else
+ SetChildWindowWidth(mnSavedSidebarWidth);
+ }
+ else
+ {
+ // Show the Deck by resizing back to the original size (before hiding).
+ Size aNewSize(mpParentWindow->GetFloatingWindow()->GetSizePixel());
+ Point aNewPos(mpParentWindow->GetFloatingWindow()->GetPosPixel());
+
+ aNewPos.setX(aNewPos.X() - mnSavedSidebarWidth + nTabBarDefaultWidth);
+ aNewSize.setWidth(mnSavedSidebarWidth);
+
+ mpParentWindow->GetFloatingWindow()->SetPosSizePixel(aNewPos, aNewSize);
+
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ // Sidebar wide enough to render the menu; enable it.
+ mpTabBar->EnableMenuButton(true);
+
+ if (const SfxViewShell* pViewShell = mpViewFrame->GetViewShell())
+ {
+ const std::string uno = UnoNameFromDeckId(msCurrentDeckId, vcl::EnumContext::Application::Impress == vcl::EnumContext::GetApplicationEnum(GetCurrentContext().msApplication));
+ if (!uno.empty())
+ pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_STATE_CHANGED,
+ (uno + "=true").c_str());
+ }
+ }
+ }
+ }
+ else
+ {
+ if ( ! mpParentWindow->IsFloatingMode())
+ mnSavedSidebarWidth = SetChildWindowWidth(nTabBarDefaultWidth);
+ else
+ {
+ // Hide the Deck by resizing to the width of the TabBar.
+ Size aNewSize(mpParentWindow->GetFloatingWindow()->GetSizePixel());
+ Point aNewPos(mpParentWindow->GetFloatingWindow()->GetPosPixel());
+ mnSavedSidebarWidth = aNewSize.Width(); // Save the current width to restore.
+
+ aNewPos.setX(aNewPos.X() + mnSavedSidebarWidth - nTabBarDefaultWidth);
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ // Hide by collapsing, otherwise with 0x0 the client might expect
+ // to get valid dimensions on rendering and not collapse the sidebar.
+ aNewSize.setWidth(1);
+ }
+ else
+ aNewSize.setWidth(nTabBarDefaultWidth);
+
+ mpParentWindow->GetFloatingWindow()->SetPosSizePixel(aNewPos, aNewSize);
+
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ // Sidebar too narrow to render the menu; disable it.
+ mpTabBar->EnableMenuButton(false);
+
+ if (const SfxViewShell* pViewShell = mpViewFrame->GetViewShell())
+ {
+ const std::string uno = UnoNameFromDeckId(msCurrentDeckId, vcl::EnumContext::Application::Impress == vcl::EnumContext::GetApplicationEnum(GetCurrentContext().msApplication));
+ if (!uno.empty())
+ pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_STATE_CHANGED,
+ (uno + "=false").c_str());
+ }
+ }
+ }
+
+ if (mnWidthOnSplitterButtonDown > nTabBarDefaultWidth)
+ mnSavedSidebarWidth = mnWidthOnSplitterButtonDown;
+ mpParentWindow->SetStyle(mpParentWindow->GetStyle() & ~WB_SIZEABLE);
+ }
+
+ mbIsDeckOpen = *mbIsDeckRequestedOpen;
+ if (*mbIsDeckOpen && mpCurrentDeck)
+ mpCurrentDeck->Show();
+ NotifyResize();
+}
+
+bool SidebarController::CanModifyChildWindowWidth()
+{
+ SfxSplitWindow* pSplitWindow = GetSplitWindow();
+ if (pSplitWindow == nullptr)
+ return false;
+
+ sal_uInt16 nRow (0xffff);
+ sal_uInt16 nColumn (0xffff);
+ if (pSplitWindow->GetWindowPos(mpParentWindow, nColumn, nRow))
+ {
+ sal_uInt16 nRowCount (pSplitWindow->GetWindowCount(nColumn));
+ return nRowCount==1;
+ }
+ else
+ return false;
+}
+
+sal_Int32 SidebarController::SetChildWindowWidth (const sal_Int32 nNewWidth)
+{
+ SfxSplitWindow* pSplitWindow = GetSplitWindow();
+ if (pSplitWindow == nullptr)
+ return 0;
+
+ sal_uInt16 nRow (0xffff);
+ sal_uInt16 nColumn (0xffff);
+ pSplitWindow->GetWindowPos(mpParentWindow, nColumn, nRow);
+ const tools::Long nColumnWidth (pSplitWindow->GetLineSize(nColumn));
+
+ vcl::Window* pWindow = mpParentWindow;
+ const Size aWindowSize (pWindow->GetSizePixel());
+
+ pSplitWindow->MoveWindow(
+ mpParentWindow,
+ Size(nNewWidth, aWindowSize.Height()),
+ nColumn,
+ nRow,
+ false);
+ static_cast<SplitWindow*>(pSplitWindow)->Split();
+
+ return static_cast<sal_Int32>(nColumnWidth);
+}
+
+void SidebarController::RestrictWidth (sal_Int32 nWidth)
+{
+ SfxSplitWindow* pSplitWindow = GetSplitWindow();
+ if (pSplitWindow != nullptr)
+ {
+ const sal_uInt16 nId (pSplitWindow->GetItemId(mpParentWindow.get()));
+ const sal_uInt16 nSetId (pSplitWindow->GetSet(nId));
+ const sal_Int32 nRequestedWidth = TabBar::GetDefaultWidth() + nWidth;
+
+ pSplitWindow->SetItemSizeRange(
+ nSetId,
+ Range(nRequestedWidth, std::max(nRequestedWidth, getMaximumWidth())));
+ }
+}
+
+SfxSplitWindow* SidebarController::GetSplitWindow()
+{
+ if (mpParentWindow != nullptr)
+ {
+ SfxSplitWindow* pSplitWindow = dynamic_cast<SfxSplitWindow*>(mpParentWindow->GetParent());
+ if (pSplitWindow != mpSplitWindow)
+ {
+ if (mpSplitWindow != nullptr)
+ mpSplitWindow->RemoveEventListener(LINK(this, SidebarController, WindowEventHandler));
+
+ mpSplitWindow = pSplitWindow;
+
+ if (mpSplitWindow != nullptr)
+ mpSplitWindow->AddEventListener(LINK(this, SidebarController, WindowEventHandler));
+ }
+ return mpSplitWindow;
+ }
+ else
+ return nullptr;
+}
+
+void SidebarController::UpdateCloseIndicator (const bool bCloseAfterDrag)
+{
+ if (mpParentWindow == nullptr)
+ return;
+
+ if (bCloseAfterDrag)
+ {
+ // Make sure that the indicator exists.
+ if (!mpCloseIndicator)
+ mpCloseIndicator.reset(VclPtr<CloseIndicator>::Create(mpParentWindow));
+
+ // Place and show the indicator.
+ const Size aWindowSize (mpParentWindow->GetSizePixel());
+ const Size aImageSize (mpCloseIndicator->GetSizePixel());
+ mpCloseIndicator->SetPosPixel(
+ Point(
+ aWindowSize.Width() - TabBar::GetDefaultWidth() - aImageSize.Width(),
+ (aWindowSize.Height() - aImageSize.Height())/2));
+ mpCloseIndicator->Show();
+ }
+ else
+ {
+ // Hide but don't delete the indicator.
+ if (mpCloseIndicator)
+ mpCloseIndicator->Hide();
+ }
+}
+
+void SidebarController::UpdateTitleBarIcons()
+{
+ if ( ! mpCurrentDeck)
+ return;
+
+ const bool bIsHighContrastModeActive (Theme::IsHighContrastMode());
+
+ const ResourceManager& rResourceManager = *mpResourceManager;
+
+ // Update the deck icon.
+ std::shared_ptr<DeckDescriptor> xDeckDescriptor = rResourceManager.GetDeckDescriptor(mpCurrentDeck->GetId());
+ if (xDeckDescriptor && mpCurrentDeck->GetTitleBar())
+ {
+ const OUString sIconURL(
+ bIsHighContrastModeActive
+ ? xDeckDescriptor->msHighContrastTitleBarIconURL
+ : xDeckDescriptor->msTitleBarIconURL);
+ mpCurrentDeck->GetTitleBar()->SetIcon(Tools::GetImage(sIconURL, mxFrame));
+ }
+
+ // Update the panel icons.
+ const SharedPanelContainer& rPanels (mpCurrentDeck->GetPanels());
+ for (const auto& rxPanel : rPanels)
+ {
+ if ( ! rxPanel)
+ continue;
+ if (!rxPanel->GetTitleBar())
+ continue;
+ std::shared_ptr<PanelDescriptor> xPanelDescriptor = rResourceManager.GetPanelDescriptor(rxPanel->GetId());
+ if (!xPanelDescriptor)
+ continue;
+ const OUString sIconURL (
+ bIsHighContrastModeActive
+ ? xPanelDescriptor->msHighContrastTitleBarIconURL
+ : xPanelDescriptor->msTitleBarIconURL);
+ rxPanel->GetTitleBar()->SetIcon(Tools::GetImage(sIconURL, mxFrame));
+ }
+}
+
+void SidebarController::ShowPanel (const Panel& rPanel)
+{
+ if (mpCurrentDeck)
+ {
+ if (!IsDeckOpen())
+ RequestOpenDeck();
+ mpCurrentDeck->ShowPanel(rPanel);
+ }
+}
+
+ResourceManager::DeckContextDescriptorContainer SidebarController::GetMatchingDecks()
+{
+ ResourceManager::DeckContextDescriptorContainer aDecks;
+ mpResourceManager->GetMatchingDecks (aDecks,
+ GetCurrentContext(),
+ IsDocumentReadOnly(),
+ mxFrame->getController());
+ return aDecks;
+}
+
+ResourceManager::PanelContextDescriptorContainer SidebarController::GetMatchingPanels(std::u16string_view rDeckId)
+{
+ ResourceManager::PanelContextDescriptorContainer aPanels;
+
+ mpResourceManager->GetMatchingPanels(aPanels,
+ GetCurrentContext(),
+ rDeckId,
+ mxFrame->getController());
+ return aPanels;
+}
+
+void SidebarController::updateModel(const css::uno::Reference<css::frame::XModel>& xModel)
+{
+ mpResourceManager->UpdateModel(xModel);
+}
+
+void SidebarController::FadeOut()
+{
+ if (mpSplitWindow)
+ mpSplitWindow->FadeOut();
+}
+
+void SidebarController::FadeIn()
+{
+ if (mpSplitWindow)
+ mpSplitWindow->FadeIn();
+}
+
+tools::Rectangle SidebarController::GetDeckDragArea() const
+{
+ tools::Rectangle aRect;
+ if (mpCurrentDeck)
+ {
+ if (DeckTitleBar* pTitleBar = mpCurrentDeck->GetTitleBar())
+ {
+ aRect = pTitleBar->GetDragArea();
+ }
+ }
+ return aRect;
+}
+
+void SidebarController::frameAction(const css::frame::FrameActionEvent& rEvent)
+{
+ if (rEvent.Frame == mxFrame)
+ {
+ if (rEvent.Action == css::frame::FrameAction_COMPONENT_DETACHING)
+ unregisterSidebarForFrame(this, mxFrame->getController());
+ else if (rEvent.Action == css::frame::FrameAction_COMPONENT_REATTACHED)
+ registerSidebarForFrame(this, mxFrame->getController());
+ }
+}
+
+void SidebarController::saveDeckState()
+{
+ // Impress shutdown : context (frame) is disposed before sidebar disposing
+ // calc writer : context (frame) is disposed after sidebar disposing
+ // so need to test if GetCurrentContext is still valid regarding msApplication
+ if (GetCurrentContext().msApplication != "none")
+ {
+ mpResourceManager->SaveDecksSettings(GetCurrentContext());
+ mpResourceManager->SaveLastActiveDeck(GetCurrentContext(), msCurrentDeckId);
+ }
+}
+
+bool SidebarController::hasChartContextCurrently() const
+{
+ return GetCurrentContext().msApplication == "com.sun.star.chart2.ChartDocument";
+}
+
+sfx2::sidebar::SidebarController* SidebarController::GetSidebarControllerForView(const SfxViewShell* pViewShell)
+{
+ if (!pViewShell)
+ return nullptr;
+
+ Reference<css::frame::XController2> xController(pViewShell->GetController(), UNO_QUERY);
+ if (!xController.is())
+ return nullptr;
+
+ // Make sure there is a model behind the controller, otherwise getSidebar() can crash.
+ if (!xController->getModel().is())
+ return nullptr;
+
+ Reference<css::ui::XSidebarProvider> xSidebarProvider = xController->getSidebar();
+ if (!xSidebarProvider.is())
+ return nullptr;
+
+ Reference<css::ui::XSidebar> xSidebar = xSidebarProvider->getSidebar();
+ if (!xSidebar.is())
+ return nullptr;
+
+ return dynamic_cast<sfx2::sidebar::SidebarController*>(xSidebar.get());
+}
+
+} // end of namespace sfx2::sidebar
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/sidebar/SidebarDockingWindow.cxx b/sfx2/source/sidebar/SidebarDockingWindow.cxx
new file mode 100644
index 000000000..0a22eb088
--- /dev/null
+++ b/sfx2/source/sidebar/SidebarDockingWindow.cxx
@@ -0,0 +1,205 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#include <sfx2/sidebar/SidebarDockingWindow.hxx>
+#include <sfx2/sidebar/SidebarChildWindow.hxx>
+#include <sfx2/sidebar/SidebarController.hxx>
+#include <sidebar/PanelDescriptor.hxx>
+
+#include <comphelper/dispatchcommand.hxx>
+#include <comphelper/processfactory.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <svtools/acceleratorexecute.hxx>
+#include <tools/gen.hxx>
+#include <vcl/event.hxx>
+#include <osl/diagnose.h>
+
+#include <boost/property_tree/json_parser.hpp>
+
+using namespace css;
+using namespace css::uno;
+
+namespace sfx2::sidebar {
+
+SidebarDockingWindow::SidebarDockingWindow(SfxBindings* pSfxBindings, SidebarChildWindow& rChildWindow,
+ vcl::Window* pParentWindow, WinBits nBits)
+ : SfxDockingWindow(pSfxBindings, &rChildWindow, pParentWindow, nBits)
+ , mbIsReadyToDrag(false)
+{
+ // Get the XFrame from the bindings.
+ if (pSfxBindings==nullptr || pSfxBindings->GetDispatcher()==nullptr)
+ {
+ OSL_ASSERT(pSfxBindings!=nullptr);
+ OSL_ASSERT(pSfxBindings->GetDispatcher()!=nullptr);
+ }
+ else
+ {
+ const SfxViewFrame* pViewFrame = pSfxBindings->GetDispatcher()->GetFrame();
+ mpSidebarController = sfx2::sidebar::SidebarController::create(this, pViewFrame);
+ }
+}
+
+SidebarDockingWindow::~SidebarDockingWindow()
+{
+ disposeOnce();
+}
+
+void SidebarDockingWindow::dispose()
+{
+ Reference<lang::XComponent> xComponent(mpSidebarController);
+ mpSidebarController.clear();
+ if (xComponent.is())
+ xComponent->dispose();
+
+ SfxDockingWindow::dispose();
+}
+
+void SidebarDockingWindow::GetFocus()
+{
+ if (mpSidebarController.is())
+ {
+ mpSidebarController->RequestOpenDeck();
+ mpSidebarController->GetFocusManager().GrabFocus();
+ }
+ else
+ SfxDockingWindow::GetFocus();
+}
+
+bool SidebarDockingWindow::Close()
+{
+ if (mpSidebarController.is())
+ mpSidebarController->SetFloatingDeckClosed(true);
+
+ return SfxDockingWindow::Close();
+}
+
+void SidebarDockingWindow::SyncUpdate()
+{
+ if (mpSidebarController.is())
+ mpSidebarController->SyncUpdate();
+}
+
+SfxChildAlignment SidebarDockingWindow::CheckAlignment (
+ SfxChildAlignment eCurrentAlignment,
+ SfxChildAlignment eRequestedAlignment)
+{
+ switch (eRequestedAlignment)
+ {
+ case SfxChildAlignment::TOP:
+ case SfxChildAlignment::HIGHESTTOP:
+ case SfxChildAlignment::LOWESTTOP:
+ case SfxChildAlignment::BOTTOM:
+ case SfxChildAlignment::LOWESTBOTTOM:
+ case SfxChildAlignment::HIGHESTBOTTOM:
+ return eCurrentAlignment;
+
+ case SfxChildAlignment::LEFT:
+ case SfxChildAlignment::RIGHT:
+ case SfxChildAlignment::FIRSTLEFT:
+ case SfxChildAlignment::LASTLEFT:
+ case SfxChildAlignment::FIRSTRIGHT:
+ case SfxChildAlignment::LASTRIGHT:
+ return eRequestedAlignment;
+
+ default:
+ return eRequestedAlignment;
+ }
+}
+
+bool SidebarDockingWindow::EventNotify(NotifyEvent& rEvent)
+{
+ MouseNotifyEvent nType = rEvent.GetType();
+ if (MouseNotifyEvent::KEYINPUT == nType)
+ {
+ const vcl::KeyCode& rKeyCode = rEvent.GetKeyEvent()->GetKeyCode();
+ switch (rKeyCode.GetCode())
+ {
+ case KEY_UP:
+ case KEY_DOWN:
+ case KEY_PAGEUP:
+ case KEY_PAGEDOWN:
+ case KEY_HOME:
+ case KEY_END:
+ case KEY_LEFT:
+ case KEY_RIGHT:
+ case KEY_BACKSPACE:
+ case KEY_DELETE:
+ case KEY_INSERT:
+ case KEY_RETURN:
+ case KEY_ESCAPE:
+ {
+ return true;
+ }
+ default:
+ break;
+ }
+ if (!mpAccel)
+ {
+ mpAccel = svt::AcceleratorExecute::createAcceleratorHelper();
+ mpAccel->init(comphelper::getProcessComponentContext(), mpSidebarController->getXFrame());
+ }
+ const OUString aCommand(mpAccel->findCommand(svt::AcceleratorExecute::st_VCLKey2AWTKey(rKeyCode)));
+ if (".uno:DesignerDialog" == aCommand)
+ {
+ std::shared_ptr<PanelDescriptor> xPanelDescriptor =
+ mpSidebarController->GetResourceManager()->GetPanelDescriptor( u"StyleListPanel" );
+ if ( xPanelDescriptor && mpSidebarController->IsDeckVisible( xPanelDescriptor->msDeckId ) )
+ Close();
+ return true;
+ }
+ if (".uno:Undo" == aCommand || ".uno:Redo" == aCommand)
+ {
+ comphelper::dispatchCommand(aCommand, {});
+ return true;
+ }
+ }
+ else if (MouseNotifyEvent::MOUSEBUTTONDOWN == nType)
+ {
+ const MouseEvent *mEvt = rEvent.GetMouseEvent();
+ if (mEvt->IsLeft())
+ {
+ tools::Rectangle aGrip = mpSidebarController->GetDeckDragArea();
+ if ( aGrip.Contains( mEvt->GetPosPixel() ) )
+ mbIsReadyToDrag = true;
+ }
+ }
+ else if (MouseNotifyEvent::MOUSEMOVE == nType)
+ {
+ const MouseEvent *mEvt = rEvent.GetMouseEvent();
+ tools::Rectangle aGrip = mpSidebarController->GetDeckDragArea();
+ if (mEvt->IsLeft() && aGrip.Contains( mEvt->GetPosPixel() ) && mbIsReadyToDrag )
+ {
+ Point aPos = mEvt->GetPosPixel();
+ vcl::Window* pWindow = rEvent.GetWindow();
+ if ( pWindow != this )
+ {
+ aPos = pWindow->OutputToScreenPixel( aPos );
+ aPos = ScreenToOutputPixel( aPos );
+ }
+ ImplStartDocking( aPos );
+ }
+ }
+
+ return SfxDockingWindow::EventNotify(rEvent);
+}
+
+} // end of namespace sfx2::sidebar
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/sidebar/SidebarModelUpdate.cxx b/sfx2/source/sidebar/SidebarModelUpdate.cxx
new file mode 100644
index 000000000..42821bc56
--- /dev/null
+++ b/sfx2/source/sidebar/SidebarModelUpdate.cxx
@@ -0,0 +1,17 @@
+/* -*- 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/sidebar/SidebarModelUpdate.hxx>
+
+namespace sfx2::sidebar
+{
+SidebarModelUpdate::~SidebarModelUpdate() {}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/sidebar/SidebarPanelBase.cxx b/sfx2/source/sidebar/SidebarPanelBase.cxx
new file mode 100644
index 000000000..5f72192f1
--- /dev/null
+++ b/sfx2/source/sidebar/SidebarPanelBase.cxx
@@ -0,0 +1,197 @@
+/* -*- 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/sidebar/SidebarPanelBase.hxx>
+#include <sfx2/sidebar/ILayoutableWindow.hxx>
+#include <sfx2/sidebar/IContextChangeReceiver.hxx>
+#include <sfx2/sidebar/PanelLayout.hxx>
+#include <sfx2/sidebar/SidebarModelUpdate.hxx>
+#include <vcl/EnumContext.hxx>
+#include <vcl/svapp.hxx>
+#include <comphelper/processfactory.hxx>
+#include <com/sun/star/awt/XWindowPeer.hpp>
+#include <com/sun/star/ui/ContextChangeEventMultiplexer.hpp>
+#include <com/sun/star/ui/UIElementType.hpp>
+
+using namespace css;
+using namespace css::uno;
+
+namespace sfx2::sidebar {
+
+Reference<ui::XUIElement> SidebarPanelBase::Create (
+ const OUString& rsResourceURL,
+ const css::uno::Reference<css::frame::XFrame>& rxFrame,
+ std::unique_ptr<PanelLayout> xControl,
+ const css::ui::LayoutSize& rLayoutSize)
+{
+ Reference<ui::XUIElement> xUIElement (
+ new SidebarPanelBase(
+ rsResourceURL,
+ rxFrame,
+ std::move(xControl),
+ rLayoutSize));
+ return xUIElement;
+}
+
+SidebarPanelBase::SidebarPanelBase (
+ const OUString& rsResourceURL,
+ const css::uno::Reference<css::frame::XFrame>& rxFrame,
+ std::unique_ptr<PanelLayout> xControl,
+ const css::ui::LayoutSize& rLayoutSize)
+ : mxFrame(rxFrame),
+ mxControl(std::move(xControl)),
+ msResourceURL(rsResourceURL),
+ maLayoutSize(rLayoutSize)
+{
+ if (mxFrame.is())
+ {
+ css::uno::Reference<css::ui::XContextChangeEventMultiplexer> xMultiplexer (
+ css::ui::ContextChangeEventMultiplexer::get(
+ ::comphelper::getProcessComponentContext()));
+ xMultiplexer->addContextChangeEventListener(this, mxFrame->getController());
+ }
+}
+
+SidebarPanelBase::~SidebarPanelBase()
+{
+}
+
+void SidebarPanelBase::SetParentPanel(sfx2::sidebar::Panel* pPanel)
+{
+ if (!mxControl)
+ return;
+ mxControl->SetPanel(pPanel);
+}
+
+void SidebarPanelBase::disposing(std::unique_lock<std::mutex>&)
+{
+ SolarMutexGuard aGuard;
+
+ mxControl.reset();
+
+ if (mxFrame.is())
+ {
+ css::uno::Reference<css::ui::XContextChangeEventMultiplexer> xMultiplexer (
+ css::ui::ContextChangeEventMultiplexer::get(
+ ::comphelper::getProcessComponentContext()));
+ xMultiplexer->removeAllContextChangeEventListeners(this);
+ mxFrame = nullptr;
+ }
+}
+
+// XContextChangeEventListener
+void SAL_CALL SidebarPanelBase::notifyContextChangeEvent (
+ const ui::ContextChangeEventObject& rEvent)
+{
+ SolarMutexGuard aGuard;
+
+ IContextChangeReceiver* pContextChangeReceiver
+ = dynamic_cast<IContextChangeReceiver*>(mxControl.get());
+ if (pContextChangeReceiver != nullptr)
+ {
+ const vcl::EnumContext aContext(
+ vcl::EnumContext::GetApplicationEnum(rEvent.ApplicationName),
+ vcl::EnumContext::GetContextEnum(rEvent.ContextName));
+ pContextChangeReceiver->HandleContextChange(aContext);
+ }
+}
+
+void SAL_CALL SidebarPanelBase::disposing (
+ const css::lang::EventObject&)
+{
+ SolarMutexGuard aGuard;
+
+ mxFrame = nullptr;
+ mxControl.reset();
+}
+
+css::uno::Reference<css::frame::XFrame> SAL_CALL SidebarPanelBase::getFrame()
+{
+ return mxFrame;
+}
+
+OUString SAL_CALL SidebarPanelBase::getResourceURL()
+{
+ return msResourceURL;
+}
+
+sal_Int16 SAL_CALL SidebarPanelBase::getType()
+{
+ return ui::UIElementType::TOOLPANEL;
+}
+
+Reference<XInterface> SAL_CALL SidebarPanelBase::getRealInterface()
+{
+ return Reference<XInterface>(static_cast<XWeak*>(this));
+}
+
+Reference<accessibility::XAccessible> SAL_CALL SidebarPanelBase::createAccessible (
+ const Reference<accessibility::XAccessible>&)
+{
+ // Not implemented.
+ return nullptr;
+}
+
+Reference<awt::XWindow> SAL_CALL SidebarPanelBase::getWindow()
+{
+ // Not implemented
+ return nullptr;
+}
+
+ui::LayoutSize SAL_CALL SidebarPanelBase::getHeightForWidth (const sal_Int32 nWidth)
+{
+ SolarMutexGuard aGuard;
+
+ if (maLayoutSize.Minimum >= 0)
+ return maLayoutSize;
+
+ ILayoutableWindow* pLayoutableWindow = dynamic_cast<ILayoutableWindow*>(mxControl.get());
+ if (pLayoutableWindow)
+ return pLayoutableWindow->GetHeightForWidth(nWidth);
+ else
+ {
+ // widget layout-based sidebar
+ mxControl->queue_resize();
+ Size aSize(mxControl->get_preferred_size());
+ return ui::LayoutSize(aSize.Height(), aSize.Height(), aSize.Height());
+ }
+}
+
+sal_Int32 SAL_CALL SidebarPanelBase::getMinimalWidth ()
+{
+ SolarMutexGuard aGuard;
+
+ // widget layout-based sidebar
+ Size aSize(mxControl->get_preferred_size());
+ return aSize.Width();
+}
+
+void SAL_CALL SidebarPanelBase::updateModel(const css::uno::Reference<css::frame::XModel>& xModel)
+{
+ SolarMutexGuard aGuard;
+
+ SidebarModelUpdate* pModelUpdate = dynamic_cast<SidebarModelUpdate*>(mxControl.get());
+ if (!pModelUpdate)
+ return;
+
+ pModelUpdate->updateModel(xModel);
+}
+
+} // end of namespace sfx2::sidebar
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/sidebar/SidebarToolBox.cxx b/sfx2/source/sidebar/SidebarToolBox.cxx
new file mode 100644
index 000000000..bb89d007b
--- /dev/null
+++ b/sfx2/source/sidebar/SidebarToolBox.cxx
@@ -0,0 +1,322 @@
+/* -*- 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 <sidebar/SidebarToolBox.hxx>
+#include <sidebar/ControllerFactory.hxx>
+#include <sfx2/viewfrm.hxx>
+
+#include <officecfg/Office/Common.hxx>
+#include <vcl/commandinfoprovider.hxx>
+#include <vcl/event.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/svapp.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+#include <svtools/miscopt.hxx>
+#include <com/sun/star/frame/XSubToolbarController.hpp>
+#include <com/sun/star/lang/XComponent.hpp>
+#include <com/sun/star/frame/XFrame.hpp>
+#include <com/sun/star/frame/XToolbarController.hpp>
+
+using namespace css;
+using namespace css::uno;
+
+namespace {
+ void lcl_RTLizeCommandURL( OUString& rCommandURL )
+ {
+ if (rCommandURL == ".uno:ParaLeftToRight")
+ rCommandURL = ".uno:ParaRightToLeft";
+ else if (rCommandURL == ".uno:ParaRightToLeft")
+ rCommandURL = ".uno:ParaLeftToRight";
+ else if (rCommandURL == ".uno:LeftPara")
+ rCommandURL = ".uno:RightPara";
+ else if (rCommandURL == ".uno:RightPara")
+ rCommandURL = ".uno:LeftPara";
+ else if (rCommandURL == ".uno:AlignLeft")
+ rCommandURL = ".uno:AlignRight";
+ else if (rCommandURL == ".uno:AlignRight")
+ rCommandURL = ".uno:AlignLeft";
+ }
+}
+
+namespace sfx2::sidebar {
+
+SidebarToolBox::SidebarToolBox (vcl::Window* pParentWindow)
+ : ToolBox(pParentWindow, 0),
+ mbAreHandlersRegistered(false),
+ mbUseDefaultButtonSize(true),
+ mbSideBar(true)
+{
+ SetBackground(Wallpaper());
+ SetPaintTransparent(true);
+ SetToolboxButtonSize(GetDefaultButtonSize());
+
+ SvtMiscOptions().AddListenerLink(LINK(this, SidebarToolBox, ChangedIconSizeHandler));
+
+#ifdef DEBUG
+ SetText(OUString("SidebarToolBox"));
+#endif
+}
+
+SidebarToolBox::~SidebarToolBox()
+{
+ disposeOnce();
+}
+
+void SidebarToolBox::dispose()
+{
+ SvtMiscOptions().RemoveListenerLink(LINK(this, SidebarToolBox, ChangedIconSizeHandler));
+
+ ControllerContainer aControllers;
+ aControllers.swap(maControllers);
+ for (auto const& controller : aControllers)
+ {
+ Reference<lang::XComponent> xComponent(controller.second, UNO_QUERY);
+ if (xComponent.is())
+ xComponent->dispose();
+ }
+
+ if (mbAreHandlersRegistered)
+ {
+ SetDropdownClickHdl(Link<ToolBox *, void>());
+ SetClickHdl(Link<ToolBox *, void>());
+ SetDoubleClickHdl(Link<ToolBox *, void>());
+ SetSelectHdl(Link<ToolBox *, void>());
+ SetActivateHdl(Link<ToolBox *, void>());
+ SetDeactivateHdl(Link<ToolBox *, void>());
+ mbAreHandlersRegistered = false;
+ }
+
+ ToolBox::dispose();
+}
+
+ToolBoxButtonSize SidebarToolBox::GetDefaultButtonSize() const
+{
+ return static_cast<ToolBoxButtonSize>(officecfg::Office::Common::Misc::SidebarIconSize::get());
+}
+
+void SidebarToolBox::InsertItem(const OUString& rCommand,
+ const css::uno::Reference<css::frame::XFrame>& rFrame,
+ ToolBoxItemBits nBits, const Size& rRequestedSize, ImplToolItems::size_type nPos)
+{
+ OUString aCommand( rCommand );
+
+ if( AllSettings::GetLayoutRTL() )
+ {
+ lcl_RTLizeCommandURL( aCommand );
+ }
+
+ ToolBox::InsertItem(aCommand, rFrame, nBits, rRequestedSize, nPos);
+
+ CreateController(GetItemId(aCommand), rFrame, std::max(rRequestedSize.Width(), ::tools::Long(0)), mbSideBar);
+ RegisterHandlers();
+}
+
+bool SidebarToolBox::EventNotify (NotifyEvent& rEvent)
+{
+ if (rEvent.GetType() == MouseNotifyEvent::KEYINPUT)
+ {
+ if (rEvent.GetKeyEvent()->GetKeyCode().GetCode() == KEY_TAB)
+ {
+ // Special handling for transferring handling of KEY_TAB
+ // that becomes necessary because of our parent that is
+ // not the dialog but a background control.
+ return DockingWindow::EventNotify(rEvent);
+ }
+ }
+ return ToolBox::EventNotify(rEvent);
+}
+
+void SidebarToolBox::KeyInput(const KeyEvent& rKEvt)
+{
+ if (KEY_ESCAPE != rKEvt.GetKeyCode().GetCode())
+ ToolBox::KeyInput(rKEvt);
+}
+
+void SidebarToolBox::CreateController (
+ const ToolBoxItemId nItemId,
+ const css::uno::Reference<css::frame::XFrame>& rxFrame,
+ const sal_Int32 nItemWidth, bool bSideBar)
+{
+ const OUString sCommandName (GetItemCommand(nItemId));
+
+ uno::Reference<frame::XToolbarController> xController(sfx2::sidebar::ControllerFactory::CreateToolBoxController(
+ this, nItemId, sCommandName, rxFrame, rxFrame->getController(),
+ VCLUnoHelper::GetInterface(this), nItemWidth, bSideBar));
+
+ if (xController.is())
+ maControllers.insert(std::make_pair(nItemId, xController));
+}
+
+Reference<frame::XToolbarController> SidebarToolBox::GetControllerForItemId (const ToolBoxItemId nItemId) const
+{
+ ControllerContainer::const_iterator iController (maControllers.find(nItemId));
+ if (iController != maControllers.end())
+ return iController->second;
+
+ return Reference<frame::XToolbarController>();
+}
+
+void SidebarToolBox::RegisterHandlers()
+{
+ if ( ! mbAreHandlersRegistered)
+ {
+ mbAreHandlersRegistered = true;
+ SetDropdownClickHdl(LINK(this, SidebarToolBox, DropDownClickHandler));
+ SetClickHdl(LINK(this, SidebarToolBox, ClickHandler));
+ SetDoubleClickHdl(LINK(this, SidebarToolBox, DoubleClickHandler));
+ SetSelectHdl(LINK(this, SidebarToolBox, SelectHandler));
+ }
+}
+
+IMPL_LINK(SidebarToolBox, DropDownClickHandler, ToolBox*, pToolBox, void)
+{
+ if (pToolBox != nullptr)
+ {
+ Reference<frame::XToolbarController> xController (GetControllerForItemId(pToolBox->GetCurItemId()));
+ if (xController.is())
+ {
+ Reference<awt::XWindow> xWindow = xController->createPopupWindow();
+ if (xWindow.is() )
+ xWindow->setFocus();
+ }
+ }
+}
+
+IMPL_LINK(SidebarToolBox, ClickHandler, ToolBox*, pToolBox, void)
+{
+ if (pToolBox == nullptr)
+ return;
+
+ Reference<frame::XToolbarController> xController (GetControllerForItemId(pToolBox->GetCurItemId()));
+ if (xController.is())
+ xController->click();
+}
+
+IMPL_LINK(SidebarToolBox, DoubleClickHandler, ToolBox*, pToolBox, void)
+{
+ if (pToolBox == nullptr)
+ return;
+
+ Reference<frame::XToolbarController> xController (GetControllerForItemId(pToolBox->GetCurItemId()));
+ if (xController.is())
+ xController->doubleClick();
+}
+
+IMPL_LINK(SidebarToolBox, SelectHandler, ToolBox*, pToolBox, void)
+{
+ if (pToolBox == nullptr)
+ return;
+
+ Reference<frame::XToolbarController> xController (GetControllerForItemId(pToolBox->GetCurItemId()));
+ if (xController.is())
+ xController->execute(static_cast<sal_Int16>(pToolBox->GetModifier()));
+}
+
+IMPL_LINK_NOARG(SidebarToolBox, ChangedIconSizeHandler, LinkParamNone*, void)
+{
+ SolarMutexGuard g;
+
+ if (mbUseDefaultButtonSize)
+ SetToolboxButtonSize(GetDefaultButtonSize());
+
+ for (auto const& it : maControllers)
+ {
+ Reference<frame::XSubToolbarController> xController(it.second, UNO_QUERY);
+ if (xController.is() && xController->opensSubToolbar())
+ {
+ // The button should show the last function that was selected from the
+ // dropdown. The controller should know better than us what it was.
+ xController->updateImage();
+ }
+ else if (SfxViewFrame::Current())
+ {
+ OUString aCommandURL = GetItemCommand(it.first);
+ css::uno::Reference<frame::XFrame> xFrame = SfxViewFrame::Current()->GetFrame().GetFrameInterface();
+ Image aImage = vcl::CommandInfoProvider::GetImageForCommand(aCommandURL, xFrame, GetImageSize());
+ SetItemImage(it.first, aImage);
+ }
+ }
+
+ Resize();
+ queue_resize();
+}
+
+void SidebarToolBox::InitToolBox(VclBuilder::stringmap& rMap)
+{
+ for (const auto& it : rMap)
+ {
+ if (it.first == "toolbar-style")
+ {
+ if (it.second == "text")
+ SetButtonType(ButtonType::TEXT);
+ else if (it.second == "both-horiz")
+ SetButtonType(ButtonType::SYMBOLTEXT);
+ else if (it.second == "both")
+ {
+ SetButtonType(ButtonType::SYMBOLTEXT);
+ SetToolBoxTextPosition(ToolBoxTextPosition::Bottom);
+ }
+ }
+ else if (it.first == "icon-size")
+ {
+ mbUseDefaultButtonSize = false;
+ if (it.second == "1" || it.second == "2" || it.second == "4")
+ SetToolboxButtonSize(ToolBoxButtonSize::Small);
+ else if (it.second == "3")
+ SetToolboxButtonSize(ToolBoxButtonSize::Large);
+ else if (it.second == "5")
+ SetToolboxButtonSize(ToolBoxButtonSize::Size32);
+ }
+ else if (it.first == "orientation" && it.second == "vertical")
+ SetAlign(WindowAlign::Left);
+ }
+}
+
+namespace {
+
+class NotebookbarToolBox : public SidebarToolBox
+{
+public:
+ explicit NotebookbarToolBox(vcl::Window* pParentWindow)
+ : SidebarToolBox(pParentWindow)
+ {
+ mbSideBar = false;
+ SetToolboxButtonSize(GetDefaultButtonSize());
+ }
+
+ virtual ToolBoxButtonSize GetDefaultButtonSize() const override
+ {
+ return static_cast<ToolBoxButtonSize>(officecfg::Office::Common::Misc::NotebookbarIconSize::get());
+ }
+};
+
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT void makeNotebookbarToolBox(VclPtr<vcl::Window> & rRet, const VclPtr<vcl::Window> & pParent, VclBuilder::stringmap & rMap)
+{
+ static_assert(std::is_same_v<std::remove_pointer_t<VclBuilder::customMakeWidget>,
+ decltype(makeNotebookbarToolBox)>);
+ VclPtrInstance<NotebookbarToolBox> pBox(pParent);
+ pBox->InitToolBox(rMap);
+ rRet = pBox;
+}
+
+} // end of namespace sfx2::sidebar
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/sidebar/TabBar.cxx b/sfx2/source/sidebar/TabBar.cxx
new file mode 100644
index 000000000..59649ec15
--- /dev/null
+++ b/sfx2/source/sidebar/TabBar.cxx
@@ -0,0 +1,376 @@
+/* -*- 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/sidebar/TabBar.hxx>
+#include <sidebar/DeckDescriptor.hxx>
+#include <sfx2/sidebar/Theme.hxx>
+#include <sidebar/Tools.hxx>
+#include <sfx2/sidebar/FocusManager.hxx>
+#include <sfx2/sidebar/SidebarController.hxx>
+
+#include <comphelper/lok.hxx>
+#include <comphelper/processfactory.hxx>
+#include <o3tl/safeint.hxx>
+#include <vcl/commandevent.hxx>
+#include <vcl/commandinfoprovider.hxx>
+#include <vcl/event.hxx>
+#include <vcl/svapp.hxx>
+#include <svtools/acceleratorexecute.hxx>
+#include <osl/diagnose.h>
+
+using namespace css;
+using namespace css::uno;
+
+static int gDefaultWidth;
+
+namespace sfx2::sidebar {
+
+TabBar::TabBar(vcl::Window* pParentWindow,
+ const Reference<frame::XFrame>& rxFrame,
+ const std::function<void (const OUString&)>& rDeckActivationFunctor,
+ const PopupMenuProvider& rPopupMenuProvider,
+ SidebarController* rParentSidebarController
+ )
+ : InterimItemWindow(pParentWindow, "sfx/ui/tabbar.ui", "TabBar")
+ , mxFrame(rxFrame)
+ , mxAuxBuilder(Application::CreateBuilder(m_xContainer.get(), "sfx/ui/tabbarcontents.ui"))
+ , mxTempToplevel(mxAuxBuilder->weld_box("toplevel"))
+ , mxContents(mxAuxBuilder->weld_widget("TabBarContents"))
+ , mxMenuButton(mxAuxBuilder->weld_menu_button("menubutton"))
+ , mxMainMenu(mxAuxBuilder->weld_menu("mainmenu"))
+ , mxSubMenu(mxAuxBuilder->weld_menu("submenu"))
+ , mxMeasureBox(mxAuxBuilder->weld_widget("measure"))
+ , maDeckActivationFunctor(rDeckActivationFunctor)
+ , maPopupMenuProvider(rPopupMenuProvider)
+ , pParentSidebarController(rParentSidebarController)
+{
+ InitControlBase(mxMenuButton.get());
+
+ mxTempToplevel->move(mxContents.get(), m_xContainer.get());
+
+ gDefaultWidth = m_xContainer->get_preferred_size().Width();
+
+ // we have this widget just so we can measure best width for static TabBar::GetDefaultWidth
+ mxMeasureBox->hide();
+
+ SetBackground(Wallpaper(Theme::GetColor(Theme::Color_TabBarBackground)));
+
+ mxMenuButton->connect_toggled(LINK(this, TabBar, OnToolboxClicked));
+
+#ifdef DEBUG
+ SetText(OUString("TabBar"));
+#endif
+}
+
+TabBar::~TabBar()
+{
+ disposeOnce();
+}
+
+void TabBar::dispose()
+{
+ m_xContainer->move(mxContents.get(), mxTempToplevel.get());
+ maItems.clear();
+ mxMeasureBox.reset();
+ mxSubMenu.reset();
+ mxMainMenu.reset();
+ mxMenuButton.reset();
+ mxAuxBuilder.reset();
+ InterimItemWindow::dispose();
+}
+
+sal_Int32 TabBar::GetDefaultWidth()
+{
+ if (!gDefaultWidth)
+ {
+ std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(nullptr, "sfx/ui/tabbarcontents.ui"));
+ std::unique_ptr<weld::Widget> xContainer(xBuilder->weld_widget("TabBarContents"));
+ gDefaultWidth = xContainer->get_preferred_size().Width();
+ }
+ return gDefaultWidth;
+}
+
+void TabBar::SetDecks(const ResourceManager::DeckContextDescriptorContainer& rDecks)
+{
+ // invisible with LOK, so keep empty to avoid invalidations
+ if (comphelper::LibreOfficeKit::isActive())
+ return;
+
+ // Remove the current buttons.
+ maItems.clear();
+ for (auto const& deck : rDecks)
+ {
+ std::shared_ptr<DeckDescriptor> xDescriptor = pParentSidebarController->GetResourceManager()->GetDeckDescriptor(deck.msId);
+ if (xDescriptor == nullptr)
+ {
+ OSL_ASSERT(xDescriptor!=nullptr);
+ continue;
+ }
+
+ maItems.emplace_back(std::make_unique<Item>(*this));
+ auto& xItem(maItems.back());
+ xItem->msDeckId = xDescriptor->msId;
+ CreateTabItem(*xItem->mxButton, *xDescriptor);
+ xItem->mxButton->connect_clicked(LINK(xItem.get(), TabBar::Item, HandleClick));
+ xItem->maDeckActivationFunctor = maDeckActivationFunctor;
+ xItem->mbIsHidden = !xDescriptor->mbIsEnabled;
+ xItem->mbIsHiddenByDefault = xItem->mbIsHidden; // the default is the state while creating
+
+ xItem->mxButton->set_sensitive(deck.mbIsEnabled);
+ }
+
+ UpdateButtonIcons();
+}
+
+void TabBar::UpdateButtonIcons()
+{
+ for (auto const& item : maItems)
+ {
+ std::shared_ptr<DeckDescriptor> xDeckDescriptor = pParentSidebarController->GetResourceManager()->GetDeckDescriptor(item->msDeckId);
+ if (!xDeckDescriptor)
+ continue;
+ item->mxButton->set_item_image("toggle", GetItemImage(*xDeckDescriptor));
+ }
+}
+
+void TabBar::HighlightDeck(std::u16string_view rsDeckId)
+{
+ for (auto const& item : maItems)
+ item->mxButton->set_item_active("toggle", item->msDeckId == rsDeckId);
+}
+
+void TabBar::RemoveDeckHighlight()
+{
+ for (auto const& item : maItems)
+ item->mxButton->set_item_active("toggle", false);
+}
+
+void TabBar::DataChanged(const DataChangedEvent& rDataChangedEvent)
+{
+ SetBackground(Theme::GetColor(Theme::Color_TabBarBackground));
+ UpdateButtonIcons();
+
+ InterimItemWindow::DataChanged(rDataChangedEvent);
+}
+
+bool TabBar::EventNotify(NotifyEvent& rEvent)
+{
+ MouseNotifyEvent nType = rEvent.GetType();
+ if(MouseNotifyEvent::KEYINPUT == nType)
+ {
+ const vcl::KeyCode& rKeyCode = rEvent.GetKeyEvent()->GetKeyCode();
+ if (!mpAccel)
+ {
+ mpAccel = svt::AcceleratorExecute::createAcceleratorHelper();
+ mpAccel->init(comphelper::getProcessComponentContext(), mxFrame);
+ }
+ const OUString aCommand(mpAccel->findCommand(svt::AcceleratorExecute::st_VCLKey2AWTKey(rKeyCode)));
+ if (".uno:Sidebar" == aCommand ||
+ (rKeyCode.IsMod1() && rKeyCode.IsShift() && rKeyCode.GetCode() == KEY_F10))
+ return InterimItemWindow::EventNotify(rEvent);
+ return true;
+ }
+ else if(MouseNotifyEvent::COMMAND == nType)
+ {
+ const CommandEvent& rCommandEvent = *rEvent.GetCommandEvent();
+ if(rCommandEvent.GetCommand() == CommandEventId::Wheel)
+ {
+ const CommandWheelData* pData = rCommandEvent.GetWheelData();
+ if(!pData->GetModifier() && (pData->GetMode() == CommandWheelMode::SCROLL))
+ {
+ auto pItem = std::find_if(maItems.begin(), maItems.end(),
+ [] (const auto& item) { return item->mxButton->get_item_active("toggle"); });
+ if(pItem == maItems.end())
+ return true;
+ if(pData->GetNotchDelta()<0)
+ {
+ if(pItem+1 == maItems.end())
+ return true;
+ ++pItem;
+ }
+ else
+ {
+ if(pItem == maItems.begin())
+ return true;
+ --pItem;
+ }
+ try
+ {
+ (*pItem)->maDeckActivationFunctor((*pItem)->msDeckId);
+ }
+ catch(const css::uno::Exception&) {};
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+void TabBar::CreateTabItem(weld::Toolbar& rItem, const DeckDescriptor& rDeckDescriptor)
+{
+ rItem.set_accessible_name(rDeckDescriptor.msTitle);
+ rItem.set_accessible_description(rDeckDescriptor.msHelpText);
+ rItem.set_tooltip_text(rDeckDescriptor.msHelpText);
+ const OUString sCommand = ".uno:SidebarDeck." + rDeckDescriptor.msId;
+ OUString sShortcut = vcl::CommandInfoProvider::GetCommandShortcut(sCommand, mxFrame);
+ if (!sShortcut.isEmpty())
+ sShortcut = u" (" + sShortcut + u")";
+ rItem.set_item_tooltip_text("toggle", rDeckDescriptor.msHelpText + sShortcut);
+}
+
+css::uno::Reference<css::graphic::XGraphic> TabBar::GetItemImage(const DeckDescriptor& rDeckDescriptor) const
+{
+ return Tools::GetImage(
+ rDeckDescriptor.msIconURL,
+ rDeckDescriptor.msHighContrastIconURL,
+ mxFrame);
+}
+
+TabBar::Item::Item(TabBar& rTabBar)
+ : mrTabBar(rTabBar)
+ , mxBuilder(Application::CreateBuilder(rTabBar.GetContainer(), "sfx/ui/tabbutton.ui"))
+ , mxButton(mxBuilder->weld_toolbar("button"))
+ , mbIsHidden(false)
+ , mbIsHiddenByDefault(false)
+{
+}
+
+TabBar::Item::~Item()
+{
+ mrTabBar.GetContainer()->move(mxButton.get(), nullptr);
+}
+
+IMPL_LINK_NOARG(TabBar::Item, HandleClick, const OString&, void)
+{
+ // tdf#143146 copy the functor and arg before calling
+ // GrabFocusToDocument which may destroy this object
+ auto aDeckActivationFunctor = maDeckActivationFunctor;
+ auto sDeckId = msDeckId;
+
+ mrTabBar.GrabFocusToDocument();
+ try
+ {
+ aDeckActivationFunctor(sDeckId);
+ }
+ catch(const css::uno::Exception&)
+ {} // workaround for #i123198#
+}
+
+OUString const & TabBar::GetDeckIdForIndex (const sal_Int32 nIndex) const
+{
+ if (nIndex<0 || o3tl::make_unsigned(nIndex)>=maItems.size())
+ throw RuntimeException();
+ return maItems[nIndex]->msDeckId;
+}
+
+void TabBar::ToggleHideFlag (const sal_Int32 nIndex)
+{
+ if (nIndex<0 || o3tl::make_unsigned(nIndex) >= maItems.size())
+ throw RuntimeException();
+
+ maItems[nIndex]->mbIsHidden = ! maItems[nIndex]->mbIsHidden;
+
+ std::shared_ptr<DeckDescriptor> xDeckDescriptor = pParentSidebarController->GetResourceManager()->GetDeckDescriptor(maItems[nIndex]->msDeckId);
+ if (xDeckDescriptor)
+ {
+ xDeckDescriptor->mbIsEnabled = ! maItems[nIndex]->mbIsHidden;
+
+ Context aContext;
+ aContext.msApplication = pParentSidebarController->GetCurrentContext().msApplication;
+ // leave aContext.msContext on default 'any' ... this func is used only for decks
+ // and we don't have context-sensitive decks anyway
+
+ xDeckDescriptor->maContextList.ToggleVisibilityForContext(
+ aContext, xDeckDescriptor->mbIsEnabled );
+ }
+}
+
+void TabBar::RestoreHideFlags()
+{
+ for (auto & item : maItems)
+ {
+ if (item->mbIsHidden != item->mbIsHiddenByDefault)
+ {
+ item->mbIsHidden = item->mbIsHiddenByDefault;
+ std::shared_ptr<DeckDescriptor> xDeckDescriptor = pParentSidebarController->GetResourceManager()->GetDeckDescriptor(item->msDeckId);
+ if (xDeckDescriptor)
+ xDeckDescriptor->mbIsEnabled = !item->mbIsHidden;
+
+ }
+ }
+}
+
+void TabBar::UpdateFocusManager(FocusManager& rFocusManager)
+{
+ std::vector<weld::Widget*> aButtons;
+ aButtons.reserve(maItems.size()+1);
+ aButtons.push_back(mxMenuButton.get());
+ for (auto const& item : maItems)
+ {
+ aButtons.push_back(item->mxButton.get());
+ }
+ rFocusManager.SetButtons(aButtons);
+}
+
+IMPL_LINK_NOARG(TabBar, OnToolboxClicked, weld::Toggleable&, void)
+{
+ if (!mxMenuButton->get_active())
+ return;
+
+ std::vector<DeckMenuData> aMenuData;
+
+ for (auto const& item : maItems)
+ {
+ std::shared_ptr<DeckDescriptor> xDeckDescriptor = pParentSidebarController->GetResourceManager()->GetDeckDescriptor(item->msDeckId);
+
+ if (!xDeckDescriptor)
+ continue;
+
+ DeckMenuData aData;
+ aData.msDisplayName = xDeckDescriptor->msTitle;
+ aData.mbIsCurrentDeck = item->mxButton->get_item_active("toggle");
+ aData.mbIsActive = !item->mbIsHidden;
+ aData.mbIsEnabled = item->mxButton->get_sensitive();
+ aMenuData.push_back(aData);
+ }
+
+ for (int i = mxMainMenu->n_children() - 1; i >= 0; --i)
+ {
+ OString sIdent = mxMainMenu->get_id(i);
+ if (sIdent.startsWith("select"))
+ mxMainMenu->remove(sIdent);
+ }
+ for (int i = mxSubMenu->n_children() - 1; i >= 0; --i)
+ {
+ OString sIdent = mxSubMenu->get_id(i);
+ if (sIdent.indexOf("customize") != -1)
+ mxSubMenu->remove(sIdent);
+ }
+
+ maPopupMenuProvider(*mxMainMenu, *mxSubMenu, aMenuData);
+}
+
+void TabBar::EnableMenuButton(const bool bEnable)
+{
+ mxMenuButton->set_sensitive(bEnable);
+}
+
+} // end of namespace sfx2::sidebar
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/sidebar/Theme.cxx b/sfx2/source/sidebar/Theme.cxx
new file mode 100644
index 000000000..b97587767
--- /dev/null
+++ b/sfx2/source/sidebar/Theme.cxx
@@ -0,0 +1,675 @@
+/* -*- 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/sidebar/Theme.hxx>
+#include <sfx2/app.hxx>
+
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <tools/diagnose_ex.h>
+
+using namespace css;
+using namespace css::uno;
+
+namespace sfx2::sidebar {
+
+Theme& Theme::GetCurrentTheme()
+{
+ OSL_ASSERT(SfxGetpApp());
+ return SfxGetpApp()->GetSidebarTheme();
+}
+
+Theme::Theme()
+ : mbIsHighContrastMode(Application::GetSettings().GetStyleSettings().GetHighContrastMode()),
+ mbIsHighContrastModeSetManually(false)
+{
+ SetupPropertyMaps();
+}
+
+Theme::~Theme()
+{
+}
+
+Color Theme::GetColor (const ThemeItem eItem)
+{
+ const PropertyType eType (GetPropertyType(eItem));
+ OSL_ASSERT(eType==PT_Color);
+ const sal_Int32 nIndex (GetIndex(eItem, eType));
+ const Theme& rTheme (GetCurrentTheme());
+ if (eType == PT_Color)
+ return rTheme.maColors[nIndex];
+ else
+ return COL_WHITE;
+}
+
+sal_Int32 Theme::GetInteger (const ThemeItem eItem)
+{
+ const PropertyType eType (GetPropertyType(eItem));
+ OSL_ASSERT(eType==PT_Integer);
+ const sal_Int32 nIndex (GetIndex(eItem, eType));
+ const Theme& rTheme (GetCurrentTheme());
+ return rTheme.maIntegers[nIndex];
+}
+
+bool Theme::IsHighContrastMode()
+{
+ const Theme& rTheme (GetCurrentTheme());
+ return rTheme.mbIsHighContrastMode;
+}
+
+void Theme::HandleDataChange()
+{
+ Theme& rTheme (GetCurrentTheme());
+
+ if ( ! rTheme.mbIsHighContrastModeSetManually)
+ {
+ // Do not modify mbIsHighContrastMode when it was manually set.
+ GetCurrentTheme().mbIsHighContrastMode = Application::GetSettings().GetStyleSettings().GetHighContrastMode();
+ rTheme.maRawValues[Bool_IsHighContrastModeActive] <<= GetCurrentTheme().mbIsHighContrastMode;
+ }
+
+ GetCurrentTheme().UpdateTheme();
+}
+
+void Theme::InitializeTheme()
+{
+ setPropertyValue(
+ maPropertyIdToNameMap[Bool_UseSystemColors],
+ Any(false));
+}
+
+void Theme::UpdateTheme()
+{
+ try
+ {
+ const StyleSettings& rStyle (Application::GetSettings().GetStyleSettings());
+
+ Color aBaseBackgroundColor (rStyle.GetDialogColor());
+ // UX says this should be a little brighter, but that looks off when compared to the other windows.
+ //aBaseBackgroundColor.IncreaseLuminance(7);
+ Color aSecondColor (aBaseBackgroundColor);
+ aSecondColor.DecreaseLuminance(15);
+
+ setPropertyValue(
+ maPropertyIdToNameMap[Color_DeckBackground],
+ Any(sal_Int32(aBaseBackgroundColor.GetRGBColor())));
+
+ setPropertyValue(
+ maPropertyIdToNameMap[Color_DeckTitleBarBackground],
+ Any(sal_Int32(aBaseBackgroundColor.GetRGBColor())));
+ setPropertyValue(
+ maPropertyIdToNameMap[Int_DeckSeparatorHeight],
+ Any(sal_Int32(1)));
+ setPropertyValue(
+ maPropertyIdToNameMap[Color_PanelBackground],
+ Any(sal_Int32(aBaseBackgroundColor.GetRGBColor())));
+
+ setPropertyValue(
+ maPropertyIdToNameMap[Color_PanelTitleBarBackground],
+ Any(sal_Int32(aSecondColor.GetRGBColor())));
+ setPropertyValue(
+ maPropertyIdToNameMap[Color_TabBarBackground],
+ Any(sal_Int32(aBaseBackgroundColor.GetRGBColor())));
+
+ setPropertyValue(
+ maPropertyIdToNameMap[Color_Highlight],
+ Any(sal_Int32(rStyle.GetHighlightColor().GetRGBColor())));
+ setPropertyValue(
+ maPropertyIdToNameMap[Color_HighlightText],
+ Any(sal_Int32(rStyle.GetHighlightTextColor().GetRGBColor())));
+ }
+ catch(beans::UnknownPropertyException const &)
+ {
+ DBG_UNHANDLED_EXCEPTION("sfx", "unknown property");
+ OSL_ASSERT(false);
+ }
+}
+
+void Theme::disposing(std::unique_lock<std::mutex>&)
+{
+ SolarMutexGuard aGuard;
+
+ ChangeListeners aListeners;
+ aListeners.swap(maChangeListeners);
+
+ const lang::EventObject aEvent (static_cast<XWeak*>(this));
+
+ for (const auto& rContainer : aListeners)
+ {
+ for (const auto& rxListener : rContainer.second)
+ {
+ try
+ {
+ rxListener->disposing(aEvent);
+ }
+ catch(const Exception&)
+ {
+ }
+ }
+ }
+}
+
+Reference<beans::XPropertySet> Theme::GetPropertySet()
+{
+ if (SfxGetpApp())
+ return Reference<beans::XPropertySet>(&GetCurrentTheme());
+ else
+ return Reference<beans::XPropertySet>();
+}
+
+Reference<beans::XPropertySetInfo> SAL_CALL Theme::getPropertySetInfo()
+{
+ return Reference<beans::XPropertySetInfo>(this);
+}
+
+void SAL_CALL Theme::setPropertyValue (
+ const OUString& rsPropertyName,
+ const css::uno::Any& rValue)
+{
+ SolarMutexGuard aGuard;
+
+ PropertyNameToIdMap::const_iterator iId (maPropertyNameToIdMap.find(rsPropertyName));
+ if (iId == maPropertyNameToIdMap.end())
+ throw beans::UnknownPropertyException(rsPropertyName);
+
+ const PropertyType eType (GetPropertyType(iId->second));
+ if (eType == PT_Invalid)
+ throw beans::UnknownPropertyException(rsPropertyName);
+
+ const ThemeItem eItem (iId->second);
+
+ if (rValue == maRawValues[eItem])
+ {
+ // Value is not different from the one in the property
+ // set => nothing to do.
+ return;
+ }
+
+ const Any aOldValue (maRawValues[eItem]);
+
+ const beans::PropertyChangeEvent aEvent(
+ static_cast<XWeak*>(this),
+ rsPropertyName,
+ false,
+ eItem,
+ aOldValue,
+ rValue);
+
+ if (DoVetoableListenersVeto(GetVetoableListeners(AnyItem_, false), aEvent))
+ return;
+ if (DoVetoableListenersVeto(GetVetoableListeners(eItem, false), aEvent))
+ return;
+
+ maRawValues[eItem] = rValue;
+ ProcessNewValue(rValue, eItem, eType);
+
+ BroadcastPropertyChange(GetChangeListeners(AnyItem_, false), aEvent);
+ BroadcastPropertyChange(GetChangeListeners(eItem, false), aEvent);
+}
+
+Any SAL_CALL Theme::getPropertyValue (
+ const OUString& rsPropertyName)
+{
+ SolarMutexGuard aGuard;
+
+ PropertyNameToIdMap::const_iterator iId (maPropertyNameToIdMap.find(rsPropertyName));
+ if (iId == maPropertyNameToIdMap.end())
+ throw beans::UnknownPropertyException(rsPropertyName);
+
+ const PropertyType eType (GetPropertyType(iId->second));
+ if (eType == PT_Invalid)
+ throw beans::UnknownPropertyException(rsPropertyName);
+
+ const ThemeItem eItem (iId->second);
+
+ return maRawValues[eItem];
+}
+
+void SAL_CALL Theme::addPropertyChangeListener(
+ const OUString& rsPropertyName,
+ const css::uno::Reference<css::beans::XPropertyChangeListener>& rxListener)
+{
+ SolarMutexGuard aGuard;
+
+ ThemeItem eItem (AnyItem_);
+ if (rsPropertyName.getLength() > 0)
+ {
+ PropertyNameToIdMap::const_iterator iId (maPropertyNameToIdMap.find(rsPropertyName));
+ if (iId == maPropertyNameToIdMap.end())
+ throw beans::UnknownPropertyException(rsPropertyName);
+
+ const PropertyType eType (GetPropertyType(iId->second));
+ if (eType == PT_Invalid)
+ throw beans::UnknownPropertyException(rsPropertyName);
+
+ eItem = iId->second;
+ }
+ ChangeListenerContainer* pListeners = GetChangeListeners(eItem, true);
+ if (pListeners != nullptr)
+ pListeners->push_back(rxListener);
+}
+
+void SAL_CALL Theme::removePropertyChangeListener(
+ const OUString& rsPropertyName,
+ const css::uno::Reference<css::beans::XPropertyChangeListener>& rxListener)
+{
+ SolarMutexGuard aGuard;
+
+ ThemeItem eItem (AnyItem_);
+ if (rsPropertyName.getLength() > 0)
+ {
+ PropertyNameToIdMap::const_iterator iId (maPropertyNameToIdMap.find(rsPropertyName));
+ if (iId == maPropertyNameToIdMap.end())
+ throw beans::UnknownPropertyException(rsPropertyName);
+
+ const PropertyType eType (GetPropertyType(iId->second));
+ if (eType == PT_Invalid)
+ throw beans::UnknownPropertyException(rsPropertyName);
+
+ eItem = iId->second;
+ }
+ ChangeListenerContainer* pContainer = GetChangeListeners(eItem, false);
+ if (pContainer != nullptr)
+ {
+ ChangeListenerContainer::iterator iListener (::std::find(pContainer->begin(), pContainer->end(), rxListener));
+ if (iListener != pContainer->end())
+ {
+ pContainer->erase(iListener);
+
+ // Remove the listener container when empty.
+ if (pContainer->empty())
+ maChangeListeners.erase(eItem);
+ }
+ }
+}
+
+void SAL_CALL Theme::addVetoableChangeListener(
+ const OUString& rsPropertyName,
+ const css::uno::Reference<css::beans::XVetoableChangeListener>& rxListener)
+{
+ SolarMutexGuard aGuard;
+
+ ThemeItem eItem (AnyItem_);
+ if (rsPropertyName.getLength() > 0)
+ {
+ PropertyNameToIdMap::const_iterator iId (maPropertyNameToIdMap.find(rsPropertyName));
+ if (iId == maPropertyNameToIdMap.end())
+ throw beans::UnknownPropertyException(rsPropertyName);
+
+ const PropertyType eType (GetPropertyType(iId->second));
+ if (eType == PT_Invalid)
+ throw beans::UnknownPropertyException(rsPropertyName);
+
+ eItem = iId->second;
+ }
+ VetoableListenerContainer* pListeners = GetVetoableListeners(eItem, true);
+ if (pListeners != nullptr)
+ pListeners->push_back(rxListener);
+}
+
+void SAL_CALL Theme::removeVetoableChangeListener(
+ const OUString& rsPropertyName,
+ const css::uno::Reference<css::beans::XVetoableChangeListener>& rxListener)
+{
+ SolarMutexGuard aGuard;
+
+ ThemeItem eItem (AnyItem_);
+ if (rsPropertyName.getLength() > 0)
+ {
+ PropertyNameToIdMap::const_iterator iId (maPropertyNameToIdMap.find(rsPropertyName));
+ if (iId == maPropertyNameToIdMap.end())
+ throw beans::UnknownPropertyException(rsPropertyName);
+
+ const PropertyType eType (GetPropertyType(iId->second));
+ if (eType == PT_Invalid)
+ throw beans::UnknownPropertyException(rsPropertyName);
+
+ eItem = iId->second;
+ }
+ VetoableListenerContainer* pContainer = GetVetoableListeners(eItem, false);
+ if (pContainer != nullptr)
+ {
+ VetoableListenerContainer::iterator iListener (::std::find(pContainer->begin(), pContainer->end(), rxListener));
+ if (iListener != pContainer->end())
+ {
+ pContainer->erase(iListener);
+ // Remove container when empty.
+ if (pContainer->empty())
+ maVetoableListeners.erase(eItem);
+ }
+ }
+}
+
+css::uno::Sequence<css::beans::Property> SAL_CALL Theme::getProperties()
+{
+ SolarMutexGuard aGuard;
+
+ ::std::vector<beans::Property> aProperties;
+
+ sal_Int32 const nEnd(End_);
+ for (sal_Int32 nItem(Begin_); nItem!=nEnd; ++nItem)
+ {
+ const ThemeItem eItem (static_cast<ThemeItem>(nItem));
+ const PropertyType eType (GetPropertyType(eItem));
+ if (eType == PT_Invalid)
+ continue;
+
+ const beans::Property aProperty(
+ maPropertyIdToNameMap[eItem],
+ eItem,
+ GetCppuType(eType),
+ 0);
+ aProperties.push_back(aProperty);
+ }
+
+ return css::uno::Sequence<css::beans::Property>(
+ aProperties.data(),
+ aProperties.size());
+}
+
+beans::Property SAL_CALL Theme::getPropertyByName (const OUString& rsPropertyName)
+{
+ SolarMutexGuard aGuard;
+
+ PropertyNameToIdMap::const_iterator iId (maPropertyNameToIdMap.find(rsPropertyName));
+ if (iId == maPropertyNameToIdMap.end())
+ throw beans::UnknownPropertyException(rsPropertyName);
+
+ const PropertyType eType (GetPropertyType(iId->second));
+ if (eType == PT_Invalid)
+ throw beans::UnknownPropertyException(rsPropertyName);
+
+ const ThemeItem eItem (iId->second);
+
+ return beans::Property(
+ rsPropertyName,
+ eItem,
+ GetCppuType(eType),
+ 0);
+}
+
+sal_Bool SAL_CALL Theme::hasPropertyByName (const OUString& rsPropertyName)
+{
+ SolarMutexGuard aGuard;
+
+ PropertyNameToIdMap::const_iterator iId (maPropertyNameToIdMap.find(rsPropertyName));
+ if (iId == maPropertyNameToIdMap.end())
+ return false;
+
+ const PropertyType eType (GetPropertyType(iId->second));
+ if (eType == PT_Invalid)
+ return false;
+
+ return true;
+}
+
+void Theme::SetupPropertyMaps()
+{
+ maPropertyIdToNameMap.resize(Post_Bool_);
+ maColors.resize(Color_Int_ - Pre_Color_ - 1);
+ maIntegers.resize(Int_Bool_ - Color_Int_ - 1);
+ maBooleans.resize(Post_Bool_ - Int_Bool_ - 1);
+
+ maPropertyNameToIdMap["Color_Highlight"]=Color_Highlight;
+ maPropertyIdToNameMap[Color_Highlight]="Color_Highlight";
+
+ maPropertyNameToIdMap["Color_HighlightText"]=Color_HighlightText;
+ maPropertyIdToNameMap[Color_HighlightText]="Color_HighlightText";
+
+
+ maPropertyNameToIdMap["Color_DeckBackground"]=Color_DeckBackground;
+ maPropertyIdToNameMap[Color_DeckBackground]="Color_DeckBackground";
+
+ maPropertyNameToIdMap["Color_DeckTitleBarBackground"]=Color_DeckTitleBarBackground;
+ maPropertyIdToNameMap[Color_DeckTitleBarBackground]="Color_DeckTitleBarBackground";
+
+ maPropertyNameToIdMap["Color_PanelBackground"]=Color_PanelBackground;
+ maPropertyIdToNameMap[Color_PanelBackground]="Color_PanelBackground";
+
+ maPropertyNameToIdMap["Color_PanelTitleBarBackground"]=Color_PanelTitleBarBackground;
+ maPropertyIdToNameMap[Color_PanelTitleBarBackground]="Color_PanelTitleBarBackground";
+
+ maPropertyNameToIdMap["Color_TabBarBackground"]=Color_TabBarBackground;
+ maPropertyIdToNameMap[Color_TabBarBackground]="Color_TabBarBackground";
+
+
+ maPropertyNameToIdMap["Int_DeckBorderSize"]=Int_DeckBorderSize;
+ maPropertyIdToNameMap[Int_DeckBorderSize]="Int_DeckBorderSize";
+
+ maPropertyNameToIdMap["Int_DeckSeparatorHeight"]=Int_DeckSeparatorHeight;
+ maPropertyIdToNameMap[Int_DeckSeparatorHeight]="Int_DeckSeparatorHeight";
+
+ maPropertyNameToIdMap["Int_DeckLeftPadding"]=Int_DeckLeftPadding;
+ maPropertyIdToNameMap[Int_DeckLeftPadding]="Int_DeckLeftPadding";
+
+ maPropertyNameToIdMap["Int_DeckTopPadding"]=Int_DeckTopPadding;
+ maPropertyIdToNameMap[Int_DeckTopPadding]="Int_DeckTopPadding";
+
+ maPropertyNameToIdMap["Int_DeckRightPadding"]=Int_DeckRightPadding;
+ maPropertyIdToNameMap[Int_DeckRightPadding]="Int_DeckRightPadding";
+
+ maPropertyNameToIdMap["Int_DeckBottomPadding"]=Int_DeckBottomPadding;
+ maPropertyIdToNameMap[Int_DeckBottomPadding]="Int_DeckBottomPadding";
+
+
+ maPropertyNameToIdMap["Bool_UseSystemColors"]=Bool_UseSystemColors;
+ maPropertyIdToNameMap[Bool_UseSystemColors]="Bool_UseSystemColors";
+
+ maPropertyNameToIdMap["Bool_IsHighContrastModeActive"]=Bool_IsHighContrastModeActive;
+ maPropertyIdToNameMap[Bool_IsHighContrastModeActive]="Bool_IsHighContrastModeActive";
+
+ maRawValues.resize(maPropertyIdToNameMap.size());
+}
+
+Theme::PropertyType Theme::GetPropertyType (const ThemeItem eItem)
+{
+ switch(eItem)
+ {
+ case Color_Highlight:
+ case Color_HighlightText:
+ case Color_DeckBackground:
+ case Color_DeckTitleBarBackground:
+ case Color_PanelBackground:
+ case Color_PanelTitleBarBackground:
+ case Color_TabBarBackground:
+ return PT_Color;
+
+ case Int_DeckBorderSize:
+ case Int_DeckSeparatorHeight:
+ case Int_DeckLeftPadding:
+ case Int_DeckTopPadding:
+ case Int_DeckRightPadding:
+ case Int_DeckBottomPadding:
+ return PT_Integer;
+
+ case Bool_UseSystemColors:
+ case Bool_IsHighContrastModeActive:
+ return PT_Boolean;
+
+ default:
+ return PT_Invalid;
+ }
+}
+
+css::uno::Type const & Theme::GetCppuType (const PropertyType eType)
+{
+ switch(eType)
+ {
+ case PT_Color:
+ return cppu::UnoType<sal_uInt32>::get();
+
+ case PT_Integer:
+ return cppu::UnoType<sal_Int32>::get();
+
+ case PT_Boolean:
+ return cppu::UnoType<sal_Bool>::get();
+
+ case PT_Invalid:
+ default:
+ return cppu::UnoType<void>::get();
+ }
+}
+
+sal_Int32 Theme::GetIndex (const ThemeItem eItem, const PropertyType eType)
+{
+ switch(eType)
+ {
+ case PT_Color:
+ return eItem - Pre_Color_-1;
+ case PT_Integer:
+ return eItem - Color_Int_-1;
+ case PT_Boolean:
+ return eItem - Int_Bool_-1;
+ default:
+ OSL_ASSERT(false);
+ return 0;
+ }
+}
+
+Theme::VetoableListenerContainer* Theme::GetVetoableListeners (
+ const ThemeItem eItem,
+ const bool bCreate)
+{
+ VetoableListeners::iterator iContainer (maVetoableListeners.find(eItem));
+ if (iContainer != maVetoableListeners.end())
+ return &iContainer->second;
+ else if (bCreate)
+ {
+ maVetoableListeners[eItem] = VetoableListenerContainer();
+ return &maVetoableListeners[eItem];
+ }
+ else
+ return nullptr;
+}
+
+Theme::ChangeListenerContainer* Theme::GetChangeListeners (
+ const ThemeItem eItem,
+ const bool bCreate)
+{
+ ChangeListeners::iterator iContainer (maChangeListeners.find(eItem));
+ if (iContainer != maChangeListeners.end())
+ return &iContainer->second;
+ else if (bCreate)
+ {
+ maChangeListeners[eItem] = ChangeListenerContainer();
+ return &maChangeListeners[eItem];
+ }
+ else
+ return nullptr;
+}
+
+bool Theme::DoVetoableListenersVeto (
+ const VetoableListenerContainer* pListeners,
+ const beans::PropertyChangeEvent& rEvent)
+{
+ if (pListeners == nullptr)
+ return false;
+
+ VetoableListenerContainer aListeners (*pListeners);
+ try
+ {
+ for (const auto& rxListener : aListeners)
+ {
+ rxListener->vetoableChange(rEvent);
+ }
+ }
+ catch(const beans::PropertyVetoException&)
+ {
+ return true;
+ }
+ catch(const Exception&)
+ {
+ // Ignore any other errors (such as disposed listeners).
+ }
+ return false;
+}
+
+void Theme::BroadcastPropertyChange (
+ const ChangeListenerContainer* pListeners,
+ const beans::PropertyChangeEvent& rEvent)
+{
+ if (pListeners == nullptr)
+ return;
+
+ const ChangeListenerContainer aListeners (*pListeners);
+ try
+ {
+ for (const auto& rxListener : aListeners)
+ {
+ rxListener->propertyChange(rEvent);
+ }
+ }
+ catch(const Exception&)
+ {
+ // Ignore any errors (such as disposed listeners).
+ }
+}
+
+void Theme::ProcessNewValue (
+ const Any& rValue,
+ const ThemeItem eItem,
+ const PropertyType eType)
+{
+ const sal_Int32 nIndex (GetIndex (eItem, eType));
+ switch (eType)
+ {
+ case PT_Color:
+ {
+ Color nColorValue;
+ if (rValue >>= nColorValue)
+ maColors[nIndex] = nColorValue;
+ break;
+ }
+ case PT_Integer:
+ {
+ sal_Int32 nValue (0);
+ if (rValue >>= nValue)
+ {
+ maIntegers[nIndex] = nValue;
+ }
+ break;
+ }
+ case PT_Boolean:
+ {
+ bool bValue (false);
+ if (rValue >>= bValue)
+ {
+ maBooleans[nIndex] = bValue;
+ if (eItem == Bool_IsHighContrastModeActive)
+ {
+ mbIsHighContrastModeSetManually = true;
+ mbIsHighContrastMode = maBooleans[nIndex];
+ HandleDataChange();
+ }
+ else if (eItem == Bool_UseSystemColors)
+ {
+ HandleDataChange();
+ }
+ }
+ break;
+ }
+ case PT_Invalid:
+ OSL_ASSERT(eType != PT_Invalid);
+ throw RuntimeException();
+ }
+}
+
+} // end of namespace sfx2::sidebar
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/sidebar/TitleBar.cxx b/sfx2/source/sidebar/TitleBar.cxx
new file mode 100644
index 000000000..dd00d4217
--- /dev/null
+++ b/sfx2/source/sidebar/TitleBar.cxx
@@ -0,0 +1,80 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sidebar/TitleBar.hxx>
+
+namespace sfx2::sidebar {
+
+TitleBar::TitleBar(weld::Builder& rBuilder, Theme::ThemeItem eThemeItem)
+ : mrBuilder(rBuilder)
+ , mxTitlebar(rBuilder.weld_box("titlebar"))
+ , mxAddonImage(rBuilder.weld_image("addonimage"))
+ , mxToolBox(rBuilder.weld_toolbar("toolbar"))
+ , meThemeItem(eThemeItem)
+{
+ SetBackground();
+
+ mxToolBox->connect_clicked(LINK(this, TitleBar, SelectionHandler));
+}
+
+void TitleBar::SetBackground()
+{
+ Color aColor(Theme::GetColor(meThemeItem));
+ mxTitlebar->set_background(aColor);
+ mxToolBox->set_background(aColor);
+}
+
+void TitleBar::DataChanged()
+{
+ SetBackground();
+}
+
+TitleBar::~TitleBar()
+{
+}
+
+Size TitleBar::get_preferred_size() const
+{
+ return mxTitlebar->get_preferred_size();
+}
+
+void TitleBar::Show(bool bShow)
+{
+ mxTitlebar->set_visible(bShow);
+}
+
+bool TitleBar::GetVisible() const
+{
+ return mxTitlebar->get_visible();
+}
+
+void TitleBar::SetIcon(const css::uno::Reference<css::graphic::XGraphic>& rIcon)
+{
+ mxAddonImage->set_image(rIcon);
+ mxAddonImage->set_visible(rIcon.is());
+}
+
+IMPL_LINK_NOARG(TitleBar, SelectionHandler, const OString&, void)
+{
+ HandleToolBoxItemClick();
+}
+
+} // end of namespace sfx2::sidebar
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/sidebar/Tools.cxx b/sfx2/source/sidebar/Tools.cxx
new file mode 100644
index 000000000..727e85a4f
--- /dev/null
+++ b/sfx2/source/sidebar/Tools.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 <sidebar/Tools.hxx>
+
+#include <sfx2/sidebar/Theme.hxx>
+
+#include <comphelper/namedvaluecollection.hxx>
+#include <comphelper/processfactory.hxx>
+#include <vcl/commandinfoprovider.hxx>
+
+#include <com/sun/star/frame/XDispatchProvider.hpp>
+#include <com/sun/star/frame/XFrame.hpp>
+#include <com/sun/star/util/URLTransformer.hpp>
+#include <com/sun/star/frame/ModuleManager.hpp>
+#include <com/sun/star/graphic/GraphicProvider.hpp>
+
+using namespace css;
+using namespace css::uno;
+
+namespace sfx2::sidebar {
+
+css::uno::Reference<css::graphic::XGraphic> Tools::GetImage(
+ const OUString& rsImageURL,
+ const OUString& rsHighContrastImageURL,
+ const Reference<frame::XFrame>& rxFrame)
+{
+ if (Theme::IsHighContrastMode() && !rsHighContrastImageURL.isEmpty())
+ return GetImage(rsHighContrastImageURL, rxFrame);
+ else
+ return GetImage(rsImageURL, rxFrame);
+}
+
+css::uno::Reference<css::graphic::XGraphic> Tools::GetImage(
+ const OUString& rsURL,
+ const Reference<frame::XFrame>& rxFrame)
+{
+ if (rsURL.getLength() > 0)
+ {
+ if (rsURL.startsWith(".uno:"))
+ return vcl::CommandInfoProvider::GetXGraphicForCommand(rsURL, rxFrame);
+
+ else
+ {
+ Reference<uno::XComponentContext> xContext(::comphelper::getProcessComponentContext());
+ Reference<graphic::XGraphicProvider> xProvider(graphic::GraphicProvider::create(xContext));
+ ::comphelper::NamedValueCollection aMediaProperties;
+ aMediaProperties.put("URL", rsURL);
+ return xProvider->queryGraphic(aMediaProperties.getPropertyValues());
+ }
+ }
+ return nullptr;
+}
+
+util::URL Tools::GetURL (const OUString& rsCommand)
+{
+ util::URL aURL;
+ aURL.Complete = rsCommand;
+
+ const Reference<XComponentContext> xComponentContext (::comphelper::getProcessComponentContext());
+ const Reference<util::XURLTransformer> xParser = util::URLTransformer::create( xComponentContext );
+ xParser->parseStrict(aURL);
+
+ return aURL;
+}
+
+Reference<frame::XDispatch> Tools::GetDispatch (
+ const css::uno::Reference<css::frame::XFrame>& rxFrame,
+ const util::URL& rURL)
+{
+ Reference<frame::XDispatchProvider> xProvider (rxFrame, UNO_QUERY_THROW);
+ Reference<frame::XDispatch> xDispatch (xProvider->queryDispatch(rURL, OUString(), 0));
+ return xDispatch;
+}
+
+OUString Tools::GetModuleName (
+ const css::uno::Reference<css::frame::XController>& rxController)
+{
+ if (!rxController.is())
+ return OUString();
+
+ try
+ {
+ const Reference<XComponentContext> xComponentContext (::comphelper::getProcessComponentContext());
+ const Reference<frame::XModuleManager> xModuleManager = frame::ModuleManager::create( xComponentContext );
+ return xModuleManager->identify(rxController);
+ }
+ catch (const Exception&)
+ {
+ // Ignored.
+ }
+ return OUString();
+}
+
+} // end of namespace sfx2::sidebar
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/sidebar/UnoDeck.cxx b/sfx2/source/sidebar/UnoDeck.cxx
new file mode 100644
index 000000000..deb4552b4
--- /dev/null
+++ b/sfx2/source/sidebar/UnoDeck.cxx
@@ -0,0 +1,281 @@
+/* -*- 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 <sidebar/UnoDeck.hxx>
+
+#include <sidebar/UnoPanels.hxx>
+
+#include <sfx2/sidebar/ResourceManager.hxx>
+#include <sfx2/sidebar/SidebarController.hxx>
+#include <sidebar/DeckTitleBar.hxx>
+#include <sfx2/sidebar/Deck.hxx>
+#include <sidebar/DeckDescriptor.hxx>
+
+#include <vcl/svapp.hxx>
+
+using namespace css;
+using namespace ::sfx2::sidebar;
+
+SfxUnoDeck::SfxUnoDeck(const uno::Reference<frame::XFrame>& rFrame, const OUString& deckId):
+xFrame(rFrame),
+mDeckId(deckId)
+{
+
+}
+SidebarController* SfxUnoDeck::getSidebarController()
+{
+ return SidebarController::GetSidebarControllerForFrame(xFrame);
+}
+
+OUString SAL_CALL SfxUnoDeck::getId()
+{
+ SolarMutexGuard aGuard;
+
+ return mDeckId;
+}
+
+OUString SAL_CALL SfxUnoDeck::getTitle()
+{
+ SolarMutexGuard aGuard;
+
+ SidebarController* pSidebarController = getSidebarController();
+ VclPtr<Deck> pDeck = pSidebarController->GetResourceManager()->GetDeckDescriptor(mDeckId)->mpDeck;
+
+ if (!pDeck)
+ {
+ pSidebarController->CreateDeck(mDeckId);
+ pDeck = pSidebarController->GetResourceManager()->GetDeckDescriptor(mDeckId)->mpDeck;
+ }
+
+ DeckTitleBar* pTitleBar = pDeck->GetTitleBar();
+ return pTitleBar->GetTitle();
+}
+
+void SAL_CALL SfxUnoDeck::setTitle( const OUString& newTitle )
+{
+ SolarMutexGuard aGuard;
+
+ SidebarController* pSidebarController = getSidebarController();
+ pSidebarController->CreateDeck(mDeckId);
+
+ std::shared_ptr<DeckDescriptor> xDeckDescriptor = pSidebarController->GetResourceManager()->GetDeckDescriptor(mDeckId);
+
+ if (xDeckDescriptor)
+ {
+ Deck* pDeck = xDeckDescriptor->mpDeck;
+ DeckTitleBar* pTitleBar = pDeck->GetTitleBar();
+ pTitleBar->SetTitle(newTitle);
+
+ xDeckDescriptor->msTitle = newTitle;
+ xDeckDescriptor->msHelpText = newTitle;
+
+ pSidebarController->notifyDeckTitle(mDeckId);
+ }
+}
+
+sal_Bool SAL_CALL SfxUnoDeck::isActive()
+{
+ SolarMutexGuard aGuard;
+
+ SidebarController* pSidebarController = getSidebarController();
+ return pSidebarController->IsDeckVisible(mDeckId);
+}
+
+
+void SAL_CALL SfxUnoDeck::activate( const sal_Bool bActivate )
+{
+ SolarMutexGuard aGuard;
+
+ SidebarController* pSidebarController = getSidebarController();
+
+ // tdf#138160: OpenThenToggleDeck takes care of minimal width
+ if (bActivate)
+ pSidebarController->OpenThenToggleDeck(mDeckId);
+ else
+ {
+ pSidebarController->SwitchToDefaultDeck();
+ // update the sidebar
+ pSidebarController->NotifyResize();
+ }
+
+}
+
+uno::Reference<ui::XPanels> SAL_CALL SfxUnoDeck::getPanels()
+{
+ SolarMutexGuard aGuard;
+
+ uno::Reference<ui::XPanels> panels = new SfxUnoPanels(xFrame, mDeckId);
+ return panels;
+}
+
+sal_Int32 SAL_CALL SfxUnoDeck::getOrderIndex()
+{
+ SolarMutexGuard aGuard;
+ SidebarController* pSidebarController = getSidebarController();
+
+ sal_Int32 index = pSidebarController->GetResourceManager()->GetDeckDescriptor(mDeckId)->mnOrderIndex;
+ return index;
+}
+
+void SAL_CALL SfxUnoDeck::setOrderIndex( const sal_Int32 newOrderIndex )
+{
+ SolarMutexGuard aGuard;
+ SidebarController* pSidebarController = getSidebarController();
+
+ std::shared_ptr<DeckDescriptor> xDeckDescriptor = pSidebarController->GetResourceManager()->GetDeckDescriptor(mDeckId);
+
+ if (xDeckDescriptor)
+ {
+ xDeckDescriptor->mnOrderIndex = newOrderIndex;
+ // update the sidebar
+ pSidebarController->NotifyResize();
+ }
+}
+
+void SAL_CALL SfxUnoDeck::moveFirst()
+{
+ SolarMutexGuard aGuard;
+ SidebarController* pSidebarController = getSidebarController();
+
+ ResourceManager::DeckContextDescriptorContainer aDecks = pSidebarController->GetMatchingDecks();
+
+ sal_Int32 minIndex = GetMinOrderIndex(aDecks);
+ sal_Int32 curOrderIndex = getOrderIndex();
+
+ if (curOrderIndex != minIndex) // is deck already in place ?
+ {
+ minIndex -= 1;
+ std::shared_ptr<DeckDescriptor> xDeckDescriptor = pSidebarController->GetResourceManager()->GetDeckDescriptor(mDeckId);
+ if (xDeckDescriptor)
+ {
+ xDeckDescriptor->mnOrderIndex = minIndex;
+ // update the sidebar
+ pSidebarController->NotifyResize();
+ }
+ }
+}
+
+void SAL_CALL SfxUnoDeck::moveLast()
+{
+ SolarMutexGuard aGuard;
+ SidebarController* pSidebarController = getSidebarController();
+
+ ResourceManager::DeckContextDescriptorContainer aDecks = pSidebarController->GetMatchingDecks();
+
+ sal_Int32 maxIndex = GetMaxOrderIndex(aDecks);
+ sal_Int32 curOrderIndex = getOrderIndex();
+
+ if (curOrderIndex != maxIndex) // is deck already in place ?
+ {
+ maxIndex += 1;
+ std::shared_ptr<DeckDescriptor> xDeckDescriptor = pSidebarController->GetResourceManager()->GetDeckDescriptor(mDeckId);
+ if (xDeckDescriptor)
+ {
+ xDeckDescriptor->mnOrderIndex = maxIndex;
+ // update the sidebar
+ pSidebarController->NotifyResize();
+ }
+ }
+}
+
+void SAL_CALL SfxUnoDeck::moveUp()
+{
+ SolarMutexGuard aGuard;
+ SidebarController* pSidebarController = getSidebarController();
+
+ // Search for previous deck OrderIndex
+ ResourceManager::DeckContextDescriptorContainer aDecks = pSidebarController->GetMatchingDecks();
+
+ sal_Int32 curOrderIndex = getOrderIndex();
+ sal_Int32 previousIndex = GetMinOrderIndex(aDecks);
+
+ for (auto const& deck : aDecks)
+ {
+ sal_Int32 index = pSidebarController->GetResourceManager()->GetDeckDescriptor(deck.msId)->mnOrderIndex;
+ if( index < curOrderIndex && index > previousIndex)
+ previousIndex = index;
+ }
+
+ if (curOrderIndex != previousIndex) // is deck already in place ?
+ {
+ previousIndex -= 1;
+ std::shared_ptr<DeckDescriptor> xDeckDescriptor = pSidebarController->GetResourceManager()->GetDeckDescriptor(mDeckId);
+ if (xDeckDescriptor)
+ {
+ xDeckDescriptor->mnOrderIndex = previousIndex;
+ // update the sidebar
+ pSidebarController->NotifyResize();
+ }
+ }
+}
+
+void SAL_CALL SfxUnoDeck::moveDown()
+{
+ SolarMutexGuard aGuard;
+ SidebarController* pSidebarController = getSidebarController();
+
+ ResourceManager::DeckContextDescriptorContainer aDecks = pSidebarController->GetMatchingDecks();
+
+ // Search for next deck OrderIndex
+ sal_Int32 curOrderIndex = getOrderIndex();
+ sal_Int32 nextIndex = GetMaxOrderIndex(aDecks);
+
+ for (auto const& deck : aDecks)
+ {
+ sal_Int32 index = pSidebarController->GetResourceManager()->GetDeckDescriptor(deck.msId)->mnOrderIndex;
+ if( index > curOrderIndex && index < nextIndex)
+ nextIndex = index;
+ }
+
+ if (curOrderIndex != nextIndex) // is deck already in place ?
+ {
+ nextIndex += 1;
+ std::shared_ptr<DeckDescriptor> xDeckDescriptor = pSidebarController->GetResourceManager()->GetDeckDescriptor(mDeckId);
+ if (xDeckDescriptor)
+ {
+ xDeckDescriptor->mnOrderIndex = nextIndex;
+ // update the sidebar
+ pSidebarController->NotifyResize();
+ }
+ }
+}
+
+sal_Int32 SfxUnoDeck::GetMinOrderIndex(const ResourceManager::DeckContextDescriptorContainer& rDecks)
+{
+ SidebarController* pSidebarController = getSidebarController();
+
+ ResourceManager::DeckContextDescriptorContainer::const_iterator iDeck = rDecks.begin();
+ sal_Int32 minIndex = pSidebarController->GetResourceManager()->GetDeckDescriptor(iDeck->msId)->mnOrderIndex;
+
+ for (auto const& deck : rDecks)
+ {
+ sal_Int32 index = pSidebarController->GetResourceManager()->GetDeckDescriptor(deck.msId)->mnOrderIndex;
+ if(minIndex > index)
+ minIndex = index;
+ }
+ return minIndex;
+}
+
+sal_Int32 SfxUnoDeck::GetMaxOrderIndex(const ResourceManager::DeckContextDescriptorContainer& rDecks)
+{
+ SidebarController* pSidebarController = getSidebarController();
+
+ sal_Int32 maxIndex = pSidebarController->GetResourceManager()->GetDeckDescriptor(rDecks.begin()->msId)->mnOrderIndex;
+
+ for (auto const& deck : rDecks)
+ {
+ sal_Int32 index = pSidebarController->GetResourceManager()->GetDeckDescriptor(deck.msId)->mnOrderIndex;
+ if(maxIndex < index)
+ maxIndex = index;
+ }
+ return maxIndex;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/sidebar/UnoDecks.cxx b/sfx2/source/sidebar/UnoDecks.cxx
new file mode 100644
index 000000000..2d07ea1a7
--- /dev/null
+++ b/sfx2/source/sidebar/UnoDecks.cxx
@@ -0,0 +1,143 @@
+/* -*- 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 <sal/config.h>
+
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <sidebar/UnoDecks.hxx>
+#include <sidebar/UnoDeck.hxx>
+
+#include <sfx2/sidebar/ResourceManager.hxx>
+#include <sfx2/sidebar/SidebarController.hxx>
+
+#include <vcl/svapp.hxx>
+
+#include <algorithm>
+
+using namespace css;
+using namespace ::sfx2::sidebar;
+
+SfxUnoDecks::SfxUnoDecks(const uno::Reference<frame::XFrame>& rFrame):
+xFrame(rFrame)
+{
+}
+
+SidebarController* SfxUnoDecks::getSidebarController()
+{
+ return SidebarController::GetSidebarControllerForFrame(xFrame);
+}
+
+// XNameAccess
+
+uno::Any SAL_CALL SfxUnoDecks::getByName( const OUString& aName )
+{
+ SolarMutexGuard aGuard;
+
+ if (!hasByName(aName))
+ throw container::NoSuchElementException();
+
+ uno::Reference<ui::XDeck> xDeck = new SfxUnoDeck(xFrame, aName);
+ return uno::Any(xDeck);
+}
+
+
+uno::Sequence< OUString > SAL_CALL SfxUnoDecks::getElementNames()
+{
+ SolarMutexGuard aGuard;
+
+ SidebarController* pSidebarController = getSidebarController();
+
+ ResourceManager::DeckContextDescriptorContainer aDecks;
+ css::uno::Sequence< OUString > deckList(aDecks.size());
+
+ if (pSidebarController)
+ {
+ pSidebarController->GetResourceManager()->GetMatchingDecks (
+ aDecks,
+ pSidebarController->GetCurrentContext(),
+ pSidebarController->IsDocumentReadOnly(),
+ xFrame->getController());
+
+ deckList.realloc(aDecks.size());
+ std::transform(aDecks.begin(), aDecks.end(), deckList.getArray(),
+ [](const auto& rDeck) { return rDeck.msId; });
+ }
+
+ return deckList;
+
+}
+
+sal_Bool SAL_CALL SfxUnoDecks::hasByName( const OUString& aName )
+{
+ SolarMutexGuard aGuard;
+
+ SidebarController* pSidebarController = getSidebarController();
+
+ bool bFound = false;
+
+ if (pSidebarController)
+ {
+ ResourceManager::DeckContextDescriptorContainer aDecks;
+
+ pSidebarController->GetResourceManager()->GetMatchingDecks (
+ aDecks,
+ pSidebarController->GetCurrentContext(),
+ pSidebarController->IsDocumentReadOnly(),
+ xFrame->getController());
+
+ bFound = std::any_of(aDecks.begin(), aDecks.end(),
+ [&aName](const ResourceManager::DeckContextDescriptor& rDeck) { return rDeck.msId == aName; });
+ }
+
+ return bFound;
+
+}
+
+// XIndexAccess
+
+sal_Int32 SAL_CALL SfxUnoDecks::getCount()
+{
+ SolarMutexGuard aGuard;
+
+ uno::Sequence< OUString > decks = getElementNames();
+ return decks.getLength();
+}
+
+uno::Any SAL_CALL SfxUnoDecks::getByIndex( sal_Int32 Index )
+{
+ SolarMutexGuard aGuard;
+ uno::Any aRet;
+
+ uno::Sequence< OUString > decks = getElementNames();
+
+ if (Index > decks.getLength()-1 || Index < 0)
+ throw lang::IndexOutOfBoundsException();
+
+ uno::Reference<ui::XDeck> xDeck = new SfxUnoDeck(xFrame, decks[Index]);
+ aRet <<= xDeck;
+ return aRet;
+
+}
+
+// XElementAccess
+uno::Type SAL_CALL SfxUnoDecks::getElementType()
+{
+ return uno::Type();
+}
+
+sal_Bool SAL_CALL SfxUnoDecks::hasElements()
+{
+ SolarMutexGuard aGuard;
+
+ uno::Sequence< OUString > decks = getElementNames();
+ return decks.hasElements();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/sidebar/UnoPanel.cxx b/sfx2/source/sidebar/UnoPanel.cxx
new file mode 100644
index 000000000..4af0b0b89
--- /dev/null
+++ b/sfx2/source/sidebar/UnoPanel.cxx
@@ -0,0 +1,295 @@
+/* -*- 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 <sidebar/UnoPanel.hxx>
+
+#include <sfx2/sidebar/ResourceManager.hxx>
+#include <sfx2/sidebar/SidebarController.hxx>
+
+#include <sidebar/PanelDescriptor.hxx>
+#include <sidebar/PanelTitleBar.hxx>
+#include <sfx2/sidebar/Panel.hxx>
+#include <sfx2/sidebar/Deck.hxx>
+#include <sidebar/DeckDescriptor.hxx>
+
+#include <vcl/svapp.hxx>
+
+using namespace css;
+using namespace ::sfx2::sidebar;
+
+SfxUnoPanel::SfxUnoPanel(const uno::Reference<frame::XFrame>& rFrame, const OUString& panelId, const OUString& deckId):
+xFrame(rFrame),
+mPanelId(panelId),
+mDeckId(deckId)
+{
+ SidebarController* pSidebarController = getSidebarController();
+
+ pSidebarController->CreateDeck(mDeckId); // creates deck object is not already
+ mpDeck = pSidebarController->GetResourceManager()->GetDeckDescriptor(mDeckId)->mpDeck;
+ mxPanel = mpDeck->GetPanel(mPanelId);
+}
+
+SidebarController* SfxUnoPanel::getSidebarController()
+{
+ return SidebarController::GetSidebarControllerForFrame(xFrame);
+}
+
+OUString SAL_CALL SfxUnoPanel::getId()
+{
+ SolarMutexGuard aGuard;
+
+ return mPanelId;
+}
+
+OUString SAL_CALL SfxUnoPanel::getTitle()
+{
+ SolarMutexGuard aGuard;
+
+ auto xPanel = mxPanel.lock();
+ PanelTitleBar* pTitleBar = xPanel ? xPanel->GetTitleBar() : nullptr;
+ if (pTitleBar)
+ return pTitleBar->GetTitle();
+ else
+ return OUString();
+}
+
+void SAL_CALL SfxUnoPanel::setTitle( const OUString& newTitle )
+{
+ SolarMutexGuard aGuard;
+
+ SidebarController* pSidebarController = getSidebarController();
+ std::shared_ptr<PanelDescriptor> xPanelDescriptor = pSidebarController->GetResourceManager()->GetPanelDescriptor(mPanelId);
+
+ if (xPanelDescriptor)
+ {
+ xPanelDescriptor->msTitle = newTitle;
+ auto xPanel = mxPanel.lock();
+ PanelTitleBar* pTitleBar = xPanel ? xPanel->GetTitleBar() : nullptr;
+ if (pTitleBar)
+ pTitleBar->SetTitle(newTitle);
+ }
+}
+
+sal_Bool SAL_CALL SfxUnoPanel::isExpanded()
+{
+ SolarMutexGuard aGuard;
+
+ auto xPanel = mxPanel.lock();
+ return xPanel && xPanel->IsExpanded();
+}
+
+
+void SAL_CALL SfxUnoPanel::expand( const sal_Bool bCollapseOther )
+{
+
+ SolarMutexGuard aGuard;
+
+ auto xPanel = mxPanel.lock();
+ if (xPanel)
+ xPanel->SetExpanded(true);
+
+ if (bCollapseOther)
+ {
+ SharedPanelContainer aPanels = mpDeck->GetPanels();
+ for (auto const& panel : aPanels)
+ {
+ if (! panel->HasIdPredicate(mPanelId))
+ panel->SetExpanded(false);
+ }
+ }
+
+ SidebarController* pSidebarController = getSidebarController();
+ pSidebarController->NotifyResize();
+
+}
+
+void SAL_CALL SfxUnoPanel::collapse()
+{
+ SolarMutexGuard aGuard;
+
+ auto xPanel = mxPanel.lock();
+ if (xPanel)
+ xPanel->SetExpanded(false);
+ SidebarController* pSidebarController = getSidebarController();
+ pSidebarController->NotifyResize();
+}
+
+uno::Reference<awt::XWindow> SAL_CALL SfxUnoPanel::getDialog()
+{
+ SolarMutexGuard aGuard;
+
+ auto xPanel = mxPanel.lock();
+ return xPanel ? xPanel->GetElementWindow() : nullptr;
+}
+
+sal_Int32 SAL_CALL SfxUnoPanel::getOrderIndex()
+{
+ SolarMutexGuard aGuard;
+ SidebarController* pSidebarController = getSidebarController();
+
+ sal_Int32 index = pSidebarController->GetResourceManager()->GetPanelDescriptor(mPanelId)->mnOrderIndex;
+ return index;
+}
+
+void SAL_CALL SfxUnoPanel::setOrderIndex( const sal_Int32 newOrderIndex )
+{
+ SolarMutexGuard aGuard;
+ SidebarController* pSidebarController = getSidebarController();
+
+ std::shared_ptr<PanelDescriptor> xPanelDescriptor = pSidebarController->GetResourceManager()->GetPanelDescriptor(mPanelId);
+
+ if (xPanelDescriptor)
+ {
+ xPanelDescriptor->mnOrderIndex = newOrderIndex;
+ // update the sidebar
+ pSidebarController->NotifyResize();
+ }
+}
+
+void SAL_CALL SfxUnoPanel::moveFirst()
+{
+ SolarMutexGuard aGuard;
+ SidebarController* pSidebarController = getSidebarController();
+
+ ResourceManager::PanelContextDescriptorContainer aPanels = pSidebarController->GetMatchingPanels(mDeckId);
+
+ sal_Int32 curOrderIndex = getOrderIndex();
+ sal_Int32 minIndex = GetMinOrderIndex(aPanels);
+
+ if (curOrderIndex != minIndex) // is current panel already in place ?
+ {
+ minIndex -= 1;
+ std::shared_ptr<PanelDescriptor> xPanelDescriptor = pSidebarController->GetResourceManager()->GetPanelDescriptor(mPanelId);
+ if (xPanelDescriptor)
+ {
+ xPanelDescriptor->mnOrderIndex = minIndex;
+ // update the sidebar
+ pSidebarController->NotifyResize();
+ }
+ }
+}
+
+void SAL_CALL SfxUnoPanel::moveLast()
+{
+ SolarMutexGuard aGuard;
+ SidebarController* pSidebarController = getSidebarController();
+
+ ResourceManager::PanelContextDescriptorContainer aPanels = pSidebarController->GetMatchingPanels(mDeckId);
+
+ sal_Int32 curOrderIndex = getOrderIndex();
+ sal_Int32 maxIndex = GetMaxOrderIndex(aPanels);
+
+ if (curOrderIndex != maxIndex) // is current panel already in place ?
+ {
+ maxIndex += 1;
+ std::shared_ptr<PanelDescriptor> xPanelDescriptor = pSidebarController->GetResourceManager()->GetPanelDescriptor(mPanelId);
+ if (xPanelDescriptor)
+ {
+ xPanelDescriptor->mnOrderIndex = maxIndex;
+ // update the sidebar
+ pSidebarController->NotifyResize();
+ }
+ }
+}
+
+void SAL_CALL SfxUnoPanel::moveUp()
+{
+ SolarMutexGuard aGuard;
+ SidebarController* pSidebarController = getSidebarController();
+
+ // Search for previous panel OrderIndex
+ ResourceManager::PanelContextDescriptorContainer aPanels = pSidebarController->GetMatchingPanels(mDeckId);
+
+ sal_Int32 curOrderIndex = getOrderIndex();
+ sal_Int32 previousIndex = GetMinOrderIndex(aPanels);
+
+ for (auto const& panel : aPanels)
+ {
+ sal_Int32 index = pSidebarController->GetResourceManager()->GetPanelDescriptor(panel.msId)->mnOrderIndex;
+ if( index < curOrderIndex && index > previousIndex)
+ previousIndex = index;
+ }
+
+ if (curOrderIndex != previousIndex) // is current panel already in place ?
+ {
+ previousIndex -= 1;
+ std::shared_ptr<PanelDescriptor> xPanelDescriptor = pSidebarController->GetResourceManager()->GetPanelDescriptor(mPanelId);
+ if (xPanelDescriptor)
+ {
+ xPanelDescriptor->mnOrderIndex = previousIndex;
+ // update the sidebar
+ pSidebarController->NotifyResize();
+ }
+ }
+}
+
+void SAL_CALL SfxUnoPanel::moveDown()
+{
+ SolarMutexGuard aGuard;
+ SidebarController* pSidebarController = getSidebarController();
+
+ ResourceManager::PanelContextDescriptorContainer aPanels = pSidebarController->GetMatchingPanels(mDeckId);
+
+ // Search for next panel OrderIndex
+ sal_Int32 curOrderIndex = getOrderIndex();
+ sal_Int32 nextIndex = GetMaxOrderIndex(aPanels);
+
+ for (auto const& panel : aPanels)
+ {
+ sal_Int32 index = pSidebarController->GetResourceManager()->GetPanelDescriptor(panel.msId)->mnOrderIndex;
+ if( index > curOrderIndex && index < nextIndex)
+ nextIndex = index;
+ }
+
+ if (curOrderIndex != nextIndex) // is current panel already in place ?
+ {
+ nextIndex += 1;
+ std::shared_ptr<PanelDescriptor> xPanelDescriptor = pSidebarController->GetResourceManager()->GetPanelDescriptor(mPanelId);
+ if (xPanelDescriptor)
+ {
+ xPanelDescriptor->mnOrderIndex = nextIndex;
+ // update the sidebar
+ pSidebarController->NotifyResize();
+ }
+ }
+}
+
+sal_Int32 SfxUnoPanel::GetMinOrderIndex(const ResourceManager::PanelContextDescriptorContainer& rPanels)
+{
+ SidebarController* pSidebarController = getSidebarController();
+
+ sal_Int32 minIndex = pSidebarController->GetResourceManager()->GetPanelDescriptor(rPanels.begin()->msId)->mnOrderIndex;
+
+ for (auto const& panel : rPanels)
+ {
+ sal_Int32 index = pSidebarController->GetResourceManager()->GetPanelDescriptor(panel.msId)->mnOrderIndex;
+ if(minIndex > index)
+ minIndex = index;
+ }
+ return minIndex;
+}
+
+sal_Int32 SfxUnoPanel::GetMaxOrderIndex(const ResourceManager::PanelContextDescriptorContainer& rPanels)
+{
+ SidebarController* pSidebarController = getSidebarController();
+
+ sal_Int32 maxIndex = pSidebarController->GetResourceManager()->GetPanelDescriptor(rPanels.begin()->msId)->mnOrderIndex;
+
+ for (auto const& panel : rPanels)
+ {
+ sal_Int32 index = pSidebarController->GetResourceManager()->GetPanelDescriptor(panel.msId)->mnOrderIndex;
+ if(maxIndex < index)
+ maxIndex = index;
+ }
+ return maxIndex;
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/sidebar/UnoPanels.cxx b/sfx2/source/sidebar/UnoPanels.cxx
new file mode 100644
index 000000000..4ef48eb5c
--- /dev/null
+++ b/sfx2/source/sidebar/UnoPanels.cxx
@@ -0,0 +1,155 @@
+/* -*- 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 <sidebar/UnoPanels.hxx>
+
+#include <sfx2/sidebar/ResourceManager.hxx>
+#include <sfx2/sidebar/SidebarController.hxx>
+
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <com/sun/star/ui/XPanel.hpp>
+#include <sidebar/UnoPanel.hxx>
+
+#include <vcl/svapp.hxx>
+
+#include <algorithm>
+
+using namespace css;
+using namespace ::sfx2::sidebar;
+
+SfxUnoPanels::SfxUnoPanels(const uno::Reference<frame::XFrame>& rFrame, const OUString& deckId):
+xFrame(rFrame),
+mDeckId(deckId)
+{
+}
+
+SidebarController* SfxUnoPanels::getSidebarController()
+{
+ return SidebarController::GetSidebarControllerForFrame(xFrame);
+}
+
+OUString SAL_CALL SfxUnoPanels::getDeckId()
+{
+ SolarMutexGuard aGuard;
+
+ return mDeckId;
+}
+
+// XNameAccess
+
+uno::Any SAL_CALL SfxUnoPanels::getByName( const OUString& aName )
+{
+ SolarMutexGuard aGuard;
+
+ if (!hasByName(aName))
+ throw container::NoSuchElementException();
+
+ uno::Reference<ui::XPanel> xPanel = new SfxUnoPanel(xFrame, aName, mDeckId);
+ return uno::Any(xPanel);
+}
+
+
+uno::Sequence< OUString > SAL_CALL SfxUnoPanels::getElementNames()
+{
+
+ SolarMutexGuard aGuard;
+
+ SidebarController* pSidebarController = getSidebarController();
+
+ ResourceManager::PanelContextDescriptorContainer aPanels;
+ uno::Sequence< OUString > panelList(aPanels.size());
+
+ if (pSidebarController)
+ {
+ pSidebarController->GetResourceManager()->GetMatchingPanels(aPanels,
+ pSidebarController->GetCurrentContext(),
+ mDeckId,
+ xFrame->getController());
+
+ panelList.realloc(aPanels.size());
+ std::transform(aPanels.begin(), aPanels.end(), panelList.getArray(),
+ [](const auto& rPanel) { return rPanel.msId; });
+ }
+
+ return panelList;
+
+}
+
+sal_Bool SAL_CALL SfxUnoPanels::hasByName( const OUString& aName )
+{
+ SolarMutexGuard aGuard;
+
+ SidebarController* pSidebarController = getSidebarController();
+
+ if (pSidebarController)
+ {
+ ResourceManager::PanelContextDescriptorContainer aPanels;
+
+ pSidebarController->GetResourceManager()->GetMatchingPanels(aPanels,
+ pSidebarController->GetCurrentContext(),
+ mDeckId,
+ xFrame->getController());
+
+ bool bIsDocumentReadOnly = pSidebarController->IsDocumentReadOnly();
+
+ return std::any_of(aPanels.begin(), aPanels.end(),
+ [&bIsDocumentReadOnly, &aName](const ResourceManager::PanelContextDescriptor& rPanel) {
+ return (!bIsDocumentReadOnly || rPanel.mbShowForReadOnlyDocuments) // Determine if the panel can be displayed.
+ && (rPanel.msId == aName);
+ });
+ }
+
+ // nothing found
+ return false;
+
+}
+
+// XIndexAccess
+
+sal_Int32 SAL_CALL SfxUnoPanels::getCount()
+{
+ SolarMutexGuard aGuard;
+
+ uno::Sequence< OUString > panels = getElementNames();
+ return panels.getLength();
+}
+
+uno::Any SAL_CALL SfxUnoPanels::getByIndex( sal_Int32 Index )
+{
+ SolarMutexGuard aGuard;
+
+ uno::Any aRet;
+
+ uno::Sequence< OUString > panels = getElementNames();
+
+ if (Index > panels.getLength()-1 || Index < 0)
+ throw lang::IndexOutOfBoundsException();
+
+ uno::Reference<ui::XPanel> xPanel = new SfxUnoPanel(xFrame, panels[Index], mDeckId);
+ aRet <<= xPanel;
+ return aRet;
+
+}
+
+// XElementAccess
+uno::Type SAL_CALL SfxUnoPanels::getElementType()
+{
+ return uno::Type();
+}
+
+sal_Bool SAL_CALL SfxUnoPanels::hasElements()
+{
+ SolarMutexGuard aGuard;
+
+ uno::Sequence< OUString > panels = getElementNames();
+ return panels.hasElements();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/sidebar/UnoSidebar.cxx b/sfx2/source/sidebar/UnoSidebar.cxx
new file mode 100644
index 000000000..b39a8519b
--- /dev/null
+++ b/sfx2/source/sidebar/UnoSidebar.cxx
@@ -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/.
+ *
+ */
+
+#include <sidebar/UnoSidebar.hxx>
+#include <sidebar/Tools.hxx>
+
+#include <sfx2/sidebar/SidebarController.hxx>
+#include <sidebar/UnoDecks.hxx>
+
+#include <com/sun/star/frame/XDispatch.hpp>
+
+#include <vcl/svapp.hxx>
+
+using namespace css;
+using namespace ::sfx2::sidebar;
+
+using ::com::sun::star::uno::RuntimeException;
+
+SfxUnoSidebar::SfxUnoSidebar(const uno::Reference<frame::XFrame>& rFrame)
+ : xFrame(rFrame)
+{
+}
+
+SidebarController* SfxUnoSidebar::getSidebarController()
+{
+ return SidebarController::GetSidebarControllerForFrame(xFrame);
+}
+
+void SAL_CALL SfxUnoSidebar::showDecks(const sal_Bool bVisible)
+{
+ SolarMutexGuard aGuard;
+
+ SidebarController* pSidebarController = getSidebarController();
+
+ if (pSidebarController)
+ {
+ if (bVisible)
+ pSidebarController->RequestOpenDeck();
+ else
+ pSidebarController->RequestCloseDeck();
+ }
+}
+
+void SAL_CALL SfxUnoSidebar::setVisible(const sal_Bool bVisible)
+{
+ SolarMutexGuard aGuard;
+
+ SidebarController* pSidebarController = getSidebarController();
+
+ if ((bVisible && !pSidebarController) || (!bVisible && pSidebarController))
+ {
+ const util::URL aURL(Tools::GetURL(".uno:Sidebar"));
+ uno::Reference<frame::XDispatch> xDispatch(Tools::GetDispatch(xFrame, aURL));
+ if (xDispatch.is())
+ xDispatch->dispatch(aURL, uno::Sequence<beans::PropertyValue>());
+ }
+}
+
+sal_Bool SAL_CALL SfxUnoSidebar::isVisible()
+{
+ SolarMutexGuard aGuard;
+
+ SidebarController* pSidebarController = getSidebarController();
+
+ return pSidebarController != nullptr;
+}
+
+uno::Reference<frame::XFrame> SAL_CALL SfxUnoSidebar::getFrame()
+{
+ SolarMutexGuard aGuard;
+
+ if (!xFrame.is())
+ throw uno::RuntimeException();
+
+ return xFrame;
+}
+
+uno::Reference<ui::XDecks> SAL_CALL SfxUnoSidebar::getDecks()
+{
+ SolarMutexGuard aGuard;
+
+ uno::Reference<ui::XDecks> decks = new SfxUnoDecks(xFrame);
+ return decks;
+}
+
+uno::Reference<ui::XSidebar> SAL_CALL SfxUnoSidebar::getSidebar() { return getSidebarController(); }
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/statbar/stbitem.cxx b/sfx2/source/statbar/stbitem.cxx
new file mode 100644
index 000000000..c48fc970e
--- /dev/null
+++ b/sfx2/source/statbar/stbitem.cxx
@@ -0,0 +1,553 @@
+/* -*- 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/stritem.hxx>
+#include <com/sun/star/util/URL.hpp>
+#include <com/sun/star/util/URLTransformer.hpp>
+#include <com/sun/star/util/XURLTransformer.hpp>
+#include <com/sun/star/frame/XController.hpp>
+#include <com/sun/star/frame/XDispatchProvider.hpp>
+#include <com/sun/star/lang/XUnoTunnel.hpp>
+#include <com/sun/star/frame/status/ItemStatus.hpp>
+#include <com/sun/star/awt/MouseButton.hpp>
+
+#include <vcl/commandevent.hxx>
+#include <vcl/event.hxx>
+#include <vcl/status.hxx>
+#include <vcl/svapp.hxx>
+
+#include <sfx2/app.hxx>
+#include <sfx2/stbitem.hxx>
+#include <sfx2/msg.hxx>
+#include <sfx2/msgpool.hxx>
+#include <sfx2/module.hxx>
+#include <sfx2/dispatch.hxx>
+#include <unoctitm.hxx>
+#include <sfx2/objsh.hxx>
+
+#include <comphelper/processfactory.hxx>
+#include <comphelper/servicehelper.hxx>
+#include <svl/eitem.hxx>
+#include <svl/intitem.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+#include <toolkit/helper/convert.hxx>
+
+using namespace ::com::sun::star;
+
+
+sal_uInt16 SfxStatusBarControl::convertAwtToVCLMouseButtons( sal_Int16 nAwtMouseButtons )
+{
+ sal_uInt16 nVCLMouseButtons( 0 );
+
+ if ( nAwtMouseButtons & awt::MouseButton::LEFT )
+ nVCLMouseButtons |= MOUSE_LEFT;
+ if ( nAwtMouseButtons & awt::MouseButton::RIGHT )
+ nVCLMouseButtons |= MOUSE_RIGHT;
+ if ( nAwtMouseButtons & awt::MouseButton::MIDDLE )
+ nVCLMouseButtons |= MOUSE_MIDDLE;
+
+ return nVCLMouseButtons;
+}
+
+
+rtl::Reference<svt::StatusbarController> SfxStatusBarControllerFactory(
+ const uno::Reference< frame::XFrame >& rFrame,
+ StatusBar* pStatusBar,
+ unsigned short nID,
+ const OUString& aCommandURL )
+{
+ SolarMutexGuard aGuard;
+
+ util::URL aTargetURL;
+ aTargetURL.Complete = aCommandURL;
+ uno::Reference < util::XURLTransformer > xTrans( util::URLTransformer::create( ::comphelper::getProcessComponentContext() ) );
+ xTrans->parseStrict( aTargetURL );
+
+ uno::Reference < frame::XController > xController;
+ uno::Reference < frame::XModel > xModel;
+ if ( rFrame.is() )
+ {
+ xController = rFrame->getController();
+ if ( xController.is() )
+ xModel = xController->getModel();
+ }
+
+ SfxObjectShell* pObjShell = SfxObjectShell::GetShellFromComponent(xModel);
+
+ SfxModule* pModule = pObjShell ? pObjShell->GetModule() : nullptr;
+ SfxSlotPool* pSlotPool = nullptr;
+
+ if ( pModule )
+ pSlotPool = pModule->GetSlotPool();
+ else
+ pSlotPool = &(SfxSlotPool::GetSlotPool());
+
+ const SfxSlot* pSlot = pSlotPool->GetUnoSlot( aTargetURL.Path );
+ if ( pSlot )
+ {
+ sal_uInt16 nSlotId = pSlot->GetSlotId();
+ if ( nSlotId > 0 )
+ {
+ OString aCmd = OString::Concat(".uno:") + pSlot->GetUnoName();
+ pStatusBar->SetHelpId( nSlotId, aCmd );
+ return SfxStatusBarControl::CreateControl( nSlotId, nID, pStatusBar, pModule );
+ }
+ }
+
+ return nullptr;
+}
+
+
+SfxStatusBarControl::SfxStatusBarControl
+(
+ sal_uInt16 nSlotID, /* Slot-Id which is connected to this
+ instance. If a Slot-Id is set to != 0 at
+ registration it will always be set there.
+ */
+
+
+ sal_uInt16 nCtrlID, /* ID of this controller in the status bar */
+
+ StatusBar& rBar /* Reference to the StatusBar,for which
+ this Control was created. */
+)
+
+
+/* [Description]
+
+ Constructor of the SfxStatusBarControl Class. The Subclasses are
+ created at the Factory if necessary.
+
+ Instances of this base class are created for all StatusBar-fields
+ for which no specific ones have been registered.
+*/
+
+: nSlotId( nSlotID ),
+ nId( nCtrlID ),
+ pBar( &rBar )
+{
+}
+
+
+SfxStatusBarControl::~SfxStatusBarControl()
+
+/* [Description]
+
+ Destructor of the SfxStatusBarControl Class. The Class and its Subclasses
+ are destroyed by SFx.
+*/
+
+{}
+
+
+// XInterface
+void SAL_CALL SfxStatusBarControl::acquire() noexcept
+{
+ OWeakObject::acquire();
+}
+
+void SAL_CALL SfxStatusBarControl::release() noexcept
+{
+ OWeakObject::release();
+}
+
+
+// XStatusListener
+void SAL_CALL SfxStatusBarControl::statusChanged( const frame::FeatureStateEvent& rEvent )
+{
+ SfxViewFrame* pViewFrame = nullptr;
+ uno::Reference < frame::XController > xController;
+
+ SolarMutexGuard aGuard;
+ if ( m_xFrame.is() )
+ xController = m_xFrame->getController();
+
+ uno::Reference < frame::XDispatchProvider > xProvider( xController, uno::UNO_QUERY );
+ if ( xProvider.is() )
+ {
+ uno::Reference < frame::XDispatch > xDisp = xProvider->queryDispatch( rEvent.FeatureURL, OUString(), 0 );
+ if ( xDisp.is() )
+ {
+ uno::Reference< lang::XUnoTunnel > xTunnel( xDisp, uno::UNO_QUERY );
+ if (auto pDisp = comphelper::getFromUnoTunnel<SfxOfficeDispatch>(xTunnel))
+ pViewFrame = pDisp->GetDispatcher_Impl()->GetFrame();
+ }
+ }
+
+ sal_uInt16 nSlotID = 0;
+ SfxSlotPool& rPool = SfxSlotPool::GetSlotPool( pViewFrame );
+ const SfxSlot* pSlot = rPool.GetUnoSlot( rEvent.FeatureURL.Path );
+ if ( pSlot )
+ nSlotID = pSlot->GetSlotId();
+
+ if ( nSlotID <= 0 )
+ return;
+
+ if ( rEvent.Requery )
+ svt::StatusbarController::statusChanged( rEvent );
+ else
+ {
+ SfxItemState eState = SfxItemState::DISABLED;
+ std::unique_ptr<SfxPoolItem> pItem;
+ if ( rEvent.IsEnabled )
+ {
+ eState = SfxItemState::DEFAULT;
+ uno::Type aType = rEvent.State.getValueType();
+
+ if ( aType == cppu::UnoType<void>::get() )
+ {
+ pItem.reset( new SfxVoidItem( nSlotID ) );
+ eState = SfxItemState::UNKNOWN;
+ }
+ else if ( aType == cppu::UnoType<bool>::get() )
+ {
+ bool bTemp = false;
+ rEvent.State >>= bTemp ;
+ pItem.reset( new SfxBoolItem( nSlotID, bTemp ) );
+ }
+ else if ( aType == ::cppu::UnoType< ::cppu::UnoUnsignedShortType >::get() )
+ {
+ sal_uInt16 nTemp = 0;
+ rEvent.State >>= nTemp ;
+ pItem.reset( new SfxUInt16Item( nSlotID, nTemp ) );
+ }
+ else if ( aType == cppu::UnoType<sal_uInt32>::get() )
+ {
+ sal_uInt32 nTemp = 0;
+ rEvent.State >>= nTemp ;
+ pItem.reset( new SfxUInt32Item( nSlotID, nTemp ) );
+ }
+ else if ( aType == cppu::UnoType<OUString>::get() )
+ {
+ OUString sTemp ;
+ rEvent.State >>= sTemp ;
+ pItem.reset( new SfxStringItem( nSlotID, sTemp ) );
+ }
+ else if ( aType == cppu::UnoType< css::frame::status::ItemStatus>::get() )
+ {
+ frame::status::ItemStatus aItemStatus;
+ rEvent.State >>= aItemStatus;
+ eState = static_cast<SfxItemState>(aItemStatus.State);
+ pItem.reset( new SfxVoidItem( nSlotID ) );
+ }
+ else
+ {
+ if ( pSlot )
+ pItem = pSlot->GetType()->CreateItem();
+ if ( pItem )
+ {
+ pItem->SetWhich( nSlotID );
+ pItem->PutValue( rEvent.State, 0 );
+ }
+ else
+ pItem.reset( new SfxVoidItem( nSlotID ) );
+ }
+ }
+
+ StateChangedAtStatusBarControl( nSlotID, eState, pItem.get() );
+ }
+}
+
+// XStatusbarController
+sal_Bool SAL_CALL SfxStatusBarControl::mouseButtonDown(
+ const awt::MouseEvent& rMouseEvent )
+{
+ SolarMutexGuard aGuard;
+ ::Point aPos( rMouseEvent.X, rMouseEvent.Y );
+
+ ::MouseEvent aMouseEvent( aPos,
+ static_cast<sal_uInt16>(rMouseEvent.ClickCount),
+ MouseEventModifiers::NONE,
+ convertAwtToVCLMouseButtons( rMouseEvent.Buttons ),
+ 0 );
+
+ return MouseButtonDown( aMouseEvent );
+}
+
+sal_Bool SAL_CALL SfxStatusBarControl::mouseMove(
+ const awt::MouseEvent& rMouseEvent )
+{
+ SolarMutexGuard aGuard;
+ ::Point aPos( rMouseEvent.X, rMouseEvent.Y );
+
+ ::MouseEvent aMouseEvent( aPos,
+ static_cast<sal_uInt16>(rMouseEvent.ClickCount),
+ MouseEventModifiers::NONE,
+ convertAwtToVCLMouseButtons( rMouseEvent.Buttons ),
+ 0 );
+ return MouseMove( aMouseEvent );
+}
+
+sal_Bool SAL_CALL SfxStatusBarControl::mouseButtonUp(
+ const ::awt::MouseEvent& rMouseEvent )
+{
+ SolarMutexGuard aGuard;
+ ::Point aPos( rMouseEvent.X, rMouseEvent.Y );
+
+ ::MouseEvent aMouseEvent( aPos,
+ static_cast<sal_uInt16>(rMouseEvent.ClickCount),
+ MouseEventModifiers::NONE,
+ convertAwtToVCLMouseButtons( rMouseEvent.Buttons ),
+ 0 );
+ return MouseButtonUp( aMouseEvent );
+}
+
+void SAL_CALL SfxStatusBarControl::command(
+ const awt::Point& rPos,
+ ::sal_Int32 nCommand,
+ sal_Bool /*bMouseEvent*/,
+ const css::uno::Any& /*aData*/ )
+{
+ SolarMutexGuard aGuard;
+ ::Point aPos( rPos.X, rPos.Y );
+ CommandEvent aCmdEvent( aPos, static_cast<CommandEventId>(nCommand), true, nullptr );
+
+ Command( aCmdEvent );
+}
+
+void SAL_CALL SfxStatusBarControl::paint(
+ const uno::Reference< awt::XGraphics >& xGraphics,
+ const awt::Rectangle& rOutputRectangle,
+ ::sal_Int32 /*nStyle*/ )
+{
+ SolarMutexGuard aGuard;
+
+ OutputDevice* pOutDev = VCLUnoHelper::GetOutputDevice( xGraphics );
+ if ( pOutDev )
+ {
+ ::tools::Rectangle aRect = VCLRectangle( rOutputRectangle );
+ UserDrawEvent aUserDrawEvent(pOutDev, aRect, pBar->GetCurItemId());
+ Paint( aUserDrawEvent );
+ }
+}
+
+void SAL_CALL SfxStatusBarControl::click( const awt::Point& )
+{
+ SolarMutexGuard aGuard;
+ Click();
+}
+
+void SAL_CALL SfxStatusBarControl::doubleClick( const awt::Point& )
+{
+}
+
+// old sfx2 interface
+void SfxStatusBarControl::StateChangedAtStatusBarControl
+(
+ sal_uInt16 nSID,
+ SfxItemState eState,
+ const SfxPoolItem* pState /* Pointer to SfxPoolItem, is only valid
+ within this Method call. This can be a
+ Null-Pointer, a Pointer to SfxVoidItem
+ or of this Type found registered by the
+ Subclass of SfxStatusBarControl.
+ */
+)
+
+/* [Description]
+
+ The base implementation includes items of type SfxStringItem
+ where the text is entered in the status row field and
+ SfxVoidItem, where the field is emptied. The base implementation
+ should not be called in overriding methods.
+*/
+
+{
+ DBG_ASSERT( pBar != nullptr, "setting state to dangling StatusBar" );
+
+ const SfxStringItem* pStr = dynamic_cast<const SfxStringItem*>( pState );
+ if ( eState == SfxItemState::DEFAULT && pStr )
+ pBar->SetItemText( nSID, pStr->GetValue() );
+ else
+ {
+ DBG_ASSERT( eState != SfxItemState::DEFAULT || pState->IsVoidItem(),
+ "wrong SfxPoolItem subclass in SfxStatusBarControl" );
+ pBar->SetItemText( nSID, OUString() );
+ }
+}
+
+
+bool SfxStatusBarControl::MouseButtonDown( const MouseEvent & )
+
+/* [Description]
+
+ This virtual method forwards the Event MouseButtonDown() of the
+ StatusBar if the mouse position is within the range of the items,
+ or if the mouse was captured by <SfxStatusBarControl::CaptureMouse()>
+
+ The default implementation is empty and returns FALSE.
+
+ [Return value]
+
+ sal_Bool TRUE
+ The event has been processed and is not intended to
+ be forwarded to StatusBar
+
+ FALSE
+ The event was not processed and is to be
+ be forwarded to StatusBar
+*/
+
+{
+ return false;
+}
+
+
+bool SfxStatusBarControl::MouseMove( const MouseEvent & )
+
+/* [Description]
+
+ This virtual method forwards the Event MouseMove() of the
+ StatusBar if the mouse position is within the range of the items,
+ or if the mouse was captured by <SfxStatusBarControl::CaptureMouse()>
+
+ The default implementation is empty and returns FALSE.
+
+ [Return value]
+
+ sal_Bool TRUE
+ The event has been processed and is not intended to
+ be forwarded to StatusBar
+
+ FALSE
+ The event was not processed and is to be
+ be forwarded to StatusBar
+*/
+
+{
+ return false;
+}
+
+
+bool SfxStatusBarControl::MouseButtonUp( const MouseEvent & )
+
+/* [Description]
+
+ This virtual method forwards the Event MouseButtonUp() of the
+ StatusBar if the mouse position is within the range of the items,
+ or if the mouse was captured by <SfxStatusBarControl::CaptureMouse()>
+
+ The default implementation is empty and returns FALSE.
+
+ [Return value]
+
+ sal_Bool TRUE
+ The event has been processed and is not intended to
+ be forwarded to StatusBar
+
+ FALSE
+ The event was not processed and is to be
+ be forwarded to StatusBar
+*/
+
+{
+ return false;
+}
+
+
+void SfxStatusBarControl::Command( const CommandEvent& )
+
+/* [Description]
+
+ This virtual method is called when a CommandEvent is received by
+ SfxStatusBarControl.
+
+ The default implementation is empty.
+*/
+
+{
+}
+
+
+void SfxStatusBarControl::Click()
+
+/* [Description]
+
+ This virtual method is called when the user clicks on the
+ field in the status row that belongs to this control.
+*/
+
+{
+ css::uno::Sequence< css::beans::PropertyValue > aArgs;
+ execute( aArgs );
+}
+
+
+void SfxStatusBarControl::Paint
+(
+ const UserDrawEvent& /* Reference to an UserDrawEvent */
+)
+
+/* [Description]
+
+ This virtual method is called to paint the contents if the field
+ at hand is marked with StatusBarItemBits::UserDraw. The output must be obtained
+ within the Rectangle of rUDEvt.GetRect() by the OutputDevice
+ given by rUDEvt.GetDevice().
+
+ The default implementation is empty.
+*/
+
+{
+}
+
+
+rtl::Reference<SfxStatusBarControl> SfxStatusBarControl::CreateControl
+(
+ sal_uInt16 nSlotID,
+ sal_uInt16 nStbId,
+ StatusBar* pBar,
+ SfxModule const * pMod
+)
+{
+ SolarMutexGuard aGuard;
+ SfxApplication *pApp = SfxGetpApp();
+
+ SfxSlotPool *pSlotPool;
+ if ( pMod )
+ pSlotPool = pMod->GetSlotPool();
+ else
+ pSlotPool = &SfxSlotPool::GetSlotPool();
+
+ const std::type_info* aSlotType = pSlotPool->GetSlotType(nSlotID);
+ if ( aSlotType )
+ {
+ if ( pMod )
+ {
+ SfxStbCtrlFactory *pFact = pMod->GetStbCtrlFactory(*aSlotType, nSlotID);
+ if ( pFact )
+ return pFact->pCtor( nSlotID, nStbId, *pBar );
+ }
+
+ SfxStbCtrlFactory* pFact = pApp->GetStbCtrlFactory(*aSlotType, nSlotID);
+ if (pFact)
+ return pFact->pCtor( nSlotID, nStbId, *pBar );
+ }
+
+ return nullptr;
+}
+
+
+void SfxStatusBarControl::RegisterStatusBarControl(SfxModule* pMod, const SfxStbCtrlFactory& rFact)
+{
+ SfxGetpApp()->RegisterStatusBarControl_Impl( pMod, rFact );
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/styles/StyleManager.cxx b/sfx2/source/styles/StyleManager.cxx
new file mode 100644
index 000000000..4504be9db
--- /dev/null
+++ b/sfx2/source/styles/StyleManager.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/.
+ */
+
+#include <sfx2/StyleManager.hxx>
+#include <sfx2/objsh.hxx>
+
+namespace sfx2
+{
+SfxStyleSheetBase* StyleManager::Search(std::u16string_view rStyleName, SfxStyleFamily eFamily)
+{
+ SfxStyleSheetBasePool* pPool = mrShell.GetStyleSheetPool();
+ if (!pPool)
+ return nullptr;
+
+ SfxStyleSheetBase* pStyle = pPool->First(eFamily);
+ while (pStyle)
+ {
+ if (rStyleName == pStyle->GetName())
+ return pStyle;
+
+ pStyle = pPool->Next();
+ }
+
+ return nullptr;
+}
+
+} // end namespace sfx2
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/toolbox/tbxitem.cxx b/sfx2/source/toolbox/tbxitem.cxx
new file mode 100644
index 000000000..e61503f94
--- /dev/null
+++ b/sfx2/source/toolbox/tbxitem.cxx
@@ -0,0 +1,506 @@
+/* -*- 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 <com/sun/star/uno/Reference.h>
+#include <com/sun/star/frame/XFrame.hpp>
+#include <com/sun/star/awt/XWindow.hpp>
+#include <com/sun/star/util/URL.hpp>
+#include <com/sun/star/util/URLTransformer.hpp>
+#include <com/sun/star/util/XURLTransformer.hpp>
+#include <com/sun/star/frame/XController.hpp>
+#include <com/sun/star/frame/XDispatchProvider.hpp>
+#include <com/sun/star/lang/XUnoTunnel.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/frame/status/ItemStatus.hpp>
+#include <com/sun/star/ui/XUIElementFactory.hpp>
+#include <com/sun/star/frame/status/Visibility.hpp>
+#include <svl/eitem.hxx>
+#include <svl/stritem.hxx>
+#include <svl/intitem.hxx>
+#include <svl/visitem.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/toolbox.hxx>
+
+#include <comphelper/processfactory.hxx>
+#include <comphelper/servicehelper.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+
+#include <vcl/InterimItemWindow.hxx>
+#include <sfx2/tbxctrl.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/msg.hxx>
+#include <sfx2/msgpool.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/module.hxx>
+#include <sfx2/app.hxx>
+#include <unoctitm.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::frame;
+using namespace ::com::sun::star::frame::status;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::util;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::ui;
+
+
+SFX_IMPL_TOOLBOX_CONTROL_ARG(SfxToolBoxControl, SfxStringItem, true);
+
+rtl::Reference<svt::ToolboxController> SfxToolBoxControllerFactory( const Reference< XFrame >& rFrame, ToolBox* pToolbox, ToolBoxItemId nID, const OUString& aCommandURL )
+{
+ SolarMutexGuard aGuard;
+
+ URL aTargetURL;
+ aTargetURL.Complete = aCommandURL;
+ Reference < XURLTransformer > xTrans( URLTransformer::create( ::comphelper::getProcessComponentContext() ) );
+ xTrans->parseStrict( aTargetURL );
+ if ( !aTargetURL.Arguments.isEmpty() )
+ return nullptr;
+
+ Reference < XController > xController;
+ Reference < XModel > xModel;
+ if ( rFrame.is() )
+ {
+ xController = rFrame->getController();
+ if ( xController.is() )
+ xModel = xController->getModel();
+ }
+
+ SfxObjectShell* pObjShell = SfxObjectShell::GetShellFromComponent(xModel);
+ SfxModule* pModule = pObjShell ? pObjShell->GetModule() : nullptr;
+ SfxSlotPool* pSlotPool = nullptr;
+
+ if ( pModule )
+ pSlotPool = pModule->GetSlotPool();
+ else
+ pSlotPool = &(SfxSlotPool::GetSlotPool());
+
+ const SfxSlot* pSlot = pSlotPool->GetUnoSlot( aTargetURL.Path );
+ if ( pSlot )
+ {
+ sal_uInt16 nSlotId = pSlot->GetSlotId();
+ if ( nSlotId > 0 )
+ return SfxToolBoxControl::CreateControl( nSlotId, nID, pToolbox, pModule );
+ }
+
+ return nullptr;
+}
+
+struct SfxToolBoxControl_Impl
+{
+ VclPtr<ToolBox> pBox;
+ bool bShowString;
+ ToolBoxItemId nTbxId;
+ sal_uInt16 nSlotId;
+};
+
+SfxToolBoxControl::SfxToolBoxControl(
+ sal_uInt16 nSlotID,
+ ToolBoxItemId nID,
+ ToolBox& rBox,
+ bool bShowStringItems )
+ : pImpl( new SfxToolBoxControl_Impl )
+{
+ pImpl->pBox = &rBox;
+ pImpl->bShowString = bShowStringItems;
+ pImpl->nTbxId = nID;
+ pImpl->nSlotId = nSlotID;
+}
+
+
+SfxToolBoxControl::~SfxToolBoxControl()
+{
+}
+
+
+ToolBox& SfxToolBoxControl::GetToolBox() const
+{
+ return *pImpl->pBox;
+}
+ToolBoxItemId SfxToolBoxControl::GetId() const
+{
+ return pImpl->nTbxId;
+}
+unsigned short SfxToolBoxControl::GetSlotId() const
+{
+ return pImpl->nSlotId;
+}
+
+
+void SAL_CALL SfxToolBoxControl::dispose()
+{
+ if ( m_bDisposed )
+ return;
+
+ svt::ToolboxController::dispose();
+
+ // Remove and destroy our item window at our toolbox
+ SolarMutexGuard aGuard;
+ VclPtr< vcl::Window > pWindow = pImpl->pBox->GetItemWindow( pImpl->nTbxId );
+ pImpl->pBox->SetItemWindow( pImpl->nTbxId, nullptr );
+ pWindow.disposeAndClear();
+}
+
+
+void SfxToolBoxControl::RegisterToolBoxControl( SfxModule* pMod, const SfxTbxCtrlFactory& rFact)
+{
+ SfxGetpApp()->RegisterToolBoxControl_Impl( pMod, rFact );
+}
+
+rtl::Reference<SfxToolBoxControl> SfxToolBoxControl::CreateControl( sal_uInt16 nSlotId, ToolBoxItemId nTbxId, ToolBox *pBox, SfxModule const * pMod )
+{
+ SolarMutexGuard aGuard;
+
+ SfxApplication *pApp = SfxGetpApp();
+
+ SfxSlotPool *pSlotPool;
+ if ( pMod )
+ pSlotPool = pMod->GetSlotPool();
+ else
+ pSlotPool = &SfxSlotPool::GetSlotPool();
+ const std::type_info* aSlotType = pSlotPool->GetSlotType( nSlotId );
+ if ( aSlotType )
+ {
+ if ( pMod )
+ {
+ SfxTbxCtrlFactory *pFact = pMod->GetTbxCtrlFactory(*aSlotType, nSlotId);
+ if ( pFact )
+ return pFact->pCtor( nSlotId, nTbxId, *pBox );
+ }
+
+ SfxTbxCtrlFactory* pFact = pApp->GetTbxCtrlFactory(*aSlotType, nSlotId);
+ if (pFact)
+ return pFact->pCtor( nSlotId, nTbxId, *pBox );
+ }
+
+ return nullptr;
+}
+
+SfxItemState SfxToolBoxControl::GetItemState(
+ const SfxPoolItem* pState )
+/* [Description]
+
+ Static method for determining the status of the SfxPoolItem-pointer,
+ used in the method <SfxControllerItem::StateChanged(const SfxPoolItem*)>.
+
+ [Return value]
+
+ SfxItemState SfxItemState::UNKNOWN
+ Enabled, however no further status information is available.
+ Typical for <Slot>s, which are temporarily disabled a
+ anyway but other than that do not change their appearance.
+
+ SfxItemState::DISABLED
+ Disabled, no further status information is available.
+ All other displayed values should be reset to the default
+ if possible.
+
+ SfxItemState::DONTCARE
+ Enabled but there were only ambiguous values available
+ (i.e. none that could be queried).
+
+ SfxItemState::DEFAULT
+ Enabled and with available values which can be queried
+ through'pState'. The type is thus by the Slot clearly
+ defined in the entire Program.
+*/
+
+{
+ return !pState
+ ? SfxItemState::DISABLED
+ : IsInvalidItem(pState)
+ ? SfxItemState::DONTCARE
+ : pState->IsVoidItem() && !pState->Which()
+ ? SfxItemState::UNKNOWN
+ : SfxItemState::DEFAULT;
+}
+
+void SfxToolBoxControl::Dispatch(
+ const Reference< XDispatchProvider >& rProvider,
+ const OUString& rCommand,
+ Sequence< ::PropertyValue > const & aArgs )
+{
+ if ( rProvider.is() )
+ {
+ css::util::URL aTargetURL;
+ aTargetURL.Complete = rCommand;
+ Reference < XURLTransformer > xTrans( URLTransformer::create( ::comphelper::getProcessComponentContext() ) );
+ xTrans->parseStrict( aTargetURL );
+
+ Reference < XDispatch > xDispatch = rProvider->queryDispatch( aTargetURL, OUString(), 0 );
+ if ( xDispatch.is() )
+ xDispatch->dispatch( aTargetURL, aArgs );
+ }
+}
+
+void SfxToolBoxControl::Dispatch( const OUString& aCommand, css::uno::Sequence< css::beans::PropertyValue > const & aArgs )
+{
+ Reference < XController > xController;
+
+ SolarMutexGuard aGuard;
+ if ( getFrameInterface().is() )
+ xController = getFrameInterface()->getController();
+
+ Reference < XDispatchProvider > xProvider( xController, UNO_QUERY );
+ if ( xProvider.is() )
+ {
+ css::util::URL aTargetURL;
+ aTargetURL.Complete = aCommand;
+ getURLTransformer()->parseStrict( aTargetURL );
+
+ Reference < XDispatch > xDispatch = xProvider->queryDispatch( aTargetURL, OUString(), 0 );
+ if ( xDispatch.is() )
+ xDispatch->dispatch( aTargetURL, aArgs );
+ }
+}
+
+// XStatusListener
+void SAL_CALL SfxToolBoxControl::statusChanged( const FeatureStateEvent& rEvent )
+{
+ SfxViewFrame* pViewFrame = nullptr;
+ Reference < XController > xController;
+
+ SolarMutexGuard aGuard;
+ if ( getFrameInterface().is() )
+ xController = getFrameInterface()->getController();
+
+ Reference < XDispatchProvider > xProvider( xController, UNO_QUERY );
+ if ( xProvider.is() )
+ {
+ Reference < XDispatch > xDisp = xProvider->queryDispatch( rEvent.FeatureURL, OUString(), 0 );
+ if ( xDisp.is() )
+ {
+ Reference< XUnoTunnel > xTunnel( xDisp, UNO_QUERY );
+ if (auto pDisp = comphelper::getFromUnoTunnel<SfxOfficeDispatch>(xTunnel))
+ pViewFrame = pDisp->GetDispatcher_Impl()->GetFrame();
+ }
+ }
+
+ sal_uInt16 nSlotId = 0;
+ SfxSlotPool& rPool = SfxSlotPool::GetSlotPool( pViewFrame );
+ const SfxSlot* pSlot = rPool.GetUnoSlot( rEvent.FeatureURL.Path );
+ if ( pSlot )
+ nSlotId = pSlot->GetSlotId();
+ else if ( m_aCommandURL == rEvent.FeatureURL.Path )
+ nSlotId = GetSlotId();
+
+ if ( nSlotId <= 0 )
+ return;
+
+ if ( rEvent.Requery )
+ svt::ToolboxController::statusChanged( rEvent );
+ else
+ {
+ SfxItemState eState = SfxItemState::DISABLED;
+ std::unique_ptr<SfxPoolItem> pItem;
+ if ( rEvent.IsEnabled )
+ {
+ eState = SfxItemState::DEFAULT;
+ css::uno::Type aType = rEvent.State.getValueType();
+
+ if ( aType == cppu::UnoType<void>::get() )
+ {
+ pItem.reset(new SfxVoidItem( nSlotId ));
+ eState = SfxItemState::UNKNOWN;
+ }
+ else if ( aType == cppu::UnoType<bool>::get() )
+ {
+ bool bTemp = false;
+ rEvent.State >>= bTemp ;
+ pItem.reset(new SfxBoolItem( nSlotId, bTemp ));
+ }
+ else if ( aType == ::cppu::UnoType< ::cppu::UnoUnsignedShortType >::get())
+ {
+ sal_uInt16 nTemp = 0;
+ rEvent.State >>= nTemp ;
+ pItem.reset(new SfxUInt16Item( nSlotId, nTemp ));
+ }
+ else if ( aType == cppu::UnoType<sal_uInt32>::get() )
+ {
+ sal_uInt32 nTemp = 0;
+ rEvent.State >>= nTemp ;
+ pItem.reset(new SfxUInt32Item( nSlotId, nTemp ));
+ }
+ else if ( aType == cppu::UnoType<OUString>::get() )
+ {
+ OUString sTemp ;
+ rEvent.State >>= sTemp ;
+ pItem.reset(new SfxStringItem( nSlotId, sTemp ));
+ }
+ else if ( aType == cppu::UnoType< css::frame::status::ItemStatus>::get() )
+ {
+ ItemStatus aItemStatus;
+ rEvent.State >>= aItemStatus;
+ SfxItemState tmpState = static_cast<SfxItemState>(aItemStatus.State);
+ // make sure no-one tries to send us a combination of states
+ if (tmpState != SfxItemState::UNKNOWN && tmpState != SfxItemState::DISABLED &&
+ tmpState != SfxItemState::DONTCARE &&
+ tmpState != SfxItemState::DEFAULT && tmpState != SfxItemState::SET)
+ throw css::uno::RuntimeException("unknown status");
+ eState = tmpState;
+ pItem.reset(new SfxVoidItem( nSlotId ));
+ }
+ else if ( aType == cppu::UnoType< css::frame::status::Visibility>::get() )
+ {
+ Visibility aVisibilityStatus;
+ rEvent.State >>= aVisibilityStatus;
+ pItem.reset(new SfxVisibilityItem( nSlotId, aVisibilityStatus.bVisible ));
+ }
+ else
+ {
+ if ( pSlot )
+ pItem = pSlot->GetType()->CreateItem();
+ if ( pItem )
+ {
+ pItem->SetWhich( nSlotId );
+ pItem->PutValue( rEvent.State, 0 );
+ }
+ else
+ pItem.reset(new SfxVoidItem( nSlotId ));
+ }
+ }
+
+ StateChangedAtToolBoxControl( nSlotId, eState, pItem.get() );
+ }
+}
+
+// XToolbarController
+void SAL_CALL SfxToolBoxControl::execute( sal_Int16 KeyModifier )
+{
+ SolarMutexGuard aGuard;
+ Select( static_cast<sal_uInt16>(KeyModifier) );
+}
+
+void SAL_CALL SfxToolBoxControl::click()
+{
+ SolarMutexGuard aGuard;
+ Click();
+}
+
+void SAL_CALL SfxToolBoxControl::doubleClick()
+{
+ SolarMutexGuard aGuard;
+ DoubleClick();
+}
+
+Reference< css::awt::XWindow > SAL_CALL SfxToolBoxControl::createPopupWindow()
+{
+ SolarMutexGuard aGuard;
+ CreatePopupWindow();
+ return nullptr;
+}
+
+Reference< css::awt::XWindow > SAL_CALL SfxToolBoxControl::createItemWindow( const Reference< css::awt::XWindow >& rParent )
+{
+ SolarMutexGuard aGuard;
+ return VCLUnoHelper::GetInterface( CreateItemWindow( VCLUnoHelper::GetWindow( rParent )));
+}
+
+void SfxToolBoxControl::StateChangedAtToolBoxControl
+(
+ sal_uInt16 /*nSlotId*/,
+ SfxItemState eState,
+ const SfxPoolItem* pState
+)
+{
+ DBG_ASSERT( pImpl->pBox != nullptr, "setting state to dangling ToolBox" );
+
+ // enabled/disabled-Flag correcting the lump sum
+ pImpl->pBox->EnableItem( GetId(), eState != SfxItemState::DISABLED );
+
+ ToolBoxItemBits nItemBits = pImpl->pBox->GetItemBits( GetId() );
+ nItemBits &= ~ToolBoxItemBits::CHECKABLE;
+ ::TriState eTri = TRISTATE_FALSE;
+ switch ( eState )
+ {
+ case SfxItemState::DEFAULT:
+ if ( pState )
+ {
+ if ( auto pBoolItem = dynamic_cast< const SfxBoolItem* >(pState) )
+ {
+ // BoolItem for checking
+ if ( pBoolItem->GetValue() )
+ eTri = TRISTATE_TRUE;
+ nItemBits |= ToolBoxItemBits::CHECKABLE;
+ }
+ else if ( auto pEnumItem = dynamic_cast< const SfxEnumItemInterface *>( pState ) )
+ {
+ if (pEnumItem->HasBoolValue())
+ {
+ // EnumItem is handled as Bool
+ if (pEnumItem->GetBoolValue())
+ eTri = TRISTATE_TRUE;
+ nItemBits |= ToolBoxItemBits::CHECKABLE;
+ }
+ }
+ else if ( pImpl->bShowString )
+ {
+ if (auto pStringItem = dynamic_cast< const SfxStringItem *>( pState ) )
+ pImpl->pBox->SetItemText(GetId(), pStringItem->GetValue() );
+ }
+ }
+ break;
+
+ case SfxItemState::DONTCARE:
+ {
+ eTri = TRISTATE_INDET;
+ nItemBits |= ToolBoxItemBits::CHECKABLE;
+ }
+ break;
+
+ default: break; // do nothing
+ }
+
+ pImpl->pBox->SetItemState( GetId(), eTri );
+ pImpl->pBox->SetItemBits( GetId(), nItemBits );
+}
+
+
+void SfxToolBoxControl::Select( sal_uInt16 nSelectModifier )
+{
+ svt::ToolboxController::execute( nSelectModifier );
+}
+
+
+void SfxToolBoxControl::DoubleClick()
+{
+}
+
+
+void SfxToolBoxControl::Click()
+{
+}
+
+void SfxToolBoxControl::CreatePopupWindow()
+{
+}
+
+VclPtr<InterimItemWindow> SfxToolBoxControl::CreateItemWindow(vcl::Window*)
+{
+ return VclPtr<InterimItemWindow>();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/toolbox/weldutils.cxx b/sfx2/source/toolbox/weldutils.cxx
new file mode 100644
index 000000000..5c1f1c58f
--- /dev/null
+++ b/sfx2/source/toolbox/weldutils.cxx
@@ -0,0 +1,203 @@
+/* -*- 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 <officecfg/Office/Common.hxx>
+#include <com/sun/star/frame/XSubToolbarController.hpp>
+#include <sidebar/ControllerFactory.hxx>
+#include <sfx2/weldutils.hxx>
+#include <vcl/commandinfoprovider.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/weld.hxx>
+
+namespace
+{
+bool lcl_RTLizeCommandURL(OUString& rCommandURL)
+{
+ if (rCommandURL == ".uno:ParaLeftToRight")
+ {
+ rCommandURL = ".uno:ParaRightToLeft";
+ return true;
+ }
+ if (rCommandURL == ".uno:ParaRightToLeft")
+ {
+ rCommandURL = ".uno:ParaLeftToRight";
+ return true;
+ }
+ if (rCommandURL == ".uno:LeftPara")
+ {
+ rCommandURL = ".uno:RightPara";
+ return true;
+ }
+ if (rCommandURL == ".uno:RightPara")
+ {
+ rCommandURL = ".uno:LeftPara";
+ return true;
+ }
+ if (rCommandURL == ".uno:AlignLeft")
+ {
+ rCommandURL = ".uno:AlignRight";
+ return true;
+ }
+ if (rCommandURL == ".uno:AlignRight")
+ {
+ rCommandURL = ".uno:AlignLeft";
+ return true;
+ }
+ return false;
+}
+}
+
+// for now all controllers are in the sidebar
+vcl::ImageType ToolbarUnoDispatcher::GetIconSize()
+{
+ vcl::ImageType eType = vcl::ImageType::Size16;
+ switch (static_cast<ToolBoxButtonSize>(officecfg::Office::Common::Misc::SidebarIconSize::get()))
+ {
+ case ToolBoxButtonSize::Large:
+ eType = vcl::ImageType::Size26;
+ break;
+ case ToolBoxButtonSize::Size32:
+ eType = vcl::ImageType::Size32;
+ break;
+ case ToolBoxButtonSize::DontCare:
+ case ToolBoxButtonSize::Small:
+ break;
+ }
+ return eType;
+}
+
+ToolbarUnoDispatcher::ToolbarUnoDispatcher(weld::Toolbar& rToolbar, weld::Builder& rBuilder,
+ const css::uno::Reference<css::frame::XFrame>& rFrame,
+ bool bSideBar)
+ : m_xFrame(rFrame)
+ , m_pToolbar(&rToolbar)
+ , m_pBuilder(&rBuilder)
+ , m_bSideBar(bSideBar)
+{
+ rToolbar.connect_clicked(LINK(this, ToolbarUnoDispatcher, SelectHdl));
+ rToolbar.connect_menu_toggled(LINK(this, ToolbarUnoDispatcher, ToggleMenuHdl));
+
+ OUString aModuleName(vcl::CommandInfoProvider::GetModuleIdentifier(rFrame));
+ vcl::ImageType eSize = GetIconSize();
+ rToolbar.set_icon_size(eSize);
+
+ bool bRTL = AllSettings::GetLayoutRTL();
+
+ for (int i = 0, nItems = rToolbar.get_n_items(); i < nItems; ++i)
+ {
+ OString sIdent(rToolbar.get_item_ident(i));
+ if (!sIdent.startsWith(".uno:"))
+ continue;
+ OUString sCommand = OUString::fromUtf8(sIdent);
+ if (bRTL && lcl_RTLizeCommandURL(sCommand))
+ rToolbar.set_item_ident(i, sCommand.toUtf8());
+
+ auto aProperties = vcl::CommandInfoProvider::GetCommandProperties(sCommand, aModuleName);
+ OUString aLabel(vcl::CommandInfoProvider::GetLabelForCommand(aProperties));
+ rToolbar.set_item_label(i, aLabel);
+ OUString aTooltip(
+ vcl::CommandInfoProvider::GetTooltipForCommand(sCommand, aProperties, rFrame));
+ rToolbar.set_item_tooltip_text(i, aTooltip);
+ auto xImage(vcl::CommandInfoProvider::GetXGraphicForCommand(sCommand, rFrame, eSize));
+ rToolbar.set_item_image(i, xImage);
+
+ CreateController(sCommand);
+ }
+
+ m_aToolbarOptions.AddListenerLink(LINK(this, ToolbarUnoDispatcher, ChangedIconSizeHandler));
+}
+
+void ToolbarUnoDispatcher::CreateController(const OUString& rCommand)
+{
+ css::uno::Reference<css::frame::XToolbarController> xController(
+ sfx2::sidebar::ControllerFactory::CreateToolBoxController(
+ *m_pToolbar, *m_pBuilder, rCommand, m_xFrame, m_xFrame->getController(), m_bSideBar));
+
+ if (xController.is())
+ maControllers.insert(std::make_pair(rCommand, xController));
+}
+
+css::uno::Reference<css::frame::XToolbarController>
+ToolbarUnoDispatcher::GetControllerForCommand(const OUString& rCommand) const
+{
+ ControllerContainer::const_iterator iController(maControllers.find(rCommand));
+ if (iController != maControllers.end())
+ return iController->second;
+
+ return css::uno::Reference<css::frame::XToolbarController>();
+}
+
+IMPL_LINK(ToolbarUnoDispatcher, SelectHdl, const OString&, rCommand, void)
+{
+ css::uno::Reference<css::frame::XToolbarController> xController(
+ GetControllerForCommand(OUString::fromUtf8(rCommand)));
+
+ if (xController.is())
+ xController->execute(0);
+}
+
+IMPL_LINK(ToolbarUnoDispatcher, ToggleMenuHdl, const OString&, rCommand, void)
+{
+ css::uno::Reference<css::frame::XToolbarController> xController(
+ GetControllerForCommand(OUString::fromUtf8(rCommand)));
+
+ if (xController.is())
+ xController->click();
+}
+
+IMPL_LINK_NOARG(ToolbarUnoDispatcher, ChangedIconSizeHandler, LinkParamNone*, void)
+{
+ vcl::ImageType eSize = GetIconSize();
+ m_pToolbar->set_icon_size(eSize);
+
+ for (int i = 0, nItems = m_pToolbar->get_n_items(); i < nItems; ++i)
+ {
+ OUString sCommand = OUString::fromUtf8(m_pToolbar->get_item_ident(i));
+ auto xImage(vcl::CommandInfoProvider::GetXGraphicForCommand(sCommand, m_xFrame, eSize));
+ m_pToolbar->set_item_image(i, xImage);
+ }
+
+ for (auto const& it : maControllers)
+ {
+ css::uno::Reference<css::frame::XSubToolbarController> xController(it.second,
+ css::uno::UNO_QUERY);
+ if (xController.is() && xController->opensSubToolbar())
+ {
+ // The button should show the last function that was selected from the
+ // dropdown. The controller should know better than us what it was.
+ xController->updateImage();
+ }
+ }
+}
+
+void ToolbarUnoDispatcher::dispose()
+{
+ if (!m_pToolbar)
+ return;
+
+ m_aToolbarOptions.RemoveListenerLink(LINK(this, ToolbarUnoDispatcher, ChangedIconSizeHandler));
+
+ ControllerContainer aControllers;
+ aControllers.swap(maControllers);
+ for (auto const& controller : aControllers)
+ {
+ css::uno::Reference<css::lang::XComponent> xComponent(controller.second,
+ css::uno::UNO_QUERY);
+ if (xComponent.is())
+ xComponent->dispose();
+ }
+
+ m_pToolbar->connect_clicked(Link<const OString&, void>());
+ m_pToolbar = nullptr;
+ m_pBuilder = nullptr;
+}
+
+ToolbarUnoDispatcher::~ToolbarUnoDispatcher() { dispose(); }
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/view/classificationcontroller.cxx b/sfx2/source/view/classificationcontroller.cxx
new file mode 100644
index 000000000..1cda4a41c
--- /dev/null
+++ b/sfx2/source/view/classificationcontroller.cxx
@@ -0,0 +1,364 @@
+/* -*- 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 <cppuhelper/implbase.hxx>
+#include <svtools/toolboxcontroller.hxx>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/document/XDocumentProperties.hpp>
+
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/frame/XFrame.hpp>
+
+#include <toolkit/helper/vclunohelper.hxx>
+#include <vcl/InterimItemWindow.hxx>
+#include <sfx2/classificationhelper.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/strings.hrc>
+#include <sfx2/sfxresid.hxx>
+#include <vcl/event.hxx>
+#include <vcl/toolbox.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/vclptr.hxx>
+#include <vcl/weld.hxx>
+
+#include <cppuhelper/supportsservice.hxx>
+#include <comphelper/propertysequence.hxx>
+#include <comphelper/dispatchcommand.hxx>
+#include <comphelper/configurationlistener.hxx>
+
+using namespace com::sun::star;
+
+namespace sfx2
+{
+
+namespace {
+
+class ClassificationCategoriesController;
+
+}
+
+using ClassificationPropertyListenerBase = comphelper::ConfigurationListenerProperty<OUString>;
+
+namespace {
+
+/// Listens to configuration changes, so no restart is needed after setting the classification path.
+class ClassificationPropertyListener : public ClassificationPropertyListenerBase
+{
+ ClassificationCategoriesController& m_rController;
+
+public:
+ ClassificationPropertyListener(const rtl::Reference<comphelper::ConfigurationListener>& xListener, ClassificationCategoriesController& rController);
+ void setProperty(const uno::Any& rProperty) override;
+};
+
+}
+
+using ClassificationCategoriesControllerBase = cppu::ImplInheritanceHelper<svt::ToolboxController, lang::XServiceInfo>;
+
+namespace {
+
+class ClassificationControl;
+
+/// Controller for .uno:ClassificationApply.
+class ClassificationCategoriesController : public ClassificationCategoriesControllerBase
+{
+ VclPtr<ClassificationControl> m_pClassification;
+ rtl::Reference<comphelper::ConfigurationListener> m_xListener;
+ ClassificationPropertyListener m_aPropertyListener;
+
+ DECL_LINK(SelectHdl, weld::ComboBox&, void);
+
+public:
+ explicit ClassificationCategoriesController(const uno::Reference<uno::XComponentContext>& rContext);
+
+ // XServiceInfo
+ OUString SAL_CALL getImplementationName() override;
+ sal_Bool SAL_CALL supportsService(const OUString& rServiceName) override;
+ uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+
+ // XComponent
+ void SAL_CALL dispose() override;
+
+ // XToolbarController
+ uno::Reference<awt::XWindow> SAL_CALL createItemWindow(const uno::Reference<awt::XWindow>& rParent) override;
+
+ // XStatusListener
+ void SAL_CALL statusChanged(const frame::FeatureStateEvent& rEvent) override;
+
+ void removeEntries();
+};
+
+/// Classification control is the parent of all widgets that belongs to ClassificationCategoriesController.
+class SAL_WARN_UNUSED ClassificationControl final : public InterimItemWindow
+{
+ std::unique_ptr<weld::Label> m_xLabel;
+ std::unique_ptr<weld::ComboBox> m_xCategory;
+
+ DECL_LINK(KeyInputHdl, const KeyEvent&, bool);
+
+ void SetOptimalSize();
+ void DataChanged(const DataChangedEvent& rEvent) override;
+
+public:
+ explicit ClassificationControl(vcl::Window* pParent);
+ ~ClassificationControl() override;
+ void dispose() override;
+ weld::ComboBox& getCategory()
+ {
+ return *m_xCategory;
+ }
+ void set_sensitive(bool bSensitive)
+ {
+ Enable(bSensitive);
+ m_xContainer->set_sensitive(bSensitive);
+ }
+ static sfx::ClassificationCreationOrigin getExistingClassificationOrigin();
+ void toggleInteractivityOnOrigin();
+ void setCategoryStateFromPolicy(const SfxClassificationHelper & rHelper);
+};
+
+OUString const & getCategoryType()
+{
+ return SfxClassificationHelper::policyTypeToString(SfxClassificationHelper::getPolicyType());
+}
+
+} // end anonymous namespace
+
+ClassificationPropertyListener::ClassificationPropertyListener(const rtl::Reference<comphelper::ConfigurationListener>& xListener, ClassificationCategoriesController& rController)
+ : ClassificationPropertyListenerBase(xListener, "WritePath")
+ , m_rController(rController)
+{
+}
+
+void ClassificationPropertyListener::setProperty(const uno::Any& /*rProperty*/)
+{
+ // So that its gets re-filled with entries from the new policy.
+ m_rController.removeEntries();
+}
+
+ClassificationCategoriesController::ClassificationCategoriesController(const uno::Reference<uno::XComponentContext>& rContext)
+ : ClassificationCategoriesControllerBase(rContext, uno::Reference<frame::XFrame>(), OUString(".uno:ClassificationApply"))
+ , m_pClassification(nullptr)
+ , m_xListener(new comphelper::ConfigurationListener("/org.openoffice.Office.Paths/Paths/Classification"))
+ , m_aPropertyListener(m_xListener, *this)
+{
+
+}
+
+OUString ClassificationCategoriesController::getImplementationName()
+{
+ return "com.sun.star.comp.sfx2.ClassificationCategoriesController";
+}
+
+sal_Bool ClassificationCategoriesController::supportsService(const OUString& rServiceName)
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+uno::Sequence<OUString> ClassificationCategoriesController::getSupportedServiceNames()
+{
+ return { "com.sun.star.frame.ToolbarController" };
+}
+
+void ClassificationCategoriesController::dispose()
+{
+ SolarMutexGuard aSolarMutexGuard;
+
+ svt::ToolboxController::dispose();
+ m_pClassification.disposeAndClear();
+ m_aPropertyListener.dispose();
+ m_xListener->dispose();
+}
+
+uno::Reference<awt::XWindow> ClassificationCategoriesController::createItemWindow(const uno::Reference<awt::XWindow>& rParent)
+{
+ VclPtr<vcl::Window> pParent = VCLUnoHelper::GetWindow(rParent);
+ auto pToolbar = dynamic_cast<ToolBox*>(pParent.get());
+ if (pToolbar)
+ {
+ m_pClassification = VclPtr<ClassificationControl>::Create(pToolbar);
+ m_pClassification->getCategory().connect_changed(LINK(this, ClassificationCategoriesController, SelectHdl));
+ m_pClassification->Show();
+ }
+
+ return VCLUnoHelper::GetInterface(m_pClassification);
+}
+
+IMPL_LINK(ClassificationCategoriesController, SelectHdl, weld::ComboBox&, rCategory, void)
+{
+ m_pClassification->toggleInteractivityOnOrigin();
+
+ if (ClassificationControl::getExistingClassificationOrigin() == sfx::ClassificationCreationOrigin::MANUAL)
+ {
+ SfxObjectShell* pObjectShell = SfxObjectShell::Current();
+ if (!pObjectShell)
+ return;
+ SfxClassificationHelper aHelper(pObjectShell->getDocProperties());
+ m_pClassification->setCategoryStateFromPolicy(aHelper);
+ }
+ else
+ {
+ OUString aEntry = rCategory.get_active_text();
+
+ const OUString& aType = getCategoryType();
+ uno::Sequence<beans::PropertyValue> aPropertyValues(comphelper::InitPropertySequence({
+ {"Name", uno::Any(aEntry)},
+ {"Type", uno::Any(aType)},
+ }));
+ comphelper::dispatchCommand(".uno:ClassificationApply", aPropertyValues);
+ }
+}
+
+void ClassificationCategoriesController::statusChanged(const frame::FeatureStateEvent& /*rEvent*/)
+{
+ if (!m_pClassification)
+ return;
+
+ SfxObjectShell* pObjectShell = SfxObjectShell::Current();
+ if (!pObjectShell)
+ return;
+
+ SfxClassificationHelper aHelper(pObjectShell->getDocProperties());
+
+ //toggle if the pop-up is enabled/disabled
+ m_pClassification->toggleInteractivityOnOrigin();
+
+ // check if classification was set via the advanced dialog
+ if (ClassificationControl::getExistingClassificationOrigin() != sfx::ClassificationCreationOrigin::MANUAL)
+ {
+ weld::ComboBox& rCategories = m_pClassification->getCategory();
+ if (rCategories.get_count() == 0)
+ {
+ std::vector<OUString> aNames = aHelper.GetBACNames();
+ for (const OUString& rName : aNames)
+ rCategories.append_text(rName);
+ }
+ }
+
+ // Restore state based on the doc. model.
+ m_pClassification->setCategoryStateFromPolicy(aHelper);
+
+}
+
+void ClassificationCategoriesController::removeEntries()
+{
+ m_pClassification->getCategory().clear();
+}
+
+ClassificationControl::ClassificationControl(vcl::Window* pParent)
+ : InterimItemWindow(pParent, "sfx/ui/classificationbox.ui", "ClassificationBox")
+ , m_xLabel(m_xBuilder->weld_label("label"))
+ , m_xCategory(m_xBuilder->weld_combo_box("combobox"))
+{
+ InitControlBase(m_xCategory.get());
+
+ m_xCategory->connect_key_press(LINK(this, ClassificationControl, KeyInputHdl));
+
+ // WB_NOLABEL means here that the control won't be replaced with a label
+ // when it wouldn't fit the available space.
+ SetStyle(GetStyle() | WB_DIALOGCONTROL | WB_NOLABEL);
+
+ OUString aText;
+ switch (SfxClassificationHelper::getPolicyType())
+ {
+ case SfxClassificationPolicyType::IntellectualProperty:
+ aText = SfxResId(STR_CLASSIFIED_INTELLECTUAL_PROPERTY);
+ break;
+ case SfxClassificationPolicyType::NationalSecurity:
+ aText = SfxResId(STR_CLASSIFIED_NATIONAL_SECURITY);
+ break;
+ case SfxClassificationPolicyType::ExportControl:
+ aText = SfxResId(STR_CLASSIFIED_EXPORT_CONTROL);
+ break;
+ }
+
+ m_xLabel->set_label(aText);
+
+ // Same as SvxColorDockingWindow.
+ const Size aLogicalAttrSize(150, 0);
+ Size aSize(LogicToPixel(aLogicalAttrSize, MapMode(MapUnit::MapAppFont)));
+ m_xCategory->set_size_request(aSize.Width() - m_xLabel->get_preferred_size().Width(), -1);
+
+ SetOptimalSize();
+}
+
+IMPL_LINK(ClassificationControl, KeyInputHdl, const KeyEvent&, rKEvt, bool)
+{
+ return ChildKeyInput(rKEvt);
+}
+
+ClassificationControl::~ClassificationControl()
+{
+ disposeOnce();
+}
+
+void ClassificationControl::dispose()
+{
+ m_xLabel.reset();
+ m_xCategory.reset();
+ InterimItemWindow::dispose();
+}
+
+void ClassificationControl::SetOptimalSize()
+{
+ SetSizePixel(get_preferred_size());
+}
+
+void ClassificationControl::DataChanged(const DataChangedEvent& rEvent)
+{
+ if ((rEvent.GetType() == DataChangedEventType::SETTINGS) && (rEvent.GetFlags() & AllSettingsFlags::STYLE))
+ SetOptimalSize();
+
+ toggleInteractivityOnOrigin();
+
+ InterimItemWindow::DataChanged(rEvent);
+}
+
+sfx::ClassificationCreationOrigin ClassificationControl::getExistingClassificationOrigin()
+{
+ SfxObjectShell* pObjectShell = SfxObjectShell::Current();
+ if (!pObjectShell)
+ return sfx::ClassificationCreationOrigin::NONE;
+
+ uno::Reference<document::XDocumentProperties> xDocumentProperties = pObjectShell->getDocProperties();
+ uno::Reference<beans::XPropertyContainer> xPropertyContainer = xDocumentProperties->getUserDefinedProperties();
+
+ sfx::ClassificationKeyCreator aKeyCreator(SfxClassificationHelper::getPolicyType());
+ return sfx::getCreationOriginProperty(xPropertyContainer, aKeyCreator);
+}
+
+void ClassificationControl::toggleInteractivityOnOrigin()
+{
+ if (getExistingClassificationOrigin() == sfx::ClassificationCreationOrigin::MANUAL)
+ {
+ set_sensitive(false);
+ }
+ else
+ {
+ set_sensitive(true);
+ }
+}
+
+void ClassificationControl::setCategoryStateFromPolicy(const SfxClassificationHelper & rHelper)
+{
+ const OUString& rCategoryName = rHelper.GetBACName(SfxClassificationHelper::getPolicyType());
+ if (!rCategoryName.isEmpty())
+ {
+ getCategory().set_active_text(rCategoryName);
+ }
+}
+
+} // namespace sfx2
+
+extern "C" SAL_DLLPUBLIC_EXPORT uno::XInterface* com_sun_star_sfx2_ClassificationCategoriesController_get_implementation(uno::XComponentContext* pContext, const uno::Sequence<uno::Any>&)
+{
+ return cppu::acquire(new sfx2::ClassificationCategoriesController(pContext));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/view/classificationhelper.cxx b/sfx2/source/view/classificationhelper.cxx
new file mode 100644
index 000000000..4494e7e7c
--- /dev/null
+++ b/sfx2/source/view/classificationhelper.cxx
@@ -0,0 +1,985 @@
+/* -*- 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/classificationhelper.hxx>
+
+#include <map>
+#include <algorithm>
+#include <iterator>
+
+#include <com/sun/star/beans/XPropertyContainer.hpp>
+#include <com/sun/star/beans/Property.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/document/XDocumentProperties.hpp>
+#include <com/sun/star/xml/sax/Parser.hpp>
+#include <com/sun/star/xml/sax/XDocumentHandler.hpp>
+#include <com/sun/star/xml/sax/SAXParseException.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+
+#include <sal/log.hxx>
+#include <i18nlangtag/languagetag.hxx>
+#include <sfx2/infobar.hxx>
+#include <comphelper/processfactory.hxx>
+#include <unotools/pathoptions.hxx>
+#include <unotools/ucbstreamhelper.hxx>
+#include <unotools/streamwrap.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <sfx2/strings.hrc>
+#include <sfx2/sfxresid.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <tools/datetime.hxx>
+#include <tools/diagnose_ex.h>
+#include <unotools/datetime.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/weld.hxx>
+#include <svl/fstathelper.hxx>
+
+#include <o3tl/string_view.hxx>
+#include <officecfg/Office/Common.hxx>
+
+using namespace com::sun::star;
+
+namespace
+{
+
+const OUString& PROP_BACNAME()
+{
+ static const OUString sProp("BusinessAuthorizationCategory:Name");
+ return sProp;
+}
+
+const OUString& PROP_STARTVALIDITY()
+{
+ static const OUString sProp("Authorization:StartValidity");
+ return sProp;
+}
+
+const OUString& PROP_NONE()
+{
+ static const OUString sProp("None");
+ return sProp;
+}
+
+const OUString& PROP_IMPACTSCALE()
+{
+ static const OUString sProp("Impact:Scale");
+ return sProp;
+}
+
+const OUString& PROP_IMPACTLEVEL()
+{
+ static const OUString sProp("Impact:Level:Confidentiality");
+ return sProp;
+}
+
+const OUString& PROP_PREFIX_EXPORTCONTROL()
+{
+ static const OUString sProp("urn:bails:ExportControl:");
+ return sProp;
+}
+
+const OUString& PROP_PREFIX_NATIONALSECURITY()
+{
+ static const OUString sProp("urn:bails:NationalSecurity:");
+ return sProp;
+}
+
+/// Represents one category of a classification policy.
+class SfxClassificationCategory
+{
+public:
+ /// PROP_BACNAME() is stored separately for easier lookup.
+ OUString m_aName;
+ OUString m_aAbbreviatedName; //< An abbreviation to display instead of m_aName.
+ OUString m_aIdentifier; //< The Identifier of this entry.
+ size_t m_nConfidentiality; //< 0 is the lowest (least-sensitive).
+ std::map<OUString, OUString> m_aLabels;
+};
+
+/// Parses a policy XML conforming to the TSCP BAF schema.
+class SfxClassificationParser : public cppu::WeakImplHelper<xml::sax::XDocumentHandler>
+{
+public:
+ std::vector<SfxClassificationCategory> m_aCategories;
+ std::vector<OUString> m_aMarkings;
+ std::vector<OUString> m_aIPParts;
+ std::vector<OUString> m_aIPPartNumbers;
+
+ OUString m_aPolicyAuthorityName;
+ bool m_bInPolicyAuthorityName = false;
+ OUString m_aPolicyName;
+ bool m_bInPolicyName = false;
+ OUString m_aProgramID;
+ bool m_bInProgramID = false;
+ OUString m_aScale;
+ bool m_bInScale = false;
+ OUString m_aConfidentalityValue;
+ bool m_bInConfidentalityValue = false;
+ OUString m_aIdentifier;
+ bool m_bInIdentifier = false;
+ OUString m_aValue;
+ bool m_bInValue = false;
+
+ /// Pointer to a value in m_aCategories, the currently parsed category.
+ SfxClassificationCategory* m_pCategory = nullptr;
+
+ SfxClassificationParser();
+
+ void SAL_CALL startDocument() override;
+
+ void SAL_CALL endDocument() override;
+
+ void SAL_CALL startElement(const OUString& rName, const uno::Reference<xml::sax::XAttributeList>& xAttribs) override;
+
+ void SAL_CALL endElement(const OUString& rName) override;
+
+ void SAL_CALL characters(const OUString& rChars) override;
+
+ void SAL_CALL ignorableWhitespace(const OUString& rWhitespaces) override;
+
+ void SAL_CALL processingInstruction(const OUString& rTarget, const OUString& rData) override;
+
+ void SAL_CALL setDocumentLocator(const uno::Reference<xml::sax::XLocator>& xLocator) override;
+};
+
+SfxClassificationParser::SfxClassificationParser() = default;
+
+void SAL_CALL SfxClassificationParser::startDocument()
+{
+}
+
+void SAL_CALL SfxClassificationParser::endDocument()
+{
+}
+
+void SAL_CALL SfxClassificationParser::startElement(const OUString& rName, const uno::Reference<xml::sax::XAttributeList>& xAttribs)
+{
+ if (rName == "baf:PolicyAuthorityName")
+ {
+ m_aPolicyAuthorityName.clear();
+ m_bInPolicyAuthorityName = true;
+ }
+ else if (rName == "baf:PolicyName")
+ {
+ m_aPolicyName.clear();
+ m_bInPolicyName = true;
+ }
+ else if (rName == "baf:ProgramID")
+ {
+ m_aProgramID.clear();
+ m_bInProgramID = true;
+ }
+ else if (rName == "baf:BusinessAuthorizationCategory")
+ {
+ const OUString aName = xAttribs->getValueByName("Name");
+ if (!m_pCategory && !aName.isEmpty())
+ {
+ OUString aIdentifier = xAttribs->getValueByName("Identifier");
+
+ // Create a new category and initialize it with the data that's true for all categories.
+ m_aCategories.emplace_back();
+ SfxClassificationCategory& rCategory = m_aCategories.back();
+
+ rCategory.m_aName = aName;
+ // Set the abbreviated name, if any, otherwise fallback on the full name.
+ const OUString aAbbreviatedName = xAttribs->getValueByName("loextAbbreviatedName");
+ rCategory.m_aAbbreviatedName = !aAbbreviatedName.isEmpty() ? aAbbreviatedName : aName;
+ rCategory.m_aIdentifier = aIdentifier;
+
+ rCategory.m_aLabels["PolicyAuthority:Name"] = m_aPolicyAuthorityName;
+ rCategory.m_aLabels["Policy:Name"] = m_aPolicyName;
+ rCategory.m_aLabels["BusinessAuthorization:Identifier"] = m_aProgramID;
+ rCategory.m_aLabels["BusinessAuthorizationCategory:Identifier"] = aIdentifier;
+
+ // Also initialize defaults.
+ rCategory.m_aLabels["PolicyAuthority:Identifier"] = PROP_NONE();
+ rCategory.m_aLabels["PolicyAuthority:Country"] = PROP_NONE();
+ rCategory.m_aLabels["Policy:Identifier"] = PROP_NONE();
+ rCategory.m_aLabels["BusinessAuthorization:Name"] = PROP_NONE();
+ rCategory.m_aLabels["BusinessAuthorization:Locator"] = PROP_NONE();
+ rCategory.m_aLabels["BusinessAuthorizationCategory:Identifier:OID"] = PROP_NONE();
+ rCategory.m_aLabels["BusinessAuthorizationCategory:Locator"] = PROP_NONE();
+ rCategory.m_aLabels["BusinessAuthorization:Locator"] = PROP_NONE();
+ rCategory.m_aLabels["MarkingPrecedence"] = PROP_NONE();
+ rCategory.m_aLabels["Marking:general-summary"].clear();
+ rCategory.m_aLabels["Marking:general-warning-statement"].clear();
+ rCategory.m_aLabels["Marking:general-warning-statement:ext:2"].clear();
+ rCategory.m_aLabels["Marking:general-warning-statement:ext:3"].clear();
+ rCategory.m_aLabels["Marking:general-warning-statement:ext:4"].clear();
+ rCategory.m_aLabels["Marking:general-distribution-statement"].clear();
+ rCategory.m_aLabels["Marking:general-distribution-statement:ext:2"].clear();
+ rCategory.m_aLabels["Marking:general-distribution-statement:ext:3"].clear();
+ rCategory.m_aLabels["Marking:general-distribution-statement:ext:4"].clear();
+ rCategory.m_aLabels[SfxClassificationHelper::PROP_DOCHEADER()].clear();
+ rCategory.m_aLabels[SfxClassificationHelper::PROP_DOCFOOTER()].clear();
+ rCategory.m_aLabels[SfxClassificationHelper::PROP_DOCWATERMARK()].clear();
+ rCategory.m_aLabels["Marking:email-first-line-of-text"].clear();
+ rCategory.m_aLabels["Marking:email-last-line-of-text"].clear();
+ rCategory.m_aLabels["Marking:email-subject-prefix"].clear();
+ rCategory.m_aLabels["Marking:email-subject-suffix"].clear();
+ rCategory.m_aLabels[PROP_STARTVALIDITY()] = PROP_NONE();
+ rCategory.m_aLabels["Authorization:StopValidity"] = PROP_NONE();
+ m_pCategory = &rCategory;
+ }
+ }
+ else if (rName == "loext:Marking")
+ {
+ OUString aName = xAttribs->getValueByName("Name");
+ m_aMarkings.push_back(aName);
+ }
+ else if (rName == "loext:IntellectualPropertyPart")
+ {
+ OUString aName = xAttribs->getValueByName("Name");
+ m_aIPParts.push_back(aName);
+ }
+ else if (rName == "loext:IntellectualPropertyPartNumber")
+ {
+ OUString aName = xAttribs->getValueByName("Name");
+ m_aIPPartNumbers.push_back(aName);
+ }
+ else if (rName == "baf:Scale")
+ {
+ m_aScale.clear();
+ m_bInScale = true;
+ }
+ else if (rName == "baf:ConfidentalityValue")
+ {
+ m_aConfidentalityValue.clear();
+ m_bInConfidentalityValue = true;
+ }
+ else if (rName == "baf:Identifier")
+ {
+ m_aIdentifier.clear();
+ m_bInIdentifier = true;
+ }
+ else if (rName == "baf:Value")
+ {
+ m_aValue.clear();
+ m_bInValue = true;
+ }
+}
+
+void SAL_CALL SfxClassificationParser::endElement(const OUString& rName)
+{
+ if (rName == "baf:PolicyAuthorityName")
+ m_bInPolicyAuthorityName = false;
+ else if (rName == "baf:PolicyName")
+ m_bInPolicyName = false;
+ else if (rName == "baf:ProgramID")
+ m_bInProgramID = false;
+ else if (rName == "baf:BusinessAuthorizationCategory")
+ m_pCategory = nullptr;
+ else if (rName == "baf:Scale")
+ {
+ m_bInScale = false;
+ if (m_pCategory)
+ m_pCategory->m_aLabels[PROP_IMPACTSCALE()] = m_aScale;
+ }
+ else if (rName == "baf:ConfidentalityValue")
+ {
+ m_bInConfidentalityValue = false;
+ if (m_pCategory)
+ {
+ std::map<OUString, OUString>& rLabels = m_pCategory->m_aLabels;
+ rLabels[PROP_IMPACTLEVEL()] = m_aConfidentalityValue;
+ m_pCategory->m_nConfidentiality = m_aConfidentalityValue.toInt32(); // 0-based class sensitivity; 0 is lowest.
+ // Set the two other type of levels as well, if they're not set
+ // yet: they're optional in BAF, but not in BAILS.
+ rLabels.try_emplace("Impact:Level:Integrity", m_aConfidentalityValue);
+ rLabels.try_emplace("Impact:Level:Availability", m_aConfidentalityValue);
+ }
+ }
+ else if (rName == "baf:Identifier")
+ m_bInIdentifier = false;
+ else if (rName == "baf:Value")
+ {
+ if (m_pCategory)
+ {
+ if (m_aIdentifier == "Document: Header")
+ m_pCategory->m_aLabels[SfxClassificationHelper::PROP_DOCHEADER()] = m_aValue;
+ else if (m_aIdentifier == "Document: Footer")
+ m_pCategory->m_aLabels[SfxClassificationHelper::PROP_DOCFOOTER()] = m_aValue;
+ else if (m_aIdentifier == "Document: Watermark")
+ m_pCategory->m_aLabels[SfxClassificationHelper::PROP_DOCWATERMARK()] = m_aValue;
+ }
+ }
+}
+
+void SAL_CALL SfxClassificationParser::characters(const OUString& rChars)
+{
+ if (m_bInPolicyAuthorityName)
+ m_aPolicyAuthorityName += rChars;
+ else if (m_bInPolicyName)
+ m_aPolicyName += rChars;
+ else if (m_bInProgramID)
+ m_aProgramID += rChars;
+ else if (m_bInScale)
+ m_aScale += rChars;
+ else if (m_bInConfidentalityValue)
+ m_aConfidentalityValue += rChars;
+ else if (m_bInIdentifier)
+ m_aIdentifier += rChars;
+ else if (m_bInValue)
+ m_aValue += rChars;
+}
+
+void SAL_CALL SfxClassificationParser::ignorableWhitespace(const OUString& /*rWhitespace*/)
+{
+}
+
+void SAL_CALL SfxClassificationParser::processingInstruction(const OUString& /*rTarget*/, const OUString& /*rData*/)
+{
+}
+
+void SAL_CALL SfxClassificationParser::setDocumentLocator(const uno::Reference<xml::sax::XLocator>& /*xLocator*/)
+{
+}
+
+} // anonymous namespace
+
+/// Implementation details of SfxClassificationHelper.
+class SfxClassificationHelper::Impl
+{
+public:
+ /// Selected categories, one category for each policy type.
+ std::map<SfxClassificationPolicyType, SfxClassificationCategory> m_aCategory;
+ /// Possible categories of a policy to choose from.
+ std::vector<SfxClassificationCategory> m_aCategories;
+ std::vector<OUString> m_aMarkings;
+ std::vector<OUString> m_aIPParts;
+ std::vector<OUString> m_aIPPartNumbers;
+
+ uno::Reference<document::XDocumentProperties> m_xDocumentProperties;
+
+ bool m_bUseLocalized;
+
+ explicit Impl(uno::Reference<document::XDocumentProperties> xDocumentProperties, bool bUseLocalized);
+ void parsePolicy();
+ /// Synchronize m_aLabels back to the document properties.
+ void pushToDocumentProperties();
+ /// Set the classification start date to the system time.
+ void setStartValidity(SfxClassificationPolicyType eType);
+};
+
+SfxClassificationHelper::Impl::Impl(uno::Reference<document::XDocumentProperties> xDocumentProperties, bool bUseLocalized)
+ : m_xDocumentProperties(std::move(xDocumentProperties))
+ , m_bUseLocalized(bUseLocalized)
+{
+ parsePolicy();
+}
+
+void SfxClassificationHelper::Impl::parsePolicy()
+{
+ uno::Reference<uno::XComponentContext> xComponentContext = comphelper::getProcessComponentContext();
+ SvtPathOptions aOptions;
+ OUString aPath = aOptions.GetClassificationPath();
+
+ // See if there is a localized variant next to the configured XML.
+ OUString aExtension(".xml");
+ if (aPath.endsWith(aExtension) && m_bUseLocalized)
+ {
+ std::u16string_view aBase = aPath.subView(0, aPath.getLength() - aExtension.getLength());
+ const LanguageTag& rLanguageTag = Application::GetSettings().GetLanguageTag();
+ // Expected format is "<original path>_xx-XX.xml".
+ OUString aLocalized = OUString::Concat(aBase) + "_" + rLanguageTag.getBcp47() + aExtension;
+ if (FStatHelper::IsDocument(aLocalized))
+ aPath = aLocalized;
+ }
+
+ std::unique_ptr<SvStream> pStream = utl::UcbStreamHelper::CreateStream(aPath, StreamMode::READ);
+ uno::Reference<io::XInputStream> xInputStream(new utl::OStreamWrapper(std::move(pStream)));
+ xml::sax::InputSource aParserInput;
+ aParserInput.aInputStream = xInputStream;
+
+ uno::Reference<xml::sax::XParser> xParser = xml::sax::Parser::create(xComponentContext);
+ rtl::Reference<SfxClassificationParser> xClassificationParser(new SfxClassificationParser());
+ xParser->setDocumentHandler(xClassificationParser);
+ try
+ {
+ xParser->parseStream(aParserInput);
+ }
+ catch (const xml::sax::SAXParseException&)
+ {
+ TOOLS_WARN_EXCEPTION("sfx.view", "parsePolicy() failed");
+ }
+ m_aCategories = xClassificationParser->m_aCategories;
+ m_aMarkings = xClassificationParser->m_aMarkings;
+ m_aIPParts = xClassificationParser->m_aIPParts;
+ m_aIPPartNumbers = xClassificationParser->m_aIPPartNumbers;
+}
+
+static bool lcl_containsProperty(const uno::Sequence<beans::Property>& rProperties, std::u16string_view rName)
+{
+ return std::any_of(rProperties.begin(), rProperties.end(), [&](const beans::Property& rProperty)
+ {
+ return rProperty.Name == rName;
+ });
+}
+
+void SfxClassificationHelper::Impl::setStartValidity(SfxClassificationPolicyType eType)
+{
+ auto itCategory = m_aCategory.find(eType);
+ if (itCategory == m_aCategory.end())
+ return;
+
+ SfxClassificationCategory& rCategory = itCategory->second;
+ auto it = rCategory.m_aLabels.find(policyTypeToString(eType) + PROP_STARTVALIDITY());
+ if (it != rCategory.m_aLabels.end())
+ {
+ if (it->second == PROP_NONE())
+ {
+ // The policy left the start date unchanged, replace it with the system time.
+ util::DateTime aDateTime = DateTime(DateTime::SYSTEM).GetUNODateTime();
+ it->second = utl::toISO8601(aDateTime);
+ }
+ }
+}
+
+void SfxClassificationHelper::Impl::pushToDocumentProperties()
+{
+ uno::Reference<beans::XPropertyContainer> xPropertyContainer = m_xDocumentProperties->getUserDefinedProperties();
+ uno::Reference<beans::XPropertySet> xPropertySet(xPropertyContainer, uno::UNO_QUERY);
+ uno::Sequence<beans::Property> aProperties = xPropertySet->getPropertySetInfo()->getProperties();
+ for (auto& rPair : m_aCategory)
+ {
+ SfxClassificationPolicyType eType = rPair.first;
+ SfxClassificationCategory& rCategory = rPair.second;
+ std::map<OUString, OUString> aLabels = rCategory.m_aLabels;
+ aLabels[policyTypeToString(eType) + PROP_BACNAME()] = rCategory.m_aName;
+ for (const auto& rLabel : aLabels)
+ {
+ try
+ {
+ if (lcl_containsProperty(aProperties, rLabel.first))
+ xPropertySet->setPropertyValue(rLabel.first, uno::Any(rLabel.second));
+ else
+ xPropertyContainer->addProperty(rLabel.first, beans::PropertyAttribute::REMOVABLE, uno::Any(rLabel.second));
+ }
+ catch (const uno::Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("sfx.view", "pushDocumentProperties() failed for property " << rLabel.first);
+ }
+ }
+ }
+}
+
+bool SfxClassificationHelper::IsClassified(const uno::Reference<document::XDocumentProperties>& xDocumentProperties)
+{
+ uno::Reference<beans::XPropertyContainer> xPropertyContainer = xDocumentProperties->getUserDefinedProperties();
+ if (!xPropertyContainer.is())
+ return false;
+
+ uno::Reference<beans::XPropertySet> xPropertySet(xPropertyContainer, uno::UNO_QUERY);
+ const uno::Sequence<beans::Property> aProperties = xPropertySet->getPropertySetInfo()->getProperties();
+ for (const beans::Property& rProperty : aProperties)
+ {
+ if (rProperty.Name.startsWith("urn:bails:"))
+ return true;
+ }
+
+ return false;
+}
+
+SfxClassificationCheckPasteResult SfxClassificationHelper::CheckPaste(const uno::Reference<document::XDocumentProperties>& xSource,
+ const uno::Reference<document::XDocumentProperties>& xDestination)
+{
+ if (!SfxClassificationHelper::IsClassified(xSource))
+ // No classification on the source side. Return early, regardless the
+ // state of the destination side.
+ return SfxClassificationCheckPasteResult::None;
+
+ if (!SfxClassificationHelper::IsClassified(xDestination))
+ {
+ // Paste from a classified document to a non-classified one -> deny.
+ return SfxClassificationCheckPasteResult::TargetDocNotClassified;
+ }
+
+ // Remaining case: paste between two classified documents.
+ SfxClassificationHelper aSource(xSource);
+ SfxClassificationHelper aDestination(xDestination);
+ if (aSource.GetImpactScale() != aDestination.GetImpactScale())
+ // It's possible to compare them if they have the same scale.
+ return SfxClassificationCheckPasteResult::None;
+
+ if (aSource.GetImpactLevel() > aDestination.GetImpactLevel())
+ // Paste from a doc that has higher classification -> deny.
+ return SfxClassificationCheckPasteResult::DocClassificationTooLow;
+
+ return SfxClassificationCheckPasteResult::None;
+}
+
+bool SfxClassificationHelper::ShowPasteInfo(SfxClassificationCheckPasteResult eResult)
+{
+ switch (eResult)
+ {
+ case SfxClassificationCheckPasteResult::None:
+ {
+ return true;
+ }
+ break;
+ case SfxClassificationCheckPasteResult::TargetDocNotClassified:
+ {
+ if (!Application::IsHeadlessModeEnabled())
+ {
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(nullptr,
+ VclMessageType::Info, VclButtonsType::Ok,
+ SfxResId(STR_TARGET_DOC_NOT_CLASSIFIED)));
+ xBox->run();
+ }
+ return false;
+ }
+ break;
+ case SfxClassificationCheckPasteResult::DocClassificationTooLow:
+ {
+ if (!Application::IsHeadlessModeEnabled())
+ {
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(nullptr,
+ VclMessageType::Info, VclButtonsType::Ok,
+ SfxResId(STR_DOC_CLASSIFICATION_TOO_LOW)));
+ xBox->run();
+ }
+ return false;
+ }
+ break;
+ }
+
+ return true;
+}
+
+SfxClassificationHelper::SfxClassificationHelper(const uno::Reference<document::XDocumentProperties>& xDocumentProperties, bool bUseLocalizedPolicy)
+ : m_pImpl(std::make_unique<Impl>(xDocumentProperties, bUseLocalizedPolicy))
+{
+ if (!xDocumentProperties.is())
+ return;
+
+ uno::Reference<beans::XPropertyContainer> xPropertyContainer = xDocumentProperties->getUserDefinedProperties();
+ if (!xPropertyContainer.is())
+ return;
+
+ uno::Reference<beans::XPropertySet> xPropertySet(xPropertyContainer, uno::UNO_QUERY);
+ const uno::Sequence<beans::Property> aProperties = xPropertySet->getPropertySetInfo()->getProperties();
+ for (const beans::Property& rProperty : aProperties)
+ {
+ if (!rProperty.Name.startsWith("urn:bails:"))
+ continue;
+
+ uno::Any aAny = xPropertySet->getPropertyValue(rProperty.Name);
+ OUString aValue;
+ if (aAny >>= aValue)
+ {
+ SfxClassificationPolicyType eType = stringToPolicyType(rProperty.Name);
+ OUString aPrefix = policyTypeToString(eType);
+ if (!rProperty.Name.startsWith(aPrefix))
+ // It's a prefix we did not recognize, ignore.
+ continue;
+
+ //TODO: Support abbreviated names(?)
+ if (rProperty.Name == OUStringConcatenation(aPrefix + PROP_BACNAME()))
+ m_pImpl->m_aCategory[eType].m_aName = aValue;
+ else
+ m_pImpl->m_aCategory[eType].m_aLabels[rProperty.Name] = aValue;
+ }
+ }
+}
+
+SfxClassificationHelper::~SfxClassificationHelper() = default;
+
+std::vector<OUString> const & SfxClassificationHelper::GetMarkings() const
+{
+ return m_pImpl->m_aMarkings;
+}
+
+std::vector<OUString> const & SfxClassificationHelper::GetIntellectualPropertyParts() const
+{
+ return m_pImpl->m_aIPParts;
+}
+
+std::vector<OUString> const & SfxClassificationHelper::GetIntellectualPropertyPartNumbers() const
+{
+ return m_pImpl->m_aIPPartNumbers;
+}
+
+const OUString& SfxClassificationHelper::GetBACName(SfxClassificationPolicyType eType) const
+{
+ return m_pImpl->m_aCategory[eType].m_aName;
+}
+
+const OUString& SfxClassificationHelper::GetAbbreviatedBACName(const OUString& sFullName)
+{
+ for (const auto& category : m_pImpl->m_aCategories)
+ {
+ if (category.m_aName == sFullName)
+ return category.m_aAbbreviatedName;
+ }
+
+ return sFullName;
+}
+
+OUString SfxClassificationHelper::GetBACNameForIdentifier(std::u16string_view sIdentifier)
+{
+ if (sIdentifier.empty())
+ return "";
+
+ for (const auto& category : m_pImpl->m_aCategories)
+ {
+ if (category.m_aIdentifier == sIdentifier)
+ return category.m_aName;
+ }
+
+ return "";
+}
+
+OUString SfxClassificationHelper::GetHigherClass(const OUString& first, const OUString& second)
+{
+ size_t nFirstConfidentiality = 0;
+ size_t nSecondConfidentiality = 0;
+ for (const auto& category : m_pImpl->m_aCategories)
+ {
+ if (category.m_aName == first)
+ nFirstConfidentiality = category.m_nConfidentiality;
+ if (category.m_aName == second)
+ nSecondConfidentiality = category.m_nConfidentiality;
+ }
+
+ return nFirstConfidentiality >= nSecondConfidentiality ? first : second;
+}
+
+bool SfxClassificationHelper::HasImpactLevel()
+{
+ auto itCategory = m_pImpl->m_aCategory.find(SfxClassificationPolicyType::IntellectualProperty);
+ if (itCategory == m_pImpl->m_aCategory.end())
+ return false;
+
+ SfxClassificationCategory& rCategory = itCategory->second;
+ auto it = rCategory.m_aLabels.find(PROP_PREFIX_INTELLECTUALPROPERTY() + PROP_IMPACTSCALE());
+ if (it == rCategory.m_aLabels.end())
+ return false;
+
+ it = rCategory.m_aLabels.find(PROP_PREFIX_INTELLECTUALPROPERTY() + PROP_IMPACTLEVEL());
+ return it != rCategory.m_aLabels.end();
+}
+
+bool SfxClassificationHelper::HasDocumentHeader()
+{
+ auto itCategory = m_pImpl->m_aCategory.find(SfxClassificationPolicyType::IntellectualProperty);
+ if (itCategory == m_pImpl->m_aCategory.end())
+ return false;
+
+ SfxClassificationCategory& rCategory = itCategory->second;
+ auto it = rCategory.m_aLabels.find(PROP_PREFIX_INTELLECTUALPROPERTY() + PROP_DOCHEADER());
+ return it != rCategory.m_aLabels.end() && !it->second.isEmpty();
+}
+
+bool SfxClassificationHelper::HasDocumentFooter()
+{
+ auto itCategory = m_pImpl->m_aCategory.find(SfxClassificationPolicyType::IntellectualProperty);
+ if (itCategory == m_pImpl->m_aCategory.end())
+ return false;
+
+ SfxClassificationCategory& rCategory = itCategory->second;
+ auto it = rCategory.m_aLabels.find(PROP_PREFIX_INTELLECTUALPROPERTY() + PROP_DOCFOOTER());
+ return it != rCategory.m_aLabels.end() && !it->second.isEmpty();
+}
+
+InfobarType SfxClassificationHelper::GetImpactLevelType()
+{
+ InfobarType aRet;
+
+ aRet = InfobarType::WARNING;
+
+ auto itCategory = m_pImpl->m_aCategory.find(SfxClassificationPolicyType::IntellectualProperty);
+ if (itCategory == m_pImpl->m_aCategory.end())
+ return aRet;
+
+ SfxClassificationCategory& rCategory = itCategory->second;
+ auto it = rCategory.m_aLabels.find(PROP_PREFIX_INTELLECTUALPROPERTY() + PROP_IMPACTSCALE());
+ if (it == rCategory.m_aLabels.end())
+ return aRet;
+ OUString aScale = it->second;
+
+ it = rCategory.m_aLabels.find(PROP_PREFIX_INTELLECTUALPROPERTY() + PROP_IMPACTLEVEL());
+ if (it == rCategory.m_aLabels.end())
+ return aRet;
+ OUString aLevel = it->second;
+
+ // The spec defines two valid scale values: FIPS-199 and UK-Cabinet.
+ if (aScale == "UK-Cabinet")
+ {
+ if (aLevel == "0")
+ aRet = InfobarType::SUCCESS;
+ else if (aLevel == "1")
+ aRet = InfobarType::WARNING;
+ else if (aLevel == "2")
+ aRet = InfobarType::WARNING;
+ else if (aLevel == "3")
+ aRet = InfobarType::DANGER;
+ }
+ else if (aScale == "FIPS-199")
+ {
+ if (aLevel == "Low")
+ aRet = InfobarType::SUCCESS;
+ else if (aLevel == "Moderate")
+ aRet = InfobarType::WARNING;
+ else if (aLevel == "High")
+ aRet = InfobarType::DANGER;
+ }
+ return aRet;
+}
+
+sal_Int32 SfxClassificationHelper::GetImpactLevel()
+{
+ sal_Int32 nRet = -1;
+
+ auto itCategory = m_pImpl->m_aCategory.find(SfxClassificationPolicyType::IntellectualProperty);
+ if (itCategory == m_pImpl->m_aCategory.end())
+ return nRet;
+
+ SfxClassificationCategory& rCategory = itCategory->second;
+ auto it = rCategory.m_aLabels.find(PROP_PREFIX_INTELLECTUALPROPERTY() + PROP_IMPACTSCALE());
+ if (it == rCategory.m_aLabels.end())
+ return nRet;
+ OUString aScale = it->second;
+
+ it = rCategory.m_aLabels.find(PROP_PREFIX_INTELLECTUALPROPERTY() + PROP_IMPACTLEVEL());
+ if (it == rCategory.m_aLabels.end())
+ return nRet;
+ OUString aLevel = it->second;
+
+ if (aScale == "UK-Cabinet")
+ {
+ sal_Int32 nValue = aLevel.toInt32();
+ if (nValue < 0 || nValue > 3)
+ return nRet;
+ nRet = nValue;
+ }
+ else if (aScale == "FIPS-199")
+ {
+ static std::map<OUString, sal_Int32> const aValues
+ {
+ { "Low", 0 },
+ { "Moderate", 1 },
+ { "High", 2 }
+ };
+ auto itValues = aValues.find(aLevel);
+ if (itValues == aValues.end())
+ return nRet;
+ nRet = itValues->second;
+ }
+
+ return nRet;
+}
+
+OUString SfxClassificationHelper::GetImpactScale()
+{
+ auto itCategory = m_pImpl->m_aCategory.find(SfxClassificationPolicyType::IntellectualProperty);
+ if (itCategory == m_pImpl->m_aCategory.end())
+ return OUString();
+
+ SfxClassificationCategory& rCategory = itCategory->second;
+ auto it = rCategory.m_aLabels.find(PROP_PREFIX_INTELLECTUALPROPERTY() + PROP_IMPACTSCALE());
+ if (it != rCategory.m_aLabels.end())
+ return it->second;
+
+ return OUString();
+}
+
+OUString SfxClassificationHelper::GetDocumentWatermark()
+{
+ auto itCategory = m_pImpl->m_aCategory.find(SfxClassificationPolicyType::IntellectualProperty);
+ if (itCategory == m_pImpl->m_aCategory.end())
+ return OUString();
+
+ SfxClassificationCategory& rCategory = itCategory->second;
+ auto it = rCategory.m_aLabels.find(PROP_PREFIX_INTELLECTUALPROPERTY() + PROP_DOCWATERMARK());
+ if (it != rCategory.m_aLabels.end())
+ return it->second;
+
+ return OUString();
+}
+
+std::vector<OUString> SfxClassificationHelper::GetBACNames()
+{
+ if (m_pImpl->m_aCategories.empty())
+ m_pImpl->parsePolicy();
+
+ std::vector<OUString> aRet;
+ std::transform(m_pImpl->m_aCategories.begin(), m_pImpl->m_aCategories.end(), std::back_inserter(aRet), [](const SfxClassificationCategory& rCategory)
+ {
+ return rCategory.m_aName;
+ });
+ return aRet;
+}
+
+std::vector<OUString> SfxClassificationHelper::GetBACIdentifiers()
+{
+ if (m_pImpl->m_aCategories.empty())
+ m_pImpl->parsePolicy();
+
+ std::vector<OUString> aRet;
+ std::transform(m_pImpl->m_aCategories.begin(), m_pImpl->m_aCategories.end(), std::back_inserter(aRet), [](const SfxClassificationCategory& rCategory)
+ {
+ return rCategory.m_aIdentifier;
+ });
+ return aRet;
+}
+
+std::vector<OUString> SfxClassificationHelper::GetAbbreviatedBACNames()
+{
+ if (m_pImpl->m_aCategories.empty())
+ m_pImpl->parsePolicy();
+
+ std::vector<OUString> aRet;
+ std::transform(m_pImpl->m_aCategories.begin(), m_pImpl->m_aCategories.end(), std::back_inserter(aRet), [](const SfxClassificationCategory& rCategory)
+ {
+ return rCategory.m_aAbbreviatedName;
+ });
+ return aRet;
+}
+
+void SfxClassificationHelper::SetBACName(const OUString& rName, SfxClassificationPolicyType eType)
+{
+ if (m_pImpl->m_aCategories.empty())
+ m_pImpl->parsePolicy();
+
+ auto it = std::find_if(m_pImpl->m_aCategories.begin(), m_pImpl->m_aCategories.end(), [&](const SfxClassificationCategory& rCategory)
+ {
+ return rCategory.m_aName == rName;
+ });
+ if (it == m_pImpl->m_aCategories.end())
+ {
+ SAL_WARN("sfx.view", "'" << rName << "' is not a recognized category name");
+ return;
+ }
+
+ m_pImpl->m_aCategory[eType].m_aName = it->m_aName;
+ m_pImpl->m_aCategory[eType].m_aAbbreviatedName = it->m_aAbbreviatedName;
+ m_pImpl->m_aCategory[eType].m_nConfidentiality = it->m_nConfidentiality;
+ m_pImpl->m_aCategory[eType].m_aLabels.clear();
+ const OUString& rPrefix = policyTypeToString(eType);
+ for (const auto& rLabel : it->m_aLabels)
+ m_pImpl->m_aCategory[eType].m_aLabels[rPrefix + rLabel.first] = rLabel.second;
+
+ m_pImpl->setStartValidity(eType);
+ m_pImpl->pushToDocumentProperties();
+ SfxViewFrame* pViewFrame = SfxViewFrame::Current();
+ if (!pViewFrame)
+ return;
+
+ UpdateInfobar(*pViewFrame);
+}
+
+void SfxClassificationHelper::UpdateInfobar(SfxViewFrame& rViewFrame)
+{
+ OUString aBACName = GetBACName(SfxClassificationPolicyType::IntellectualProperty);
+ bool bImpactLevel = HasImpactLevel();
+ if (!aBACName.isEmpty() && bImpactLevel)
+ {
+ OUString aMessage = SfxResId(STR_CLASSIFIED_DOCUMENT);
+ aMessage = aMessage.replaceFirst("%1", aBACName);
+
+ rViewFrame.RemoveInfoBar(u"classification");
+ rViewFrame.AppendInfoBar("classification", "", aMessage, GetImpactLevelType());
+ }
+}
+
+SfxClassificationPolicyType SfxClassificationHelper::stringToPolicyType(std::u16string_view rType)
+{
+ if (o3tl::starts_with(rType, PROP_PREFIX_EXPORTCONTROL()))
+ return SfxClassificationPolicyType::ExportControl;
+ else if (o3tl::starts_with(rType, PROP_PREFIX_NATIONALSECURITY()))
+ return SfxClassificationPolicyType::NationalSecurity;
+ else
+ return SfxClassificationPolicyType::IntellectualProperty;
+}
+
+const OUString& SfxClassificationHelper::policyTypeToString(SfxClassificationPolicyType eType)
+{
+ switch (eType)
+ {
+ case SfxClassificationPolicyType::ExportControl:
+ return PROP_PREFIX_EXPORTCONTROL();
+ case SfxClassificationPolicyType::NationalSecurity:
+ return PROP_PREFIX_NATIONALSECURITY();
+ case SfxClassificationPolicyType::IntellectualProperty:
+ break;
+ }
+
+ return PROP_PREFIX_INTELLECTUALPROPERTY();
+}
+
+const OUString& SfxClassificationHelper::PROP_DOCHEADER()
+{
+ static const OUString sProp("Marking:document-header");
+ return sProp;
+}
+
+const OUString& SfxClassificationHelper::PROP_DOCFOOTER()
+{
+ static const OUString sProp("Marking:document-footer");
+ return sProp;
+}
+
+const OUString& SfxClassificationHelper::PROP_DOCWATERMARK()
+{
+ static const OUString sProp("Marking:document-watermark");
+ return sProp;
+}
+
+const OUString& SfxClassificationHelper::PROP_PREFIX_INTELLECTUALPROPERTY()
+{
+ static const OUString sProp("urn:bails:IntellectualProperty:");
+ return sProp;
+}
+
+SfxClassificationPolicyType SfxClassificationHelper::getPolicyType()
+{
+ sal_Int32 nPolicyTypeNumber = officecfg::Office::Common::Classification::Policy::get();
+ auto eType = static_cast<SfxClassificationPolicyType>(nPolicyTypeNumber);
+ return eType;
+}
+
+namespace sfx
+{
+
+namespace
+{
+
+OUString getProperty(uno::Reference<beans::XPropertyContainer> const& rxPropertyContainer,
+ OUString const& rName)
+{
+ try
+ {
+ uno::Reference<beans::XPropertySet> xPropertySet(rxPropertyContainer, uno::UNO_QUERY);
+ return xPropertySet->getPropertyValue(rName).get<OUString>();
+ }
+ catch (const css::uno::Exception&)
+ {
+ }
+
+ return OUString();
+}
+
+} // end anonymous namespace
+
+sfx::ClassificationCreationOrigin getCreationOriginProperty(uno::Reference<beans::XPropertyContainer> const & rxPropertyContainer,
+ sfx::ClassificationKeyCreator const & rKeyCreator)
+{
+ OUString sValue = getProperty(rxPropertyContainer, rKeyCreator.makeCreationOriginKey());
+ if (sValue.isEmpty())
+ return sfx::ClassificationCreationOrigin::NONE;
+
+ return (sValue == "BAF_POLICY")
+ ? sfx::ClassificationCreationOrigin::BAF_POLICY
+ : sfx::ClassificationCreationOrigin::MANUAL;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/view/frame.cxx b/sfx2/source/view/frame.cxx
new file mode 100644
index 000000000..8e577221c
--- /dev/null
+++ b/sfx2/source/view/frame.cxx
@@ -0,0 +1,719 @@
+/* -*- 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/uno/Reference.h>
+#include <com/sun/star/awt/XTopWindow.hpp>
+#include <com/sun/star/awt/XWindow.hpp>
+#include <com/sun/star/util/XCloseable.hpp>
+#include <com/sun/star/util/CloseVetoException.hpp>
+#include <com/sun/star/lang/DisposedException.hpp>
+#include <com/sun/star/container/XChild.hpp>
+#include <com/sun/star/embed/XEmbeddedObject.hpp>
+
+#include <svl/intitem.hxx>
+#include <svl/stritem.hxx>
+#include <tools/svborder.hxx>
+#include <tools/diagnose_ex.h>
+
+#include <appdata.hxx>
+#include <sfx2/app.hxx>
+#include <sfx2/event.hxx>
+#include <sfx2/frame.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/docfile.hxx>
+#include <sfx2/docfilt.hxx>
+#include <sfx2/frmdescr.hxx>
+#include <sfx2/sfxsids.hrc>
+#include <sfx2/viewsh.hxx>
+#include <sfx2/viewfrm.hxx>
+#include "impframe.hxx"
+#include <workwin.hxx>
+#include <sfx2/ipclient.hxx>
+#include <vector>
+
+using namespace com::sun::star;
+
+static std::vector<SfxFrame*> gaFramesArr_Impl;
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::util;
+using namespace ::com::sun::star::frame;
+using namespace ::com::sun::star::container;
+
+SfxPoolItem* SfxUnoAnyItem::CreateDefault() { SAL_WARN( "sfx", "No SfxUnoAnyItem factory available"); return nullptr; }
+
+SfxPoolItem* SfxUnoFrameItem::CreateDefault()
+{
+ return new SfxUnoFrameItem();
+}
+void SfxFrame::Construct_Impl()
+{
+ pImpl.reset(new SfxFrame_Impl);
+ gaFramesArr_Impl.push_back( this );
+}
+
+
+SfxFrame::~SfxFrame()
+{
+ RemoveTopFrame_Impl( this );
+ pWindow.disposeAndClear();
+
+ auto it = std::find( gaFramesArr_Impl.begin(), gaFramesArr_Impl.end(), this );
+ if ( it != gaFramesArr_Impl.end() )
+ gaFramesArr_Impl.erase( it );
+
+ delete pImpl->pDescr;
+}
+
+bool SfxFrame::DoClose()
+{
+ // Actually, one more PrepareClose is still needed!
+ bool bRet = false;
+ if ( !pImpl->bClosing )
+ {
+ pImpl->bClosing = true;
+ CancelTransfers();
+
+ // now close frame; it will be deleted if this call is successful, so don't use any members after that!
+ bRet = true;
+ try
+ {
+ Reference< XCloseable > xCloseable ( pImpl->xFrame, UNO_QUERY );
+ if (xCloseable.is())
+ xCloseable->close(true);
+ else if ( pImpl->xFrame.is() )
+ {
+ Reference < XFrame > xFrame = pImpl->xFrame;
+ xFrame->setComponent( Reference < css::awt::XWindow >(), Reference < XController >() );
+ xFrame->dispose();
+ }
+ else
+ DoClose_Impl();
+ }
+ catch( css::util::CloseVetoException& )
+ {
+ pImpl->bClosing = false;
+ bRet = false;
+ }
+ catch( css::lang::DisposedException& )
+ {
+ }
+ }
+
+ return bRet;
+}
+
+void SfxFrame::DoClose_Impl()
+{
+ SfxBindings* pBindings = nullptr;
+ if ( pImpl->pCurrentViewFrame )
+ pBindings = &pImpl->pCurrentViewFrame->GetBindings();
+
+ // For internal tasks Controllers and Tools must be cleared
+ if ( pImpl->pWorkWin )
+ pImpl->pWorkWin->DeleteControllers_Impl();
+
+ if ( pImpl->pCurrentViewFrame )
+ pImpl->pCurrentViewFrame->Close();
+
+ if ( pImpl->bOwnsBindings )
+ {
+ delete pBindings;
+ pBindings = nullptr;
+ }
+
+ delete this;
+}
+
+bool SfxFrame::DocIsModified_Impl()
+{
+ return pImpl->pCurrentViewFrame && pImpl->pCurrentViewFrame->GetObjectShell() &&
+ pImpl->pCurrentViewFrame->GetObjectShell()->IsModified();
+}
+
+bool SfxFrame::PrepareClose_Impl( bool bUI )
+{
+ bool bRet = true;
+
+ // prevent recursive calls
+ if( !pImpl->bPrepClosing )
+ {
+ pImpl->bPrepClosing = true;
+
+ SfxObjectShell* pCur = GetCurrentDocument() ;
+ if( pCur )
+ {
+ // SFX components have a known behaviour
+ // First check if this frame is the only view to its current document
+ bool bOther = false;
+ for ( const SfxViewFrame *pFrame = SfxViewFrame::GetFirst( pCur );
+ !bOther && pFrame; pFrame = SfxViewFrame::GetNext( *pFrame, pCur ) )
+ {
+ bOther = ( &pFrame->GetFrame() != this );
+ }
+
+ SfxGetpApp()->NotifyEvent( SfxViewEventHint(SfxEventHintId::PrepareCloseView, GlobalEventConfig::GetEventName( GlobalEventId::PREPARECLOSEVIEW ), pCur, GetController() ) );
+
+ if ( bOther )
+ // if there are other views only the current view of this frame must be asked
+ bRet = GetCurrentViewFrame()->GetViewShell()->PrepareClose( bUI );
+ else
+ // otherwise ask the document
+ bRet = pCur->PrepareClose( bUI );
+ }
+
+ pImpl->bPrepClosing = false;
+ }
+
+ if ( bRet && pImpl->pWorkWin )
+ // if closing was accepted by the component the UI subframes must be asked also
+ bRet = pImpl->pWorkWin->PrepareClose_Impl();
+
+ return bRet;
+}
+
+
+bool SfxFrame::IsClosing_Impl() const
+{
+ return pImpl->bClosing;
+}
+
+void SfxFrame::SetIsClosing_Impl()
+{
+ pImpl->bClosing = true;
+}
+
+void SfxFrame::CancelTransfers()
+{
+ if( pImpl->bInCancelTransfers )
+ return;
+
+ pImpl->bInCancelTransfers = true;
+ SfxObjectShell* pObj = GetCurrentDocument();
+ if( pObj ) //&& !( pObj->Get_Impl()->nLoadedFlags & SfxLoadedFlags::ALL ))
+ {
+ SfxViewFrame* pFrm;
+ for( pFrm = SfxViewFrame::GetFirst( pObj );
+ pFrm && &pFrm->GetFrame() == this;
+ pFrm = SfxViewFrame::GetNext( *pFrm, pObj ) ) ;
+ // No more Frame in Document -> Cancel
+ if( !pFrm )
+ {
+ pObj->CancelTransfers();
+ GetCurrentDocument()->Broadcast( SfxHint(SfxHintId::TitleChanged) );
+ }
+ }
+
+ // Check if StarOne-Loader should be canceled
+ SfxFrameWeakRef wFrame( this );
+ if (wFrame.is())
+ pImpl->bInCancelTransfers = false;
+}
+
+SfxViewFrame* SfxFrame::GetCurrentViewFrame() const
+{
+ return pImpl->pCurrentViewFrame;
+}
+
+bool SfxFrame::IsAutoLoadLocked_Impl() const
+{
+ // Its own Document is locked?
+ const SfxObjectShell* pObjSh = GetCurrentDocument();
+ if ( !pObjSh || !pObjSh->IsAutoLoadLocked() )
+ return false;
+
+ // otherwise allow AutoLoad
+ return true;
+}
+
+SfxObjectShell* SfxFrame::GetCurrentDocument() const
+{
+ return pImpl->pCurrentViewFrame ?
+ pImpl->pCurrentViewFrame->GetObjectShell() :
+ nullptr;
+}
+
+void SfxFrame::SetCurrentViewFrame_Impl( SfxViewFrame *pFrame )
+{
+ pImpl->pCurrentViewFrame = pFrame;
+}
+
+bool SfxFrame::GetHasTitle() const
+{
+ return pImpl->mbHasTitle;
+}
+
+void SfxFrame::SetHasTitle( bool n )
+{
+ pImpl->mbHasTitle = n;
+}
+
+void SfxFrame::GetViewData_Impl()
+{
+ // Update all modifiable data between load and unload, the
+ // fixed data is only processed once (after PrepareForDoc_Impl in
+ // updateDescriptor) to save time.
+
+ SfxViewFrame* pViewFrame = GetCurrentViewFrame();
+ if( pViewFrame && pViewFrame->GetViewShell() )
+ {
+ SfxItemSet *pSet = GetDescriptor()->GetArgs();
+ if ( GetController().is() && pSet->GetItemState( SID_VIEW_DATA ) != SfxItemState::SET )
+ {
+ css::uno::Any aData = GetController()->getViewData();
+ pSet->Put( SfxUnoAnyItem( SID_VIEW_DATA, aData ) );
+ }
+
+ if ( pViewFrame->GetCurViewId() )
+ pSet->Put( SfxUInt16Item( SID_VIEW_ID, static_cast<sal_uInt16>(pViewFrame->GetCurViewId()) ) );
+ }
+}
+
+void SfxFrame::UpdateDescriptor( SfxObjectShell const *pDoc )
+{
+ // For PrepareForDoc_Impl frames, the descriptor of the updated
+ // and new itemset to be initialized. All data fir restoring the view
+ // are thus saved. If the document be replaced, GetViewData_Impl (so)
+ // the latest information hinzugef by "added. All together then the
+ // browser-history saved in. When you activate such frame pick entry
+ // is complete itemsets and the descriptor in the OpenDoc sent;.
+ // Here only the fixed properties identified "other adjustable, the
+ // retrieved by GetViewData (saves time).
+
+ assert(pDoc && "NULL-Document inserted ?!");
+
+ const SfxMedium *pMed = pDoc->GetMedium();
+ GetDescriptor()->SetActualURL();
+
+ // Mark FileOpen parameter
+ SfxItemSet* pItemSet = pMed->GetItemSet();
+
+ const std::shared_ptr<const SfxFilter>& pFilter = pMed->GetFilter();
+ OUString aFilter;
+ if ( pFilter )
+ aFilter = pFilter->GetFilterName();
+
+ const SfxStringItem* pRefererItem = SfxItemSet::GetItem<SfxStringItem>(pItemSet, SID_REFERER, false);
+ const SfxStringItem* pOptionsItem = SfxItemSet::GetItem<SfxStringItem>(pItemSet, SID_FILE_FILTEROPTIONS, false);
+ const SfxStringItem* pTitle1Item = SfxItemSet::GetItem<SfxStringItem>(pItemSet, SID_DOCINFO_TITLE, false);
+
+ SfxItemSet *pSet = GetDescriptor()->GetArgs();
+
+ // Delete all old Items
+ pSet->ClearItem();
+
+ if ( pRefererItem )
+ pSet->Put( *pRefererItem );
+ else
+ pSet->Put( SfxStringItem( SID_REFERER, OUString() ) );
+
+ if ( pOptionsItem )
+ pSet->Put( *pOptionsItem );
+
+ if ( pTitle1Item )
+ pSet->Put( *pTitle1Item );
+
+ pSet->Put( SfxStringItem( SID_FILTER_NAME, aFilter ));
+}
+
+
+SfxFrameDescriptor* SfxFrame::GetDescriptor() const
+{
+ // Create a FrameDescriptor On Demand; if there is no TopLevel-Frame
+ // will result in an error, as no valid link is created.
+
+ if ( !pImpl->pDescr )
+ {
+ DBG_ASSERT( true, "No TopLevel-Frame, but no Descriptor!" );
+ pImpl->pDescr = new SfxFrameDescriptor;
+ if ( GetCurrentDocument() )
+ pImpl->pDescr->SetURL( GetCurrentDocument()->GetMedium()->GetOrigURL() );
+ }
+ return pImpl->pDescr;
+}
+
+void SfxFrame::GetDefaultTargetList(TargetList& rList)
+{
+ // An empty string for 'No Target'
+ rList.emplace_back( );
+ rList.emplace_back( "_top" );
+ rList.emplace_back( "_parent" );
+ rList.emplace_back( "_blank" );
+ rList.emplace_back( "_self" );
+}
+
+void SfxFrame::InsertTopFrame_Impl( SfxFrame* pFrame )
+{
+ auto& rArr = SfxGetpApp()->Get_Impl()->vTopFrames;
+ rArr.push_back( pFrame );
+}
+
+void SfxFrame::RemoveTopFrame_Impl( SfxFrame* pFrame )
+{
+ auto& rArr = SfxGetpApp()->Get_Impl()->vTopFrames;
+ auto it = std::find( rArr.begin(), rArr.end(), pFrame );
+ if ( it != rArr.end() )
+ rArr.erase( it );
+}
+
+SfxFrameItem::SfxFrameItem( sal_uInt16 nWhichId, SfxViewFrame const *p )
+ : SfxPoolItem( nWhichId ), pFrame( p ? &p->GetFrame() : nullptr )
+{
+ wFrame = pFrame;
+}
+
+SfxFrameItem::SfxFrameItem( sal_uInt16 nWhichId, SfxFrame *p ):
+ SfxPoolItem( nWhichId ),
+ pFrame( p ), wFrame( p )
+{
+}
+
+SfxFrameItem::SfxFrameItem( SfxFrame *p ):
+ SfxPoolItem( 0 ),
+ pFrame( p ), wFrame( p )
+{
+}
+
+bool SfxFrameItem::operator==( const SfxPoolItem &rItem ) const
+{
+ return SfxPoolItem::operator==(rItem) &&
+ static_cast<const SfxFrameItem&>(rItem).pFrame == pFrame &&
+ static_cast<const SfxFrameItem&>(rItem).wFrame == wFrame;
+}
+
+SfxFrameItem* SfxFrameItem::Clone( SfxItemPool *) const
+{
+ SfxFrameItem* pNew = new SfxFrameItem( wFrame);
+ pNew->pFrame = pFrame;
+ return pNew;
+}
+
+bool SfxFrameItem::QueryValue( css::uno::Any& rVal, sal_uInt8 ) const
+{
+ if ( wFrame )
+ {
+ rVal <<= wFrame->GetFrameInterface();
+ return true;
+ }
+
+ return false;
+}
+
+bool SfxFrameItem::PutValue( const css::uno::Any& rVal, sal_uInt8 )
+{
+ Reference < XFrame > xFrame;
+ if ( (rVal >>= xFrame) && xFrame.is() )
+ {
+ SfxFrame* pFr = SfxFrame::GetFirst();
+ while ( pFr )
+ {
+ if ( pFr->GetFrameInterface() == xFrame )
+ {
+ wFrame = pFrame = pFr;
+ return true;
+ }
+
+ pFr = SfxFrame::GetNext( *pFr );
+ }
+ return true;
+ }
+
+ return false;
+}
+
+
+SfxUnoAnyItem::SfxUnoAnyItem( sal_uInt16 nWhichId, const css::uno::Any& rAny )
+ : SfxPoolItem( nWhichId )
+{
+ aValue = rAny;
+}
+
+bool SfxUnoAnyItem::operator==( const SfxPoolItem& rItem ) const
+{
+ assert(SfxPoolItem::operator==(rItem)); (void)rItem;
+ return false;
+}
+
+SfxUnoAnyItem* SfxUnoAnyItem::Clone( SfxItemPool *) const
+{
+ return new SfxUnoAnyItem( *this );
+}
+
+bool SfxUnoAnyItem::QueryValue( css::uno::Any& rVal, sal_uInt8 /*nMemberId*/ ) const
+{
+ rVal = aValue;
+ return true;
+}
+
+bool SfxUnoAnyItem::PutValue( const css::uno::Any& rVal, sal_uInt8 /*nMemberId*/ )
+{
+ aValue = rVal;
+ return true;
+}
+
+SfxUnoFrameItem::SfxUnoFrameItem()
+{
+}
+
+SfxUnoFrameItem::SfxUnoFrameItem( sal_uInt16 nWhichId, const css::uno::Reference< css::frame::XFrame >& i_rFrame )
+ : SfxPoolItem( nWhichId )
+ , m_xFrame( i_rFrame )
+{
+}
+
+bool SfxUnoFrameItem::operator==( const SfxPoolItem& i_rItem ) const
+{
+ return SfxPoolItem::operator==(i_rItem) &&
+ static_cast< const SfxUnoFrameItem& >( i_rItem ).m_xFrame == m_xFrame;
+}
+
+SfxUnoFrameItem* SfxUnoFrameItem::Clone( SfxItemPool* ) const
+{
+ return new SfxUnoFrameItem( *this );
+}
+
+bool SfxUnoFrameItem::QueryValue( css::uno::Any& rVal, sal_uInt8 /*nMemberId*/ ) const
+{
+ rVal <<= m_xFrame;
+ return true;
+}
+
+bool SfxUnoFrameItem::PutValue( const css::uno::Any& rVal, sal_uInt8 /*nMemberId*/ )
+{
+ return ( rVal >>= m_xFrame );
+}
+
+css::uno::Reference< css::frame::XController > SfxFrame::GetController() const
+{
+ if ( pImpl->pCurrentViewFrame && pImpl->pCurrentViewFrame->GetViewShell() )
+ return pImpl->pCurrentViewFrame->GetViewShell()->GetController();
+ else
+ return css::uno::Reference< css::frame::XController > ();
+}
+
+const css::uno::Reference< css::frame::XFrame >& SfxFrame::GetFrameInterface() const
+{
+ return pImpl->xFrame;
+}
+
+void SfxFrame::SetFrameInterface_Impl( const css::uno::Reference< css::frame::XFrame >& rFrame )
+{
+ pImpl->xFrame = rFrame;
+ css::uno::Reference< css::frame::XDispatchRecorder > xRecorder;
+ if ( !rFrame.is() && GetCurrentViewFrame() )
+ GetCurrentViewFrame()->GetBindings().SetRecorder_Impl( xRecorder );
+}
+
+void SfxFrame::Appear()
+{
+ if ( GetCurrentViewFrame() )
+ {
+ GetCurrentViewFrame()->Show();
+ GetWindow().Show();
+ pImpl->xFrame->getContainerWindow()->setVisible( true );
+ Reference < css::awt::XTopWindow > xTopWindow( pImpl->xFrame->getContainerWindow(), UNO_QUERY );
+ if ( xTopWindow.is() )
+ xTopWindow->toFront();
+ }
+}
+
+void SfxFrame::AppearWithUpdate()
+{
+ Appear();
+ if ( GetCurrentViewFrame() )
+ GetCurrentViewFrame()->GetDispatcher()->Update_Impl( true );
+}
+
+void SfxFrame::SetOwnsBindings_Impl( bool bSet )
+{
+ pImpl->bOwnsBindings = bSet;
+}
+
+bool SfxFrame::OwnsBindings_Impl() const
+{
+ return pImpl->bOwnsBindings;
+}
+
+void SfxFrame::SetToolSpaceBorderPixel_Impl( const SvBorder& rBorder )
+{
+ pImpl->aBorder = rBorder;
+ SfxViewFrame *pF = GetCurrentViewFrame();
+ if ( !pF )
+ return;
+
+ Point aPos ( rBorder.Left(), rBorder.Top() );
+ Size aSize( GetWindow().GetOutputSizePixel() );
+ tools::Long nDeltaX = rBorder.Left() + rBorder.Right();
+ if ( aSize.Width() > nDeltaX )
+ aSize.AdjustWidth( -nDeltaX );
+ else
+ aSize.setWidth( 0 );
+
+ tools::Long nDeltaY = rBorder.Top() + rBorder.Bottom();
+ if ( aSize.Height() > nDeltaY )
+ aSize.AdjustHeight( -nDeltaY );
+ else
+ aSize.setHeight( 0 );
+
+ pF->GetWindow().SetPosSizePixel( aPos, aSize );
+}
+
+tools::Rectangle SfxFrame::GetTopOuterRectPixel_Impl() const
+{
+ Size aSize( GetWindow().GetOutputSizePixel() );
+ return tools::Rectangle( Point(), aSize );
+}
+
+SfxWorkWindow* SfxFrame::GetWorkWindow_Impl() const
+{
+ if ( pImpl->pWorkWin )
+ return pImpl->pWorkWin;
+ else
+ return nullptr;
+}
+
+void SfxFrame::CreateWorkWindow_Impl()
+{
+ SfxFrame* pFrame = this;
+
+ if ( IsInPlace() )
+ {
+ // this makes sense only for inplace activated objects
+ try
+ {
+ Reference < XChild > xChild( GetCurrentDocument()->GetModel(), UNO_QUERY );
+ if ( xChild.is() )
+ {
+ Reference < XModel > xParent( xChild->getParent(), UNO_QUERY );
+ if ( xParent.is() )
+ {
+ Reference< XController > xParentCtrler = xParent->getCurrentController();
+ if ( xParentCtrler.is() )
+ {
+ Reference < XFrame > xFrame( xParentCtrler->getFrame() );
+ SfxFrame* pFr = SfxFrame::GetFirst();
+ while ( pFr )
+ {
+ if ( pFr->GetFrameInterface() == xFrame )
+ {
+ pFrame = pFr;
+ break;
+ }
+
+ pFr = SfxFrame::GetNext( *pFr );
+ }
+ }
+ }
+ }
+ }
+ catch(Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "sfx.view", "SfxFrame::CreateWorkWindow_Impl: Exception caught. Please try to submit a reproducible bug!");
+ }
+ }
+
+ pImpl->pWorkWin = new SfxWorkWindow( &pFrame->GetWindow(), this, pFrame );
+}
+
+void SfxFrame::GrabFocusOnComponent_Impl()
+{
+ if ( pImpl->bReleasingComponent )
+ {
+ GetWindow().GrabFocus();
+ return;
+ }
+
+ vcl::Window* pFocusWindow = &GetWindow();
+ if ( GetCurrentViewFrame() && GetCurrentViewFrame()->GetViewShell() && GetCurrentViewFrame()->GetViewShell()->GetWindow() )
+ pFocusWindow = GetCurrentViewFrame()->GetViewShell()->GetWindow();
+
+ if( !pFocusWindow->HasChildPathFocus() )
+ pFocusWindow->GrabFocus();
+}
+
+void SfxFrame::ReleasingComponent_Impl()
+{
+ pImpl->bReleasingComponent = true;
+}
+
+bool SfxFrame::IsInPlace() const
+{
+ return pImpl->bInPlace;
+}
+
+void SfxFrame::Resize()
+{
+ if ( IsClosing_Impl() )
+ return;
+
+ if ( OwnsBindings_Impl() )
+ {
+ if ( IsInPlace() )
+ {
+ SetToolSpaceBorderPixel_Impl( SvBorder() );
+ }
+ else
+ {
+ // check for IPClient that contains UIactive object or object that is currently UI activating
+ SfxWorkWindow *pWork = GetWorkWindow_Impl();
+ SfxInPlaceClient* pClient = GetCurrentViewFrame()->GetViewShell() ? GetCurrentViewFrame()->GetViewShell()->GetUIActiveIPClient_Impl() : nullptr;
+ if ( pClient )
+ {
+ SfxObjectShell* pDoc
+ = SfxObjectShell::GetShellFromComponent(pClient->GetObject()->getComponent());
+ SfxViewFrame* pFrame = SfxViewFrame::GetFirst(pDoc);
+ pWork = pFrame ? pFrame->GetFrame().GetWorkWindow_Impl() : nullptr;
+ }
+
+ if ( pWork )
+ {
+ pWork->ArrangeChildren_Impl();
+ pWork->ShowChildren_Impl();
+ }
+
+ // problem in presence of UIActive object: when the window is resized, but the toolspace border
+ // remains the same, setting the toolspace border at the ContainerEnvironment doesn't force a
+ // resize on the IPEnvironment; without that no resize is called for the SfxViewFrame. So always
+ // set the window size of the SfxViewFrame explicit.
+ SetToolSpaceBorderPixel_Impl( pImpl->aBorder );
+ }
+ }
+ else if ( pImpl->pCurrentViewFrame )
+ {
+ pImpl->pCurrentViewFrame->GetWindow().SetSizePixel( GetWindow().GetOutputSizePixel() );
+ }
+
+}
+
+SfxFrame* SfxFrame::GetFirst()
+{
+ return gaFramesArr_Impl.empty() ? nullptr : gaFramesArr_Impl.front();
+}
+
+SfxFrame* SfxFrame::GetNext( SfxFrame& rFrame )
+{
+ auto it = std::find( gaFramesArr_Impl.begin(), gaFramesArr_Impl.end(), &rFrame );
+ if ( it != gaFramesArr_Impl.end() && (++it) != gaFramesArr_Impl.end() )
+ return *it;
+ else
+ return nullptr;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/view/frame2.cxx b/sfx2/source/view/frame2.cxx
new file mode 100644
index 000000000..2811c3218
--- /dev/null
+++ b/sfx2/source/view/frame2.cxx
@@ -0,0 +1,404 @@
+/* -*- 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 "impframe.hxx"
+#include <workwin.hxx>
+
+#include <sfx2/bindings.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/docfile.hxx>
+#include <sfx2/sfxsids.hrc>
+#include <sfx2/sfxuno.hxx>
+#include <sfx2/viewsh.hxx>
+
+#include <com/sun/star/awt/XWindow2.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/frame/Desktop.hpp>
+#include <com/sun/star/frame/XComponentLoader.hpp>
+#include <com/sun/star/frame/Frame.hpp>
+#include <com/sun/star/frame/XLayoutManager.hpp>
+
+#include <comphelper/namedvaluecollection.hxx>
+#include <comphelper/processfactory.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+#include <tools/diagnose_ex.h>
+#include <vcl/event.hxx>
+#include <vcl/syswin.hxx>
+#include <sal/log.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::frame;
+using namespace ::com::sun::star::util;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::beans;
+using ::com::sun::star::frame::XComponentLoader;
+
+class SfxFrameWindow_Impl : public vcl::Window
+{
+ DECL_LINK(ModalHierarchyHdl, bool, void);
+public:
+ SfxFrame* pFrame;
+
+ SfxFrameWindow_Impl( SfxFrame* pF, vcl::Window& i_rContainerWindow );
+
+ virtual void DataChanged( const DataChangedEvent& rDCEvt ) override;
+ virtual void StateChanged( StateChangedType nStateChange ) override;
+ virtual bool PreNotify( NotifyEvent& rNEvt ) override;
+ virtual bool EventNotify( NotifyEvent& rEvt ) override;
+ virtual void Resize() override;
+ virtual void GetFocus() override;
+ virtual void dispose() override;
+ void DoResize();
+};
+
+SfxFrameWindow_Impl::SfxFrameWindow_Impl(SfxFrame* pF, vcl::Window& i_rContainerWindow)
+ : Window(&i_rContainerWindow, WB_BORDER | WB_CLIPCHILDREN | WB_NODIALOGCONTROL | WB_3DLOOK)
+ , pFrame(pF)
+{
+ i_rContainerWindow.SetModalHierarchyHdl(LINK(this, SfxFrameWindow_Impl, ModalHierarchyHdl));
+}
+
+void SfxFrameWindow_Impl::dispose()
+{
+ GetParent()->SetModalHierarchyHdl(Link<bool, void>());
+ vcl::Window::dispose();
+}
+
+void SfxFrameWindow_Impl::DataChanged( const DataChangedEvent& rDCEvt )
+{
+ Window::DataChanged( rDCEvt );
+ // tdf#131613 the printers changing has no effect on window layout
+ if (rDCEvt.GetType() == DataChangedEventType::PRINTER)
+ return;
+ SfxWorkWindow *pWorkWin = pFrame->GetWorkWindow_Impl();
+ if ( pWorkWin )
+ pWorkWin->DataChanged_Impl();
+}
+
+bool SfxFrameWindow_Impl::EventNotify( NotifyEvent& rNEvt )
+{
+ if ( pFrame->IsClosing_Impl() || !pFrame->GetFrameInterface().is() )
+ return false;
+
+ SfxViewFrame* pView = pFrame->GetCurrentViewFrame();
+ if ( !pView || !pView->GetObjectShell() )
+ return Window::EventNotify( rNEvt );
+
+ if ( rNEvt.GetType() == MouseNotifyEvent::GETFOCUS )
+ {
+ if ( pView->GetViewShell() && !pView->GetViewShell()->GetUIActiveIPClient_Impl() && !pFrame->IsInPlace() )
+ {
+ SAL_INFO("sfx", "SfxFrame: GotFocus");
+ pView->MakeActive_Impl( false );
+ }
+
+ // if focus was on an external window, the clipboard content might have been changed
+ pView->GetBindings().Invalidate( SID_PASTE );
+ pView->GetBindings().Invalidate( SID_PASTE_SPECIAL );
+ return true;
+ }
+ else if( rNEvt.GetType() == MouseNotifyEvent::KEYINPUT )
+ {
+ if ( pView->GetViewShell()->KeyInput( *rNEvt.GetKeyEvent() ) )
+ return true;
+ }
+
+ return Window::EventNotify( rNEvt );
+}
+
+IMPL_LINK(SfxFrameWindow_Impl, ModalHierarchyHdl, bool, bSetModal, void)
+{
+ SfxViewFrame* pView = pFrame->GetCurrentViewFrame();
+ if (!pView || !pView->GetObjectShell())
+ return;
+ pView->SetModalMode(bSetModal);
+}
+
+bool SfxFrameWindow_Impl::PreNotify( NotifyEvent& rNEvt )
+{
+ MouseNotifyEvent nType = rNEvt.GetType();
+ if ( nType == MouseNotifyEvent::KEYINPUT || nType == MouseNotifyEvent::KEYUP )
+ {
+ SfxViewFrame* pView = pFrame->GetCurrentViewFrame();
+ SfxViewShell* pShell = pView ? pView->GetViewShell() : nullptr;
+ if ( pShell && pShell->HasKeyListeners_Impl() && pShell->HandleNotifyEvent_Impl( rNEvt ) )
+ return true;
+ }
+ else if ( nType == MouseNotifyEvent::MOUSEBUTTONUP || nType == MouseNotifyEvent::MOUSEBUTTONDOWN )
+ {
+ vcl::Window* pWindow = rNEvt.GetWindow();
+ SfxViewFrame* pView = pFrame->GetCurrentViewFrame();
+ SfxViewShell* pShell = pView ? pView->GetViewShell() : nullptr;
+ if ( pShell )
+ if ( pWindow == pShell->GetWindow() || pShell->GetWindow()->IsChild( pWindow ) )
+ if ( pShell->HasMouseClickListeners_Impl() && pShell->HandleNotifyEvent_Impl( rNEvt ) )
+ return true;
+ }
+
+ if ( nType == MouseNotifyEvent::MOUSEBUTTONDOWN )
+ {
+ vcl::Window* pWindow = rNEvt.GetWindow();
+ const MouseEvent* pMEvent = rNEvt.GetMouseEvent();
+ Point aPos = pWindow->OutputToScreenPixel( pMEvent->GetPosPixel() );
+ SfxWorkWindow *pWorkWin = pFrame->GetWorkWindow_Impl();
+ if ( pWorkWin )
+ pWorkWin->EndAutoShow_Impl( aPos );
+ }
+
+ return Window::PreNotify( rNEvt );
+}
+
+void SfxFrameWindow_Impl::GetFocus()
+{
+ if ( pFrame && !pFrame->IsClosing_Impl() && pFrame->GetCurrentViewFrame() && pFrame->GetFrameInterface().is() )
+ pFrame->GetCurrentViewFrame()->MakeActive_Impl( true );
+}
+
+void SfxFrameWindow_Impl::Resize()
+{
+ if ( IsReallyVisible() || IsReallyShown() || GetOutputSizePixel().Width() )
+ DoResize();
+}
+
+void SfxFrameWindow_Impl::StateChanged( StateChangedType nStateChange )
+{
+ if ( nStateChange == StateChangedType::InitShow )
+ {
+ pFrame->pImpl->bHidden = false;
+ if ( pFrame->IsInPlace() )
+ // TODO/MBA: workaround for bug in LayoutManager: the final resize does not get through because the
+ // LayoutManager works asynchronously and between resize and time execution the DockingAcceptor was exchanged so that
+ // the resize event never is sent to the component
+ SetSizePixel( GetParent()->GetOutputSizePixel() );
+
+ DoResize();
+ SfxViewFrame* pView = pFrame->GetCurrentViewFrame();
+ if ( pView )
+ pView->GetBindings().GetWorkWindow_Impl()->ShowChildren_Impl();
+ }
+
+ Window::StateChanged( nStateChange );
+}
+
+void SfxFrameWindow_Impl::DoResize()
+{
+ if ( !pFrame->pImpl->bLockResize )
+ pFrame->Resize();
+}
+
+Reference < XFrame > SfxFrame::CreateBlankFrame()
+{
+ Reference < XFrame > xFrame;
+ try
+ {
+ Reference < XDesktop2 > xDesktop = Desktop::create( ::comphelper::getProcessComponentContext() );
+ xFrame.set( xDesktop->findFrame( "_blank", 0 ), UNO_SET_THROW );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("sfx.view");
+ }
+ return xFrame;
+}
+
+SfxFrame* SfxFrame::CreateHidden( SfxObjectShell const & rDoc, vcl::Window& rWindow, SfxInterfaceId nViewId )
+{
+ SfxFrame* pFrame = nullptr;
+ try
+ {
+ // create and initialize new top level frame for this window
+ Reference < XComponentContext > xContext( ::comphelper::getProcessComponentContext() );
+ Reference < XDesktop2 > xDesktop = Desktop::create( xContext );
+ Reference < XFrame2 > xFrame = Frame::create( xContext );
+
+ Reference< awt::XWindow2 > xWin( VCLUnoHelper::GetInterface ( &rWindow ), uno::UNO_QUERY_THROW );
+ xFrame->initialize( xWin );
+ xDesktop->getFrames()->append( xFrame );
+
+ if ( xWin->isActive() )
+ xFrame->activate();
+
+ // create load arguments
+ Sequence< PropertyValue > aLoadArgs;
+ TransformItems( SID_OPENDOC, *rDoc.GetMedium()->GetItemSet(), aLoadArgs );
+
+ ::comphelper::NamedValueCollection aArgs( aLoadArgs );
+ aArgs.put( "Model", rDoc.GetModel() );
+ aArgs.put( "Hidden", true );
+ if ( nViewId != SFX_INTERFACE_NONE )
+ aArgs.put( "ViewId", static_cast<sal_uInt16>(nViewId) );
+
+ aLoadArgs = aArgs.getPropertyValues();
+
+ // load the doc into that frame
+ Reference< XComponentLoader > xLoader( xFrame, UNO_QUERY_THROW );
+ xLoader->loadComponentFromURL(
+ "private:object",
+ "_self",
+ 0,
+ aLoadArgs
+ );
+
+ for ( pFrame = SfxFrame::GetFirst();
+ pFrame;
+ pFrame = SfxFrame::GetNext( *pFrame )
+ )
+ {
+ if ( pFrame->GetFrameInterface() == xFrame )
+ break;
+ }
+
+ OSL_ENSURE( pFrame, "SfxFrame::Create: load succeeded, but no SfxFrame was created during this!" );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("sfx.view");
+ }
+
+ return pFrame;
+}
+
+SfxFrame* SfxFrame::Create( const Reference < XFrame >& i_rFrame )
+{
+ // create a new TopFrame to an external XFrame object ( wrap controller )
+ ENSURE_OR_THROW( i_rFrame.is(), "NULL frame not allowed" );
+ VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( i_rFrame->getContainerWindow() );
+ ENSURE_OR_THROW( pWindow, "frame without container window not allowed" );
+
+ SfxFrame* pFrame = new SfxFrame( *pWindow );
+ pFrame->SetFrameInterface_Impl( i_rFrame );
+ return pFrame;
+}
+
+SfxFrame::SfxFrame( vcl::Window& i_rContainerWindow )
+ :SvCompatWeakBase<SfxFrame>( this )
+ ,pWindow( nullptr )
+{
+ Construct_Impl();
+
+ pImpl->bHidden = false;
+ InsertTopFrame_Impl( this );
+ pImpl->pExternalContainerWindow = &i_rContainerWindow;
+
+ pWindow = VclPtr<SfxFrameWindow_Impl>::Create( this, i_rContainerWindow );
+
+ // always show pWindow, which is the ComponentWindow of the XFrame we live in
+ // nowadays, since SfxFrames can be created with an XFrame only, hiding or showing the complete XFrame
+ // is not done at level of the container window, not at SFX level. Thus, the component window can
+ // always be visible.
+ pWindow->Show();
+}
+
+void SfxFrame::SetPresentationMode( bool bSet )
+{
+ if ( GetCurrentViewFrame() )
+ GetCurrentViewFrame()->GetWindow().SetBorderStyle( bSet ? WindowBorderStyle::NOBORDER : WindowBorderStyle::NORMAL );
+
+ Reference< css::beans::XPropertySet > xPropSet( GetFrameInterface(), UNO_QUERY );
+ Reference< css::frame::XLayoutManager > xLayoutManager;
+
+ if ( xPropSet.is() )
+ {
+ Any aValue = xPropSet->getPropertyValue("LayoutManager");
+ aValue >>= xLayoutManager;
+ }
+
+ if ( xLayoutManager.is() )
+ xLayoutManager->setVisible( !bSet ); // we don't want to have ui in presentation mode
+
+ SetMenuBarOn_Impl( !bSet );
+ if ( GetWorkWindow_Impl() )
+ GetWorkWindow_Impl()->SetDockingAllowed( !bSet );
+ if ( GetCurrentViewFrame() )
+ GetCurrentViewFrame()->GetDispatcher()->Update_Impl( true );
+}
+
+SystemWindow* SfxFrame::GetSystemWindow() const
+{
+ return GetTopWindow_Impl();
+}
+
+SystemWindow* SfxFrame::GetTopWindow_Impl() const
+{
+ if ( pImpl->pExternalContainerWindow->IsSystemWindow() )
+ return static_cast<SystemWindow*>( pImpl->pExternalContainerWindow.get() );
+ else
+ return nullptr;
+}
+
+
+void SfxFrame::LockResize_Impl( bool bLock )
+{
+ pImpl->bLockResize = bLock;
+}
+
+void SfxFrame::SetMenuBarOn_Impl( bool bOn )
+{
+ pImpl->bMenuBarOn = bOn;
+
+ Reference< css::beans::XPropertySet > xPropSet( GetFrameInterface(), UNO_QUERY );
+ Reference< css::frame::XLayoutManager > xLayoutManager;
+
+ if ( xPropSet.is() )
+ {
+ Any aValue = xPropSet->getPropertyValue("LayoutManager");
+ aValue >>= xLayoutManager;
+ }
+
+ if ( xLayoutManager.is() )
+ {
+ OUString aMenuBarURL( "private:resource/menubar/menubar" );
+
+ if ( bOn )
+ xLayoutManager->showElement( aMenuBarURL );
+ else
+ xLayoutManager->hideElement( aMenuBarURL );
+ }
+}
+
+bool SfxFrame::IsMenuBarOn_Impl() const
+{
+ return pImpl->bMenuBarOn;
+}
+
+void SfxFrame::PrepareForDoc_Impl( const SfxObjectShell& i_rDoc )
+{
+ const ::comphelper::NamedValueCollection aDocumentArgs( i_rDoc.GetModel()->getArgs2( { "Hidden", "PluginMode" } ) );
+
+ // hidden?
+ OSL_ENSURE( !pImpl->bHidden, "when does this happen?" );
+ pImpl->bHidden = aDocumentArgs.getOrDefault( "Hidden", pImpl->bHidden );
+
+ // update our descriptor
+ UpdateDescriptor( &i_rDoc );
+
+ // plugin mode
+ sal_Int16 nPluginMode = aDocumentArgs.getOrDefault( "PluginMode", sal_Int16( 0 ) );
+ if ( nPluginMode && ( nPluginMode != 2 ) )
+ pImpl->bInPlace = true;
+}
+
+bool SfxFrame::IsMarkedHidden_Impl() const
+{
+ return pImpl->bHidden;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/view/frmload.cxx b/sfx2/source/view/frmload.cxx
new file mode 100644
index 000000000..b3830914a
--- /dev/null
+++ b/sfx2/source/view/frmload.cxx
@@ -0,0 +1,829 @@
+/* -*- 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/app.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/docfac.hxx>
+#include <sfx2/docfile.hxx>
+#include <sfx2/docfilt.hxx>
+#include <sfx2/doctempl.hxx>
+#include <sfx2/fcontnr.hxx>
+#include <sfx2/frame.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/request.hxx>
+#include <sfx2/sfxsids.hrc>
+#include <sfx2/viewfac.hxx>
+
+#include <com/sun/star/container/XContainerQuery.hpp>
+#include <com/sun/star/document/XTypeDetection.hpp>
+#include <com/sun/star/frame/XFrame.hpp>
+#include <com/sun/star/frame/XLoadable.hpp>
+#include <com/sun/star/task/XInteractionHandler.hpp>
+#include <com/sun/star/task/XInteractionHandler2.hpp>
+#include <com/sun/star/document/XViewDataSupplier.hpp>
+#include <com/sun/star/container/XIndexAccess.hpp>
+#include <com/sun/star/frame/XSynchronousFrameLoader.hpp>
+#include <com/sun/star/frame/XController2.hpp>
+#include <com/sun/star/frame/XModel2.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/lang/XInitialization.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/util/XCloseable.hpp>
+
+#include <comphelper/interaction.hxx>
+#include <comphelper/namedvaluecollection.hxx>
+#include <cppuhelper/exc_hlp.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <framework/interaction.hxx>
+#include <rtl/ref.hxx>
+#include <sal/log.hxx>
+#include <svl/eitem.hxx>
+#include <svl/stritem.hxx>
+#include <unotools/moduleoptions.hxx>
+#include <tools/diagnose_ex.h>
+#include <tools/stream.hxx>
+#include <tools/urlobj.hxx>
+#include <vcl/svapp.hxx>
+#include <o3tl/string_view.hxx>
+
+using namespace com::sun::star;
+using ::com::sun::star::beans::PropertyValue;
+using ::com::sun::star::container::XContainerQuery;
+using ::com::sun::star::container::XEnumeration;
+using ::com::sun::star::document::XTypeDetection;
+using ::com::sun::star::frame::XFrame;
+using ::com::sun::star::frame::XLoadable;
+using ::com::sun::star::task::XInteractionHandler;
+using ::com::sun::star::task::XInteractionHandler2;
+using ::com::sun::star::uno::Any;
+using ::com::sun::star::uno::Exception;
+using ::com::sun::star::uno::Reference;
+using ::com::sun::star::uno::RuntimeException;
+using ::com::sun::star::uno::Sequence;
+using ::com::sun::star::uno::UNO_QUERY;
+using ::com::sun::star::uno::UNO_QUERY_THROW;
+using ::com::sun::star::uno::UNO_SET_THROW;
+using ::com::sun::star::util::XCloseable;
+using ::com::sun::star::document::XViewDataSupplier;
+using ::com::sun::star::container::XIndexAccess;
+using ::com::sun::star::frame::XController2;
+using ::com::sun::star::frame::XModel2;
+
+namespace {
+
+class SfxFrameLoader_Impl : public ::cppu::WeakImplHelper< css::frame::XSynchronousFrameLoader, css::lang::XServiceInfo >
+{
+ css::uno::Reference < css::uno::XComponentContext > m_aContext;
+
+public:
+ explicit SfxFrameLoader_Impl( const css::uno::Reference < css::uno::XComponentContext >& _rxContext );
+
+ virtual OUString SAL_CALL getImplementationName() override;
+
+ virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override;
+
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+
+
+ // XSynchronousFrameLoader
+
+ virtual sal_Bool SAL_CALL load( const css::uno::Sequence< css::beans::PropertyValue >& _rArgs, const css::uno::Reference< css::frame::XFrame >& _rxFrame ) override;
+ virtual void SAL_CALL cancel() override;
+
+protected:
+ virtual ~SfxFrameLoader_Impl() override;
+
+private:
+ std::shared_ptr<const SfxFilter> impl_getFilterFromServiceName_nothrow(
+ const OUString& i_rServiceName
+ ) const;
+
+ static OUString impl_askForFilter_nothrow(
+ const css::uno::Reference< css::task::XInteractionHandler >& i_rxHandler,
+ const OUString& i_rDocumentURL
+ );
+
+ std::shared_ptr<const SfxFilter> impl_detectFilterForURL(
+ const OUString& _rURL,
+ const ::comphelper::NamedValueCollection& i_rDescriptor,
+ const SfxFilterMatcher& rMatcher
+ ) const;
+
+ static bool impl_createNewDocWithSlotParam(
+ const sal_uInt16 _nSlotID,
+ const css::uno::Reference< css::frame::XFrame >& i_rxFrame,
+ const bool i_bHidden
+ );
+
+ void impl_determineFilter(
+ ::comphelper::NamedValueCollection& io_rDescriptor
+ ) const;
+
+ bool impl_determineTemplateDocument(
+ ::comphelper::NamedValueCollection& io_rDescriptor
+ ) const;
+
+ static sal_uInt16 impl_findSlotParam(
+ std::u16string_view i_rFactoryURL
+ );
+
+ static SfxObjectShellRef impl_findObjectShell(
+ const css::uno::Reference< css::frame::XModel2 >& i_rxDocument
+ );
+
+ static void impl_handleCaughtError_nothrow(
+ const css::uno::Any& i_rCaughtError,
+ const ::comphelper::NamedValueCollection& i_rDescriptor
+ );
+
+ static void impl_removeLoaderArguments(
+ ::comphelper::NamedValueCollection& io_rDescriptor
+ );
+
+ static SfxInterfaceId impl_determineEffectiveViewId_nothrow(
+ const SfxObjectShell& i_rDocument,
+ const ::comphelper::NamedValueCollection& i_rDescriptor
+ );
+
+ static ::comphelper::NamedValueCollection
+ impl_extractViewCreationArgs(
+ ::comphelper::NamedValueCollection& io_rDescriptor
+ );
+
+ static css::uno::Reference< css::frame::XController2 >
+ impl_createDocumentView(
+ const css::uno::Reference< css::frame::XModel2 >& i_rModel,
+ const css::uno::Reference< css::frame::XFrame >& i_rFrame,
+ const ::comphelper::NamedValueCollection& i_rViewFactoryArgs,
+ const OUString& i_rViewName
+ );
+};
+
+SfxFrameLoader_Impl::SfxFrameLoader_Impl( const Reference< css::uno::XComponentContext >& _rxContext )
+ :m_aContext( _rxContext )
+{
+}
+
+SfxFrameLoader_Impl::~SfxFrameLoader_Impl()
+{
+}
+
+
+std::shared_ptr<const SfxFilter> SfxFrameLoader_Impl::impl_detectFilterForURL( const OUString& sURL,
+ const ::comphelper::NamedValueCollection& i_rDescriptor, const SfxFilterMatcher& rMatcher ) const
+{
+ OUString sFilter;
+ try
+ {
+ if ( sURL.isEmpty() )
+ return nullptr;
+
+ Reference< XTypeDetection > xDetect(
+ m_aContext->getServiceManager()->createInstanceWithContext("com.sun.star.document.TypeDetection", m_aContext),
+ UNO_QUERY_THROW);
+
+ ::comphelper::NamedValueCollection aNewArgs;
+ aNewArgs.put( "URL", sURL );
+
+ if ( i_rDescriptor.has( "InteractionHandler" ) )
+ aNewArgs.put( "InteractionHandler", i_rDescriptor.get( "InteractionHandler" ) );
+ if ( i_rDescriptor.has( "StatusIndicator" ) )
+ aNewArgs.put( "StatusIndicator", i_rDescriptor.get( "StatusIndicator" ) );
+
+ Sequence< PropertyValue > aQueryArgs( aNewArgs.getPropertyValues() );
+ OUString sType = xDetect->queryTypeByDescriptor( aQueryArgs, true );
+ if ( !sType.isEmpty() )
+ {
+ std::shared_ptr<const SfxFilter> pFilter = rMatcher.GetFilter4EA( sType );
+ if ( pFilter )
+ sFilter = pFilter->GetName();
+ }
+ }
+ catch ( const RuntimeException& )
+ {
+ throw;
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("sfx.view");
+ sFilter.clear();
+ }
+
+ std::shared_ptr<const SfxFilter> pFilter;
+ if (!sFilter.isEmpty())
+ pFilter = rMatcher.GetFilter4FilterName(sFilter);
+ return pFilter;
+}
+
+
+std::shared_ptr<const SfxFilter> SfxFrameLoader_Impl::impl_getFilterFromServiceName_nothrow( const OUString& i_rServiceName ) const
+{
+ try
+ {
+ ::comphelper::NamedValueCollection aQuery;
+ aQuery.put( "DocumentService", i_rServiceName );
+
+ const Reference< XContainerQuery > xQuery(
+ m_aContext->getServiceManager()->createInstanceWithContext("com.sun.star.document.FilterFactory", m_aContext),
+ UNO_QUERY_THROW );
+
+ const SfxFilterMatcher& rMatcher = SfxGetpApp()->GetFilterMatcher();
+ const SfxFilterFlags nMust = SfxFilterFlags::IMPORT;
+ const SfxFilterFlags nDont = SFX_FILTER_NOTINSTALLED;
+
+ Reference < XEnumeration > xEnum( xQuery->createSubSetEnumerationByProperties(
+ aQuery.getNamedValues() ), UNO_SET_THROW );
+ while ( xEnum->hasMoreElements() )
+ {
+ ::comphelper::NamedValueCollection aType( xEnum->nextElement() );
+ OUString sFilterName = aType.getOrDefault( "Name", OUString() );
+ if ( sFilterName.isEmpty() )
+ continue;
+
+ std::shared_ptr<const SfxFilter> pFilter = rMatcher.GetFilter4FilterName( sFilterName );
+ if ( !pFilter )
+ continue;
+
+ SfxFilterFlags nFlags = pFilter->GetFilterFlags();
+ if ( ( ( nFlags & nMust ) == nMust )
+ && ( ( nFlags & nDont ) == SfxFilterFlags::NONE )
+ )
+ {
+ return pFilter;
+ }
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("sfx.view");
+ }
+ return nullptr;
+}
+
+
+OUString SfxFrameLoader_Impl::impl_askForFilter_nothrow( const Reference< XInteractionHandler >& i_rxHandler,
+ const OUString& i_rDocumentURL )
+{
+ ENSURE_OR_THROW( i_rxHandler.is(), "invalid interaction handler" );
+
+ OUString sFilterName;
+ try
+ {
+ ::framework::RequestFilterSelect aRequest( i_rDocumentURL );
+ i_rxHandler->handle( aRequest.GetRequest() );
+ if( !aRequest.isAbort() )
+ sFilterName = aRequest.getFilter();
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("sfx.view");
+ }
+
+ return sFilterName;
+}
+
+bool lcl_getDispatchResult( const SfxPoolItem* _pResult )
+{
+ if ( !_pResult )
+ return false;
+
+ // default must be set to true, because some return values
+ // can't be checked, but nonetheless indicate "success"!
+ bool bSuccess = true;
+
+ // On the other side some special slots return a boolean state,
+ // which can be set to FALSE.
+ const SfxBoolItem *pItem = dynamic_cast<const SfxBoolItem*>( _pResult );
+ if ( pItem )
+ bSuccess = pItem->GetValue();
+
+ return bSuccess;
+}
+
+bool SfxFrameLoader_Impl::impl_createNewDocWithSlotParam( const sal_uInt16 _nSlotID, const Reference< XFrame >& i_rxFrame,
+ const bool i_bHidden )
+{
+ SfxRequest aRequest( _nSlotID, SfxCallMode::SYNCHRON, SfxGetpApp()->GetPool() );
+ aRequest.AppendItem( SfxUnoFrameItem( SID_FILLFRAME, i_rxFrame ) );
+ if ( i_bHidden )
+ aRequest.AppendItem( SfxBoolItem( SID_HIDDEN, true ) );
+ return lcl_getDispatchResult( SfxGetpApp()->ExecuteSlot( aRequest ) );
+}
+
+
+void SfxFrameLoader_Impl::impl_determineFilter( ::comphelper::NamedValueCollection& io_rDescriptor ) const
+{
+ const OUString sURL = io_rDescriptor.getOrDefault( "URL", OUString() );
+ const OUString sTypeName = io_rDescriptor.getOrDefault( "TypeName", OUString() );
+ const OUString sFilterName = io_rDescriptor.getOrDefault( "FilterName", OUString() );
+ const OUString sServiceName = io_rDescriptor.getOrDefault( "DocumentService", OUString() );
+ const Reference< XInteractionHandler >
+ xInteraction = io_rDescriptor.getOrDefault( "InteractionHandler", Reference< XInteractionHandler >() );
+
+ const SfxFilterMatcher& rMatcher = SfxGetpApp()->GetFilterMatcher();
+ std::shared_ptr<const SfxFilter> pFilter;
+
+ // get filter by its name directly ...
+ if ( !sFilterName.isEmpty() )
+ pFilter = rMatcher.GetFilter4FilterName( sFilterName );
+
+ // or search the preferred filter for the detected type ...
+ if ( !pFilter && !sTypeName.isEmpty() )
+ pFilter = rMatcher.GetFilter4EA( sTypeName );
+
+ // or use given document service for detection, too
+ if ( !pFilter && !sServiceName.isEmpty() )
+ pFilter = impl_getFilterFromServiceName_nothrow( sServiceName );
+
+ // or use interaction to ask user for right filter.
+ if ( !pFilter && xInteraction.is() && !sURL.isEmpty() )
+ {
+ OUString sSelectedFilter = impl_askForFilter_nothrow( xInteraction, sURL );
+ if ( !sSelectedFilter.isEmpty() )
+ pFilter = rMatcher.GetFilter4FilterName( sSelectedFilter );
+ }
+
+ if ( !pFilter )
+ return;
+
+ io_rDescriptor.put( "FilterName", pFilter->GetFilterName() );
+
+ // If detected filter indicates using of an own template format
+ // add property "AsTemplate" to descriptor. But suppress this step
+ // if such property already exists.
+ if ( pFilter->IsOwnTemplateFormat() && !io_rDescriptor.has( "AsTemplate" ) )
+ io_rDescriptor.put( "AsTemplate", true );
+
+ // The DocumentService property will finally be used to determine the document type to create, so
+ // override it with the service name as indicated by the found filter.
+ io_rDescriptor.put( "DocumentService", pFilter->GetServiceName() );
+}
+
+
+SfxObjectShellRef SfxFrameLoader_Impl::impl_findObjectShell( const Reference< XModel2 >& i_rxDocument )
+{
+ for ( SfxObjectShell* pDoc = SfxObjectShell::GetFirst( nullptr, false ); pDoc;
+ pDoc = SfxObjectShell::GetNext( *pDoc, nullptr, false ) )
+ {
+ if ( i_rxDocument == pDoc->GetModel() )
+ {
+ return pDoc;
+ }
+ }
+
+ SAL_WARN( "sfx.view", "SfxFrameLoader_Impl::impl_findObjectShell: model is not based on SfxObjectShell - wrong frame loader usage!" );
+ return nullptr;
+}
+
+
+bool SfxFrameLoader_Impl::impl_determineTemplateDocument( ::comphelper::NamedValueCollection& io_rDescriptor ) const
+{
+ try
+ {
+ const OUString sTemplateRegioName = io_rDescriptor.getOrDefault( "TemplateRegionName", OUString() );
+ const OUString sTemplateName = io_rDescriptor.getOrDefault( "TemplateName", OUString() );
+ const OUString sServiceName = io_rDescriptor.getOrDefault( "DocumentService", OUString() );
+ const OUString sURL = io_rDescriptor.getOrDefault( "URL", OUString() );
+
+ // determine the full URL of the template to use, if any
+ OUString sTemplateURL;
+ if ( !sTemplateRegioName.isEmpty() && !sTemplateName.isEmpty() )
+ {
+ SfxDocumentTemplates aTmpFac;
+ aTmpFac.GetFull( sTemplateRegioName, sTemplateName, sTemplateURL );
+ }
+ else
+ {
+ if ( !sServiceName.isEmpty() )
+ sTemplateURL = SfxObjectFactory::GetStandardTemplate( sServiceName );
+ else
+ sTemplateURL = SfxObjectFactory::GetStandardTemplate( SfxObjectShell::GetServiceNameFromFactory( sURL ) );
+ }
+
+ if ( !sTemplateURL.isEmpty() )
+ {
+ // detect the filter for the template. Might still be NULL (if the template is broken, or does not
+ // exist, or some such), but this is handled by our caller the same way as if no template/URL was present.
+ std::shared_ptr<const SfxFilter> pTemplateFilter = impl_detectFilterForURL( sTemplateURL, io_rDescriptor, SfxGetpApp()->GetFilterMatcher() );
+ if ( pTemplateFilter )
+ {
+ // load the template document, but, well, "as template"
+ io_rDescriptor.put( "FilterName", pTemplateFilter->GetName() );
+ io_rDescriptor.put( "FileName", sTemplateURL );
+ io_rDescriptor.put( "AsTemplate", true );
+
+ // #i21583#
+ // the DocumentService property will finally be used to create the document. Thus, override any possibly
+ // present value with the document service of the template.
+ io_rDescriptor.put( "DocumentService", pTemplateFilter->GetServiceName() );
+ return true;
+ }
+ }
+ }
+ catch (...)
+ {
+ }
+ return false;
+}
+
+
+sal_uInt16 SfxFrameLoader_Impl::impl_findSlotParam( std::u16string_view i_rFactoryURL )
+{
+ std::u16string_view sSlotParam;
+ const size_t nParamPos = i_rFactoryURL.find( '?' );
+ if ( nParamPos != std::u16string_view::npos )
+ {
+ // currently only the "slot" parameter is supported
+ const size_t nSlotPos = i_rFactoryURL.find( u"slot=", nParamPos );
+ if ( nSlotPos > 0 && nSlotPos != std::u16string_view::npos )
+ sSlotParam = i_rFactoryURL.substr( nSlotPos + 5 );
+ }
+
+ if ( !sSlotParam.empty() )
+ return sal_uInt16( o3tl::toInt32(sSlotParam) );
+
+ return 0;
+}
+
+
+void SfxFrameLoader_Impl::impl_handleCaughtError_nothrow( const Any& i_rCaughtError, const ::comphelper::NamedValueCollection& i_rDescriptor )
+{
+ try
+ {
+ const Reference< XInteractionHandler > xInteraction =
+ i_rDescriptor.getOrDefault( "InteractionHandler", Reference< XInteractionHandler >() );
+ if ( !xInteraction.is() )
+ return;
+ ::rtl::Reference< ::comphelper::OInteractionRequest > pRequest( new ::comphelper::OInteractionRequest( i_rCaughtError ) );
+ ::rtl::Reference< ::comphelper::OInteractionApprove > pApprove( new ::comphelper::OInteractionApprove );
+ pRequest->addContinuation( pApprove );
+
+ const Reference< XInteractionHandler2 > xHandler( xInteraction, UNO_QUERY );
+ #if OSL_DEBUG_LEVEL > 0
+ const bool bHandled =
+ #endif
+ xHandler.is() && xHandler->handleInteractionRequest( pRequest );
+
+ #if OSL_DEBUG_LEVEL > 0
+ if ( !bHandled )
+ // the interaction handler couldn't deal with this error
+ // => report it as assertion, at least (done in the DBG_UNHANDLED_EXCEPTION below)
+ ::cppu::throwException( i_rCaughtError );
+ #endif
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("sfx.view");
+ }
+}
+
+
+void SfxFrameLoader_Impl::impl_removeLoaderArguments( ::comphelper::NamedValueCollection& io_rDescriptor )
+{
+ // remove the arguments which are for the loader only, and not for a call to attachResource
+ io_rDescriptor.remove( "StatusIndicator" );
+ io_rDescriptor.remove( "Model" );
+}
+
+
+::comphelper::NamedValueCollection SfxFrameLoader_Impl::impl_extractViewCreationArgs( ::comphelper::NamedValueCollection& io_rDescriptor )
+{
+ static const std::u16string_view sKnownViewArgs[] = { u"JumpMark", u"PickListEntry" };
+
+ ::comphelper::NamedValueCollection aViewArgs;
+ for (const auto& rKnownViewArg : sKnownViewArgs)
+ {
+ const OUString sKnownViewArg(rKnownViewArg);
+ if ( io_rDescriptor.has( sKnownViewArg ) )
+ {
+ aViewArgs.put( sKnownViewArg, io_rDescriptor.get( sKnownViewArg ) );
+ io_rDescriptor.remove( sKnownViewArg );
+ }
+ }
+ return aViewArgs;
+}
+
+
+SfxInterfaceId SfxFrameLoader_Impl::impl_determineEffectiveViewId_nothrow( const SfxObjectShell& i_rDocument, const ::comphelper::NamedValueCollection& i_rDescriptor )
+{
+ SfxInterfaceId nViewId(i_rDescriptor.getOrDefault( "ViewId", sal_Int16( 0 ) ));
+ try
+ {
+ if ( nViewId == SFX_INTERFACE_NONE )
+ do
+ {
+ Reference< XViewDataSupplier > xViewDataSupplier( i_rDocument.GetModel(), UNO_QUERY );
+ Reference< XIndexAccess > xViewData;
+ if ( xViewDataSupplier.is() )
+ xViewData.set( xViewDataSupplier->getViewData() );
+
+ if ( !xViewData.is() || ( xViewData->getCount() == 0 ) )
+ // no view data stored together with the model
+ break;
+
+ // obtain the ViewID from the view data
+ Sequence< PropertyValue > aViewData;
+ if ( !( xViewData->getByIndex( 0 ) >>= aViewData ) )
+ break;
+
+ OUString sViewId = ::comphelper::NamedValueCollection::getOrDefault( aViewData, u"ViewId", OUString() );
+ if ( sViewId.isEmpty() )
+ break;
+
+ // somewhat weird convention here ... in the view data, the ViewId is a string, effectively describing
+ // a view name. In the document load descriptor, the ViewId is in fact the numeric ID.
+
+ SfxViewFactory* pViewFactory = i_rDocument.GetFactory().GetViewFactoryByViewName( sViewId );
+ if ( pViewFactory )
+ nViewId = pViewFactory->GetOrdinal();
+ }
+ while ( false );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("sfx.view");
+ }
+
+ if ( nViewId == SFX_INTERFACE_NONE )
+ nViewId = i_rDocument.GetFactory().GetViewFactory().GetOrdinal();
+ return nViewId;
+}
+
+
+Reference< XController2 > SfxFrameLoader_Impl::impl_createDocumentView( const Reference< XModel2 >& i_rModel,
+ const Reference< XFrame >& i_rFrame, const ::comphelper::NamedValueCollection& i_rViewFactoryArgs,
+ const OUString& i_rViewName )
+{
+ // let the model create a new controller
+ const Reference< XController2 > xController( i_rModel->createViewController(
+ i_rViewName,
+ i_rViewFactoryArgs.getPropertyValues(),
+ i_rFrame
+ ), UNO_SET_THROW );
+
+ // introduce model/view/controller to each other
+ xController->attachModel( i_rModel );
+ i_rModel->connectController( xController );
+ i_rFrame->setComponent( xController->getComponentWindow(), xController );
+ xController->attachFrame( i_rFrame );
+ i_rModel->setCurrentController( xController );
+
+ return xController;
+}
+
+std::shared_ptr<const SfxFilter> getEmptyURLFilter(std::u16string_view sURL)
+{
+ INetURLObject aParser(sURL);
+ const OUString aExt = aParser.getExtension(INetURLObject::LAST_SEGMENT, true,
+ INetURLObject::DecodeMechanism::WithCharset);
+ const SfxFilterMatcher& rMatcher = SfxGetpApp()->GetFilterMatcher();
+
+ // Requiring the export+preferred flags helps to find the relevant filter, e.g. .doc -> WW8 (and
+ // not WW6 or Mac_Word).
+ std::shared_ptr<const SfxFilter> pFilter = rMatcher.GetFilter4Extension(
+ aExt, SfxFilterFlags::IMPORT | SfxFilterFlags::EXPORT | SfxFilterFlags::PREFERED);
+ if (!pFilter)
+ {
+ // retry without PREFERED so we can find at least something for 0-byte *.ods
+ pFilter
+ = rMatcher.GetFilter4Extension(aExt, SfxFilterFlags::IMPORT | SfxFilterFlags::EXPORT);
+ }
+ return pFilter;
+}
+
+sal_Bool SAL_CALL SfxFrameLoader_Impl::load( const Sequence< PropertyValue >& rArgs,
+ const Reference< XFrame >& _rTargetFrame )
+{
+ ENSURE_OR_THROW( _rTargetFrame.is(), "illegal NULL frame" );
+
+ SolarMutexGuard aGuard;
+
+ SAL_INFO( "sfx.view", "SfxFrameLoader::load" );
+
+ ::comphelper::NamedValueCollection aDescriptor( rArgs );
+
+ // ensure the descriptor contains a referrer
+ if ( !aDescriptor.has( "Referer" ) )
+ aDescriptor.put( "Referer", OUString() );
+
+ // did the caller already pass a model?
+ Reference< XModel2 > xModel = aDescriptor.getOrDefault( "Model", Reference< XModel2 >() );
+ const bool bExternalModel = xModel.is();
+
+ // check for factory URLs to create a new doc, instead of loading one
+ const OUString sURL = aDescriptor.getOrDefault( "URL", OUString() );
+ const bool bIsFactoryURL = sURL.startsWith( "private:factory/" );
+ std::shared_ptr<const SfxFilter> pEmptyURLFilter;
+ bool bInitNewModel = bIsFactoryURL;
+ const bool bIsDefault = bIsFactoryURL && !bExternalModel;
+ if (!aDescriptor.has("Replaceable"))
+ aDescriptor.put("Replaceable", bIsDefault);
+ if (bIsDefault)
+ {
+ const OUString sFactory = sURL.copy( sizeof( "private:factory/" ) -1 );
+ // special handling for some weird factory URLs a la private:factory/swriter?slot=21053
+ const sal_uInt16 nSlotParam = impl_findSlotParam( sFactory );
+ if ( nSlotParam != 0 )
+ {
+ return impl_createNewDocWithSlotParam( nSlotParam, _rTargetFrame, aDescriptor.getOrDefault( "Hidden", false ) );
+ }
+
+ const bool bDescribesValidTemplate = impl_determineTemplateDocument( aDescriptor );
+ if ( bDescribesValidTemplate )
+ {
+ // if the media descriptor allowed us to determine a template document to create the new document
+ // from, then do not init a new document model from scratch (below), but instead load the
+ // template document
+ bInitNewModel = false;
+ }
+ else
+ {
+ const OUString sServiceName = SfxObjectShell::GetServiceNameFromFactory( sFactory );
+ aDescriptor.put( "DocumentService", sServiceName );
+ }
+ }
+ else
+ {
+ // compatibility
+ aDescriptor.put( "FileName", aDescriptor.get( "URL" ) );
+
+ if (!bIsFactoryURL && !bExternalModel && tools::isEmptyFileUrl(sURL))
+ {
+ pEmptyURLFilter = getEmptyURLFilter(sURL);
+ if (pEmptyURLFilter)
+ {
+ aDescriptor.put("DocumentService", pEmptyURLFilter->GetServiceName());
+ if (impl_determineTemplateDocument(aDescriptor))
+ {
+ // if the media descriptor allowed us to determine a template document
+ // to create the new document from, then do not init a new document model
+ // from scratch (below), but instead load the template document
+ bInitNewModel = false;
+ // Do not try to load from empty UCB content
+ aDescriptor.remove("UCBContent");
+ }
+ else
+ {
+ bInitNewModel = true;
+ }
+ }
+ }
+ }
+
+ bool bLoadSuccess = false;
+ try
+ {
+ // extract view relevant arguments from the loader args
+ ::comphelper::NamedValueCollection aViewCreationArgs( impl_extractViewCreationArgs( aDescriptor ) );
+
+ // no model passed from outside? => create one from scratch
+ if ( !bExternalModel )
+ {
+ bool bInternalFilter = aDescriptor.getOrDefault<OUString>("FilterProvider", OUString()).isEmpty();
+
+ if (bInternalFilter && !bInitNewModel)
+ {
+ // Ensure that the current SfxFilter instance is loaded before
+ // going further. We don't need to do this for external
+ // filter providers.
+ impl_determineFilter(aDescriptor);
+ }
+
+ // create the new doc
+ const OUString sServiceName = aDescriptor.getOrDefault( "DocumentService", OUString() );
+ xModel.set( m_aContext->getServiceManager()->createInstanceWithContext(sServiceName, m_aContext), UNO_QUERY_THROW );
+
+ // load resp. init it
+ const Reference< XLoadable > xLoadable( xModel, UNO_QUERY_THROW );
+ if ( bInitNewModel )
+ {
+ xLoadable->initNew();
+
+ impl_removeLoaderArguments( aDescriptor );
+ xModel->attachResource( OUString(), aDescriptor.getPropertyValues() );
+ }
+ else
+ {
+ xLoadable->load( aDescriptor.getPropertyValues() );
+ }
+ }
+ else
+ {
+ // tell the doc its (current) load args.
+ impl_removeLoaderArguments( aDescriptor );
+ xModel->attachResource( xModel->getURL(), aDescriptor.getPropertyValues() );
+ }
+
+ // get the SfxObjectShell (still needed at the moment)
+ // SfxObjectShellRef is used here ( instead of ...Lock ) since the model is closed below if necessary
+ // SfxObjectShellLock would be even dangerous here, since the lifetime control should be done outside in case of success
+ const SfxObjectShellRef xDoc = impl_findObjectShell( xModel );
+ ENSURE_OR_THROW( xDoc.is(), "no SfxObjectShell for the given model" );
+
+ if (pEmptyURLFilter)
+ {
+ // Detach the medium from the template, and set proper document name and filter
+ auto pMedium = xDoc->GetMedium();
+ auto pItemSet = pMedium->GetItemSet();
+ pItemSet->ClearItem(SID_TEMPLATE);
+ pItemSet->Put(SfxStringItem(SID_FILTER_NAME, pEmptyURLFilter->GetFilterName()));
+ pMedium->SetName(sURL, true);
+ pMedium->SetFilter(pEmptyURLFilter);
+ pMedium->GetInitFileDate(true);
+ xDoc->SetLoading(SfxLoadedFlags::NONE);
+ xDoc->FinishedLoading();
+ }
+
+ // ensure the ID of the to-be-created view is in the descriptor, if possible
+ const SfxInterfaceId nViewId = impl_determineEffectiveViewId_nothrow( *xDoc, aDescriptor );
+ const sal_Int16 nViewNo = xDoc->GetFactory().GetViewNo_Impl( nViewId, 0 );
+ const OUString sViewName( xDoc->GetFactory().GetViewFactory( nViewNo ).GetAPIViewName() );
+
+ // plug the document into the frame
+ Reference<XController2> xController =
+ impl_createDocumentView( xModel, _rTargetFrame, aViewCreationArgs, sViewName );
+
+ Reference<lang::XInitialization> xInit(xController, UNO_QUERY);
+ if (xInit.is())
+ {
+ uno::Sequence<uno::Any> aArgs; // empty for now.
+ xInit->initialize(aArgs);
+ }
+
+ bLoadSuccess = true;
+ }
+ catch ( Exception& )
+ {
+ const Any aError( ::cppu::getCaughtException() );
+ if ( !aDescriptor.getOrDefault( "Silent", false ) )
+ impl_handleCaughtError_nothrow( aError, aDescriptor );
+ }
+
+ // if loading was not successful, close the document
+ if ( !bLoadSuccess && !bExternalModel )
+ {
+ try
+ {
+ const Reference< XCloseable > xCloseable( xModel, UNO_QUERY_THROW );
+ xCloseable->close( true );
+ }
+ catch ( Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("sfx.view");
+ }
+ }
+
+ return bLoadSuccess;
+}
+
+void SfxFrameLoader_Impl::cancel()
+{
+}
+
+/* XServiceInfo */
+OUString SAL_CALL SfxFrameLoader_Impl::getImplementationName()
+{
+ return "com.sun.star.comp.office.FrameLoader";
+}
+
+/* XServiceInfo */
+sal_Bool SAL_CALL SfxFrameLoader_Impl::supportsService( const OUString& sServiceName )
+{
+ return cppu::supportsService(this, sServiceName);
+}
+
+/* XServiceInfo */
+Sequence< OUString > SAL_CALL SfxFrameLoader_Impl::getSupportedServiceNames()
+{
+ return { "com.sun.star.frame.SynchronousFrameLoader", "com.sun.star.frame.OfficeFrameLoader" };
+}
+
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_office_FrameLoader_get_implementation(
+ css::uno::XComponentContext *context,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new SfxFrameLoader_Impl(context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/view/impframe.hxx b/sfx2/source/view/impframe.hxx
new file mode 100644
index 000000000..b98d9170f
--- /dev/null
+++ b/sfx2/source/view/impframe.hxx
@@ -0,0 +1,71 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SFX2_SOURCE_VIEW_IMPFRAME_HXX
+#define INCLUDED_SFX2_SOURCE_VIEW_IMPFRAME_HXX
+
+#include <sfx2/frame.hxx>
+#include <sfx2/viewfrm.hxx>
+
+#include <tools/svborder.hxx>
+#include <vcl/window.hxx>
+
+class SfxFrame_Impl : public SfxBroadcaster
+{
+public:
+ css::uno::Reference< css::frame::XFrame > xFrame;
+ bool mbHasTitle;
+ SfxViewFrame* pCurrentViewFrame;
+ SfxFrameDescriptor* pDescr;
+ bool bClosing : 1;
+ bool bPrepClosing : 1;
+ bool bInCancelTransfers : 1;
+ bool bOwnsBindings : 1;
+ bool bReleasingComponent : 1;
+ bool bInPlace : 1;
+ SfxWorkWindow* pWorkWin;
+ SvBorder aBorder;
+ // formerly SfxTopFrame
+ VclPtr<vcl::Window> pExternalContainerWindow;
+ bool bHidden;
+ bool bLockResize;
+ bool bMenuBarOn;
+
+ explicit SfxFrame_Impl()
+ :mbHasTitle( false )
+ ,pCurrentViewFrame( nullptr )
+ ,pDescr( nullptr )
+ ,bClosing(false)
+ ,bPrepClosing(false)
+ ,bInCancelTransfers( false )
+ ,bOwnsBindings( false )
+ ,bReleasingComponent( false )
+ ,bInPlace( false )
+ ,pWorkWin( nullptr )
+ ,pExternalContainerWindow( nullptr )
+ ,bHidden( false )
+ ,bLockResize( false )
+ ,bMenuBarOn( true )
+ {
+ }
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/view/impviewframe.hxx b/sfx2/source/view/impviewframe.hxx
new file mode 100644
index 000000000..18675d48e
--- /dev/null
+++ b/sfx2/source/view/impviewframe.hxx
@@ -0,0 +1,81 @@
+/* -*- 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_VIEW_IMPVIEWFRAME_HXX
+#define INCLUDED_SFX2_SOURCE_VIEW_IMPVIEWFRAME_HXX
+
+#include <sfx2/viewfrm.hxx>
+
+#include <tools/svborder.hxx>
+#include <vcl/window.hxx>
+
+struct SfxViewFrame_Impl
+{
+ SvBorder aBorder;
+ Size aMargin;
+ Size aSize;
+ OUString aActualURL;
+ SfxFrame& rFrame;
+ VclPtr<vcl::Window> pWindow;
+ sal_uInt16 nDocViewNo;
+ SfxInterfaceId nCurViewId;
+ bool bResizeInToOut:1;
+ bool bObjLocked:1;
+ bool bReloading:1;
+ bool bIsDowning:1;
+ bool bModal:1;
+ bool bEnabled:1;
+ bool bWindowWasEnabled:1;
+ OUString aFactoryName;
+
+ explicit SfxViewFrame_Impl(SfxFrame& i_rFrame)
+ : rFrame(i_rFrame)
+ , pWindow(nullptr)
+ , nDocViewNo(0)
+ , nCurViewId(0)
+ , bResizeInToOut(false)
+ , bObjLocked(false)
+ , bReloading(false)
+ , bIsDowning(false)
+ , bModal(false)
+ , bEnabled(false)
+ , bWindowWasEnabled(true)
+ {
+ }
+};
+
+class SfxFrameViewWindow_Impl : public vcl::Window
+{
+ SfxViewFrame* pFrame;
+
+public:
+ SfxFrameViewWindow_Impl( SfxViewFrame* p, vcl::Window& rParent ) :
+ Window( &rParent, WB_CLIPCHILDREN ),
+ pFrame( p )
+ {
+ p->GetFrame().GetWindow().SetBorderStyle( WindowBorderStyle::NOBORDER );
+ }
+
+ virtual void Resize() override;
+ virtual void StateChanged( StateChangedType nStateChange ) override;
+};
+
+#endif // INCLUDED_SFX2_SOURCE_VIEW_IMPVIEWFRAME_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/view/ipclient.cxx b/sfx2/source/view/ipclient.cxx
new file mode 100644
index 000000000..0c3f39b9e
--- /dev/null
+++ b/sfx2/source/view/ipclient.cxx
@@ -0,0 +1,1147 @@
+/* -*- 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/awt/XWindowPeer.hpp>
+#include <com/sun/star/embed/Aspects.hpp>
+#include <com/sun/star/embed/EmbedStates.hpp>
+#include <com/sun/star/embed/UnreachableStateException.hpp>
+#include <com/sun/star/embed/XEmbeddedClient.hpp>
+#include <com/sun/star/embed/XInplaceClient.hpp>
+#include <com/sun/star/embed/XInplaceObject.hpp>
+#include <com/sun/star/embed/XWindowSupplier.hpp>
+#include <com/sun/star/embed/EmbedVerbs.hpp>
+#include <com/sun/star/embed/XEmbeddedOleObject.hpp>
+#include <com/sun/star/embed/XEmbeddedObject.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/embed/XStateChangeListener.hpp>
+#include <com/sun/star/embed/StateChangeInProgressException.hpp>
+#include <com/sun/star/embed/XLinkageSupport.hpp>
+#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
+#include <com/sun/star/task/ErrorCodeIOException.hpp>
+#include <com/sun/star/task/StatusIndicatorFactory.hpp>
+#include <com/sun/star/task/XStatusIndicator.hpp>
+
+#include <com/sun/star/embed/EmbedMisc.hpp>
+#include <svtools/embedhlp.hxx>
+#include <vcl/svapp.hxx>
+
+#include <sfx2/ipclient.hxx>
+#include <sfx2/viewsh.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/objsh.hxx>
+#include <guisaveas.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <svtools/ehdl.hxx>
+
+#include <vcl/timer.hxx>
+#include <vcl/window.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+#include <toolkit/helper/convert.hxx>
+#include <tools/debug.hxx>
+#include <tools/diagnose_ex.h>
+#include <tools/fract.hxx>
+#include <tools/gen.hxx>
+#include <svtools/soerr.hxx>
+#include <comphelper/lok.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <cppuhelper/exc_hlp.hxx>
+
+#include <LibreOfficeKit/LibreOfficeKitEnums.h>
+
+#define SFX_CLIENTACTIVATE_TIMEOUT 100
+
+using namespace com::sun::star;
+
+namespace {
+
+// SfxEmbedResizeGuard
+class SfxBooleanFlagGuard
+{
+ bool& m_rFlag;
+public:
+ explicit SfxBooleanFlagGuard(bool& bFlag)
+ : m_rFlag( bFlag )
+ {
+ m_rFlag = true;
+ }
+
+ ~SfxBooleanFlagGuard()
+ {
+ m_rFlag = false;
+ }
+};
+
+tools::Rectangle lcl_negateRectX(const tools::Rectangle& rRect)
+{
+ return tools::Rectangle(
+ std::max(static_cast<tools::Long>(0l), -rRect.Right()),
+ rRect.Top(),
+ std::max(static_cast<tools::Long>(0l), -rRect.Left()),
+ rRect.Bottom());
+}
+
+}
+
+// SfxInPlaceClient_Impl
+
+
+class SfxInPlaceClient_Impl : public ::cppu::WeakImplHelper< embed::XEmbeddedClient,
+ embed::XInplaceClient,
+ document::XEventListener,
+ embed::XStateChangeListener,
+ embed::XWindowSupplier >
+{
+public:
+ Timer m_aTimer { "sfx::SfxInPlaceClient m_xImpl::m_aTimer" }; // activation timeout, starts after object connection
+ tools::Rectangle m_aObjArea; // area of object in coordinate system of the container (without scaling)
+ Fraction m_aScaleWidth; // scaling that was applied to the object when it was not active
+ Fraction m_aScaleHeight;
+ SfxInPlaceClient* m_pClient;
+ sal_Int64 m_nAspect; // ViewAspect that is assigned from the container
+ bool m_bStoreObject;
+ bool m_bUIActive; // set and cleared when notification for UI (de)activation is sent
+ bool m_bResizeNoScale;
+ bool m_bNegativeX;
+
+ uno::Reference < embed::XEmbeddedObject > m_xObject;
+
+
+ SfxInPlaceClient_Impl()
+ : m_pClient( nullptr )
+ , m_nAspect( 0 )
+ , m_bStoreObject( true )
+ , m_bUIActive( false )
+ , m_bResizeNoScale( false )
+ , m_bNegativeX( false )
+ {}
+
+ void SizeHasChanged();
+ DECL_LINK(TimerHdl, Timer *, void);
+ uno::Reference < frame::XFrame > const & GetFrame() const;
+
+ // XEmbeddedClient
+ virtual void SAL_CALL saveObject() override;
+ virtual void SAL_CALL visibilityChanged( sal_Bool bVisible ) override;
+
+ // XInplaceClient
+ virtual sal_Bool SAL_CALL canInplaceActivate() override;
+ virtual void SAL_CALL activatingInplace() override;
+ virtual void SAL_CALL activatingUI() override;
+ virtual void SAL_CALL deactivatedInplace() override;
+ virtual void SAL_CALL deactivatedUI() override;
+ virtual uno::Reference< css::frame::XLayoutManager > SAL_CALL getLayoutManager() override;
+ virtual uno::Reference< frame::XDispatchProvider > SAL_CALL getInplaceDispatchProvider() override;
+ virtual awt::Rectangle SAL_CALL getPlacement() override;
+ virtual awt::Rectangle SAL_CALL getClipRectangle() override;
+ virtual void SAL_CALL translateAccelerators( const uno::Sequence< awt::KeyEvent >& aKeys ) override;
+ virtual void SAL_CALL scrollObject( const awt::Size& aOffset ) override;
+ virtual void SAL_CALL changedPlacement( const awt::Rectangle& aPosRect ) override;
+
+ // XComponentSupplier
+ virtual uno::Reference< util::XCloseable > SAL_CALL getComponent() override;
+
+ // XWindowSupplier
+ virtual uno::Reference< awt::XWindow > SAL_CALL getWindow() override;
+
+ // document::XEventListener
+ virtual void SAL_CALL notifyEvent( const document::EventObject& aEvent ) override;
+
+ // XStateChangeListener
+ virtual void SAL_CALL changingState( const css::lang::EventObject& aEvent, ::sal_Int32 nOldState, ::sal_Int32 nNewState ) override;
+ virtual void SAL_CALL stateChanged( const css::lang::EventObject& aEvent, ::sal_Int32 nOldState, ::sal_Int32 nNewState ) override;
+ virtual void SAL_CALL disposing( const css::lang::EventObject& aEvent ) override;
+};
+
+void SAL_CALL SfxInPlaceClient_Impl::changingState(
+ const css::lang::EventObject& /*aEvent*/,
+ ::sal_Int32 /*nOldState*/,
+ ::sal_Int32 /*nNewState*/ )
+{
+}
+
+void SAL_CALL SfxInPlaceClient_Impl::stateChanged(
+ const css::lang::EventObject& /*aEvent*/,
+ ::sal_Int32 nOldState,
+ ::sal_Int32 nNewState )
+{
+ if ( m_pClient && nOldState != embed::EmbedStates::LOADED && nNewState == embed::EmbedStates::RUNNING )
+ {
+ // deactivation of object
+ uno::Reference< frame::XModel > xDocument;
+ if ( m_pClient->GetViewShell()->GetObjectShell() )
+ xDocument = m_pClient->GetViewShell()->GetObjectShell()->GetModel();
+ SfxObjectShell::SetCurrentComponent( xDocument );
+ }
+}
+
+void SAL_CALL SfxInPlaceClient_Impl::notifyEvent( const document::EventObject& aEvent )
+{
+ SolarMutexGuard aGuard;
+
+ if ( m_pClient && aEvent.EventName == "OnVisAreaChanged" && m_nAspect != embed::Aspects::MSOLE_ICON )
+ {
+ m_pClient->FormatChanged(); // for Writer when format of the object is changed with the area
+ m_pClient->ViewChanged();
+ m_pClient->Invalidate();
+ }
+}
+
+void SAL_CALL SfxInPlaceClient_Impl::disposing( const css::lang::EventObject& /*aEvent*/ )
+{
+ delete m_pClient;
+ m_pClient = nullptr;
+}
+
+// XEmbeddedClient
+
+uno::Reference < frame::XFrame > const & SfxInPlaceClient_Impl::GetFrame() const
+{
+ if ( !m_pClient )
+ throw uno::RuntimeException();
+ return m_pClient->GetViewShell()->GetViewFrame()->GetFrame().GetFrameInterface();
+}
+
+void SAL_CALL SfxInPlaceClient_Impl::saveObject()
+{
+ if (!m_bStoreObject || (m_pClient && m_pClient->IsProtected()))
+ // client wants to discard the object (usually it means the container document is closed while an object is active
+ // and the user didn't request saving the changes
+ return;
+
+ // the common persistence is supported by objects and links
+ uno::Reference< embed::XCommonEmbedPersist > xPersist( m_xObject, uno::UNO_QUERY_THROW );
+
+ uno::Reference< frame::XFrame > xFrame;
+ uno::Reference< task::XStatusIndicator > xStatusIndicator;
+ uno::Reference< frame::XModel > xModel( m_xObject->getComponent(), uno::UNO_QUERY );
+ uno::Reference< uno::XComponentContext > xContext( ::comphelper::getProcessComponentContext() );
+
+ if ( xModel.is() )
+ {
+ uno::Reference< frame::XController > xController = xModel->getCurrentController();
+ if ( xController.is() )
+ xFrame = xController->getFrame();
+ }
+
+ if ( xFrame.is() )
+ {
+ // set non-reschedule progress to prevent problems when asynchronous calls are made
+ // during storing of the embedded object
+ uno::Reference< task::XStatusIndicatorFactory > xStatusIndicatorFactory =
+ task::StatusIndicatorFactory::createWithFrame( xContext, xFrame, true/*DisableReschedule*/, false/*AllowParentShow*/ );
+
+ uno::Reference< beans::XPropertySet > xPropSet( xFrame, uno::UNO_QUERY );
+ if ( xPropSet.is() )
+ {
+ try
+ {
+ xStatusIndicator = xStatusIndicatorFactory->createStatusIndicator();
+ xPropSet->setPropertyValue( "IndicatorInterception" , uno::Any( xStatusIndicator ));
+ }
+ catch ( const uno::RuntimeException& )
+ {
+ throw;
+ }
+ catch ( uno::Exception& )
+ {
+ }
+ }
+ }
+
+ try
+ {
+ xPersist->storeOwn();
+ m_xObject->update();
+ }
+ catch ( uno::Exception& )
+ {
+ //TODO/LATER: what should happen if object can't be saved?!
+ }
+
+ // reset status indicator interception after storing
+ try
+ {
+ uno::Reference< beans::XPropertySet > xPropSet( xFrame, uno::UNO_QUERY );
+ if ( xPropSet.is() )
+ {
+ xStatusIndicator.clear();
+ xPropSet->setPropertyValue( "IndicatorInterception" , uno::Any( xStatusIndicator ));
+ }
+ }
+ catch ( const uno::RuntimeException& )
+ {
+ throw;
+ }
+ catch ( uno::Exception& )
+ {
+ }
+
+ // the client can exist only in case there is a view shell
+ if ( !m_pClient || !m_pClient->GetViewShell() )
+ throw uno::RuntimeException();
+
+ SfxObjectShell* pDocShell = m_pClient->GetViewShell()->GetObjectShell();
+ if ( !pDocShell )
+ throw uno::RuntimeException();
+
+ pDocShell->SetModified();
+
+ //TODO/LATER: invalidation might be necessary when object was modified, but is not
+ //saved through this method
+ // m_pClient->Invalidate();
+}
+
+
+void SAL_CALL SfxInPlaceClient_Impl::visibilityChanged( sal_Bool bVisible )
+{
+ SolarMutexGuard aGuard;
+
+ if ( !m_pClient || !m_pClient->GetViewShell() )
+ throw uno::RuntimeException();
+
+ m_pClient->GetViewShell()->OutplaceActivated( bVisible );
+ if (m_pClient) // it can change in the above code
+ m_pClient->Invalidate();
+}
+
+
+// XInplaceClient
+
+sal_Bool SAL_CALL SfxInPlaceClient_Impl::canInplaceActivate()
+{
+ if ( !m_xObject.is() )
+ throw uno::RuntimeException();
+
+ // we don't want to switch directly from outplace to inplace mode
+ if ( m_xObject->getCurrentState() == embed::EmbedStates::ACTIVE || m_nAspect == embed::Aspects::MSOLE_ICON )
+ return false;
+
+ return true;
+}
+
+
+void SAL_CALL SfxInPlaceClient_Impl::activatingInplace()
+{
+ if ( !m_pClient || !m_pClient->GetViewShell() )
+ throw uno::RuntimeException();
+
+ if ( !comphelper::LibreOfficeKit::isActive() )
+ return;
+
+ if ( SfxViewShell* pViewShell = m_pClient->GetViewShell() )
+ {
+ tools::Rectangle aRect(m_pClient->GetObjArea());
+
+ if (m_pClient->GetEditWin())
+ {
+ if (m_pClient->GetEditWin()->GetMapMode().GetMapUnit() == MapUnit::Map100thMM)
+ aRect = o3tl::convert(aRect, o3tl::Length::mm100, o3tl::Length::twip);
+ }
+
+ OString str = (m_bNegativeX ? lcl_negateRectX(aRect) : aRect).toString() + ", \"INPLACE\"";
+ pViewShell->libreOfficeKitViewCallback( LOK_CALLBACK_GRAPHIC_SELECTION, str.getStr() );
+ }
+
+}
+
+
+void SAL_CALL SfxInPlaceClient_Impl::activatingUI()
+{
+ if ( !m_pClient || !m_pClient->GetViewShell() )
+ throw uno::RuntimeException();
+
+ m_pClient->GetViewShell()->ResetAllClients_Impl(m_pClient);
+ m_bUIActive = true;
+ m_pClient->GetViewShell()->UIActivating( m_pClient );
+}
+
+
+void SAL_CALL SfxInPlaceClient_Impl::deactivatedInplace()
+{
+ if ( !m_pClient || !m_pClient->GetViewShell() )
+ throw uno::RuntimeException();
+
+ if ( comphelper::LibreOfficeKit::isActive() )
+ {
+ if ( SfxViewShell* pViewShell = m_pClient->GetViewShell() ) {
+ pViewShell->libreOfficeKitViewCallback( LOK_CALLBACK_GRAPHIC_SELECTION, "INPLACE EXIT" );
+ }
+ }
+}
+
+
+void SAL_CALL SfxInPlaceClient_Impl::deactivatedUI()
+{
+ if ( !m_pClient || !m_pClient->GetViewShell() )
+ throw uno::RuntimeException();
+
+ m_pClient->GetViewShell()->UIDeactivated( m_pClient );
+ m_bUIActive = false;
+}
+
+
+uno::Reference< css::frame::XLayoutManager > SAL_CALL SfxInPlaceClient_Impl::getLayoutManager()
+{
+ uno::Reference < beans::XPropertySet > xFrame( GetFrame(), uno::UNO_QUERY_THROW );
+
+ uno::Reference< css::frame::XLayoutManager > xMan;
+ try
+ {
+ uno::Any aAny = xFrame->getPropertyValue( "LayoutManager" );
+ aAny >>= xMan;
+ }
+ catch ( uno::Exception& ex )
+ {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw css::lang::WrappedTargetRuntimeException( ex.Message,
+ nullptr, anyEx );
+ }
+
+ return xMan;
+}
+
+
+uno::Reference< frame::XDispatchProvider > SAL_CALL SfxInPlaceClient_Impl::getInplaceDispatchProvider()
+{
+ return uno::Reference < frame::XDispatchProvider >( GetFrame(), uno::UNO_QUERY_THROW );
+}
+
+
+awt::Rectangle SAL_CALL SfxInPlaceClient_Impl::getPlacement()
+{
+ if ( !m_pClient || !m_pClient->GetViewShell() )
+ throw uno::RuntimeException();
+
+ // apply scaling to object area and convert to pixels
+ tools::Rectangle aRealObjArea( m_aObjArea );
+ aRealObjArea.SetSize( Size( tools::Long( aRealObjArea.GetWidth() * m_aScaleWidth),
+ tools::Long( aRealObjArea.GetHeight() * m_aScaleHeight) ) );
+
+ vcl::Window* pEditWin = m_pClient->GetEditWin();
+ // In Writer and Impress the map mode is disabled. So when a chart is
+ // activated (for in place editing) we get the chart win size in 100th mm
+ // and any method that should return pixels returns 100th mm and the chart
+ // window map mode has a ~26.485 scale factor.
+ // All that does not fit with current implementation for handling chart
+ // editing in LOK.
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ bool bMapModeEnabled = pEditWin->IsMapModeEnabled();
+ if (!bMapModeEnabled)
+ pEditWin->EnableMapMode();
+ aRealObjArea = pEditWin->LogicToPixel(aRealObjArea);
+ if (!bMapModeEnabled && pEditWin->IsMapModeEnabled())
+ pEditWin->EnableMapMode(false);
+ }
+ else
+ {
+ aRealObjArea = pEditWin->LogicToPixel(aRealObjArea);
+ }
+
+ return AWTRectangle( aRealObjArea );
+}
+
+
+awt::Rectangle SAL_CALL SfxInPlaceClient_Impl::getClipRectangle()
+{
+ if ( !m_pClient || !m_pClient->GetViewShell() )
+ throw uno::RuntimeException();
+
+ // currently(?) same as placement
+ tools::Rectangle aRealObjArea( m_aObjArea );
+ aRealObjArea.SetSize( Size( tools::Long( aRealObjArea.GetWidth() * m_aScaleWidth),
+ tools::Long( aRealObjArea.GetHeight() * m_aScaleHeight) ) );
+
+ vcl::Window* pEditWin = m_pClient->GetEditWin();
+ // See comment for SfxInPlaceClient_Impl::getPlacement.
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ bool bMapModeEnabled = pEditWin->IsMapModeEnabled();
+ if (!bMapModeEnabled)
+ pEditWin->EnableMapMode();
+ aRealObjArea = pEditWin->LogicToPixel(aRealObjArea);
+ if (!bMapModeEnabled && pEditWin->IsMapModeEnabled())
+ pEditWin->EnableMapMode(false);
+ }
+ else
+ {
+ aRealObjArea = pEditWin->LogicToPixel(aRealObjArea);
+ }
+
+ return AWTRectangle( aRealObjArea );
+}
+
+
+void SAL_CALL SfxInPlaceClient_Impl::translateAccelerators( const uno::Sequence< awt::KeyEvent >& /*aKeys*/ )
+{
+ if ( !m_pClient || !m_pClient->GetViewShell() )
+ throw uno::RuntimeException();
+
+ // TODO/MBA: keyboard accelerators
+}
+
+
+void SAL_CALL SfxInPlaceClient_Impl::scrollObject( const awt::Size& /*aOffset*/ )
+{
+ if ( !m_pClient || !m_pClient->GetViewShell() )
+ throw uno::RuntimeException();
+}
+
+
+void SAL_CALL SfxInPlaceClient_Impl::changedPlacement( const awt::Rectangle& aPosRect )
+{
+ uno::Reference< embed::XInplaceObject > xInplace( m_xObject, uno::UNO_QUERY_THROW );
+ if ( !m_pClient || !m_pClient->GetEditWin() || !m_pClient->GetViewShell() )
+ throw uno::RuntimeException();
+
+ // check if the change is at least one pixel in size
+ awt::Rectangle aOldRect = getPlacement();
+ tools::Rectangle aNewPixelRect = VCLRectangle( aPosRect );
+ tools::Rectangle aOldPixelRect = VCLRectangle( aOldRect );
+ if ( aOldPixelRect == aNewPixelRect )
+ // nothing has changed
+ return;
+
+ // new scaled object area
+ tools::Rectangle aNewLogicRect = m_pClient->GetEditWin()->PixelToLogic( aNewPixelRect );
+
+ // all the size changes in this method should happen without scaling
+ // SfxBooleanFlagGuard aGuard( m_bResizeNoScale, sal_True );
+
+ // allow container to apply restrictions on the requested new area;
+ // the container might change the object view during size calculation;
+ // currently only writer does it
+ m_pClient->RequestNewObjectArea( aNewLogicRect);
+
+ if ( aNewLogicRect != m_pClient->GetScaledObjArea() )
+ {
+ // the calculation of the object area has not changed the object size
+ // it should be done here then
+ SfxBooleanFlagGuard aGuard( m_bResizeNoScale );
+
+ // new size of the object area without scaling
+ Size aNewObjSize( tools::Long( aNewLogicRect.GetWidth() / m_aScaleWidth ),
+ tools::Long( aNewLogicRect.GetHeight() / m_aScaleHeight ) );
+
+ // now remove scaling from new placement and keep this at the new object area
+ aNewLogicRect.SetSize( aNewObjSize );
+ m_aObjArea = aNewLogicRect;
+
+ // let the window size be recalculated
+ SizeHasChanged();
+ }
+
+ // notify container view about changes
+ m_pClient->ObjectAreaChanged();
+}
+
+// XComponentSupplier
+
+uno::Reference< util::XCloseable > SAL_CALL SfxInPlaceClient_Impl::getComponent()
+{
+ if ( !m_pClient || !m_pClient->GetViewShell() )
+ throw uno::RuntimeException();
+
+ SfxObjectShell* pDocShell = m_pClient->GetViewShell()->GetObjectShell();
+ if ( !pDocShell )
+ throw uno::RuntimeException();
+
+ // all the components must implement XCloseable
+ uno::Reference< util::XCloseable > xComp( pDocShell->GetModel(), uno::UNO_QUERY_THROW );
+ return xComp;
+}
+
+
+// XWindowSupplier
+
+uno::Reference< awt::XWindow > SAL_CALL SfxInPlaceClient_Impl::getWindow()
+{
+ if ( !m_pClient || !m_pClient->GetEditWin() )
+ throw uno::RuntimeException();
+
+ uno::Reference< awt::XWindow > xWin( m_pClient->GetEditWin()->GetComponentInterface(), uno::UNO_QUERY );
+ return xWin;
+}
+
+
+// notification to the client implementation that either the object area or the scaling has been changed
+// as a result the logical size of the window has changed also
+void SfxInPlaceClient_Impl::SizeHasChanged()
+{
+ if ( !m_pClient || !m_pClient->GetViewShell() )
+ throw uno::RuntimeException();
+
+ try {
+ if ( m_xObject.is()
+ && ( m_xObject->getCurrentState() == embed::EmbedStates::INPLACE_ACTIVE
+ || m_xObject->getCurrentState() == embed::EmbedStates::UI_ACTIVE ) )
+ {
+ // only possible in active states
+ uno::Reference< embed::XInplaceObject > xInplace( m_xObject, uno::UNO_QUERY_THROW );
+
+ if ( m_bResizeNoScale )
+ {
+ // the resizing should be done without scaling
+ // set the correct size to the object to avoid the scaling
+ MapMode aObjectMap( VCLUnoHelper::UnoEmbed2VCLMapUnit( m_xObject->getMapUnit( m_nAspect ) ) );
+ MapMode aClientMap( m_pClient->GetEditWin()->GetMapMode().GetMapUnit() );
+
+ // convert to logical coordinates of the embedded object
+ Size aNewSize = m_pClient->GetEditWin()->LogicToLogic( m_aObjArea.GetSize(), &aClientMap, &aObjectMap );
+ m_xObject->setVisualAreaSize( m_nAspect, awt::Size( aNewSize.Width(), aNewSize.Height() ) );
+ }
+
+ xInplace->setObjectRectangles( getPlacement(), getClipRectangle() );
+ }
+ }
+ catch( uno::Exception& )
+ {
+ // TODO/LATER: handle error
+ }
+}
+
+
+IMPL_LINK_NOARG(SfxInPlaceClient_Impl, TimerHdl, Timer *, void)
+{
+ if ( m_pClient && m_xObject.is() )
+ {
+ m_pClient->GetViewShell()->CheckIPClient_Impl(m_pClient,
+ m_pClient->GetViewShell()->GetObjectShell()->GetVisArea());
+ }
+}
+
+
+// SfxInPlaceClient
+
+
+SfxInPlaceClient::SfxInPlaceClient( SfxViewShell* pViewShell, vcl::Window *pDraw, sal_Int64 nAspect ) :
+ m_xImp( new SfxInPlaceClient_Impl ),
+ m_pViewSh( pViewShell ),
+ m_pEditWin( pDraw )
+{
+ m_xImp->m_pClient = this;
+ m_xImp->m_nAspect = nAspect;
+ m_xImp->m_aScaleWidth = m_xImp->m_aScaleHeight = Fraction(1,1);
+ pViewShell->NewIPClient_Impl(this);
+ m_xImp->m_aTimer.SetTimeout( SFX_CLIENTACTIVATE_TIMEOUT );
+ m_xImp->m_aTimer.SetInvokeHandler( LINK( m_xImp.get(), SfxInPlaceClient_Impl, TimerHdl ) );
+}
+
+
+SfxInPlaceClient::~SfxInPlaceClient()
+{
+ m_pViewSh->IPClientGone_Impl(this);
+
+ // deleting the client before storing the object means discarding all changes
+ m_xImp->m_bStoreObject = false;
+ SetObject(nullptr);
+
+ m_xImp->m_pClient = nullptr;
+
+ // the next call will destroy m_xImp if no other reference to it exists
+ m_xImp.clear();
+
+ // TODO/LATER:
+ // the class is not intended to be used in multithreaded environment;
+ // if it will this disconnection and all the parts that use the m_pClient
+ // must be guarded with mutex
+}
+
+
+void SfxInPlaceClient::SetObjectState( sal_Int32 nState )
+{
+ if ( !GetObject().is() )
+ return;
+
+ if ( m_xImp->m_nAspect == embed::Aspects::MSOLE_ICON
+ && ( nState == embed::EmbedStates::UI_ACTIVE || nState == embed::EmbedStates::INPLACE_ACTIVE ) )
+ {
+ OSL_FAIL( "Iconified object should not be activated inplace!" );
+ return;
+ }
+
+ try
+ {
+ GetObject()->changeState( nState );
+ }
+ catch ( uno::Exception& )
+ {}
+}
+
+
+sal_Int64 SfxInPlaceClient::GetObjectMiscStatus() const
+{
+ if ( GetObject().is() )
+ return GetObject()->getStatus( m_xImp->m_nAspect );
+ return 0;
+}
+
+
+const uno::Reference < embed::XEmbeddedObject >& SfxInPlaceClient::GetObject() const
+{
+ return m_xImp->m_xObject;
+}
+
+
+void SfxInPlaceClient::SetObject( const uno::Reference < embed::XEmbeddedObject >& rObject )
+{
+ if ( m_xImp->m_xObject.is() && rObject != m_xImp->m_xObject )
+ {
+ DBG_ASSERT( GetObject()->getClientSite() == static_cast<cppu::OWeakObject*>(m_xImp.get()), "Wrong ClientSite!" );
+ if ( GetObject()->getClientSite() == static_cast<cppu::OWeakObject*>(m_xImp.get()) )
+ {
+ if ( GetObject()->getCurrentState() != embed::EmbedStates::LOADED )
+ SetObjectState( embed::EmbedStates::RUNNING );
+ m_xImp->m_xObject->removeEventListener( m_xImp );
+ m_xImp->m_xObject->removeStateChangeListener( m_xImp );
+ try
+ {
+ m_xImp->m_xObject->setClientSite( nullptr );
+ }
+ catch( uno::Exception& )
+ {
+ OSL_FAIL( "Can not clean the client site!" );
+ }
+ }
+ }
+
+ if ( m_pViewSh->GetViewFrame()->GetFrame().IsClosing_Impl() )
+ // sometimes applications reconnect clients on shutting down because it happens in their Paint methods
+ return;
+
+ m_xImp->m_xObject = rObject;
+
+ if ( rObject.is() )
+ {
+ // as soon as an object was connected to a client it has to be checked whether the object wants
+ // to be activated
+ rObject->addStateChangeListener( m_xImp );
+ rObject->addEventListener( m_xImp );
+
+ try
+ {
+ rObject->setClientSite( m_xImp );
+ }
+ catch( uno::Exception& )
+ {
+ OSL_FAIL( "Can not set the client site!" );
+ }
+
+ m_xImp->m_aTimer.Start();
+ }
+ else
+ m_xImp->m_aTimer.Stop();
+}
+
+
+bool SfxInPlaceClient::SetObjArea( const tools::Rectangle& rArea )
+{
+ if( rArea != m_xImp->m_aObjArea )
+ {
+ m_xImp->m_aObjArea = rArea;
+ m_xImp->SizeHasChanged();
+
+ Invalidate();
+ return true;
+ }
+
+ return false;
+}
+
+
+const tools::Rectangle& SfxInPlaceClient::GetObjArea() const
+{
+ return m_xImp->m_aObjArea;
+}
+
+tools::Rectangle SfxInPlaceClient::GetScaledObjArea() const
+{
+ tools::Rectangle aRealObjArea( m_xImp->m_aObjArea );
+ aRealObjArea.SetSize( Size( tools::Long( aRealObjArea.GetWidth() * m_xImp->m_aScaleWidth ),
+ tools::Long( aRealObjArea.GetHeight() * m_xImp->m_aScaleHeight ) ) );
+ return aRealObjArea;
+}
+
+
+void SfxInPlaceClient::SetSizeScale( const Fraction & rScaleWidth, const Fraction & rScaleHeight )
+{
+ if ( m_xImp->m_aScaleWidth != rScaleWidth || m_xImp->m_aScaleHeight != rScaleHeight )
+ {
+ m_xImp->m_aScaleWidth = rScaleWidth;
+ m_xImp->m_aScaleHeight = rScaleHeight;
+
+ m_xImp->SizeHasChanged();
+
+ // TODO/LATER: Invalidate seems to trigger (wrong) recalculations of the ObjArea, so it's better
+ // not to call it here, but maybe it sounds reasonable to do so.
+ //Invalidate();
+ }
+}
+
+
+void SfxInPlaceClient::SetObjAreaAndScale( const tools::Rectangle& rArea, const Fraction& rScaleWidth, const Fraction& rScaleHeight )
+{
+ if( rArea != m_xImp->m_aObjArea || m_xImp->m_aScaleWidth != rScaleWidth || m_xImp->m_aScaleHeight != rScaleHeight )
+ {
+ m_xImp->m_aObjArea = rArea;
+ m_xImp->m_aScaleWidth = rScaleWidth;
+ m_xImp->m_aScaleHeight = rScaleHeight;
+
+ m_xImp->SizeHasChanged();
+
+ Invalidate();
+ }
+}
+
+
+const Fraction& SfxInPlaceClient::GetScaleWidth() const
+{
+ return m_xImp->m_aScaleWidth;
+}
+
+
+const Fraction& SfxInPlaceClient::GetScaleHeight() const
+{
+ return m_xImp->m_aScaleHeight;
+}
+
+
+void SfxInPlaceClient::Invalidate()
+{
+ // TODO/LATER: do we need both?
+
+ // the object area is provided in logical coordinates of the window but without scaling applied
+ tools::Rectangle aRealObjArea( m_xImp->m_aObjArea );
+ aRealObjArea.SetSize( Size( tools::Long( aRealObjArea.GetWidth() * m_xImp->m_aScaleWidth ),
+ tools::Long( aRealObjArea.GetHeight() * m_xImp->m_aScaleHeight ) ) );
+
+ m_pEditWin->Invalidate( IsNegativeX() ? lcl_negateRectX(aRealObjArea) : aRealObjArea );
+
+ ViewChanged();
+}
+
+
+bool SfxInPlaceClient::IsObjectUIActive() const
+{
+ try {
+ return ( m_xImp->m_xObject.is() && ( m_xImp->m_xObject->getCurrentState() == embed::EmbedStates::UI_ACTIVE ) );
+ }
+ catch( uno::Exception& )
+ {}
+
+ return false;
+}
+
+
+bool SfxInPlaceClient::IsObjectInPlaceActive() const
+{
+ try {
+ return(
+ (
+ m_xImp->m_xObject.is() &&
+ (m_xImp->m_xObject->getCurrentState() == embed::EmbedStates::INPLACE_ACTIVE)
+ ) ||
+ (
+ m_xImp->m_xObject.is() &&
+ (m_xImp->m_xObject->getCurrentState() == embed::EmbedStates::UI_ACTIVE)
+ )
+ );
+ }
+ catch( uno::Exception& )
+ {}
+
+ return false;
+}
+
+
+SfxInPlaceClient* SfxInPlaceClient::GetClient( SfxObjectShell const * pDoc, const css::uno::Reference < css::embed::XEmbeddedObject >& xObject )
+{
+ for ( SfxViewFrame* pFrame = SfxViewFrame::GetFirst(pDoc); pFrame; pFrame=SfxViewFrame::GetNext(*pFrame,pDoc) )
+ {
+ if( pFrame->GetViewShell() )
+ {
+ SfxInPlaceClient* pClient = pFrame->GetViewShell()->FindIPClient( xObject, nullptr );
+ if ( pClient )
+ return pClient;
+ }
+ }
+
+ return nullptr;
+}
+
+sal_Int64 SfxInPlaceClient::GetAspect() const
+{
+ return m_xImp->m_nAspect;
+}
+
+ErrCode SfxInPlaceClient::DoVerb(sal_Int32 nVerb)
+{
+ SfxErrorContext aEc(ERRCTX_SO_DOVERB, m_pViewSh->GetFrameWeld(), RID_SO_ERRCTX);
+ ErrCode nError = ERRCODE_NONE;
+
+ if ( m_xImp->m_xObject.is() )
+ {
+ bool bSaveCopyAs = false;
+ if ( nVerb == -8 ) // "Save Copy as..."
+ {
+ svt::EmbeddedObjectRef::TryRunningState( m_xImp->m_xObject );
+ // TODO/LATER: this special verb should disappear when outplace activation is completely available
+ uno::Reference< frame::XModel > xEmbModel( m_xImp->m_xObject->getComponent(), uno::UNO_QUERY );
+ if ( xEmbModel.is() )
+ {
+ bSaveCopyAs = true;
+
+ try
+ {
+ SfxStoringHelper aHelper;
+ uno::Sequence< beans::PropertyValue > aDispatchArgs{
+ comphelper::makePropertyValue("SaveTo", true)
+ };
+
+ aHelper.GUIStoreModel( xEmbModel,
+ u"SaveAs",
+ aDispatchArgs,
+ false,
+ SignatureState::NOSIGNATURES );
+ }
+ catch( const task::ErrorCodeIOException& aErrorEx )
+ {
+ nError = ErrCode(aErrorEx.ErrCode);
+ }
+ catch( uno::Exception& )
+ {
+ nError = ERRCODE_IO_GENERAL;
+ // TODO/LATER: better error handling
+ }
+ }
+ }
+
+ if ( !bSaveCopyAs )
+ {
+ if ( m_xImp->m_nAspect == embed::Aspects::MSOLE_ICON )
+ {
+ // the common persistence is supported by objects and links
+
+ uno::Reference< embed::XEmbeddedOleObject > xEmbeddedOleObject( m_xImp->m_xObject, uno::UNO_QUERY );
+
+ if ( xEmbeddedOleObject.is() && (nVerb == embed::EmbedVerbs::MS_OLEVERB_PRIMARY || nVerb == embed::EmbedVerbs::MS_OLEVERB_OPEN || nVerb == embed::EmbedVerbs::MS_OLEVERB_SHOW ))
+ nVerb = embed::EmbedVerbs::MS_OLEVERB_SHOW;
+ else if ( nVerb == embed::EmbedVerbs::MS_OLEVERB_PRIMARY || nVerb == embed::EmbedVerbs::MS_OLEVERB_SHOW )
+ nVerb = embed::EmbedVerbs::MS_OLEVERB_OPEN; // outplace activation
+ else if ( nVerb == embed::EmbedVerbs::MS_OLEVERB_UIACTIVATE
+ || nVerb == embed::EmbedVerbs::MS_OLEVERB_IPACTIVATE )
+ nError = ERRCODE_SO_GENERALERROR;
+ }
+
+ if ( !nError )
+ {
+ // See comment for SfxInPlaceClient_Impl::getPlacement.
+ vcl::Window* pEditWin = GetEditWin();
+ bool bMapModeEnabled = pEditWin->IsMapModeEnabled();
+ if (comphelper::LibreOfficeKit::isActive() && !bMapModeEnabled)
+ {
+ pEditWin->EnableMapMode();
+ }
+ m_pViewSh->GetViewFrame()->GetFrame().LockResize_Impl(true);
+ try
+ {
+ m_xImp->m_xObject->setClientSite( m_xImp );
+
+ m_xImp->m_xObject->doVerb( nVerb );
+ }
+ catch ( embed::UnreachableStateException& )
+ {
+ if (nVerb == embed::EmbedVerbs::MS_OLEVERB_PRIMARY || nVerb == embed::EmbedVerbs::MS_OLEVERB_OPEN || nVerb == embed::EmbedVerbs::MS_OLEVERB_SHOW)
+ {
+ // a workaround for the default verb, usually makes sense for alien objects
+ try
+ {
+ m_xImp->m_xObject->doVerb( -9 ); // open own view, a workaround verb that is not visible
+
+ if ( m_xImp->m_xObject->getCurrentState() == embed::EmbedStates::UI_ACTIVE )
+ {
+ // the object was converted to OOo object
+ awt::Size aSize = m_xImp->m_xObject->getVisualAreaSize( m_xImp->m_nAspect );
+ MapMode aObjectMap( VCLUnoHelper::UnoEmbed2VCLMapUnit( m_xImp->m_xObject->getMapUnit( m_xImp->m_nAspect ) ) );
+ MapMode aClientMap( GetEditWin()->GetMapMode().GetMapUnit() );
+ Size aNewSize = GetEditWin()->LogicToLogic( Size( aSize.Width, aSize.Height ), &aObjectMap, &aClientMap );
+
+ tools::Rectangle aScaledArea = GetScaledObjArea();
+ m_xImp->m_aObjArea.SetSize( aNewSize );
+ m_xImp->m_aScaleWidth = Fraction( aScaledArea.GetWidth(), aNewSize.Width() );
+ m_xImp->m_aScaleHeight = Fraction( aScaledArea.GetHeight(), aNewSize.Height() );
+ }
+ }
+ catch (uno::Exception const&)
+ {
+ TOOLS_WARN_EXCEPTION("embeddedobj", "SfxInPlaceClient::DoVerb: -9 fallback path");
+ nError = ERRCODE_SO_GENERALERROR;
+ }
+ }
+ }
+ catch ( embed::StateChangeInProgressException& )
+ {
+ // TODO/LATER: it would be nice to be able to provide the current target state outside
+ nError = ERRCODE_SO_CANNOT_DOVERB_NOW;
+ }
+ catch (uno::Exception const&)
+ {
+ TOOLS_WARN_EXCEPTION("embeddedobj", "SfxInPlaceClient::DoVerb");
+ nError = ERRCODE_SO_GENERALERROR;
+ //TODO/LATER: better error handling
+
+ }
+ if (comphelper::LibreOfficeKit::isActive() && !bMapModeEnabled
+ && pEditWin->IsMapModeEnabled())
+ {
+ pEditWin->EnableMapMode(false);
+ }
+ SfxViewFrame* pFrame = m_pViewSh->GetViewFrame();
+ pFrame->GetFrame().LockResize_Impl(false);
+ pFrame->GetFrame().Resize();
+ }
+ }
+ }
+
+ if( nError )
+ ErrorHandler::HandleError( nError );
+
+ return nError;
+}
+
+void SfxInPlaceClient::VisAreaChanged()
+{
+ uno::Reference < embed::XInplaceObject > xObj( m_xImp->m_xObject, uno::UNO_QUERY );
+ if ( xObj.is() )
+ m_xImp->SizeHasChanged();
+}
+
+void SfxInPlaceClient::ObjectAreaChanged()
+{
+ // dummy implementation
+}
+
+void SfxInPlaceClient::RequestNewObjectArea( tools::Rectangle& )
+{
+ // dummy implementation
+}
+
+void SfxInPlaceClient::ViewChanged()
+{
+ // dummy implementation
+}
+
+void SfxInPlaceClient::FormatChanged()
+{
+ // dummy implementation
+}
+
+bool SfxInPlaceClient::IsProtected() const { return false; }
+
+void SfxInPlaceClient::DeactivateObject()
+{
+ if ( !GetObject().is() )
+ return;
+
+ try
+ {
+ m_xImp->m_bUIActive = false;
+ bool bHasFocus = false;
+ uno::Reference< frame::XModel > xModel( m_xImp->m_xObject->getComponent(), uno::UNO_QUERY );
+ if ( xModel.is() )
+ {
+ uno::Reference< frame::XController > xController = xModel->getCurrentController();
+ if ( xController.is() )
+ {
+ VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( xController->getFrame()->getContainerWindow() );
+ bHasFocus = pWindow->HasChildPathFocus( true );
+ }
+ }
+
+ m_pViewSh->GetViewFrame()->GetFrame().LockResize_Impl(true);
+
+ if ( m_xImp->m_xObject->getStatus( m_xImp->m_nAspect ) & embed::EmbedMisc::MS_EMBED_ACTIVATEWHENVISIBLE )
+ {
+ m_xImp->m_xObject->changeState( embed::EmbedStates::INPLACE_ACTIVE );
+ if (bHasFocus)
+ m_pViewSh->GetWindow()->GrabFocus();
+ }
+ else
+ {
+ // the links should not stay in running state for long time because of locking
+ uno::Reference< embed::XLinkageSupport > xLink( m_xImp->m_xObject, uno::UNO_QUERY );
+ if ( xLink.is() && xLink->isLink() )
+ m_xImp->m_xObject->changeState( embed::EmbedStates::LOADED );
+ else
+ m_xImp->m_xObject->changeState( embed::EmbedStates::RUNNING );
+ }
+
+ SfxViewFrame* pFrame = m_pViewSh->GetViewFrame();
+ SfxViewFrame::SetViewFrame( pFrame );
+ pFrame->GetFrame().LockResize_Impl(false);
+ pFrame->GetFrame().Resize();
+ }
+ catch (css::uno::Exception& )
+ {}
+}
+
+void SfxInPlaceClient::ResetObject()
+{
+ if ( !GetObject().is() )
+ return;
+
+ try
+ {
+ m_xImp->m_bUIActive = false;
+ if ( m_xImp->m_xObject->getStatus( m_xImp->m_nAspect ) & embed::EmbedMisc::MS_EMBED_ACTIVATEWHENVISIBLE )
+ m_xImp->m_xObject->changeState( embed::EmbedStates::INPLACE_ACTIVE );
+ else
+ {
+ // the links should not stay in running state for long time because of locking
+ uno::Reference< embed::XLinkageSupport > xLink( m_xImp->m_xObject, uno::UNO_QUERY );
+ if ( xLink.is() && xLink->isLink() )
+ m_xImp->m_xObject->changeState( embed::EmbedStates::LOADED );
+ else
+ m_xImp->m_xObject->changeState( embed::EmbedStates::RUNNING );
+ }
+ }
+ catch (css::uno::Exception& )
+ {}
+}
+
+bool SfxInPlaceClient::IsUIActive() const
+{
+ return m_xImp->m_bUIActive;
+}
+
+void SfxInPlaceClient::SetNegativeX(bool bSet)
+{
+ m_xImp->m_bNegativeX = bSet;
+}
+
+bool SfxInPlaceClient::IsNegativeX() const
+{
+ return m_xImp->m_bNegativeX;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/view/lokcharthelper.cxx b/sfx2/source/view/lokcharthelper.cxx
new file mode 100644
index 000000000..c7941e6aa
--- /dev/null
+++ b/sfx2/source/view/lokcharthelper.cxx
@@ -0,0 +1,369 @@
+/* -*- 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/lokcomponenthelpers.hxx>
+
+#include <comphelper/lok.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <LibreOfficeKit/LibreOfficeKitEnums.h>
+#include <sfx2/ipclient.hxx>
+#include <sfx2/lokhelper.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+#include <tools/fract.hxx>
+#include <tools/UnitConversion.hxx>
+#include <vcl/virdev.hxx>
+#include <vcl/window.hxx>
+
+#include <com/sun/star/embed/XEmbeddedObject.hpp>
+#include <com/sun/star/frame/XDispatch.hpp>
+#include <com/sun/star/chart2/XChartDocument.hpp>
+
+using namespace com::sun::star;
+
+css::uno::Reference<css::frame::XController>& LokChartHelper::GetXController()
+{
+ if(!mxController.is() && mpViewShell)
+ {
+ SfxInPlaceClient* pIPClient = mpViewShell->GetIPClient();
+ if (pIPClient)
+ {
+ const css::uno::Reference< ::css::embed::XEmbeddedObject >& xEmbObj = pIPClient->GetObject();
+ if( xEmbObj.is() )
+ {
+ ::css::uno::Reference< ::css::chart2::XChartDocument > xChart( xEmbObj->getComponent(), uno::UNO_QUERY );
+ if( xChart.is() )
+ {
+ ::css::uno::Reference< ::css::frame::XController > xChartController = xChart->getCurrentController();
+ if( xChartController.is() )
+ {
+ mxController = xChartController;
+ }
+ }
+ }
+ }
+ }
+
+ return mxController;
+}
+
+css::uno::Reference<css::frame::XDispatch>& LokChartHelper::GetXDispatcher()
+{
+ if( !mxDispatcher.is() )
+ {
+ ::css::uno::Reference< ::css::frame::XController >& xChartController = GetXController();
+ if( xChartController.is() )
+ {
+ ::css::uno::Reference< ::css::frame::XDispatch > xDispatcher( xChartController, uno::UNO_QUERY );
+ if( xDispatcher.is() )
+ {
+ mxDispatcher = xDispatcher;
+ }
+ }
+ }
+
+ return mxDispatcher;
+}
+
+vcl::Window* LokChartHelper::GetWindow()
+{
+ if (!mpWindow)
+ {
+ ::css::uno::Reference< ::css::frame::XController >& xChartController = GetXController();
+ if( xChartController.is() )
+ {
+ ::css::uno::Reference< ::css::frame::XFrame > xFrame = xChartController->getFrame();
+ if (xFrame.is())
+ {
+ ::css::uno::Reference< ::css::awt::XWindow > xDockerWin = xFrame->getContainerWindow();
+ vcl::Window* pParent = VCLUnoHelper::GetWindow( xDockerWin );
+ if (pParent)
+ {
+ sal_uInt16 nTotChildren = pParent->GetChildCount();
+ while (nTotChildren--)
+ {
+ vcl::Window* pChildWin = pParent->GetChild(nTotChildren);
+ if (pChildWin && pChildWin->IsChart())
+ {
+ mpWindow = pChildWin;
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return mpWindow.get();
+}
+
+tools::Rectangle LokChartHelper::GetChartBoundingBox()
+{
+ tools::Rectangle aBBox;
+ if (mpViewShell)
+ {
+ SfxInPlaceClient* pIPClient = mpViewShell->GetIPClient();
+ if (pIPClient)
+ {
+ vcl::Window* pRootWin = pIPClient->GetEditWin();
+ if (pRootWin)
+ {
+ vcl::Window* pWindow = GetWindow();
+ if (pWindow)
+ {
+ // In all cases, the following code fragment
+ // returns the chart bounding box in twips.
+ const MapMode& aCWMapMode = pWindow->GetMapMode();
+ constexpr auto p = o3tl::getConversionMulDiv(o3tl::Length::px, o3tl::Length::twip);
+ const auto& scaleX = aCWMapMode.GetScaleX();
+ const auto& scaleY = aCWMapMode.GetScaleY();
+ const auto nXNum = p.first * scaleX.GetDenominator();
+ const auto nXDen = p.second * scaleX.GetNumerator();
+ const auto nYNum = p.first * scaleY.GetDenominator();
+ const auto nYDen = p.second * scaleY.GetNumerator();
+
+ Point aOffset = pWindow->GetOffsetPixelFrom(*pRootWin);
+ if (mbNegativeX && AllSettings::GetLayoutRTL())
+ {
+ // If global RTL flag is set, vcl-window X offset of chart window is
+ // mirrored w.r.t parent window rectangle. This needs to be reverted.
+ aOffset.setX(pRootWin->GetOutOffXPixel() + pRootWin->GetSizePixel().Width()
+ - pWindow->GetOutOffXPixel() - pWindow->GetSizePixel().Width());
+
+ }
+
+ aOffset = aOffset.scale(nXNum, nXDen, nYNum, nYDen);
+ Size aSize = pWindow->GetSizePixel().scale(nXNum, nXDen, nYNum, nYDen);
+ aBBox = tools::Rectangle(aOffset, aSize);
+ }
+ }
+ }
+ }
+ return aBBox;
+}
+
+void LokChartHelper::Invalidate()
+{
+ mpWindow = nullptr;
+ mxDispatcher.clear();
+ mxController.clear();
+}
+
+bool LokChartHelper::Hit(const Point& aPos)
+{
+ if (mpViewShell)
+ {
+ vcl::Window* pChartWindow = GetWindow();
+ if (pChartWindow)
+ {
+ tools::Rectangle rChartBBox = GetChartBoundingBox();
+ return rChartBBox.Contains(aPos);
+ }
+ }
+ return false;
+}
+
+bool LokChartHelper::HitAny(const Point& aPos, bool bNegativeX)
+{
+ SfxViewShell* pCurView = SfxViewShell::Current();
+ int nPartForCurView = pCurView ? pCurView->getPart() : -1;
+ SfxViewShell* pViewShell = SfxViewShell::GetFirst();
+ while (pViewShell)
+ {
+ if (pViewShell->GetDocId() == pCurView->GetDocId() && pViewShell->getPart() == nPartForCurView)
+ {
+ LokChartHelper aChartHelper(pViewShell, bNegativeX);
+ if (aChartHelper.Hit(aPos))
+ return true;
+ }
+ pViewShell = SfxViewShell::GetNext(*pViewShell);
+ }
+ return false;
+}
+
+void LokChartHelper::PaintTile(VirtualDevice& rRenderContext, const tools::Rectangle& rTileRect)
+{
+ if (!mpViewShell)
+ return;
+
+ vcl::Window* pChartWindow = GetWindow();
+ if (!pChartWindow)
+ return;
+
+ tools::Rectangle aChartRect = GetChartBoundingBox();
+ tools::Rectangle aTestRect = rTileRect;
+ aTestRect.Intersection( aChartRect );
+ if (aTestRect.IsEmpty())
+ return;
+
+ Point aOffset( aChartRect.Left() - rTileRect.Left(), aChartRect.Top() - rTileRect.Top() );
+ Point aOffsetFromTile = convertTwipToMm100(aOffset);
+ Size aSize = convertTwipToMm100(aChartRect.GetSize());
+ tools::Rectangle aRectangle(Point(0,0), aSize);
+
+ bool bEnableMapMode = !pChartWindow->IsMapModeEnabled();
+ pChartWindow->EnableMapMode();
+ bool bRenderContextEnableMapMode = !rRenderContext.IsMapModeEnabled();
+ rRenderContext.EnableMapMode();
+
+ rRenderContext.Push(vcl::PushFlags::MAPMODE);
+
+ MapMode aCWMapMode = pChartWindow->GetMapMode();
+ aCWMapMode.SetScaleX(rRenderContext.GetMapMode().GetScaleX());
+ aCWMapMode.SetScaleY(rRenderContext.GetMapMode().GetScaleY());
+
+ aCWMapMode.SetOrigin(aOffsetFromTile);
+ rRenderContext.SetMapMode(aCWMapMode);
+
+ pChartWindow->Paint(rRenderContext, aRectangle);
+
+ rRenderContext.Pop();
+
+ if (bRenderContextEnableMapMode)
+ rRenderContext.EnableMapMode(false);
+ if (bEnableMapMode)
+ pChartWindow->EnableMapMode(false);
+}
+
+void LokChartHelper::PaintAllChartsOnTile(VirtualDevice& rDevice,
+ int nOutputWidth, int nOutputHeight,
+ int nTilePosX, int nTilePosY,
+ tools::Long nTileWidth, tools::Long nTileHeight,
+ bool bNegativeX)
+{
+ if (comphelper::LibreOfficeKit::isTiledAnnotations())
+ return;
+
+ // Resizes the virtual device so to contain the entries context
+ rDevice.SetOutputSizePixel(Size(nOutputWidth, nOutputHeight));
+
+ rDevice.Push(vcl::PushFlags::MAPMODE);
+ MapMode aMapMode(rDevice.GetMapMode());
+
+ // Scaling. Must convert from pixels to twips. We know
+ // that VirtualDevices use a DPI of 96.
+ const Fraction scale = conversionFract(o3tl::Length::px, o3tl::Length::twip);
+ Fraction scaleX = Fraction(nOutputWidth, nTileWidth) * scale;
+ Fraction scaleY = Fraction(nOutputHeight, nTileHeight) * scale;
+ aMapMode.SetScaleX(scaleX);
+ aMapMode.SetScaleY(scaleY);
+ rDevice.SetMapMode(aMapMode);
+
+ SfxViewShell* pCurView = SfxViewShell::Current();
+ int nPartForCurView = pCurView ? pCurView->getPart() : -1;
+ tools::Long nTileRectLeft = bNegativeX ? -nTilePosX - nTileWidth : nTilePosX;
+ tools::Rectangle aTileRect(Point(nTileRectLeft, nTilePosY), Size(nTileWidth, nTileHeight));
+ SfxViewShell* pViewShell = SfxViewShell::GetFirst();
+ while (pViewShell)
+ {
+ if (pCurView && pViewShell->GetDocId() == pCurView->GetDocId() && pViewShell->getPart() == nPartForCurView)
+ {
+ LokChartHelper aChartHelper(pViewShell, bNegativeX);
+ aChartHelper.PaintTile(rDevice, aTileRect);
+ }
+ pViewShell = SfxViewShell::GetNext(*pViewShell);
+ }
+ rDevice.Pop();
+}
+
+bool LokChartHelper::postMouseEvent(int nType, int nX, int nY,
+ int nCount, int nButtons, int nModifier,
+ double fScaleX, double fScaleY)
+{
+ Point aMousePos(nX, nY);
+ vcl::Window* pChartWindow = GetWindow();
+ if (pChartWindow)
+ {
+ tools::Rectangle rChartBBox = GetChartBoundingBox();
+ if (rChartBBox.Contains(aMousePos))
+ {
+ int nChartWinX = nX - rChartBBox.Left();
+ int nChartWinY = nY - rChartBBox.Top();
+
+ // chart window expects pixels, but the conversion factor
+ // can depend on the client zoom
+ Point aPos(nChartWinX * fScaleX, nChartWinY * fScaleY);
+
+ LokMouseEventData aMouseEventData(nType, aPos, nCount, MouseEventModifiers::SIMPLECLICK,
+ nButtons, nModifier);
+ SfxLokHelper::postMouseEventAsync(pChartWindow, aMouseEventData);
+
+ return true;
+ }
+ }
+ return false;
+}
+
+bool LokChartHelper::setTextSelection(int nType, int nX, int nY)
+{
+ tools::Rectangle rChartBBox = GetChartBoundingBox();
+ if (rChartBBox.Contains(Point(nX, nY)))
+ {
+ css::uno::Reference<css::frame::XDispatch> xDispatcher = GetXDispatcher();
+ if (xDispatcher.is())
+ {
+ int nChartWinX = nX - rChartBBox.Left();
+ int nChartWinY = nY - rChartBBox.Top();
+
+ // no scale here the chart controller expects twips
+ // that are converted to hmm
+ util::URL aURL;
+ aURL.Path = "LOKSetTextSelection";
+ uno::Sequence< beans::PropertyValue > aArgs{
+ comphelper::makePropertyValue({}, static_cast<sal_Int32>(nType)), // Why no name?
+ comphelper::makePropertyValue({}, static_cast<sal_Int32>(nChartWinX)),
+ comphelper::makePropertyValue({}, static_cast<sal_Int32>(nChartWinY))
+ };
+ xDispatcher->dispatch(aURL, aArgs);
+ }
+ return true;
+ }
+ return false;
+}
+
+bool LokChartHelper::setGraphicSelection(int nType, int nX, int nY,
+ double fScaleX, double fScaleY)
+{
+ tools::Rectangle rChartBBox = GetChartBoundingBox();
+ if (rChartBBox.Contains(Point(nX, nY)))
+ {
+ int nChartWinX = nX - rChartBBox.Left();
+ int nChartWinY = nY - rChartBBox.Top();
+
+ vcl::Window* pChartWindow = GetWindow();
+
+ Point aPos(nChartWinX * fScaleX, nChartWinY * fScaleY);
+ switch (nType)
+ {
+ case LOK_SETGRAPHICSELECTION_START:
+ {
+ MouseEvent aClickEvent(aPos, 1, MouseEventModifiers::SIMPLECLICK, MOUSE_LEFT);
+ pChartWindow->MouseButtonDown(aClickEvent);
+ MouseEvent aMoveEvent(aPos, 0, MouseEventModifiers::SIMPLEMOVE, MOUSE_LEFT);
+ pChartWindow->MouseMove(aMoveEvent);
+ }
+ break;
+ case LOK_SETGRAPHICSELECTION_END:
+ {
+ MouseEvent aMoveEvent(aPos, 0, MouseEventModifiers::SIMPLEMOVE, MOUSE_LEFT);
+ pChartWindow->MouseMove(aMoveEvent);
+ MouseEvent aClickEvent(aPos, 1, MouseEventModifiers::SIMPLECLICK, MOUSE_LEFT);
+ pChartWindow->MouseButtonUp(aClickEvent);
+ }
+ break;
+ default:
+ assert(false);
+ break;
+ }
+ return true;
+ }
+ return false;
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/view/lokhelper.cxx b/sfx2/source/view/lokhelper.cxx
new file mode 100644
index 000000000..69cbc8b3d
--- /dev/null
+++ b/sfx2/source/view/lokhelper.cxx
@@ -0,0 +1,854 @@
+/* -*- 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 <sal/config.h>
+
+#include <string_view>
+
+#include <sfx2/lokhelper.hxx>
+
+#include <com/sun/star/frame/Desktop.hpp>
+
+#include <comphelper/processfactory.hxx>
+#include <rtl/strbuf.hxx>
+#include <vcl/lok.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/commandevent.hxx>
+#include <vcl/window.hxx>
+#include <sal/log.hxx>
+#include <sfx2/app.hxx>
+#include <sfx2/msg.hxx>
+#include <sfx2/viewsh.hxx>
+#include <sfx2/request.hxx>
+#include <sfx2/sfxsids.hrc>
+#include <sfx2/viewfrm.hxx>
+#include <LibreOfficeKit/LibreOfficeKitEnums.h>
+#include <comphelper/lok.hxx>
+#include <sfx2/msgpool.hxx>
+
+#include <boost/property_tree/json_parser.hpp>
+
+using namespace com::sun::star;
+
+namespace {
+/// Used to disable callbacks.
+/// Needed to avoid recursion when switching views,
+/// which can cause clients to invoke LOKit API and
+/// implicitly set the view, which might cause an
+/// infinite recursion if not detected and prevented.
+class DisableCallbacks
+{
+public:
+ DisableCallbacks()
+ {
+ assert(m_nDisabled >= 0 && "Expected non-negative DisabledCallbacks state when disabling.");
+ ++m_nDisabled;
+ }
+
+ ~DisableCallbacks()
+ {
+ assert(m_nDisabled > 0 && "Expected positive DisabledCallbacks state when re-enabling.");
+ --m_nDisabled;
+ }
+
+ static inline bool disabled()
+ {
+ return !comphelper::LibreOfficeKit::isActive() || m_nDisabled != 0;
+ }
+
+private:
+ static int m_nDisabled;
+};
+
+int DisableCallbacks::m_nDisabled = 0;
+}
+
+namespace
+{
+LanguageTag g_defaultLanguageTag("en-US", true);
+LOKDeviceFormFactor g_deviceFormFactor = LOKDeviceFormFactor::UNKNOWN;
+}
+
+int SfxLokHelper::createView(SfxViewFrame* pViewFrame, ViewShellDocId docId)
+{
+ assert(docId >= ViewShellDocId(0) && "Cannot createView for invalid (negative) DocId.");
+
+ if (pViewFrame == nullptr)
+ return -1;
+
+ SfxViewShell::SetCurrentDocId(docId);
+ SfxRequest aRequest(pViewFrame, SID_NEWWINDOW);
+ pViewFrame->ExecView_Impl(aRequest);
+ SfxViewShell* pViewShell = SfxViewShell::Current();
+ if (pViewShell == nullptr)
+ return -1;
+
+ assert(pViewShell->GetDocId() == docId && "DocId must be already set!");
+ return static_cast<sal_Int32>(pViewShell->GetViewShellId());
+}
+
+int SfxLokHelper::createView()
+{
+ // Assumes a single document, or at least that the
+ // current view belongs to the document on which the
+ // view will be created.
+ SfxViewShell* pViewShell = SfxViewShell::Current();
+ if (pViewShell == nullptr)
+ return -1;
+
+ return createView(pViewShell->GetViewFrame(), pViewShell->GetDocId());
+}
+
+int SfxLokHelper::createView(int nDocId)
+{
+ const SfxApplication* pApp = SfxApplication::Get();
+ if (pApp == nullptr)
+ return -1;
+
+ // Find a shell with the given DocId.
+ const ViewShellDocId docId(nDocId);
+ for (const SfxViewShell* pViewShell : pApp->GetViewShells_Impl())
+ {
+ if (pViewShell->GetDocId() == docId)
+ return createView(pViewShell->GetViewFrame(), docId);
+ }
+
+ // No frame with nDocId found.
+ return -1;
+}
+
+void SfxLokHelper::destroyView(int nId)
+{
+ const SfxApplication* pApp = SfxApplication::Get();
+ if (pApp == nullptr)
+ return;
+
+ const ViewShellId nViewShellId(nId);
+ std::vector<SfxViewShell*>& rViewArr = pApp->GetViewShells_Impl();
+
+ for (const SfxViewShell* pViewShell : rViewArr)
+ {
+ if (pViewShell->GetViewShellId() == nViewShellId)
+ {
+ SfxViewFrame* pViewFrame = pViewShell->GetViewFrame();
+ SfxRequest aRequest(pViewFrame, SID_CLOSEWIN);
+ pViewFrame->Exec_Impl(aRequest);
+ break;
+ }
+ }
+}
+
+void SfxLokHelper::setView(int nId)
+{
+ SfxApplication* pApp = SfxApplication::Get();
+ if (pApp == nullptr)
+ return;
+
+ const ViewShellId nViewShellId(nId);
+ std::vector<SfxViewShell*>& rViewArr = pApp->GetViewShells_Impl();
+
+ for (const SfxViewShell* pViewShell : rViewArr)
+ {
+ if (pViewShell->GetViewShellId() == nViewShellId)
+ {
+ DisableCallbacks dc;
+
+ // update the current LOK language and locale for the dialog tunneling
+ comphelper::LibreOfficeKit::setLanguageTag(pViewShell->GetLOKLanguageTag());
+ comphelper::LibreOfficeKit::setLocale(pViewShell->GetLOKLocale());
+
+ if (pViewShell == SfxViewShell::Current())
+ return;
+
+ SfxViewFrame* pViewFrame = pViewShell->GetViewFrame();
+ pViewFrame->MakeActive_Impl(false);
+
+ // Make comphelper::dispatchCommand() find the correct frame.
+ uno::Reference<frame::XFrame> xFrame = pViewFrame->GetFrame().GetFrameInterface();
+ uno::Reference<frame::XDesktop2> xDesktop = frame::Desktop::create(comphelper::getProcessComponentContext());
+ xDesktop->setActiveFrame(xFrame);
+ return;
+ }
+ }
+
+}
+
+SfxViewShell* SfxLokHelper::getViewOfId(int nId)
+{
+ SfxApplication* pApp = SfxApplication::Get();
+ if (pApp == nullptr)
+ return nullptr;
+
+ const ViewShellId nViewShellId(nId);
+ std::vector<SfxViewShell*>& rViewArr = pApp->GetViewShells_Impl();
+ for (SfxViewShell* pViewShell : rViewArr)
+ {
+ if (pViewShell->GetViewShellId() == nViewShellId)
+ return pViewShell;
+ }
+
+ return nullptr;
+}
+
+int SfxLokHelper::getView(const SfxViewShell* pViewShell)
+{
+ if (!pViewShell)
+ pViewShell = SfxViewShell::Current();
+ // Still no valid view shell? Then no idea.
+ if (!pViewShell)
+ return -1;
+
+ return static_cast<sal_Int32>(pViewShell->GetViewShellId());
+}
+
+std::size_t SfxLokHelper::getViewsCount(int nDocId)
+{
+ assert(nDocId != -1 && "Cannot getViewsCount for invalid DocId -1");
+
+ SfxApplication* pApp = SfxApplication::Get();
+ if (!pApp)
+ return 0;
+
+ const ViewShellDocId nCurrentDocId(nDocId);
+ std::size_t n = 0;
+ SfxViewShell* pViewShell = SfxViewShell::GetFirst();
+ while (pViewShell)
+ {
+ if (pViewShell->GetDocId() == nCurrentDocId)
+ n++;
+ pViewShell = SfxViewShell::GetNext(*pViewShell);
+ }
+
+ return n;
+}
+
+bool SfxLokHelper::getViewIds(int nDocId, int* pArray, size_t nSize)
+{
+ assert(nDocId != -1 && "Cannot getViewsIds for invalid DocId -1");
+
+ SfxApplication* pApp = SfxApplication::Get();
+ if (!pApp)
+ return false;
+
+ const ViewShellDocId nCurrentDocId(nDocId);
+ std::size_t n = 0;
+ SfxViewShell* pViewShell = SfxViewShell::GetFirst();
+ while (pViewShell)
+ {
+ if (pViewShell->GetDocId() == nCurrentDocId)
+ {
+ if (n == nSize)
+ return false;
+
+ pArray[n] = static_cast<sal_Int32>(pViewShell->GetViewShellId());
+ n++;
+ }
+
+ pViewShell = SfxViewShell::GetNext(*pViewShell);
+ }
+
+ return true;
+}
+
+int SfxLokHelper::getDocumentIdOfView(int nViewId)
+{
+ SfxViewShell* pViewShell = SfxViewShell::GetFirst();
+ while (pViewShell)
+ {
+ if (pViewShell->GetViewShellId() == ViewShellId(nViewId))
+ return static_cast<int>(pViewShell->GetDocId());
+ pViewShell = SfxViewShell::GetNext(*pViewShell);
+ }
+ return -1;
+}
+
+const LanguageTag & SfxLokHelper::getDefaultLanguage()
+{
+ return g_defaultLanguageTag;
+}
+
+void SfxLokHelper::setDefaultLanguage(const OUString& rBcp47LanguageTag)
+{
+ g_defaultLanguageTag = LanguageTag(rBcp47LanguageTag, true);
+}
+
+void SfxLokHelper::setViewLanguage(int nId, const OUString& rBcp47LanguageTag)
+{
+ std::vector<SfxViewShell*>& rViewArr = SfxGetpApp()->GetViewShells_Impl();
+
+ for (SfxViewShell* pViewShell : rViewArr)
+ {
+ if (pViewShell->GetViewShellId() == ViewShellId(nId))
+ {
+ pViewShell->SetLOKLanguageTag(rBcp47LanguageTag);
+ return;
+ }
+ }
+}
+
+void SfxLokHelper::setViewLocale(int nId, const OUString& rBcp47LanguageTag)
+{
+ std::vector<SfxViewShell*>& rViewArr = SfxGetpApp()->GetViewShells_Impl();
+
+ for (SfxViewShell* pViewShell : rViewArr)
+ {
+ if (pViewShell->GetViewShellId() == ViewShellId(nId))
+ {
+ pViewShell->SetLOKLocale(rBcp47LanguageTag);
+ return;
+ }
+ }
+}
+
+LOKDeviceFormFactor SfxLokHelper::getDeviceFormFactor()
+{
+ return g_deviceFormFactor;
+}
+
+void SfxLokHelper::setDeviceFormFactor(std::u16string_view rDeviceFormFactor)
+{
+ if (rDeviceFormFactor == u"desktop")
+ g_deviceFormFactor = LOKDeviceFormFactor::DESKTOP;
+ else if (rDeviceFormFactor == u"tablet")
+ g_deviceFormFactor = LOKDeviceFormFactor::TABLET;
+ else if (rDeviceFormFactor == u"mobile")
+ g_deviceFormFactor = LOKDeviceFormFactor::MOBILE;
+ else
+ g_deviceFormFactor = LOKDeviceFormFactor::UNKNOWN;
+}
+
+/*
+* Used for putting a whole JSON string into a string value
+* e.g { key: "{JSON}" }
+*/
+static OString lcl_sanitizeJSONAsValue(const OString &rStr)
+{
+ if (rStr.getLength() < 1)
+ return rStr;
+ // FIXME: need an optimized 'escape' method for O[U]String.
+ OStringBuffer aBuf(rStr.getLength() + 8);
+ for (sal_Int32 i = 0; i < rStr.getLength(); ++i)
+ {
+ if (rStr[i] == '"' || rStr[i] == '\\')
+ aBuf.append('\\');
+
+ if (rStr[i] != '\n')
+ aBuf.append(rStr[i]);
+ }
+ return aBuf.makeStringAndClear();
+}
+
+static OString lcl_generateJSON(const SfxViewShell* pView, const boost::property_tree::ptree& rTree)
+{
+ assert(pView != nullptr && "pView must be valid");
+ boost::property_tree::ptree aMessageProps = rTree;
+ aMessageProps.put("viewId", SfxLokHelper::getView(pView));
+ aMessageProps.put("part", pView->getPart());
+ std::stringstream aStream;
+ boost::property_tree::write_json(aStream, aMessageProps, false /* pretty */);
+ const std::string aString = aStream.str();
+ return OString(aString.c_str(), aString.size()).trim();
+}
+
+static inline OString lcl_generateJSON(const SfxViewShell* pView, int nViewId, std::string_view rKey,
+ const OString& rPayload)
+{
+ assert(pView != nullptr && "pView must be valid");
+ return OString::Concat("{ \"viewId\": \"") + OString::number(nViewId)
+ + "\", \"part\": \"" + OString::number(pView->getPart()) + "\", \"" + rKey + "\": \""
+ + lcl_sanitizeJSONAsValue(rPayload) + "\" }";
+}
+
+static inline OString lcl_generateJSON(const SfxViewShell* pView, std::string_view rKey,
+ const OString& rPayload)
+{
+ return lcl_generateJSON(pView, SfxLokHelper::getView(pView), rKey, rPayload);
+}
+
+void SfxLokHelper::notifyOtherView(const SfxViewShell* pThisView, SfxViewShell const* pOtherView,
+ int nType, std::string_view rKey, const OString& rPayload)
+{
+ assert(pThisView != nullptr && "pThisView must be valid");
+ if (DisableCallbacks::disabled())
+ return;
+
+ const OString aPayload = lcl_generateJSON(pThisView, rKey, rPayload);
+ const int viewId = SfxLokHelper::getView(pThisView);
+ pOtherView->libreOfficeKitViewCallbackWithViewId(nType, aPayload.getStr(), viewId);
+}
+
+void SfxLokHelper::notifyOtherView(const SfxViewShell* pThisView, SfxViewShell const* pOtherView,
+ int nType, const boost::property_tree::ptree& rTree)
+{
+ assert(pThisView != nullptr && "pThisView must be valid");
+ if (DisableCallbacks::disabled())
+ return;
+
+ const int viewId = SfxLokHelper::getView(pThisView);
+ pOtherView->libreOfficeKitViewCallbackWithViewId(nType, lcl_generateJSON(pThisView, rTree).getStr(), viewId);
+}
+
+void SfxLokHelper::notifyOtherViews(const SfxViewShell* pThisView, int nType, std::string_view rKey,
+ const OString& rPayload)
+{
+ assert(pThisView != nullptr && "pThisView must be valid");
+ if (DisableCallbacks::disabled())
+ return;
+
+ // Cache the payload so we only have to generate it once, at most.
+ OString aPayload;
+ int viewId = -1;
+
+ const ViewShellDocId nCurrentDocId = pThisView->GetDocId();
+ SfxViewShell* pViewShell = SfxViewShell::GetFirst();
+ while (pViewShell)
+ {
+ if (pViewShell != pThisView && nCurrentDocId == pViewShell->GetDocId())
+ {
+ // Payload is only dependent on pThisView.
+ if (aPayload.isEmpty())
+ {
+ aPayload = lcl_generateJSON(pThisView, rKey, rPayload);
+ viewId = SfxLokHelper::getView(pThisView);
+ }
+
+ pViewShell->libreOfficeKitViewCallbackWithViewId(nType, aPayload.getStr(), viewId);
+ }
+
+ pViewShell = SfxViewShell::GetNext(*pViewShell);
+ }
+}
+
+void SfxLokHelper::notifyOtherViews(const SfxViewShell* pThisView, int nType,
+ const boost::property_tree::ptree& rTree)
+{
+ assert(pThisView != nullptr && "pThisView must be valid");
+ if (DisableCallbacks::disabled())
+ return;
+
+ // Cache the payload so we only have to generate it once, at most.
+ OString aPayload;
+ int viewId = -1;
+
+ const ViewShellDocId nCurrentDocId = pThisView->GetDocId();
+ SfxViewShell* pViewShell = SfxViewShell::GetFirst();
+ while (pViewShell)
+ {
+ if (pViewShell != pThisView && nCurrentDocId == pViewShell->GetDocId())
+ {
+ // Payload is only dependent on pThisView.
+ if (aPayload.isEmpty())
+ {
+ aPayload = lcl_generateJSON(pThisView, rTree);
+ viewId = SfxLokHelper::getView(pThisView);
+ }
+
+ pViewShell->libreOfficeKitViewCallbackWithViewId(nType, aPayload.getStr(), viewId);
+ }
+
+ pViewShell = SfxViewShell::GetNext(*pViewShell);
+ }
+}
+
+OString SfxLokHelper::makePayloadJSON(const SfxViewShell* pThisView, int nViewId, std::string_view rKey, const OString& rPayload)
+{
+ return lcl_generateJSON(pThisView, nViewId, rKey, rPayload);
+}
+
+namespace {
+ OUString lcl_getNameForSlot(const SfxViewShell* pShell, sal_uInt16 nWhich)
+ {
+ if (pShell && pShell->GetFrame())
+ {
+ const SfxSlot* pSlot = SfxSlotPool::GetSlotPool(pShell->GetFrame()).GetSlot(nWhich);
+ if (pSlot)
+ {
+ const char* pName = pSlot->GetUnoName();
+ if (pName)
+ {
+ return ".uno:" + OStringToOUString(pName, RTL_TEXTENCODING_ASCII_US);
+ }
+ }
+ }
+
+ return "";
+ }
+}
+
+void SfxLokHelper::sendUnoStatus(const SfxViewShell* pShell, const SfxPoolItem* pItem)
+{
+ if (!pShell || !pItem || pItem == INVALID_POOL_ITEM || DisableCallbacks::disabled())
+ return;
+
+ boost::property_tree::ptree aItem = pItem->dumpAsJSON();
+
+ if (aItem.count("state"))
+ {
+ OUString sCommand = lcl_getNameForSlot(pShell, pItem->Which());
+ if (!sCommand.isEmpty())
+ aItem.put("commandName", sCommand);
+
+ std::stringstream aStream;
+ boost::property_tree::write_json(aStream, aItem);
+ pShell->libreOfficeKitViewCallback(LOK_CALLBACK_STATE_CHANGED, aStream.str().c_str());
+ }
+}
+
+void SfxLokHelper::notifyWindow(const SfxViewShell* pThisView,
+ vcl::LOKWindowId nLOKWindowId,
+ std::u16string_view rAction,
+ const std::vector<vcl::LOKPayloadItem>& rPayload)
+{
+ assert(pThisView != nullptr && "pThisView must be valid");
+
+ if (nLOKWindowId == 0 || DisableCallbacks::disabled())
+ return;
+
+ OStringBuffer aPayload =
+ "{ \"id\": \"" + OString::number(nLOKWindowId) + "\""
+ ", \"action\": \"" + OUStringToOString(rAction, RTL_TEXTENCODING_UTF8) + "\"";
+
+ for (const auto& rItem: rPayload)
+ {
+ if (!rItem.first.isEmpty() && !rItem.second.isEmpty())
+ {
+ aPayload.append(", \"" + rItem.first + "\": \"" +
+ rItem.second).append('"');
+ }
+ }
+ aPayload.append('}');
+
+ const OString s = aPayload.makeStringAndClear();
+ pThisView->libreOfficeKitViewCallback(LOK_CALLBACK_WINDOW, s.getStr());
+}
+
+void SfxLokHelper::notifyInvalidation(SfxViewShell const* pThisView, tools::Rectangle const* pRect)
+{
+ if (DisableCallbacks::disabled())
+ return;
+
+ const int nPart = comphelper::LibreOfficeKit::isPartInInvalidation() ? pThisView->getPart() : INT_MIN;
+ pThisView->libreOfficeKitViewInvalidateTilesCallback(pRect, nPart);
+}
+
+void SfxLokHelper::notifyDocumentSizeChanged(SfxViewShell const* pThisView, const OString& rPayload, vcl::ITiledRenderable* pDoc, bool bInvalidateAll)
+{
+ if (!pDoc || pDoc->isDisposed() || DisableCallbacks::disabled())
+ return;
+
+ if (bInvalidateAll)
+ {
+ for (int i = 0; i < pDoc->getParts(); ++i)
+ {
+ tools::Rectangle aRectangle(0, 0, 1000000000, 1000000000);
+ pThisView->libreOfficeKitViewInvalidateTilesCallback(&aRectangle, i);
+ }
+ }
+ pThisView->libreOfficeKitViewCallback(LOK_CALLBACK_DOCUMENT_SIZE_CHANGED, rPayload.getStr());
+}
+
+void SfxLokHelper::notifyDocumentSizeChangedAllViews(vcl::ITiledRenderable* pDoc, bool bInvalidateAll)
+{
+ if (DisableCallbacks::disabled())
+ return;
+
+ // FIXME: Do we know whether it is the views for the document that is in the "current" view that has changed?
+ const SfxViewShell* const pCurrentViewShell = SfxViewShell::Current();
+ SfxViewShell* pViewShell = SfxViewShell::GetFirst();
+ while (pViewShell)
+ {
+ // FIXME: What if SfxViewShell::Current() returned null?
+ // Should we then do this for all views of all open documents
+ // or not?
+ if (pCurrentViewShell == nullptr || pViewShell->GetDocId() == pCurrentViewShell-> GetDocId())
+ {
+ SfxLokHelper::notifyDocumentSizeChanged(pViewShell, "", pDoc, bInvalidateAll);
+ bInvalidateAll = false; // we direct invalidations to all views anyway.
+ }
+ pViewShell = SfxViewShell::GetNext(*pViewShell);
+ }
+}
+
+OString SfxLokHelper::makeVisCursorInvalidation(int nViewId, const OString& rRectangle,
+ bool bMispelledWord, const OString& rHyperlink)
+{
+ if (comphelper::LibreOfficeKit::isViewIdForVisCursorInvalidation())
+ {
+ OString sHyperlink = rHyperlink.isEmpty() ? "{}" : rHyperlink;
+ return OString::Concat("{ \"viewId\": \"") + OString::number(nViewId) +
+ "\", \"rectangle\": \"" + rRectangle +
+ "\", \"mispelledWord\": \"" + OString::number(bMispelledWord ? 1 : 0) +
+ "\", \"hyperlink\": " + sHyperlink + " }";
+ }
+ else
+ {
+ return rRectangle;
+ }
+}
+
+void SfxLokHelper::notifyAllViews(int nType, const OString& rPayload)
+{
+ if (DisableCallbacks::disabled())
+ return;
+
+ const auto payload = rPayload.getStr();
+ const SfxViewShell* const pCurrentViewShell = SfxViewShell::Current();
+ SfxViewShell* pViewShell = SfxViewShell::GetFirst();
+ while (pViewShell)
+ {
+ if (pViewShell->GetDocId() == pCurrentViewShell->GetDocId())
+ pViewShell->libreOfficeKitViewCallback(nType, payload);
+ pViewShell = SfxViewShell::GetNext(*pViewShell);
+ }
+}
+
+void SfxLokHelper::notifyContextChange(SfxViewShell const* pViewShell, const OUString& aApplication, const OUString& aContext)
+{
+ if (DisableCallbacks::disabled())
+ return;
+
+ OString aBuffer =
+ OUStringToOString(aApplication.replace(' ', '_'), RTL_TEXTENCODING_UTF8) +
+ " " +
+ OUStringToOString(aContext.replace(' ', '_'), RTL_TEXTENCODING_UTF8);
+ pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_CONTEXT_CHANGED, aBuffer.getStr());
+}
+
+void SfxLokHelper::notifyUpdate(SfxViewShell const* pThisView, int nType)
+{
+ if (DisableCallbacks::disabled())
+ return;
+
+ pThisView->libreOfficeKitViewUpdatedCallback(nType);
+}
+
+void SfxLokHelper::notifyUpdatePerViewId(SfxViewShell const* pThisView, int nType)
+{
+ notifyUpdatePerViewId(pThisView, pThisView, pThisView, nType);
+}
+
+void SfxLokHelper::notifyUpdatePerViewId(SfxViewShell const* pTargetShell, SfxViewShell const* pViewShell,
+ SfxViewShell const* pSourceShell, int nType)
+{
+ if (DisableCallbacks::disabled())
+ return;
+
+ int viewId = SfxLokHelper::getView(pViewShell);
+ int sourceViewId = SfxLokHelper::getView(pSourceShell);
+ pTargetShell->libreOfficeKitViewUpdatedCallbackPerViewId(nType, viewId, sourceViewId);
+}
+
+void SfxLokHelper::notifyOtherViewsUpdatePerViewId(SfxViewShell const* pThisView, int nType)
+{
+ assert(pThisView != nullptr && "pThisView must be valid");
+ if (DisableCallbacks::disabled())
+ return;
+
+ int viewId = SfxLokHelper::getView(pThisView);
+ const ViewShellDocId nCurrentDocId = pThisView->GetDocId();
+ SfxViewShell* pViewShell = SfxViewShell::GetFirst();
+ while (pViewShell)
+ {
+ if (pViewShell != pThisView && nCurrentDocId == pViewShell->GetDocId())
+ pViewShell->libreOfficeKitViewUpdatedCallbackPerViewId(nType, viewId, viewId);
+
+ pViewShell = SfxViewShell::GetNext(*pViewShell);
+ }
+}
+
+namespace
+{
+ struct LOKAsyncEventData
+ {
+ int mnView; // Window is not enough.
+ VclPtr<vcl::Window> mpWindow;
+ VclEventId mnEvent;
+ MouseEvent maMouseEvent;
+ KeyEvent maKeyEvent;
+ OUString maText;
+ };
+
+ void LOKPostAsyncEvent(void* pEv, void*)
+ {
+ std::unique_ptr<LOKAsyncEventData> pLOKEv(static_cast<LOKAsyncEventData*>(pEv));
+ if (pLOKEv->mpWindow->isDisposed())
+ return;
+
+ int nView = SfxLokHelper::getView(nullptr);
+ if (nView != pLOKEv->mnView)
+ {
+ SAL_INFO("sfx.view", "LOK - view mismatch " << nView << " vs. " << pLOKEv->mnView);
+ SfxLokHelper::setView(pLOKEv->mnView);
+ }
+
+ if (!pLOKEv->mpWindow->HasChildPathFocus(true))
+ {
+ SAL_INFO("sfx.view", "LOK - focus mismatch, switching focus");
+ pLOKEv->mpWindow->GrabFocus();
+ }
+
+ VclPtr<vcl::Window> pFocusWindow = pLOKEv->mpWindow->GetFocusedWindow();
+ if (!pFocusWindow)
+ pFocusWindow = pLOKEv->mpWindow;
+
+ if (pLOKEv->mpWindow->isDisposed())
+ return;
+
+ switch (pLOKEv->mnEvent)
+ {
+ case VclEventId::WindowKeyInput:
+ {
+ sal_uInt16 nRepeat = pLOKEv->maKeyEvent.GetRepeat();
+ KeyEvent singlePress(pLOKEv->maKeyEvent.GetCharCode(),
+ pLOKEv->maKeyEvent.GetKeyCode());
+ for (sal_uInt16 i = 0; i <= nRepeat; ++i)
+ if (!pFocusWindow->isDisposed())
+ pFocusWindow->KeyInput(singlePress);
+ break;
+ }
+ case VclEventId::WindowKeyUp:
+ if (!pFocusWindow->isDisposed())
+ pFocusWindow->KeyUp(pLOKEv->maKeyEvent);
+ break;
+ case VclEventId::WindowMouseButtonDown:
+ pLOKEv->mpWindow->LogicMouseButtonDown(pLOKEv->maMouseEvent);
+ // Invoke the context menu
+ if (pLOKEv->maMouseEvent.GetButtons() & MOUSE_RIGHT)
+ {
+ const CommandEvent aCEvt(pLOKEv->maMouseEvent.GetPosPixel(), CommandEventId::ContextMenu, true, nullptr);
+ pLOKEv->mpWindow->Command(aCEvt);
+ }
+ break;
+ case VclEventId::WindowMouseButtonUp:
+ pLOKEv->mpWindow->LogicMouseButtonUp(pLOKEv->maMouseEvent);
+
+ // sometimes MouseButtonDown captures mouse and starts tracking, and VCL
+ // will not take care of releasing that with tiled rendering
+ if (pLOKEv->mpWindow->IsTracking())
+ pLOKEv->mpWindow->EndTracking();
+
+ break;
+ case VclEventId::WindowMouseMove:
+ pLOKEv->mpWindow->LogicMouseMove(pLOKEv->maMouseEvent);
+ break;
+ case VclEventId::ExtTextInput:
+ case VclEventId::EndExtTextInput:
+ pLOKEv->mpWindow->PostExtTextInputEvent(pLOKEv->mnEvent, pLOKEv->maText);
+ break;
+ default:
+ assert(false);
+ break;
+ }
+ }
+
+ void postEventAsync(LOKAsyncEventData *pEvent)
+ {
+ if (!pEvent->mpWindow || pEvent->mpWindow->isDisposed())
+ {
+ SAL_WARN("vcl", "Async event post - but no valid window as destination " << pEvent->mpWindow.get());
+ delete pEvent;
+ return;
+ }
+
+ pEvent->mnView = SfxLokHelper::getView(nullptr);
+ if (vcl::lok::isUnipoll())
+ {
+ if (!Application::IsMainThread())
+ SAL_WARN("lok", "Posting event directly but not called from main thread!");
+ LOKPostAsyncEvent(pEvent, nullptr);
+ }
+ else
+ Application::PostUserEvent(Link<void*, void>(pEvent, LOKPostAsyncEvent));
+ }
+}
+
+void SfxLokHelper::postKeyEventAsync(const VclPtr<vcl::Window> &xWindow,
+ int nType, int nCharCode, int nKeyCode, int nRepeat)
+{
+ LOKAsyncEventData* pLOKEv = new LOKAsyncEventData;
+ switch (nType)
+ {
+ case LOK_KEYEVENT_KEYINPUT:
+ pLOKEv->mnEvent = VclEventId::WindowKeyInput;
+ break;
+ case LOK_KEYEVENT_KEYUP:
+ pLOKEv->mnEvent = VclEventId::WindowKeyUp;
+ break;
+ default:
+ assert(false);
+ }
+ pLOKEv->maKeyEvent = KeyEvent(nCharCode, nKeyCode, nRepeat);
+ pLOKEv->mpWindow = xWindow;
+ postEventAsync(pLOKEv);
+}
+
+void SfxLokHelper::setBlockedCommandList(int nViewId, const char* blockedCommandList)
+{
+ SfxViewShell* pViewShell = SfxLokHelper::getViewOfId(nViewId);
+
+ if(pViewShell)
+ {
+ pViewShell->setBlockedCommandList(blockedCommandList);
+ }
+}
+
+void SfxLokHelper::postExtTextEventAsync(const VclPtr<vcl::Window> &xWindow,
+ int nType, const OUString &rText)
+{
+ LOKAsyncEventData* pLOKEv = new LOKAsyncEventData;
+ switch (nType)
+ {
+ case LOK_EXT_TEXTINPUT:
+ pLOKEv->mnEvent = VclEventId::ExtTextInput;
+ pLOKEv->maText = rText;
+ break;
+ case LOK_EXT_TEXTINPUT_END:
+ pLOKEv->mnEvent = VclEventId::EndExtTextInput;
+ pLOKEv->maText = "";
+ break;
+ default:
+ assert(false);
+ }
+ pLOKEv->mpWindow = xWindow;
+ postEventAsync(pLOKEv);
+}
+
+void SfxLokHelper::postMouseEventAsync(const VclPtr<vcl::Window> &xWindow, LokMouseEventData const & rLokMouseEventData)
+{
+ LOKAsyncEventData* pLOKEv = new LOKAsyncEventData;
+ switch (rLokMouseEventData.mnType)
+ {
+ case LOK_MOUSEEVENT_MOUSEBUTTONDOWN:
+ pLOKEv->mnEvent = VclEventId::WindowMouseButtonDown;
+ break;
+ case LOK_MOUSEEVENT_MOUSEBUTTONUP:
+ pLOKEv->mnEvent = VclEventId::WindowMouseButtonUp;
+ break;
+ case LOK_MOUSEEVENT_MOUSEMOVE:
+ pLOKEv->mnEvent = VclEventId::WindowMouseMove;
+ break;
+ default:
+ assert(false);
+ }
+
+ // no reason - just always true so far.
+ assert (rLokMouseEventData.meModifiers == MouseEventModifiers::SIMPLECLICK);
+
+ pLOKEv->maMouseEvent = MouseEvent(rLokMouseEventData.maPosition, rLokMouseEventData.mnCount,
+ rLokMouseEventData.meModifiers, rLokMouseEventData.mnButtons,
+ rLokMouseEventData.mnModifier);
+ if (rLokMouseEventData.maLogicPosition)
+ {
+ pLOKEv->maMouseEvent.setLogicPosition(*rLokMouseEventData.maLogicPosition);
+ }
+ pLOKEv->mpWindow = xWindow;
+ postEventAsync(pLOKEv);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/view/lokstarmathhelper.cxx b/sfx2/source/view/lokstarmathhelper.cxx
new file mode 100644
index 000000000..9df487595
--- /dev/null
+++ b/sfx2/source/view/lokstarmathhelper.cxx
@@ -0,0 +1,174 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * 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 <sal/config.h>
+
+#include <sfx2/ipclient.hxx>
+#include <sfx2/lokcomponenthelpers.hxx>
+#include <sfx2/lokhelper.hxx>
+
+#include <toolkit/helper/vclunohelper.hxx>
+#include <tools/fract.hxx>
+#include <vcl/layout.hxx>
+#include <vcl/window.hxx>
+
+#include <com/sun/star/embed/XEmbeddedObject.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+
+css::uno::Reference<css::frame::XController>& LokStarMathHelper::GetXController()
+{
+ if (!mxController && mpViewShell)
+ {
+ if (const SfxInPlaceClient* pIPClient = mpViewShell->GetIPClient())
+ {
+ if (const auto& xEmbObj = pIPClient->GetObject())
+ {
+ css::uno::Reference<css::lang::XServiceInfo> xComp(xEmbObj->getComponent(),
+ css::uno::UNO_QUERY);
+ if (xComp && xComp->supportsService("com.sun.star.formula.FormulaProperties"))
+ if (css::uno::Reference<css::frame::XModel> xModel{ xComp,
+ css::uno::UNO_QUERY })
+ mxController = xModel->getCurrentController();
+ }
+ }
+ }
+
+ return mxController;
+}
+
+namespace
+{
+// Find a child SmGraphicWindow*
+vcl::Window* FindSmGraphicWindow(vcl::Window* pWin)
+{
+ if (!pWin)
+ return nullptr;
+
+ if (pWin->IsStarMath())
+ return pWin;
+
+ pWin = pWin->GetWindow(GetWindowType::FirstChild);
+ while (pWin)
+ {
+ if (vcl::Window* pSmGraphicWindow = FindSmGraphicWindow(pWin))
+ return pSmGraphicWindow;
+ pWin = pWin->GetWindow(GetWindowType::Next);
+ }
+ return nullptr;
+}
+
+// Find a child window that corresponds to SmGraphicWidget
+vcl::Window* FindChildSmGraphicWidgetWindow(vcl::Window* pWin)
+{
+ if (!pWin)
+ return nullptr;
+
+ // The needed window is a VclDrawingArea
+ if (dynamic_cast<VclDrawingArea*>(pWin))
+ return pWin;
+
+ pWin = pWin->GetWindow(GetWindowType::FirstChild);
+ while (pWin)
+ {
+ if (vcl::Window* pSmGraphicWidgetWindow = FindChildSmGraphicWidgetWindow(pWin))
+ return pSmGraphicWidgetWindow;
+ pWin = pWin->GetWindow(GetWindowType::Next);
+ }
+ return nullptr;
+}
+}
+
+vcl::Window* LokStarMathHelper::GetGraphicWindow()
+{
+ if (!mpGraphicWindow)
+ {
+ if (const css::uno::Reference<css::frame::XController>& xController = GetXController())
+ {
+ if (const css::uno::Reference<css::frame::XFrame> xFrame = xController->getFrame())
+ {
+ css::uno::Reference<css::awt::XWindow> xDockerWin = xFrame->getContainerWindow();
+ mpGraphicWindow.set(FindSmGraphicWindow(VCLUnoHelper::GetWindow(xDockerWin)));
+ }
+ }
+ }
+
+ return mpGraphicWindow.get();
+}
+
+vcl::Window* LokStarMathHelper::GetWidgetWindow()
+{
+ if (!mpWidgetWindow)
+ mpWidgetWindow.set(FindChildSmGraphicWidgetWindow(GetGraphicWindow()));
+
+ return mpWidgetWindow.get();
+}
+
+tools::Rectangle LokStarMathHelper::GetBoundingBox()
+{
+ if (mpViewShell)
+ {
+ if (SfxInPlaceClient* pIPClient = mpViewShell->GetIPClient())
+ {
+ if (vcl::Window* pRootWin = pIPClient->GetEditWin())
+ {
+ if (vcl::Window* pWindow = GetWidgetWindow())
+ {
+ // In all cases, the following code fragment
+ // returns the bounding box in twips.
+ // Note: the correct mapmode (representing document zoom) is provided by
+ // GraphicWindow, not WidgetWindow
+ const MapMode& aMapMode = GetGraphicWindow()->GetMapMode();
+ const auto & [ m, d ]
+ = o3tl::getConversionMulDiv(o3tl::Length::px, o3tl::Length::twip);
+ const Fraction& scaleX = aMapMode.GetScaleX();
+ const Fraction& scaleY = aMapMode.GetScaleY();
+ const auto nXNum = m * scaleX.GetDenominator();
+ const auto nXDen = d * scaleX.GetNumerator();
+ const auto nYNum = m * scaleY.GetDenominator();
+ const auto nYDen = d * scaleY.GetNumerator();
+
+ Point aOffset
+ = pWindow->GetOffsetPixelFrom(*pRootWin).scale(nXNum, nXDen, nYNum, nYDen);
+ Size aSize = pWindow->GetSizePixel().scale(nXNum, nXDen, nYNum, nYDen);
+ return { aOffset, aSize };
+ }
+ }
+ }
+ }
+ return {};
+}
+
+bool LokStarMathHelper::postMouseEvent(int nType, int nX, int nY, int nCount, int nButtons,
+ int nModifier, double fScaleX, double fScaleY)
+{
+ if (vcl::Window* pWindow = GetWidgetWindow())
+ {
+ Point aMousePos(nX, nY);
+ tools::Rectangle rBBox = GetBoundingBox();
+ if (rBBox.Contains(aMousePos))
+ {
+ int nWinX = nX - rBBox.Left();
+ int nWinY = nY - rBBox.Top();
+
+ // window expects pixels, but the conversion factor
+ // can depend on the client zoom
+ Point aPos(nWinX * fScaleX, nWinY * fScaleY);
+
+ LokMouseEventData aMouseEventData(nType, aPos, nCount, MouseEventModifiers::SIMPLECLICK,
+ nButtons, nModifier);
+ SfxLokHelper::postMouseEventAsync(pWindow, aMouseEventData);
+
+ return true;
+ }
+ }
+ return false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/sfx2/source/view/printer.cxx b/sfx2/source/view/printer.cxx
new file mode 100644
index 000000000..7b7745349
--- /dev/null
+++ b/sfx2/source/view/printer.cxx
@@ -0,0 +1,189 @@
+/* -*- 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 <tools/debug.hxx>
+
+#include <utility>
+
+#include <sfx2/printer.hxx>
+#include <sfx2/viewsh.hxx>
+#include <sfx2/tabdlg.hxx>
+#include "prnmon.hxx"
+
+// class SfxPrinter ------------------------------------------------------
+
+VclPtr<SfxPrinter> SfxPrinter::Create( SvStream& rStream, std::unique_ptr<SfxItemSet>&& pOptions )
+
+/* [Description]
+
+ Creates a <SfxPrinter> from the stream. Loading is really only a jobsetup.
+ If such a printer is not available on the system, then the original is
+ marked as the original Job-setup and a comparable printer is selected from
+ existing ones.
+
+ The 'pOptions' are taken over in the generated SfxPrinter, the return
+ value belongs to the caller.
+*/
+
+{
+ // Load JobSetup
+ JobSetup aFileJobSetup;
+ ReadJobSetup( rStream, aFileJobSetup );
+
+ // Get printers
+ VclPtr<SfxPrinter> pPrinter = VclPtr<SfxPrinter>::Create( std::move(pOptions), aFileJobSetup );
+ return pPrinter;
+}
+
+
+void SfxPrinter::Store( SvStream& rStream ) const
+
+/* [Description]
+
+ Saves the used JobSetup of <SfxPrinter>s.
+*/
+
+{
+ WriteJobSetup( rStream, GetJobSetup() );
+}
+
+
+SfxPrinter::SfxPrinter( std::unique_ptr<SfxItemSet>&& pTheOptions ) :
+
+/* [Description]
+
+ This constructor creates a default printer.
+*/
+ pOptions( std::move(pTheOptions) ),
+ bKnown( true )
+{
+ assert(pOptions);
+}
+
+
+SfxPrinter::SfxPrinter( std::unique_ptr<SfxItemSet>&& pTheOptions,
+ const JobSetup& rTheOrigJobSetup ) :
+ Printer( rTheOrigJobSetup.GetPrinterName() ),
+ pOptions( std::move(pTheOptions) )
+{
+ assert(pOptions);
+ bKnown = GetName() == rTheOrigJobSetup.GetPrinterName();
+
+ if ( bKnown )
+ SetJobSetup( rTheOrigJobSetup );
+}
+
+
+SfxPrinter::SfxPrinter( std::unique_ptr<SfxItemSet>&& pTheOptions,
+ const OUString& rPrinterName ) :
+ Printer( rPrinterName ),
+ pOptions( std::move(pTheOptions) ),
+ bKnown( GetName() == rPrinterName )
+{
+ assert(pOptions);
+}
+
+
+SfxPrinter::SfxPrinter( const SfxPrinter& rPrinter ) :
+ VclReferenceBase(),
+ Printer( rPrinter.GetName() ),
+ pOptions( rPrinter.GetOptions().Clone() ),
+ bKnown( rPrinter.IsKnown() )
+{
+ assert(pOptions);
+ SetJobSetup( rPrinter.GetJobSetup() );
+ SetPrinterProps( &rPrinter );
+ SetMapMode( rPrinter.GetMapMode() );
+}
+
+
+VclPtr<SfxPrinter> SfxPrinter::Clone() const
+{
+ if ( IsDefPrinter() )
+ {
+ VclPtr<SfxPrinter> pNewPrinter = VclPtr<SfxPrinter>::Create( GetOptions().Clone() );
+ pNewPrinter->SetJobSetup( GetJobSetup() );
+ pNewPrinter->SetPrinterProps( this );
+ pNewPrinter->SetMapMode( GetMapMode() );
+ return pNewPrinter;
+ }
+ else
+ return VclPtr<SfxPrinter>::Create( *this );
+}
+
+
+SfxPrinter::~SfxPrinter()
+{
+ disposeOnce();
+}
+
+void SfxPrinter::dispose()
+{
+ pOptions.reset();
+ Printer::dispose();
+}
+
+
+void SfxPrinter::SetOptions( const SfxItemSet &rNewOptions )
+{
+ pOptions->Set(rNewOptions);
+}
+
+
+SfxPrintOptionsDialog::SfxPrintOptionsDialog(weld::Window *pParent,
+ SfxViewShell *pViewShell,
+ const SfxItemSet *pSet)
+ : GenericDialogController(pParent, "sfx/ui/printeroptionsdialog.ui", "PrinterOptionsDialog")
+ , pOptions(pSet->Clone())
+ , m_xHelpBtn(m_xBuilder->weld_widget("help"))
+ , m_xContainer(m_xDialog->weld_content_area())
+ , m_xPage(pViewShell->CreatePrintOptionsPage(m_xContainer.get(), this, *pOptions)) // Insert TabPage
+{
+ DBG_ASSERT( m_xPage, "CreatePrintOptions != SFX_VIEW_HAS_PRINTOPTIONS" );
+ if (m_xPage)
+ {
+ m_xPage->Reset( pOptions.get() );
+ m_xDialog->set_help_id(m_xPage->GetHelpId());
+ }
+}
+
+SfxPrintOptionsDialog::~SfxPrintOptionsDialog()
+{
+}
+
+short SfxPrintOptionsDialog::run()
+{
+ if (!m_xPage)
+ return RET_CANCEL;
+
+ short nRet = GenericDialogController::run();
+
+ if (nRet == RET_OK)
+ m_xPage->FillItemSet( pOptions.get() );
+ else
+ m_xPage->Reset( pOptions.get() );
+ return nRet;
+}
+
+void SfxPrintOptionsDialog::DisableHelp()
+{
+ m_xHelpBtn->set_sensitive(false);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/view/prnmon.hxx b/sfx2/source/view/prnmon.hxx
new file mode 100644
index 000000000..06b3217fa
--- /dev/null
+++ b/sfx2/source/view/prnmon.hxx
@@ -0,0 +1,54 @@
+/* -*- 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_PRNMON_HXX
+#define INCLUDED_SFX2_PRNMON_HXX
+
+#include <memory>
+#include <sal/config.h>
+#include <vcl/weld.hxx>
+
+
+class SfxViewShell;
+class SfxTabPage;
+class SfxItemSet;
+
+
+class SfxPrintOptionsDialog final : public weld::GenericDialogController
+{
+private:
+ std::unique_ptr<SfxItemSet> pOptions;
+ std::unique_ptr<weld::Widget> m_xHelpBtn;
+ std::unique_ptr<weld::Container> m_xContainer;
+ std::unique_ptr<SfxTabPage> m_xPage;
+
+public:
+ SfxPrintOptionsDialog(weld::Window *pParent,
+ SfxViewShell *pViewShell,
+ const SfxItemSet *rOptions);
+ virtual ~SfxPrintOptionsDialog() override;
+
+ virtual short run() override;
+
+ const SfxItemSet& GetOptions() const { return *pOptions; }
+ void DisableHelp();
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/view/sfxbasecontroller.cxx b/sfx2/source/view/sfxbasecontroller.cxx
new file mode 100644
index 000000000..e7c098fcd
--- /dev/null
+++ b/sfx2/source/view/sfxbasecontroller.cxx
@@ -0,0 +1,1539 @@
+/* -*- 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 <time.h>
+#include <sfx2/sfxbasecontroller.hxx>
+#include <com/sun/star/awt/XWindowPeer.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/util/XCloseable.hpp>
+#include <com/sun/star/util/XCloseBroadcaster.hpp>
+#include <com/sun/star/util/XCloseListener.hpp>
+#include <com/sun/star/util/CloseVetoException.hpp>
+#include <com/sun/star/document/XCmisDocument.hpp>
+#include <com/sun/star/document/XViewDataSupplier.hpp>
+#include <cppuhelper/implbase.hxx>
+#include <com/sun/star/frame/FrameActionEvent.hpp>
+#include <com/sun/star/frame/FrameAction.hpp>
+#include <com/sun/star/frame/FrameSearchFlag.hpp>
+#include <com/sun/star/frame/CommandGroup.hpp>
+#include <com/sun/star/frame/XFrame.hpp>
+#include <com/sun/star/frame/XBorderResizeListener.hpp>
+#include <com/sun/star/frame/XUntitledNumbers.hpp>
+#include <com/sun/star/lang/DisposedException.hpp>
+#include <com/sun/star/lang/EventObject.hpp>
+#include <com/sun/star/lang/XEventListener.hpp>
+#include <com/sun/star/lang/XComponent.hpp>
+#include <com/sun/star/container/XIndexAccess.hpp>
+#include <comphelper/interfacecontainer3.hxx>
+#include <comphelper/multicontainer2.hxx>
+#include <sfx2/viewsh.hxx>
+#include <sfx2/docfac.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/app.hxx>
+#include <sfx2/msgpool.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/userinputinterception.hxx>
+
+#include <unoctitm.hxx>
+#include <sfx2/childwin.hxx>
+#include <sfx2/sfxsids.hrc>
+#include <sfx2/sfxresid.hxx>
+#include <workwin.hxx>
+#include <sfx2/infobar.hxx>
+
+#include <osl/mutex.hxx>
+#include <tools/diagnose_ex.h>
+#include <comphelper/namedvaluecollection.hxx>
+#include <comphelper/sequence.hxx>
+#include <toolkit/helper/convert.hxx>
+#include <framework/titlehelper.hxx>
+#include <comphelper/processfactory.hxx>
+#include <vcl/svapp.hxx>
+#include <tools/svborder.hxx>
+
+#include <sfx2/event.hxx>
+#include <sfx2/viewfac.hxx>
+#include <sfx2/strings.hrc>
+#include <sfxbasecontroller_internal.hxx>
+
+#include <unordered_map>
+
+#include <com/sun/star/ui/XSidebarProvider.hpp>
+#include <sidebar/UnoSidebar.hxx>
+
+#define TIMEOUT_START_RESCHEDULE 10L /* 10th s */
+
+using namespace ::com::sun::star;
+using ::com::sun::star::uno::Reference;
+using ::com::sun::star::uno::RuntimeException;
+using ::com::sun::star::uno::UNO_QUERY_THROW;
+using ::com::sun::star::lang::DisposedException;
+using ::com::sun::star::awt::XWindow;
+using ::com::sun::star::frame::XController;
+using ::com::sun::star::frame::XDispatchProvider;
+using ::com::sun::star::document::XViewDataSupplier;
+using ::com::sun::star::container::XIndexAccess;
+using ::com::sun::star::beans::PropertyValue;
+using ::com::sun::star::beans::StringPair;
+using ::com::sun::star::uno::Sequence;
+using ::com::sun::star::uno::UNO_QUERY;
+using ::com::sun::star::uno::Exception;
+using ::com::sun::star::frame::XFrame;
+using ::com::sun::star::frame::XFrameActionListener;
+using ::com::sun::star::util::XCloseListener;
+using ::com::sun::star::task::XStatusIndicator;
+using ::com::sun::star::frame::XTitle;
+using ::com::sun::star::ui::XSidebarProvider;
+
+
+typedef std::unordered_map< SfxGroupId, sal_Int16 > GroupHashMap;
+
+sal_Int16 MapGroupIDToCommandGroup( SfxGroupId nGroupID )
+{
+ static GroupHashMap s_aHashMap
+ {
+ { SfxGroupId::Intern , frame::CommandGroup::INTERNAL },
+ { SfxGroupId::Application , frame::CommandGroup::APPLICATION },
+ { SfxGroupId::Document , frame::CommandGroup::DOCUMENT },
+ { SfxGroupId::View , frame::CommandGroup::VIEW },
+ { SfxGroupId::Edit , frame::CommandGroup::EDIT },
+ { SfxGroupId::Macro , frame::CommandGroup::MACRO },
+ { SfxGroupId::Options , frame::CommandGroup::OPTIONS },
+ { SfxGroupId::Math , frame::CommandGroup::MATH },
+ { SfxGroupId::Navigator , frame::CommandGroup::NAVIGATOR },
+ { SfxGroupId::Insert , frame::CommandGroup::INSERT },
+ { SfxGroupId::Format , frame::CommandGroup::FORMAT },
+ { SfxGroupId::Template , frame::CommandGroup::TEMPLATE },
+ { SfxGroupId::Text , frame::CommandGroup::TEXT },
+ { SfxGroupId::Frame , frame::CommandGroup::FRAME },
+ { SfxGroupId::Graphic , frame::CommandGroup::GRAPHIC },
+ { SfxGroupId::Table , frame::CommandGroup::TABLE },
+ { SfxGroupId::Enumeration , frame::CommandGroup::ENUMERATION },
+ { SfxGroupId::Data , frame::CommandGroup::DATA },
+ { SfxGroupId::Special , frame::CommandGroup::SPECIAL },
+ { SfxGroupId::Image , frame::CommandGroup::IMAGE },
+ { SfxGroupId::Chart , frame::CommandGroup::CHART },
+ { SfxGroupId::Explorer , frame::CommandGroup::EXPLORER },
+ { SfxGroupId::Connector , frame::CommandGroup::CONNECTOR },
+ { SfxGroupId::Modify , frame::CommandGroup::MODIFY },
+ { SfxGroupId::Drawing , frame::CommandGroup::DRAWING },
+ { SfxGroupId::Controls , frame::CommandGroup::CONTROLS },
+ };
+
+
+ GroupHashMap::const_iterator pIter = s_aHashMap.find( nGroupID );
+ if ( pIter != s_aHashMap.end() )
+ return pIter->second;
+ else
+ return frame::CommandGroup::INTERNAL;
+}
+
+sal_uInt32 Get10ThSec()
+{
+ sal_uInt32 n10Ticks = 10 * static_cast<sal_uInt32>(clock());
+ return n10Ticks / CLOCKS_PER_SEC;
+}
+
+static sal_Int32 m_nInReschedule = 0; /// static counter for rescheduling
+
+static void reschedule()
+{
+ if ( m_nInReschedule == 0 )
+ {
+ ++m_nInReschedule;
+ Application::Reschedule();
+ --m_nInReschedule;
+ }
+}
+
+namespace {
+
+class SfxStatusIndicator : public ::cppu::WeakImplHelper< task::XStatusIndicator, lang::XEventListener >
+{
+ Reference < XController > xOwner;
+ Reference < task::XStatusIndicator > xProgress;
+ SfxWorkWindow* pWorkWindow;
+ tools::Long _nStartTime;
+public:
+ SfxStatusIndicator(SfxBaseController* pController, SfxWorkWindow* pWork)
+ : xOwner( pController )
+ , pWorkWindow( pWork )
+ , _nStartTime(0)
+ {
+ osl_atomic_increment(&m_refCount);
+ Reference< lang::XComponent > xComponent = pController;
+ if (xComponent.is())
+ xComponent->addEventListener(this);
+ osl_atomic_decrement(&m_refCount);
+ }
+
+ virtual void SAL_CALL start(const OUString& aText, sal_Int32 nRange) override;
+ virtual void SAL_CALL end() override;
+ virtual void SAL_CALL setText(const OUString& aText) override;
+ virtual void SAL_CALL setValue(sal_Int32 nValue) override;
+ virtual void SAL_CALL reset() override;
+
+ virtual void SAL_CALL disposing( const lang::EventObject& Source ) override;
+};
+
+}
+
+void SAL_CALL SfxStatusIndicator::start(const OUString& aText, sal_Int32 nRange)
+{
+ SolarMutexGuard aGuard;
+ if ( xOwner.is() )
+ {
+ if ( !xProgress.is() )
+ xProgress = pWorkWindow->GetStatusIndicator();
+
+ if ( xProgress.is() )
+ xProgress->start( aText, nRange );
+
+ _nStartTime = Get10ThSec();
+ reschedule();
+ }
+}
+
+void SAL_CALL SfxStatusIndicator::end()
+{
+ SolarMutexGuard aGuard;
+ if ( xOwner.is() )
+ {
+ if ( !xProgress.is() )
+ xProgress = pWorkWindow->GetStatusIndicator();
+
+ if ( xProgress.is() )
+ xProgress->end();
+
+ reschedule();
+ }
+}
+
+void SAL_CALL SfxStatusIndicator::setText(const OUString& aText)
+{
+ SolarMutexGuard aGuard;
+ if ( xOwner.is() )
+ {
+ if ( !xProgress.is() )
+ xProgress = pWorkWindow->GetStatusIndicator();
+
+ if ( xProgress.is() )
+ xProgress->setText( aText );
+
+ reschedule();
+ }
+}
+
+void SAL_CALL SfxStatusIndicator::setValue( sal_Int32 nValue )
+{
+ SolarMutexGuard aGuard;
+ if ( xOwner.is() )
+ {
+ if ( !xProgress.is() )
+ xProgress = pWorkWindow->GetStatusIndicator();
+
+ if ( xProgress.is() )
+ xProgress->setValue( nValue );
+
+ bool bReschedule = (( Get10ThSec() - _nStartTime ) > TIMEOUT_START_RESCHEDULE );
+ if ( bReschedule )
+ reschedule();
+ }
+}
+
+void SAL_CALL SfxStatusIndicator::reset()
+{
+ SolarMutexGuard aGuard;
+ if ( xOwner.is() )
+ {
+ if ( !xProgress.is() )
+ xProgress = pWorkWindow->GetStatusIndicator();
+
+ if ( xProgress.is() )
+ xProgress->reset();
+
+ reschedule();
+ }
+}
+
+void SAL_CALL SfxStatusIndicator::disposing( const lang::EventObject& /*Source*/ )
+{
+ SolarMutexGuard aGuard;
+ xOwner = nullptr;
+ xProgress.clear();
+}
+
+
+// declaration IMPL_SfxBaseController_ListenerHelper
+
+namespace {
+
+class IMPL_SfxBaseController_ListenerHelper : public ::cppu::WeakImplHelper< frame::XFrameActionListener >
+{
+public:
+ explicit IMPL_SfxBaseController_ListenerHelper( SfxBaseController* pController ) ;
+
+ virtual void SAL_CALL frameAction( const frame::FrameActionEvent& aEvent ) override ;
+ virtual void SAL_CALL disposing( const lang::EventObject& aEvent ) override ;
+
+private:
+
+ SfxBaseController* m_pController ;
+
+} ; // class IMPL_SfxBaseController_ListenerContainer
+
+class IMPL_SfxBaseController_CloseListenerHelper : public ::cppu::WeakImplHelper< util::XCloseListener >
+{
+public:
+ explicit IMPL_SfxBaseController_CloseListenerHelper( SfxBaseController* pController ) ;
+
+ virtual void SAL_CALL queryClosing( const lang::EventObject& aEvent, sal_Bool bDeliverOwnership ) override ;
+ virtual void SAL_CALL notifyClosing( const lang::EventObject& aEvent ) override ;
+ virtual void SAL_CALL disposing( const lang::EventObject& aEvent ) override ;
+
+private:
+
+ SfxBaseController* m_pController;
+
+} ; // class IMPL_SfxBaseController_ListenerContainer
+
+}
+
+IMPL_SfxBaseController_CloseListenerHelper::IMPL_SfxBaseController_CloseListenerHelper( SfxBaseController* pController )
+ : m_pController ( pController )
+{
+}
+
+void SAL_CALL IMPL_SfxBaseController_CloseListenerHelper::disposing( const lang::EventObject& /*aEvent*/ )
+{
+}
+
+void SAL_CALL IMPL_SfxBaseController_CloseListenerHelper::queryClosing( const lang::EventObject& /*aEvent*/, sal_Bool /*bDeliverOwnership*/ )
+{
+ SolarMutexGuard aGuard;
+ SfxViewShell* pShell = m_pController->GetViewShell_Impl();
+ if (pShell)
+ {
+ bool bCanClose = pShell->PrepareClose( false );
+ if ( !bCanClose )
+ {
+ throw util::CloseVetoException("Controller disagree ...",static_cast< ::cppu::OWeakObject*>(this));
+ }
+ }
+}
+
+void SAL_CALL IMPL_SfxBaseController_CloseListenerHelper::notifyClosing( const lang::EventObject& /*aEvent*/ )
+{
+}
+
+
+// declaration IMPL_SfxBaseController_DataContainer
+
+
+struct IMPL_SfxBaseController_DataContainer
+{
+ Reference< XFrame > m_xFrame ;
+ Reference< XFrameActionListener > m_xListener ;
+ Reference< XCloseListener > m_xCloseListener ;
+ ::sfx2::UserInputInterception m_aUserInputInterception;
+ ::comphelper::OMultiTypeInterfaceContainerHelper2 m_aListenerContainer ;
+ ::comphelper::OInterfaceContainerHelper3<ui::XContextMenuInterceptor> m_aInterceptorContainer ;
+ Reference< XStatusIndicator > m_xIndicator ;
+ SfxViewShell* m_pViewShell ;
+ SfxBaseController* m_pController ;
+ bool m_bDisposing ;
+ bool m_bSuspendState ;
+ Reference< XTitle > m_xTitleHelper ;
+ Sequence< PropertyValue > m_aCreationArgs ;
+
+ IMPL_SfxBaseController_DataContainer( ::osl::Mutex& aMutex ,
+ SfxViewShell* pViewShell ,
+ SfxBaseController* pController )
+ : m_xListener ( new IMPL_SfxBaseController_ListenerHelper( pController ) )
+ , m_xCloseListener ( new IMPL_SfxBaseController_CloseListenerHelper( pController ) )
+ , m_aUserInputInterception ( *pController, aMutex )
+ , m_aListenerContainer ( aMutex )
+ , m_aInterceptorContainer ( aMutex )
+ , m_pViewShell ( pViewShell )
+ , m_pController ( pController )
+ , m_bDisposing ( false )
+ , m_bSuspendState ( false )
+ {
+ }
+
+} ; // struct IMPL_SfxBaseController_DataContainer
+
+
+// IMPL_SfxBaseController_ListenerHelper constructor
+
+
+IMPL_SfxBaseController_ListenerHelper::IMPL_SfxBaseController_ListenerHelper( SfxBaseController* pController )
+ : m_pController ( pController )
+{
+}
+
+void SAL_CALL IMPL_SfxBaseController_ListenerHelper::frameAction( const frame::FrameActionEvent& aEvent )
+{
+ SolarMutexGuard aGuard;
+ if (
+ ( m_pController != nullptr ) &&
+ ( aEvent.Frame == m_pController->getFrame() ) &&
+ ( m_pController->GetViewShell_Impl() && m_pController->GetViewShell_Impl()->GetWindow() != nullptr )
+ )
+ {
+ if ( aEvent.Action == frame::FrameAction_FRAME_UI_ACTIVATED )
+ {
+ if ( !m_pController->GetViewShell_Impl()->GetUIActiveIPClient_Impl() )
+ m_pController->GetViewShell_Impl()->GetViewFrame()->MakeActive_Impl( false );
+ }
+ else if ( aEvent.Action == frame::FrameAction_CONTEXT_CHANGED )
+ {
+ m_pController->GetViewShell_Impl()->GetViewFrame()->GetBindings().ContextChanged_Impl();
+ }
+ }
+}
+
+
+// IMPL_SfxBaseController_ListenerHelper -> XEventListener
+
+
+void SAL_CALL IMPL_SfxBaseController_ListenerHelper::disposing( const lang::EventObject& /*aEvent*/ )
+{
+ SolarMutexGuard aGuard;
+ if ( m_pController && m_pController->getFrame().is() )
+ m_pController->getFrame()->removeFrameActionListener( this ) ;
+}
+
+SfxBaseController::SfxBaseController( SfxViewShell* pViewShell )
+ : m_pData ( new IMPL_SfxBaseController_DataContainer( m_aMutex, pViewShell, this ))
+{
+ m_pData->m_pViewShell->SetController( this );
+}
+
+
+// SfxBaseController -> destructor
+
+
+SfxBaseController::~SfxBaseController()
+{
+}
+
+
+// SfxBaseController -> XController2
+
+
+Reference< XWindow > SAL_CALL SfxBaseController::getComponentWindow()
+{
+ SolarMutexGuard aGuard;
+ if ( !m_pData->m_pViewShell )
+ throw DisposedException();
+
+ return Reference< XWindow >( GetViewFrame_Impl().GetFrame().GetWindow().GetComponentInterface(), UNO_QUERY_THROW );
+}
+
+OUString SAL_CALL SfxBaseController::getViewControllerName()
+{
+ SolarMutexGuard aGuard;
+ if ( !m_pData->m_pViewShell || !m_pData->m_pViewShell->GetObjectShell() )
+ throw DisposedException();
+
+ const SfxObjectFactory& rDocFac( m_pData->m_pViewShell->GetObjectShell()->GetFactory() );
+ sal_uInt16 nViewNo = rDocFac.GetViewNo_Impl( GetViewFrame_Impl().GetCurViewId(), rDocFac.GetViewFactoryCount() );
+ OSL_ENSURE( nViewNo < rDocFac.GetViewFactoryCount(), "SfxBaseController::getViewControllerName: view ID not found in view factories!" );
+
+ OUString sViewName;
+ if ( nViewNo < rDocFac.GetViewFactoryCount() )
+ sViewName = rDocFac.GetViewFactory( nViewNo ).GetAPIViewName();
+
+ return sViewName;
+}
+
+Sequence< PropertyValue > SAL_CALL SfxBaseController::getCreationArguments()
+{
+ SolarMutexGuard aGuard;
+ if ( !m_pData->m_pViewShell || !m_pData->m_pViewShell->GetObjectShell() )
+ throw DisposedException();
+
+ return m_pData->m_aCreationArgs;
+}
+
+void SfxBaseController::SetCreationArguments_Impl( const Sequence< PropertyValue >& i_rCreationArgs )
+{
+ OSL_ENSURE( !m_pData->m_aCreationArgs.hasElements(), "SfxBaseController::SetCreationArguments_Impl: not intended to be called twice!" );
+ m_pData->m_aCreationArgs = i_rCreationArgs;
+}
+
+SfxViewFrame& SfxBaseController::GetViewFrame_Impl() const
+{
+ ENSURE_OR_THROW( m_pData->m_pViewShell, "not to be called without a view shell" );
+ SfxViewFrame* pActFrame = m_pData->m_pViewShell->GetFrame();
+ ENSURE_OR_THROW( pActFrame, "a view shell without a view frame is pretty pathological" );
+ return *pActFrame;
+}
+
+
+Reference<XSidebarProvider> SAL_CALL SfxBaseController::getSidebar()
+{
+ SfxViewFrame& rViewFrame = GetViewFrame_Impl();
+ SfxFrame& rFrame = rViewFrame.GetFrame();
+
+ Reference<XSidebarProvider> rSidebar = new SfxUnoSidebar(rFrame.GetFrameInterface());
+ return rSidebar;
+}
+
+
+// SfxBaseController -> XController2 -> XController
+
+
+void SAL_CALL SfxBaseController::attachFrame( const Reference< frame::XFrame >& xFrame )
+{
+ Reference< frame::XFrame > xTemp( getFrame() ) ;
+
+ SolarMutexGuard aGuard;
+ if ( xTemp.is() )
+ {
+ xTemp->removeFrameActionListener( m_pData->m_xListener ) ;
+ Reference < util::XCloseBroadcaster > xCloseable( xTemp, uno::UNO_QUERY );
+ if ( xCloseable.is() )
+ xCloseable->removeCloseListener( m_pData->m_xCloseListener );
+ }
+
+ m_pData->m_xFrame = xFrame;
+
+ if ( !xFrame.is() )
+ return;
+
+ xFrame->addFrameActionListener( m_pData->m_xListener ) ;
+ Reference < util::XCloseBroadcaster > xCloseable( xFrame, uno::UNO_QUERY );
+ if ( xCloseable.is() )
+ xCloseable->addCloseListener( m_pData->m_xCloseListener );
+
+ if ( m_pData->m_pViewShell )
+ {
+ ConnectSfxFrame_Impl( E_CONNECT );
+ ShowInfoBars( );
+
+ // attaching the frame to the controller is the last step in the creation of a new view, so notify this
+ SfxViewEventHint aHint( SfxEventHintId::ViewCreated, GlobalEventConfig::GetEventName( GlobalEventId::VIEWCREATED ), m_pData->m_pViewShell->GetObjectShell(), Reference< frame::XController2 >( this ) );
+ SfxGetpApp()->NotifyEvent( aHint );
+ }
+}
+
+
+// SfxBaseController -> XController
+
+
+sal_Bool SAL_CALL SfxBaseController::attachModel( const Reference< frame::XModel >& xModel )
+{
+ if ( m_pData->m_pViewShell && xModel.is() && xModel != m_pData->m_pViewShell->GetObjectShell()->GetModel() )
+ {
+ // don't allow to reattach a model!
+ OSL_FAIL("Can't reattach model!");
+ return false;
+ }
+
+ Reference < util::XCloseBroadcaster > xCloseable( xModel, uno::UNO_QUERY );
+ if ( xCloseable.is() )
+ xCloseable->addCloseListener( m_pData->m_xCloseListener );
+ return true;
+}
+
+
+// SfxBaseController -> XController
+
+
+sal_Bool SAL_CALL SfxBaseController::suspend( sal_Bool bSuspend )
+{
+ SolarMutexGuard aGuard;
+
+ // ignore duplicate calls, which doesn't change anything real
+ if (bool(bSuspend) == m_pData->m_bSuspendState)
+ return true;
+
+ if ( bSuspend )
+ {
+ if ( !m_pData->m_pViewShell )
+ {
+ m_pData->m_bSuspendState = true;
+ return true;
+ }
+
+ if ( !m_pData->m_pViewShell->PrepareClose() )
+ return false;
+
+ if ( getFrame().is() )
+ getFrame()->removeFrameActionListener( m_pData->m_xListener ) ;
+ SfxViewFrame* pActFrame = m_pData->m_pViewShell->GetFrame() ;
+
+ // More Views on the same document?
+ SfxObjectShell* pDocShell = m_pData->m_pViewShell->GetObjectShell() ;
+ bool bOther = false ;
+
+ for ( const SfxViewFrame* pFrame = SfxViewFrame::GetFirst( pDocShell ); !bOther && pFrame; pFrame = SfxViewFrame::GetNext( *pFrame, pDocShell ) )
+ bOther = (pFrame != pActFrame);
+
+ bool bRet = bOther || pDocShell->PrepareClose();
+ if ( bRet )
+ {
+ ConnectSfxFrame_Impl( E_DISCONNECT );
+ m_pData->m_bSuspendState = true;
+ }
+
+ return bRet;
+ }
+ else
+ {
+ if ( getFrame().is() )
+ getFrame()->addFrameActionListener( m_pData->m_xListener ) ;
+
+ if ( m_pData->m_pViewShell )
+ {
+ ConnectSfxFrame_Impl( E_RECONNECT );
+ }
+
+ m_pData->m_bSuspendState = false;
+ return true ;
+ }
+}
+
+
+// SfxBaseController -> XController
+
+
+uno::Any SfxBaseController::getViewData()
+{
+ uno::Any aAny;
+ SolarMutexGuard aGuard;
+ if ( m_pData->m_pViewShell )
+ {
+ OUString sData;
+ m_pData->m_pViewShell->WriteUserData( sData ) ;
+ aAny <<= sData ;
+ }
+
+ return aAny ;
+}
+
+
+// SfxBaseController -> XController
+
+
+void SAL_CALL SfxBaseController::restoreViewData( const uno::Any& aValue )
+{
+ SolarMutexGuard aGuard;
+ if ( m_pData->m_pViewShell )
+ {
+ OUString sData;
+ aValue >>= sData ;
+ m_pData->m_pViewShell->ReadUserData( sData ) ;
+ }
+}
+
+
+// SfxBaseController -> XController
+
+
+Reference< frame::XFrame > SAL_CALL SfxBaseController::getFrame()
+{
+ SolarMutexGuard aGuard;
+ return m_pData->m_xFrame;
+}
+
+
+// SfxBaseController -> XController
+
+
+Reference< frame::XModel > SAL_CALL SfxBaseController::getModel()
+{
+ SolarMutexGuard aGuard;
+ return m_pData->m_pViewShell ? m_pData->m_pViewShell->GetObjectShell()->GetModel() : Reference < frame::XModel > () ;
+}
+
+
+// SfxBaseController -> XDispatchProvider
+
+
+Reference< frame::XDispatch > SAL_CALL SfxBaseController::queryDispatch( const util::URL& aURL ,
+ const OUString& sTargetFrameName,
+ sal_Int32 eSearchFlags )
+{
+ SolarMutexGuard aGuard;
+ Reference< frame::XDispatch > xDisp;
+ if ( m_pData->m_pViewShell )
+ {
+ SfxViewFrame* pAct = m_pData->m_pViewShell->GetViewFrame() ;
+ if ( !m_pData->m_bDisposing )
+ {
+ if ( sTargetFrameName == "_beamer" )
+ {
+ SfxViewFrame *pFrame = m_pData->m_pViewShell->GetViewFrame();
+ if ( eSearchFlags & frame::FrameSearchFlag::CREATE )
+ pFrame->SetChildWindow( SID_BROWSER, true );
+ SfxChildWindow* pChildWin = pFrame->GetChildWindow( SID_BROWSER );
+ Reference < frame::XFrame > xFrame;
+ if ( pChildWin )
+ xFrame = pChildWin->GetFrame();
+ if ( xFrame.is() )
+ xFrame->setName( sTargetFrameName );
+
+ Reference< XDispatchProvider > xProv( xFrame, uno::UNO_QUERY );
+ if ( xProv.is() )
+ return xProv->queryDispatch( aURL, sTargetFrameName, frame::FrameSearchFlag::SELF );
+ }
+
+ if ( aURL.Protocol == ".uno:" )
+ {
+ OUString aMasterCommand = SfxOfficeDispatch::GetMasterUnoCommand( aURL );
+ bool bMasterCommand( !aMasterCommand.isEmpty() );
+
+ pAct = m_pData->m_pViewShell->GetViewFrame() ;
+ SfxSlotPool& rSlotPool = SfxSlotPool::GetSlotPool( pAct );
+
+ const SfxSlot* pSlot( nullptr );
+ if ( bMasterCommand )
+ pSlot = rSlotPool.GetUnoSlot( aMasterCommand );
+ else
+ pSlot = rSlotPool.GetUnoSlot( aURL.Path );
+ if ( pSlot && ( !pAct->GetFrame().IsInPlace() || !pSlot->IsMode( SfxSlotMode::CONTAINER ) ) )
+ return pAct->GetBindings().GetDispatch( pSlot, aURL, bMasterCommand );
+ else
+ {
+ // try to find parent SfxViewFrame
+ Reference< frame::XFrame > xParentFrame;
+ Reference< frame::XFrame > xOwnFrame = pAct->GetFrame().GetFrameInterface();
+ if ( xOwnFrame.is() )
+ xParentFrame = xOwnFrame->getCreator();
+
+ if ( xParentFrame.is() )
+ {
+ // TODO/LATER: in future probably SfxViewFrame hierarchy should be the same as XFrame hierarchy
+ // SfxViewFrame* pParentFrame = pAct->GetParentViewFrame();
+
+ // search the related SfxViewFrame
+ SfxViewFrame* pParentFrame = nullptr;
+ for ( SfxViewFrame* pFrame = SfxViewFrame::GetFirst();
+ pFrame;
+ pFrame = SfxViewFrame::GetNext( *pFrame ) )
+ {
+ if ( pFrame->GetFrame().GetFrameInterface() == xParentFrame )
+ {
+ pParentFrame = pFrame;
+ break;
+ }
+ }
+
+ if ( pParentFrame )
+ {
+ SfxSlotPool& rFrameSlotPool = SfxSlotPool::GetSlotPool( pParentFrame );
+ const SfxSlot* pSlot2( nullptr );
+ if ( bMasterCommand )
+ pSlot2 = rFrameSlotPool.GetUnoSlot( aMasterCommand );
+ else
+ pSlot2 = rFrameSlotPool.GetUnoSlot( aURL.Path );
+
+ if ( pSlot2 )
+ return pParentFrame->GetBindings().GetDispatch( pSlot2, aURL, bMasterCommand );
+ }
+ }
+ }
+ }
+ else if ( aURL.Protocol == "slot:" )
+ {
+ sal_uInt16 nId = static_cast<sal_uInt16>(aURL.Path.toInt32());
+
+ pAct = m_pData->m_pViewShell->GetViewFrame() ;
+ if (nId >= SID_VERB_START && nId <= SID_VERB_END)
+ {
+ const SfxSlot* pSlot = m_pData->m_pViewShell->GetVerbSlot_Impl(nId);
+ if ( pSlot )
+ return pAct->GetBindings().GetDispatch( pSlot, aURL, false );
+ }
+
+ SfxSlotPool& rSlotPool = SfxSlotPool::GetSlotPool( pAct );
+ const SfxSlot* pSlot = rSlotPool.GetSlot( nId );
+ if ( pSlot && ( !pAct->GetFrame().IsInPlace() || !pSlot->IsMode( SfxSlotMode::CONTAINER ) ) )
+ return pAct->GetBindings().GetDispatch( pSlot, aURL, false );
+ else
+ {
+ // try to find parent SfxViewFrame
+ Reference< frame::XFrame > xParentFrame;
+ Reference< frame::XFrame > xOwnFrame = pAct->GetFrame().GetFrameInterface();
+ if ( xOwnFrame.is() )
+ xParentFrame = xOwnFrame->getCreator();
+
+ if ( xParentFrame.is() )
+ {
+ // TODO/LATER: in future probably SfxViewFrame hierarchy should be the same as XFrame hierarchy
+ // SfxViewFrame* pParentFrame = pAct->GetParentViewFrame();
+
+ // search the related SfxViewFrame
+ SfxViewFrame* pParentFrame = nullptr;
+ for ( SfxViewFrame* pFrame = SfxViewFrame::GetFirst();
+ pFrame;
+ pFrame = SfxViewFrame::GetNext( *pFrame ) )
+ {
+ if ( pFrame->GetFrame().GetFrameInterface() == xParentFrame )
+ {
+ pParentFrame = pFrame;
+ break;
+ }
+ }
+
+ if ( pParentFrame )
+ {
+ SfxSlotPool& rSlotPool2 = SfxSlotPool::GetSlotPool( pParentFrame );
+ const SfxSlot* pSlot2 = rSlotPool2.GetUnoSlot( aURL.Path );
+ if ( pSlot2 )
+ return pParentFrame->GetBindings().GetDispatch( pSlot2, aURL, false );
+ }
+ }
+ }
+ }
+ else if( sTargetFrameName == "_self" || sTargetFrameName.isEmpty() )
+ {
+ // check for already loaded URL ... but with additional jumpmark!
+ Reference< frame::XModel > xModel = getModel();
+ if( xModel.is() && !aURL.Mark.isEmpty() )
+ {
+ SfxSlotPool& rSlotPool = SfxSlotPool::GetSlotPool( pAct );
+ const SfxSlot* pSlot = rSlotPool.GetSlot( SID_JUMPTOMARK );
+ if( !aURL.Main.isEmpty() && aURL.Main == xModel->getURL() && pSlot )
+ return Reference< frame::XDispatch >( new SfxOfficeDispatch( pAct->GetBindings(), pAct->GetDispatcher(), pSlot, aURL) );
+ }
+ }
+ }
+ }
+
+ return xDisp;
+}
+
+
+// SfxBaseController -> XDispatchProvider
+
+
+uno::Sequence< Reference< frame::XDispatch > > SAL_CALL SfxBaseController::queryDispatches( const uno::Sequence< frame::DispatchDescriptor >& seqDescripts )
+{
+ // Create return list - which must have same size then the given descriptor
+ // It's not allowed to pack it!
+ sal_Int32 nCount = seqDescripts.getLength();
+ uno::Sequence< Reference< frame::XDispatch > > lDispatcher( nCount );
+
+ std::transform(seqDescripts.begin(), seqDescripts.end(), lDispatcher.getArray(),
+ [this](const frame::DispatchDescriptor& rDesc) -> Reference< frame::XDispatch > {
+ return queryDispatch(rDesc.FeatureURL, rDesc.FrameName, rDesc.SearchFlags); });
+
+ return lDispatcher;
+}
+
+
+// SfxBaseController -> XControllerBorder
+
+
+frame::BorderWidths SAL_CALL SfxBaseController::getBorder()
+{
+ frame::BorderWidths aResult;
+
+ SolarMutexGuard aGuard;
+ if ( m_pData->m_pViewShell )
+ {
+ SvBorder aBorder = m_pData->m_pViewShell->GetBorderPixel();
+ aResult.Left = aBorder.Left();
+ aResult.Top = aBorder.Top();
+ aResult.Right = aBorder.Right();
+ aResult.Bottom = aBorder.Bottom();
+ }
+
+ return aResult;
+}
+
+void SAL_CALL SfxBaseController::addBorderResizeListener( const Reference< frame::XBorderResizeListener >& xListener )
+{
+ m_pData->m_aListenerContainer.addInterface( cppu::UnoType<frame::XBorderResizeListener>::get(),
+ xListener );
+}
+
+void SAL_CALL SfxBaseController::removeBorderResizeListener( const Reference< frame::XBorderResizeListener >& xListener )
+{
+ m_pData->m_aListenerContainer.removeInterface( cppu::UnoType<frame::XBorderResizeListener>::get(),
+ xListener );
+}
+
+awt::Rectangle SAL_CALL SfxBaseController::queryBorderedArea( const awt::Rectangle& aPreliminaryRectangle )
+{
+ SolarMutexGuard aGuard;
+ if ( m_pData->m_pViewShell )
+ {
+ tools::Rectangle aTmpRect = VCLRectangle( aPreliminaryRectangle );
+ m_pData->m_pViewShell->QueryObjAreaPixel( aTmpRect );
+ return AWTRectangle( aTmpRect );
+ }
+
+ return aPreliminaryRectangle;
+}
+
+void SfxBaseController::BorderWidthsChanged_Impl()
+{
+ ::comphelper::OInterfaceContainerHelper2* pContainer = m_pData->m_aListenerContainer.getContainer(
+ cppu::UnoType<frame::XBorderResizeListener>::get());
+ if ( !pContainer )
+ return;
+
+ frame::BorderWidths aBWidths = getBorder();
+ Reference< uno::XInterface > xThis( static_cast< ::cppu::OWeakObject* >(this), uno::UNO_QUERY );
+
+ ::comphelper::OInterfaceIteratorHelper2 pIterator(*pContainer);
+ while (pIterator.hasMoreElements())
+ {
+ try
+ {
+ static_cast<frame::XBorderResizeListener*>(pIterator.next())->borderWidthsChanged( xThis, aBWidths );
+ }
+ catch (const RuntimeException&)
+ {
+ pIterator.remove();
+ }
+ }
+}
+
+
+// SfxBaseController -> XComponent
+
+
+void SAL_CALL SfxBaseController::dispose()
+{
+ SolarMutexGuard aGuard;
+ Reference< XController > xKeepAlive( this );
+ m_pData->m_bDisposing = true ;
+
+ lang::EventObject aEventObject;
+ aEventObject.Source = *this ;
+ m_pData->m_aListenerContainer.disposeAndClear( aEventObject ) ;
+
+ if ( m_pData->m_pController && m_pData->m_pController->getFrame().is() )
+ m_pData->m_pController->getFrame()->removeFrameActionListener( m_pData->m_xListener ) ;
+
+ if ( !m_pData->m_pViewShell )
+ return;
+
+ SfxViewFrame* pFrame = m_pData->m_pViewShell->GetViewFrame() ;
+ if ( pFrame && pFrame->GetViewShell() == m_pData->m_pViewShell )
+ pFrame->GetFrame().SetIsClosing_Impl();
+ m_pData->m_pViewShell->DisconnectAllClients();
+
+ if ( !pFrame )
+ return;
+
+ lang::EventObject aObject;
+ aObject.Source = *this ;
+
+ SfxObjectShell* pDoc = pFrame->GetObjectShell() ;
+ SfxViewFrame *pView = SfxViewFrame::GetFirst(pDoc);
+ while( pView )
+ {
+ // if there is another ViewFrame or currently the ViewShell in my ViewFrame is switched (PagePreview)
+ if ( pView != pFrame || pView->GetViewShell() != m_pData->m_pViewShell )
+ break;
+ pView = SfxViewFrame::GetNext( *pView, pDoc );
+ }
+
+ SfxGetpApp()->NotifyEvent( SfxViewEventHint(SfxEventHintId::CloseView, GlobalEventConfig::GetEventName( GlobalEventId::CLOSEVIEW ), pDoc, Reference< frame::XController2 >( this ) ) );
+ if ( !pView )
+ SfxGetpApp()->NotifyEvent( SfxEventHint(SfxEventHintId::CloseDoc, GlobalEventConfig::GetEventName( GlobalEventId::CLOSEDOC ), pDoc) );
+
+ Reference< frame::XModel > xModel = pDoc->GetModel();
+ Reference < util::XCloseable > xCloseable( xModel, uno::UNO_QUERY );
+ if ( xModel.is() )
+ {
+ xModel->disconnectController( this );
+ if ( xCloseable.is() )
+ xCloseable->removeCloseListener( m_pData->m_xCloseListener );
+ }
+
+ Reference < frame::XFrame > aXFrame;
+ attachFrame( aXFrame );
+
+ m_pData->m_xListener->disposing( aObject );
+ SfxViewShell *pShell = m_pData->m_pViewShell;
+ m_pData->m_pViewShell = nullptr;
+ if ( pFrame->GetViewShell() == pShell )
+ {
+ // Enter registrations only allowed if we are the owner!
+ if ( pFrame->GetFrame().OwnsBindings_Impl() )
+ pFrame->GetBindings().ENTERREGISTRATIONS();
+ pFrame->GetFrame().SetFrameInterface_Impl( aXFrame );
+ pFrame->GetFrame().DoClose_Impl();
+ }
+}
+
+
+// SfxBaseController -> XComponent
+
+
+void SAL_CALL SfxBaseController::addEventListener( const Reference< lang::XEventListener >& aListener )
+{
+ m_pData->m_aListenerContainer.addInterface( cppu::UnoType<lang::XEventListener>::get(), aListener );
+}
+
+
+// SfxBaseController -> XComponent
+
+
+void SAL_CALL SfxBaseController::removeEventListener( const Reference< lang::XEventListener >& aListener )
+{
+ m_pData->m_aListenerContainer.removeInterface( cppu::UnoType<lang::XEventListener>::get(), aListener );
+}
+
+void SfxBaseController::ReleaseShell_Impl()
+{
+ SolarMutexGuard aGuard;
+ if ( !m_pData->m_pViewShell )
+ return;
+
+ SfxObjectShell* pDoc = m_pData->m_pViewShell->GetObjectShell() ;
+ Reference< frame::XModel > xModel = pDoc->GetModel();
+ Reference < util::XCloseable > xCloseable( xModel, uno::UNO_QUERY );
+ if ( xModel.is() )
+ {
+ xModel->disconnectController( this );
+ if ( xCloseable.is() )
+ xCloseable->removeCloseListener( m_pData->m_xCloseListener );
+ }
+ m_pData->m_pViewShell = nullptr;
+
+ Reference < frame::XFrame > aXFrame;
+ attachFrame( aXFrame );
+}
+
+SfxViewShell* SfxBaseController::GetViewShell_Impl() const
+{
+ return m_pData->m_pViewShell;
+}
+
+Reference< task::XStatusIndicator > SAL_CALL SfxBaseController::getStatusIndicator( )
+{
+ SolarMutexGuard aGuard;
+ if ( m_pData->m_pViewShell && !m_pData->m_xIndicator.is() )
+ m_pData->m_xIndicator = new SfxStatusIndicator( this, m_pData->m_pViewShell->GetViewFrame()->GetFrame().GetWorkWindow_Impl() );
+ return m_pData->m_xIndicator;
+}
+
+void SAL_CALL SfxBaseController::registerContextMenuInterceptor( const Reference< ui::XContextMenuInterceptor >& xInterceptor )
+
+{
+ m_pData->m_aInterceptorContainer.addInterface( xInterceptor );
+
+ SolarMutexGuard aGuard;
+ if ( m_pData->m_pViewShell )
+ m_pData->m_pViewShell->AddContextMenuInterceptor_Impl( xInterceptor );
+}
+
+void SAL_CALL SfxBaseController::releaseContextMenuInterceptor( const Reference< ui::XContextMenuInterceptor >& xInterceptor )
+
+{
+ m_pData->m_aInterceptorContainer.removeInterface( xInterceptor );
+
+ SolarMutexGuard aGuard;
+ if ( m_pData->m_pViewShell )
+ m_pData->m_pViewShell->RemoveContextMenuInterceptor_Impl( xInterceptor );
+}
+
+void SAL_CALL SfxBaseController::addKeyHandler( const Reference< awt::XKeyHandler >& xHandler )
+{
+ SolarMutexGuard aGuard;
+ m_pData->m_aUserInputInterception.addKeyHandler( xHandler );
+}
+
+void SAL_CALL SfxBaseController::removeKeyHandler( const Reference< awt::XKeyHandler >& xHandler )
+{
+ SolarMutexGuard aGuard;
+ m_pData->m_aUserInputInterception.removeKeyHandler( xHandler );
+}
+
+void SAL_CALL SfxBaseController::addMouseClickHandler( const Reference< awt::XMouseClickHandler >& xHandler )
+{
+ SolarMutexGuard aGuard;
+ m_pData->m_aUserInputInterception.addMouseClickHandler( xHandler );
+}
+
+void SAL_CALL SfxBaseController::removeMouseClickHandler( const Reference< awt::XMouseClickHandler >& xHandler )
+{
+ SolarMutexGuard aGuard;
+ m_pData->m_aUserInputInterception.removeMouseClickHandler( xHandler );
+}
+
+uno::Sequence< sal_Int16 > SAL_CALL SfxBaseController::getSupportedCommandGroups()
+{
+ SolarMutexGuard aGuard;
+
+ std::vector< sal_Int16 > aGroupList;
+ SfxViewFrame* pViewFrame = m_pData->m_pViewShell ? m_pData->m_pViewShell->GetFrame() : nullptr;
+ SfxSlotPool* pSlotPool = pViewFrame ? &SfxSlotPool::GetSlotPool(pViewFrame) : &SFX_SLOTPOOL();
+ const SfxSlotMode nMode( SfxSlotMode::TOOLBOXCONFIG|SfxSlotMode::ACCELCONFIG|SfxSlotMode::MENUCONFIG );
+
+ // Select Group ( Group 0 is internal )
+ for ( sal_uInt16 i=0; i<pSlotPool->GetGroupCount(); i++ )
+ {
+ pSlotPool->SeekGroup( i );
+ const SfxSlot* pSfxSlot = pSlotPool->FirstSlot();
+ while ( pSfxSlot )
+ {
+ if ( pSfxSlot->GetMode() & nMode )
+ {
+ sal_Int16 nCommandGroup = MapGroupIDToCommandGroup( pSfxSlot->GetGroupId() );
+ aGroupList.push_back( nCommandGroup );
+ break;
+ }
+ pSfxSlot = pSlotPool->NextSlot();
+ }
+ }
+
+ return comphelper::containerToSequence( aGroupList );
+}
+
+uno::Sequence< frame::DispatchInformation > SAL_CALL SfxBaseController::getConfigurableDispatchInformation( sal_Int16 nCmdGroup )
+{
+ std::vector< frame::DispatchInformation > aCmdVector;
+
+ SolarMutexGuard aGuard;
+ if ( m_pData->m_pViewShell )
+ {
+ const SfxSlotMode nMode( SfxSlotMode::TOOLBOXCONFIG|SfxSlotMode::ACCELCONFIG|SfxSlotMode::MENUCONFIG );
+
+ SfxViewFrame* pViewFrame( m_pData->m_pViewShell->GetFrame() );
+ SfxSlotPool* pSlotPool
+ = pViewFrame ? &SfxSlotPool::GetSlotPool(pViewFrame) : &SFX_SLOTPOOL();
+ for ( sal_uInt16 i=0; i<pSlotPool->GetGroupCount(); i++ )
+ {
+ pSlotPool->SeekGroup( i );
+ const SfxSlot* pSfxSlot = pSlotPool->FirstSlot();
+ if ( pSfxSlot )
+ {
+ sal_Int16 nCommandGroup = MapGroupIDToCommandGroup( pSfxSlot->GetGroupId() );
+ if ( nCommandGroup == nCmdGroup )
+ {
+ while ( pSfxSlot )
+ {
+ if ( pSfxSlot->GetMode() & nMode )
+ {
+ frame::DispatchInformation aCmdInfo;
+ aCmdInfo.Command = ".uno:" + OUString::createFromAscii( pSfxSlot->GetUnoName() );
+ aCmdInfo.GroupId = nCommandGroup;
+ aCmdVector.push_back( aCmdInfo );
+ }
+ pSfxSlot = pSlotPool->NextSlot();
+ }
+ }
+ }
+ }
+ }
+
+ return comphelper::containerToSequence( aCmdVector );
+}
+
+bool SfxBaseController::HandleEvent_Impl( NotifyEvent const & rEvent )
+{
+ return m_pData->m_aUserInputInterception.handleNotifyEvent( rEvent );
+}
+
+bool SfxBaseController::HasKeyListeners_Impl() const
+{
+ return m_pData->m_aUserInputInterception.hasKeyHandlers();
+}
+
+bool SfxBaseController::HasMouseClickListeners_Impl() const
+{
+ return m_pData->m_aUserInputInterception.hasMouseClickListeners();
+}
+
+void SfxBaseController::ConnectSfxFrame_Impl( const ConnectSfxFrame i_eConnect )
+{
+ ENSURE_OR_THROW( m_pData->m_pViewShell, "not to be called without a view shell" );
+ SfxViewFrame* pViewFrame = m_pData->m_pViewShell->GetFrame();
+ ENSURE_OR_THROW( pViewFrame, "a view shell without a view frame is pretty pathological" );
+
+ const bool bConnect = ( i_eConnect != E_DISCONNECT );
+
+ // disable window and dispatcher
+ pViewFrame->Enable( bConnect );
+ pViewFrame->GetDispatcher()->Lock( !bConnect );
+
+ if ( bConnect )
+ {
+ if ( i_eConnect == E_CONNECT )
+ {
+ if ( ( m_pData->m_pViewShell->GetObjectShell() != nullptr )
+ && ( m_pData->m_pViewShell->GetObjectShell()->GetCreateMode() == SfxObjectCreateMode::EMBEDDED )
+ )
+ {
+ SfxViewFrame* pViewFrm = m_pData->m_pViewShell->GetViewFrame();
+ if ( !pViewFrm->GetFrame().IsInPlace() )
+ {
+ // for outplace embedded objects, we want the layout manager to keep the content window
+ // size constant, if possible
+ try
+ {
+ Reference< beans::XPropertySet > xFrameProps( m_pData->m_xFrame, uno::UNO_QUERY_THROW );
+ Reference< beans::XPropertySet > xLayouterProps(
+ xFrameProps->getPropertyValue("LayoutManager"), uno::UNO_QUERY_THROW );
+ xLayouterProps->setPropertyValue("PreserveContentSize", uno::Any( true ) );
+ }
+ catch (const uno::Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("sfx.view");
+ }
+ }
+ }
+ }
+
+ // upon DISCONNECT, we did *not* pop the shells from the stack (this is done elsewhere), so upon
+ // RECONNECT, we're not allowed to push them
+ if ( i_eConnect != E_RECONNECT )
+ {
+ pViewFrame->GetDispatcher()->Push( *m_pData->m_pViewShell );
+ m_pData->m_pViewShell->PushSubShells_Impl();
+ pViewFrame->GetDispatcher()->Flush();
+ }
+
+ vcl::Window* pEditWin = m_pData->m_pViewShell->GetWindow();
+ if ( pEditWin )
+ pEditWin->Show();
+
+ if ( SfxViewFrame::Current() == pViewFrame )
+ pViewFrame->GetDispatcher()->Update_Impl( true );
+
+ vcl::Window* pFrameWin = &pViewFrame->GetWindow();
+ if ( pFrameWin != &pViewFrame->GetFrame().GetWindow() )
+ pFrameWin->Show();
+
+ if ( i_eConnect == E_CONNECT )
+ {
+ css::uno::Reference<css::frame::XModel3> xModel(getModel(), css::uno::UNO_QUERY_THROW);
+ const sal_Int16 nPluginMode = ::comphelper::NamedValueCollection::getOrDefault( xModel->getArgs2( { "PluginMode" } ), u"PluginMode", sal_Int16( 0 ) );
+ const bool bHasPluginMode = ( nPluginMode != 0 );
+
+ SfxFrame& rFrame = pViewFrame->GetFrame();
+ SfxObjectShell& rDoc = *m_pData->m_pViewShell->GetObjectShell();
+ if ( !rFrame.IsMarkedHidden_Impl() )
+ {
+ if ( rDoc.IsHelpDocument() || ( nPluginMode == 2 ) )
+ pViewFrame->GetDispatcher()->HideUI();
+ else
+ pViewFrame->GetDispatcher()->HideUI( false );
+
+ if ( rFrame.IsInPlace() )
+ pViewFrame->LockAdjustPosSizePixel();
+
+ if ( nPluginMode == 3 )
+ rFrame.GetWorkWindow_Impl()->SetInternalDockingAllowed( false );
+
+ if ( !rFrame.IsInPlace() )
+ pViewFrame->GetDispatcher()->Update_Impl();
+ pViewFrame->Show();
+ rFrame.GetWindow().Show();
+ if ( !rFrame.IsInPlace() || ( nPluginMode == 3 ) )
+ pViewFrame->MakeActive_Impl( rFrame.GetFrameInterface()->isActive() );
+
+ if ( rFrame.IsInPlace() )
+ {
+ pViewFrame->UnlockAdjustPosSizePixel();
+ // force resize for OLE server to fix layout problems of writer and math
+ // see i53651
+ if ( nPluginMode == 3 )
+ pViewFrame->Resize( true );
+ }
+ }
+ else
+ {
+ DBG_ASSERT( !rFrame.IsInPlace() && !bHasPluginMode, "Special modes not compatible with hidden mode!" );
+ rFrame.GetWindow().Show();
+ }
+
+ // UpdateTitle now, hidden TopFrames have otherwise no Name!
+ pViewFrame->UpdateTitle();
+
+ if ( !rFrame.IsInPlace() )
+ pViewFrame->Resize( true );
+
+ ::comphelper::NamedValueCollection aViewArgs(getCreationArguments());
+
+ // sometimes we want to avoid adding to the recent documents
+ bool bAllowPickListEntry = aViewArgs.getOrDefault("PickListEntry", true);
+ m_pData->m_pViewShell->GetObjectShell()->AvoidRecentDocs(!bAllowPickListEntry);
+
+ // if there's a JumpMark given, then, well, jump to it
+ const OUString sJumpMark = aViewArgs.getOrDefault( "JumpMark", OUString() );
+ const bool bHasJumpMark = !sJumpMark.isEmpty();
+ OSL_ENSURE( ( !m_pData->m_pViewShell->GetObjectShell()->IsLoading() )
+ || ( sJumpMark.isEmpty() ),
+ "SfxBaseController::ConnectSfxFrame_Impl: so this code wasn't dead?" );
+ // Before CWS autorecovery, there was code which postponed jumping to the Mark to a later time
+ // (SfxObjectShell::PositionView_Impl), but it seems this branch was never used, since this method
+ // here is never called before the load process finished. At least not with a non-empty jump mark
+ if ( !sJumpMark.isEmpty() )
+ m_pData->m_pViewShell->JumpToMark( sJumpMark );
+
+ // if no plugin mode and no jump mark was supplied, check whether the document itself can provide view data, and
+ // if so, forward it to the view/shell.
+ if ( !bHasPluginMode && !bHasJumpMark )
+ {
+ // Note that this might not be the ideal place here. Restoring view data should, IMO, be the
+ // responsibility of the loader, not an implementation detail buried here deep within the controller's
+ // implementation.
+ // What I think should be done to replace the below code:
+ // - change SfxBaseController::restoreViewData to also accept a PropertyValue[] (it currently accepts
+ // a string only), and forward it to its ViewShell's ReadUserDataSequence
+ // - change the frame loader so that when a new document is loaded (as opposed to an existing
+ // document being loaded into a new frame), the model's view data is examine the very same
+ // way as below, and the proper view data is set via XController::restoreViewData
+ // - extend SfxViewFrame::SwitchToViewShell_Impl. Currently, it cares for the case where a non-PrintPreview
+ // view is exchanged, and sets the old view's data at the model. It should also care for the other
+ // way, were the PrintPreview view is left: in this case, the new view should also be initialized
+ // with the model's view data
+ try
+ {
+ Reference< XViewDataSupplier > xViewDataSupplier( getModel(), UNO_QUERY_THROW );
+ Reference< XIndexAccess > xViewData( xViewDataSupplier->getViewData() );
+
+ // find the view data item whose ViewId matches the ID of the view we're just connecting to
+ const SfxObjectFactory& rDocFactory( rDoc.GetFactory() );
+ const sal_Int32 nCount = xViewData.is() ? xViewData->getCount() : 0;
+ sal_Int32 nViewDataIndex = 0;
+ for ( sal_Int32 i=0; i<nCount; ++i )
+ {
+ const ::comphelper::NamedValueCollection aViewData( xViewData->getByIndex(i) );
+ OUString sViewId( aViewData.getOrDefault( "ViewId", OUString() ) );
+ if ( sViewId.isEmpty() )
+ continue;
+
+ const SfxViewFactory* pViewFactory = rDocFactory.GetViewFactoryByViewName( sViewId );
+ if ( pViewFactory == nullptr )
+ continue;
+
+ if ( pViewFactory->GetOrdinal() == pViewFrame->GetCurViewId() )
+ {
+ nViewDataIndex = i;
+ break;
+ }
+ }
+ if (nViewDataIndex < nCount || !xViewData.is())
+ {
+ Sequence< PropertyValue > aViewData;
+ if (xViewData.is())
+ {
+ OSL_VERIFY(xViewData->getByIndex(nViewDataIndex) >>= aViewData);
+ }
+ if (aViewData.hasElements() || !xViewData.is())
+ {
+ // Tolerate empty xViewData, ReadUserDataSequence() has side effects.
+ m_pData->m_pViewShell->ReadUserDataSequence( aViewData );
+ }
+ }
+ }
+ catch (const Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("sfx.view");
+ }
+ }
+ }
+ }
+
+ // invalidate slot corresponding to the view shell
+ const sal_uInt16 nViewNo = m_pData->m_pViewShell->GetObjectShell()->GetFactory().GetViewNo_Impl( pViewFrame->GetCurViewId(), USHRT_MAX );
+ DBG_ASSERT( nViewNo != USHRT_MAX, "view shell id not found" );
+ if ( nViewNo != USHRT_MAX )
+ pViewFrame->GetBindings().Invalidate( nViewNo + SID_VIEWSHELL0 );
+}
+
+void SfxBaseController::ShowInfoBars( )
+{
+ if ( !m_pData->m_pViewShell )
+ return;
+
+ // CMIS verifications
+ Reference< document::XCmisDocument > xCmisDoc( m_pData->m_pViewShell->GetObjectShell()->GetModel(), uno::UNO_QUERY );
+ if ( !xCmisDoc.is( ) || !xCmisDoc->canCheckOut( ) )
+ return;
+
+ const uno::Sequence< document::CmisProperty> aCmisProperties = xCmisDoc->getCmisProperties( );
+
+ if ( !(xCmisDoc->isVersionable( ) && aCmisProperties.hasElements( )) )
+ return;
+
+ // Loop over the CMIS Properties to find cmis:isVersionSeriesCheckedOut
+ // and find if it is a Google Drive file.
+ bool bIsGoogleFile = false;
+ bool bCheckedOut = false;
+ for ( const auto& rCmisProp : aCmisProperties )
+ {
+ if ( rCmisProp.Id == "cmis:isVersionSeriesCheckedOut" ) {
+ uno::Sequence< sal_Bool > bTmp;
+ rCmisProp.Value >>= bTmp;
+ bCheckedOut = bTmp[0];
+ }
+ // if it is a Google Drive file, we don't need the checkout bar,
+ // still need the checkout feature for the version dialog.
+ if ( rCmisProp.Name == "title" )
+ bIsGoogleFile = true;
+ }
+
+ if ( bCheckedOut || bIsGoogleFile )
+ return;
+
+ // Get the Frame and show the InfoBar if not checked out
+ SfxViewFrame* pViewFrame = m_pData->m_pViewShell->GetFrame();
+ auto pInfoBar = pViewFrame->AppendInfoBar("checkout", "", SfxResId(STR_NONCHECKEDOUT_DOCUMENT),
+ InfobarType::WARNING);
+ if (pInfoBar)
+ {
+ weld::Button &rBtn = pInfoBar->addButton();
+ rBtn.set_label(SfxResId(STR_CHECKOUT));
+ rBtn.connect_clicked(LINK(this, SfxBaseController, CheckOutHandler));
+ }
+}
+
+IMPL_LINK_NOARG ( SfxBaseController, CheckOutHandler, weld::Button&, void )
+{
+ if ( m_pData->m_pViewShell )
+ m_pData->m_pViewShell->GetObjectShell()->CheckOut( );
+}
+
+
+Reference< frame::XTitle > SfxBaseController::impl_getTitleHelper ()
+{
+ SolarMutexGuard aGuard;
+
+ if ( ! m_pData->m_xTitleHelper.is ())
+ {
+ Reference< frame::XModel > xModel = getModel ();
+ Reference< frame::XUntitledNumbers > xUntitledProvider(xModel , uno::UNO_QUERY );
+
+ m_pData->m_xTitleHelper = new ::framework::TitleHelper(::comphelper::getProcessComponentContext(),
+ Reference< frame::XController >(this), xUntitledProvider);
+ }
+
+ return m_pData->m_xTitleHelper;
+}
+
+
+// frame::XTitle
+OUString SAL_CALL SfxBaseController::getTitle()
+{
+ return impl_getTitleHelper()->getTitle ();
+}
+
+
+// frame::XTitle
+void SAL_CALL SfxBaseController::setTitle(const OUString& sTitle)
+{
+ impl_getTitleHelper()->setTitle (sTitle);
+}
+
+
+// frame::XTitleChangeBroadcaster
+void SAL_CALL SfxBaseController::addTitleChangeListener(const Reference< frame::XTitleChangeListener >& xListener)
+{
+ Reference< frame::XTitleChangeBroadcaster > xBroadcaster(impl_getTitleHelper(), uno::UNO_QUERY);
+ if (xBroadcaster.is ())
+ xBroadcaster->addTitleChangeListener (xListener);
+}
+
+
+// frame::XTitleChangeBroadcaster
+void SAL_CALL SfxBaseController::removeTitleChangeListener(const Reference< frame::XTitleChangeListener >& xListener)
+{
+ Reference< frame::XTitleChangeBroadcaster > xBroadcaster(impl_getTitleHelper(), uno::UNO_QUERY);
+ if (xBroadcaster.is ())
+ xBroadcaster->removeTitleChangeListener (xListener);
+}
+
+void SfxBaseController::initialize( const css::uno::Sequence< css::uno::Any >& /*aArguments*/ )
+{
+}
+
+void SAL_CALL SfxBaseController::appendInfobar(const OUString& sId, const OUString& sPrimaryMessage,
+ const OUString& sSecondaryMessage,
+ sal_Int32 aInfobarType,
+ const Sequence<StringPair>& actionButtons,
+ sal_Bool bShowCloseButton)
+{
+ SolarMutexGuard aGuard;
+
+ if (aInfobarType < static_cast<sal_Int32>(InfobarType::INFO)
+ || aInfobarType > static_cast<sal_Int32>(InfobarType::DANGER))
+ throw lang::IllegalArgumentException("Undefined InfobarType: "
+ + OUString::number(aInfobarType),
+ static_cast<::cppu::OWeakObject*>(this), 0);
+ SfxViewFrame* pViewFrame = m_pData->m_pViewShell->GetFrame();
+ if (pViewFrame->HasInfoBarWithID(sId))
+ throw lang::IllegalArgumentException("Infobar with ID '" + sId + "' already existing.",
+ static_cast<::cppu::OWeakObject*>(this), 0);
+
+ auto pInfoBar
+ = pViewFrame->AppendInfoBar(sId, sPrimaryMessage, sSecondaryMessage,
+ static_cast<InfobarType>(aInfobarType), bShowCloseButton);
+ if (!pInfoBar)
+ throw uno::RuntimeException("Could not create Infobar");
+
+ for (const StringPair & actionButton : std::as_const(actionButtons))
+ {
+ if (actionButton.First.isEmpty() || actionButton.Second.isEmpty())
+ continue;
+ weld::Button& rBtn = pInfoBar->addButton(&actionButton.Second);
+ rBtn.set_label(actionButton.First);
+ }
+}
+
+void SAL_CALL SfxBaseController::updateInfobar(const OUString& sId, const OUString& sPrimaryMessage,
+ const OUString& sSecondaryMessage,
+ sal_Int32 aInfobarType)
+{
+ SolarMutexGuard aGuard;
+
+ if (aInfobarType < static_cast<sal_Int32>(InfobarType::INFO)
+ || aInfobarType > static_cast<sal_Int32>(InfobarType::DANGER))
+ throw lang::IllegalArgumentException("Undefined InfobarType: "
+ + OUString::number(aInfobarType),
+ static_cast<::cppu::OWeakObject*>(this), 0);
+ SfxViewFrame* pViewFrame = m_pData->m_pViewShell->GetFrame();
+ if (!pViewFrame->HasInfoBarWithID(sId))
+ throw css::container::NoSuchElementException("Infobar with ID '" + sId + "' not found.");
+
+ pViewFrame->UpdateInfoBar(sId, sPrimaryMessage, sSecondaryMessage,
+ static_cast<InfobarType>(aInfobarType));
+}
+
+void SAL_CALL SfxBaseController::removeInfobar(const OUString& sId)
+{
+ SolarMutexGuard aGuard;
+
+ SfxViewFrame* pViewFrame = m_pData->m_pViewShell->GetFrame();
+ if (!pViewFrame->HasInfoBarWithID(sId))
+ throw css::container::NoSuchElementException("Infobar with ID '" + sId + "' not found.");
+ pViewFrame->RemoveInfoBar(sId);
+}
+
+sal_Bool SAL_CALL SfxBaseController::hasInfobar(const OUString& sId)
+{
+ SolarMutexGuard aGuard;
+ SfxViewFrame* pViewFrame = m_pData->m_pViewShell->GetFrame();
+ return pViewFrame->HasInfoBarWithID(sId);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/view/userinputinterception.cxx b/sfx2/source/view/userinputinterception.cxx
new file mode 100644
index 000000000..f1f0d365d
--- /dev/null
+++ b/sfx2/source/view/userinputinterception.cxx
@@ -0,0 +1,263 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <sfx2/userinputinterception.hxx>
+
+#include <com/sun/star/awt/MouseButton.hpp>
+#include <com/sun/star/awt/MouseEvent.hpp>
+#include <com/sun/star/awt/InputEvent.hpp>
+#include <com/sun/star/awt/KeyEvent.hpp>
+#include <com/sun/star/awt/KeyModifier.hpp>
+#include <com/sun/star/awt/XKeyHandler.hpp>
+#include <com/sun/star/awt/XMouseClickHandler.hpp>
+#include <com/sun/star/awt/XWindowPeer.hpp>
+#include <com/sun/star/uno/XInterface.hpp>
+
+#include <comphelper/interfacecontainer3.hxx>
+#include <cppuhelper/weak.hxx>
+#include <vcl/event.hxx>
+#include <vcl/window.hxx>
+#include <osl/diagnose.h>
+
+namespace sfx2
+{
+
+
+ using ::com::sun::star::uno::Reference;
+ using ::com::sun::star::uno::XInterface;
+ using ::com::sun::star::uno::Exception;
+ using ::com::sun::star::uno::RuntimeException;
+ using ::com::sun::star::awt::MouseEvent;
+ using ::com::sun::star::awt::KeyEvent;
+ using ::com::sun::star::awt::InputEvent;
+ using ::com::sun::star::awt::XKeyHandler;
+ using ::com::sun::star::awt::XMouseClickHandler;
+ using ::com::sun::star::lang::DisposedException;
+
+ namespace MouseButton = ::com::sun::star::awt::MouseButton;
+ namespace KeyModifier = ::com::sun::star::awt::KeyModifier;
+
+ struct UserInputInterception_Data
+ {
+ public:
+ ::cppu::OWeakObject& m_rControllerImpl;
+ ::comphelper::OInterfaceContainerHelper3<XKeyHandler> m_aKeyHandlers;
+ ::comphelper::OInterfaceContainerHelper3<XMouseClickHandler> m_aMouseClickHandlers;
+
+ public:
+ UserInputInterception_Data( ::cppu::OWeakObject& _rControllerImpl, ::osl::Mutex& _rMutex )
+ :m_rControllerImpl( _rControllerImpl )
+ ,m_aKeyHandlers( _rMutex )
+ ,m_aMouseClickHandlers( _rMutex )
+ {
+ }
+ };
+
+ namespace
+ {
+ template< class VCLEVENT >
+ void lcl_initModifiers( InputEvent& _rEvent, const VCLEVENT& _rVclEvent )
+ {
+ _rEvent.Modifiers = 0;
+
+ if ( _rVclEvent.IsShift() )
+ _rEvent.Modifiers |= KeyModifier::SHIFT;
+ if ( _rVclEvent.IsMod1() )
+ _rEvent.Modifiers |= KeyModifier::MOD1;
+ if ( _rVclEvent.IsMod2() )
+ _rEvent.Modifiers |= KeyModifier::MOD2;
+ if ( _rVclEvent.IsMod3() )
+ _rEvent.Modifiers |= KeyModifier::MOD3;
+ }
+
+ void lcl_initKeyEvent( KeyEvent& rEvent, const ::KeyEvent& rEvt )
+ {
+ lcl_initModifiers( rEvent, rEvt.GetKeyCode() );
+
+ rEvent.KeyCode = rEvt.GetKeyCode().GetCode();
+ rEvent.KeyChar = rEvt.GetCharCode();
+ rEvent.KeyFunc = sal::static_int_cast< sal_Int16 >( rEvt.GetKeyCode().GetFunction());
+ }
+
+ void lcl_initMouseEvent( MouseEvent& rEvent, const ::MouseEvent& rEvt )
+ {
+ lcl_initModifiers( rEvent, rEvt );
+
+ rEvent.Buttons = 0;
+ if ( rEvt.IsLeft() )
+ rEvent.Buttons |= MouseButton::LEFT;
+ if ( rEvt.IsRight() )
+ rEvent.Buttons |= MouseButton::RIGHT;
+ if ( rEvt.IsMiddle() )
+ rEvent.Buttons |= MouseButton::MIDDLE;
+
+ rEvent.X = rEvt.GetPosPixel().X();
+ rEvent.Y = rEvt.GetPosPixel().Y();
+ rEvent.ClickCount = rEvt.GetClicks();
+ rEvent.PopupTrigger = false;
+ }
+
+ }
+
+
+ //= UserInputInterception
+
+
+ UserInputInterception::UserInputInterception( ::cppu::OWeakObject& _rControllerImpl, ::osl::Mutex& _rMutex )
+ :m_pData( new UserInputInterception_Data( _rControllerImpl, _rMutex ) )
+ {
+ }
+
+
+ UserInputInterception::~UserInputInterception()
+ {
+ }
+
+
+ void UserInputInterception::addKeyHandler( const Reference< XKeyHandler >& _rxHandler )
+ {
+ if ( _rxHandler.is() )
+ m_pData->m_aKeyHandlers.addInterface( _rxHandler );
+ }
+
+
+ void UserInputInterception::removeKeyHandler( const Reference< XKeyHandler >& _rxHandler )
+ {
+ m_pData->m_aKeyHandlers.removeInterface( _rxHandler );
+ }
+
+
+ void UserInputInterception::addMouseClickHandler( const Reference< XMouseClickHandler >& _rxHandler )
+ {
+ if ( _rxHandler.is() )
+ m_pData->m_aMouseClickHandlers.addInterface( _rxHandler );
+ }
+
+
+ void UserInputInterception::removeMouseClickHandler( const Reference< XMouseClickHandler >& _rxHandler )
+ {
+ m_pData->m_aMouseClickHandlers.removeInterface( _rxHandler );
+ }
+
+
+ bool UserInputInterception::hasKeyHandlers() const
+ {
+ return m_pData->m_aKeyHandlers.getLength() > 0;
+ }
+
+
+ bool UserInputInterception::hasMouseClickListeners() const
+ {
+ return m_pData->m_aMouseClickHandlers.getLength() > 0;
+ }
+
+
+ bool UserInputInterception::handleNotifyEvent( const NotifyEvent& _rEvent )
+ {
+ Reference < XInterface > xHoldAlive( m_pData->m_rControllerImpl );
+
+ MouseNotifyEvent nType = _rEvent.GetType();
+ bool bHandled = false;
+
+ switch ( nType )
+ {
+ case MouseNotifyEvent::KEYINPUT:
+ case MouseNotifyEvent::KEYUP:
+ {
+ KeyEvent aEvent;
+ lcl_initKeyEvent( aEvent, *_rEvent.GetKeyEvent() );
+ if ( _rEvent.GetWindow() )
+ aEvent.Source = _rEvent.GetWindow()->GetComponentInterface();
+
+ ::comphelper::OInterfaceIteratorHelper3 aIterator( m_pData->m_aKeyHandlers );
+ while ( aIterator.hasMoreElements() )
+ {
+ Reference< XKeyHandler > xHandler( aIterator.next() );
+ try
+ {
+ if ( nType == MouseNotifyEvent::KEYINPUT )
+ bHandled = xHandler->keyPressed( aEvent );
+ else
+ bHandled = xHandler->keyReleased( aEvent );
+ }
+ catch( const DisposedException& e )
+ {
+ if ( e.Context == xHandler )
+ aIterator.remove();
+ }
+ catch( const RuntimeException& )
+ {
+ throw;
+ }
+ catch( const Exception& )
+ {
+ }
+ }
+ }
+ break;
+
+ case MouseNotifyEvent::MOUSEBUTTONDOWN:
+ case MouseNotifyEvent::MOUSEBUTTONUP:
+ {
+ MouseEvent aEvent;
+ lcl_initMouseEvent( aEvent, *_rEvent.GetMouseEvent() );
+ if ( _rEvent.GetWindow() )
+ aEvent.Source = _rEvent.GetWindow()->GetComponentInterface();
+
+ ::comphelper::OInterfaceIteratorHelper3 aIterator( m_pData->m_aMouseClickHandlers );
+ while ( aIterator.hasMoreElements() )
+ {
+ Reference< XMouseClickHandler > xHandler( aIterator.next() );
+ try
+ {
+ if ( nType == MouseNotifyEvent::MOUSEBUTTONDOWN )
+ bHandled = xHandler->mousePressed( aEvent );
+ else
+ bHandled = xHandler->mouseReleased( aEvent );
+ }
+ catch( const DisposedException& e )
+ {
+ if ( e.Context == xHandler )
+ aIterator.remove();
+ }
+ catch( const RuntimeException& )
+ {
+ throw;
+ }
+ catch( const Exception& )
+ {
+ }
+ }
+ }
+ break;
+
+ default:
+ OSL_FAIL( "UserInputInterception::handleNotifyEvent: illegal event type!" );
+ break;
+ }
+
+ return bHandled;
+ }
+
+
+} // namespace sfx2
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/view/viewfac.cxx b/sfx2/source/view/viewfac.cxx
new file mode 100644
index 000000000..b83f41847
--- /dev/null
+++ b/sfx2/source/view/viewfac.cxx
@@ -0,0 +1,56 @@
+/* -*- 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/viewfac.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/viewsh.hxx>
+
+SfxViewShell *SfxViewFactory::CreateInstance(SfxViewFrame *pFrame, SfxViewShell *pOldSh )
+{
+ return (*fnCreate)(pFrame, pOldSh);
+}
+
+OUString SfxViewFactory::GetLegacyViewName() const
+{
+ return "view" + OUString::number( sal_uInt16( GetOrdinal() ) );
+}
+
+OUString SfxViewFactory::GetAPIViewName() const
+{
+ if ( !m_sViewName.isEmpty() )
+ return m_sViewName;
+
+ if ( GetOrdinal() == SFX_INTERFACE_NONE )
+ return "Default";
+
+ return GetLegacyViewName();
+}
+
+// CTOR / DTOR -----------------------------------------------------------
+
+SfxViewFactory::SfxViewFactory( SfxViewCtor fnC,
+ SfxInterfaceId nOrdinal, const char* asciiViewName ):
+ fnCreate(fnC),
+ nOrd(nOrdinal),
+ m_sViewName( OUString::createFromAscii( asciiViewName ) )
+{
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/view/viewfrm.cxx b/sfx2/source/view/viewfrm.cxx
new file mode 100644
index 000000000..da5477928
--- /dev/null
+++ b/sfx2/source/view/viewfrm.cxx
@@ -0,0 +1,3527 @@
+/* -*- 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 <config_feature_desktop.h>
+#include <config_wasm_strip.h>
+
+#include <osl/file.hxx>
+#include <sfx2/docfilt.hxx>
+#include <sfx2/infobar.hxx>
+#include <sfx2/sfxsids.hrc>
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/classificationhelper.hxx>
+#include <sfx2/notebookbar/SfxNotebookBar.hxx>
+#include <com/sun/star/document/MacroExecMode.hpp>
+#include <com/sun/star/frame/Desktop.hpp>
+#include <com/sun/star/frame/DispatchRecorder.hpp>
+#include <com/sun/star/frame/DispatchRecorderSupplier.hpp>
+#include <com/sun/star/frame/XLoadable.hpp>
+#include <com/sun/star/frame/XLayoutManager.hpp>
+#include <com/sun/star/frame/XComponentLoader.hpp>
+#include <com/sun/star/task/PasswordContainer.hpp>
+#include <officecfg/Office/Common.hxx>
+#include <officecfg/Setup.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+#include <vcl/wrkwin.hxx>
+#include <unotools/moduleoptions.hxx>
+#include <svl/intitem.hxx>
+#include <svl/visitem.hxx>
+#include <svl/stritem.hxx>
+#include <svl/eitem.hxx>
+#include <svl/whiter.hxx>
+#include <svl/undo.hxx>
+#include <vcl/stdtext.hxx>
+#include <vcl/weld.hxx>
+#include <vcl/weldutils.hxx>
+#if !ENABLE_WASM_STRIP_PINGUSER
+#include <unotools/VersionConfig.hxx>
+#endif
+#include <svtools/miscopt.hxx>
+#include <tools/diagnose_ex.h>
+#include <com/sun/star/container/XIndexAccess.hpp>
+#include <com/sun/star/frame/XFramesSupplier.hpp>
+#include <com/sun/star/frame/FrameSearchFlag.hpp>
+#include <com/sun/star/frame/XFrame.hpp>
+#include <com/sun/star/awt/XWindow.hpp>
+#include <com/sun/star/frame/XController.hpp>
+#include <com/sun/star/util/URLTransformer.hpp>
+#include <com/sun/star/util/XURLTransformer.hpp>
+#include <com/sun/star/util/XCloseable.hpp>
+#include <com/sun/star/frame/XDispatchRecorderSupplier.hpp>
+#include <com/sun/star/document/UpdateDocMode.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/uri/UriReferenceFactory.hpp>
+#include <com/sun/star/uri/XVndSunStarScriptUrl.hpp>
+#include <com/sun/star/document/XViewDataSupplier.hpp>
+#include <com/sun/star/container/XIndexContainer.hpp>
+#include <com/sun/star/task/InteractionHandler.hpp>
+#include <com/sun/star/drawing/XDrawView.hpp>
+#include <com/sun/star/drawing/XDrawPagesSupplier.hpp>
+#include <rtl/ustrbuf.hxx>
+#include <sal/log.hxx>
+
+#include <unotools/ucbhelper.hxx>
+#include <comphelper/lok.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/namedvaluecollection.hxx>
+#include <comphelper/docpasswordrequest.hxx>
+#include <comphelper/docpasswordhelper.hxx>
+
+#include <com/sun/star/uno/Reference.h>
+
+#include <basic/basmgr.hxx>
+#include <basic/sbmod.hxx>
+#include <basic/sbmeth.hxx>
+#include <svtools/strings.hrc>
+#include <svtools/svtresid.hxx>
+#include <framework/framelistanalyzer.hxx>
+
+#include <optional>
+
+#include <comphelper/sequenceashashmap.hxx>
+
+#include <commandpopup/CommandPopup.hxx>
+
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::ucb;
+using namespace ::com::sun::star::frame;
+using namespace ::com::sun::star::lang;
+using ::com::sun::star::awt::XWindow;
+using ::com::sun::star::beans::PropertyValue;
+using ::com::sun::star::document::XViewDataSupplier;
+using ::com::sun::star::container::XIndexContainer;
+
+// Due to ViewFrame::Current
+#include <appdata.hxx>
+#include <sfx2/app.hxx>
+#include <sfx2/objface.hxx>
+#include <openflag.hxx>
+#include <objshimp.hxx>
+#include <sfx2/viewsh.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/request.hxx>
+#include <sfx2/docfac.hxx>
+#include <sfx2/ipclient.hxx>
+#include <sfx2/sfxresid.hxx>
+#include <sfx2/viewfac.hxx>
+#include <sfx2/event.hxx>
+#include <sfx2/fcontnr.hxx>
+#include <sfx2/docfile.hxx>
+#include <sfx2/module.hxx>
+#include <sfx2/sfxuno.hxx>
+#include <sfx2/progress.hxx>
+#include <sfx2/sidebar/Sidebar.hxx>
+#include <workwin.hxx>
+#include <sfx2/minfitem.hxx>
+#include <sfx2/strings.hrc>
+#include "impviewframe.hxx"
+#include <vcl/commandinfoprovider.hxx>
+#include <vcl/svapp.hxx>
+
+#define ShellClass_SfxViewFrame
+#include <sfxslots.hxx>
+
+constexpr OUStringLiteral CHANGES_STR = u"private:resource/toolbar/changes";
+
+SFX_IMPL_SUPERCLASS_INTERFACE(SfxViewFrame,SfxShell)
+
+void SfxViewFrame::InitInterface_Impl()
+{
+ GetStaticInterface()->RegisterChildWindow(SID_BROWSER);
+ GetStaticInterface()->RegisterChildWindow(SID_RECORDING_FLOATWINDOW);
+#if HAVE_FEATURE_DESKTOP
+ GetStaticInterface()->RegisterObjectBar(SFX_OBJECTBAR_FULLSCREEN, SfxVisibilityFlags::FullScreen, ToolbarId::FullScreenToolbox);
+ GetStaticInterface()->RegisterObjectBar(SFX_OBJECTBAR_APPLICATION, SfxVisibilityFlags::Standard, ToolbarId::EnvToolbox);
+#endif
+}
+
+namespace {
+/// Asks the user if editing a read-only document is really wanted.
+class SfxEditDocumentDialog : public weld::MessageDialogController
+{
+private:
+ std::unique_ptr<weld::Button> m_xEditDocument;
+ std::unique_ptr<weld::Button> m_xCancel;
+
+public:
+ SfxEditDocumentDialog(weld::Widget* pParent);
+};
+
+SfxEditDocumentDialog::SfxEditDocumentDialog(weld::Widget* pParent)
+ : MessageDialogController(pParent, "sfx/ui/editdocumentdialog.ui",
+ "EditDocumentDialog")
+ , m_xEditDocument(m_xBuilder->weld_button("edit"))
+ , m_xCancel(m_xBuilder->weld_button("cancel"))
+{
+}
+
+class SfxQueryOpenAsTemplate
+{
+private:
+ std::unique_ptr<weld::MessageDialog> m_xQueryBox;
+public:
+ SfxQueryOpenAsTemplate(weld::Window* pParent, bool bAllowIgnoreLock, LockFileEntry& rLockData)
+ : m_xQueryBox(Application::CreateMessageDialog(pParent, VclMessageType::Question,
+ VclButtonsType::NONE, ""))
+ {
+ m_xQueryBox->add_button(SfxResId(STR_QUERY_OPENASTEMPLATE_OPENCOPY_BTN), RET_YES);
+ bAllowIgnoreLock
+ = bAllowIgnoreLock && officecfg::Office::Common::Misc::AllowOverrideLocking::get();
+ if (bAllowIgnoreLock)
+ m_xQueryBox->add_button(SfxResId(STR_QUERY_OPENASTEMPLATE_OPEN_BTN), RET_IGNORE);
+ m_xQueryBox->add_button(GetStandardText( StandardButtonType::Cancel ), RET_CANCEL);
+ m_xQueryBox->set_primary_text(QueryString(bAllowIgnoreLock, rLockData));
+ m_xQueryBox->set_default_response(RET_YES);
+ }
+ short run() { return m_xQueryBox->run(); }
+
+private:
+ static OUString QueryString(bool bAllowIgnoreLock, LockFileEntry& rLockData)
+ {
+ OUString sLockUserData;
+ if (!rLockData[LockFileComponent::OOOUSERNAME].isEmpty())
+ sLockUserData = rLockData[LockFileComponent::OOOUSERNAME];
+ else
+ sLockUserData = rLockData[LockFileComponent::SYSUSERNAME];
+
+ if (!sLockUserData.isEmpty() && !rLockData[LockFileComponent::EDITTIME].isEmpty())
+ sLockUserData += " ( " + rLockData[LockFileComponent::EDITTIME] + " )";
+
+ if (!sLockUserData.isEmpty())
+ sLockUserData = "\n\n" + sLockUserData + "\n";
+
+ const bool bUseLockStr = bAllowIgnoreLock || !sLockUserData.isEmpty();
+
+ OUString sMsg(
+ SfxResId(bUseLockStr ? STR_QUERY_OPENASTEMPLATE_LOCKED : STR_QUERY_OPENASTEMPLATE));
+
+ if (bAllowIgnoreLock)
+ sMsg += "\n\n" + SfxResId(STR_QUERY_OPENASTEMPLATE_ALLOW_IGNORE);
+
+ return sMsg.replaceFirst("%LOCKINFO", sLockUserData);
+ }
+};
+
+bool AskPasswordToModify_Impl( const uno::Reference< task::XInteractionHandler >& xHandler, const OUString& aPath, const std::shared_ptr<const SfxFilter>& pFilter, sal_uInt32 nPasswordHash, const uno::Sequence< beans::PropertyValue >& aInfo )
+{
+ // TODO/LATER: In future the info should replace the direct hash completely
+ bool bResult = ( !nPasswordHash && !aInfo.hasElements() );
+
+ SAL_WARN_IF( !(pFilter && ( pFilter->GetFilterFlags() & SfxFilterFlags::PASSWORDTOMODIFY )), "sfx.view",
+ "PasswordToModify feature is active for a filter that does not support it!");
+
+ if ( pFilter && xHandler.is() )
+ {
+ bool bCancel = false;
+ bool bFirstTime = true;
+
+ while ( !bResult && !bCancel )
+ {
+ bool bMSType = !pFilter->IsOwnFormat();
+
+ ::rtl::Reference< ::comphelper::DocPasswordRequest > pPasswordRequest(
+ new ::comphelper::DocPasswordRequest(
+ bMSType ? ::comphelper::DocPasswordRequestType::MS : ::comphelper::DocPasswordRequestType::Standard,
+ bFirstTime ? css::task::PasswordRequestMode_PASSWORD_ENTER : css::task::PasswordRequestMode_PASSWORD_REENTER,
+ aPath,
+ true ) );
+
+ xHandler->handle( pPasswordRequest );
+
+ if ( pPasswordRequest->isPassword() )
+ {
+ if ( aInfo.hasElements() )
+ {
+ bResult = ::comphelper::DocPasswordHelper::IsModifyPasswordCorrect( pPasswordRequest->getPasswordToModify(), aInfo );
+ }
+ else
+ {
+ // the binary format
+ bResult = ( SfxMedium::CreatePasswordToModifyHash( pPasswordRequest->getPasswordToModify(), pFilter->GetServiceName()=="com.sun.star.text.TextDocument" ) == nPasswordHash );
+ }
+ }
+ else
+ bCancel = true;
+
+ bFirstTime = false;
+ }
+ }
+
+ return bResult;
+}
+
+bool physObjIsOlder(INetURLObject const & aMedObj, INetURLObject const & aPhysObj) {
+ return ::utl::UCBContentHelper::IsYounger(aMedObj.GetMainURL( INetURLObject::DecodeMechanism::NONE),
+ aPhysObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
+}
+}
+
+void SfxViewFrame::ExecReload_Impl( SfxRequest& rReq )
+{
+ SfxObjectShell* pSh = GetObjectShell();
+ switch ( rReq.GetSlot() )
+ {
+ case SID_EDITDOC:
+ case SID_READONLYDOC:
+ {
+ // Due to Double occupancy in toolboxes (with or without Ctrl),
+ // it is also possible that the slot is enabled, but Ctrl-click
+ // despite this is not!
+ if( !pSh || !pSh->HasName() || !(pSh->Get_Impl()->nLoadedFlags & SfxLoadedFlags::MAINDOCUMENT ))
+ break;
+
+ if (pSh->isEditDocLocked())
+ break;
+
+ // Only change read-only UI and remove info bar when we succeed
+ struct ReadOnlyUIGuard
+ {
+ SfxViewFrame* m_pFrame;
+ SfxObjectShell* m_pSh;
+ SfxMedium* m_pMed = nullptr;
+ bool m_bSetRO;
+ ReadOnlyUIGuard(SfxViewFrame* pFrame, SfxObjectShell* p_Sh)
+ : m_pFrame(pFrame), m_pSh(p_Sh), m_bSetRO(p_Sh->IsReadOnlyUI())
+ {}
+ ~ReadOnlyUIGuard() COVERITY_NOEXCEPT_FALSE
+ {
+ if (m_bSetRO != m_pSh->IsReadOnlyUI())
+ {
+ m_pSh->SetReadOnlyUI(m_bSetRO);
+ if (!m_bSetRO)
+ m_pFrame->RemoveInfoBar(u"readonly");
+ if (m_pMed)
+ {
+ // tdf#116066: DoSaveCompleted should be called after SetReadOnlyUI
+ m_pSh->DoSaveCompleted(m_pMed);
+ m_pSh->Broadcast(SfxHint(SfxHintId::ModeChanged));
+ }
+ }
+ }
+ } aReadOnlyUIGuard(this, pSh);
+
+ SfxMedium* pMed = pSh->GetMedium();
+
+ std::shared_ptr<std::recursive_mutex> pChkEditMutex = pMed->GetCheckEditableMutex();
+ std::unique_lock<std::recursive_mutex> chkEditLock;
+ if (pChkEditMutex != nullptr)
+ chkEditLock = std::unique_lock<std::recursive_mutex>(*pChkEditMutex);
+ pMed->CancelCheckEditableEntry();
+
+ const SfxBoolItem* pItem = SfxItemSet::GetItem<SfxBoolItem>(pSh->GetMedium()->GetItemSet(), SID_VIEWONLY, false);
+ if ( pItem && pItem->GetValue() )
+ {
+ SfxApplication* pApp = SfxGetpApp();
+ SfxAllItemSet aSet( pApp->GetPool() );
+ aSet.Put( SfxStringItem( SID_FILE_NAME, pMed->GetURLObject().GetMainURL(INetURLObject::DecodeMechanism::NONE) ) );
+ aSet.Put( SfxBoolItem( SID_TEMPLATE, true ) );
+ aSet.Put( SfxStringItem( SID_TARGETNAME, "_blank" ) );
+ const SfxStringItem* pReferer = SfxItemSet::GetItem<SfxStringItem>(pMed->GetItemSet(), SID_REFERER, false);
+ if ( pReferer )
+ aSet.Put( *pReferer );
+ const SfxInt16Item* pVersionItem = SfxItemSet::GetItem<SfxInt16Item>(pSh->GetMedium()->GetItemSet(), SID_VERSION, false);
+ if ( pVersionItem )
+ aSet.Put( *pVersionItem );
+
+ if( pMed->GetFilter() )
+ {
+ aSet.Put( SfxStringItem( SID_FILTER_NAME, pMed->GetFilter()->GetFilterName() ) );
+ const SfxStringItem* pOptions = SfxItemSet::GetItem<SfxStringItem>(pMed->GetItemSet(), SID_FILE_FILTEROPTIONS, false);
+ if ( pOptions )
+ aSet.Put( *pOptions );
+ }
+
+ GetDispatcher()->Execute( SID_OPENDOC, SfxCallMode::ASYNCHRON, aSet );
+ return;
+ }
+
+ StreamMode nOpenMode;
+ bool bNeedsReload = false;
+ bool bPasswordEntered = false;
+ if ( !pSh->IsReadOnly() )
+ {
+ // Save and reload Readonly
+ if( pSh->IsModified() )
+ {
+ if ( pSh->PrepareClose() )
+ {
+ // the storing could let the medium be changed
+ pMed = pSh->GetMedium();
+ bNeedsReload = true;
+ }
+ else
+ {
+ rReq.SetReturnValue( SfxBoolItem( rReq.GetSlot(), false ) );
+ return;
+ }
+ }
+ nOpenMode = SFX_STREAM_READONLY;
+ aReadOnlyUIGuard.m_bSetRO = true;
+ }
+ else
+ {
+ if ( pSh->IsReadOnlyMedium()
+ && ( pSh->GetModifyPasswordHash() || pSh->GetModifyPasswordInfo().hasElements() )
+ && !pSh->IsModifyPasswordEntered() )
+ {
+ const OUString aDocumentName = INetURLObject( pMed->GetOrigURL() ).GetMainURL( INetURLObject::DecodeMechanism::WithCharset );
+ if( !AskPasswordToModify_Impl( pMed->GetInteractionHandler(), aDocumentName, pMed->GetFilter(), pSh->GetModifyPasswordHash(), pSh->GetModifyPasswordInfo() ) )
+ {
+ // this is a read-only document, if it has "Password to modify"
+ // the user should enter password before he can edit the document
+ rReq.SetReturnValue( SfxBoolItem( rReq.GetSlot(), false ) );
+ return;
+ }
+
+ pSh->SetModifyPasswordEntered();
+ bPasswordEntered = true;
+ }
+
+ nOpenMode = pSh->IsOriginallyReadOnlyMedium() ? SFX_STREAM_READONLY : SFX_STREAM_READWRITE;
+ aReadOnlyUIGuard.m_bSetRO = false;
+
+ // if only the view was in the readonly mode then there is no need to do the reload
+ if ( !pSh->IsReadOnlyMedium() )
+ {
+ // SetReadOnlyUI causes recomputation of window title, using
+ // open mode among other things, so call SetOpenMode before
+ // SetReadOnlyUI:
+ pMed->SetOpenMode( nOpenMode );
+ return;
+ }
+ }
+
+ if ( rReq.IsAPI() )
+ {
+ // Control through API if r/w or r/o
+ const SfxBoolItem* pEditItem = rReq.GetArg<SfxBoolItem>(SID_EDITDOC);
+ if ( pEditItem )
+ nOpenMode = pEditItem->GetValue() ? SFX_STREAM_READWRITE : SFX_STREAM_READONLY;
+ }
+
+ // doing
+
+ OUString sTemp;
+ osl::FileBase::getFileURLFromSystemPath( pMed->GetPhysicalName(), sTemp );
+ INetURLObject aPhysObj( sTemp );
+ const SfxInt16Item* pVersionItem = SfxItemSet::GetItem<SfxInt16Item>(pSh->GetMedium()->GetItemSet(), SID_VERSION, false);
+
+ INetURLObject aMedObj( pMed->GetName() );
+
+ // -> tdf#82744
+ // the logic below is following:
+ // if the document seems not to need to be reloaded
+ // and the physical name is different to the logical one,
+ // then on file system it can be checked that the copy is still newer than the original and no document reload is required.
+ // Did some semplification to enhance readability of the 'if' expression
+ //
+ // when the 'http/https' protocol is active, the bool bPhysObjIsYounger relies upon the getlastmodified Property of a WebDAV resource.
+ // Said property should be implemented, but sometimes it's not.
+ // implemented. On this case the reload activated here will not work properly.
+ // TODO: change the check age method for WebDAV to etag (entity-tag) property value, need some rethinking, since the
+ // etag tells that the cache representation (e.g. in LO) is different from the one on the server,
+ // but tells nothing about the age
+ // Details at this link: http://tools.ietf.org/html/rfc4918#section-15, section 15.7
+ bool bIsWebDAV = aMedObj.isAnyKnownWebDAVScheme();
+
+ // tdf#118938 Reload the document when the user enters the editing password,
+ // even if the physical name isn't different to the logical name.
+ if ( ( !bNeedsReload && ( ( aMedObj.GetProtocol() == INetProtocol::File &&
+ ( aMedObj.getFSysPath( FSysStyle::Detect ) != aPhysObj.getFSysPath( FSysStyle::Detect )
+ || bPasswordEntered ) &&
+ !physObjIsOlder(aMedObj, aPhysObj))
+ || (bIsWebDAV && !physObjIsOlder(aMedObj, aPhysObj))
+ || ( pMed->IsRemote() && !bIsWebDAV ) ) )
+ || pVersionItem )
+ // <- tdf#82744
+ {
+ bool bOK = false;
+ bool bRetryIgnoringLock = false;
+ bool bOpenTemplate = false;
+ std::optional<bool> aOrigROVal;
+ if (!pVersionItem)
+ {
+ auto pRO = pMed->GetItemSet()->GetItem<SfxBoolItem>(SID_DOC_READONLY, false);
+ if (pRO)
+ aOrigROVal = pRO->GetValue();
+ }
+ do {
+ LockFileEntry aLockData;
+ if ( !pVersionItem )
+ {
+ if (bRetryIgnoringLock)
+ pMed->ResetError();
+
+ bool bHasStorage = pMed->HasStorage_Impl();
+ // switching edit mode could be possible without reload
+ if ( bHasStorage && pMed->GetStorage() == pSh->GetStorage() )
+ {
+ // TODO/LATER: faster creation of copy
+ if ( !pSh->ConnectTmpStorage_Impl( pMed->GetStorage(), pMed ) )
+ return;
+ }
+
+ pMed->CloseAndRelease();
+ pMed->SetOpenMode( nOpenMode );
+ // We need to clear the SID_DOC_READONLY item from the set, to allow
+ // MediaDescriptor::impl_openStreamWithURL (called indirectly by
+ // SfxMedium::CompleteReOpen) to properly fill input stream of the
+ // descriptor, even when the file can't be open in read-write mode.
+ // Only then can following call to SfxMedium::LockOrigFileOnDemand
+ // return proper information about who has locked the file, to show
+ // in the SfxQueryOpenAsTemplate box below; otherwise it exits right
+ // after call to SfxMedium::GetMedium_Impl. This mimics what happens
+ // when the file is opened initially, when filter detection code also
+ // calls MediaDescriptor::impl_openStreamWithURL without the item set.
+ pMed->GetItemSet()->ClearItem(SID_DOC_READONLY);
+ pMed->CompleteReOpen();
+ pMed->GetItemSet()->Put(
+ SfxBoolItem(SID_DOC_READONLY, !(nOpenMode & StreamMode::WRITE)));
+ if ( nOpenMode & StreamMode::WRITE )
+ {
+ auto eResult = pMed->LockOrigFileOnDemand(
+ true, true, bRetryIgnoringLock, &aLockData);
+ bRetryIgnoringLock
+ = eResult == SfxMedium::LockFileResult::FailedLockFile;
+ }
+
+ // LockOrigFileOnDemand might set the readonly flag itself, it should be set back
+ pMed->GetItemSet()->Put( SfxBoolItem( SID_DOC_READONLY, !( nOpenMode & StreamMode::WRITE ) ) );
+
+ if ( !pMed->GetErrorCode() )
+ bOK = true;
+ }
+
+ if( !bOK )
+ {
+ if (nOpenMode == SFX_STREAM_READWRITE && !rReq.IsAPI())
+ {
+ // css::sdbcx::User offering to open it as a template
+ SfxQueryOpenAsTemplate aBox(GetWindow().GetFrameWeld(),
+ bRetryIgnoringLock, aLockData);
+
+ short nUserAnswer = aBox.run();
+ bOpenTemplate = RET_YES == nUserAnswer;
+ // Always reset this here to avoid infinite loop
+ bRetryIgnoringLock = RET_IGNORE == nUserAnswer;
+ if (RET_CANCEL == nUserAnswer)
+ pMed->AddToCheckEditableWorkerList();
+ }
+ else
+ bRetryIgnoringLock = false;
+ }
+ }
+ while ( !bOK && bRetryIgnoringLock );
+
+ if( !bOK )
+ {
+ ErrCode nErr = pMed->GetErrorCode();
+ if ( pVersionItem )
+ nErr = ERRCODE_IO_ACCESSDENIED;
+ else
+ {
+ pMed->ResetError();
+ pMed->SetOpenMode( SFX_STREAM_READONLY );
+ if (aOrigROVal)
+ pMed->GetItemSet()->Put(SfxBoolItem(SID_DOC_READONLY, *aOrigROVal));
+ else
+ pMed->GetItemSet()->ClearItem(SID_DOC_READONLY);
+ pMed->ReOpen();
+ pSh->DoSaveCompleted( pMed );
+ }
+
+ // Readonly document can not be switched to edit mode?
+ rReq.Done();
+
+ if ( nOpenMode == SFX_STREAM_READWRITE && !rReq.IsAPI() )
+ {
+ if ( bOpenTemplate )
+ {
+ SfxApplication* pApp = SfxGetpApp();
+ SfxAllItemSet aSet( pApp->GetPool() );
+ aSet.Put( SfxStringItem( SID_FILE_NAME, pMed->GetName() ) );
+ const SfxStringItem* pReferer = SfxItemSet::GetItem<SfxStringItem>(pMed->GetItemSet(), SID_REFERER, false);
+ if ( pReferer )
+ aSet.Put( *pReferer );
+ aSet.Put( SfxBoolItem( SID_TEMPLATE, true ) );
+ if ( pVersionItem )
+ aSet.Put( *pVersionItem );
+
+ if( pMed->GetFilter() )
+ {
+ aSet.Put( SfxStringItem( SID_FILTER_NAME, pMed->GetFilter()->GetFilterName() ) );
+ const SfxStringItem* pOptions = SfxItemSet::GetItem<SfxStringItem>(pMed->GetItemSet(), SID_FILE_FILTEROPTIONS, false);
+ if ( pOptions )
+ aSet.Put( *pOptions );
+ }
+
+ GetDispatcher()->Execute( SID_OPENDOC, SfxCallMode::ASYNCHRON, aSet );
+ return;
+ }
+
+ nErr = ERRCODE_NONE;
+ }
+
+ // Keep the read-only UI
+ aReadOnlyUIGuard.m_bSetRO = true;
+
+ ErrorHandler::HandleError( nErr );
+ rReq.SetReturnValue(
+ SfxBoolItem( rReq.GetSlot(), false ) );
+ return;
+ }
+ else
+ {
+ aReadOnlyUIGuard.m_pMed = pMed;
+ rReq.SetReturnValue( SfxBoolItem( rReq.GetSlot(), true ) );
+ rReq.Done( true );
+ return;
+ }
+ }
+
+ rReq.AppendItem( SfxBoolItem(SID_FORCERELOAD,
+ rReq.GetSlot() == SID_EDITDOC || bNeedsReload) );
+ rReq.AppendItem( SfxBoolItem( SID_SILENT, true ));
+
+ [[fallthrough]]; //TODO ???
+ }
+
+ case SID_RELOAD:
+ {
+ // Due to Double occupancy in toolboxes (with or without Ctrl),
+ // it is also possible that the slot is enabled, but Ctrl-click
+ // despite this is not!
+ if ( !pSh || !pSh->CanReload_Impl() )
+ break;
+ SfxApplication* pApp = SfxGetpApp();
+ const SfxBoolItem* pForceReloadItem = rReq.GetArg<SfxBoolItem>(SID_FORCERELOAD);
+ if( pForceReloadItem && !pForceReloadItem->GetValue() &&
+ !pSh->GetMedium()->IsExpired() )
+ return;
+ if( m_pImpl->bReloading || pSh->IsInModalMode() )
+ return;
+
+ // AutoLoad is prohibited if possible
+ const SfxBoolItem* pAutoLoadItem = rReq.GetArg<SfxBoolItem>(SID_AUTOLOAD);
+ if ( pAutoLoadItem && pAutoLoadItem->GetValue() &&
+ GetFrame().IsAutoLoadLocked_Impl() )
+ return;
+
+ SfxObjectShellLock xOldObj( pSh );
+ m_pImpl->bReloading = true;
+ const SfxStringItem* pURLItem = rReq.GetArg<SfxStringItem>(SID_FILE_NAME);
+ // Open as editable?
+ bool bForEdit = !pSh->IsReadOnly();
+
+ // If possible ask the User
+ bool bDo = GetViewShell()->PrepareClose();
+ const SfxBoolItem* pSilentItem = rReq.GetArg<SfxBoolItem>(SID_SILENT);
+ if (getenv("SAL_NO_QUERYSAVE"))
+ bDo = true;
+ else if (bDo && GetFrame().DocIsModified_Impl() && !rReq.IsAPI()
+ && (!pSilentItem || !pSilentItem->GetValue()))
+ {
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(
+ GetWindow().GetFrameWeld(), VclMessageType::Question, VclButtonsType::YesNo,
+ SfxResId(STR_QUERY_LASTVERSION)));
+ bDo = RET_YES == xBox->run();
+ }
+
+ if ( bDo )
+ {
+ SfxMedium *pMedium = xOldObj->GetMedium();
+ std::shared_ptr<std::recursive_mutex> pChkEditMutex
+ = pMedium->GetCheckEditableMutex();
+ std::unique_lock<std::recursive_mutex> chkEditLock;
+ if (pChkEditMutex != nullptr)
+ chkEditLock = std::unique_lock<std::recursive_mutex>(*pChkEditMutex);
+ pMedium->CancelCheckEditableEntry();
+
+ bool bHandsOff =
+ ( pMedium->GetURLObject().GetProtocol() == INetProtocol::File && !xOldObj->IsDocShared() );
+
+ // Empty existing SfxMDIFrames for this Document
+ // in native format or R/O, open it now for editing?
+ SfxObjectShellLock xNewObj;
+
+ // collect the views of the document
+ // TODO: when UNO ViewFactories are available for SFX-based documents, the below code should
+ // be UNOized, too
+ typedef ::std::pair< Reference< XFrame >, SfxInterfaceId > ViewDescriptor;
+ ::std::vector< ViewDescriptor > aViewFrames;
+ SfxViewFrame *pView = GetFirst( xOldObj );
+ while ( pView )
+ {
+ Reference< XFrame > xFrame( pView->GetFrame().GetFrameInterface() );
+ SAL_WARN_IF( !xFrame.is(), "sfx.view", "SfxViewFrame::ExecReload_Impl: no XFrame?!");
+ aViewFrames.emplace_back( xFrame, pView->GetCurViewId() );
+
+ pView = GetNext( *pView, xOldObj );
+ }
+
+ xOldObj->Get_Impl()->pReloadTimer.reset();
+
+ std::optional<SfxAllItemSet> pNewSet;
+ std::shared_ptr<const SfxFilter> pFilter = pMedium->GetFilter();
+ if( pURLItem )
+ {
+ pNewSet.emplace( pApp->GetPool() );
+ pNewSet->Put( *pURLItem );
+
+ // Filter Detection
+ OUString referer;
+ const SfxStringItem* refererItem = rReq.GetArg<SfxStringItem>(SID_REFERER);
+ if (refererItem != nullptr) {
+ referer = refererItem->GetValue();
+ }
+ SfxMedium aMedium( pURLItem->GetValue(), referer, SFX_STREAM_READWRITE );
+ SfxFilterMatcher().GuessFilter( aMedium, pFilter );
+ if ( pFilter )
+ pNewSet->Put( SfxStringItem( SID_FILTER_NAME, pFilter->GetName() ) );
+ pNewSet->Put( *aMedium.GetItemSet() );
+ }
+ else
+ {
+ pNewSet.emplace( *pMedium->GetItemSet() );
+ pNewSet->ClearItem( SID_VIEW_ID );
+ pNewSet->ClearItem( SID_STREAM );
+ pNewSet->ClearItem( SID_INPUTSTREAM );
+ pNewSet->Put( SfxStringItem( SID_FILTER_NAME, pMedium->GetFilter()->GetName() ) );
+
+ // let the current security settings be checked again
+ pNewSet->Put( SfxUInt16Item( SID_MACROEXECMODE, document::MacroExecMode::USE_CONFIG ) );
+
+ if ( pSh->IsOriginallyReadOnlyMedium()
+ || pSh->IsOriginallyLoadedReadOnlyMedium() )
+ // edit mode is switched or reload of readonly document
+ pNewSet->Put( SfxBoolItem( SID_DOC_READONLY, true ) );
+ else
+ // Reload of file opened for writing
+ pNewSet->ClearItem( SID_DOC_READONLY );
+ }
+
+ // If a salvaged file is present, do not enclose the OrigURL
+ // again, since the Template is invalid after reload.
+ const SfxStringItem* pSalvageItem = SfxItemSet::GetItem<SfxStringItem>(&*pNewSet, SID_DOC_SALVAGE, false);
+ if( pSalvageItem )
+ {
+ pNewSet->ClearItem( SID_DOC_SALVAGE );
+ }
+
+#if HAVE_FEATURE_MULTIUSER_ENVIRONMENT
+ // TODO/LATER: Temporary solution, the SfxMedium must know the original URL as aLogicName
+ // SfxMedium::Transfer_Impl() will be forbidden then.
+ if ( xOldObj->IsDocShared() )
+ pNewSet->Put( SfxStringItem( SID_FILE_NAME, xOldObj->GetSharedFileURL() ) );
+#endif
+ if ( pURLItem )
+ pNewSet->Put( SfxStringItem( SID_REFERER, pMedium->GetName() ) );
+ else
+ pNewSet->Put( SfxStringItem( SID_REFERER, OUString() ) );
+
+ xOldObj->CancelTransfers();
+
+
+ if ( pSilentItem && pSilentItem->GetValue() )
+ pNewSet->Put( SfxBoolItem( SID_SILENT, true ) );
+
+ const SfxUnoAnyItem* pInteractionItem = SfxItemSet::GetItem<SfxUnoAnyItem>(&*pNewSet, SID_INTERACTIONHANDLER, false);
+ const SfxUInt16Item* pMacroExecItem = SfxItemSet::GetItem<SfxUInt16Item>(&*pNewSet, SID_MACROEXECMODE, false);
+ const SfxUInt16Item* pDocTemplateItem = SfxItemSet::GetItem<SfxUInt16Item>(&*pNewSet, SID_UPDATEDOCMODE, false);
+
+ if (!pInteractionItem)
+ {
+ Reference < task::XInteractionHandler2 > xHdl = task::InteractionHandler::createWithParent( ::comphelper::getProcessComponentContext(), nullptr );
+ if (xHdl.is())
+ pNewSet->Put( SfxUnoAnyItem(SID_INTERACTIONHANDLER,css::uno::Any(xHdl)) );
+ }
+
+ if (!pMacroExecItem)
+ pNewSet->Put( SfxUInt16Item(SID_MACROEXECMODE,css::document::MacroExecMode::USE_CONFIG) );
+ if (!pDocTemplateItem)
+ pNewSet->Put( SfxUInt16Item(SID_UPDATEDOCMODE,css::document::UpdateDocMode::ACCORDING_TO_CONFIG) );
+
+ xOldObj->SetModified( false );
+ // Do not cache the old Document! Is invalid when loading
+ // another document.
+
+ bool bHasStorage = pMedium->HasStorage_Impl();
+ if( bHandsOff )
+ {
+ if ( bHasStorage && pMedium->GetStorage() == xOldObj->GetStorage() )
+ {
+ // TODO/LATER: faster creation of copy
+ if ( !xOldObj->ConnectTmpStorage_Impl( pMedium->GetStorage(), pMedium ) )
+ return;
+ }
+
+ pMedium->CloseAndRelease();
+ }
+
+ xNewObj = SfxObjectShell::CreateObject( pFilter->GetServiceName() );
+
+ if ( xOldObj->IsModifyPasswordEntered() )
+ xNewObj->SetModifyPasswordEntered();
+
+ uno::Sequence < beans::PropertyValue > aLoadArgs;
+ TransformItems( SID_OPENDOC, *pNewSet, aLoadArgs );
+ try
+ {
+ uno::Reference < frame::XLoadable > xLoad( xNewObj->GetModel(), uno::UNO_QUERY );
+ xLoad->load( aLoadArgs );
+ }
+ catch ( uno::Exception& )
+ {
+ xNewObj->DoClose();
+ xNewObj = nullptr;
+ pMedium->AddToCheckEditableWorkerList();
+ }
+
+ pNewSet.reset();
+
+ if( !xNewObj.Is() )
+ {
+ if( bHandsOff )
+ {
+ // back to old medium
+ pMedium->ReOpen();
+ pMedium->LockOrigFileOnDemand( false, true );
+
+ xOldObj->DoSaveCompleted( pMedium );
+ }
+ }
+ else
+ {
+ if ( xNewObj->GetModifyPasswordHash() && xNewObj->GetModifyPasswordHash() != xOldObj->GetModifyPasswordHash() )
+ {
+ xNewObj->SetModifyPasswordEntered( false );
+ xNewObj->SetReadOnly();
+ }
+ else if ( rReq.GetSlot() == SID_EDITDOC || rReq.GetSlot() == SID_READONLYDOC )
+ {
+ xNewObj->SetReadOnlyUI( !bForEdit );
+ }
+
+#if HAVE_FEATURE_MULTIUSER_ENVIRONMENT
+ if ( xNewObj->IsDocShared() )
+ {
+ // the file is shared but the closing can change the sharing control file
+ xOldObj->DoNotCleanShareControlFile();
+ }
+#endif
+ // the Reload and Silent items were only temporary, remove them
+ xNewObj->GetMedium()->GetItemSet()->ClearItem( SID_RELOAD );
+ xNewObj->GetMedium()->GetItemSet()->ClearItem( SID_SILENT );
+ TransformItems( SID_OPENDOC, *xNewObj->GetMedium()->GetItemSet(), aLoadArgs );
+
+ UpdateDocument_Impl();
+
+ if (vcl::CommandInfoProvider::GetModuleIdentifier(GetFrame().GetFrameInterface()) == "com.sun.star.text.TextDocument")
+ sfx2::SfxNotebookBar::ReloadNotebookBar(u"modules/swriter/ui/");
+
+ try
+ {
+ for (auto const& viewFrame : aViewFrames)
+ {
+ LoadViewIntoFrame_Impl( *xNewObj, viewFrame.first, aLoadArgs, viewFrame.second, false );
+ }
+ aViewFrames.clear();
+ }
+ catch( const Exception& )
+ {
+ // close the remaining frames
+ // Don't catch exceptions herein, if this fails, then we're left in an indetermined state, and
+ // crashing is better than trying to proceed
+ for (auto const& viewFrame : aViewFrames)
+ {
+ Reference< util::XCloseable > xClose( viewFrame.first, UNO_QUERY_THROW );
+ xClose->close( true );
+ }
+ aViewFrames.clear();
+ }
+
+ const SfxInt32Item* pPageNumber = rReq.GetArg<SfxInt32Item>(SID_PAGE_NUMBER);
+ if (pPageNumber && pPageNumber->GetValue() >= 0)
+ {
+ // Restore current page after reload.
+ uno::Reference<drawing::XDrawView> xController(
+ xNewObj->GetModel()->getCurrentController(), uno::UNO_QUERY);
+ uno::Reference<drawing::XDrawPagesSupplier> xSupplier(xNewObj->GetModel(),
+ uno::UNO_QUERY);
+ uno::Reference<drawing::XDrawPages> xDrawPages = xSupplier->getDrawPages();
+ uno::Reference<drawing::XDrawPage> xDrawPage(
+ xDrawPages->getByIndex(pPageNumber->GetValue()), uno::UNO_QUERY);
+ xController->setCurrentPage(xDrawPage);
+ }
+
+ // Propagate document closure.
+ SfxGetpApp()->NotifyEvent( SfxEventHint( SfxEventHintId::CloseDoc, GlobalEventConfig::GetEventName( GlobalEventId::CLOSEDOC ), xOldObj ) );
+ }
+
+ // Record as done
+ rReq.Done( true );
+ rReq.SetReturnValue(SfxBoolItem(rReq.GetSlot(), true));
+ return;
+ }
+ else
+ {
+ // Record as not done
+ rReq.Done();
+ rReq.SetReturnValue(SfxBoolItem(rReq.GetSlot(), false));
+ m_pImpl->bReloading = false;
+ return;
+ }
+ }
+ }
+}
+
+void SfxViewFrame::StateReload_Impl( SfxItemSet& rSet )
+{
+ SfxObjectShell* pSh = GetObjectShell();
+ if ( !pSh )
+ {
+ // I'm just on reload and am yielding myself ...
+ return;
+ }
+
+ SfxWhichIter aIter( rSet );
+ for ( sal_uInt16 nWhich = aIter.FirstWhich(); nWhich; nWhich = aIter.NextWhich() )
+ {
+ switch ( nWhich )
+ {
+ case SID_EDITDOC:
+ case SID_READONLYDOC:
+ {
+ const SfxViewShell *pVSh;
+ const SfxShell *pFSh;
+ if ( !pSh->HasName() ||
+ !( pSh->Get_Impl()->nLoadedFlags & SfxLoadedFlags::MAINDOCUMENT ) ||
+ (pSh->isEditDocLocked()) ||
+ ( pSh->GetCreateMode() == SfxObjectCreateMode::EMBEDDED &&
+ ( !(pVSh = pSh->GetViewShell()) ||
+ !(pFSh = pVSh->GetFormShell()) ||
+ !pFSh->IsDesignMode())))
+ rSet.DisableItem( nWhich );
+ else
+ {
+ const SfxBoolItem* pItem = SfxItemSet::GetItem<SfxBoolItem>(pSh->GetMedium()->GetItemSet(), SID_EDITDOC, false);
+ if ( pItem && !pItem->GetValue() )
+ rSet.DisableItem( nWhich );
+ else
+ {
+ if (nWhich==SID_EDITDOC)
+ rSet.Put( SfxBoolItem( nWhich, !pSh->IsReadOnly() ) );
+ else if (nWhich==SID_READONLYDOC)
+ rSet.Put( SfxBoolItem( nWhich, pSh->IsReadOnly() ) );
+ }
+ }
+ break;
+ }
+
+ case SID_RELOAD:
+ {
+ if ( !pSh->CanReload_Impl() || pSh->GetCreateMode() == SfxObjectCreateMode::EMBEDDED )
+ rSet.DisableItem(nWhich);
+ else
+ {
+ // If any ChildFrame is reloadable, the slot is enabled,
+ // so you can perform CTRL-Reload
+ rSet.Put( SfxBoolItem( nWhich, false));
+ }
+
+ break;
+ }
+ }
+ }
+}
+
+void SfxViewFrame::ExecHistory_Impl( SfxRequest &rReq )
+{
+ // Is there an Undo-Manager on the top Shell?
+ SfxShell *pSh = GetDispatcher()->GetShell(0);
+ SfxUndoManager* pShUndoMgr = pSh->GetUndoManager();
+ bool bOK = false;
+ if ( pShUndoMgr )
+ {
+ switch ( rReq.GetSlot() )
+ {
+ case SID_CLEARHISTORY:
+ pShUndoMgr->Clear();
+ bOK = true;
+ break;
+
+ case SID_UNDO:
+ pShUndoMgr->Undo();
+ GetBindings().InvalidateAll(false);
+ bOK = true;
+ break;
+
+ case SID_REDO:
+ pShUndoMgr->Redo();
+ GetBindings().InvalidateAll(false);
+ bOK = true;
+ break;
+
+ case SID_REPEAT:
+ if ( pSh->GetRepeatTarget() )
+ pShUndoMgr->Repeat( *pSh->GetRepeatTarget() );
+ bOK = true;
+ break;
+ }
+ }
+ else if ( GetViewShell() )
+ {
+ // The SW has its own undo in the View
+ const SfxPoolItem *pRet = GetViewShell()->ExecuteSlot( rReq );
+ if ( pRet )
+ bOK = static_cast<const SfxBoolItem*>(pRet)->GetValue();
+ }
+
+ rReq.SetReturnValue( SfxBoolItem( rReq.GetSlot(), bOK ) );
+ rReq.Done();
+}
+
+void SfxViewFrame::StateHistory_Impl( SfxItemSet &rSet )
+{
+ // Search for Undo-Manager
+ SfxShell *pSh = GetDispatcher()->GetShell(0);
+ if ( !pSh )
+ // I'm just on reload and am yielding myself ...
+ return;
+
+ SfxUndoManager *pShUndoMgr = pSh->GetUndoManager();
+ if ( !pShUndoMgr )
+ {
+ // The SW has its own undo in the View
+ SfxWhichIter aIter( rSet );
+ SfxViewShell *pViewSh = GetViewShell();
+ if( !pViewSh ) return;
+ for ( sal_uInt16 nSID = aIter.FirstWhich(); nSID; nSID = aIter.NextWhich() )
+ pViewSh->GetSlotState( nSID, nullptr, &rSet );
+ return;
+ }
+
+ if ( pShUndoMgr->GetUndoActionCount() == 0 &&
+ pShUndoMgr->GetRedoActionCount() == 0 &&
+ pShUndoMgr->GetRepeatActionCount() == 0 )
+ rSet.DisableItem( SID_CLEARHISTORY );
+
+ if (pShUndoMgr->GetUndoActionCount())
+ {
+ const SfxUndoAction* pAction = pShUndoMgr->GetUndoAction();
+ SfxViewShell *pViewSh = GetViewShell();
+ if (pViewSh && pAction->GetViewShellId() != pViewSh->GetViewShellId())
+ {
+ rSet.Put(SfxUInt32Item(SID_UNDO, static_cast<sal_uInt32>(SID_REPAIRPACKAGE)));
+ }
+ else
+ {
+ rSet.Put( SfxStringItem( SID_UNDO, SvtResId(STR_UNDO)+pShUndoMgr->GetUndoActionComment() ) );
+ }
+ }
+ else
+ rSet.DisableItem( SID_UNDO );
+
+ if (pShUndoMgr->GetRedoActionCount())
+ {
+ const SfxUndoAction* pAction = pShUndoMgr->GetRedoAction();
+ SfxViewShell *pViewSh = GetViewShell();
+ if (pViewSh && pAction->GetViewShellId() != pViewSh->GetViewShellId())
+ {
+ rSet.Put(SfxUInt32Item(SID_REDO, static_cast<sal_uInt32>(SID_REPAIRPACKAGE)));
+ }
+ else
+ {
+ rSet.Put(SfxStringItem(SID_REDO, SvtResId(STR_REDO) + pShUndoMgr->GetRedoActionComment()));
+ }
+ }
+ else
+ rSet.DisableItem( SID_REDO );
+
+ SfxRepeatTarget *pTarget = pSh->GetRepeatTarget();
+ if (pTarget && pShUndoMgr->GetRepeatActionCount() && pShUndoMgr->CanRepeat(*pTarget))
+ rSet.Put( SfxStringItem( SID_REPEAT, SvtResId(STR_REPEAT)+pShUndoMgr->GetRepeatActionComment(*pTarget) ) );
+ else
+ rSet.DisableItem( SID_REPEAT );
+}
+
+void SfxViewFrame::PopShellAndSubShells_Impl( SfxViewShell& i_rViewShell )
+{
+ i_rViewShell.PopSubShells_Impl();
+ sal_uInt16 nLevel = m_pDispatcher->GetShellLevel( i_rViewShell );
+ if ( nLevel != USHRT_MAX )
+ {
+ if ( nLevel )
+ {
+ // more sub shells on the stack, which were not affected by PopSubShells_Impl
+ SfxShell *pSubShell = m_pDispatcher->GetShell( nLevel-1 );
+ m_pDispatcher->Pop( *pSubShell, SfxDispatcherPopFlags::POP_UNTIL | SfxDispatcherPopFlags::POP_DELETE );
+ }
+ m_pDispatcher->Pop( i_rViewShell );
+ m_pDispatcher->Flush();
+ }
+
+}
+
+/* [Description]
+
+ This method empties the SfxViewFrame, i.e. takes the <SfxObjectShell>
+ from the dispatcher and ends its <SfxListener> Relationship to this
+ SfxObjectShell (by which they may even destroy themselves).
+
+ Thus, by invoking ReleaseObjectShell() and SetObjectShell() the
+ SfxObjectShell can be replaced.
+
+ Between ReleaseObjectShell() and SetObjectShell() the control cannot
+ be handed over to the system.
+
+ [Cross-reference]
+
+ <SfxViewFrame::SetObjectShell(SfxObjectShell&)>
+*/
+void SfxViewFrame::ReleaseObjectShell_Impl()
+{
+ DBG_ASSERT( m_xObjSh.is(), "no SfxObjectShell to release!" );
+
+ GetFrame().ReleasingComponent_Impl();
+ if ( GetWindow().HasChildPathFocus( true ) )
+ {
+ GetWindow().GrabFocus();
+ }
+
+ SfxViewShell *pDyingViewSh = GetViewShell();
+ if ( pDyingViewSh )
+ {
+ PopShellAndSubShells_Impl( *pDyingViewSh );
+ pDyingViewSh->DisconnectAllClients();
+ SetViewShell_Impl(nullptr);
+ delete pDyingViewSh;
+ }
+#ifdef DBG_UTIL
+ else
+ OSL_FAIL("No Shell");
+#endif
+
+ if ( m_xObjSh.is() )
+ {
+ m_pDispatcher->Pop( *m_xObjSh );
+ SfxModule* pModule = m_xObjSh->GetModule();
+ if( pModule )
+ m_pDispatcher->RemoveShell_Impl( *pModule );
+ m_pDispatcher->Flush();
+ EndListening( *m_xObjSh );
+
+ Notify( *m_xObjSh, SfxHint(SfxHintId::TitleChanged) );
+ Notify( *m_xObjSh, SfxHint(SfxHintId::DocChanged) );
+
+ if ( 1 == m_xObjSh->GetOwnerLockCount() && m_pImpl->bObjLocked && m_xObjSh->GetCreateMode() == SfxObjectCreateMode::EMBEDDED )
+ m_xObjSh->DoClose();
+ SfxObjectShellRef xDyingObjSh = m_xObjSh;
+ m_xObjSh.clear();
+ if( GetFrame().GetHasTitle() && m_pImpl->nDocViewNo )
+ xDyingObjSh->GetNoSet_Impl().ReleaseIndex(m_pImpl->nDocViewNo-1);
+ if ( m_pImpl->bObjLocked )
+ {
+ xDyingObjSh->OwnerLock( false );
+ m_pImpl->bObjLocked = false;
+ }
+ }
+
+ GetDispatcher()->SetDisableFlags( SfxDisableFlags::NONE );
+}
+
+void SfxViewFrame::Close()
+{
+
+ DBG_ASSERT( GetFrame().IsClosing_Impl() || !GetFrame().GetFrameInterface().is(), "ViewFrame closed too early!" );
+
+ // If no saving have been made up until now, then embedded Objects should
+ // not be saved automatically anymore.
+ if ( GetViewShell() )
+ GetViewShell()->DisconnectAllClients();
+ Broadcast( SfxHint( SfxHintId::Dying ) );
+
+ if (SfxViewFrame::Current() == this)
+ SfxViewFrame::SetViewFrame( nullptr );
+
+ // Since the Dispatcher is emptied, it can not be used in any reasonable
+ // manner, thus it is better to let the dispatcher be.
+ GetDispatcher()->Lock(true);
+ delete this;
+}
+
+void SfxViewFrame::DoActivate( bool bUI )
+{
+ m_pDispatcher->DoActivate_Impl( bUI );
+}
+
+void SfxViewFrame::DoDeactivate(bool bUI, SfxViewFrame const * pNewFrame )
+{
+ m_pDispatcher->DoDeactivate_Impl( bUI, pNewFrame );
+}
+
+void SfxViewFrame::InvalidateBorderImpl( const SfxViewShell* pSh )
+{
+ if( !pSh || m_nAdjustPosPixelLock )
+ return;
+
+ if ( GetViewShell() && GetWindow().IsVisible() )
+ {
+ if ( GetFrame().IsInPlace() )
+ {
+ return;
+ }
+
+ DoAdjustPosSizePixel( GetViewShell(), Point(),
+ GetWindow().GetOutputSizePixel(),
+ false );
+ }
+}
+
+void SfxViewFrame::SetBorderPixelImpl
+(
+ const SfxViewShell* pVSh,
+ const SvBorder& rBorder
+)
+
+{
+ m_pImpl->aBorder = rBorder;
+
+ if ( m_pImpl->bResizeInToOut && !GetFrame().IsInPlace() )
+ {
+ Size aSize = pVSh->GetWindow()->GetOutputSizePixel();
+ if ( aSize.Width() && aSize.Height() )
+ {
+ aSize.AdjustWidth(rBorder.Left() + rBorder.Right() );
+ aSize.AdjustHeight(rBorder.Top() + rBorder.Bottom() );
+
+ Size aOldSize = GetWindow().GetOutputSizePixel();
+ GetWindow().SetOutputSizePixel( aSize );
+ vcl::Window* pParent = &GetWindow();
+ while ( pParent->GetParent() )
+ pParent = pParent->GetParent();
+ Size aOuterSize = pParent->GetOutputSizePixel();
+ aOuterSize.AdjustWidth( aSize.Width() - aOldSize.Width() );
+ aOuterSize.AdjustHeight( aSize.Height() - aOldSize.Height() );
+ pParent->SetOutputSizePixel( aOuterSize );
+ }
+ }
+ else
+ {
+ tools::Rectangle aEditArea( Point(), GetWindow().GetOutputSizePixel() );
+ aEditArea.AdjustLeft(rBorder.Left() );
+ aEditArea.AdjustRight( -(rBorder.Right()) );
+ aEditArea.AdjustTop(rBorder.Top() );
+ aEditArea.AdjustBottom( -(rBorder.Bottom()) );
+ pVSh->GetWindow()->SetPosSizePixel( aEditArea.TopLeft(), aEditArea.GetSize() );
+ }
+}
+
+const SvBorder& SfxViewFrame::GetBorderPixelImpl() const
+{
+ return m_pImpl->aBorder;
+}
+
+void SfxViewFrame::AppendReadOnlyInfobar()
+{
+ bool bSignPDF = m_xObjSh->IsSignPDF();
+ bool bSignWithCert = false;
+ if (bSignPDF)
+ {
+ SfxObjectShell* pObjectShell = GetObjectShell();
+ uno::Reference<security::XCertificate> xCertificate = pObjectShell->GetSignPDFCertificate();
+ bSignWithCert = xCertificate.is();
+ }
+
+ auto pInfoBar = AppendInfoBar("readonly", "",
+ SfxResId(bSignPDF ? STR_READONLY_PDF : STR_READONLY_DOCUMENT),
+ InfobarType::INFO);
+ if (!pInfoBar)
+ return;
+
+ if (bSignPDF)
+ {
+ // SID_SIGNPDF opened a read-write PDF
+ // read-only for signing purposes.
+ weld::Button& rSignButton = pInfoBar->addButton();
+ if (bSignWithCert)
+ {
+ rSignButton.set_label(SfxResId(STR_READONLY_FINISH_SIGN));
+ }
+ else
+ {
+ rSignButton.set_label(SfxResId(STR_READONLY_SIGN));
+ }
+
+ rSignButton.connect_clicked(LINK(this, SfxViewFrame, SignDocumentHandler));
+ }
+
+ bool showEditDocumentButton = true;
+ if (m_xObjSh->isEditDocLocked())
+ showEditDocumentButton = false;
+
+ if (showEditDocumentButton)
+ {
+ weld::Button& rBtn = pInfoBar->addButton();
+ rBtn.set_label(SfxResId(STR_READONLY_EDIT));
+ rBtn.connect_clicked(LINK(this, SfxViewFrame, SwitchReadOnlyHandler));
+ }
+}
+
+namespace
+{
+css::uno::Reference<css::frame::XLayoutManager> getLayoutManager(const SfxFrame& rFrame)
+{
+ css::uno::Reference<css::frame::XLayoutManager> xLayoutManager;
+ css::uno::Reference<css::beans::XPropertySet> xPropSet(rFrame.GetFrameInterface(),
+ uno::UNO_QUERY);
+ if (xPropSet.is())
+ {
+ try
+ {
+ xLayoutManager.set(xPropSet->getPropertyValue("LayoutManager"), uno::UNO_QUERY);
+ }
+ catch (const Exception& e)
+ {
+ SAL_WARN("sfx.view", "Failure getting layout manager: " + e.Message);
+ }
+ }
+ return xLayoutManager;
+}
+}
+
+bool SfxApplication::IsHeadlessOrUITest()
+{
+ if (Application::IsHeadlessModeEnabled())
+ return true;
+
+ bool bIsUITest = false; //uitest.uicheck fails when the dialog is open
+ for (sal_uInt16 i = 0, nCount = Application::GetCommandLineParamCount(); i < nCount; ++i)
+ {
+ if (Application::GetCommandLineParam(i) == "--nologo")
+ {
+ bIsUITest = true;
+ break;
+ }
+ }
+ return bIsUITest;
+}
+
+bool SfxApplication::IsTipOfTheDayDue()
+{
+ const bool bShowTipOfTheDay = officecfg::Office::Common::Misc::ShowTipOfTheDay::get();
+ if (!bShowTipOfTheDay)
+ return false;
+
+ const auto t0 = std::chrono::system_clock::now().time_since_epoch();
+
+ // show tip-of-the-day dialog ?
+ const sal_Int32 nLastTipOfTheDay = officecfg::Office::Common::Misc::LastTipOfTheDayShown::get();
+ const sal_Int32 nDay = std::chrono::duration_cast<std::chrono::hours>(t0).count()/24; // days since 1970-01-01
+ return nDay - nLastTipOfTheDay > 0; //only once per day
+}
+
+void SfxViewFrame::Notify( SfxBroadcaster& /*rBC*/, const SfxHint& rHint )
+{
+ if(m_pImpl->bIsDowning)
+ return;
+
+ // we know only SfxEventHint or simple SfxHint
+ if (const SfxEventHint* pEventHint = dynamic_cast<const SfxEventHint*>(&rHint))
+ {
+ // When the Document is loaded asynchronously, was the Dispatcher
+ // set as ReadOnly, to what must be returned when the document itself
+ // is not read only, and the loading is finished.
+ switch ( pEventHint->GetEventId() )
+ {
+ case SfxEventHintId::ModifyChanged:
+ {
+ SfxBindings& rBind = GetBindings();
+ rBind.Invalidate( SID_DOC_MODIFIED );
+ rBind.Invalidate( SID_RELOAD );
+ rBind.Invalidate( SID_EDITDOC );
+ break;
+ }
+
+ case SfxEventHintId::OpenDoc:
+ case SfxEventHintId::CreateDoc:
+ {
+ if ( !m_xObjSh.is() )
+ break;
+
+ SfxBindings& rBind = GetBindings();
+ rBind.Invalidate( SID_RELOAD );
+ rBind.Invalidate( SID_EDITDOC );
+
+#if !ENABLE_WASM_STRIP_PINGUSER
+ bool bIsHeadlessOrUITest = SfxApplication::IsHeadlessOrUITest(); //uitest.uicheck fails when the dialog is open
+
+ //what's new infobar
+ if (utl::isProductVersionUpgraded(true) && !bIsHeadlessOrUITest)
+ {
+ VclPtr<SfxInfoBarWindow> pInfoBar = AppendInfoBar("whatsnew", "", SfxResId(STR_WHATSNEW_TEXT), InfobarType::INFO);
+ if (pInfoBar)
+ {
+ weld::Button& rWhatsNewButton = pInfoBar->addButton();
+ rWhatsNewButton.set_label(SfxResId(STR_WHATSNEW_BUTTON));
+ rWhatsNewButton.connect_clicked(LINK(this, SfxViewFrame, WhatsNewHandler));
+ }
+ }
+
+ // show tip-of-the-day dialog if it due, but not if there is the impress modal template dialog
+ // open where SdModule::ExecuteNewDocument will launch it instead when that dialog is dismissed
+ if (SfxApplication::IsTipOfTheDayDue() && !bIsHeadlessOrUITest && !IsInModalMode())
+ {
+ // tdf#127946 pass in argument for dialog parent
+ SfxUnoFrameItem aDocFrame(SID_FILLFRAME, GetFrame().GetFrameInterface());
+ GetDispatcher()->ExecuteList(SID_TIPOFTHEDAY, SfxCallMode::SLOT, {}, { &aDocFrame });
+ }
+
+ // inform about the community involvement
+ const auto t0 = std::chrono::system_clock::now().time_since_epoch();
+ const sal_Int64 nLastGetInvolvedShown = officecfg::Setup::Product::LastTimeGetInvolvedShown::get();
+ const sal_Int64 nNow = std::chrono::duration_cast<std::chrono::seconds>(t0).count();
+ const sal_Int64 nPeriodSec(60 * 60 * 24 * 180); // 180 days in seconds
+ bool bUpdateLastTimeGetInvolvedShown = false;
+
+ if (nLastGetInvolvedShown == 0)
+ bUpdateLastTimeGetInvolvedShown = true;
+ else if (nPeriodSec < nNow && nLastGetInvolvedShown < (nNow + nPeriodSec/2) - nPeriodSec) // 90d alternating with donation
+ {
+ bUpdateLastTimeGetInvolvedShown = true;
+
+ VclPtr<SfxInfoBarWindow> pInfoBar = AppendInfoBar("getinvolved", "", SfxResId(STR_GET_INVOLVED_TEXT), InfobarType::INFO);
+
+ if (pInfoBar)
+ {
+ weld::Button& rGetInvolvedButton = pInfoBar->addButton();
+ rGetInvolvedButton.set_label(SfxResId(STR_GET_INVOLVED_BUTTON));
+ rGetInvolvedButton.connect_clicked(LINK(this, SfxViewFrame, GetInvolvedHandler));
+ }
+ }
+
+ if (bUpdateLastTimeGetInvolvedShown
+ && !officecfg::Setup::Product::LastTimeGetInvolvedShown::isReadOnly())
+ {
+ std::shared_ptr<comphelper::ConfigurationChanges> batch(comphelper::ConfigurationChanges::create());
+ officecfg::Setup::Product::LastTimeGetInvolvedShown::set(nNow, batch);
+ batch->commit();
+ }
+
+ // inform about donations
+ const sal_Int64 nLastDonateShown = officecfg::Setup::Product::LastTimeDonateShown::get();
+ bool bUpdateLastTimeDonateShown = false;
+
+ if (nLastDonateShown == 0)
+ bUpdateLastTimeDonateShown = true;
+ else if (nPeriodSec < nNow && nLastDonateShown < nNow - nPeriodSec) // 90d alternating with getinvolved
+ {
+ bUpdateLastTimeDonateShown = true;
+
+ VclPtr<SfxInfoBarWindow> pInfoBar = AppendInfoBar("donate", "", SfxResId(STR_DONATE_TEXT), InfobarType::INFO);
+ if (pInfoBar)
+ {
+ weld::Button& rDonateButton = pInfoBar->addButton();
+ rDonateButton.set_label(SfxResId(STR_DONATE_BUTTON));
+ rDonateButton.connect_clicked(LINK(this, SfxViewFrame, DonationHandler));
+ }
+ }
+
+ if (bUpdateLastTimeDonateShown
+ && !officecfg::Setup::Product::LastTimeDonateShown::isReadOnly())
+ {
+ std::shared_ptr<comphelper::ConfigurationChanges> batch(comphelper::ConfigurationChanges::create());
+ officecfg::Setup::Product::LastTimeDonateShown::set(nNow, batch);
+ batch->commit();
+ }
+#endif
+ if (officecfg::Office::Common::Passwords::HasMaster::get() &&
+ officecfg::Office::Common::Passwords::StorageVersion::get() == 0)
+ {
+ // master password stored in deprecated format
+ VclPtr<SfxInfoBarWindow> pOldMasterPasswordInfoBar =
+ AppendInfoBar("oldmasterpassword", "",
+ SfxResId(STR_REFRESH_MASTER_PASSWORD), InfobarType::DANGER, false);
+ if (pOldMasterPasswordInfoBar)
+ {
+ weld::Button& rButton = pOldMasterPasswordInfoBar->addButton();
+ rButton.set_label(SfxResId(STR_REFRESH_PASSWORD));
+ rButton.connect_clicked(LINK(this,
+ SfxViewFrame, RefreshMasterPasswordHdl));
+ }
+ }
+
+ // read-only infobar if necessary
+ const SfxViewShell *pVSh;
+ const SfxShell *pFSh;
+ if ( m_xObjSh->IsReadOnly() &&
+ ! m_xObjSh->IsSecurityOptOpenReadOnly() &&
+ ( m_xObjSh->GetCreateMode() != SfxObjectCreateMode::EMBEDDED ||
+ (( pVSh = m_xObjSh->GetViewShell()) && (pFSh = pVSh->GetFormShell()) && pFSh->IsDesignMode())))
+ {
+ AppendReadOnlyInfobar();
+ }
+
+ if (vcl::CommandInfoProvider::GetModuleIdentifier(GetFrame().GetFrameInterface()) == "com.sun.star.text.TextDocument")
+ sfx2::SfxNotebookBar::ReloadNotebookBar(u"modules/swriter/ui/");
+
+ if (SfxClassificationHelper::IsClassified(m_xObjSh->getDocProperties()))
+ {
+ // Document has BAILS properties, display an infobar accordingly.
+ SfxClassificationHelper aHelper(m_xObjSh->getDocProperties());
+ aHelper.UpdateInfobar(*this);
+ }
+
+ // Add pending infobars
+ std::vector<InfobarData>& aPendingInfobars = m_xObjSh->getPendingInfobars();
+ while (!aPendingInfobars.empty())
+ {
+ InfobarData& aInfobarData = aPendingInfobars.back();
+
+ // don't show Track Changes infobar, if Track Changes toolbar is visible
+ if (aInfobarData.msId == "hiddentrackchanges")
+ {
+ if (auto xLayoutManager = getLayoutManager(GetFrame()))
+ {
+ if ( xLayoutManager->getElement(CHANGES_STR).is() )
+ {
+ aPendingInfobars.pop_back();
+ continue;
+ }
+ }
+ }
+
+ // Track Changes infobar: add a button to show/hide Track Changes functions
+ // Hyphenation infobar: add a button to get more information
+ // tdf#148913 limit VclPtr usage for these
+ bool bTrackChanges = aInfobarData.msId == "hiddentrackchanges";
+ if ( bTrackChanges || aInfobarData.msId == "hyphenationmissing" )
+ {
+ VclPtr<SfxInfoBarWindow> pInfoBar =
+ AppendInfoBar(aInfobarData.msId, aInfobarData.msPrimaryMessage,
+ aInfobarData.msSecondaryMessage, aInfobarData.maInfobarType,
+ aInfobarData.mbShowCloseButton);
+
+ // tdf#148913 don't extend this condition to keep it thread-safe
+ if (pInfoBar)
+ {
+ weld::Button& rButton = pInfoBar->addButton();
+ rButton.set_label(SfxResId(bTrackChanges
+ ? STR_TRACK_CHANGES_BUTTON
+ : STR_HYPHENATION_BUTTON));
+ if (bTrackChanges)
+ {
+ rButton.connect_clicked(LINK(this,
+ SfxViewFrame, HiddenTrackChangesHandler));
+ }
+ else
+ {
+ rButton.connect_clicked(LINK(this,
+ SfxViewFrame, HyphenationMissingHandler));
+ }
+ }
+ }
+ else
+ {
+ AppendInfoBar(aInfobarData.msId, aInfobarData.msPrimaryMessage,
+ aInfobarData.msSecondaryMessage, aInfobarData.maInfobarType,
+ aInfobarData.mbShowCloseButton);
+ }
+
+ aPendingInfobars.pop_back();
+ }
+
+ break;
+ }
+ default: break;
+ }
+ }
+ else
+ {
+ switch( rHint.GetId() )
+ {
+ case SfxHintId::ModeChanged:
+ {
+ UpdateTitle();
+
+ if ( !m_xObjSh.is() )
+ break;
+
+ // Switch r/o?
+ SfxBindings& rBind = GetBindings();
+ rBind.Invalidate( SID_RELOAD );
+ SfxDispatcher *pDispat = GetDispatcher();
+ bool bWasReadOnly = pDispat->GetReadOnly_Impl();
+ bool bIsReadOnly = m_xObjSh->IsReadOnly();
+ if ( bWasReadOnly != bIsReadOnly )
+ {
+ // Then also TITLE_CHANGED
+ UpdateTitle();
+ rBind.Invalidate( SID_FILE_NAME );
+ rBind.Invalidate( SID_DOCINFO_TITLE );
+ rBind.Invalidate( SID_EDITDOC );
+
+ pDispat->GetBindings()->InvalidateAll(true);
+ pDispat->SetReadOnly_Impl( bIsReadOnly );
+
+ // Only force and Dispatcher-Update, if it is done next
+ // anyway, otherwise flickering or GPF is possible since
+ // the Writer for example prefers in Resize perform some
+ // actions which has a SetReadOnlyUI in Dispatcher as a
+ // result!
+
+ if ( pDispat->IsUpdated_Impl() )
+ pDispat->Update_Impl(true);
+ }
+
+ Enable( !m_xObjSh->IsInModalMode() );
+ break;
+ }
+
+ case SfxHintId::TitleChanged:
+ {
+ UpdateTitle();
+ SfxBindings& rBind = GetBindings();
+ rBind.Invalidate( SID_FILE_NAME );
+ rBind.Invalidate( SID_DOCINFO_TITLE );
+ rBind.Invalidate( SID_EDITDOC );
+ rBind.Invalidate( SID_RELOAD );
+ break;
+ }
+
+ case SfxHintId::DocumentRepair:
+ {
+ GetBindings().Invalidate( SID_DOC_REPAIR );
+ break;
+ }
+
+ case SfxHintId::Deinitializing:
+ {
+ vcl::Window* pFrameWin = GetWindow().GetFrameWindow();
+ if (pFrameWin && pFrameWin->GetLOKNotifier())
+ pFrameWin->ReleaseLOKNotifier();
+
+ GetFrame().DoClose();
+ break;
+ }
+ case SfxHintId::Dying:
+ // when the Object is being deleted, destroy the view too
+ if ( m_xObjSh.is() )
+ ReleaseObjectShell_Impl();
+ else
+ GetFrame().DoClose();
+ break;
+ default: break;
+ }
+ }
+}
+
+#if !ENABLE_WASM_STRIP_PINGUSER
+IMPL_LINK_NOARG(SfxViewFrame, WhatsNewHandler, weld::Button&, void)
+{
+ GetDispatcher()->Execute(SID_WHATSNEW);
+}
+
+IMPL_LINK_NOARG(SfxViewFrame, GetInvolvedHandler, weld::Button&, void)
+{
+ GetDispatcher()->Execute(SID_GETINVOLVED);
+}
+
+IMPL_LINK_NOARG(SfxViewFrame, DonationHandler, weld::Button&, void)
+{
+ GetDispatcher()->Execute(SID_DONATION);
+}
+#endif
+
+IMPL_LINK(SfxViewFrame, SwitchReadOnlyHandler, weld::Button&, rButton, void)
+{
+ if (m_xObjSh.is() && m_xObjSh->IsSignPDF())
+ {
+ SfxEditDocumentDialog aDialog(&rButton);
+ if (aDialog.run() != RET_OK)
+ return;
+ }
+ GetDispatcher()->Execute(SID_EDITDOC);
+}
+
+IMPL_LINK_NOARG(SfxViewFrame, SignDocumentHandler, weld::Button&, void)
+{
+ GetDispatcher()->Execute(SID_SIGNATURE);
+}
+
+IMPL_LINK(SfxViewFrame, HiddenTrackChangesHandler, weld::Button&, rButton, void)
+{
+ // enable Track Changes toolbar, if it is disabled.
+ // Otherwise disable the toolbar, and close the infobar
+ auto xLayoutManager = getLayoutManager(GetFrame());
+ if (!xLayoutManager)
+ return;
+
+ if (!xLayoutManager->getElement(CHANGES_STR).is())
+ {
+ xLayoutManager->createElement(CHANGES_STR);
+ xLayoutManager->showElement(CHANGES_STR);
+ rButton.set_label(SfxResId(STR_TRACK_CHANGES_BUTTON_HIDE));
+ }
+ else
+ {
+ xLayoutManager->hideElement(CHANGES_STR);
+ xLayoutManager->destroyElement(CHANGES_STR);
+ RemoveInfoBar(u"hiddentrackchanges");
+ }
+}
+
+IMPL_LINK_NOARG(SfxViewFrame, HyphenationMissingHandler, weld::Button&, void)
+{
+ GetDispatcher()->Execute(SID_HYPHENATIONMISSING);
+ RemoveInfoBar(u"hyphenationmissing");
+}
+
+IMPL_LINK_NOARG(SfxViewFrame, RefreshMasterPasswordHdl, weld::Button&, void)
+{
+ bool bChanged = false;
+ try
+ {
+ Reference< task::XPasswordContainer2 > xMasterPasswd(
+ task::PasswordContainer::create(comphelper::getProcessComponentContext()));
+
+ css::uno::Reference<css::frame::XFrame> xFrame = GetFrame().GetFrameInterface();
+ css::uno::Reference<css::awt::XWindow> xContainerWindow = xFrame->getContainerWindow();
+
+ uno::Reference<task::XInteractionHandler> xTmpHandler(task::InteractionHandler::createWithParent(comphelper::getProcessComponentContext(),
+ xContainerWindow));
+ bChanged = xMasterPasswd->changeMasterPassword(xTmpHandler);
+ }
+ catch (const Exception&)
+ {}
+ if (bChanged)
+ RemoveInfoBar(u"oldmasterpassword");
+}
+
+void SfxViewFrame::Construct_Impl( SfxObjectShell *pObjSh )
+{
+ m_pImpl->bResizeInToOut = true;
+ m_pImpl->bObjLocked = false;
+ m_pImpl->nCurViewId = SFX_INTERFACE_NONE;
+ m_pImpl->bReloading = false;
+ m_pImpl->bIsDowning = false;
+ m_pImpl->bModal = false;
+ m_pImpl->bEnabled = true;
+ m_pImpl->nDocViewNo = 0;
+ m_pImpl->aMargin = Size( -1, -1 );
+ m_pImpl->pWindow = nullptr;
+
+ SetPool( &SfxGetpApp()->GetPool() );
+ m_pDispatcher.reset( new SfxDispatcher(this) );
+ if ( !GetBindings().GetDispatcher() )
+ GetBindings().SetDispatcher( m_pDispatcher.get() );
+
+ m_xObjSh = pObjSh;
+ if ( m_xObjSh.is() && m_xObjSh->IsPreview() )
+ GetDispatcher()->SetQuietMode_Impl( true );
+
+ if ( pObjSh )
+ {
+ m_pDispatcher->Push( *SfxGetpApp() );
+ SfxModule* pModule = m_xObjSh->GetModule();
+ if( pModule )
+ m_pDispatcher->Push( *pModule );
+ m_pDispatcher->Push( *this );
+ m_pDispatcher->Push( *pObjSh );
+ m_pDispatcher->Flush();
+ StartListening( *pObjSh );
+ Notify( *pObjSh, SfxHint(SfxHintId::TitleChanged) );
+ Notify( *pObjSh, SfxHint(SfxHintId::DocChanged) );
+ m_pDispatcher->SetReadOnly_Impl( pObjSh->IsReadOnly() );
+ }
+ else
+ {
+ m_pDispatcher->Push( *SfxGetpApp() );
+ m_pDispatcher->Push( *this );
+ m_pDispatcher->Flush();
+ }
+
+ SfxGetpApp()->GetViewFrames_Impl().push_back(this);
+}
+
+/* [Description]
+
+ Constructor of SfxViewFrame for a <SfxObjectShell> from the Resource.
+ The 'nViewId' to the created <SfxViewShell> can be returned.
+ (default is the SfxViewShell-Subclass that was registered first).
+*/
+SfxViewFrame::SfxViewFrame
+(
+ SfxFrame& rFrame,
+ SfxObjectShell* pObjShell
+)
+ : m_pImpl( new SfxViewFrame_Impl( rFrame ) )
+ , m_pBindings( new SfxBindings )
+ , m_pHelpData(CreateSVHelpData())
+ , m_pWinData(CreateSVWinData())
+ , m_nAdjustPosPixelLock( 0 )
+ , m_pCommandPopupHandler(new CommandPopupHandler)
+{
+
+ rFrame.SetCurrentViewFrame_Impl( this );
+ rFrame.SetHasTitle( true );
+ Construct_Impl( pObjShell );
+
+ m_pImpl->pWindow = VclPtr<SfxFrameViewWindow_Impl>::Create( this, rFrame.GetWindow() );
+ m_pImpl->pWindow->SetSizePixel( rFrame.GetWindow().GetOutputSizePixel() );
+ rFrame.SetOwnsBindings_Impl( true );
+ rFrame.CreateWorkWindow_Impl();
+}
+
+SfxViewFrame::~SfxViewFrame()
+{
+ m_pImpl->bIsDowning = true;
+
+ if ( SfxViewFrame::Current() == this )
+ SfxViewFrame::SetViewFrame( nullptr );
+
+ ReleaseObjectShell_Impl();
+
+ if ( GetFrame().OwnsBindings_Impl() )
+ // The Bindings delete the Frame!
+ KillDispatcher_Impl();
+
+ m_pImpl->pWindow.disposeAndClear();
+
+ if ( GetFrame().GetCurrentViewFrame() == this )
+ GetFrame().SetCurrentViewFrame_Impl( nullptr );
+
+ // Unregister from the Frame List.
+ SfxApplication *pSfxApp = SfxApplication::Get();
+ if (pSfxApp)
+ {
+ auto &rFrames = pSfxApp->GetViewFrames_Impl();
+ auto it = std::find( rFrames.begin(), rFrames.end(), this );
+ rFrames.erase( it );
+ }
+
+ // Delete Member
+ KillDispatcher_Impl();
+
+ DestroySVHelpData(m_pHelpData);
+ m_pHelpData = nullptr;
+
+ DestroySVWinData(m_pWinData);
+ m_pWinData = nullptr;
+}
+
+// Remove and delete the Dispatcher.
+void SfxViewFrame::KillDispatcher_Impl()
+{
+
+ SfxModule* pModule = m_xObjSh.is() ? m_xObjSh->GetModule() : nullptr;
+ if ( m_xObjSh.is() )
+ ReleaseObjectShell_Impl();
+ if ( m_pDispatcher )
+ {
+ if( pModule )
+ m_pDispatcher->Pop( *pModule, SfxDispatcherPopFlags::POP_UNTIL );
+ else
+ m_pDispatcher->Pop( *this );
+ m_pDispatcher.reset();
+ }
+}
+
+SfxViewFrame* SfxViewFrame::Current()
+{
+ SfxApplication* pApp = SfxApplication::Get();
+ return pApp ? pApp->Get_Impl()->pViewFrame : nullptr;
+}
+
+// returns the first window of spec. type viewing the specified doc.
+SfxViewFrame* SfxViewFrame::GetFirst
+(
+ const SfxObjectShell* pDoc,
+ bool bOnlyIfVisible
+)
+{
+ SfxApplication *pSfxApp = SfxApplication::Get();
+ if (!pSfxApp)
+ return nullptr;
+
+ // search for a SfxDocument of the specified type
+ for (SfxViewFrame* pFrame : pSfxApp->GetViewFrames_Impl())
+ {
+ if ( ( !pDoc || pDoc == pFrame->GetObjectShell() )
+ && ( !bOnlyIfVisible || pFrame->IsVisible() )
+ )
+ return pFrame;
+ }
+
+ return nullptr;
+}
+
+// returns the next window of spec. type viewing the specified doc.
+SfxViewFrame* SfxViewFrame::GetNext
+(
+ const SfxViewFrame& rPrev,
+ const SfxObjectShell* pDoc,
+ bool bOnlyIfVisible
+)
+{
+ SfxApplication *pSfxApp = SfxApplication::Get();
+ if (!pSfxApp)
+ return nullptr;
+
+ auto &rFrames = pSfxApp->GetViewFrames_Impl();
+
+ // refind the specified predecessor
+ size_t nPos;
+ for ( nPos = 0; nPos < rFrames.size(); ++nPos )
+ if ( rFrames[nPos] == &rPrev )
+ break;
+
+ // search for a Frame of the specified type
+ for ( ++nPos; nPos < rFrames.size(); ++nPos )
+ {
+ SfxViewFrame *pFrame = rFrames[nPos];
+ if ( ( !pDoc || pDoc == pFrame->GetObjectShell() )
+ && ( !bOnlyIfVisible || pFrame->IsVisible() )
+ )
+ return pFrame;
+ }
+ return nullptr;
+}
+
+SfxProgress* SfxViewFrame::GetProgress() const
+{
+ SfxObjectShell *pObjSh = m_xObjSh.get();
+ return pObjSh ? pObjSh->GetProgress() : nullptr;
+}
+
+void SfxViewFrame::DoAdjustPosSizePixel //! divide on Inner.../Outer...
+(
+ SfxViewShell* pSh,
+ const Point& rPos,
+ const Size& rSize,
+ bool inplaceEditModeChange
+)
+{
+
+ // Components do not use this Method!
+ if( pSh && pSh->GetWindow() && !m_nAdjustPosPixelLock )
+ {
+ m_nAdjustPosPixelLock++;
+ if ( m_pImpl->bResizeInToOut )
+ pSh->InnerResizePixel( rPos, rSize, inplaceEditModeChange );
+ else
+ pSh->OuterResizePixel( rPos, rSize );
+ m_nAdjustPosPixelLock--;
+ }
+}
+
+bool SfxViewFrameItem::operator==( const SfxPoolItem &rItem ) const
+{
+ return SfxPoolItem::operator==(rItem) &&
+ static_cast<const SfxViewFrameItem&>(rItem).pFrame == pFrame;
+}
+
+SfxViewFrameItem* SfxViewFrameItem::Clone( SfxItemPool *) const
+{
+ return new SfxViewFrameItem( *this );
+}
+
+void SfxViewFrame::SetViewShell_Impl( SfxViewShell *pVSh )
+/* [Description]
+
+ Internal Method to set the current <SfxViewShell> Instance,
+ that is active int this SfxViewFrame at the moment.
+*/
+{
+ SfxShell::SetViewShell_Impl( pVSh );
+
+ // Hack: InPlaceMode
+ if ( pVSh )
+ m_pImpl->bResizeInToOut = false;
+}
+
+void SfxViewFrame::ForceOuterResize_Impl()
+{
+ m_pImpl->bResizeInToOut = true;
+}
+
+void SfxViewFrame::GetDocNumber_Impl()
+{
+ DBG_ASSERT( GetObjectShell(), "No Document!" );
+ GetObjectShell()->SetNamedVisibility_Impl();
+ m_pImpl->nDocViewNo = GetObjectShell()->GetNoSet_Impl().GetFreeIndex()+1;
+}
+
+void SfxViewFrame::Enable( bool bEnable )
+{
+ if ( bEnable == m_pImpl->bEnabled )
+ return;
+
+ m_pImpl->bEnabled = bEnable;
+
+ vcl::Window *pWindow = &GetFrame().GetWindow();
+ if ( !bEnable )
+ m_pImpl->bWindowWasEnabled = pWindow->IsInputEnabled();
+ if ( !bEnable || m_pImpl->bWindowWasEnabled )
+ pWindow->EnableInput( bEnable );
+
+ // cursor and focus
+ SfxViewShell* pViewSh = GetViewShell();
+ if ( bEnable )
+ {
+ // show cursor
+ if ( pViewSh )
+ pViewSh->ShowCursor();
+ }
+ else
+ {
+ // hide cursor
+ if ( pViewSh )
+ pViewSh->ShowCursor(false);
+ }
+}
+
+/* [Description]
+
+ This method makes the Frame-Window visible and before transmits the
+ window name. In addition, the document is held. In general one can never
+ show the window directly!
+*/
+void SfxViewFrame::Show()
+{
+ // First lock the objectShell so that UpdateTitle() is valid:
+ // IsVisible() == true (:#)
+ if ( m_xObjSh.is() )
+ {
+ m_xObjSh->GetMedium()->GetItemSet()->ClearItem( SID_HIDDEN );
+ if ( !m_pImpl->bObjLocked )
+ LockObjectShell_Impl();
+
+ // Adjust Doc-Shell title number, get unique view-no
+ if ( 0 == m_pImpl->nDocViewNo )
+ {
+ GetDocNumber_Impl();
+ UpdateTitle();
+ }
+ }
+ else
+ UpdateTitle();
+
+ // Display Frame-window, but only if the ViewFrame has no window of its
+ // own or if it does not contain a Component
+ GetWindow().Show();
+ GetFrame().GetWindow().Show();
+}
+
+
+bool SfxViewFrame::IsVisible() const
+{
+ return m_pImpl->bObjLocked;
+}
+
+
+void SfxViewFrame::LockObjectShell_Impl()
+{
+ DBG_ASSERT( !m_pImpl->bObjLocked, "Wrong Locked status!" );
+
+ DBG_ASSERT( GetObjectShell(), "No Document!" );
+ GetObjectShell()->OwnerLock(true);
+ m_pImpl->bObjLocked = true;
+}
+
+
+void SfxViewFrame::MakeActive_Impl( bool bGrabFocus )
+{
+ if ( !GetViewShell() || GetFrame().IsClosing_Impl() )
+ return;
+
+ if ( !IsVisible() )
+ return;
+
+ bool bPreview = false;
+ if (GetObjectShell()->IsPreview())
+ {
+ bPreview = true;
+ }
+
+ css::uno::Reference<css::frame::XFrame> xFrame = GetFrame().GetFrameInterface();
+ if (!bPreview)
+ {
+ SetViewFrame(this);
+ GetBindings().SetActiveFrame(css::uno::Reference<css::frame::XFrame>());
+ uno::Reference<frame::XFramesSupplier> xSupp(xFrame, uno::UNO_QUERY);
+ if (xSupp.is())
+ xSupp->setActiveFrame(uno::Reference<frame::XFrame>());
+
+ css::uno::Reference< css::awt::XWindow > xContainerWindow = xFrame->getContainerWindow();
+ VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow(xContainerWindow);
+ if (pWindow && pWindow->HasChildPathFocus() && bGrabFocus)
+ {
+ SfxInPlaceClient *pCli = GetViewShell()->GetUIActiveClient();
+ if (!pCli || !pCli->IsObjectUIActive())
+ GetFrame().GrabFocusOnComponent_Impl();
+ }
+ }
+ else
+ {
+ GetBindings().SetDispatcher(GetDispatcher());
+ GetBindings().SetActiveFrame(css::uno::Reference<css::frame::XFrame>());
+ GetDispatcher()->Update_Impl();
+ }
+}
+
+SfxObjectShell* SfxViewFrame::GetObjectShell()
+{
+ return m_xObjSh.get();
+}
+
+const Size& SfxViewFrame::GetMargin_Impl() const
+{
+ return m_pImpl->aMargin;
+}
+
+SfxViewFrame* SfxViewFrame::LoadViewIntoFrame_Impl_NoThrow( const SfxObjectShell& i_rDoc, const Reference< XFrame >& i_rFrame,
+ const SfxInterfaceId i_nViewId, const bool i_bHidden )
+{
+ Reference< XFrame > xFrame( i_rFrame );
+ bool bOwnFrame = false;
+ SfxViewShell* pSuccessView = nullptr;
+ try
+ {
+ if ( !xFrame.is() )
+ {
+ Reference < XDesktop2 > xDesktop = Desktop::create( ::comphelper::getProcessComponentContext() );
+
+ if ( !i_bHidden )
+ {
+ try
+ {
+ // if there is a backing component, use it
+ ::framework::FrameListAnalyzer aAnalyzer( xDesktop, Reference< XFrame >(), FrameAnalyzerFlags::BackingComponent );
+
+ if ( aAnalyzer.m_xBackingComponent.is() )
+ xFrame = aAnalyzer.m_xBackingComponent;
+ }
+ catch( uno::Exception& )
+ {}
+ }
+
+ if ( !xFrame.is() )
+ xFrame.set( xDesktop->findFrame( "_blank", 0 ), UNO_SET_THROW );
+
+ bOwnFrame = true;
+ }
+
+ pSuccessView = LoadViewIntoFrame_Impl(
+ i_rDoc,
+ xFrame,
+ Sequence< PropertyValue >(), // means "reuse existing model's args"
+ i_nViewId,
+ i_bHidden
+ );
+
+ if ( bOwnFrame && !i_bHidden )
+ {
+ // ensure the frame/window is visible
+ Reference< XWindow > xContainerWindow( xFrame->getContainerWindow(), UNO_SET_THROW );
+ xContainerWindow->setVisible( true );
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("sfx.view");
+ }
+
+ if ( pSuccessView )
+ return pSuccessView->GetViewFrame();
+
+ if ( bOwnFrame )
+ {
+ try
+ {
+ xFrame->dispose();
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("sfx.view");
+ }
+ }
+
+ return nullptr;
+}
+
+SfxViewShell* SfxViewFrame::LoadViewIntoFrame_Impl( const SfxObjectShell& i_rDoc, const Reference< XFrame >& i_rFrame,
+ const Sequence< PropertyValue >& i_rLoadArgs, const SfxInterfaceId i_nViewId,
+ const bool i_bHidden )
+{
+ Reference< XModel > xDocument( i_rDoc.GetModel(), UNO_SET_THROW );
+
+ ::comphelper::NamedValueCollection aTransformLoadArgs( i_rLoadArgs.hasElements() ? i_rLoadArgs : xDocument->getArgs() );
+ aTransformLoadArgs.put( "Model", xDocument );
+ if ( i_nViewId )
+ aTransformLoadArgs.put( "ViewId", sal_uInt16( i_nViewId ) );
+ if ( i_bHidden )
+ aTransformLoadArgs.put( "Hidden", i_bHidden );
+ else
+ aTransformLoadArgs.remove( "Hidden" );
+
+ Reference< XComponentLoader > xLoader( i_rFrame, UNO_QUERY_THROW );
+ xLoader->loadComponentFromURL( "private:object", "_self", 0,
+ aTransformLoadArgs.getPropertyValues() );
+
+ SfxViewShell* pViewShell = SfxViewShell::Get( i_rFrame->getController() );
+ ENSURE_OR_THROW( pViewShell,
+ "SfxViewFrame::LoadViewIntoFrame_Impl: loading an SFX doc into a frame resulted in a non-SFX view - quite impossible" );
+ return pViewShell;
+}
+
+SfxViewFrame* SfxViewFrame::LoadHiddenDocument( SfxObjectShell const & i_rDoc, SfxInterfaceId i_nViewId )
+{
+ return LoadViewIntoFrame_Impl_NoThrow( i_rDoc, Reference< XFrame >(), i_nViewId, true );
+}
+
+SfxViewFrame* SfxViewFrame::LoadDocument( SfxObjectShell const & i_rDoc, SfxInterfaceId i_nViewId )
+{
+ return LoadViewIntoFrame_Impl_NoThrow( i_rDoc, Reference< XFrame >(), i_nViewId, false );
+}
+
+SfxViewFrame* SfxViewFrame::LoadDocumentIntoFrame( SfxObjectShell const & i_rDoc, const Reference< XFrame >& i_rTargetFrame )
+{
+ return LoadViewIntoFrame_Impl_NoThrow( i_rDoc, i_rTargetFrame, SFX_INTERFACE_NONE, false );
+}
+
+SfxViewFrame* SfxViewFrame::LoadDocumentIntoFrame( SfxObjectShell const & i_rDoc, const SfxFrameItem* i_pFrameItem, SfxInterfaceId i_nViewId )
+{
+ return LoadViewIntoFrame_Impl_NoThrow( i_rDoc, i_pFrameItem && i_pFrameItem->GetFrame() ? i_pFrameItem->GetFrame()->GetFrameInterface() : nullptr, i_nViewId, false );
+}
+
+SfxViewFrame* SfxViewFrame::DisplayNewDocument( SfxObjectShell const & i_rDoc, const SfxRequest& i_rCreateDocRequest )
+{
+ const SfxUnoFrameItem* pFrameItem = i_rCreateDocRequest.GetArg<SfxUnoFrameItem>(SID_FILLFRAME);
+ const SfxBoolItem* pHiddenItem = i_rCreateDocRequest.GetArg<SfxBoolItem>(SID_HIDDEN);
+
+ return LoadViewIntoFrame_Impl_NoThrow(
+ i_rDoc,
+ pFrameItem ? pFrameItem->GetFrame() : nullptr,
+ SFX_INTERFACE_NONE,
+ pHiddenItem && pHiddenItem->GetValue()
+ );
+}
+
+SfxViewFrame* SfxViewFrame::Get( const Reference< XController>& i_rController, const SfxObjectShell* i_pDoc )
+{
+ if ( !i_rController.is() )
+ return nullptr;
+
+ const SfxObjectShell* pDoc = i_pDoc;
+ if ( !pDoc )
+ {
+ Reference< XModel > xDocument( i_rController->getModel() );
+ for ( pDoc = SfxObjectShell::GetFirst( nullptr, false );
+ pDoc;
+ pDoc = SfxObjectShell::GetNext( *pDoc, nullptr, false )
+ )
+ {
+ if ( pDoc->GetModel() == xDocument )
+ break;
+ }
+ }
+
+ SfxViewFrame* pViewFrame = nullptr;
+ for ( pViewFrame = SfxViewFrame::GetFirst( pDoc, false );
+ pViewFrame;
+ pViewFrame = SfxViewFrame::GetNext( *pViewFrame, pDoc, false )
+ )
+ {
+ if ( pViewFrame->GetViewShell()->GetController() == i_rController )
+ break;
+ }
+
+ return pViewFrame;
+}
+
+void SfxViewFrame::SaveCurrentViewData_Impl( const SfxInterfaceId i_nNewViewId )
+{
+ SfxViewShell* pCurrentShell = GetViewShell();
+ ENSURE_OR_RETURN_VOID( pCurrentShell != nullptr, "SfxViewFrame::SaveCurrentViewData_Impl: no current view shell -> no current view data!" );
+
+ // determine the logical (API) view name
+ const SfxObjectFactory& rDocFactory( pCurrentShell->GetObjectShell()->GetFactory() );
+ const sal_uInt16 nCurViewNo = rDocFactory.GetViewNo_Impl( GetCurViewId(), 0 );
+ const OUString sCurrentViewName = rDocFactory.GetViewFactory( nCurViewNo ).GetAPIViewName();
+ const sal_uInt16 nNewViewNo = rDocFactory.GetViewNo_Impl( i_nNewViewId, 0 );
+ const OUString sNewViewName = rDocFactory.GetViewFactory( nNewViewNo ).GetAPIViewName();
+ if ( sCurrentViewName.isEmpty() || sNewViewName.isEmpty() )
+ {
+ // can't say anything about the view, the respective application did not yet migrate its code to
+ // named view factories => bail out
+ OSL_FAIL( "SfxViewFrame::SaveCurrentViewData_Impl: views without API names? Shouldn't happen anymore?" );
+ return;
+ }
+ SAL_WARN_IF(sNewViewName == sCurrentViewName, "sfx.view", "SfxViewFrame::SaveCurrentViewData_Impl: suspicious: new and old view name are identical!");
+
+ // save the view data only when we're moving from a non-print-preview to the print-preview view
+ if ( sNewViewName != "PrintPreview" )
+ return;
+
+ // retrieve the view data from the view
+ Sequence< PropertyValue > aViewData;
+ pCurrentShell->WriteUserDataSequence( aViewData );
+
+ try
+ {
+ // retrieve view data (for *all* views) from the model
+ const Reference< XController > xController( pCurrentShell->GetController(), UNO_SET_THROW );
+ const Reference< XViewDataSupplier > xViewDataSupplier( xController->getModel(), UNO_QUERY_THROW );
+ const Reference< XIndexContainer > xViewData( xViewDataSupplier->getViewData(), UNO_QUERY_THROW );
+
+ // look up the one view data item which corresponds to our current view, and remove it
+ const sal_Int32 nCount = xViewData->getCount();
+ for ( sal_Int32 i=0; i<nCount; ++i )
+ {
+ const ::comphelper::NamedValueCollection aCurViewData( xViewData->getByIndex(i) );
+ const OUString sViewId( aCurViewData.getOrDefault( "ViewId", OUString() ) );
+ if ( sViewId.isEmpty() )
+ continue;
+
+ const SfxViewFactory* pViewFactory = rDocFactory.GetViewFactoryByViewName( sViewId );
+ if ( pViewFactory == nullptr )
+ continue;
+
+ if ( pViewFactory->GetOrdinal() == GetCurViewId() )
+ {
+ xViewData->removeByIndex(i);
+ break;
+ }
+ }
+
+ // then replace it with the most recent view data we just obtained
+ xViewData->insertByIndex( 0, Any( aViewData ) );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("sfx.view");
+ }
+}
+
+/* [Description]
+
+ Internal Method for switching to another <SfxViewShell> subclass,
+ which should be created in this SfxMDIFrame. If no SfxViewShell exist
+ in this SfxMDIFrame, then one will first be created.
+
+
+ [Return Value]
+
+ bool true
+ requested SfxViewShell was created and a
+ possibly existing one deleted
+
+ false
+ SfxViewShell requested could not be created,
+ the existing SfxViewShell thus continue to exist
+*/
+bool SfxViewFrame::SwitchToViewShell_Impl
+(
+ sal_uInt16 nViewIdOrNo, /* > 0
+ Registration-Id of the View, to which the
+ method should switch, for example the one
+ that will be created.
+
+ == 0
+ First use the Default view. */
+
+ bool bIsIndex /* true
+ 'nViewIdOrNo' is no Registration-Id instead
+ an Index of <SfxViewFrame> in <SfxObjectShell>.
+ */
+)
+{
+ try
+ {
+ ENSURE_OR_THROW( GetObjectShell() != nullptr, "not possible without a document" );
+
+ // if we already have a view shell, remove it
+ SfxViewShell* pOldSh = GetViewShell();
+ OSL_PRECOND( pOldSh, "SfxViewFrame::SwitchToViewShell_Impl: that's called *switch* (not for *initial-load*) for a reason" );
+ if ( pOldSh )
+ {
+ // ask whether it can be closed
+ if ( !pOldSh->PrepareClose() )
+ return false;
+
+ // remove sub shells from Dispatcher before switching to new ViewShell
+ PopShellAndSubShells_Impl( *pOldSh );
+ }
+
+ GetBindings().ENTERREGISTRATIONS();
+ LockAdjustPosSizePixel();
+
+ // ID of the new view
+ SfxObjectFactory& rDocFact = GetObjectShell()->GetFactory();
+ const SfxInterfaceId nViewId = ( bIsIndex || !nViewIdOrNo ) ? rDocFact.GetViewFactory( nViewIdOrNo ).GetOrdinal() : SfxInterfaceId(nViewIdOrNo);
+
+ // save the view data of the old view, so it can be restored later on (when needed)
+ SaveCurrentViewData_Impl( nViewId );
+
+ // create and load new ViewShell
+ SfxViewShell* pNewSh = LoadViewIntoFrame_Impl(
+ *GetObjectShell(),
+ GetFrame().GetFrameInterface(),
+ Sequence< PropertyValue >(), // means "reuse existing model's args"
+ nViewId,
+ false
+ );
+
+ // allow resize events to be processed
+ UnlockAdjustPosSizePixel();
+
+ if ( GetWindow().IsReallyVisible() )
+ DoAdjustPosSizePixel( pNewSh, Point(), GetWindow().GetOutputSizePixel(), false );
+
+ GetBindings().LEAVEREGISTRATIONS();
+ delete pOldSh;
+ }
+ catch ( const css::uno::Exception& )
+ {
+ // the SfxCode is not able to cope with exceptions thrown while creating views
+ // the code will crash in the stack unwinding procedure, so we shouldn't let exceptions go through here
+ DBG_UNHANDLED_EXCEPTION("sfx.view");
+ return false;
+ }
+
+ DBG_ASSERT( SfxGetpApp()->GetViewFrames_Impl().size() == SfxGetpApp()->GetViewShells_Impl().size(), "Inconsistent view arrays!" );
+ return true;
+}
+
+void SfxViewFrame::SetCurViewId_Impl( const SfxInterfaceId i_nID )
+{
+ m_pImpl->nCurViewId = i_nID;
+}
+
+SfxInterfaceId SfxViewFrame::GetCurViewId() const
+{
+ return m_pImpl->nCurViewId;
+}
+
+/* [Description]
+
+ Internal method to run the slot for the <SfxShell> Subclass in the
+ SfxViewFrame <SVIDL> described slots.
+*/
+void SfxViewFrame::ExecView_Impl
+(
+ SfxRequest& rReq // The executable <SfxRequest>
+)
+{
+
+ // If the Shells are just being replaced...
+ if ( !GetObjectShell() || !GetViewShell() )
+ return;
+
+ switch ( rReq.GetSlot() )
+ {
+ case SID_TERMINATE_INPLACEACTIVATION :
+ {
+ SfxInPlaceClient* pClient = GetViewShell()->GetUIActiveClient();
+ if ( pClient )
+ pClient->DeactivateObject();
+ break;
+ }
+
+ case SID_VIEWSHELL:
+ {
+ const SfxUInt16Item *pItem = nullptr;
+ if ( rReq.GetArgs()
+ && (pItem = rReq.GetArgs()->GetItemIfSet( SID_VIEWSHELL, false ))
+ )
+ {
+ const sal_uInt16 nViewId = pItem->GetValue();
+ bool bSuccess = SwitchToViewShell_Impl( nViewId );
+ rReq.SetReturnValue( SfxBoolItem( 0, bSuccess ) );
+ }
+ break;
+ }
+
+ case SID_VIEWSHELL0:
+ case SID_VIEWSHELL1:
+ case SID_VIEWSHELL2:
+ case SID_VIEWSHELL3:
+ case SID_VIEWSHELL4:
+ {
+ const sal_uInt16 nViewNo = rReq.GetSlot() - SID_VIEWSHELL0;
+ bool bSuccess = SwitchToViewShell_Impl( nViewNo, true );
+ rReq.SetReturnValue( SfxBoolItem( 0, bSuccess ) );
+ break;
+ }
+
+ case SID_NEWWINDOW:
+ {
+ // Hack. at the moment a virtual Function
+ if ( !GetViewShell()->NewWindowAllowed() )
+ {
+ OSL_FAIL( "You should have disabled the 'Window/New Window' slot!" );
+ return;
+ }
+
+ // Get ViewData of FrameSets recursively.
+ GetFrame().GetViewData_Impl();
+ SfxMedium* pMed = GetObjectShell()->GetMedium();
+
+ // do not open the new window hidden
+ pMed->GetItemSet()->ClearItem( SID_HIDDEN );
+
+ // the view ID (optional arg. TODO: this is currently not supported in the slot definition ...)
+ const SfxUInt16Item* pViewIdItem = rReq.GetArg<SfxUInt16Item>(SID_VIEW_ID);
+ const SfxInterfaceId nViewId = pViewIdItem ? SfxInterfaceId(pViewIdItem->GetValue()) : GetCurViewId();
+
+ Reference < XFrame > xFrame;
+ // the frame (optional arg. TODO: this is currently not supported in the slot definition ...)
+ const SfxUnoFrameItem* pFrameItem = rReq.GetArg<SfxUnoFrameItem>(SID_FILLFRAME);
+ if ( pFrameItem )
+ xFrame = pFrameItem->GetFrame();
+
+ LoadViewIntoFrame_Impl_NoThrow( *GetObjectShell(), xFrame, nViewId, false );
+
+ rReq.Done();
+ break;
+ }
+
+ case SID_OBJECT:
+ {
+ const SfxInt16Item* pItem = rReq.GetArg<SfxInt16Item>(SID_OBJECT);
+
+ if (pItem)
+ {
+ GetViewShell()->DoVerb( pItem->GetValue() );
+ rReq.Done();
+ break;
+ }
+ }
+ }
+}
+
+/* TODO as96863:
+ This method try to collect information about the count of currently open documents.
+ But the algorithm is implemented very simple ...
+ E.g. hidden documents should be ignored here ... but they are counted.
+ TODO: export special helper "framework::FrameListAnalyzer" within the framework module
+ and use it here.
+*/
+static bool impl_maxOpenDocCountReached()
+{
+ css::uno::Reference< css::uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext();
+ std::optional<sal_Int32> x(officecfg::Office::Common::Misc::MaxOpenDocuments::get());
+ // NIL means: count of allowed documents = infinite !
+ if (!x)
+ return false;
+ sal_Int32 nMaxDocs(*x);
+ sal_Int32 nOpenDocs = 0;
+
+ css::uno::Reference< css::frame::XDesktop2 > xDesktop = css::frame::Desktop::create(xContext);
+ css::uno::Reference< css::container::XIndexAccess > xCont(xDesktop->getFrames(), css::uno::UNO_QUERY_THROW);
+
+ sal_Int32 c = xCont->getCount();
+ sal_Int32 i = 0;
+
+ for (i=0; i<c; ++i)
+ {
+ try
+ {
+ css::uno::Reference< css::frame::XFrame > xFrame;
+ xCont->getByIndex(i) >>= xFrame;
+ if ( ! xFrame.is())
+ continue;
+
+ // a) do not count the help window
+ if ( xFrame->getName() == "OFFICE_HELP_TASK" )
+ continue;
+
+ // b) count all other frames
+ ++nOpenDocs;
+ }
+ catch(const css::uno::Exception&)
+ // An IndexOutOfBoundsException can happen in multithreaded
+ // environments, where any other thread can change this
+ // container !
+ { continue; }
+ }
+
+ return (nOpenDocs >= nMaxDocs);
+}
+
+/* [Description]
+
+ This internal method returns in 'rSet' the Status for the <SfxShell>
+ Subclass SfxViewFrame in the <SVIDL> described <Slots>.
+
+ Thus exactly those Slots-IDs that are recognized as being invalid by Sfx
+ are included as Which-ranges in 'rSet'. If there exists a mapping for
+ single slot-IDs of the <SfxItemPool> set in the shell, then the respective
+ Which-IDs are used so that items can be replaced directly with a working
+ Core::sun::com::star::script::Engine of the Which-IDs if possible. .
+*/
+void SfxViewFrame::StateView_Impl
+(
+ SfxItemSet& rSet /* empty <SfxItemSet> with <Which-Ranges>,
+ which describes the Slot Ids */
+)
+{
+
+ SfxObjectShell *pDocSh = GetObjectShell();
+
+ if ( !pDocSh )
+ // I'm just on reload and am yielding myself ...
+ return;
+
+ const WhichRangesContainer & pRanges = rSet.GetRanges();
+ assert(!pRanges.empty() && "Set with no Range");
+ for ( auto const & pRange : pRanges )
+ {
+ sal_uInt16 nStartWhich = pRange.first;
+ sal_uInt16 nEndWhich = pRange.second;
+ for ( sal_uInt16 nWhich = nStartWhich; nWhich <= nEndWhich; ++nWhich )
+ {
+ switch(nWhich)
+ {
+ case SID_VIEWSHELL:
+ {
+ rSet.Put( SfxUInt16Item( nWhich, sal_uInt16(m_pImpl->nCurViewId )) );
+ break;
+ }
+
+ case SID_VIEWSHELL0:
+ case SID_VIEWSHELL1:
+ case SID_VIEWSHELL2:
+ case SID_VIEWSHELL3:
+ case SID_VIEWSHELL4:
+ {
+ sal_uInt16 nViewNo = nWhich - SID_VIEWSHELL0;
+ if ( GetObjectShell()->GetFactory().GetViewFactoryCount() >
+ nViewNo && !GetObjectShell()->IsInPlaceActive() )
+ {
+ SfxViewFactory &rViewFactory =
+ GetObjectShell()->GetFactory().GetViewFactory(nViewNo);
+ rSet.Put( SfxBoolItem(
+ nWhich, m_pImpl->nCurViewId == rViewFactory.GetOrdinal() ) );
+ }
+ else
+ rSet.DisableItem( nWhich );
+ break;
+ }
+
+ case SID_NEWWINDOW:
+ {
+ if ( !GetViewShell()->NewWindowAllowed()
+ || impl_maxOpenDocCountReached()
+ )
+ rSet.DisableItem( nWhich );
+ break;
+ }
+ }
+ }
+ }
+}
+
+
+void SfxViewFrame::ToTop()
+{
+ GetFrame().Appear();
+}
+
+
+/* [Description]
+
+ GetFrame returns the Frame, in which the ViewFrame is located.
+*/
+SfxFrame& SfxViewFrame::GetFrame() const
+{
+ return m_pImpl->rFrame;
+}
+
+SfxViewFrame* SfxViewFrame::GetTopViewFrame() const
+{
+ return GetFrame().GetCurrentViewFrame();
+}
+
+vcl::Window& SfxViewFrame::GetWindow() const
+{
+ return m_pImpl->pWindow ? *m_pImpl->pWindow : GetFrame().GetWindow();
+}
+
+weld::Window* SfxViewFrame::GetFrameWeld() const
+{
+ return GetWindow().GetFrameWeld();
+}
+
+bool SfxViewFrame::DoClose()
+{
+ return GetFrame().DoClose();
+}
+
+OUString SfxViewFrame::GetActualPresentationURL_Impl() const
+{
+ if ( m_xObjSh.is() )
+ return m_xObjSh->GetMedium()->GetName();
+ return OUString();
+}
+
+void SfxViewFrame::SetModalMode( bool bModal )
+{
+ // no real modality for LOK
+ if (comphelper::LibreOfficeKit::isActive())
+ return;
+
+ m_pImpl->bModal = bModal;
+ if ( m_xObjSh.is() )
+ {
+ for ( SfxViewFrame* pFrame = SfxViewFrame::GetFirst( m_xObjSh.get() );
+ !bModal && pFrame; pFrame = SfxViewFrame::GetNext( *pFrame, m_xObjSh.get() ) )
+ bModal = pFrame->m_pImpl->bModal;
+ m_xObjSh->SetModalMode_Impl( bModal );
+ }
+}
+
+bool SfxViewFrame::IsInModalMode() const
+{
+ return m_pImpl->bModal || GetFrame().GetWindow().IsInModalMode();
+}
+
+void SfxViewFrame::Resize( bool bForce )
+{
+ Size aSize = GetWindow().GetOutputSizePixel();
+ if ( !bForce && aSize == m_pImpl->aSize )
+ return;
+
+ m_pImpl->aSize = aSize;
+ SfxViewShell *pShell = GetViewShell();
+ if ( pShell )
+ {
+ if ( GetFrame().IsInPlace() )
+ {
+ Point aPoint = GetWindow().GetPosPixel();
+ DoAdjustPosSizePixel( pShell, aPoint, aSize, true );
+ }
+ else
+ {
+ DoAdjustPosSizePixel( pShell, Point(), aSize, false );
+ }
+ }
+}
+
+#if HAVE_FEATURE_SCRIPTING
+
+#define LINE_SEP 0x0A
+
+static void CutLines( OUString& rStr, sal_Int32 nStartLine, sal_Int32 nLines )
+{
+ sal_Int32 nStartPos = 0;
+ sal_Int32 nLine = 0;
+ while ( nLine < nStartLine )
+ {
+ nStartPos = rStr.indexOf( LINE_SEP, nStartPos );
+ if( nStartPos == -1 )
+ break;
+ nStartPos++; // not the \n.
+ nLine++;
+ }
+
+ SAL_WARN_IF(nStartPos == -1, "sfx.view", "CutLines: Start row not found!");
+
+ if ( nStartPos != -1 )
+ {
+ sal_Int32 nEndPos = nStartPos;
+ for ( sal_Int32 i = 0; i < nLines; i++ )
+ nEndPos = rStr.indexOf( LINE_SEP, nEndPos+1 );
+
+ if ( nEndPos == -1 ) // Can happen at the last row.
+ nEndPos = rStr.getLength();
+ else
+ nEndPos++;
+
+ rStr = OUString::Concat(rStr.subView( 0, nStartPos )) + rStr.subView( nEndPos );
+ }
+ // erase trailing lines
+ if ( nStartPos != -1 )
+ {
+ sal_Int32 n = nStartPos;
+ sal_Int32 nLen = rStr.getLength();
+ while ( ( n < nLen ) && ( rStr[ n ] == LINE_SEP ) )
+ n++;
+
+ if ( n > nStartPos )
+ rStr = OUString::Concat(rStr.subView( 0, nStartPos )) + rStr.subView( n );
+ }
+}
+
+#endif
+
+/*
+ add new recorded dispatch macro script into the application global basic
+ lib container. It generates a new unique id for it and insert the macro
+ by using this number as name for the module
+ */
+void SfxViewFrame::AddDispatchMacroToBasic_Impl( const OUString& sMacro )
+{
+#if !HAVE_FEATURE_SCRIPTING
+ (void) sMacro;
+#else
+ if ( sMacro.isEmpty() )
+ return;
+
+ SfxApplication* pSfxApp = SfxGetpApp();
+ SfxItemPool& rPool = pSfxApp->GetPool();
+ SfxRequest aReq(SID_BASICCHOOSER, SfxCallMode::SYNCHRON, rPool);
+
+ //seen in tdf#122598, no parent for subsequent dialog
+ SfxAllItemSet aSet(rPool);
+ css::uno::Reference< css::frame::XFrame > xFrame =
+ GetFrame().GetFrameInterface();
+ aSet.Put(SfxUnoFrameItem(SID_FILLFRAME, xFrame));
+ aReq.SetInternalArgs_Impl(aSet);
+
+ aReq.AppendItem( SfxBoolItem(SID_RECORDMACRO,true) );
+ const SfxPoolItem* pRet = SfxGetpApp()->ExecuteSlot( aReq );
+ OUString aScriptURL;
+ if ( pRet )
+ aScriptURL = static_cast<const SfxStringItem*>(pRet)->GetValue();
+ if ( !aScriptURL.isEmpty() )
+ {
+ // parse scriptURL
+ OUString aLibName;
+ OUString aModuleName;
+ OUString aMacroName;
+ OUString aLocation;
+ Reference< XComponentContext > xContext = ::comphelper::getProcessComponentContext();
+ Reference< css::uri::XUriReferenceFactory > xFactory =
+ css::uri::UriReferenceFactory::create( xContext );
+ Reference< css::uri::XVndSunStarScriptUrl > xUrl( xFactory->parse( aScriptURL ), UNO_QUERY );
+ if ( xUrl.is() )
+ {
+ // get name
+ const OUString aName = xUrl->getName();
+ const sal_Unicode cTok = '.';
+ sal_Int32 nIndex = 0;
+ aLibName = aName.getToken( 0, cTok, nIndex );
+ if ( nIndex != -1 )
+ aModuleName = aName.getToken( 0, cTok, nIndex );
+ if ( nIndex != -1 )
+ aMacroName = aName.getToken( 0, cTok, nIndex );
+
+ // get location
+ aLocation = xUrl->getParameter( "location" );
+ }
+
+ BasicManager* pBasMgr = nullptr;
+ if ( aLocation == "application" )
+ {
+ // application basic
+ pBasMgr = SfxApplication::GetBasicManager();
+ }
+ else if ( aLocation == "document" )
+ {
+ pBasMgr = GetObjectShell()->GetBasicManager();
+ }
+
+ OUString aOUSource;
+ if ( pBasMgr)
+ {
+ StarBASIC* pBasic = pBasMgr->GetLib( aLibName );
+ if ( pBasic )
+ {
+ SbModule* pModule = pBasic->FindModule( aModuleName );
+ SbMethod* pMethod = pModule ? pModule->FindMethod(aMacroName, SbxClassType::Method) : nullptr;
+ if (pMethod)
+ {
+ aOUSource = pModule->GetSource32();
+ sal_uInt16 nStart, nEnd;
+ pMethod->GetLineRange( nStart, nEnd );
+ sal_uInt16 nlStart = nStart;
+ sal_uInt16 nlEnd = nEnd;
+ CutLines( aOUSource, nlStart-1, nlEnd-nlStart+1 );
+ }
+ }
+ }
+
+ // open lib container and break operation if it couldn't be opened
+ css::uno::Reference< css::script::XLibraryContainer > xLibCont;
+ if ( aLocation == "application" )
+ {
+ xLibCont = SfxGetpApp()->GetBasicContainer();
+ }
+ else if ( aLocation == "document" )
+ {
+ xLibCont = GetObjectShell()->GetBasicContainer();
+ }
+
+ if(!xLibCont.is())
+ {
+ SAL_WARN("sfx.view", "couldn't get access to the basic lib container. Adding of macro isn't possible.");
+ return;
+ }
+
+ // get LibraryContainer
+ css::uno::Any aTemp;
+
+ css::uno::Reference< css::container::XNameAccess > xLib;
+ if(xLibCont->hasByName(aLibName))
+ {
+ // library must be loaded
+ aTemp = xLibCont->getByName(aLibName);
+ xLibCont->loadLibrary(aLibName);
+ aTemp >>= xLib;
+ }
+ else
+ {
+ xLib = xLibCont->createLibrary(aLibName);
+ }
+
+ // pack the macro as direct usable "sub" routine
+ OUStringBuffer sRoutine(10000);
+ bool bReplace = false;
+
+ // get module
+ if(xLib->hasByName(aModuleName))
+ {
+ if ( !aOUSource.isEmpty() )
+ {
+ sRoutine.append( aOUSource );
+ }
+ else
+ {
+ OUString sCode;
+ aTemp = xLib->getByName(aModuleName);
+ aTemp >>= sCode;
+ sRoutine.append( sCode );
+ }
+
+ bReplace = true;
+ }
+
+ // append new method
+ sRoutine.append( "\nsub " );
+ sRoutine.append(aMacroName);
+ sRoutine.append( "\n" );
+ sRoutine.append(sMacro);
+ sRoutine.append( "\nend sub\n" );
+
+ // create the module inside the library and insert the macro routine
+ aTemp <<= sRoutine.makeStringAndClear();
+ if ( bReplace )
+ {
+ css::uno::Reference< css::container::XNameContainer > xModulCont(
+ xLib,
+ css::uno::UNO_QUERY);
+ xModulCont->replaceByName(aModuleName,aTemp);
+ }
+ else
+ {
+ css::uno::Reference< css::container::XNameContainer > xModulCont(
+ xLib,
+ css::uno::UNO_QUERY);
+ xModulCont->insertByName(aModuleName,aTemp);
+ }
+
+ // #i17355# update the Basic IDE
+ for ( SfxViewShell* pViewShell = SfxViewShell::GetFirst(); pViewShell; pViewShell = SfxViewShell::GetNext( *pViewShell ) )
+ {
+ if ( pViewShell->GetName() == "BasicIDE" )
+ {
+ SfxViewFrame* pViewFrame = pViewShell->GetViewFrame();
+ SfxDispatcher* pDispat = pViewFrame ? pViewFrame->GetDispatcher() : nullptr;
+ if ( pDispat )
+ {
+ SfxMacroInfoItem aInfoItem( SID_BASICIDE_ARG_MACROINFO, pBasMgr, aLibName, aModuleName, OUString(), OUString() );
+ pDispat->ExecuteList(SID_BASICIDE_UPDATEMODULESOURCE,
+ SfxCallMode::SYNCHRON, { &aInfoItem });
+ }
+ }
+ }
+ }
+ else
+ {
+ // add code for "session only" macro
+ }
+#endif
+}
+
+void SfxViewFrame::MiscExec_Impl( SfxRequest& rReq )
+{
+ switch ( rReq.GetSlot() )
+ {
+ case SID_STOP_RECORDING :
+ case SID_RECORDMACRO :
+ {
+ // try to find any active recorder on this frame
+ static const OUStringLiteral sProperty(u"DispatchRecorderSupplier");
+ css::uno::Reference< css::frame::XFrame > xFrame =
+ GetFrame().GetFrameInterface();
+
+ css::uno::Reference< css::beans::XPropertySet > xSet(xFrame,css::uno::UNO_QUERY);
+ css::uno::Any aProp = xSet->getPropertyValue(sProperty);
+ css::uno::Reference< css::frame::XDispatchRecorderSupplier > xSupplier;
+ aProp >>= xSupplier;
+ css::uno::Reference< css::frame::XDispatchRecorder > xRecorder;
+ if (xSupplier.is())
+ xRecorder = xSupplier->getDispatchRecorder();
+
+ bool bIsRecording = xRecorder.is();
+ const SfxBoolItem* pItem = rReq.GetArg<SfxBoolItem>(SID_RECORDMACRO);
+ if ( pItem && pItem->GetValue() == bIsRecording )
+ return;
+
+ if ( xRecorder.is() )
+ {
+ // disable active recording
+ aProp <<= css::uno::Reference< css::frame::XDispatchRecorderSupplier >();
+ xSet->setPropertyValue(sProperty,aProp);
+
+ const SfxBoolItem* pRecordItem = rReq.GetArg<SfxBoolItem>(FN_PARAM_1);
+ if ( !pRecordItem || !pRecordItem->GetValue() )
+ // insert script into basic library container of application
+ AddDispatchMacroToBasic_Impl(xRecorder->getRecordedMacro());
+
+ xRecorder->endRecording();
+ xRecorder = nullptr;
+ GetBindings().SetRecorder_Impl( xRecorder );
+
+ SetChildWindow( SID_RECORDING_FLOATWINDOW, false );
+ if ( rReq.GetSlot() != SID_RECORDMACRO )
+ GetBindings().Invalidate( SID_RECORDMACRO );
+ }
+ else if ( rReq.GetSlot() == SID_RECORDMACRO )
+ {
+ // enable recording
+ css::uno::Reference< css::uno::XComponentContext > xContext(
+ ::comphelper::getProcessComponentContext());
+
+ xRecorder = css::frame::DispatchRecorder::create( xContext );
+
+ xSupplier = css::frame::DispatchRecorderSupplier::create( xContext );
+
+ xSupplier->setDispatchRecorder(xRecorder);
+ xRecorder->startRecording(xFrame);
+ aProp <<= xSupplier;
+ xSet->setPropertyValue(sProperty,aProp);
+ GetBindings().SetRecorder_Impl( xRecorder );
+ SetChildWindow( SID_RECORDING_FLOATWINDOW, true );
+ }
+
+ rReq.Done();
+ break;
+ }
+
+ case SID_TOGGLESTATUSBAR:
+ {
+ if ( auto xLayoutManager = getLayoutManager(GetFrame()) )
+ {
+ static const OUStringLiteral aStatusbarResString( u"private:resource/statusbar/statusbar" );
+ // Evaluate parameter.
+ const SfxBoolItem* pShowItem = rReq.GetArg<SfxBoolItem>(rReq.GetSlot());
+ bool bShow( true );
+ if ( !pShowItem )
+ bShow = xLayoutManager->isElementVisible( aStatusbarResString );
+ else
+ bShow = pShowItem->GetValue();
+
+ if ( bShow )
+ {
+ xLayoutManager->createElement( aStatusbarResString );
+ xLayoutManager->showElement( aStatusbarResString );
+ }
+ else
+ xLayoutManager->hideElement( aStatusbarResString );
+
+ if ( !pShowItem )
+ rReq.AppendItem( SfxBoolItem( SID_TOGGLESTATUSBAR, bShow ) );
+ }
+ rReq.Done();
+ break;
+ }
+ case SID_COMMAND_POPUP:
+ {
+ tools::Rectangle aRectangle(Point(0,0), GetWindow().GetSizePixel());
+ weld::Window* pParent = weld::GetPopupParent(GetWindow(), aRectangle);
+ m_pCommandPopupHandler->showPopup(pParent, GetFrame().GetFrameInterface());
+
+ rReq.Done();
+ break;
+ }
+ case SID_WIN_FULLSCREEN:
+ {
+ const SfxBoolItem* pItem = rReq.GetArg<SfxBoolItem>(rReq.GetSlot());
+ SfxViewFrame *pTop = GetTopViewFrame();
+ if ( pTop )
+ {
+ WorkWindow* pWork = static_cast<WorkWindow*>( pTop->GetFrame().GetTopWindow_Impl() );
+ if ( pWork )
+ {
+ Reference< css::frame::XLayoutManager > xLayoutManager = getLayoutManager(GetFrame());
+ bool bNewFullScreenMode = pItem ? pItem->GetValue() : !pWork->IsFullScreenMode();
+ if ( bNewFullScreenMode != pWork->IsFullScreenMode() )
+ {
+ if ( bNewFullScreenMode )
+ sfx2::SfxNotebookBar::LockNotebookBar();
+ else
+ sfx2::SfxNotebookBar::UnlockNotebookBar();
+
+ Reference< css::beans::XPropertySet > xLMPropSet( xLayoutManager, UNO_QUERY );
+ if ( xLMPropSet.is() )
+ {
+ try
+ {
+ xLMPropSet->setPropertyValue(
+ "HideCurrentUI",
+ Any( bNewFullScreenMode ));
+ }
+ catch ( css::beans::UnknownPropertyException& )
+ {
+ }
+ }
+ pWork->ShowFullScreenMode( bNewFullScreenMode );
+ pWork->SetMenuBarMode( bNewFullScreenMode ? MenuBarMode::Hide : MenuBarMode::Normal );
+ GetFrame().GetWorkWindow_Impl()->SetFullScreen_Impl( bNewFullScreenMode );
+ if ( !pItem )
+ rReq.AppendItem( SfxBoolItem( SID_WIN_FULLSCREEN, bNewFullScreenMode ) );
+ rReq.Done();
+ }
+ else
+ rReq.Ignore();
+ }
+ }
+ else
+ rReq.Ignore();
+
+ GetDispatcher()->Update_Impl( true );
+ break;
+ }
+ }
+}
+
+void SfxViewFrame::MiscState_Impl(SfxItemSet &rSet)
+{
+ const WhichRangesContainer & pRanges = rSet.GetRanges();
+ DBG_ASSERT(!pRanges.empty(), "Set without range");
+ for ( auto const & pRange : pRanges )
+ {
+ for(sal_uInt16 nWhich = pRange.first; nWhich <= pRange.second; ++nWhich)
+ {
+ switch(nWhich)
+ {
+ case SID_CURRENT_URL:
+ {
+ rSet.Put( SfxStringItem( nWhich, GetActualPresentationURL_Impl() ) );
+ break;
+ }
+
+ case SID_RECORDMACRO :
+ {
+ const OUString& sName{GetObjectShell()->GetFactory().GetFactoryName()};
+ bool bMacrosDisabled = officecfg::Office::Common::Security::Scripting::DisableMacrosExecution::get();
+ if (bMacrosDisabled ||
+ !officecfg::Office::Common::Misc::MacroRecorderMode::get() ||
+ ( sName!="swriter" && sName!="scalc" ) )
+ {
+ rSet.DisableItem( nWhich );
+ rSet.Put(SfxVisibilityItem(nWhich, false));
+ break;
+ }
+
+ css::uno::Reference< css::beans::XPropertySet > xSet(
+ GetFrame().GetFrameInterface(),
+ css::uno::UNO_QUERY);
+
+ css::uno::Any aProp = xSet->getPropertyValue("DispatchRecorderSupplier");
+ css::uno::Reference< css::frame::XDispatchRecorderSupplier > xSupplier;
+ if ( aProp >>= xSupplier )
+ rSet.Put( SfxBoolItem( nWhich, xSupplier.is() ) );
+ else
+ rSet.DisableItem( nWhich );
+ break;
+ }
+
+ case SID_STOP_RECORDING :
+ {
+ const OUString& sName{GetObjectShell()->GetFactory().GetFactoryName()};
+ if ( !officecfg::Office::Common::Misc::MacroRecorderMode::get() ||
+ ( sName!="swriter" && sName!="scalc" ) )
+ {
+ rSet.DisableItem( nWhich );
+ break;
+ }
+
+ css::uno::Reference< css::beans::XPropertySet > xSet(
+ GetFrame().GetFrameInterface(),
+ css::uno::UNO_QUERY);
+
+ css::uno::Any aProp = xSet->getPropertyValue("DispatchRecorderSupplier");
+ css::uno::Reference< css::frame::XDispatchRecorderSupplier > xSupplier;
+ if ( !(aProp >>= xSupplier) || !xSupplier.is() )
+ rSet.DisableItem( nWhich );
+ break;
+ }
+
+ case SID_TOGGLESTATUSBAR:
+ {
+ css::uno::Reference< css::frame::XLayoutManager > xLayoutManager;
+ css::uno::Reference< css::beans::XPropertySet > xSet(
+ GetFrame().GetFrameInterface(),
+ css::uno::UNO_QUERY);
+ css::uno::Any aProp = xSet->getPropertyValue( "LayoutManager" );
+
+ if ( !( aProp >>= xLayoutManager ))
+ rSet.Put( SfxBoolItem( nWhich, false ));
+ else
+ {
+ bool bShow = xLayoutManager->isElementVisible( "private:resource/statusbar/statusbar" );
+ rSet.Put( SfxBoolItem( nWhich, bShow ));
+ }
+ break;
+ }
+
+ case SID_WIN_FULLSCREEN:
+ {
+ SfxViewFrame* pTop = GetTopViewFrame();
+ if ( pTop )
+ {
+ WorkWindow* pWork = static_cast<WorkWindow*>( pTop->GetFrame().GetTopWindow_Impl() );
+ if ( pWork )
+ {
+ rSet.Put( SfxBoolItem( nWhich, pWork->IsFullScreenMode() ) );
+ break;
+ }
+ }
+
+ rSet.DisableItem( nWhich );
+ break;
+ }
+
+ default:
+ break;
+ }
+ }
+ }
+}
+
+/* [Description]
+
+ This method can be included in the Execute method for the on- and off-
+ switching of ChildWindows, to implement this and API-bindings.
+
+ Simply include as 'ExecuteMethod' in the IDL.
+*/
+void SfxViewFrame::ChildWindowExecute( SfxRequest &rReq )
+{
+ // Evaluate Parameter
+ sal_uInt16 nSID = rReq.GetSlot();
+
+ if (nSID == SID_SIDEBAR_DECK)
+ {
+ const SfxStringItem* pDeckIdItem = rReq.GetArg<SfxStringItem>(SID_SIDEBAR_DECK);
+ if (pDeckIdItem)
+ {
+ const OUString aDeckId(pDeckIdItem->GetValue());
+ ::sfx2::sidebar::Sidebar::ToggleDeck(aDeckId, this);
+ }
+ rReq.Done();
+ return;
+ }
+
+ const SfxBoolItem* pShowItem = rReq.GetArg<SfxBoolItem>(nSID);
+ if ( nSID == SID_VIEW_DATA_SOURCE_BROWSER )
+ {
+ if (!SvtModuleOptions().IsModuleInstalled(SvtModuleOptions::EModule::DATABASE))
+ return;
+ Reference < XFrame > xFrame = GetFrame().GetFrameInterface();
+ Reference < XFrame > xBeamer( xFrame->findFrame( "_beamer", FrameSearchFlag::CHILDREN ) );
+ bool bHasChild = xBeamer.is();
+ bool bShow = pShowItem ? pShowItem->GetValue() : !bHasChild;
+ if ( pShowItem )
+ {
+ if( bShow == bHasChild )
+ return;
+ }
+ else
+ rReq.AppendItem( SfxBoolItem( nSID, bShow ) );
+
+ if ( !bShow )
+ {
+ SetChildWindow( SID_BROWSER, false );
+ }
+ else
+ {
+ css::util::URL aTargetURL;
+ aTargetURL.Complete = ".component:DB/DataSourceBrowser";
+ Reference < css::util::XURLTransformer > xTrans(
+ css::util::URLTransformer::create(
+ ::comphelper::getProcessComponentContext() ) );
+ xTrans->parseStrict( aTargetURL );
+
+ Reference < XDispatchProvider > xProv( xFrame, UNO_QUERY );
+ Reference < css::frame::XDispatch > xDisp;
+ if ( xProv.is() )
+ xDisp = xProv->queryDispatch( aTargetURL, "_beamer", 31 );
+ if ( xDisp.is() )
+ {
+ Sequence < css::beans::PropertyValue > aArgs(1);
+ css::beans::PropertyValue* pArg = aArgs.getArray();
+ pArg[0].Name = "Referer";
+ pArg[0].Value <<= OUString("private:user");
+ xDisp->dispatch( aTargetURL, aArgs );
+ }
+ }
+
+ rReq.Done();
+ return;
+ }
+ if (nSID == SID_STYLE_DESIGNER)
+ {
+ // First make sure that the sidebar is visible
+ ShowChildWindow(SID_SIDEBAR);
+
+ ::sfx2::sidebar::Sidebar::ShowPanel(u"StyleListPanel",
+ GetFrame().GetFrameInterface(), true);
+ rReq.Done();
+ return;
+ }
+
+ bool bHasChild = HasChildWindow(nSID);
+ bool bShow = pShowItem ? pShowItem->GetValue() : !bHasChild;
+ GetDispatcher()->Update_Impl( true );
+
+ // Perform action.
+ if ( !pShowItem || bShow != bHasChild )
+ ToggleChildWindow( nSID );
+
+ GetBindings().Invalidate( nSID );
+
+ // Record if possible.
+ if ( nSID == SID_HYPERLINK_DIALOG || nSID == SID_SEARCH_DLG )
+ {
+ rReq.Ignore();
+ }
+ else
+ {
+ rReq.AppendItem( SfxBoolItem( nSID, bShow ) );
+ rReq.Done();
+ }
+}
+
+/* [Description]
+
+ This method can be used in the state method for the on and off-state
+ of child-windows, in order to implement this.
+
+ Just register the IDL as 'StateMethod'.
+*/
+void SfxViewFrame::ChildWindowState( SfxItemSet& rState )
+{
+ SfxWhichIter aIter( rState );
+ for ( sal_uInt16 nSID = aIter.FirstWhich(); nSID; nSID = aIter.NextWhich() )
+ {
+ if ( nSID == SID_VIEW_DATA_SOURCE_BROWSER )
+ {
+ rState.Put( SfxBoolItem( nSID, HasChildWindow( SID_BROWSER ) ) );
+ }
+ else if ( nSID == SID_HYPERLINK_DIALOG )
+ {
+ const SfxPoolItem* pDummy = nullptr;
+ SfxItemState eState = GetDispatcher()->QueryState( SID_HYPERLINK_SETLINK, pDummy );
+ if ( SfxItemState::DISABLED == eState )
+ rState.DisableItem(nSID);
+ else
+ {
+ if ( KnowsChildWindow(nSID) )
+ rState.Put( SfxBoolItem( nSID, HasChildWindow(nSID)) );
+ else
+ rState.DisableItem(nSID);
+ }
+ }
+ else if ( nSID == SID_BROWSER )
+ {
+ Reference < XFrame > xFrame = GetFrame().GetFrameInterface()->
+ findFrame( "_beamer", FrameSearchFlag::CHILDREN );
+ if ( !xFrame.is() )
+ rState.DisableItem( nSID );
+ else if ( KnowsChildWindow(nSID) )
+ rState.Put( SfxBoolItem( nSID, HasChildWindow(nSID) ) );
+ }
+ else if ( nSID == SID_SIDEBAR )
+ {
+ if ( !KnowsChildWindow( nSID ) )
+ {
+ SAL_INFO("sfx.view", "SID_SIDEBAR state requested, but no task pane child window exists for this ID!");
+ rState.DisableItem( nSID );
+ }
+ else
+ {
+ rState.Put( SfxBoolItem( nSID, HasChildWindow( nSID ) ) );
+ }
+ }
+ else if ( KnowsChildWindow(nSID) )
+ rState.Put( SfxBoolItem( nSID, HasChildWindow(nSID) ) );
+ else
+ rState.DisableItem(nSID);
+ }
+}
+
+SfxWorkWindow* SfxViewFrame::GetWorkWindow_Impl()
+{
+ SfxWorkWindow* pWork = GetFrame().GetWorkWindow_Impl();
+ return pWork;
+}
+
+void SfxViewFrame::SetChildWindow(sal_uInt16 nId, bool bOn, bool bSetFocus )
+{
+ SfxWorkWindow* pWork = GetWorkWindow_Impl();
+ if ( pWork )
+ pWork->SetChildWindow_Impl( nId, bOn, bSetFocus );
+}
+
+void SfxViewFrame::ToggleChildWindow(sal_uInt16 nId)
+{
+ SfxWorkWindow* pWork = GetWorkWindow_Impl();
+ if ( pWork )
+ pWork->ToggleChildWindow_Impl( nId, true );
+}
+
+bool SfxViewFrame::HasChildWindow( sal_uInt16 nId )
+{
+ SfxWorkWindow* pWork = GetWorkWindow_Impl();
+ return pWork && pWork->HasChildWindow_Impl(nId);
+}
+
+bool SfxViewFrame::KnowsChildWindow( sal_uInt16 nId )
+{
+ SfxWorkWindow* pWork = GetWorkWindow_Impl();
+ return pWork && pWork->KnowsChildWindow_Impl(nId);
+}
+
+void SfxViewFrame::ShowChildWindow( sal_uInt16 nId, bool bVisible )
+{
+ SfxWorkWindow* pWork = GetWorkWindow_Impl();
+ if ( pWork )
+ {
+ GetDispatcher()->Update_Impl(true);
+ pWork->ShowChildWindow_Impl(nId, bVisible, true );
+ }
+}
+
+SfxChildWindow* SfxViewFrame::GetChildWindow(sal_uInt16 nId)
+{
+ SfxWorkWindow* pWork = GetWorkWindow_Impl();
+ return pWork ? pWork->GetChildWindow_Impl(nId) : nullptr;
+}
+
+void SfxViewFrame::UpdateDocument_Impl()
+{
+ SfxObjectShell* pDoc = GetObjectShell();
+ if ( pDoc->IsLoadingFinished() )
+ pDoc->CheckSecurityOnLoading_Impl();
+
+ // check if document depends on a template
+ pDoc->UpdateFromTemplate_Impl();
+}
+
+void SfxViewFrame::SetViewFrame( SfxViewFrame* pFrame )
+{
+ if(pFrame)
+ SetSVHelpData(pFrame->m_pHelpData);
+
+ SetSVWinData(pFrame ? pFrame->m_pWinData : nullptr);
+
+ SfxGetpApp()->SetViewFrame_Impl( pFrame );
+}
+
+VclPtr<SfxInfoBarWindow> SfxViewFrame::AppendInfoBar(const OUString& sId,
+ const OUString& sPrimaryMessage,
+ const OUString& sSecondaryMessage,
+ InfobarType aInfobarType, bool bShowCloseButton)
+{
+ SfxChildWindow* pChild = GetChildWindow(SfxInfoBarContainerChild::GetChildWindowId());
+ if (!pChild)
+ return nullptr;
+
+ if (HasInfoBarWithID(sId))
+ return nullptr;
+
+ SfxInfoBarContainerWindow* pInfoBarContainer = static_cast<SfxInfoBarContainerWindow*>(pChild->GetWindow());
+ auto pInfoBar = pInfoBarContainer->appendInfoBar(sId, sPrimaryMessage, sSecondaryMessage,
+ aInfobarType, bShowCloseButton);
+ ShowChildWindow(SfxInfoBarContainerChild::GetChildWindowId());
+ return pInfoBar;
+}
+
+void SfxViewFrame::UpdateInfoBar(std::u16string_view sId, const OUString& sPrimaryMessage,
+ const OUString& sSecondaryMessage, InfobarType eType)
+{
+ const sal_uInt16 nId = SfxInfoBarContainerChild::GetChildWindowId();
+
+ // Make sure the InfoBar container is visible
+ if (!HasChildWindow(nId))
+ ToggleChildWindow(nId);
+
+ SfxChildWindow* pChild = GetChildWindow(nId);
+ if (pChild)
+ {
+ SfxInfoBarContainerWindow* pInfoBarContainer = static_cast<SfxInfoBarContainerWindow*>(pChild->GetWindow());
+ auto pInfoBar = pInfoBarContainer->getInfoBar(sId);
+
+ if (pInfoBar)
+ pInfoBar->Update(sPrimaryMessage, sSecondaryMessage, eType);
+ }
+}
+
+void SfxViewFrame::RemoveInfoBar( std::u16string_view sId )
+{
+ const sal_uInt16 nId = SfxInfoBarContainerChild::GetChildWindowId();
+
+ // Make sure the InfoBar container is visible
+ if (!HasChildWindow(nId))
+ ToggleChildWindow(nId);
+
+ SfxChildWindow* pChild = GetChildWindow(nId);
+ if (pChild)
+ {
+ SfxInfoBarContainerWindow* pInfoBarContainer = static_cast<SfxInfoBarContainerWindow*>(pChild->GetWindow());
+ auto pInfoBar = pInfoBarContainer->getInfoBar(sId);
+ pInfoBarContainer->removeInfoBar(pInfoBar);
+ ShowChildWindow(nId);
+ }
+}
+
+bool SfxViewFrame::HasInfoBarWithID( std::u16string_view sId )
+{
+ const sal_uInt16 nId = SfxInfoBarContainerChild::GetChildWindowId();
+
+ SfxChildWindow* pChild = GetChildWindow(nId);
+ if (pChild)
+ {
+ SfxInfoBarContainerWindow* pInfoBarContainer = static_cast<SfxInfoBarContainerWindow*>(pChild->GetWindow());
+ return pInfoBarContainer->hasInfoBarWithID(sId);
+ }
+
+ return false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/view/viewfrm2.cxx b/sfx2/source/view/viewfrm2.cxx
new file mode 100644
index 000000000..e3cdb6b1a
--- /dev/null
+++ b/sfx2/source/view/viewfrm2.cxx
@@ -0,0 +1,379 @@
+/* -*- 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 "impviewframe.hxx"
+#include <statcach.hxx>
+#include <workwin.hxx>
+
+#include <sfx2/app.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/ctrlitem.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/docfac.hxx>
+#include <sfx2/docfile.hxx>
+#include <sfx2/objitem.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/request.hxx>
+#include <sfx2/sfxsids.hrc>
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/viewsh.hxx>
+#include <com/sun/star/lang/DisposedException.hpp>
+#include <com/sun/star/util/CloseVetoException.hpp>
+#include <com/sun/star/util/XCloseable.hpp>
+#include <com/sun/star/embed/VerbDescriptor.hpp>
+
+#include <osl/diagnose.h>
+#include <svl/eitem.hxx>
+#include <svl/stritem.hxx>
+#include <tools/urlobj.hxx>
+#include <sal/log.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::util;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::beans;
+
+
+void SfxFrameViewWindow_Impl::StateChanged( StateChangedType nStateChange )
+{
+ if ( nStateChange == StateChangedType::InitShow )
+ {
+ SfxObjectShell* pDoc = pFrame->GetObjectShell();
+ if ( pDoc && !pFrame->IsVisible() )
+ pFrame->Show();
+
+ pFrame->Resize();
+ }
+ else
+ Window::StateChanged( nStateChange );
+}
+
+void SfxFrameViewWindow_Impl::Resize()
+{
+ if ( IsReallyVisible() || IsReallyShown() || GetOutputSizePixel().Width() )
+ pFrame->Resize();
+}
+
+
+void SfxViewFrame::UpdateTitle()
+
+/* [Description]
+
+ With this method, can the SfxViewFrame be forced to immediately provide
+ the new title from the <SfxObjectShell>.
+
+ [Note]
+
+ This is for example necessary if one listens to the SfxObjectShell as
+ SfxListener and then react on the <SfxSimpleHint> SfxHintId::TitleChanged,
+ then query the title of his views. However these views (SfxTopViewFrames)
+ are also SfxListener and because the order of notifications might not be
+ fixed, the title update will be enforced in advance.
+
+ [Example]
+
+ void SwDocShell::Notify( SfxBroadcaster& rBC, const SfxHint& rHint )
+ {
+ if ( dynamic_cast<const SfxSimpleHint *>(&rHint) != nullptr )
+ {
+ switch( ( (SfxSimpleHint&) rHint ).GetId() )
+ {
+ case SfxHintId::TitleChanged:
+ for ( SfxViewFrame *pTop = SfxViewFrame::GetFirst( this );
+ pTop;
+ pTop = SfxViewFrame::GetNext( this );
+ {
+ pTop->UpdateTitle();
+ ... pTop->GetName() ...
+ }
+ break;
+ ...
+ }
+ }
+ }
+*/
+
+{
+
+ const SfxObjectFactory &rFact = GetObjectShell()->GetFactory();
+ m_pImpl->aFactoryName = rFact.GetFactoryName();
+
+ SfxObjectShell *pObjSh = GetObjectShell();
+ if ( !pObjSh )
+ return;
+
+
+ const SfxMedium *pMedium = pObjSh->GetMedium();
+ OUString aURL;
+ GetFrame(); // -Wall required??
+ if ( pObjSh->HasName() )
+ {
+ INetURLObject aTmp( pMedium->GetName() );
+ aURL = aTmp.getName( INetURLObject::LAST_SEGMENT, true, INetURLObject::DecodeMechanism::WithCharset );
+ }
+
+ if ( aURL != m_pImpl->aActualURL )
+ // URL has changed
+ m_pImpl->aActualURL = aURL;
+
+ // SbxObjects name
+ OUString aSbxName = pObjSh->SfxShell::GetName();
+ if ( IsVisible() )
+ {
+ aSbxName += ":" + OUString::number(m_pImpl->nDocViewNo);
+ }
+
+ SetName( aSbxName );
+ GetBindings().Invalidate( SID_CURRENT_URL );
+ GetBindings().Invalidate( SID_NEWDOCDIRECT );
+}
+
+void SfxViewFrame::Exec_Impl(SfxRequest &rReq )
+{
+ // If presently the shells are replaced...
+ if ( !GetObjectShell() || !GetViewShell() )
+ return;
+
+ switch ( rReq.GetSlot() )
+ {
+ case SID_SHOWPOPUPS :
+ {
+ const SfxBoolItem* pShowItem = rReq.GetArg<SfxBoolItem>(SID_SHOWPOPUPS);
+ bool bShow = pShowItem == nullptr || pShowItem->GetValue();
+
+ SfxWorkWindow *pWorkWin = GetFrame().GetWorkWindow_Impl();
+ if ( bShow )
+ {
+ // First, make the floats viewable
+ pWorkWin->MakeChildrenVisible_Impl(true);
+ GetDispatcher()->Update_Impl( true );
+
+ // Then view it
+ GetBindings().HidePopups(false);
+ }
+ else
+ {
+ pWorkWin->HidePopups_Impl(true);
+ pWorkWin->MakeChildrenVisible_Impl(false);
+ }
+
+ Invalidate( rReq.GetSlot() );
+ rReq.Done();
+ break;
+ }
+
+ case SID_ACTIVATE:
+ {
+ MakeActive_Impl( true );
+ rReq.SetReturnValue( SfxObjectItem( 0, this ) );
+ break;
+ }
+
+ case SID_NEWDOCDIRECT :
+ {
+ const SfxStringItem* pFactoryItem = rReq.GetArg<SfxStringItem>(SID_NEWDOCDIRECT);
+ OUString aFactName;
+ if ( pFactoryItem )
+ aFactName = pFactoryItem->GetValue();
+ else if ( !m_pImpl->aFactoryName.isEmpty() )
+ aFactName = m_pImpl->aFactoryName;
+ else
+ {
+ SAL_WARN("sfx.view", "Missing argument!");
+ break;
+ }
+
+ SfxRequest aReq( SID_OPENDOC, SfxCallMode::SYNCHRON, GetPool() );
+ const OUString aFact("private:factory/" + aFactName);
+ aReq.AppendItem( SfxStringItem( SID_FILE_NAME, aFact ) );
+ aReq.AppendItem( SfxFrameItem( SID_DOCFRAME, &GetFrame() ) );
+ aReq.AppendItem( SfxStringItem( SID_TARGETNAME, "_blank" ) );
+ SfxGetpApp()->ExecuteSlot( aReq );
+ const SfxViewFrameItem* pItem = dynamic_cast<const SfxViewFrameItem*>( aReq.GetReturnValue() );
+ if ( pItem )
+ rReq.SetReturnValue( SfxFrameItem( 0, pItem->GetFrame() ) );
+ break;
+ }
+
+ case SID_CLOSEWIN:
+ {
+ // disable CloseWin, if frame is not a task
+ Reference < XCloseable > xTask( GetFrame().GetFrameInterface(), UNO_QUERY );
+ if ( !xTask.is() )
+ break;
+
+ if ( GetViewShell()->PrepareClose() )
+ {
+ // More Views on the same Document?
+ SfxObjectShell *pDocSh = GetObjectShell();
+ bool bOther = false;
+ for ( const SfxViewFrame* pFrame = SfxViewFrame::GetFirst( pDocSh );
+ !bOther && pFrame;
+ pFrame = SfxViewFrame::GetNext( *pFrame, pDocSh ) )
+ bOther = (pFrame != this);
+
+ // Document only needs to be queried, if no other View present.
+ bool bClosed = false;
+ if ( bOther || pDocSh->PrepareClose( true/*bUI*/ ) )
+ {
+ if ( !bOther )
+ pDocSh->SetModified( false );
+ rReq.Done(); // Must call this before Close()!
+ bClosed = false;
+ try
+ {
+ xTask->close(true);
+ bClosed = true;
+ }
+ catch (css::lang::DisposedException &) {
+ // already closed; ignore
+ }
+ catch( CloseVetoException& )
+ {
+ bClosed = false;
+ }
+ }
+
+ rReq.SetReturnValue( SfxBoolItem( rReq.GetSlot(), bClosed ));
+ }
+ return;
+ }
+ }
+
+ rReq.Done();
+}
+
+void SfxViewFrame::GetState_Impl( SfxItemSet &rSet )
+{
+ SfxObjectShell *pDocSh = GetObjectShell();
+
+ if ( !pDocSh )
+ return;
+
+ const WhichRangesContainer & pRanges = rSet.GetRanges();
+ DBG_ASSERT(!pRanges.empty(), "Set without Range");
+ for ( auto const & pRange : pRanges )
+ {
+ for ( sal_uInt16 nWhich = pRange.first; nWhich <= pRange.second; ++nWhich )
+ {
+ switch(nWhich)
+ {
+ case SID_NEWDOCDIRECT :
+ {
+ if ( !m_pImpl->aFactoryName.isEmpty() )
+ {
+ rSet.Put( SfxStringItem( nWhich, "private:factory/"+m_pImpl->aFactoryName ) );
+ }
+ break;
+ }
+
+ case SID_NEWWINDOW:
+ rSet.DisableItem(nWhich);
+ break;
+
+ case SID_CLOSEWIN:
+ {
+ // disable CloseWin, if frame is not a task
+ Reference < XCloseable > xTask( GetFrame().GetFrameInterface(), UNO_QUERY );
+ if ( !xTask.is() )
+ rSet.DisableItem(nWhich);
+ break;
+ }
+
+ case SID_SHOWPOPUPS :
+ break;
+
+ case SID_OBJECT:
+ if ( GetViewShell() && GetViewShell()->GetVerbs().hasElements() && !GetObjectShell()->IsInPlaceActive() )
+ {
+ uno::Any aAny(GetViewShell()->GetVerbs());
+ rSet.Put( SfxUnoAnyItem( sal_uInt16( SID_OBJECT ), aAny ) );
+ }
+ else
+ rSet.DisableItem( SID_OBJECT );
+ break;
+
+ default:
+ OSL_FAIL( "invalid message-id" );
+ }
+ }
+ }
+}
+
+void SfxViewFrame::INetExecute_Impl( SfxRequest &rRequest )
+{
+ sal_uInt16 nSlotId = rRequest.GetSlot();
+ switch( nSlotId )
+ {
+ case SID_BROWSE_FORWARD:
+ case SID_BROWSE_BACKWARD:
+ OSL_FAIL( "SfxViewFrame::INetExecute_Impl: SID_BROWSE_FORWARD/BACKWARD are dead!" );
+ break;
+ case SID_CREATELINK:
+ {
+/*! (pb) we need new implementation to create a link
+*/
+ break;
+ }
+ case SID_FOCUSURLBOX:
+ {
+ SfxStateCache *pCache = GetBindings().GetAnyStateCache_Impl( SID_OPENURL );
+ if( pCache )
+ {
+ SfxControllerItem* pCtrl = pCache->GetItemLink();
+ while( pCtrl )
+ {
+ pCtrl->StateChangedAtToolBoxControl( SID_FOCUSURLBOX, SfxItemState::UNKNOWN, nullptr );
+ pCtrl = pCtrl->GetItemLink();
+ }
+ }
+ }
+ }
+
+ // Recording
+ rRequest.Done();
+}
+
+void SfxViewFrame::INetState_Impl( SfxItemSet &rItemSet )
+{
+ rItemSet.DisableItem( SID_BROWSE_FORWARD );
+ rItemSet.DisableItem( SID_BROWSE_BACKWARD );
+
+ // Add/SaveToBookmark at BASIC-IDE, QUERY-EDITOR etc. disable
+ SfxObjectShell *pDocSh = GetObjectShell();
+ bool bEmbedded = pDocSh && pDocSh->GetCreateMode() == SfxObjectCreateMode::EMBEDDED;
+ if ( !pDocSh || bEmbedded || !pDocSh->HasName() )
+ rItemSet.DisableItem( SID_CREATELINK );
+}
+
+void SfxViewFrame::Activate( bool /*bMDI*/ )
+{
+ DBG_ASSERT(GetViewShell(), "No Shell");
+//(mba): here maybe as in Beanframe NotifyEvent ?!
+}
+
+void SfxViewFrame::Deactivate( bool /*bMDI*/ )
+{
+ DBG_ASSERT(GetViewShell(), "No Shell");
+//(mba): here maybe as in Beanframe NotifyEvent ?!
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/view/viewimp.hxx b/sfx2/source/view/viewimp.hxx
new file mode 100644
index 000000000..54e8267be
--- /dev/null
+++ b/sfx2/source/view/viewimp.hxx
@@ -0,0 +1,68 @@
+/* -*- 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_VIEW_VIEWIMP_HXX
+#define INCLUDED_SFX2_SOURCE_VIEW_VIEWIMP_HXX
+
+#include <com/sun/star/ui/XContextMenuInterceptor.hpp>
+#include <memory>
+#include <sfx2/viewsh.hxx>
+#include <mutex>
+#include <comphelper/interfacecontainer4.hxx>
+#include <svtools/acceleratorexecute.hxx>
+#include <rtl/ref.hxx>
+#include <vcl/print.hxx>
+#include <vector>
+
+class SfxBaseController;
+typedef std::vector<SfxShell*> SfxShellArr_Impl;
+class SfxClipboardChangeListener;
+
+struct SfxViewShell_Impl
+{
+ std::mutex aMutex;
+ ::comphelper::OInterfaceContainerHelper4<css::ui::XContextMenuInterceptor>
+ aInterceptorContainer;
+ SfxShellArr_Impl aArr;
+ Size aMargin;
+ bool m_bHasPrintOptions;
+ sal_uInt16 m_nFamily;
+ ::rtl::Reference<SfxBaseController> m_pController;
+ std::unique_ptr<::svt::AcceleratorExecute> m_xAccExec;
+ ::rtl::Reference<SfxClipboardChangeListener> xClipboardListener;
+ std::shared_ptr<vcl::PrinterController> m_xPrinterController;
+
+ mutable std::vector<SfxInPlaceClient*> maIPClients;
+
+ SfxLokCallbackInterface* m_pLibreOfficeKitViewCallback;
+ /// Set if we are in the middle of a tiled search.
+ bool m_bTiledSearching;
+ static sal_uInt32 m_nLastViewShellId;
+ const ViewShellId m_nViewShellId;
+ const ViewShellDocId m_nDocId;
+
+ explicit SfxViewShell_Impl(SfxViewShellFlags const nFlags, ViewShellDocId nDocId);
+ ~SfxViewShell_Impl();
+
+ std::vector<SfxInPlaceClient*>& GetIPClients_Impl();
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/view/viewprn.cxx b/sfx2/source/view/viewprn.cxx
new file mode 100644
index 000000000..975dad2a6
--- /dev/null
+++ b/sfx2/source/view/viewprn.cxx
@@ -0,0 +1,916 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <memory>
+
+#include <com/sun/star/document/XDocumentProperties.hpp>
+#include <com/sun/star/view/XRenderable.hpp>
+#include <com/sun/star/view/XSelectionSupplier.hpp>
+
+#include <comphelper/propertyvalue.hxx>
+#include <officecfg/Office/Common.hxx>
+#include <sal/log.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+#include <svtools/prnsetup.hxx>
+#include <svl/flagitem.hxx>
+#include <svl/stritem.hxx>
+#include <svl/eitem.hxx>
+#include <unotools/useroptions.hxx>
+#include <tools/datetime.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/objface.hxx>
+#include <sfx2/viewsh.hxx>
+#include "viewimp.hxx"
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/printer.hxx>
+#include <sfx2/sfxresid.hxx>
+#include <sfx2/request.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/event.hxx>
+#include <sfx2/docfile.hxx>
+#include <sfx2/docfilt.hxx>
+#include <sfx2/sfxsids.hrc>
+#include <sfx2/strings.hrc>
+#include <sfx2/sfxuno.hxx>
+#include <sfx2/tabdlg.hxx>
+
+#include <toolkit/awt/vclxdevice.hxx>
+
+#include "prnmon.hxx"
+
+using namespace com::sun::star;
+using namespace com::sun::star::uno;
+
+class SfxPrinterController : public vcl::PrinterController, public SfxListener
+{
+ Any maCompleteSelection;
+ Any maSelection;
+ Reference< view::XRenderable > mxRenderable;
+ mutable VclPtr<Printer> mpLastPrinter;
+ mutable Reference<awt::XDevice> mxDevice;
+ SfxViewShell* mpViewShell;
+ SfxObjectShell* mpObjectShell;
+ bool m_bOrigStatus;
+ bool m_bNeedsChange;
+ bool m_bApi;
+ bool m_bTempPrinter;
+ util::DateTime m_aLastPrinted;
+ OUString m_aLastPrintedBy;
+
+ Sequence< beans::PropertyValue > getMergedOptions() const;
+ const Any& getSelectionObject() const;
+
+public:
+ SfxPrinterController( const VclPtr<Printer>& i_rPrinter,
+ const Any& i_rComplete,
+ const Any& i_rSelection,
+ const Any& i_rViewProp,
+ const Reference< view::XRenderable >& i_xRender,
+ bool i_bApi, bool i_bDirect,
+ SfxViewShell* pView,
+ const uno::Sequence< beans::PropertyValue >& rProps
+ );
+
+ virtual void Notify( SfxBroadcaster&, const SfxHint& ) override;
+
+ virtual int getPageCount() const override;
+ virtual Sequence< beans::PropertyValue > getPageParameters( int i_nPage ) const override;
+ virtual void printPage( int i_nPage ) const override;
+ virtual void jobStarted() override;
+ virtual void jobFinished( css::view::PrintableState ) override;
+};
+
+SfxPrinterController::SfxPrinterController( const VclPtr<Printer>& i_rPrinter,
+ const Any& i_rComplete,
+ const Any& i_rSelection,
+ const Any& i_rViewProp,
+ const Reference< view::XRenderable >& i_xRender,
+ bool i_bApi, bool i_bDirect,
+ SfxViewShell* pView,
+ const uno::Sequence< beans::PropertyValue >& rProps
+ )
+ : PrinterController(i_rPrinter, pView ? pView->GetFrameWeld() : nullptr)
+ , maCompleteSelection( i_rComplete )
+ , maSelection( i_rSelection )
+ , mxRenderable( i_xRender )
+ , mpLastPrinter( nullptr )
+ , mpViewShell( pView )
+ , mpObjectShell(nullptr)
+ , m_bOrigStatus( false )
+ , m_bNeedsChange( false )
+ , m_bApi(i_bApi)
+ , m_bTempPrinter( i_rPrinter )
+{
+ if ( mpViewShell )
+ {
+ StartListening( *mpViewShell );
+ mpObjectShell = mpViewShell->GetObjectShell();
+ StartListening( *mpObjectShell );
+ }
+
+ // initialize extra ui options
+ if( mxRenderable.is() )
+ {
+ for (const auto& rProp : rProps)
+ setValue( rProp.Name, rProp.Value );
+
+ Sequence< beans::PropertyValue > aRenderOptions{
+ comphelper::makePropertyValue("ExtraPrintUIOptions", Any{}),
+ comphelper::makePropertyValue("View", i_rViewProp),
+ comphelper::makePropertyValue("IsPrinter", true)
+ };
+ try
+ {
+ const Sequence< beans::PropertyValue > aRenderParms( mxRenderable->getRenderer( 0 , getSelectionObject(), aRenderOptions ) );
+ for( const auto& rRenderParm : aRenderParms )
+ {
+ if ( rRenderParm.Name == "ExtraPrintUIOptions" )
+ {
+ Sequence< beans::PropertyValue > aUIProps;
+ rRenderParm.Value >>= aUIProps;
+ setUIOptions( aUIProps );
+ }
+ else if( rRenderParm.Name == "NUp" )
+ {
+ setValue( rRenderParm.Name, rRenderParm.Value );
+ }
+ }
+ }
+ catch( lang::IllegalArgumentException& )
+ {
+ // the first renderer should always be available for the UI options,
+ // but catch the exception to be safe
+ }
+ }
+
+ // set some job parameters
+ setValue( "IsApi", Any( i_bApi ) );
+ setValue( "IsDirect", Any( i_bDirect ) );
+ setValue( "IsPrinter", Any( true ) );
+ setValue( "View", i_rViewProp );
+}
+
+void SfxPrinterController::Notify( SfxBroadcaster& , const SfxHint& rHint )
+{
+ if ( rHint.GetId() == SfxHintId::Dying )
+ {
+ EndListening(*mpViewShell);
+ EndListening(*mpObjectShell);
+ dialogsParentClosing();
+ mpViewShell = nullptr;
+ mpObjectShell = nullptr;
+ }
+}
+
+const Any& SfxPrinterController::getSelectionObject() const
+{
+ const beans::PropertyValue* pVal = getValue( OUString( "PrintSelectionOnly" ) );
+ if( pVal )
+ {
+ bool bSel = false;
+ pVal->Value >>= bSel;
+ return bSel ? maSelection : maCompleteSelection;
+ }
+
+ sal_Int32 nChoice = 0;
+ pVal = getValue( OUString( "PrintContent" ) );
+ if( pVal )
+ pVal->Value >>= nChoice;
+
+ return (nChoice > 1) ? maSelection : maCompleteSelection;
+}
+
+Sequence< beans::PropertyValue > SfxPrinterController::getMergedOptions() const
+{
+ VclPtr<Printer> xPrinter( getPrinter() );
+ if( xPrinter.get() != mpLastPrinter )
+ {
+ mpLastPrinter = xPrinter.get();
+ rtl::Reference<VCLXDevice> pXDevice = new VCLXDevice();
+ pXDevice->SetOutputDevice( mpLastPrinter );
+ mxDevice.set( pXDevice );
+ }
+
+ Sequence< beans::PropertyValue > aRenderOptions{ comphelper::makePropertyValue(
+ "RenderDevice", mxDevice) };
+
+ aRenderOptions = getJobProperties( aRenderOptions );
+ return aRenderOptions;
+}
+
+int SfxPrinterController::getPageCount() const
+{
+ int nPages = 0;
+ VclPtr<Printer> xPrinter( getPrinter() );
+ if( mxRenderable.is() && xPrinter )
+ {
+ Sequence< beans::PropertyValue > aJobOptions( getMergedOptions() );
+ try
+ {
+ nPages = mxRenderable->getRendererCount( getSelectionObject(), aJobOptions );
+ }
+ catch (lang::DisposedException &)
+ {
+ SAL_WARN("sfx", "SfxPrinterController: document disposed while printing");
+ const_cast<SfxPrinterController*>(this)->setJobState(
+ view::PrintableState_JOB_ABORTED);
+ }
+ }
+ return nPages;
+}
+
+Sequence< beans::PropertyValue > SfxPrinterController::getPageParameters( int i_nPage ) const
+{
+ VclPtr<Printer> xPrinter( getPrinter() );
+ Sequence< beans::PropertyValue > aResult;
+
+ if (mxRenderable.is() && xPrinter)
+ {
+ Sequence< beans::PropertyValue > aJobOptions( getMergedOptions() );
+ try
+ {
+ aResult = mxRenderable->getRenderer( i_nPage, getSelectionObject(), aJobOptions );
+ }
+ catch( lang::IllegalArgumentException& )
+ {
+ }
+ catch (lang::DisposedException &)
+ {
+ SAL_WARN("sfx", "SfxPrinterController: document disposed while printing");
+ const_cast<SfxPrinterController*>(this)->setJobState(
+ view::PrintableState_JOB_ABORTED);
+ }
+ }
+ return aResult;
+}
+
+void SfxPrinterController::printPage( int i_nPage ) const
+{
+ VclPtr<Printer> xPrinter( getPrinter() );
+ if( !mxRenderable.is() || !xPrinter )
+ return;
+
+ Sequence< beans::PropertyValue > aJobOptions( getMergedOptions() );
+ try
+ {
+ mxRenderable->render( i_nPage, getSelectionObject(), aJobOptions );
+ }
+ catch( lang::IllegalArgumentException& )
+ {
+ // don't care enough about nonexistent page here
+ // to provoke a crash
+ }
+ catch (lang::DisposedException &)
+ {
+ SAL_WARN("sfx", "SfxPrinterController: document disposed while printing");
+ const_cast<SfxPrinterController*>(this)->setJobState(
+ view::PrintableState_JOB_ABORTED);
+ }
+}
+
+void SfxPrinterController::jobStarted()
+{
+ if ( !mpObjectShell )
+ return;
+
+ m_bOrigStatus = mpObjectShell->IsEnableSetModified();
+
+ // check configuration: shall update of printing information in DocInfo set the document to "modified"?
+ if (m_bOrigStatus && !officecfg::Office::Common::Print::PrintingModifiesDocument::get())
+ {
+ mpObjectShell->EnableSetModified( false );
+ m_bNeedsChange = true;
+ }
+
+ // refresh document info
+ uno::Reference<document::XDocumentProperties> xDocProps(mpObjectShell->getDocProperties());
+ m_aLastPrintedBy = xDocProps->getPrintedBy();
+ m_aLastPrinted = xDocProps->getPrintDate();
+
+ xDocProps->setPrintedBy( mpObjectShell->IsUseUserData()
+ ? SvtUserOptions().GetFullName()
+ : OUString() );
+ ::DateTime now( ::DateTime::SYSTEM );
+
+ xDocProps->setPrintDate( now.GetUNODateTime() );
+
+ uno::Sequence < beans::PropertyValue > aOpts;
+ aOpts = getJobProperties( aOpts );
+
+ uno::Reference< frame::XController2 > xController;
+ if ( mpViewShell )
+ xController.set( mpViewShell->GetController(), uno::UNO_QUERY );
+
+ mpObjectShell->Broadcast( SfxPrintingHint(
+ view::PrintableState_JOB_STARTED, aOpts, mpObjectShell, xController ) );
+}
+
+void SfxPrinterController::jobFinished( css::view::PrintableState nState )
+{
+ if ( !mpObjectShell )
+ return;
+
+ bool bCopyJobSetup = false;
+ mpObjectShell->Broadcast( SfxPrintingHint( nState ) );
+ switch ( nState )
+ {
+ case view::PrintableState_JOB_SPOOLING_FAILED :
+ case view::PrintableState_JOB_FAILED :
+ {
+ // "real" problem (not simply printing cancelled by user)
+ OUString aMsg( SfxResId(STR_NOSTARTPRINTER) );
+ if ( !m_bApi )
+ {
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(mpViewShell->GetFrameWeld(),
+ VclMessageType::Warning, VclButtonsType::Ok,
+ aMsg));
+ xBox->run();
+ }
+ [[fallthrough]];
+ }
+ case view::PrintableState_JOB_ABORTED :
+ {
+ // printing not successful, reset DocInfo
+ uno::Reference<document::XDocumentProperties> xDocProps(mpObjectShell->getDocProperties());
+ xDocProps->setPrintedBy(m_aLastPrintedBy);
+ xDocProps->setPrintDate(m_aLastPrinted);
+ break;
+ }
+
+ case view::PrintableState_JOB_SPOOLED :
+ case view::PrintableState_JOB_COMPLETED :
+ {
+ SfxBindings& rBind = mpViewShell->GetViewFrame()->GetBindings();
+ rBind.Invalidate( SID_PRINTDOC );
+ rBind.Invalidate( SID_PRINTDOCDIRECT );
+ rBind.Invalidate( SID_SETUPPRINTER );
+ bCopyJobSetup = ! m_bTempPrinter;
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ if( bCopyJobSetup && mpViewShell )
+ {
+ // #i114306#
+ // Note: this possibly creates a printer that gets immediately replaced
+ // by a new one. The reason for this is that otherwise we would not get
+ // the printer's SfxItemSet here to copy. Awkward, but at the moment there is no
+ // other way here to get the item set.
+ SfxPrinter* pDocPrt = mpViewShell->GetPrinter(true);
+ if( pDocPrt )
+ {
+ if( pDocPrt->GetName() == getPrinter()->GetName() )
+ pDocPrt->SetJobSetup( getPrinter()->GetJobSetup() );
+ else
+ {
+ VclPtr<SfxPrinter> pNewPrt = VclPtr<SfxPrinter>::Create( pDocPrt->GetOptions().Clone(), getPrinter()->GetName() );
+ pNewPrt->SetJobSetup( getPrinter()->GetJobSetup() );
+ mpViewShell->SetPrinter( pNewPrt, SfxPrinterChangeFlags::PRINTER | SfxPrinterChangeFlags::JOBSETUP );
+ }
+ }
+ }
+
+ if ( m_bNeedsChange )
+ mpObjectShell->EnableSetModified( m_bOrigStatus );
+
+ if ( mpViewShell )
+ {
+ mpViewShell->pImpl->m_xPrinterController.reset();
+ }
+}
+
+namespace {
+
+/**
+ An instance of this class is created for the life span of the
+ printer dialogue, to create in its click handler for the additions by the
+ virtual method of the derived SfxViewShell generated print options dialogue
+ and to cache the options set there as SfxItemSet.
+*/
+class SfxDialogExecutor_Impl
+{
+private:
+ SfxViewShell* _pViewSh;
+ PrinterSetupDialog& _rSetupParent;
+ std::unique_ptr<SfxItemSet> _pOptions;
+ bool _bHelpDisabled;
+
+ DECL_LINK( Execute, weld::Button&, void );
+
+public:
+ SfxDialogExecutor_Impl( SfxViewShell* pViewSh, PrinterSetupDialog& rParent );
+
+ Link<weld::Button&, void> GetLink() const { return LINK(const_cast<SfxDialogExecutor_Impl*>(this), SfxDialogExecutor_Impl, Execute); }
+ const SfxItemSet* GetOptions() const { return _pOptions.get(); }
+ void DisableHelp() { _bHelpDisabled = true; }
+};
+
+}
+
+SfxDialogExecutor_Impl::SfxDialogExecutor_Impl( SfxViewShell* pViewSh, PrinterSetupDialog& rParent ) :
+
+ _pViewSh ( pViewSh ),
+ _rSetupParent ( rParent ),
+ _bHelpDisabled ( false )
+
+{
+}
+
+IMPL_LINK_NOARG(SfxDialogExecutor_Impl, Execute, weld::Button&, void)
+{
+ // Options noted locally
+ if ( !_pOptions )
+ {
+ _pOptions = static_cast<SfxPrinter*>( _rSetupParent.GetPrinter() )->GetOptions().Clone();
+ }
+
+ assert(_pOptions);
+ if (!_pOptions)
+ return;
+
+ // Create Dialog
+ SfxPrintOptionsDialog aDlg(_rSetupParent.GetFrameWeld(), _pViewSh, _pOptions.get() );
+ if (_bHelpDisabled)
+ aDlg.DisableHelp();
+ if (aDlg.run() == RET_OK)
+ {
+ _pOptions = aDlg.GetOptions().Clone();
+ }
+}
+
+/**
+ Internal method for setting the differences between 'pNewPrinter' to the
+ current printer. pNewPrinter is either taken over or deleted.
+*/
+void SfxViewShell::SetPrinter_Impl( VclPtr<SfxPrinter>& pNewPrinter )
+{
+ // get current Printer
+ SfxPrinter *pDocPrinter = GetPrinter();
+
+ // Evaluate Printer Options
+ const SfxFlagItem *pFlagItem = pDocPrinter->GetOptions().GetItemIfSet( SID_PRINTER_CHANGESTODOC, false );
+ bool bOriToDoc = pFlagItem && (static_cast<SfxPrinterChangeFlags>(pFlagItem->GetValue()) & SfxPrinterChangeFlags::CHG_ORIENTATION);
+ bool bSizeToDoc = pFlagItem && (static_cast<SfxPrinterChangeFlags>(pFlagItem->GetValue()) & SfxPrinterChangeFlags::CHG_SIZE);
+
+ // Determine the previous format and size
+ Orientation eOldOri = pDocPrinter->GetOrientation();
+ Size aOldPgSz = pDocPrinter->GetPaperSizePixel();
+
+ // Determine the new format and size
+ Orientation eNewOri = pNewPrinter->GetOrientation();
+ Size aNewPgSz = pNewPrinter->GetPaperSizePixel();
+
+ // Determine the changes in page format
+ bool bOriChg = (eOldOri != eNewOri) && bOriToDoc;
+ bool bPgSzChg = ( aOldPgSz.Height() !=
+ ( bOriChg ? aNewPgSz.Width() : aNewPgSz.Height() ) ||
+ aOldPgSz.Width() !=
+ ( bOriChg ? aNewPgSz.Height() : aNewPgSz.Width() ) ) &&
+ bSizeToDoc;
+
+ // Message and Flags for page format changes
+ OUString aMsg;
+ SfxPrinterChangeFlags nNewOpt = SfxPrinterChangeFlags::NONE;
+ if( bOriChg && bPgSzChg )
+ {
+ aMsg = SfxResId(STR_PRINT_NEWORISIZE);
+ nNewOpt = SfxPrinterChangeFlags::CHG_ORIENTATION | SfxPrinterChangeFlags::CHG_SIZE;
+ }
+ else if (bOriChg )
+ {
+ aMsg = SfxResId(STR_PRINT_NEWORI);
+ nNewOpt = SfxPrinterChangeFlags::CHG_ORIENTATION;
+ }
+ else if (bPgSzChg)
+ {
+ aMsg = SfxResId(STR_PRINT_NEWSIZE);
+ nNewOpt = SfxPrinterChangeFlags::CHG_SIZE;
+ }
+
+ // Summarize in this variable what has been changed.
+ SfxPrinterChangeFlags nChangedFlags = SfxPrinterChangeFlags::NONE;
+
+ // Ask if possible, if page format should be taken over from printer.
+ if (bOriChg || bPgSzChg)
+ {
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(nullptr,
+ VclMessageType::Question, VclButtonsType::YesNo,
+ aMsg));
+ if (RET_YES == xBox->run())
+ {
+ // Flags with changes for <SetPrinter(SfxPrinter*)> are maintained
+ nChangedFlags |= nNewOpt;
+ }
+ }
+
+ // Was the printer selection changed from Default to Specific
+ // or the other way around?
+ if ( (pNewPrinter->GetName() != pDocPrinter->GetName())
+ || (pDocPrinter->IsDefPrinter() != pNewPrinter->IsDefPrinter()) )
+ {
+ nChangedFlags |= SfxPrinterChangeFlags::PRINTER|SfxPrinterChangeFlags::JOBSETUP;
+ if ( ! (pNewPrinter->GetOptions() == pDocPrinter->GetOptions()) )
+ {
+ nChangedFlags |= SfxPrinterChangeFlags::OPTIONS;
+ }
+
+ pDocPrinter = pNewPrinter;
+ }
+ else
+ {
+ // Compare extra options
+ if ( ! (pNewPrinter->GetOptions() == pDocPrinter->GetOptions()) )
+ {
+ // Option have changed
+ pDocPrinter->SetOptions( pNewPrinter->GetOptions() );
+ nChangedFlags |= SfxPrinterChangeFlags::OPTIONS;
+ }
+
+ // Compare JobSetups
+ JobSetup aNewJobSetup = pNewPrinter->GetJobSetup();
+ JobSetup aOldJobSetup = pDocPrinter->GetJobSetup();
+ if ( aNewJobSetup != aOldJobSetup )
+ {
+ nChangedFlags |= SfxPrinterChangeFlags::JOBSETUP;
+ }
+
+ // Keep old changed Printer.
+ pDocPrinter->SetPrinterProps( pNewPrinter );
+ pNewPrinter.disposeAndClear();
+ }
+
+ if ( SfxPrinterChangeFlags::NONE != nChangedFlags )
+ // SetPrinter will delete the old printer if it changes
+ SetPrinter( pDocPrinter, nChangedFlags );
+}
+
+void SfxViewShell::StartPrint( const uno::Sequence < beans::PropertyValue >& rProps, bool bIsAPI, bool bIsDirect )
+{
+ assert( !pImpl->m_xPrinterController );
+
+ // get the current selection; our controller should know it
+ Reference< frame::XController > xController( GetController() );
+ Reference< view::XSelectionSupplier > xSupplier( xController, UNO_QUERY );
+
+ Any aSelection;
+ if( xSupplier.is() )
+ aSelection = xSupplier->getSelection();
+ else
+ aSelection <<= GetObjectShell()->GetModel();
+ Any aComplete( Any( GetObjectShell()->GetModel() ) );
+ Any aViewProp( xController );
+ VclPtr<Printer> aPrt;
+
+ const beans::PropertyValue* pVal = std::find_if(rProps.begin(), rProps.end(),
+ [](const beans::PropertyValue& rVal) { return rVal.Name == "PrinterName"; });
+ if (pVal != rProps.end())
+ {
+ OUString aPrinterName;
+ pVal->Value >>= aPrinterName;
+ aPrt.reset( VclPtr<Printer>::Create( aPrinterName ) );
+ }
+
+ std::shared_ptr<vcl::PrinterController> xNewController(std::make_shared<SfxPrinterController>(
+ aPrt,
+ aComplete,
+ aSelection,
+ aViewProp,
+ GetRenderable(),
+ bIsAPI,
+ bIsDirect,
+ this,
+ rProps
+ ));
+ pImpl->m_xPrinterController = xNewController;
+
+ SfxObjectShell *pObjShell = GetObjectShell();
+ xNewController->setValue( "JobName",
+ Any( pObjShell->GetTitle(1) ) );
+ xNewController->setPrinterModified( mbPrinterSettingsModified );
+}
+
+void SfxViewShell::ExecPrint( const uno::Sequence < beans::PropertyValue >& rProps, bool bIsAPI, bool bIsDirect )
+{
+ StartPrint( rProps, bIsAPI, bIsDirect );
+ // FIXME: job setup
+ SfxPrinter* pDocPrt = GetPrinter();
+ JobSetup aJobSetup = pDocPrt ? pDocPrt->GetJobSetup() : JobSetup();
+ Printer::PrintJob( GetPrinterController(), aJobSetup );
+}
+
+const std::shared_ptr< vcl::PrinterController >& SfxViewShell::GetPrinterController() const
+{
+ return pImpl->m_xPrinterController;
+}
+
+Printer* SfxViewShell::GetActivePrinter() const
+{
+ return pImpl->m_xPrinterController
+ ? pImpl->m_xPrinterController->getPrinter().get() : nullptr;
+}
+
+void SfxViewShell::ExecPrint_Impl( SfxRequest &rReq )
+{
+ sal_uInt16 nDialogRet = RET_CANCEL;
+ VclPtr<SfxPrinter> pPrinter;
+ bool bSilent = false;
+
+ // does the function have been called by the user interface or by an API call
+ bool bIsAPI = rReq.GetArgs() && rReq.GetArgs()->Count();
+ if ( bIsAPI )
+ {
+ // the function have been called by the API
+
+ // Should it be visible on the user interface,
+ // should it launch popup dialogue ?
+ const SfxBoolItem* pSilentItem = rReq.GetArg<SfxBoolItem>(SID_SILENT);
+ bSilent = pSilentItem && pSilentItem->GetValue();
+ }
+
+ // no help button in dialogs if called from the help window
+ // (pressing help button would exchange the current page inside the help
+ // document that is going to be printed!)
+ SfxMedium* pMedium = GetViewFrame()->GetObjectShell()->GetMedium();
+ std::shared_ptr<const SfxFilter> pFilter = pMedium ? pMedium->GetFilter() : nullptr;
+ bool bPrintOnHelp = ( pFilter && pFilter->GetFilterName() == "writer_web_HTML_help" );
+
+ const sal_uInt16 nId = rReq.GetSlot();
+ switch( nId )
+ {
+ case SID_PRINTDOC: // display the printer selection and properties dialogue : File > Print...
+ case SID_PRINTDOCDIRECT: // Print the document directly, without displaying the dialogue
+ {
+ SfxObjectShell* pDoc = GetObjectShell();
+
+ // derived class may decide to abort this
+ if( pDoc == nullptr || !pDoc->QuerySlotExecutable( nId ) )
+ {
+ rReq.SetReturnValue( SfxBoolItem( 0, false ) );
+ return;
+ }
+
+ if ( !bSilent && pDoc->QueryHiddenInformation( HiddenWarningFact::WhenPrinting, nullptr ) != RET_YES )
+ return;
+
+ // should we print only the selection or the whole document
+ const SfxBoolItem* pSelectItem = rReq.GetArg<SfxBoolItem>(SID_SELECTION);
+ bool bSelection = ( pSelectItem != nullptr && pSelectItem->GetValue() );
+ // detect non api call from writer ( that adds SID_SELECTION ) and reset bIsAPI
+ if ( pSelectItem && rReq.GetArgs()->Count() == 1 )
+ bIsAPI = false;
+
+ uno::Sequence < beans::PropertyValue > aProps;
+ if ( bIsAPI )
+ {
+ // supported properties:
+ // String PrinterName
+ // String FileName
+ // Int16 From
+ // Int16 To
+ // In16 Copies
+ // String RangeText
+ // bool Selection
+ // bool Asynchron
+ // bool Collate
+ // bool Silent
+
+ // the TransformItems function overwrite aProps
+ TransformItems( nId, *rReq.GetArgs(), aProps, GetInterface()->GetSlot(nId) );
+
+ for ( auto& rProp : asNonConstRange(aProps) )
+ {
+ if ( rProp.Name == "Copies" )
+ {
+ rProp.Name = "CopyCount";
+ }
+ else if ( rProp.Name == "RangeText" )
+ {
+ rProp.Name = "Pages";
+ }
+ else if ( rProp.Name == "Asynchron" )
+ {
+ rProp.Name = "Wait";
+ bool bAsynchron = false;
+ rProp.Value >>= bAsynchron;
+ rProp.Value <<= !bAsynchron;
+ }
+ else if ( rProp.Name == "Silent" )
+ {
+ rProp.Name = "MonitorVisible";
+ bool bPrintSilent = false;
+ rProp.Value >>= bPrintSilent;
+ rProp.Value <<= !bPrintSilent;
+ }
+ }
+ }
+
+ // we will add the "PrintSelectionOnly" or "HideHelpButton" properties
+ // we have to increase the capacity of aProps
+ sal_Int32 nLen = aProps.getLength();
+ aProps.realloc( nLen + 1 );
+ auto pProps = aProps.getArray();
+
+ // HACK: writer sets the SID_SELECTION item when printing directly and expects
+ // to get only the selection document in that case (see getSelectionObject)
+ // however it also reacts to the PrintContent property. We need this distinction here, too,
+ // else one of the combinations print / print direct and selection / all will not work.
+ // it would be better if writer handled this internally
+ if( nId == SID_PRINTDOCDIRECT )
+ {
+ pProps[nLen].Name = "PrintSelectionOnly";
+ pProps[nLen].Value <<= bSelection;
+ }
+ else // if nId == SID_PRINTDOC ; nothing to do with the previous HACK
+ {
+ // should the printer selection and properties dialogue display an help button
+ pProps[nLen].Name = "HideHelpButton";
+ pProps[nLen].Value <<= bPrintOnHelp;
+ }
+
+ ExecPrint( aProps, bIsAPI, (nId == SID_PRINTDOCDIRECT) );
+
+ // FIXME: Recording
+ rReq.Done();
+ break;
+ }
+
+ case SID_PRINTER_NAME: // for recorded macros
+ {
+ // get printer and printer settings from the document
+ SfxPrinter* pDocPrinter = GetPrinter(true);
+ const SfxStringItem* pPrinterItem = rReq.GetArg<SfxStringItem>(SID_PRINTER_NAME);
+ if (!pPrinterItem)
+ {
+ rReq.Ignore();
+ break;
+ }
+ // use PrinterName parameter to create a printer
+ pPrinter = VclPtr<SfxPrinter>::Create(pDocPrinter->GetOptions().Clone(),
+ pPrinterItem->GetValue());
+
+ if (!pPrinter->IsKnown())
+ {
+ pPrinter.disposeAndClear();
+ rReq.Ignore();
+ break;
+ }
+ SetPrinter(pPrinter, SfxPrinterChangeFlags::PRINTER);
+ rReq.Done();
+ break;
+ }
+ case SID_SETUPPRINTER : // display the printer settings dialog : File > Printer Settings...
+ {
+ // get printer and printer settings from the document
+ SfxPrinter *pDocPrinter = GetPrinter(true);
+
+ // look for printer in parameters
+ const SfxStringItem* pPrinterItem = rReq.GetArg<SfxStringItem>(SID_PRINTER_NAME);
+ if ( pPrinterItem )
+ {
+ // use PrinterName parameter to create a printer
+ pPrinter = VclPtr<SfxPrinter>::Create( pDocPrinter->GetOptions().Clone(), pPrinterItem->GetValue() );
+
+ // if printer is unknown, it can't be used - now printer from document will be used
+ if ( !pPrinter->IsKnown() )
+ pPrinter.disposeAndClear();
+ }
+
+ // no PrinterName parameter in ItemSet or the PrinterName points to an unknown printer
+ if ( !pPrinter )
+ // use default printer from document
+ pPrinter = pDocPrinter;
+
+ if( !pPrinter || !pPrinter->IsValid() )
+ {
+ // no valid printer either in ItemSet or at the document
+ if ( !bSilent )
+ {
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(nullptr,
+ VclMessageType::Warning, VclButtonsType::Ok,
+ SfxResId(STR_NODEFPRINTER)));
+ xBox->run();
+ }
+
+ rReq.SetReturnValue(SfxBoolItem(0,false));
+
+ break;
+ }
+
+ // FIXME: printer isn't used for printing anymore!
+ if( pPrinter->IsPrinting() )
+ {
+ // if printer is busy, abort configuration
+ if ( !bSilent )
+ {
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(nullptr,
+ VclMessageType::Info, VclButtonsType::Ok,
+ SfxResId(STR_ERROR_PRINTER_BUSY)));
+ xBox->run();
+ }
+ rReq.SetReturnValue(SfxBoolItem(0,false));
+
+ return;
+ }
+
+ // Open Printer Setup dialog (needs a temporary printer)
+ VclPtr<SfxPrinter> pDlgPrinter = pPrinter->Clone();
+ PrinterSetupDialog aPrintSetupDlg(GetFrameWeld());
+ std::unique_ptr<SfxDialogExecutor_Impl> pExecutor;
+
+ if (pImpl->m_bHasPrintOptions && HasPrintOptionsPage())
+ {
+ // additional controls for dialog
+ pExecutor.reset(new SfxDialogExecutor_Impl(this, aPrintSetupDlg));
+ if (bPrintOnHelp)
+ pExecutor->DisableHelp();
+ aPrintSetupDlg.SetOptionsHdl(pExecutor->GetLink());
+ }
+
+ aPrintSetupDlg.SetPrinter(pDlgPrinter);
+ nDialogRet = aPrintSetupDlg.run();
+
+ if (pExecutor && pExecutor->GetOptions())
+ {
+ if (nDialogRet == RET_OK)
+ // remark: have to be recorded if possible!
+ pDlgPrinter->SetOptions(*pExecutor->GetOptions());
+ else
+ {
+ pPrinter->SetOptions(*pExecutor->GetOptions());
+ SetPrinter(pPrinter, SfxPrinterChangeFlags::OPTIONS);
+ }
+ }
+
+ // no recording of PrinterSetup except printer name (is printer dependent)
+ rReq.Ignore();
+
+ if (nDialogRet == RET_OK)
+ {
+ if (pPrinter->GetName() != pDlgPrinter->GetName())
+ {
+ // user has changed the printer -> macro recording
+ SfxRequest aReq(GetViewFrame(), SID_PRINTER_NAME);
+ aReq.AppendItem(SfxStringItem(SID_PRINTER_NAME, pDlgPrinter->GetName()));
+ aReq.Done();
+ }
+
+ // take the changes made in the dialog
+ SetPrinter_Impl(pDlgPrinter);
+
+ // forget new printer, it was taken over (as pPrinter) or deleted
+ pDlgPrinter = nullptr;
+ mbPrinterSettingsModified = true;
+ }
+ else
+ {
+ // PrinterDialog is used to transfer information on printing,
+ // so it will only be deleted here if dialog was cancelled
+ pDlgPrinter.disposeAndClear();
+ rReq.Ignore();
+ }
+ break;
+ }
+ }
+}
+
+SfxPrinter* SfxViewShell::GetPrinter( bool /*bCreate*/ )
+{
+ return nullptr;
+}
+
+sal_uInt16 SfxViewShell::SetPrinter( SfxPrinter* /*pNewPrinter*/, SfxPrinterChangeFlags /*nDiffFlags*/ )
+{
+ return 0;
+}
+
+std::unique_ptr<SfxTabPage> SfxViewShell::CreatePrintOptionsPage(weld::Container*, weld::DialogController*, const SfxItemSet&)
+{
+ return nullptr;
+}
+
+bool SfxViewShell::HasPrintOptionsPage() const
+{
+ return false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/view/viewsh.cxx b/sfx2/source/view/viewsh.cxx
new file mode 100644
index 000000000..6968e66e2
--- /dev/null
+++ b/sfx2/source/view/viewsh.cxx
@@ -0,0 +1,2092 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * 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 <config_features.h>
+
+#include <sal/log.hxx>
+#include <svl/stritem.hxx>
+#include <svl/eitem.hxx>
+#include <svl/whiter.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/toolbox.hxx>
+#include <vcl/weld.hxx>
+#include <svl/intitem.hxx>
+#include <svtools/langhelp.hxx>
+#include <com/sun/star/awt/XPopupMenu.hpp>
+#include <com/sun/star/frame/XLayoutManager.hpp>
+#include <com/sun/star/frame/ModuleManager.hpp>
+#include <com/sun/star/io/IOException.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/embed/EmbedStates.hpp>
+#include <com/sun/star/embed/EmbedMisc.hpp>
+#include <com/sun/star/embed/XEmbeddedObject.hpp>
+#include <com/sun/star/container/XContainerQuery.hpp>
+#include <com/sun/star/frame/XStorable.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/datatransfer/clipboard/XClipboard.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/datatransfer/clipboard/XClipboardListener.hpp>
+#include <com/sun/star/datatransfer/clipboard/XClipboardNotifier.hpp>
+#include <com/sun/star/view/XRenderable.hpp>
+#include <com/sun/star/uno/Reference.hxx>
+#include <cppuhelper/implbase.hxx>
+
+#include <tools/diagnose_ex.h>
+#include <tools/urlobj.hxx>
+#include <unotools/tempfile.hxx>
+#include <svtools/soerr.hxx>
+#include <tools/svborder.hxx>
+
+#include <framework/actiontriggerhelper.hxx>
+#include <comphelper/lok.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <comphelper/sequenceashashmap.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/commandinfoprovider.hxx>
+#include <LibreOfficeKit/LibreOfficeKitEnums.h>
+
+#include <officecfg/Setup.hxx>
+#include <sfx2/app.hxx>
+#include <sfx2/flatpak.hxx>
+#include <sfx2/viewsh.hxx>
+#include "viewimp.hxx"
+#include <sfx2/sfxresid.hxx>
+#include <sfx2/request.hxx>
+#include <sfx2/printer.hxx>
+#include <sfx2/docfile.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/strings.hrc>
+#include <sfx2/sfxbasecontroller.hxx>
+#include <sfx2/mailmodelapi.hxx>
+#include <bluthsndapi.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/event.hxx>
+#include <sfx2/ipclient.hxx>
+#include <sfx2/sfxsids.hrc>
+#include <sfx2/objface.hxx>
+#include <sfx2/lokhelper.hxx>
+#include <sfx2/lokcallback.hxx>
+#include <openuriexternally.hxx>
+#include <iostream>
+#include <vector>
+#include <libxml/xmlwriter.h>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::frame;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::util;
+using namespace ::cppu;
+
+#define ShellClass_SfxViewShell
+#include <sfxslots.hxx>
+
+
+class SfxClipboardChangeListener : public ::cppu::WeakImplHelper<
+ datatransfer::clipboard::XClipboardListener >
+{
+public:
+ SfxClipboardChangeListener( SfxViewShell* pView, const uno::Reference< datatransfer::clipboard::XClipboardNotifier >& xClpbrdNtfr );
+
+ // XEventListener
+ virtual void SAL_CALL disposing( const lang::EventObject& rEventObject ) override;
+
+ // XClipboardListener
+ virtual void SAL_CALL changedContents( const datatransfer::clipboard::ClipboardEvent& rEventObject ) override;
+
+ void DisconnectViewShell() { m_pViewShell = nullptr; }
+ void ChangedContents();
+
+ enum AsyncExecuteCmd
+ {
+ ASYNCEXECUTE_CMD_DISPOSING,
+ ASYNCEXECUTE_CMD_CHANGEDCONTENTS
+ };
+
+ struct AsyncExecuteInfo
+ {
+ AsyncExecuteInfo( AsyncExecuteCmd eCmd, SfxClipboardChangeListener* pListener ) :
+ m_eCmd( eCmd ), m_xListener( pListener ) {}
+
+ AsyncExecuteCmd m_eCmd;
+ rtl::Reference<SfxClipboardChangeListener> m_xListener;
+ };
+
+private:
+ SfxViewShell* m_pViewShell;
+ uno::Reference< datatransfer::clipboard::XClipboardNotifier > m_xClpbrdNtfr;
+ uno::Reference< lang::XComponent > m_xCtrl;
+
+ DECL_STATIC_LINK( SfxClipboardChangeListener, AsyncExecuteHdl_Impl, void*, void );
+};
+
+SfxClipboardChangeListener::SfxClipboardChangeListener( SfxViewShell* pView, const uno::Reference< datatransfer::clipboard::XClipboardNotifier >& xClpbrdNtfr )
+ : m_pViewShell( nullptr ), m_xClpbrdNtfr( xClpbrdNtfr ), m_xCtrl(pView->GetController())
+{
+ if ( m_xCtrl.is() )
+ {
+ m_xCtrl->addEventListener( uno::Reference < lang::XEventListener > ( static_cast < lang::XEventListener* >( this ) ) );
+ m_pViewShell = pView;
+ }
+ if ( m_xClpbrdNtfr.is() )
+ {
+ m_xClpbrdNtfr->addClipboardListener( uno::Reference< datatransfer::clipboard::XClipboardListener >(
+ static_cast< datatransfer::clipboard::XClipboardListener* >( this )));
+ }
+}
+
+void SfxClipboardChangeListener::ChangedContents()
+{
+ const SolarMutexGuard aGuard;
+ if (!m_pViewShell)
+ return;
+
+ SfxBindings& rBind = m_pViewShell->GetViewFrame()->GetBindings();
+ rBind.Invalidate(SID_PASTE);
+ rBind.Invalidate(SID_PASTE_SPECIAL);
+ rBind.Invalidate(SID_CLIPBOARD_FORMAT_ITEMS);
+
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ // In the future we might send the payload as well.
+ SfxLokHelper::notifyAllViews(LOK_CALLBACK_CLIPBOARD_CHANGED, "");
+ }
+}
+
+IMPL_STATIC_LINK( SfxClipboardChangeListener, AsyncExecuteHdl_Impl, void*, p, void )
+{
+ AsyncExecuteInfo* pAsyncExecuteInfo = static_cast<AsyncExecuteInfo*>(p);
+ if ( pAsyncExecuteInfo )
+ {
+ if ( pAsyncExecuteInfo->m_xListener.is() )
+ {
+ if ( pAsyncExecuteInfo->m_eCmd == ASYNCEXECUTE_CMD_DISPOSING )
+ pAsyncExecuteInfo->m_xListener->DisconnectViewShell();
+ else if ( pAsyncExecuteInfo->m_eCmd == ASYNCEXECUTE_CMD_CHANGEDCONTENTS )
+ pAsyncExecuteInfo->m_xListener->ChangedContents();
+ }
+ }
+ delete pAsyncExecuteInfo;
+}
+
+void SAL_CALL SfxClipboardChangeListener::disposing( const lang::EventObject& /*rEventObject*/ )
+{
+ // Either clipboard or ViewShell is going to be destroyed -> no interest in listening anymore
+ uno::Reference< lang::XComponent > xCtrl( m_xCtrl );
+ uno::Reference< datatransfer::clipboard::XClipboardNotifier > xNotify( m_xClpbrdNtfr );
+
+ uno::Reference< datatransfer::clipboard::XClipboardListener > xThis( static_cast< datatransfer::clipboard::XClipboardListener* >( this ));
+ if ( xCtrl.is() )
+ xCtrl->removeEventListener( uno::Reference < lang::XEventListener > ( static_cast < lang::XEventListener* >( this )));
+ if ( xNotify.is() )
+ xNotify->removeClipboardListener( xThis );
+
+ // Make asynchronous call to avoid locking SolarMutex which is the
+ // root for many deadlocks, especially in conjunction with the "Windows"
+ // based single thread apartment clipboard code!
+ AsyncExecuteInfo* pInfo = new AsyncExecuteInfo( ASYNCEXECUTE_CMD_DISPOSING, this );
+ if (!Application::PostUserEvent( LINK( nullptr, SfxClipboardChangeListener, AsyncExecuteHdl_Impl ), pInfo ))
+ delete pInfo;
+}
+
+void SAL_CALL SfxClipboardChangeListener::changedContents( const datatransfer::clipboard::ClipboardEvent& )
+{
+ // Make asynchronous call to avoid locking SolarMutex which is the
+ // root for many deadlocks, especially in conjunction with the "Windows"
+ // based single thread apartment clipboard code!
+ AsyncExecuteInfo* pInfo = new AsyncExecuteInfo( ASYNCEXECUTE_CMD_CHANGEDCONTENTS, this );
+ if (!Application::PostUserEvent( LINK( nullptr, SfxClipboardChangeListener, AsyncExecuteHdl_Impl ), pInfo ))
+ delete pInfo;
+}
+
+sal_uInt32 SfxViewShell_Impl::m_nLastViewShellId = 0;
+
+SfxViewShell_Impl::SfxViewShell_Impl(SfxViewShellFlags const nFlags, ViewShellDocId nDocId)
+: m_bHasPrintOptions(nFlags & SfxViewShellFlags::HAS_PRINTOPTIONS)
+, m_nFamily(0xFFFF) // undefined, default set by TemplateDialog
+, m_pLibreOfficeKitViewCallback(nullptr)
+, m_bTiledSearching(false)
+, m_nViewShellId(SfxViewShell_Impl::m_nLastViewShellId++)
+, m_nDocId(nDocId)
+{
+}
+
+SfxViewShell_Impl::~SfxViewShell_Impl()
+{
+}
+
+std::vector< SfxInPlaceClient* >& SfxViewShell_Impl::GetIPClients_Impl()
+{
+ return maIPClients;
+}
+
+SFX_IMPL_SUPERCLASS_INTERFACE(SfxViewShell,SfxShell)
+
+void SfxViewShell::InitInterface_Impl()
+{
+}
+
+
+/** search for a filter name dependent on type and module
+ */
+static OUString impl_retrieveFilterNameFromTypeAndModule(
+ const css::uno::Reference< css::container::XContainerQuery >& rContainerQuery,
+ const OUString& rType,
+ const OUString& rModuleIdentifier,
+ const sal_Int32 nFlags )
+{
+ // Retrieve filter from type
+ css::uno::Sequence< css::beans::NamedValue > aQuery {
+ { "Type", css::uno::Any( rType ) },
+ { "DocumentService", css::uno::Any( rModuleIdentifier ) }
+ };
+
+ css::uno::Reference< css::container::XEnumeration > xEnumeration =
+ rContainerQuery->createSubSetEnumerationByProperties( aQuery );
+
+ OUString aFoundFilterName;
+ while ( xEnumeration->hasMoreElements() )
+ {
+ ::comphelper::SequenceAsHashMap aFilterPropsHM( xEnumeration->nextElement() );
+ OUString aFilterName = aFilterPropsHM.getUnpackedValueOrDefault(
+ "Name",
+ OUString() );
+
+ sal_Int32 nFilterFlags = aFilterPropsHM.getUnpackedValueOrDefault(
+ "Flags",
+ sal_Int32( 0 ) );
+
+ if ( nFilterFlags & nFlags )
+ {
+ aFoundFilterName = aFilterName;
+ break;
+ }
+ }
+
+ return aFoundFilterName;
+}
+
+namespace {
+
+/** search for an internal typename, which map to the current app module
+ and map also to a "family" of file formats as e.g. PDF/MS Doc/OOo Doc.
+ */
+enum ETypeFamily
+{
+ E_MS_DOC,
+ E_OOO_DOC
+};
+
+}
+
+static OUString impl_searchFormatTypeForApp(const css::uno::Reference< css::frame::XFrame >& xFrame ,
+ ETypeFamily eTypeFamily)
+{
+ try
+ {
+ css::uno::Reference< css::uno::XComponentContext > xContext (::comphelper::getProcessComponentContext());
+ css::uno::Reference< css::frame::XModuleManager2 > xModuleManager(css::frame::ModuleManager::create(xContext));
+
+ OUString sModule = xModuleManager->identify(xFrame);
+ OUString sType ;
+
+ switch(eTypeFamily)
+ {
+ case E_MS_DOC:
+ {
+ if ( sModule == "com.sun.star.text.TextDocument" )
+ sType = "writer_MS_Word_2007";
+ else
+ if ( sModule == "com.sun.star.sheet.SpreadsheetDocument" )
+ sType = "MS Excel 2007 XML";
+ else
+ if ( sModule == "com.sun.star.presentation.PresentationDocument" )
+ sType = "MS PowerPoint 2007 XML";
+ }
+ break;
+
+ case E_OOO_DOC:
+ {
+ if ( sModule == "com.sun.star.text.TextDocument" )
+ sType = "writer8";
+ else
+ if ( sModule == "com.sun.star.sheet.SpreadsheetDocument" )
+ sType = "calc8";
+ else
+ if ( sModule == "com.sun.star.drawing.DrawingDocument" )
+ sType = "draw8";
+ else
+ if ( sModule == "com.sun.star.presentation.PresentationDocument" )
+ sType = "impress8";
+ }
+ break;
+ }
+
+ return sType;
+ }
+ catch (const css::uno::RuntimeException&)
+ {
+ throw;
+ }
+ catch (const css::uno::Exception&)
+ {
+ }
+
+ return OUString();
+}
+
+void SfxViewShell::NewIPClient_Impl( SfxInPlaceClient *pIPClient )
+{
+ pImpl->GetIPClients_Impl().push_back(pIPClient);
+}
+
+void SfxViewShell::IPClientGone_Impl( SfxInPlaceClient const *pIPClient )
+{
+ std::vector< SfxInPlaceClient* >& pClients = pImpl->GetIPClients_Impl();
+
+ auto it = std::find(pClients.begin(), pClients.end(), pIPClient);
+ if (it != pClients.end())
+ pClients.erase( it );
+}
+
+
+void SfxViewShell::ExecMisc_Impl( SfxRequest &rReq )
+{
+ const sal_uInt16 nId = rReq.GetSlot();
+ switch( nId )
+ {
+ case SID_STYLE_FAMILY :
+ {
+ const SfxUInt16Item* pItem = rReq.GetArg<SfxUInt16Item>(nId);
+ if (pItem)
+ {
+ pImpl->m_nFamily = pItem->GetValue();
+ }
+ break;
+ }
+ case SID_ACTIVATE_STYLE_APPLY:
+ {
+ uno::Reference< frame::XFrame > xFrame =
+ GetViewFrame()->GetFrame().GetFrameInterface();
+
+ Reference< beans::XPropertySet > xPropSet( xFrame, UNO_QUERY );
+ Reference< frame::XLayoutManager > xLayoutManager;
+ if ( xPropSet.is() )
+ {
+ try
+ {
+ Any aValue = xPropSet->getPropertyValue("LayoutManager");
+ aValue >>= xLayoutManager;
+ if ( xLayoutManager.is() )
+ {
+ uno::Reference< ui::XUIElement > xElement = xLayoutManager->getElement( "private:resource/toolbar/textobjectbar" );
+ if(!xElement.is())
+ {
+ xElement = xLayoutManager->getElement( "private:resource/toolbar/frameobjectbar" );
+ }
+ if(!xElement.is())
+ {
+ xElement = xLayoutManager->getElement( "private:resource/toolbar/oleobjectbar" );
+ }
+ if(xElement.is())
+ {
+ uno::Reference< awt::XWindow > xWin( xElement->getRealInterface(), uno::UNO_QUERY_THROW );
+ VclPtr<vcl::Window> pWin = VCLUnoHelper::GetWindow( xWin );
+ ToolBox* pTextToolbox = dynamic_cast< ToolBox* >( pWin.get() );
+ if( pTextToolbox )
+ {
+ ToolBox::ImplToolItems::size_type nItemCount = pTextToolbox->GetItemCount();
+ for( ToolBox::ImplToolItems::size_type nItem = 0; nItem < nItemCount; ++nItem )
+ {
+ ToolBoxItemId nItemId = pTextToolbox->GetItemId( nItem );
+ const OUString& rCommand = pTextToolbox->GetItemCommand( nItemId );
+ if (rCommand == ".uno:StyleApply")
+ {
+ vcl::Window* pItemWin = pTextToolbox->GetItemWindow( nItemId );
+ if( pItemWin )
+ pItemWin->GrabFocus();
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ catch (const Exception&)
+ {
+ }
+ }
+ rReq.Done();
+ }
+ break;
+
+ case SID_MAIL_SENDDOCASMS:
+ case SID_MAIL_SENDDOCASOOO:
+ case SID_MAIL_SENDDOCASPDF:
+ case SID_MAIL_SENDDOC:
+ case SID_MAIL_SENDDOCASFORMAT:
+ {
+ SfxObjectShell* pDoc = GetObjectShell();
+ if ( pDoc && pDoc->QueryHiddenInformation(
+ HiddenWarningFact::WhenSaving, GetViewFrame()->GetFrameWeld() ) != RET_YES )
+ break;
+
+
+ SfxMailModel aModel;
+ OUString aDocType;
+
+ const SfxStringItem* pMailRecipient = rReq.GetArg<SfxStringItem>(SID_MAIL_RECIPIENT);
+ if ( pMailRecipient )
+ {
+ OUString aRecipient( pMailRecipient->GetValue() );
+ OUString aMailToStr("mailto:");
+
+ if ( aRecipient.startsWith( aMailToStr ) )
+ aRecipient = aRecipient.copy( aMailToStr.getLength() );
+ aModel.AddToAddress( aRecipient );
+ }
+ const SfxStringItem* pMailDocType = rReq.GetArg<SfxStringItem>(SID_TYPE_NAME);
+ if ( pMailDocType )
+ aDocType = pMailDocType->GetValue();
+
+ uno::Reference < frame::XFrame > xFrame( pFrame->GetFrame().GetFrameInterface() );
+ SfxMailModel::SendMailResult eResult = SfxMailModel::SEND_MAIL_ERROR;
+
+ if ( nId == SID_MAIL_SENDDOC )
+ eResult = aModel.SaveAndSend( xFrame, OUString() );
+ else if ( nId == SID_MAIL_SENDDOCASPDF )
+ eResult = aModel.SaveAndSend( xFrame, "pdf_Portable_Document_Format");
+ else if ( nId == SID_MAIL_SENDDOCASMS )
+ {
+ aDocType = impl_searchFormatTypeForApp(xFrame, E_MS_DOC);
+ if (!aDocType.isEmpty())
+ eResult = aModel.SaveAndSend( xFrame, aDocType );
+ }
+ else if ( nId == SID_MAIL_SENDDOCASOOO )
+ {
+ aDocType = impl_searchFormatTypeForApp(xFrame, E_OOO_DOC);
+ if (!aDocType.isEmpty())
+ eResult = aModel.SaveAndSend( xFrame, aDocType );
+ }
+
+ if ( eResult == SfxMailModel::SEND_MAIL_ERROR )
+ {
+ weld::Window* pWin = SfxGetpApp()->GetTopWindow();
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(pWin,
+ VclMessageType::Info, VclButtonsType::Ok,
+ SfxResId(STR_ERROR_SEND_MAIL)));
+ xBox->run();
+ rReq.Ignore();
+ }
+ else
+ rReq.Done();
+ }
+ break;
+
+ case SID_BLUETOOTH_SENDDOC:
+ {
+ SfxBluetoothModel aModel;
+ SfxObjectShell* pDoc = GetObjectShell();
+ if ( pDoc && pDoc->QueryHiddenInformation(
+ HiddenWarningFact::WhenSaving, GetViewFrame()->GetFrameWeld() ) != RET_YES )
+ break;
+ uno::Reference < frame::XFrame > xFrame( pFrame->GetFrame().GetFrameInterface() );
+ SfxMailModel::SendMailResult eResult = aModel.SaveAndSend( xFrame );
+ if( eResult == SfxMailModel::SEND_MAIL_ERROR )
+ {
+ weld::Window* pWin = SfxGetpApp()->GetTopWindow();
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(pWin,
+ VclMessageType::Info, VclButtonsType::Ok,
+ SfxResId(STR_ERROR_SEND_MAIL)));
+ xBox->run();
+ rReq.Ignore();
+ }
+ else
+ rReq.Done();
+ }
+ break;
+
+ // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ case SID_WEBHTML:
+ {
+ css::uno::Reference< lang::XMultiServiceFactory > xSMGR(::comphelper::getProcessServiceFactory(), css::uno::UNO_SET_THROW);
+ css::uno::Reference< uno::XComponentContext > xContext(::comphelper::getProcessComponentContext(), css::uno::UNO_SET_THROW);
+ css::uno::Reference< css::frame::XFrame > xFrame( pFrame->GetFrame().GetFrameInterface() );
+ css::uno::Reference< css::frame::XModel > xModel;
+
+ css::uno::Reference< css::frame::XModuleManager2 > xModuleManager( css::frame::ModuleManager::create(xContext) );
+
+ OUString aModule;
+ try
+ {
+ aModule = xModuleManager->identify( xFrame );
+ }
+ catch (const css::uno::RuntimeException&)
+ {
+ throw;
+ }
+ catch (const css::uno::Exception&)
+ {
+ }
+
+ 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
+ css::uno::Reference< css::frame::XStorable > xStorable( xModel, css::uno::UNO_QUERY );
+ if ( xModel.is() && xStorable.is() )
+ {
+ OUString aFilterName;
+ OUString aTypeName( "generic_HTML" );
+ OUString aFileName;
+
+ OUString aLocation = xStorable->getLocation();
+ INetURLObject aFileObj( aLocation );
+
+ bool bPrivateProtocol = ( aFileObj.GetProtocol() == INetProtocol::PrivSoffice );
+ bool bHasLocation = !aLocation.isEmpty() && !bPrivateProtocol;
+
+ css::uno::Reference< css::container::XContainerQuery > xContainerQuery(
+ xSMGR->createInstance( "com.sun.star.document.FilterFactory" ),
+ css::uno::UNO_QUERY_THROW );
+
+ // Retrieve filter from type
+
+ sal_Int32 nFilterFlags = 0x00000002; // export
+ aFilterName = impl_retrieveFilterNameFromTypeAndModule( xContainerQuery, aTypeName, aModule, nFilterFlags );
+ if ( aFilterName.isEmpty() )
+ {
+ // Draw/Impress uses a different type. 2nd chance try to use alternative type name
+ aFilterName = impl_retrieveFilterNameFromTypeAndModule(
+ xContainerQuery, "graphic_HTML", aModule, nFilterFlags );
+ }
+
+ // No filter found => error
+ // No type and no location => error
+ if ( aFilterName.isEmpty() || aTypeName.isEmpty())
+ {
+ rReq.Done();
+ return;
+ }
+
+ // Use provided save file name. If empty determine file name
+ if ( !bHasLocation )
+ {
+ // Create a default file name with the correct extension
+ aFileName = "webpreview";
+ }
+ else
+ {
+ // Determine file name from model
+ INetURLObject aFObj( xStorable->getLocation() );
+ aFileName = aFObj.getName( INetURLObject::LAST_SEGMENT, true, INetURLObject::DecodeMechanism::NONE );
+ }
+
+ OSL_ASSERT( !aFilterName.isEmpty() );
+ OSL_ASSERT( !aFileName.isEmpty() );
+
+ // Creates a temporary directory to store our predefined file into it (for the
+ // flatpak case, create it in XDG_CACHE_HOME instead of /tmp for technical reasons,
+ // so that it can be accessed by the browser running outside the sandbox):
+ OUString * parent = nullptr;
+ if (flatpak::isFlatpak() && !flatpak::createTemporaryHtmlDirectory(&parent))
+ {
+ SAL_WARN("sfx.view", "cannot create Flatpak html temp dir");
+ }
+ ::utl::TempFile aTempDir( parent, true );
+
+ INetURLObject aFilePathObj( aTempDir.GetURL() );
+ aFilePathObj.insertName( aFileName );
+ aFilePathObj.setExtension( u"htm" );
+
+ OUString aFileURL = aFilePathObj.GetMainURL( INetURLObject::DecodeMechanism::NONE );
+
+ css::uno::Sequence< css::beans::PropertyValue > aArgs{
+ comphelper::makePropertyValue("FilterName", aFilterName)
+ };
+
+ // Store document in the html format
+ try
+ {
+ xStorable->storeToURL( aFileURL, aArgs );
+ }
+ catch (const io::IOException&)
+ {
+ rReq.Done();
+ return;
+ }
+
+ sfx2::openUriExternally(aFileURL, true, rReq.GetFrameWeld());
+ rReq.Done(true);
+ break;
+ }
+ else
+ {
+ rReq.Done();
+ return;
+ }
+ }
+ }
+}
+
+
+void SfxViewShell::GetState_Impl( SfxItemSet &rSet )
+{
+
+ SfxWhichIter aIter( rSet );
+ SfxObjectShell *pSh = GetViewFrame()->GetObjectShell();
+ for ( sal_uInt16 nSID = aIter.FirstWhich(); nSID; nSID = aIter.NextWhich() )
+ {
+ switch ( nSID )
+ {
+
+ case SID_BLUETOOTH_SENDDOC:
+ case SID_MAIL_SENDDOC:
+ case SID_MAIL_SENDDOCASFORMAT:
+ case SID_MAIL_SENDDOCASMS:
+ case SID_MAIL_SENDDOCASOOO:
+ case SID_MAIL_SENDDOCASPDF:
+ {
+#if HAVE_FEATURE_MACOSX_SANDBOX
+ rSet.DisableItem(nSID);
+#endif
+ if (pSh && pSh->isExportLocked())
+ rSet.DisableItem(nSID);
+ break;
+ }
+ case SID_WEBHTML:
+ {
+ if (pSh && pSh->isExportLocked())
+ rSet.DisableItem(nSID);
+ break;
+ }
+ // Printer functions
+ case SID_PRINTDOC:
+ case SID_PRINTDOCDIRECT:
+ case SID_SETUPPRINTER:
+ case SID_PRINTER_NAME:
+ {
+ if (Application::GetSettings().GetMiscSettings().GetDisablePrinting()
+ || (pSh && pSh->isPrintLocked()))
+ {
+ rSet.DisableItem(nSID);
+ break;
+ }
+
+ SfxPrinter *pPrinter = GetPrinter();
+
+ if ( SID_PRINTDOCDIRECT == nSID )
+ {
+ OUString aPrinterName;
+ if ( pPrinter != nullptr )
+ aPrinterName = pPrinter->GetName();
+ else
+ aPrinterName = Printer::GetDefaultPrinterName();
+ if ( !aPrinterName.isEmpty() )
+ {
+ uno::Reference < frame::XFrame > xFrame( pFrame->GetFrame().GetFrameInterface() );
+
+ auto aProperties = vcl::CommandInfoProvider::GetCommandProperties(".uno:PrintDefault",
+ vcl::CommandInfoProvider::GetModuleIdentifier(xFrame));
+ OUString val = vcl::CommandInfoProvider::GetLabelForCommand(aProperties) +
+ " (" + aPrinterName + ")";
+
+ rSet.Put( SfxStringItem( SID_PRINTDOCDIRECT, val ) );
+ }
+ }
+ break;
+ }
+ case SID_STYLE_FAMILY :
+ {
+ rSet.Put( SfxUInt16Item( SID_STYLE_FAMILY, pImpl->m_nFamily ) );
+ break;
+ }
+ }
+ }
+}
+
+void SfxViewShell::SetZoomFactor( const Fraction &rZoomX,
+ const Fraction &rZoomY )
+{
+ DBG_ASSERT( GetWindow(), "no window" );
+ MapMode aMap( GetWindow()->GetMapMode() );
+ aMap.SetScaleX( rZoomX );
+ aMap.SetScaleY( rZoomY );
+ GetWindow()->SetMapMode( aMap );
+}
+
+ErrCode SfxViewShell::DoVerb(sal_Int32 /*nVerb*/)
+
+/* [Description]
+
+ Virtual Method used to perform a Verb on a selected Object.
+ Since this Object is only known by the derived classes, they must override
+ DoVerb.
+*/
+
+{
+ return ERRCODE_SO_NOVERBS;
+}
+
+void SfxViewShell::OutplaceActivated( bool bActive )
+{
+ if ( !bActive )
+ GetFrame()->GetFrame().Appear();
+}
+
+void SfxViewShell::UIActivating( SfxInPlaceClient* /*pClient*/ )
+{
+ uno::Reference < frame::XFrame > xOwnFrame( pFrame->GetFrame().GetFrameInterface() );
+ uno::Reference < frame::XFramesSupplier > xParentFrame = xOwnFrame->getCreator();
+ if ( xParentFrame.is() )
+ xParentFrame->setActiveFrame( xOwnFrame );
+
+ pFrame->GetBindings().HidePopups();
+ pFrame->GetDispatcher()->Update_Impl( true );
+}
+
+
+void SfxViewShell::UIDeactivated( SfxInPlaceClient* /*pClient*/ )
+{
+ if ( !pFrame->GetFrame().IsClosing_Impl() || SfxViewFrame::Current() != pFrame )
+ pFrame->GetDispatcher()->Update_Impl( true );
+ pFrame->GetBindings().HidePopups(false);
+
+ pFrame->GetBindings().InvalidateAll(true);
+}
+
+
+SfxInPlaceClient* SfxViewShell::FindIPClient
+(
+ const uno::Reference < embed::XEmbeddedObject >& xObj,
+ vcl::Window* pObjParentWin
+) const
+{
+ std::vector< SfxInPlaceClient* >& rClients = pImpl->GetIPClients_Impl();
+ if ( rClients.empty() )
+ return nullptr;
+
+ if( !pObjParentWin )
+ pObjParentWin = GetWindow();
+ for (SfxInPlaceClient* pIPClient : rClients)
+ {
+ if ( pIPClient->GetObject() == xObj && pIPClient->GetEditWin() == pObjParentWin )
+ return pIPClient;
+ }
+
+ return nullptr;
+}
+
+
+SfxInPlaceClient* SfxViewShell::GetIPClient() const
+{
+ return GetUIActiveClient();
+}
+
+
+SfxInPlaceClient* SfxViewShell::GetUIActiveIPClient_Impl() const
+{
+ // this method is needed as long as SFX still manages the border space for ChildWindows (see SfxFrame::Resize)
+ std::vector< SfxInPlaceClient* >& rClients = pImpl->GetIPClients_Impl();
+ if ( rClients.empty() )
+ return nullptr;
+
+ for (SfxInPlaceClient* pIPClient : rClients)
+ {
+ if ( pIPClient->IsUIActive() )
+ return pIPClient;
+ }
+
+ return nullptr;
+}
+
+SfxInPlaceClient* SfxViewShell::GetUIActiveClient() const
+{
+ std::vector< SfxInPlaceClient* >& rClients = pImpl->GetIPClients_Impl();
+ if ( rClients.empty() )
+ return nullptr;
+
+ const bool bIsTiledRendering = comphelper::LibreOfficeKit::isActive();
+
+ for (SfxInPlaceClient* pIPClient : rClients)
+ {
+ if ( pIPClient->IsObjectUIActive() || ( bIsTiledRendering && pIPClient->IsObjectInPlaceActive() ) )
+ return pIPClient;
+ }
+
+ return nullptr;
+}
+
+
+void SfxViewShell::Activate( bool bMDI )
+{
+ if ( bMDI )
+ {
+ SfxObjectShell *pSh = GetViewFrame()->GetObjectShell();
+ if ( pSh->GetModel().is() )
+ pSh->GetModel()->setCurrentController( GetViewFrame()->GetFrame().GetController() );
+
+ SetCurrentDocument();
+ }
+}
+
+
+void SfxViewShell::Deactivate(bool /*bMDI*/)
+{
+}
+
+
+void SfxViewShell::Move()
+
+/* [Description]
+
+ This virtual Method is called when the window displayed in the
+ SfxViewShell gets a StarView-Move() notification.
+
+ This base implementation does not have to be called. .
+
+ [Note]
+
+ This Method can be used to cancel a selection, in order to catch the
+ mouse movement which is due to moving a window.
+
+ For now the notification does not work In-Place.
+*/
+
+{
+}
+
+
+void SfxViewShell::OuterResizePixel
+(
+ const Point& /*rToolOffset*/,// Upper left corner Tools in Frame-Window
+ const Size& /*rSize*/ // All available sizes.
+)
+
+/* [Description]
+
+ Override this Method to be able to react to the size-change of
+ the View. Thus the View is defined as the Edit window and also the
+ attached Tools are defined (for example the ruler).
+
+ The Edit window must not be changed either in size or position.
+
+ The Vis-Area of SfxObjectShell, its scale and position can be changed
+ here. The main use is to change the size of the Vis-Area.
+
+ If the Border is changed due to the new calculation then this has to be set
+ by <SfxViewShell::SetBorderPixel(const SvBorder&)>. The Positioning of Tools
+ is only allowed after the calling of 'SetBorderPixel'.
+
+ [Example]
+
+ void AppViewSh::OuterViewResizePixel( const Point &rOfs, const Size &rSz )
+ {
+ // Calculate Tool position and size externally, do not set!
+ // (due to the following Border calculation)
+ Point aHLinPos...; Size aHLinSz...;
+ ...
+
+ // Calculate and Set a Border of Tools which matches rSize.
+ SvBorder aBorder...
+ SetBorderPixel( aBorder ); // Allow Positioning from here on.
+
+ // Arrange Tools
+ pHLin->SetPosSizePixel( aHLinPos, aHLinSz );
+ ...
+ }
+
+ [Cross-reference]
+
+ <SfxViewShell::InnerResizePixel(const Point&,const Size& rSize)>
+*/
+
+{
+ SetBorderPixel( SvBorder() );
+}
+
+
+void SfxViewShell::InnerResizePixel
+(
+ const Point& /*rToolOffset*/,// Upper left corner Tools in Frame-Window
+ const Size& /*rSize*/, // All available sizes.
+ bool
+)
+
+/* [Description]
+
+ Override this Method to be able to react to the size-change of
+ the Edit window.
+
+ The Edit window must not be changed either in size or position.
+ Neither the Vis-Area of SfxObjectShell nor its scale or position are
+ allowed to be changed
+
+ If the Border is changed due to the new calculation then is has to be set
+ by <SfxViewShell::SetBorderPixel(const SvBorder&)>.
+ The Positioning of Tools is only allowed after the calling of
+ 'SetBorderPixel'.
+
+
+ [Note]
+
+ void AppViewSh::InnerViewResizePixel( const Point &rOfs, const Size &rSz )
+ {
+ // Calculate Tool position and size internally, do not set!
+ // (due to the following Border calculation)
+ Point aHLinPos...; Size aHLinSz...;
+ ...
+
+ // Calculate and Set a Border of Tools which matches rSize.
+ SvBorder aBorder...
+ SetBorderPixel( aBorder ); // Allow Positioning from here on.
+
+ // Arrange Tools
+ pHLin->SetPosSizePixel( aHLinPos, aHLinSz );
+ ...
+ }
+
+ [Cross-reference]
+
+ <SfxViewShell::OuterResizePixel(const Point&,const Size& rSize)>
+*/
+
+{
+ SetBorderPixel( SvBorder() );
+}
+
+
+void SfxViewShell::InvalidateBorder()
+{
+ DBG_ASSERT( GetViewFrame(), "SfxViewShell without SfxViewFrame" );
+
+ GetViewFrame()->InvalidateBorderImpl( this );
+ if (pImpl->m_pController.is())
+ {
+ pImpl->m_pController->BorderWidthsChanged_Impl();
+ }
+}
+
+
+void SfxViewShell::SetBorderPixel( const SvBorder &rBorder )
+{
+ DBG_ASSERT( GetViewFrame(), "SfxViewShell without SfxViewFrame" );
+
+ GetViewFrame()->SetBorderPixelImpl( this, rBorder );
+
+ // notify related controller that border size is changed
+ if (pImpl->m_pController.is())
+ {
+ pImpl->m_pController->BorderWidthsChanged_Impl();
+ }
+}
+
+
+const SvBorder& SfxViewShell::GetBorderPixel() const
+{
+ DBG_ASSERT( GetViewFrame(), "SfxViewShell without SfxViewFrame" );
+
+ return GetViewFrame()->GetBorderPixelImpl();
+}
+
+
+void SfxViewShell::SetWindow
+(
+ vcl::Window* pViewPort // For example Null pointer in the Destructor.
+)
+
+/* [Description]
+
+ With this method the SfxViewShell is set in the data window. This is
+ needed for the in-place container and for restoring the proper focus.
+
+ Even in-place-active the conversion of the ViewPort Windows is forbidden.
+*/
+
+{
+ if( pWindow == pViewPort )
+ return;
+
+ // Disconnect existing IP-Clients if possible
+ DisconnectAllClients();
+
+ // Switch View-Port
+ bool bHadFocus = pWindow && pWindow->HasChildPathFocus( true );
+ pWindow = pViewPort;
+
+ if( pWindow )
+ {
+ // Disable automatic GUI mirroring (right-to-left) for document windows
+ pWindow->EnableRTL( false );
+ }
+
+ if ( bHadFocus && pWindow )
+ pWindow->GrabFocus();
+ //TODO/CLEANUP
+ //Do we still need this Method?!
+ //SfxGetpApp()->GrabFocus( pWindow );
+}
+
+ViewShellDocId SfxViewShell::mnCurrentDocId(0);
+
+SfxViewShell::SfxViewShell
+(
+ SfxViewFrame* pViewFrame, /* <SfxViewFrame>, which will be
+ displayed in this View */
+ SfxViewShellFlags nFlags /* See <SfxViewShell-Flags> */
+)
+
+: SfxShell(this)
+, pImpl( new SfxViewShell_Impl(nFlags, SfxViewShell::mnCurrentDocId) )
+, pFrame(pViewFrame)
+, pWindow(nullptr)
+, bNoNewWindow( nFlags & SfxViewShellFlags::NO_NEWWINDOW )
+, mbPrinterSettingsModified(false)
+, maLOKLanguageTag(LANGUAGE_NONE)
+, maLOKLocale(LANGUAGE_NONE)
+, maLOKDeviceFormFactor(LOKDeviceFormFactor::UNKNOWN)
+{
+ SetMargin( pViewFrame->GetMargin_Impl() );
+
+ SetPool( &pViewFrame->GetObjectShell()->GetPool() );
+ StartListening(*pViewFrame->GetObjectShell());
+
+ // Insert into list
+ std::vector<SfxViewShell*> &rViewArr = SfxGetpApp()->GetViewShells_Impl();
+ rViewArr.push_back(this);
+
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ maLOKLanguageTag = SfxLokHelper::getDefaultLanguage();
+ maLOKLocale = SfxLokHelper::getDefaultLanguage();
+
+ maLOKDeviceFormFactor = SfxLokHelper::getDeviceFormFactor();
+
+ vcl::Window* pFrameWin = pViewFrame->GetWindow().GetFrameWindow();
+ if (pFrameWin && !pFrameWin->GetLOKNotifier())
+ pFrameWin->SetLOKNotifier(this, true);
+ }
+}
+
+
+SfxViewShell::~SfxViewShell()
+{
+ // Remove from list
+ const SfxViewShell *pThis = this;
+ std::vector<SfxViewShell*> &rViewArr = SfxGetpApp()->GetViewShells_Impl();
+ auto it = std::find( rViewArr.begin(), rViewArr.end(), pThis );
+ rViewArr.erase( it );
+
+ if ( pImpl->xClipboardListener.is() )
+ {
+ pImpl->xClipboardListener->DisconnectViewShell();
+ pImpl->xClipboardListener = nullptr;
+ }
+
+ if (pImpl->m_pController.is())
+ {
+ pImpl->m_pController->ReleaseShell_Impl();
+ pImpl->m_pController.clear();
+ }
+
+ vcl::Window* pFrameWin = GetViewFrame()->GetWindow().GetFrameWindow();
+ if (pFrameWin && pFrameWin->GetLOKNotifier() == this)
+ pFrameWin->ReleaseLOKNotifier();
+}
+
+bool SfxViewShell::PrepareClose
+(
+ bool bUI // TRUE: Allow Dialog and so on, FALSE: silent-mode
+)
+{
+ if (GetViewFrame()->GetWindow().GetLOKNotifier() == this)
+ GetViewFrame()->GetWindow().ReleaseLOKNotifier();
+
+ SfxPrinter *pPrinter = GetPrinter();
+ if ( pPrinter && pPrinter->IsPrinting() )
+ {
+ if ( bUI )
+ {
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(GetViewFrame()->GetFrameWeld(),
+ VclMessageType::Info, VclButtonsType::Ok,
+ SfxResId(STR_CANT_CLOSE)));
+ xBox->run();
+ }
+
+ return false;
+ }
+
+ if( GetViewFrame()->IsInModalMode() )
+ return false;
+
+ if( bUI && GetViewFrame()->GetDispatcher()->IsLocked() )
+ return false;
+
+ return true;
+}
+
+
+SfxViewShell* SfxViewShell::Current()
+{
+ SfxViewFrame *pCurrent = SfxViewFrame::Current();
+ return pCurrent ? pCurrent->GetViewShell() : nullptr;
+}
+
+
+SfxViewShell* SfxViewShell::Get( const Reference< XController>& i_rController )
+{
+ if ( !i_rController.is() )
+ return nullptr;
+
+ for ( SfxViewShell* pViewShell = SfxViewShell::GetFirst( false );
+ pViewShell;
+ pViewShell = SfxViewShell::GetNext( *pViewShell, false )
+ )
+ {
+ if ( pViewShell->GetController() == i_rController )
+ return pViewShell;
+ }
+ return nullptr;
+}
+
+
+SdrView* SfxViewShell::GetDrawView() const
+
+/* [Description]
+
+ This virtual Method has to be overloaded by the sub classes, to be able
+ make the Property-Editor available.
+
+ The default implementation does always return zero.
+*/
+
+{
+ return nullptr;
+}
+
+
+OUString SfxViewShell::GetSelectionText
+(
+ bool /*bCompleteWords*/, /* FALSE (default)
+ Only the actual selected text is returned.
+
+ TRUE
+ The selected text is expanded so that only
+ whole words are returned. As word separators
+ these are used: white spaces and punctuation
+ ".,;" and single and double quotes.
+ */
+ bool /*bOnlyASample*/ /* used by some dialogs to avoid constructing monster strings e.g. in calc */
+)
+
+/* [Description]
+
+ Override this Method to return a text that
+ is included in the current selection. This is for example used when
+ sending emails.
+
+ When called with "CompleteWords == TRUE", it is for example sufficient
+ with having the Cursor positioned somewhere within a URL in-order
+ to have the entire URL returned.
+*/
+
+{
+ return OUString();
+}
+
+
+bool SfxViewShell::HasSelection( bool ) const
+
+/* [Description]
+
+ With this virtual Method can a for example a Dialog be queried, to
+ check if something is selected in the current view. If the Parameter
+ is <BOOL> TRUE then it is checked whether some text is selected.
+*/
+
+{
+ return false;
+}
+
+void SfxViewShell::AddSubShell( SfxShell& rShell )
+{
+ pImpl->aArr.push_back(&rShell);
+ SfxDispatcher *pDisp = pFrame->GetDispatcher();
+ if ( pDisp->IsActive(*this) )
+ {
+ pDisp->Push(rShell);
+ pDisp->Flush();
+ }
+}
+
+void SfxViewShell::RemoveSubShell( SfxShell* pShell )
+{
+ SfxDispatcher *pDisp = pFrame->GetDispatcher();
+ if ( !pShell )
+ {
+ size_t nCount = pImpl->aArr.size();
+ if ( pDisp->IsActive(*this) )
+ {
+ for(size_t n = nCount; n > 0; --n)
+ pDisp->Pop(*pImpl->aArr[n - 1]);
+ pDisp->Flush();
+ }
+ pImpl->aArr.clear();
+ }
+ else
+ {
+ SfxShellArr_Impl::iterator i = std::find(pImpl->aArr.begin(), pImpl->aArr.end(), pShell);
+ if(i != pImpl->aArr.end())
+ {
+ pImpl->aArr.erase(i);
+ if(pDisp->IsActive(*this))
+ {
+ pDisp->RemoveShell_Impl(*pShell);
+ pDisp->Flush();
+ }
+ }
+ }
+}
+
+SfxShell* SfxViewShell::GetSubShell( sal_uInt16 nNo )
+{
+ sal_uInt16 nCount = pImpl->aArr.size();
+ if(nNo < nCount)
+ return pImpl->aArr[nCount - nNo - 1];
+ return nullptr;
+}
+
+void SfxViewShell::PushSubShells_Impl( bool bPush )
+{
+ SfxDispatcher *pDisp = pFrame->GetDispatcher();
+ if ( bPush )
+ {
+ for (auto const& elem : pImpl->aArr)
+ pDisp->Push(*elem);
+ }
+ else if(!pImpl->aArr.empty())
+ {
+ SfxShell& rPopUntil = *pImpl->aArr[0];
+ if ( pDisp->GetShellLevel( rPopUntil ) != USHRT_MAX )
+ pDisp->Pop( rPopUntil, SfxDispatcherPopFlags::POP_UNTIL );
+ }
+
+ pDisp->Flush();
+}
+
+
+void SfxViewShell::WriteUserData( OUString&, bool )
+{
+}
+
+
+void SfxViewShell::ReadUserData(const OUString&, bool )
+{
+}
+
+void SfxViewShell::ReadUserDataSequence ( const uno::Sequence < beans::PropertyValue >& )
+{
+}
+
+void SfxViewShell::WriteUserDataSequence ( uno::Sequence < beans::PropertyValue >& )
+{
+}
+
+
+// returns the first shell of spec. type viewing the specified doc.
+SfxViewShell* SfxViewShell::GetFirst
+(
+ bool bOnlyVisible,
+ const std::function< bool ( const SfxViewShell* ) >& isViewShell
+)
+{
+ // search for a SfxViewShell of the specified type
+ std::vector<SfxViewShell*> &rShells = SfxGetpApp()->GetViewShells_Impl();
+ for (SfxViewShell* pShell : rShells)
+ {
+ if ( pShell )
+ {
+ // This code used to check that the frame exists in the other list,
+ // because of https://bz.apache.org/ooo/show_bug.cgi?id=62084, with the explanation:
+ // sometimes dangling SfxViewShells exist that point to a dead SfxViewFrame
+ // these ViewShells shouldn't be accessible anymore
+ // a destroyed ViewFrame is not in the ViewFrame array anymore, so checking this array helps
+ // That doesn't seem to be needed anymore, but keep an assert, just in case.
+ assert(std::find(SfxGetpApp()->GetViewFrames_Impl().begin(), SfxGetpApp()->GetViewFrames_Impl().end(),
+ pShell->GetViewFrame()) != SfxGetpApp()->GetViewFrames_Impl().end());
+ if ( ( !bOnlyVisible || pShell->GetViewFrame()->IsVisible() ) && (!isViewShell || isViewShell(pShell)))
+ return pShell;
+ }
+ }
+
+ return nullptr;
+}
+
+
+// returns the next shell of spec. type viewing the specified doc.
+
+SfxViewShell* SfxViewShell::GetNext
+(
+ const SfxViewShell& rPrev,
+ bool bOnlyVisible,
+ const std::function<bool ( const SfxViewShell* )>& isViewShell
+)
+{
+ std::vector<SfxViewShell*> &rShells = SfxGetpApp()->GetViewShells_Impl();
+ size_t nPos;
+ for ( nPos = 0; nPos < rShells.size(); ++nPos )
+ if ( rShells[nPos] == &rPrev )
+ break;
+
+ for ( ++nPos; nPos < rShells.size(); ++nPos )
+ {
+ SfxViewShell *pShell = rShells[nPos];
+ if ( pShell )
+ {
+ assert(std::find(SfxGetpApp()->GetViewFrames_Impl().begin(), SfxGetpApp()->GetViewFrames_Impl().end(),
+ pShell->GetViewFrame()) != SfxGetpApp()->GetViewFrames_Impl().end());
+ if ( ( !bOnlyVisible || pShell->GetViewFrame()->IsVisible() ) && (!isViewShell || isViewShell(pShell)) )
+ return pShell;
+ }
+ }
+
+ return nullptr;
+}
+
+
+void SfxViewShell::Notify( SfxBroadcaster& rBC,
+ const SfxHint& rHint )
+{
+ const SfxEventHint* pEventHint = dynamic_cast<const SfxEventHint*>(&rHint);
+ if ( !(pEventHint && pEventHint->GetEventId() == SfxEventHintId::LoadFinished) )
+ return;
+
+ if ( !GetController().is() )
+ return;
+
+ // avoid access to dangling ViewShells
+ auto &rFrames = SfxGetpApp()->GetViewFrames_Impl();
+ for (SfxViewFrame* frame : rFrames)
+ {
+ if ( frame == GetViewFrame() && &rBC == GetObjectShell() )
+ {
+ SfxItemSet* pSet = GetObjectShell()->GetMedium()->GetItemSet();
+ const SfxUnoAnyItem* pItem = SfxItemSet::GetItem<SfxUnoAnyItem>(pSet, SID_VIEW_DATA, false);
+ if ( pItem )
+ {
+ pImpl->m_pController->restoreViewData( pItem->GetValue() );
+ pSet->ClearItem( SID_VIEW_DATA );
+ }
+ break;
+ }
+ }
+}
+
+bool SfxViewShell::ExecKey_Impl(const KeyEvent& aKey)
+{
+ if (!pImpl->m_xAccExec)
+ {
+ pImpl->m_xAccExec = ::svt::AcceleratorExecute::createAcceleratorHelper();
+ pImpl->m_xAccExec->init(::comphelper::getProcessComponentContext(),
+ pFrame->GetFrame().GetFrameInterface());
+ }
+
+ return pImpl->m_xAccExec->execute(aKey.GetKeyCode());
+}
+
+void SfxViewShell::setLibreOfficeKitViewCallback(SfxLokCallbackInterface* pCallback)
+{
+ pImpl->m_pLibreOfficeKitViewCallback = nullptr;
+ pImpl->m_pLibreOfficeKitViewCallback = pCallback;
+
+ afterCallbackRegistered();
+
+ if (!pImpl->m_pLibreOfficeKitViewCallback)
+ return;
+
+ // Ask other views to tell us about their cursors.
+ SfxViewShell* pViewShell = SfxViewShell::GetFirst();
+ while (pViewShell)
+ {
+ if (pViewShell->GetDocId() == GetDocId())
+ pViewShell->NotifyCursor(this);
+ pViewShell = SfxViewShell::GetNext(*pViewShell);
+ }
+}
+
+static bool ignoreLibreOfficeKitViewCallback(int nType, const SfxViewShell_Impl* pImpl)
+{
+ if (!comphelper::LibreOfficeKit::isActive())
+ return true;
+
+ if (comphelper::LibreOfficeKit::isTiledPainting())
+ {
+ switch (nType)
+ {
+ case LOK_CALLBACK_FORM_FIELD_BUTTON:
+ case LOK_CALLBACK_TEXT_SELECTION:
+ case LOK_CALLBACK_COMMENT:
+ break;
+ default:
+ // Reject e.g. invalidate during paint.
+ return true;
+ }
+ }
+
+ if (pImpl->m_bTiledSearching)
+ {
+ switch (nType)
+ {
+ case LOK_CALLBACK_TEXT_SELECTION:
+ case LOK_CALLBACK_TEXT_VIEW_SELECTION:
+ case LOK_CALLBACK_TEXT_SELECTION_START:
+ case LOK_CALLBACK_TEXT_SELECTION_END:
+ case LOK_CALLBACK_GRAPHIC_SELECTION:
+ case LOK_CALLBACK_GRAPHIC_VIEW_SELECTION:
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void SfxViewShell::libreOfficeKitViewInvalidateTilesCallback(const tools::Rectangle* pRect, int nPart) const
+{
+ if (ignoreLibreOfficeKitViewCallback(LOK_CALLBACK_INVALIDATE_TILES, pImpl.get()))
+ return;
+ if (pImpl->m_pLibreOfficeKitViewCallback)
+ pImpl->m_pLibreOfficeKitViewCallback->libreOfficeKitViewInvalidateTilesCallback(pRect, nPart);
+ else
+ SAL_INFO(
+ "sfx.view",
+ "SfxViewShell::libreOfficeKitViewInvalidateTilesCallback no callback set!");
+}
+
+void SfxViewShell::libreOfficeKitViewCallbackWithViewId(int nType, const char* pPayload, int nViewId) const
+{
+ if (ignoreLibreOfficeKitViewCallback(nType, pImpl.get()))
+ return;
+ if (pImpl->m_pLibreOfficeKitViewCallback)
+ pImpl->m_pLibreOfficeKitViewCallback->libreOfficeKitViewCallbackWithViewId(nType, pPayload, nViewId);
+ else
+ SAL_INFO(
+ "sfx.view",
+ "SfxViewShell::libreOfficeKitViewCallbackWithViewId no callback set! Dropped payload of type "
+ << lokCallbackTypeToString(nType) << ": [" << pPayload << ']');
+}
+
+void SfxViewShell::libreOfficeKitViewCallback(int nType, const char* pPayload) const
+{
+ if (ignoreLibreOfficeKitViewCallback(nType, pImpl.get()))
+ return;
+ if (pImpl->m_pLibreOfficeKitViewCallback)
+ pImpl->m_pLibreOfficeKitViewCallback->libreOfficeKitViewCallback(nType, pPayload);
+ else
+ SAL_INFO(
+ "sfx.view",
+ "SfxViewShell::libreOfficeKitViewCallback no callback set! Dropped payload of type "
+ << lokCallbackTypeToString(nType) << ": [" << pPayload << ']');
+}
+
+void SfxViewShell::libreOfficeKitViewUpdatedCallback(int nType) const
+{
+ if (ignoreLibreOfficeKitViewCallback(nType, pImpl.get()))
+ return;
+ if (pImpl->m_pLibreOfficeKitViewCallback)
+ pImpl->m_pLibreOfficeKitViewCallback->libreOfficeKitViewUpdatedCallback(nType);
+ else
+ SAL_INFO(
+ "sfx.view",
+ "SfxViewShell::libreOfficeKitViewUpdatedCallback no callback set! Dropped payload of type "
+ << lokCallbackTypeToString(nType));
+}
+
+void SfxViewShell::libreOfficeKitViewUpdatedCallbackPerViewId(int nType, int nViewId, int nSourceViewId) const
+{
+ if (ignoreLibreOfficeKitViewCallback(nType, pImpl.get()))
+ return;
+ if (pImpl->m_pLibreOfficeKitViewCallback)
+ pImpl->m_pLibreOfficeKitViewCallback->libreOfficeKitViewUpdatedCallbackPerViewId(nType, nViewId, nSourceViewId);
+ else
+ SAL_INFO(
+ "sfx.view",
+ "SfxViewShell::libreOfficeKitViewUpdatedCallbackPerViewId no callback set! Dropped payload of type "
+ << lokCallbackTypeToString(nType));
+}
+
+void SfxViewShell::afterCallbackRegistered()
+{
+}
+
+void SfxViewShell::flushPendingLOKInvalidateTiles()
+{
+ // SfxViewShell itself does not delay any tile invalidations.
+}
+
+OString SfxViewShell::getLOKPayload(int nType, int /*nViewId*/, bool* /*ignore*/) const
+{
+ // SfxViewShell itself currently doesn't handle any updated-payload types.
+ SAL_WARN("sfx.view", "SfxViewShell::getLOKPayload unhandled type " << lokCallbackTypeToString(nType));
+ abort();
+}
+
+vcl::Window* SfxViewShell::GetEditWindowForActiveOLEObj() const
+{
+ vcl::Window* pEditWin = nullptr;
+ SfxInPlaceClient* pIPClient = GetIPClient();
+ if (pIPClient)
+ {
+ pEditWin = pIPClient->GetEditWin();
+ }
+ return pEditWin;
+}
+
+void SfxViewShell::SetLOKLanguageTag(const OUString& rBcp47LanguageTag)
+{
+ LanguageTag aTag(rBcp47LanguageTag, true);
+
+ css::uno::Sequence<OUString> inst(officecfg::Setup::Office::InstalledLocales::get()->getElementNames());
+ LanguageTag aFallbackTag = LanguageTag(getInstalledLocaleForSystemUILanguage(inst, /* bRequestInstallIfMissing */ false, rBcp47LanguageTag), true).makeFallback();
+
+ // If we want de-CH, and the de localisation is available, we don't want to use de-DE as then
+ // the magic in Translate::get() won't turn ess-zet into double s. Possibly other similar cases?
+ if (comphelper::LibreOfficeKit::isActive() && aTag.getLanguage() == aFallbackTag.getLanguage())
+ maLOKLanguageTag = aTag;
+ else
+ maLOKLanguageTag = aFallbackTag;
+}
+
+void SfxViewShell::SetLOKLocale(const OUString& rBcp47LanguageTag)
+{
+ maLOKLocale = LanguageTag(rBcp47LanguageTag, true).makeFallback();
+}
+
+void SfxViewShell::NotifyCursor(SfxViewShell* /*pViewShell*/) const
+{
+}
+
+void SfxViewShell::setTiledSearching(bool bTiledSearching)
+{
+ pImpl->m_bTiledSearching = bTiledSearching;
+}
+
+int SfxViewShell::getPart() const
+{
+ return 0;
+}
+
+ViewShellId SfxViewShell::GetViewShellId() const
+{
+ return pImpl->m_nViewShellId;
+}
+
+void SfxViewShell::SetCurrentDocId(ViewShellDocId nId)
+{
+ mnCurrentDocId = nId;
+}
+
+ViewShellDocId SfxViewShell::GetDocId() const
+{
+ assert(pImpl->m_nDocId >= ViewShellDocId(0) && "m_nDocId should have been initialized, but it is invalid.");
+ return pImpl->m_nDocId;
+}
+
+void SfxViewShell::NotifyOtherViews(int nType, const OString& rKey, const OString& rPayload)
+{
+ SfxLokHelper::notifyOtherViews(this, nType, rKey, rPayload);
+}
+
+void SfxViewShell::NotifyOtherView(OutlinerViewShell* pOther, int nType, const OString& rKey, const OString& rPayload)
+{
+ auto pOtherShell = dynamic_cast<SfxViewShell*>(pOther);
+ if (!pOtherShell)
+ return;
+
+ SfxLokHelper::notifyOtherView(this, pOtherShell, nType, rKey, rPayload);
+}
+
+void SfxViewShell::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SfxViewShell"));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("id"), BAD_CAST(OString::number(static_cast<sal_Int32>(GetViewShellId())).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+bool SfxViewShell::KeyInput( const KeyEvent &rKeyEvent )
+
+/* [Description]
+
+ This Method executes the KeyEvent 'rKeyEvent' of the Keys (Accelerator)
+ configured either direct or indirect (for example by the Application)
+ in the SfxViewShell.
+
+ [Return value]
+
+ bool TRUE
+ The Key (Accelerator) is configured and the
+ associated Handler was called
+
+ FALSE
+ The Key (Accelerator) is not configured and
+ subsequently no Handler was called
+
+ [Cross-reference]
+
+ <SfxApplication::KeyInput(const KeyEvent&)>
+*/
+{
+ return ExecKey_Impl(rKeyEvent);
+}
+
+bool SfxViewShell::GlobalKeyInput_Impl( const KeyEvent &rKeyEvent )
+{
+ return ExecKey_Impl(rKeyEvent);
+}
+
+
+void SfxViewShell::ShowCursor( bool /*bOn*/ )
+
+/* [Description]
+
+ Subclasses must override this Method so that SFx can switch the
+ Cursor on and off, for example while a <SfxProgress> is running.
+*/
+
+{
+}
+
+
+void SfxViewShell::ResetAllClients_Impl( SfxInPlaceClient const *pIP )
+{
+
+ std::vector< SfxInPlaceClient* >& rClients = pImpl->GetIPClients_Impl();
+ if ( rClients.empty() )
+ return;
+
+ for (SfxInPlaceClient* pIPClient : rClients)
+ {
+ if( pIPClient != pIP )
+ pIPClient->ResetObject();
+ }
+}
+
+
+void SfxViewShell::DisconnectAllClients()
+{
+ std::vector< SfxInPlaceClient* >& rClients = pImpl->GetIPClients_Impl();
+ if ( rClients.empty() )
+ return;
+
+ for ( size_t n = 0; n < rClients.size(); )
+ // clients will remove themselves from the list
+ delete rClients.at( n );
+}
+
+
+void SfxViewShell::QueryObjAreaPixel( tools::Rectangle& ) const
+{
+}
+
+
+void SfxViewShell::VisAreaChanged()
+{
+ std::vector< SfxInPlaceClient* >& rClients = pImpl->GetIPClients_Impl();
+ if ( rClients.empty() )
+ return;
+
+ for (SfxInPlaceClient* pIPClient : rClients)
+ {
+ if ( pIPClient->IsObjectInPlaceActive() )
+ // client is active, notify client that the VisArea might have changed
+ pIPClient->VisAreaChanged();
+ }
+}
+
+
+void SfxViewShell::CheckIPClient_Impl(
+ SfxInPlaceClient const *const pIPClient, const tools::Rectangle& rVisArea)
+{
+ if ( GetObjectShell()->IsInClose() )
+ return;
+
+ bool bAlwaysActive =
+ ( ( pIPClient->GetObjectMiscStatus() & embed::EmbedMisc::EMBED_ACTIVATEIMMEDIATELY ) != 0 );
+ bool bActiveWhenVisible =
+ ( pIPClient->GetObjectMiscStatus() & embed::EmbedMisc::MS_EMBED_ACTIVATEWHENVISIBLE ) != 0;
+
+ // this method is called when a client is created
+ if (pIPClient->IsObjectInPlaceActive())
+ return;
+
+ // object in client is currently not active
+ // check if the object wants to be activated always or when it becomes at least partially visible
+ // TODO/LATER: maybe we should use the scaled area instead of the ObjArea?!
+ if (bAlwaysActive || (bActiveWhenVisible && rVisArea.Overlaps(pIPClient->GetObjArea())))
+ {
+ try
+ {
+ pIPClient->GetObject()->changeState( embed::EmbedStates::INPLACE_ACTIVE );
+ }
+ catch (const uno::Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("sfx.view", "SfxViewShell::CheckIPClient_Impl");
+ }
+ }
+}
+
+
+SfxObjectShell* SfxViewShell::GetObjectShell()
+{
+ return pFrame ? pFrame->GetObjectShell() : nullptr;
+}
+
+
+Reference< XModel > SfxViewShell::GetCurrentDocument() const
+{
+ Reference< XModel > xDocument;
+
+ const SfxObjectShell* pDocShell( const_cast< SfxViewShell* >( this )->GetObjectShell() );
+ OSL_ENSURE( pDocShell, "SfxViewFrame::GetCurrentDocument: no DocShell!?" );
+ if ( pDocShell )
+ xDocument = pDocShell->GetModel();
+ return xDocument;
+}
+
+
+void SfxViewShell::SetCurrentDocument() const
+{
+ uno::Reference< frame::XModel > xDocument( GetCurrentDocument() );
+ if ( xDocument.is() )
+ SfxObjectShell::SetCurrentComponent( xDocument );
+}
+
+
+const Size& SfxViewShell::GetMargin() const
+{
+ return pImpl->aMargin;
+}
+
+
+void SfxViewShell::SetMargin( const Size& rSize )
+{
+ // the default margin was verified using www.apple.com !!
+ Size aMargin = rSize;
+ if ( aMargin.Width() == -1 )
+ aMargin.setWidth( DEFAULT_MARGIN_WIDTH );
+ if ( aMargin.Height() == -1 )
+ aMargin.setHeight( DEFAULT_MARGIN_HEIGHT );
+
+ if ( aMargin != pImpl->aMargin )
+ {
+ pImpl->aMargin = aMargin;
+ MarginChanged();
+ }
+}
+
+void SfxViewShell::MarginChanged()
+{
+}
+
+void SfxViewShell::JumpToMark( const OUString& rMark )
+{
+ SfxStringItem aMarkItem( SID_JUMPTOMARK, rMark );
+ GetViewFrame()->GetDispatcher()->ExecuteList(
+ SID_JUMPTOMARK,
+ SfxCallMode::SYNCHRON|SfxCallMode::RECORD,
+ { &aMarkItem });
+}
+
+void SfxViewShell::SetController( SfxBaseController* pController )
+{
+ pImpl->m_pController = pController;
+
+ // there should be no old listener, but if there is one, it should be disconnected
+ if ( pImpl->xClipboardListener.is() )
+ pImpl->xClipboardListener->DisconnectViewShell();
+
+ pImpl->xClipboardListener = new SfxClipboardChangeListener( this, GetClipboardNotifier() );
+}
+
+Reference < XController > SfxViewShell::GetController() const
+{
+ return pImpl->m_pController;
+}
+
+SfxBaseController* SfxViewShell::GetBaseController_Impl() const
+{
+ return pImpl->m_pController.get();
+}
+
+void SfxViewShell::AddContextMenuInterceptor_Impl( const uno::Reference< ui::XContextMenuInterceptor >& xInterceptor )
+{
+ std::unique_lock g(pImpl->aMutex);
+ pImpl->aInterceptorContainer.addInterface( g, xInterceptor );
+}
+
+void SfxViewShell::RemoveContextMenuInterceptor_Impl( const uno::Reference< ui::XContextMenuInterceptor >& xInterceptor )
+{
+ std::unique_lock g(pImpl->aMutex);
+ pImpl->aInterceptorContainer.removeInterface( g, xInterceptor );
+}
+
+bool SfxViewShell::TryContextMenuInterception(const css::uno::Reference<css::awt::XPopupMenu>& rIn,
+ const OUString& rMenuIdentifier,
+ css::uno::Reference<css::awt::XPopupMenu>& rOut,
+ ui::ContextMenuExecuteEvent aEvent)
+{
+ rOut.clear();
+ bool bModified = false;
+
+ // create container from menu
+ aEvent.ActionTriggerContainer = ::framework::ActionTriggerHelper::CreateActionTriggerContainerFromMenu(
+ rIn, &rMenuIdentifier);
+
+ // get selection from controller
+ aEvent.Selection.set( GetController(), uno::UNO_QUERY );
+
+ // call interceptors
+ std::unique_lock g(pImpl->aMutex);
+ std::vector<uno::Reference< ui::XContextMenuInterceptor>> aInterceptors =
+ pImpl->aInterceptorContainer.getElements(g);
+ g.unlock();
+ for (const auto & rListener : aInterceptors )
+ {
+ try
+ {
+ ui::ContextMenuInterceptorAction eAction;
+ {
+ SolarMutexReleaser rel;
+ eAction = rListener->notifyContextMenuExecute( aEvent );
+ }
+ switch ( eAction )
+ {
+ case ui::ContextMenuInterceptorAction_CANCELLED :
+ // interceptor does not want execution
+ return false;
+ case ui::ContextMenuInterceptorAction_EXECUTE_MODIFIED :
+ // interceptor wants his modified menu to be executed
+ bModified = true;
+ break;
+ case ui::ContextMenuInterceptorAction_CONTINUE_MODIFIED :
+ // interceptor has modified menu, but allows for calling other interceptors
+ bModified = true;
+ continue;
+ case ui::ContextMenuInterceptorAction_IGNORED :
+ // interceptor is indifferent
+ continue;
+ default:
+ OSL_FAIL("Wrong return value of ContextMenuInterceptor!");
+ continue;
+ }
+ }
+ catch (...)
+ {
+ g.lock();
+ pImpl->aInterceptorContainer.removeInterface(g, rListener);
+ g.unlock();
+ }
+
+ break;
+ }
+
+ if (bModified)
+ {
+ // container was modified, create a new menu out of it
+ css::uno::Reference<uno::XComponentContext> xContext(::comphelper::getProcessComponentContext(), css::uno::UNO_SET_THROW);
+ rOut.set(xContext->getServiceManager()->createInstanceWithContext("com.sun.star.awt.PopupMenu", xContext), css::uno::UNO_QUERY_THROW);
+ ::framework::ActionTriggerHelper::CreateMenuFromActionTriggerContainer(rOut, aEvent.ActionTriggerContainer);
+ }
+
+ return true;
+}
+
+bool SfxViewShell::TryContextMenuInterception(const css::uno::Reference<css::awt::XPopupMenu>& rPopupMenu,
+ const OUString& rMenuIdentifier, css::ui::ContextMenuExecuteEvent aEvent)
+{
+ bool bModified = false;
+
+ // create container from menu
+ aEvent.ActionTriggerContainer = ::framework::ActionTriggerHelper::CreateActionTriggerContainerFromMenu(
+ rPopupMenu, &rMenuIdentifier);
+
+ // get selection from controller
+ aEvent.Selection = css::uno::Reference< css::view::XSelectionSupplier >( GetController(), css::uno::UNO_QUERY );
+
+ // call interceptors
+ std::unique_lock g(pImpl->aMutex);
+ std::vector<uno::Reference< ui::XContextMenuInterceptor>> aInterceptors =
+ pImpl->aInterceptorContainer.getElements(g);
+ g.unlock();
+ for (const auto & rListener : aInterceptors )
+ {
+ try
+ {
+ css::ui::ContextMenuInterceptorAction eAction;
+ {
+ SolarMutexReleaser rel;
+ eAction = rListener->notifyContextMenuExecute( aEvent );
+ }
+ switch ( eAction )
+ {
+ case css::ui::ContextMenuInterceptorAction_CANCELLED:
+ // interceptor does not want execution
+ return false;
+ case css::ui::ContextMenuInterceptorAction_EXECUTE_MODIFIED:
+ // interceptor wants his modified menu to be executed
+ bModified = true;
+ break;
+ case css::ui::ContextMenuInterceptorAction_CONTINUE_MODIFIED:
+ // interceptor has modified menu, but allows for calling other interceptors
+ bModified = true;
+ continue;
+ case css::ui::ContextMenuInterceptorAction_IGNORED:
+ // interceptor is indifferent
+ continue;
+ default:
+ SAL_WARN( "sfx.view", "Wrong return value of ContextMenuInterceptor!" );
+ continue;
+ }
+ }
+ catch (...)
+ {
+ g.lock();
+ pImpl->aInterceptorContainer.removeInterface(g, rListener);
+ g.unlock();
+ }
+
+ break;
+ }
+
+ if ( bModified )
+ {
+ rPopupMenu->clear();
+ ::framework::ActionTriggerHelper::CreateMenuFromActionTriggerContainer(rPopupMenu, aEvent.ActionTriggerContainer);
+ }
+
+ return true;
+}
+
+bool SfxViewShell::HandleNotifyEvent_Impl( NotifyEvent const & rEvent )
+{
+ if (pImpl->m_pController.is())
+ return pImpl->m_pController->HandleEvent_Impl( rEvent );
+ return false;
+}
+
+bool SfxViewShell::HasKeyListeners_Impl() const
+{
+ return (pImpl->m_pController.is())
+ && pImpl->m_pController->HasKeyListeners_Impl();
+}
+
+bool SfxViewShell::HasMouseClickListeners_Impl() const
+{
+ return (pImpl->m_pController.is())
+ && pImpl->m_pController->HasMouseClickListeners_Impl();
+}
+
+bool SfxViewShell::Escape()
+{
+ return GetViewFrame()->GetBindings().Execute( SID_TERMINATE_INPLACEACTIVATION );
+}
+
+Reference< view::XRenderable > SfxViewShell::GetRenderable()
+{
+ Reference< view::XRenderable >xRender;
+ SfxObjectShell* pObj = GetObjectShell();
+ if( pObj )
+ {
+ Reference< frame::XModel > xModel( pObj->GetModel() );
+ if( xModel.is() )
+ xRender.set( xModel, UNO_QUERY );
+ }
+ return xRender;
+}
+
+void SfxViewShell::notifyWindow(vcl::LOKWindowId nDialogId, const OUString& rAction, const std::vector<vcl::LOKPayloadItem>& rPayload) const
+{
+ SfxLokHelper::notifyWindow(this, nDialogId, rAction, rPayload);
+}
+
+uno::Reference< datatransfer::clipboard::XClipboardNotifier > SfxViewShell::GetClipboardNotifier() const
+{
+ uno::Reference< datatransfer::clipboard::XClipboardNotifier > xClipboardNotifier;
+ if ( GetViewFrame() )
+ xClipboardNotifier.set( GetViewFrame()->GetWindow().GetClipboard(), uno::UNO_QUERY );
+
+ return xClipboardNotifier;
+}
+
+void SfxViewShell::AddRemoveClipboardListener( const uno::Reference < datatransfer::clipboard::XClipboardListener >& rClp, bool bAdd )
+{
+ try
+ {
+ if ( GetViewFrame() )
+ {
+ uno::Reference< datatransfer::clipboard::XClipboard > xClipboard( GetViewFrame()->GetWindow().GetClipboard() );
+ if( xClipboard.is() )
+ {
+ uno::Reference< datatransfer::clipboard::XClipboardNotifier > xClpbrdNtfr( xClipboard, uno::UNO_QUERY );
+ if( xClpbrdNtfr.is() )
+ {
+ if( bAdd )
+ xClpbrdNtfr->addClipboardListener( rClp );
+ else
+ xClpbrdNtfr->removeClipboardListener( rClp );
+ }
+ }
+ }
+ }
+ catch (const uno::Exception&)
+ {
+ }
+}
+
+weld::Window* SfxViewShell::GetFrameWeld() const
+{
+ return pWindow ? pWindow->GetFrameWeld() : nullptr;
+}
+
+void SfxViewShell::setBlockedCommandList(const char* blockedCommandList)
+{
+ if(!mvLOKBlockedCommandList.empty())
+ return;
+
+ OUString BlockedListString(blockedCommandList, strlen(blockedCommandList), RTL_TEXTENCODING_UTF8);
+ OUString command = BlockedListString.getToken(0, ' ');
+ for (size_t i = 1; !command.isEmpty(); i++)
+ {
+ mvLOKBlockedCommandList.emplace(command);
+ command = BlockedListString.getToken(i, ' ');
+ }
+}
+
+bool SfxViewShell::isBlockedCommand(OUString command)
+{
+ return mvLOKBlockedCommandList.find(command) != mvLOKBlockedCommandList.end();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */