summaryrefslogtreecommitdiffstats
path: root/sfx2
diff options
context:
space:
mode:
Diffstat (limited to 'sfx2')
-rw-r--r--sfx2/AllLangMoTarget_sfx2.mk13
-rw-r--r--sfx2/CppunitTest_sfx2_classification.mk46
-rw-r--r--sfx2/CppunitTest_sfx2_controlleritem.mk25
-rw-r--r--sfx2/CppunitTest_sfx2_dialogs_test.mk69
-rw-r--r--sfx2/CppunitTest_sfx2_doc.mk46
-rw-r--r--sfx2/CppunitTest_sfx2_metadatable.mk37
-rw-r--r--sfx2/CppunitTest_sfx2_misc.mk44
-rw-r--r--sfx2/CppunitTest_sfx2_view.mk45
-rw-r--r--sfx2/CustomTarget_classification.mk28
-rw-r--r--sfx2/IwyuFilter_sfx2.yaml184
-rw-r--r--sfx2/JunitTest_sfx2_complex.mk62
-rw-r--r--sfx2/JunitTest_sfx2_unoapi.mk24
-rw-r--r--sfx2/Library_sfx.mk370
-rw-r--r--sfx2/Makefile7
-rw-r--r--sfx2/Module_sfx2.mk64
-rw-r--r--sfx2/Package_classification.mk23
-rw-r--r--sfx2/Package_emoji.mk16
-rw-r--r--sfx2/PythonTest_sfx2_python.mk21
-rw-r--r--sfx2/README.md30
-rw-r--r--sfx2/UIConfig_sfx.mk81
-rw-r--r--sfx2/UITest_sfx2_doc.mk20
-rw-r--r--sfx2/classification/CommonTypes.xsd103
-rw-r--r--sfx2/classification/README7
-rw-r--r--sfx2/classification/baf.xsd299
-rw-r--r--sfx2/classification/baf_loext.xsd19
-rw-r--r--sfx2/classification/example.xml89
-rw-r--r--sfx2/classification/example_ca-ES.xml71
-rw-r--r--sfx2/classification/example_fr-FR.xml84
-rw-r--r--sfx2/classification/example_hu-HU.xml71
-rw-r--r--sfx2/classification/example_nl-NL.xml71
-rw-r--r--sfx2/classification/example_pt-BR.xml89
-rw-r--r--sfx2/classification/example_sl-SI.xml76
-rw-r--r--sfx2/classification/example_zh-CN.xml89
-rw-r--r--sfx2/classification/xAL-types.xsd510
-rw-r--r--sfx2/classification/xAL.xsd671
-rw-r--r--sfx2/classification/xNL-types.xsd221
-rw-r--r--sfx2/classification/xNL.xsd283
-rw-r--r--sfx2/classification/xlink-2003-12-31.xsd89
-rw-r--r--sfx2/doc/sfx2doc.html77
-rw-r--r--sfx2/doc/sfx2doc0001.svg99
-rw-r--r--sfx2/emojiconfig/emoji.json26822
-rw-r--r--sfx2/inc/SfxRedactionHelper.hxx146
-rw-r--r--sfx2/inc/arrdecl.hxx30
-rw-r--r--sfx2/inc/autoredactdialog.hxx175
-rw-r--r--sfx2/inc/bitmaps.hlst94
-rw-r--r--sfx2/inc/bitset.hxx49
-rw-r--r--sfx2/inc/bluthsndapi.hxx28
-rw-r--r--sfx2/inc/charmapcontrol.hxx66
-rw-r--r--sfx2/inc/charmappopup.hxx41
-rw-r--r--sfx2/inc/checkin.hxx31
-rw-r--r--sfx2/inc/commandpopup/CommandPopup.hxx114
-rw-r--r--sfx2/inc/dinfdlg.hrc87
-rw-r--r--sfx2/inc/doctempl.hrc45
-rw-r--r--sfx2/inc/emojicontrol.hxx56
-rw-r--r--sfx2/inc/emojipopup.hxx41
-rw-r--r--sfx2/inc/emojiview.hxx76
-rw-r--r--sfx2/inc/emojiviewitem.hxx35
-rw-r--r--sfx2/inc/fwkhelper.hxx31
-rw-r--r--sfx2/inc/guisaveas.hxx87
-rw-r--r--sfx2/inc/inettbc.hxx64
-rw-r--r--sfx2/inc/notebookbar/NotebookbarTabControl.hxx48
-rw-r--r--sfx2/inc/pch/precompiled_sfx.cxx12
-rw-r--r--sfx2/inc/pch/precompiled_sfx.hxx486
-rw-r--r--sfx2/inc/preventduplicateinteraction.hxx329
-rw-r--r--sfx2/inc/recentdocsview.hxx112
-rw-r--r--sfx2/inc/saveastemplatedlg.hxx58
-rw-r--r--sfx2/inc/sfxbasecontroller_internal.hxx19
-rw-r--r--sfx2/inc/sidebar/ContextChangeBroadcaster.hxx67
-rw-r--r--sfx2/inc/sidebar/ContextList.hxx66
-rw-r--r--sfx2/inc/sidebar/ControllerFactory.hxx72
-rw-r--r--sfx2/inc/sidebar/DeckDescriptor.hxx53
-rw-r--r--sfx2/inc/sidebar/DeckLayouter.hxx47
-rw-r--r--sfx2/inc/sidebar/DeckTitleBar.hxx57
-rw-r--r--sfx2/inc/sidebar/PanelDescriptor.hxx51
-rw-r--r--sfx2/inc/sidebar/PanelTitleBar.hxx69
-rw-r--r--sfx2/inc/sidebar/SidebarToolBox.hxx82
-rw-r--r--sfx2/inc/sidebar/TitleBar.hxx70
-rw-r--r--sfx2/inc/sidebar/Tools.hxx56
-rw-r--r--sfx2/inc/sidebar/UnoDeck.hxx62
-rw-r--r--sfx2/inc/sidebar/UnoDecks.hxx54
-rw-r--r--sfx2/inc/sidebar/UnoPanel.hxx69
-rw-r--r--sfx2/inc/sidebar/UnoPanels.hxx57
-rw-r--r--sfx2/inc/sidebar/UnoSidebar.hxx54
-rw-r--r--sfx2/inc/sorgitm.hxx44
-rw-r--r--sfx2/inc/srchdlg.hxx73
-rw-r--r--sfx2/inc/strings.hxx57
-rw-r--r--sfx2/inc/templatecontaineritem.hxx27
-rw-r--r--sfx2/inc/templatedefaultview.hxx29
-rw-r--r--sfx2/inc/templateviewitem.hxx45
-rw-r--r--sfx2/inc/unoctitm.hxx153
-rw-r--r--sfx2/qa/complex/sfx2/DocumentEvents.java221
-rw-r--r--sfx2/qa/complex/sfx2/DocumentMetadataAccess.java1228
-rw-r--r--sfx2/qa/complex/sfx2/DocumentProperties.java516
-rw-r--r--sfx2/qa/complex/sfx2/GlobalEventBroadcaster.java249
-rw-r--r--sfx2/qa/complex/sfx2/UndoManager.java1459
-rw-r--r--sfx2/qa/complex/sfx2/testdocuments/CUSTOM.odtbin0 -> 1021 bytes
-rw-r--r--sfx2/qa/complex/sfx2/testdocuments/TEST.odtbin0 -> 13803 bytes
-rw-r--r--sfx2/qa/complex/sfx2/testdocuments/TESTRDFA.odtbin0 -> 7540 bytes
-rw-r--r--sfx2/qa/complex/sfx2/testdocuments/empty.rdf13
-rw-r--r--sfx2/qa/complex/sfx2/tools/TestDocument.java34
-rw-r--r--sfx2/qa/complex/sfx2/tools/WriterHelper.java210
-rw-r--r--sfx2/qa/complex/sfx2/undo/CalcDocumentTest.java113
-rw-r--r--sfx2/qa/complex/sfx2/undo/ChartDocumentTest.java257
-rw-r--r--sfx2/qa/complex/sfx2/undo/DocumentTest.java77
-rw-r--r--sfx2/qa/complex/sfx2/undo/DocumentTestBase.java44
-rw-r--r--sfx2/qa/complex/sfx2/undo/DrawDocumentTest.java35
-rw-r--r--sfx2/qa/complex/sfx2/undo/DrawingOrPresentationDocumentTest.java207
-rw-r--r--sfx2/qa/complex/sfx2/undo/ImpressDocumentTest.java35
-rw-r--r--sfx2/qa/complex/sfx2/undo/WriterDocumentTest.java121
-rw-r--r--sfx2/qa/cppunit/data/reload-page.odgbin0 -> 8824 bytes
-rw-r--r--sfx2/qa/cppunit/doc.cxx110
-rw-r--r--sfx2/qa/cppunit/misc/hello.odtbin0 -> 8159 bytes
-rw-r--r--sfx2/qa/cppunit/test_classification.cxx126
-rw-r--r--sfx2/qa/cppunit/test_controlleritem.cxx58
-rw-r--r--sfx2/qa/cppunit/test_metadatable.cxx248
-rw-r--r--sfx2/qa/cppunit/test_misc.cxx229
-rw-r--r--sfx2/qa/cppunit/view.cxx86
-rw-r--r--sfx2/qa/python/check_sidebar.py169
-rw-r--r--sfx2/qa/python/check_sidebar_registry.py89
-rw-r--r--sfx2/qa/uitest/doc/data/pdf-sign.pdfbin0 -> 2235 bytes
-rw-r--r--sfx2/qa/uitest/doc/objserv.py24
-rw-r--r--sfx2/qa/unit/data/sfx2-dialogs-test.txt72
-rw-r--r--sfx2/qa/unit/sfx2-dialogs-test.cxx58
-rw-r--r--sfx2/qa/unoapi/knownissues.xcl5
-rw-r--r--sfx2/qa/unoapi/sfx.sce4
-rw-r--r--sfx2/qa/unoapi/testdocuments/SfxStandaloneDocInfoObject.sdwbin0 -> 8192 bytes
-rw-r--r--sfx2/qa/unoapi/testdocuments/report.stwbin0 -> 11186 bytes
-rw-r--r--sfx2/qa/unoapi/testdocuments/report2.stwbin0 -> 11000 bytes
-rw-r--r--sfx2/sdi/appslots.sdi339
-rw-r--r--sfx2/sdi/docslots.sdi276
-rw-r--r--sfx2/sdi/frmslots.sdi318
-rw-r--r--sfx2/sdi/sfx.sdi5790
-rw-r--r--sfx2/sdi/sfxitems.sdi118
-rw-r--r--sfx2/sdi/sfxslots.sdi33
-rw-r--r--sfx2/sdi/viwslots.sdi93
-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
-rw-r--r--sfx2/uiconfig/ui/addtargetdialog.ui278
-rw-r--r--sfx2/uiconfig/ui/alienwarndialog.ui85
-rw-r--r--sfx2/uiconfig/ui/autoredactdialog.ui322
-rw-r--r--sfx2/uiconfig/ui/bookmarkdialog.ui141
-rw-r--r--sfx2/uiconfig/ui/bookmarkmenu.ui39
-rw-r--r--sfx2/uiconfig/ui/charmapcontrol.ui514
-rw-r--r--sfx2/uiconfig/ui/charviewmenu.ui25
-rw-r--r--sfx2/uiconfig/ui/checkin.ui154
-rw-r--r--sfx2/uiconfig/ui/classificationbox.ui49
-rw-r--r--sfx2/uiconfig/ui/cmisinfopage.ui47
-rw-r--r--sfx2/uiconfig/ui/cmisline.ui134
-rw-r--r--sfx2/uiconfig/ui/commandpopup.ui93
-rw-r--r--sfx2/uiconfig/ui/custominfopage.ui142
-rw-r--r--sfx2/uiconfig/ui/deck.ui132
-rw-r--r--sfx2/uiconfig/ui/descriptioninfopage.ui154
-rw-r--r--sfx2/uiconfig/ui/developmenttool.ui661
-rw-r--r--sfx2/uiconfig/ui/devtoolsmenu.ui17
-rw-r--r--sfx2/uiconfig/ui/documentfontspage.ui162
-rw-r--r--sfx2/uiconfig/ui/documentinfopage.ui483
-rw-r--r--sfx2/uiconfig/ui/documentpropertiesdialog.ui349
-rw-r--r--sfx2/uiconfig/ui/editdocumentdialog.ui70
-rw-r--r--sfx2/uiconfig/ui/editdurationdialog.ui364
-rw-r--r--sfx2/uiconfig/ui/emojicontrol.ui181
-rw-r--r--sfx2/uiconfig/ui/errorfindemaildialog.ui35
-rw-r--r--sfx2/uiconfig/ui/extrabutton.ui24
-rw-r--r--sfx2/uiconfig/ui/floatingrecord.ui64
-rw-r--r--sfx2/uiconfig/ui/helpbookmarkpage.ui103
-rw-r--r--sfx2/uiconfig/ui/helpcontentpage.ui71
-rw-r--r--sfx2/uiconfig/ui/helpcontrol.ui248
-rw-r--r--sfx2/uiconfig/ui/helpindexpage.ui135
-rw-r--r--sfx2/uiconfig/ui/helpmanual.ui79
-rw-r--r--sfx2/uiconfig/ui/helpsearchpage.ui175
-rw-r--r--sfx2/uiconfig/ui/helpwindow.ui199
-rw-r--r--sfx2/uiconfig/ui/infobar.ui187
-rw-r--r--sfx2/uiconfig/ui/inputdialog.ui128
-rw-r--r--sfx2/uiconfig/ui/licensedialog.ui98
-rw-r--r--sfx2/uiconfig/ui/linefragment.ui240
-rw-r--r--sfx2/uiconfig/ui/linkeditdialog.ui220
-rw-r--r--sfx2/uiconfig/ui/loadtemplatedialog.ui453
-rw-r--r--sfx2/uiconfig/ui/managestylepage.ui241
-rw-r--r--sfx2/uiconfig/ui/navigator.ui14
-rw-r--r--sfx2/uiconfig/ui/newstyle.ui212
-rw-r--r--sfx2/uiconfig/ui/notebookbar.ui95
-rw-r--r--sfx2/uiconfig/ui/notebookbarpopup.ui28
-rw-r--r--sfx2/uiconfig/ui/optprintpage.ui682
-rw-r--r--sfx2/uiconfig/ui/panel.ui121
-rw-r--r--sfx2/uiconfig/ui/password.ui366
-rw-r--r--sfx2/uiconfig/ui/printeroptionsdialog.ui91
-rw-r--r--sfx2/uiconfig/ui/querysavedialog.ui86
-rw-r--r--sfx2/uiconfig/ui/safemodequerydialog.ui69
-rw-r--r--sfx2/uiconfig/ui/saveastemplatedlg.ui263
-rw-r--r--sfx2/uiconfig/ui/searchdialog.ui200
-rw-r--r--sfx2/uiconfig/ui/securityinfopage.ui125
-rw-r--r--sfx2/uiconfig/ui/singletabdialog.ui87
-rw-r--r--sfx2/uiconfig/ui/startcenter.ui618
-rw-r--r--sfx2/uiconfig/ui/stylecontextmenu.ui49
-rw-r--r--sfx2/uiconfig/ui/tabbar.ui13
-rw-r--r--sfx2/uiconfig/ui/tabbarcontents.ui157
-rw-r--r--sfx2/uiconfig/ui/tabbutton.ui24
-rw-r--r--sfx2/uiconfig/ui/templatecategorydlg.ui227
-rw-r--r--sfx2/uiconfig/ui/templatedlg.ui514
-rw-r--r--sfx2/uiconfig/ui/templatepanel.ui315
-rw-r--r--sfx2/uiconfig/ui/urlbox.ui30
-rw-r--r--sfx2/uiconfig/ui/versioncommentdialog.ui168
-rw-r--r--sfx2/uiconfig/ui/versionscmis.ui256
-rw-r--r--sfx2/uiconfig/ui/versionsofdialog.ui371
-rw-r--r--sfx2/util/sfx.component100
-rw-r--r--sfx2/util/sfx.component.extended7
497 files changed, 196221 insertions, 0 deletions
diff --git a/sfx2/AllLangMoTarget_sfx2.mk b/sfx2/AllLangMoTarget_sfx2.mk
new file mode 100644
index 000000000..b0f7dc3ef
--- /dev/null
+++ b/sfx2/AllLangMoTarget_sfx2.mk
@@ -0,0 +1,13 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+$(eval $(call gb_AllLangMoTarget_AllLangMoTarget,sfx))
+
+$(eval $(call gb_AllLangMoTarget_set_polocation,sfx,sfx2))
+
+# vim: set noet sw=4 ts=4:
diff --git a/sfx2/CppunitTest_sfx2_classification.mk b/sfx2/CppunitTest_sfx2_classification.mk
new file mode 100644
index 000000000..303d3099d
--- /dev/null
+++ b/sfx2/CppunitTest_sfx2_classification.mk
@@ -0,0 +1,46 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+$(eval $(call gb_CppunitTest_CppunitTest,sfx2_classification))
+
+$(eval $(call gb_CppunitTest_add_exception_objects,sfx2_classification, \
+ sfx2/qa/cppunit/test_classification \
+))
+
+$(eval $(call gb_CppunitTest_use_sdk_api,sfx2_classification))
+
+$(eval $(call gb_CppunitTest_use_libraries,sfx2_classification, \
+ comphelper \
+ cppu \
+ cppuhelper \
+ test \
+ unotest \
+ vcl \
+ sal \
+ sfx \
+))
+
+$(eval $(call gb_CppunitTest_use_external,sfx2_classification,boost_headers))
+
+$(eval $(call gb_CppunitTest_use_ure,sfx2_classification))
+$(eval $(call gb_CppunitTest_use_vcl,sfx2_classification))
+
+$(eval $(call gb_CppunitTest_use_rdb,sfx2_classification,services))
+
+$(eval $(call gb_CppunitTest_use_configuration,sfx2_classification))
+
+$(eval $(call gb_CppunitTest_use_uiconfigs,sfx2_classification,\
+ svx \
+))
+
+$(eval $(call gb_CppunitTest_use_packages,sfx2_classification,\
+ sfx2_classification \
+))
+
+# vim: set noet sw=4 ts=4:
diff --git a/sfx2/CppunitTest_sfx2_controlleritem.mk b/sfx2/CppunitTest_sfx2_controlleritem.mk
new file mode 100644
index 000000000..f81ac1330
--- /dev/null
+++ b/sfx2/CppunitTest_sfx2_controlleritem.mk
@@ -0,0 +1,25 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+$(eval $(call gb_CppunitTest_CppunitTest,sfx2_controlleritem))
+$(eval $(call gb_CppunitTest_use_sdk_api,sfx2_controlleritem))
+$(eval $(call gb_CppunitTest_add_exception_objects,sfx2_controlleritem, \
+ sfx2/qa/cppunit/test_controlleritem \
+))
+$(eval $(call gb_CppunitTest_use_externals,sfx2_controlleritem, \
+ boost_headers \
+))
+$(eval $(call gb_CppunitTest_use_libraries,sfx2_controlleritem, \
+ cppu \
+ cppuhelper \
+ sal \
+ sfx \
+))
+
+# vim: set noet sw=4 ts=4:
diff --git a/sfx2/CppunitTest_sfx2_dialogs_test.mk b/sfx2/CppunitTest_sfx2_dialogs_test.mk
new file mode 100644
index 000000000..88646565d
--- /dev/null
+++ b/sfx2/CppunitTest_sfx2_dialogs_test.mk
@@ -0,0 +1,69 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#*************************************************************************
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+#*************************************************************************
+
+$(eval $(call gb_CppunitTest_CppunitScreenShot,sfx2_dialogs_test))
+
+$(eval $(call gb_CppunitTest_add_exception_objects,sfx2_dialogs_test, \
+ sfx2/qa/unit/sfx2-dialogs-test \
+))
+
+$(eval $(call gb_CppunitTest_use_sdk_api,sfx2_dialogs_test))
+
+$(eval $(call gb_CppunitTest_set_include,desktop_dialogs_test,\
+ -I$(SRCDIR)/sfx2/source/inc \
+ -I$(SRCDIR)/sfx2/inc \
+ $$(INCLUDE) \
+))
+
+$(eval $(call gb_CppunitTest_use_libraries,sfx2_dialogs_test, \
+ basegfx \
+ comphelper \
+ cppu \
+ cppuhelper \
+ drawinglayer \
+ editeng \
+ i18nlangtag \
+ i18nutil \
+ msfilter \
+ oox \
+ sal \
+ salhelper \
+ sax \
+ sfx \
+ sot \
+ svl \
+ svt \
+ test \
+ tl \
+ tk \
+ ucbhelper \
+ unotest \
+ utl \
+ vcl \
+ xo \
+))
+
+$(eval $(call gb_CppunitTest_use_external,sfx2_dialogs_test,boost_headers))
+
+$(eval $(call gb_CppunitTest_use_sdk_api,sfx2_dialogs_test))
+
+$(eval $(call gb_CppunitTest_use_ure,sfx2_dialogs_test))
+$(eval $(call gb_CppunitTest_use_vcl_non_headless_with_windows,sfx2_dialogs_test))
+
+$(eval $(call gb_CppunitTest_use_rdb,sfx2_dialogs_test,services))
+
+$(eval $(call gb_CppunitTest_use_configuration,sfx2_dialogs_test))
+
+$(eval $(call gb_CppunitTest_use_uiconfigs,sfx2_dialogs_test,\
+ sfx \
+))
+
+# vim: set noet sw=4 ts=4:
diff --git a/sfx2/CppunitTest_sfx2_doc.mk b/sfx2/CppunitTest_sfx2_doc.mk
new file mode 100644
index 000000000..bf181c232
--- /dev/null
+++ b/sfx2/CppunitTest_sfx2_doc.mk
@@ -0,0 +1,46 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#*************************************************************************
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+#*************************************************************************
+
+$(eval $(call gb_CppunitTest_CppunitTest,sfx2_doc))
+
+$(eval $(call gb_CppunitTest_use_externals,sfx2_doc,\
+ boost_headers \
+))
+
+$(eval $(call gb_CppunitTest_add_exception_objects,sfx2_doc, \
+ sfx2/qa/cppunit/doc \
+))
+
+$(eval $(call gb_CppunitTest_use_libraries,sfx2_doc, \
+ comphelper \
+ cppu \
+ sal \
+ test \
+ unotest \
+ sfx \
+ svl \
+ utl \
+))
+
+$(eval $(call gb_CppunitTest_use_sdk_api,sfx2_doc))
+
+$(eval $(call gb_CppunitTest_use_ure,sfx2_doc))
+$(eval $(call gb_CppunitTest_use_vcl,sfx2_doc))
+
+$(eval $(call gb_CppunitTest_use_rdb,sfx2_doc,services))
+
+$(eval $(call gb_CppunitTest_use_custom_headers,sfx2_doc,\
+ officecfg/registry \
+))
+
+$(eval $(call gb_CppunitTest_use_configuration,sfx2_doc))
+
+# vim: set noet sw=4 ts=4:
diff --git a/sfx2/CppunitTest_sfx2_metadatable.mk b/sfx2/CppunitTest_sfx2_metadatable.mk
new file mode 100644
index 000000000..7e6d8889a
--- /dev/null
+++ b/sfx2/CppunitTest_sfx2_metadatable.mk
@@ -0,0 +1,37 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# This file incorporates work covered by the following license notice:
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed
+# with this work for additional information regarding copyright
+# ownership. The ASF licenses this file to you under the Apache
+# License, Version 2.0 (the "License"); you may not use this file
+# except in compliance with the License. You may obtain a copy of
+# the License at http://www.apache.org/licenses/LICENSE-2.0 .
+#
+
+$(eval $(call gb_CppunitTest_CppunitTest,sfx2_metadatable))
+
+$(eval $(call gb_CppunitTest_add_exception_objects,sfx2_metadatable, \
+ sfx2/qa/cppunit/test_metadatable \
+))
+
+$(eval $(call gb_CppunitTest_use_sdk_api,sfx2_metadatable))
+
+$(eval $(call gb_CppunitTest_use_libraries,sfx2_metadatable, \
+ cppu \
+ cppuhelper \
+ sal \
+ sfx \
+))
+
+$(eval $(call gb_CppunitTest_use_external,sfx2_metadatable,boost_headers))
+
+# vim: set noet sw=4 ts=4:
diff --git a/sfx2/CppunitTest_sfx2_misc.mk b/sfx2/CppunitTest_sfx2_misc.mk
new file mode 100644
index 000000000..c97d97ca4
--- /dev/null
+++ b/sfx2/CppunitTest_sfx2_misc.mk
@@ -0,0 +1,44 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+$(eval $(call gb_CppunitTest_CppunitTest,sfx2_misc))
+
+$(eval $(call gb_CppunitTest_add_exception_objects,sfx2_misc, \
+ sfx2/qa/cppunit/test_misc \
+))
+
+$(eval $(call gb_CppunitTest_use_sdk_api,sfx2_misc))
+
+$(eval $(call gb_CppunitTest_use_libraries,sfx2_misc, \
+ comphelper \
+ cppu \
+ cppuhelper \
+ test \
+ unotest \
+ vcl \
+ sal \
+ sfx \
+ utl \
+))
+
+$(eval $(call gb_CppunitTest_use_externals,sfx2_misc,\
+ libxml2 \
+ boost_headers \
+))
+
+$(eval $(call gb_CppunitTest_use_sdk_api,sfx2_misc))
+
+$(eval $(call gb_CppunitTest_use_ure,sfx2_misc))
+$(eval $(call gb_CppunitTest_use_vcl,sfx2_misc))
+
+$(eval $(call gb_CppunitTest_use_rdb,sfx2_misc,services))
+
+$(eval $(call gb_CppunitTest_use_configuration,sfx2_misc))
+
+# vim: set noet sw=4 ts=4:
diff --git a/sfx2/CppunitTest_sfx2_view.mk b/sfx2/CppunitTest_sfx2_view.mk
new file mode 100644
index 000000000..e39346c10
--- /dev/null
+++ b/sfx2/CppunitTest_sfx2_view.mk
@@ -0,0 +1,45 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#*************************************************************************
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+#*************************************************************************
+
+$(eval $(call gb_CppunitTest_CppunitTest,sfx2_view))
+
+$(eval $(call gb_CppunitTest_use_externals,sfx2_view,\
+ boost_headers \
+))
+
+$(eval $(call gb_CppunitTest_add_exception_objects,sfx2_view, \
+ sfx2/qa/cppunit/view \
+))
+
+$(eval $(call gb_CppunitTest_use_libraries,sfx2_view, \
+ comphelper \
+ cppu \
+ sal \
+ test \
+ unotest \
+ sfx \
+ svl \
+))
+
+$(eval $(call gb_CppunitTest_use_sdk_api,sfx2_view))
+
+$(eval $(call gb_CppunitTest_use_ure,sfx2_view))
+$(eval $(call gb_CppunitTest_use_vcl,sfx2_view))
+
+$(eval $(call gb_CppunitTest_use_rdb,sfx2_view,services))
+
+$(eval $(call gb_CppunitTest_use_custom_headers,sfx2_view,\
+ officecfg/registry \
+))
+
+$(eval $(call gb_CppunitTest_use_configuration,sfx2_view))
+
+# vim: set noet sw=4 ts=4:
diff --git a/sfx2/CustomTarget_classification.mk b/sfx2/CustomTarget_classification.mk
new file mode 100644
index 000000000..4ecc1e750
--- /dev/null
+++ b/sfx2/CustomTarget_classification.mk
@@ -0,0 +1,28 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+$(eval $(call gb_CustomTarget_CustomTarget,sfx2/classification))
+
+sfx2_classification_SRC := $(SRCDIR)/sfx2/classification
+sfx2_classification_WORK := $(call gb_CustomTarget_get_workdir,sfx2/classification)
+sfx2_classification_GEN_example_validated=$(sfx2_classification_WORK)/example.validated
+
+sfx2_classification_XMLLINTCOMMAND := $(call gb_ExternalExecutable_get_command,xmllint)
+
+$(sfx2_classification_GEN_example_validated) : $(sfx2_classification_SRC)/baf.xsd $(sfx2_classification_SRC)/example.xml
+ $(call gb_Output_announce,$(subst $(WORKDIR)/,,$@),build,VAL,1)
+ $(call gb_Helper_abbreviate_dirs,\
+ $(sfx2_classification_XMLLINTCOMMAND) --noout --schema $(sfx2_classification_SRC)/baf.xsd $(sfx2_classification_SRC)/example.xml > $@ 2>&1 \
+ || (cat $@; false))
+
+$(call gb_CustomTarget_get_target,sfx2/classification) : $(sfx2_classification_GEN_example_validated)
+
+$(sfx2_classification_GEN_example_validated) :| $(call gb_ExternalExecutable_get_dependencies,xmllint) $(sfx2_classification_WORK)/.dir
+
+# vim: set noet sw=4 ts=4:
diff --git a/sfx2/IwyuFilter_sfx2.yaml b/sfx2/IwyuFilter_sfx2.yaml
new file mode 100644
index 000000000..f75639931
--- /dev/null
+++ b/sfx2/IwyuFilter_sfx2.yaml
@@ -0,0 +1,184 @@
+---
+assumeFilename: sfx2/source/appl/app.cxx
+excludelist:
+ sfx2/inc/sidebar/UnoDeck.hxx:
+ # base class has to be a complete type
+ - com/sun/star/ui/XDeck.hpp
+ sfx2/inc/sidebar/UnoDecks.hxx:
+ # base class has to be a complete type
+ - com/sun/star/ui/XDecks.hpp
+ sfx2/inc/sidebar/UnoPanel.hxx:
+ # base class has to be a complete type
+ - com/sun/star/ui/XPanel.hpp
+ sfx2/inc/sidebar/UnoPanels.hxx:
+ # base class has to be a complete type
+ - com/sun/star/ui/XPanels.hpp
+ sfx2/inc/recentdocsview.hxx:
+ # Needed for struct declaration
+ - com/sun/star/beans/PropertyValue.hpp
+ sfx2/inc/unoctitm.hxx:
+ # base class has to be a complete type
+ - com/sun/star/frame/XNotifyingDispatch.hpp
+ - com/sun/star/lang/XUnoTunnel.hpp
+ sfx2/source/doc/syspathw32.hxx:
+ # Needed on WIN32
+ - sal/types.h
+ sfx2/source/appl/appdata.cxx:
+ # Needed for direct member access
+ - sfx2/module.hxx
+ sfx2/source/appl/appdde.cxx:
+ # Needed on WIN32
+ - config_features.h
+ - rtl/character.hxx
+ - rtl/malformeduriexception.hxx
+ - rtl/uri.hxx
+ - sot/exchange.hxx
+ - svl/eitem.hxx
+ - basic/sbstar.hxx
+ - svl/stritem.hxx
+ - sfx2/lnkbase.hxx
+ - sfx2/linkmgr.hxx
+ - tools/debug.hxx
+ - tools/urlobj.hxx
+ - tools/diagnose_ex.h
+ - unotools/pathoptions.hxx
+ - vcl/svapp.hxx
+ - sfx2/viewfrm.hxx
+ - sfx2/dispatch.hxx
+ - sfx2/sfxsids.hrc
+ - sfx2/docfile.hxx
+ - ucbhelper/content.hxx
+ - comphelper/processfactory.hxx
+ sfx2/source/appl/appmisc.cxx:
+ # Needed for TypedWhichId macro
+ - svl/stritem.hxx
+ sfx2/source/appl/appquit.cxx:
+ # Full type is needed for implicit dtor
+ - sfx2/stbitem.hxx
+ - sfx2/tbxctrl.hxx
+ sfx2/source/appl/appbas.cxx:
+ # Needed for createSfxPoolItem calls
+ - svl/eitem.hxx
+ - sfx2/dinfdlg.hxx
+ - sfx2/frame.hxx
+ - sfx2/msg.hxx
+ - sorgitm.hxx
+ sfx2/source/appl/newhelp.cxx:
+ # Actually used
+ - com/sun/star/i18n/XBreakIterator.hpp
+ sfx2/source/appl/shutdownicon.cxx:
+ # Needed on MAC
+ - osl/file.hxx
+ # Needed on WIN32
+ - sfx2/sfxresid.hxx
+ - sfx2/strings.hrc
+ # Don't propose hxx -> h change in URE libs
+ - osl/module.hxx
+ sfx2/source/bastyp/fltfnc.cxx:
+ # Needed for UnoType to work
+ - com/sun/star/task/XInteractionHandler.hpp
+ sfx2/source/control/dispatch.cxx:
+ # Needed for DENTERREGISTRATIONS DLEAVEREGISTRATIONS macros from sfx2/bindings.hxx in DBG_UTIL mode
+ - rtl/strbuf.hxx
+ sfx2/source/control/listview.cxx:
+ # Needed for MOREBUTTON in sfx2/inc/bitmaps.hlst to not fail, until a bit of cleanup there.
+ - tools/wintypes.hxx
+ sfx2/source/control/sfxstatuslistener.cxx:
+ # Actually used
+ - com/sun/star/frame/XDispatchProvider.hpp
+ sfx2/source/control/statcach.cxx:
+ # Needed for template
+ - com/sun/star/frame/XFrame.hpp
+ sfx2/source/dialog/documentfontsdialog.cxx:
+ # Needed for template
+ - com/sun/star/frame/XModel.hpp
+ sfx2/source/dialog/versdlg.cxx:
+ # Needed for template
+ - com/sun/star/frame/XModel.hpp
+ sfx2/source/doc/docinf.cxx:
+ # Actually used
+ - com/sun/star/document/XDocumentProperties.hpp
+ sfx2/source/doc/DocumentMetadataAccess.cxx:
+ # Actually used
+ - com/sun/star/embed/XStorage.hpp
+ sfx2/source/doc/graphhelp.cxx:
+ # Needed on WIN32
+ - o3tl/char16_t2wchar_t.hxx
+ sfx2/source/doc/doctempl.cxx:
+ # Needed for template
+ - com/sun/star/ucb/NumberedSortingInfo.hpp
+ sfx2/source/doc/guisaveas.cxx:
+ # Needed on WIN32
+ - o3tl/char16_t2wchar_t.hxx
+ sfx2/source/doc/iframe.cxx:
+ # Needed for template
+ - com/sun/star/awt/XWindowPeer.hpp
+ sfx2/source/doc/objstor.cxx:
+ # Actually used
+ - com/sun/star/frame/XModel.hpp
+ sfx2/source/doc/syspath.cxx:
+ # Needed on WIN32
+ - syspathw32.hxx
+ sfx2/source/doc/syspathw32.cxx:
+ # Needed on WIN32
+ - o3tl/char16_t2wchar_t.hxx
+ sfx2/source/doc/printhelper.cxx:
+ # Don't propose hxx -> h change in URE libs
+ - cppuhelper/interfacecontainer.hxx
+ sfx2/source/doc/sfxbasemodel.cxx:
+ # Don't propose hxx -> h change in URE libs
+ - cppuhelper/interfacecontainer.hxx
+ sfx2/source/doc/signaturestate.cxx:
+ # Actually used
+ - com/sun/star/security/DocumentSignatureInformation.hpp
+ sfx2/source/inc/asyncfunc.hxx:
+ # base class has to be a complete type
+ - com/sun/star/lang/XUnoTunnel.hpp
+ sfx2/source/sidebar/ControllerFactory.cxx:
+ # Actually used
+ - com/sun/star/frame/XFrame.hpp
+ # Needed for instantiation of function template specialization
+ - com/sun/star/lang/XMultiServiceFactory.hpp
+ sfx2/source/sidebar/Panel.cxx:
+ # Actually used
+ - com/sun/star/ui/XUIElement.hpp
+ sfx2/source/sidebar/ResourceManager.cxx:
+ # Needed for template
+ - com/sun/star/ui/XSidebarPanel.hpp
+ sfx2/source/sidebar/SidebarToolBox.cxx:
+ # Actually used
+ - com/sun/star/frame/XFrame.hpp
+ - com/sun/star/frame/XToolbarController.hpp
+ sfx2/source/sidebar/SidebarController.cxx:
+ # Actually used
+ - com/sun/star/awt/XWindowPeer.hpp
+ - com/sun/star/frame/XDispatch.hpp
+ - com/sun/star/ui/ContextChangeEventObject.hpp
+ sfx2/source/sidebar/SidebarDockingWindow.cxx:
+ # Needed for json_parser_error
+ - boost/property_tree/json_parser.hpp
+ sfx2/source/sidebar/SidebarPanelBase.cxx:
+ # Needed for template
+ - com/sun/star/awt/XWindowPeer.hpp
+ sfx2/source/view/ipclient.cxx:
+ # Needed for template
+ - com/sun/star/awt/XWindowPeer.hpp
+ sfx2/source/view/sfxbasecontroller.cxx:
+ # Needed for template
+ - com/sun/star/awt/XWindowPeer.hpp
+ sfx2/source/view/viewfrm2.cxx:
+ # Needed for direct member access
+ - com/sun/star/embed/VerbDescriptor.hpp
+ sfx2/source/view/classificationhelper.cxx:
+ # Actually used
+ - com/sun/star/beans/XPropertyContainer.hpp
+ - com/sun/star/document/XDocumentProperties.hpp
+ sfx2/source/view/userinputinterception.cxx:
+ # Actually used
+ - com/sun/star/awt/XKeyHandler.hpp
+ - com/sun/star/awt/XMouseClickHandler.hpp
+ # Needed for template
+ - com/sun/star/awt/XWindowPeer.hpp
+ sfx2/source/view/viewsh.cxx:
+ # Needed for TypedWhichId define
+ - svl/eitem.hxx
diff --git a/sfx2/JunitTest_sfx2_complex.mk b/sfx2/JunitTest_sfx2_complex.mk
new file mode 100644
index 000000000..ac9cb60a4
--- /dev/null
+++ b/sfx2/JunitTest_sfx2_complex.mk
@@ -0,0 +1,62 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# This file incorporates work covered by the following license notice:
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed
+# with this work for additional information regarding copyright
+# ownership. The ASF licenses this file to you under the Apache
+# License, Version 2.0 (the "License"); you may not use this file
+# except in compliance with the License. You may obtain a copy of
+# the License at http://www.apache.org/licenses/LICENSE-2.0 .
+#
+
+$(eval $(call gb_JunitTest_JunitTest,sfx2_complex))
+
+$(eval $(call gb_JunitTest_set_defs,sfx2_complex,\
+ $$(DEFS) \
+ -Dorg.openoffice.test.arg.tdoc=$(SRCDIR)/sfx2/qa/complex/sfx2/testdocuments \
+))
+
+$(eval $(call gb_JunitTest_use_unoapi_jars,sfx2_complex))
+
+$(eval $(call gb_JunitTest_use_jars,sfx2_complex,\
+ test-tools \
+))
+
+$(eval $(call gb_JunitTest_add_sourcefiles,sfx2_complex,\
+ sfx2/qa/complex/sfx2/DocumentMetadataAccess \
+ sfx2/qa/complex/sfx2/DocumentProperties \
+ sfx2/qa/complex/sfx2/GlobalEventBroadcaster \
+ sfx2/qa/complex/sfx2/UndoManager \
+ sfx2/qa/complex/sfx2/DocumentEvents \
+ sfx2/qa/complex/sfx2/tools/TestDocument \
+ sfx2/qa/complex/sfx2/tools/WriterHelper \
+ sfx2/qa/complex/sfx2/undo/CalcDocumentTest \
+ sfx2/qa/complex/sfx2/undo/ChartDocumentTest \
+ sfx2/qa/complex/sfx2/undo/DocumentTest \
+ sfx2/qa/complex/sfx2/undo/DocumentTestBase \
+ sfx2/qa/complex/sfx2/undo/DrawDocumentTest \
+ sfx2/qa/complex/sfx2/undo/DrawingOrPresentationDocumentTest \
+ sfx2/qa/complex/sfx2/undo/ImpressDocumentTest \
+ sfx2/qa/complex/sfx2/undo/WriterDocumentTest \
+))
+
+$(eval $(call gb_JunitTest_add_classes,sfx2_complex,\
+ complex.sfx2.DocumentProperties \
+ complex.sfx2.DocumentMetadataAccess \
+ complex.sfx2.DocumentEvents \
+ complex.sfx2.UndoManager \
+))
+# fd#35663 fails currently:
+# complex.sfx2.UndoManager \
+# #i115674# fails currently: misses some OnUnfocus event
+# complex.sfx2.GlobalEventBroadcaster \
+
+# vim: set noet sw=4 ts=4:
diff --git a/sfx2/JunitTest_sfx2_unoapi.mk b/sfx2/JunitTest_sfx2_unoapi.mk
new file mode 100644
index 000000000..d812f5e0b
--- /dev/null
+++ b/sfx2/JunitTest_sfx2_unoapi.mk
@@ -0,0 +1,24 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# This file incorporates work covered by the following license notice:
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed
+# with this work for additional information regarding copyright
+# ownership. The ASF licenses this file to you under the Apache
+# License, Version 2.0 (the "License"); you may not use this file
+# except in compliance with the License. You may obtain a copy of
+# the License at http://www.apache.org/licenses/LICENSE-2.0 .
+#
+
+$(eval $(call gb_JunitTest_JunitTest,sfx2_unoapi))
+
+$(eval $(call gb_JunitTest_set_unoapi_test_defaults,sfx2_unoapi,,sfx.sce))
+
+# vim: set noet sw=4 ts=4:
diff --git a/sfx2/Library_sfx.mk b/sfx2/Library_sfx.mk
new file mode 100644
index 000000000..a8873df7c
--- /dev/null
+++ b/sfx2/Library_sfx.mk
@@ -0,0 +1,370 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# This file incorporates work covered by the following license notice:
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed
+# with this work for additional information regarding copyright
+# ownership. The ASF licenses this file to you under the Apache
+# License, Version 2.0 (the "License"); you may not use this file
+# except in compliance with the License. You may obtain a copy of
+# the License at http://www.apache.org/licenses/LICENSE-2.0 .
+#
+
+$(eval $(call gb_Library_Library,sfx))
+
+$(eval $(call gb_Library_add_sdi_headers,sfx,sfx2/sdi/sfxslots))
+
+$(eval $(call gb_Library_set_componentfile,sfx,sfx2/util/sfx,services))
+
+$(eval $(call gb_Library_add_componentimpls,sfx, \
+ $(if $(ENABLE_WASM_STRIP_RECENT),,extended) \
+))
+
+$(eval $(call gb_Library_set_precompiled_header,sfx,sfx2/inc/pch/precompiled_sfx))
+
+$(eval $(call gb_Library_use_custom_headers,sfx,\
+ officecfg/registry \
+))
+
+$(eval $(call gb_Library_use_sdk_api,sfx))
+
+$(eval $(call gb_Library_set_include,sfx,\
+ -I$(SRCDIR)/sfx2/inc \
+ -I$(SRCDIR)/sfx2/source/inc \
+ -I$(WORKDIR)/SdiTarget/sfx2/sdi \
+ $$(INCLUDE) \
+))
+
+$(eval $(call gb_Library_add_defs,sfx,-DSFX2_DLLIMPLEMENTATION))
+
+$(eval $(call gb_Library_add_defs,sfx,\
+ $(if $(filter TRUE,$(ENABLE_CUPS)),-DENABLE_CUPS) \
+))
+
+$(eval $(call gb_Library_use_libraries,sfx,\
+ basegfx \
+ comphelper \
+ cppu \
+ cppuhelper \
+ drawinglayercore \
+ drawinglayer \
+ fwk \
+ i18nlangtag \
+ i18nutil \
+ sal \
+ salhelper \
+ sax \
+ sb \
+ sot \
+ svl \
+ svt \
+ tk \
+ tl \
+ ucbhelper \
+ utl \
+ vcl \
+ $(if $(ENABLE_BREAKPAD), \
+ crashreport \
+ ) \
+))
+
+$(eval $(call gb_Library_use_externals,sfx,\
+ boost_headers \
+ icu_headers \
+ icui18n \
+ icuuc \
+ libxml2 \
+ orcus \
+ orcus-parser\
+))
+
+ifneq ($(ENABLE_WASM_STRIP_RECENT),TRUE)
+$(eval $(call gb_Library_add_exception_objects,sfx,\
+ sfx2/source/dialog/backingcomp \
+ sfx2/source/dialog/backingwindow \
+ sfx2/source/control/recentdocsview \
+ sfx2/source/control/recentdocsviewitem \
+))
+endif
+
+$(eval $(call gb_Library_add_exception_objects,sfx,\
+ sfx2/source/accessibility/AccessibilityCheck \
+ sfx2/source/accessibility/AccessibilityIssue \
+ sfx2/source/appl/app \
+ sfx2/source/appl/appbas \
+ sfx2/source/appl/appbaslib \
+ sfx2/source/appl/appcfg \
+ sfx2/source/appl/appchild \
+ sfx2/source/appl/appdata \
+ sfx2/source/appl/appdde \
+ sfx2/source/appl/appdispatchprovider \
+ sfx2/source/appl/appinit \
+ sfx2/source/appl/appmain \
+ sfx2/source/appl/appmisc \
+ sfx2/source/appl/appopen \
+ sfx2/source/appl/appquit \
+ sfx2/source/appl/appreg \
+ sfx2/source/appl/appserv \
+ sfx2/source/appl/appuno \
+ sfx2/source/appl/childwin \
+ sfx2/source/appl/fileobj \
+ sfx2/source/appl/flatpak \
+ sfx2/source/appl/fwkhelper \
+ sfx2/source/appl/getbasctlfunction \
+ sfx2/source/appl/helpdispatch \
+ sfx2/source/appl/helpinterceptor \
+ sfx2/source/appl/impldde \
+ sfx2/source/appl/linkmgr2 \
+ sfx2/source/appl/linksrc \
+ sfx2/source/appl/lnkbase2 \
+ sfx2/source/appl/macroloader \
+ sfx2/source/appl/module \
+ sfx2/source/appl/newhelp \
+ sfx2/source/appl/opengrf \
+ sfx2/source/appl/openuriexternally \
+ sfx2/source/appl/preventduplicateinteraction \
+ sfx2/source/appl/sfxhelp \
+ sfx2/source/appl/sfxpicklist \
+ sfx2/source/appl/shutdownicon \
+ sfx2/source/appl/workwin \
+ sfx2/source/appl/xpackcreator \
+ sfx2/source/bastyp/bitset \
+ sfx2/source/bastyp/fltfnc \
+ sfx2/source/bastyp/fltlst \
+ sfx2/source/bastyp/frmhtml \
+ sfx2/source/bastyp/frmhtmlw \
+ sfx2/source/bastyp/helper \
+ sfx2/source/bastyp/mieclip \
+ sfx2/source/bastyp/progress \
+ sfx2/source/bastyp/sfxhtml \
+ sfx2/source/bastyp/sfxresid \
+ sfx2/source/commandpopup/CommandPopup \
+ sfx2/source/config/evntconf \
+ sfx2/source/control/bindings \
+ sfx2/source/control/ctrlitem \
+ sfx2/source/control/dispatch \
+ sfx2/source/control/itemdel \
+ sfx2/source/control/minfitem \
+ sfx2/source/control/msg \
+ sfx2/source/control/msgpool \
+ sfx2/source/control/objface \
+ sfx2/source/control/request \
+ sfx2/source/control/sfxstatuslistener \
+ sfx2/source/control/shell \
+ sfx2/source/control/sorgitm \
+ sfx2/source/control/statcach \
+ sfx2/source/control/templatedefaultview \
+ sfx2/source/control/templateviewitem \
+ sfx2/source/control/templatelocalview \
+ sfx2/source/control/templatecontaineritem \
+ sfx2/source/control/templatedlglocalview \
+ sfx2/source/control/listview \
+ sfx2/source/control/thumbnailviewitem \
+ sfx2/source/control/thumbnailviewacc \
+ sfx2/source/control/thumbnailview \
+ sfx2/source/control/emojiviewitem \
+ sfx2/source/control/emojiview \
+ sfx2/source/control/emojicontrol \
+ sfx2/source/control/emojipopup \
+ sfx2/source/control/charmapcontrol \
+ sfx2/source/control/charwin \
+ sfx2/source/control/unoctitm \
+ sfx2/source/devtools/DevelopmentToolChildWindow \
+ sfx2/source/devtools/DevelopmentToolDockingWindow \
+ sfx2/source/devtools/DocumentModelTreeHandler \
+ sfx2/source/devtools/ObjectInspectorTreeHandler \
+ sfx2/source/dialog/alienwarn \
+ sfx2/source/dialog/basedlgs \
+ sfx2/source/dialog/checkin \
+ sfx2/source/dialog/dialoghelper \
+ sfx2/source/dialog/charmappopup \
+ sfx2/source/dialog/dinfdlg \
+ sfx2/source/dialog/dockwin \
+ sfx2/source/dialog/documentfontsdialog \
+ sfx2/source/dialog/filedlghelper \
+ sfx2/source/dialog/filtergrouping \
+ sfx2/source/dialog/infobar \
+ sfx2/source/dialog/inputdlg \
+ sfx2/source/dialog/mailmodel \
+ sfx2/source/dialog/bluthsnd \
+ sfx2/source/dialog/mgetempl \
+ sfx2/source/dialog/navigat \
+ sfx2/source/dialog/newstyle \
+ sfx2/source/dialog/partwnd \
+ sfx2/source/dialog/passwd \
+ sfx2/source/dialog/printopt \
+ sfx2/source/dialog/recfloat \
+ sfx2/source/dialog/securitypage \
+ sfx2/source/dialog/sfxdlg \
+ sfx2/source/dialog/splitwin \
+ sfx2/source/dialog/srchdlg \
+ sfx2/source/dialog/styfitem \
+ sfx2/source/dialog/styledlg \
+ sfx2/source/dialog/tabdlg \
+ sfx2/source/dialog/templdlg \
+ sfx2/source/dialog/StyleList \
+ sfx2/source/dialog/tplcitem \
+ sfx2/source/dialog/tplpitem \
+ sfx2/source/dialog/versdlg \
+ sfx2/source/doc/DocumentMetadataAccess \
+ sfx2/source/doc/DocumentSigner \
+ sfx2/source/doc/Metadatable \
+ sfx2/source/doc/QuerySaveDocument \
+ sfx2/source/doc/SfxDocumentMetaData \
+ sfx2/source/doc/autoredactdialog \
+ sfx2/source/doc/docfac \
+ sfx2/source/doc/docfile \
+ sfx2/source/doc/docfilt \
+ sfx2/source/doc/docinf \
+ sfx2/source/doc/docinsert \
+ sfx2/source/doc/docmacromode \
+ sfx2/source/doc/docstoragemodifylistener \
+ sfx2/source/doc/doctempl \
+ sfx2/source/doc/doctemplates \
+ sfx2/source/doc/doctemplateslocal \
+ sfx2/source/doc/exoticfileloadexception \
+ sfx2/source/doc/frmdescr \
+ sfx2/source/doc/graphhelp \
+ sfx2/source/doc/guisaveas \
+ sfx2/source/doc/iframe \
+ sfx2/source/doc/new \
+ sfx2/source/doc/objcont \
+ sfx2/source/doc/objembed \
+ sfx2/source/doc/objitem \
+ sfx2/source/doc/objmisc \
+ sfx2/source/doc/objserv \
+ sfx2/source/doc/objstor \
+ sfx2/source/doc/objxtor \
+ sfx2/source/doc/oleprops \
+ sfx2/source/doc/ownsubfilterservice \
+ sfx2/source/doc/printhelper \
+ sfx2/source/doc/docundomanager \
+ sfx2/source/doc/sfxbasemodel \
+ sfx2/source/doc/sfxmodelfactory \
+ sfx2/source/doc/SfxRedactionHelper \
+ sfx2/source/doc/signaturestate \
+ sfx2/source/doc/syspath \
+ sfx2/source/doc/zoomitem \
+ sfx2/source/doc/templatedlg \
+ sfx2/source/doc/watermarkitem \
+ sfx2/source/doc/saveastemplatedlg \
+ sfx2/source/explorer/nochaos \
+ sfx2/source/inet/inettbc \
+ sfx2/source/notebookbar/SfxNotebookBar \
+ sfx2/source/notebookbar/NotebookbarTabControl \
+ sfx2/source/notify/eventsupplier \
+ sfx2/source/notify/globalevents \
+ sfx2/source/notify/hintpost \
+ sfx2/source/notify/openurlhint \
+ sfx2/source/safemode/safemode \
+ sfx2/source/sidebar/Sidebar \
+ sfx2/source/sidebar/SidebarChildWindow \
+ sfx2/source/sidebar/SidebarDockingWindow \
+ sfx2/source/sidebar/SidebarController \
+ sfx2/source/sidebar/SidebarModelUpdate \
+ sfx2/source/sidebar/SidebarPanelBase \
+ sfx2/source/sidebar/SidebarToolBox \
+ sfx2/source/sidebar/AsynchronousCall \
+ sfx2/source/sidebar/Context \
+ sfx2/source/sidebar/ContextChangeBroadcaster \
+ sfx2/source/sidebar/ContextList \
+ sfx2/source/sidebar/ControllerFactory \
+ sfx2/source/sidebar/ControllerItem \
+ sfx2/source/sidebar/Deck \
+ sfx2/source/sidebar/DeckDescriptor \
+ sfx2/source/sidebar/DeckLayouter \
+ sfx2/source/sidebar/DeckTitleBar \
+ sfx2/source/sidebar/FocusManager \
+ sfx2/source/sidebar/IContextChangeReceiver \
+ sfx2/source/sidebar/ILayoutableWindow \
+ sfx2/source/sidebar/Panel \
+ sfx2/source/sidebar/PanelDescriptor \
+ sfx2/source/sidebar/PanelLayout \
+ sfx2/source/sidebar/PanelTitleBar \
+ sfx2/source/sidebar/ResourceManager \
+ sfx2/source/sidebar/TabBar \
+ sfx2/source/sidebar/TitleBar \
+ sfx2/source/sidebar/Theme \
+ sfx2/source/sidebar/Tools \
+ sfx2/source/sidebar/UnoPanel \
+ sfx2/source/sidebar/UnoPanels \
+ sfx2/source/sidebar/UnoDeck \
+ sfx2/source/sidebar/UnoDecks \
+ sfx2/source/sidebar/UnoSidebar \
+ sfx2/source/statbar/stbitem \
+ sfx2/source/styles/StyleManager \
+ sfx2/source/toolbox/tbxitem \
+ sfx2/source/toolbox/weldutils \
+ sfx2/source/view/classificationcontroller \
+ sfx2/source/view/classificationhelper \
+ sfx2/source/view/frame \
+ sfx2/source/view/frame2 \
+ sfx2/source/view/frmload \
+ sfx2/source/view/ipclient \
+ sfx2/source/view/lokcharthelper \
+ sfx2/source/view/lokstarmathhelper \
+ sfx2/source/view/lokhelper \
+ sfx2/source/view/printer \
+ sfx2/source/view/sfxbasecontroller \
+ sfx2/source/view/userinputinterception \
+ sfx2/source/view/viewfac \
+ sfx2/source/view/viewfrm \
+ sfx2/source/view/viewfrm2 \
+ sfx2/source/view/viewprn \
+ sfx2/source/view/viewsh \
+))
+
+$(eval $(call gb_SdiTarget_SdiTarget,sfx2/sdi/sfxslots,sfx2/sdi/sfx))
+
+$(eval $(call gb_SdiTarget_set_include,sfx2/sdi/sfxslots,\
+ $$(INCLUDE) \
+ -I$(SRCDIR)/sfx2/inc \
+ -I$(SRCDIR)/sfx2/sdi \
+))
+
+ifeq ($(OS),$(filter WNT MACOSX,$(OS)))
+$(eval $(call gb_Library_add_defs,sfx,\
+ -DENABLE_QUICKSTART_APPLET \
+))
+endif
+
+ifeq ($(OS),MACOSX)
+$(eval $(call gb_Library_add_cxxflags,sfx,\
+ $(gb_OBJCXXFLAGS) \
+))
+$(eval $(call gb_Library_add_objcxxobjects,sfx,\
+ sfx2/source/appl/shutdowniconaqua \
+))
+$(eval $(call gb_Library_add_libs,sfx,\
+ -lobjc \
+))
+$(eval $(call gb_Library_use_system_darwin_frameworks,sfx,\
+ Cocoa \
+))
+endif
+
+ifeq ($(OS),WNT)
+
+$(eval $(call gb_Library_add_exception_objects,sfx,\
+ sfx2/source/appl/shutdowniconw32 \
+ sfx2/source/doc/syspathw32 \
+))
+
+$(eval $(call gb_Library_use_system_win32_libs,sfx,\
+ advapi32 \
+ gdi32 \
+ ole32 \
+ shell32 \
+ uuid \
+))
+
+endif
+
+# vim: set noet sw=4 ts=4:
diff --git a/sfx2/Makefile b/sfx2/Makefile
new file mode 100644
index 000000000..ccb1c85a0
--- /dev/null
+++ b/sfx2/Makefile
@@ -0,0 +1,7 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+
+module_directory:=$(dir $(realpath $(firstword $(MAKEFILE_LIST))))
+
+include $(module_directory)/../solenv/gbuild/partial_build.mk
+
+# vim: set noet sw=4 ts=4:
diff --git a/sfx2/Module_sfx2.mk b/sfx2/Module_sfx2.mk
new file mode 100644
index 000000000..dbab052e1
--- /dev/null
+++ b/sfx2/Module_sfx2.mk
@@ -0,0 +1,64 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# This file incorporates work covered by the following license notice:
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed
+# with this work for additional information regarding copyright
+# ownership. The ASF licenses this file to you under the Apache
+# License, Version 2.0 (the "License"); you may not use this file
+# except in compliance with the License. You may obtain a copy of
+# the License at http://www.apache.org/licenses/LICENSE-2.0 .
+#
+
+$(eval $(call gb_Module_Module,sfx2))
+
+$(eval $(call gb_Module_add_targets,sfx2,\
+ CustomTarget_classification \
+ Library_sfx \
+ Package_classification \
+ Package_emoji \
+ UIConfig_sfx \
+))
+
+$(eval $(call gb_Module_add_l10n_targets,sfx2,\
+ AllLangMoTarget_sfx2 \
+))
+
+$(eval $(call gb_Module_add_check_targets,sfx2,\
+ CppunitTest_sfx2_metadatable \
+ CppunitTest_sfx2_misc \
+ CppunitTest_sfx2_controlleritem \
+ CppunitTest_sfx2_classification \
+ CppunitTest_sfx2_view \
+ CppunitTest_sfx2_doc \
+))
+
+$(eval $(call gb_Module_add_subsequentcheck_targets,sfx2,\
+ JunitTest_sfx2_complex \
+ JunitTest_sfx2_unoapi \
+))
+
+$(eval $(call gb_Module_add_subsequentcheck_targets,sfx2,\
+ PythonTest_sfx2_python \
+))
+
+#todo: clean up quickstarter stuff in both libraries
+#todo: move standard pool to svl
+
+# screenshots
+$(eval $(call gb_Module_add_screenshot_targets,sfx2,\
+ CppunitTest_sfx2_dialogs_test \
+))
+
+$(eval $(call gb_Module_add_uicheck_targets,sfx2,\
+ UITest_sfx2_doc \
+))
+
+# vim: set noet sw=4 ts=4:
diff --git a/sfx2/Package_classification.mk b/sfx2/Package_classification.mk
new file mode 100644
index 000000000..c76e07d17
--- /dev/null
+++ b/sfx2/Package_classification.mk
@@ -0,0 +1,23 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+$(eval $(call gb_Package_Package,sfx2_classification,$(SRCDIR)/sfx2))
+
+$(eval $(call gb_Package_add_files,sfx2_classification,$(LIBO_SHARE_FOLDER)/classification,\
+ classification/example.xml \
+ classification/example_ca-ES.xml \
+ classification/example_fr-FR.xml \
+ classification/example_hu-HU.xml \
+ classification/example_nl-NL.xml \
+ classification/example_pt-BR.xml \
+ classification/example_sl-SI.xml \
+ classification/example_zh-CN.xml \
+))
+
+# vim: set noet sw=4 ts=4:
diff --git a/sfx2/Package_emoji.mk b/sfx2/Package_emoji.mk
new file mode 100644
index 000000000..457f0ffdc
--- /dev/null
+++ b/sfx2/Package_emoji.mk
@@ -0,0 +1,16 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+$(eval $(call gb_Package_Package,sfx2_emojiconfig,$(SRCDIR)/sfx2))
+
+$(eval $(call gb_Package_add_files,sfx2_emojiconfig,$(LIBO_SHARE_FOLDER)/emojiconfig,\
+ emojiconfig/emoji.json \
+))
+
+# vim: set noet sw=4 ts=4:
diff --git a/sfx2/PythonTest_sfx2_python.mk b/sfx2/PythonTest_sfx2_python.mk
new file mode 100644
index 000000000..a3a80079d
--- /dev/null
+++ b/sfx2/PythonTest_sfx2_python.mk
@@ -0,0 +1,21 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+$(eval $(call gb_PythonTest_PythonTest,sfx2_python))
+
+$(eval $(call gb_PythonTest_set_defs,sfx2_python,\
+ TDOC="$(SRCDIR)/sfx2/qa/python/testdocuments" \
+))
+
+$(eval $(call gb_PythonTest_add_modules,sfx2_python,$(SRCDIR)/sfx2/qa/python,\
+ check_sidebar \
+ check_sidebar_registry \
+))
+
+# vim: set noet sw=4 ts=4:
diff --git a/sfx2/README.md b/sfx2/README.md
new file mode 100644
index 000000000..3b9158475
--- /dev/null
+++ b/sfx2/README.md
@@ -0,0 +1,30 @@
+# Legacy Framework
+
+`SFX` is the "old" framework, used for historical reasons.
+
+An attempt of documentation of this module is located in `[git:sfx2/doc]`.
+
+It contains base classes for document model, view and controller, used
+by "old" applications like `sw`, `sc`, `sd` (while "new" applications
+are based on the "new" UNO based framework in "framework").
+
+The SFX framework is based on dispatching slots identified by integers
+(`SlotIDs`) to `SfxShells`, and there is a dedicated IDL compiler (`svidl`)
+involved that generates C++ slot headers from SDI files in modules' `sdi/`
+subdirectory.
+
+Documentation about SFX dispatch, SDI etc.:
+<https://wiki.openoffice.org/wiki/Framework/Article/Implementation_of_the_Dispatch_API_In_SFX2>
+
+Document load/save code is maintained in `[git:sfx2/source/doc/docfile.cxx`]
+`SfxMedium` class, which handles all the twisty load and save corner cases.
+
+`[git:sfx2/source/appl/sfxhelp.cxx]` Start procedure for the online
+help viewer top level window; handling of help URL creation and
+dispatch.
+
+There are also some UNO services here that could really be implemented
+anywhere, e.g. the `DocumentProperties` or `DocumentMetadataAccess`.
+
+Notable files:
+`sfx2/source/dialog/backingwindow.cxx` `Startcenter` buttons and the corresponding event handler.
diff --git a/sfx2/UIConfig_sfx.mk b/sfx2/UIConfig_sfx.mk
new file mode 100644
index 000000000..0ba3a5700
--- /dev/null
+++ b/sfx2/UIConfig_sfx.mk
@@ -0,0 +1,81 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+$(eval $(call gb_UIConfig_UIConfig,sfx))
+
+$(eval $(call gb_UIConfig_add_uifiles,sfx,\
+ sfx2/uiconfig/ui/alienwarndialog \
+ sfx2/uiconfig/ui/addtargetdialog \
+ sfx2/uiconfig/ui/autoredactdialog \
+ sfx2/uiconfig/ui/bookmarkdialog \
+ sfx2/uiconfig/ui/bookmarkmenu \
+ sfx2/uiconfig/ui/charmapcontrol \
+ sfx2/uiconfig/ui/charviewmenu \
+ sfx2/uiconfig/ui/checkin \
+ sfx2/uiconfig/ui/classificationbox \
+ sfx2/uiconfig/ui/cmisinfopage \
+ sfx2/uiconfig/ui/cmisline \
+ sfx2/uiconfig/ui/commandpopup \
+ sfx2/uiconfig/ui/custominfopage \
+ sfx2/uiconfig/ui/deck \
+ sfx2/uiconfig/ui/descriptioninfopage \
+ sfx2/uiconfig/ui/developmenttool \
+ sfx2/uiconfig/ui/devtoolsmenu \
+ sfx2/uiconfig/ui/documentfontspage \
+ sfx2/uiconfig/ui/documentinfopage \
+ sfx2/uiconfig/ui/documentpropertiesdialog \
+ sfx2/uiconfig/ui/editdocumentdialog \
+ sfx2/uiconfig/ui/editdurationdialog \
+ sfx2/uiconfig/ui/emojicontrol \
+ sfx2/uiconfig/ui/extrabutton \
+ sfx2/uiconfig/ui/errorfindemaildialog \
+ sfx2/uiconfig/ui/floatingrecord \
+ sfx2/uiconfig/ui/helpbookmarkpage \
+ sfx2/uiconfig/ui/helpcontrol \
+ sfx2/uiconfig/ui/helpcontentpage \
+ sfx2/uiconfig/ui/helpindexpage \
+ sfx2/uiconfig/ui/helpmanual \
+ sfx2/uiconfig/ui/helpsearchpage \
+ sfx2/uiconfig/ui/helpwindow \
+ sfx2/uiconfig/ui/infobar \
+ sfx2/uiconfig/ui/inputdialog \
+ sfx2/uiconfig/ui/licensedialog \
+ sfx2/uiconfig/ui/linefragment \
+ sfx2/uiconfig/ui/linkeditdialog \
+ sfx2/uiconfig/ui/loadtemplatedialog \
+ sfx2/uiconfig/ui/managestylepage \
+ sfx2/uiconfig/ui/navigator \
+ sfx2/uiconfig/ui/newstyle \
+ sfx2/uiconfig/ui/notebookbar \
+ sfx2/uiconfig/ui/optprintpage \
+ sfx2/uiconfig/ui/panel \
+ sfx2/uiconfig/ui/password \
+ sfx2/uiconfig/ui/notebookbarpopup \
+ sfx2/uiconfig/ui/printeroptionsdialog \
+ sfx2/uiconfig/ui/querysavedialog \
+ sfx2/uiconfig/ui/saveastemplatedlg \
+ sfx2/uiconfig/ui/safemodequerydialog \
+ sfx2/uiconfig/ui/searchdialog \
+ sfx2/uiconfig/ui/securityinfopage \
+ sfx2/uiconfig/ui/singletabdialog \
+ sfx2/uiconfig/ui/startcenter \
+ sfx2/uiconfig/ui/stylecontextmenu \
+ sfx2/uiconfig/ui/tabbar \
+ sfx2/uiconfig/ui/tabbarcontents \
+ sfx2/uiconfig/ui/tabbutton \
+ sfx2/uiconfig/ui/templatedlg \
+ sfx2/uiconfig/ui/templatecategorydlg \
+ sfx2/uiconfig/ui/templatepanel \
+ sfx2/uiconfig/ui/urlbox \
+ sfx2/uiconfig/ui/versionsofdialog \
+ sfx2/uiconfig/ui/versioncommentdialog \
+ sfx2/uiconfig/ui/versionscmis \
+))
+
+# vim: set noet sw=4 ts=4:
diff --git a/sfx2/UITest_sfx2_doc.mk b/sfx2/UITest_sfx2_doc.mk
new file mode 100644
index 000000000..b5e3ef8ce
--- /dev/null
+++ b/sfx2/UITest_sfx2_doc.mk
@@ -0,0 +1,20 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+$(eval $(call gb_UITest_UITest,sfx2_doc))
+
+$(eval $(call gb_UITest_add_modules,sfx2_doc,$(SRCDIR)/sfx2/qa/uitest,\
+ doc/ \
+))
+
+$(eval $(call gb_UITest_set_defs,sfx2_doc, \
+ TDOC="$(SRCDIR)/sfx2/qa/uitest/doc/data" \
+))
+
+# vim: set noet sw=4 ts=4:
diff --git a/sfx2/classification/CommonTypes.xsd b/sfx2/classification/CommonTypes.xsd
new file mode 100644
index 000000000..b0404bc9b
--- /dev/null
+++ b/sfx2/classification/CommonTypes.xsd
@@ -0,0 +1,103 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns="urn:oasis:names:tc:ciq:ct:3" targetNamespace="urn:oasis:names:tc:ciq:ct:3" elementFormDefault="qualified" attributeFormDefault="qualified">
+ <xs:annotation>
+ <xs:documentation>
+ Specification Name: OASIS CIQ TC - CIQ V3.0
+ Description: Defines the W3C schema with commonly used types in the name, address and party schemas
+ (Using XML Schema based standard code list/enumeration mechanism - OPTION 1 AND DEFAULT)
+ Produced by: OASIS Customer Information Quality Technical Committee
+ URL: http://www.oasis-open.org/committees/ciq
+ Version: 3.0
+ Status: Committee Specification
+ Copyright: 2006-07, OASIS, http://www.oasis-open.org
+ Last Modified: 18 September 2007
+ Last Modified by: Ram Kumar, Chair, OASIS CIQ TC
+ </xs:documentation>
+ </xs:annotation>
+ <xs:simpleType name="String">
+ <xs:annotation>
+ <xs:documentation>Normalized and Collapsed String</xs:documentation>
+ </xs:annotation>
+ <xs:restriction base="xs:normalizedString">
+ <xs:whiteSpace value="collapse"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType name="DataQualityTypeList">
+ <xs:annotation>
+ <xs:documentation>A list of values to indicate the level of reliability of the data</xs:documentation>
+ </xs:annotation>
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="Valid">
+ <xs:annotation>
+ <xs:documentation>The data was validated and is considered to be true and correct.</xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ <xs:enumeration value="Invalid">
+ <xs:annotation>
+ <xs:documentation>Indicates that at least some part of the content is known to be incorrect.</xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType name="StatusList">
+ <xs:annotation>
+ <xs:documentation>A list of values to indicate the status of the entity</xs:documentation>
+ </xs:annotation>
+ <xs:restriction base="xs:string"/>
+ </xs:simpleType>
+ <xs:attributeGroup name="grValidityDate">
+ <xs:annotation>
+ <xs:documentation>Date Valid from to Date Valid to</xs:documentation>
+ </xs:annotation>
+ <xs:attribute name="DateValidFrom" type="xs:dateTime">
+ <xs:annotation>
+ <xs:documentation>Could be start date, issue date, validity start date, etc</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="DateValidTo" type="xs:dateTime">
+ <xs:annotation>
+ <xs:documentation>Could be end date, expiry date, validity end date, etc</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ </xs:attributeGroup>
+ <xs:attributeGroup name="grAbbreviation">
+ <xs:annotation>
+ <xs:documentation>A group of commonly used attributes for internal reuse</xs:documentation>
+ </xs:annotation>
+ <xs:attribute name="Abbreviation" type="xs:boolean">
+ <xs:annotation>
+ <xs:documentation>If set to true then indicates that the value is an abbreviation or initial. If set to false then the value is definitely not an abbreviation. If omitted then it is not known if the value is an abbreviation or not.</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ </xs:attributeGroup>
+ <xs:attributeGroup name="grDataQuality">
+ <xs:annotation>
+ <xs:documentation>A group of commonly used attributes for internal reuse</xs:documentation>
+ </xs:annotation>
+ <xs:attribute name="DataQualityType" type="DataQualityTypeList">
+ <xs:annotation>
+ <xs:documentation>This attribute indicates what level of trust can be given to the parent element. Omit this attribute if the data quality is unknown. If the data quality is known, the value is "Valid, else "InValid"</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="ValidFrom" type="xs:dateTime">
+ <xs:annotation>
+ <xs:documentation>Date the data quality is valid from </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="ValidTo" type="xs:dateTime">
+ <xs:annotation>
+ <xs:documentation>Date the data quality is valid to</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ </xs:attributeGroup>
+ <xs:attributeGroup name="grLanguageCode">
+ <xs:annotation>
+ <xs:documentation>The language used (name of human language, e.g. en, en-US)</xs:documentation>
+ </xs:annotation>
+ <xs:attribute name="LanguageCode" type="xs:language">
+ <xs:annotation>
+ <xs:documentation>Human Language used. e.g. "en", "en-US", "en-AUS", etc</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ </xs:attributeGroup>
+</xs:schema>
diff --git a/sfx2/classification/README b/sfx2/classification/README
new file mode 100644
index 000000000..54c778ff4
--- /dev/null
+++ b/sfx2/classification/README
@@ -0,0 +1,7 @@
+Involved standards:
+
+- BAF is <https://www.tscp.org/wp-content/uploads/2013/08/TSCP_BAFv1.pdf>
+- BAILS is <http://www.tscp.org/wp-content/uploads/2013/08/TSCP_BAILSv1.pdf>
+- xAL is <http://www.oasis-open.org/committees/ciq>
+- xNL is <http://www.oasis-open.org/committees/ciq>
+- xlink is <http://www.oasis-open.org/committees/ciq> \ No newline at end of file
diff --git a/sfx2/classification/baf.xsd b/sfx2/classification/baf.xsd
new file mode 100644
index 000000000..abf003b38
--- /dev/null
+++ b/sfx2/classification/baf.xsd
@@ -0,0 +1,299 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0" xmlns:xal="urn:oasis:names:tc:ciq:xal:3" xmlns:xnl="urn:oasis:names:tc:ciq:xnl:3" xmlns="urn:tscp:names:baf:1.1" targetNamespace="urn:tscp:names:baf:1.1">
+ <xs:import namespace="urn:oasis:names:tc:ciq:xal:3" schemaLocation="xAL.xsd"/>
+ <xs:import namespace="urn:oasis:names:tc:ciq:xnl:3" schemaLocation="xNL.xsd"/>
+ <xs:import namespace="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0" schemaLocation="baf_loext.xsd"/>
+
+ <!-- Business Authorization -->
+ <xs:complexType name="BusinessAuthorization">
+ <xs:sequence>
+ <xs:element ref="PolicyAuthorityName"/>
+ <xs:element ref="PolicyName"/>
+ <xs:element ref="AdministrativeData"/>
+ <xs:element ref="Scope" minOccurs="0" maxOccurs="1"/>
+ <xs:element ref="Included"/>
+ <xs:element ref="Excluded" minOccurs="0" maxOccurs="1"/>
+ </xs:sequence>
+ </xs:complexType>
+ <xs:element name="AdministrativeData">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element ref="ProgramID"/>
+ <xs:element ref="LicenseID" minOccurs="0" maxOccurs="1"/>
+ <xs:element ref="StartValidityDate" minOccurs="0" maxOccurs="1"/>
+ <xs:element ref="StopValidityDate" minOccurs="0" maxOccurs="1"/>
+ <xs:element ref="Applicant" minOccurs="0" maxOccurs="1"/>
+ <xs:element ref="Signatories" minOccurs="0" maxOccurs="1"/>
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="Included">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element ref="BusinessAuthorizationCategory" maxOccurs="unbounded"/>
+ <xs:element ref="loext:Marking" minOccurs="0" maxOccurs="unbounded"/>
+ <xs:element ref="loext:IntellectualPropertyPart" minOccurs="0" maxOccurs="unbounded"/>
+ <xs:element ref="loext:IntellectualPropertyPartNumber" minOccurs="0" maxOccurs="unbounded"/>
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="Excluded">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element ref="BusinessAuthorizationCategory" minOccurs="0" maxOccurs="unbounded"/>
+ <xs:element ref="loext:Marking" minOccurs="0" maxOccurs="unbounded"/>
+ <xs:element ref="loext:IntellectualPropertyPart" minOccurs="0" maxOccurs="unbounded"/>
+ <xs:element ref="loext:IntellectualPropertyPartNumber" minOccurs="0" maxOccurs="unbounded"/>
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="StopValidityDate">
+ <xs:simpleType>
+ <xs:restriction base="xs:date"/>
+ </xs:simpleType>
+ </xs:element>
+ <xs:element name="StartValidityDate">
+ <xs:simpleType>
+ <xs:restriction base="xs:date"/>
+ </xs:simpleType>
+ </xs:element>
+ <!-- Business Authorization Category -->
+ <xs:element name="BusinessAuthorizationCategory">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element ref="AccessRules" minOccurs="0" maxOccurs="1"/>
+ <xs:element ref="HandlingRules" minOccurs="0" maxOccurs="1"/>
+ <xs:element ref="LabelingRules"/>
+ <xs:element ref="ImpactLevel"/>
+ </xs:sequence>
+ <xs:attribute name="Identifier" type="xs:anyURI" use="required"/>
+ <xs:attribute name="Name" type="xs:string" use="optional"/>
+ <xs:attribute name="loextAbbreviatedName" type="xs:string" use="optional"/>
+ </xs:complexType>
+ </xs:element>
+ <!-- Impact Level -->
+ <xs:element name="ImpactLevel">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element ref="Scale"/>
+ <xs:element ref="ConfidentalityValue" minOccurs="0" maxOccurs="1"/>
+ <xs:element ref="IntegrityValue" minOccurs="0" maxOccurs="1"/>
+ <xs:element ref="AvailabilityValue" minOccurs="0" maxOccurs="1"/>
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="Scale" type="xs:string"/>
+ <xs:element name="ConfidentalityValue" type="xs:string"/>
+ <xs:element name="IntegrityValue" type="xs:string"/>
+ <xs:element name="AvailabilityValue" type="xs:string"/>
+ <!-- Handling Rule -->
+ <xs:complexType name="HandlingRule" abstract="true"/>
+ <xs:complexType name="SecureWEBTransmission">
+ <xs:complexContent>
+ <xs:extension base="HandlingRule"/>
+ </xs:complexContent>
+ </xs:complexType>
+ <xs:complexType name="StorageRule">
+ <xs:complexContent>
+ <xs:extension base="HandlingRule"/>
+ </xs:complexContent>
+ </xs:complexType>
+ <xs:complexType name="SecureWEBStorage">
+ <xs:complexContent>
+ <xs:extension base="StorageRule"/>
+ </xs:complexContent>
+ </xs:complexType>
+ <xs:complexType name="SecureFileTransferTransmission">
+ <xs:complexContent>
+ <xs:extension base="HandlingRule"/>
+ </xs:complexContent>
+ </xs:complexType>
+ <xs:complexType name="SecureEmailTransmission">
+ <xs:complexContent>
+ <xs:extension base="HandlingRule"/>
+ </xs:complexContent>
+ </xs:complexType>
+ <xs:complexType name="MediumAuthentication">
+ <xs:complexContent>
+ <xs:extension base="HandlingRule"/>
+ </xs:complexContent>
+ </xs:complexType>
+ <xs:complexType name="FileDeletion">
+ <xs:complexContent>
+ <xs:extension base="HandlingRule"/>
+ </xs:complexContent>
+ </xs:complexType>
+ <xs:complexType name="DesktopStorage">
+ <xs:complexContent>
+ <xs:extension base="HandlingRule"/>
+ </xs:complexContent>
+ </xs:complexType>
+ <!-- Labeling Rule -->
+ <xs:element name="VisualMarkingPart">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element ref="Identifier"/>
+ <xs:element ref="Value"/>
+ </xs:sequence>
+ <xs:attribute name="type" type="xs:anyURI" use="optional"/>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="Identifier"/>
+ <xs:element name="Value"/>
+ <!-- Others -->
+ <xs:element name="WorkEffortsScope">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element ref="WorkEfforts"/>
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="WorkEfforts">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element ref="WorkEffort" maxOccurs="unbounded"/>
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="WorkEffort">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element ref="Name"/>
+ </xs:sequence>
+ <xs:attribute name="id" type="xs:string" use="required"/>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="Signatories">
+ <xs:complexType/>
+ </xs:element>
+ <xs:element name="Scope">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element ref="OrganizationsScope"/>
+ <xs:element ref="WorkEffortsScope"/>
+ <xs:element ref="ActionsScope"/>
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="Rules">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element ref="HandlingRules"/>
+ <xs:element ref="LabelingRules"/>
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="PolicyAuthorityName" type="xs:string"/>
+ <xs:element name="PolicyName" type="xs:string"/>
+ <xs:element name="ProgramID" type="xs:string"/>
+ <xs:element name="OrganizationsScope">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element ref="Organizations"/>
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="Organizations">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element ref="Organization" maxOccurs="unbounded"/>
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="Organization">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element ref="xnl:PartyName"/>
+ <xs:element ref="xal:Address"/>
+ </xs:sequence>
+ <xs:attribute name="id" use="required" type="xs:string"/>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="Name" type="xs:string"/>
+ <xs:element name="LicenseID" type="xs:anyURI"/>
+ <xs:element name="Level">
+ <xs:simpleType>
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="Moderate"/>
+ </xs:restriction>
+ </xs:simpleType>
+ </xs:element>
+ <xs:element name="LabelingRules">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element ref="VisualMarkingPart" minOccurs="0" maxOccurs="unbounded"/>
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="InformationScope">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element ref="ImpactLevel"/>
+ <xs:element ref="ClassificationNumbers"/>
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="HandlingRules">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element ref="HandlingRule" minOccurs="0" maxOccurs="unbounded"/>
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="HandlingRule" type="HandlingRule"/>
+ <xs:element name="Countries">
+ <xs:complexType/>
+ </xs:element>
+ <xs:element name="ClassificationNumbers">
+ <xs:complexType/>
+ </xs:element>
+ <xs:element name="BusinessAuthorization" type="BusinessAuthorization"/>
+ <xs:element name="Applicant">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element ref="xnl:PartyName"/>
+ <xs:element ref="xal:Address"/>
+ </xs:sequence>
+ <xs:attribute name="id" use="required" type="xs:string"/>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="ActionsScope">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element ref="Actions"/>
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="Actions">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element ref="Action" maxOccurs="unbounded"/>
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="Action">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element ref="Name"/>
+ </xs:sequence>
+ <xs:attribute name="id" use="required" type="xs:string"/>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="AccessRules">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element ref="AccessRule" maxOccurs="unbounded"/>
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="AccessRule">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element ref="Organization"/>
+ <xs:element ref="Countries"/>
+ <xs:element ref="WorkEffort"/>
+ <xs:element ref="Actions"/>
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+</xs:schema>
diff --git a/sfx2/classification/baf_loext.xsd b/sfx2/classification/baf_loext.xsd
new file mode 100644
index 000000000..593eb0117
--- /dev/null
+++ b/sfx2/classification/baf_loext.xsd
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0" targetNamespace="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0">
+ <!-- Markings -->
+ <xs:element name="Marking">
+ <xs:complexType>
+ <xs:attribute name="Name" type="xs:string" use="optional"/>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="IntellectualPropertyPart">
+ <xs:complexType>
+ <xs:attribute name="Name" type="xs:string" use="optional"/>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="IntellectualPropertyPartNumber">
+ <xs:complexType>
+ <xs:attribute name="Name" type="xs:string" use="optional"/>
+ </xs:complexType>
+ </xs:element>
+</xs:schema>
diff --git a/sfx2/classification/example.xml b/sfx2/classification/example.xml
new file mode 100644
index 000000000..a5065e3d9
--- /dev/null
+++ b/sfx2/classification/example.xml
@@ -0,0 +1,89 @@
+<?xml version="1.0"?>
+<baf:BusinessAuthorization xmlns:baf="urn:tscp:names:baf:1.1" xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0">
+ <!-- Translators: this string can be localized -->
+ <baf:PolicyAuthorityName>TSCP Example Policy Authority</baf:PolicyAuthorityName>
+ <!-- Translators: this string can be localized -->
+ <baf:PolicyName>TSCP Example Policy</baf:PolicyName>
+ <baf:AdministrativeData>
+ <baf:ProgramID>urn:example:tscp:1</baf:ProgramID>
+ </baf:AdministrativeData>
+ <baf:Included>
+ <!-- Translators: the Name attribute in this string can be localized -->
+ <baf:BusinessAuthorizationCategory Identifier="urn:example:tscp:1:non-business" Name="Non-Business" loextAbbreviatedName="NB">
+ <baf:LabelingRules/>
+ <baf:ImpactLevel>
+ <baf:Scale>UK-Cabinet</baf:Scale>
+ <baf:ConfidentalityValue>0</baf:ConfidentalityValue>
+ </baf:ImpactLevel>
+ </baf:BusinessAuthorizationCategory>
+ <!-- Translators: the Name attribute in this string can be localized -->
+ <baf:BusinessAuthorizationCategory Identifier="urn:example:tscp:1:general-business" Name="General Business" loextAbbreviatedName="GB">
+ <baf:LabelingRules>
+ <baf:VisualMarkingPart>
+ <baf:Identifier>Document: Header</baf:Identifier>
+ <!-- Translators: this string can be localized -->
+ <baf:Value>Classification: General Business</baf:Value>
+ </baf:VisualMarkingPart>
+ </baf:LabelingRules>
+ <baf:ImpactLevel>
+ <baf:Scale>UK-Cabinet</baf:Scale>
+ <baf:ConfidentalityValue>1</baf:ConfidentalityValue>
+ </baf:ImpactLevel>
+ </baf:BusinessAuthorizationCategory>
+ <!-- Translators: the Name attribute in this string can be localized -->
+ <baf:BusinessAuthorizationCategory Identifier="urn:example:tscp:1:confidential" Name="Confidential" loextAbbreviatedName="Conf">
+ <baf:LabelingRules>
+ <baf:VisualMarkingPart>
+ <baf:Identifier>Document: Header</baf:Identifier>
+ <!-- Translators: this string can be localized -->
+ <baf:Value>Classification: Confidential</baf:Value>
+ </baf:VisualMarkingPart>
+ <baf:VisualMarkingPart>
+ <baf:Identifier>Document: Footer</baf:Identifier>
+ <!-- Translators: this string can be localized -->
+ <baf:Value>This content is marked Confidential. Do not distribute it externally without business approval.</baf:Value>
+ </baf:VisualMarkingPart>
+ <baf:VisualMarkingPart>
+ <baf:Identifier>Document: Watermark</baf:Identifier>
+ <!-- Translators: this string can be localized -->
+ <baf:Value>Confidential</baf:Value>
+ </baf:VisualMarkingPart>
+ </baf:LabelingRules>
+ <baf:ImpactLevel>
+ <baf:Scale>UK-Cabinet</baf:Scale>
+ <baf:ConfidentalityValue>2</baf:ConfidentalityValue>
+ </baf:ImpactLevel>
+ </baf:BusinessAuthorizationCategory>
+ <!-- Translators: the Name attribute in this string can be localized -->
+ <baf:BusinessAuthorizationCategory Identifier="urn:example:tscp:1:internal-only" Name="Internal Only" loextAbbreviatedName="IO">
+ <baf:LabelingRules>
+ <baf:VisualMarkingPart>
+ <baf:Identifier>Document: Header</baf:Identifier>
+ <!-- Translators: this string can be localized -->
+ <baf:Value>Classification: Internal Only</baf:Value>
+ </baf:VisualMarkingPart>
+ <baf:VisualMarkingPart>
+ <baf:Identifier>Document: Footer</baf:Identifier>
+ <!-- Translators: this string can be localized -->
+ <baf:Value>This content is marked Internal Only. Do not distribute it outside of the business.</baf:Value>
+ </baf:VisualMarkingPart>
+ <baf:VisualMarkingPart>
+ <baf:Identifier>Document: Watermark</baf:Identifier>
+ <!-- Translators: this string can be localized -->
+ <baf:Value>Internal Only</baf:Value>
+ </baf:VisualMarkingPart>
+ </baf:LabelingRules>
+ <baf:ImpactLevel>
+ <baf:Scale>UK-Cabinet</baf:Scale>
+ <baf:ConfidentalityValue>3</baf:ConfidentalityValue>
+ </baf:ImpactLevel>
+ </baf:BusinessAuthorizationCategory>
+ <loext:Marking Name="Example Marking" />
+ <loext:IntellectualPropertyPart Name="Example First IP Part" />
+ <loext:IntellectualPropertyPart Name="Example Second IP Part" />
+ <loext:IntellectualPropertyPartNumber Name="1" />
+ <loext:IntellectualPropertyPartNumber Name="2" />
+ </baf:Included>
+</baf:BusinessAuthorization>
+<!-- vim:set shiftwidth=4 softtabstop=4 expandtab:
+-->
diff --git a/sfx2/classification/example_ca-ES.xml b/sfx2/classification/example_ca-ES.xml
new file mode 100644
index 000000000..f62d6473f
--- /dev/null
+++ b/sfx2/classification/example_ca-ES.xml
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<baf:BusinessAuthorization xmlns:baf="urn:tscp:names:baf:1.1">
+ <baf:PolicyAuthorityName>Autoritat de polítiques TSCP de exemple</baf:PolicyAuthorityName>
+ <baf:PolicyName>Política TSCP de exemple</baf:PolicyName>
+ <baf:AdministrativeData>
+ <baf:ProgramID>urn:example:tscp:1</baf:ProgramID>
+ </baf:AdministrativeData>
+ <baf:Included>
+ <baf:BusinessAuthorizationCategory Identifier="urn:example:tscp:1:non-business" Name="Altres afers">
+ <baf:LabelingRules/>
+ <baf:ImpactLevel>
+ <baf:Scale>UK-Cabinet</baf:Scale>
+ <baf:ConfidentalityValue>0</baf:ConfidentalityValue>
+ </baf:ImpactLevel>
+ </baf:BusinessAuthorizationCategory>
+ <baf:BusinessAuthorizationCategory Identifier="urn:example:tscp:1:general-business" Name="Afers generals">
+ <baf:LabelingRules>
+ <baf:VisualMarkingPart>
+ <baf:Identifier>Document: Header</baf:Identifier>
+ <baf:Value>Classificació: afers generals</baf:Value>
+ </baf:VisualMarkingPart>
+ </baf:LabelingRules>
+ <baf:ImpactLevel>
+ <baf:Scale>UK-Cabinet</baf:Scale>
+ <baf:ConfidentalityValue>1</baf:ConfidentalityValue>
+ </baf:ImpactLevel>
+ </baf:BusinessAuthorizationCategory>
+ <baf:BusinessAuthorizationCategory Identifier="urn:example:tscp:1:confidential" Name="Confidencial">
+ <baf:LabelingRules>
+ <baf:VisualMarkingPart>
+ <baf:Identifier>Document: Header</baf:Identifier>
+ <baf:Value>Classificació: confidencial</baf:Value>
+ </baf:VisualMarkingPart>
+ <baf:VisualMarkingPart>
+ <baf:Identifier>Document: Footer</baf:Identifier>
+ <baf:Value>Aquest contingut és de caràcter confidencial. No el distribuïu externament sense autorització.</baf:Value>
+ </baf:VisualMarkingPart>
+ <baf:VisualMarkingPart>
+ <baf:Identifier>Document: Watermark</baf:Identifier>
+ <baf:Value>Confidencial</baf:Value>
+ </baf:VisualMarkingPart>
+ </baf:LabelingRules>
+ <baf:ImpactLevel>
+ <baf:Scale>UK-Cabinet</baf:Scale>
+ <baf:ConfidentalityValue>2</baf:ConfidentalityValue>
+ </baf:ImpactLevel>
+ </baf:BusinessAuthorizationCategory>
+ <baf:BusinessAuthorizationCategory Identifier="urn:example:tscp:1:internal-only" Name="Ús intern">
+ <baf:LabelingRules>
+ <baf:VisualMarkingPart>
+ <baf:Identifier>Document: Header</baf:Identifier>
+ <baf:Value>Classificació: només d’ús intern</baf:Value>
+ </baf:VisualMarkingPart>
+ <baf:VisualMarkingPart>
+ <baf:Identifier>Document: Footer</baf:Identifier>
+ <baf:Value>Aquest contingut és només d’ús intern. No el distribuïu externament.</baf:Value>
+ </baf:VisualMarkingPart>
+ <baf:VisualMarkingPart>
+ <baf:Identifier>Document: Watermark</baf:Identifier>
+ <baf:Value>Ús intern</baf:Value>
+ </baf:VisualMarkingPart>
+ </baf:LabelingRules>
+ <baf:ImpactLevel>
+ <baf:Scale>UK-Cabinet</baf:Scale>
+ <baf:ConfidentalityValue>3</baf:ConfidentalityValue>
+ </baf:ImpactLevel>
+ </baf:BusinessAuthorizationCategory>
+ </baf:Included>
+</baf:BusinessAuthorization>
+<!-- vim:set shiftwidth=4 softtabstop=4 expandtab:
+-->
diff --git a/sfx2/classification/example_fr-FR.xml b/sfx2/classification/example_fr-FR.xml
new file mode 100644
index 000000000..4e0ab9922
--- /dev/null
+++ b/sfx2/classification/example_fr-FR.xml
@@ -0,0 +1,84 @@
+<?xml version="1.0"?>
+<baf:BusinessAuthorization xmlns:baf="urn:tscp:names:baf:1.1">
+ <!-- Translators: this string can be localized -->
+ <baf:PolicyAuthorityName>Exemple d'autorité TSCP</baf:PolicyAuthorityName>
+ <!-- Translators: this string can be localized -->
+ <baf:PolicyName>Exemple de stratégie TSCP</baf:PolicyName>
+ <baf:AdministrativeData>
+ <baf:ProgramID>urn:example:tscp:1</baf:ProgramID>
+ </baf:AdministrativeData>
+ <baf:Included>
+ <!-- Translators: the Name attribute in this string can be localized -->
+ <baf:BusinessAuthorizationCategory Identifier="urn:example:tscp:1:non-business" Name="Public">
+ <baf:LabelingRules/>
+ <baf:ImpactLevel>
+ <baf:Scale>UK-Cabinet</baf:Scale>
+ <baf:ConfidentalityValue>0</baf:ConfidentalityValue>
+ </baf:ImpactLevel>
+ </baf:BusinessAuthorizationCategory>
+ <!-- Translators: the Name attribute in this string can be localized -->
+ <baf:BusinessAuthorizationCategory Identifier="urn:example:tscp:1:general-business" Name="Professionnel">
+ <baf:LabelingRules>
+ <baf:VisualMarkingPart>
+ <baf:Identifier>Document: Header</baf:Identifier>
+ <!-- Translators: this string can be localized -->
+ <baf:Value>Classification : professionnel</baf:Value>
+ </baf:VisualMarkingPart>
+ </baf:LabelingRules>
+ <baf:ImpactLevel>
+ <baf:Scale>UK-Cabinet</baf:Scale>
+ <baf:ConfidentalityValue>1</baf:ConfidentalityValue>
+ </baf:ImpactLevel>
+ </baf:BusinessAuthorizationCategory>
+ <!-- Translators: the Name attribute in this string can be localized -->
+ <baf:BusinessAuthorizationCategory Identifier="urn:example:tscp:1:confidential" Name="Confidentiel">
+ <baf:LabelingRules>
+ <baf:VisualMarkingPart>
+ <baf:Identifier>Document: Header</baf:Identifier>
+ <!-- Translators: this string can be localized -->
+ <baf:Value>Classification : confidentiel</baf:Value>
+ </baf:VisualMarkingPart>
+ <baf:VisualMarkingPart>
+ <baf:Identifier>Document: Footer</baf:Identifier>
+ <!-- Translators: this string can be localized -->
+ <baf:Value>Ce contenu est marqué confidentiel. Ne le communiquez pas à l'extérieur sans un accord de l'entreprise.</baf:Value>
+ </baf:VisualMarkingPart>
+ <baf:VisualMarkingPart>
+ <baf:Identifier>Document: Watermark</baf:Identifier>
+ <!-- Translators: this string can be localized -->
+ <baf:Value>Confidentiel</baf:Value>
+ </baf:VisualMarkingPart>
+ </baf:LabelingRules>
+ <baf:ImpactLevel>
+ <baf:Scale>UK-Cabinet</baf:Scale>
+ <baf:ConfidentalityValue>2</baf:ConfidentalityValue>
+ </baf:ImpactLevel>
+ </baf:BusinessAuthorizationCategory>
+ <!-- Translators: the Name attribute in this string can be localized -->
+ <baf:BusinessAuthorizationCategory Identifier="urn:example:tscp:1:internal-only" Name="Exclusivement en interne">
+ <baf:LabelingRules>
+ <baf:VisualMarkingPart>
+ <baf:Identifier>Document: Header</baf:Identifier>
+ <!-- Translators: this string can be localized -->
+ <baf:Value>Classification : exclusivement en interne</baf:Value>
+ </baf:VisualMarkingPart>
+ <baf:VisualMarkingPart>
+ <baf:Identifier>Document: Footer</baf:Identifier>
+ <!-- Translators: this string can be localized -->
+ <baf:Value>Ce contenu est marqué pour un usage interne exclusivement. Il ne doit pas être communiqué à l'extérieur de l'entreprise.</baf:Value>
+ </baf:VisualMarkingPart>
+ <baf:VisualMarkingPart>
+ <baf:Identifier>Document: Watermark</baf:Identifier>
+ <!-- Translators: this string can be localized -->
+ <baf:Value>Interne exclusivement</baf:Value>
+ </baf:VisualMarkingPart>
+ </baf:LabelingRules>
+ <baf:ImpactLevel>
+ <baf:Scale>UK-Cabinet</baf:Scale>
+ <baf:ConfidentalityValue>3</baf:ConfidentalityValue>
+ </baf:ImpactLevel>
+ </baf:BusinessAuthorizationCategory>
+ </baf:Included>
+</baf:BusinessAuthorization>
+<!-- vim:set shiftwidth=4 softtabstop=4 expandtab:
+-->
diff --git a/sfx2/classification/example_hu-HU.xml b/sfx2/classification/example_hu-HU.xml
new file mode 100644
index 000000000..1bb6e9488
--- /dev/null
+++ b/sfx2/classification/example_hu-HU.xml
@@ -0,0 +1,71 @@
+<?xml version="1.0"?>
+<baf:BusinessAuthorization xmlns:baf="urn:tscp:names:baf:1.1">
+ <baf:PolicyAuthorityName>TSCP példa irányelv-szolgáltató</baf:PolicyAuthorityName>
+ <baf:PolicyName>TSCP példa irányelv</baf:PolicyName>
+ <baf:AdministrativeData>
+ <baf:ProgramID>urn:example:tscp:1</baf:ProgramID>
+ </baf:AdministrativeData>
+ <baf:Included>
+ <baf:BusinessAuthorizationCategory Identifier="urn:example:tscp:1:non-business" Name="Nem üzleti">
+ <baf:LabelingRules/>
+ <baf:ImpactLevel>
+ <baf:Scale>UK-Cabinet</baf:Scale>
+ <baf:ConfidentalityValue>0</baf:ConfidentalityValue>
+ </baf:ImpactLevel>
+ </baf:BusinessAuthorizationCategory>
+ <baf:BusinessAuthorizationCategory Identifier="urn:example:tscp:1:general-business" Name="Általános üzleti">
+ <baf:LabelingRules>
+ <baf:VisualMarkingPart>
+ <baf:Identifier>Document: Header</baf:Identifier>
+ <baf:Value>Besorolás: Általános üzleti</baf:Value>
+ </baf:VisualMarkingPart>
+ </baf:LabelingRules>
+ <baf:ImpactLevel>
+ <baf:Scale>UK-Cabinet</baf:Scale>
+ <baf:ConfidentalityValue>1</baf:ConfidentalityValue>
+ </baf:ImpactLevel>
+ </baf:BusinessAuthorizationCategory>
+ <baf:BusinessAuthorizationCategory Identifier="urn:example:tscp:1:confidential" Name="Bizalmas">
+ <baf:LabelingRules>
+ <baf:VisualMarkingPart>
+ <baf:Identifier>Document: Header</baf:Identifier>
+ <baf:Value>Besorolás: bizalmas</baf:Value>
+ </baf:VisualMarkingPart>
+ <baf:VisualMarkingPart>
+ <baf:Identifier>Document: Footer</baf:Identifier>
+ <baf:Value>Ez a tartalom bizalmasként van megjelölve. Ne terjessze külső feleknek vezetői jóváhagyás nélkül.</baf:Value>
+ </baf:VisualMarkingPart>
+ <baf:VisualMarkingPart>
+ <baf:Identifier>Document: Watermark</baf:Identifier>
+ <baf:Value>Bizalmas</baf:Value>
+ </baf:VisualMarkingPart>
+ </baf:LabelingRules>
+ <baf:ImpactLevel>
+ <baf:Scale>UK-Cabinet</baf:Scale>
+ <baf:ConfidentalityValue>2</baf:ConfidentalityValue>
+ </baf:ImpactLevel>
+ </baf:BusinessAuthorizationCategory>
+ <baf:BusinessAuthorizationCategory Identifier="urn:example:tscp:1:internal-only" Name="Csak belső">
+ <baf:LabelingRules>
+ <baf:VisualMarkingPart>
+ <baf:Identifier>Document: Header</baf:Identifier>
+ <baf:Value>Besorolás: csak belső</baf:Value>
+ </baf:VisualMarkingPart>
+ <baf:VisualMarkingPart>
+ <baf:Identifier>Document: Footer</baf:Identifier>
+ <baf:Value>Ez a tartalom csak belső felhasználásúként van megjelölve. Ne terjessze a szervezeten kívüli feleknek.</baf:Value>
+ </baf:VisualMarkingPart>
+ <baf:VisualMarkingPart>
+ <baf:Identifier>Document: Watermark</baf:Identifier>
+ <baf:Value>Csak belső</baf:Value>
+ </baf:VisualMarkingPart>
+ </baf:LabelingRules>
+ <baf:ImpactLevel>
+ <baf:Scale>UK-Cabinet</baf:Scale>
+ <baf:ConfidentalityValue>3</baf:ConfidentalityValue>
+ </baf:ImpactLevel>
+ </baf:BusinessAuthorizationCategory>
+ </baf:Included>
+</baf:BusinessAuthorization>
+<!-- vim:set shiftwidth=4 softtabstop=4 expandtab:
+-->
diff --git a/sfx2/classification/example_nl-NL.xml b/sfx2/classification/example_nl-NL.xml
new file mode 100644
index 000000000..7afc17043
--- /dev/null
+++ b/sfx2/classification/example_nl-NL.xml
@@ -0,0 +1,71 @@
+<?xml version="1.0"?>
+<baf:BusinessAuthorization xmlns:baf="urn:tscp:names:baf:1.1">
+ <baf:PolicyAuthorityName>Voorbeeld beleidsauthoriteit TSCP</baf:PolicyAuthorityName>
+ <baf:PolicyName>Voorbeeld beleid TSCP</baf:PolicyName>
+ <baf:AdministrativeData>
+ <baf:ProgramID>urn:example:tscp:1</baf:ProgramID>
+ </baf:AdministrativeData>
+ <baf:Included>
+ <baf:BusinessAuthorizationCategory Identifier="urn:example:tscp:1:non-business" Name="Openbaar">
+ <baf:LabelingRules/>
+ <baf:ImpactLevel>
+ <baf:Scale>UK-Cabinet</baf:Scale>
+ <baf:ConfidentalityValue>0</baf:ConfidentalityValue>
+ </baf:ImpactLevel>
+ </baf:BusinessAuthorizationCategory>
+ <baf:BusinessAuthorizationCategory Identifier="urn:example:tscp:1:general-business" Name="Algemeen bedrijfsvoering">
+ <baf:LabelingRules>
+ <baf:VisualMarkingPart>
+ <baf:Identifier>Document: Header</baf:Identifier>
+ <baf:Value>Classificatie: Algemeen bedrijfsvoering</baf:Value>
+ </baf:VisualMarkingPart>
+ </baf:LabelingRules>
+ <baf:ImpactLevel>
+ <baf:Scale>UK-Cabinet</baf:Scale>
+ <baf:ConfidentalityValue>1</baf:ConfidentalityValue>
+ </baf:ImpactLevel>
+ </baf:BusinessAuthorizationCategory>
+ <baf:BusinessAuthorizationCategory Identifier="urn:example:tscp:1:confidential" Name="Vertrouwelijk">
+ <baf:LabelingRules>
+ <baf:VisualMarkingPart>
+ <baf:Identifier>Document: Header</baf:Identifier>
+ <baf:Value>Classificatie: Vertrouwelijk</baf:Value>
+ </baf:VisualMarkingPart>
+ <baf:VisualMarkingPart>
+ <baf:Identifier>Document: Footer</baf:Identifier>
+ <baf:Value>De inhoud van dit document is vertrouwelijk. Verspreid het uitsluitend met de vereiste toestemming.</baf:Value>
+ </baf:VisualMarkingPart>
+ <baf:VisualMarkingPart>
+ <baf:Identifier>Document: Watermark</baf:Identifier>
+ <baf:Value>Vertrouwelijk</baf:Value>
+ </baf:VisualMarkingPart>
+ </baf:LabelingRules>
+ <baf:ImpactLevel>
+ <baf:Scale>UK-Cabinet</baf:Scale>
+ <baf:ConfidentalityValue>2</baf:ConfidentalityValue>
+ </baf:ImpactLevel>
+ </baf:BusinessAuthorizationCategory>
+ <baf:BusinessAuthorizationCategory Identifier="urn:example:tscp:1:internal-only" Name="Intern">
+ <baf:LabelingRules>
+ <baf:VisualMarkingPart>
+ <baf:Identifier>Document: Header</baf:Identifier>
+ <baf:Value>Classificatie: Intern</baf:Value>
+ </baf:VisualMarkingPart>
+ <baf:VisualMarkingPart>
+ <baf:Identifier>Document: Footer</baf:Identifier>
+ <baf:Value>De inhoud van dit document is uitsluitend voor intern gebruik. Verspreid het niet buiten de organisatie.</baf:Value>
+ </baf:VisualMarkingPart>
+ <baf:VisualMarkingPart>
+ <baf:Identifier>Document: Watermark</baf:Identifier>
+ <baf:Value>Intern</baf:Value>
+ </baf:VisualMarkingPart>
+ </baf:LabelingRules>
+ <baf:ImpactLevel>
+ <baf:Scale>UK-Cabinet</baf:Scale>
+ <baf:ConfidentalityValue>3</baf:ConfidentalityValue>
+ </baf:ImpactLevel>
+ </baf:BusinessAuthorizationCategory>
+ </baf:Included>
+</baf:BusinessAuthorization>
+<!-- vim:set shiftwidth=4 softtabstop=4 expandtab:
+-->
diff --git a/sfx2/classification/example_pt-BR.xml b/sfx2/classification/example_pt-BR.xml
new file mode 100644
index 000000000..e8a0bfdb2
--- /dev/null
+++ b/sfx2/classification/example_pt-BR.xml
@@ -0,0 +1,89 @@
+<?xml version="1.0"?>
+<baf:BusinessAuthorization xmlns:baf="urn:tscp:names:baf:1.1" xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0">
+ <!-- Translators: this string can be localized -->
+ <baf:PolicyAuthorityName>Exemplo de autoridade de políticas TSCP de segurança</baf:PolicyAuthorityName>
+ <!-- Translators: this string can be localized -->
+ <baf:PolicyName>Exemplo de política de segurança da informação</baf:PolicyName>
+ <baf:AdministrativeData>
+ <baf:ProgramID>urn:example:tscp:1</baf:ProgramID>
+ </baf:AdministrativeData>
+ <baf:Included>
+ <!-- Translators: the Name attribute in this string can be localized -->
+ <baf:BusinessAuthorizationCategory Identifier="urn:example:tscp:1:non-business" Name="Público" loextAbbreviatedName="PUB">
+ <baf:LabelingRules/>
+ <baf:ImpactLevel>
+ <baf:Scale>UK-Cabinet</baf:Scale>
+ <baf:ConfidentalityValue>0</baf:ConfidentalityValue>
+ </baf:ImpactLevel>
+ </baf:BusinessAuthorizationCategory>
+ <!-- Translators: the Name attribute in this string can be localized -->
+ <baf:BusinessAuthorizationCategory Identifier="urn:example:tscp:1:general-business" Name="Corporativo" loextAbbreviatedName="CORP">
+ <baf:LabelingRules>
+ <baf:VisualMarkingPart>
+ <baf:Identifier>Document: Header</baf:Identifier>
+ <!-- Translators: this string can be localized -->
+ <baf:Value>Classificação: Corporativo</baf:Value>
+ </baf:VisualMarkingPart>
+ </baf:LabelingRules>
+ <baf:ImpactLevel>
+ <baf:Scale>UK-Cabinet</baf:Scale>
+ <baf:ConfidentalityValue>1</baf:ConfidentalityValue>
+ </baf:ImpactLevel>
+ </baf:BusinessAuthorizationCategory>
+ <!-- Translators: the Name attribute in this string can be localized -->
+ <baf:BusinessAuthorizationCategory Identifier="urn:example:tscp:1:confidential" Name="Confidencial" loextAbbreviatedName="CONF">
+ <baf:LabelingRules>
+ <baf:VisualMarkingPart>
+ <baf:Identifier>Document: Header</baf:Identifier>
+ <!-- Translators: this string can be localized -->
+ <baf:Value>Classificação: Confidencial</baf:Value>
+ </baf:VisualMarkingPart>
+ <baf:VisualMarkingPart>
+ <baf:Identifier>Document: Footer</baf:Identifier>
+ <!-- Translators: this string can be localized -->
+ <baf:Value>Conteúdo confidencial. Vedada a distribuição externa sem aprovação da empresa.</baf:Value>
+ </baf:VisualMarkingPart>
+ <baf:VisualMarkingPart>
+ <baf:Identifier>Document: Watermark</baf:Identifier>
+ <!-- Translators: this string can be localized -->
+ <baf:Value>CONFIDENCIAL</baf:Value>
+ </baf:VisualMarkingPart>
+ </baf:LabelingRules>
+ <baf:ImpactLevel>
+ <baf:Scale>UK-Cabinet</baf:Scale>
+ <baf:ConfidentalityValue>2</baf:ConfidentalityValue>
+ </baf:ImpactLevel>
+ </baf:BusinessAuthorizationCategory>
+ <!-- Translators: the Name attribute in this string can be localized -->
+ <baf:BusinessAuthorizationCategory Identifier="urn:example:tscp:1:internal-only" Name="Restrito" loextAbbreviatedName="RES">
+ <baf:LabelingRules>
+ <baf:VisualMarkingPart>
+ <baf:Identifier>Document: Header</baf:Identifier>
+ <!-- Translators: this string can be localized -->
+ <baf:Value>Classificação: Restrito</baf:Value>
+ </baf:VisualMarkingPart>
+ <baf:VisualMarkingPart>
+ <baf:Identifier>Document: Footer</baf:Identifier>
+ <!-- Translators: this string can be localized -->
+ <baf:Value>Conteúdo restrito. Vedada a distribuição externa.</baf:Value>
+ </baf:VisualMarkingPart>
+ <baf:VisualMarkingPart>
+ <baf:Identifier>Document: Watermark</baf:Identifier>
+ <!-- Translators: this string can be localized -->
+ <baf:Value>RESTRITO</baf:Value>
+ </baf:VisualMarkingPart>
+ </baf:LabelingRules>
+ <baf:ImpactLevel>
+ <baf:Scale>UK-Cabinet</baf:Scale>
+ <baf:ConfidentalityValue>3</baf:ConfidentalityValue>
+ </baf:ImpactLevel>
+ </baf:BusinessAuthorizationCategory>
+ <loext:Marking Name="Exemplo de marcação" />
+ <loext:IntellectualPropertyPart Name="Exemplo de 1ª parte de propriedade intelectual" />
+ <loext:IntellectualPropertyPart Name="Examplo de 2ª parte de propriedade intelectual" />
+ <loext:IntellectualPropertyPartNumber Name="1" />
+ <loext:IntellectualPropertyPartNumber Name="2" />
+ </baf:Included>
+</baf:BusinessAuthorization>
+<!-- vim:set shiftwidth=4 softtabstop=4 expandtab:
+-->
diff --git a/sfx2/classification/example_sl-SI.xml b/sfx2/classification/example_sl-SI.xml
new file mode 100644
index 000000000..13a4bcfdf
--- /dev/null
+++ b/sfx2/classification/example_sl-SI.xml
@@ -0,0 +1,76 @@
+<?xml version="1.0"?>
+<baf:BusinessAuthorization xmlns:baf="urn:tscp:names:baf:1.1" xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0">
+ <baf:PolicyAuthorityName>Avtoriteta primera politike TSCP</baf:PolicyAuthorityName>
+ <baf:PolicyName>Primer politike TSCP</baf:PolicyName>
+ <baf:AdministrativeData>
+ <baf:ProgramID>urn:example:tscp:1</baf:ProgramID>
+ </baf:AdministrativeData>
+ <baf:Included>
+ <baf:BusinessAuthorizationCategory Identifier="urn:example:tscp:1:non-business" Name="Neposlovno">
+ <baf:LabelingRules/>
+ <baf:ImpactLevel>
+ <baf:Scale>UK-Cabinet</baf:Scale>
+ <baf:ConfidentalityValue>0</baf:ConfidentalityValue>
+ </baf:ImpactLevel>
+ </baf:BusinessAuthorizationCategory>
+ <baf:BusinessAuthorizationCategory Identifier="urn:example:tscp:1:general-business" Name="Splošno poslovanje">
+ <baf:LabelingRules>
+ <baf:VisualMarkingPart>
+ <baf:Identifier>Document: Header</baf:Identifier>
+ <baf:Value>Stopnja zaupnosti: splošno poslovanje</baf:Value>
+ </baf:VisualMarkingPart>
+ </baf:LabelingRules>
+ <baf:ImpactLevel>
+ <baf:Scale>UK-Cabinet</baf:Scale>
+ <baf:ConfidentalityValue>1</baf:ConfidentalityValue>
+ </baf:ImpactLevel>
+ </baf:BusinessAuthorizationCategory>
+ <baf:BusinessAuthorizationCategory Identifier="urn:example:tscp:1:confidential" Name="Zaupno">
+ <baf:LabelingRules>
+ <baf:VisualMarkingPart>
+ <baf:Identifier>Document: Header</baf:Identifier>
+ <baf:Value>Stopnja zaupnosti: zaupno</baf:Value>
+ </baf:VisualMarkingPart>
+ <baf:VisualMarkingPart>
+ <baf:Identifier>Document: Footer</baf:Identifier>
+ <baf:Value>Ta vsebina ima oznako zaupno. Ne razširjajte je zunaj brez poslovne odobritve.</baf:Value>
+ </baf:VisualMarkingPart>
+ <baf:VisualMarkingPart>
+ <baf:Identifier>Document: Watermark</baf:Identifier>
+ <baf:Value>Zaupno</baf:Value>
+ </baf:VisualMarkingPart>
+ </baf:LabelingRules>
+ <baf:ImpactLevel>
+ <baf:Scale>UK-Cabinet</baf:Scale>
+ <baf:ConfidentalityValue>2</baf:ConfidentalityValue>
+ </baf:ImpactLevel>
+ </baf:BusinessAuthorizationCategory>
+ <baf:BusinessAuthorizationCategory Identifier="urn:example:tscp:1:internal-only" Name="Interno">
+ <baf:LabelingRules>
+ <baf:VisualMarkingPart>
+ <baf:Identifier>Document: Header</baf:Identifier>
+ <baf:Value>Stopnja zaupnosti: interno</baf:Value>
+ </baf:VisualMarkingPart>
+ <baf:VisualMarkingPart>
+ <baf:Identifier>Document: Footer</baf:Identifier>
+ <baf:Value>Ta vsebina ima oznako interno. Ne razširjajte je izven podjetja.</baf:Value>
+ </baf:VisualMarkingPart>
+ <baf:VisualMarkingPart>
+ <baf:Identifier>Document: Watermark</baf:Identifier>
+ <baf:Value>Interno</baf:Value>
+ </baf:VisualMarkingPart>
+ </baf:LabelingRules>
+ <baf:ImpactLevel>
+ <baf:Scale>UK-Cabinet</baf:Scale>
+ <baf:ConfidentalityValue>3</baf:ConfidentalityValue>
+ </baf:ImpactLevel>
+ </baf:BusinessAuthorizationCategory>
+ <loext:Marking Name="Primer oznake" />
+ <loext:IntellectualPropertyPart Name="Primer - del intelektualne lastnine ena" />
+ <loext:IntellectualPropertyPart Name="Primer - del intelektualne lastnine dve" />
+ <loext:IntellectualPropertyPartNumber Name="1" />
+ <loext:IntellectualPropertyPartNumber Name="2" />
+ </baf:Included>
+</baf:BusinessAuthorization>
+<!-- vim:set shiftwidth=4 softtabstop=4 expandtab:
+-->
diff --git a/sfx2/classification/example_zh-CN.xml b/sfx2/classification/example_zh-CN.xml
new file mode 100644
index 000000000..43acce556
--- /dev/null
+++ b/sfx2/classification/example_zh-CN.xml
@@ -0,0 +1,89 @@
+<?xml version="1.0"?>
+<baf:BusinessAuthorization xmlns:baf="urn:tscp:names:baf:1.1" xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0">
+ <!-- Translators: this string can be localized -->
+ <baf:PolicyAuthorityName>TSCP 示例策略授权</baf:PolicyAuthorityName>
+ <!-- Translators: this string can be localized -->
+ <baf:PolicyName>TSCP 示例策略</baf:PolicyName>
+ <baf:AdministrativeData>
+ <baf:ProgramID>urn:example:tscp:1</baf:ProgramID>
+ </baf:AdministrativeData>
+ <baf:Included>
+ <!-- Translators: the Name attribute in this string can be localized -->
+ <baf:BusinessAuthorizationCategory Identifier="urn:example:tscp:1:non-business" Name="非商务" loextAbbreviatedName="NB">
+ <baf:LabelingRules/>
+ <baf:ImpactLevel>
+ <baf:Scale>UK-Cabinet</baf:Scale>
+ <baf:ConfidentalityValue>0</baf:ConfidentalityValue>
+ </baf:ImpactLevel>
+ </baf:BusinessAuthorizationCategory>
+ <!-- Translators: the Name attribute in this string can be localized -->
+ <baf:BusinessAuthorizationCategory Identifier="urn:example:tscp:1:general-business" Name="一般性商务" loextAbbreviatedName="GB">
+ <baf:LabelingRules>
+ <baf:VisualMarkingPart>
+ <baf:Identifier>Document: Header</baf:Identifier>
+ <!-- Translators: this string can be localized -->
+ <baf:Value>保密等级:一般性商务</baf:Value>
+ </baf:VisualMarkingPart>
+ </baf:LabelingRules>
+ <baf:ImpactLevel>
+ <baf:Scale>UK-Cabinet</baf:Scale>
+ <baf:ConfidentalityValue>1</baf:ConfidentalityValue>
+ </baf:ImpactLevel>
+ </baf:BusinessAuthorizationCategory>
+ <!-- Translators: the Name attribute in this string can be localized -->
+ <baf:BusinessAuthorizationCategory Identifier="urn:example:tscp:1:confidential" Name="保密" loextAbbreviatedName="Conf">
+ <baf:LabelingRules>
+ <baf:VisualMarkingPart>
+ <baf:Identifier>Document: Header</baf:Identifier>
+ <!-- Translators: this string can be localized -->
+ <baf:Value>保密等级:保密</baf:Value>
+ </baf:VisualMarkingPart>
+ <baf:VisualMarkingPart>
+ <baf:Identifier>Document: Footer</baf:Identifier>
+ <!-- Translators: this string can be localized -->
+ <baf:Value>此处的内容被标记为“保密”。未经许可,请勿对外披露。</baf:Value>
+ </baf:VisualMarkingPart>
+ <baf:VisualMarkingPart>
+ <baf:Identifier>Document: Watermark</baf:Identifier>
+ <!-- Translators: this string can be localized -->
+ <baf:Value>保密</baf:Value>
+ </baf:VisualMarkingPart>
+ </baf:LabelingRules>
+ <baf:ImpactLevel>
+ <baf:Scale>UK-Cabinet</baf:Scale>
+ <baf:ConfidentalityValue>2</baf:ConfidentalityValue>
+ </baf:ImpactLevel>
+ </baf:BusinessAuthorizationCategory>
+ <!-- Translators: the Name attribute in this string can be localized -->
+ <baf:BusinessAuthorizationCategory Identifier="urn:example:tscp:1:internal-only" Name="内部专用" loextAbbreviatedName="IO">
+ <baf:LabelingRules>
+ <baf:VisualMarkingPart>
+ <baf:Identifier>Document: Header</baf:Identifier>
+ <!-- Translators: this string can be localized -->
+ <baf:Value>保密等级:内部专用</baf:Value>
+ </baf:VisualMarkingPart>
+ <baf:VisualMarkingPart>
+ <baf:Identifier>Document: Footer</baf:Identifier>
+ <!-- Translators: this string can be localized -->
+ <baf:Value>此处的内容被标记为“内部专用”。未经许可,请勿对外披露。</baf:Value>
+ </baf:VisualMarkingPart>
+ <baf:VisualMarkingPart>
+ <baf:Identifier>Document: Watermark</baf:Identifier>
+ <!-- Translators: this string can be localized -->
+ <baf:Value>内部专用</baf:Value>
+ </baf:VisualMarkingPart>
+ </baf:LabelingRules>
+ <baf:ImpactLevel>
+ <baf:Scale>UK-Cabinet</baf:Scale>
+ <baf:ConfidentalityValue>3</baf:ConfidentalityValue>
+ </baf:ImpactLevel>
+ </baf:BusinessAuthorizationCategory>
+ <loext:Marking Name="示例标记" />
+ <loext:IntellectualPropertyPart Name="示例知识产权第一部分" />
+ <loext:IntellectualPropertyPart Name="示例知识产权第二部分" />
+ <loext:IntellectualPropertyPartNumber Name="1" />
+ <loext:IntellectualPropertyPartNumber Name="2" />
+ </baf:Included>
+</baf:BusinessAuthorization>
+<!-- vim:set shiftwidth=4 softtabstop=4 expandtab:
+-->
diff --git a/sfx2/classification/xAL-types.xsd b/sfx2/classification/xAL-types.xsd
new file mode 100644
index 000000000..052299007
--- /dev/null
+++ b/sfx2/classification/xAL-types.xsd
@@ -0,0 +1,510 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xs:schema xmlns="urn:oasis:names:tc:ciq:xal:3" xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="urn:oasis:names:tc:ciq:xal:3" elementFormDefault="qualified" attributeFormDefault="qualified">
+ <xs:annotation>
+ <xs:documentation>
+ Specification Name: OASIS CIQ TC - extensible AddressLanguage Types (xAL-types)
+ Description: Defines the W3C schema that provides enumeration lists to support xNL v3.0
+ (Using XML Schema based standard code list/enumeration mechanism - OPTION 1 AND DEFAULT)
+ Produced by: OASIS Customer Information Quality Technical Committee
+ URL: http://www.oasis-open.org/committees/ciq
+ Version: 3.0
+ Status: Committee Specification
+ Copyright: 2006-07, OASIS, http://www.oasis-open.org
+ Last Modified: 18 September 2007
+ Last Modified by: Ram Kumar, Chair, OASIS CIQ TC
+
+ NOTE: This is the schema that users can customise the enumeration lists to meet their
+ exchange requirements. The enumeration values provided are ONLY SAMPLES and
+ is not complete. It is up to the application to decide what the values should be. To achieve
+ interoperability between applications using this specification, it is recommended that an
+ SLA/agreement is in place as to what the enumeration values will be used in this file
+ </xs:documentation>
+ </xs:annotation>
+ <xs:simpleType name="AddressTypeList">
+ <xs:annotation>
+ <xs:documentation>A list of types of addresses</xs:documentation>
+ </xs:annotation>
+ <xs:restriction base="xs:normalizedString">
+ <xs:enumeration value="Airport"/>
+ <xs:enumeration value="Business"/>
+ <xs:enumeration value="CaravanPark"/>
+ <xs:enumeration value="CommercialPark"/>
+ <xs:enumeration value="CommunityDevelopment"/>
+ <xs:enumeration value="EducationalInstitution"/>
+ <xs:enumeration value="Entertainment"/>
+ <xs:enumeration value="Hospital"/>
+ <xs:enumeration value="Location"/>
+ <xs:enumeration value="Marina"/>
+ <xs:enumeration value="MilitaryBase"/>
+ <xs:enumeration value="OverseasMilitary"/>
+ <xs:enumeration value="Port"/>
+ <xs:enumeration value="Primary"/>
+ <xs:enumeration value="RecreationalPark"/>
+ <xs:enumeration value="Resort"/>
+ <xs:enumeration value="RetirementVillage"/>
+ <xs:enumeration value="Rural"/>
+ <xs:enumeration value="Secondary"/>
+ <xs:enumeration value="ShoppingCentre"/>
+ <xs:enumeration value="SportingCentre"/>
+ <xs:enumeration value="Urban"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType name="AddressIDTypeList">
+ <xs:annotation>
+ <xs:documentation>A list of types of address identifiers </xs:documentation>
+ </xs:annotation>
+ <xs:restriction base="xs:normalizedString"/>
+ </xs:simpleType>
+ <xs:simpleType name="AddressLineTypeList">
+ <xs:annotation>
+ <xs:documentation>A list of types of address line, e.g. street details, locality details</xs:documentation>
+ </xs:annotation>
+ <xs:restriction base="xs:normalizedString"/>
+ </xs:simpleType>
+ <xs:simpleType name="AddressUsageList">
+ <xs:annotation>
+ <xs:documentation>A list of types of usage of the address</xs:documentation>
+ </xs:annotation>
+ <xs:restriction base="xs:normalizedString">
+ <xs:enumeration value="Business"/>
+ <xs:enumeration value="Billing"/>
+ <xs:enumeration value="Communication"/>
+ <xs:enumeration value="Contact"/>
+ <xs:enumeration value="Mailing"/>
+ <xs:enumeration value="Personal"/>
+ <xs:enumeration value="Postal"/>
+ <xs:enumeration value="Residential"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType name="AdministrativeAreaTypeList">
+ <xs:annotation>
+ <xs:documentation>A list of administrative area types</xs:documentation>
+ </xs:annotation>
+ <xs:restriction base="xs:normalizedString">
+ <xs:enumeration value="City">
+ <xs:annotation>
+ <xs:documentation>Only name of the administrative area without its type, e.g. NSW, CA, Quebec</xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ <xs:enumeration value="State">
+ <xs:annotation>
+ <xs:documentation>The type of the area, e.g. state, district, province, etc.</xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ <xs:enumeration value="Territory"/>
+ <xs:enumeration value="Province"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType name="AdministrativeAreaNameTypeList">
+ <xs:annotation>
+ <xs:documentation>A list of administrative area name element types</xs:documentation>
+ </xs:annotation>
+ <xs:restriction base="xs:normalizedString">
+ <xs:enumeration value="Name">
+ <xs:annotation>
+ <xs:documentation>Name of the administrative area</xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ <xs:enumeration value="Number"/>
+ <xs:enumeration value="ReferenceLocation">
+ <xs:annotation>
+ <xs:documentation>Reference location information in support of the administrative area. e.g. Territory of France</xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ <xs:enumeration value="Type">
+ <xs:annotation>
+ <xs:documentation>Other supporting information </xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType name="AdministrativeAreaNameCodeList">
+ <xs:annotation>
+ <xs:documentation>A list of codes for name of administrative area</xs:documentation>
+ </xs:annotation>
+ <xs:restriction base="xs:normalizedString"/>
+ </xs:simpleType>
+ <xs:simpleType name="CountryNameTypeList">
+ <xs:annotation>
+ <xs:documentation>A list of country name element types</xs:documentation>
+ </xs:annotation>
+ <xs:restriction base="xs:normalizedString">
+ <xs:enumeration value="Name">
+ <xs:annotation>
+ <xs:documentation>Name of the country e.g. AUSTRALIA</xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ <xs:enumeration value="Type">
+ <xs:annotation>
+ <xs:documentation>Although a Country, could be classified as a territory of a country. For example, "NOUVELLE CALEDONIE" is a territory of "FRANCE".</xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType name="CountryNameCodeList">
+ <xs:annotation>
+ <xs:documentation>A list of codes for name of country</xs:documentation>
+ </xs:annotation>
+ <xs:restriction base="xs:normalizedString"/>
+ </xs:simpleType>
+ <xs:simpleType name="DatumCodeList">
+ <xs:annotation>
+ <xs:documentation>A list of codes for datum</xs:documentation>
+ </xs:annotation>
+ <xs:restriction base="xs:normalizedString"/>
+ </xs:simpleType>
+ <xs:simpleType name="DeliveryModeList">
+ <xs:annotation>
+ <xs:documentation>A list of codes for mode of delivery of address</xs:documentation>
+ </xs:annotation>
+ <xs:restriction base="xs:normalizedString"/>
+ </xs:simpleType>
+ <xs:simpleType name="DirectionTypeList">
+ <xs:annotation>
+ <xs:documentation>A list of directions for geo-coordinates</xs:documentation>
+ </xs:annotation>
+ <xs:restriction base="xs:normalizedString">
+ <xs:enumeration value="East"/>
+ <xs:enumeration value="West"/>
+ <xs:enumeration value="North"/>
+ <xs:enumeration value="South"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType name="IdentifierElementTypeList">
+ <xs:annotation>
+ <xs:documentation>A list of name types for commonly used Number type</xs:documentation>
+ </xs:annotation>
+ <xs:restriction base="xs:normalizedString">
+ <xs:enumeration value="Name">
+ <xs:annotation>
+ <xs:documentation>Applicable to mail box office names such as PO BOX, GPO BOX, MAIL BAG NO., etc. </xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ <xs:enumeration value="RangeFrom">
+ <xs:annotation>
+ <xs:documentation>Indicates that the element contains the lower value of a range, e.g. 25 in 25-37</xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ <xs:enumeration value="Range">
+ <xs:annotation>
+ <xs:documentation>Indicates that the value is a range, e.g. 25-37</xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ <xs:enumeration value="RangeTo">
+ <xs:annotation>
+ <xs:documentation>Indicates that the element contains the top value of a range, e.g. 25 in 25-37</xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ <xs:enumeration value="Prefix">
+ <xs:annotation>
+ <xs:documentation>Indicates that the element contains some value that is important, but not exactly the number itself. E.g. PoBox can be a prefix in PoBox 2020, street no. A-15, where A is the prefix and 15 is the number</xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ <xs:enumeration value="Suffix">
+ <xs:annotation>
+ <xs:documentation>Indicates that the element contains some value that is important, but not exactly the number itself. E.g. 'bis' in '45 bis'</xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ <xs:enumeration value="Number">
+ <xs:annotation>
+ <xs:documentation>Indicates that the value is number, e.g. 2020 in PoBox 2020. The actual value can be alpha-numeric. </xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ <xs:enumeration value="Separator">
+ <xs:annotation>
+ <xs:documentation>Indicates that the value is a separator that is expected to be preserved. Examples are / - #, as in 15-A where "-" is the separator</xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ <xs:enumeration value="Extension">
+ <xs:annotation>
+ <xs:documentation>Indicates that the value is an extension number of some identifier, e.g. 01 in Private Bag 2330-01, where the main number of the private bag is 2330, 12345-1223 in post code where 1223 is the extension</xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType name="LocalityNameTypeList">
+ <xs:annotation>
+ <xs:documentation>A list of locality name element types such as name of locality, reference data in support of locality</xs:documentation>
+ </xs:annotation>
+ <xs:restriction base="xs:normalizedString">
+ <xs:enumeration value="Name">
+ <xs:annotation>
+ <xs:documentation>Name of the locality</xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ <xs:enumeration value="Number"/>
+ <xs:enumeration value="ReferenceLocation">
+ <xs:annotation>
+ <xs:documentation>Any reference locality data in support of the locality. e.g. Next town north of Town A, via-town name</xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ <xs:enumeration value="Type">
+ <xs:annotation>
+ <xs:documentation>Other supporting information </xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType name="LocalityNameCodeList">
+ <xs:annotation>
+ <xs:documentation>A list of codes for name of locality</xs:documentation>
+ </xs:annotation>
+ <xs:restriction base="xs:normalizedString"/>
+ </xs:simpleType>
+ <xs:simpleType name="LocalityTypeList">
+ <xs:annotation>
+ <xs:documentation>A list of locality name types such as Municipality, Village, Area, etc</xs:documentation>
+ </xs:annotation>
+ <xs:restriction base="xs:normalizedString">
+ <xs:enumeration value="Municipality"/>
+ <xs:enumeration value="PostTown"/>
+ <xs:enumeration value="Place"/>
+ <xs:enumeration value="Suburb"/>
+ <xs:enumeration value="Town"/>
+ <xs:enumeration value="Village"/>
+ <xs:enumeration value="Area"/>
+ <xs:enumeration value="Zone"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType name="MeridianCodeList">
+ <xs:annotation>
+ <xs:documentation>A list of meridian codes</xs:documentation>
+ </xs:annotation>
+ <xs:restriction base="xs:normalizedString"/>
+ </xs:simpleType>
+ <xs:simpleType name="PostOfficeTypeList">
+ <xs:annotation>
+ <xs:documentation>A list of types of postal delivery offices</xs:documentation>
+ </xs:annotation>
+ <xs:restriction base="xs:normalizedString"/>
+ </xs:simpleType>
+ <xs:simpleType name="PostalDeliveryPointTypeList">
+ <xs:annotation>
+ <xs:documentation>A list of postal delivery point types</xs:documentation>
+ </xs:annotation>
+ <xs:restriction base="xs:normalizedString">
+ <xs:enumeration value="GPOBox"/>
+ <xs:enumeration value="POBox"/>
+ <xs:enumeration value="LockedBag"/>
+ <xs:enumeration value="MailStop"/>
+ <xs:enumeration value="PigeonHole"/>
+ <xs:enumeration value="PrivateBag"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType name="ProjectionCodeList">
+ <xs:annotation>
+ <xs:documentation>A list of codes for projection</xs:documentation>
+ </xs:annotation>
+ <xs:restriction base="xs:normalizedString"/>
+ </xs:simpleType>
+ <xs:simpleType name="PremisesElementTypeList">
+ <xs:annotation>
+ <xs:documentation>A list of name types for premises</xs:documentation>
+ </xs:annotation>
+ <xs:restriction base="xs:normalizedString">
+ <xs:enumeration value="Name">
+ <xs:annotation>
+ <xs:documentation>Names of Premises such as airport, hospital, university, military base, etc. Can also be the name of the building or house or apartment</xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ <xs:enumeration value="Location">
+ <xs:annotation>
+ <xs:documentation>Where in the building/landmark the premises is located, e.g. lobby, ground floor, penthouse, or where in a larger complex (e.g. airport) the address is located.</xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ <xs:enumeration value="SubPremisesConnector">
+ <xs:annotation>
+ <xs:documentation>Free text description that is required to logically connect the 2 premises</xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ <xs:enumeration value="InternalThoroughfare">
+ <xs:annotation>
+ <xs:documentation>Roads and streets within boundaries of larger complexes/premises such as hospitals, airports, etc.</xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ <xs:enumeration value="ReferenceLocation">
+ <xs:annotation>
+ <xs:documentation>Free text description of some other location and how this premises relates to it, e.g. 300m from water station, new the police station, etc.</xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ <xs:enumeration value="Type">
+ <xs:annotation>
+ <xs:documentation>additional supporting information</xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType name="PremisesTypeList">
+ <xs:annotation>
+ <xs:documentation>A list of premises type</xs:documentation>
+ </xs:annotation>
+ <xs:restriction base="xs:normalizedString">
+ <xs:enumeration value="Airport"/>
+ <xs:enumeration value="Area"/>
+ <xs:enumeration value="Building"/>
+ <xs:enumeration value="Farm"/>
+ <xs:enumeration value="Hospital"/>
+ <xs:enumeration value="House"/>
+ <xs:enumeration value="LandMark"/>
+ <xs:enumeration value="LargeMailUser"/>
+ <xs:enumeration value="Lot"/>
+ <xs:enumeration value="RailwayStation"/>
+ <xs:enumeration value="ShoppingComplex"/>
+ <xs:enumeration value="University"/>
+ <xs:enumeration value="Unit"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType name="RuralDeliveryTypeList">
+ <xs:annotation>
+ <xs:documentation>A list of rural delivery types such as road, air, water</xs:documentation>
+ </xs:annotation>
+ <xs:restriction base="xs:normalizedString"/>
+ </xs:simpleType>
+ <xs:simpleType name="SubAdministrativeAreaNameTypeList">
+ <xs:annotation>
+ <xs:documentation>A list of sub administrative area name element types</xs:documentation>
+ </xs:annotation>
+ <xs:restriction base="xs:normalizedString">
+ <xs:enumeration value="Name">
+ <xs:annotation>
+ <xs:documentation>Name of the sub administrative area</xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ <xs:enumeration value="Number"/>
+ <xs:enumeration value="ReferenceLocation">
+ <xs:annotation>
+ <xs:documentation>Reference location information in support of the sub administrative area</xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ <xs:enumeration value="Type">
+ <xs:annotation>
+ <xs:documentation>Other supporting information </xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType name="SubAdministrativeAreaNameCodeList">
+ <xs:annotation>
+ <xs:documentation>A list of codes for name of sub administrative area</xs:documentation>
+ </xs:annotation>
+ <xs:restriction base="xs:normalizedString"/>
+ </xs:simpleType>
+ <xs:simpleType name="SubAdministrativeAreaTypeList">
+ <xs:annotation>
+ <xs:documentation>A list of sub administrative area name types</xs:documentation>
+ </xs:annotation>
+ <xs:restriction base="xs:normalizedString">
+ <xs:enumeration value="County"/>
+ <xs:enumeration value="District"/>
+ <xs:enumeration value="Province"/>
+ <xs:enumeration value="Region"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType name="SubLocalityNameTypeList">
+ <xs:annotation>
+ <xs:documentation>A list of sub locality name element types</xs:documentation>
+ </xs:annotation>
+ <xs:restriction base="xs:normalizedString">
+ <xs:enumeration value="Name"/>
+ <xs:enumeration value="Number"/>
+ <xs:enumeration value="ReferenceLocation"/>
+ <xs:enumeration value="Type">
+ <xs:annotation>
+ <xs:documentation>Other supporting information </xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType name="SubLocalityNameCodeList">
+ <xs:annotation>
+ <xs:documentation>A list of codes for names of sub locality</xs:documentation>
+ </xs:annotation>
+ <xs:restriction base="xs:normalizedString"/>
+ </xs:simpleType>
+ <xs:simpleType name="SubLocalityTypeList">
+ <xs:annotation>
+ <xs:documentation>A list of sub locality types</xs:documentation>
+ </xs:annotation>
+ <xs:restriction base="xs:normalizedString">
+ <xs:enumeration value="Municipality"/>
+ <xs:enumeration value="Village"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType name="SubPremisesTypeList">
+ <xs:annotation>
+ <xs:documentation>A list of sub premises types</xs:documentation>
+ </xs:annotation>
+ <xs:restriction base="xs:normalizedString">
+ <xs:enumeration value="Room"/>
+ <xs:enumeration value="Suite"/>
+ <xs:enumeration value="Apartment"/>
+ <xs:enumeration value="Shop"/>
+ <xs:enumeration value="Office"/>
+ <xs:enumeration value="Unit"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType name="ThoroughfareNameTypeList">
+ <xs:annotation>
+ <xs:documentation>A list of name element types for thoroughfare</xs:documentation>
+ </xs:annotation>
+ <xs:restriction base="xs:normalizedString">
+ <xs:enumeration value="NameOnly">
+ <xs:annotation>
+ <xs:documentation>Just the name part, such as Baker in Baker Street.</xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ <xs:enumeration value="PreDirection">
+ <xs:annotation>
+ <xs:documentation>North Archer Street, where "North" is PreDirection</xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ <xs:enumeration value="PostDirection">
+ <xs:annotation>
+ <xs:documentation>Archer Street North, where "North" is PostDirection</xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ <xs:enumeration value="NameAndNumber">
+ <xs:annotation>
+ <xs:documentation>This value indicates that the element contains the street name and street number. E.g. 39 Baker Street. Use this when you do not want to break the thoroughfare into atomic types</xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ <xs:enumeration value="NameAndType">
+ <xs:annotation>
+ <xs:documentation>Baker Street, where Baker is Name and Street is Type</xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ <xs:enumeration value="NameNumberAndType">
+ <xs:annotation>
+ <xs:documentation>21 Archer Street (Full thoroughfare details)</xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ <xs:enumeration value="Unstructured">
+ <xs:annotation>
+ <xs:documentation>Full details of a thoroughfare in a single line (unstructured)
+e.g. 39 Baker Street North</xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ <xs:enumeration value="SubThoroughfareConnector">
+ <xs:annotation>
+ <xs:documentation>When more than one street name is required to identify the location this type can be used to connect them with values such as CORNER OF or VIA.</xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ <xs:enumeration value="ReferenceLocation">
+ <xs:annotation>
+ <xs:documentation>Free text description of some other location and how this thoroughfare relates to it, e.g. 300m from water station, new the police station, etc.</xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ <xs:enumeration value="Type">
+ <xs:annotation>
+ <xs:documentation>Additional description like intersection, cross streets, etc</xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType name="ThoroughfareTypeList">
+ <xs:annotation>
+ <xs:documentation>A list of types for thoroughfare (e.g. STREET, ROAD, CRT)</xs:documentation>
+ </xs:annotation>
+ <xs:restriction base="xs:normalizedString"/>
+ </xs:simpleType>
+</xs:schema>
diff --git a/sfx2/classification/xAL.xsd b/sfx2/classification/xAL.xsd
new file mode 100644
index 000000000..13babc615
--- /dev/null
+++ b/sfx2/classification/xAL.xsd
@@ -0,0 +1,671 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns="urn:oasis:names:tc:ciq:xal:3" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ct="urn:oasis:names:tc:ciq:ct:3" targetNamespace="urn:oasis:names:tc:ciq:xal:3" elementFormDefault="qualified" attributeFormDefault="qualified">
+ <xs:annotation>
+ <xs:documentation>
+ Specification Name: OASIS CIQ TC - extensible Address Language (xAL)
+ Description: Defines the W3C schema for representing addresses
+ (Using XML Schema based standard code list/enumeration mechanism - OPTION 1 AND DEFAULT)
+ Produced by: OASIS Customer Information Quality Technical Committee
+ URL: http://www.oasis-open.org/committees/ciq
+ Version: 3.0
+ Status: Public Review Draft 03 ERRATA
+ Copyright: 2007-08, OASIS, http://www.oasis-open.org
+ Last Modified: 08 April 2008
+ Last Modified by: Ram Kumar, Chair, OASIS CIQ TC
+ NOTE: Do not modify this schema as it will break specifications compatibility
+ </xs:documentation>
+ </xs:annotation>
+ <xs:include schemaLocation="xAL-types.xsd"/>
+ <xs:import namespace="urn:oasis:names:tc:ciq:ct:3" schemaLocation="CommonTypes.xsd"/>
+ <xs:import namespace="http://www.w3.org/1999/xlink" schemaLocation="xlink-2003-12-31.xsd"/>
+ <xs:element name="Address" type="AddressType">
+ <xs:annotation>
+ <xs:documentation>Top level element for address with geocode details</xs:documentation>
+ </xs:annotation>
+ </xs:element>
+ <xs:complexType name="AddressType">
+ <xs:annotation>
+ <xs:documentation>Complex type that defines the structure of an address with geocode details for reuse</xs:documentation>
+ </xs:annotation>
+ <xs:sequence>
+ <xs:element name="FreeTextAddress" minOccurs="0">
+ <xs:annotation>
+ <xs:documentation>Container for free text address elements where address elements are not parsed </xs:documentation>
+ </xs:annotation>
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="AddressLine" maxOccurs="unbounded">
+ <xs:annotation>
+ <xs:documentation>Free format address representation. An address can have more than one line. The order of the AddressLine elements must be preserved.</xs:documentation>
+ </xs:annotation>
+ <xs:complexType>
+ <xs:simpleContent>
+ <xs:extension base="ct:String">
+ <xs:attribute name="Type" type="AddressLineTypeList">
+ <xs:annotation>
+ <xs:documentation>What does the address line describe? e.g. Street details, suburb details, post code details, whole address, etc</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attributeGroup ref="ct:grDataQuality"/>
+ <xs:anyAttribute namespace="##other"/>
+ </xs:extension>
+ </xs:simpleContent>
+ </xs:complexType>
+ </xs:element>
+ </xs:sequence>
+ <xs:attributeGroup ref="ct:grDataQuality"/>
+ <xs:anyAttribute namespace="##other" processContents="lax"/>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="Country" minOccurs="0">
+ <xs:annotation>
+ <xs:documentation>Country details</xs:documentation>
+ </xs:annotation>
+ <xs:complexType>
+ <xs:complexContent>
+ <xs:extension base="CountryType"/>
+ </xs:complexContent>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="AdministrativeArea" minOccurs="0">
+ <xs:annotation>
+ <xs:documentation>Details of the top-level area division in the country, such as state, district, province, island, region, etc. Note that some countries do not have this</xs:documentation>
+ </xs:annotation>
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="NameElement" maxOccurs="unbounded">
+ <xs:annotation>
+ <xs:documentation>Data associated with the Administrative Area. e.g. Full name of administrative area or part of it. eg. MI in USA, NSW in Australia, reference location to the administrative area</xs:documentation>
+ </xs:annotation>
+ <xs:complexType>
+ <xs:simpleContent>
+ <xs:extension base="ct:String">
+ <xs:attributeGroup ref="ct:grAbbreviation"/>
+ <xs:attribute name="NameType" type="AdministrativeAreaNameTypeList">
+ <xs:annotation>
+ <xs:documentation>semantics of data associated with name</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="NameCode" type="AdministrativeAreaNameCodeList">
+ <xs:annotation>
+ <xs:documentation>Name of administrative area represented as a code. e.g. "COL" for COLORADO</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="NameCodeType" type="ct:String">
+ <xs:annotation>
+ <xs:documentation>Type of code used to represent name as a code</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:anyAttribute namespace="##other"/>
+ </xs:extension>
+ </xs:simpleContent>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="SubAdministrativeArea" minOccurs="0">
+ <xs:annotation>
+ <xs:documentation>The next level down division of the area. E.g. state / county, province / reservation. Note that not all countries have a subadministrative area</xs:documentation>
+ </xs:annotation>
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="NameElement" maxOccurs="unbounded">
+ <xs:annotation>
+ <xs:documentation>Data associated with the SubAdministrative Area. e.g. Full name of sub administrative area or part of it. </xs:documentation>
+ </xs:annotation>
+ <xs:complexType>
+ <xs:simpleContent>
+ <xs:extension base="ct:String">
+ <xs:attributeGroup ref="ct:grAbbreviation"/>
+ <xs:attribute name="NameType" type="SubAdministrativeAreaNameTypeList">
+ <xs:annotation>
+ <xs:documentation>semantics of data associated with name</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="NameCode" type="SubAdministrativeAreaNameCodeList">
+ <xs:annotation>
+ <xs:documentation>Name of administrative area represented as a code. e.g. "COL" for COLORADO</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="NameCodeType" type="ct:String">
+ <xs:annotation>
+ <xs:documentation>Type of code used to represent name as a code</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:anyAttribute namespace="##other"/>
+ </xs:extension>
+ </xs:simpleContent>
+ </xs:complexType>
+ </xs:element>
+ </xs:sequence>
+ <xs:attribute name="Type" type="SubAdministrativeAreaTypeList">
+ <xs:annotation>
+ <xs:documentation>Type of sub administrative area</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attributeGroup ref="ct:grDataQuality"/>
+ <xs:anyAttribute namespace="##other" processContents="lax"/>
+ </xs:complexType>
+ </xs:element>
+ </xs:sequence>
+ <xs:attribute name="Type" type="AdministrativeAreaTypeList">
+ <xs:annotation>
+ <xs:documentation>Type of administrative area. e.g. state, city, town, etc</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attributeGroup ref="ct:grDataQuality"/>
+ <xs:anyAttribute namespace="##other" processContents="lax"/>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="Locality" minOccurs="0">
+ <xs:annotation>
+ <xs:documentation>Details of Locality which is a named densely populated area (a place) such as town, village, suburb, etc. A locality composes of many individual addresses. Many localities exist in an administrative area or a sub administrative area. A locality can also have sub localities. For example, a municipality locality can have many villages associated with it which are sub localities. Example: Tamil Nadu State, Erode District, Bhavani Taluk, Paruvachi Village is a valid address in India. Tamil Nadu is the Administrative Area, Erode is the sub admin area, Bhavani is the locality, and Paruvachi is the sub locality</xs:documentation>
+ </xs:annotation>
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="NameElement" maxOccurs="unbounded">
+ <xs:annotation>
+ <xs:documentation>Data associated with the locality. e.g. Full name of the locality or part of it, reference location to the locality</xs:documentation>
+ </xs:annotation>
+ <xs:complexType>
+ <xs:simpleContent>
+ <xs:extension base="ct:String">
+ <xs:attributeGroup ref="ct:grAbbreviation"/>
+ <xs:attribute name="NameType" type="LocalityNameTypeList">
+ <xs:annotation>
+ <xs:documentation>semantics of data associated with name</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="NameCode" type="LocalityNameCodeList">
+ <xs:annotation>
+ <xs:documentation>name of locality represented as a code</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="NameCodeType" type="ct:String">
+ <xs:annotation>
+ <xs:documentation>type of code used to represent name as a code</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:anyAttribute namespace="##other"/>
+ </xs:extension>
+ </xs:simpleContent>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="SubLocality" minOccurs="0">
+ <xs:annotation>
+ <xs:documentation>A locality that is smaller and is contained within the boundaries of its parent locality. Note that not all localities have sub locality. For example, many areas within a locality where each area is a sub locality</xs:documentation>
+ </xs:annotation>
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="NameElement" maxOccurs="unbounded">
+ <xs:annotation>
+ <xs:documentation>Data associated with the sub locality. e.g. Full name of the locality or part of it, reference location to the locality</xs:documentation>
+ </xs:annotation>
+ <xs:complexType>
+ <xs:simpleContent>
+ <xs:extension base="ct:String">
+ <xs:attributeGroup ref="ct:grAbbreviation"/>
+ <xs:attribute name="NameType" type="SubLocalityNameTypeList">
+ <xs:annotation>
+ <xs:documentation>semantics of data associated with name</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="NameCode" type="SubLocalityNameCodeList">
+ <xs:annotation>
+ <xs:documentation>name of locality represented as a code</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="NameCodeType" type="ct:String">
+ <xs:annotation>
+ <xs:documentation>type of code used to represent name as a code</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:anyAttribute namespace="##other"/>
+ </xs:extension>
+ </xs:simpleContent>
+ </xs:complexType>
+ </xs:element>
+ </xs:sequence>
+ <xs:attribute name="Type" type="SubLocalityTypeList">
+ <xs:annotation>
+ <xs:documentation>Type of sub locality</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attributeGroup ref="ct:grDataQuality"/>
+ <xs:anyAttribute namespace="##other" processContents="lax"/>
+ </xs:complexType>
+ </xs:element>
+ </xs:sequence>
+ <xs:attribute name="Type" type="LocalityTypeList">
+ <xs:annotation>
+ <xs:documentation>Type of locality. e.g. suburb, area, zone, village, etc</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attributeGroup ref="ct:grDataQuality"/>
+ <xs:anyAttribute namespace="##other" processContents="lax"/>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="Thoroughfare" minOccurs="0">
+ <xs:annotation>
+ <xs:documentation>Details of the Access route along which buildings/lot/land are located, such as street, road, channel, crescent, avenue, etc. This also includes canals/banks on which houses/boat houses are located where people live</xs:documentation>
+ </xs:annotation>
+ <xs:complexType>
+ <xs:complexContent>
+ <xs:extension base="ThoroughfareType">
+ <xs:sequence>
+ <xs:element name="SubThoroughfare" minOccurs="0" maxOccurs="5">
+ <xs:annotation>
+ <xs:documentation>Another thoroughfare that is required to uniquely identify the location, such as an access route, intersection, corner, adjacent, boundary, etc</xs:documentation>
+ </xs:annotation>
+ <xs:complexType>
+ <xs:complexContent>
+ <xs:extension base="ThoroughfareType"/>
+ </xs:complexContent>
+ </xs:complexType>
+ </xs:element>
+ </xs:sequence>
+ </xs:extension>
+ </xs:complexContent>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="Premises" minOccurs="0">
+ <xs:annotation>
+ <xs:documentation>Details of the Premises (could be building(s), site, location, property, premise, place) which is a landmark place which has a main address such as large mail user (e.g. Airport, Hospital, University) or could be a building (e.g. apartment, house) or a building or complex of buildings (e.g. an apartment complex or shopping centre) or even a vacant land (e.g. LOT). Premises can have many sub-addresses such as apartments in a building having its own addresses or buildings within an airport having its own addresses including its own thoroughfares</xs:documentation>
+ </xs:annotation>
+ <xs:complexType>
+ <xs:complexContent>
+ <xs:extension base="PremisesType">
+ <xs:sequence>
+ <xs:element name="SubPremises" minOccurs="0" maxOccurs="unbounded">
+ <xs:annotation>
+ <xs:documentation>Examples of sub-premises are apartments and suites in buildings, shops in malls, etc. or sub-addresses in a land mark place such as airports, military bases, hospitals, etc. Some countries have blocks within blocks</xs:documentation>
+ </xs:annotation>
+ <xs:complexType>
+ <xs:complexContent>
+ <xs:extension base="PremisesType">
+ <xs:attribute name="Type" type="SubPremisesTypeList"/>
+ <xs:attribute name="TypeCode" type="ct:String">
+ <xs:annotation>
+ <xs:documentation>Type of code used for sub premises type attribute</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ </xs:extension>
+ </xs:complexContent>
+ </xs:complexType>
+ </xs:element>
+ </xs:sequence>
+ <xs:attribute name="Type" type="PremisesTypeList"/>
+ <xs:attribute name="TypeCode" type="ct:String">
+ <xs:annotation>
+ <xs:documentation>Type of code use for Premises Type attribute</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ </xs:extension>
+ </xs:complexContent>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="PostCode" minOccurs="0">
+ <xs:annotation>
+ <xs:documentation>A container for a single free text or structured postcode. Note that not all countries have post codes</xs:documentation>
+ </xs:annotation>
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="Identifier" type="IdentifierType" maxOccurs="unbounded">
+ <xs:annotation>
+ <xs:documentation>The postcode is formatted according to country-specific rules. Example: SW3 0A8-1A, 600074, 2067. This element can also be used to define the semantics of what each code in the post code means</xs:documentation>
+ </xs:annotation>
+ </xs:element>
+ </xs:sequence>
+ <xs:attributeGroup ref="ct:grDataQuality"/>
+ <xs:anyAttribute namespace="##other" processContents="lax"/>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="RuralDelivery" minOccurs="0">
+ <xs:annotation>
+ <xs:documentation>A container for postal-specific delivery identifier for remote communities. Note that not all countries have RuralDelivery</xs:documentation>
+ </xs:annotation>
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="Identifier" type="IdentifierType" maxOccurs="unbounded">
+ <xs:annotation>
+ <xs:documentation>Free text or structured description of rural delivery route. e.g. RD 6, </xs:documentation>
+ </xs:annotation>
+ </xs:element>
+ </xs:sequence>
+ <xs:attribute name="Type" type="RuralDeliveryTypeList">
+ <xs:annotation>
+ <xs:documentation>Type of rural delivery. For some addresses, delivery to rural areas happens via water, air or road</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attributeGroup ref="ct:grDataQuality"/>
+ <xs:anyAttribute namespace="##other" processContents="lax"/>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="PostalDeliveryPoint" minOccurs="0">
+ <xs:annotation>
+ <xs:documentation>Final mail delivery point where the mail is dropped off for recipients to pick them up directly. E.g. POBox, Private Bag, pigeon hole, free mail numbers, etc.</xs:documentation>
+ </xs:annotation>
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="Identifier" type="IdentifierType" maxOccurs="unbounded">
+ <xs:annotation>
+ <xs:documentation>Free text or structured description of a postal delivery point.</xs:documentation>
+ </xs:annotation>
+ </xs:element>
+ </xs:sequence>
+ <xs:attribute name="Type" type="PostalDeliveryPointTypeList"/>
+ <xs:attributeGroup ref="ct:grDataQuality"/>
+ <xs:anyAttribute namespace="##other" processContents="lax"/>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="PostOffice" minOccurs="0">
+ <xs:annotation>
+ <xs:documentation>A delivery point/installation where all mails are delivered and the post man/delivery service picks up the mails and delivers it to the recipients through a delivery mode. Examples are a rural post office where post is delivered, a post office containing post office boxes/personal mail boxes. Note that not all countries have PostOffice. Can be used to represent overseas military addresses also along with PostalDeliveryPoint element</xs:documentation>
+ </xs:annotation>
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="Identifier" type="IdentifierType" maxOccurs="unbounded">
+ <xs:annotation>
+ <xs:documentation>Name or number of the post office in free text or structured form.</xs:documentation>
+ </xs:annotation>
+ </xs:element>
+ </xs:sequence>
+ <xs:attribute name="Type" type="PostOfficeTypeList">
+ <xs:annotation>
+ <xs:documentation>Indicates the type of postal delivery office from where the mail will be distributed to the final delivery point by a delivery mode. Example: Post Office, Mail Collection Centre, Letter Carrier Depot, Station, etc. </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attributeGroup ref="ct:grDataQuality"/>
+ <xs:anyAttribute namespace="##other" processContents="lax"/>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="GeoRSS" minOccurs="0">
+ <xs:annotation>
+ <xs:documentation>GeoRSS GML from Open Geospatial Consortium (OGC – www.opengeospatial.net) is a formal GML Application Profile, and supports a greater range of features than Simple, notably coordinate reference systems other than WGS84 latitude/longitude. It is designed for use with Atom 1.0, RSS 2.0 and RSS 1.0, although it can be used just as easily in non-RSS XML encodings. </xs:documentation>
+ </xs:annotation>
+ <xs:complexType>
+ <xs:sequence>
+ <xs:any namespace="http://www.georss.org/georss" maxOccurs="unbounded">
+ <xs:annotation>
+ <xs:documentation>Could be GeoRSS Simple or GeoRSS GML versions. Refer to http://georss.org/ and http://georss.org/gml for further documentation</xs:documentation>
+ </xs:annotation>
+ </xs:any>
+ </xs:sequence>
+ <xs:anyAttribute namespace="##other" processContents="lax"/>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="LocationByCoordinates" minOccurs="0">
+ <xs:annotation>
+ <xs:documentation>Simple Geo-coordinates of the address/location</xs:documentation>
+ </xs:annotation>
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="Latitude" minOccurs="0">
+ <xs:annotation>
+ <xs:documentation>Latitude details</xs:documentation>
+ </xs:annotation>
+ <xs:complexType>
+ <xs:attribute name="DegreesMeasure" type="ct:String">
+ <xs:annotation>
+ <xs:documentation>Measure of the latitude in degrees</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="MinutesMeasure" type="ct:String">
+ <xs:annotation>
+ <xs:documentation>Measure of the latitude in minutes</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="SecondsMeasure" type="ct:String">
+ <xs:annotation>
+ <xs:documentation>Measure of the latitude in seconds</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="Direction" type="DirectionTypeList">
+ <xs:annotation>
+ <xs:documentation>The direction of latitude measurement offset from the equator</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:anyAttribute namespace="##other" processContents="lax"/>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="Longitude" minOccurs="0">
+ <xs:annotation>
+ <xs:documentation>Longitude details</xs:documentation>
+ </xs:annotation>
+ <xs:complexType>
+ <xs:attribute name="DegreesMeasure" type="ct:String">
+ <xs:annotation>
+ <xs:documentation>Measure of the longitude in degrees</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="MinutesMeasure" type="ct:String">
+ <xs:annotation>
+ <xs:documentation>Measure of the longitude in minutes</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="SecondsMeasure" type="ct:String">
+ <xs:annotation>
+ <xs:documentation>Measure of the longitude in seconds</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="Direction" type="DirectionTypeList">
+ <xs:annotation>
+ <xs:documentation>The direction of longitude measurement offset from the equator</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:anyAttribute namespace="##other" processContents="lax"/>
+ </xs:complexType>
+ </xs:element>
+ </xs:sequence>
+ <xs:attribute name="Meridian" type="MeridianCodeList">
+ <xs:annotation>
+ <xs:documentation>The collection of the coordinate numeric values for latitude amd longitude depends on the agreed position of the meridian. Declaration of the meridian is necessary as it cannot be assumed in the data</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="MeridianCodeType" type="ct:String">
+ <xs:annotation>
+ <xs:documentation>Type of code used. e.g. EPSG Code</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="Datum" type="DatumCodeList">
+ <xs:annotation>
+ <xs:documentation>The collection of the coordinate numeric values depends on the agreed datum within which the measurement was taken. Declaration of the datum is necessary as it cannot be assumed in the data</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="DatumCodeType" type="ct:String">
+ <xs:annotation>
+ <xs:documentation>Type of code used. e.g. EPSG Code, WGS-84</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="Projection" type="ProjectionCodeList">
+ <xs:annotation>
+ <xs:documentation>Coordinates have limited utility and application depending on the projection required for visualisation in a map. Declaration of projection is necessary as it cannot be assumed in data</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="ProjectionCodeType" type="ct:String">
+ <xs:annotation>
+ <xs:documentation>Type of code used. e.g. EPSG Code</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attributeGroup ref="ct:grDataQuality"/>
+ <xs:anyAttribute namespace="##other" processContents="lax"/>
+ </xs:complexType>
+ </xs:element>
+ </xs:sequence>
+ <xs:attribute name="Type" type="AddressTypeList">
+ <xs:annotation>
+ <xs:documentation>Defines the type of address. An address type can be" Primary Address, Secondary Address, Rural Address, Military Address, etc.</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="AddressID" type="ct:String">
+ <xs:annotation>
+ <xs:documentation>A unique address identifier such as postal delivery identifier assigned to the address by local postal authority, e.g. DPID in Australia.</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="AddressIDType" type="AddressIDTypeList">
+ <xs:annotation>
+ <xs:documentation>Type of address ID used. e.g. DPID, etc</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="ID" type="ct:String">
+ <xs:annotation>
+ <xs:documentation>A globally unique identifier assigned to the address</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="Usage" type="AddressUsageList">
+ <xs:annotation>
+ <xs:documentation>The purpose the address is used for. E.g. Postal, residential, business, exchange, update, create, delete, etc</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="DeliveryMode" type="DeliveryModeList">
+ <xs:annotation>
+ <xs:documentation>Mode of delivery of address. For example: rural route, normal delivery, post office box, etc. </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="Status" type="ct:StatusList">
+ <xs:annotation>
+ <xs:documentation>Status of the entity. e.g. Old, Current, Inactive, Active, etc</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attributeGroup ref="ct:grValidityDate"/>
+ <xs:attribute name="AddressKey" type="ct:String">
+ <xs:annotation>
+ <xs:documentation>A primary key to reference Address.</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="AddressKeyRef" type="ct:String">
+ <xs:annotation>
+ <xs:documentation>A foreign key to reference attribute Key of Address.</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute ref="xlink:type"/>
+ <xs:attribute ref="xlink:label"/>
+ <xs:attribute ref="xlink:href"/>
+ <xs:attributeGroup ref="ct:grDataQuality"/>
+ <xs:attributeGroup ref="ct:grLanguageCode"/>
+ <xs:anyAttribute namespace="##other" processContents="lax"/>
+ </xs:complexType>
+ <xs:complexType name="CountryType">
+ <xs:annotation>
+ <xs:documentation>Complex type that defines the name of the country and is reused in other CIQ specs</xs:documentation>
+ </xs:annotation>
+ <xs:sequence>
+ <xs:element name="NameElement" maxOccurs="unbounded">
+ <xs:annotation>
+ <xs:documentation>Data associated with the name of the country in whatever form available, e.g. full, abbreviation, common use, code of the country, etc.</xs:documentation>
+ </xs:annotation>
+ <xs:complexType>
+ <xs:simpleContent>
+ <xs:extension base="ct:String">
+ <xs:attributeGroup ref="ct:grAbbreviation"/>
+ <xs:attribute name="NameType" type="CountryNameTypeList">
+ <xs:annotation>
+ <xs:documentation>Semantics of data associated with name. </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="NameCode" type="CountryNameCodeList">
+ <xs:annotation>
+ <xs:documentation>Name of the country represented as a code</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="NameCodeType" type="ct:String">
+ <xs:annotation>
+ <xs:documentation>Type of code used to represent name of country, e.g. iso-3166</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:anyAttribute namespace="##other"/>
+ </xs:extension>
+ </xs:simpleContent>
+ </xs:complexType>
+ </xs:element>
+ </xs:sequence>
+ <xs:anyAttribute namespace="##other" processContents="lax"/>
+ </xs:complexType>
+ <xs:complexType name="IdentifierType">
+ <xs:annotation>
+ <xs:documentation>Complex type for internal reuse</xs:documentation>
+ </xs:annotation>
+ <xs:simpleContent>
+ <xs:extension base="ct:String">
+ <xs:attribute name="Type" type="IdentifierElementTypeList">
+ <xs:annotation>
+ <xs:documentation>Indicates which part of number or identifier this element contains. Some "numbers" are as simple as 42 and some "numbers" are more like complex alphanumeric identifiers as Postcodes in UK or Canada, e.g. M2H 2S5. It may be necessary to separate the "number" into sub-elements and indicate what type of information each of them contains.</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attributeGroup ref="ct:grAbbreviation"/>
+ <xs:anyAttribute namespace="##other" processContents="lax"/>
+ </xs:extension>
+ </xs:simpleContent>
+ </xs:complexType>
+ <xs:complexType name="PremisesType">
+ <xs:annotation>
+ <xs:documentation>Complex type for internal reuse</xs:documentation>
+ </xs:annotation>
+ <xs:choice maxOccurs="unbounded">
+ <xs:element name="NameElement">
+ <xs:annotation>
+ <xs:documentation>Data associated with the name of the Premises. e.g. Full name of premises or part of the name. E.g. Westfield shopping center, reference data to support the premises location, street in the premises</xs:documentation>
+ </xs:annotation>
+ <xs:complexType>
+ <xs:simpleContent>
+ <xs:extension base="ct:String">
+ <xs:attributeGroup ref="ct:grAbbreviation"/>
+ <xs:attribute name="NameType" type="PremisesElementTypeList">
+ <xs:annotation>
+ <xs:documentation>Describes the type / part of name this element contains.</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:anyAttribute namespace="##other"/>
+ </xs:extension>
+ </xs:simpleContent>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="Number" type="IdentifierType">
+ <xs:annotation>
+ <xs:documentation>Data associated with the number of the premises. E.g.House 15, number range, number suffix</xs:documentation>
+ </xs:annotation>
+ </xs:element>
+ </xs:choice>
+ <xs:anyAttribute namespace="##other" processContents="lax"/>
+ </xs:complexType>
+ <xs:complexType name="ThoroughfareType">
+ <xs:annotation>
+ <xs:documentation>Complex type for internal reuse</xs:documentation>
+ </xs:annotation>
+ <xs:choice maxOccurs="unbounded">
+ <xs:element name="NameElement">
+ <xs:annotation>
+ <xs:documentation>Data associated with the thoroughfare details. e.g. Full thoroughfare name or part of it, type of thoroughfare, old name, new name, reference data in support of the thoroughfare</xs:documentation>
+ </xs:annotation>
+ <xs:complexType>
+ <xs:simpleContent>
+ <xs:extension base="ct:String">
+ <xs:attributeGroup ref="ct:grAbbreviation"/>
+ <xs:attribute name="NameType" type="ThoroughfareNameTypeList">
+ <xs:annotation>
+ <xs:documentation>Describes the type / part of name this element contains.</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:anyAttribute namespace="##other"/>
+ </xs:extension>
+ </xs:simpleContent>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="Number" type="IdentifierType">
+ <xs:annotation>
+ <xs:documentation>Data associated with the number of the thoroughfare. E.g. 39 in 39 Baker Street, street range, street suffix</xs:documentation>
+ </xs:annotation>
+ </xs:element>
+ </xs:choice>
+ <xs:attribute name="Type" type="ThoroughfareTypeList">
+ <xs:annotation>
+ <xs:documentation>Type of thoroughfare. eg. primary road, secondary road, road branch (e.g. Lane 14), road sub branch (e.g. Alley 21), adjourning street, cross street, closest street, etc</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="TypeCode" type="ct:String">
+ <xs:annotation>
+ <xs:documentation>Type of code use for thoroughfare</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attributeGroup ref="ct:grDataQuality"/>
+ <xs:anyAttribute namespace="##other" processContents="lax"/>
+ </xs:complexType>
+</xs:schema>
diff --git a/sfx2/classification/xNL-types.xsd b/sfx2/classification/xNL-types.xsd
new file mode 100644
index 000000000..dc8769ae3
--- /dev/null
+++ b/sfx2/classification/xNL-types.xsd
@@ -0,0 +1,221 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xs:schema xmlns="urn:oasis:names:tc:ciq:xnl:3" xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="urn:oasis:names:tc:ciq:xnl:3" elementFormDefault="qualified" attributeFormDefault="qualified">
+ <xs:annotation>
+ <xs:documentation>
+ Specification Name: OASIS CIQ TC - extensible Name Language Types (xNL-types)
+ Description: Defines the W3C schema that provides enumeration lists to support xNL v3.0
+ (Using XML Schema based standard code list/enumeration mechanism - OPTION 1 AND DEFAULT)
+ Produced by: OASIS Customer Information Quality Technical Committee
+ URL: http://www.oasis-open.org/committees/ciq
+ Version: 3.0
+ Status: Committee Specification
+ Copyright: 2006-07, OASIS, http://www.oasis-open.org
+ Last Modified: 18 September 2007
+ Last Modified by: Ram Kumar, Chair, OASIS CIQ TC
+
+ NOTE: This is the schema that users can customise the enumeration lists to meet their
+ exchange requirements. The enumeration values provided are ONLY SAMPLES and
+ is not complete. It is up to the application to decide what the values should be. To achieve
+ interoperability between applications using this specification, it is recommended that an
+ SLA/agreement is in place as to what the enumeration values will be used in this file
+ </xs:documentation>
+ </xs:annotation>
+ <xs:simpleType name="JointNameConnectorList">
+ <xs:annotation>
+ <xs:documentation>A list of possible values for joint name connector</xs:documentation>
+ </xs:annotation>
+ <xs:restriction base="xs:normalizedString"/>
+ </xs:simpleType>
+ <xs:simpleType name="NameLineTypeList">
+ <xs:annotation>
+ <xs:documentation>A list of possible values for types of name lines</xs:documentation>
+ </xs:annotation>
+ <xs:restriction base="xs:normalizedString"/>
+ </xs:simpleType>
+ <xs:simpleType name="PartyNameIDTypeList">
+ <xs:annotation>
+ <xs:documentation>A list of all types of Party Name IDs</xs:documentation>
+ </xs:annotation>
+ <xs:restriction base="xs:normalizedString"/>
+ </xs:simpleType>
+ <xs:simpleType name="PartyNameUsageList">
+ <xs:annotation>
+ <xs:documentation>A list of usage types of party name</xs:documentation>
+ </xs:annotation>
+ <xs:restriction base="xs:string"/>
+ </xs:simpleType>
+ <xs:simpleType name="PersonNameElementList">
+ <xs:annotation>
+ <xs:documentation>A list of person name element types, e.g. First Name, Last Name, Title, etc.</xs:documentation>
+ </xs:annotation>
+ <xs:restriction base="xs:normalizedString">
+ <xs:enumeration value="PrecedingTitle">
+ <xs:annotation>
+ <xs:documentation>His Excellency, Honorable, etc.</xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ <xs:enumeration value="Title">
+ <xs:annotation>
+ <xs:documentation>A title signifies some sort of status, such as Mr, Miss, Ms (marriage status), or education such as Professor, PhD, Dr, etc.</xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ <xs:enumeration value="FirstName">
+ <xs:annotation>
+ <xs:documentation>The most important name element by which this particular individual is identified in the group. E.g. John, Sam, Brian for Anglo-Saxon cultures.</xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ <xs:enumeration value="MiddleName">
+ <xs:annotation>
+ <xs:documentation>Name elements related to additional identification of the individual, such as names are parents or places.</xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ <xs:enumeration value="LastName">
+ <xs:annotation>
+ <xs:documentation>Name element that identifies the group the individual belongs to and is identified by, such as Last Name, Surname, Family Name, etc. </xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ <xs:enumeration value="OtherName">
+ <xs:annotation>
+ <xs:documentation>Any other additional names that are not directly used to identify or call the individual, such as names of ancestors, saints, etc.</xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ <xs:enumeration value="Alias">
+ <xs:annotation>
+ <xs:documentation>A simple nick name that is commonly used as part of the name. E.g. a fancy kick-boxer can be commonly known as Bill "Storm" Bababoons, where "Storm" is obviously an alias.</xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ <xs:enumeration value="GenerationIdentifier">
+ <xs:annotation>
+ <xs:documentation>Junior, Senior, The Second, IV, etc.</xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ <xs:enumeration value="Degree"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType name="PersonNameUsageList">
+ <xs:annotation>
+ <xs:documentation>A list of usage types of person name</xs:documentation>
+ </xs:annotation>
+ <xs:restriction base="xs:normalizedString"/>
+ </xs:simpleType>
+ <xs:simpleType name="PersonIDTypeList">
+ <xs:annotation>
+ <xs:documentation>A list of all types of person name IDs</xs:documentation>
+ </xs:annotation>
+ <xs:restriction base="xs:normalizedString"/>
+ </xs:simpleType>
+ <xs:simpleType name="OrganisationIDTypeList">
+ <xs:annotation>
+ <xs:documentation>A list of all types of organisation name IDs</xs:documentation>
+ </xs:annotation>
+ <xs:restriction base="xs:normalizedString"/>
+ </xs:simpleType>
+ <xs:simpleType name="OrganisationNameElementList">
+ <xs:annotation>
+ <xs:documentation>A list of organisation name element types, e.g. Name, property type, liability type, etc.</xs:documentation>
+ </xs:annotation>
+ <xs:restriction base="xs:normalizedString">
+ <xs:enumeration value="NameOnly">
+ <xs:annotation>
+ <xs:documentation>"Sakthisoft" in "Sakthisoft Pty. Ltd". "Pty.Ltd" is the legal entity for the organisation name "Sakthisoft"</xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ <xs:enumeration value="TypeOnly">
+ <xs:annotation>
+ <xs:documentation>"Pty. Ltd" in Sakthisoft Pty.Ltd, where "Sakthisoft" is the name of the organisation.
+
+""Inc" in ABC Inc, where "ABC" is organisation name</xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ <xs:enumeration value="FullName">
+ <xs:annotation>
+ <xs:documentation>Full Name of the organisation. e.g. Sakthisoft Pty. Ltd</xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType name="OrganisationNameUsageList">
+ <xs:annotation>
+ <xs:documentation>A list of usage types for organisation name</xs:documentation>
+ </xs:annotation>
+ <xs:restriction base="xs:normalizedString"/>
+ </xs:simpleType>
+ <xs:simpleType name="PersonNameTypeList">
+ <xs:annotation>
+ <xs:documentation>A list of common types for person names</xs:documentation>
+ </xs:annotation>
+ <xs:restriction base="xs:normalizedString">
+ <xs:enumeration value="Alias"/>
+ <xs:enumeration value="LegalName"/>
+ <xs:enumeration value="KnownAs"/>
+ <xs:enumeration value="MaidenName">
+ <xs:annotation>
+ <xs:documentation>Name of an individual before marriage.</xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ <xs:enumeration value="FormerName">
+ <xs:annotation>
+ <xs:documentation>Former name of the person</xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ <xs:enumeration value="CommonUse">
+ <xs:annotation>
+ <xs:documentation>Name that is commonly used by others, e.g. a simplified form of the official name.</xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ <xs:enumeration value="NameAtBirth">
+ <xs:annotation>
+ <xs:documentation>A name given to an individual at birth, but later changed (common in some cultures)</xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ <xs:enumeration value="PreferredName">
+ <xs:annotation>
+ <xs:documentation>Indicates that the party prefers to be called by this name</xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ <xs:enumeration value="OfficialName">
+ <xs:annotation>
+ <xs:documentation>An official name of the person, e.g. as in the passport. incorporation certificate, etc.</xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ <xs:enumeration value="UnofficialName"/>
+ <xs:enumeration value="NickName"/>
+ <xs:enumeration value="PetName"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType name="OrganisationNameTypeList">
+ <xs:annotation>
+ <xs:documentation>A list of common types for organisation names</xs:documentation>
+ </xs:annotation>
+ <xs:restriction base="xs:normalizedString">
+ <xs:enumeration value="LegalName"/>
+ <xs:enumeration value="FormerName">
+ <xs:annotation>
+ <xs:documentation>Former name of the organisation</xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ <xs:enumeration value="CommonUse"/>
+ <xs:enumeration value="PublishingName"/>
+ <xs:enumeration value="OfficialName"/>
+ <xs:enumeration value="UnofficialName"/>
+ <xs:enumeration value="Undefined">
+ <xs:annotation>
+ <xs:documentation>unknown</xs:documentation>
+ </xs:annotation>
+ </xs:enumeration>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType name="SubDivisionTypeList">
+ <xs:annotation>
+ <xs:documentation>A list of common types for subdivisions</xs:documentation>
+ </xs:annotation>
+ <xs:restriction base="xs:normalizedString">
+ <xs:enumeration value="Department"/>
+ <xs:enumeration value="Division"/>
+ <xs:enumeration value="Branch"/>
+ <xs:enumeration value="BusinessUnit"/>
+ <xs:enumeration value="School"/>
+ <xs:enumeration value="Section"/>
+ </xs:restriction>
+ </xs:simpleType>
+</xs:schema>
diff --git a/sfx2/classification/xNL.xsd b/sfx2/classification/xNL.xsd
new file mode 100644
index 000000000..d82c6ab7b
--- /dev/null
+++ b/sfx2/classification/xNL.xsd
@@ -0,0 +1,283 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xs:schema xmlns="urn:oasis:names:tc:ciq:xnl:3" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ct="urn:oasis:names:tc:ciq:ct:3" targetNamespace="urn:oasis:names:tc:ciq:xnl:3" elementFormDefault="qualified" attributeFormDefault="qualified">
+ <xs:annotation>
+ <xs:documentation>
+ Specification Name: OASIS CIQ TC - extensible Name Language (xNL)
+ Description: Defines the W3C schema for representing party names (Person or Organisation)
+ (Using XML Schema based standard code list/enumeration mechanism - OPTION 1 AND DEFAULT)
+ Produced by: OASIS Customer Information Quality Technical Committee
+ URL: http://www.oasis-open.org/committees/ciq
+ Version: 3.0
+ Status: Committee Specification
+ Copyright: 2006-07, OASIS, http://www.oasis-open.org
+ Last Modified: 18 September 2007
+ Last Modified by: Ram Kumar, Chair, OASIS CIQ TC
+
+ NOTE: Do not modify this schema as it will break specifications compatibility
+ </xs:documentation>
+ </xs:annotation>
+ <xs:include schemaLocation="xNL-types.xsd"/>
+ <xs:import namespace="urn:oasis:names:tc:ciq:ct:3" schemaLocation="CommonTypes.xsd"/>
+ <xs:import namespace="http://www.w3.org/1999/xlink" schemaLocation="xlink-2003-12-31.xsd"/>
+ <xs:attributeGroup name="grNameKey">
+ <xs:annotation>
+ <xs:documentation>Reference to another Person Name or Organisation Name with primary and foreign key reinforcement. </xs:documentation>
+ </xs:annotation>
+ <xs:attribute name="NameKey" type="ct:String">
+ <xs:annotation>
+ <xs:documentation>A primary key to reference Party Name.</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="NameKeyRef" type="ct:String">
+ <xs:annotation>
+ <xs:documentation>A foreign key to reference attribute Key of Party Name.</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ </xs:attributeGroup>
+ <xs:complexType name="PartyNameType">
+ <xs:annotation>
+ <xs:documentation>Reusable complex type for a party. A party is a person or an organisation</xs:documentation>
+ </xs:annotation>
+ <xs:sequence>
+ <xs:element ref="NameLine" minOccurs="0" maxOccurs="unbounded"/>
+ <xs:element name="PersonName" minOccurs="0" maxOccurs="unbounded">
+ <xs:annotation>
+ <xs:documentation>Container for person name details. Same person with many types (e.g. alias, pet name, nick name) of names can be used by this container. </xs:documentation>
+ </xs:annotation>
+ <xs:complexType>
+ <xs:complexContent>
+ <xs:extension base="PersonNameType"/>
+ </xs:complexContent>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="OrganisationName" minOccurs="0" maxOccurs="unbounded">
+ <xs:annotation>
+ <xs:documentation>A container for organisation name details. Same organisation with many types of names can be used by this container</xs:documentation>
+ </xs:annotation>
+ <xs:complexType>
+ <xs:complexContent>
+ <xs:extension base="OrganisationNameType"/>
+ </xs:complexContent>
+ </xs:complexType>
+ </xs:element>
+ </xs:sequence>
+ <xs:attribute name="PartyNameID" type="ct:String">
+ <xs:annotation>
+ <xs:documentation>A unique identifier of a party</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="PartyNameIDType" type="PartyNameIDTypeList">
+ <xs:annotation>
+ <xs:documentation>Type of Party Name ID</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="ID" type="ct:String">
+ <xs:annotation>
+ <xs:documentation>Globally unique identifier</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="Usage" type="PartyNameUsageList">
+ <xs:annotation>
+ <xs:documentation>Type of use of this data. e.g. data exchange, contact, update, create</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="Status" type="ct:StatusList">
+ <xs:annotation>
+ <xs:documentation>Status of the entity. e.g. Old, Current, Inactive, Active, etc</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="JointNameConnector" type="JointNameConnectorList">
+ <xs:annotation>
+ <xs:documentation>The connector used to join more than one person name. Example: Mr Hunt AND Mrs Clark, where AND is the JointNameConnector. The flow is from the preceding to the following. If there is more than 2 names then all names are connected using this connector in the natural order.</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attributeGroup ref="ct:grValidityDate"/>
+ <xs:attributeGroup ref="grNameKey"/>
+ <xs:attribute ref="xlink:type"/>
+ <xs:attribute ref="xlink:label"/>
+ <xs:attribute ref="xlink:href"/>
+ <xs:attributeGroup ref="ct:grDataQuality"/>
+ <xs:attributeGroup ref="ct:grLanguageCode"/>
+ <xs:anyAttribute namespace="##other" processContents="lax"/>
+ </xs:complexType>
+ <xs:complexType name="PersonNameType">
+ <xs:annotation>
+ <xs:documentation>Reusable complex type</xs:documentation>
+ </xs:annotation>
+ <xs:sequence>
+ <xs:element name="NameElement" minOccurs="0" maxOccurs="unbounded">
+ <xs:annotation>
+ <xs:documentation>Name or part of a name. </xs:documentation>
+ </xs:annotation>
+ <xs:complexType>
+ <xs:simpleContent>
+ <xs:extension base="ct:String">
+ <xs:attribute name="ElementType" type="PersonNameElementList">
+ <xs:annotation>
+ <xs:documentation>Clarifies the meaning of the element.Could be first name, middle name, etc. that is defined in the List list. Omit this attribute if the type of the name element is not known.</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attributeGroup ref="ct:grAbbreviation"/>
+ <xs:anyAttribute namespace="##other" processContents="lax"/>
+ </xs:extension>
+ </xs:simpleContent>
+ </xs:complexType>
+ </xs:element>
+ </xs:sequence>
+ <xs:attribute name="Type" type="PersonNameTypeList">
+ <xs:annotation>
+ <xs:documentation>Enumerated list of type of name. example: Alias, Nick Name, former name, known as, etc</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="PersonID" type="ct:String">
+ <xs:annotation>
+ <xs:documentation>A unique identifier of a person</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="PersonIDType" type="PersonIDTypeList">
+ <xs:annotation>
+ <xs:documentation>Type of identifier</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="ID" type="ct:String">
+ <xs:annotation>
+ <xs:documentation>Globally unique identifier</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="Usage" type="PersonNameUsageList">
+ <xs:annotation>
+ <xs:documentation>Usage of a person name. How is it used and for what purpose. Allows user which name in a set of names to select for a given purpose.
+e.g. used for legal purposes</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="Status" type="ct:StatusList">
+ <xs:annotation>
+ <xs:documentation>Status of the entity. e.g. Old, Current, Inactive, Active, etc</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attributeGroup ref="ct:grValidityDate"/>
+ <xs:attributeGroup ref="grNameKey"/>
+ <xs:attribute ref="xlink:type"/>
+ <xs:attribute ref="xlink:label"/>
+ <xs:attribute ref="xlink:href"/>
+ <xs:attributeGroup ref="ct:grDataQuality"/>
+ <xs:attributeGroup ref="ct:grLanguageCode"/>
+ <xs:anyAttribute namespace="##other" processContents="lax"/>
+ </xs:complexType>
+ <xs:complexType name="OrganisationNameType">
+ <xs:annotation>
+ <xs:documentation>Reusable complex type</xs:documentation>
+ </xs:annotation>
+ <xs:sequence>
+ <xs:element name="NameElement" minOccurs="0" maxOccurs="unbounded">
+ <xs:annotation>
+ <xs:documentation>Name of the organisation. E.g. ACME Inc.</xs:documentation>
+ </xs:annotation>
+ <xs:complexType>
+ <xs:simpleContent>
+ <xs:extension base="ct:String">
+ <xs:attribute name="ElementType" type="OrganisationNameElementList">
+ <xs:annotation>
+ <xs:documentation>Clarifies the meaning of the element. Example: name, type . Omit this attribute if the type of the name element is not known.</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attributeGroup ref="ct:grAbbreviation"/>
+ <xs:anyAttribute namespace="##other" processContents="lax"/>
+ </xs:extension>
+ </xs:simpleContent>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="SubDivisionName" minOccurs="0" maxOccurs="unbounded">
+ <xs:annotation>
+ <xs:documentation>Name of a subdivision of an organisation (e.g. department) </xs:documentation>
+ </xs:annotation>
+ <xs:complexType>
+ <xs:simpleContent>
+ <xs:extension base="ct:String">
+ <xs:attribute name="Type" type="SubDivisionTypeList">
+ <xs:annotation>
+ <xs:documentation>Type of sub division. e.g. department, warehouse, branch</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attributeGroup ref="ct:grAbbreviation"/>
+ <xs:anyAttribute namespace="##other" processContents="lax"/>
+ </xs:extension>
+ </xs:simpleContent>
+ </xs:complexType>
+ </xs:element>
+ </xs:sequence>
+ <xs:attribute name="Type" type="OrganisationNameTypeList">
+ <xs:annotation>
+ <xs:documentation>Enumerated list of common types of aliases or name types.</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="OrganisationID" type="ct:String">
+ <xs:annotation>
+ <xs:documentation>A unique identifier of an organisation</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="OrganisationIDType" type="OrganisationIDTypeList">
+ <xs:annotation>
+ <xs:documentation>Type of identifier</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="ID" type="ct:String">
+ <xs:annotation>
+ <xs:documentation>Globally unique identifier</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="Usage" type="OrganisationNameUsageList">
+ <xs:annotation>
+ <xs:documentation>Usage of organisation name. How is it used and for what purpose. Allows user which name in a set of names to select for a given purpose.
+e.g. used for legal purposes</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="Status" type="ct:StatusList">
+ <xs:annotation>
+ <xs:documentation>Status of the entity. e.g. Old, Current, Inactive, Active, etc</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attributeGroup ref="ct:grValidityDate"/>
+ <xs:attributeGroup ref="grNameKey"/>
+ <xs:attribute ref="xlink:type"/>
+ <xs:attribute ref="xlink:label"/>
+ <xs:attribute ref="xlink:href"/>
+ <xs:attributeGroup ref="ct:grDataQuality"/>
+ <xs:attributeGroup ref="ct:grLanguageCode"/>
+ <xs:anyAttribute namespace="##other" processContents="lax"/>
+ </xs:complexType>
+ <xs:element name="NameLine">
+ <xs:annotation>
+ <xs:documentation>Define name as a free format text. Use this when the type of the entity (person or organisation) is unknown, or is not broken down into individual elements (e.g. unstructured, unparsed) or is beyond the provided types. The name represented may be formatted in the right order or may not be as it is not parsed/broken into atomic fields</xs:documentation>
+ </xs:annotation>
+ <xs:complexType>
+ <xs:simpleContent>
+ <xs:extension base="ct:String">
+ <xs:attribute name="Type" type="NameLineTypeList">
+ <xs:annotation>
+ <xs:documentation>Type define what this free format name line could mean. For example, the Type could be "Unknown" </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attributeGroup ref="ct:grAbbreviation"/>
+ <xs:attributeGroup ref="ct:grDataQuality"/>
+ <xs:anyAttribute namespace="##other" processContents="lax"/>
+ </xs:extension>
+ </xs:simpleContent>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="PartyName" type="PartyNameType">
+ <xs:annotation>
+ <xs:documentation>Container for defining a name of a Person, an Organisation or combination of the above as a joint name.</xs:documentation>
+ </xs:annotation>
+ </xs:element>
+ <xs:element name="PersonName" type="PersonNameType">
+ <xs:annotation>
+ <xs:documentation>Person Name</xs:documentation>
+ </xs:annotation>
+ </xs:element>
+ <xs:element name="OrganisationName" type="OrganisationNameType">
+ <xs:annotation>
+ <xs:documentation>Organisation Name</xs:documentation>
+ </xs:annotation>
+ </xs:element>
+</xs:schema>
diff --git a/sfx2/classification/xlink-2003-12-31.xsd b/sfx2/classification/xlink-2003-12-31.xsd
new file mode 100644
index 000000000..bfe7a792e
--- /dev/null
+++ b/sfx2/classification/xlink-2003-12-31.xsd
@@ -0,0 +1,89 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- (c) XBRL International. See www.xbrl.org/legal
+ XLink attribute specification - produced by xBRL group in December 2006
+ Thanks to xBRL for giving OASIS CIQ TC permission to use this specification
+-->
+<schema xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.w3.org/1999/xlink" elementFormDefault="qualified" attributeFormDefault="qualified">
+ <annotation>
+ <documentation>
+ XLink attribute specification
+ </documentation>
+ </annotation>
+ <attribute name="type">
+ <simpleType>
+ <annotation>
+ <documentation>
+ Enumeration of values for the type attribute
+ </documentation>
+ </annotation>
+ <restriction base="string">
+ <enumeration value="simple"/>
+ <enumeration value="extended"/>
+ <enumeration value="locator"/>
+ <enumeration value="arc"/>
+ <enumeration value="resource"/>
+ <enumeration value="title"/>
+ </restriction>
+ </simpleType>
+ </attribute>
+ <attribute name="role">
+ <simpleType>
+ <annotation>
+ <documentation>
+ A URI with a minimum length of 1 character.
+ </documentation>
+ </annotation>
+ <restriction base="anyURI">
+ <minLength value="1"/>
+ </restriction>
+ </simpleType>
+ </attribute>
+ <attribute name="arcrole">
+ <simpleType>
+ <annotation>
+ <documentation>
+ A URI with a minimum length of 1 character.
+ </documentation>
+ </annotation>
+ <restriction base="anyURI">
+ <minLength value="1"/>
+ </restriction>
+ </simpleType>
+ </attribute>
+ <attribute name="title" type="string"/>
+ <attribute name="show">
+ <simpleType>
+ <annotation>
+ <documentation>
+ Enumeration of values for the show attribute
+ </documentation>
+ </annotation>
+ <restriction base="string">
+ <enumeration value="new"/>
+ <enumeration value="replace"/>
+ <enumeration value="embed"/>
+ <enumeration value="other"/>
+ <enumeration value="none"/>
+ </restriction>
+ </simpleType>
+ </attribute>
+ <attribute name="actuate">
+ <simpleType>
+ <annotation>
+ <documentation>
+ Enumeration of values for the actuate attribute
+ </documentation>
+ </annotation>
+ <restriction base="string">
+ <enumeration value="onLoad"/>
+ <enumeration value="onRequest"/>
+ <enumeration value="other"/>
+ <enumeration value="none"/>
+ </restriction>
+ </simpleType>
+ </attribute>
+ <attribute name="label" type="NCName"/>
+ <attribute name="from" type="NCName"/>
+ <attribute name="to" type="NCName"/>
+ <attribute name="href" type="anyURI"/>
+</schema>
diff --git a/sfx2/doc/sfx2doc.html b/sfx2/doc/sfx2doc.html
new file mode 100644
index 000000000..2d7964360
--- /dev/null
+++ b/sfx2/doc/sfx2doc.html
@@ -0,0 +1,77 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<HTML>
+<BODY LANG="de-DE" DIR="LTR">
+<P CLASS="western">Signal processing in the sfx2 Interface.
+State of 5.5.2013<BR><BR>
+related modules are: svidl (idl compiler), framework (command processing routines), rsc (resource compiler, see below), vcl (command processing routines)<BR>
+Here is a chart how the "slot-model" of the command processing framework works.
+<BR>
+Menu commands and Key accelerators are stored
+asynchronously into a UNO command stack and later executed
+sequentially.<BR>
+</P>
+<P CLASS="western" STYLE="margin-bottom: 0.5cm"><IMG SRC="sfx2doc0001.svg" NAME="Grafik1" ALIGN=BOTTOM WIDTH=950 HEIGHT=350 BORDER=0></P>
+<P CLASS="western" STYLE="margin-bottom: 0.5cm"><BR><BR>
+
+Changing the system: <b>In Order to add a function to a shell,
+Add an entry to the .sdi file corresponding to that SfxShell</b><BR>
+<FONT style="color: red">Example:</FONT>
+<BR>
+<PRE><FONT style="background-color: black" color="white">SID_PASTE [ ExecMethod = ExecDrawFunc; StateMethod = GetDrawFuncState; Export = FALSE; ]</FONT></PRE><BR>
+The "state" method is the method called to checked whether that command is active now. The "exec" method is called when the command should be processed by the shell. Both functions obtain a SfxRequest Object as a parameter, declared in ./include/sfx2/request.hxx.<BR>
+Each Application Window has ONE Dispatcher.
+This registers all SfxShells (or rather their
+subclasses) which can get commands from the user AT THE MOMENT.
+These shells are created when the edit mode they
+are part of is first used, but not discarded until the window closes.
+./sfx2/source/control/dispatch.cxx:
+void SfxDispatcher::Pop receives Pushes and Pops
+of the shell to/from the stack.
+The real Pushing/Popping, however, is done during
+SfxDispatcher::FlushImpl
+Main dispatching routine In
+./sfx2/source/control/unoctitm.cxx
+SfxDispatchController_Impl::dispatch.
+<BR>
+SfxDispatchController::_FindServer
+gets the SfxInterfaces of the shells. They are defined by macro calls
+to SFX_IMPL_INTERFACE, defined in include/sfx2/shell.hxx .
+
+Upon GetInterface, they yield their pInterface,
+which, upon GetSlot, returns a SfxSlot of that Shell
+For that command, which is then Executed with
+Dispatcher::Execute.
+If the program is set into another mode, for
+example by clicking a hovering object in writer
+after Text editing, all shells are removed from
+the dispatcher and new shells are pushed
+to match the current mode.
+<BR>
+<B> How to build menus</B><BR>
+In order to introduce a menu command, the uno command must be linked to a slot name.<BR>
+This is done in different sdi files, such as sfx2/sdi/sfx.sdi.
+There, you <U>define</U> the slot:<BR>
+<FONT style="color: red">Structure:</FONT>
+
+<PRE>
+<FONT style="background-color: black" color="white">
+SfxVoidItem uno_name_the_part_after_colon SID_COMMAND_NAME
+()
+[
+ /* initialization of variables is coming here */
+]
+</PRE>
+</FONT>
+In ./include/sfx2/sfxsids.hrc, the defines of the slot names and their numbers are entered. The numbers are just needed because definitions cannot be enums, and for compatibility.<BR>
+
+In ./officecfg/registry/data/org/openoffice/Office/UI,
+there are .xcu ended xml files that define the single menu items.
+They contain the US- language name and the .uno: command.<BR>
+
+
+<BR><BR>
+The resource compiler, which creates files some GetState methods refer to, is in the ./rsc directory. It is a separate executable, invoked at compile time.<BR>
+
+</P>
+</BODY>
+</HTML>
diff --git a/sfx2/doc/sfx2doc0001.svg b/sfx2/doc/sfx2doc0001.svg
new file mode 100644
index 000000000..070f7568f
--- /dev/null
+++ b/sfx2/doc/sfx2doc0001.svg
@@ -0,0 +1,99 @@
+<!DOCTYPE svg>
+<svg xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 950 350">
+ <defs>
+ <g id="box00" transform="translate(50,75)">
+ <rect height="50" width="100" style="fill:none;stroke-width:1;stroke:rgb(0,0,0)"/>
+ <text x="10" y="20" font-size="16">SfxView</text>
+ <text x="10" y="40" font-size="16">Subclass</text>
+ </g>
+ <g id="box01" transform="translate(50,75)">
+ <rect height="50" width="100" style="fill:none;stroke-width:1;stroke:rgb(0,0,0)"/>
+ <text x="70" y="40" font-size="7">Missing</text>
+ </g>
+ <g id="box02" transform="translate(50,75)">
+ <rect height="50" width="100" style="fill:none;stroke-width:1;stroke:rgb(0,0,0)"/>
+ <text x="10" y="10" font-size="10">Ability to process</text>
+ <text x="60" y="40" font-size="7">SfxSlot</text>
+ </g>
+ </defs>
+ <g>
+ <use xlink:href="#box00" transform="translate(150,0)"/>
+ <g transform="translate(250,0)">
+ <use xlink:href="#box02" />
+ <text x="60" y="100" font-size="10">"SID_PASTE"</text>
+ </g>
+ <g transform="translate(350,0)">
+ <use xlink:href="#box02" />
+ <text x="60" y="100" font-size="10">"SID_CUT"</text>
+ </g>
+ <use xlink:href="#box01" transform="translate(450,0)"/>
+ <use xlink:href="#box01" transform="translate(550,0)"/>
+ <use xlink:href="#box01" transform="translate(650,0)"/>
+ <use xlink:href="#box00" transform="translate(150,50)"/>
+ <g transform="translate(250,50)">
+ <use xlink:href="#box02" />
+ <text x="60" y="100" font-size="10">"SID_PASTE"</text>
+ </g>
+ <use xlink:href="#box01" transform="translate(350,50)"/>
+ <use xlink:href="#box01" transform="translate(450,50)"/>
+ <use xlink:href="#box01" transform="translate(550,50)"/>
+ <g transform="translate(650,50)">
+ <use xlink:href="#box02" />
+ <text x="60" y="100" font-size="10">"SID_FLOAT"</text>
+ </g>
+ <use xlink:href="#box00" transform="translate(150,100)"/>
+ <g transform="translate(250,100)">
+ <use xlink:href="#box02" />
+ <text x="60" y="100" font-size="10">"SID_PASTE"</text>
+ </g>
+ <g transform="translate(350,100)">
+ <use xlink:href="#box02" />
+ <text x="60" y="100" font-size="10">"SID_CUT"</text>
+ </g>
+ <use xlink:href="#box01" transform="translate(450,100)"/>
+ <g transform="translate(550,100)">
+ <use xlink:href="#box02" />
+ <text x="60" y="100" font-size="10">"SID_VOMIT"</text>
+ </g>
+ <g transform="translate(650,100)">
+ <use xlink:href="#box02" />
+ <text x="60" y="100" font-size="10">"SID_FLOAT"</text>
+ </g>
+ <g transform="translate(350,0)">
+ <polygon fill="red" points="-20 80 -10 80 -10 0 10 0 10 80 20 80 0 100"/>
+ <text fill="white" stroke="white" stroke-width="0.1" transform="translate(-6,0) rotate(90, 0, 0) "> SID_PASTE </text>
+ </g>
+ <g transform="translate(450,0)">
+ <polygon fill="cyan" points="-20 80 -10 80 -10 0 10 0 10 80 20 80 0 100"/>
+ <text fill="white" stroke="white" stroke-width="0.1" transform="translate(-6,0) rotate(90, 0, 0) "> SID_CUT </text>
+ </g>
+ <g transform="translate(550,0)">
+ <polygon fill="lightsteelblue" points="-20 230 -10 230 -10 0 10 0 10 230 20 230 0 250"/>
+ <text fill="white" stroke="white" stroke-width="0.1" transform="translate(-6,0) rotate(90, 0, 0) "> SID_NEY </text>
+ </g>
+ <g transform="translate(650,0)">
+ <polygon fill="orange" points="-20 180 -10 180 -10 0 10 0 10 180 20 180 0 200"/>
+ <text fill="white" stroke="white" stroke-width="0.1" transform="translate(-6,0) rotate(90, 0, 0) "> SID_VOMIT </text>
+ </g>
+ <g transform="translate(100,25)">
+ <polygon fill="steelblue" points="-100 130 -50 130 -50 200 50 200 50 130 100 130 0 50"/>
+ <text fill="black" stroke="none" transform="translate(-40,100) " font-size="10">last pushed</text>
+ <text fill="black" stroke="none" transform="translate(-40,115) " font-size="10">=lower nSlot</text>
+ <text fill="black" stroke="none" transform="translate(-40,135) " font-size="10">last pushed slots</text>
+ <text fill="black" stroke="none" transform="translate(-40,150) " font-size="10">go first</text>
+ </g>
+ <path d="M200,75 a10,10 0 0,1 10,-10 l35,0 l5,-10 l5,10 l35,0 a10,10 0 0,1 10,10" stroke-width="1" stroke="rgb(0,0,0)" style="fill:none"/>
+ <text fill="black" stroke="none" transform="translate(200,10)" font-size="10"> dispatcher's Shell stack</text>
+ <text fill="black" stroke="none" transform="translate(200,25)" font-size="10"> (a Stack of pointers) </text>
+ <text fill="black" stroke="none" transform="translate(200,40)" font-size="10"> Declared in dispatch.cxx </text>
+ <text fill="black" stroke="none" transform="translate(200,55)" font-size="10"> As SfxShellStack_Impl </text>
+ <text fill="black" stroke="none" transform="translate(475,270)" font-size="20"> Command ignored </text>
+ <polygon fill="black" points="750,110 750,90 755,95 845,45 855,55 765,105 770,110"/>
+ <text fill="black" stroke="none" transform="translate(850,30)" font-size="10">Empty: no entry in</text>
+ <text fill="black" stroke="none" transform="translate(850,40)" font-size="10">sdi file corresponding</text>
+ <text fill="black" stroke="none" transform="translate(850,50)" font-size="10">to that subclass</text>
+ </g>
+
+</svg>
+<!-- vim:set shiftwidth=2 softtabstop=2 expandtab: -->
diff --git a/sfx2/emojiconfig/emoji.json b/sfx2/emojiconfig/emoji.json
new file mode 100644
index 000000000..4b2a99746
--- /dev/null
+++ b/sfx2/emojiconfig/emoji.json
@@ -0,0 +1,26822 @@
+{
+ "100": {
+ "unicode": "1f4af",
+ "unicode_alternates": "",
+ "name": "hundred points symbol",
+ "shortname": ":100:",
+ "category": "symbols",
+ "emoji_order": "856",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol",
+ "wow",
+ "wow",
+ "win",
+ "win",
+ "perfect",
+ "perfect",
+ "parties",
+ "parties"
+ ]
+ },
+ "1234": {
+ "unicode": "1f522",
+ "unicode_alternates": "",
+ "name": "input symbol for numbers",
+ "shortname": ":1234:",
+ "category": "symbols",
+ "emoji_order": "913",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol"
+ ]
+ },
+ "grinning": {
+ "unicode": "1f600",
+ "unicode_alternates": "",
+ "name": "grinning face",
+ "shortname": ":grinning:",
+ "category": "people",
+ "emoji_order": "1",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "happy",
+ "smiley",
+ "emotion",
+ "emotion"
+ ]
+ },
+ "grimacing": {
+ "unicode": "1f62c",
+ "unicode_alternates": "",
+ "name": "grimacing face",
+ "shortname": ":grimacing:",
+ "category": "people",
+ "emoji_order": "2",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "silly",
+ "smiley",
+ "emotion",
+ "emotion",
+ "selfie",
+ "selfie"
+ ]
+ },
+ "grin": {
+ "unicode": "1f601",
+ "unicode_alternates": "",
+ "name": "grinning face with smiling eyes",
+ "shortname": ":grin:",
+ "category": "people",
+ "emoji_order": "3",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "happy",
+ "silly",
+ "smiley",
+ "emotion",
+ "emotion",
+ "good",
+ "good",
+ "selfie",
+ "selfie"
+ ]
+ },
+ "joy": {
+ "unicode": "1f602",
+ "unicode_alternates": "",
+ "name": "face with tears of joy",
+ "shortname": ":joy:",
+ "category": "people",
+ "emoji_order": "4",
+ "aliases": [],
+ "aliases_ascii": [
+ ":')",
+ ":'-)"
+ ],
+ "keywords": [
+ "happy",
+ "silly",
+ "smiley",
+ "cry",
+ "laugh",
+ "laugh",
+ "emotion",
+ "emotion",
+ "sarcastic",
+ "sarcastic"
+ ]
+ },
+ "smiley": {
+ "unicode": "1f603",
+ "unicode_alternates": "",
+ "name": "smiling face with open mouth",
+ "shortname": ":smiley:",
+ "category": "people",
+ "emoji_order": "5",
+ "aliases": [],
+ "aliases_ascii": [
+ ":D",
+ ":-D",
+ "=D"
+ ],
+ "keywords": [
+ "happy",
+ "smiley",
+ "emotion",
+ "emotion",
+ "good",
+ "good"
+ ]
+ },
+ "smile": {
+ "unicode": "1f604",
+ "unicode_alternates": "",
+ "name": "smiling face with open mouth and smiling eyes",
+ "shortname": ":smile:",
+ "category": "people",
+ "emoji_order": "6",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "happy",
+ "smiley",
+ "emotion",
+ "emotion"
+ ]
+ },
+ "sweat_smile": {
+ "unicode": "1f605",
+ "unicode_alternates": "",
+ "name": "smiling face with open mouth and cold sweat",
+ "shortname": ":sweat_smile:",
+ "category": "people",
+ "emoji_order": "7",
+ "aliases": [],
+ "aliases_ascii": [
+ "':)",
+ "':-)",
+ "'=)",
+ "':D",
+ "':-D",
+ "'=D"
+ ],
+ "keywords": [
+ "smiley",
+ "workout",
+ "sweat",
+ "emotion",
+ "emotion"
+ ]
+ },
+ "laughing": {
+ "unicode": "1f606",
+ "unicode_alternates": "",
+ "name": "smiling face with open mouth and tightly-closed eyes",
+ "shortname": ":laughing:",
+ "category": "people",
+ "emoji_order": "8",
+ "aliases": [
+ ":satisfied:"
+ ],
+ "aliases_ascii": [
+ ">:)",
+ ">;)",
+ ">:-)",
+ ">=)"
+ ],
+ "keywords": [
+ "happy",
+ "smiley",
+ "laugh",
+ "laugh",
+ "emotion",
+ "emotion"
+ ]
+ },
+ "innocent": {
+ "unicode": "1f607",
+ "unicode_alternates": "",
+ "name": "smiling face with halo",
+ "shortname": ":innocent:",
+ "category": "people",
+ "emoji_order": "9",
+ "aliases": [],
+ "aliases_ascii": [
+ "O:-)",
+ "0:-3",
+ "0:3",
+ "0:-)",
+ "0:)",
+ "0;^)",
+ "O:)",
+ "O;-)",
+ "O=)",
+ "0;-)",
+ "O:-3",
+ "O:3"
+ ],
+ "keywords": [
+ "smiley",
+ "emotion",
+ "emotion"
+ ]
+ },
+ "wink": {
+ "unicode": "1f609",
+ "unicode_alternates": "",
+ "name": "winking face",
+ "shortname": ":wink:",
+ "category": "people",
+ "emoji_order": "10",
+ "aliases": [],
+ "aliases_ascii": [
+ ";)",
+ ";-)",
+ "*-)",
+ "*)",
+ ";-]",
+ ";]",
+ ";D",
+ ";^)"
+ ],
+ "keywords": [
+ "silly",
+ "smiley",
+ "emotion",
+ "emotion"
+ ]
+ },
+ "blush": {
+ "unicode": "1f60a",
+ "unicode_alternates": "",
+ "name": "smiling face with smiling eyes",
+ "shortname": ":blush:",
+ "category": "people",
+ "emoji_order": "11",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "happy",
+ "smiley",
+ "emotion",
+ "emotion",
+ "good",
+ "good",
+ "beautiful",
+ "beautiful"
+ ]
+ },
+ "slight_smile": {
+ "unicode": "1f642",
+ "unicode_alternates": "",
+ "name": "slightly smiling face",
+ "shortname": ":slight_smile:",
+ "category": "people",
+ "emoji_order": "12",
+ "aliases": [
+ ":slightly_smiling_face:"
+ ],
+ "aliases_ascii": [
+ ":)",
+ ":-)",
+ "=]",
+ "=)",
+ ":]"
+ ],
+ "keywords": [
+ "happy",
+ "smiley"
+ ]
+ },
+ "upside_down": {
+ "unicode": "1f643",
+ "unicode_alternates": "",
+ "name": "upside-down face",
+ "shortname": ":upside_down:",
+ "category": "people",
+ "emoji_order": "13",
+ "aliases": [
+ ":upside_down_face:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "silly",
+ "smiley",
+ "sarcastic",
+ "sarcastic"
+ ]
+ },
+ "relaxed": {
+ "unicode": "263a",
+ "unicode_alternates": "263a-fe0f",
+ "name": "white smiling face",
+ "shortname": ":relaxed:",
+ "category": "people",
+ "emoji_order": "14",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "happy",
+ "smiley"
+ ]
+ },
+ "yum": {
+ "unicode": "1f60b",
+ "unicode_alternates": "",
+ "name": "face savouring delicious food",
+ "shortname": ":yum:",
+ "category": "people",
+ "emoji_order": "15",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "happy",
+ "silly",
+ "smiley",
+ "emotion",
+ "emotion",
+ "sarcastic",
+ "sarcastic",
+ "good",
+ "good"
+ ]
+ },
+ "relieved": {
+ "unicode": "1f60c",
+ "unicode_alternates": "",
+ "name": "relieved face",
+ "shortname": ":relieved:",
+ "category": "people",
+ "emoji_order": "16",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "smiley",
+ "emotion",
+ "emotion"
+ ]
+ },
+ "heart_eyes": {
+ "unicode": "1f60d",
+ "unicode_alternates": "",
+ "name": "smiling face with heart-shaped eyes",
+ "shortname": ":heart_eyes:",
+ "category": "people",
+ "emoji_order": "17",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "happy",
+ "smiley",
+ "love",
+ "sex",
+ "heart eyes",
+ "emotion",
+ "emotion",
+ "beautiful",
+ "beautiful"
+ ]
+ },
+ "kissing_heart": {
+ "unicode": "1f618",
+ "unicode_alternates": "",
+ "name": "face throwing a kiss",
+ "shortname": ":kissing_heart:",
+ "category": "people",
+ "emoji_order": "18",
+ "aliases": [],
+ "aliases_ascii": [
+ ":*",
+ ":-*",
+ "=*",
+ ":^*"
+ ],
+ "keywords": [
+ "smiley",
+ "love",
+ "sexy"
+ ]
+ },
+ "kissing": {
+ "unicode": "1f617",
+ "unicode_alternates": "",
+ "name": "kissing face",
+ "shortname": ":kissing:",
+ "category": "people",
+ "emoji_order": "19",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "smiley",
+ "sexy"
+ ]
+ },
+ "kissing_smiling_eyes": {
+ "unicode": "1f619",
+ "unicode_alternates": "",
+ "name": "kissing face with smiling eyes",
+ "shortname": ":kissing_smiling_eyes:",
+ "category": "people",
+ "emoji_order": "20",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "smiley",
+ "sexy"
+ ]
+ },
+ "kissing_closed_eyes": {
+ "unicode": "1f61a",
+ "unicode_alternates": "",
+ "name": "kissing face with closed eyes",
+ "shortname": ":kissing_closed_eyes:",
+ "category": "people",
+ "emoji_order": "21",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "smiley",
+ "sexy"
+ ]
+ },
+ "stuck_out_tongue_winking_eye": {
+ "unicode": "1f61c",
+ "unicode_alternates": "",
+ "name": "face with stuck-out tongue and winking eye",
+ "shortname": ":stuck_out_tongue_winking_eye:",
+ "category": "people",
+ "emoji_order": "22",
+ "aliases": [],
+ "aliases_ascii": [
+ ">:P",
+ "X-P",
+ "x-p"
+ ],
+ "keywords": [
+ "happy",
+ "smiley",
+ "emotion",
+ "emotion",
+ "parties",
+ "parties"
+ ]
+ },
+ "stuck_out_tongue_closed_eyes": {
+ "unicode": "1f61d",
+ "unicode_alternates": "",
+ "name": "face with stuck-out tongue and tightly-closed eyes",
+ "shortname": ":stuck_out_tongue_closed_eyes:",
+ "category": "people",
+ "emoji_order": "23",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "happy",
+ "smiley",
+ "emotion",
+ "emotion"
+ ]
+ },
+ "stuck_out_tongue": {
+ "unicode": "1f61b",
+ "unicode_alternates": "",
+ "name": "face with stuck-out tongue",
+ "shortname": ":stuck_out_tongue:",
+ "category": "people",
+ "emoji_order": "24",
+ "aliases": [],
+ "aliases_ascii": [
+ ":P",
+ ":-P",
+ "=P",
+ ":-p",
+ ":p",
+ "=p",
+ ":-Þ",
+ ":Þ",
+ ":þ",
+ ":-þ",
+ ":-b",
+ ":b",
+ "d:"
+ ],
+ "keywords": [
+ "smiley",
+ "sex",
+ "emotion",
+ "emotion"
+ ]
+ },
+ "money_mouth": {
+ "unicode": "1f911",
+ "unicode_alternates": "",
+ "name": "money-mouth face",
+ "shortname": ":money_mouth:",
+ "category": "people",
+ "emoji_order": "25",
+ "aliases": [
+ ":money_mouth_face:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "smiley",
+ "win",
+ "win",
+ "money",
+ "money",
+ "emotion",
+ "emotion",
+ "boys night",
+ "boys night"
+ ]
+ },
+ "nerd": {
+ "unicode": "1f913",
+ "unicode_alternates": "",
+ "name": "nerd face",
+ "shortname": ":nerd:",
+ "category": "people",
+ "emoji_order": "26",
+ "aliases": [
+ ":nerd_face:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "smiley",
+ "glasses"
+ ]
+ },
+ "sunglasses": {
+ "unicode": "1f60e",
+ "unicode_alternates": "",
+ "name": "smiling face with sunglasses",
+ "shortname": ":sunglasses:",
+ "category": "people",
+ "emoji_order": "27",
+ "aliases": [],
+ "aliases_ascii": [
+ "B-)",
+ "B)",
+ "8)",
+ "8-)",
+ "B-D",
+ "8-D"
+ ],
+ "keywords": [
+ "silly",
+ "smiley",
+ "emojione",
+ "glasses",
+ "boys night",
+ "boys night"
+ ]
+ },
+ "hugging": {
+ "unicode": "1f917",
+ "unicode_alternates": "",
+ "name": "hugging face",
+ "shortname": ":hugging:",
+ "category": "people",
+ "emoji_order": "28",
+ "aliases": [
+ ":hugging_face:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "smiley",
+ "hug",
+ "thank you"
+ ]
+ },
+ "smirk": {
+ "unicode": "1f60f",
+ "unicode_alternates": "",
+ "name": "smirking face",
+ "shortname": ":smirk:",
+ "category": "people",
+ "emoji_order": "29",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "silly",
+ "smiley",
+ "sexy",
+ "sarcastic",
+ "sarcastic"
+ ]
+ },
+ "no_mouth": {
+ "unicode": "1f636",
+ "unicode_alternates": "",
+ "name": "face without mouth",
+ "shortname": ":no_mouth:",
+ "category": "people",
+ "emoji_order": "30",
+ "aliases": [],
+ "aliases_ascii": [
+ ":-X",
+ ":X",
+ ":-#",
+ ":#",
+ "=X",
+ "=x",
+ ":x",
+ ":-x",
+ "=#"
+ ],
+ "keywords": [
+ "mad",
+ "smiley",
+ "neutral",
+ "emotion",
+ "emotion"
+ ]
+ },
+ "neutral_face": {
+ "unicode": "1f610",
+ "unicode_alternates": "",
+ "name": "neutral face",
+ "shortname": ":neutral_face:",
+ "category": "people",
+ "emoji_order": "31",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "mad",
+ "smiley",
+ "shrug",
+ "neutral",
+ "emotion",
+ "emotion"
+ ]
+ },
+ "expressionless": {
+ "unicode": "1f611",
+ "unicode_alternates": "",
+ "name": "expressionless face",
+ "shortname": ":expressionless:",
+ "category": "people",
+ "emoji_order": "32",
+ "aliases": [],
+ "aliases_ascii": [
+ "-_-",
+ "-__-",
+ "-___-"
+ ],
+ "keywords": [
+ "mad",
+ "smiley",
+ "neutral",
+ "emotion",
+ "emotion"
+ ]
+ },
+ "unamused": {
+ "unicode": "1f612",
+ "unicode_alternates": "",
+ "name": "unamused face",
+ "shortname": ":unamused:",
+ "category": "people",
+ "emoji_order": "33",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "sad",
+ "mad",
+ "smiley",
+ "tired",
+ "emotion",
+ "emotion"
+ ]
+ },
+ "rolling_eyes": {
+ "unicode": "1f644",
+ "unicode_alternates": "",
+ "name": "face with rolling eyes",
+ "shortname": ":rolling_eyes:",
+ "category": "people",
+ "emoji_order": "34",
+ "aliases": [
+ ":face_with_rolling_eyes:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "mad",
+ "smiley",
+ "rolling eyes",
+ "emotion",
+ "emotion",
+ "sarcastic",
+ "sarcastic"
+ ]
+ },
+ "thinking": {
+ "unicode": "1f914",
+ "unicode_alternates": "",
+ "name": "thinking face",
+ "shortname": ":thinking:",
+ "category": "people",
+ "emoji_order": "35",
+ "aliases": [
+ ":thinking_face:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "smiley",
+ "thinking",
+ "boys night",
+ "boys night"
+ ]
+ },
+ "flushed": {
+ "unicode": "1f633",
+ "unicode_alternates": "",
+ "name": "flushed face",
+ "shortname": ":flushed:",
+ "category": "people",
+ "emoji_order": "36",
+ "aliases": [],
+ "aliases_ascii": [
+ ":$",
+ "=$"
+ ],
+ "keywords": [
+ "smiley",
+ "emotion",
+ "emotion",
+ "omg",
+ "omg"
+ ]
+ },
+ "disappointed": {
+ "unicode": "1f61e",
+ "unicode_alternates": "",
+ "name": "disappointed face",
+ "shortname": ":disappointed:",
+ "category": "people",
+ "emoji_order": "37",
+ "aliases": [],
+ "aliases_ascii": [
+ ">:[",
+ ":-(",
+ ":(",
+ ":-[",
+ ":[",
+ "=("
+ ],
+ "keywords": [
+ "sad",
+ "smiley",
+ "tired",
+ "emotion",
+ "emotion"
+ ]
+ },
+ "worried": {
+ "unicode": "1f61f",
+ "unicode_alternates": "",
+ "name": "worried face",
+ "shortname": ":worried:",
+ "category": "people",
+ "emoji_order": "38",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "sad",
+ "smiley",
+ "emotion",
+ "emotion"
+ ]
+ },
+ "angry": {
+ "unicode": "1f620",
+ "unicode_alternates": "",
+ "name": "angry face",
+ "shortname": ":angry:",
+ "category": "people",
+ "emoji_order": "39",
+ "aliases": [],
+ "aliases_ascii": [
+ ">:(",
+ ">:-(",
+ ":@"
+ ],
+ "keywords": [
+ "mad",
+ "smiley",
+ "emotion",
+ "emotion"
+ ]
+ },
+ "rage": {
+ "unicode": "1f621",
+ "unicode_alternates": "",
+ "name": "pouting face",
+ "shortname": ":rage:",
+ "category": "people",
+ "emoji_order": "40",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "mad",
+ "smiley",
+ "angry",
+ "emotion",
+ "emotion"
+ ]
+ },
+ "pensive": {
+ "unicode": "1f614",
+ "unicode_alternates": "",
+ "name": "pensive face",
+ "shortname": ":pensive:",
+ "category": "people",
+ "emoji_order": "41",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "sad",
+ "smiley",
+ "emotion",
+ "emotion",
+ "rip",
+ "rip"
+ ]
+ },
+ "confused": {
+ "unicode": "1f615",
+ "unicode_alternates": "",
+ "name": "confused face",
+ "shortname": ":confused:",
+ "category": "people",
+ "emoji_order": "42",
+ "aliases": [],
+ "aliases_ascii": [
+ ">:\\",
+ ">: /",
+ ": -/",
+ ": -.",
+ ": /",
+ ": \\",
+ "=/",
+ "=\\",
+ ": L",
+ "=L"
+ ],
+ "keywords": [
+ "smiley",
+ "surprised",
+ "emotion",
+ "emotion"
+ ]
+ },
+ "slight_frown": {
+ "unicode": "1f641",
+ "unicode_alternates": "",
+ "name": "slightlyfrowningface",
+ "shortname": ": slight_frown: ",
+ "category": "people",
+ "emoji_order": "43",
+ "aliases": [
+ ": slightly_frowning_face: "
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "sad",
+ "smiley",
+ "emotion",
+ "emotion"
+ ]
+ },
+ "frowning2": {
+ "unicode": "2639",
+ "unicode_alternates": "2639-fe0f",
+ "name": "whitefrowningface",
+ "shortname": ": frowning2: ",
+ "category": "people",
+ "emoji_order": "44",
+ "aliases": [
+ ": white_frowning_face: "
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "sad",
+ "smiley",
+ "emotion",
+ "emotion"
+ ]
+ },
+ "persevere": {
+ "unicode": "1f623",
+ "unicode_alternates": "",
+ "name": "perseveringface",
+ "shortname": ": persevere: ",
+ "category": "people",
+ "emoji_order": "45",
+ "aliases": [],
+ "aliases_ascii": [
+ ">.<"
+ ],
+ "keywords": [
+ "sad",
+ "smiley",
+ "angry",
+ "emotion",
+ "emotion"
+ ]
+ },
+ "confounded": {
+ "unicode": "1f616",
+ "unicode_alternates": "",
+ "name": "confoundedface",
+ "shortname": ": confounded: ",
+ "category": "people",
+ "emoji_order": "46",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "sad",
+ "smiley",
+ "angry",
+ "emotion",
+ "emotion"
+ ]
+ },
+ "tired_face": {
+ "unicode": "1f62b",
+ "unicode_alternates": "",
+ "name": "tiredface",
+ "shortname": ": tired_face: ",
+ "category": "people",
+ "emoji_order": "47",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "sad",
+ "smiley",
+ "tired",
+ "emotion",
+ "emotion"
+ ]
+ },
+ "weary": {
+ "unicode": "1f629",
+ "unicode_alternates": "",
+ "name": "wearyface",
+ "shortname": ": weary: ",
+ "category": "people",
+ "emoji_order": "48",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "sad",
+ "smiley",
+ "tired",
+ "stressed",
+ "emotion",
+ "emotion"
+ ]
+ },
+ "triumph": {
+ "unicode": "1f624",
+ "unicode_alternates": "",
+ "name": "facewithlookoftriumph",
+ "shortname": ": triumph: ",
+ "category": "people",
+ "emoji_order": "49",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "mad",
+ "smiley",
+ "angry",
+ "emotion",
+ "emotion",
+ "steam",
+ "steam"
+ ]
+ },
+ "open_mouth": {
+ "unicode": "1f62e",
+ "unicode_alternates": "",
+ "name": "facewithopenmouth",
+ "shortname": ": open_mouth: ",
+ "category": "people",
+ "emoji_order": "50",
+ "aliases": [],
+ "aliases_ascii": [
+ ": -O",
+ ": O",
+ ": -o",
+ ": o",
+ "O_O",
+ ">: O"
+ ],
+ "keywords": [
+ "smiley",
+ "surprised",
+ "wow",
+ "wow",
+ "emotion",
+ "emotion"
+ ]
+ },
+ "scream": {
+ "unicode": "1f631",
+ "unicode_alternates": "",
+ "name": "facescreaminginfear",
+ "shortname": ": scream: ",
+ "category": "people",
+ "emoji_order": "51",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "smiley",
+ "surprised",
+ "wow",
+ "wow",
+ "emotion",
+ "emotion",
+ "omg",
+ "omg"
+ ]
+ },
+ "fearful": {
+ "unicode": "1f628",
+ "unicode_alternates": "",
+ "name": "fearfulface",
+ "shortname": ": fearful: ",
+ "category": "people",
+ "emoji_order": "52",
+ "aliases": [],
+ "aliases_ascii": [
+ "D: "
+ ],
+ "keywords": [
+ "smiley",
+ "surprised",
+ "emotion",
+ "emotion"
+ ]
+ },
+ "cold_sweat": {
+ "unicode": "1f630",
+ "unicode_alternates": "",
+ "name": "facewithopenmouthandcoldsweat",
+ "shortname": ": cold_sweat: ",
+ "category": "people",
+ "emoji_order": "53",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "smiley",
+ "sweat",
+ "emotion",
+ "emotion"
+ ]
+ },
+ "hushed": {
+ "unicode": "1f62f",
+ "unicode_alternates": "",
+ "name": "hushedface",
+ "shortname": ": hushed: ",
+ "category": "people",
+ "emoji_order": "54",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "smiley",
+ "surprised",
+ "wow",
+ "wow"
+ ]
+ },
+ "frowning": {
+ "unicode": "1f626",
+ "unicode_alternates": "",
+ "name": "frowningfacewithopenmouth",
+ "shortname": ": frowning: ",
+ "category": "people",
+ "emoji_order": "55",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "sad",
+ "smiley",
+ "surprised",
+ "emotion",
+ "emotion"
+ ]
+ },
+ "anguished": {
+ "unicode": "1f627",
+ "unicode_alternates": "",
+ "name": "anguishedface",
+ "shortname": ": anguished: ",
+ "category": "people",
+ "emoji_order": "56",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "sad",
+ "smiley",
+ "surprised",
+ "emotion",
+ "emotion"
+ ]
+ },
+ "cry": {
+ "unicode": "1f622",
+ "unicode_alternates": "",
+ "name": "cryingface",
+ "shortname": ": cry: ",
+ "category": "people",
+ "emoji_order": "57",
+ "aliases": [],
+ "aliases_ascii": [
+ ": '(",
+ ":'-(",
+ ";(",
+ ";-("
+ ],
+ "keywords": [
+ "sad",
+ "smiley",
+ "cry",
+ "emotion",
+ "emotion",
+ "rip",
+ "rip",
+ "heartbreak",
+ "heartbreak"
+ ]
+ },
+ "disappointed_relieved": {
+ "unicode": "1f625",
+ "unicode_alternates": "",
+ "name": "disappointedbutrelievedface",
+ "shortname": ": disappointed_relieved: ",
+ "category": "people",
+ "emoji_order": "58",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "sad",
+ "smiley",
+ "stressed",
+ "sweat",
+ "cry",
+ "emotion",
+ "emotion"
+ ]
+ },
+ "sleepy": {
+ "unicode": "1f62a",
+ "unicode_alternates": "",
+ "name": "sleepyface",
+ "shortname": ": sleepy: ",
+ "category": "people",
+ "emoji_order": "59",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "smiley",
+ "sick",
+ "emotion",
+ "emotion"
+ ]
+ },
+ "sweat": {
+ "unicode": "1f613",
+ "unicode_alternates": "",
+ "name": "facewithcoldsweat",
+ "shortname": ": sweat: ",
+ "category": "people",
+ "emoji_order": "60",
+ "aliases": [],
+ "aliases_ascii": [
+ "':(",
+ "': -(",
+ "'=("
+ ],
+ "keywords": [
+ "sad",
+ "smiley",
+ "stressed",
+ "sweat",
+ "emotion",
+ "emotion"
+ ]
+ },
+ "sob": {
+ "unicode": "1f62d",
+ "unicode_alternates": "",
+ "name": "loudly crying face",
+ "shortname": ":sob:",
+ "category": "people",
+ "emoji_order": "61",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "sad",
+ "smiley",
+ "cry",
+ "emotion",
+ "emotion",
+ "heartbreak",
+ "heartbreak"
+ ]
+ },
+ "dizzy_face": {
+ "unicode": "1f635",
+ "unicode_alternates": "",
+ "name": "dizzy face",
+ "shortname": ":dizzy_face:",
+ "category": "people",
+ "emoji_order": "62",
+ "aliases": [],
+ "aliases_ascii": [
+ "#-)",
+ "#)",
+ "%-)",
+ "%)",
+ "X)",
+ "X-)"
+ ],
+ "keywords": [
+ "smiley",
+ "surprised",
+ "dead",
+ "wow",
+ "wow",
+ "emotion",
+ "emotion",
+ "omg",
+ "omg"
+ ]
+ },
+ "astonished": {
+ "unicode": "1f632",
+ "unicode_alternates": "",
+ "name": "astonished face",
+ "shortname": ":astonished:",
+ "category": "people",
+ "emoji_order": "63",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "smiley",
+ "surprised",
+ "wow",
+ "wow",
+ "emotion",
+ "emotion",
+ "omg",
+ "omg"
+ ]
+ },
+ "zipper_mouth": {
+ "unicode": "1f910",
+ "unicode_alternates": "",
+ "name": "zipper-mouth face",
+ "shortname": ":zipper_mouth:",
+ "category": "people",
+ "emoji_order": "64",
+ "aliases": [
+ ":zipper_mouth_face:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "mad",
+ "smiley"
+ ]
+ },
+ "mask": {
+ "unicode": "1f637",
+ "unicode_alternates": "",
+ "name": "face with medical mask",
+ "shortname": ":mask:",
+ "category": "people",
+ "emoji_order": "65",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "smiley",
+ "dead",
+ "health",
+ "sick"
+ ]
+ },
+ "thermometer_face": {
+ "unicode": "1f912",
+ "unicode_alternates": "",
+ "name": "face with thermometer",
+ "shortname": ":thermometer_face:",
+ "category": "people",
+ "emoji_order": "66",
+ "aliases": [
+ ":face_with_thermometer:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "smiley",
+ "health",
+ "sick",
+ "emotion",
+ "emotion"
+ ]
+ },
+ "head_bandage": {
+ "unicode": "1f915",
+ "unicode_alternates": "",
+ "name": "face with head-bandage",
+ "shortname": ":head_bandage:",
+ "category": "people",
+ "emoji_order": "67",
+ "aliases": [
+ ":face_with_head_bandage:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "smiley",
+ "health",
+ "sick",
+ "emotion",
+ "emotion"
+ ]
+ },
+ "sleeping": {
+ "unicode": "1f634",
+ "unicode_alternates": "",
+ "name": "sleeping face",
+ "shortname": ":sleeping:",
+ "category": "people",
+ "emoji_order": "68",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "smiley",
+ "tired",
+ "emotion",
+ "emotion",
+ "goodnight",
+ "goodnight"
+ ]
+ },
+ "zzz": {
+ "unicode": "1f4a4",
+ "unicode_alternates": "",
+ "name": "sleeping symbol",
+ "shortname": ":zzz:",
+ "category": "people",
+ "emoji_order": "69",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "tired",
+ "goodnight",
+ "goodnight"
+ ]
+ },
+ "poop": {
+ "unicode": "1f4a9",
+ "unicode_alternates": "",
+ "name": "pile of poo",
+ "shortname": ":poop:",
+ "category": "people",
+ "emoji_order": "70",
+ "aliases": [
+ ":shit:",
+ ":hankey:",
+ ":poo:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "bathroom",
+ "shit",
+ "sol",
+ "sol",
+ "diarrhea",
+ "diarrhea"
+ ]
+ },
+ "smiling_imp": {
+ "unicode": "1f608",
+ "unicode_alternates": "",
+ "name": "smiling face with horns",
+ "shortname": ":smiling_imp:",
+ "category": "people",
+ "emoji_order": "71",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "silly",
+ "smiley",
+ "angry",
+ "monster",
+ "devil",
+ "devil",
+ "boys night",
+ "boys night"
+ ]
+ },
+ "imp": {
+ "unicode": "1f47f",
+ "unicode_alternates": "",
+ "name": "imp",
+ "shortname": ":imp:",
+ "category": "people",
+ "emoji_order": "72",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "smiley",
+ "monster",
+ "devil",
+ "devil",
+ "wth",
+ "wth"
+ ]
+ },
+ "japanese_ogre": {
+ "unicode": "1f479",
+ "unicode_alternates": "",
+ "name": "japanese ogre",
+ "shortname": ":japanese_ogre:",
+ "category": "people",
+ "emoji_order": "73",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "monster"
+ ]
+ },
+ "japanese_goblin": {
+ "unicode": "1f47a",
+ "unicode_alternates": "",
+ "name": "japanese goblin",
+ "shortname": ":japanese_goblin:",
+ "category": "people",
+ "emoji_order": "74",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "angry",
+ "monster"
+ ]
+ },
+ "skull": {
+ "unicode": "1f480",
+ "unicode_alternates": "",
+ "name": "skull",
+ "shortname": ":skull:",
+ "category": "people",
+ "emoji_order": "75",
+ "aliases": [
+ ":skeleton:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "dead",
+ "halloween",
+ "skull"
+ ]
+ },
+ "ghost": {
+ "unicode": "1f47b",
+ "unicode_alternates": "",
+ "name": "ghost",
+ "shortname": ":ghost:",
+ "category": "people",
+ "emoji_order": "76",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "holidays",
+ "halloween",
+ "monster"
+ ]
+ },
+ "alien": {
+ "unicode": "1f47d",
+ "unicode_alternates": "",
+ "name": "extraterrestrial alien",
+ "shortname": ":alien:",
+ "category": "people",
+ "emoji_order": "77",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "space",
+ "monster",
+ "alien",
+ "scientology",
+ "scientology"
+ ]
+ },
+ "robot": {
+ "unicode": "1f916",
+ "unicode_alternates": "",
+ "name": "robot face",
+ "shortname": ":robot:",
+ "category": "people",
+ "emoji_order": "78",
+ "aliases": [
+ ":robot_face:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "monster",
+ "robot"
+ ]
+ },
+ "smiley_cat": {
+ "unicode": "1f63a",
+ "unicode_alternates": "",
+ "name": "smiling cat face with open mouth",
+ "shortname": ":smiley_cat:",
+ "category": "people",
+ "emoji_order": "79",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "happy",
+ "cat",
+ "cat",
+ "animal",
+ "animal"
+ ]
+ },
+ "smile_cat": {
+ "unicode": "1f638",
+ "unicode_alternates": "",
+ "name": "grinning cat face with smiling eyes",
+ "shortname": ":smile_cat:",
+ "category": "people",
+ "emoji_order": "80",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "happy",
+ "cat",
+ "cat",
+ "animal",
+ "animal"
+ ]
+ },
+ "joy_cat": {
+ "unicode": "1f639",
+ "unicode_alternates": "",
+ "name": "cat face with tears of joy",
+ "shortname": ":joy_cat:",
+ "category": "people",
+ "emoji_order": "81",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "happy",
+ "silly",
+ "cry",
+ "laugh",
+ "laugh",
+ "cat",
+ "cat",
+ "animal",
+ "animal",
+ "sarcastic",
+ "sarcastic"
+ ]
+ },
+ "heart_eyes_cat": {
+ "unicode": "1f63b",
+ "unicode_alternates": "",
+ "name": "smiling cat face with heart-shaped eyes",
+ "shortname": ":heart_eyes_cat:",
+ "category": "people",
+ "emoji_order": "82",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "heart eyes",
+ "cat",
+ "cat",
+ "animal",
+ "animal",
+ "beautiful",
+ "beautiful"
+ ]
+ },
+ "smirk_cat": {
+ "unicode": "1f63c",
+ "unicode_alternates": "",
+ "name": "cat face with wry smile",
+ "shortname": ":smirk_cat:",
+ "category": "people",
+ "emoji_order": "83",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "cat",
+ "cat",
+ "animal",
+ "animal"
+ ]
+ },
+ "kissing_cat": {
+ "unicode": "1f63d",
+ "unicode_alternates": "",
+ "name": "kissing cat face with closed eyes",
+ "shortname": ":kissing_cat:",
+ "category": "people",
+ "emoji_order": "84",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "cat",
+ "cat",
+ "animal",
+ "animal"
+ ]
+ },
+ "scream_cat": {
+ "unicode": "1f640",
+ "unicode_alternates": "",
+ "name": "weary cat face",
+ "shortname": ":scream_cat:",
+ "category": "people",
+ "emoji_order": "85",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "cat",
+ "cat",
+ "animal",
+ "animal"
+ ]
+ },
+ "crying_cat_face": {
+ "unicode": "1f63f",
+ "unicode_alternates": "",
+ "name": "crying cat face",
+ "shortname": ":crying_cat_face:",
+ "category": "people",
+ "emoji_order": "86",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "cry",
+ "cat",
+ "cat",
+ "animal",
+ "animal"
+ ]
+ },
+ "pouting_cat": {
+ "unicode": "1f63e",
+ "unicode_alternates": "",
+ "name": "pouting cat face",
+ "shortname": ":pouting_cat:",
+ "category": "people",
+ "emoji_order": "87",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "cat",
+ "cat",
+ "animal",
+ "animal"
+ ]
+ },
+ "raised_hands": {
+ "unicode": "1f64c",
+ "unicode_alternates": "",
+ "name": "person raising both hands in celebration",
+ "shortname": ":raised_hands:",
+ "category": "people",
+ "emoji_order": "88",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "body",
+ "hands",
+ "diversity",
+ "diversity",
+ "perfect",
+ "perfect",
+ "good",
+ "good",
+ "parties",
+ "parties"
+ ]
+ },
+ "clap": {
+ "unicode": "1f44f",
+ "unicode_alternates": "",
+ "name": "clapping hands sign",
+ "shortname": ":clap:",
+ "category": "people",
+ "emoji_order": "89",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "body",
+ "hands",
+ "win",
+ "win",
+ "diversity",
+ "diversity",
+ "good",
+ "good",
+ "beautiful",
+ "beautiful"
+ ]
+ },
+ "wave": {
+ "unicode": "1f44b",
+ "unicode_alternates": "",
+ "name": "waving hand sign",
+ "shortname": ":wave:",
+ "category": "people",
+ "emoji_order": "90",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "body",
+ "hands",
+ "hi",
+ "diversity",
+ "diversity"
+ ]
+ },
+ "thumbsup": {
+ "unicode": "1f44d",
+ "unicode_alternates": "",
+ "name": "thumbs up sign",
+ "shortname": ":thumbsup:",
+ "category": "people",
+ "emoji_order": "91",
+ "aliases": [
+ ":+1:",
+ ":thumbup:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "body",
+ "hands",
+ "hi",
+ "luck",
+ "thank you",
+ "diversity",
+ "diversity",
+ "perfect",
+ "perfect",
+ "good",
+ "good",
+ "beautiful",
+ "beautiful"
+ ]
+ },
+ "thumbsdown": {
+ "unicode": "1f44e",
+ "unicode_alternates": "",
+ "name": "thumbs down sign",
+ "shortname": ":thumbsdown:",
+ "category": "people",
+ "emoji_order": "92",
+ "aliases": [
+ ":-1:",
+ ":thumbdown:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "body",
+ "hands",
+ "diversity",
+ "diversity"
+ ]
+ },
+ "punch": {
+ "unicode": "1f44a",
+ "unicode_alternates": "",
+ "name": "fisted hand sign",
+ "shortname": ":punch:",
+ "category": "people",
+ "emoji_order": "93",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "body",
+ "hands",
+ "hi",
+ "fist bump",
+ "diversity",
+ "diversity",
+ "boys night",
+ "boys night"
+ ]
+ },
+ "fist": {
+ "unicode": "270a",
+ "unicode_alternates": "",
+ "name": "raised fist",
+ "shortname": ":fist:",
+ "category": "people",
+ "emoji_order": "94",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "body",
+ "hands",
+ "hi",
+ "fist bump",
+ "diversity",
+ "diversity",
+ "condolence",
+ "condolence"
+ ]
+ },
+ "v": {
+ "unicode": "270c",
+ "unicode_alternates": "270c-fe0f",
+ "name": "victory hand",
+ "shortname": ":v:",
+ "category": "people",
+ "emoji_order": "95",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "body",
+ "hands",
+ "hi",
+ "thank you",
+ "peace",
+ "peace",
+ "diversity",
+ "diversity",
+ "girls night",
+ "girls night"
+ ]
+ },
+ "ok_hand": {
+ "unicode": "1f44c",
+ "unicode_alternates": "",
+ "name": "ok hand sign",
+ "shortname": ":ok_hand:",
+ "category": "people",
+ "emoji_order": "96",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "body",
+ "hands",
+ "hi",
+ "diversity",
+ "diversity",
+ "perfect",
+ "perfect",
+ "good",
+ "good",
+ "beautiful",
+ "beautiful"
+ ]
+ },
+ "raised_hand": {
+ "unicode": "270b",
+ "unicode_alternates": "",
+ "name": "raised hand",
+ "shortname": ":raised_hand:",
+ "category": "people",
+ "emoji_order": "97",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "body",
+ "hands",
+ "hi",
+ "diversity",
+ "diversity",
+ "girls night",
+ "girls night"
+ ]
+ },
+ "open_hands": {
+ "unicode": "1f450",
+ "unicode_alternates": "",
+ "name": "open hands sign",
+ "shortname": ":open_hands:",
+ "category": "people",
+ "emoji_order": "98",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "body",
+ "hands",
+ "diversity",
+ "diversity",
+ "condolence",
+ "condolence"
+ ]
+ },
+ "muscle": {
+ "unicode": "1f4aa",
+ "unicode_alternates": "",
+ "name": "flexed biceps",
+ "shortname": ":muscle:",
+ "category": "people",
+ "emoji_order": "99",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "body",
+ "hands",
+ "workout",
+ "flex",
+ "win",
+ "win",
+ "diversity",
+ "diversity",
+ "feminist",
+ "feminist",
+ "boys night",
+ "boys night"
+ ]
+ },
+ "pray": {
+ "unicode": "1f64f",
+ "unicode_alternates": "",
+ "name": "person with folded hands",
+ "shortname": ":pray:",
+ "category": "people",
+ "emoji_order": "100",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "body",
+ "hands",
+ "hi",
+ "luck",
+ "thank you",
+ "pray",
+ "pray",
+ "diversity",
+ "diversity",
+ "scientology",
+ "scientology"
+ ]
+ },
+ "point_up": {
+ "unicode": "261d",
+ "unicode_alternates": "261d-fe0f",
+ "name": "white up pointing index",
+ "shortname": ":point_up:",
+ "category": "people",
+ "emoji_order": "101",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "body",
+ "hands",
+ "emojione",
+ "diversity",
+ "diversity"
+ ]
+ },
+ "point_up_2": {
+ "unicode": "1f446",
+ "unicode_alternates": "",
+ "name": "white up pointing backhand index",
+ "shortname": ":point_up_2:",
+ "category": "people",
+ "emoji_order": "102",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "body",
+ "hands",
+ "diversity",
+ "diversity"
+ ]
+ },
+ "point_down": {
+ "unicode": "1f447",
+ "unicode_alternates": "",
+ "name": "white down pointing backhand index",
+ "shortname": ":point_down:",
+ "category": "people",
+ "emoji_order": "103",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "body",
+ "hands",
+ "diversity",
+ "diversity"
+ ]
+ },
+ "point_left": {
+ "unicode": "1f448",
+ "unicode_alternates": "",
+ "name": "white left pointing backhand index",
+ "shortname": ":point_left:",
+ "category": "people",
+ "emoji_order": "104",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "body",
+ "hands",
+ "hi",
+ "diversity",
+ "diversity"
+ ]
+ },
+ "point_right": {
+ "unicode": "1f449",
+ "unicode_alternates": "",
+ "name": "white right pointing backhand index",
+ "shortname": ":point_right:",
+ "category": "people",
+ "emoji_order": "105",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "body",
+ "hands",
+ "hi",
+ "diversity",
+ "diversity"
+ ]
+ },
+ "middle_finger": {
+ "unicode": "1f595",
+ "unicode_alternates": "",
+ "name": "reversed hand with middle finger extended",
+ "shortname": ":middle_finger:",
+ "category": "people",
+ "emoji_order": "106",
+ "aliases": [
+ ":reversed_hand_with_middle_finger_extended:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "body",
+ "hands",
+ "middle finger",
+ "diversity",
+ "diversity"
+ ]
+ },
+ "hand_splayed": {
+ "unicode": "1f590",
+ "unicode_alternates": "1f590-fe0f",
+ "name": "raised hand with fingers splayed",
+ "shortname": ":hand_splayed:",
+ "category": "people",
+ "emoji_order": "107",
+ "aliases": [
+ ":raised_hand_with_fingers_splayed:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "body",
+ "hands",
+ "hi",
+ "diversity",
+ "diversity"
+ ]
+ },
+ "metal": {
+ "unicode": "1f918",
+ "unicode_alternates": "",
+ "name": "sign of the horns",
+ "shortname": ":metal:",
+ "category": "people",
+ "emoji_order": "108",
+ "aliases": [
+ ":sign_of_the_horns:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "body",
+ "hands",
+ "hi",
+ "diversity",
+ "diversity",
+ "boys night",
+ "boys night",
+ "parties",
+ "parties"
+ ]
+ },
+ "vulcan": {
+ "unicode": "1f596",
+ "unicode_alternates": "",
+ "name": "raised hand with part between middle and ring fingers",
+ "shortname": ":vulcan:",
+ "category": "people",
+ "emoji_order": "109",
+ "aliases": [
+ ":raised_hand_with_part_between_middle_and_ring_fingers:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "body",
+ "hands",
+ "hi",
+ "diversity",
+ "diversity"
+ ]
+ },
+ "writing_hand": {
+ "unicode": "270d",
+ "unicode_alternates": "270d-fe0f",
+ "name": "writing hand",
+ "shortname": ":writing_hand:",
+ "category": "people",
+ "emoji_order": "110",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "body",
+ "hands",
+ "write",
+ "diversity",
+ "diversity"
+ ]
+ },
+ "nail_care": {
+ "unicode": "1f485",
+ "unicode_alternates": "",
+ "name": "nail polish",
+ "shortname": ":nail_care:",
+ "category": "people",
+ "emoji_order": "111",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "women",
+ "body",
+ "hands",
+ "nailpolish",
+ "diversity",
+ "diversity",
+ "girls night",
+ "girls night"
+ ]
+ },
+ "lips": {
+ "unicode": "1f444",
+ "unicode_alternates": "",
+ "name": "mouth",
+ "shortname": ":lips:",
+ "category": "people",
+ "emoji_order": "112",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "women",
+ "body",
+ "sexy",
+ "lip"
+ ]
+ },
+ "tongue": {
+ "unicode": "1f445",
+ "unicode_alternates": "",
+ "name": "tongue",
+ "shortname": ":tongue:",
+ "category": "people",
+ "emoji_order": "113",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "body",
+ "sexy",
+ "lip"
+ ]
+ },
+ "ear": {
+ "unicode": "1f442",
+ "unicode_alternates": "",
+ "name": "ear",
+ "shortname": ":ear:",
+ "category": "people",
+ "emoji_order": "114",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "body",
+ "diversity",
+ "diversity"
+ ]
+ },
+ "nose": {
+ "unicode": "1f443",
+ "unicode_alternates": "",
+ "name": "nose",
+ "shortname": ":nose:",
+ "category": "people",
+ "emoji_order": "115",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "body",
+ "diversity",
+ "diversity"
+ ]
+ },
+ "eye": {
+ "unicode": "1f441",
+ "unicode_alternates": "1f441-fe0f",
+ "name": "eye",
+ "shortname": ":eye:",
+ "category": "people",
+ "emoji_order": "116",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "body",
+ "eyes"
+ ]
+ },
+ "eyes": {
+ "unicode": "1f440",
+ "unicode_alternates": "",
+ "name": "eyes",
+ "shortname": ":eyes:",
+ "category": "people",
+ "emoji_order": "117",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "body",
+ "eyes"
+ ]
+ },
+ "bust_in_silhouette": {
+ "unicode": "1f464",
+ "unicode_alternates": "",
+ "name": "bust in silhouette",
+ "shortname": ":bust_in_silhouette:",
+ "category": "people",
+ "emoji_order": "118",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "people"
+ ]
+ },
+ "busts_in_silhouette": {
+ "unicode": "1f465",
+ "unicode_alternates": "",
+ "name": "busts in silhouette",
+ "shortname": ":busts_in_silhouette:",
+ "category": "people",
+ "emoji_order": "119",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "people"
+ ]
+ },
+ "speaking_head": {
+ "unicode": "1f5e3",
+ "unicode_alternates": "1f5e3-fe0f",
+ "name": "speaking head in silhouette",
+ "shortname": ":speaking_head:",
+ "category": "people",
+ "emoji_order": "120",
+ "aliases": [
+ ":speaking_head_in_silhouette:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "people",
+ "talk"
+ ]
+ },
+ "baby": {
+ "unicode": "1f476",
+ "unicode_alternates": "",
+ "name": "baby",
+ "shortname": ":baby:",
+ "category": "people",
+ "emoji_order": "121",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "people",
+ "baby",
+ "diversity",
+ "diversity"
+ ]
+ },
+ "boy": {
+ "unicode": "1f466",
+ "unicode_alternates": "",
+ "name": "boy",
+ "shortname": ":boy:",
+ "category": "people",
+ "emoji_order": "122",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "people",
+ "baby",
+ "diversity",
+ "diversity"
+ ]
+ },
+ "girl": {
+ "unicode": "1f467",
+ "unicode_alternates": "",
+ "name": "girl",
+ "shortname": ":girl:",
+ "category": "people",
+ "emoji_order": "123",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "people",
+ "women",
+ "baby",
+ "diversity",
+ "diversity"
+ ]
+ },
+ "man": {
+ "unicode": "1f468",
+ "unicode_alternates": "",
+ "name": "man",
+ "shortname": ":man:",
+ "category": "people",
+ "emoji_order": "124",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "people",
+ "men",
+ "sex",
+ "diversity",
+ "diversity",
+ "selfie",
+ "selfie",
+ "boys night",
+ "boys night"
+ ]
+ },
+ "woman": {
+ "unicode": "1f469",
+ "unicode_alternates": "",
+ "name": "woman",
+ "shortname": ":woman:",
+ "category": "people",
+ "emoji_order": "125",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "people",
+ "women",
+ "sex",
+ "diversity",
+ "diversity",
+ "feminist",
+ "feminist",
+ "selfie",
+ "selfie",
+ "girls night",
+ "girls night"
+ ]
+ },
+ "person_with_blond_hair": {
+ "unicode": "1f471",
+ "unicode_alternates": "",
+ "name": "person with blond hair",
+ "shortname": ":person_with_blond_hair:",
+ "category": "people",
+ "emoji_order": "126",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "people",
+ "men",
+ "diversity",
+ "diversity"
+ ]
+ },
+ "older_man": {
+ "unicode": "1f474",
+ "unicode_alternates": "",
+ "name": "older man",
+ "shortname": ":older_man:",
+ "category": "people",
+ "emoji_order": "127",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "people",
+ "men",
+ "old people",
+ "diversity",
+ "diversity"
+ ]
+ },
+ "older_woman": {
+ "unicode": "1f475",
+ "unicode_alternates": "",
+ "name": "older woman",
+ "shortname": ":older_woman:",
+ "category": "people",
+ "emoji_order": "128",
+ "aliases": [
+ ":grandma:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "people",
+ "old people",
+ "diversity",
+ "diversity"
+ ]
+ },
+ "man_with_gua_pi_mao": {
+ "unicode": "1f472",
+ "unicode_alternates": "",
+ "name": "man with gua pi mao",
+ "shortname": ":man_with_gua_pi_mao:",
+ "category": "people",
+ "emoji_order": "129",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "people",
+ "hat",
+ "men",
+ "diversity",
+ "diversity"
+ ]
+ },
+ "man_with_turban": {
+ "unicode": "1f473",
+ "unicode_alternates": "",
+ "name": "man with turban",
+ "shortname": ":man_with_turban:",
+ "category": "people",
+ "emoji_order": "130",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "people",
+ "hat",
+ "diversity",
+ "diversity"
+ ]
+ },
+ "cop": {
+ "unicode": "1f46e",
+ "unicode_alternates": "",
+ "name": "police officer",
+ "shortname": ":cop:",
+ "category": "people",
+ "emoji_order": "131",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "people",
+ "hat",
+ "men",
+ "diversity",
+ "diversity",
+ "job",
+ "job",
+ "police",
+ "police",
+ "911",
+ "911"
+ ]
+ },
+ "construction_worker": {
+ "unicode": "1f477",
+ "unicode_alternates": "",
+ "name": "construction worker",
+ "shortname": ":construction_worker:",
+ "category": "people",
+ "emoji_order": "132",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "people",
+ "hat",
+ "men",
+ "diversity",
+ "diversity",
+ "job",
+ "job"
+ ]
+ },
+ "guardsman": {
+ "unicode": "1f482",
+ "unicode_alternates": "",
+ "name": "guardsman",
+ "shortname": ":guardsman:",
+ "category": "people",
+ "emoji_order": "133",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "people",
+ "hat",
+ "men",
+ "diversity",
+ "diversity",
+ "job",
+ "job"
+ ]
+ },
+ "spy": {
+ "unicode": "1f575",
+ "unicode_alternates": "1f575-fe0f",
+ "name": "sleuth or spy",
+ "shortname": ":spy:",
+ "category": "people",
+ "emoji_order": "134",
+ "aliases": [
+ ":sleuth_or_spy:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "people",
+ "hat",
+ "men",
+ "glasses",
+ "diversity",
+ "diversity",
+ "job",
+ "job"
+ ]
+ },
+ "santa": {
+ "unicode": "1f385",
+ "unicode_alternates": "",
+ "name": "father christmas",
+ "shortname": ":santa:",
+ "category": "people",
+ "emoji_order": "135",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "people",
+ "hat",
+ "winter",
+ "holidays",
+ "christmas",
+ "diversity",
+ "diversity",
+ "santa",
+ "santa"
+ ]
+ },
+ "angel": {
+ "unicode": "1f47c",
+ "unicode_alternates": "",
+ "name": "baby angel",
+ "shortname": ":angel:",
+ "category": "people",
+ "emoji_order": "136",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "people",
+ "diversity",
+ "diversity",
+ "omg",
+ "omg"
+ ]
+ },
+ "princess": {
+ "unicode": "1f478",
+ "unicode_alternates": "",
+ "name": "princess",
+ "shortname": ":princess:",
+ "category": "people",
+ "emoji_order": "137",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "people",
+ "women",
+ "diversity",
+ "diversity",
+ "beautiful",
+ "beautiful",
+ "girls night",
+ "girls night"
+ ]
+ },
+ "bride_with_veil": {
+ "unicode": "1f470",
+ "unicode_alternates": "",
+ "name": "bride with veil",
+ "shortname": ":bride_with_veil:",
+ "category": "people",
+ "emoji_order": "138",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "people",
+ "wedding",
+ "women",
+ "diversity",
+ "diversity"
+ ]
+ },
+ "walking": {
+ "unicode": "1f6b6",
+ "unicode_alternates": "",
+ "name": "pedestrian",
+ "shortname": ":walking:",
+ "category": "people",
+ "emoji_order": "139",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "people",
+ "men",
+ "diversity",
+ "diversity"
+ ]
+ },
+ "runner": {
+ "unicode": "1f3c3",
+ "unicode_alternates": "",
+ "name": "runner",
+ "shortname": ":runner:",
+ "category": "people",
+ "emoji_order": "140",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "people",
+ "men",
+ "diversity",
+ "diversity",
+ "boys night",
+ "boys night",
+ "run",
+ "run"
+ ]
+ },
+ "dancer": {
+ "unicode": "1f483",
+ "unicode_alternates": "",
+ "name": "dancer",
+ "shortname": ":dancer:",
+ "category": "people",
+ "emoji_order": "141",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "people",
+ "women",
+ "sexy",
+ "diversity",
+ "diversity",
+ "girls night",
+ "girls night",
+ "dance",
+ "dance"
+ ]
+ },
+ "dancers": {
+ "unicode": "1f46f",
+ "unicode_alternates": "",
+ "name": "woman with bunny ears",
+ "shortname": ":dancers:",
+ "category": "people",
+ "emoji_order": "142",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "people",
+ "women",
+ "sexy",
+ "girls night",
+ "girls night",
+ "boys night",
+ "boys night",
+ "parties",
+ "parties",
+ "dance",
+ "dance"
+ ]
+ },
+ "couple": {
+ "unicode": "1f46b",
+ "unicode_alternates": "",
+ "name": "man and woman holding hands",
+ "shortname": ":couple:",
+ "category": "people",
+ "emoji_order": "143",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "people",
+ "sex",
+ "creationism",
+ "creationism"
+ ]
+ },
+ "two_men_holding_hands": {
+ "unicode": "1f46c",
+ "unicode_alternates": "",
+ "name": "two men holding hands",
+ "shortname": ":two_men_holding_hands:",
+ "category": "people",
+ "emoji_order": "144",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "people",
+ "gay",
+ "men",
+ "sex",
+ "lgbt",
+ "lgbt"
+ ]
+ },
+ "two_women_holding_hands": {
+ "unicode": "1f46d",
+ "unicode_alternates": "",
+ "name": "two women holding hands",
+ "shortname": ":two_women_holding_hands:",
+ "category": "people",
+ "emoji_order": "145",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "people",
+ "women",
+ "sex",
+ "lgbt",
+ "lgbt",
+ "lesbian",
+ "lesbian",
+ "girls night",
+ "girls night"
+ ]
+ },
+ "bow": {
+ "unicode": "1f647",
+ "unicode_alternates": "",
+ "name": "person bowing deeply",
+ "shortname": ":bow:",
+ "category": "people",
+ "emoji_order": "146",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "people",
+ "pray",
+ "pray",
+ "diversity",
+ "diversity"
+ ]
+ },
+ "information_desk_person": {
+ "unicode": "1f481",
+ "unicode_alternates": "",
+ "name": "information desk person",
+ "shortname": ":information_desk_person:",
+ "category": "people",
+ "emoji_order": "147",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "people",
+ "women",
+ "diversity",
+ "diversity"
+ ]
+ },
+ "no_good": {
+ "unicode": "1f645",
+ "unicode_alternates": "",
+ "name": "face with no good gesture",
+ "shortname": ":no_good:",
+ "category": "people",
+ "emoji_order": "148",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "people",
+ "women",
+ "diversity",
+ "diversity",
+ "girls night",
+ "girls night"
+ ]
+ },
+ "ok_woman": {
+ "unicode": "1f646",
+ "unicode_alternates": "",
+ "name": "face with ok gesture",
+ "shortname": ":ok_woman:",
+ "category": "people",
+ "emoji_order": "149",
+ "aliases": [],
+ "aliases_ascii": [
+ "*\\0/*",
+ "\\0/",
+ "*\\O/*",
+ "\\O/"
+ ],
+ "keywords": [
+ "people",
+ "women",
+ "diversity",
+ "diversity"
+ ]
+ },
+ "raising_hand": {
+ "unicode": "1f64b",
+ "unicode_alternates": "",
+ "name": "happy person raising one hand",
+ "shortname": ":raising_hand:",
+ "category": "people",
+ "emoji_order": "150",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "people",
+ "women",
+ "diversity",
+ "diversity"
+ ]
+ },
+ "person_with_pouting_face": {
+ "unicode": "1f64e",
+ "unicode_alternates": "",
+ "name": "person with pouting face",
+ "shortname": ":person_with_pouting_face:",
+ "category": "people",
+ "emoji_order": "151",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "people",
+ "women",
+ "diversity",
+ "diversity"
+ ]
+ },
+ "person_frowning": {
+ "unicode": "1f64d",
+ "unicode_alternates": "",
+ "name": "person frowning",
+ "shortname": ":person_frowning:",
+ "category": "people",
+ "emoji_order": "152",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "people",
+ "women",
+ "diversity",
+ "diversity"
+ ]
+ },
+ "haircut": {
+ "unicode": "1f487",
+ "unicode_alternates": "",
+ "name": "haircut",
+ "shortname": ":haircut:",
+ "category": "people",
+ "emoji_order": "153",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "people",
+ "women",
+ "diversity",
+ "diversity"
+ ]
+ },
+ "massage": {
+ "unicode": "1f486",
+ "unicode_alternates": "",
+ "name": "face massage",
+ "shortname": ":massage:",
+ "category": "people",
+ "emoji_order": "154",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "people",
+ "women",
+ "diversity",
+ "diversity"
+ ]
+ },
+ "couple_with_heart": {
+ "unicode": "1f491",
+ "unicode_alternates": "",
+ "name": "couple with heart",
+ "shortname": ":couple_with_heart:",
+ "category": "people",
+ "emoji_order": "155",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "people",
+ "love",
+ "sex"
+ ]
+ },
+ "couple_ww": {
+ "unicode": "1f469-2764-1f469",
+ "unicode_alternates": "1f469-200d-2764-fe0f-200d-1f469",
+ "name": "couple (woman,woman)",
+ "shortname": ":couple_ww:",
+ "category": "people",
+ "emoji_order": "156",
+ "aliases": [
+ ":couple_with_heart_ww:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "people",
+ "women",
+ "love",
+ "sex",
+ "lgbt",
+ "lgbt"
+ ]
+ },
+ "couple_mm": {
+ "unicode": "1f468-2764-1f468",
+ "unicode_alternates": "1f468-200d-2764-fe0f-200d-1f468",
+ "name": "couple (man,man)",
+ "shortname": ":couple_mm:",
+ "category": "people",
+ "emoji_order": "157",
+ "aliases": [
+ ":couple_with_heart_mm:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "people",
+ "gay",
+ "men",
+ "love",
+ "sex",
+ "lgbt",
+ "lgbt"
+ ]
+ },
+ "couplekiss": {
+ "unicode": "1f48f",
+ "unicode_alternates": "",
+ "name": "kiss",
+ "shortname": ":couplekiss:",
+ "category": "people",
+ "emoji_order": "158",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "people",
+ "love",
+ "sex"
+ ]
+ },
+ "kiss_ww": {
+ "unicode": "1f469-2764-1f48b-1f469",
+ "unicode_alternates": "1f469-200d-2764-fe0f-200d-1f48b-200d-1f469",
+ "name": "kiss (woman,woman)",
+ "shortname": ":kiss_ww:",
+ "category": "people",
+ "emoji_order": "159",
+ "aliases": [
+ ":couplekiss_ww:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "people",
+ "women",
+ "love",
+ "sex",
+ "lgbt",
+ "lgbt",
+ "lesbian",
+ "lesbian"
+ ]
+ },
+ "kiss_mm": {
+ "unicode": "1f468-2764-1f48b-1f468",
+ "unicode_alternates": "1f468-200d-2764-fe0f-200d-1f48b-200d-1f468",
+ "name": "kiss (man,man)",
+ "shortname": ":kiss_mm:",
+ "category": "people",
+ "emoji_order": "160",
+ "aliases": [
+ ":couplekiss_mm:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "people",
+ "gay",
+ "men",
+ "love",
+ "sex",
+ "lgbt",
+ "lgbt"
+ ]
+ },
+ "family": {
+ "unicode": "1f46a",
+ "unicode_alternates": "",
+ "name": "family",
+ "shortname": ":family:",
+ "category": "people",
+ "emoji_order": "161",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "people",
+ "family",
+ "baby"
+ ]
+ },
+ "family_mwg": {
+ "unicode": "1f468-1f469-1f467",
+ "unicode_alternates": "1f468-200d-1f469-200d-1f467",
+ "name": "family (man,woman,girl)",
+ "shortname": ":family_mwg:",
+ "category": "people",
+ "emoji_order": "162",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "people",
+ "family",
+ "baby"
+ ]
+ },
+ "family_mwgb": {
+ "unicode": "1f468-1f469-1f467-1f466",
+ "unicode_alternates": "1f468-200d-1f469-200d-1f467-200d-1f466",
+ "name": "family (man,woman,girl,boy)",
+ "shortname": ":family_mwgb:",
+ "category": "people",
+ "emoji_order": "163",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "people",
+ "family",
+ "baby"
+ ]
+ },
+ "family_mwbb": {
+ "unicode": "1f468-1f469-1f466-1f466",
+ "unicode_alternates": "1f468-200d-1f469-200d-1f466-200d-1f466",
+ "name": "family (man,woman,boy,boy)",
+ "shortname": ":family_mwbb:",
+ "category": "people",
+ "emoji_order": "164",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "people",
+ "family",
+ "baby"
+ ]
+ },
+ "family_mwgg": {
+ "unicode": "1f468-1f469-1f467-1f467",
+ "unicode_alternates": "1f468-200d-1f469-200d-1f467-200d-1f467",
+ "name": "family (man,woman,girl,girl)",
+ "shortname": ":family_mwgg:",
+ "category": "people",
+ "emoji_order": "165",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "people",
+ "family",
+ "baby"
+ ]
+ },
+ "family_wwb": {
+ "unicode": "1f469-1f469-1f466",
+ "unicode_alternates": "1f469-200d-1f469-200d-1f466",
+ "name": "family (woman,woman,boy)",
+ "shortname": ":family_wwb:",
+ "category": "people",
+ "emoji_order": "166",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "people",
+ "family",
+ "women",
+ "baby",
+ "lgbt",
+ "lgbt",
+ "lesbian",
+ "lesbian"
+ ]
+ },
+ "family_wwg": {
+ "unicode": "1f469-1f469-1f467",
+ "unicode_alternates": "1f469-200d-1f469-200d-1f467",
+ "name": "family (woman,woman,girl)",
+ "shortname": ":family_wwg:",
+ "category": "people",
+ "emoji_order": "167",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "people",
+ "family",
+ "women",
+ "baby",
+ "lgbt",
+ "lgbt",
+ "lesbian",
+ "lesbian"
+ ]
+ },
+ "family_wwgb": {
+ "unicode": "1f469-1f469-1f467-1f466",
+ "unicode_alternates": "1f469-200d-1f469-200d-1f467-200d-1f466",
+ "name": "family (woman,woman,girl,boy)",
+ "shortname": ":family_wwgb:",
+ "category": "people",
+ "emoji_order": "168",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "people",
+ "family",
+ "women",
+ "baby",
+ "lgbt",
+ "lgbt",
+ "lesbian",
+ "lesbian"
+ ]
+ },
+ "family_wwbb": {
+ "unicode": "1f469-1f469-1f466-1f466",
+ "unicode_alternates": "1f469-200d-1f469-200d-1f466-200d-1f466",
+ "name": "family (woman,woman,boy,boy)",
+ "shortname": ":family_wwbb:",
+ "category": "people",
+ "emoji_order": "169",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "people",
+ "family",
+ "women",
+ "baby",
+ "lgbt",
+ "lgbt",
+ "lesbian",
+ "lesbian"
+ ]
+ },
+ "family_wwgg": {
+ "unicode": "1f469-1f469-1f467-1f467",
+ "unicode_alternates": "1f469-200d-1f469-200d-1f467-200d-1f467",
+ "name": "family (woman,woman,girl,girl)",
+ "shortname": ":family_wwgg:",
+ "category": "people",
+ "emoji_order": "170",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "people",
+ "family",
+ "women",
+ "baby",
+ "lgbt",
+ "lgbt",
+ "lesbian",
+ "lesbian"
+ ]
+ },
+ "family_mmb": {
+ "unicode": "1f468-1f468-1f466",
+ "unicode_alternates": "1f468-200d-1f468-200d-1f466",
+ "name": "family (man,man,boy)",
+ "shortname": ":family_mmb:",
+ "category": "people",
+ "emoji_order": "171",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "people",
+ "gay",
+ "family",
+ "men",
+ "baby",
+ "lgbt",
+ "lgbt"
+ ]
+ },
+ "family_mmg": {
+ "unicode": "1f468-1f468-1f467",
+ "unicode_alternates": "1f468-200d-1f468-200d-1f467",
+ "name": "family (man,man,girl)",
+ "shortname": ":family_mmg:",
+ "category": "people",
+ "emoji_order": "172",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "people",
+ "gay",
+ "family",
+ "men",
+ "baby",
+ "lgbt",
+ "lgbt"
+ ]
+ },
+ "family_mmgb": {
+ "unicode": "1f468-1f468-1f467-1f466",
+ "unicode_alternates": "1f468-200d-1f468-200d-1f467-200d-1f466",
+ "name": "family (man,man,girl,boy)",
+ "shortname": ":family_mmgb:",
+ "category": "people",
+ "emoji_order": "173",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "people",
+ "gay",
+ "family",
+ "men",
+ "baby",
+ "lgbt",
+ "lgbt"
+ ]
+ },
+ "family_mmbb": {
+ "unicode": "1f468-1f468-1f466-1f466",
+ "unicode_alternates": "1f468-200d-1f468-200d-1f466-200d-1f466",
+ "name": "family (man,man,boy,boy)",
+ "shortname": ":family_mmbb:",
+ "category": "people",
+ "emoji_order": "174",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "people",
+ "gay",
+ "family",
+ "men",
+ "baby",
+ "lgbt",
+ "lgbt"
+ ]
+ },
+ "family_mmgg": {
+ "unicode": "1f468-1f468-1f467-1f467",
+ "unicode_alternates": "1f468-200d-1f468-200d-1f467-200d-1f467",
+ "name": "family (man,man,girl,girl)",
+ "shortname": ":family_mmgg:",
+ "category": "people",
+ "emoji_order": "175",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "people",
+ "gay",
+ "family",
+ "men",
+ "baby",
+ "lgbt",
+ "lgbt"
+ ]
+ },
+ "womans_clothes": {
+ "unicode": "1f45a",
+ "unicode_alternates": "",
+ "name": "womans clothes",
+ "shortname": ":womans_clothes:",
+ "category": "people",
+ "emoji_order": "176",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "women",
+ "fashion"
+ ]
+ },
+ "shirt": {
+ "unicode": "1f455",
+ "unicode_alternates": "",
+ "name": "t-shirt",
+ "shortname": ":shirt:",
+ "category": "people",
+ "emoji_order": "177",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "fashion"
+ ]
+ },
+ "jeans": {
+ "unicode": "1f456",
+ "unicode_alternates": "",
+ "name": "jeans",
+ "shortname": ":jeans:",
+ "category": "people",
+ "emoji_order": "178",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "fashion"
+ ]
+ },
+ "necktie": {
+ "unicode": "1f454",
+ "unicode_alternates": "",
+ "name": "necktie",
+ "shortname": ":necktie:",
+ "category": "people",
+ "emoji_order": "179",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "fashion"
+ ]
+ },
+ "dress": {
+ "unicode": "1f457",
+ "unicode_alternates": "",
+ "name": "dress",
+ "shortname": ":dress:",
+ "category": "people",
+ "emoji_order": "180",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "women",
+ "fashion",
+ "sexy",
+ "girls night",
+ "girls night"
+ ]
+ },
+ "bikini": {
+ "unicode": "1f459",
+ "unicode_alternates": "",
+ "name": "bikini",
+ "shortname": ":bikini:",
+ "category": "people",
+ "emoji_order": "181",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "women",
+ "fashion",
+ "sexy",
+ "vacation",
+ "tropical",
+ "swim"
+ ]
+ },
+ "kimono": {
+ "unicode": "1f458",
+ "unicode_alternates": "",
+ "name": "kimono",
+ "shortname": ":kimono:",
+ "category": "people",
+ "emoji_order": "182",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "fashion"
+ ]
+ },
+ "lipstick": {
+ "unicode": "1f484",
+ "unicode_alternates": "",
+ "name": "lipstick",
+ "shortname": ":lipstick:",
+ "category": "people",
+ "emoji_order": "183",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "women",
+ "fashion",
+ "sexy",
+ "lip"
+ ]
+ },
+ "kiss": {
+ "unicode": "1f48b",
+ "unicode_alternates": "",
+ "name": "kiss mark",
+ "shortname": ":kiss:",
+ "category": "people",
+ "emoji_order": "184",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "women",
+ "love",
+ "sexy",
+ "lip",
+ "beautiful",
+ "beautiful",
+ "girls night",
+ "girls night"
+ ]
+ },
+ "footprints": {
+ "unicode": "1f463",
+ "unicode_alternates": "",
+ "name": "footprints",
+ "shortname": ":footprints:",
+ "category": "people",
+ "emoji_order": "185",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "high_heel": {
+ "unicode": "1f460",
+ "unicode_alternates": "",
+ "name": "high-heeled shoe",
+ "shortname": ":high_heel:",
+ "category": "people",
+ "emoji_order": "186",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "women",
+ "fashion",
+ "shoe",
+ "sexy",
+ "accessories",
+ "girls night",
+ "girls night"
+ ]
+ },
+ "sandal": {
+ "unicode": "1f461",
+ "unicode_alternates": "",
+ "name": "womans sandal",
+ "shortname": ":sandal:",
+ "category": "people",
+ "emoji_order": "187",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "fashion",
+ "shoe",
+ "accessories"
+ ]
+ },
+ "boot": {
+ "unicode": "1f462",
+ "unicode_alternates": "",
+ "name": "womans boots",
+ "shortname": ":boot:",
+ "category": "people",
+ "emoji_order": "188",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "women",
+ "fashion",
+ "shoe",
+ "sexy",
+ "accessories"
+ ]
+ },
+ "mans_shoe": {
+ "unicode": "1f45e",
+ "unicode_alternates": "",
+ "name": "mans shoe",
+ "shortname": ":mans_shoe:",
+ "category": "people",
+ "emoji_order": "189",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "fashion",
+ "shoe",
+ "accessories"
+ ]
+ },
+ "athletic_shoe": {
+ "unicode": "1f45f",
+ "unicode_alternates": "",
+ "name": "athletic shoe",
+ "shortname": ":athletic_shoe:",
+ "category": "people",
+ "emoji_order": "190",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "fashion",
+ "shoe",
+ "accessories",
+ "boys night",
+ "boys night"
+ ]
+ },
+ "womans_hat": {
+ "unicode": "1f452",
+ "unicode_alternates": "",
+ "name": "womans hat",
+ "shortname": ":womans_hat:",
+ "category": "people",
+ "emoji_order": "191",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "women",
+ "fashion",
+ "accessories"
+ ]
+ },
+ "tophat": {
+ "unicode": "1f3a9",
+ "unicode_alternates": "",
+ "name": "top hat",
+ "shortname": ":tophat:",
+ "category": "people",
+ "emoji_order": "192",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "hat",
+ "fashion",
+ "accessories"
+ ]
+ },
+ "helmet_with_cross": {
+ "unicode": "26d1",
+ "unicode_alternates": "26d1-fe0f",
+ "name": "helmet with white cross",
+ "shortname": ":helmet_with_cross:",
+ "category": "people",
+ "emoji_order": "193",
+ "aliases": [
+ ":helmet_with_white_cross:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "hat",
+ "accessories",
+ "job",
+ "job"
+ ]
+ },
+ "mortar_board": {
+ "unicode": "1f393",
+ "unicode_alternates": "",
+ "name": "graduation cap",
+ "shortname": ":mortar_board:",
+ "category": "people",
+ "emoji_order": "194",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "hat",
+ "office",
+ "accessories"
+ ]
+ },
+ "crown": {
+ "unicode": "1f451",
+ "unicode_alternates": "",
+ "name": "crown",
+ "shortname": ":crown:",
+ "category": "people",
+ "emoji_order": "195",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "gem",
+ "accessories"
+ ]
+ },
+ "school_satchel": {
+ "unicode": "1f392",
+ "unicode_alternates": "",
+ "name": "school satchel",
+ "shortname": ":school_satchel:",
+ "category": "people",
+ "emoji_order": "196",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "bag",
+ "fashion",
+ "office",
+ "vacation",
+ "accessories"
+ ]
+ },
+ "pouch": {
+ "unicode": "1f45d",
+ "unicode_alternates": "",
+ "name": "pouch",
+ "shortname": ":pouch:",
+ "category": "people",
+ "emoji_order": "197",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "bag",
+ "women",
+ "fashion",
+ "accessories"
+ ]
+ },
+ "purse": {
+ "unicode": "1f45b",
+ "unicode_alternates": "",
+ "name": "purse",
+ "shortname": ":purse:",
+ "category": "people",
+ "emoji_order": "198",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "bag",
+ "women",
+ "fashion",
+ "accessories",
+ "money",
+ "money"
+ ]
+ },
+ "handbag": {
+ "unicode": "1f45c",
+ "unicode_alternates": "",
+ "name": "handbag",
+ "shortname": ":handbag:",
+ "category": "people",
+ "emoji_order": "199",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "bag",
+ "women",
+ "fashion",
+ "vacation",
+ "accessories"
+ ]
+ },
+ "briefcase": {
+ "unicode": "1f4bc",
+ "unicode_alternates": "",
+ "name": "briefcase",
+ "shortname": ":briefcase:",
+ "category": "people",
+ "emoji_order": "200",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "bag",
+ "work",
+ "accessories",
+ "nutcase",
+ "nutcase",
+ "job",
+ "job"
+ ]
+ },
+ "eyeglasses": {
+ "unicode": "1f453",
+ "unicode_alternates": "",
+ "name": "eyeglasses",
+ "shortname": ":eyeglasses:",
+ "category": "people",
+ "emoji_order": "201",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "fashion",
+ "glasses",
+ "accessories"
+ ]
+ },
+ "dark_sunglasses": {
+ "unicode": "1f576",
+ "unicode_alternates": "1f576-fe0f",
+ "name": "dark sunglasses",
+ "shortname": ":dark_sunglasses:",
+ "category": "people",
+ "emoji_order": "202",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "fashion",
+ "glasses",
+ "accessories"
+ ]
+ },
+ "ring": {
+ "unicode": "1f48d",
+ "unicode_alternates": "",
+ "name": "ring",
+ "shortname": ":ring:",
+ "category": "people",
+ "emoji_order": "203",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "wedding",
+ "object",
+ "fashion",
+ "gem",
+ "accessories"
+ ]
+ },
+ "closed_umbrella": {
+ "unicode": "1f302",
+ "unicode_alternates": "",
+ "name": "closed umbrella",
+ "shortname": ":closed_umbrella:",
+ "category": "people",
+ "emoji_order": "204",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "sky",
+ "rain",
+ "accessories"
+ ]
+ },
+ "dog": {
+ "unicode": "1f436",
+ "unicode_alternates": "",
+ "name": "dog face",
+ "shortname": ":dog:",
+ "category": "nature",
+ "emoji_order": "205",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "dog",
+ "dog",
+ "pug",
+ "pug",
+ "animal",
+ "animal"
+ ]
+ },
+ "cat": {
+ "unicode": "1f431",
+ "unicode_alternates": "",
+ "name": "cat face",
+ "shortname": ":cat:",
+ "category": "nature",
+ "emoji_order": "206",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "halloween",
+ "vagina",
+ "cat",
+ "cat",
+ "animal",
+ "animal"
+ ]
+ },
+ "mouse": {
+ "unicode": "1f42d",
+ "unicode_alternates": "",
+ "name": "mouse face",
+ "shortname": ":mouse:",
+ "category": "nature",
+ "emoji_order": "207",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "animal",
+ "animal"
+ ]
+ },
+ "hamster": {
+ "unicode": "1f439",
+ "unicode_alternates": "",
+ "name": "hamster face",
+ "shortname": ":hamster:",
+ "category": "nature",
+ "emoji_order": "208",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "animal",
+ "animal"
+ ]
+ },
+ "rabbit": {
+ "unicode": "1f430",
+ "unicode_alternates": "",
+ "name": "rabbit face",
+ "shortname": ":rabbit:",
+ "category": "nature",
+ "emoji_order": "209",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "wildlife",
+ "animal",
+ "animal"
+ ]
+ },
+ "bear": {
+ "unicode": "1f43b",
+ "unicode_alternates": "",
+ "name": "bear face",
+ "shortname": ":bear:",
+ "category": "nature",
+ "emoji_order": "210",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "wildlife",
+ "roar",
+ "animal",
+ "animal"
+ ]
+ },
+ "panda_face": {
+ "unicode": "1f43c",
+ "unicode_alternates": "",
+ "name": "panda face",
+ "shortname": ":panda_face:",
+ "category": "nature",
+ "emoji_order": "211",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "wildlife",
+ "roar",
+ "animal",
+ "animal"
+ ]
+ },
+ "koala": {
+ "unicode": "1f428",
+ "unicode_alternates": "",
+ "name": "koala",
+ "shortname": ":koala:",
+ "category": "nature",
+ "emoji_order": "212",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "wildlife",
+ "animal",
+ "animal"
+ ]
+ },
+ "tiger": {
+ "unicode": "1f42f",
+ "unicode_alternates": "",
+ "name": "tiger face",
+ "shortname": ":tiger:",
+ "category": "nature",
+ "emoji_order": "213",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "wildlife",
+ "roar",
+ "cat",
+ "cat",
+ "animal",
+ "animal"
+ ]
+ },
+ "lion_face": {
+ "unicode": "1f981",
+ "unicode_alternates": "",
+ "name": "lion face",
+ "shortname": ":lion_face:",
+ "category": "nature",
+ "emoji_order": "214",
+ "aliases": [
+ ":lion:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "wildlife",
+ "roar",
+ "cat",
+ "cat",
+ "animal",
+ "animal"
+ ]
+ },
+ "cow": {
+ "unicode": "1f42e",
+ "unicode_alternates": "",
+ "name": "cow face",
+ "shortname": ":cow:",
+ "category": "nature",
+ "emoji_order": "215",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "animal",
+ "animal"
+ ]
+ },
+ "pig": {
+ "unicode": "1f437",
+ "unicode_alternates": "",
+ "name": "pig face",
+ "shortname": ":pig:",
+ "category": "nature",
+ "emoji_order": "216",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "animal",
+ "animal"
+ ]
+ },
+ "pig_nose": {
+ "unicode": "1f43d",
+ "unicode_alternates": "",
+ "name": "pig nose",
+ "shortname": ":pig_nose:",
+ "category": "nature",
+ "emoji_order": "217",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "animal",
+ "animal"
+ ]
+ },
+ "frog": {
+ "unicode": "1f438",
+ "unicode_alternates": "",
+ "name": "frog face",
+ "shortname": ":frog:",
+ "category": "nature",
+ "emoji_order": "218",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "wildlife",
+ "animal",
+ "animal"
+ ]
+ },
+ "octopus": {
+ "unicode": "1f419",
+ "unicode_alternates": "",
+ "name": "octopus",
+ "shortname": ":octopus:",
+ "category": "nature",
+ "emoji_order": "219",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "wildlife",
+ "animal",
+ "animal"
+ ]
+ },
+ "monkey_face": {
+ "unicode": "1f435",
+ "unicode_alternates": "",
+ "name": "monkey face",
+ "shortname": ":monkey_face:",
+ "category": "nature",
+ "emoji_order": "220",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "animal",
+ "animal"
+ ]
+ },
+ "see_no_evil": {
+ "unicode": "1f648",
+ "unicode_alternates": "",
+ "name": "see-no-evil monkey",
+ "shortname": ":see_no_evil:",
+ "category": "nature",
+ "emoji_order": "221",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "animal",
+ "animal"
+ ]
+ },
+ "hear_no_evil": {
+ "unicode": "1f649",
+ "unicode_alternates": "",
+ "name": "hear-no-evil monkey",
+ "shortname": ":hear_no_evil:",
+ "category": "nature",
+ "emoji_order": "222",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "animal",
+ "animal"
+ ]
+ },
+ "speak_no_evil": {
+ "unicode": "1f64a",
+ "unicode_alternates": "",
+ "name": "speak-no-evil monkey",
+ "shortname": ":speak_no_evil:",
+ "category": "nature",
+ "emoji_order": "223",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "animal",
+ "animal"
+ ]
+ },
+ "monkey": {
+ "unicode": "1f412",
+ "unicode_alternates": "",
+ "name": "monkey",
+ "shortname": ":monkey:",
+ "category": "nature",
+ "emoji_order": "224",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "wildlife",
+ "animal",
+ "animal"
+ ]
+ },
+ "chicken": {
+ "unicode": "1f414",
+ "unicode_alternates": "",
+ "name": "chicken",
+ "shortname": ":chicken:",
+ "category": "nature",
+ "emoji_order": "225",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "animal",
+ "animal",
+ "chicken",
+ "chicken"
+ ]
+ },
+ "penguin": {
+ "unicode": "1f427",
+ "unicode_alternates": "",
+ "name": "penguin",
+ "shortname": ":penguin:",
+ "category": "nature",
+ "emoji_order": "226",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "wildlife",
+ "animal",
+ "animal"
+ ]
+ },
+ "bird": {
+ "unicode": "1f426",
+ "unicode_alternates": "",
+ "name": "bird",
+ "shortname": ":bird:",
+ "category": "nature",
+ "emoji_order": "227",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "wildlife",
+ "animal",
+ "animal"
+ ]
+ },
+ "baby_chick": {
+ "unicode": "1f424",
+ "unicode_alternates": "",
+ "name": "baby chick",
+ "shortname": ":baby_chick:",
+ "category": "nature",
+ "emoji_order": "228",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "animal",
+ "animal",
+ "chicken",
+ "chicken"
+ ]
+ },
+ "hatching_chick": {
+ "unicode": "1f423",
+ "unicode_alternates": "",
+ "name": "hatching chick",
+ "shortname": ":hatching_chick:",
+ "category": "nature",
+ "emoji_order": "229",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "animal",
+ "animal",
+ "chicken",
+ "chicken"
+ ]
+ },
+ "hatched_chick": {
+ "unicode": "1f425",
+ "unicode_alternates": "",
+ "name": "front-facing baby chick",
+ "shortname": ":hatched_chick:",
+ "category": "nature",
+ "emoji_order": "230",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "animal",
+ "animal",
+ "chicken",
+ "chicken"
+ ]
+ },
+ "wolf": {
+ "unicode": "1f43a",
+ "unicode_alternates": "",
+ "name": "wolf face",
+ "shortname": ":wolf:",
+ "category": "nature",
+ "emoji_order": "231",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "wildlife",
+ "roar",
+ "animal",
+ "animal"
+ ]
+ },
+ "boar": {
+ "unicode": "1f417",
+ "unicode_alternates": "",
+ "name": "boar",
+ "shortname": ":boar:",
+ "category": "nature",
+ "emoji_order": "232",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "wildlife",
+ "animal",
+ "animal"
+ ]
+ },
+ "horse": {
+ "unicode": "1f434",
+ "unicode_alternates": "",
+ "name": "horse face",
+ "shortname": ":horse:",
+ "category": "nature",
+ "emoji_order": "233",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "wildlife",
+ "animal",
+ "animal"
+ ]
+ },
+ "unicorn": {
+ "unicode": "1f984",
+ "unicode_alternates": "",
+ "name": "unicorn face",
+ "shortname": ":unicorn:",
+ "category": "nature",
+ "emoji_order": "234",
+ "aliases": [
+ ":unicorn_face:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "animal",
+ "animal"
+ ]
+ },
+ "bee": {
+ "unicode": "1f41d",
+ "unicode_alternates": "",
+ "name": "honeybee",
+ "shortname": ":bee:",
+ "category": "nature",
+ "emoji_order": "235",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "insects",
+ "animal",
+ "animal"
+ ]
+ },
+ "bug": {
+ "unicode": "1f41b",
+ "unicode_alternates": "",
+ "name": "bug",
+ "shortname": ":bug:",
+ "category": "nature",
+ "emoji_order": "236",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "insects",
+ "animal",
+ "animal"
+ ]
+ },
+ "snail": {
+ "unicode": "1f40c",
+ "unicode_alternates": "",
+ "name": "snail",
+ "shortname": ":snail:",
+ "category": "nature",
+ "emoji_order": "237",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "insects",
+ "animal",
+ "animal"
+ ]
+ },
+ "beetle": {
+ "unicode": "1f41e",
+ "unicode_alternates": "",
+ "name": "lady beetle",
+ "shortname": ":beetle:",
+ "category": "nature",
+ "emoji_order": "238",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "insects",
+ "animal",
+ "animal"
+ ]
+ },
+ "ant": {
+ "unicode": "1f41c",
+ "unicode_alternates": "",
+ "name": "ant",
+ "shortname": ":ant:",
+ "category": "nature",
+ "emoji_order": "239",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "insects",
+ "animal",
+ "animal"
+ ]
+ },
+ "spider": {
+ "unicode": "1f577",
+ "unicode_alternates": "1f577-fe0f",
+ "name": "spider",
+ "shortname": ":spider:",
+ "category": "nature",
+ "emoji_order": "240",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "insects",
+ "halloween",
+ "animal",
+ "animal"
+ ]
+ },
+ "scorpion": {
+ "unicode": "1f982",
+ "unicode_alternates": "",
+ "name": "scorpion",
+ "shortname": ":scorpion:",
+ "category": "nature",
+ "emoji_order": "241",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "insects",
+ "reptile",
+ "reptile",
+ "animal",
+ "animal"
+ ]
+ },
+ "crab": {
+ "unicode": "1f980",
+ "unicode_alternates": "",
+ "name": "crab",
+ "shortname": ":crab:",
+ "category": "nature",
+ "emoji_order": "242",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "tropical",
+ "animal",
+ "animal"
+ ]
+ },
+ "snake": {
+ "unicode": "1f40d",
+ "unicode_alternates": "",
+ "name": "snake",
+ "shortname": ":snake:",
+ "category": "nature",
+ "emoji_order": "243",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "wildlife",
+ "reptile",
+ "reptile",
+ "animal",
+ "animal",
+ "creationism",
+ "creationism"
+ ]
+ },
+ "turtle": {
+ "unicode": "1f422",
+ "unicode_alternates": "",
+ "name": "turtle",
+ "shortname": ":turtle:",
+ "category": "nature",
+ "emoji_order": "244",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "wildlife",
+ "reptile",
+ "reptile",
+ "animal",
+ "animal"
+ ]
+ },
+ "tropical_fish": {
+ "unicode": "1f420",
+ "unicode_alternates": "",
+ "name": "tropical fish",
+ "shortname": ":tropical_fish:",
+ "category": "nature",
+ "emoji_order": "245",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "wildlife",
+ "animal",
+ "animal"
+ ]
+ },
+ "fish": {
+ "unicode": "1f41f",
+ "unicode_alternates": "",
+ "name": "fish",
+ "shortname": ":fish:",
+ "category": "nature",
+ "emoji_order": "246",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "wildlife",
+ "animal",
+ "animal"
+ ]
+ },
+ "blowfish": {
+ "unicode": "1f421",
+ "unicode_alternates": "",
+ "name": "blowfish",
+ "shortname": ":blowfish:",
+ "category": "nature",
+ "emoji_order": "247",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "wildlife",
+ "animal",
+ "animal"
+ ]
+ },
+ "dolphin": {
+ "unicode": "1f42c",
+ "unicode_alternates": "",
+ "name": "dolphin",
+ "shortname": ":dolphin:",
+ "category": "nature",
+ "emoji_order": "248",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "wildlife",
+ "tropical",
+ "animal",
+ "animal"
+ ]
+ },
+ "whale": {
+ "unicode": "1f433",
+ "unicode_alternates": "",
+ "name": "spouting whale",
+ "shortname": ":whale:",
+ "category": "nature",
+ "emoji_order": "249",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "wildlife",
+ "tropical",
+ "whales",
+ "whales",
+ "animal",
+ "animal"
+ ]
+ },
+ "whale2": {
+ "unicode": "1f40b",
+ "unicode_alternates": "",
+ "name": "whale",
+ "shortname": ":whale2:",
+ "category": "nature",
+ "emoji_order": "250",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "wildlife",
+ "tropical",
+ "whales",
+ "whales",
+ "animal",
+ "animal"
+ ]
+ },
+ "crocodile": {
+ "unicode": "1f40a",
+ "unicode_alternates": "",
+ "name": "crocodile",
+ "shortname": ":crocodile:",
+ "category": "nature",
+ "emoji_order": "251",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "wildlife",
+ "reptile",
+ "reptile",
+ "animal",
+ "animal"
+ ]
+ },
+ "leopard": {
+ "unicode": "1f406",
+ "unicode_alternates": "",
+ "name": "leopard",
+ "shortname": ":leopard:",
+ "category": "nature",
+ "emoji_order": "252",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "wildlife",
+ "roar",
+ "animal",
+ "animal"
+ ]
+ },
+ "tiger2": {
+ "unicode": "1f405",
+ "unicode_alternates": "",
+ "name": "tiger",
+ "shortname": ":tiger2:",
+ "category": "nature",
+ "emoji_order": "253",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "wildlife",
+ "roar",
+ "animal",
+ "animal"
+ ]
+ },
+ "water_buffalo": {
+ "unicode": "1f403",
+ "unicode_alternates": "",
+ "name": "water buffalo",
+ "shortname": ":water_buffalo:",
+ "category": "nature",
+ "emoji_order": "254",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "wildlife",
+ "animal",
+ "animal"
+ ]
+ },
+ "ox": {
+ "unicode": "1f402",
+ "unicode_alternates": "",
+ "name": "ox",
+ "shortname": ":ox:",
+ "category": "nature",
+ "emoji_order": "255",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "animal",
+ "animal"
+ ]
+ },
+ "cow2": {
+ "unicode": "1f404",
+ "unicode_alternates": "",
+ "name": "cow",
+ "shortname": ":cow2:",
+ "category": "nature",
+ "emoji_order": "256",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "animal",
+ "animal"
+ ]
+ },
+ "dromedary_camel": {
+ "unicode": "1f42a",
+ "unicode_alternates": "",
+ "name": "dromedary camel",
+ "shortname": ":dromedary_camel:",
+ "category": "nature",
+ "emoji_order": "257",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "wildlife",
+ "animal",
+ "animal"
+ ]
+ },
+ "camel": {
+ "unicode": "1f42b",
+ "unicode_alternates": "",
+ "name": "bactrian camel",
+ "shortname": ":camel:",
+ "category": "nature",
+ "emoji_order": "258",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "wildlife",
+ "animal",
+ "animal",
+ "hump day",
+ "hump day"
+ ]
+ },
+ "elephant": {
+ "unicode": "1f418",
+ "unicode_alternates": "",
+ "name": "elephant",
+ "shortname": ":elephant:",
+ "category": "nature",
+ "emoji_order": "259",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "wildlife",
+ "animal",
+ "animal"
+ ]
+ },
+ "goat": {
+ "unicode": "1f410",
+ "unicode_alternates": "",
+ "name": "goat",
+ "shortname": ":goat:",
+ "category": "nature",
+ "emoji_order": "260",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "animal",
+ "animal"
+ ]
+ },
+ "ram": {
+ "unicode": "1f40f",
+ "unicode_alternates": "",
+ "name": "ram",
+ "shortname": ":ram:",
+ "category": "nature",
+ "emoji_order": "261",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "wildlife",
+ "animal",
+ "animal"
+ ]
+ },
+ "sheep": {
+ "unicode": "1f411",
+ "unicode_alternates": "",
+ "name": "sheep",
+ "shortname": ":sheep:",
+ "category": "nature",
+ "emoji_order": "262",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "animal",
+ "animal"
+ ]
+ },
+ "racehorse": {
+ "unicode": "1f40e",
+ "unicode_alternates": "",
+ "name": "horse",
+ "shortname": ":racehorse:",
+ "category": "nature",
+ "emoji_order": "263",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "wildlife",
+ "animal",
+ "animal"
+ ]
+ },
+ "pig2": {
+ "unicode": "1f416",
+ "unicode_alternates": "",
+ "name": "pig",
+ "shortname": ":pig2:",
+ "category": "nature",
+ "emoji_order": "264",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "animal",
+ "animal"
+ ]
+ },
+ "rat": {
+ "unicode": "1f400",
+ "unicode_alternates": "",
+ "name": "rat",
+ "shortname": ":rat:",
+ "category": "nature",
+ "emoji_order": "265",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "animal",
+ "animal"
+ ]
+ },
+ "mouse2": {
+ "unicode": "1f401",
+ "unicode_alternates": "",
+ "name": "mouse",
+ "shortname": ":mouse2:",
+ "category": "nature",
+ "emoji_order": "266",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "animal",
+ "animal"
+ ]
+ },
+ "rooster": {
+ "unicode": "1f413",
+ "unicode_alternates": "",
+ "name": "rooster",
+ "shortname": ":rooster:",
+ "category": "nature",
+ "emoji_order": "267",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "animal",
+ "animal"
+ ]
+ },
+ "turkey": {
+ "unicode": "1f983",
+ "unicode_alternates": "",
+ "name": "turkey",
+ "shortname": ":turkey:",
+ "category": "nature",
+ "emoji_order": "268",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "wildlife",
+ "animal",
+ "animal"
+ ]
+ },
+ "dove": {
+ "unicode": "1f54a",
+ "unicode_alternates": "1f54a-fe0f",
+ "name": "dove of peace",
+ "shortname": ":dove:",
+ "category": "nature",
+ "emoji_order": "269",
+ "aliases": [
+ ":dove_of_peace:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "animal",
+ "animal"
+ ]
+ },
+ "dog2": {
+ "unicode": "1f415",
+ "unicode_alternates": "",
+ "name": "dog",
+ "shortname": ":dog2:",
+ "category": "nature",
+ "emoji_order": "270",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "dog",
+ "dog",
+ "pug",
+ "pug",
+ "animal",
+ "animal"
+ ]
+ },
+ "poodle": {
+ "unicode": "1f429",
+ "unicode_alternates": "",
+ "name": "poodle",
+ "shortname": ":poodle:",
+ "category": "nature",
+ "emoji_order": "271",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "dog",
+ "dog",
+ "animal",
+ "animal"
+ ]
+ },
+ "cat2": {
+ "unicode": "1f408",
+ "unicode_alternates": "",
+ "name": "cat",
+ "shortname": ":cat2:",
+ "category": "nature",
+ "emoji_order": "272",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "halloween",
+ "cat",
+ "cat",
+ "animal",
+ "animal"
+ ]
+ },
+ "rabbit2": {
+ "unicode": "1f407",
+ "unicode_alternates": "",
+ "name": "rabbit",
+ "shortname": ":rabbit2:",
+ "category": "nature",
+ "emoji_order": "273",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "wildlife",
+ "animal",
+ "animal"
+ ]
+ },
+ "chipmunk": {
+ "unicode": "1f43f",
+ "unicode_alternates": "1f43f-fe0f",
+ "name": "chipmunk",
+ "shortname": ":chipmunk:",
+ "category": "nature",
+ "emoji_order": "274",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "wildlife",
+ "animal",
+ "animal"
+ ]
+ },
+ "feet": {
+ "unicode": "1f43e",
+ "unicode_alternates": "",
+ "name": "paw prints",
+ "shortname": ":feet:",
+ "category": "nature",
+ "emoji_order": "275",
+ "aliases": [
+ ":paw_prints:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "animal",
+ "animal"
+ ]
+ },
+ "dragon": {
+ "unicode": "1f409",
+ "unicode_alternates": "",
+ "name": "dragon",
+ "shortname": ":dragon:",
+ "category": "nature",
+ "emoji_order": "276",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "roar",
+ "reptile",
+ "reptile",
+ "animal",
+ "animal"
+ ]
+ },
+ "dragon_face": {
+ "unicode": "1f432",
+ "unicode_alternates": "",
+ "name": "dragon face",
+ "shortname": ":dragon_face:",
+ "category": "nature",
+ "emoji_order": "277",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "roar",
+ "monster",
+ "reptile",
+ "reptile",
+ "animal",
+ "animal"
+ ]
+ },
+ "cactus": {
+ "unicode": "1f335",
+ "unicode_alternates": "",
+ "name": "cactus",
+ "shortname": ":cactus:",
+ "category": "nature",
+ "emoji_order": "278",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "nature",
+ "plant",
+ "trees",
+ "trees"
+ ]
+ },
+ "christmas_tree": {
+ "unicode": "1f384",
+ "unicode_alternates": "",
+ "name": "christmas tree",
+ "shortname": ":christmas_tree:",
+ "category": "nature",
+ "emoji_order": "279",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "plant",
+ "holidays",
+ "christmas",
+ "trees",
+ "trees"
+ ]
+ },
+ "evergreen_tree": {
+ "unicode": "1f332",
+ "unicode_alternates": "",
+ "name": "evergreen tree",
+ "shortname": ":evergreen_tree:",
+ "category": "nature",
+ "emoji_order": "280",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "nature",
+ "plant",
+ "holidays",
+ "christmas",
+ "camp",
+ "trees",
+ "trees"
+ ]
+ },
+ "deciduous_tree": {
+ "unicode": "1f333",
+ "unicode_alternates": "",
+ "name": "deciduous tree",
+ "shortname": ":deciduous_tree:",
+ "category": "nature",
+ "emoji_order": "281",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "nature",
+ "plant",
+ "camp",
+ "trees",
+ "trees"
+ ]
+ },
+ "palm_tree": {
+ "unicode": "1f334",
+ "unicode_alternates": "",
+ "name": "palm tree",
+ "shortname": ":palm_tree:",
+ "category": "nature",
+ "emoji_order": "282",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "nature",
+ "plant",
+ "tropical",
+ "trees",
+ "trees"
+ ]
+ },
+ "seedling": {
+ "unicode": "1f331",
+ "unicode_alternates": "",
+ "name": "seedling",
+ "shortname": ":seedling:",
+ "category": "nature",
+ "emoji_order": "283",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "nature",
+ "plant",
+ "leaf",
+ "leaf"
+ ]
+ },
+ "herb": {
+ "unicode": "1f33f",
+ "unicode_alternates": "",
+ "name": "herb",
+ "shortname": ":herb:",
+ "category": "nature",
+ "emoji_order": "284",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "nature",
+ "plant",
+ "leaf",
+ "leaf"
+ ]
+ },
+ "shamrock": {
+ "unicode": "2618",
+ "unicode_alternates": "2618-fe0f",
+ "name": "shamrock",
+ "shortname": ":shamrock:",
+ "category": "nature",
+ "emoji_order": "285",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "nature",
+ "plant",
+ "luck",
+ "leaf",
+ "leaf"
+ ]
+ },
+ "four_leaf_clover": {
+ "unicode": "1f340",
+ "unicode_alternates": "",
+ "name": "four leaf clover",
+ "shortname": ":four_leaf_clover:",
+ "category": "nature",
+ "emoji_order": "286",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "nature",
+ "plant",
+ "luck",
+ "leaf",
+ "leaf",
+ "sol",
+ "sol"
+ ]
+ },
+ "bamboo": {
+ "unicode": "1f38d",
+ "unicode_alternates": "",
+ "name": "pine decoration",
+ "shortname": ":bamboo:",
+ "category": "nature",
+ "emoji_order": "287",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "nature",
+ "plant"
+ ]
+ },
+ "tanabata_tree": {
+ "unicode": "1f38b",
+ "unicode_alternates": "",
+ "name": "tanabata tree",
+ "shortname": ":tanabata_tree:",
+ "category": "nature",
+ "emoji_order": "288",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "nature",
+ "plant",
+ "trees",
+ "trees"
+ ]
+ },
+ "leaves": {
+ "unicode": "1f343",
+ "unicode_alternates": "",
+ "name": "leaf fluttering in wind",
+ "shortname": ":leaves:",
+ "category": "nature",
+ "emoji_order": "289",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "nature",
+ "plant",
+ "leaf",
+ "leaf"
+ ]
+ },
+ "fallen_leaf": {
+ "unicode": "1f342",
+ "unicode_alternates": "",
+ "name": "fallen leaf",
+ "shortname": ":fallen_leaf:",
+ "category": "nature",
+ "emoji_order": "290",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "nature",
+ "plant",
+ "leaf",
+ "leaf"
+ ]
+ },
+ "maple_leaf": {
+ "unicode": "1f341",
+ "unicode_alternates": "",
+ "name": "maple leaf",
+ "shortname": ":maple_leaf:",
+ "category": "nature",
+ "emoji_order": "291",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "nature",
+ "plant",
+ "leaf",
+ "leaf"
+ ]
+ },
+ "ear_of_rice": {
+ "unicode": "1f33e",
+ "unicode_alternates": "",
+ "name": "ear of rice",
+ "shortname": ":ear_of_rice:",
+ "category": "nature",
+ "emoji_order": "292",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "nature",
+ "plant",
+ "leaf",
+ "leaf"
+ ]
+ },
+ "hibiscus": {
+ "unicode": "1f33a",
+ "unicode_alternates": "",
+ "name": "hibiscus",
+ "shortname": ":hibiscus:",
+ "category": "nature",
+ "emoji_order": "293",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "nature",
+ "flower",
+ "plant",
+ "tropical"
+ ]
+ },
+ "sunflower": {
+ "unicode": "1f33b",
+ "unicode_alternates": "",
+ "name": "sunflower",
+ "shortname": ":sunflower:",
+ "category": "nature",
+ "emoji_order": "294",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "nature",
+ "flower",
+ "plant"
+ ]
+ },
+ "rose": {
+ "unicode": "1f339",
+ "unicode_alternates": "",
+ "name": "rose",
+ "shortname": ":rose:",
+ "category": "nature",
+ "emoji_order": "295",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "nature",
+ "flower",
+ "plant",
+ "rip",
+ "rip",
+ "condolence",
+ "condolence",
+ "beautiful",
+ "beautiful"
+ ]
+ },
+ "tulip": {
+ "unicode": "1f337",
+ "unicode_alternates": "",
+ "name": "tulip",
+ "shortname": ":tulip:",
+ "category": "nature",
+ "emoji_order": "296",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "nature",
+ "flower",
+ "plant",
+ "vagina",
+ "girls night",
+ "girls night"
+ ]
+ },
+ "blossom": {
+ "unicode": "1f33c",
+ "unicode_alternates": "",
+ "name": "blossom",
+ "shortname": ":blossom:",
+ "category": "nature",
+ "emoji_order": "297",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "nature",
+ "flower",
+ "plant"
+ ]
+ },
+ "cherry_blossom": {
+ "unicode": "1f338",
+ "unicode_alternates": "",
+ "name": "cherry blossom",
+ "shortname": ":cherry_blossom:",
+ "category": "nature",
+ "emoji_order": "298",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "nature",
+ "flower",
+ "plant",
+ "tropical"
+ ]
+ },
+ "bouquet": {
+ "unicode": "1f490",
+ "unicode_alternates": "",
+ "name": "bouquet",
+ "shortname": ":bouquet:",
+ "category": "nature",
+ "emoji_order": "299",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "nature",
+ "flower",
+ "plant",
+ "rip",
+ "rip",
+ "condolence",
+ "condolence"
+ ]
+ },
+ "mushroom": {
+ "unicode": "1f344",
+ "unicode_alternates": "",
+ "name": "mushroom",
+ "shortname": ":mushroom:",
+ "category": "nature",
+ "emoji_order": "300",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "nature",
+ "plant",
+ "drugs",
+ "drugs"
+ ]
+ },
+ "chestnut": {
+ "unicode": "1f330",
+ "unicode_alternates": "",
+ "name": "chestnut",
+ "shortname": ":chestnut:",
+ "category": "nature",
+ "emoji_order": "301",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "nature",
+ "plant"
+ ]
+ },
+ "jack_o_lantern": {
+ "unicode": "1f383",
+ "unicode_alternates": "",
+ "name": "jack-o-lantern",
+ "shortname": ":jack_o_lantern:",
+ "category": "nature",
+ "emoji_order": "302",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "holidays",
+ "halloween"
+ ]
+ },
+ "shell": {
+ "unicode": "1f41a",
+ "unicode_alternates": "",
+ "name": "spiral shell",
+ "shortname": ":shell:",
+ "category": "nature",
+ "emoji_order": "303",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "spider_web": {
+ "unicode": "1f578",
+ "unicode_alternates": "1f578-fe0f",
+ "name": "spider web",
+ "shortname": ":spider_web:",
+ "category": "nature",
+ "emoji_order": "304",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "halloween"
+ ]
+ },
+ "earth_americas": {
+ "unicode": "1f30e",
+ "unicode_alternates": "",
+ "name": "earth globe americas",
+ "shortname": ":earth_americas:",
+ "category": "nature",
+ "emoji_order": "305",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "map",
+ "vacation",
+ "globe",
+ "globe"
+ ]
+ },
+ "earth_africa": {
+ "unicode": "1f30d",
+ "unicode_alternates": "",
+ "name": "earth globe europe-africa",
+ "shortname": ":earth_africa:",
+ "category": "nature",
+ "emoji_order": "306",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "map",
+ "vacation",
+ "globe",
+ "globe"
+ ]
+ },
+ "earth_asia": {
+ "unicode": "1f30f",
+ "unicode_alternates": "",
+ "name": "earth globe asia-australia",
+ "shortname": ":earth_asia:",
+ "category": "nature",
+ "emoji_order": "307",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "map",
+ "vacation",
+ "globe",
+ "globe"
+ ]
+ },
+ "full_moon": {
+ "unicode": "1f315",
+ "unicode_alternates": "",
+ "name": "full moon symbol",
+ "shortname": ":full_moon:",
+ "category": "nature",
+ "emoji_order": "308",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "space",
+ "sky",
+ "moon",
+ "moon"
+ ]
+ },
+ "waning_gibbous_moon": {
+ "unicode": "1f316",
+ "unicode_alternates": "",
+ "name": "waning gibbous moon symbol",
+ "shortname": ":waning_gibbous_moon:",
+ "category": "nature",
+ "emoji_order": "309",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "space",
+ "sky",
+ "moon",
+ "moon"
+ ]
+ },
+ "last_quarter_moon": {
+ "unicode": "1f317",
+ "unicode_alternates": "",
+ "name": "last quarter moon symbol",
+ "shortname": ":last_quarter_moon:",
+ "category": "nature",
+ "emoji_order": "310",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "space",
+ "sky",
+ "moon",
+ "moon"
+ ]
+ },
+ "waning_crescent_moon": {
+ "unicode": "1f318",
+ "unicode_alternates": "",
+ "name": "waning crescent moon symbol",
+ "shortname": ":waning_crescent_moon:",
+ "category": "nature",
+ "emoji_order": "311",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "space",
+ "sky",
+ "moon",
+ "moon"
+ ]
+ },
+ "new_moon": {
+ "unicode": "1f311",
+ "unicode_alternates": "",
+ "name": "new moon symbol",
+ "shortname": ":new_moon:",
+ "category": "nature",
+ "emoji_order": "312",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "space",
+ "sky",
+ "moon",
+ "moon"
+ ]
+ },
+ "waxing_crescent_moon": {
+ "unicode": "1f312",
+ "unicode_alternates": "",
+ "name": "waxing crescent moon symbol",
+ "shortname": ":waxing_crescent_moon:",
+ "category": "nature",
+ "emoji_order": "313",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "space",
+ "sky",
+ "moon",
+ "moon"
+ ]
+ },
+ "first_quarter_moon": {
+ "unicode": "1f313",
+ "unicode_alternates": "",
+ "name": "first quarter moon symbol",
+ "shortname": ":first_quarter_moon:",
+ "category": "nature",
+ "emoji_order": "314",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "space",
+ "sky",
+ "moon",
+ "moon"
+ ]
+ },
+ "waxing_gibbous_moon": {
+ "unicode": "1f314",
+ "unicode_alternates": "",
+ "name": "waxing gibbous moon symbol",
+ "shortname": ":waxing_gibbous_moon:",
+ "category": "nature",
+ "emoji_order": "315",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "space",
+ "sky",
+ "moon",
+ "moon"
+ ]
+ },
+ "new_moon_with_face": {
+ "unicode": "1f31a",
+ "unicode_alternates": "",
+ "name": "new moon with face",
+ "shortname": ":new_moon_with_face:",
+ "category": "nature",
+ "emoji_order": "316",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "space",
+ "sky",
+ "goodnight",
+ "goodnight",
+ "moon",
+ "moon"
+ ]
+ },
+ "full_moon_with_face": {
+ "unicode": "1f31d",
+ "unicode_alternates": "",
+ "name": "full moon with face",
+ "shortname": ":full_moon_with_face:",
+ "category": "nature",
+ "emoji_order": "317",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "space",
+ "sky",
+ "goodnight",
+ "goodnight",
+ "moon",
+ "moon"
+ ]
+ },
+ "first_quarter_moon_with_face": {
+ "unicode": "1f31b",
+ "unicode_alternates": "",
+ "name": "first quarter moon with face",
+ "shortname": ":first_quarter_moon_with_face:",
+ "category": "nature",
+ "emoji_order": "318",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "space",
+ "sky",
+ "moon",
+ "moon"
+ ]
+ },
+ "last_quarter_moon_with_face": {
+ "unicode": "1f31c",
+ "unicode_alternates": "",
+ "name": "last quarter moon with face",
+ "shortname": ":last_quarter_moon_with_face:",
+ "category": "nature",
+ "emoji_order": "319",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "space",
+ "sky",
+ "moon",
+ "moon"
+ ]
+ },
+ "sun_with_face": {
+ "unicode": "1f31e",
+ "unicode_alternates": "",
+ "name": "sun with face",
+ "shortname": ":sun_with_face:",
+ "category": "nature",
+ "emoji_order": "320",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "sky",
+ "day",
+ "sun",
+ "hump day",
+ "hump day",
+ "morning",
+ "morning"
+ ]
+ },
+ "crescent_moon": {
+ "unicode": "1f319",
+ "unicode_alternates": "",
+ "name": "crescent moon",
+ "shortname": ":crescent_moon:",
+ "category": "nature",
+ "emoji_order": "321",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "space",
+ "sky",
+ "goodnight",
+ "goodnight",
+ "moon",
+ "moon"
+ ]
+ },
+ "star": {
+ "unicode": "2b50",
+ "unicode_alternates": "2b50-fe0f",
+ "name": "white medium star",
+ "shortname": ":star:",
+ "category": "nature",
+ "emoji_order": "322",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "space",
+ "sky",
+ "star"
+ ]
+ },
+ "star2": {
+ "unicode": "1f31f",
+ "unicode_alternates": "",
+ "name": "glowing star",
+ "shortname": ":star2:",
+ "category": "nature",
+ "emoji_order": "323",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "space",
+ "sky",
+ "star"
+ ]
+ },
+ "dizzy": {
+ "unicode": "1f4ab",
+ "unicode_alternates": "",
+ "name": "dizzy symbol",
+ "shortname": ":dizzy:",
+ "category": "nature",
+ "emoji_order": "324",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "star",
+ "symbol"
+ ]
+ },
+ "sparkles": {
+ "unicode": "2728",
+ "unicode_alternates": "",
+ "name": "sparkles",
+ "shortname": ":sparkles:",
+ "category": "nature",
+ "emoji_order": "325",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "star",
+ "girls night",
+ "girls night"
+ ]
+ },
+ "comet": {
+ "unicode": "2604",
+ "unicode_alternates": "2604-fe0f",
+ "name": "comet",
+ "shortname": ":comet:",
+ "category": "nature",
+ "emoji_order": "326",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "space",
+ "sky"
+ ]
+ },
+ "sunny": {
+ "unicode": "2600",
+ "unicode_alternates": "2600-fe0f",
+ "name": "black sun with rays",
+ "shortname": ":sunny:",
+ "category": "nature",
+ "emoji_order": "327",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "weather",
+ "sky",
+ "day",
+ "sun",
+ "hot",
+ "hot",
+ "morning",
+ "morning"
+ ]
+ },
+ "white_sun_small_cloud": {
+ "unicode": "1f324",
+ "unicode_alternates": "1f324-fe0f",
+ "name": "white sun with small cloud",
+ "shortname": ":white_sun_small_cloud:",
+ "category": "nature",
+ "emoji_order": "328",
+ "aliases": [
+ ":white_sun_with_small_cloud:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "weather",
+ "sky",
+ "cloud",
+ "sun"
+ ]
+ },
+ "partly_sunny": {
+ "unicode": "26c5",
+ "unicode_alternates": "26c5-fe0f",
+ "name": "sun behind cloud",
+ "shortname": ":partly_sunny:",
+ "category": "nature",
+ "emoji_order": "329",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "weather",
+ "sky",
+ "cloud",
+ "sun"
+ ]
+ },
+ "white_sun_cloud": {
+ "unicode": "1f325",
+ "unicode_alternates": "1f325-fe0f",
+ "name": "white sun behind cloud",
+ "shortname": ":white_sun_cloud:",
+ "category": "nature",
+ "emoji_order": "330",
+ "aliases": [
+ ":white_sun_behind_cloud:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "weather",
+ "sky",
+ "cloud",
+ "cold",
+ "sun"
+ ]
+ },
+ "white_sun_rain_cloud": {
+ "unicode": "1f326",
+ "unicode_alternates": "1f326-fe0f",
+ "name": "white sun behind cloud with rain",
+ "shortname": ":white_sun_rain_cloud:",
+ "category": "nature",
+ "emoji_order": "331",
+ "aliases": [
+ ":white_sun_behind_cloud_with_rain:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "weather",
+ "sky",
+ "cloud",
+ "cold",
+ "rain",
+ "sun"
+ ]
+ },
+ "cloud": {
+ "unicode": "2601",
+ "unicode_alternates": "2601-fe0f",
+ "name": "cloud",
+ "shortname": ":cloud:",
+ "category": "nature",
+ "emoji_order": "332",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "weather",
+ "sky",
+ "cloud",
+ "cold",
+ "rain"
+ ]
+ },
+ "cloud_rain": {
+ "unicode": "1f327",
+ "unicode_alternates": "1f327-fe0f",
+ "name": "cloud with rain",
+ "shortname": ":cloud_rain:",
+ "category": "nature",
+ "emoji_order": "333",
+ "aliases": [
+ ":cloud_with_rain:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "weather",
+ "winter",
+ "sky",
+ "cloud",
+ "cold",
+ "rain"
+ ]
+ },
+ "thunder_cloud_rain": {
+ "unicode": "26c8",
+ "unicode_alternates": "26c8-fe0f",
+ "name": "thunder cloud and rain",
+ "shortname": ":thunder_cloud_rain:",
+ "category": "nature",
+ "emoji_order": "334",
+ "aliases": [
+ ":thunder_cloud_and_rain:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "weather",
+ "sky",
+ "cloud",
+ "cold",
+ "rain"
+ ]
+ },
+ "cloud_lightning": {
+ "unicode": "1f329",
+ "unicode_alternates": "1f329-fe0f",
+ "name": "cloud with lightning",
+ "shortname": ":cloud_lightning:",
+ "category": "nature",
+ "emoji_order": "335",
+ "aliases": [
+ ":cloud_with_lightning:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "weather",
+ "sky",
+ "cloud",
+ "cold",
+ "rain"
+ ]
+ },
+ "zap": {
+ "unicode": "26a1",
+ "unicode_alternates": "26a1-fe0f",
+ "name": "high voltage sign",
+ "shortname": ":zap:",
+ "category": "nature",
+ "emoji_order": "336",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "weather",
+ "sky",
+ "diarrhea",
+ "diarrhea"
+ ]
+ },
+ "fire": {
+ "unicode": "1f525",
+ "unicode_alternates": "",
+ "name": "fire",
+ "shortname": ":fire:",
+ "category": "nature",
+ "emoji_order": "337",
+ "aliases": [
+ ":flame:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "wth",
+ "wth",
+ "hot",
+ "hot"
+ ]
+ },
+ "boom": {
+ "unicode": "1f4a5",
+ "unicode_alternates": "",
+ "name": "collision symbol",
+ "shortname": ":boom:",
+ "category": "nature",
+ "emoji_order": "338",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol",
+ "blast",
+ "blast"
+ ]
+ },
+ "snowflake": {
+ "unicode": "2744",
+ "unicode_alternates": "2744-fe0f",
+ "name": "snowflake",
+ "shortname": ":snowflake:",
+ "category": "nature",
+ "emoji_order": "339",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "weather",
+ "winter",
+ "sky",
+ "holidays",
+ "cold",
+ "snow",
+ "snow"
+ ]
+ },
+ "cloud_snow": {
+ "unicode": "1f328",
+ "unicode_alternates": "1f328-fe0f",
+ "name": "cloud with snow",
+ "shortname": ":cloud_snow:",
+ "category": "nature",
+ "emoji_order": "340",
+ "aliases": [
+ ":cloud_with_snow:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "weather",
+ "winter",
+ "sky",
+ "cloud",
+ "cold",
+ "snow",
+ "snow"
+ ]
+ },
+ "snowman2": {
+ "unicode": "2603",
+ "unicode_alternates": "2603-fe0f",
+ "name": "snowman",
+ "shortname": ":snowman2:",
+ "category": "nature",
+ "emoji_order": "341",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "weather",
+ "winter",
+ "holidays",
+ "christmas",
+ "cold",
+ "snow",
+ "snow"
+ ]
+ },
+ "snowman": {
+ "unicode": "26c4",
+ "unicode_alternates": "26c4-fe0f",
+ "name": "snowman without snow",
+ "shortname": ":snowman:",
+ "category": "nature",
+ "emoji_order": "342",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "weather",
+ "winter",
+ "holidays",
+ "cold",
+ "snow",
+ "snow"
+ ]
+ },
+ "wind_blowing_face": {
+ "unicode": "1f32c",
+ "unicode_alternates": "1f32c-fe0f",
+ "name": "wind blowing face",
+ "shortname": ":wind_blowing_face:",
+ "category": "nature",
+ "emoji_order": "343",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "weather",
+ "cold"
+ ]
+ },
+ "dash": {
+ "unicode": "1f4a8",
+ "unicode_alternates": "",
+ "name": "dash symbol",
+ "shortname": ":dash:",
+ "category": "nature",
+ "emoji_order": "344",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "cloud",
+ "cold",
+ "smoking",
+ "smoking"
+ ]
+ },
+ "cloud_tornado": {
+ "unicode": "1f32a",
+ "unicode_alternates": "1f32a-fe0f",
+ "name": "cloud with tornado",
+ "shortname": ":cloud_tornado:",
+ "category": "nature",
+ "emoji_order": "345",
+ "aliases": [
+ ":cloud_with_tornado:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "weather",
+ "sky",
+ "cold"
+ ]
+ },
+ "fog": {
+ "unicode": "1f32b",
+ "unicode_alternates": "1f32b-fe0f",
+ "name": "fog",
+ "shortname": ":fog:",
+ "category": "nature",
+ "emoji_order": "346",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "weather",
+ "sky",
+ "cold"
+ ]
+ },
+ "umbrella2": {
+ "unicode": "2602",
+ "unicode_alternates": "2602-fe0f",
+ "name": "umbrella",
+ "shortname": ":umbrella2:",
+ "category": "nature",
+ "emoji_order": "347",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "weather",
+ "object",
+ "sky",
+ "cold"
+ ]
+ },
+ "umbrella": {
+ "unicode": "2614",
+ "unicode_alternates": "2614-fe0f",
+ "name": "umbrella with rain drops",
+ "shortname": ":umbrella:",
+ "category": "nature",
+ "emoji_order": "348",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "weather",
+ "sky",
+ "cold",
+ "rain"
+ ]
+ },
+ "droplet": {
+ "unicode": "1f4a7",
+ "unicode_alternates": "",
+ "name": "droplet",
+ "shortname": ":droplet:",
+ "category": "nature",
+ "emoji_order": "349",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "weather",
+ "sky",
+ "rain"
+ ]
+ },
+ "sweat_drops": {
+ "unicode": "1f4a6",
+ "unicode_alternates": "",
+ "name": "splashing sweat symbol",
+ "shortname": ":sweat_drops:",
+ "category": "nature",
+ "emoji_order": "350",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "rain",
+ "stressed",
+ "sweat"
+ ]
+ },
+ "ocean": {
+ "unicode": "1f30a",
+ "unicode_alternates": "",
+ "name": "water wave",
+ "shortname": ":ocean:",
+ "category": "nature",
+ "emoji_order": "351",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "weather",
+ "boat",
+ "tropical",
+ "swim"
+ ]
+ },
+ "green_apple": {
+ "unicode": "1f34f",
+ "unicode_alternates": "",
+ "name": "green apple",
+ "shortname": ":green_apple:",
+ "category": "food",
+ "emoji_order": "352",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "fruit",
+ "food"
+ ]
+ },
+ "apple": {
+ "unicode": "1f34e",
+ "unicode_alternates": "",
+ "name": "red apple",
+ "shortname": ":apple:",
+ "category": "food",
+ "emoji_order": "353",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "fruit",
+ "food",
+ "creationism",
+ "creationism"
+ ]
+ },
+ "pear": {
+ "unicode": "1f350",
+ "unicode_alternates": "",
+ "name": "pear",
+ "shortname": ":pear:",
+ "category": "food",
+ "emoji_order": "354",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "fruit",
+ "food"
+ ]
+ },
+ "tangerine": {
+ "unicode": "1f34a",
+ "unicode_alternates": "",
+ "name": "tangerine",
+ "shortname": ":tangerine:",
+ "category": "food",
+ "emoji_order": "355",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "fruit",
+ "food"
+ ]
+ },
+ "lemon": {
+ "unicode": "1f34b",
+ "unicode_alternates": "",
+ "name": "lemon",
+ "shortname": ":lemon:",
+ "category": "food",
+ "emoji_order": "356",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "fruit",
+ "food"
+ ]
+ },
+ "banana": {
+ "unicode": "1f34c",
+ "unicode_alternates": "",
+ "name": "banana",
+ "shortname": ":banana:",
+ "category": "food",
+ "emoji_order": "357",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "fruit",
+ "penis",
+ "food"
+ ]
+ },
+ "watermelon": {
+ "unicode": "1f349",
+ "unicode_alternates": "",
+ "name": "watermelon",
+ "shortname": ":watermelon:",
+ "category": "food",
+ "emoji_order": "358",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "fruit",
+ "food"
+ ]
+ },
+ "grapes": {
+ "unicode": "1f347",
+ "unicode_alternates": "",
+ "name": "grapes",
+ "shortname": ":grapes:",
+ "category": "food",
+ "emoji_order": "359",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "fruit",
+ "food"
+ ]
+ },
+ "strawberry": {
+ "unicode": "1f353",
+ "unicode_alternates": "",
+ "name": "strawberry",
+ "shortname": ":strawberry:",
+ "category": "food",
+ "emoji_order": "360",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "fruit",
+ "food"
+ ]
+ },
+ "melon": {
+ "unicode": "1f348",
+ "unicode_alternates": "",
+ "name": "melon",
+ "shortname": ":melon:",
+ "category": "food",
+ "emoji_order": "361",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "fruit",
+ "boobs",
+ "food"
+ ]
+ },
+ "cherries": {
+ "unicode": "1f352",
+ "unicode_alternates": "",
+ "name": "cherries",
+ "shortname": ":cherries:",
+ "category": "food",
+ "emoji_order": "362",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "fruit",
+ "food"
+ ]
+ },
+ "peach": {
+ "unicode": "1f351",
+ "unicode_alternates": "",
+ "name": "peach",
+ "shortname": ":peach:",
+ "category": "food",
+ "emoji_order": "363",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "fruit",
+ "butt",
+ "food"
+ ]
+ },
+ "pineapple": {
+ "unicode": "1f34d",
+ "unicode_alternates": "",
+ "name": "pineapple",
+ "shortname": ":pineapple:",
+ "category": "food",
+ "emoji_order": "364",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "fruit",
+ "food",
+ "tropical"
+ ]
+ },
+ "tomato": {
+ "unicode": "1f345",
+ "unicode_alternates": "",
+ "name": "tomato",
+ "shortname": ":tomato:",
+ "category": "food",
+ "emoji_order": "365",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "fruit",
+ "vegetables",
+ "food"
+ ]
+ },
+ "eggplant": {
+ "unicode": "1f346",
+ "unicode_alternates": "",
+ "name": "aubergine",
+ "shortname": ":eggplant:",
+ "category": "food",
+ "emoji_order": "366",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "vegetables",
+ "penis",
+ "food"
+ ]
+ },
+ "hot_pepper": {
+ "unicode": "1f336",
+ "unicode_alternates": "1f336-fe0f",
+ "name": "hot pepper",
+ "shortname": ":hot_pepper:",
+ "category": "food",
+ "emoji_order": "367",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "vegetables",
+ "food"
+ ]
+ },
+ "corn": {
+ "unicode": "1f33d",
+ "unicode_alternates": "",
+ "name": "ear of maize",
+ "shortname": ":corn:",
+ "category": "food",
+ "emoji_order": "368",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "vegetables",
+ "food"
+ ]
+ },
+ "sweet_potato": {
+ "unicode": "1f360",
+ "unicode_alternates": "",
+ "name": "roasted sweet potato",
+ "shortname": ":sweet_potato:",
+ "category": "food",
+ "emoji_order": "369",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "vegetables",
+ "food"
+ ]
+ },
+ "honey_pot": {
+ "unicode": "1f36f",
+ "unicode_alternates": "",
+ "name": "honey pot",
+ "shortname": ":honey_pot:",
+ "category": "food",
+ "emoji_order": "370",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "food",
+ "vagina"
+ ]
+ },
+ "bread": {
+ "unicode": "1f35e",
+ "unicode_alternates": "",
+ "name": "bread",
+ "shortname": ":bread:",
+ "category": "food",
+ "emoji_order": "371",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "food"
+ ]
+ },
+ "cheese": {
+ "unicode": "1f9c0",
+ "unicode_alternates": "",
+ "name": "cheese wedge",
+ "shortname": ":cheese:",
+ "category": "food",
+ "emoji_order": "372",
+ "aliases": [
+ ":cheese_wedge:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "food"
+ ]
+ },
+ "poultry_leg": {
+ "unicode": "1f357",
+ "unicode_alternates": "",
+ "name": "poultry leg",
+ "shortname": ":poultry_leg:",
+ "category": "food",
+ "emoji_order": "373",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "food",
+ "holidays"
+ ]
+ },
+ "meat_on_bone": {
+ "unicode": "1f356",
+ "unicode_alternates": "",
+ "name": "meat on bone",
+ "shortname": ":meat_on_bone:",
+ "category": "food",
+ "emoji_order": "374",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "food"
+ ]
+ },
+ "fried_shrimp": {
+ "unicode": "1f364",
+ "unicode_alternates": "",
+ "name": "fried shrimp",
+ "shortname": ":fried_shrimp:",
+ "category": "food",
+ "emoji_order": "375",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "food"
+ ]
+ },
+ "egg": {
+ "unicode": "1f95a",
+ "unicode_alternates": "",
+ "name": "egg",
+ "shortname": ":egg:",
+ "category": "unicode9",
+ "emoji_order": "75",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "hamburger": {
+ "unicode": "1f354",
+ "unicode_alternates": "",
+ "name": "hamburger",
+ "shortname": ":hamburger:",
+ "category": "food",
+ "emoji_order": "377",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "america",
+ "food"
+ ]
+ },
+ "fries": {
+ "unicode": "1f35f",
+ "unicode_alternates": "",
+ "name": "french fries",
+ "shortname": ":fries:",
+ "category": "food",
+ "emoji_order": "378",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "america",
+ "food"
+ ]
+ },
+ "hotdog": {
+ "unicode": "1f32d",
+ "unicode_alternates": "",
+ "name": "hot dog",
+ "shortname": ":hotdog:",
+ "category": "food",
+ "emoji_order": "379",
+ "aliases": [
+ ":hot_dog:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "america",
+ "food"
+ ]
+ },
+ "pizza": {
+ "unicode": "1f355",
+ "unicode_alternates": "",
+ "name": "slice of pizza",
+ "shortname": ":pizza:",
+ "category": "food",
+ "emoji_order": "380",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "italian",
+ "food",
+ "boys night",
+ "boys night"
+ ]
+ },
+ "spaghetti": {
+ "unicode": "1f35d",
+ "unicode_alternates": "",
+ "name": "spaghetti",
+ "shortname": ":spaghetti:",
+ "category": "food",
+ "emoji_order": "381",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "noodles",
+ "pasta",
+ "italian",
+ "food"
+ ]
+ },
+ "taco": {
+ "unicode": "1f32e",
+ "unicode_alternates": "",
+ "name": "taco",
+ "shortname": ":taco:",
+ "category": "food",
+ "emoji_order": "382",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "food",
+ "mexican",
+ "vagina"
+ ]
+ },
+ "burrito": {
+ "unicode": "1f32f",
+ "unicode_alternates": "",
+ "name": "burrito",
+ "shortname": ":burrito:",
+ "category": "food",
+ "emoji_order": "383",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "food",
+ "mexican"
+ ]
+ },
+ "ramen": {
+ "unicode": "1f35c",
+ "unicode_alternates": "",
+ "name": "steaming bowl",
+ "shortname": ":ramen:",
+ "category": "food",
+ "emoji_order": "384",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "noodles",
+ "ramen",
+ "japan",
+ "food"
+ ]
+ },
+ "stew": {
+ "unicode": "1f372",
+ "unicode_alternates": "",
+ "name": "pot of food",
+ "shortname": ":stew:",
+ "category": "food",
+ "emoji_order": "385",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "food",
+ "steam",
+ "steam"
+ ]
+ },
+ "fish_cake": {
+ "unicode": "1f365",
+ "unicode_alternates": "",
+ "name": "fish cake with swirl design",
+ "shortname": ":fish_cake:",
+ "category": "food",
+ "emoji_order": "386",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "sushi",
+ "food"
+ ]
+ },
+ "sushi": {
+ "unicode": "1f363",
+ "unicode_alternates": "",
+ "name": "sushi",
+ "shortname": ":sushi:",
+ "category": "food",
+ "emoji_order": "387",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "sushi",
+ "japan",
+ "food"
+ ]
+ },
+ "bento": {
+ "unicode": "1f371",
+ "unicode_alternates": "",
+ "name": "bento box",
+ "shortname": ":bento:",
+ "category": "food",
+ "emoji_order": "388",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "sushi",
+ "japan",
+ "food"
+ ]
+ },
+ "curry": {
+ "unicode": "1f35b",
+ "unicode_alternates": "",
+ "name": "curry and rice",
+ "shortname": ":curry:",
+ "category": "food",
+ "emoji_order": "389",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "food"
+ ]
+ },
+ "rice_ball": {
+ "unicode": "1f359",
+ "unicode_alternates": "",
+ "name": "rice ball",
+ "shortname": ":rice_ball:",
+ "category": "food",
+ "emoji_order": "390",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "sushi",
+ "japan",
+ "food"
+ ]
+ },
+ "rice": {
+ "unicode": "1f35a",
+ "unicode_alternates": "",
+ "name": "cooked rice",
+ "shortname": ":rice:",
+ "category": "food",
+ "emoji_order": "391",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "sushi",
+ "japan",
+ "food"
+ ]
+ },
+ "rice_cracker": {
+ "unicode": "1f358",
+ "unicode_alternates": "",
+ "name": "rice cracker",
+ "shortname": ":rice_cracker:",
+ "category": "food",
+ "emoji_order": "392",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "sushi",
+ "food"
+ ]
+ },
+ "oden": {
+ "unicode": "1f362",
+ "unicode_alternates": "",
+ "name": "oden",
+ "shortname": ":oden:",
+ "category": "food",
+ "emoji_order": "393",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "food"
+ ]
+ },
+ "dango": {
+ "unicode": "1f361",
+ "unicode_alternates": "",
+ "name": "dango",
+ "shortname": ":dango:",
+ "category": "food",
+ "emoji_order": "394",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "food"
+ ]
+ },
+ "shaved_ice": {
+ "unicode": "1f367",
+ "unicode_alternates": "",
+ "name": "shaved ice",
+ "shortname": ":shaved_ice:",
+ "category": "food",
+ "emoji_order": "395",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "food"
+ ]
+ },
+ "ice_cream": {
+ "unicode": "1f368",
+ "unicode_alternates": "",
+ "name": "ice cream",
+ "shortname": ":ice_cream:",
+ "category": "food",
+ "emoji_order": "396",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "food"
+ ]
+ },
+ "icecream": {
+ "unicode": "1f366",
+ "unicode_alternates": "",
+ "name": "soft ice cream",
+ "shortname": ":icecream:",
+ "category": "food",
+ "emoji_order": "397",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "food"
+ ]
+ },
+ "cake": {
+ "unicode": "1f370",
+ "unicode_alternates": "",
+ "name": "shortcake",
+ "shortname": ":cake:",
+ "category": "food",
+ "emoji_order": "398",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "food"
+ ]
+ },
+ "birthday": {
+ "unicode": "1f382",
+ "unicode_alternates": "",
+ "name": "birthday cake",
+ "shortname": ":birthday:",
+ "category": "food",
+ "emoji_order": "399",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "birthday",
+ "food",
+ "parties",
+ "parties"
+ ]
+ },
+ "custard": {
+ "unicode": "1f36e",
+ "unicode_alternates": "",
+ "name": "custard",
+ "shortname": ":custard:",
+ "category": "food",
+ "emoji_order": "400",
+ "aliases": [
+ ":pudding:",
+ ":flan:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "food"
+ ]
+ },
+ "candy": {
+ "unicode": "1f36c",
+ "unicode_alternates": "",
+ "name": "candy",
+ "shortname": ":candy:",
+ "category": "food",
+ "emoji_order": "401",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "food",
+ "halloween"
+ ]
+ },
+ "lollipop": {
+ "unicode": "1f36d",
+ "unicode_alternates": "",
+ "name": "lollipop",
+ "shortname": ":lollipop:",
+ "category": "food",
+ "emoji_order": "402",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "food",
+ "halloween"
+ ]
+ },
+ "chocolate_bar": {
+ "unicode": "1f36b",
+ "unicode_alternates": "",
+ "name": "chocolate bar",
+ "shortname": ":chocolate_bar:",
+ "category": "food",
+ "emoji_order": "403",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "food",
+ "halloween"
+ ]
+ },
+ "popcorn": {
+ "unicode": "1f37f",
+ "unicode_alternates": "",
+ "name": "popcorn",
+ "shortname": ":popcorn:",
+ "category": "food",
+ "emoji_order": "404",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "food",
+ "parties",
+ "parties"
+ ]
+ },
+ "doughnut": {
+ "unicode": "1f369",
+ "unicode_alternates": "",
+ "name": "doughnut",
+ "shortname": ":doughnut:",
+ "category": "food",
+ "emoji_order": "405",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "food"
+ ]
+ },
+ "cookie": {
+ "unicode": "1f36a",
+ "unicode_alternates": "",
+ "name": "cookie",
+ "shortname": ":cookie:",
+ "category": "food",
+ "emoji_order": "406",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "food",
+ "vagina"
+ ]
+ },
+ "beer": {
+ "unicode": "1f37a",
+ "unicode_alternates": "",
+ "name": "beer mug",
+ "shortname": ":beer:",
+ "category": "food",
+ "emoji_order": "407",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "drink",
+ "beer",
+ "alcohol",
+ "parties",
+ "parties"
+ ]
+ },
+ "beers": {
+ "unicode": "1f37b",
+ "unicode_alternates": "",
+ "name": "clinking beer mugs",
+ "shortname": ":beers:",
+ "category": "food",
+ "emoji_order": "408",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "drink",
+ "cheers",
+ "beer",
+ "alcohol",
+ "thank you",
+ "boys night",
+ "boys night",
+ "parties",
+ "parties"
+ ]
+ },
+ "wine_glass": {
+ "unicode": "1f377",
+ "unicode_alternates": "",
+ "name": "wine glass",
+ "shortname": ":wine_glass:",
+ "category": "food",
+ "emoji_order": "409",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "drink",
+ "italian",
+ "alcohol",
+ "girls night",
+ "girls night",
+ "parties",
+ "parties"
+ ]
+ },
+ "cocktail": {
+ "unicode": "1f378",
+ "unicode_alternates": "",
+ "name": "cocktail glass",
+ "shortname": ":cocktail:",
+ "category": "food",
+ "emoji_order": "410",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "drink",
+ "cocktail",
+ "alcohol",
+ "girls night",
+ "girls night",
+ "parties",
+ "parties"
+ ]
+ },
+ "tropical_drink": {
+ "unicode": "1f379",
+ "unicode_alternates": "",
+ "name": "tropical drink",
+ "shortname": ":tropical_drink:",
+ "category": "food",
+ "emoji_order": "411",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "drink",
+ "cocktail",
+ "tropical",
+ "alcohol"
+ ]
+ },
+ "champagne": {
+ "unicode": "1f37e",
+ "unicode_alternates": "",
+ "name": "bottle with popping cork",
+ "shortname": ":champagne:",
+ "category": "food",
+ "emoji_order": "412",
+ "aliases": [
+ ":bottle_with_popping_cork:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "drink",
+ "cheers",
+ "alcohol",
+ "parties",
+ "parties"
+ ]
+ },
+ "sake": {
+ "unicode": "1f376",
+ "unicode_alternates": "",
+ "name": "sake bottle and cup",
+ "shortname": ":sake:",
+ "category": "food",
+ "emoji_order": "413",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "drink",
+ "japan",
+ "sake",
+ "alcohol",
+ "girls night",
+ "girls night"
+ ]
+ },
+ "tea": {
+ "unicode": "1f375",
+ "unicode_alternates": "",
+ "name": "teacup without handle",
+ "shortname": ":tea:",
+ "category": "food",
+ "emoji_order": "414",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "drink",
+ "japan",
+ "caffeine",
+ "steam",
+ "steam",
+ "morning",
+ "morning"
+ ]
+ },
+ "coffee": {
+ "unicode": "2615",
+ "unicode_alternates": "2615-fe0f",
+ "name": "hot beverage",
+ "shortname": ":coffee:",
+ "category": "food",
+ "emoji_order": "415",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "drink",
+ "caffeine",
+ "steam",
+ "steam",
+ "morning",
+ "morning"
+ ]
+ },
+ "baby_bottle": {
+ "unicode": "1f37c",
+ "unicode_alternates": "",
+ "name": "baby bottle",
+ "shortname": ":baby_bottle:",
+ "category": "food",
+ "emoji_order": "416",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "drink",
+ "object",
+ "food",
+ "baby"
+ ]
+ },
+ "fork_and_knife": {
+ "unicode": "1f374",
+ "unicode_alternates": "",
+ "name": "fork and knife",
+ "shortname": ":fork_and_knife:",
+ "category": "food",
+ "emoji_order": "417",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "weapon",
+ "food"
+ ]
+ },
+ "fork_knife_plate": {
+ "unicode": "1f37d",
+ "unicode_alternates": "1f37d-fe0f",
+ "name": "fork and knife with plate",
+ "shortname": ":fork_knife_plate:",
+ "category": "food",
+ "emoji_order": "418",
+ "aliases": [
+ ":fork_and_knife_with_plate:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "food"
+ ]
+ },
+ "soccer": {
+ "unicode": "26bd",
+ "unicode_alternates": "26bd-fe0f",
+ "name": "soccer ball",
+ "shortname": ":soccer:",
+ "category": "activity",
+ "emoji_order": "419",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "game",
+ "ball",
+ "sport",
+ "soccer",
+ "football"
+ ]
+ },
+ "basketball": {
+ "unicode": "1f3c0",
+ "unicode_alternates": "",
+ "name": "basketball and hoop",
+ "shortname": ":basketball:",
+ "category": "activity",
+ "emoji_order": "420",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "game",
+ "ball",
+ "sport",
+ "basketball"
+ ]
+ },
+ "football": {
+ "unicode": "1f3c8",
+ "unicode_alternates": "",
+ "name": "american football",
+ "shortname": ":football:",
+ "category": "activity",
+ "emoji_order": "421",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "america",
+ "game",
+ "ball",
+ "sport",
+ "football"
+ ]
+ },
+ "baseball": {
+ "unicode": "26be",
+ "unicode_alternates": "26be-fe0f",
+ "name": "baseball",
+ "shortname": ":baseball:",
+ "category": "activity",
+ "emoji_order": "422",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "game",
+ "ball",
+ "sport",
+ "baseball"
+ ]
+ },
+ "tennis": {
+ "unicode": "1f3be",
+ "unicode_alternates": "",
+ "name": "tennis racquet and ball",
+ "shortname": ":tennis:",
+ "category": "activity",
+ "emoji_order": "423",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "game",
+ "ball",
+ "sport",
+ "tennis"
+ ]
+ },
+ "volleyball": {
+ "unicode": "1f3d0",
+ "unicode_alternates": "",
+ "name": "volleyball",
+ "shortname": ":volleyball:",
+ "category": "activity",
+ "emoji_order": "424",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "game",
+ "ball",
+ "sport",
+ "volleyball"
+ ]
+ },
+ "rugby_football": {
+ "unicode": "1f3c9",
+ "unicode_alternates": "",
+ "name": "rugby football",
+ "shortname": ":rugby_football:",
+ "category": "activity",
+ "emoji_order": "425",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "game",
+ "sport",
+ "football"
+ ]
+ },
+ "8ball": {
+ "unicode": "1f3b1",
+ "unicode_alternates": "",
+ "name": "billiards",
+ "shortname": ":8ball:",
+ "category": "activity",
+ "emoji_order": "426",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "game",
+ "ball",
+ "sport",
+ "billiards",
+ "luck",
+ "boys night",
+ "boys night"
+ ]
+ },
+ "golf": {
+ "unicode": "26f3",
+ "unicode_alternates": "26f3-fe0f",
+ "name": "flag in hole",
+ "shortname": ":golf:",
+ "category": "activity",
+ "emoji_order": "427",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "game",
+ "ball",
+ "vacation",
+ "sport",
+ "golf",
+ "golf"
+ ]
+ },
+ "golfer": {
+ "unicode": "1f3cc",
+ "unicode_alternates": "1f3cc-fe0f",
+ "name": "golfer",
+ "shortname": ":golfer:",
+ "category": "activity",
+ "emoji_order": "428",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "men",
+ "game",
+ "ball",
+ "vacation",
+ "sport",
+ "golf",
+ "golf"
+ ]
+ },
+ "ping_pong": {
+ "unicode": "1f3d3",
+ "unicode_alternates": "",
+ "name": "table tennis paddle and ball",
+ "shortname": ":ping_pong:",
+ "category": "activity",
+ "emoji_order": "429",
+ "aliases": [
+ ":table_tennis:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "game",
+ "ball",
+ "sport",
+ "ping pong"
+ ]
+ },
+ "badminton": {
+ "unicode": "1f3f8",
+ "unicode_alternates": "",
+ "name": "badminton racquet",
+ "shortname": ":badminton:",
+ "category": "activity",
+ "emoji_order": "430",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "game",
+ "sport",
+ "badminton"
+ ]
+ },
+ "hockey": {
+ "unicode": "1f3d2",
+ "unicode_alternates": "",
+ "name": "ice hockey stick and puck",
+ "shortname": ":hockey:",
+ "category": "activity",
+ "emoji_order": "431",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "game",
+ "sport",
+ "hockey"
+ ]
+ },
+ "field_hockey": {
+ "unicode": "1f3d1",
+ "unicode_alternates": "",
+ "name": "field hockey stick and ball",
+ "shortname": ":field_hockey:",
+ "category": "activity",
+ "emoji_order": "432",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "ball",
+ "sport",
+ "hockey"
+ ]
+ },
+ "cricket": {
+ "unicode": "1f3cf",
+ "unicode_alternates": "",
+ "name": "cricket bat and ball",
+ "shortname": ":cricket:",
+ "category": "activity",
+ "emoji_order": "433",
+ "aliases": [
+ ":cricket_bat_ball:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "ball",
+ "sport",
+ "cricket"
+ ]
+ },
+ "ski": {
+ "unicode": "1f3bf",
+ "unicode_alternates": "",
+ "name": "ski and ski boot",
+ "shortname": ":ski:",
+ "category": "activity",
+ "emoji_order": "434",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "cold",
+ "sport",
+ "skiing"
+ ]
+ },
+ "skier": {
+ "unicode": "26f7",
+ "unicode_alternates": "26f7-fe0f",
+ "name": "skier",
+ "shortname": ":skier:",
+ "category": "activity",
+ "emoji_order": "435",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "hat",
+ "vacation",
+ "cold",
+ "sport",
+ "skiing"
+ ]
+ },
+ "snowboarder": {
+ "unicode": "1f3c2",
+ "unicode_alternates": "",
+ "name": "snowboarder",
+ "shortname": ":snowboarder:",
+ "category": "activity",
+ "emoji_order": "436",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "hat",
+ "vacation",
+ "cold",
+ "sport",
+ "snowboarding"
+ ]
+ },
+ "ice_skate": {
+ "unicode": "26f8",
+ "unicode_alternates": "26f8-fe0f",
+ "name": "ice skate",
+ "shortname": ":ice_skate:",
+ "category": "activity",
+ "emoji_order": "437",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "cold",
+ "sport",
+ "ice skating"
+ ]
+ },
+ "bow_and_arrow": {
+ "unicode": "1f3f9",
+ "unicode_alternates": "",
+ "name": "bow and arrow",
+ "shortname": ":bow_and_arrow:",
+ "category": "activity",
+ "emoji_order": "438",
+ "aliases": [
+ ":archery:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "weapon",
+ "sport"
+ ]
+ },
+ "fishing_pole_and_fish": {
+ "unicode": "1f3a3",
+ "unicode_alternates": "",
+ "name": "fishing pole and fish",
+ "shortname": ":fishing_pole_and_fish:",
+ "category": "activity",
+ "emoji_order": "439",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "vacation",
+ "sport",
+ "fishing"
+ ]
+ },
+ "rowboat": {
+ "unicode": "1f6a3",
+ "unicode_alternates": "",
+ "name": "rowboat",
+ "shortname": ":rowboat:",
+ "category": "activity",
+ "emoji_order": "440",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "men",
+ "workout",
+ "sport",
+ "rowing",
+ "diversity",
+ "diversity"
+ ]
+ },
+ "swimmer": {
+ "unicode": "1f3ca",
+ "unicode_alternates": "",
+ "name": "swimmer",
+ "shortname": ":swimmer:",
+ "category": "activity",
+ "emoji_order": "441",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "workout",
+ "sport",
+ "swim",
+ "diversity",
+ "diversity"
+ ]
+ },
+ "surfer": {
+ "unicode": "1f3c4",
+ "unicode_alternates": "",
+ "name": "surfer",
+ "shortname": ":surfer:",
+ "category": "activity",
+ "emoji_order": "442",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "men",
+ "vacation",
+ "tropical",
+ "sport",
+ "diversity",
+ "diversity"
+ ]
+ },
+ "bath": {
+ "unicode": "1f6c0",
+ "unicode_alternates": "",
+ "name": "bath",
+ "shortname": ":bath:",
+ "category": "activity",
+ "emoji_order": "443",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "bathroom",
+ "tired",
+ "diversity",
+ "diversity",
+ "steam",
+ "steam"
+ ]
+ },
+ "basketball_player": {
+ "unicode": "26f9",
+ "unicode_alternates": "26f9-fe0f",
+ "name": "person with ball",
+ "shortname": ":basketball_player:",
+ "category": "activity",
+ "emoji_order": "444",
+ "aliases": [
+ ":person_with_ball:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "men",
+ "game",
+ "ball",
+ "sport",
+ "basketball",
+ "diversity",
+ "diversity"
+ ]
+ },
+ "lifter": {
+ "unicode": "1f3cb",
+ "unicode_alternates": "1f3cb-fe0f",
+ "name": "weight lifter",
+ "shortname": ":lifter:",
+ "category": "activity",
+ "emoji_order": "445",
+ "aliases": [
+ ":weight_lifter:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "men",
+ "workout",
+ "flex",
+ "sport",
+ "weight lifting",
+ "win",
+ "win",
+ "diversity",
+ "diversity"
+ ]
+ },
+ "bicyclist": {
+ "unicode": "1f6b4",
+ "unicode_alternates": "",
+ "name": "bicyclist",
+ "shortname": ":bicyclist:",
+ "category": "activity",
+ "emoji_order": "446",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "men",
+ "workout",
+ "sport",
+ "bike",
+ "diversity",
+ "diversity"
+ ]
+ },
+ "mountain_bicyclist": {
+ "unicode": "1f6b5",
+ "unicode_alternates": "",
+ "name": "mountain bicyclist",
+ "shortname": ":mountain_bicyclist:",
+ "category": "activity",
+ "emoji_order": "447",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "men",
+ "sport",
+ "bike",
+ "diversity",
+ "diversity"
+ ]
+ },
+ "horse_racing": {
+ "unicode": "1f3c7",
+ "unicode_alternates": "",
+ "name": "horse racing",
+ "shortname": ":horse_racing:",
+ "category": "activity",
+ "emoji_order": "448",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "men",
+ "sport",
+ "horse racing"
+ ]
+ },
+ "levitate": {
+ "unicode": "1f574",
+ "unicode_alternates": "1f574-fe0f",
+ "name": "man in business suit levitating",
+ "shortname": ":levitate:",
+ "category": "activity",
+ "emoji_order": "449",
+ "aliases": [
+ ":man_in_business_suit_levitating:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "men",
+ "job",
+ "job"
+ ]
+ },
+ "trophy": {
+ "unicode": "1f3c6",
+ "unicode_alternates": "",
+ "name": "trophy",
+ "shortname": ":trophy:",
+ "category": "activity",
+ "emoji_order": "450",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "game",
+ "award",
+ "win",
+ "win",
+ "perfect",
+ "perfect",
+ "parties",
+ "parties"
+ ]
+ },
+ "running_shirt_with_sash": {
+ "unicode": "1f3bd",
+ "unicode_alternates": "",
+ "name": "running shirt with sash",
+ "shortname": ":running_shirt_with_sash:",
+ "category": "activity",
+ "emoji_order": "451",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "award"
+ ]
+ },
+ "medal": {
+ "unicode": "1f3c5",
+ "unicode_alternates": "",
+ "name": "sports medal",
+ "shortname": ":medal:",
+ "category": "activity",
+ "emoji_order": "452",
+ "aliases": [
+ ":sports_medal:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "award",
+ "sport",
+ "win",
+ "win",
+ "perfect",
+ "perfect"
+ ]
+ },
+ "military_medal": {
+ "unicode": "1f396",
+ "unicode_alternates": "1f396-fe0f",
+ "name": "military medal",
+ "shortname": ":military_medal:",
+ "category": "activity",
+ "emoji_order": "453",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "award",
+ "win",
+ "win"
+ ]
+ },
+ "reminder_ribbon": {
+ "unicode": "1f397",
+ "unicode_alternates": "1f397-fe0f",
+ "name": "reminder ribbon",
+ "shortname": ":reminder_ribbon:",
+ "category": "activity",
+ "emoji_order": "454",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "award"
+ ]
+ },
+ "rosette": {
+ "unicode": "1f3f5",
+ "unicode_alternates": "1f3f5-fe0f",
+ "name": "rosette",
+ "shortname": ":rosette:",
+ "category": "activity",
+ "emoji_order": "455",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "tropical"
+ ]
+ },
+ "ticket": {
+ "unicode": "1f3ab",
+ "unicode_alternates": "",
+ "name": "ticket",
+ "shortname": ":ticket:",
+ "category": "activity",
+ "emoji_order": "456",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "theatre",
+ "movie",
+ "parties",
+ "parties"
+ ]
+ },
+ "tickets": {
+ "unicode": "1f39f",
+ "unicode_alternates": "1f39f-fe0f",
+ "name": "admission tickets",
+ "shortname": ":tickets:",
+ "category": "activity",
+ "emoji_order": "457",
+ "aliases": [
+ ":admission_tickets:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "theatre",
+ "movie",
+ "parties",
+ "parties"
+ ]
+ },
+ "performing_arts": {
+ "unicode": "1f3ad",
+ "unicode_alternates": "",
+ "name": "performing arts",
+ "shortname": ":performing_arts:",
+ "category": "activity",
+ "emoji_order": "458",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "theatre",
+ "movie"
+ ]
+ },
+ "art": {
+ "unicode": "1f3a8",
+ "unicode_alternates": "",
+ "name": "artist palette",
+ "shortname": ":art:",
+ "category": "activity",
+ "emoji_order": "459",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "circus_tent": {
+ "unicode": "1f3aa",
+ "unicode_alternates": "",
+ "name": "circus tent",
+ "shortname": ":circus_tent:",
+ "category": "activity",
+ "emoji_order": "460",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "circus tent"
+ ]
+ },
+ "microphone": {
+ "unicode": "1f3a4",
+ "unicode_alternates": "",
+ "name": "microphone",
+ "shortname": ":microphone:",
+ "category": "activity",
+ "emoji_order": "461",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "instruments"
+ ]
+ },
+ "headphones": {
+ "unicode": "1f3a7",
+ "unicode_alternates": "",
+ "name": "headphone",
+ "shortname": ":headphones:",
+ "category": "activity",
+ "emoji_order": "462",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "instruments"
+ ]
+ },
+ "musical_score": {
+ "unicode": "1f3bc",
+ "unicode_alternates": "",
+ "name": "musical score",
+ "shortname": ":musical_score:",
+ "category": "activity",
+ "emoji_order": "463",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "instruments"
+ ]
+ },
+ "musical_keyboard": {
+ "unicode": "1f3b9",
+ "unicode_alternates": "",
+ "name": "musical keyboard",
+ "shortname": ":musical_keyboard:",
+ "category": "activity",
+ "emoji_order": "464",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "instruments"
+ ]
+ },
+ "saxophone": {
+ "unicode": "1f3b7",
+ "unicode_alternates": "",
+ "name": "saxophone",
+ "shortname": ":saxophone:",
+ "category": "activity",
+ "emoji_order": "465",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "instruments"
+ ]
+ },
+ "trumpet": {
+ "unicode": "1f3ba",
+ "unicode_alternates": "",
+ "name": "trumpet",
+ "shortname": ":trumpet:",
+ "category": "activity",
+ "emoji_order": "466",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "instruments"
+ ]
+ },
+ "guitar": {
+ "unicode": "1f3b8",
+ "unicode_alternates": "",
+ "name": "guitar",
+ "shortname": ":guitar:",
+ "category": "activity",
+ "emoji_order": "467",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "instruments"
+ ]
+ },
+ "violin": {
+ "unicode": "1f3bb",
+ "unicode_alternates": "",
+ "name": "violin",
+ "shortname": ":violin:",
+ "category": "activity",
+ "emoji_order": "468",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "instruments",
+ "sarcastic",
+ "sarcastic"
+ ]
+ },
+ "clapper": {
+ "unicode": "1f3ac",
+ "unicode_alternates": "",
+ "name": "clapper board",
+ "shortname": ":clapper:",
+ "category": "activity",
+ "emoji_order": "469",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "movie"
+ ]
+ },
+ "video_game": {
+ "unicode": "1f3ae",
+ "unicode_alternates": "",
+ "name": "video game",
+ "shortname": ":video_game:",
+ "category": "activity",
+ "emoji_order": "470",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "electronics",
+ "game",
+ "boys night",
+ "boys night"
+ ]
+ },
+ "space_invader": {
+ "unicode": "1f47e",
+ "unicode_alternates": "",
+ "name": "alien monster",
+ "shortname": ":space_invader:",
+ "category": "activity",
+ "emoji_order": "471",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "monster",
+ "alien"
+ ]
+ },
+ "dart": {
+ "unicode": "1f3af",
+ "unicode_alternates": "",
+ "name": "direct hit",
+ "shortname": ":dart:",
+ "category": "activity",
+ "emoji_order": "472",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "game",
+ "sport",
+ "boys night",
+ "boys night"
+ ]
+ },
+ "game_die": {
+ "unicode": "1f3b2",
+ "unicode_alternates": "",
+ "name": "game die",
+ "shortname": ":game_die:",
+ "category": "activity",
+ "emoji_order": "473",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "game",
+ "boys night",
+ "boys night"
+ ]
+ },
+ "slot_machine": {
+ "unicode": "1f3b0",
+ "unicode_alternates": "",
+ "name": "slot machine",
+ "shortname": ":slot_machine:",
+ "category": "activity",
+ "emoji_order": "474",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "game",
+ "boys night",
+ "boys night"
+ ]
+ },
+ "bowling": {
+ "unicode": "1f3b3",
+ "unicode_alternates": "",
+ "name": "bowling",
+ "shortname": ":bowling:",
+ "category": "activity",
+ "emoji_order": "475",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "game",
+ "ball",
+ "sport",
+ "boys night",
+ "boys night"
+ ]
+ },
+ "red_car": {
+ "unicode": "1f697",
+ "unicode_alternates": "",
+ "name": "automobile",
+ "shortname": ":red_car:",
+ "category": "travel",
+ "emoji_order": "476",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "transportation",
+ "car",
+ "travel"
+ ]
+ },
+ "taxi": {
+ "unicode": "1f695",
+ "unicode_alternates": "",
+ "name": "taxi",
+ "shortname": ":taxi:",
+ "category": "travel",
+ "emoji_order": "477",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "transportation",
+ "car",
+ "travel"
+ ]
+ },
+ "blue_car": {
+ "unicode": "1f699",
+ "unicode_alternates": "",
+ "name": "recreational vehicle",
+ "shortname": ":blue_car:",
+ "category": "travel",
+ "emoji_order": "478",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "transportation",
+ "car",
+ "travel"
+ ]
+ },
+ "bus": {
+ "unicode": "1f68c",
+ "unicode_alternates": "",
+ "name": "bus",
+ "shortname": ":bus:",
+ "category": "travel",
+ "emoji_order": "479",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "transportation",
+ "bus",
+ "office"
+ ]
+ },
+ "trolleybus": {
+ "unicode": "1f68e",
+ "unicode_alternates": "",
+ "name": "trolleybus",
+ "shortname": ":trolleybus:",
+ "category": "travel",
+ "emoji_order": "480",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "transportation",
+ "bus",
+ "travel"
+ ]
+ },
+ "race_car": {
+ "unicode": "1f3ce",
+ "unicode_alternates": "1f3ce-fe0f",
+ "name": "racing car",
+ "shortname": ":race_car:",
+ "category": "travel",
+ "emoji_order": "481",
+ "aliases": [
+ ":racing_car:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "transportation",
+ "car"
+ ]
+ },
+ "police_car": {
+ "unicode": "1f693",
+ "unicode_alternates": "",
+ "name": "police car",
+ "shortname": ":police_car:",
+ "category": "travel",
+ "emoji_order": "482",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "transportation",
+ "car",
+ "police",
+ "police",
+ "911",
+ "911"
+ ]
+ },
+ "ambulance": {
+ "unicode": "1f691",
+ "unicode_alternates": "",
+ "name": "ambulance",
+ "shortname": ":ambulance:",
+ "category": "travel",
+ "emoji_order": "483",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "transportation",
+ "911",
+ "911"
+ ]
+ },
+ "fire_engine": {
+ "unicode": "1f692",
+ "unicode_alternates": "",
+ "name": "fire engine",
+ "shortname": ":fire_engine:",
+ "category": "travel",
+ "emoji_order": "484",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "transportation",
+ "truck",
+ "911",
+ "911"
+ ]
+ },
+ "minibus": {
+ "unicode": "1f690",
+ "unicode_alternates": "",
+ "name": "minibus",
+ "shortname": ":minibus:",
+ "category": "travel",
+ "emoji_order": "485",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "transportation",
+ "bus"
+ ]
+ },
+ "truck": {
+ "unicode": "1f69a",
+ "unicode_alternates": "",
+ "name": "delivery truck",
+ "shortname": ":truck:",
+ "category": "travel",
+ "emoji_order": "486",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "transportation",
+ "truck"
+ ]
+ },
+ "articulated_lorry": {
+ "unicode": "1f69b",
+ "unicode_alternates": "",
+ "name": "articulated lorry",
+ "shortname": ":articulated_lorry:",
+ "category": "travel",
+ "emoji_order": "487",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "transportation",
+ "truck"
+ ]
+ },
+ "tractor": {
+ "unicode": "1f69c",
+ "unicode_alternates": "",
+ "name": "tractor",
+ "shortname": ":tractor:",
+ "category": "travel",
+ "emoji_order": "488",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "transportation"
+ ]
+ },
+ "motorcycle": {
+ "unicode": "1f3cd",
+ "unicode_alternates": "1f3cd-fe0f",
+ "name": "racing motorcycle",
+ "shortname": ":motorcycle:",
+ "category": "travel",
+ "emoji_order": "489",
+ "aliases": [
+ ":racing_motorcycle:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "transportation",
+ "travel",
+ "bike"
+ ]
+ },
+ "bike": {
+ "unicode": "1f6b2",
+ "unicode_alternates": "",
+ "name": "bicycle",
+ "shortname": ":bike:",
+ "category": "travel",
+ "emoji_order": "490",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "transportation",
+ "travel",
+ "bike"
+ ]
+ },
+ "rotating_light": {
+ "unicode": "1f6a8",
+ "unicode_alternates": "",
+ "name": "police cars revolving light",
+ "shortname": ":rotating_light:",
+ "category": "travel",
+ "emoji_order": "491",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "transportation",
+ "object",
+ "police",
+ "police",
+ "911",
+ "911"
+ ]
+ },
+ "oncoming_police_car": {
+ "unicode": "1f694",
+ "unicode_alternates": "",
+ "name": "oncoming police car",
+ "shortname": ":oncoming_police_car:",
+ "category": "travel",
+ "emoji_order": "492",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "transportation",
+ "car",
+ "police",
+ "police",
+ "911",
+ "911"
+ ]
+ },
+ "oncoming_bus": {
+ "unicode": "1f68d",
+ "unicode_alternates": "",
+ "name": "oncoming bus",
+ "shortname": ":oncoming_bus:",
+ "category": "travel",
+ "emoji_order": "493",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "transportation",
+ "bus",
+ "travel"
+ ]
+ },
+ "oncoming_automobile": {
+ "unicode": "1f698",
+ "unicode_alternates": "",
+ "name": "oncoming automobile",
+ "shortname": ":oncoming_automobile:",
+ "category": "travel",
+ "emoji_order": "494",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "transportation",
+ "car",
+ "travel"
+ ]
+ },
+ "oncoming_taxi": {
+ "unicode": "1f696",
+ "unicode_alternates": "",
+ "name": "oncoming taxi",
+ "shortname": ":oncoming_taxi:",
+ "category": "travel",
+ "emoji_order": "495",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "transportation",
+ "car",
+ "travel"
+ ]
+ },
+ "aerial_tramway": {
+ "unicode": "1f6a1",
+ "unicode_alternates": "",
+ "name": "aerial tramway",
+ "shortname": ":aerial_tramway:",
+ "category": "travel",
+ "emoji_order": "496",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "transportation",
+ "travel",
+ "train"
+ ]
+ },
+ "mountain_cableway": {
+ "unicode": "1f6a0",
+ "unicode_alternates": "",
+ "name": "mountain cableway",
+ "shortname": ":mountain_cableway:",
+ "category": "travel",
+ "emoji_order": "497",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "transportation",
+ "travel",
+ "train"
+ ]
+ },
+ "suspension_railway": {
+ "unicode": "1f69f",
+ "unicode_alternates": "",
+ "name": "suspension railway",
+ "shortname": ":suspension_railway:",
+ "category": "travel",
+ "emoji_order": "498",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "transportation",
+ "travel",
+ "train"
+ ]
+ },
+ "railway_car": {
+ "unicode": "1f683",
+ "unicode_alternates": "",
+ "name": "railway car",
+ "shortname": ":railway_car:",
+ "category": "travel",
+ "emoji_order": "499",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "transportation",
+ "travel",
+ "train"
+ ]
+ },
+ "train": {
+ "unicode": "1f68b",
+ "unicode_alternates": "",
+ "name": "tram car",
+ "shortname": ":train:",
+ "category": "travel",
+ "emoji_order": "500",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "transportation",
+ "travel",
+ "train"
+ ]
+ },
+ "monorail": {
+ "unicode": "1f69d",
+ "unicode_alternates": "",
+ "name": "monorail",
+ "shortname": ":monorail:",
+ "category": "travel",
+ "emoji_order": "501",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "transportation",
+ "travel",
+ "train",
+ "vacation"
+ ]
+ },
+ "bullettrain_side": {
+ "unicode": "1f684",
+ "unicode_alternates": "",
+ "name": "high-speed train",
+ "shortname": ":bullettrain_side:",
+ "category": "travel",
+ "emoji_order": "502",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "transportation",
+ "travel",
+ "train"
+ ]
+ },
+ "bullettrain_front": {
+ "unicode": "1f685",
+ "unicode_alternates": "",
+ "name": "high-speed train with bullet nose",
+ "shortname": ":bullettrain_front:",
+ "category": "travel",
+ "emoji_order": "503",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "transportation",
+ "travel",
+ "train"
+ ]
+ },
+ "light_rail": {
+ "unicode": "1f688",
+ "unicode_alternates": "",
+ "name": "light rail",
+ "shortname": ":light_rail:",
+ "category": "travel",
+ "emoji_order": "504",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "transportation",
+ "travel",
+ "train"
+ ]
+ },
+ "mountain_railway": {
+ "unicode": "1f69e",
+ "unicode_alternates": "",
+ "name": "mountain railway",
+ "shortname": ":mountain_railway:",
+ "category": "travel",
+ "emoji_order": "505",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "transportation",
+ "travel",
+ "train"
+ ]
+ },
+ "steam_locomotive": {
+ "unicode": "1f682",
+ "unicode_alternates": "",
+ "name": "steam locomotive",
+ "shortname": ":steam_locomotive:",
+ "category": "travel",
+ "emoji_order": "506",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "transportation",
+ "travel",
+ "train",
+ "steam",
+ "steam"
+ ]
+ },
+ "train2": {
+ "unicode": "1f686",
+ "unicode_alternates": "",
+ "name": "train",
+ "shortname": ":train2:",
+ "category": "travel",
+ "emoji_order": "507",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "transportation",
+ "travel",
+ "train"
+ ]
+ },
+ "metro": {
+ "unicode": "1f687",
+ "unicode_alternates": "",
+ "name": "metro",
+ "shortname": ":metro:",
+ "category": "travel",
+ "emoji_order": "508",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "transportation",
+ "travel",
+ "train"
+ ]
+ },
+ "tram": {
+ "unicode": "1f68a",
+ "unicode_alternates": "",
+ "name": "tram",
+ "shortname": ":tram:",
+ "category": "travel",
+ "emoji_order": "509",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "transportation",
+ "travel",
+ "train"
+ ]
+ },
+ "station": {
+ "unicode": "1f689",
+ "unicode_alternates": "",
+ "name": "station",
+ "shortname": ":station:",
+ "category": "travel",
+ "emoji_order": "510",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "transportation",
+ "travel",
+ "train"
+ ]
+ },
+ "helicopter": {
+ "unicode": "1f681",
+ "unicode_alternates": "",
+ "name": "helicopter",
+ "shortname": ":helicopter:",
+ "category": "travel",
+ "emoji_order": "511",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "transportation",
+ "plane",
+ "travel",
+ "fly",
+ "fly"
+ ]
+ },
+ "airplane_small": {
+ "unicode": "1f6e9",
+ "unicode_alternates": "1f6e9-fe0f",
+ "name": "small airplane",
+ "shortname": ":airplane_small:",
+ "category": "travel",
+ "emoji_order": "512",
+ "aliases": [
+ ":small_airplane:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "transportation",
+ "plane",
+ "travel",
+ "vacation",
+ "fly",
+ "fly"
+ ]
+ },
+ "airplane": {
+ "unicode": "2708",
+ "unicode_alternates": "2708-fe0f",
+ "name": "airplane",
+ "shortname": ":airplane:",
+ "category": "travel",
+ "emoji_order": "513",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "transportation",
+ "plane",
+ "travel",
+ "vacation",
+ "fly",
+ "fly"
+ ]
+ },
+ "airplane_departure": {
+ "unicode": "1f6eb",
+ "unicode_alternates": "",
+ "name": "airplane departure",
+ "shortname": ":airplane_departure:",
+ "category": "travel",
+ "emoji_order": "514",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "transportation",
+ "plane",
+ "travel",
+ "vacation",
+ "fly",
+ "fly"
+ ]
+ },
+ "airplane_arriving": {
+ "unicode": "1f6ec",
+ "unicode_alternates": "",
+ "name": "airplane arriving",
+ "shortname": ":airplane_arriving:",
+ "category": "travel",
+ "emoji_order": "515",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "transportation",
+ "plane",
+ "travel",
+ "vacation",
+ "fly",
+ "fly"
+ ]
+ },
+ "sailboat": {
+ "unicode": "26f5",
+ "unicode_alternates": "26f5-fe0f",
+ "name": "sailboat",
+ "shortname": ":sailboat:",
+ "category": "travel",
+ "emoji_order": "516",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "transportation",
+ "travel",
+ "boat",
+ "vacation"
+ ]
+ },
+ "motorboat": {
+ "unicode": "1f6e5",
+ "unicode_alternates": "1f6e5-fe0f",
+ "name": "motorboat",
+ "shortname": ":motorboat:",
+ "category": "travel",
+ "emoji_order": "517",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "transportation",
+ "travel",
+ "boat"
+ ]
+ },
+ "speedboat": {
+ "unicode": "1f6a4",
+ "unicode_alternates": "",
+ "name": "speedboat",
+ "shortname": ":speedboat:",
+ "category": "travel",
+ "emoji_order": "518",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "transportation",
+ "travel",
+ "boat",
+ "vacation",
+ "tropical"
+ ]
+ },
+ "ferry": {
+ "unicode": "26f4",
+ "unicode_alternates": "26f4-fe0f",
+ "name": "ferry",
+ "shortname": ":ferry:",
+ "category": "travel",
+ "emoji_order": "519",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "transportation",
+ "travel",
+ "boat",
+ "vacation"
+ ]
+ },
+ "cruise_ship": {
+ "unicode": "1f6f3",
+ "unicode_alternates": "1f6f3-fe0f",
+ "name": "passenger ship",
+ "shortname": ":cruise_ship:",
+ "category": "travel",
+ "emoji_order": "520",
+ "aliases": [
+ ":passenger_ship:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "transportation",
+ "travel",
+ "boat",
+ "vacation"
+ ]
+ },
+ "rocket": {
+ "unicode": "1f680",
+ "unicode_alternates": "",
+ "name": "rocket",
+ "shortname": ":rocket:",
+ "category": "travel",
+ "emoji_order": "521",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "transportation",
+ "object",
+ "space",
+ "fly",
+ "fly",
+ "blast",
+ "blast"
+ ]
+ },
+ "satellite_orbital": {
+ "unicode": "1f6f0",
+ "unicode_alternates": "1f6f0-fe0f",
+ "name": "satellite",
+ "shortname": ":satellite_orbital:",
+ "category": "travel",
+ "emoji_order": "522",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object"
+ ]
+ },
+ "seat": {
+ "unicode": "1f4ba",
+ "unicode_alternates": "",
+ "name": "seat",
+ "shortname": ":seat:",
+ "category": "travel",
+ "emoji_order": "523",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "transportation",
+ "object",
+ "travel",
+ "vacation"
+ ]
+ },
+ "anchor": {
+ "unicode": "2693",
+ "unicode_alternates": "2693-fe0f",
+ "name": "anchor",
+ "shortname": ":anchor:",
+ "category": "travel",
+ "emoji_order": "524",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "travel",
+ "boat",
+ "vacation"
+ ]
+ },
+ "construction": {
+ "unicode": "1f6a7",
+ "unicode_alternates": "",
+ "name": "construction sign",
+ "shortname": ":construction:",
+ "category": "travel",
+ "emoji_order": "525",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object"
+ ]
+ },
+ "fuelpump": {
+ "unicode": "26fd",
+ "unicode_alternates": "26fd-fe0f",
+ "name": "fuel pump",
+ "shortname": ":fuelpump:",
+ "category": "travel",
+ "emoji_order": "526",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "gas pump"
+ ]
+ },
+ "busstop": {
+ "unicode": "1f68f",
+ "unicode_alternates": "",
+ "name": "bus stop",
+ "shortname": ":busstop:",
+ "category": "travel",
+ "emoji_order": "527",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object"
+ ]
+ },
+ "vertical_traffic_light": {
+ "unicode": "1f6a6",
+ "unicode_alternates": "",
+ "name": "vertical traffic light",
+ "shortname": ":vertical_traffic_light:",
+ "category": "travel",
+ "emoji_order": "528",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "stop light"
+ ]
+ },
+ "traffic_light": {
+ "unicode": "1f6a5",
+ "unicode_alternates": "",
+ "name": "horizontal traffic light",
+ "shortname": ":traffic_light:",
+ "category": "travel",
+ "emoji_order": "529",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "stop light"
+ ]
+ },
+ "checkered_flag": {
+ "unicode": "1f3c1",
+ "unicode_alternates": "",
+ "name": "chequered flag",
+ "shortname": ":checkered_flag:",
+ "category": "travel",
+ "emoji_order": "530",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object"
+ ]
+ },
+ "ship": {
+ "unicode": "1f6a2",
+ "unicode_alternates": "",
+ "name": "ship",
+ "shortname": ":ship:",
+ "category": "travel",
+ "emoji_order": "531",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "transportation",
+ "travel",
+ "boat",
+ "vacation"
+ ]
+ },
+ "ferris_wheel": {
+ "unicode": "1f3a1",
+ "unicode_alternates": "",
+ "name": "ferris wheel",
+ "shortname": ":ferris_wheel:",
+ "category": "travel",
+ "emoji_order": "532",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "places",
+ "vacation",
+ "ferris wheel"
+ ]
+ },
+ "roller_coaster": {
+ "unicode": "1f3a2",
+ "unicode_alternates": "",
+ "name": "roller coaster",
+ "shortname": ":roller_coaster:",
+ "category": "travel",
+ "emoji_order": "533",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "places",
+ "vacation",
+ "roller coaster"
+ ]
+ },
+ "carousel_horse": {
+ "unicode": "1f3a0",
+ "unicode_alternates": "",
+ "name": "carousel horse",
+ "shortname": ":carousel_horse:",
+ "category": "travel",
+ "emoji_order": "534",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "places",
+ "object",
+ "vacation",
+ "roller coaster",
+ "carousel"
+ ]
+ },
+ "construction_site": {
+ "unicode": "1f3d7",
+ "unicode_alternates": "1f3d7-fe0f",
+ "name": "building construction",
+ "shortname": ":construction_site:",
+ "category": "travel",
+ "emoji_order": "535",
+ "aliases": [
+ ":building_construction:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "building",
+ "crane"
+ ]
+ },
+ "foggy": {
+ "unicode": "1f301",
+ "unicode_alternates": "",
+ "name": "foggy",
+ "shortname": ":foggy:",
+ "category": "travel",
+ "emoji_order": "536",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "places",
+ "building",
+ "sky",
+ "travel",
+ "vacation"
+ ]
+ },
+ "tokyo_tower": {
+ "unicode": "1f5fc",
+ "unicode_alternates": "",
+ "name": "tokyo tower",
+ "shortname": ":tokyo_tower:",
+ "category": "travel",
+ "emoji_order": "537",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "places",
+ "travel",
+ "vacation",
+ "eiffel tower"
+ ]
+ },
+ "factory": {
+ "unicode": "1f3ed",
+ "unicode_alternates": "",
+ "name": "factory",
+ "shortname": ":factory:",
+ "category": "travel",
+ "emoji_order": "538",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "places",
+ "building",
+ "travel",
+ "steam",
+ "steam"
+ ]
+ },
+ "fountain": {
+ "unicode": "26f2",
+ "unicode_alternates": "26f2-fe0f",
+ "name": "fountain",
+ "shortname": ":fountain:",
+ "category": "travel",
+ "emoji_order": "539",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "travel",
+ "vacation"
+ ]
+ },
+ "rice_scene": {
+ "unicode": "1f391",
+ "unicode_alternates": "",
+ "name": "moon viewing ceremony",
+ "shortname": ":rice_scene:",
+ "category": "travel",
+ "emoji_order": "540",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "places",
+ "space",
+ "sky",
+ "travel"
+ ]
+ },
+ "mountain": {
+ "unicode": "26f0",
+ "unicode_alternates": "26f0-fe0f",
+ "name": "mountain",
+ "shortname": ":mountain:",
+ "category": "travel",
+ "emoji_order": "541",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "places",
+ "travel",
+ "vacation",
+ "camp"
+ ]
+ },
+ "mountain_snow": {
+ "unicode": "1f3d4",
+ "unicode_alternates": "1f3d4-fe0f",
+ "name": "snow capped mountain",
+ "shortname": ":mountain_snow:",
+ "category": "travel",
+ "emoji_order": "542",
+ "aliases": [
+ ":snow_capped_mountain:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "places",
+ "travel",
+ "vacation",
+ "cold",
+ "camp"
+ ]
+ },
+ "mount_fuji": {
+ "unicode": "1f5fb",
+ "unicode_alternates": "",
+ "name": "mount fuji",
+ "shortname": ":mount_fuji:",
+ "category": "travel",
+ "emoji_order": "543",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "places",
+ "travel",
+ "vacation",
+ "cold",
+ "camp"
+ ]
+ },
+ "volcano": {
+ "unicode": "1f30b",
+ "unicode_alternates": "",
+ "name": "volcano",
+ "shortname": ":volcano:",
+ "category": "travel",
+ "emoji_order": "544",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "places",
+ "tropical"
+ ]
+ },
+ "japan": {
+ "unicode": "1f5fe",
+ "unicode_alternates": "",
+ "name": "silhouette of japan",
+ "shortname": ":japan:",
+ "category": "travel",
+ "emoji_order": "545",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "places",
+ "travel",
+ "map",
+ "vacation",
+ "tropical"
+ ]
+ },
+ "camping": {
+ "unicode": "1f3d5",
+ "unicode_alternates": "1f3d5-fe0f",
+ "name": "camping",
+ "shortname": ":camping:",
+ "category": "travel",
+ "emoji_order": "546",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "places",
+ "travel",
+ "vacation",
+ "camp"
+ ]
+ },
+ "tent": {
+ "unicode": "26fa",
+ "unicode_alternates": "26fa-fe0f",
+ "name": "tent",
+ "shortname": ":tent:",
+ "category": "travel",
+ "emoji_order": "547",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "places",
+ "travel",
+ "vacation",
+ "camp"
+ ]
+ },
+ "park": {
+ "unicode": "1f3de",
+ "unicode_alternates": "1f3de-fe0f",
+ "name": "national park",
+ "shortname": ":park:",
+ "category": "travel",
+ "emoji_order": "548",
+ "aliases": [
+ ":national_park:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "travel",
+ "vacation",
+ "park",
+ "camp"
+ ]
+ },
+ "motorway": {
+ "unicode": "1f6e3",
+ "unicode_alternates": "1f6e3-fe0f",
+ "name": "motorway",
+ "shortname": ":motorway:",
+ "category": "travel",
+ "emoji_order": "549",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "travel",
+ "vacation",
+ "camp"
+ ]
+ },
+ "railway_track": {
+ "unicode": "1f6e4",
+ "unicode_alternates": "1f6e4-fe0f",
+ "name": "railway track",
+ "shortname": ":railway_track:",
+ "category": "travel",
+ "emoji_order": "550",
+ "aliases": [
+ ":railroad_track:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "travel",
+ "train",
+ "vacation"
+ ]
+ },
+ "sunrise": {
+ "unicode": "1f305",
+ "unicode_alternates": "",
+ "name": "sunrise",
+ "shortname": ":sunrise:",
+ "category": "travel",
+ "emoji_order": "551",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "places",
+ "sky",
+ "travel",
+ "vacation",
+ "tropical",
+ "day",
+ "sun",
+ "hump day",
+ "hump day",
+ "morning",
+ "morning"
+ ]
+ },
+ "sunrise_over_mountains": {
+ "unicode": "1f304",
+ "unicode_alternates": "",
+ "name": "sunrise over mountains",
+ "shortname": ":sunrise_over_mountains:",
+ "category": "travel",
+ "emoji_order": "552",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "places",
+ "sky",
+ "travel",
+ "vacation",
+ "day",
+ "sun",
+ "camp",
+ "morning",
+ "morning"
+ ]
+ },
+ "desert": {
+ "unicode": "1f3dc",
+ "unicode_alternates": "1f3dc-fe0f",
+ "name": "desert",
+ "shortname": ":desert:",
+ "category": "travel",
+ "emoji_order": "553",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "places",
+ "travel",
+ "vacation",
+ "hot",
+ "hot"
+ ]
+ },
+ "beach": {
+ "unicode": "1f3d6",
+ "unicode_alternates": "1f3d6-fe0f",
+ "name": "beach with umbrella",
+ "shortname": ":beach:",
+ "category": "travel",
+ "emoji_order": "554",
+ "aliases": [
+ ":beach_with_umbrella:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "places",
+ "travel",
+ "vacation",
+ "tropical",
+ "beach",
+ "swim"
+ ]
+ },
+ "island": {
+ "unicode": "1f3dd",
+ "unicode_alternates": "1f3dd-fe0f",
+ "name": "desert island",
+ "shortname": ":island:",
+ "category": "travel",
+ "emoji_order": "555",
+ "aliases": [
+ ":desert_island:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "places",
+ "travel",
+ "vacation",
+ "tropical",
+ "beach",
+ "swim"
+ ]
+ },
+ "city_sunset": {
+ "unicode": "1f307",
+ "unicode_alternates": "",
+ "name": "sunset over buildings",
+ "shortname": ":city_sunset:",
+ "category": "travel",
+ "emoji_order": "556",
+ "aliases": [
+ ":city_sunrise:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "places",
+ "building",
+ "sky",
+ "vacation"
+ ]
+ },
+ "city_dusk": {
+ "unicode": "1f306",
+ "unicode_alternates": "",
+ "name": "cityscape at dusk",
+ "shortname": ":city_dusk:",
+ "category": "travel",
+ "emoji_order": "557",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "places",
+ "building"
+ ]
+ },
+ "cityscape": {
+ "unicode": "1f3d9",
+ "unicode_alternates": "1f3d9-fe0f",
+ "name": "cityscape",
+ "shortname": ":cityscape:",
+ "category": "travel",
+ "emoji_order": "558",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "places",
+ "building",
+ "vacation"
+ ]
+ },
+ "night_with_stars": {
+ "unicode": "1f303",
+ "unicode_alternates": "",
+ "name": "night with stars",
+ "shortname": ":night_with_stars:",
+ "category": "travel",
+ "emoji_order": "559",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "places",
+ "building",
+ "sky",
+ "vacation",
+ "goodnight",
+ "goodnight"
+ ]
+ },
+ "bridge_at_night": {
+ "unicode": "1f309",
+ "unicode_alternates": "",
+ "name": "bridge at night",
+ "shortname": ":bridge_at_night:",
+ "category": "travel",
+ "emoji_order": "560",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "places",
+ "travel",
+ "vacation",
+ "goodnight",
+ "goodnight"
+ ]
+ },
+ "milky_way": {
+ "unicode": "1f30c",
+ "unicode_alternates": "",
+ "name": "milky way",
+ "shortname": ":milky_way:",
+ "category": "travel",
+ "emoji_order": "561",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "places",
+ "space",
+ "sky",
+ "travel",
+ "vacation"
+ ]
+ },
+ "stars": {
+ "unicode": "1f320",
+ "unicode_alternates": "",
+ "name": "shooting star",
+ "shortname": ":stars:",
+ "category": "travel",
+ "emoji_order": "562",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "space"
+ ]
+ },
+ "sparkler": {
+ "unicode": "1f387",
+ "unicode_alternates": "",
+ "name": "firework sparkler",
+ "shortname": ":sparkler:",
+ "category": "travel",
+ "emoji_order": "563",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "parties",
+ "parties"
+ ]
+ },
+ "fireworks": {
+ "unicode": "1f386",
+ "unicode_alternates": "",
+ "name": "fireworks",
+ "shortname": ":fireworks:",
+ "category": "travel",
+ "emoji_order": "564",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "parties",
+ "parties"
+ ]
+ },
+ "rainbow": {
+ "unicode": "1f308",
+ "unicode_alternates": "",
+ "name": "rainbow",
+ "shortname": ":rainbow:",
+ "category": "travel",
+ "emoji_order": "565",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "weather",
+ "gay",
+ "sky",
+ "rain"
+ ]
+ },
+ "homes": {
+ "unicode": "1f3d8",
+ "unicode_alternates": "1f3d8-fe0f",
+ "name": "house buildings",
+ "shortname": ":homes:",
+ "category": "travel",
+ "emoji_order": "566",
+ "aliases": [
+ ":house_buildings:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "places",
+ "building",
+ "house"
+ ]
+ },
+ "european_castle": {
+ "unicode": "1f3f0",
+ "unicode_alternates": "",
+ "name": "european castle",
+ "shortname": ":european_castle:",
+ "category": "travel",
+ "emoji_order": "567",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "places",
+ "building",
+ "travel",
+ "vacation"
+ ]
+ },
+ "japanese_castle": {
+ "unicode": "1f3ef",
+ "unicode_alternates": "",
+ "name": "japanese castle",
+ "shortname": ":japanese_castle:",
+ "category": "travel",
+ "emoji_order": "568",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "places",
+ "building",
+ "travel",
+ "vacation"
+ ]
+ },
+ "stadium": {
+ "unicode": "1f3df",
+ "unicode_alternates": "1f3df-fe0f",
+ "name": "stadium",
+ "shortname": ":stadium:",
+ "category": "travel",
+ "emoji_order": "569",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "places",
+ "building",
+ "travel",
+ "vacation",
+ "boys night",
+ "boys night"
+ ]
+ },
+ "statue_of_liberty": {
+ "unicode": "1f5fd",
+ "unicode_alternates": "",
+ "name": "statue of liberty",
+ "shortname": ":statue_of_liberty:",
+ "category": "travel",
+ "emoji_order": "570",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "places",
+ "america",
+ "travel",
+ "vacation",
+ "statue of liberty",
+ "free speech",
+ "free speech"
+ ]
+ },
+ "house": {
+ "unicode": "1f3e0",
+ "unicode_alternates": "",
+ "name": "house building",
+ "shortname": ":house:",
+ "category": "travel",
+ "emoji_order": "571",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "places",
+ "building",
+ "house"
+ ]
+ },
+ "house_with_garden": {
+ "unicode": "1f3e1",
+ "unicode_alternates": "",
+ "name": "house with garden",
+ "shortname": ":house_with_garden:",
+ "category": "travel",
+ "emoji_order": "572",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "places",
+ "building",
+ "house"
+ ]
+ },
+ "house_abandoned": {
+ "unicode": "1f3da",
+ "unicode_alternates": "1f3da-fe0f",
+ "name": "derelict house building",
+ "shortname": ":house_abandoned:",
+ "category": "travel",
+ "emoji_order": "573",
+ "aliases": [
+ ":derelict_house_building:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "places",
+ "building",
+ "house"
+ ]
+ },
+ "office": {
+ "unicode": "1f3e2",
+ "unicode_alternates": "",
+ "name": "office building",
+ "shortname": ":office:",
+ "category": "travel",
+ "emoji_order": "574",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "places",
+ "building",
+ "work"
+ ]
+ },
+ "department_store": {
+ "unicode": "1f3ec",
+ "unicode_alternates": "",
+ "name": "department store",
+ "shortname": ":department_store:",
+ "category": "travel",
+ "emoji_order": "575",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "places",
+ "building"
+ ]
+ },
+ "post_office": {
+ "unicode": "1f3e3",
+ "unicode_alternates": "",
+ "name": "japanese post office",
+ "shortname": ":post_office:",
+ "category": "travel",
+ "emoji_order": "576",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "places",
+ "building",
+ "post office"
+ ]
+ },
+ "european_post_office": {
+ "unicode": "1f3e4",
+ "unicode_alternates": "",
+ "name": "european post office",
+ "shortname": ":european_post_office:",
+ "category": "travel",
+ "emoji_order": "577",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "places",
+ "building",
+ "post office"
+ ]
+ },
+ "hospital": {
+ "unicode": "1f3e5",
+ "unicode_alternates": "",
+ "name": "hospital",
+ "shortname": ":hospital:",
+ "category": "travel",
+ "emoji_order": "578",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "places",
+ "building",
+ "health",
+ "911",
+ "911"
+ ]
+ },
+ "bank": {
+ "unicode": "1f3e6",
+ "unicode_alternates": "",
+ "name": "bank",
+ "shortname": ":bank:",
+ "category": "travel",
+ "emoji_order": "579",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "places",
+ "building"
+ ]
+ },
+ "hotel": {
+ "unicode": "1f3e8",
+ "unicode_alternates": "",
+ "name": "hotel",
+ "shortname": ":hotel:",
+ "category": "travel",
+ "emoji_order": "580",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "places",
+ "building",
+ "vacation"
+ ]
+ },
+ "convenience_store": {
+ "unicode": "1f3ea",
+ "unicode_alternates": "",
+ "name": "convenience store",
+ "shortname": ":convenience_store:",
+ "category": "travel",
+ "emoji_order": "581",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "places",
+ "building"
+ ]
+ },
+ "school": {
+ "unicode": "1f3eb",
+ "unicode_alternates": "",
+ "name": "school",
+ "shortname": ":school:",
+ "category": "travel",
+ "emoji_order": "582",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "places",
+ "building"
+ ]
+ },
+ "love_hotel": {
+ "unicode": "1f3e9",
+ "unicode_alternates": "",
+ "name": "love hotel",
+ "shortname": ":love_hotel:",
+ "category": "travel",
+ "emoji_order": "583",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "places",
+ "building",
+ "love"
+ ]
+ },
+ "wedding": {
+ "unicode": "1f492",
+ "unicode_alternates": "",
+ "name": "wedding",
+ "shortname": ":wedding:",
+ "category": "travel",
+ "emoji_order": "584",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "places",
+ "wedding",
+ "building",
+ "love",
+ "parties",
+ "parties"
+ ]
+ },
+ "classical_building": {
+ "unicode": "1f3db",
+ "unicode_alternates": "1f3db-fe0f",
+ "name": "classical building",
+ "shortname": ":classical_building:",
+ "category": "travel",
+ "emoji_order": "585",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "places",
+ "building",
+ "travel",
+ "vacation"
+ ]
+ },
+ "church": {
+ "unicode": "26ea",
+ "unicode_alternates": "26ea-fe0f",
+ "name": "church",
+ "shortname": ":church:",
+ "category": "travel",
+ "emoji_order": "586",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "places",
+ "wedding",
+ "religion",
+ "building",
+ "condolence",
+ "condolence"
+ ]
+ },
+ "mosque": {
+ "unicode": "1f54c",
+ "unicode_alternates": "",
+ "name": "mosque",
+ "shortname": ":mosque:",
+ "category": "travel",
+ "emoji_order": "587",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "places",
+ "religion",
+ "building",
+ "vacation",
+ "condolence",
+ "condolence"
+ ]
+ },
+ "synagogue": {
+ "unicode": "1f54d",
+ "unicode_alternates": "",
+ "name": "synagogue",
+ "shortname": ":synagogue:",
+ "category": "travel",
+ "emoji_order": "588",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "places",
+ "religion",
+ "building",
+ "travel",
+ "vacation",
+ "condolence",
+ "condolence"
+ ]
+ },
+ "kaaba": {
+ "unicode": "1f54b",
+ "unicode_alternates": "",
+ "name": "kaaba",
+ "shortname": ":kaaba:",
+ "category": "travel",
+ "emoji_order": "589",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "places",
+ "religion",
+ "building",
+ "condolence",
+ "condolence"
+ ]
+ },
+ "shinto_shrine": {
+ "unicode": "26e9",
+ "unicode_alternates": "26e9-fe0f",
+ "name": "shinto shrine",
+ "shortname": ":shinto_shrine:",
+ "category": "travel",
+ "emoji_order": "590",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "places",
+ "building",
+ "travel",
+ "vacation"
+ ]
+ },
+ "watch": {
+ "unicode": "231a",
+ "unicode_alternates": "231a-fe0f",
+ "name": "watch",
+ "shortname": ":watch:",
+ "category": "objects",
+ "emoji_order": "591",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "electronics",
+ "time"
+ ]
+ },
+ "iphone": {
+ "unicode": "1f4f1",
+ "unicode_alternates": "",
+ "name": "mobile phone",
+ "shortname": ":iphone:",
+ "category": "objects",
+ "emoji_order": "592",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "electronics",
+ "phone",
+ "selfie",
+ "selfie"
+ ]
+ },
+ "calling": {
+ "unicode": "1f4f2",
+ "unicode_alternates": "",
+ "name": "mobile phone with rightwards arrow at left",
+ "shortname": ":calling:",
+ "category": "objects",
+ "emoji_order": "593",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "electronics",
+ "phone",
+ "selfie",
+ "selfie"
+ ]
+ },
+ "computer": {
+ "unicode": "1f4bb",
+ "unicode_alternates": "",
+ "name": "personal computer",
+ "shortname": ":computer:",
+ "category": "objects",
+ "emoji_order": "594",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "electronics",
+ "work",
+ "office"
+ ]
+ },
+ "keyboard": {
+ "unicode": "2328",
+ "unicode_alternates": "2328-fe0f",
+ "name": "keyboard",
+ "shortname": ":keyboard:",
+ "category": "objects",
+ "emoji_order": "595",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "electronics",
+ "work",
+ "office"
+ ]
+ },
+ "desktop": {
+ "unicode": "1f5a5",
+ "unicode_alternates": "1f5a5-fe0f",
+ "name": "desktop computer",
+ "shortname": ":desktop:",
+ "category": "objects",
+ "emoji_order": "596",
+ "aliases": [
+ ":desktop_computer:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "electronics",
+ "work"
+ ]
+ },
+ "printer": {
+ "unicode": "1f5a8",
+ "unicode_alternates": "1f5a8-fe0f",
+ "name": "printer",
+ "shortname": ":printer:",
+ "category": "objects",
+ "emoji_order": "597",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "electronics",
+ "work",
+ "office"
+ ]
+ },
+ "mouse_three_button": {
+ "unicode": "1f5b1",
+ "unicode_alternates": "1f5b1-fe0f",
+ "name": "three button mouse",
+ "shortname": ":mouse_three_button:",
+ "category": "objects",
+ "emoji_order": "598",
+ "aliases": [
+ ":three_button_mouse:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "electronics",
+ "work",
+ "game",
+ "office"
+ ]
+ },
+ "trackball": {
+ "unicode": "1f5b2",
+ "unicode_alternates": "1f5b2-fe0f",
+ "name": "trackball",
+ "shortname": ":trackball:",
+ "category": "objects",
+ "emoji_order": "599",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "electronics",
+ "work",
+ "game",
+ "office"
+ ]
+ },
+ "joystick": {
+ "unicode": "1f579",
+ "unicode_alternates": "1f579-fe0f",
+ "name": "joystick",
+ "shortname": ":joystick:",
+ "category": "objects",
+ "emoji_order": "600",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "electronics",
+ "game",
+ "boys night",
+ "boys night"
+ ]
+ },
+ "compression": {
+ "unicode": "1f5dc",
+ "unicode_alternates": "1f5dc-fe0f",
+ "name": "compression",
+ "shortname": ":compression:",
+ "category": "objects",
+ "emoji_order": "601",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "minidisc": {
+ "unicode": "1f4bd",
+ "unicode_alternates": "",
+ "name": "minidisc",
+ "shortname": ":minidisc:",
+ "category": "objects",
+ "emoji_order": "602",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "electronics"
+ ]
+ },
+ "floppy_disk": {
+ "unicode": "1f4be",
+ "unicode_alternates": "",
+ "name": "floppy disk",
+ "shortname": ":floppy_disk:",
+ "category": "objects",
+ "emoji_order": "603",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "electronics",
+ "office"
+ ]
+ },
+ "cd": {
+ "unicode": "1f4bf",
+ "unicode_alternates": "",
+ "name": "optical disc",
+ "shortname": ":cd:",
+ "category": "objects",
+ "emoji_order": "604",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "electronics"
+ ]
+ },
+ "dvd": {
+ "unicode": "1f4c0",
+ "unicode_alternates": "",
+ "name": "dvd",
+ "shortname": ":dvd:",
+ "category": "objects",
+ "emoji_order": "605",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "electronics"
+ ]
+ },
+ "vhs": {
+ "unicode": "1f4fc",
+ "unicode_alternates": "",
+ "name": "videocassette",
+ "shortname": ":vhs:",
+ "category": "objects",
+ "emoji_order": "606",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "electronics"
+ ]
+ },
+ "camera": {
+ "unicode": "1f4f7",
+ "unicode_alternates": "",
+ "name": "camera",
+ "shortname": ":camera:",
+ "category": "objects",
+ "emoji_order": "607",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "electronics",
+ "camera",
+ "selfie",
+ "selfie"
+ ]
+ },
+ "camera_with_flash": {
+ "unicode": "1f4f8",
+ "unicode_alternates": "",
+ "name": "camera with flash",
+ "shortname": ":camera_with_flash:",
+ "category": "objects",
+ "emoji_order": "608",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "electronics",
+ "camera"
+ ]
+ },
+ "video_camera": {
+ "unicode": "1f4f9",
+ "unicode_alternates": "",
+ "name": "video camera",
+ "shortname": ":video_camera:",
+ "category": "objects",
+ "emoji_order": "609",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "electronics",
+ "camera",
+ "movie"
+ ]
+ },
+ "movie_camera": {
+ "unicode": "1f3a5",
+ "unicode_alternates": "",
+ "name": "movie camera",
+ "shortname": ":movie_camera:",
+ "category": "objects",
+ "emoji_order": "610",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "camera",
+ "movie"
+ ]
+ },
+ "projector": {
+ "unicode": "1f4fd",
+ "unicode_alternates": "1f4fd-fe0f",
+ "name": "film projector",
+ "shortname": ":projector:",
+ "category": "objects",
+ "emoji_order": "611",
+ "aliases": [
+ ":film_projector:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "camera",
+ "movie"
+ ]
+ },
+ "film_frames": {
+ "unicode": "1f39e",
+ "unicode_alternates": "1f39e-fe0f",
+ "name": "film frames",
+ "shortname": ":film_frames:",
+ "category": "objects",
+ "emoji_order": "612",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "camera",
+ "movie"
+ ]
+ },
+ "telephone_receiver": {
+ "unicode": "1f4de",
+ "unicode_alternates": "",
+ "name": "telephone receiver",
+ "shortname": ":telephone_receiver:",
+ "category": "objects",
+ "emoji_order": "613",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "electronics",
+ "phone"
+ ]
+ },
+ "telephone": {
+ "unicode": "260e",
+ "unicode_alternates": "260e-fe0f",
+ "name": "black telephone",
+ "shortname": ":telephone:",
+ "category": "objects",
+ "emoji_order": "614",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "electronics",
+ "phone"
+ ]
+ },
+ "pager": {
+ "unicode": "1f4df",
+ "unicode_alternates": "",
+ "name": "pager",
+ "shortname": ":pager:",
+ "category": "objects",
+ "emoji_order": "615",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "electronics",
+ "work"
+ ]
+ },
+ "fax": {
+ "unicode": "1f4e0",
+ "unicode_alternates": "",
+ "name": "fax machine",
+ "shortname": ":fax:",
+ "category": "objects",
+ "emoji_order": "616",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "electronics",
+ "work",
+ "office"
+ ]
+ },
+ "tv": {
+ "unicode": "1f4fa",
+ "unicode_alternates": "",
+ "name": "television",
+ "shortname": ":tv:",
+ "category": "objects",
+ "emoji_order": "617",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "electronics"
+ ]
+ },
+ "radio": {
+ "unicode": "1f4fb",
+ "unicode_alternates": "",
+ "name": "radio",
+ "shortname": ":radio:",
+ "category": "objects",
+ "emoji_order": "618",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "electronics"
+ ]
+ },
+ "microphone2": {
+ "unicode": "1f399",
+ "unicode_alternates": "1f399-fe0f",
+ "name": "studio microphone",
+ "shortname": ":microphone2:",
+ "category": "objects",
+ "emoji_order": "619",
+ "aliases": [
+ ":studio_microphone:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "electronics",
+ "object"
+ ]
+ },
+ "level_slider": {
+ "unicode": "1f39a",
+ "unicode_alternates": "1f39a-fe0f",
+ "name": "level slider",
+ "shortname": ":level_slider:",
+ "category": "objects",
+ "emoji_order": "620",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "control_knobs": {
+ "unicode": "1f39b",
+ "unicode_alternates": "1f39b-fe0f",
+ "name": "control knobs",
+ "shortname": ":control_knobs:",
+ "category": "objects",
+ "emoji_order": "621",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "time"
+ ]
+ },
+ "stopwatch": {
+ "unicode": "23f1",
+ "unicode_alternates": "23f1-fe0f",
+ "name": "stopwatch",
+ "shortname": ":stopwatch:",
+ "category": "objects",
+ "emoji_order": "622",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "electronics",
+ "time"
+ ]
+ },
+ "timer": {
+ "unicode": "23f2",
+ "unicode_alternates": "23f2-fe0f",
+ "name": "timer clock",
+ "shortname": ":timer:",
+ "category": "objects",
+ "emoji_order": "623",
+ "aliases": [
+ ":timer_clock:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "time"
+ ]
+ },
+ "alarm_clock": {
+ "unicode": "23f0",
+ "unicode_alternates": "",
+ "name": "alarm clock",
+ "shortname": ":alarm_clock:",
+ "category": "objects",
+ "emoji_order": "624",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "time"
+ ]
+ },
+ "clock": {
+ "unicode": "1f570",
+ "unicode_alternates": "1f570-fe0f",
+ "name": "mantlepiece clock",
+ "shortname": ":clock:",
+ "category": "objects",
+ "emoji_order": "625",
+ "aliases": [
+ ":mantlepiece_clock:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "time"
+ ]
+ },
+ "hourglass_flowing_sand": {
+ "unicode": "23f3",
+ "unicode_alternates": "",
+ "name": "hourglass with flowing sand",
+ "shortname": ":hourglass_flowing_sand:",
+ "category": "objects",
+ "emoji_order": "626",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "time"
+ ]
+ },
+ "hourglass": {
+ "unicode": "231b",
+ "unicode_alternates": "231b-fe0f",
+ "name": "hourglass",
+ "shortname": ":hourglass:",
+ "category": "objects",
+ "emoji_order": "627",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "time"
+ ]
+ },
+ "satellite": {
+ "unicode": "1f4e1",
+ "unicode_alternates": "",
+ "name": "satellite antenna",
+ "shortname": ":satellite:",
+ "category": "objects",
+ "emoji_order": "628",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object"
+ ]
+ },
+ "battery": {
+ "unicode": "1f50b",
+ "unicode_alternates": "",
+ "name": "battery",
+ "shortname": ":battery:",
+ "category": "objects",
+ "emoji_order": "629",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object"
+ ]
+ },
+ "electric_plug": {
+ "unicode": "1f50c",
+ "unicode_alternates": "",
+ "name": "electric plug",
+ "shortname": ":electric_plug:",
+ "category": "objects",
+ "emoji_order": "630",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "electronics"
+ ]
+ },
+ "bulb": {
+ "unicode": "1f4a1",
+ "unicode_alternates": "",
+ "name": "electric light bulb",
+ "shortname": ":bulb:",
+ "category": "objects",
+ "emoji_order": "631",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "science"
+ ]
+ },
+ "flashlight": {
+ "unicode": "1f526",
+ "unicode_alternates": "",
+ "name": "electric torch",
+ "shortname": ":flashlight:",
+ "category": "objects",
+ "emoji_order": "632",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "electronics",
+ "object"
+ ]
+ },
+ "candle": {
+ "unicode": "1f56f",
+ "unicode_alternates": "1f56f-fe0f",
+ "name": "candle",
+ "shortname": ":candle:",
+ "category": "objects",
+ "emoji_order": "633",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object"
+ ]
+ },
+ "wastebasket": {
+ "unicode": "1f5d1",
+ "unicode_alternates": "1f5d1-fe0f",
+ "name": "wastebasket",
+ "shortname": ":wastebasket:",
+ "category": "objects",
+ "emoji_order": "634",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "work"
+ ]
+ },
+ "oil": {
+ "unicode": "1f6e2",
+ "unicode_alternates": "1f6e2-fe0f",
+ "name": "oil drum",
+ "shortname": ":oil:",
+ "category": "objects",
+ "emoji_order": "635",
+ "aliases": [
+ ":oil_drum:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "object"
+ ]
+ },
+ "money_with_wings": {
+ "unicode": "1f4b8",
+ "unicode_alternates": "",
+ "name": "money with wings",
+ "shortname": ":money_with_wings:",
+ "category": "objects",
+ "emoji_order": "636",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "money",
+ "money",
+ "boys night",
+ "boys night"
+ ]
+ },
+ "dollar": {
+ "unicode": "1f4b5",
+ "unicode_alternates": "",
+ "name": "banknote with dollar sign",
+ "shortname": ":dollar:",
+ "category": "objects",
+ "emoji_order": "637",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "money",
+ "money"
+ ]
+ },
+ "yen": {
+ "unicode": "1f4b4",
+ "unicode_alternates": "",
+ "name": "banknote with yen sign",
+ "shortname": ":yen:",
+ "category": "objects",
+ "emoji_order": "638",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "money",
+ "money"
+ ]
+ },
+ "euro": {
+ "unicode": "1f4b6",
+ "unicode_alternates": "",
+ "name": "banknote with euro sign",
+ "shortname": ":euro:",
+ "category": "objects",
+ "emoji_order": "639",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "money",
+ "money"
+ ]
+ },
+ "pound": {
+ "unicode": "1f4b7",
+ "unicode_alternates": "",
+ "name": "banknote with pound sign",
+ "shortname": ":pound:",
+ "category": "objects",
+ "emoji_order": "640",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "money",
+ "money"
+ ]
+ },
+ "moneybag": {
+ "unicode": "1f4b0",
+ "unicode_alternates": "",
+ "name": "money bag",
+ "shortname": ":moneybag:",
+ "category": "objects",
+ "emoji_order": "641",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "bag",
+ "award",
+ "money",
+ "money"
+ ]
+ },
+ "credit_card": {
+ "unicode": "1f4b3",
+ "unicode_alternates": "",
+ "name": "credit card",
+ "shortname": ":credit_card:",
+ "category": "objects",
+ "emoji_order": "642",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "money",
+ "money",
+ "boys night",
+ "boys night"
+ ]
+ },
+ "gem": {
+ "unicode": "1f48e",
+ "unicode_alternates": "",
+ "name": "gem stone",
+ "shortname": ":gem:",
+ "category": "objects",
+ "emoji_order": "643",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "gem"
+ ]
+ },
+ "scales": {
+ "unicode": "2696",
+ "unicode_alternates": "2696-fe0f",
+ "name": "scales",
+ "shortname": ":scales:",
+ "category": "objects",
+ "emoji_order": "644",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object"
+ ]
+ },
+ "wrench": {
+ "unicode": "1f527",
+ "unicode_alternates": "",
+ "name": "wrench",
+ "shortname": ":wrench:",
+ "category": "objects",
+ "emoji_order": "645",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "tool"
+ ]
+ },
+ "hammer": {
+ "unicode": "1f528",
+ "unicode_alternates": "",
+ "name": "hammer",
+ "shortname": ":hammer:",
+ "category": "objects",
+ "emoji_order": "646",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "tool",
+ "weapon"
+ ]
+ },
+ "hammer_pick": {
+ "unicode": "2692",
+ "unicode_alternates": "2692-fe0f",
+ "name": "hammer and pick",
+ "shortname": ":hammer_pick:",
+ "category": "objects",
+ "emoji_order": "647",
+ "aliases": [
+ ":hammer_and_pick:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "tool",
+ "weapon"
+ ]
+ },
+ "tools": {
+ "unicode": "1f6e0",
+ "unicode_alternates": "1f6e0-fe0f",
+ "name": "hammer and wrench",
+ "shortname": ":tools:",
+ "category": "objects",
+ "emoji_order": "648",
+ "aliases": [
+ ":hammer_and_wrench:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "tool"
+ ]
+ },
+ "pick": {
+ "unicode": "26cf",
+ "unicode_alternates": "26cf-fe0f",
+ "name": "pick",
+ "shortname": ":pick:",
+ "category": "objects",
+ "emoji_order": "649",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "tool",
+ "weapon"
+ ]
+ },
+ "nut_and_bolt": {
+ "unicode": "1f529",
+ "unicode_alternates": "",
+ "name": "nut and bolt",
+ "shortname": ":nut_and_bolt:",
+ "category": "objects",
+ "emoji_order": "650",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "tool",
+ "nutcase",
+ "nutcase"
+ ]
+ },
+ "gear": {
+ "unicode": "2699",
+ "unicode_alternates": "2699-fe0f",
+ "name": "gear",
+ "shortname": ":gear:",
+ "category": "objects",
+ "emoji_order": "651",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "tool"
+ ]
+ },
+ "chains": {
+ "unicode": "26d3",
+ "unicode_alternates": "26d3-fe0f",
+ "name": "chains",
+ "shortname": ":chains:",
+ "category": "objects",
+ "emoji_order": "652",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "tool"
+ ]
+ },
+ "gun": {
+ "unicode": "1f52b",
+ "unicode_alternates": "",
+ "name": "pistol",
+ "shortname": ":gun:",
+ "category": "objects",
+ "emoji_order": "653",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "weapon",
+ "dead",
+ "gun",
+ "sarcastic",
+ "sarcastic"
+ ]
+ },
+ "bomb": {
+ "unicode": "1f4a3",
+ "unicode_alternates": "",
+ "name": "bomb",
+ "shortname": ":bomb:",
+ "category": "objects",
+ "emoji_order": "654",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "weapon",
+ "dead",
+ "blast",
+ "blast"
+ ]
+ },
+ "knife": {
+ "unicode": "1f52a",
+ "unicode_alternates": "",
+ "name": "hocho",
+ "shortname": ":knife:",
+ "category": "objects",
+ "emoji_order": "655",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "weapon"
+ ]
+ },
+ "dagger": {
+ "unicode": "1f5e1",
+ "unicode_alternates": "1f5e1-fe0f",
+ "name": "dagger knife",
+ "shortname": ":dagger:",
+ "category": "objects",
+ "emoji_order": "656",
+ "aliases": [
+ ":dagger_knife:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "weapon"
+ ]
+ },
+ "crossed_swords": {
+ "unicode": "2694",
+ "unicode_alternates": "2694-fe0f",
+ "name": "crossed swords",
+ "shortname": ":crossed_swords:",
+ "category": "objects",
+ "emoji_order": "657",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "weapon"
+ ]
+ },
+ "shield": {
+ "unicode": "1f6e1",
+ "unicode_alternates": "1f6e1-fe0f",
+ "name": "shield",
+ "shortname": ":shield:",
+ "category": "objects",
+ "emoji_order": "658",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object"
+ ]
+ },
+ "smoking": {
+ "unicode": "1f6ac",
+ "unicode_alternates": "",
+ "name": "smoking symbol",
+ "shortname": ":smoking:",
+ "category": "objects",
+ "emoji_order": "659",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol",
+ "drugs",
+ "drugs",
+ "smoking",
+ "smoking"
+ ]
+ },
+ "skull_crossbones": {
+ "unicode": "2620",
+ "unicode_alternates": "2620-fe0f",
+ "name": "skull and crossbones",
+ "shortname": ":skull_crossbones:",
+ "category": "objects",
+ "emoji_order": "660",
+ "aliases": [
+ ":skull_and_crossbones:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol",
+ "dead",
+ "skull"
+ ]
+ },
+ "coffin": {
+ "unicode": "26b0",
+ "unicode_alternates": "26b0-fe0f",
+ "name": "coffin",
+ "shortname": ":coffin:",
+ "category": "objects",
+ "emoji_order": "661",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "dead",
+ "rip",
+ "rip"
+ ]
+ },
+ "urn": {
+ "unicode": "26b1",
+ "unicode_alternates": "26b1-fe0f",
+ "name": "funeral urn",
+ "shortname": ":urn:",
+ "category": "objects",
+ "emoji_order": "662",
+ "aliases": [
+ ":funeral_urn:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "dead",
+ "rip",
+ "rip"
+ ]
+ },
+ "amphora": {
+ "unicode": "1f3fa",
+ "unicode_alternates": "",
+ "name": "amphora",
+ "shortname": ":amphora:",
+ "category": "objects",
+ "emoji_order": "663",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object"
+ ]
+ },
+ "crystal_ball": {
+ "unicode": "1f52e",
+ "unicode_alternates": "",
+ "name": "crystal ball",
+ "shortname": ":crystal_ball:",
+ "category": "objects",
+ "emoji_order": "664",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "ball"
+ ]
+ },
+ "prayer_beads": {
+ "unicode": "1f4ff",
+ "unicode_alternates": "",
+ "name": "prayer beads",
+ "shortname": ":prayer_beads:",
+ "category": "objects",
+ "emoji_order": "665",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "rosary"
+ ]
+ },
+ "barber": {
+ "unicode": "1f488",
+ "unicode_alternates": "",
+ "name": "barber pole",
+ "shortname": ":barber:",
+ "category": "objects",
+ "emoji_order": "666",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object"
+ ]
+ },
+ "alembic": {
+ "unicode": "2697",
+ "unicode_alternates": "2697-fe0f",
+ "name": "alembic",
+ "shortname": ":alembic:",
+ "category": "objects",
+ "emoji_order": "667",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "science"
+ ]
+ },
+ "telescope": {
+ "unicode": "1f52d",
+ "unicode_alternates": "",
+ "name": "telescope",
+ "shortname": ":telescope:",
+ "category": "objects",
+ "emoji_order": "668",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "space",
+ "science"
+ ]
+ },
+ "microscope": {
+ "unicode": "1f52c",
+ "unicode_alternates": "",
+ "name": "microscope",
+ "shortname": ":microscope:",
+ "category": "objects",
+ "emoji_order": "669",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "science"
+ ]
+ },
+ "hole": {
+ "unicode": "1f573",
+ "unicode_alternates": "1f573-fe0f",
+ "name": "hole",
+ "shortname": ":hole:",
+ "category": "objects",
+ "emoji_order": "670",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object"
+ ]
+ },
+ "pill": {
+ "unicode": "1f48a",
+ "unicode_alternates": "",
+ "name": "pill",
+ "shortname": ":pill:",
+ "category": "objects",
+ "emoji_order": "671",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "health",
+ "drugs",
+ "drugs"
+ ]
+ },
+ "syringe": {
+ "unicode": "1f489",
+ "unicode_alternates": "",
+ "name": "syringe",
+ "shortname": ":syringe:",
+ "category": "objects",
+ "emoji_order": "672",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "weapon",
+ "health",
+ "drugs",
+ "drugs"
+ ]
+ },
+ "thermometer": {
+ "unicode": "1f321",
+ "unicode_alternates": "1f321-fe0f",
+ "name": "thermometer",
+ "shortname": ":thermometer:",
+ "category": "objects",
+ "emoji_order": "673",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "science",
+ "health",
+ "hot",
+ "hot"
+ ]
+ },
+ "label": {
+ "unicode": "1f3f7",
+ "unicode_alternates": "1f3f7-fe0f",
+ "name": "label",
+ "shortname": ":label:",
+ "category": "objects",
+ "emoji_order": "674",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object"
+ ]
+ },
+ "bookmark": {
+ "unicode": "1f516",
+ "unicode_alternates": "",
+ "name": "bookmark",
+ "shortname": ":bookmark:",
+ "category": "objects",
+ "emoji_order": "675",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "book"
+ ]
+ },
+ "toilet": {
+ "unicode": "1f6bd",
+ "unicode_alternates": "",
+ "name": "toilet",
+ "shortname": ":toilet:",
+ "category": "objects",
+ "emoji_order": "676",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "bathroom"
+ ]
+ },
+ "shower": {
+ "unicode": "1f6bf",
+ "unicode_alternates": "",
+ "name": "shower",
+ "shortname": ":shower:",
+ "category": "objects",
+ "emoji_order": "677",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "bathroom"
+ ]
+ },
+ "bathtub": {
+ "unicode": "1f6c1",
+ "unicode_alternates": "",
+ "name": "bathtub",
+ "shortname": ":bathtub:",
+ "category": "objects",
+ "emoji_order": "678",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "bathroom",
+ "tired",
+ "steam",
+ "steam"
+ ]
+ },
+ "key": {
+ "unicode": "1f511",
+ "unicode_alternates": "",
+ "name": "key",
+ "shortname": ":key:",
+ "category": "objects",
+ "emoji_order": "679",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "lock"
+ ]
+ },
+ "key2": {
+ "unicode": "1f5dd",
+ "unicode_alternates": "1f5dd-fe0f",
+ "name": "old key",
+ "shortname": ":key2:",
+ "category": "objects",
+ "emoji_order": "680",
+ "aliases": [
+ ":old_key:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "lock"
+ ]
+ },
+ "couch": {
+ "unicode": "1f6cb",
+ "unicode_alternates": "1f6cb-fe0f",
+ "name": "couch and lamp",
+ "shortname": ":couch:",
+ "category": "objects",
+ "emoji_order": "681",
+ "aliases": [
+ ":couch_and_lamp:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "object"
+ ]
+ },
+ "sleeping_accommodation": {
+ "unicode": "1f6cc",
+ "unicode_alternates": "",
+ "name": "sleeping accommodation",
+ "shortname": ":sleeping_accommodation:",
+ "category": "objects",
+ "emoji_order": "682",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "tired"
+ ]
+ },
+ "bed": {
+ "unicode": "1f6cf",
+ "unicode_alternates": "1f6cf-fe0f",
+ "name": "bed",
+ "shortname": ":bed:",
+ "category": "objects",
+ "emoji_order": "683",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "tired"
+ ]
+ },
+ "door": {
+ "unicode": "1f6aa",
+ "unicode_alternates": "",
+ "name": "door",
+ "shortname": ":door:",
+ "category": "objects",
+ "emoji_order": "684",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object"
+ ]
+ },
+ "bellhop": {
+ "unicode": "1f6ce",
+ "unicode_alternates": "1f6ce-fe0f",
+ "name": "bellhop bell",
+ "shortname": ":bellhop:",
+ "category": "objects",
+ "emoji_order": "685",
+ "aliases": [
+ ":bellhop_bell:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "object"
+ ]
+ },
+ "frame_photo": {
+ "unicode": "1f5bc",
+ "unicode_alternates": "1f5bc-fe0f",
+ "name": "frame with picture",
+ "shortname": ":frame_photo:",
+ "category": "objects",
+ "emoji_order": "686",
+ "aliases": [
+ ":frame_with_picture:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "travel",
+ "vacation"
+ ]
+ },
+ "map": {
+ "unicode": "1f5fa",
+ "unicode_alternates": "1f5fa-fe0f",
+ "name": "world map",
+ "shortname": ":map:",
+ "category": "objects",
+ "emoji_order": "687",
+ "aliases": [
+ ":world_map:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "travel",
+ "map",
+ "vacation"
+ ]
+ },
+ "beach_umbrella": {
+ "unicode": "26f1",
+ "unicode_alternates": "26f1-fe0f",
+ "name": "umbrella on ground",
+ "shortname": ":beach_umbrella:",
+ "category": "objects",
+ "emoji_order": "688",
+ "aliases": [
+ ":umbrella_on_ground:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "travel",
+ "vacation",
+ "tropical"
+ ]
+ },
+ "moyai": {
+ "unicode": "1f5ff",
+ "unicode_alternates": "",
+ "name": "moyai",
+ "shortname": ":moyai:",
+ "category": "objects",
+ "emoji_order": "689",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "travel",
+ "vacation"
+ ]
+ },
+ "shopping_bags": {
+ "unicode": "1f6cd",
+ "unicode_alternates": "1f6cd-fe0f",
+ "name": "shopping bags",
+ "shortname": ":shopping_bags:",
+ "category": "objects",
+ "emoji_order": "690",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "birthday",
+ "parties",
+ "parties"
+ ]
+ },
+ "balloon": {
+ "unicode": "1f388",
+ "unicode_alternates": "",
+ "name": "balloon",
+ "shortname": ":balloon:",
+ "category": "objects",
+ "emoji_order": "691",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "birthday",
+ "good",
+ "good",
+ "parties",
+ "parties"
+ ]
+ },
+ "flags": {
+ "unicode": "1f38f",
+ "unicode_alternates": "",
+ "name": "carp streamer",
+ "shortname": ":flags:",
+ "category": "objects",
+ "emoji_order": "692",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "japan"
+ ]
+ },
+ "ribbon": {
+ "unicode": "1f380",
+ "unicode_alternates": "",
+ "name": "ribbon",
+ "shortname": ":ribbon:",
+ "category": "objects",
+ "emoji_order": "693",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "gift",
+ "birthday"
+ ]
+ },
+ "gift": {
+ "unicode": "1f381",
+ "unicode_alternates": "",
+ "name": "wrapped present",
+ "shortname": ":gift:",
+ "category": "objects",
+ "emoji_order": "694",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "gift",
+ "birthday",
+ "holidays",
+ "christmas",
+ "parties",
+ "parties"
+ ]
+ },
+ "confetti_ball": {
+ "unicode": "1f38a",
+ "unicode_alternates": "",
+ "name": "confetti ball",
+ "shortname": ":confetti_ball:",
+ "category": "objects",
+ "emoji_order": "695",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "birthday",
+ "holidays",
+ "cheers",
+ "girls night",
+ "girls night",
+ "boys night",
+ "boys night",
+ "parties",
+ "parties"
+ ]
+ },
+ "tada": {
+ "unicode": "1f389",
+ "unicode_alternates": "",
+ "name": "party popper",
+ "shortname": ":tada:",
+ "category": "objects",
+ "emoji_order": "696",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "birthday",
+ "holidays",
+ "cheers",
+ "good",
+ "good",
+ "girls night",
+ "girls night",
+ "boys night",
+ "boys night",
+ "parties",
+ "parties"
+ ]
+ },
+ "dolls": {
+ "unicode": "1f38e",
+ "unicode_alternates": "",
+ "name": "japanese dolls",
+ "shortname": ":dolls:",
+ "category": "objects",
+ "emoji_order": "697",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "people",
+ "japan"
+ ]
+ },
+ "wind_chime": {
+ "unicode": "1f390",
+ "unicode_alternates": "",
+ "name": "wind chime",
+ "shortname": ":wind_chime:",
+ "category": "objects",
+ "emoji_order": "698",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "japan"
+ ]
+ },
+ "crossed_flags": {
+ "unicode": "1f38c",
+ "unicode_alternates": "",
+ "name": "crossed flags",
+ "shortname": ":crossed_flags:",
+ "category": "objects",
+ "emoji_order": "699",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "japan"
+ ]
+ },
+ "izakaya_lantern": {
+ "unicode": "1f3ee",
+ "unicode_alternates": "",
+ "name": "izakaya lantern",
+ "shortname": ":izakaya_lantern:",
+ "category": "objects",
+ "emoji_order": "700",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "japan"
+ ]
+ },
+ "envelope": {
+ "unicode": "2709",
+ "unicode_alternates": "2709-fe0f",
+ "name": "envelope",
+ "shortname": ":envelope:",
+ "category": "objects",
+ "emoji_order": "701",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "office",
+ "write"
+ ]
+ },
+ "envelope_with_arrow": {
+ "unicode": "1f4e9",
+ "unicode_alternates": "",
+ "name": "envelope with downwards arrow above",
+ "shortname": ":envelope_with_arrow:",
+ "category": "objects",
+ "emoji_order": "702",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "office"
+ ]
+ },
+ "incoming_envelope": {
+ "unicode": "1f4e8",
+ "unicode_alternates": "",
+ "name": "incoming envelope",
+ "shortname": ":incoming_envelope:",
+ "category": "objects",
+ "emoji_order": "703",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object"
+ ]
+ },
+ "e-mail": {
+ "unicode": "1f4e7",
+ "unicode_alternates": "",
+ "name": "e-mail symbol",
+ "shortname": ":e-mail:",
+ "category": "objects",
+ "emoji_order": "704",
+ "aliases": [
+ ":email:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "office"
+ ]
+ },
+ "love_letter": {
+ "unicode": "1f48c",
+ "unicode_alternates": "",
+ "name": "love letter",
+ "shortname": ":love_letter:",
+ "category": "objects",
+ "emoji_order": "705",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object"
+ ]
+ },
+ "postbox": {
+ "unicode": "1f4ee",
+ "unicode_alternates": "",
+ "name": "postbox",
+ "shortname": ":postbox:",
+ "category": "objects",
+ "emoji_order": "706",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object"
+ ]
+ },
+ "mailbox_closed": {
+ "unicode": "1f4ea",
+ "unicode_alternates": "",
+ "name": "closed mailbox with lowered flag",
+ "shortname": ":mailbox_closed:",
+ "category": "objects",
+ "emoji_order": "707",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "office"
+ ]
+ },
+ "mailbox": {
+ "unicode": "1f4eb",
+ "unicode_alternates": "",
+ "name": "closed mailbox with raised flag",
+ "shortname": ":mailbox:",
+ "category": "objects",
+ "emoji_order": "708",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object"
+ ]
+ },
+ "mailbox_with_mail": {
+ "unicode": "1f4ec",
+ "unicode_alternates": "",
+ "name": "open mailbox with raised flag",
+ "shortname": ":mailbox_with_mail:",
+ "category": "objects",
+ "emoji_order": "709",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object"
+ ]
+ },
+ "mailbox_with_no_mail": {
+ "unicode": "1f4ed",
+ "unicode_alternates": "",
+ "name": "open mailbox with lowered flag",
+ "shortname": ":mailbox_with_no_mail:",
+ "category": "objects",
+ "emoji_order": "710",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object"
+ ]
+ },
+ "package": {
+ "unicode": "1f4e6",
+ "unicode_alternates": "",
+ "name": "package",
+ "shortname": ":package:",
+ "category": "objects",
+ "emoji_order": "711",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "gift",
+ "office"
+ ]
+ },
+ "postal_horn": {
+ "unicode": "1f4ef",
+ "unicode_alternates": "",
+ "name": "postal horn",
+ "shortname": ":postal_horn:",
+ "category": "objects",
+ "emoji_order": "712",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object"
+ ]
+ },
+ "inbox_tray": {
+ "unicode": "1f4e5",
+ "unicode_alternates": "",
+ "name": "inbox tray",
+ "shortname": ":inbox_tray:",
+ "category": "objects",
+ "emoji_order": "713",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "work",
+ "office"
+ ]
+ },
+ "outbox_tray": {
+ "unicode": "1f4e4",
+ "unicode_alternates": "",
+ "name": "outbox tray",
+ "shortname": ":outbox_tray:",
+ "category": "objects",
+ "emoji_order": "714",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "work",
+ "office"
+ ]
+ },
+ "scroll": {
+ "unicode": "1f4dc",
+ "unicode_alternates": "",
+ "name": "scroll",
+ "shortname": ":scroll:",
+ "category": "objects",
+ "emoji_order": "715",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "office"
+ ]
+ },
+ "page_with_curl": {
+ "unicode": "1f4c3",
+ "unicode_alternates": "",
+ "name": "page with curl",
+ "shortname": ":page_with_curl:",
+ "category": "objects",
+ "emoji_order": "716",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "office",
+ "write"
+ ]
+ },
+ "bookmark_tabs": {
+ "unicode": "1f4d1",
+ "unicode_alternates": "",
+ "name": "bookmark tabs",
+ "shortname": ":bookmark_tabs:",
+ "category": "objects",
+ "emoji_order": "717",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "office",
+ "write"
+ ]
+ },
+ "bar_chart": {
+ "unicode": "1f4ca",
+ "unicode_alternates": "",
+ "name": "bar chart",
+ "shortname": ":bar_chart:",
+ "category": "objects",
+ "emoji_order": "718",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "work",
+ "office"
+ ]
+ },
+ "chart_with_upwards_trend": {
+ "unicode": "1f4c8",
+ "unicode_alternates": "",
+ "name": "chart with upwards trend",
+ "shortname": ":chart_with_upwards_trend:",
+ "category": "objects",
+ "emoji_order": "719",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "work",
+ "office"
+ ]
+ },
+ "chart_with_downwards_trend": {
+ "unicode": "1f4c9",
+ "unicode_alternates": "",
+ "name": "chart with downwards trend",
+ "shortname": ":chart_with_downwards_trend:",
+ "category": "objects",
+ "emoji_order": "720",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "work",
+ "office"
+ ]
+ },
+ "page_facing_up": {
+ "unicode": "1f4c4",
+ "unicode_alternates": "",
+ "name": "page facing up",
+ "shortname": ":page_facing_up:",
+ "category": "objects",
+ "emoji_order": "721",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "work",
+ "office",
+ "write"
+ ]
+ },
+ "date": {
+ "unicode": "1f4c5",
+ "unicode_alternates": "",
+ "name": "calendar",
+ "shortname": ":date:",
+ "category": "objects",
+ "emoji_order": "722",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "office"
+ ]
+ },
+ "calendar": {
+ "unicode": "1f4c6",
+ "unicode_alternates": "",
+ "name": "tear-off calendar",
+ "shortname": ":calendar:",
+ "category": "objects",
+ "emoji_order": "723",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "office"
+ ]
+ },
+ "calendar_spiral": {
+ "unicode": "1f5d3",
+ "unicode_alternates": "1f5d3-fe0f",
+ "name": "spiral calendar pad",
+ "shortname": ":calendar_spiral:",
+ "category": "objects",
+ "emoji_order": "724",
+ "aliases": [
+ ":spiral_calendar_pad:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "office"
+ ]
+ },
+ "card_index": {
+ "unicode": "1f4c7",
+ "unicode_alternates": "",
+ "name": "card index",
+ "shortname": ":card_index:",
+ "category": "objects",
+ "emoji_order": "725",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "work",
+ "office"
+ ]
+ },
+ "card_box": {
+ "unicode": "1f5c3",
+ "unicode_alternates": "1f5c3-fe0f",
+ "name": "card file box",
+ "shortname": ":card_box:",
+ "category": "objects",
+ "emoji_order": "726",
+ "aliases": [
+ ":card_file_box:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "work",
+ "office"
+ ]
+ },
+ "ballot_box": {
+ "unicode": "1f5f3",
+ "unicode_alternates": "1f5f3-fe0f",
+ "name": "ballot box with ballot",
+ "shortname": ":ballot_box:",
+ "category": "objects",
+ "emoji_order": "727",
+ "aliases": [
+ ":ballot_box_with_ballot:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "office"
+ ]
+ },
+ "file_cabinet": {
+ "unicode": "1f5c4",
+ "unicode_alternates": "1f5c4-fe0f",
+ "name": "file cabinet",
+ "shortname": ":file_cabinet:",
+ "category": "objects",
+ "emoji_order": "728",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "work",
+ "office"
+ ]
+ },
+ "clipboard": {
+ "unicode": "1f4cb",
+ "unicode_alternates": "",
+ "name": "clipboard",
+ "shortname": ":clipboard:",
+ "category": "objects",
+ "emoji_order": "729",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "work",
+ "office",
+ "write"
+ ]
+ },
+ "notepad_spiral": {
+ "unicode": "1f5d2",
+ "unicode_alternates": "1f5d2-fe0f",
+ "name": "spiral note pad",
+ "shortname": ":notepad_spiral:",
+ "category": "objects",
+ "emoji_order": "730",
+ "aliases": [
+ ":spiral_note_pad:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "work",
+ "office",
+ "write"
+ ]
+ },
+ "file_folder": {
+ "unicode": "1f4c1",
+ "unicode_alternates": "",
+ "name": "file folder",
+ "shortname": ":file_folder:",
+ "category": "objects",
+ "emoji_order": "731",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "work",
+ "office"
+ ]
+ },
+ "open_file_folder": {
+ "unicode": "1f4c2",
+ "unicode_alternates": "",
+ "name": "open file folder",
+ "shortname": ":open_file_folder:",
+ "category": "objects",
+ "emoji_order": "732",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "work",
+ "office"
+ ]
+ },
+ "dividers": {
+ "unicode": "1f5c2",
+ "unicode_alternates": "1f5c2-fe0f",
+ "name": "card index dividers",
+ "shortname": ":dividers:",
+ "category": "objects",
+ "emoji_order": "733",
+ "aliases": [
+ ":card_index_dividers:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "work",
+ "office"
+ ]
+ },
+ "newspaper2": {
+ "unicode": "1f5de",
+ "unicode_alternates": "1f5de-fe0f",
+ "name": "rolled-up newspaper",
+ "shortname": ":newspaper2:",
+ "category": "objects",
+ "emoji_order": "734",
+ "aliases": [
+ ":rolled_up_newspaper:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "office",
+ "write"
+ ]
+ },
+ "newspaper": {
+ "unicode": "1f4f0",
+ "unicode_alternates": "",
+ "name": "newspaper",
+ "shortname": ":newspaper:",
+ "category": "objects",
+ "emoji_order": "735",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "office",
+ "write"
+ ]
+ },
+ "notebook": {
+ "unicode": "1f4d3",
+ "unicode_alternates": "",
+ "name": "notebook",
+ "shortname": ":notebook:",
+ "category": "objects",
+ "emoji_order": "736",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "office",
+ "write"
+ ]
+ },
+ "closed_book": {
+ "unicode": "1f4d5",
+ "unicode_alternates": "",
+ "name": "closed book",
+ "shortname": ":closed_book:",
+ "category": "objects",
+ "emoji_order": "737",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "office",
+ "write",
+ "book"
+ ]
+ },
+ "green_book": {
+ "unicode": "1f4d7",
+ "unicode_alternates": "",
+ "name": "green book",
+ "shortname": ":green_book:",
+ "category": "objects",
+ "emoji_order": "738",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "office",
+ "book"
+ ]
+ },
+ "blue_book": {
+ "unicode": "1f4d8",
+ "unicode_alternates": "",
+ "name": "blue book",
+ "shortname": ":blue_book:",
+ "category": "objects",
+ "emoji_order": "739",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "office",
+ "write",
+ "book"
+ ]
+ },
+ "orange_book": {
+ "unicode": "1f4d9",
+ "unicode_alternates": "",
+ "name": "orange book",
+ "shortname": ":orange_book:",
+ "category": "objects",
+ "emoji_order": "740",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "office",
+ "write",
+ "book"
+ ]
+ },
+ "notebook_with_decorative_cover": {
+ "unicode": "1f4d4",
+ "unicode_alternates": "",
+ "name": "notebook with decorative cover",
+ "shortname": ":notebook_with_decorative_cover:",
+ "category": "objects",
+ "emoji_order": "741",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "office",
+ "write"
+ ]
+ },
+ "ledger": {
+ "unicode": "1f4d2",
+ "unicode_alternates": "",
+ "name": "ledger",
+ "shortname": ":ledger:",
+ "category": "objects",
+ "emoji_order": "742",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "office",
+ "write"
+ ]
+ },
+ "books": {
+ "unicode": "1f4da",
+ "unicode_alternates": "",
+ "name": "books",
+ "shortname": ":books:",
+ "category": "objects",
+ "emoji_order": "743",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "office",
+ "write",
+ "book"
+ ]
+ },
+ "book": {
+ "unicode": "1f4d6",
+ "unicode_alternates": "",
+ "name": "open book",
+ "shortname": ":book:",
+ "category": "objects",
+ "emoji_order": "744",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "office",
+ "write",
+ "book"
+ ]
+ },
+ "link": {
+ "unicode": "1f517",
+ "unicode_alternates": "",
+ "name": "link symbol",
+ "shortname": ":link:",
+ "category": "objects",
+ "emoji_order": "745",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol",
+ "office"
+ ]
+ },
+ "paperclip": {
+ "unicode": "1f4ce",
+ "unicode_alternates": "",
+ "name": "paperclip",
+ "shortname": ":paperclip:",
+ "category": "objects",
+ "emoji_order": "746",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "work",
+ "office"
+ ]
+ },
+ "paperclips": {
+ "unicode": "1f587",
+ "unicode_alternates": "1f587-fe0f",
+ "name": "linked paperclips",
+ "shortname": ":paperclips:",
+ "category": "objects",
+ "emoji_order": "747",
+ "aliases": [
+ ":linked_paperclips:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "work",
+ "office"
+ ]
+ },
+ "scissors": {
+ "unicode": "2702",
+ "unicode_alternates": "2702-fe0f",
+ "name": "black scissors",
+ "shortname": ":scissors:",
+ "category": "objects",
+ "emoji_order": "748",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "tool",
+ "weapon",
+ "office"
+ ]
+ },
+ "triangular_ruler": {
+ "unicode": "1f4d0",
+ "unicode_alternates": "",
+ "name": "triangular ruler",
+ "shortname": ":triangular_ruler:",
+ "category": "objects",
+ "emoji_order": "749",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "tool",
+ "office"
+ ]
+ },
+ "straight_ruler": {
+ "unicode": "1f4cf",
+ "unicode_alternates": "",
+ "name": "straight ruler",
+ "shortname": ":straight_ruler:",
+ "category": "objects",
+ "emoji_order": "750",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "tool",
+ "office"
+ ]
+ },
+ "pushpin": {
+ "unicode": "1f4cc",
+ "unicode_alternates": "",
+ "name": "pushpin",
+ "shortname": ":pushpin:",
+ "category": "objects",
+ "emoji_order": "751",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "office"
+ ]
+ },
+ "round_pushpin": {
+ "unicode": "1f4cd",
+ "unicode_alternates": "",
+ "name": "round pushpin",
+ "shortname": ":round_pushpin:",
+ "category": "objects",
+ "emoji_order": "752",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "office"
+ ]
+ },
+ "triangular_flag_on_post": {
+ "unicode": "1f6a9",
+ "unicode_alternates": "",
+ "name": "triangular flag on post",
+ "shortname": ":triangular_flag_on_post:",
+ "category": "objects",
+ "emoji_order": "753",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object"
+ ]
+ },
+ "flag_white": {
+ "unicode": "1f3f3",
+ "unicode_alternates": "1f3f3-fe0f",
+ "name": "waving white flag",
+ "shortname": ":flag_white:",
+ "category": "objects",
+ "emoji_order": "754",
+ "aliases": [
+ ":waving_white_flag:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "object"
+ ]
+ },
+ "flag_black": {
+ "unicode": "1f3f4",
+ "unicode_alternates": "",
+ "name": "waving black flag",
+ "shortname": ":flag_black:",
+ "category": "objects",
+ "emoji_order": "755",
+ "aliases": [
+ ":waving_black_flag:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "object"
+ ]
+ },
+ "closed_lock_with_key": {
+ "unicode": "1f510",
+ "unicode_alternates": "",
+ "name": "closed lock with key",
+ "shortname": ":closed_lock_with_key:",
+ "category": "objects",
+ "emoji_order": "756",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "lock"
+ ]
+ },
+ "lock": {
+ "unicode": "1f512",
+ "unicode_alternates": "",
+ "name": "lock",
+ "shortname": ":lock:",
+ "category": "objects",
+ "emoji_order": "757",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "lock"
+ ]
+ },
+ "unlock": {
+ "unicode": "1f513",
+ "unicode_alternates": "",
+ "name": "open lock",
+ "shortname": ":unlock:",
+ "category": "objects",
+ "emoji_order": "758",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "lock"
+ ]
+ },
+ "lock_with_ink_pen": {
+ "unicode": "1f50f",
+ "unicode_alternates": "",
+ "name": "lock with ink pen",
+ "shortname": ":lock_with_ink_pen:",
+ "category": "objects",
+ "emoji_order": "759",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "lock"
+ ]
+ },
+ "pen_ballpoint": {
+ "unicode": "1f58a",
+ "unicode_alternates": "1f58a-fe0f",
+ "name": "lower left ballpoint pen",
+ "shortname": ":pen_ballpoint:",
+ "category": "objects",
+ "emoji_order": "760",
+ "aliases": [
+ ":lower_left_ballpoint_pen:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "office",
+ "write"
+ ]
+ },
+ "pen_fountain": {
+ "unicode": "1f58b",
+ "unicode_alternates": "1f58b-fe0f",
+ "name": "lower left fountain pen",
+ "shortname": ":pen_fountain:",
+ "category": "objects",
+ "emoji_order": "761",
+ "aliases": [
+ ":lower_left_fountain_pen:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "office",
+ "write"
+ ]
+ },
+ "black_nib": {
+ "unicode": "2712",
+ "unicode_alternates": "2712-fe0f",
+ "name": "black nib",
+ "shortname": ":black_nib:",
+ "category": "objects",
+ "emoji_order": "762",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "office",
+ "write"
+ ]
+ },
+ "pencil": {
+ "unicode": "1f4dd",
+ "unicode_alternates": "",
+ "name": "memo",
+ "shortname": ":pencil:",
+ "category": "objects",
+ "emoji_order": "763",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "work",
+ "office",
+ "write"
+ ]
+ },
+ "pencil2": {
+ "unicode": "270f",
+ "unicode_alternates": "270f-fe0f",
+ "name": "pencil",
+ "shortname": ":pencil2:",
+ "category": "objects",
+ "emoji_order": "764",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "office",
+ "write"
+ ]
+ },
+ "crayon": {
+ "unicode": "1f58d",
+ "unicode_alternates": "1f58d-fe0f",
+ "name": "lower left crayon",
+ "shortname": ":crayon:",
+ "category": "objects",
+ "emoji_order": "765",
+ "aliases": [
+ ":lower_left_crayon:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "office",
+ "write"
+ ]
+ },
+ "paintbrush": {
+ "unicode": "1f58c",
+ "unicode_alternates": "1f58c-fe0f",
+ "name": "lower left paintbrush",
+ "shortname": ":paintbrush:",
+ "category": "objects",
+ "emoji_order": "766",
+ "aliases": [
+ ":lower_left_paintbrush:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "office",
+ "write"
+ ]
+ },
+ "mag": {
+ "unicode": "1f50d",
+ "unicode_alternates": "",
+ "name": "left-pointing magnifying glass",
+ "shortname": ":mag:",
+ "category": "objects",
+ "emoji_order": "767",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object"
+ ]
+ },
+ "mag_right": {
+ "unicode": "1f50e",
+ "unicode_alternates": "",
+ "name": "right-pointing magnifying glass",
+ "shortname": ":mag_right:",
+ "category": "objects",
+ "emoji_order": "768",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object"
+ ]
+ },
+ "heart": {
+ "unicode": "2764",
+ "unicode_alternates": "2764-fe0f",
+ "name": "heavy black heart",
+ "shortname": ":heart:",
+ "category": "symbols",
+ "emoji_order": "769",
+ "aliases": [],
+ "aliases_ascii": [
+ "<3"
+ ],
+ "keywords": [
+ "love",
+ "symbol",
+ "parties",
+ "parties"
+ ]
+ },
+ "yellow_heart": {
+ "unicode": "1f49b",
+ "unicode_alternates": "",
+ "name": "yellow heart",
+ "shortname": ":yellow_heart:",
+ "category": "symbols",
+ "emoji_order": "770",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "love",
+ "symbol"
+ ]
+ },
+ "green_heart": {
+ "unicode": "1f49a",
+ "unicode_alternates": "",
+ "name": "green heart",
+ "shortname": ":green_heart:",
+ "category": "symbols",
+ "emoji_order": "771",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "love",
+ "symbol"
+ ]
+ },
+ "blue_heart": {
+ "unicode": "1f499",
+ "unicode_alternates": "",
+ "name": "blue heart",
+ "shortname": ":blue_heart:",
+ "category": "symbols",
+ "emoji_order": "772",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "love",
+ "symbol"
+ ]
+ },
+ "purple_heart": {
+ "unicode": "1f49c",
+ "unicode_alternates": "",
+ "name": "purple heart",
+ "shortname": ":purple_heart:",
+ "category": "symbols",
+ "emoji_order": "773",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "love",
+ "symbol"
+ ]
+ },
+ "broken_heart": {
+ "unicode": "1f494",
+ "unicode_alternates": "",
+ "name": "broken heart",
+ "shortname": ":broken_heart:",
+ "category": "symbols",
+ "emoji_order": "774",
+ "aliases": [],
+ "aliases_ascii": [
+ "</3"
+ ],
+ "keywords": [
+ "love",
+ "symbol",
+ "heartbreak",
+ "heartbreak"
+ ]
+ },
+ "heart_exclamation": {
+ "unicode": "2763",
+ "unicode_alternates": "2763-fe0f",
+ "name": "heavy heart exclamation mark ornament",
+ "shortname": ":heart_exclamation:",
+ "category": "symbols",
+ "emoji_order": "775",
+ "aliases": [
+ ":heavy_heart_exclamation_mark_ornament:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "love",
+ "symbol"
+ ]
+ },
+ "two_hearts": {
+ "unicode": "1f495",
+ "unicode_alternates": "",
+ "name": "two hearts",
+ "shortname": ":two_hearts:",
+ "category": "symbols",
+ "emoji_order": "776",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "love",
+ "symbol"
+ ]
+ },
+ "revolving_hearts": {
+ "unicode": "1f49e",
+ "unicode_alternates": "",
+ "name": "revolving hearts",
+ "shortname": ":revolving_hearts:",
+ "category": "symbols",
+ "emoji_order": "777",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "love",
+ "symbol"
+ ]
+ },
+ "heartbeat": {
+ "unicode": "1f493",
+ "unicode_alternates": "",
+ "name": "beating heart",
+ "shortname": ":heartbeat:",
+ "category": "symbols",
+ "emoji_order": "778",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "love",
+ "symbol"
+ ]
+ },
+ "heartpulse": {
+ "unicode": "1f497",
+ "unicode_alternates": "",
+ "name": "growing heart",
+ "shortname": ":heartpulse:",
+ "category": "symbols",
+ "emoji_order": "779",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "love",
+ "symbol"
+ ]
+ },
+ "sparkling_heart": {
+ "unicode": "1f496",
+ "unicode_alternates": "",
+ "name": "sparkling heart",
+ "shortname": ":sparkling_heart:",
+ "category": "symbols",
+ "emoji_order": "780",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "love",
+ "symbol",
+ "girls night",
+ "girls night"
+ ]
+ },
+ "cupid": {
+ "unicode": "1f498",
+ "unicode_alternates": "",
+ "name": "heart with arrow",
+ "shortname": ":cupid:",
+ "category": "symbols",
+ "emoji_order": "781",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "love",
+ "symbol"
+ ]
+ },
+ "gift_heart": {
+ "unicode": "1f49d",
+ "unicode_alternates": "",
+ "name": "heart with ribbon",
+ "shortname": ":gift_heart:",
+ "category": "symbols",
+ "emoji_order": "782",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "love",
+ "symbol",
+ "condolence",
+ "condolence"
+ ]
+ },
+ "heart_decoration": {
+ "unicode": "1f49f",
+ "unicode_alternates": "",
+ "name": "heart decoration",
+ "shortname": ":heart_decoration:",
+ "category": "symbols",
+ "emoji_order": "783",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "love",
+ "symbol"
+ ]
+ },
+ "peace": {
+ "unicode": "262e",
+ "unicode_alternates": "262e-fe0f",
+ "name": "peace symbol",
+ "shortname": ":peace:",
+ "category": "symbols",
+ "emoji_order": "784",
+ "aliases": [
+ ":peace_symbol:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol",
+ "peace",
+ "peace",
+ "drugs",
+ "drugs"
+ ]
+ },
+ "cross": {
+ "unicode": "271d",
+ "unicode_alternates": "271d-fe0f",
+ "name": "latin cross",
+ "shortname": ":cross:",
+ "category": "symbols",
+ "emoji_order": "785",
+ "aliases": [
+ ":latin_cross:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "religion",
+ "symbol"
+ ]
+ },
+ "star_and_crescent": {
+ "unicode": "262a",
+ "unicode_alternates": "262a-fe0f",
+ "name": "star and crescent",
+ "shortname": ":star_and_crescent:",
+ "category": "symbols",
+ "emoji_order": "786",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "religion",
+ "symbol"
+ ]
+ },
+ "om_symbol": {
+ "unicode": "1f549",
+ "unicode_alternates": "1f549-fe0f",
+ "name": "om symbol",
+ "shortname": ":om_symbol:",
+ "category": "symbols",
+ "emoji_order": "787",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "religion",
+ "symbol"
+ ]
+ },
+ "wheel_of_dharma": {
+ "unicode": "2638",
+ "unicode_alternates": "2638-fe0f",
+ "name": "wheel of dharma",
+ "shortname": ":wheel_of_dharma:",
+ "category": "symbols",
+ "emoji_order": "788",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "religion",
+ "symbol"
+ ]
+ },
+ "star_of_david": {
+ "unicode": "2721",
+ "unicode_alternates": "2721-fe0f",
+ "name": "star of david",
+ "shortname": ":star_of_david:",
+ "category": "symbols",
+ "emoji_order": "789",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "religion",
+ "jew",
+ "star",
+ "symbol"
+ ]
+ },
+ "six_pointed_star": {
+ "unicode": "1f52f",
+ "unicode_alternates": "",
+ "name": "six pointed star with middle dot",
+ "shortname": ":six_pointed_star:",
+ "category": "symbols",
+ "emoji_order": "790",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "religion",
+ "jew",
+ "star",
+ "symbol"
+ ]
+ },
+ "menorah": {
+ "unicode": "1f54e",
+ "unicode_alternates": "",
+ "name": "menorah with nine branches",
+ "shortname": ":menorah:",
+ "category": "symbols",
+ "emoji_order": "791",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "religion",
+ "object",
+ "jew",
+ "symbol",
+ "holidays"
+ ]
+ },
+ "yin_yang": {
+ "unicode": "262f",
+ "unicode_alternates": "262f-fe0f",
+ "name": "yin yang",
+ "shortname": ":yin_yang:",
+ "category": "symbols",
+ "emoji_order": "792",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol"
+ ]
+ },
+ "orthodox_cross": {
+ "unicode": "2626",
+ "unicode_alternates": "2626-fe0f",
+ "name": "orthodox cross",
+ "shortname": ":orthodox_cross:",
+ "category": "symbols",
+ "emoji_order": "793",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "religion",
+ "symbol"
+ ]
+ },
+ "place_of_worship": {
+ "unicode": "1f6d0",
+ "unicode_alternates": "",
+ "name": "place of worship",
+ "shortname": ":place_of_worship:",
+ "category": "symbols",
+ "emoji_order": "794",
+ "aliases": [
+ ":worship_symbol:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "religion",
+ "symbol",
+ "pray",
+ "pray"
+ ]
+ },
+ "ophiuchus": {
+ "unicode": "26ce",
+ "unicode_alternates": "",
+ "name": "ophiuchus",
+ "shortname": ":ophiuchus:",
+ "category": "symbols",
+ "emoji_order": "795",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol"
+ ]
+ },
+ "aries": {
+ "unicode": "2648",
+ "unicode_alternates": "2648-fe0f",
+ "name": "aries",
+ "shortname": ":aries:",
+ "category": "symbols",
+ "emoji_order": "796",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "zodiac",
+ "symbol"
+ ]
+ },
+ "taurus": {
+ "unicode": "2649",
+ "unicode_alternates": "2649-fe0f",
+ "name": "taurus",
+ "shortname": ":taurus:",
+ "category": "symbols",
+ "emoji_order": "797",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "zodiac",
+ "symbol"
+ ]
+ },
+ "gemini": {
+ "unicode": "264a",
+ "unicode_alternates": "264a-fe0f",
+ "name": "gemini",
+ "shortname": ":gemini:",
+ "category": "symbols",
+ "emoji_order": "798",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "zodiac",
+ "symbol"
+ ]
+ },
+ "cancer": {
+ "unicode": "264b",
+ "unicode_alternates": "264b-fe0f",
+ "name": "cancer",
+ "shortname": ":cancer:",
+ "category": "symbols",
+ "emoji_order": "799",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "zodiac",
+ "symbol"
+ ]
+ },
+ "leo": {
+ "unicode": "264c",
+ "unicode_alternates": "264c-fe0f",
+ "name": "leo",
+ "shortname": ":leo:",
+ "category": "symbols",
+ "emoji_order": "800",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "zodiac",
+ "symbol"
+ ]
+ },
+ "virgo": {
+ "unicode": "264d",
+ "unicode_alternates": "264d-fe0f",
+ "name": "virgo",
+ "shortname": ":virgo:",
+ "category": "symbols",
+ "emoji_order": "801",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "zodiac",
+ "symbol"
+ ]
+ },
+ "libra": {
+ "unicode": "264e",
+ "unicode_alternates": "264e-fe0f",
+ "name": "libra",
+ "shortname": ":libra:",
+ "category": "symbols",
+ "emoji_order": "802",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "zodiac",
+ "symbol"
+ ]
+ },
+ "scorpius": {
+ "unicode": "264f",
+ "unicode_alternates": "264f-fe0f",
+ "name": "scorpius",
+ "shortname": ":scorpius:",
+ "category": "symbols",
+ "emoji_order": "803",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "zodiac",
+ "symbol"
+ ]
+ },
+ "sagittarius": {
+ "unicode": "2650",
+ "unicode_alternates": "2650-fe0f",
+ "name": "sagittarius",
+ "shortname": ":sagittarius:",
+ "category": "symbols",
+ "emoji_order": "804",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "zodiac",
+ "symbol"
+ ]
+ },
+ "capricorn": {
+ "unicode": "2651",
+ "unicode_alternates": "2651-fe0f",
+ "name": "capricorn",
+ "shortname": ":capricorn:",
+ "category": "symbols",
+ "emoji_order": "805",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "zodiac",
+ "symbol"
+ ]
+ },
+ "aquarius": {
+ "unicode": "2652",
+ "unicode_alternates": "2652-fe0f",
+ "name": "aquarius",
+ "shortname": ":aquarius:",
+ "category": "symbols",
+ "emoji_order": "806",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "zodiac",
+ "symbol"
+ ]
+ },
+ "pisces": {
+ "unicode": "2653",
+ "unicode_alternates": "2653-fe0f",
+ "name": "pisces",
+ "shortname": ":pisces:",
+ "category": "symbols",
+ "emoji_order": "807",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "zodiac",
+ "symbol"
+ ]
+ },
+ "id": {
+ "unicode": "1f194",
+ "unicode_alternates": "",
+ "name": "squared id",
+ "shortname": ":id:",
+ "category": "symbols",
+ "emoji_order": "808",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol"
+ ]
+ },
+ "atom": {
+ "unicode": "269b",
+ "unicode_alternates": "269b-fe0f",
+ "name": "atom symbol",
+ "shortname": ":atom:",
+ "category": "symbols",
+ "emoji_order": "809",
+ "aliases": [
+ ":atom_symbol:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol",
+ "science"
+ ]
+ },
+ "u7a7a": {
+ "unicode": "1f233",
+ "unicode_alternates": "",
+ "name": "squared cjk unified ideograph-7a7a",
+ "shortname": ":u7a7a:",
+ "category": "symbols",
+ "emoji_order": "810",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol"
+ ]
+ },
+ "u5272": {
+ "unicode": "1f239",
+ "unicode_alternates": "",
+ "name": "squared cjk unified ideograph-5272",
+ "shortname": ":u5272:",
+ "category": "symbols",
+ "emoji_order": "811",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol"
+ ]
+ },
+ "radioactive": {
+ "unicode": "2622",
+ "unicode_alternates": "2622-fe0f",
+ "name": "radioactive sign",
+ "shortname": ":radioactive:",
+ "category": "symbols",
+ "emoji_order": "812",
+ "aliases": [
+ ":radioactive_sign:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol",
+ "science"
+ ]
+ },
+ "biohazard": {
+ "unicode": "2623",
+ "unicode_alternates": "2623-fe0f",
+ "name": "biohazard sign",
+ "shortname": ":biohazard:",
+ "category": "symbols",
+ "emoji_order": "813",
+ "aliases": [
+ ":biohazard_sign:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol",
+ "science"
+ ]
+ },
+ "mobile_phone_off": {
+ "unicode": "1f4f4",
+ "unicode_alternates": "",
+ "name": "mobile phone off",
+ "shortname": ":mobile_phone_off:",
+ "category": "symbols",
+ "emoji_order": "814",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol"
+ ]
+ },
+ "vibration_mode": {
+ "unicode": "1f4f3",
+ "unicode_alternates": "",
+ "name": "vibration mode",
+ "shortname": ":vibration_mode:",
+ "category": "symbols",
+ "emoji_order": "815",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol"
+ ]
+ },
+ "u6709": {
+ "unicode": "1f236",
+ "unicode_alternates": "",
+ "name": "squared cjk unified ideograph-6709",
+ "shortname": ":u6709:",
+ "category": "symbols",
+ "emoji_order": "816",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol"
+ ]
+ },
+ "u7121": {
+ "unicode": "1f21a",
+ "unicode_alternates": "1f21a-fe0f",
+ "name": "squared cjk unified ideograph-7121",
+ "shortname": ":u7121:",
+ "category": "symbols",
+ "emoji_order": "817",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol"
+ ]
+ },
+ "u7533": {
+ "unicode": "1f238",
+ "unicode_alternates": "",
+ "name": "squared cjk unified ideograph-7533",
+ "shortname": ":u7533:",
+ "category": "symbols",
+ "emoji_order": "818",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol"
+ ]
+ },
+ "u55b6": {
+ "unicode": "1f23a",
+ "unicode_alternates": "",
+ "name": "squared cjk unified ideograph-55b6",
+ "shortname": ":u55b6:",
+ "category": "symbols",
+ "emoji_order": "819",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol"
+ ]
+ },
+ "u6708": {
+ "unicode": "1f237",
+ "unicode_alternates": "1f237-fe0f",
+ "name": "squared cjk unified ideograph-6708",
+ "shortname": ":u6708:",
+ "category": "symbols",
+ "emoji_order": "820",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol"
+ ]
+ },
+ "eight_pointed_black_star": {
+ "unicode": "2734",
+ "unicode_alternates": "2734-fe0f",
+ "name": "eight pointed black star",
+ "shortname": ":eight_pointed_black_star:",
+ "category": "symbols",
+ "emoji_order": "821",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol"
+ ]
+ },
+ "vs": {
+ "unicode": "1f19a",
+ "unicode_alternates": "",
+ "name": "squared vs",
+ "shortname": ":vs:",
+ "category": "symbols",
+ "emoji_order": "822",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol"
+ ]
+ },
+ "accept": {
+ "unicode": "1f251",
+ "unicode_alternates": "",
+ "name": "circled ideograph accept",
+ "shortname": ":accept:",
+ "category": "symbols",
+ "emoji_order": "823",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol"
+ ]
+ },
+ "white_flower": {
+ "unicode": "1f4ae",
+ "unicode_alternates": "",
+ "name": "white flower",
+ "shortname": ":white_flower:",
+ "category": "symbols",
+ "emoji_order": "824",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "flower",
+ "symbol"
+ ]
+ },
+ "ideograph_advantage": {
+ "unicode": "1f250",
+ "unicode_alternates": "",
+ "name": "circled ideograph advantage",
+ "shortname": ":ideograph_advantage:",
+ "category": "symbols",
+ "emoji_order": "825",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "japan",
+ "symbol"
+ ]
+ },
+ "secret": {
+ "unicode": "3299",
+ "unicode_alternates": "3299-fe0f",
+ "name": "circled ideograph secret",
+ "shortname": ":secret:",
+ "category": "symbols",
+ "emoji_order": "826",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "japan",
+ "symbol"
+ ]
+ },
+ "congratulations": {
+ "unicode": "3297",
+ "unicode_alternates": "3297-fe0f",
+ "name": "circled ideograph congratulation",
+ "shortname": ":congratulations:",
+ "category": "symbols",
+ "emoji_order": "827",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "japan",
+ "symbol"
+ ]
+ },
+ "u5408": {
+ "unicode": "1f234",
+ "unicode_alternates": "",
+ "name": "squared cjk unified ideograph-5408",
+ "shortname": ":u5408:",
+ "category": "symbols",
+ "emoji_order": "828",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "japan",
+ "symbol"
+ ]
+ },
+ "u6e80": {
+ "unicode": "1f235",
+ "unicode_alternates": "",
+ "name": "squared cjk unified ideograph-6e80",
+ "shortname": ":u6e80:",
+ "category": "symbols",
+ "emoji_order": "829",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "japan",
+ "symbol"
+ ]
+ },
+ "u7981": {
+ "unicode": "1f232",
+ "unicode_alternates": "",
+ "name": "squared cjk unified ideograph-7981",
+ "shortname": ":u7981:",
+ "category": "symbols",
+ "emoji_order": "830",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "japan",
+ "symbol"
+ ]
+ },
+ "a": {
+ "unicode": "1f170",
+ "unicode_alternates": "",
+ "name": "negative squared latin capital letter a",
+ "shortname": ":a:",
+ "category": "symbols",
+ "emoji_order": "831",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol"
+ ]
+ },
+ "b": {
+ "unicode": "1f171",
+ "unicode_alternates": "",
+ "name": "negative squared latin capital letter b",
+ "shortname": ":b:",
+ "category": "symbols",
+ "emoji_order": "832",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol"
+ ]
+ },
+ "ab": {
+ "unicode": "1f18e",
+ "unicode_alternates": "",
+ "name": "negative squared ab",
+ "shortname": ":ab:",
+ "category": "symbols",
+ "emoji_order": "833",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol"
+ ]
+ },
+ "cl": {
+ "unicode": "1f191",
+ "unicode_alternates": "",
+ "name": "squared cl",
+ "shortname": ":cl:",
+ "category": "symbols",
+ "emoji_order": "834",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol"
+ ]
+ },
+ "o2": {
+ "unicode": "1f17e",
+ "unicode_alternates": "",
+ "name": "negative squared latin capital letter o",
+ "shortname": ":o2:",
+ "category": "symbols",
+ "emoji_order": "835",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol"
+ ]
+ },
+ "sos": {
+ "unicode": "1f198",
+ "unicode_alternates": "",
+ "name": "squared sos",
+ "shortname": ":sos:",
+ "category": "symbols",
+ "emoji_order": "836",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol"
+ ]
+ },
+ "no_entry": {
+ "unicode": "26d4",
+ "unicode_alternates": "26d4-fe0f",
+ "name": "no entry",
+ "shortname": ":no_entry:",
+ "category": "symbols",
+ "emoji_order": "837",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol",
+ "circle",
+ "circle"
+ ]
+ },
+ "name_badge": {
+ "unicode": "1f4db",
+ "unicode_alternates": "",
+ "name": "name badge",
+ "shortname": ":name_badge:",
+ "category": "symbols",
+ "emoji_order": "838",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "work"
+ ]
+ },
+ "no_entry_sign": {
+ "unicode": "1f6ab",
+ "unicode_alternates": "",
+ "name": "no entry sign",
+ "shortname": ":no_entry_sign:",
+ "category": "symbols",
+ "emoji_order": "839",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol",
+ "circle",
+ "circle"
+ ]
+ },
+ "x": {
+ "unicode": "274c",
+ "unicode_alternates": "",
+ "name": "cross mark",
+ "shortname": ":x:",
+ "category": "symbols",
+ "emoji_order": "840",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol",
+ "sol",
+ "sol"
+ ]
+ },
+ "o": {
+ "unicode": "2b55",
+ "unicode_alternates": "2b55-fe0f",
+ "name": "heavy large circle",
+ "shortname": ":o:",
+ "category": "symbols",
+ "emoji_order": "841",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol",
+ "circle",
+ "circle"
+ ]
+ },
+ "anger": {
+ "unicode": "1f4a2",
+ "unicode_alternates": "",
+ "name": "anger symbol",
+ "shortname": ":anger:",
+ "category": "symbols",
+ "emoji_order": "842",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol"
+ ]
+ },
+ "hotsprings": {
+ "unicode": "2668",
+ "unicode_alternates": "2668-fe0f",
+ "name": "hot springs",
+ "shortname": ":hotsprings:",
+ "category": "symbols",
+ "emoji_order": "843",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol"
+ ]
+ },
+ "no_pedestrians": {
+ "unicode": "1f6b7",
+ "unicode_alternates": "",
+ "name": "no pedestrians",
+ "shortname": ":no_pedestrians:",
+ "category": "symbols",
+ "emoji_order": "844",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol"
+ ]
+ },
+ "do_not_litter": {
+ "unicode": "1f6af",
+ "unicode_alternates": "",
+ "name": "do not litter symbol",
+ "shortname": ":do_not_litter:",
+ "category": "symbols",
+ "emoji_order": "845",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol"
+ ]
+ },
+ "no_bicycles": {
+ "unicode": "1f6b3",
+ "unicode_alternates": "",
+ "name": "no bicycles",
+ "shortname": ":no_bicycles:",
+ "category": "symbols",
+ "emoji_order": "846",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol"
+ ]
+ },
+ "non-potable_water": {
+ "unicode": "1f6b1",
+ "unicode_alternates": "",
+ "name": "non-potable water symbol",
+ "shortname": ":non-potable_water:",
+ "category": "symbols",
+ "emoji_order": "847",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol"
+ ]
+ },
+ "underage": {
+ "unicode": "1f51e",
+ "unicode_alternates": "",
+ "name": "no one under eighteen symbol",
+ "shortname": ":underage:",
+ "category": "symbols",
+ "emoji_order": "848",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol"
+ ]
+ },
+ "no_mobile_phones": {
+ "unicode": "1f4f5",
+ "unicode_alternates": "",
+ "name": "no mobile phones",
+ "shortname": ":no_mobile_phones:",
+ "category": "symbols",
+ "emoji_order": "849",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol",
+ "phone"
+ ]
+ },
+ "exclamation": {
+ "unicode": "2757",
+ "unicode_alternates": "2757-fe0f",
+ "name": "heavy exclamation mark symbol",
+ "shortname": ":exclamation:",
+ "category": "symbols",
+ "emoji_order": "850",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol",
+ "punctuation"
+ ]
+ },
+ "grey_exclamation": {
+ "unicode": "2755",
+ "unicode_alternates": "",
+ "name": "white exclamation mark ornament",
+ "shortname": ":grey_exclamation:",
+ "category": "symbols",
+ "emoji_order": "851",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol",
+ "punctuation"
+ ]
+ },
+ "question": {
+ "unicode": "2753",
+ "unicode_alternates": "",
+ "name": "black question mark ornament",
+ "shortname": ":question:",
+ "category": "symbols",
+ "emoji_order": "852",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol",
+ "punctuation",
+ "wth",
+ "wth"
+ ]
+ },
+ "grey_question": {
+ "unicode": "2754",
+ "unicode_alternates": "",
+ "name": "white question mark ornament",
+ "shortname": ":grey_question:",
+ "category": "symbols",
+ "emoji_order": "853",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol",
+ "punctuation"
+ ]
+ },
+ "bangbang": {
+ "unicode": "203c",
+ "unicode_alternates": "203c-fe0f",
+ "name": "double exclamation mark",
+ "shortname": ":bangbang:",
+ "category": "symbols",
+ "emoji_order": "854",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol",
+ "punctuation"
+ ]
+ },
+ "interrobang": {
+ "unicode": "2049",
+ "unicode_alternates": "2049-fe0f",
+ "name": "exclamation question mark",
+ "shortname": ":interrobang:",
+ "category": "symbols",
+ "emoji_order": "855",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol",
+ "punctuation"
+ ]
+ },
+ "low_brightness": {
+ "unicode": "1f505",
+ "unicode_alternates": "",
+ "name": "low brightness symbol",
+ "shortname": ":low_brightness:",
+ "category": "symbols",
+ "emoji_order": "857",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol",
+ "sun"
+ ]
+ },
+ "high_brightness": {
+ "unicode": "1f506",
+ "unicode_alternates": "",
+ "name": "high brightness symbol",
+ "shortname": ":high_brightness:",
+ "category": "symbols",
+ "emoji_order": "858",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol",
+ "sun"
+ ]
+ },
+ "trident": {
+ "unicode": "1f531",
+ "unicode_alternates": "",
+ "name": "trident emblem",
+ "shortname": ":trident:",
+ "category": "symbols",
+ "emoji_order": "859",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "symbol"
+ ]
+ },
+ "fleur-de-lis": {
+ "unicode": "269c",
+ "unicode_alternates": "269c-fe0f",
+ "name": "fleur-de-lis",
+ "shortname": ":fleur-de-lis:",
+ "category": "symbols",
+ "emoji_order": "860",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "symbol"
+ ]
+ },
+ "part_alternation_mark": {
+ "unicode": "303d",
+ "unicode_alternates": "303d-fe0f",
+ "name": "part alternation mark",
+ "shortname": ":part_alternation_mark:",
+ "category": "symbols",
+ "emoji_order": "861",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol"
+ ]
+ },
+ "warning": {
+ "unicode": "26a0",
+ "unicode_alternates": "26a0-fe0f",
+ "name": "warning sign",
+ "shortname": ":warning:",
+ "category": "symbols",
+ "emoji_order": "862",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol",
+ "punctuation"
+ ]
+ },
+ "children_crossing": {
+ "unicode": "1f6b8",
+ "unicode_alternates": "",
+ "name": "children crossing",
+ "shortname": ":children_crossing:",
+ "category": "symbols",
+ "emoji_order": "863",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol"
+ ]
+ },
+ "beginner": {
+ "unicode": "1f530",
+ "unicode_alternates": "",
+ "name": "japanese symbol for beginner",
+ "shortname": ":beginner:",
+ "category": "symbols",
+ "emoji_order": "864",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol"
+ ]
+ },
+ "recycle": {
+ "unicode": "267b",
+ "unicode_alternates": "267b-fe0f",
+ "name": "black universal recycling symbol",
+ "shortname": ":recycle:",
+ "category": "symbols",
+ "emoji_order": "865",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol"
+ ]
+ },
+ "u6307": {
+ "unicode": "1f22f",
+ "unicode_alternates": "1f22f-fe0f",
+ "name": "squared cjk unified ideograph-6307",
+ "shortname": ":u6307:",
+ "category": "symbols",
+ "emoji_order": "866",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol"
+ ]
+ },
+ "chart": {
+ "unicode": "1f4b9",
+ "unicode_alternates": "",
+ "name": "chart with upwards trend and yen sign",
+ "shortname": ":chart:",
+ "category": "symbols",
+ "emoji_order": "867",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol",
+ "money",
+ "money"
+ ]
+ },
+ "sparkle": {
+ "unicode": "2747",
+ "unicode_alternates": "2747-fe0f",
+ "name": "sparkle",
+ "shortname": ":sparkle:",
+ "category": "symbols",
+ "emoji_order": "868",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol"
+ ]
+ },
+ "eight_spoked_asterisk": {
+ "unicode": "2733",
+ "unicode_alternates": "2733-fe0f",
+ "name": "eight spoked asterisk",
+ "shortname": ":eight_spoked_asterisk:",
+ "category": "symbols",
+ "emoji_order": "869",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol"
+ ]
+ },
+ "negative_squared_cross_mark": {
+ "unicode": "274e",
+ "unicode_alternates": "",
+ "name": "negative squared cross mark",
+ "shortname": ":negative_squared_cross_mark:",
+ "category": "symbols",
+ "emoji_order": "870",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol"
+ ]
+ },
+ "white_check_mark": {
+ "unicode": "2705",
+ "unicode_alternates": "",
+ "name": "white heavy check mark",
+ "shortname": ":white_check_mark:",
+ "category": "symbols",
+ "emoji_order": "871",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol"
+ ]
+ },
+ "diamond_shape_with_a_dot_inside": {
+ "unicode": "1f4a0",
+ "unicode_alternates": "",
+ "name": "diamond shape with a dot inside",
+ "shortname": ":diamond_shape_with_a_dot_inside:",
+ "category": "symbols",
+ "emoji_order": "872",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol"
+ ]
+ },
+ "cyclone": {
+ "unicode": "1f300",
+ "unicode_alternates": "",
+ "name": "cyclone",
+ "shortname": ":cyclone:",
+ "category": "symbols",
+ "emoji_order": "873",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol",
+ "drugs",
+ "drugs"
+ ]
+ },
+ "loop": {
+ "unicode": "27bf",
+ "unicode_alternates": "",
+ "name": "double curly loop",
+ "shortname": ":loop:",
+ "category": "symbols",
+ "emoji_order": "874",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol"
+ ]
+ },
+ "globe_with_meridians": {
+ "unicode": "1f310",
+ "unicode_alternates": "",
+ "name": "globe with meridians",
+ "shortname": ":globe_with_meridians:",
+ "category": "symbols",
+ "emoji_order": "875",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol",
+ "globe",
+ "globe"
+ ]
+ },
+ "m": {
+ "unicode": "24c2",
+ "unicode_alternates": "24c2-fe0f",
+ "name": "circled latin capital letter m",
+ "shortname": ":m:",
+ "category": "symbols",
+ "emoji_order": "876",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol"
+ ]
+ },
+ "atm": {
+ "unicode": "1f3e7",
+ "unicode_alternates": "",
+ "name": "automated teller machine",
+ "shortname": ":atm:",
+ "category": "symbols",
+ "emoji_order": "877",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "electronics",
+ "symbol",
+ "money",
+ "money"
+ ]
+ },
+ "sa": {
+ "unicode": "1f202",
+ "unicode_alternates": "1f202-fe0f",
+ "name": "squared katakana sa",
+ "shortname": ":sa:",
+ "category": "symbols",
+ "emoji_order": "878",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol"
+ ]
+ },
+ "passport_control": {
+ "unicode": "1f6c2",
+ "unicode_alternates": "",
+ "name": "passport control",
+ "shortname": ":passport_control:",
+ "category": "symbols",
+ "emoji_order": "879",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol"
+ ]
+ },
+ "customs": {
+ "unicode": "1f6c3",
+ "unicode_alternates": "",
+ "name": "customs",
+ "shortname": ":customs:",
+ "category": "symbols",
+ "emoji_order": "880",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol"
+ ]
+ },
+ "baggage_claim": {
+ "unicode": "1f6c4",
+ "unicode_alternates": "",
+ "name": "baggage claim",
+ "shortname": ":baggage_claim:",
+ "category": "symbols",
+ "emoji_order": "881",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol"
+ ]
+ },
+ "left_luggage": {
+ "unicode": "1f6c5",
+ "unicode_alternates": "",
+ "name": "left luggage",
+ "shortname": ":left_luggage:",
+ "category": "symbols",
+ "emoji_order": "882",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol"
+ ]
+ },
+ "wheelchair": {
+ "unicode": "267f",
+ "unicode_alternates": "267f-fe0f",
+ "name": "wheelchair symbol",
+ "shortname": ":wheelchair:",
+ "category": "symbols",
+ "emoji_order": "883",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol"
+ ]
+ },
+ "no_smoking": {
+ "unicode": "1f6ad",
+ "unicode_alternates": "",
+ "name": "no smoking symbol",
+ "shortname": ":no_smoking:",
+ "category": "symbols",
+ "emoji_order": "884",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol",
+ "smoking",
+ "smoking"
+ ]
+ },
+ "wc": {
+ "unicode": "1f6be",
+ "unicode_alternates": "",
+ "name": "water closet",
+ "shortname": ":wc:",
+ "category": "symbols",
+ "emoji_order": "885",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol"
+ ]
+ },
+ "parking": {
+ "unicode": "1f17f",
+ "unicode_alternates": "1f17f-fe0f",
+ "name": "negative squared latin capital letter p",
+ "shortname": ":parking:",
+ "category": "symbols",
+ "emoji_order": "886",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol"
+ ]
+ },
+ "potable_water": {
+ "unicode": "1f6b0",
+ "unicode_alternates": "",
+ "name": "potable water symbol",
+ "shortname": ":potable_water:",
+ "category": "symbols",
+ "emoji_order": "887",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol"
+ ]
+ },
+ "mens": {
+ "unicode": "1f6b9",
+ "unicode_alternates": "",
+ "name": "mens symbol",
+ "shortname": ":mens:",
+ "category": "symbols",
+ "emoji_order": "888",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol"
+ ]
+ },
+ "womens": {
+ "unicode": "1f6ba",
+ "unicode_alternates": "",
+ "name": "womens symbol",
+ "shortname": ":womens:",
+ "category": "symbols",
+ "emoji_order": "889",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol"
+ ]
+ },
+ "baby_symbol": {
+ "unicode": "1f6bc",
+ "unicode_alternates": "",
+ "name": "baby symbol",
+ "shortname": ":baby_symbol:",
+ "category": "symbols",
+ "emoji_order": "890",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol"
+ ]
+ },
+ "restroom": {
+ "unicode": "1f6bb",
+ "unicode_alternates": "",
+ "name": "restroom",
+ "shortname": ":restroom:",
+ "category": "symbols",
+ "emoji_order": "891",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol"
+ ]
+ },
+ "put_litter_in_its_place": {
+ "unicode": "1f6ae",
+ "unicode_alternates": "",
+ "name": "put litter in its place symbol",
+ "shortname": ":put_litter_in_its_place:",
+ "category": "symbols",
+ "emoji_order": "892",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol"
+ ]
+ },
+ "cinema": {
+ "unicode": "1f3a6",
+ "unicode_alternates": "",
+ "name": "cinema",
+ "shortname": ":cinema:",
+ "category": "symbols",
+ "emoji_order": "893",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol",
+ "camera",
+ "movie"
+ ]
+ },
+ "signal_strength": {
+ "unicode": "1f4f6",
+ "unicode_alternates": "",
+ "name": "antenna with bars",
+ "shortname": ":signal_strength:",
+ "category": "symbols",
+ "emoji_order": "894",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol"
+ ]
+ },
+ "koko": {
+ "unicode": "1f201",
+ "unicode_alternates": "",
+ "name": "squared katakana koko",
+ "shortname": ":koko:",
+ "category": "symbols",
+ "emoji_order": "895",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol"
+ ]
+ },
+ "ng": {
+ "unicode": "1f196",
+ "unicode_alternates": "",
+ "name": "squared ng",
+ "shortname": ":ng:",
+ "category": "symbols",
+ "emoji_order": "896",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol"
+ ]
+ },
+ "ok": {
+ "unicode": "1f197",
+ "unicode_alternates": "",
+ "name": "squared ok",
+ "shortname": ":ok:",
+ "category": "symbols",
+ "emoji_order": "897",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol"
+ ]
+ },
+ "up": {
+ "unicode": "1f199",
+ "unicode_alternates": "",
+ "name": "squared up with exclamation mark",
+ "shortname": ":up:",
+ "category": "symbols",
+ "emoji_order": "898",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol"
+ ]
+ },
+ "cool": {
+ "unicode": "1f192",
+ "unicode_alternates": "",
+ "name": "squared cool",
+ "shortname": ":cool:",
+ "category": "symbols",
+ "emoji_order": "899",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol"
+ ]
+ },
+ "new": {
+ "unicode": "1f195",
+ "unicode_alternates": "",
+ "name": "squared new",
+ "shortname": ":new:",
+ "category": "symbols",
+ "emoji_order": "900",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol"
+ ]
+ },
+ "free": {
+ "unicode": "1f193",
+ "unicode_alternates": "",
+ "name": "squared free",
+ "shortname": ":free:",
+ "category": "symbols",
+ "emoji_order": "901",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol"
+ ]
+ },
+ "zero": {
+ "unicode": "0030-20e3",
+ "unicode_alternates": "0030-fe0f-20e3",
+ "name": "keycap digit zero",
+ "shortname": ":zero:",
+ "category": "symbols",
+ "emoji_order": "902",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "number",
+ "math",
+ "symbol"
+ ]
+ },
+ "one": {
+ "unicode": "0031-20e3",
+ "unicode_alternates": "0031-fe0f-20e3",
+ "name": "keycap digit one",
+ "shortname": ":one:",
+ "category": "symbols",
+ "emoji_order": "903",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "number",
+ "math",
+ "symbol"
+ ]
+ },
+ "two": {
+ "unicode": "0032-20e3",
+ "unicode_alternates": "0032-fe0f-20e3",
+ "name": "keycap digit two",
+ "shortname": ":two:",
+ "category": "symbols",
+ "emoji_order": "904",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "number",
+ "math",
+ "symbol"
+ ]
+ },
+ "three": {
+ "unicode": "0033-20e3",
+ "unicode_alternates": "0033-fe0f-20e3",
+ "name": "keycap digit three",
+ "shortname": ":three:",
+ "category": "symbols",
+ "emoji_order": "905",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "number",
+ "math",
+ "symbol"
+ ]
+ },
+ "four": {
+ "unicode": "0034-20e3",
+ "unicode_alternates": "0034-fe0f-20e3",
+ "name": "keycap digit four",
+ "shortname": ":four:",
+ "category": "symbols",
+ "emoji_order": "906",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "number",
+ "math",
+ "symbol"
+ ]
+ },
+ "five": {
+ "unicode": "0035-20e3",
+ "unicode_alternates": "0035-fe0f-20e3",
+ "name": "keycap digit five",
+ "shortname": ":five:",
+ "category": "symbols",
+ "emoji_order": "907",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "number",
+ "math",
+ "symbol"
+ ]
+ },
+ "six": {
+ "unicode": "0036-20e3",
+ "unicode_alternates": "0036-fe0f-20e3",
+ "name": "keycap digit six",
+ "shortname": ":six:",
+ "category": "symbols",
+ "emoji_order": "908",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "number",
+ "math",
+ "symbol"
+ ]
+ },
+ "seven": {
+ "unicode": "0037-20e3",
+ "unicode_alternates": "0037-fe0f-20e3",
+ "name": "keycap digit seven",
+ "shortname": ":seven:",
+ "category": "symbols",
+ "emoji_order": "909",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "number",
+ "math",
+ "symbol"
+ ]
+ },
+ "eight": {
+ "unicode": "0038-20e3",
+ "unicode_alternates": "0038-fe0f-20e3",
+ "name": "keycap digit eight",
+ "shortname": ":eight:",
+ "category": "symbols",
+ "emoji_order": "910",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "number",
+ "math",
+ "symbol"
+ ]
+ },
+ "nine": {
+ "unicode": "0039-20e3",
+ "unicode_alternates": "0039-fe0f-20e3",
+ "name": "keycap digit nine",
+ "shortname": ":nine:",
+ "category": "symbols",
+ "emoji_order": "911",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "number",
+ "math",
+ "symbol"
+ ]
+ },
+ "ten": {
+ "unicode": "1f51f",
+ "unicode_alternates": "",
+ "name": "keycap ten",
+ "shortname": ":ten:",
+ "category": "symbols",
+ "emoji_order": "912",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "number",
+ "math",
+ "symbol"
+ ]
+ },
+ "arrow_forward": {
+ "unicode": "25b6",
+ "unicode_alternates": "25b6-fe0f",
+ "name": "black right-pointing triangle",
+ "shortname": ":arrow_forward:",
+ "category": "symbols",
+ "emoji_order": "914",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "arrow",
+ "symbol",
+ "triangle",
+ "triangle"
+ ]
+ },
+ "pause_button": {
+ "unicode": "23f8",
+ "unicode_alternates": "23f8-fe0f",
+ "name": "double vertical bar",
+ "shortname": ":pause_button:",
+ "category": "symbols",
+ "emoji_order": "915",
+ "aliases": [
+ ":double_vertical_bar:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol"
+ ]
+ },
+ "play_pause": {
+ "unicode": "23ef",
+ "unicode_alternates": "23ef-fe0f",
+ "name": "black right-pointing double triangle with double vertical bar",
+ "shortname": ":play_pause:",
+ "category": "symbols",
+ "emoji_order": "916",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "arrow",
+ "symbol"
+ ]
+ },
+ "stop_button": {
+ "unicode": "23f9",
+ "unicode_alternates": "23f9-fe0f",
+ "name": "black square for stop",
+ "shortname": ":stop_button:",
+ "category": "symbols",
+ "emoji_order": "917",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol",
+ "square",
+ "square"
+ ]
+ },
+ "record_button": {
+ "unicode": "23fa",
+ "unicode_alternates": "23fa-fe0f",
+ "name": "black circle for record",
+ "shortname": ":record_button:",
+ "category": "symbols",
+ "emoji_order": "918",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol",
+ "circle",
+ "circle"
+ ]
+ },
+ "track_next": {
+ "unicode": "23ed",
+ "unicode_alternates": "23ed-fe0f",
+ "name": "black right-pointing double triangle with vertical bar",
+ "shortname": ":track_next:",
+ "category": "symbols",
+ "emoji_order": "919",
+ "aliases": [
+ ":next_track:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "arrow",
+ "symbol"
+ ]
+ },
+ "track_previous": {
+ "unicode": "23ee",
+ "unicode_alternates": "23ee-fe0f",
+ "name": "black left-pointing double triangle with vertical bar",
+ "shortname": ":track_previous:",
+ "category": "symbols",
+ "emoji_order": "920",
+ "aliases": [
+ ":previous_track:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "arrow",
+ "symbol"
+ ]
+ },
+ "fast_forward": {
+ "unicode": "23e9",
+ "unicode_alternates": "",
+ "name": "black right-pointing double triangle",
+ "shortname": ":fast_forward:",
+ "category": "symbols",
+ "emoji_order": "921",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "arrow",
+ "symbol"
+ ]
+ },
+ "rewind": {
+ "unicode": "23ea",
+ "unicode_alternates": "",
+ "name": "black left-pointing double triangle",
+ "shortname": ":rewind:",
+ "category": "symbols",
+ "emoji_order": "922",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "arrow",
+ "symbol"
+ ]
+ },
+ "twisted_rightwards_arrows": {
+ "unicode": "1f500",
+ "unicode_alternates": "",
+ "name": "twisted rightwards arrows",
+ "shortname": ":twisted_rightwards_arrows:",
+ "category": "symbols",
+ "emoji_order": "923",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "arrow",
+ "symbol"
+ ]
+ },
+ "repeat": {
+ "unicode": "1f501",
+ "unicode_alternates": "",
+ "name": "clockwise rightwards and leftwards open circle arrows",
+ "shortname": ":repeat:",
+ "category": "symbols",
+ "emoji_order": "924",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "arrow",
+ "symbol"
+ ]
+ },
+ "repeat_one": {
+ "unicode": "1f502",
+ "unicode_alternates": "",
+ "name": "clockwise rightwards and leftwards open circle arrows with circled one overlay",
+ "shortname": ":repeat_one:",
+ "category": "symbols",
+ "emoji_order": "925",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "arrow",
+ "symbol"
+ ]
+ },
+ "arrow_backward": {
+ "unicode": "25c0",
+ "unicode_alternates": "25c0-fe0f",
+ "name": "black left-pointing triangle",
+ "shortname": ":arrow_backward:",
+ "category": "symbols",
+ "emoji_order": "926",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "arrow",
+ "symbol",
+ "triangle",
+ "triangle"
+ ]
+ },
+ "arrow_up_small": {
+ "unicode": "1f53c",
+ "unicode_alternates": "",
+ "name": "up-pointing small red triangle",
+ "shortname": ":arrow_up_small:",
+ "category": "symbols",
+ "emoji_order": "927",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "arrow",
+ "symbol",
+ "triangle",
+ "triangle"
+ ]
+ },
+ "arrow_down_small": {
+ "unicode": "1f53d",
+ "unicode_alternates": "",
+ "name": "down-pointing small red triangle",
+ "shortname": ":arrow_down_small:",
+ "category": "symbols",
+ "emoji_order": "928",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "arrow",
+ "symbol",
+ "triangle",
+ "triangle"
+ ]
+ },
+ "arrow_double_up": {
+ "unicode": "23eb",
+ "unicode_alternates": "",
+ "name": "black up-pointing double triangle",
+ "shortname": ":arrow_double_up:",
+ "category": "symbols",
+ "emoji_order": "929",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "arrow",
+ "symbol"
+ ]
+ },
+ "arrow_double_down": {
+ "unicode": "23ec",
+ "unicode_alternates": "",
+ "name": "black down-pointing double triangle",
+ "shortname": ":arrow_double_down:",
+ "category": "symbols",
+ "emoji_order": "930",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "arrow",
+ "symbol"
+ ]
+ },
+ "arrow_right": {
+ "unicode": "27a1",
+ "unicode_alternates": "27a1-fe0f",
+ "name": "black rightwards arrow",
+ "shortname": ":arrow_right:",
+ "category": "symbols",
+ "emoji_order": "931",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "arrow",
+ "symbol"
+ ]
+ },
+ "arrow_left": {
+ "unicode": "2b05",
+ "unicode_alternates": "2b05-fe0f",
+ "name": "leftwards black arrow",
+ "shortname": ":arrow_left:",
+ "category": "symbols",
+ "emoji_order": "932",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "arrow",
+ "symbol"
+ ]
+ },
+ "arrow_up": {
+ "unicode": "2b06",
+ "unicode_alternates": "2b06-fe0f",
+ "name": "upwards black arrow",
+ "shortname": ":arrow_up:",
+ "category": "symbols",
+ "emoji_order": "933",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "arrow",
+ "symbol"
+ ]
+ },
+ "arrow_down": {
+ "unicode": "2b07",
+ "unicode_alternates": "2b07-fe0f",
+ "name": "downwards black arrow",
+ "shortname": ":arrow_down:",
+ "category": "symbols",
+ "emoji_order": "934",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "arrow",
+ "symbol"
+ ]
+ },
+ "arrow_upper_right": {
+ "unicode": "2197",
+ "unicode_alternates": "2197-fe0f",
+ "name": "north east arrow",
+ "shortname": ":arrow_upper_right:",
+ "category": "symbols",
+ "emoji_order": "935",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "arrow",
+ "symbol"
+ ]
+ },
+ "arrow_lower_right": {
+ "unicode": "2198",
+ "unicode_alternates": "2198-fe0f",
+ "name": "south east arrow",
+ "shortname": ":arrow_lower_right:",
+ "category": "symbols",
+ "emoji_order": "936",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "arrow",
+ "symbol"
+ ]
+ },
+ "arrow_lower_left": {
+ "unicode": "2199",
+ "unicode_alternates": "2199-fe0f",
+ "name": "south west arrow",
+ "shortname": ":arrow_lower_left:",
+ "category": "symbols",
+ "emoji_order": "937",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "arrow",
+ "symbol"
+ ]
+ },
+ "arrow_upper_left": {
+ "unicode": "2196",
+ "unicode_alternates": "2196-fe0f",
+ "name": "north west arrow",
+ "shortname": ":arrow_upper_left:",
+ "category": "symbols",
+ "emoji_order": "938",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "arrow",
+ "symbol"
+ ]
+ },
+ "arrow_up_down": {
+ "unicode": "2195",
+ "unicode_alternates": "2195-fe0f",
+ "name": "up down arrow",
+ "shortname": ":arrow_up_down:",
+ "category": "symbols",
+ "emoji_order": "939",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "arrow",
+ "symbol"
+ ]
+ },
+ "left_right_arrow": {
+ "unicode": "2194",
+ "unicode_alternates": "2194-fe0f",
+ "name": "left right arrow",
+ "shortname": ":left_right_arrow:",
+ "category": "symbols",
+ "emoji_order": "940",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "arrow",
+ "symbol"
+ ]
+ },
+ "arrows_counterclockwise": {
+ "unicode": "1f504",
+ "unicode_alternates": "",
+ "name": "anticlockwise downwards and upwards open circle arrows",
+ "shortname": ":arrows_counterclockwise:",
+ "category": "symbols",
+ "emoji_order": "941",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "arrow",
+ "symbol"
+ ]
+ },
+ "arrow_right_hook": {
+ "unicode": "21aa",
+ "unicode_alternates": "21aa-fe0f",
+ "name": "rightwards arrow with hook",
+ "shortname": ":arrow_right_hook:",
+ "category": "symbols",
+ "emoji_order": "942",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "arrow",
+ "symbol"
+ ]
+ },
+ "leftwards_arrow_with_hook": {
+ "unicode": "21a9",
+ "unicode_alternates": "21a9-fe0f",
+ "name": "leftwards arrow with hook",
+ "shortname": ":leftwards_arrow_with_hook:",
+ "category": "symbols",
+ "emoji_order": "943",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "arrow",
+ "symbol"
+ ]
+ },
+ "arrow_heading_up": {
+ "unicode": "2934",
+ "unicode_alternates": "2934-fe0f",
+ "name": "arrow pointing rightwards then curving upwards",
+ "shortname": ":arrow_heading_up:",
+ "category": "symbols",
+ "emoji_order": "944",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "arrow",
+ "symbol"
+ ]
+ },
+ "arrow_heading_down": {
+ "unicode": "2935",
+ "unicode_alternates": "2935-fe0f",
+ "name": "arrow pointing rightwards then curving downwards",
+ "shortname": ":arrow_heading_down:",
+ "category": "symbols",
+ "emoji_order": "945",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "arrow",
+ "symbol"
+ ]
+ },
+ "hash": {
+ "unicode": "0023-20e3",
+ "unicode_alternates": "0023-fe0f-20e3",
+ "name": "keycap number sign",
+ "shortname": ":hash:",
+ "category": "symbols",
+ "emoji_order": "946",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "number",
+ "symbol"
+ ]
+ },
+ "asterisk": {
+ "unicode": "002a-20e3",
+ "unicode_alternates": "002a-fe0f-20e3",
+ "name": "keycap asterisk",
+ "shortname": ":asterisk:",
+ "category": "symbols",
+ "emoji_order": "947",
+ "aliases": [
+ ":keycap_asterisk:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol"
+ ]
+ },
+ "information_source": {
+ "unicode": "2139",
+ "unicode_alternates": "2139-fe0f",
+ "name": "information source",
+ "shortname": ":information_source:",
+ "category": "symbols",
+ "emoji_order": "948",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol"
+ ]
+ },
+ "abc": {
+ "unicode": "1f524",
+ "unicode_alternates": "",
+ "name": "input symbol for latin letters",
+ "shortname": ":abc:",
+ "category": "symbols",
+ "emoji_order": "949",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol"
+ ]
+ },
+ "abcd": {
+ "unicode": "1f521",
+ "unicode_alternates": "",
+ "name": "input symbol for latin small letters",
+ "shortname": ":abcd:",
+ "category": "symbols",
+ "emoji_order": "950",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol"
+ ]
+ },
+ "capital_abcd": {
+ "unicode": "1f520",
+ "unicode_alternates": "",
+ "name": "input symbol for latin capital letters",
+ "shortname": ":capital_abcd:",
+ "category": "symbols",
+ "emoji_order": "951",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol"
+ ]
+ },
+ "symbols": {
+ "unicode": "1f523",
+ "unicode_alternates": "",
+ "name": "input symbol for symbols",
+ "shortname": ":symbols:",
+ "category": "symbols",
+ "emoji_order": "952",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol"
+ ]
+ },
+ "musical_note": {
+ "unicode": "1f3b5",
+ "unicode_alternates": "",
+ "name": "musical note",
+ "shortname": ":musical_note:",
+ "category": "symbols",
+ "emoji_order": "953",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "instruments",
+ "symbol"
+ ]
+ },
+ "notes": {
+ "unicode": "1f3b6",
+ "unicode_alternates": "",
+ "name": "multiple musical notes",
+ "shortname": ":notes:",
+ "category": "symbols",
+ "emoji_order": "954",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "instruments",
+ "symbol"
+ ]
+ },
+ "wavy_dash": {
+ "unicode": "3030",
+ "unicode_alternates": "3030-fe0f",
+ "name": "wavy dash",
+ "shortname": ":wavy_dash:",
+ "category": "symbols",
+ "emoji_order": "955",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol"
+ ]
+ },
+ "curly_loop": {
+ "unicode": "27b0",
+ "unicode_alternates": "",
+ "name": "curly loop",
+ "shortname": ":curly_loop:",
+ "category": "symbols",
+ "emoji_order": "956",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol"
+ ]
+ },
+ "heavy_check_mark": {
+ "unicode": "2714",
+ "unicode_alternates": "2714-fe0f",
+ "name": "heavy check mark",
+ "shortname": ":heavy_check_mark:",
+ "category": "symbols",
+ "emoji_order": "957",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol"
+ ]
+ },
+ "arrows_clockwise": {
+ "unicode": "1f503",
+ "unicode_alternates": "",
+ "name": "clockwise downwards and upwards open circle arrows",
+ "shortname": ":arrows_clockwise:",
+ "category": "symbols",
+ "emoji_order": "958",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "arrow",
+ "symbol"
+ ]
+ },
+ "heavy_plus_sign": {
+ "unicode": "2795",
+ "unicode_alternates": "",
+ "name": "heavy plus sign",
+ "shortname": ":heavy_plus_sign:",
+ "category": "symbols",
+ "emoji_order": "959",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "math",
+ "symbol"
+ ]
+ },
+ "heavy_minus_sign": {
+ "unicode": "2796",
+ "unicode_alternates": "",
+ "name": "heavy minus sign",
+ "shortname": ":heavy_minus_sign:",
+ "category": "symbols",
+ "emoji_order": "960",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "math",
+ "symbol"
+ ]
+ },
+ "heavy_division_sign": {
+ "unicode": "2797",
+ "unicode_alternates": "",
+ "name": "heavy division sign",
+ "shortname": ":heavy_division_sign:",
+ "category": "symbols",
+ "emoji_order": "961",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "math",
+ "symbol"
+ ]
+ },
+ "heavy_multiplication_x": {
+ "unicode": "2716",
+ "unicode_alternates": "2716-fe0f",
+ "name": "heavy multiplication x",
+ "shortname": ":heavy_multiplication_x:",
+ "category": "symbols",
+ "emoji_order": "962",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "math",
+ "symbol"
+ ]
+ },
+ "heavy_dollar_sign": {
+ "unicode": "1f4b2",
+ "unicode_alternates": "",
+ "name": "heavy dollar sign",
+ "shortname": ":heavy_dollar_sign:",
+ "category": "symbols",
+ "emoji_order": "963",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "math",
+ "symbol",
+ "money",
+ "money"
+ ]
+ },
+ "currency_exchange": {
+ "unicode": "1f4b1",
+ "unicode_alternates": "",
+ "name": "currency exchange",
+ "shortname": ":currency_exchange:",
+ "category": "symbols",
+ "emoji_order": "964",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol",
+ "money",
+ "money"
+ ]
+ },
+ "copyright": {
+ "unicode": "00a9",
+ "unicode_alternates": "00a9-fe0f",
+ "name": "copyright sign",
+ "shortname": ":copyright:",
+ "category": "symbols",
+ "emoji_order": "965",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol"
+ ]
+ },
+ "registered": {
+ "unicode": "00ae",
+ "unicode_alternates": "00ae-fe0f",
+ "name": "registered sign",
+ "shortname": ":registered:",
+ "category": "symbols",
+ "emoji_order": "966",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol"
+ ]
+ },
+ "tm": {
+ "unicode": "2122",
+ "unicode_alternates": "2122-fe0f",
+ "name": "trade mark sign",
+ "shortname": ":tm:",
+ "category": "symbols",
+ "emoji_order": "967",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol"
+ ]
+ },
+ "end": {
+ "unicode": "1f51a",
+ "unicode_alternates": "",
+ "name": "end with leftwards arrow above",
+ "shortname": ":end:",
+ "category": "symbols",
+ "emoji_order": "968",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "arrow",
+ "symbol"
+ ]
+ },
+ "back": {
+ "unicode": "1f519",
+ "unicode_alternates": "",
+ "name": "back with leftwards arrow above",
+ "shortname": ":back:",
+ "category": "symbols",
+ "emoji_order": "969",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "arrow",
+ "symbol"
+ ]
+ },
+ "on": {
+ "unicode": "1f51b",
+ "unicode_alternates": "",
+ "name": "on with exclamation mark with left right arrow abo",
+ "shortname": ":on:",
+ "category": "symbols",
+ "emoji_order": "970",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "arrow",
+ "symbol"
+ ]
+ },
+ "top": {
+ "unicode": "1f51d",
+ "unicode_alternates": "",
+ "name": "top with upwards arrow above",
+ "shortname": ":top:",
+ "category": "symbols",
+ "emoji_order": "971",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "arrow",
+ "symbol"
+ ]
+ },
+ "soon": {
+ "unicode": "1f51c",
+ "unicode_alternates": "",
+ "name": "soon with rightwards arrow above",
+ "shortname": ":soon:",
+ "category": "symbols",
+ "emoji_order": "972",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "arrow",
+ "symbol"
+ ]
+ },
+ "ballot_box_with_check": {
+ "unicode": "2611",
+ "unicode_alternates": "2611-fe0f",
+ "name": "ballot box with check",
+ "shortname": ":ballot_box_with_check:",
+ "category": "symbols",
+ "emoji_order": "973",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol"
+ ]
+ },
+ "radio_button": {
+ "unicode": "1f518",
+ "unicode_alternates": "",
+ "name": "radio button",
+ "shortname": ":radio_button:",
+ "category": "symbols",
+ "emoji_order": "974",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol",
+ "circle",
+ "circle"
+ ]
+ },
+ "white_circle": {
+ "unicode": "26aa",
+ "unicode_alternates": "26aa-fe0f",
+ "name": "medium white circle",
+ "shortname": ":white_circle:",
+ "category": "symbols",
+ "emoji_order": "975",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "shapes",
+ "symbol",
+ "circle",
+ "circle"
+ ]
+ },
+ "black_circle": {
+ "unicode": "26ab",
+ "unicode_alternates": "26ab-fe0f",
+ "name": "medium black circle",
+ "shortname": ":black_circle:",
+ "category": "symbols",
+ "emoji_order": "976",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "shapes",
+ "symbol",
+ "circle",
+ "circle"
+ ]
+ },
+ "red_circle": {
+ "unicode": "1f534",
+ "unicode_alternates": "",
+ "name": "large red circle",
+ "shortname": ":red_circle:",
+ "category": "symbols",
+ "emoji_order": "977",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "shapes",
+ "symbol",
+ "circle",
+ "circle"
+ ]
+ },
+ "large_blue_circle": {
+ "unicode": "1f535",
+ "unicode_alternates": "",
+ "name": "large blue circle",
+ "shortname": ":large_blue_circle:",
+ "category": "symbols",
+ "emoji_order": "978",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "shapes",
+ "symbol",
+ "circle",
+ "circle"
+ ]
+ },
+ "small_orange_diamond": {
+ "unicode": "1f538",
+ "unicode_alternates": "",
+ "name": "small orange diamond",
+ "shortname": ":small_orange_diamond:",
+ "category": "symbols",
+ "emoji_order": "979",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "shapes",
+ "symbol"
+ ]
+ },
+ "small_blue_diamond": {
+ "unicode": "1f539",
+ "unicode_alternates": "",
+ "name": "small blue diamond",
+ "shortname": ":small_blue_diamond:",
+ "category": "symbols",
+ "emoji_order": "980",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "shapes",
+ "symbol"
+ ]
+ },
+ "large_orange_diamond": {
+ "unicode": "1f536",
+ "unicode_alternates": "",
+ "name": "large orange diamond",
+ "shortname": ":large_orange_diamond:",
+ "category": "symbols",
+ "emoji_order": "981",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "shapes",
+ "symbol"
+ ]
+ },
+ "large_blue_diamond": {
+ "unicode": "1f537",
+ "unicode_alternates": "",
+ "name": "large blue diamond",
+ "shortname": ":large_blue_diamond:",
+ "category": "symbols",
+ "emoji_order": "982",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "shapes",
+ "symbol"
+ ]
+ },
+ "small_red_triangle": {
+ "unicode": "1f53a",
+ "unicode_alternates": "",
+ "name": "up-pointing red triangle",
+ "shortname": ":small_red_triangle:",
+ "category": "symbols",
+ "emoji_order": "983",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "shapes",
+ "symbol",
+ "triangle",
+ "triangle"
+ ]
+ },
+ "black_small_square": {
+ "unicode": "25aa",
+ "unicode_alternates": "25aa-fe0f",
+ "name": "black small square",
+ "shortname": ":black_small_square:",
+ "category": "symbols",
+ "emoji_order": "984",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "shapes",
+ "symbol",
+ "square",
+ "square"
+ ]
+ },
+ "white_small_square": {
+ "unicode": "25ab",
+ "unicode_alternates": "25ab-fe0f",
+ "name": "white small square",
+ "shortname": ":white_small_square:",
+ "category": "symbols",
+ "emoji_order": "985",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "shapes",
+ "symbol",
+ "square",
+ "square"
+ ]
+ },
+ "black_large_square": {
+ "unicode": "2b1b",
+ "unicode_alternates": "2b1b-fe0f",
+ "name": "black large square",
+ "shortname": ":black_large_square:",
+ "category": "symbols",
+ "emoji_order": "986",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "shapes",
+ "symbol",
+ "square",
+ "square"
+ ]
+ },
+ "white_large_square": {
+ "unicode": "2b1c",
+ "unicode_alternates": "2b1c-fe0f",
+ "name": "white large square",
+ "shortname": ":white_large_square:",
+ "category": "symbols",
+ "emoji_order": "987",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "shapes",
+ "symbol",
+ "square",
+ "square"
+ ]
+ },
+ "small_red_triangle_down": {
+ "unicode": "1f53b",
+ "unicode_alternates": "",
+ "name": "down-pointing red triangle",
+ "shortname": ":small_red_triangle_down:",
+ "category": "symbols",
+ "emoji_order": "988",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "shapes",
+ "symbol",
+ "triangle",
+ "triangle"
+ ]
+ },
+ "black_medium_square": {
+ "unicode": "25fc",
+ "unicode_alternates": "25fc-fe0f",
+ "name": "black medium square",
+ "shortname": ":black_medium_square:",
+ "category": "symbols",
+ "emoji_order": "989",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "shapes",
+ "symbol",
+ "square",
+ "square"
+ ]
+ },
+ "white_medium_square": {
+ "unicode": "25fb",
+ "unicode_alternates": "25fb-fe0f",
+ "name": "white medium square",
+ "shortname": ":white_medium_square:",
+ "category": "symbols",
+ "emoji_order": "990",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "shapes",
+ "symbol",
+ "square",
+ "square"
+ ]
+ },
+ "black_medium_small_square": {
+ "unicode": "25fe",
+ "unicode_alternates": "25fe-fe0f",
+ "name": "black medium small square",
+ "shortname": ":black_medium_small_square:",
+ "category": "symbols",
+ "emoji_order": "991",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "shapes",
+ "symbol",
+ "square",
+ "square"
+ ]
+ },
+ "white_medium_small_square": {
+ "unicode": "25fd",
+ "unicode_alternates": "25fd-fe0f",
+ "name": "white medium small square",
+ "shortname": ":white_medium_small_square:",
+ "category": "symbols",
+ "emoji_order": "992",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "shapes",
+ "symbol",
+ "square",
+ "square"
+ ]
+ },
+ "black_square_button": {
+ "unicode": "1f532",
+ "unicode_alternates": "",
+ "name": "black square button",
+ "shortname": ":black_square_button:",
+ "category": "symbols",
+ "emoji_order": "993",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "shapes",
+ "symbol",
+ "square",
+ "square"
+ ]
+ },
+ "white_square_button": {
+ "unicode": "1f533",
+ "unicode_alternates": "",
+ "name": "white square button",
+ "shortname": ":white_square_button:",
+ "category": "symbols",
+ "emoji_order": "994",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "shapes",
+ "symbol",
+ "square",
+ "square"
+ ]
+ },
+ "speaker": {
+ "unicode": "1f508",
+ "unicode_alternates": "",
+ "name": "speaker",
+ "shortname": ":speaker:",
+ "category": "symbols",
+ "emoji_order": "995",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "alarm",
+ "symbol"
+ ]
+ },
+ "sound": {
+ "unicode": "1f509",
+ "unicode_alternates": "",
+ "name": "speaker with one sound wave",
+ "shortname": ":sound:",
+ "category": "symbols",
+ "emoji_order": "996",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "alarm",
+ "symbol"
+ ]
+ },
+ "loud_sound": {
+ "unicode": "1f50a",
+ "unicode_alternates": "",
+ "name": "speaker with three sound waves",
+ "shortname": ":loud_sound:",
+ "category": "symbols",
+ "emoji_order": "997",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "alarm",
+ "symbol"
+ ]
+ },
+ "mute": {
+ "unicode": "1f507",
+ "unicode_alternates": "",
+ "name": "speaker with cancellation stroke",
+ "shortname": ":mute:",
+ "category": "symbols",
+ "emoji_order": "998",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "alarm",
+ "symbol"
+ ]
+ },
+ "mega": {
+ "unicode": "1f4e3",
+ "unicode_alternates": "",
+ "name": "cheering megaphone",
+ "shortname": ":mega:",
+ "category": "symbols",
+ "emoji_order": "999",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "sport"
+ ]
+ },
+ "loudspeaker": {
+ "unicode": "1f4e2",
+ "unicode_alternates": "",
+ "name": "public address loudspeaker",
+ "shortname": ":loudspeaker:",
+ "category": "symbols",
+ "emoji_order": "1000",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "alarm",
+ "symbol"
+ ]
+ },
+ "bell": {
+ "unicode": "1f514",
+ "unicode_alternates": "",
+ "name": "bell",
+ "shortname": ":bell:",
+ "category": "symbols",
+ "emoji_order": "1001",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "alarm",
+ "symbol"
+ ]
+ },
+ "no_bell": {
+ "unicode": "1f515",
+ "unicode_alternates": "",
+ "name": "bell with cancellation stroke",
+ "shortname": ":no_bell:",
+ "category": "symbols",
+ "emoji_order": "1002",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "alarm",
+ "symbol"
+ ]
+ },
+ "black_joker": {
+ "unicode": "1f0cf",
+ "unicode_alternates": "",
+ "name": "playing card black joker",
+ "shortname": ":black_joker:",
+ "category": "symbols",
+ "emoji_order": "1003",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "symbol",
+ "game"
+ ]
+ },
+ "mahjong": {
+ "unicode": "1f004",
+ "unicode_alternates": "1f004-fe0f",
+ "name": "mahjong tile red dragon",
+ "shortname": ":mahjong:",
+ "category": "symbols",
+ "emoji_order": "1004",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "symbol",
+ "game"
+ ]
+ },
+ "spades": {
+ "unicode": "2660",
+ "unicode_alternates": "2660-fe0f",
+ "name": "black spade suit",
+ "shortname": ":spades:",
+ "category": "symbols",
+ "emoji_order": "1005",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol",
+ "game"
+ ]
+ },
+ "clubs": {
+ "unicode": "2663",
+ "unicode_alternates": "2663-fe0f",
+ "name": "black club suit",
+ "shortname": ":clubs:",
+ "category": "symbols",
+ "emoji_order": "1006",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol",
+ "game"
+ ]
+ },
+ "hearts": {
+ "unicode": "2665",
+ "unicode_alternates": "2665-fe0f",
+ "name": "black heart suit",
+ "shortname": ":hearts:",
+ "category": "symbols",
+ "emoji_order": "1007",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "love",
+ "symbol",
+ "game"
+ ]
+ },
+ "diamonds": {
+ "unicode": "2666",
+ "unicode_alternates": "2666-fe0f",
+ "name": "black diamond suit",
+ "shortname": ":diamonds:",
+ "category": "symbols",
+ "emoji_order": "1008",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "shapes",
+ "symbol",
+ "game"
+ ]
+ },
+ "flower_playing_cards": {
+ "unicode": "1f3b4",
+ "unicode_alternates": "",
+ "name": "flower playing cards",
+ "shortname": ":flower_playing_cards:",
+ "category": "symbols",
+ "emoji_order": "1009",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "symbol"
+ ]
+ },
+ "thought_balloon": {
+ "unicode": "1f4ad",
+ "unicode_alternates": "",
+ "name": "thought balloon",
+ "shortname": ":thought_balloon:",
+ "category": "symbols",
+ "emoji_order": "1010",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol"
+ ]
+ },
+ "anger_right": {
+ "unicode": "1f5ef",
+ "unicode_alternates": "1f5ef-fe0f",
+ "name": "right anger bubble",
+ "shortname": ":anger_right:",
+ "category": "symbols",
+ "emoji_order": "1011",
+ "aliases": [
+ ":right_anger_bubble:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol"
+ ]
+ },
+ "speech_balloon": {
+ "unicode": "1f4ac",
+ "unicode_alternates": "",
+ "name": "speech balloon",
+ "shortname": ":speech_balloon:",
+ "category": "symbols",
+ "emoji_order": "1012",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol",
+ "free speech",
+ "free speech"
+ ]
+ },
+ "clock1": {
+ "unicode": "1f550",
+ "unicode_alternates": "",
+ "name": "clock face one oclock",
+ "shortname": ":clock1:",
+ "category": "symbols",
+ "emoji_order": "1013",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol",
+ "time"
+ ]
+ },
+ "clock2": {
+ "unicode": "1f551",
+ "unicode_alternates": "",
+ "name": "clock face two oclock",
+ "shortname": ":clock2:",
+ "category": "symbols",
+ "emoji_order": "1014",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol",
+ "time"
+ ]
+ },
+ "clock3": {
+ "unicode": "1f552",
+ "unicode_alternates": "",
+ "name": "clock face three oclock",
+ "shortname": ":clock3:",
+ "category": "symbols",
+ "emoji_order": "1015",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol",
+ "time"
+ ]
+ },
+ "clock4": {
+ "unicode": "1f553",
+ "unicode_alternates": "",
+ "name": "clock face four oclock",
+ "shortname": ":clock4:",
+ "category": "symbols",
+ "emoji_order": "1016",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol",
+ "time"
+ ]
+ },
+ "clock5": {
+ "unicode": "1f554",
+ "unicode_alternates": "",
+ "name": "clock face five oclock",
+ "shortname": ":clock5:",
+ "category": "symbols",
+ "emoji_order": "1017",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol",
+ "time"
+ ]
+ },
+ "clock6": {
+ "unicode": "1f555",
+ "unicode_alternates": "",
+ "name": "clock face six oclock",
+ "shortname": ":clock6:",
+ "category": "symbols",
+ "emoji_order": "1018",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol",
+ "time"
+ ]
+ },
+ "clock7": {
+ "unicode": "1f556",
+ "unicode_alternates": "",
+ "name": "clock face seven oclock",
+ "shortname": ":clock7:",
+ "category": "symbols",
+ "emoji_order": "1019",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol",
+ "time"
+ ]
+ },
+ "clock8": {
+ "unicode": "1f557",
+ "unicode_alternates": "",
+ "name": "clock face eight oclock",
+ "shortname": ":clock8:",
+ "category": "symbols",
+ "emoji_order": "1020",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol",
+ "time"
+ ]
+ },
+ "clock9": {
+ "unicode": "1f558",
+ "unicode_alternates": "",
+ "name": "clock face nine oclock",
+ "shortname": ":clock9:",
+ "category": "symbols",
+ "emoji_order": "1021",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol",
+ "time"
+ ]
+ },
+ "clock10": {
+ "unicode": "1f559",
+ "unicode_alternates": "",
+ "name": "clock face ten oclock",
+ "shortname": ":clock10:",
+ "category": "symbols",
+ "emoji_order": "1022",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol",
+ "time"
+ ]
+ },
+ "clock11": {
+ "unicode": "1f55a",
+ "unicode_alternates": "",
+ "name": "clock face eleven oclock",
+ "shortname": ":clock11:",
+ "category": "symbols",
+ "emoji_order": "1023",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol",
+ "time"
+ ]
+ },
+ "clock12": {
+ "unicode": "1f55b",
+ "unicode_alternates": "",
+ "name": "clock face twelve oclock",
+ "shortname": ":clock12:",
+ "category": "symbols",
+ "emoji_order": "1024",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol",
+ "time"
+ ]
+ },
+ "clock130": {
+ "unicode": "1f55c",
+ "unicode_alternates": "",
+ "name": "clock face one-thirty",
+ "shortname": ":clock130:",
+ "category": "symbols",
+ "emoji_order": "1025",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol",
+ "time"
+ ]
+ },
+ "clock230": {
+ "unicode": "1f55d",
+ "unicode_alternates": "",
+ "name": "clock face two-thirty",
+ "shortname": ":clock230:",
+ "category": "symbols",
+ "emoji_order": "1026",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol",
+ "time"
+ ]
+ },
+ "clock330": {
+ "unicode": "1f55e",
+ "unicode_alternates": "",
+ "name": "clock face three-thirty",
+ "shortname": ":clock330:",
+ "category": "symbols",
+ "emoji_order": "1027",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol",
+ "time"
+ ]
+ },
+ "clock430": {
+ "unicode": "1f55f",
+ "unicode_alternates": "",
+ "name": "clock face four-thirty",
+ "shortname": ":clock430:",
+ "category": "symbols",
+ "emoji_order": "1028",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol",
+ "time"
+ ]
+ },
+ "clock530": {
+ "unicode": "1f560",
+ "unicode_alternates": "",
+ "name": "clock face five-thirty",
+ "shortname": ":clock530:",
+ "category": "symbols",
+ "emoji_order": "1029",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol",
+ "time"
+ ]
+ },
+ "clock630": {
+ "unicode": "1f561",
+ "unicode_alternates": "",
+ "name": "clock face six-thirty",
+ "shortname": ":clock630:",
+ "category": "symbols",
+ "emoji_order": "1030",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol",
+ "time"
+ ]
+ },
+ "clock730": {
+ "unicode": "1f562",
+ "unicode_alternates": "",
+ "name": "clock face seven-thirty",
+ "shortname": ":clock730:",
+ "category": "symbols",
+ "emoji_order": "1031",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol",
+ "time"
+ ]
+ },
+ "clock830": {
+ "unicode": "1f563",
+ "unicode_alternates": "",
+ "name": "clock face eight-thirty",
+ "shortname": ":clock830:",
+ "category": "symbols",
+ "emoji_order": "1032",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol",
+ "time"
+ ]
+ },
+ "clock930": {
+ "unicode": "1f564",
+ "unicode_alternates": "",
+ "name": "clock face nine-thirty",
+ "shortname": ":clock930:",
+ "category": "symbols",
+ "emoji_order": "1033",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol",
+ "time"
+ ]
+ },
+ "clock1030": {
+ "unicode": "1f565",
+ "unicode_alternates": "",
+ "name": "clock face ten-thirty",
+ "shortname": ":clock1030:",
+ "category": "symbols",
+ "emoji_order": "1034",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol",
+ "time"
+ ]
+ },
+ "clock1130": {
+ "unicode": "1f566",
+ "unicode_alternates": "",
+ "name": "clock face eleven-thirty",
+ "shortname": ":clock1130:",
+ "category": "symbols",
+ "emoji_order": "1035",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol",
+ "time"
+ ]
+ },
+ "clock1230": {
+ "unicode": "1f567",
+ "unicode_alternates": "",
+ "name": "clock face twelve-thirty",
+ "shortname": ":clock1230:",
+ "category": "symbols",
+ "emoji_order": "1036",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "symbol",
+ "time"
+ ]
+ },
+ "eye_in_speech_bubble": {
+ "unicode": "1f441-1f5e8",
+ "unicode_alternates": "1f441-200d-1f5e8",
+ "name": "eye in speech bubble",
+ "shortname": ":eye_in_speech_bubble:",
+ "category": "symbols",
+ "emoji_order": "1037",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "object",
+ "symbol",
+ "eyes",
+ "talk"
+ ]
+ },
+ "flag_ac": {
+ "unicode": "1f1e6-1f1e8",
+ "unicode_alternates": "",
+ "name": "ascension",
+ "shortname": ":flag_ac:",
+ "category": "flags",
+ "emoji_order": "1038",
+ "aliases": [
+ ":ac:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_af": {
+ "unicode": "1f1e6-1f1eb",
+ "unicode_alternates": "",
+ "name": "afghanistan",
+ "shortname": ":flag_af:",
+ "category": "flags",
+ "emoji_order": "1039",
+ "aliases": [
+ ":af:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_al": {
+ "unicode": "1f1e6-1f1f1",
+ "unicode_alternates": "",
+ "name": "albania",
+ "shortname": ":flag_al:",
+ "category": "flags",
+ "emoji_order": "1040",
+ "aliases": [
+ ":al:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_dz": {
+ "unicode": "1f1e9-1f1ff",
+ "unicode_alternates": "",
+ "name": "algeria",
+ "shortname": ":flag_dz:",
+ "category": "flags",
+ "emoji_order": "1041",
+ "aliases": [
+ ":dz:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_ad": {
+ "unicode": "1f1e6-1f1e9",
+ "unicode_alternates": "",
+ "name": "andorra",
+ "shortname": ":flag_ad:",
+ "category": "flags",
+ "emoji_order": "1042",
+ "aliases": [
+ ":ad:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_ao": {
+ "unicode": "1f1e6-1f1f4",
+ "unicode_alternates": "",
+ "name": "angola",
+ "shortname": ":flag_ao:",
+ "category": "flags",
+ "emoji_order": "1043",
+ "aliases": [
+ ":ao:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_ai": {
+ "unicode": "1f1e6-1f1ee",
+ "unicode_alternates": "",
+ "name": "anguilla",
+ "shortname": ":flag_ai:",
+ "category": "flags",
+ "emoji_order": "1044",
+ "aliases": [
+ ":ai:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_ag": {
+ "unicode": "1f1e6-1f1ec",
+ "unicode_alternates": "",
+ "name": "antigua and barbuda",
+ "shortname": ":flag_ag:",
+ "category": "flags",
+ "emoji_order": "1045",
+ "aliases": [
+ ":ag:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_ar": {
+ "unicode": "1f1e6-1f1f7",
+ "unicode_alternates": "",
+ "name": "argentina",
+ "shortname": ":flag_ar:",
+ "category": "flags",
+ "emoji_order": "1046",
+ "aliases": [
+ ":ar:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_am": {
+ "unicode": "1f1e6-1f1f2",
+ "unicode_alternates": "",
+ "name": "armenia",
+ "shortname": ":flag_am:",
+ "category": "flags",
+ "emoji_order": "1047",
+ "aliases": [
+ ":am:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_aw": {
+ "unicode": "1f1e6-1f1fc",
+ "unicode_alternates": "",
+ "name": "aruba",
+ "shortname": ":flag_aw:",
+ "category": "flags",
+ "emoji_order": "1048",
+ "aliases": [
+ ":aw:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_au": {
+ "unicode": "1f1e6-1f1fa",
+ "unicode_alternates": "",
+ "name": "australia",
+ "shortname": ":flag_au:",
+ "category": "flags",
+ "emoji_order": "1049",
+ "aliases": [
+ ":au:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_at": {
+ "unicode": "1f1e6-1f1f9",
+ "unicode_alternates": "",
+ "name": "austria",
+ "shortname": ":flag_at:",
+ "category": "flags",
+ "emoji_order": "1050",
+ "aliases": [
+ ":at:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_az": {
+ "unicode": "1f1e6-1f1ff",
+ "unicode_alternates": "",
+ "name": "azerbaijan",
+ "shortname": ":flag_az:",
+ "category": "flags",
+ "emoji_order": "1051",
+ "aliases": [
+ ":az:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_bs": {
+ "unicode": "1f1e7-1f1f8",
+ "unicode_alternates": "",
+ "name": "the bahamas",
+ "shortname": ":flag_bs:",
+ "category": "flags",
+ "emoji_order": "1052",
+ "aliases": [
+ ":bs:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_bh": {
+ "unicode": "1f1e7-1f1ed",
+ "unicode_alternates": "",
+ "name": "bahrain",
+ "shortname": ":flag_bh:",
+ "category": "flags",
+ "emoji_order": "1053",
+ "aliases": [
+ ":bh:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_bd": {
+ "unicode": "1f1e7-1f1e9",
+ "unicode_alternates": "",
+ "name": "bangladesh",
+ "shortname": ":flag_bd:",
+ "category": "flags",
+ "emoji_order": "1054",
+ "aliases": [
+ ":bd:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_bb": {
+ "unicode": "1f1e7-1f1e7",
+ "unicode_alternates": "",
+ "name": "barbados",
+ "shortname": ":flag_bb:",
+ "category": "flags",
+ "emoji_order": "1055",
+ "aliases": [
+ ":bb:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_by": {
+ "unicode": "1f1e7-1f1fe",
+ "unicode_alternates": "",
+ "name": "belarus",
+ "shortname": ":flag_by:",
+ "category": "flags",
+ "emoji_order": "1056",
+ "aliases": [
+ ":by:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_be": {
+ "unicode": "1f1e7-1f1ea",
+ "unicode_alternates": "",
+ "name": "belgium",
+ "shortname": ":flag_be:",
+ "category": "flags",
+ "emoji_order": "1057",
+ "aliases": [
+ ":be:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_bz": {
+ "unicode": "1f1e7-1f1ff",
+ "unicode_alternates": "",
+ "name": "belize",
+ "shortname": ":flag_bz:",
+ "category": "flags",
+ "emoji_order": "1058",
+ "aliases": [
+ ":bz:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_bj": {
+ "unicode": "1f1e7-1f1ef",
+ "unicode_alternates": "",
+ "name": "benin",
+ "shortname": ":flag_bj:",
+ "category": "flags",
+ "emoji_order": "1059",
+ "aliases": [
+ ":bj:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_bm": {
+ "unicode": "1f1e7-1f1f2",
+ "unicode_alternates": "",
+ "name": "bermuda",
+ "shortname": ":flag_bm:",
+ "category": "flags",
+ "emoji_order": "1060",
+ "aliases": [
+ ":bm:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_bt": {
+ "unicode": "1f1e7-1f1f9",
+ "unicode_alternates": "",
+ "name": "bhutan",
+ "shortname": ":flag_bt:",
+ "category": "flags",
+ "emoji_order": "1061",
+ "aliases": [
+ ":bt:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_bo": {
+ "unicode": "1f1e7-1f1f4",
+ "unicode_alternates": "",
+ "name": "bolivia",
+ "shortname": ":flag_bo:",
+ "category": "flags",
+ "emoji_order": "1062",
+ "aliases": [
+ ":bo:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_ba": {
+ "unicode": "1f1e7-1f1e6",
+ "unicode_alternates": "",
+ "name": "bosnia and herzegovina",
+ "shortname": ":flag_ba:",
+ "category": "flags",
+ "emoji_order": "1063",
+ "aliases": [
+ ":ba:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_bw": {
+ "unicode": "1f1e7-1f1fc",
+ "unicode_alternates": "",
+ "name": "botswana",
+ "shortname": ":flag_bw:",
+ "category": "flags",
+ "emoji_order": "1064",
+ "aliases": [
+ ":bw:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_br": {
+ "unicode": "1f1e7-1f1f7",
+ "unicode_alternates": "",
+ "name": "brazil",
+ "shortname": ":flag_br:",
+ "category": "flags",
+ "emoji_order": "1065",
+ "aliases": [
+ ":br:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_bn": {
+ "unicode": "1f1e7-1f1f3",
+ "unicode_alternates": "",
+ "name": "brunei",
+ "shortname": ":flag_bn:",
+ "category": "flags",
+ "emoji_order": "1066",
+ "aliases": [
+ ":bn:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_bg": {
+ "unicode": "1f1e7-1f1ec",
+ "unicode_alternates": "",
+ "name": "bulgaria",
+ "shortname": ":flag_bg:",
+ "category": "flags",
+ "emoji_order": "1067",
+ "aliases": [
+ ":bg:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_bf": {
+ "unicode": "1f1e7-1f1eb",
+ "unicode_alternates": "",
+ "name": "burkina faso",
+ "shortname": ":flag_bf:",
+ "category": "flags",
+ "emoji_order": "1068",
+ "aliases": [
+ ":bf:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_bi": {
+ "unicode": "1f1e7-1f1ee",
+ "unicode_alternates": "",
+ "name": "burundi",
+ "shortname": ":flag_bi:",
+ "category": "flags",
+ "emoji_order": "1069",
+ "aliases": [
+ ":bi:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_cv": {
+ "unicode": "1f1e8-1f1fb",
+ "unicode_alternates": "",
+ "name": "cape verde",
+ "shortname": ":flag_cv:",
+ "category": "flags",
+ "emoji_order": "1070",
+ "aliases": [
+ ":cv:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_kh": {
+ "unicode": "1f1f0-1f1ed",
+ "unicode_alternates": "",
+ "name": "cambodia",
+ "shortname": ":flag_kh:",
+ "category": "flags",
+ "emoji_order": "1071",
+ "aliases": [
+ ":kh:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_cm": {
+ "unicode": "1f1e8-1f1f2",
+ "unicode_alternates": "",
+ "name": "cameroon",
+ "shortname": ":flag_cm:",
+ "category": "flags",
+ "emoji_order": "1072",
+ "aliases": [
+ ":cm:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_ca": {
+ "unicode": "1f1e8-1f1e6",
+ "unicode_alternates": "",
+ "name": "canada",
+ "shortname": ":flag_ca:",
+ "category": "flags",
+ "emoji_order": "1073",
+ "aliases": [
+ ":ca:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_ky": {
+ "unicode": "1f1f0-1f1fe",
+ "unicode_alternates": "",
+ "name": "cayman islands",
+ "shortname": ":flag_ky:",
+ "category": "flags",
+ "emoji_order": "1074",
+ "aliases": [
+ ":ky:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_cf": {
+ "unicode": "1f1e8-1f1eb",
+ "unicode_alternates": "",
+ "name": "central african republic",
+ "shortname": ":flag_cf:",
+ "category": "flags",
+ "emoji_order": "1075",
+ "aliases": [
+ ":cf:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_td": {
+ "unicode": "1f1f9-1f1e9",
+ "unicode_alternates": "",
+ "name": "chad",
+ "shortname": ":flag_td:",
+ "category": "flags",
+ "emoji_order": "1076",
+ "aliases": [
+ ":td:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_cl": {
+ "unicode": "1f1e8-1f1f1",
+ "unicode_alternates": "",
+ "name": "chile",
+ "shortname": ":flag_cl:",
+ "category": "flags",
+ "emoji_order": "1077",
+ "aliases": [
+ ":chile:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_cn": {
+ "unicode": "1f1e8-1f1f3",
+ "unicode_alternates": "",
+ "name": "china",
+ "shortname": ":flag_cn:",
+ "category": "flags",
+ "emoji_order": "1078",
+ "aliases": [
+ ":cn:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_co": {
+ "unicode": "1f1e8-1f1f4",
+ "unicode_alternates": "",
+ "name": "colombia",
+ "shortname": ":flag_co:",
+ "category": "flags",
+ "emoji_order": "1079",
+ "aliases": [
+ ":co:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_km": {
+ "unicode": "1f1f0-1f1f2",
+ "unicode_alternates": "",
+ "name": "the comoros",
+ "shortname": ":flag_km:",
+ "category": "flags",
+ "emoji_order": "1080",
+ "aliases": [
+ ":km:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_cg": {
+ "unicode": "1f1e8-1f1ec",
+ "unicode_alternates": "",
+ "name": "the republic of the congo",
+ "shortname": ":flag_cg:",
+ "category": "flags",
+ "emoji_order": "1081",
+ "aliases": [
+ ":cg:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_cd": {
+ "unicode": "1f1e8-1f1e9",
+ "unicode_alternates": "",
+ "name": "the democratic republic of the congo",
+ "shortname": ":flag_cd:",
+ "category": "flags",
+ "emoji_order": "1082",
+ "aliases": [
+ ":congo:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_cr": {
+ "unicode": "1f1e8-1f1f7",
+ "unicode_alternates": "",
+ "name": "costa rica",
+ "shortname": ":flag_cr:",
+ "category": "flags",
+ "emoji_order": "1083",
+ "aliases": [
+ ":cr:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_hr": {
+ "unicode": "1f1ed-1f1f7",
+ "unicode_alternates": "",
+ "name": "croatia",
+ "shortname": ":flag_hr:",
+ "category": "flags",
+ "emoji_order": "1084",
+ "aliases": [
+ ":hr:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_cu": {
+ "unicode": "1f1e8-1f1fa",
+ "unicode_alternates": "",
+ "name": "cuba",
+ "shortname": ":flag_cu:",
+ "category": "flags",
+ "emoji_order": "1085",
+ "aliases": [
+ ":cu:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_cy": {
+ "unicode": "1f1e8-1f1fe",
+ "unicode_alternates": "",
+ "name": "cyprus",
+ "shortname": ":flag_cy:",
+ "category": "flags",
+ "emoji_order": "1086",
+ "aliases": [
+ ":cy:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_cz": {
+ "unicode": "1f1e8-1f1ff",
+ "unicode_alternates": "",
+ "name": "the czech republic",
+ "shortname": ":flag_cz:",
+ "category": "flags",
+ "emoji_order": "1087",
+ "aliases": [
+ ":cz:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_dk": {
+ "unicode": "1f1e9-1f1f0",
+ "unicode_alternates": "",
+ "name": "denmark",
+ "shortname": ":flag_dk:",
+ "category": "flags",
+ "emoji_order": "1088",
+ "aliases": [
+ ":dk:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_dj": {
+ "unicode": "1f1e9-1f1ef",
+ "unicode_alternates": "",
+ "name": "djibouti",
+ "shortname": ":flag_dj:",
+ "category": "flags",
+ "emoji_order": "1089",
+ "aliases": [
+ ":dj:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_dm": {
+ "unicode": "1f1e9-1f1f2",
+ "unicode_alternates": "",
+ "name": "dominica",
+ "shortname": ":flag_dm:",
+ "category": "flags",
+ "emoji_order": "1090",
+ "aliases": [
+ ":dm:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_do": {
+ "unicode": "1f1e9-1f1f4",
+ "unicode_alternates": "",
+ "name": "the dominican republic",
+ "shortname": ":flag_do:",
+ "category": "flags",
+ "emoji_order": "1091",
+ "aliases": [
+ ":do:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_ec": {
+ "unicode": "1f1ea-1f1e8",
+ "unicode_alternates": "",
+ "name": "ecuador",
+ "shortname": ":flag_ec:",
+ "category": "flags",
+ "emoji_order": "1092",
+ "aliases": [
+ ":ec:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_eg": {
+ "unicode": "1f1ea-1f1ec",
+ "unicode_alternates": "",
+ "name": "egypt",
+ "shortname": ":flag_eg:",
+ "category": "flags",
+ "emoji_order": "1093",
+ "aliases": [
+ ":eg:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_sv": {
+ "unicode": "1f1f8-1f1fb",
+ "unicode_alternates": "",
+ "name": "el salvador",
+ "shortname": ":flag_sv:",
+ "category": "flags",
+ "emoji_order": "1094",
+ "aliases": [
+ ":sv:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_gq": {
+ "unicode": "1f1ec-1f1f6",
+ "unicode_alternates": "",
+ "name": "equatorial guinea",
+ "shortname": ":flag_gq:",
+ "category": "flags",
+ "emoji_order": "1095",
+ "aliases": [
+ ":gq:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_er": {
+ "unicode": "1f1ea-1f1f7",
+ "unicode_alternates": "",
+ "name": "eritrea",
+ "shortname": ":flag_er:",
+ "category": "flags",
+ "emoji_order": "1096",
+ "aliases": [
+ ":er:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_ee": {
+ "unicode": "1f1ea-1f1ea",
+ "unicode_alternates": "",
+ "name": "estonia",
+ "shortname": ":flag_ee:",
+ "category": "flags",
+ "emoji_order": "1097",
+ "aliases": [
+ ":ee:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_et": {
+ "unicode": "1f1ea-1f1f9",
+ "unicode_alternates": "",
+ "name": "ethiopia",
+ "shortname": ":flag_et:",
+ "category": "flags",
+ "emoji_order": "1098",
+ "aliases": [
+ ":et:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_fk": {
+ "unicode": "1f1eb-1f1f0",
+ "unicode_alternates": "",
+ "name": "falkland islands",
+ "shortname": ":flag_fk:",
+ "category": "flags",
+ "emoji_order": "1099",
+ "aliases": [
+ ":fk:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_fo": {
+ "unicode": "1f1eb-1f1f4",
+ "unicode_alternates": "",
+ "name": "faroe islands",
+ "shortname": ":flag_fo:",
+ "category": "flags",
+ "emoji_order": "1100",
+ "aliases": [
+ ":fo:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_fj": {
+ "unicode": "1f1eb-1f1ef",
+ "unicode_alternates": "",
+ "name": "fiji",
+ "shortname": ":flag_fj:",
+ "category": "flags",
+ "emoji_order": "1101",
+ "aliases": [
+ ":fj:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_fi": {
+ "unicode": "1f1eb-1f1ee",
+ "unicode_alternates": "",
+ "name": "finland",
+ "shortname": ":flag_fi:",
+ "category": "flags",
+ "emoji_order": "1102",
+ "aliases": [
+ ":fi:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_fr": {
+ "unicode": "1f1eb-1f1f7",
+ "unicode_alternates": "",
+ "name": "france",
+ "shortname": ":flag_fr:",
+ "category": "flags",
+ "emoji_order": "1103",
+ "aliases": [
+ ":fr:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_pf": {
+ "unicode": "1f1f5-1f1eb",
+ "unicode_alternates": "",
+ "name": "french polynesia",
+ "shortname": ":flag_pf:",
+ "category": "flags",
+ "emoji_order": "1104",
+ "aliases": [
+ ":pf:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_ga": {
+ "unicode": "1f1ec-1f1e6",
+ "unicode_alternates": "",
+ "name": "gabon",
+ "shortname": ":flag_ga:",
+ "category": "flags",
+ "emoji_order": "1105",
+ "aliases": [
+ ":ga:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_gm": {
+ "unicode": "1f1ec-1f1f2",
+ "unicode_alternates": "",
+ "name": "the gambia",
+ "shortname": ":flag_gm:",
+ "category": "flags",
+ "emoji_order": "1106",
+ "aliases": [
+ ":gm:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_ge": {
+ "unicode": "1f1ec-1f1ea",
+ "unicode_alternates": "",
+ "name": "georgia",
+ "shortname": ":flag_ge:",
+ "category": "flags",
+ "emoji_order": "1107",
+ "aliases": [
+ ":ge:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_de": {
+ "unicode": "1f1e9-1f1ea",
+ "unicode_alternates": "",
+ "name": "germany",
+ "shortname": ":flag_de:",
+ "category": "flags",
+ "emoji_order": "1108",
+ "aliases": [
+ ":de:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_gh": {
+ "unicode": "1f1ec-1f1ed",
+ "unicode_alternates": "",
+ "name": "ghana",
+ "shortname": ":flag_gh:",
+ "category": "flags",
+ "emoji_order": "1109",
+ "aliases": [
+ ":gh:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_gi": {
+ "unicode": "1f1ec-1f1ee",
+ "unicode_alternates": "",
+ "name": "gibraltar",
+ "shortname": ":flag_gi:",
+ "category": "flags",
+ "emoji_order": "1110",
+ "aliases": [
+ ":gi:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_gr": {
+ "unicode": "1f1ec-1f1f7",
+ "unicode_alternates": "",
+ "name": "greece",
+ "shortname": ":flag_gr:",
+ "category": "flags",
+ "emoji_order": "1111",
+ "aliases": [
+ ":gr:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_gl": {
+ "unicode": "1f1ec-1f1f1",
+ "unicode_alternates": "",
+ "name": "greenland",
+ "shortname": ":flag_gl:",
+ "category": "flags",
+ "emoji_order": "1112",
+ "aliases": [
+ ":gl:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_gd": {
+ "unicode": "1f1ec-1f1e9",
+ "unicode_alternates": "",
+ "name": "grenada",
+ "shortname": ":flag_gd:",
+ "category": "flags",
+ "emoji_order": "1113",
+ "aliases": [
+ ":gd:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_gu": {
+ "unicode": "1f1ec-1f1fa",
+ "unicode_alternates": "",
+ "name": "guam",
+ "shortname": ":flag_gu:",
+ "category": "flags",
+ "emoji_order": "1114",
+ "aliases": [
+ ":gu:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_gt": {
+ "unicode": "1f1ec-1f1f9",
+ "unicode_alternates": "",
+ "name": "guatemala",
+ "shortname": ":flag_gt:",
+ "category": "flags",
+ "emoji_order": "1115",
+ "aliases": [
+ ":gt:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_gn": {
+ "unicode": "1f1ec-1f1f3",
+ "unicode_alternates": "",
+ "name": "guinea",
+ "shortname": ":flag_gn:",
+ "category": "flags",
+ "emoji_order": "1116",
+ "aliases": [
+ ":gn:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_gw": {
+ "unicode": "1f1ec-1f1fc",
+ "unicode_alternates": "",
+ "name": "guinea-bissau",
+ "shortname": ":flag_gw:",
+ "category": "flags",
+ "emoji_order": "1117",
+ "aliases": [
+ ":gw:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_gy": {
+ "unicode": "1f1ec-1f1fe",
+ "unicode_alternates": "",
+ "name": "guyana",
+ "shortname": ":flag_gy:",
+ "category": "flags",
+ "emoji_order": "1118",
+ "aliases": [
+ ":gy:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_ht": {
+ "unicode": "1f1ed-1f1f9",
+ "unicode_alternates": "",
+ "name": "haiti",
+ "shortname": ":flag_ht:",
+ "category": "flags",
+ "emoji_order": "1119",
+ "aliases": [
+ ":ht:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_hn": {
+ "unicode": "1f1ed-1f1f3",
+ "unicode_alternates": "",
+ "name": "honduras",
+ "shortname": ":flag_hn:",
+ "category": "flags",
+ "emoji_order": "1120",
+ "aliases": [
+ ":hn:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_hk": {
+ "unicode": "1f1ed-1f1f0",
+ "unicode_alternates": "",
+ "name": "hong kong",
+ "shortname": ":flag_hk:",
+ "category": "flags",
+ "emoji_order": "1121",
+ "aliases": [
+ ":hk:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_hu": {
+ "unicode": "1f1ed-1f1fa",
+ "unicode_alternates": "",
+ "name": "hungary",
+ "shortname": ":flag_hu:",
+ "category": "flags",
+ "emoji_order": "1122",
+ "aliases": [
+ ":hu:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_is": {
+ "unicode": "1f1ee-1f1f8",
+ "unicode_alternates": "",
+ "name": "iceland",
+ "shortname": ":flag_is:",
+ "category": "flags",
+ "emoji_order": "1123",
+ "aliases": [
+ ":is:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_in": {
+ "unicode": "1f1ee-1f1f3",
+ "unicode_alternates": "",
+ "name": "india",
+ "shortname": ":flag_in:",
+ "category": "flags",
+ "emoji_order": "1124",
+ "aliases": [
+ ":in:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_id": {
+ "unicode": "1f1ee-1f1e9",
+ "unicode_alternates": "",
+ "name": "indonesia",
+ "shortname": ":flag_id:",
+ "category": "flags",
+ "emoji_order": "1125",
+ "aliases": [
+ ":indonesia:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_ir": {
+ "unicode": "1f1ee-1f1f7",
+ "unicode_alternates": "",
+ "name": "iran",
+ "shortname": ":flag_ir:",
+ "category": "flags",
+ "emoji_order": "1126",
+ "aliases": [
+ ":ir:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_iq": {
+ "unicode": "1f1ee-1f1f6",
+ "unicode_alternates": "",
+ "name": "iraq",
+ "shortname": ":flag_iq:",
+ "category": "flags",
+ "emoji_order": "1127",
+ "aliases": [
+ ":iq:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_ie": {
+ "unicode": "1f1ee-1f1ea",
+ "unicode_alternates": "",
+ "name": "ireland",
+ "shortname": ":flag_ie:",
+ "category": "flags",
+ "emoji_order": "1128",
+ "aliases": [
+ ":ie:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_il": {
+ "unicode": "1f1ee-1f1f1",
+ "unicode_alternates": "",
+ "name": "israel",
+ "shortname": ":flag_il:",
+ "category": "flags",
+ "emoji_order": "1129",
+ "aliases": [
+ ":il:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "jew",
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_it": {
+ "unicode": "1f1ee-1f1f9",
+ "unicode_alternates": "",
+ "name": "italy",
+ "shortname": ":flag_it:",
+ "category": "flags",
+ "emoji_order": "1130",
+ "aliases": [
+ ":it:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "italian",
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_ci": {
+ "unicode": "1f1e8-1f1ee",
+ "unicode_alternates": "",
+ "name": "côte d’ivoire",
+ "shortname": ":flag_ci:",
+ "category": "flags",
+ "emoji_order": "1131",
+ "aliases": [
+ ":ci:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_jm": {
+ "unicode": "1f1ef-1f1f2",
+ "unicode_alternates": "",
+ "name": "jamaica",
+ "shortname": ":flag_jm:",
+ "category": "flags",
+ "emoji_order": "1132",
+ "aliases": [
+ ":jm:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_jp": {
+ "unicode": "1f1ef-1f1f5",
+ "unicode_alternates": "",
+ "name": "japan",
+ "shortname": ":flag_jp:",
+ "category": "flags",
+ "emoji_order": "1133",
+ "aliases": [
+ ":jp:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "japan",
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_je": {
+ "unicode": "1f1ef-1f1ea",
+ "unicode_alternates": "",
+ "name": "jersey",
+ "shortname": ":flag_je:",
+ "category": "flags",
+ "emoji_order": "1134",
+ "aliases": [
+ ":je:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_jo": {
+ "unicode": "1f1ef-1f1f4",
+ "unicode_alternates": "",
+ "name": "jordan",
+ "shortname": ":flag_jo:",
+ "category": "flags",
+ "emoji_order": "1135",
+ "aliases": [
+ ":jo:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_kz": {
+ "unicode": "1f1f0-1f1ff",
+ "unicode_alternates": "",
+ "name": "kazakhstan",
+ "shortname": ":flag_kz:",
+ "category": "flags",
+ "emoji_order": "1136",
+ "aliases": [
+ ":kz:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_ke": {
+ "unicode": "1f1f0-1f1ea",
+ "unicode_alternates": "",
+ "name": "kenya",
+ "shortname": ":flag_ke:",
+ "category": "flags",
+ "emoji_order": "1137",
+ "aliases": [
+ ":ke:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_ki": {
+ "unicode": "1f1f0-1f1ee",
+ "unicode_alternates": "",
+ "name": "kiribati",
+ "shortname": ":flag_ki:",
+ "category": "flags",
+ "emoji_order": "1138",
+ "aliases": [
+ ":ki:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_xk": {
+ "unicode": "1f1fd-1f1f0",
+ "unicode_alternates": "",
+ "name": "kosovo",
+ "shortname": ":flag_xk:",
+ "category": "flags",
+ "emoji_order": "1139",
+ "aliases": [
+ ":xk:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_kw": {
+ "unicode": "1f1f0-1f1fc",
+ "unicode_alternates": "",
+ "name": "kuwait",
+ "shortname": ":flag_kw:",
+ "category": "flags",
+ "emoji_order": "1140",
+ "aliases": [
+ ":kw:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_kg": {
+ "unicode": "1f1f0-1f1ec",
+ "unicode_alternates": "",
+ "name": "kyrgyzstan",
+ "shortname": ":flag_kg:",
+ "category": "flags",
+ "emoji_order": "1141",
+ "aliases": [
+ ":kg:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_la": {
+ "unicode": "1f1f1-1f1e6",
+ "unicode_alternates": "",
+ "name": "laos",
+ "shortname": ":flag_la:",
+ "category": "flags",
+ "emoji_order": "1142",
+ "aliases": [
+ ":la:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_lv": {
+ "unicode": "1f1f1-1f1fb",
+ "unicode_alternates": "",
+ "name": "latvia",
+ "shortname": ":flag_lv:",
+ "category": "flags",
+ "emoji_order": "1143",
+ "aliases": [
+ ":lv:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_lb": {
+ "unicode": "1f1f1-1f1e7",
+ "unicode_alternates": "",
+ "name": "lebanon",
+ "shortname": ":flag_lb:",
+ "category": "flags",
+ "emoji_order": "1144",
+ "aliases": [
+ ":lb:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_ls": {
+ "unicode": "1f1f1-1f1f8",
+ "unicode_alternates": "",
+ "name": "lesotho",
+ "shortname": ":flag_ls:",
+ "category": "flags",
+ "emoji_order": "1145",
+ "aliases": [
+ ":ls:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_lr": {
+ "unicode": "1f1f1-1f1f7",
+ "unicode_alternates": "",
+ "name": "liberia",
+ "shortname": ":flag_lr:",
+ "category": "flags",
+ "emoji_order": "1146",
+ "aliases": [
+ ":lr:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_ly": {
+ "unicode": "1f1f1-1f1fe",
+ "unicode_alternates": "",
+ "name": "libya",
+ "shortname": ":flag_ly:",
+ "category": "flags",
+ "emoji_order": "1147",
+ "aliases": [
+ ":ly:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_li": {
+ "unicode": "1f1f1-1f1ee",
+ "unicode_alternates": "",
+ "name": "liechtenstein",
+ "shortname": ":flag_li:",
+ "category": "flags",
+ "emoji_order": "1148",
+ "aliases": [
+ ":li:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_lt": {
+ "unicode": "1f1f1-1f1f9",
+ "unicode_alternates": "",
+ "name": "lithuania",
+ "shortname": ":flag_lt:",
+ "category": "flags",
+ "emoji_order": "1149",
+ "aliases": [
+ ":lt:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_lu": {
+ "unicode": "1f1f1-1f1fa",
+ "unicode_alternates": "",
+ "name": "luxembourg",
+ "shortname": ":flag_lu:",
+ "category": "flags",
+ "emoji_order": "1150",
+ "aliases": [
+ ":lu:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_mo": {
+ "unicode": "1f1f2-1f1f4",
+ "unicode_alternates": "",
+ "name": "macau",
+ "shortname": ":flag_mo:",
+ "category": "flags",
+ "emoji_order": "1151",
+ "aliases": [
+ ":mo:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_mk": {
+ "unicode": "1f1f2-1f1f0",
+ "unicode_alternates": "",
+ "name": "macedonia",
+ "shortname": ":flag_mk:",
+ "category": "flags",
+ "emoji_order": "1152",
+ "aliases": [
+ ":mk:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_mg": {
+ "unicode": "1f1f2-1f1ec",
+ "unicode_alternates": "",
+ "name": "madagascar",
+ "shortname": ":flag_mg:",
+ "category": "flags",
+ "emoji_order": "1153",
+ "aliases": [
+ ":mg:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_mw": {
+ "unicode": "1f1f2-1f1fc",
+ "unicode_alternates": "",
+ "name": "malawi",
+ "shortname": ":flag_mw:",
+ "category": "flags",
+ "emoji_order": "1154",
+ "aliases": [
+ ":mw:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_my": {
+ "unicode": "1f1f2-1f1fe",
+ "unicode_alternates": "",
+ "name": "malaysia",
+ "shortname": ":flag_my:",
+ "category": "flags",
+ "emoji_order": "1155",
+ "aliases": [
+ ":my:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_mv": {
+ "unicode": "1f1f2-1f1fb",
+ "unicode_alternates": "",
+ "name": "maldives",
+ "shortname": ":flag_mv:",
+ "category": "flags",
+ "emoji_order": "1156",
+ "aliases": [
+ ":mv:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_ml": {
+ "unicode": "1f1f2-1f1f1",
+ "unicode_alternates": "",
+ "name": "mali",
+ "shortname": ":flag_ml:",
+ "category": "flags",
+ "emoji_order": "1157",
+ "aliases": [
+ ":ml:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_mt": {
+ "unicode": "1f1f2-1f1f9",
+ "unicode_alternates": "",
+ "name": "malta",
+ "shortname": ":flag_mt:",
+ "category": "flags",
+ "emoji_order": "1158",
+ "aliases": [
+ ":mt:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_mh": {
+ "unicode": "1f1f2-1f1ed",
+ "unicode_alternates": "",
+ "name": "the marshall islands",
+ "shortname": ":flag_mh:",
+ "category": "flags",
+ "emoji_order": "1159",
+ "aliases": [
+ ":mh:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_mr": {
+ "unicode": "1f1f2-1f1f7",
+ "unicode_alternates": "",
+ "name": "mauritania",
+ "shortname": ":flag_mr:",
+ "category": "flags",
+ "emoji_order": "1160",
+ "aliases": [
+ ":mr:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_mu": {
+ "unicode": "1f1f2-1f1fa",
+ "unicode_alternates": "",
+ "name": "mauritius",
+ "shortname": ":flag_mu:",
+ "category": "flags",
+ "emoji_order": "1161",
+ "aliases": [
+ ":mu:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_mx": {
+ "unicode": "1f1f2-1f1fd",
+ "unicode_alternates": "",
+ "name": "mexico",
+ "shortname": ":flag_mx:",
+ "category": "flags",
+ "emoji_order": "1162",
+ "aliases": [
+ ":mx:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "mexican",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_fm": {
+ "unicode": "1f1eb-1f1f2",
+ "unicode_alternates": "",
+ "name": "micronesia",
+ "shortname": ":flag_fm:",
+ "category": "flags",
+ "emoji_order": "1163",
+ "aliases": [
+ ":fm:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_md": {
+ "unicode": "1f1f2-1f1e9",
+ "unicode_alternates": "",
+ "name": "moldova",
+ "shortname": ":flag_md:",
+ "category": "flags",
+ "emoji_order": "1164",
+ "aliases": [
+ ":md:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_mc": {
+ "unicode": "1f1f2-1f1e8",
+ "unicode_alternates": "",
+ "name": "monaco",
+ "shortname": ":flag_mc:",
+ "category": "flags",
+ "emoji_order": "1165",
+ "aliases": [
+ ":mc:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_mn": {
+ "unicode": "1f1f2-1f1f3",
+ "unicode_alternates": "",
+ "name": "mongolia",
+ "shortname": ":flag_mn:",
+ "category": "flags",
+ "emoji_order": "1166",
+ "aliases": [
+ ":mn:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_me": {
+ "unicode": "1f1f2-1f1ea",
+ "unicode_alternates": "",
+ "name": "montenegro",
+ "shortname": ":flag_me:",
+ "category": "flags",
+ "emoji_order": "1167",
+ "aliases": [
+ ":me:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_ms": {
+ "unicode": "1f1f2-1f1f8",
+ "unicode_alternates": "",
+ "name": "montserrat",
+ "shortname": ":flag_ms:",
+ "category": "flags",
+ "emoji_order": "1168",
+ "aliases": [
+ ":ms:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_ma": {
+ "unicode": "1f1f2-1f1e6",
+ "unicode_alternates": "",
+ "name": "morocco",
+ "shortname": ":flag_ma:",
+ "category": "flags",
+ "emoji_order": "1169",
+ "aliases": [
+ ":ma:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_mz": {
+ "unicode": "1f1f2-1f1ff",
+ "unicode_alternates": "",
+ "name": "mozambique",
+ "shortname": ":flag_mz:",
+ "category": "flags",
+ "emoji_order": "1170",
+ "aliases": [
+ ":mz:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_mm": {
+ "unicode": "1f1f2-1f1f2",
+ "unicode_alternates": "",
+ "name": "myanmar",
+ "shortname": ":flag_mm:",
+ "category": "flags",
+ "emoji_order": "1171",
+ "aliases": [
+ ":mm:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_na": {
+ "unicode": "1f1f3-1f1e6",
+ "unicode_alternates": "",
+ "name": "namibia",
+ "shortname": ":flag_na:",
+ "category": "flags",
+ "emoji_order": "1172",
+ "aliases": [
+ ":na:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_nr": {
+ "unicode": "1f1f3-1f1f7",
+ "unicode_alternates": "",
+ "name": "nauru",
+ "shortname": ":flag_nr:",
+ "category": "flags",
+ "emoji_order": "1173",
+ "aliases": [
+ ":nr:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_np": {
+ "unicode": "1f1f3-1f1f5",
+ "unicode_alternates": "",
+ "name": "nepal",
+ "shortname": ":flag_np:",
+ "category": "flags",
+ "emoji_order": "1174",
+ "aliases": [
+ ":np:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_nl": {
+ "unicode": "1f1f3-1f1f1",
+ "unicode_alternates": "",
+ "name": "the netherlands",
+ "shortname": ":flag_nl:",
+ "category": "flags",
+ "emoji_order": "1175",
+ "aliases": [
+ ":nl:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_nc": {
+ "unicode": "1f1f3-1f1e8",
+ "unicode_alternates": "",
+ "name": "new caledonia",
+ "shortname": ":flag_nc:",
+ "category": "flags",
+ "emoji_order": "1176",
+ "aliases": [
+ ":nc:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_nz": {
+ "unicode": "1f1f3-1f1ff",
+ "unicode_alternates": "",
+ "name": "new zealand",
+ "shortname": ":flag_nz:",
+ "category": "flags",
+ "emoji_order": "1177",
+ "aliases": [
+ ":nz:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_ni": {
+ "unicode": "1f1f3-1f1ee",
+ "unicode_alternates": "",
+ "name": "nicaragua",
+ "shortname": ":flag_ni:",
+ "category": "flags",
+ "emoji_order": "1178",
+ "aliases": [
+ ":ni:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_ne": {
+ "unicode": "1f1f3-1f1ea",
+ "unicode_alternates": "",
+ "name": "niger",
+ "shortname": ":flag_ne:",
+ "category": "flags",
+ "emoji_order": "1179",
+ "aliases": [
+ ":ne:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_ng": {
+ "unicode": "1f1f3-1f1ec",
+ "unicode_alternates": "",
+ "name": "nigeria",
+ "shortname": ":flag_ng:",
+ "category": "flags",
+ "emoji_order": "1180",
+ "aliases": [
+ ":nigeria:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_nu": {
+ "unicode": "1f1f3-1f1fa",
+ "unicode_alternates": "",
+ "name": "niue",
+ "shortname": ":flag_nu:",
+ "category": "flags",
+ "emoji_order": "1181",
+ "aliases": [
+ ":nu:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_kp": {
+ "unicode": "1f1f0-1f1f5",
+ "unicode_alternates": "",
+ "name": "north korea",
+ "shortname": ":flag_kp:",
+ "category": "flags",
+ "emoji_order": "1182",
+ "aliases": [
+ ":kp:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_no": {
+ "unicode": "1f1f3-1f1f4",
+ "unicode_alternates": "",
+ "name": "norway",
+ "shortname": ":flag_no:",
+ "category": "flags",
+ "emoji_order": "1183",
+ "aliases": [
+ ":no:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_om": {
+ "unicode": "1f1f4-1f1f2",
+ "unicode_alternates": "",
+ "name": "oman",
+ "shortname": ":flag_om:",
+ "category": "flags",
+ "emoji_order": "1184",
+ "aliases": [
+ ":om:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_pk": {
+ "unicode": "1f1f5-1f1f0",
+ "unicode_alternates": "",
+ "name": "pakistan",
+ "shortname": ":flag_pk:",
+ "category": "flags",
+ "emoji_order": "1185",
+ "aliases": [
+ ":pk:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_pw": {
+ "unicode": "1f1f5-1f1fc",
+ "unicode_alternates": "",
+ "name": "palau",
+ "shortname": ":flag_pw:",
+ "category": "flags",
+ "emoji_order": "1186",
+ "aliases": [
+ ":pw:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_ps": {
+ "unicode": "1f1f5-1f1f8",
+ "unicode_alternates": "",
+ "name": "palestinian authority",
+ "shortname": ":flag_ps:",
+ "category": "flags",
+ "emoji_order": "1187",
+ "aliases": [
+ ":ps:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_pa": {
+ "unicode": "1f1f5-1f1e6",
+ "unicode_alternates": "",
+ "name": "panama",
+ "shortname": ":flag_pa:",
+ "category": "flags",
+ "emoji_order": "1188",
+ "aliases": [
+ ":pa:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_pg": {
+ "unicode": "1f1f5-1f1ec",
+ "unicode_alternates": "",
+ "name": "papua new guinea",
+ "shortname": ":flag_pg:",
+ "category": "flags",
+ "emoji_order": "1189",
+ "aliases": [
+ ":pg:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_py": {
+ "unicode": "1f1f5-1f1fe",
+ "unicode_alternates": "",
+ "name": "paraguay",
+ "shortname": ":flag_py:",
+ "category": "flags",
+ "emoji_order": "1190",
+ "aliases": [
+ ":py:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_pe": {
+ "unicode": "1f1f5-1f1ea",
+ "unicode_alternates": "",
+ "name": "peru",
+ "shortname": ":flag_pe:",
+ "category": "flags",
+ "emoji_order": "1191",
+ "aliases": [
+ ":pe:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_ph": {
+ "unicode": "1f1f5-1f1ed",
+ "unicode_alternates": "",
+ "name": "the philippines",
+ "shortname": ":flag_ph:",
+ "category": "flags",
+ "emoji_order": "1192",
+ "aliases": [
+ ":ph:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_pl": {
+ "unicode": "1f1f5-1f1f1",
+ "unicode_alternates": "",
+ "name": "poland",
+ "shortname": ":flag_pl:",
+ "category": "flags",
+ "emoji_order": "1193",
+ "aliases": [
+ ":pl:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_pt": {
+ "unicode": "1f1f5-1f1f9",
+ "unicode_alternates": "",
+ "name": "portugal",
+ "shortname": ":flag_pt:",
+ "category": "flags",
+ "emoji_order": "1194",
+ "aliases": [
+ ":pt:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_pr": {
+ "unicode": "1f1f5-1f1f7",
+ "unicode_alternates": "",
+ "name": "puerto rico",
+ "shortname": ":flag_pr:",
+ "category": "flags",
+ "emoji_order": "1195",
+ "aliases": [
+ ":pr:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_qa": {
+ "unicode": "1f1f6-1f1e6",
+ "unicode_alternates": "",
+ "name": "qatar",
+ "shortname": ":flag_qa:",
+ "category": "flags",
+ "emoji_order": "1196",
+ "aliases": [
+ ":qa:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_ro": {
+ "unicode": "1f1f7-1f1f4",
+ "unicode_alternates": "",
+ "name": "romania",
+ "shortname": ":flag_ro:",
+ "category": "flags",
+ "emoji_order": "1197",
+ "aliases": [
+ ":ro:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_ru": {
+ "unicode": "1f1f7-1f1fa",
+ "unicode_alternates": "",
+ "name": "russia",
+ "shortname": ":flag_ru:",
+ "category": "flags",
+ "emoji_order": "1198",
+ "aliases": [
+ ":ru:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_rw": {
+ "unicode": "1f1f7-1f1fc",
+ "unicode_alternates": "",
+ "name": "rwanda",
+ "shortname": ":flag_rw:",
+ "category": "flags",
+ "emoji_order": "1199",
+ "aliases": [
+ ":rw:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_sh": {
+ "unicode": "1f1f8-1f1ed",
+ "unicode_alternates": "",
+ "name": "saint helena",
+ "shortname": ":flag_sh:",
+ "category": "flags",
+ "emoji_order": "1200",
+ "aliases": [
+ ":sh:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_kn": {
+ "unicode": "1f1f0-1f1f3",
+ "unicode_alternates": "",
+ "name": "saint kitts and nevis",
+ "shortname": ":flag_kn:",
+ "category": "flags",
+ "emoji_order": "1201",
+ "aliases": [
+ ":kn:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_lc": {
+ "unicode": "1f1f1-1f1e8",
+ "unicode_alternates": "",
+ "name": "saint lucia",
+ "shortname": ":flag_lc:",
+ "category": "flags",
+ "emoji_order": "1202",
+ "aliases": [
+ ":lc:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_vc": {
+ "unicode": "1f1fb-1f1e8",
+ "unicode_alternates": "",
+ "name": "saint vincent and the grenadines",
+ "shortname": ":flag_vc:",
+ "category": "flags",
+ "emoji_order": "1203",
+ "aliases": [
+ ":vc:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_ws": {
+ "unicode": "1f1fc-1f1f8",
+ "unicode_alternates": "",
+ "name": "samoa",
+ "shortname": ":flag_ws:",
+ "category": "flags",
+ "emoji_order": "1204",
+ "aliases": [
+ ":ws:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_sm": {
+ "unicode": "1f1f8-1f1f2",
+ "unicode_alternates": "",
+ "name": "san marino",
+ "shortname": ":flag_sm:",
+ "category": "flags",
+ "emoji_order": "1205",
+ "aliases": [
+ ":sm:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_st": {
+ "unicode": "1f1f8-1f1f9",
+ "unicode_alternates": "",
+ "name": "são tomé and príncipe",
+ "shortname": ":flag_st:",
+ "category": "flags",
+ "emoji_order": "1206",
+ "aliases": [
+ ":st:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_sa": {
+ "unicode": "1f1f8-1f1e6",
+ "unicode_alternates": "",
+ "name": "saudi arabia",
+ "shortname": ":flag_sa:",
+ "category": "flags",
+ "emoji_order": "1207",
+ "aliases": [
+ ":saudiarabia:",
+ ":saudi:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_sn": {
+ "unicode": "1f1f8-1f1f3",
+ "unicode_alternates": "",
+ "name": "senegal",
+ "shortname": ":flag_sn:",
+ "category": "flags",
+ "emoji_order": "1208",
+ "aliases": [
+ ":sn:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_rs": {
+ "unicode": "1f1f7-1f1f8",
+ "unicode_alternates": "",
+ "name": "serbia",
+ "shortname": ":flag_rs:",
+ "category": "flags",
+ "emoji_order": "1209",
+ "aliases": [
+ ":rs:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_sc": {
+ "unicode": "1f1f8-1f1e8",
+ "unicode_alternates": "",
+ "name": "the seychelles",
+ "shortname": ":flag_sc:",
+ "category": "flags",
+ "emoji_order": "1210",
+ "aliases": [
+ ":sc:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_sl": {
+ "unicode": "1f1f8-1f1f1",
+ "unicode_alternates": "",
+ "name": "sierra leone",
+ "shortname": ":flag_sl:",
+ "category": "flags",
+ "emoji_order": "1211",
+ "aliases": [
+ ":sl:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_sg": {
+ "unicode": "1f1f8-1f1ec",
+ "unicode_alternates": "",
+ "name": "singapore",
+ "shortname": ":flag_sg:",
+ "category": "flags",
+ "emoji_order": "1212",
+ "aliases": [
+ ":sg:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_sk": {
+ "unicode": "1f1f8-1f1f0",
+ "unicode_alternates": "",
+ "name": "slovakia",
+ "shortname": ":flag_sk:",
+ "category": "flags",
+ "emoji_order": "1213",
+ "aliases": [
+ ":sk:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_si": {
+ "unicode": "1f1f8-1f1ee",
+ "unicode_alternates": "",
+ "name": "slovenia",
+ "shortname": ":flag_si:",
+ "category": "flags",
+ "emoji_order": "1214",
+ "aliases": [
+ ":si:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_sb": {
+ "unicode": "1f1f8-1f1e7",
+ "unicode_alternates": "",
+ "name": "the solomon islands",
+ "shortname": ":flag_sb:",
+ "category": "flags",
+ "emoji_order": "1215",
+ "aliases": [
+ ":sb:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_so": {
+ "unicode": "1f1f8-1f1f4",
+ "unicode_alternates": "",
+ "name": "somalia",
+ "shortname": ":flag_so:",
+ "category": "flags",
+ "emoji_order": "1216",
+ "aliases": [
+ ":so:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_za": {
+ "unicode": "1f1ff-1f1e6",
+ "unicode_alternates": "",
+ "name": "south africa",
+ "shortname": ":flag_za:",
+ "category": "flags",
+ "emoji_order": "1217",
+ "aliases": [
+ ":za:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_kr": {
+ "unicode": "1f1f0-1f1f7",
+ "unicode_alternates": "",
+ "name": "korea",
+ "shortname": ":flag_kr:",
+ "category": "flags",
+ "emoji_order": "1218",
+ "aliases": [
+ ":kr:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_es": {
+ "unicode": "1f1ea-1f1f8",
+ "unicode_alternates": "",
+ "name": "spain",
+ "shortname": ":flag_es:",
+ "category": "flags",
+ "emoji_order": "1219",
+ "aliases": [
+ ":es:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_lk": {
+ "unicode": "1f1f1-1f1f0",
+ "unicode_alternates": "",
+ "name": "sri lanka",
+ "shortname": ":flag_lk:",
+ "category": "flags",
+ "emoji_order": "1220",
+ "aliases": [
+ ":lk:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_sd": {
+ "unicode": "1f1f8-1f1e9",
+ "unicode_alternates": "",
+ "name": "sudan",
+ "shortname": ":flag_sd:",
+ "category": "flags",
+ "emoji_order": "1221",
+ "aliases": [
+ ":sd:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_sr": {
+ "unicode": "1f1f8-1f1f7",
+ "unicode_alternates": "",
+ "name": "suriname",
+ "shortname": ":flag_sr:",
+ "category": "flags",
+ "emoji_order": "1222",
+ "aliases": [
+ ":sr:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_sz": {
+ "unicode": "1f1f8-1f1ff",
+ "unicode_alternates": "",
+ "name": "swaziland",
+ "shortname": ":flag_sz:",
+ "category": "flags",
+ "emoji_order": "1223",
+ "aliases": [
+ ":sz:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_se": {
+ "unicode": "1f1f8-1f1ea",
+ "unicode_alternates": "",
+ "name": "sweden",
+ "shortname": ":flag_se:",
+ "category": "flags",
+ "emoji_order": "1224",
+ "aliases": [
+ ":se:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_ch": {
+ "unicode": "1f1e8-1f1ed",
+ "unicode_alternates": "",
+ "name": "switzerland",
+ "shortname": ":flag_ch:",
+ "category": "flags",
+ "emoji_order": "1225",
+ "aliases": [
+ ":ch:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "neutral",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_sy": {
+ "unicode": "1f1f8-1f1fe",
+ "unicode_alternates": "",
+ "name": "syria",
+ "shortname": ":flag_sy:",
+ "category": "flags",
+ "emoji_order": "1226",
+ "aliases": [
+ ":sy:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_tw": {
+ "unicode": "1f1f9-1f1fc",
+ "unicode_alternates": "",
+ "name": "the republic of china",
+ "shortname": ":flag_tw:",
+ "category": "flags",
+ "emoji_order": "1227",
+ "aliases": [
+ ":tw:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_tj": {
+ "unicode": "1f1f9-1f1ef",
+ "unicode_alternates": "",
+ "name": "tajikistan",
+ "shortname": ":flag_tj:",
+ "category": "flags",
+ "emoji_order": "1228",
+ "aliases": [
+ ":tj:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_tz": {
+ "unicode": "1f1f9-1f1ff",
+ "unicode_alternates": "",
+ "name": "tanzania",
+ "shortname": ":flag_tz:",
+ "category": "flags",
+ "emoji_order": "1229",
+ "aliases": [
+ ":tz:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_th": {
+ "unicode": "1f1f9-1f1ed",
+ "unicode_alternates": "",
+ "name": "thailand",
+ "shortname": ":flag_th:",
+ "category": "flags",
+ "emoji_order": "1230",
+ "aliases": [
+ ":th:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_tl": {
+ "unicode": "1f1f9-1f1f1",
+ "unicode_alternates": "",
+ "name": "timor-leste",
+ "shortname": ":flag_tl:",
+ "category": "flags",
+ "emoji_order": "1231",
+ "aliases": [
+ ":tl:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_tg": {
+ "unicode": "1f1f9-1f1ec",
+ "unicode_alternates": "",
+ "name": "togo",
+ "shortname": ":flag_tg:",
+ "category": "flags",
+ "emoji_order": "1232",
+ "aliases": [
+ ":tg:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_to": {
+ "unicode": "1f1f9-1f1f4",
+ "unicode_alternates": "",
+ "name": "tonga",
+ "shortname": ":flag_to:",
+ "category": "flags",
+ "emoji_order": "1233",
+ "aliases": [
+ ":to:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_tt": {
+ "unicode": "1f1f9-1f1f9",
+ "unicode_alternates": "",
+ "name": "trinidad and tobago",
+ "shortname": ":flag_tt:",
+ "category": "flags",
+ "emoji_order": "1234",
+ "aliases": [
+ ":tt:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_tn": {
+ "unicode": "1f1f9-1f1f3",
+ "unicode_alternates": "",
+ "name": "tunisia",
+ "shortname": ":flag_tn:",
+ "category": "flags",
+ "emoji_order": "1235",
+ "aliases": [
+ ":tn:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_tr": {
+ "unicode": "1f1f9-1f1f7",
+ "unicode_alternates": "",
+ "name": "turkey",
+ "shortname": ":flag_tr:",
+ "category": "flags",
+ "emoji_order": "1236",
+ "aliases": [
+ ":tr:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_tm": {
+ "unicode": "1f1f9-1f1f2",
+ "unicode_alternates": "",
+ "name": "turkmenistan",
+ "shortname": ":flag_tm:",
+ "category": "flags",
+ "emoji_order": "1237",
+ "aliases": [
+ ":turkmenistan:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_tv": {
+ "unicode": "1f1f9-1f1fb",
+ "unicode_alternates": "",
+ "name": "tuvalu",
+ "shortname": ":flag_tv:",
+ "category": "flags",
+ "emoji_order": "1238",
+ "aliases": [
+ ":tuvalu:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_ug": {
+ "unicode": "1f1fa-1f1ec",
+ "unicode_alternates": "",
+ "name": "uganda",
+ "shortname": ":flag_ug:",
+ "category": "flags",
+ "emoji_order": "1239",
+ "aliases": [
+ ":ug:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_ua": {
+ "unicode": "1f1fa-1f1e6",
+ "unicode_alternates": "",
+ "name": "ukraine",
+ "shortname": ":flag_ua:",
+ "category": "flags",
+ "emoji_order": "1240",
+ "aliases": [
+ ":ua:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_ae": {
+ "unicode": "1f1e6-1f1ea",
+ "unicode_alternates": "",
+ "name": "the united arab emirates",
+ "shortname": ":flag_ae:",
+ "category": "flags",
+ "emoji_order": "1241",
+ "aliases": [
+ ":ae:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_gb": {
+ "unicode": "1f1ec-1f1e7",
+ "unicode_alternates": "",
+ "name": "great britain",
+ "shortname": ":flag_gb:",
+ "category": "flags",
+ "emoji_order": "1242",
+ "aliases": [
+ ":gb:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_us": {
+ "unicode": "1f1fa-1f1f8",
+ "unicode_alternates": "",
+ "name": "united states",
+ "shortname": ":flag_us:",
+ "category": "flags",
+ "emoji_order": "1243",
+ "aliases": [
+ ":us:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "america",
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_vi": {
+ "unicode": "1f1fb-1f1ee",
+ "unicode_alternates": "",
+ "name": "u.s. virgin islands",
+ "shortname": ":flag_vi:",
+ "category": "flags",
+ "emoji_order": "1244",
+ "aliases": [
+ ":vi:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_uy": {
+ "unicode": "1f1fa-1f1fe",
+ "unicode_alternates": "",
+ "name": "uruguay",
+ "shortname": ":flag_uy:",
+ "category": "flags",
+ "emoji_order": "1245",
+ "aliases": [
+ ":uy:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_uz": {
+ "unicode": "1f1fa-1f1ff",
+ "unicode_alternates": "",
+ "name": "uzbekistan",
+ "shortname": ":flag_uz:",
+ "category": "flags",
+ "emoji_order": "1246",
+ "aliases": [
+ ":uz:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_vu": {
+ "unicode": "1f1fb-1f1fa",
+ "unicode_alternates": "",
+ "name": "vanuatu",
+ "shortname": ":flag_vu:",
+ "category": "flags",
+ "emoji_order": "1247",
+ "aliases": [
+ ":vu:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_va": {
+ "unicode": "1f1fb-1f1e6",
+ "unicode_alternates": "",
+ "name": "the vatican city",
+ "shortname": ":flag_va:",
+ "category": "flags",
+ "emoji_order": "1248",
+ "aliases": [
+ ":va:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_ve": {
+ "unicode": "1f1fb-1f1ea",
+ "unicode_alternates": "",
+ "name": "venezuela",
+ "shortname": ":flag_ve:",
+ "category": "flags",
+ "emoji_order": "1249",
+ "aliases": [
+ ":ve:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_vn": {
+ "unicode": "1f1fb-1f1f3",
+ "unicode_alternates": "",
+ "name": "vietnam",
+ "shortname": ":flag_vn:",
+ "category": "flags",
+ "emoji_order": "1250",
+ "aliases": [
+ ":vn:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_wf": {
+ "unicode": "1f1fc-1f1eb",
+ "unicode_alternates": "",
+ "name": "wallis and futuna",
+ "shortname": ":flag_wf:",
+ "category": "flags",
+ "emoji_order": "1251",
+ "aliases": [
+ ":wf:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_eh": {
+ "unicode": "1f1ea-1f1ed",
+ "unicode_alternates": "",
+ "name": "western sahara",
+ "shortname": ":flag_eh:",
+ "category": "flags",
+ "emoji_order": "1252",
+ "aliases": [
+ ":eh:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_ye": {
+ "unicode": "1f1fe-1f1ea",
+ "unicode_alternates": "",
+ "name": "yemen",
+ "shortname": ":flag_ye:",
+ "category": "flags",
+ "emoji_order": "1253",
+ "aliases": [
+ ":ye:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_zm": {
+ "unicode": "1f1ff-1f1f2",
+ "unicode_alternates": "",
+ "name": "zambia",
+ "shortname": ":flag_zm:",
+ "category": "flags",
+ "emoji_order": "1254",
+ "aliases": [
+ ":zm:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_zw": {
+ "unicode": "1f1ff-1f1fc",
+ "unicode_alternates": "",
+ "name": "zimbabwe",
+ "shortname": ":flag_zw:",
+ "category": "flags",
+ "emoji_order": "1255",
+ "aliases": [
+ ":zw:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_re": {
+ "unicode": "1f1f7-1f1ea",
+ "unicode_alternates": "",
+ "name": "réunion",
+ "shortname": ":flag_re:",
+ "category": "flags",
+ "emoji_order": "1256",
+ "aliases": [
+ ":re:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_ax": {
+ "unicode": "1f1e6-1f1fd",
+ "unicode_alternates": "",
+ "name": "åland islands",
+ "shortname": ":flag_ax:",
+ "category": "flags",
+ "emoji_order": "1257",
+ "aliases": [
+ ":ax:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_ta": {
+ "unicode": "1f1f9-1f1e6",
+ "unicode_alternates": "",
+ "name": "tristan da cunha",
+ "shortname": ":flag_ta:",
+ "category": "flags",
+ "emoji_order": "1258",
+ "aliases": [
+ ":ta:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_io": {
+ "unicode": "1f1ee-1f1f4",
+ "unicode_alternates": "",
+ "name": "british indian ocean territory",
+ "shortname": ":flag_io:",
+ "category": "flags",
+ "emoji_order": "1259",
+ "aliases": [
+ ":io:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_bq": {
+ "unicode": "1f1e7-1f1f6",
+ "unicode_alternates": "",
+ "name": "caribbean netherlands",
+ "shortname": ":flag_bq:",
+ "category": "flags",
+ "emoji_order": "1260",
+ "aliases": [
+ ":bq:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_cx": {
+ "unicode": "1f1e8-1f1fd",
+ "unicode_alternates": "",
+ "name": "christmas island",
+ "shortname": ":flag_cx:",
+ "category": "flags",
+ "emoji_order": "1261",
+ "aliases": [
+ ":cx:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_cc": {
+ "unicode": "1f1e8-1f1e8",
+ "unicode_alternates": "",
+ "name": "cocos (keeling) islands",
+ "shortname": ":flag_cc:",
+ "category": "flags",
+ "emoji_order": "1262",
+ "aliases": [
+ ":cc:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_gg": {
+ "unicode": "1f1ec-1f1ec",
+ "unicode_alternates": "",
+ "name": "guernsey",
+ "shortname": ":flag_gg:",
+ "category": "flags",
+ "emoji_order": "1263",
+ "aliases": [
+ ":gg:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_im": {
+ "unicode": "1f1ee-1f1f2",
+ "unicode_alternates": "",
+ "name": "isle of man",
+ "shortname": ":flag_im:",
+ "category": "flags",
+ "emoji_order": "1264",
+ "aliases": [
+ ":im:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_yt": {
+ "unicode": "1f1fe-1f1f9",
+ "unicode_alternates": "",
+ "name": "mayotte",
+ "shortname": ":flag_yt:",
+ "category": "flags",
+ "emoji_order": "1265",
+ "aliases": [
+ ":yt:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_nf": {
+ "unicode": "1f1f3-1f1eb",
+ "unicode_alternates": "",
+ "name": "norfolk island",
+ "shortname": ":flag_nf:",
+ "category": "flags",
+ "emoji_order": "1266",
+ "aliases": [
+ ":nf:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_pn": {
+ "unicode": "1f1f5-1f1f3",
+ "unicode_alternates": "",
+ "name": "pitcairn",
+ "shortname": ":flag_pn:",
+ "category": "flags",
+ "emoji_order": "1267",
+ "aliases": [
+ ":pn:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_bl": {
+ "unicode": "1f1e7-1f1f1",
+ "unicode_alternates": "",
+ "name": "saint barthélemy",
+ "shortname": ":flag_bl:",
+ "category": "flags",
+ "emoji_order": "1268",
+ "aliases": [
+ ":bl:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_pm": {
+ "unicode": "1f1f5-1f1f2",
+ "unicode_alternates": "",
+ "name": "saint pierre and miquelon",
+ "shortname": ":flag_pm:",
+ "category": "flags",
+ "emoji_order": "1269",
+ "aliases": [
+ ":pm:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_gs": {
+ "unicode": "1f1ec-1f1f8",
+ "unicode_alternates": "",
+ "name": "south georgia",
+ "shortname": ":flag_gs:",
+ "category": "flags",
+ "emoji_order": "1270",
+ "aliases": [
+ ":gs:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_tk": {
+ "unicode": "1f1f9-1f1f0",
+ "unicode_alternates": "",
+ "name": "tokelau",
+ "shortname": ":flag_tk:",
+ "category": "flags",
+ "emoji_order": "1271",
+ "aliases": [
+ ":tk:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_bv": {
+ "unicode": "1f1e7-1f1fb",
+ "unicode_alternates": "",
+ "name": "bouvet island",
+ "shortname": ":flag_bv:",
+ "category": "flags",
+ "emoji_order": "1272",
+ "aliases": [
+ ":bv:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_hm": {
+ "unicode": "1f1ed-1f1f2",
+ "unicode_alternates": "",
+ "name": "heard island and mcdonald islands",
+ "shortname": ":flag_hm:",
+ "category": "flags",
+ "emoji_order": "1273",
+ "aliases": [
+ ":hm:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_sj": {
+ "unicode": "1f1f8-1f1ef",
+ "unicode_alternates": "",
+ "name": "svalbard and jan mayen",
+ "shortname": ":flag_sj:",
+ "category": "flags",
+ "emoji_order": "1274",
+ "aliases": [
+ ":sj:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_um": {
+ "unicode": "1f1fa-1f1f2",
+ "unicode_alternates": "",
+ "name": "united states minor outlying islands",
+ "shortname": ":flag_um:",
+ "category": "flags",
+ "emoji_order": "1275",
+ "aliases": [
+ ":um:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_ic": {
+ "unicode": "1f1ee-1f1e8",
+ "unicode_alternates": "",
+ "name": "canary islands",
+ "shortname": ":flag_ic:",
+ "category": "flags",
+ "emoji_order": "1276",
+ "aliases": [
+ ":ic:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_ea": {
+ "unicode": "1f1ea-1f1e6",
+ "unicode_alternates": "",
+ "name": "ceuta, melilla",
+ "shortname": ":flag_ea:",
+ "category": "flags",
+ "emoji_order": "1277",
+ "aliases": [
+ ":ea:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_cp": {
+ "unicode": "1f1e8-1f1f5",
+ "unicode_alternates": "",
+ "name": "clipperton island",
+ "shortname": ":flag_cp:",
+ "category": "flags",
+ "emoji_order": "1278",
+ "aliases": [
+ ":cp:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_dg": {
+ "unicode": "1f1e9-1f1ec",
+ "unicode_alternates": "",
+ "name": "diego garcia",
+ "shortname": ":flag_dg:",
+ "category": "flags",
+ "emoji_order": "1279",
+ "aliases": [
+ ":dg:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_as": {
+ "unicode": "1f1e6-1f1f8",
+ "unicode_alternates": "",
+ "name": "american samoa",
+ "shortname": ":flag_as:",
+ "category": "flags",
+ "emoji_order": "1280",
+ "aliases": [
+ ":as:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_aq": {
+ "unicode": "1f1e6-1f1f6",
+ "unicode_alternates": "",
+ "name": "antarctica",
+ "shortname": ":flag_aq:",
+ "category": "flags",
+ "emoji_order": "1281",
+ "aliases": [
+ ":aq:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_vg": {
+ "unicode": "1f1fb-1f1ec",
+ "unicode_alternates": "",
+ "name": "british virgin islands",
+ "shortname": ":flag_vg:",
+ "category": "flags",
+ "emoji_order": "1282",
+ "aliases": [
+ ":vg:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_ck": {
+ "unicode": "1f1e8-1f1f0",
+ "unicode_alternates": "",
+ "name": "cook islands",
+ "shortname": ":flag_ck:",
+ "category": "flags",
+ "emoji_order": "1283",
+ "aliases": [
+ ":ck:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_cw": {
+ "unicode": "1f1e8-1f1fc",
+ "unicode_alternates": "",
+ "name": "curaçao",
+ "shortname": ":flag_cw:",
+ "category": "flags",
+ "emoji_order": "1284",
+ "aliases": [
+ ":cw:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_eu": {
+ "unicode": "1f1ea-1f1fa",
+ "unicode_alternates": "",
+ "name": "european union",
+ "shortname": ":flag_eu:",
+ "category": "flags",
+ "emoji_order": "1285",
+ "aliases": [
+ ":eu:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_gf": {
+ "unicode": "1f1ec-1f1eb",
+ "unicode_alternates": "",
+ "name": "french guiana",
+ "shortname": ":flag_gf:",
+ "category": "flags",
+ "emoji_order": "1286",
+ "aliases": [
+ ":gf:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_tf": {
+ "unicode": "1f1f9-1f1eb",
+ "unicode_alternates": "",
+ "name": "french southern territories",
+ "shortname": ":flag_tf:",
+ "category": "flags",
+ "emoji_order": "1287",
+ "aliases": [
+ ":tf:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_gp": {
+ "unicode": "1f1ec-1f1f5",
+ "unicode_alternates": "",
+ "name": "guadeloupe",
+ "shortname": ":flag_gp:",
+ "category": "flags",
+ "emoji_order": "1288",
+ "aliases": [
+ ":gp:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_mq": {
+ "unicode": "1f1f2-1f1f6",
+ "unicode_alternates": "",
+ "name": "martinique",
+ "shortname": ":flag_mq:",
+ "category": "flags",
+ "emoji_order": "1289",
+ "aliases": [
+ ":mq:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_mp": {
+ "unicode": "1f1f2-1f1f5",
+ "unicode_alternates": "",
+ "name": "northern mariana islands",
+ "shortname": ":flag_mp:",
+ "category": "flags",
+ "emoji_order": "1290",
+ "aliases": [
+ ":mp:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_sx": {
+ "unicode": "1f1f8-1f1fd",
+ "unicode_alternates": "",
+ "name": "sint maarten",
+ "shortname": ":flag_sx:",
+ "category": "flags",
+ "emoji_order": "1291",
+ "aliases": [
+ ":sx:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_ss": {
+ "unicode": "1f1f8-1f1f8",
+ "unicode_alternates": "",
+ "name": "south sudan",
+ "shortname": ":flag_ss:",
+ "category": "flags",
+ "emoji_order": "1292",
+ "aliases": [
+ ":ss:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_tc": {
+ "unicode": "1f1f9-1f1e8",
+ "unicode_alternates": "",
+ "name": "turks and caicos islands",
+ "shortname": ":flag_tc:",
+ "category": "flags",
+ "emoji_order": "1293",
+ "aliases": [
+ ":tc:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "flag_mf": {
+ "unicode": "1f1f2-1f1eb",
+ "unicode_alternates": "",
+ "name": "saint martin",
+ "shortname": ":flag_mf:",
+ "category": "flags",
+ "emoji_order": "1294",
+ "aliases": [
+ ":mf:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "country",
+ "flag",
+ "flag"
+ ]
+ },
+ "raised_hands_tone1": {
+ "unicode": "1f64c-1f3fb",
+ "unicode_alternates": "",
+ "name": "person raising both hands in celebration tone 1",
+ "shortname": ":raised_hands_tone1:",
+ "category": "people",
+ "emoji_order": "1295",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "raised_hands_tone2": {
+ "unicode": "1f64c-1f3fc",
+ "unicode_alternates": "",
+ "name": "person raising both hands in celebration tone 2",
+ "shortname": ":raised_hands_tone2:",
+ "category": "people",
+ "emoji_order": "1296",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "raised_hands_tone3": {
+ "unicode": "1f64c-1f3fd",
+ "unicode_alternates": "",
+ "name": "person raising both hands in celebration tone 3",
+ "shortname": ":raised_hands_tone3:",
+ "category": "people",
+ "emoji_order": "1297",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "raised_hands_tone4": {
+ "unicode": "1f64c-1f3fe",
+ "unicode_alternates": "",
+ "name": "person raising both hands in celebration tone 4",
+ "shortname": ":raised_hands_tone4:",
+ "category": "people",
+ "emoji_order": "1298",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "raised_hands_tone5": {
+ "unicode": "1f64c-1f3ff",
+ "unicode_alternates": "",
+ "name": "person raising both hands in celebration tone 5",
+ "shortname": ":raised_hands_tone5:",
+ "category": "people",
+ "emoji_order": "1299",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "clap_tone1": {
+ "unicode": "1f44f-1f3fb",
+ "unicode_alternates": "",
+ "name": "clapping hands sign tone 1",
+ "shortname": ":clap_tone1:",
+ "category": "people",
+ "emoji_order": "1300",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "clap_tone2": {
+ "unicode": "1f44f-1f3fc",
+ "unicode_alternates": "",
+ "name": "clapping hands sign tone 2",
+ "shortname": ":clap_tone2:",
+ "category": "people",
+ "emoji_order": "1301",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "clap_tone3": {
+ "unicode": "1f44f-1f3fd",
+ "unicode_alternates": "",
+ "name": "clapping hands sign tone 3",
+ "shortname": ":clap_tone3:",
+ "category": "people",
+ "emoji_order": "1302",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "clap_tone4": {
+ "unicode": "1f44f-1f3fe",
+ "unicode_alternates": "",
+ "name": "clapping hands sign tone 4",
+ "shortname": ":clap_tone4:",
+ "category": "people",
+ "emoji_order": "1303",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "clap_tone5": {
+ "unicode": "1f44f-1f3ff",
+ "unicode_alternates": "",
+ "name": "clapping hands sign tone 5",
+ "shortname": ":clap_tone5:",
+ "category": "people",
+ "emoji_order": "1304",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "wave_tone1": {
+ "unicode": "1f44b-1f3fb",
+ "unicode_alternates": "",
+ "name": "waving hand sign tone 1",
+ "shortname": ":wave_tone1:",
+ "category": "people",
+ "emoji_order": "1305",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "wave_tone2": {
+ "unicode": "1f44b-1f3fc",
+ "unicode_alternates": "",
+ "name": "waving hand sign tone 2",
+ "shortname": ":wave_tone2:",
+ "category": "people",
+ "emoji_order": "1306",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "wave_tone3": {
+ "unicode": "1f44b-1f3fd",
+ "unicode_alternates": "",
+ "name": "waving hand sign tone 3",
+ "shortname": ":wave_tone3:",
+ "category": "people",
+ "emoji_order": "1307",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "wave_tone4": {
+ "unicode": "1f44b-1f3fe",
+ "unicode_alternates": "",
+ "name": "waving hand sign tone 4",
+ "shortname": ":wave_tone4:",
+ "category": "people",
+ "emoji_order": "1308",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "wave_tone5": {
+ "unicode": "1f44b-1f3ff",
+ "unicode_alternates": "",
+ "name": "waving hand sign tone 5",
+ "shortname": ":wave_tone5:",
+ "category": "people",
+ "emoji_order": "1309",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "thumbsup_tone1": {
+ "unicode": "1f44d-1f3fb",
+ "unicode_alternates": "",
+ "name": "thumbs up sign tone 1",
+ "shortname": ":thumbsup_tone1:",
+ "category": "people",
+ "emoji_order": "1310",
+ "aliases": [
+ ":+1_tone1:",
+ ":thumbup_tone1:"
+ ],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "thumbsup_tone2": {
+ "unicode": "1f44d-1f3fc",
+ "unicode_alternates": "",
+ "name": "thumbs up sign tone 2",
+ "shortname": ":thumbsup_tone2:",
+ "category": "people",
+ "emoji_order": "1311",
+ "aliases": [
+ ":+1_tone2:",
+ ":thumbup_tone2:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "thumbsup_tone3": {
+ "unicode": "1f44d-1f3fd",
+ "unicode_alternates": "",
+ "name": "thumbs up sign tone 3",
+ "shortname": ":thumbsup_tone3:",
+ "category": "people",
+ "emoji_order": "1312",
+ "aliases": [
+ ":+1_tone3:",
+ ":thumbup_tone3:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "thumbsup_tone4": {
+ "unicode": "1f44d-1f3fe",
+ "unicode_alternates": "",
+ "name": "thumbs up sign tone 4",
+ "shortname": ":thumbsup_tone4:",
+ "category": "people",
+ "emoji_order": "1313",
+ "aliases": [
+ ":+1_tone4:",
+ ":thumbup_tone4:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "thumbsup_tone5": {
+ "unicode": "1f44d-1f3ff",
+ "unicode_alternates": "",
+ "name": "thumbs up sign tone 5",
+ "shortname": ":thumbsup_tone5:",
+ "category": "people",
+ "emoji_order": "1314",
+ "aliases": [
+ ":+1_tone5:",
+ ":thumbup_tone5:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "thumbsdown_tone1": {
+ "unicode": "1f44e-1f3fb",
+ "unicode_alternates": "",
+ "name": "thumbs down sign tone 1",
+ "shortname": ":thumbsdown_tone1:",
+ "category": "people",
+ "emoji_order": "1315",
+ "aliases": [
+ ":-1_tone1:",
+ ":thumbdown_tone1:"
+ ],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "thumbsdown_tone2": {
+ "unicode": "1f44e-1f3fc",
+ "unicode_alternates": "",
+ "name": "thumbs down sign tone 2",
+ "shortname": ":thumbsdown_tone2:",
+ "category": "people",
+ "emoji_order": "1316",
+ "aliases": [
+ ":-1_tone2:",
+ ":thumbdown_tone2:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "thumbsdown_tone3": {
+ "unicode": "1f44e-1f3fd",
+ "unicode_alternates": "",
+ "name": "thumbs down sign tone 3",
+ "shortname": ":thumbsdown_tone3:",
+ "category": "people",
+ "emoji_order": "1317",
+ "aliases": [
+ ":-1_tone3:",
+ ":thumbdown_tone3:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "thumbsdown_tone4": {
+ "unicode": "1f44e-1f3fe",
+ "unicode_alternates": "",
+ "name": "thumbs down sign tone 4",
+ "shortname": ":thumbsdown_tone4:",
+ "category": "people",
+ "emoji_order": "1318",
+ "aliases": [
+ ":-1_tone4:",
+ ":thumbdown_tone4:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "thumbsdown_tone5": {
+ "unicode": "1f44e-1f3ff",
+ "unicode_alternates": "",
+ "name": "thumbs down sign tone 5",
+ "shortname": ":thumbsdown_tone5:",
+ "category": "people",
+ "emoji_order": "1319",
+ "aliases": [
+ ":-1_tone5:",
+ ":thumbdown_tone5:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "punch_tone1": {
+ "unicode": "1f44a-1f3fb",
+ "unicode_alternates": "",
+ "name": "fisted hand sign tone 1",
+ "shortname": ":punch_tone1:",
+ "category": "people",
+ "emoji_order": "1320",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "punch_tone2": {
+ "unicode": "1f44a-1f3fc",
+ "unicode_alternates": "",
+ "name": "fisted hand sign tone 2",
+ "shortname": ":punch_tone2:",
+ "category": "people",
+ "emoji_order": "1321",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "punch_tone3": {
+ "unicode": "1f44a-1f3fd",
+ "unicode_alternates": "",
+ "name": "fisted hand sign tone 3",
+ "shortname": ":punch_tone3:",
+ "category": "people",
+ "emoji_order": "1322",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "punch_tone4": {
+ "unicode": "1f44a-1f3fe",
+ "unicode_alternates": "",
+ "name": "fisted hand sign tone 4",
+ "shortname": ":punch_tone4:",
+ "category": "people",
+ "emoji_order": "1323",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "punch_tone5": {
+ "unicode": "1f44a-1f3ff",
+ "unicode_alternates": "",
+ "name": "fisted hand sign tone 5",
+ "shortname": ":punch_tone5:",
+ "category": "people",
+ "emoji_order": "1324",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "fist_tone1": {
+ "unicode": "270a-1f3fb",
+ "unicode_alternates": "",
+ "name": "raised fist tone 1",
+ "shortname": ":fist_tone1:",
+ "category": "people",
+ "emoji_order": "1325",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "fist_tone2": {
+ "unicode": "270a-1f3fc",
+ "unicode_alternates": "",
+ "name": "raised fist tone 2",
+ "shortname": ":fist_tone2:",
+ "category": "people",
+ "emoji_order": "1326",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "fist_tone3": {
+ "unicode": "270a-1f3fd",
+ "unicode_alternates": "",
+ "name": "raised fist tone 3",
+ "shortname": ":fist_tone3:",
+ "category": "people",
+ "emoji_order": "1327",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "fist_tone4": {
+ "unicode": "270a-1f3fe",
+ "unicode_alternates": "",
+ "name": "raised fist tone 4",
+ "shortname": ":fist_tone4:",
+ "category": "people",
+ "emoji_order": "1328",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "fist_tone5": {
+ "unicode": "270a-1f3ff",
+ "unicode_alternates": "",
+ "name": "raised fist tone 5",
+ "shortname": ":fist_tone5:",
+ "category": "people",
+ "emoji_order": "1329",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "v_tone1": {
+ "unicode": "270c-1f3fb",
+ "unicode_alternates": "",
+ "name": "victory hand tone 1",
+ "shortname": ":v_tone1:",
+ "category": "people",
+ "emoji_order": "1330",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "v_tone2": {
+ "unicode": "270c-1f3fc",
+ "unicode_alternates": "",
+ "name": "victory hand tone 2",
+ "shortname": ":v_tone2:",
+ "category": "people",
+ "emoji_order": "1331",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "v_tone3": {
+ "unicode": "270c-1f3fd",
+ "unicode_alternates": "",
+ "name": "victory hand tone 3",
+ "shortname": ":v_tone3:",
+ "category": "people",
+ "emoji_order": "1332",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "v_tone4": {
+ "unicode": "270c-1f3fe",
+ "unicode_alternates": "",
+ "name": "victory hand tone 4",
+ "shortname": ":v_tone4:",
+ "category": "people",
+ "emoji_order": "1333",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "v_tone5": {
+ "unicode": "270c-1f3ff",
+ "unicode_alternates": "",
+ "name": "victory hand tone 5",
+ "shortname": ":v_tone5:",
+ "category": "people",
+ "emoji_order": "1334",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "ok_hand_tone1": {
+ "unicode": "1f44c-1f3fb",
+ "unicode_alternates": "",
+ "name": "ok hand sign tone 1",
+ "shortname": ":ok_hand_tone1:",
+ "category": "people",
+ "emoji_order": "1335",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "ok_hand_tone2": {
+ "unicode": "1f44c-1f3fc",
+ "unicode_alternates": "",
+ "name": "ok hand sign tone 2",
+ "shortname": ":ok_hand_tone2:",
+ "category": "people",
+ "emoji_order": "1336",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "ok_hand_tone3": {
+ "unicode": "1f44c-1f3fd",
+ "unicode_alternates": "",
+ "name": "ok hand sign tone 3",
+ "shortname": ":ok_hand_tone3:",
+ "category": "people",
+ "emoji_order": "1337",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "ok_hand_tone4": {
+ "unicode": "1f44c-1f3fe",
+ "unicode_alternates": "",
+ "name": "ok hand sign tone 4",
+ "shortname": ":ok_hand_tone4:",
+ "category": "people",
+ "emoji_order": "1338",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "ok_hand_tone5": {
+ "unicode": "1f44c-1f3ff",
+ "unicode_alternates": "",
+ "name": "ok hand sign tone 5",
+ "shortname": ":ok_hand_tone5:",
+ "category": "people",
+ "emoji_order": "1339",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "raised_hand_tone1": {
+ "unicode": "270b-1f3fb",
+ "unicode_alternates": "",
+ "name": "raised hand tone 1",
+ "shortname": ":raised_hand_tone1:",
+ "category": "people",
+ "emoji_order": "1340",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "raised_hand_tone2": {
+ "unicode": "270b-1f3fc",
+ "unicode_alternates": "",
+ "name": "raised hand tone 2",
+ "shortname": ":raised_hand_tone2:",
+ "category": "people",
+ "emoji_order": "1341",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "raised_hand_tone3": {
+ "unicode": "270b-1f3fd",
+ "unicode_alternates": "",
+ "name": "raised hand tone 3",
+ "shortname": ":raised_hand_tone3:",
+ "category": "people",
+ "emoji_order": "1342",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "raised_hand_tone4": {
+ "unicode": "270b-1f3fe",
+ "unicode_alternates": "",
+ "name": "raised hand tone 4",
+ "shortname": ":raised_hand_tone4:",
+ "category": "people",
+ "emoji_order": "1343",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "raised_hand_tone5": {
+ "unicode": "270b-1f3ff",
+ "unicode_alternates": "",
+ "name": "raised hand tone 5",
+ "shortname": ":raised_hand_tone5:",
+ "category": "people",
+ "emoji_order": "1344",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "open_hands_tone1": {
+ "unicode": "1f450-1f3fb",
+ "unicode_alternates": "",
+ "name": "open hands sign tone 1",
+ "shortname": ":open_hands_tone1:",
+ "category": "people",
+ "emoji_order": "1345",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "open_hands_tone2": {
+ "unicode": "1f450-1f3fc",
+ "unicode_alternates": "",
+ "name": "open hands sign tone 2",
+ "shortname": ":open_hands_tone2:",
+ "category": "people",
+ "emoji_order": "1346",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "open_hands_tone3": {
+ "unicode": "1f450-1f3fd",
+ "unicode_alternates": "",
+ "name": "open hands sign tone 3",
+ "shortname": ":open_hands_tone3:",
+ "category": "people",
+ "emoji_order": "1347",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "open_hands_tone4": {
+ "unicode": "1f450-1f3fe",
+ "unicode_alternates": "",
+ "name": "open hands sign tone 4",
+ "shortname": ":open_hands_tone4:",
+ "category": "people",
+ "emoji_order": "1348",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "open_hands_tone5": {
+ "unicode": "1f450-1f3ff",
+ "unicode_alternates": "",
+ "name": "open hands sign tone 5",
+ "shortname": ":open_hands_tone5:",
+ "category": "people",
+ "emoji_order": "1349",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "muscle_tone1": {
+ "unicode": "1f4aa-1f3fb",
+ "unicode_alternates": "",
+ "name": "flexed biceps tone 1",
+ "shortname": ":muscle_tone1:",
+ "category": "people",
+ "emoji_order": "1350",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "muscle_tone2": {
+ "unicode": "1f4aa-1f3fc",
+ "unicode_alternates": "",
+ "name": "flexed biceps tone 2",
+ "shortname": ":muscle_tone2:",
+ "category": "people",
+ "emoji_order": "1351",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "muscle_tone3": {
+ "unicode": "1f4aa-1f3fd",
+ "unicode_alternates": "",
+ "name": "flexed biceps tone 3",
+ "shortname": ":muscle_tone3:",
+ "category": "people",
+ "emoji_order": "1352",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "muscle_tone4": {
+ "unicode": "1f4aa-1f3fe",
+ "unicode_alternates": "",
+ "name": "flexed biceps tone 4",
+ "shortname": ":muscle_tone4:",
+ "category": "people",
+ "emoji_order": "1353",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "muscle_tone5": {
+ "unicode": "1f4aa-1f3ff",
+ "unicode_alternates": "",
+ "name": "flexed biceps tone 5",
+ "shortname": ":muscle_tone5:",
+ "category": "people",
+ "emoji_order": "1354",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "pray_tone1": {
+ "unicode": "1f64f-1f3fb",
+ "unicode_alternates": "",
+ "name": "person with folded hands tone 1",
+ "shortname": ":pray_tone1:",
+ "category": "people",
+ "emoji_order": "1355",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "pray_tone2": {
+ "unicode": "1f64f-1f3fc",
+ "unicode_alternates": "",
+ "name": "person with folded hands tone 2",
+ "shortname": ":pray_tone2:",
+ "category": "people",
+ "emoji_order": "1356",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "pray_tone3": {
+ "unicode": "1f64f-1f3fd",
+ "unicode_alternates": "",
+ "name": "person with folded hands tone 3",
+ "shortname": ":pray_tone3:",
+ "category": "people",
+ "emoji_order": "1357",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "pray_tone4": {
+ "unicode": "1f64f-1f3fe",
+ "unicode_alternates": "",
+ "name": "person with folded hands tone 4",
+ "shortname": ":pray_tone4:",
+ "category": "people",
+ "emoji_order": "1358",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "pray_tone5": {
+ "unicode": "1f64f-1f3ff",
+ "unicode_alternates": "",
+ "name": "person with folded hands tone 5",
+ "shortname": ":pray_tone5:",
+ "category": "people",
+ "emoji_order": "1359",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "point_up_tone1": {
+ "unicode": "261d-1f3fb",
+ "unicode_alternates": "",
+ "name": "white up pointing index tone 1",
+ "shortname": ":point_up_tone1:",
+ "category": "people",
+ "emoji_order": "1360",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "point_up_tone2": {
+ "unicode": "261d-1f3fc",
+ "unicode_alternates": "",
+ "name": "white up pointing index tone 2",
+ "shortname": ":point_up_tone2:",
+ "category": "people",
+ "emoji_order": "1361",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "point_up_tone3": {
+ "unicode": "261d-1f3fd",
+ "unicode_alternates": "",
+ "name": "white up pointing index tone 3",
+ "shortname": ":point_up_tone3:",
+ "category": "people",
+ "emoji_order": "1362",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "point_up_tone4": {
+ "unicode": "261d-1f3fe",
+ "unicode_alternates": "",
+ "name": "white up pointing index tone 4",
+ "shortname": ":point_up_tone4:",
+ "category": "people",
+ "emoji_order": "1363",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "point_up_tone5": {
+ "unicode": "261d-1f3ff",
+ "unicode_alternates": "",
+ "name": "white up pointing index tone 5",
+ "shortname": ":point_up_tone5:",
+ "category": "people",
+ "emoji_order": "1364",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "point_up_2_tone1": {
+ "unicode": "1f446-1f3fb",
+ "unicode_alternates": "",
+ "name": "white up pointing backhand index tone 1",
+ "shortname": ":point_up_2_tone1:",
+ "category": "people",
+ "emoji_order": "1365",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "point_up_2_tone2": {
+ "unicode": "1f446-1f3fc",
+ "unicode_alternates": "",
+ "name": "white up pointing backhand index tone 2",
+ "shortname": ":point_up_2_tone2:",
+ "category": "people",
+ "emoji_order": "1366",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "point_up_2_tone3": {
+ "unicode": "1f446-1f3fd",
+ "unicode_alternates": "",
+ "name": "white up pointing backhand index tone 3",
+ "shortname": ":point_up_2_tone3:",
+ "category": "people",
+ "emoji_order": "1367",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "point_up_2_tone4": {
+ "unicode": "1f446-1f3fe",
+ "unicode_alternates": "",
+ "name": "white up pointing backhand index tone 4",
+ "shortname": ":point_up_2_tone4:",
+ "category": "people",
+ "emoji_order": "1368",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "point_up_2_tone5": {
+ "unicode": "1f446-1f3ff",
+ "unicode_alternates": "",
+ "name": "white up pointing backhand index tone 5",
+ "shortname": ":point_up_2_tone5:",
+ "category": "people",
+ "emoji_order": "1369",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "point_down_tone1": {
+ "unicode": "1f447-1f3fb",
+ "unicode_alternates": "",
+ "name": "white down pointing backhand index tone 1",
+ "shortname": ":point_down_tone1:",
+ "category": "people",
+ "emoji_order": "1370",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "point_down_tone2": {
+ "unicode": "1f447-1f3fc",
+ "unicode_alternates": "",
+ "name": "white down pointing backhand index tone 2",
+ "shortname": ":point_down_tone2:",
+ "category": "people",
+ "emoji_order": "1371",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "point_down_tone3": {
+ "unicode": "1f447-1f3fd",
+ "unicode_alternates": "",
+ "name": "white down pointing backhand index tone 3",
+ "shortname": ":point_down_tone3:",
+ "category": "people",
+ "emoji_order": "1372",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "point_down_tone4": {
+ "unicode": "1f447-1f3fe",
+ "unicode_alternates": "",
+ "name": "white down pointing backhand index tone 4",
+ "shortname": ":point_down_tone4:",
+ "category": "people",
+ "emoji_order": "1373",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "point_down_tone5": {
+ "unicode": "1f447-1f3ff",
+ "unicode_alternates": "",
+ "name": "white down pointing backhand index tone 5",
+ "shortname": ":point_down_tone5:",
+ "category": "people",
+ "emoji_order": "1374",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "point_left_tone1": {
+ "unicode": "1f448-1f3fb",
+ "unicode_alternates": "",
+ "name": "white left pointing backhand index tone 1",
+ "shortname": ":point_left_tone1:",
+ "category": "people",
+ "emoji_order": "1375",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "point_left_tone2": {
+ "unicode": "1f448-1f3fc",
+ "unicode_alternates": "",
+ "name": "white left pointing backhand index tone 2",
+ "shortname": ":point_left_tone2:",
+ "category": "people",
+ "emoji_order": "1376",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "point_left_tone3": {
+ "unicode": "1f448-1f3fd",
+ "unicode_alternates": "",
+ "name": "white left pointing backhand index tone 3",
+ "shortname": ":point_left_tone3:",
+ "category": "people",
+ "emoji_order": "1377",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "point_left_tone4": {
+ "unicode": "1f448-1f3fe",
+ "unicode_alternates": "",
+ "name": "white left pointing backhand index tone 4",
+ "shortname": ":point_left_tone4:",
+ "category": "people",
+ "emoji_order": "1378",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "point_left_tone5": {
+ "unicode": "1f448-1f3ff",
+ "unicode_alternates": "",
+ "name": "white left pointing backhand index tone 5",
+ "shortname": ":point_left_tone5:",
+ "category": "people",
+ "emoji_order": "1379",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "point_right_tone1": {
+ "unicode": "1f449-1f3fb",
+ "unicode_alternates": "",
+ "name": "white right pointing backhand index tone 1",
+ "shortname": ":point_right_tone1:",
+ "category": "people",
+ "emoji_order": "1380",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "point_right_tone2": {
+ "unicode": "1f449-1f3fc",
+ "unicode_alternates": "",
+ "name": "white right pointing backhand index tone 2",
+ "shortname": ":point_right_tone2:",
+ "category": "people",
+ "emoji_order": "1381",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "point_right_tone3": {
+ "unicode": "1f449-1f3fd",
+ "unicode_alternates": "",
+ "name": "white right pointing backhand index tone 3",
+ "shortname": ":point_right_tone3:",
+ "category": "people",
+ "emoji_order": "1382",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "point_right_tone4": {
+ "unicode": "1f449-1f3fe",
+ "unicode_alternates": "",
+ "name": "white right pointing backhand index tone 4",
+ "shortname": ":point_right_tone4:",
+ "category": "people",
+ "emoji_order": "1383",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "point_right_tone5": {
+ "unicode": "1f449-1f3ff",
+ "unicode_alternates": "",
+ "name": "white right pointing backhand index tone 5",
+ "shortname": ":point_right_tone5:",
+ "category": "people",
+ "emoji_order": "1384",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "middle_finger_tone1": {
+ "unicode": "1f595-1f3fb",
+ "unicode_alternates": "",
+ "name": "reversed hand with middle finger extended tone 1",
+ "shortname": ":middle_finger_tone1:",
+ "category": "people",
+ "emoji_order": "1385",
+ "aliases": [
+ ":reversed_hand_with_middle_finger_extended_tone1:"
+ ],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "middle_finger_tone2": {
+ "unicode": "1f595-1f3fc",
+ "unicode_alternates": "",
+ "name": "reversed hand with middle finger extended tone 2",
+ "shortname": ":middle_finger_tone2:",
+ "category": "people",
+ "emoji_order": "1386",
+ "aliases": [
+ ":reversed_hand_with_middle_finger_extended_tone2:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "middle_finger_tone3": {
+ "unicode": "1f595-1f3fd",
+ "unicode_alternates": "",
+ "name": "reversed hand with middle finger extended tone 3",
+ "shortname": ":middle_finger_tone3:",
+ "category": "people",
+ "emoji_order": "1387",
+ "aliases": [
+ ":reversed_hand_with_middle_finger_extended_tone3:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "middle_finger_tone4": {
+ "unicode": "1f595-1f3fe",
+ "unicode_alternates": "",
+ "name": "reversed hand with middle finger extended tone 4",
+ "shortname": ":middle_finger_tone4:",
+ "category": "people",
+ "emoji_order": "1388",
+ "aliases": [
+ ":reversed_hand_with_middle_finger_extended_tone4:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "middle_finger_tone5": {
+ "unicode": "1f595-1f3ff",
+ "unicode_alternates": "",
+ "name": "reversed hand with middle finger extended tone 5",
+ "shortname": ":middle_finger_tone5:",
+ "category": "people",
+ "emoji_order": "1389",
+ "aliases": [
+ ":reversed_hand_with_middle_finger_extended_tone5:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "hand_splayed_tone1": {
+ "unicode": "1f590-1f3fb",
+ "unicode_alternates": "",
+ "name": "raised hand with fingers splayed tone 1",
+ "shortname": ":hand_splayed_tone1:",
+ "category": "people",
+ "emoji_order": "1390",
+ "aliases": [
+ ":raised_hand_with_fingers_splayed_tone1:"
+ ],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "hand_splayed_tone2": {
+ "unicode": "1f590-1f3fc",
+ "unicode_alternates": "",
+ "name": "raised hand with fingers splayed tone 2",
+ "shortname": ":hand_splayed_tone2:",
+ "category": "people",
+ "emoji_order": "1391",
+ "aliases": [
+ ":raised_hand_with_fingers_splayed_tone2:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "hand_splayed_tone3": {
+ "unicode": "1f590-1f3fd",
+ "unicode_alternates": "",
+ "name": "raised hand with fingers splayed tone 3",
+ "shortname": ":hand_splayed_tone3:",
+ "category": "people",
+ "emoji_order": "1392",
+ "aliases": [
+ ":raised_hand_with_fingers_splayed_tone3:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "hand_splayed_tone4": {
+ "unicode": "1f590-1f3fe",
+ "unicode_alternates": "",
+ "name": "raised hand with fingers splayed tone 4",
+ "shortname": ":hand_splayed_tone4:",
+ "category": "people",
+ "emoji_order": "1393",
+ "aliases": [
+ ":raised_hand_with_fingers_splayed_tone4:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "hand_splayed_tone5": {
+ "unicode": "1f590-1f3ff",
+ "unicode_alternates": "",
+ "name": "raised hand with fingers splayed tone 5",
+ "shortname": ":hand_splayed_tone5:",
+ "category": "people",
+ "emoji_order": "1394",
+ "aliases": [
+ ":raised_hand_with_fingers_splayed_tone5:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "metal_tone1": {
+ "unicode": "1f918-1f3fb",
+ "unicode_alternates": "",
+ "name": "sign of the horns tone 1",
+ "shortname": ":metal_tone1:",
+ "category": "people",
+ "emoji_order": "1395",
+ "aliases": [
+ ":sign_of_the_horns_tone1:"
+ ],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "metal_tone2": {
+ "unicode": "1f918-1f3fc",
+ "unicode_alternates": "",
+ "name": "sign of the horns tone 2",
+ "shortname": ":metal_tone2:",
+ "category": "people",
+ "emoji_order": "1396",
+ "aliases": [
+ ":sign_of_the_horns_tone2:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "metal_tone3": {
+ "unicode": "1f918-1f3fd",
+ "unicode_alternates": "",
+ "name": "sign of the horns tone 3",
+ "shortname": ":metal_tone3:",
+ "category": "people",
+ "emoji_order": "1397",
+ "aliases": [
+ ":sign_of_the_horns_tone3:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "metal_tone4": {
+ "unicode": "1f918-1f3fe",
+ "unicode_alternates": "",
+ "name": "sign of the horns tone 4",
+ "shortname": ":metal_tone4:",
+ "category": "people",
+ "emoji_order": "1398",
+ "aliases": [
+ ":sign_of_the_horns_tone4:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "metal_tone5": {
+ "unicode": "1f918-1f3ff",
+ "unicode_alternates": "",
+ "name": "sign of the horns tone 5",
+ "shortname": ":metal_tone5:",
+ "category": "people",
+ "emoji_order": "1399",
+ "aliases": [
+ ":sign_of_the_horns_tone5:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "vulcan_tone1": {
+ "unicode": "1f596-1f3fb",
+ "unicode_alternates": "",
+ "name": "raised hand with part between middle and ring fingers tone 1",
+ "shortname": ":vulcan_tone1:",
+ "category": "people",
+ "emoji_order": "1400",
+ "aliases": [
+ ":raised_hand_with_part_between_middle_and_ring_fingers_tone1:"
+ ],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "vulcan_tone2": {
+ "unicode": "1f596-1f3fc",
+ "unicode_alternates": "",
+ "name": "raised hand with part between middle and ring fingers tone 2",
+ "shortname": ":vulcan_tone2:",
+ "category": "people",
+ "emoji_order": "1401",
+ "aliases": [
+ ":raised_hand_with_part_between_middle_and_ring_fingers_tone2:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "vulcan_tone3": {
+ "unicode": "1f596-1f3fd",
+ "unicode_alternates": "",
+ "name": "raised hand with part between middle and ring fingers tone 3",
+ "shortname": ":vulcan_tone3:",
+ "category": "people",
+ "emoji_order": "1402",
+ "aliases": [
+ ":raised_hand_with_part_between_middle_and_ring_fingers_tone3:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "vulcan_tone4": {
+ "unicode": "1f596-1f3fe",
+ "unicode_alternates": "",
+ "name": "raised hand with part between middle and ring fingers tone 4",
+ "shortname": ":vulcan_tone4:",
+ "category": "people",
+ "emoji_order": "1403",
+ "aliases": [
+ ":raised_hand_with_part_between_middle_and_ring_fingers_tone4:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "vulcan_tone5": {
+ "unicode": "1f596-1f3ff",
+ "unicode_alternates": "",
+ "name": "raised hand with part between middle and ring fingers tone 5",
+ "shortname": ":vulcan_tone5:",
+ "category": "people",
+ "emoji_order": "1404",
+ "aliases": [
+ ":raised_hand_with_part_between_middle_and_ring_fingers_tone5:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "writing_hand_tone1": {
+ "unicode": "270d-1f3fb",
+ "unicode_alternates": "",
+ "name": "writing hand tone 1",
+ "shortname": ":writing_hand_tone1:",
+ "category": "people",
+ "emoji_order": "1405",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "writing_hand_tone2": {
+ "unicode": "270d-1f3fc",
+ "unicode_alternates": "",
+ "name": "writing hand tone 2",
+ "shortname": ":writing_hand_tone2:",
+ "category": "people",
+ "emoji_order": "1406",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "writing_hand_tone3": {
+ "unicode": "270d-1f3fd",
+ "unicode_alternates": "",
+ "name": "writing hand tone 3",
+ "shortname": ":writing_hand_tone3:",
+ "category": "people",
+ "emoji_order": "1407",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "writing_hand_tone4": {
+ "unicode": "270d-1f3fe",
+ "unicode_alternates": "",
+ "name": "writing hand tone 4",
+ "shortname": ":writing_hand_tone4:",
+ "category": "people",
+ "emoji_order": "1408",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "writing_hand_tone5": {
+ "unicode": "270d-1f3ff",
+ "unicode_alternates": "",
+ "name": "writing hand tone 5",
+ "shortname": ":writing_hand_tone5:",
+ "category": "people",
+ "emoji_order": "1409",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "nail_care_tone1": {
+ "unicode": "1f485-1f3fb",
+ "unicode_alternates": "",
+ "name": "nail polish tone 1",
+ "shortname": ":nail_care_tone1:",
+ "category": "people",
+ "emoji_order": "1410",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "nail_care_tone2": {
+ "unicode": "1f485-1f3fc",
+ "unicode_alternates": "",
+ "name": "nail polish tone 2",
+ "shortname": ":nail_care_tone2:",
+ "category": "people",
+ "emoji_order": "1411",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "nail_care_tone3": {
+ "unicode": "1f485-1f3fd",
+ "unicode_alternates": "",
+ "name": "nail polish tone 3",
+ "shortname": ":nail_care_tone3:",
+ "category": "people",
+ "emoji_order": "1412",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "nail_care_tone4": {
+ "unicode": "1f485-1f3fe",
+ "unicode_alternates": "",
+ "name": "nail polish tone 4",
+ "shortname": ":nail_care_tone4:",
+ "category": "people",
+ "emoji_order": "1413",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "nail_care_tone5": {
+ "unicode": "1f485-1f3ff",
+ "unicode_alternates": "",
+ "name": "nail polish tone 5",
+ "shortname": ":nail_care_tone5:",
+ "category": "people",
+ "emoji_order": "1414",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "ear_tone1": {
+ "unicode": "1f442-1f3fb",
+ "unicode_alternates": "",
+ "name": "ear tone 1",
+ "shortname": ":ear_tone1:",
+ "category": "people",
+ "emoji_order": "1415",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "ear_tone2": {
+ "unicode": "1f442-1f3fc",
+ "unicode_alternates": "",
+ "name": "ear tone 2",
+ "shortname": ":ear_tone2:",
+ "category": "people",
+ "emoji_order": "1416",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "ear_tone3": {
+ "unicode": "1f442-1f3fd",
+ "unicode_alternates": "",
+ "name": "ear tone 3",
+ "shortname": ":ear_tone3:",
+ "category": "people",
+ "emoji_order": "1417",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "ear_tone4": {
+ "unicode": "1f442-1f3fe",
+ "unicode_alternates": "",
+ "name": "ear tone 4",
+ "shortname": ":ear_tone4:",
+ "category": "people",
+ "emoji_order": "1418",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "ear_tone5": {
+ "unicode": "1f442-1f3ff",
+ "unicode_alternates": "",
+ "name": "ear tone 5",
+ "shortname": ":ear_tone5:",
+ "category": "people",
+ "emoji_order": "1419",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "nose_tone1": {
+ "unicode": "1f443-1f3fb",
+ "unicode_alternates": "",
+ "name": "nose tone 1",
+ "shortname": ":nose_tone1:",
+ "category": "people",
+ "emoji_order": "1420",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "nose_tone2": {
+ "unicode": "1f443-1f3fc",
+ "unicode_alternates": "",
+ "name": "nose tone 2",
+ "shortname": ":nose_tone2:",
+ "category": "people",
+ "emoji_order": "1421",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "nose_tone3": {
+ "unicode": "1f443-1f3fd",
+ "unicode_alternates": "",
+ "name": "nose tone 3",
+ "shortname": ":nose_tone3:",
+ "category": "people",
+ "emoji_order": "1422",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "nose_tone4": {
+ "unicode": "1f443-1f3fe",
+ "unicode_alternates": "",
+ "name": "nose tone 4",
+ "shortname": ":nose_tone4:",
+ "category": "people",
+ "emoji_order": "1423",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "nose_tone5": {
+ "unicode": "1f443-1f3ff",
+ "unicode_alternates": "",
+ "name": "nose tone 5",
+ "shortname": ":nose_tone5:",
+ "category": "people",
+ "emoji_order": "1424",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "baby_tone1": {
+ "unicode": "1f476-1f3fb",
+ "unicode_alternates": "",
+ "name": "baby tone 1",
+ "shortname": ":baby_tone1:",
+ "category": "people",
+ "emoji_order": "1425",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "baby_tone2": {
+ "unicode": "1f476-1f3fc",
+ "unicode_alternates": "",
+ "name": "baby tone 2",
+ "shortname": ":baby_tone2:",
+ "category": "people",
+ "emoji_order": "1426",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "baby_tone3": {
+ "unicode": "1f476-1f3fd",
+ "unicode_alternates": "",
+ "name": "baby tone 3",
+ "shortname": ":baby_tone3:",
+ "category": "people",
+ "emoji_order": "1427",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "baby_tone4": {
+ "unicode": "1f476-1f3fe",
+ "unicode_alternates": "",
+ "name": "baby tone 4",
+ "shortname": ":baby_tone4:",
+ "category": "people",
+ "emoji_order": "1428",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "baby_tone5": {
+ "unicode": "1f476-1f3ff",
+ "unicode_alternates": "",
+ "name": "baby tone 5",
+ "shortname": ":baby_tone5:",
+ "category": "people",
+ "emoji_order": "1429",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "boy_tone1": {
+ "unicode": "1f466-1f3fb",
+ "unicode_alternates": "",
+ "name": "boy tone 1",
+ "shortname": ":boy_tone1:",
+ "category": "people",
+ "emoji_order": "1430",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "boy_tone2": {
+ "unicode": "1f466-1f3fc",
+ "unicode_alternates": "",
+ "name": "boy tone 2",
+ "shortname": ":boy_tone2:",
+ "category": "people",
+ "emoji_order": "1431",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "boy_tone3": {
+ "unicode": "1f466-1f3fd",
+ "unicode_alternates": "",
+ "name": "boy tone 3",
+ "shortname": ":boy_tone3:",
+ "category": "people",
+ "emoji_order": "1432",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "boy_tone4": {
+ "unicode": "1f466-1f3fe",
+ "unicode_alternates": "",
+ "name": "boy tone 4",
+ "shortname": ":boy_tone4:",
+ "category": "people",
+ "emoji_order": "1433",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "boy_tone5": {
+ "unicode": "1f466-1f3ff",
+ "unicode_alternates": "",
+ "name": "boy tone 5",
+ "shortname": ":boy_tone5:",
+ "category": "people",
+ "emoji_order": "1434",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "girl_tone1": {
+ "unicode": "1f467-1f3fb",
+ "unicode_alternates": "",
+ "name": "girl tone 1",
+ "shortname": ":girl_tone1:",
+ "category": "people",
+ "emoji_order": "1435",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "girl_tone2": {
+ "unicode": "1f467-1f3fc",
+ "unicode_alternates": "",
+ "name": "girl tone 2",
+ "shortname": ":girl_tone2:",
+ "category": "people",
+ "emoji_order": "1436",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "girl_tone3": {
+ "unicode": "1f467-1f3fd",
+ "unicode_alternates": "",
+ "name": "girl tone 3",
+ "shortname": ":girl_tone3:",
+ "category": "people",
+ "emoji_order": "1437",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "girl_tone4": {
+ "unicode": "1f467-1f3fe",
+ "unicode_alternates": "",
+ "name": "girl tone 4",
+ "shortname": ":girl_tone4:",
+ "category": "people",
+ "emoji_order": "1438",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "girl_tone5": {
+ "unicode": "1f467-1f3ff",
+ "unicode_alternates": "",
+ "name": "girl tone 5",
+ "shortname": ":girl_tone5:",
+ "category": "people",
+ "emoji_order": "1439",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "man_tone1": {
+ "unicode": "1f468-1f3fb",
+ "unicode_alternates": "",
+ "name": "man tone 1",
+ "shortname": ":man_tone1:",
+ "category": "people",
+ "emoji_order": "1440",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "man_tone2": {
+ "unicode": "1f468-1f3fc",
+ "unicode_alternates": "",
+ "name": "man tone 2",
+ "shortname": ":man_tone2:",
+ "category": "people",
+ "emoji_order": "1441",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "man_tone3": {
+ "unicode": "1f468-1f3fd",
+ "unicode_alternates": "",
+ "name": "man tone 3",
+ "shortname": ":man_tone3:",
+ "category": "people",
+ "emoji_order": "1442",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "man_tone4": {
+ "unicode": "1f468-1f3fe",
+ "unicode_alternates": "",
+ "name": "man tone 4",
+ "shortname": ":man_tone4:",
+ "category": "people",
+ "emoji_order": "1443",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "man_tone5": {
+ "unicode": "1f468-1f3ff",
+ "unicode_alternates": "",
+ "name": "man tone 5",
+ "shortname": ":man_tone5:",
+ "category": "people",
+ "emoji_order": "1444",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "woman_tone1": {
+ "unicode": "1f469-1f3fb",
+ "unicode_alternates": "",
+ "name": "woman tone 1",
+ "shortname": ":woman_tone1:",
+ "category": "people",
+ "emoji_order": "1445",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "woman_tone2": {
+ "unicode": "1f469-1f3fc",
+ "unicode_alternates": "",
+ "name": "woman tone 2",
+ "shortname": ":woman_tone2:",
+ "category": "people",
+ "emoji_order": "1446",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "woman_tone3": {
+ "unicode": "1f469-1f3fd",
+ "unicode_alternates": "",
+ "name": "woman tone 3",
+ "shortname": ":woman_tone3:",
+ "category": "people",
+ "emoji_order": "1447",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "woman_tone4": {
+ "unicode": "1f469-1f3fe",
+ "unicode_alternates": "",
+ "name": "woman tone 4",
+ "shortname": ":woman_tone4:",
+ "category": "people",
+ "emoji_order": "1448",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "woman_tone5": {
+ "unicode": "1f469-1f3ff",
+ "unicode_alternates": "",
+ "name": "woman tone 5",
+ "shortname": ":woman_tone5:",
+ "category": "people",
+ "emoji_order": "1449",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "person_with_blond_hair_tone1": {
+ "unicode": "1f471-1f3fb",
+ "unicode_alternates": "",
+ "name": "person with blond hair tone 1",
+ "shortname": ":person_with_blond_hair_tone1:",
+ "category": "people",
+ "emoji_order": "1450",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "person_with_blond_hair_tone2": {
+ "unicode": "1f471-1f3fc",
+ "unicode_alternates": "",
+ "name": "person with blond hair tone 2",
+ "shortname": ":person_with_blond_hair_tone2:",
+ "category": "people",
+ "emoji_order": "1451",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "person_with_blond_hair_tone3": {
+ "unicode": "1f471-1f3fd",
+ "unicode_alternates": "",
+ "name": "person with blond hair tone 3",
+ "shortname": ":person_with_blond_hair_tone3:",
+ "category": "people",
+ "emoji_order": "1452",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "person_with_blond_hair_tone4": {
+ "unicode": "1f471-1f3fe",
+ "unicode_alternates": "",
+ "name": "person with blond hair tone 4",
+ "shortname": ":person_with_blond_hair_tone4:",
+ "category": "people",
+ "emoji_order": "1453",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "person_with_blond_hair_tone5": {
+ "unicode": "1f471-1f3ff",
+ "unicode_alternates": "",
+ "name": "person with blond hair tone 5",
+ "shortname": ":person_with_blond_hair_tone5:",
+ "category": "people",
+ "emoji_order": "1454",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "older_man_tone1": {
+ "unicode": "1f474-1f3fb",
+ "unicode_alternates": "",
+ "name": "older man tone 1",
+ "shortname": ":older_man_tone1:",
+ "category": "people",
+ "emoji_order": "1455",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "older_man_tone2": {
+ "unicode": "1f474-1f3fc",
+ "unicode_alternates": "",
+ "name": "older man tone 2",
+ "shortname": ":older_man_tone2:",
+ "category": "people",
+ "emoji_order": "1456",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "older_man_tone3": {
+ "unicode": "1f474-1f3fd",
+ "unicode_alternates": "",
+ "name": "older man tone 3",
+ "shortname": ":older_man_tone3:",
+ "category": "people",
+ "emoji_order": "1457",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "older_man_tone4": {
+ "unicode": "1f474-1f3fe",
+ "unicode_alternates": "",
+ "name": "older man tone 4",
+ "shortname": ":older_man_tone4:",
+ "category": "people",
+ "emoji_order": "1458",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "older_man_tone5": {
+ "unicode": "1f474-1f3ff",
+ "unicode_alternates": "",
+ "name": "older man tone 5",
+ "shortname": ":older_man_tone5:",
+ "category": "people",
+ "emoji_order": "1459",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "older_woman_tone1": {
+ "unicode": "1f475-1f3fb",
+ "unicode_alternates": "",
+ "name": "older woman tone 1",
+ "shortname": ":older_woman_tone1:",
+ "category": "people",
+ "emoji_order": "1460",
+ "aliases": [
+ ":grandma_tone1:"
+ ],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "older_woman_tone2": {
+ "unicode": "1f475-1f3fc",
+ "unicode_alternates": "",
+ "name": "older woman tone 2",
+ "shortname": ":older_woman_tone2:",
+ "category": "people",
+ "emoji_order": "1461",
+ "aliases": [
+ ":grandma_tone2:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "older_woman_tone3": {
+ "unicode": "1f475-1f3fd",
+ "unicode_alternates": "",
+ "name": "older woman tone 3",
+ "shortname": ":older_woman_tone3:",
+ "category": "people",
+ "emoji_order": "1462",
+ "aliases": [
+ ":grandma_tone3:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "older_woman_tone4": {
+ "unicode": "1f475-1f3fe",
+ "unicode_alternates": "",
+ "name": "older woman tone 4",
+ "shortname": ":older_woman_tone4:",
+ "category": "people",
+ "emoji_order": "1463",
+ "aliases": [
+ ":grandma_tone4:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "older_woman_tone5": {
+ "unicode": "1f475-1f3ff",
+ "unicode_alternates": "",
+ "name": "older woman tone 5",
+ "shortname": ":older_woman_tone5:",
+ "category": "people",
+ "emoji_order": "1464",
+ "aliases": [
+ ":grandma_tone5:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "man_with_gua_pi_mao_tone1": {
+ "unicode": "1f472-1f3fb",
+ "unicode_alternates": "",
+ "name": "man with gua pi mao tone 1",
+ "shortname": ":man_with_gua_pi_mao_tone1:",
+ "category": "people",
+ "emoji_order": "1465",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "man_with_gua_pi_mao_tone2": {
+ "unicode": "1f472-1f3fc",
+ "unicode_alternates": "",
+ "name": "man with gua pi mao tone 2",
+ "shortname": ":man_with_gua_pi_mao_tone2:",
+ "category": "people",
+ "emoji_order": "1466",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "man_with_gua_pi_mao_tone3": {
+ "unicode": "1f472-1f3fd",
+ "unicode_alternates": "",
+ "name": "man with gua pi mao tone 3",
+ "shortname": ":man_with_gua_pi_mao_tone3:",
+ "category": "people",
+ "emoji_order": "1467",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "man_with_gua_pi_mao_tone4": {
+ "unicode": "1f472-1f3fe",
+ "unicode_alternates": "",
+ "name": "man with gua pi mao tone 4",
+ "shortname": ":man_with_gua_pi_mao_tone4:",
+ "category": "people",
+ "emoji_order": "1468",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "man_with_gua_pi_mao_tone5": {
+ "unicode": "1f472-1f3ff",
+ "unicode_alternates": "",
+ "name": "man with gua pi mao tone 5",
+ "shortname": ":man_with_gua_pi_mao_tone5:",
+ "category": "people",
+ "emoji_order": "1469",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "man_with_turban_tone1": {
+ "unicode": "1f473-1f3fb",
+ "unicode_alternates": "",
+ "name": "man with turban tone 1",
+ "shortname": ":man_with_turban_tone1:",
+ "category": "people",
+ "emoji_order": "1470",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "man_with_turban_tone2": {
+ "unicode": "1f473-1f3fc",
+ "unicode_alternates": "",
+ "name": "man with turban tone 2",
+ "shortname": ":man_with_turban_tone2:",
+ "category": "people",
+ "emoji_order": "1471",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "man_with_turban_tone3": {
+ "unicode": "1f473-1f3fd",
+ "unicode_alternates": "",
+ "name": "man with turban tone 3",
+ "shortname": ":man_with_turban_tone3:",
+ "category": "people",
+ "emoji_order": "1472",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "man_with_turban_tone4": {
+ "unicode": "1f473-1f3fe",
+ "unicode_alternates": "",
+ "name": "man with turban tone 4",
+ "shortname": ":man_with_turban_tone4:",
+ "category": "people",
+ "emoji_order": "1473",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "man_with_turban_tone5": {
+ "unicode": "1f473-1f3ff",
+ "unicode_alternates": "",
+ "name": "man with turban tone 5",
+ "shortname": ":man_with_turban_tone5:",
+ "category": "people",
+ "emoji_order": "1474",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "cop_tone1": {
+ "unicode": "1f46e-1f3fb",
+ "unicode_alternates": "",
+ "name": "police officer tone 1",
+ "shortname": ":cop_tone1:",
+ "category": "people",
+ "emoji_order": "1475",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "cop_tone2": {
+ "unicode": "1f46e-1f3fc",
+ "unicode_alternates": "",
+ "name": "police officer tone 2",
+ "shortname": ":cop_tone2:",
+ "category": "people",
+ "emoji_order": "1476",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "cop_tone3": {
+ "unicode": "1f46e-1f3fd",
+ "unicode_alternates": "",
+ "name": "police officer tone 3",
+ "shortname": ":cop_tone3:",
+ "category": "people",
+ "emoji_order": "1477",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "cop_tone4": {
+ "unicode": "1f46e-1f3fe",
+ "unicode_alternates": "",
+ "name": "police officer tone 4",
+ "shortname": ":cop_tone4:",
+ "category": "people",
+ "emoji_order": "1478",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "cop_tone5": {
+ "unicode": "1f46e-1f3ff",
+ "unicode_alternates": "",
+ "name": "police officer tone 5",
+ "shortname": ":cop_tone5:",
+ "category": "people",
+ "emoji_order": "1479",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "construction_worker_tone1": {
+ "unicode": "1f477-1f3fb",
+ "unicode_alternates": "",
+ "name": "construction worker tone 1",
+ "shortname": ":construction_worker_tone1:",
+ "category": "people",
+ "emoji_order": "1480",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "construction_worker_tone2": {
+ "unicode": "1f477-1f3fc",
+ "unicode_alternates": "",
+ "name": "construction worker tone 2",
+ "shortname": ":construction_worker_tone2:",
+ "category": "people",
+ "emoji_order": "1481",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "construction_worker_tone3": {
+ "unicode": "1f477-1f3fd",
+ "unicode_alternates": "",
+ "name": "construction worker tone 3",
+ "shortname": ":construction_worker_tone3:",
+ "category": "people",
+ "emoji_order": "1482",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "construction_worker_tone4": {
+ "unicode": "1f477-1f3fe",
+ "unicode_alternates": "",
+ "name": "construction worker tone 4",
+ "shortname": ":construction_worker_tone4:",
+ "category": "people",
+ "emoji_order": "1483",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "construction_worker_tone5": {
+ "unicode": "1f477-1f3ff",
+ "unicode_alternates": "",
+ "name": "construction worker tone 5",
+ "shortname": ":construction_worker_tone5:",
+ "category": "people",
+ "emoji_order": "1484",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "guardsman_tone1": {
+ "unicode": "1f482-1f3fb",
+ "unicode_alternates": "",
+ "name": "guardsman tone 1",
+ "shortname": ":guardsman_tone1:",
+ "category": "people",
+ "emoji_order": "1485",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "guardsman_tone2": {
+ "unicode": "1f482-1f3fc",
+ "unicode_alternates": "",
+ "name": "guardsman tone 2",
+ "shortname": ":guardsman_tone2:",
+ "category": "people",
+ "emoji_order": "1486",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "guardsman_tone3": {
+ "unicode": "1f482-1f3fd",
+ "unicode_alternates": "",
+ "name": "guardsman tone 3",
+ "shortname": ":guardsman_tone3:",
+ "category": "people",
+ "emoji_order": "1487",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "guardsman_tone4": {
+ "unicode": "1f482-1f3fe",
+ "unicode_alternates": "",
+ "name": "guardsman tone 4",
+ "shortname": ":guardsman_tone4:",
+ "category": "people",
+ "emoji_order": "1488",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "guardsman_tone5": {
+ "unicode": "1f482-1f3ff",
+ "unicode_alternates": "",
+ "name": "guardsman tone 5",
+ "shortname": ":guardsman_tone5:",
+ "category": "people",
+ "emoji_order": "1489",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "santa_tone1": {
+ "unicode": "1f385-1f3fb",
+ "unicode_alternates": "",
+ "name": "father christmas tone 1",
+ "shortname": ":santa_tone1:",
+ "category": "people",
+ "emoji_order": "1490",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "santa_tone2": {
+ "unicode": "1f385-1f3fc",
+ "unicode_alternates": "",
+ "name": "father christmas tone 2",
+ "shortname": ":santa_tone2:",
+ "category": "people",
+ "emoji_order": "1491",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "santa_tone3": {
+ "unicode": "1f385-1f3fd",
+ "unicode_alternates": "",
+ "name": "father christmas tone 3",
+ "shortname": ":santa_tone3:",
+ "category": "people",
+ "emoji_order": "1492",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "santa_tone4": {
+ "unicode": "1f385-1f3fe",
+ "unicode_alternates": "",
+ "name": "father christmas tone 4",
+ "shortname": ":santa_tone4:",
+ "category": "people",
+ "emoji_order": "1493",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "santa_tone5": {
+ "unicode": "1f385-1f3ff",
+ "unicode_alternates": "",
+ "name": "father christmas tone 5",
+ "shortname": ":santa_tone5:",
+ "category": "people",
+ "emoji_order": "1494",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "angel_tone1": {
+ "unicode": "1f47c-1f3fb",
+ "unicode_alternates": "",
+ "name": "baby angel tone 1",
+ "shortname": ":angel_tone1:",
+ "category": "people",
+ "emoji_order": "1495",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "angel_tone2": {
+ "unicode": "1f47c-1f3fc",
+ "unicode_alternates": "",
+ "name": "baby angel tone 2",
+ "shortname": ":angel_tone2:",
+ "category": "people",
+ "emoji_order": "1496",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "angel_tone3": {
+ "unicode": "1f47c-1f3fd",
+ "unicode_alternates": "",
+ "name": "baby angel tone 3",
+ "shortname": ":angel_tone3:",
+ "category": "people",
+ "emoji_order": "1497",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "angel_tone4": {
+ "unicode": "1f47c-1f3fe",
+ "unicode_alternates": "",
+ "name": "baby angel tone 4",
+ "shortname": ":angel_tone4:",
+ "category": "people",
+ "emoji_order": "1498",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "angel_tone5": {
+ "unicode": "1f47c-1f3ff",
+ "unicode_alternates": "",
+ "name": "baby angel tone 5",
+ "shortname": ":angel_tone5:",
+ "category": "people",
+ "emoji_order": "1499",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "princess_tone1": {
+ "unicode": "1f478-1f3fb",
+ "unicode_alternates": "",
+ "name": "princess tone 1",
+ "shortname": ":princess_tone1:",
+ "category": "people",
+ "emoji_order": "1500",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "princess_tone2": {
+ "unicode": "1f478-1f3fc",
+ "unicode_alternates": "",
+ "name": "princess tone 2",
+ "shortname": ":princess_tone2:",
+ "category": "people",
+ "emoji_order": "1501",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "princess_tone3": {
+ "unicode": "1f478-1f3fd",
+ "unicode_alternates": "",
+ "name": "princess tone 3",
+ "shortname": ":princess_tone3:",
+ "category": "people",
+ "emoji_order": "1502",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "princess_tone4": {
+ "unicode": "1f478-1f3fe",
+ "unicode_alternates": "",
+ "name": "princess tone 4",
+ "shortname": ":princess_tone4:",
+ "category": "people",
+ "emoji_order": "1503",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "princess_tone5": {
+ "unicode": "1f478-1f3ff",
+ "unicode_alternates": "",
+ "name": "princess tone 5",
+ "shortname": ":princess_tone5:",
+ "category": "people",
+ "emoji_order": "1504",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "bride_with_veil_tone1": {
+ "unicode": "1f470-1f3fb",
+ "unicode_alternates": "",
+ "name": "bride with veil tone 1",
+ "shortname": ":bride_with_veil_tone1:",
+ "category": "people",
+ "emoji_order": "1505",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "bride_with_veil_tone2": {
+ "unicode": "1f470-1f3fc",
+ "unicode_alternates": "",
+ "name": "bride with veil tone 2",
+ "shortname": ":bride_with_veil_tone2:",
+ "category": "people",
+ "emoji_order": "1506",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "bride_with_veil_tone3": {
+ "unicode": "1f470-1f3fd",
+ "unicode_alternates": "",
+ "name": "bride with veil tone 3",
+ "shortname": ":bride_with_veil_tone3:",
+ "category": "people",
+ "emoji_order": "1507",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "bride_with_veil_tone4": {
+ "unicode": "1f470-1f3fe",
+ "unicode_alternates": "",
+ "name": "bride with veil tone 4",
+ "shortname": ":bride_with_veil_tone4:",
+ "category": "people",
+ "emoji_order": "1508",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "bride_with_veil_tone5": {
+ "unicode": "1f470-1f3ff",
+ "unicode_alternates": "",
+ "name": "bride with veil tone 5",
+ "shortname": ":bride_with_veil_tone5:",
+ "category": "people",
+ "emoji_order": "1509",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "walking_tone1": {
+ "unicode": "1f6b6-1f3fb",
+ "unicode_alternates": "",
+ "name": "pedestrian tone 1",
+ "shortname": ":walking_tone1:",
+ "category": "people",
+ "emoji_order": "1510",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "walking_tone2": {
+ "unicode": "1f6b6-1f3fc",
+ "unicode_alternates": "",
+ "name": "pedestrian tone 2",
+ "shortname": ":walking_tone2:",
+ "category": "people",
+ "emoji_order": "1511",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "walking_tone3": {
+ "unicode": "1f6b6-1f3fd",
+ "unicode_alternates": "",
+ "name": "pedestrian tone 3",
+ "shortname": ":walking_tone3:",
+ "category": "people",
+ "emoji_order": "1512",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "walking_tone4": {
+ "unicode": "1f6b6-1f3fe",
+ "unicode_alternates": "",
+ "name": "pedestrian tone 4",
+ "shortname": ":walking_tone4:",
+ "category": "people",
+ "emoji_order": "1513",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "walking_tone5": {
+ "unicode": "1f6b6-1f3ff",
+ "unicode_alternates": "",
+ "name": "pedestrian tone 5",
+ "shortname": ":walking_tone5:",
+ "category": "people",
+ "emoji_order": "1514",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "runner_tone1": {
+ "unicode": "1f3c3-1f3fb",
+ "unicode_alternates": "",
+ "name": "runner tone 1",
+ "shortname": ":runner_tone1:",
+ "category": "people",
+ "emoji_order": "1515",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "runner_tone2": {
+ "unicode": "1f3c3-1f3fc",
+ "unicode_alternates": "",
+ "name": "runner tone 2",
+ "shortname": ":runner_tone2:",
+ "category": "people",
+ "emoji_order": "1516",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "runner_tone3": {
+ "unicode": "1f3c3-1f3fd",
+ "unicode_alternates": "",
+ "name": "runner tone 3",
+ "shortname": ":runner_tone3:",
+ "category": "people",
+ "emoji_order": "1517",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "runner_tone4": {
+ "unicode": "1f3c3-1f3fe",
+ "unicode_alternates": "",
+ "name": "runner tone 4",
+ "shortname": ":runner_tone4:",
+ "category": "people",
+ "emoji_order": "1518",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "runner_tone5": {
+ "unicode": "1f3c3-1f3ff",
+ "unicode_alternates": "",
+ "name": "runner tone 5",
+ "shortname": ":runner_tone5:",
+ "category": "people",
+ "emoji_order": "1519",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "dancer_tone1": {
+ "unicode": "1f483-1f3fb",
+ "unicode_alternates": "",
+ "name": "dancer tone 1",
+ "shortname": ":dancer_tone1:",
+ "category": "people",
+ "emoji_order": "1520",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "dancer_tone2": {
+ "unicode": "1f483-1f3fc",
+ "unicode_alternates": "",
+ "name": "dancer tone 2",
+ "shortname": ":dancer_tone2:",
+ "category": "people",
+ "emoji_order": "1521",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "dancer_tone3": {
+ "unicode": "1f483-1f3fd",
+ "unicode_alternates": "",
+ "name": "dancer tone 3",
+ "shortname": ":dancer_tone3:",
+ "category": "people",
+ "emoji_order": "1522",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "dancer_tone4": {
+ "unicode": "1f483-1f3fe",
+ "unicode_alternates": "",
+ "name": "dancer tone 4",
+ "shortname": ":dancer_tone4:",
+ "category": "people",
+ "emoji_order": "1523",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "dancer_tone5": {
+ "unicode": "1f483-1f3ff",
+ "unicode_alternates": "",
+ "name": "dancer tone 5",
+ "shortname": ":dancer_tone5:",
+ "category": "people",
+ "emoji_order": "1524",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "bow_tone1": {
+ "unicode": "1f647-1f3fb",
+ "unicode_alternates": "",
+ "name": "person bowing deeply tone 1",
+ "shortname": ":bow_tone1:",
+ "category": "people",
+ "emoji_order": "1525",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "bow_tone2": {
+ "unicode": "1f647-1f3fc",
+ "unicode_alternates": "",
+ "name": "person bowing deeply tone 2",
+ "shortname": ":bow_tone2:",
+ "category": "people",
+ "emoji_order": "1526",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "bow_tone3": {
+ "unicode": "1f647-1f3fd",
+ "unicode_alternates": "",
+ "name": "person bowing deeply tone 3",
+ "shortname": ":bow_tone3:",
+ "category": "people",
+ "emoji_order": "1527",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "bow_tone4": {
+ "unicode": "1f647-1f3fe",
+ "unicode_alternates": "",
+ "name": "person bowing deeply tone 4",
+ "shortname": ":bow_tone4:",
+ "category": "people",
+ "emoji_order": "1528",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "bow_tone5": {
+ "unicode": "1f647-1f3ff",
+ "unicode_alternates": "",
+ "name": "person bowing deeply tone 5",
+ "shortname": ":bow_tone5:",
+ "category": "people",
+ "emoji_order": "1529",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "information_desk_person_tone1": {
+ "unicode": "1f481-1f3fb",
+ "unicode_alternates": "",
+ "name": "information desk person tone 1",
+ "shortname": ":information_desk_person_tone1:",
+ "category": "people",
+ "emoji_order": "1530",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "information_desk_person_tone2": {
+ "unicode": "1f481-1f3fc",
+ "unicode_alternates": "",
+ "name": "information desk person tone 2",
+ "shortname": ":information_desk_person_tone2:",
+ "category": "people",
+ "emoji_order": "1531",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "information_desk_person_tone3": {
+ "unicode": "1f481-1f3fd",
+ "unicode_alternates": "",
+ "name": "information desk person tone 3",
+ "shortname": ":information_desk_person_tone3:",
+ "category": "people",
+ "emoji_order": "1532",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "information_desk_person_tone4": {
+ "unicode": "1f481-1f3fe",
+ "unicode_alternates": "",
+ "name": "information desk person tone 4",
+ "shortname": ":information_desk_person_tone4:",
+ "category": "people",
+ "emoji_order": "1533",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "information_desk_person_tone5": {
+ "unicode": "1f481-1f3ff",
+ "unicode_alternates": "",
+ "name": "information desk person tone 5",
+ "shortname": ":information_desk_person_tone5:",
+ "category": "people",
+ "emoji_order": "1534",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "no_good_tone1": {
+ "unicode": "1f645-1f3fb",
+ "unicode_alternates": "",
+ "name": "face with no good gesture tone 1",
+ "shortname": ":no_good_tone1:",
+ "category": "people",
+ "emoji_order": "1535",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "no_good_tone2": {
+ "unicode": "1f645-1f3fc",
+ "unicode_alternates": "",
+ "name": "face with no good gesture tone 2",
+ "shortname": ":no_good_tone2:",
+ "category": "people",
+ "emoji_order": "1536",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "no_good_tone3": {
+ "unicode": "1f645-1f3fd",
+ "unicode_alternates": "",
+ "name": "face with no good gesture tone 3",
+ "shortname": ":no_good_tone3:",
+ "category": "people",
+ "emoji_order": "1537",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "no_good_tone4": {
+ "unicode": "1f645-1f3fe",
+ "unicode_alternates": "",
+ "name": "face with no good gesture tone 4",
+ "shortname": ":no_good_tone4:",
+ "category": "people",
+ "emoji_order": "1538",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "no_good_tone5": {
+ "unicode": "1f645-1f3ff",
+ "unicode_alternates": "",
+ "name": "face with no good gesture tone 5",
+ "shortname": ":no_good_tone5:",
+ "category": "people",
+ "emoji_order": "1539",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "ok_woman_tone1": {
+ "unicode": "1f646-1f3fb",
+ "unicode_alternates": "",
+ "name": "face with ok gesture tone1",
+ "shortname": ":ok_woman_tone1:",
+ "category": "people",
+ "emoji_order": "1540",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "ok_woman_tone2": {
+ "unicode": "1f646-1f3fc",
+ "unicode_alternates": "",
+ "name": "face with ok gesture tone2",
+ "shortname": ":ok_woman_tone2:",
+ "category": "people",
+ "emoji_order": "1541",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "ok_woman_tone3": {
+ "unicode": "1f646-1f3fd",
+ "unicode_alternates": "",
+ "name": "face with ok gesture tone3",
+ "shortname": ":ok_woman_tone3:",
+ "category": "people",
+ "emoji_order": "1542",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "ok_woman_tone4": {
+ "unicode": "1f646-1f3fe",
+ "unicode_alternates": "",
+ "name": "face with ok gesture tone4",
+ "shortname": ":ok_woman_tone4:",
+ "category": "people",
+ "emoji_order": "1543",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "ok_woman_tone5": {
+ "unicode": "1f646-1f3ff",
+ "unicode_alternates": "",
+ "name": "face with ok gesture tone5",
+ "shortname": ":ok_woman_tone5:",
+ "category": "people",
+ "emoji_order": "1544",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "raising_hand_tone1": {
+ "unicode": "1f64b-1f3fb",
+ "unicode_alternates": "",
+ "name": "happy person raising one hand tone1",
+ "shortname": ":raising_hand_tone1:",
+ "category": "people",
+ "emoji_order": "1545",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "raising_hand_tone2": {
+ "unicode": "1f64b-1f3fc",
+ "unicode_alternates": "",
+ "name": "happy person raising one hand tone2",
+ "shortname": ":raising_hand_tone2:",
+ "category": "people",
+ "emoji_order": "1546",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "raising_hand_tone3": {
+ "unicode": "1f64b-1f3fd",
+ "unicode_alternates": "",
+ "name": "happy person raising one hand tone3",
+ "shortname": ":raising_hand_tone3:",
+ "category": "people",
+ "emoji_order": "1547",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "raising_hand_tone4": {
+ "unicode": "1f64b-1f3fe",
+ "unicode_alternates": "",
+ "name": "happy person raising one hand tone4",
+ "shortname": ":raising_hand_tone4:",
+ "category": "people",
+ "emoji_order": "1548",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "raising_hand_tone5": {
+ "unicode": "1f64b-1f3ff",
+ "unicode_alternates": "",
+ "name": "happy person raising one hand tone5",
+ "shortname": ":raising_hand_tone5:",
+ "category": "people",
+ "emoji_order": "1549",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "person_with_pouting_face_tone1": {
+ "unicode": "1f64e-1f3fb",
+ "unicode_alternates": "",
+ "name": "person with pouting face tone1",
+ "shortname": ":person_with_pouting_face_tone1:",
+ "category": "people",
+ "emoji_order": "1550",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "person_with_pouting_face_tone2": {
+ "unicode": "1f64e-1f3fc",
+ "unicode_alternates": "",
+ "name": "person with pouting face tone2",
+ "shortname": ":person_with_pouting_face_tone2:",
+ "category": "people",
+ "emoji_order": "1551",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "person_with_pouting_face_tone3": {
+ "unicode": "1f64e-1f3fd",
+ "unicode_alternates": "",
+ "name": "person with pouting face tone3",
+ "shortname": ":person_with_pouting_face_tone3:",
+ "category": "people",
+ "emoji_order": "1552",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "person_with_pouting_face_tone4": {
+ "unicode": "1f64e-1f3fe",
+ "unicode_alternates": "",
+ "name": "person with pouting face tone4",
+ "shortname": ":person_with_pouting_face_tone4:",
+ "category": "people",
+ "emoji_order": "1553",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "person_with_pouting_face_tone5": {
+ "unicode": "1f64e-1f3ff",
+ "unicode_alternates": "",
+ "name": "person with pouting face tone5",
+ "shortname": ":person_with_pouting_face_tone5:",
+ "category": "people",
+ "emoji_order": "1554",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "person_frowning_tone1": {
+ "unicode": "1f64d-1f3fb",
+ "unicode_alternates": "",
+ "name": "person frowning tone 1",
+ "shortname": ":person_frowning_tone1:",
+ "category": "people",
+ "emoji_order": "1555",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "person_frowning_tone2": {
+ "unicode": "1f64d-1f3fc",
+ "unicode_alternates": "",
+ "name": "person frowning tone 2",
+ "shortname": ":person_frowning_tone2:",
+ "category": "people",
+ "emoji_order": "1556",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "person_frowning_tone3": {
+ "unicode": "1f64d-1f3fd",
+ "unicode_alternates": "",
+ "name": "person frowning tone 3",
+ "shortname": ":person_frowning_tone3:",
+ "category": "people",
+ "emoji_order": "1557",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "person_frowning_tone4": {
+ "unicode": "1f64d-1f3fe",
+ "unicode_alternates": "",
+ "name": "person frowning tone 4",
+ "shortname": ":person_frowning_tone4:",
+ "category": "people",
+ "emoji_order": "1558",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "person_frowning_tone5": {
+ "unicode": "1f64d-1f3ff",
+ "unicode_alternates": "",
+ "name": "person frowning tone 5",
+ "shortname": ":person_frowning_tone5:",
+ "category": "people",
+ "emoji_order": "1559",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "haircut_tone1": {
+ "unicode": "1f487-1f3fb",
+ "unicode_alternates": "",
+ "name": "haircut tone 1",
+ "shortname": ":haircut_tone1:",
+ "category": "people",
+ "emoji_order": "1560",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "haircut_tone2": {
+ "unicode": "1f487-1f3fc",
+ "unicode_alternates": "",
+ "name": "haircut tone 2",
+ "shortname": ":haircut_tone2:",
+ "category": "people",
+ "emoji_order": "1561",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "haircut_tone3": {
+ "unicode": "1f487-1f3fd",
+ "unicode_alternates": "",
+ "name": "haircut tone 3",
+ "shortname": ":haircut_tone3:",
+ "category": "people",
+ "emoji_order": "1562",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "haircut_tone4": {
+ "unicode": "1f487-1f3fe",
+ "unicode_alternates": "",
+ "name": "haircut tone 4",
+ "shortname": ":haircut_tone4:",
+ "category": "people",
+ "emoji_order": "1563",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "haircut_tone5": {
+ "unicode": "1f487-1f3ff",
+ "unicode_alternates": "",
+ "name": "haircut tone 5",
+ "shortname": ":haircut_tone5:",
+ "category": "people",
+ "emoji_order": "1564",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "massage_tone1": {
+ "unicode": "1f486-1f3fb",
+ "unicode_alternates": "",
+ "name": "face massage tone 1",
+ "shortname": ":massage_tone1:",
+ "category": "people",
+ "emoji_order": "1565",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "massage_tone2": {
+ "unicode": "1f486-1f3fc",
+ "unicode_alternates": "",
+ "name": "face massage tone 2",
+ "shortname": ":massage_tone2:",
+ "category": "people",
+ "emoji_order": "1566",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "massage_tone3": {
+ "unicode": "1f486-1f3fd",
+ "unicode_alternates": "",
+ "name": "face massage tone 3",
+ "shortname": ":massage_tone3:",
+ "category": "people",
+ "emoji_order": "1567",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "massage_tone4": {
+ "unicode": "1f486-1f3fe",
+ "unicode_alternates": "",
+ "name": "face massage tone 4",
+ "shortname": ":massage_tone4:",
+ "category": "people",
+ "emoji_order": "1568",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "massage_tone5": {
+ "unicode": "1f486-1f3ff",
+ "unicode_alternates": "",
+ "name": "face massage tone 5",
+ "shortname": ":massage_tone5:",
+ "category": "people",
+ "emoji_order": "1569",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "rowboat_tone1": {
+ "unicode": "1f6a3-1f3fb",
+ "unicode_alternates": "",
+ "name": "rowboat tone 1",
+ "shortname": ":rowboat_tone1:",
+ "category": "activity",
+ "emoji_order": "1570",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "rowboat_tone2": {
+ "unicode": "1f6a3-1f3fc",
+ "unicode_alternates": "",
+ "name": "rowboat tone 2",
+ "shortname": ":rowboat_tone2:",
+ "category": "activity",
+ "emoji_order": "1571",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "rowboat_tone3": {
+ "unicode": "1f6a3-1f3fd",
+ "unicode_alternates": "",
+ "name": "rowboat tone 3",
+ "shortname": ":rowboat_tone3:",
+ "category": "activity",
+ "emoji_order": "1572",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "rowboat_tone4": {
+ "unicode": "1f6a3-1f3fe",
+ "unicode_alternates": "",
+ "name": "rowboat tone 4",
+ "shortname": ":rowboat_tone4:",
+ "category": "activity",
+ "emoji_order": "1573",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "rowboat_tone5": {
+ "unicode": "1f6a3-1f3ff",
+ "unicode_alternates": "",
+ "name": "rowboat tone 5",
+ "shortname": ":rowboat_tone5:",
+ "category": "activity",
+ "emoji_order": "1574",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "swimmer_tone1": {
+ "unicode": "1f3ca-1f3fb",
+ "unicode_alternates": "",
+ "name": "swimmer tone 1",
+ "shortname": ":swimmer_tone1:",
+ "category": "activity",
+ "emoji_order": "1575",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "swimmer_tone2": {
+ "unicode": "1f3ca-1f3fc",
+ "unicode_alternates": "",
+ "name": "swimmer tone 2",
+ "shortname": ":swimmer_tone2:",
+ "category": "activity",
+ "emoji_order": "1576",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "swimmer_tone3": {
+ "unicode": "1f3ca-1f3fd",
+ "unicode_alternates": "",
+ "name": "swimmer tone 3",
+ "shortname": ":swimmer_tone3:",
+ "category": "activity",
+ "emoji_order": "1577",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "swimmer_tone4": {
+ "unicode": "1f3ca-1f3fe",
+ "unicode_alternates": "",
+ "name": "swimmer tone 4",
+ "shortname": ":swimmer_tone4:",
+ "category": "activity",
+ "emoji_order": "1578",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "swimmer_tone5": {
+ "unicode": "1f3ca-1f3ff",
+ "unicode_alternates": "",
+ "name": "swimmer tone 5",
+ "shortname": ":swimmer_tone5:",
+ "category": "activity",
+ "emoji_order": "1579",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "surfer_tone1": {
+ "unicode": "1f3c4-1f3fb",
+ "unicode_alternates": "",
+ "name": "surfer tone 1",
+ "shortname": ":surfer_tone1:",
+ "category": "activity",
+ "emoji_order": "1580",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "surfer_tone2": {
+ "unicode": "1f3c4-1f3fc",
+ "unicode_alternates": "",
+ "name": "surfer tone 2",
+ "shortname": ":surfer_tone2:",
+ "category": "activity",
+ "emoji_order": "1581",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "surfer_tone3": {
+ "unicode": "1f3c4-1f3fd",
+ "unicode_alternates": "",
+ "name": "surfer tone 3",
+ "shortname": ":surfer_tone3:",
+ "category": "activity",
+ "emoji_order": "1582",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "surfer_tone4": {
+ "unicode": "1f3c4-1f3fe",
+ "unicode_alternates": "",
+ "name": "surfer tone 4",
+ "shortname": ":surfer_tone4:",
+ "category": "activity",
+ "emoji_order": "1583",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "surfer_tone5": {
+ "unicode": "1f3c4-1f3ff",
+ "unicode_alternates": "",
+ "name": "surfer tone 5",
+ "shortname": ":surfer_tone5:",
+ "category": "activity",
+ "emoji_order": "1584",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "bath_tone1": {
+ "unicode": "1f6c0-1f3fb",
+ "unicode_alternates": "",
+ "name": "bath tone 1",
+ "shortname": ":bath_tone1:",
+ "category": "activity",
+ "emoji_order": "1585",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "bath_tone2": {
+ "unicode": "1f6c0-1f3fc",
+ "unicode_alternates": "",
+ "name": "bath tone 2",
+ "shortname": ":bath_tone2:",
+ "category": "activity",
+ "emoji_order": "1586",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "bath_tone3": {
+ "unicode": "1f6c0-1f3fd",
+ "unicode_alternates": "",
+ "name": "bath tone 3",
+ "shortname": ":bath_tone3:",
+ "category": "activity",
+ "emoji_order": "1587",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "bath_tone4": {
+ "unicode": "1f6c0-1f3fe",
+ "unicode_alternates": "",
+ "name": "bath tone 4",
+ "shortname": ":bath_tone4:",
+ "category": "activity",
+ "emoji_order": "1588",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "bath_tone5": {
+ "unicode": "1f6c0-1f3ff",
+ "unicode_alternates": "",
+ "name": "bath tone 5",
+ "shortname": ":bath_tone5:",
+ "category": "activity",
+ "emoji_order": "1589",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "basketball_player_tone1": {
+ "unicode": "26f9-1f3fb",
+ "unicode_alternates": "",
+ "name": "person with ball tone 1",
+ "shortname": ":basketball_player_tone1:",
+ "category": "activity",
+ "emoji_order": "1590",
+ "aliases": [
+ ":person_with_ball_tone1:"
+ ],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "basketball_player_tone2": {
+ "unicode": "26f9-1f3fc",
+ "unicode_alternates": "",
+ "name": "person with ball tone 2",
+ "shortname": ":basketball_player_tone2:",
+ "category": "activity",
+ "emoji_order": "1591",
+ "aliases": [
+ ":person_with_ball_tone2:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "basketball_player_tone3": {
+ "unicode": "26f9-1f3fd",
+ "unicode_alternates": "",
+ "name": "person with ball tone 3",
+ "shortname": ":basketball_player_tone3:",
+ "category": "activity",
+ "emoji_order": "1592",
+ "aliases": [
+ ":person_with_ball_tone3:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "basketball_player_tone4": {
+ "unicode": "26f9-1f3fe",
+ "unicode_alternates": "",
+ "name": "person with ball tone 4",
+ "shortname": ":basketball_player_tone4:",
+ "category": "activity",
+ "emoji_order": "1593",
+ "aliases": [
+ ":person_with_ball_tone4:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "basketball_player_tone5": {
+ "unicode": "26f9-1f3ff",
+ "unicode_alternates": "",
+ "name": "person with ball tone 5",
+ "shortname": ":basketball_player_tone5:",
+ "category": "activity",
+ "emoji_order": "1594",
+ "aliases": [
+ ":person_with_ball_tone5:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "lifter_tone1": {
+ "unicode": "1f3cb-1f3fb",
+ "unicode_alternates": "",
+ "name": "weight lifter tone 1",
+ "shortname": ":lifter_tone1:",
+ "category": "activity",
+ "emoji_order": "1595",
+ "aliases": [
+ ":weight_lifter_tone1:"
+ ],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "lifter_tone2": {
+ "unicode": "1f3cb-1f3fc",
+ "unicode_alternates": "",
+ "name": "weight lifter tone 2",
+ "shortname": ":lifter_tone2:",
+ "category": "activity",
+ "emoji_order": "1596",
+ "aliases": [
+ ":weight_lifter_tone2:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "lifter_tone3": {
+ "unicode": "1f3cb-1f3fd",
+ "unicode_alternates": "",
+ "name": "weight lifter tone 3",
+ "shortname": ":lifter_tone3:",
+ "category": "activity",
+ "emoji_order": "1597",
+ "aliases": [
+ ":weight_lifter_tone3:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "lifter_tone4": {
+ "unicode": "1f3cb-1f3fe",
+ "unicode_alternates": "",
+ "name": "weight lifter tone 4",
+ "shortname": ":lifter_tone4:",
+ "category": "activity",
+ "emoji_order": "1598",
+ "aliases": [
+ ":weight_lifter_tone4:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "lifter_tone5": {
+ "unicode": "1f3cb-1f3ff",
+ "unicode_alternates": "",
+ "name": "weight lifter tone 5",
+ "shortname": ":lifter_tone5:",
+ "category": "activity",
+ "emoji_order": "1599",
+ "aliases": [
+ ":weight_lifter_tone5:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "bicyclist_tone1": {
+ "unicode": "1f6b4-1f3fb",
+ "unicode_alternates": "",
+ "name": "bicyclist tone 1",
+ "shortname": ":bicyclist_tone1:",
+ "category": "activity",
+ "emoji_order": "1600",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "bicyclist_tone2": {
+ "unicode": "1f6b4-1f3fc",
+ "unicode_alternates": "",
+ "name": "bicyclist tone 2",
+ "shortname": ":bicyclist_tone2:",
+ "category": "activity",
+ "emoji_order": "1601",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "bicyclist_tone3": {
+ "unicode": "1f6b4-1f3fd",
+ "unicode_alternates": "",
+ "name": "bicyclist tone 3",
+ "shortname": ":bicyclist_tone3:",
+ "category": "activity",
+ "emoji_order": "1602",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "bicyclist_tone4": {
+ "unicode": "1f6b4-1f3fe",
+ "unicode_alternates": "",
+ "name": "bicyclist tone 4",
+ "shortname": ":bicyclist_tone4:",
+ "category": "activity",
+ "emoji_order": "1603",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "bicyclist_tone5": {
+ "unicode": "1f6b4-1f3ff",
+ "unicode_alternates": "",
+ "name": "bicyclist tone 5",
+ "shortname": ":bicyclist_tone5:",
+ "category": "activity",
+ "emoji_order": "1604",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "mountain_bicyclist_tone1": {
+ "unicode": "1f6b5-1f3fb",
+ "unicode_alternates": "",
+ "name": "mountain bicyclist tone 1",
+ "shortname": ":mountain_bicyclist_tone1:",
+ "category": "activity",
+ "emoji_order": "1605",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "mountain_bicyclist_tone2": {
+ "unicode": "1f6b5-1f3fc",
+ "unicode_alternates": "",
+ "name": "mountain bicyclist tone 2",
+ "shortname": ":mountain_bicyclist_tone2:",
+ "category": "activity",
+ "emoji_order": "1606",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "mountain_bicyclist_tone3": {
+ "unicode": "1f6b5-1f3fd",
+ "unicode_alternates": "",
+ "name": "mountain bicyclist tone 3",
+ "shortname": ":mountain_bicyclist_tone3:",
+ "category": "activity",
+ "emoji_order": "1607",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "mountain_bicyclist_tone4": {
+ "unicode": "1f6b5-1f3fe",
+ "unicode_alternates": "",
+ "name": "mountain bicyclist tone 4",
+ "shortname": ":mountain_bicyclist_tone4:",
+ "category": "activity",
+ "emoji_order": "1608",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "mountain_bicyclist_tone5": {
+ "unicode": "1f6b5-1f3ff",
+ "unicode_alternates": "",
+ "name": "mountain bicyclist tone 5",
+ "shortname": ":mountain_bicyclist_tone5:",
+ "category": "activity",
+ "emoji_order": "1609",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "horse_racing_tone1": {
+ "unicode": "1f3c7-1f3fb",
+ "unicode_alternates": "",
+ "name": "horse racing tone 1",
+ "shortname": ":horse_racing_tone1:",
+ "category": "activity",
+ "emoji_order": "1610",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "horse_racing_tone2": {
+ "unicode": "1f3c7-1f3fc",
+ "unicode_alternates": "",
+ "name": "horse racing tone 2",
+ "shortname": ":horse_racing_tone2:",
+ "category": "activity",
+ "emoji_order": "1611",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "horse_racing_tone3": {
+ "unicode": "1f3c7-1f3fd",
+ "unicode_alternates": "",
+ "name": "horse racing tone 3",
+ "shortname": ":horse_racing_tone3:",
+ "category": "activity",
+ "emoji_order": "1612",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "horse_racing_tone4": {
+ "unicode": "1f3c7-1f3fe",
+ "unicode_alternates": "",
+ "name": "horse racing tone 4",
+ "shortname": ":horse_racing_tone4:",
+ "category": "activity",
+ "emoji_order": "1613",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "horse_racing_tone5": {
+ "unicode": "1f3c7-1f3ff",
+ "unicode_alternates": "",
+ "name": "horse racing tone 5",
+ "shortname": ":horse_racing_tone5:",
+ "category": "activity",
+ "emoji_order": "1614",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "spy_tone1": {
+ "unicode": "1f575-1f3fb",
+ "unicode_alternates": "",
+ "name": "sleuth or spy tone 1",
+ "shortname": ":spy_tone1:",
+ "category": "people",
+ "emoji_order": "1615",
+ "aliases": [
+ ":sleuth_or_spy_tone1:"
+ ],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "spy_tone2": {
+ "unicode": "1f575-1f3fc",
+ "unicode_alternates": "",
+ "name": "sleuth or spy tone 2",
+ "shortname": ":spy_tone2:",
+ "category": "people",
+ "emoji_order": "1616",
+ "aliases": [
+ ":sleuth_or_spy_tone2:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "spy_tone3": {
+ "unicode": "1f575-1f3fd",
+ "unicode_alternates": "",
+ "name": "sleuth or spy tone 3",
+ "shortname": ":spy_tone3:",
+ "category": "people",
+ "emoji_order": "1617",
+ "aliases": [
+ ":sleuth_or_spy_tone3:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "spy_tone4": {
+ "unicode": "1f575-1f3fe",
+ "unicode_alternates": "",
+ "name": "sleuth or spy tone 4",
+ "shortname": ":spy_tone4:",
+ "category": "people",
+ "emoji_order": "1618",
+ "aliases": [
+ ":sleuth_or_spy_tone4:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "spy_tone5": {
+ "unicode": "1f575-1f3ff",
+ "unicode_alternates": "",
+ "name": "sleuth or spy tone 5",
+ "shortname": ":spy_tone5:",
+ "category": "people",
+ "emoji_order": "1619",
+ "aliases": [
+ ":sleuth_or_spy_tone5:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "tone1": {
+ "unicode": "1f3fb",
+ "unicode_alternates": "",
+ "name": "emoji modifier Fitzpatrick type-1-2",
+ "shortname": ":tone1:",
+ "category": "modifier",
+ "emoji_order": "1620",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "tone2": {
+ "unicode": "1f3fc",
+ "unicode_alternates": "",
+ "name": "emoji modifier Fitzpatrick type-3",
+ "shortname": ":tone2:",
+ "category": "modifier",
+ "emoji_order": "1621",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "tone3": {
+ "unicode": "1f3fd",
+ "unicode_alternates": "",
+ "name": "emoji modifier Fitzpatrick type-4",
+ "shortname": ":tone3:",
+ "category": "modifier",
+ "emoji_order": "1622",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "tone4": {
+ "unicode": "1f3fe",
+ "unicode_alternates": "",
+ "name": "emoji modifier Fitzpatrick type-5",
+ "shortname": ":tone4:",
+ "category": "modifier",
+ "emoji_order": "1623",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "tone5": {
+ "unicode": "1f3ff",
+ "unicode_alternates": "",
+ "name": "emoji modifier Fitzpatrick type-6",
+ "shortname": ":tone5:",
+ "category": "modifier",
+ "emoji_order": "1624",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "cowboy": {
+ "unicode": "1f920",
+ "unicode_alternates": "",
+ "name": "face with cowboy hat",
+ "shortname": ":cowboy:",
+ "category": "unicode9",
+ "emoji_order": "6",
+ "aliases": [
+ ":face_with_cowboy_hat:"
+ ],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "clown": {
+ "unicode": "1f921",
+ "unicode_alternates": "",
+ "name": "clown face",
+ "shortname": ":clown:",
+ "category": "unicode9",
+ "emoji_order": "7",
+ "aliases": [
+ ":clown_face:"
+ ],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "nauseated_face": {
+ "unicode": "1f922",
+ "unicode_alternates": "",
+ "name": "nauseated face",
+ "shortname": ":nauseated_face:",
+ "category": "unicode9",
+ "emoji_order": "8",
+ "aliases": [
+ ":sick:"
+ ],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "rofl": {
+ "unicode": "1f923",
+ "unicode_alternates": "",
+ "name": "rolling on the floor laughing",
+ "shortname": ":rofl:",
+ "category": "unicode9",
+ "emoji_order": "9",
+ "aliases": [
+ ":rolling on the floor laughing:"
+ ],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "drooling_face": {
+ "unicode": "1f924",
+ "unicode_alternates": "",
+ "name": "drooling face",
+ "shortname": ":drooling_face:",
+ "category": "unicode9",
+ "emoji_order": "10",
+ "aliases": [
+ ":drool:"
+ ],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "lying_face": {
+ "unicode": "1f925",
+ "unicode_alternates": "",
+ "name": "lying face",
+ "shortname": ":lying_face:",
+ "category": "unicode9",
+ "emoji_order": "11",
+ "aliases": [
+ ":liar:"
+ ],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "sneezing_face": {
+ "unicode": "1f927",
+ "unicode_alternates": "",
+ "name": "sneezing face",
+ "shortname": ":sneezing_face:",
+ "category": "unicode9",
+ "emoji_order": "12",
+ "aliases": [
+ ":sneeze:"
+ ],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "prince": {
+ "unicode": "1f934",
+ "unicode_alternates": "",
+ "name": "prince",
+ "shortname": ":prince:",
+ "category": "unicode9",
+ "emoji_order": "13",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "man_in_tuxedo": {
+ "unicode": "1f935",
+ "unicode_alternates": "",
+ "name": "man in tuxedo",
+ "shortname": ":man_in_tuxedo:",
+ "category": "unicode9",
+ "emoji_order": "14",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "mother_christmas": {
+ "unicode": "1f936",
+ "unicode_alternates": "",
+ "name": "mother christmas",
+ "shortname": ":mother_christmas:",
+ "category": "unicode9",
+ "emoji_order": "15",
+ "aliases": [
+ ":mrs_clause:"
+ ],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "face_palm": {
+ "unicode": "1f926",
+ "unicode_alternates": "",
+ "name": "face palm",
+ "shortname": ":face_palm:",
+ "category": "unicode9",
+ "emoji_order": "16",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "shrug": {
+ "unicode": "1f937",
+ "unicode_alternates": "",
+ "name": "shrug",
+ "shortname": ":shrug:",
+ "category": "unicode9",
+ "emoji_order": "17",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "pregnant_woman": {
+ "unicode": "1f930",
+ "unicode_alternates": "",
+ "name": "pregnant woman",
+ "shortname": ":pregnant_woman:",
+ "category": "unicode9",
+ "emoji_order": "18",
+ "aliases": [
+ ":expecting_woman:"
+ ],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "selfie": {
+ "unicode": "1f933",
+ "unicode_alternates": "",
+ "name": "selfie",
+ "shortname": ":selfie:",
+ "category": "unicode9",
+ "emoji_order": "19",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "man_dancing": {
+ "unicode": "1f57a",
+ "unicode_alternates": "",
+ "name": "man dancing",
+ "shortname": ":man_dancing:",
+ "category": "unicode9",
+ "emoji_order": "20",
+ "aliases": [
+ ":male_dancer:"
+ ],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "call_me": {
+ "unicode": "1f919",
+ "unicode_alternates": "",
+ "name": "call me hand",
+ "shortname": ":call_me:",
+ "category": "unicode9",
+ "emoji_order": "21",
+ "aliases": [
+ ":call_me_hand:"
+ ],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "raised_back_of_hand": {
+ "unicode": "1f91a",
+ "unicode_alternates": "",
+ "name": "raised back of hand",
+ "shortname": ":raised_back_of_hand:",
+ "category": "unicode9",
+ "emoji_order": "22",
+ "aliases": [
+ ":back_of_hand:"
+ ],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "left_facing_fist": {
+ "unicode": "1f91b",
+ "unicode_alternates": "",
+ "name": "left-facing fist",
+ "shortname": ":left_facing_fist:",
+ "category": "unicode9",
+ "emoji_order": "23",
+ "aliases": [
+ ":left_fist:"
+ ],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "right_facing_fist": {
+ "unicode": "1f91c",
+ "unicode_alternates": "",
+ "name": "right-facing fist",
+ "shortname": ":right_facing_fist:",
+ "category": "unicode9",
+ "emoji_order": "24",
+ "aliases": [
+ ":right_fist:"
+ ],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "handshake": {
+ "unicode": "1f91d",
+ "unicode_alternates": "",
+ "name": "handshake",
+ "shortname": ":handshake:",
+ "category": "unicode9",
+ "emoji_order": "25",
+ "aliases": [
+ ":shaking_hands:"
+ ],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "fingers_crossed": {
+ "unicode": "1f91e",
+ "unicode_alternates": "",
+ "name": "hand with first and index finger crossed",
+ "shortname": ":fingers_crossed:",
+ "category": "unicode9",
+ "emoji_order": "26",
+ "aliases": [
+ ":hand_with_index_and_middle_finger_crossed:"
+ ],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "black_heart": {
+ "unicode": "1f5a4",
+ "unicode_alternates": "",
+ "name": "black heart",
+ "shortname": ":black_heart:",
+ "category": "unicode9",
+ "emoji_order": "27",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "eagle": {
+ "unicode": "1f985",
+ "unicode_alternates": "",
+ "name": "eagle",
+ "shortname": ":eagle:",
+ "category": "unicode9",
+ "emoji_order": "28",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "duck": {
+ "unicode": "1f986",
+ "unicode_alternates": "",
+ "name": "duck",
+ "shortname": ":duck:",
+ "category": "unicode9",
+ "emoji_order": "29",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "bat": {
+ "unicode": "1f987",
+ "unicode_alternates": "",
+ "name": "bat",
+ "shortname": ":bat:",
+ "category": "unicode9",
+ "emoji_order": "30",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "shark": {
+ "unicode": "1f988",
+ "unicode_alternates": "",
+ "name": "shark",
+ "shortname": ":shark:",
+ "category": "unicode9",
+ "emoji_order": "31",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "owl": {
+ "unicode": "1f989",
+ "unicode_alternates": "",
+ "name": "owl",
+ "shortname": ":owl:",
+ "category": "unicode9",
+ "emoji_order": "32",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "fox": {
+ "unicode": "1f98a",
+ "unicode_alternates": "",
+ "name": "fox face",
+ "shortname": ":fox:",
+ "category": "unicode9",
+ "emoji_order": "33",
+ "aliases": [
+ ":fox_face:"
+ ],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "butterfly": {
+ "unicode": "1f98b",
+ "unicode_alternates": "",
+ "name": "butterfly",
+ "shortname": ":butterfly:",
+ "category": "unicode9",
+ "emoji_order": "34",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "deer": {
+ "unicode": "1f98c",
+ "unicode_alternates": "",
+ "name": "deer",
+ "shortname": ":deer:",
+ "category": "unicode9",
+ "emoji_order": "35",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "gorilla": {
+ "unicode": "1f98d",
+ "unicode_alternates": "",
+ "name": "gorilla",
+ "shortname": ":gorilla:",
+ "category": "unicode9",
+ "emoji_order": "36",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "lizard": {
+ "unicode": "1f98e",
+ "unicode_alternates": "",
+ "name": "lizard",
+ "shortname": ":lizard:",
+ "category": "unicode9",
+ "emoji_order": "37",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "rhino": {
+ "unicode": "1f98f",
+ "unicode_alternates": "",
+ "name": "rhinoceros",
+ "shortname": ":rhino:",
+ "category": "unicode9",
+ "emoji_order": "38",
+ "aliases": [
+ ":rhinoceros:"
+ ],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "wilted_rose": {
+ "unicode": "1f940",
+ "unicode_alternates": "",
+ "name": "wilted flower",
+ "shortname": ":wilted_rose:",
+ "category": "unicode9",
+ "emoji_order": "39",
+ "aliases": [
+ ":wilted_flower:"
+ ],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "croissant": {
+ "unicode": "1f950",
+ "unicode_alternates": "",
+ "name": "croissant",
+ "shortname": ":croissant:",
+ "category": "unicode9",
+ "emoji_order": "40",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "avocado": {
+ "unicode": "1f951",
+ "unicode_alternates": "",
+ "name": "avocado",
+ "shortname": ":avocado:",
+ "category": "unicode9",
+ "emoji_order": "41",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "cucumber": {
+ "unicode": "1f952",
+ "unicode_alternates": "",
+ "name": "cucumber",
+ "shortname": ":cucumber:",
+ "category": "unicode9",
+ "emoji_order": "42",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "bacon": {
+ "unicode": "1f953",
+ "unicode_alternates": "",
+ "name": "bacon",
+ "shortname": ":bacon:",
+ "category": "unicode9",
+ "emoji_order": "43",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [
+ "pig"
+ ]
+ },
+ "potato": {
+ "unicode": "1f954",
+ "unicode_alternates": "",
+ "name": "potato",
+ "shortname": ":potato:",
+ "category": "unicode9",
+ "emoji_order": "44",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "carrot": {
+ "unicode": "1f955",
+ "unicode_alternates": "",
+ "name": "carrot",
+ "shortname": ":carrot:",
+ "category": "unicode9",
+ "emoji_order": "45",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "french_bread": {
+ "unicode": "1f956",
+ "unicode_alternates": "",
+ "name": "baguette bread",
+ "shortname": ":french_bread:",
+ "category": "unicode9",
+ "emoji_order": "46",
+ "aliases": [
+ ":baguette_bread:"
+ ],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "salad": {
+ "unicode": "1f957",
+ "unicode_alternates": "",
+ "name": "green salad",
+ "shortname": ":salad:",
+ "category": "unicode9",
+ "emoji_order": "47",
+ "aliases": [
+ ":green_salad:"
+ ],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "shallow_pan_of_food": {
+ "unicode": "1f958",
+ "unicode_alternates": "",
+ "name": "shallow pan of food",
+ "shortname": ":shallow_pan_of_food:",
+ "category": "unicode9",
+ "emoji_order": "48",
+ "aliases": [
+ ":paella:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "pan of food"
+ ]
+ },
+ "stuffed_flatbread": {
+ "unicode": "1f959",
+ "unicode_alternates": "",
+ "name": "stuffed flatbread",
+ "shortname": ":stuffed_flatbread:",
+ "category": "unicode9",
+ "emoji_order": "49",
+ "aliases": [
+ ":stuffed_pita:"
+ ],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "champagne_glass": {
+ "unicode": "1f942",
+ "unicode_alternates": "",
+ "name": "clinking glasses",
+ "shortname": ":champagne_glass:",
+ "category": "unicode9",
+ "emoji_order": "50",
+ "aliases": [
+ ":clinking_glass:"
+ ],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "tumbler_glass": {
+ "unicode": "1f943",
+ "unicode_alternates": "",
+ "name": "tumbler glass",
+ "shortname": ":tumbler_glass:",
+ "category": "unicode9",
+ "emoji_order": "51",
+ "aliases": [
+ ":whisky:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "booze"
+ ]
+ },
+ "spoon": {
+ "unicode": "1f944",
+ "unicode_alternates": "",
+ "name": "spoon",
+ "shortname": ":spoon:",
+ "category": "unicode9",
+ "emoji_order": "52",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "octagonal_sign": {
+ "unicode": "1f6d1",
+ "unicode_alternates": "",
+ "name": "octagonal sign",
+ "shortname": ":octagonal_sign:",
+ "category": "unicode9",
+ "emoji_order": "53",
+ "aliases": [
+ ":stop_sign:"
+ ],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "shopping_cart": {
+ "unicode": "1f6d2",
+ "unicode_alternates": "",
+ "name": "shopping trolley",
+ "shortname": ":shopping_cart:",
+ "category": "unicode9",
+ "emoji_order": "54",
+ "aliases": [
+ ":shopping_trolley:"
+ ],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "scooter": {
+ "unicode": "1f6f4",
+ "unicode_alternates": "",
+ "name": "scooter",
+ "shortname": ":scooter:",
+ "category": "unicode9",
+ "emoji_order": "55",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "motor_scooter": {
+ "unicode": "1f6f5",
+ "unicode_alternates": "",
+ "name": "motor scooter",
+ "shortname": ":motor_scooter:",
+ "category": "unicode9",
+ "emoji_order": "56",
+ "aliases": [
+ ":motorbike:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [
+ "moped"
+ ]
+ },
+ "canoe": {
+ "unicode": "1f6f6",
+ "unicode_alternates": "",
+ "name": "canoe",
+ "shortname": ":canoe:",
+ "category": "unicode9",
+ "emoji_order": "57",
+ "aliases": [
+ ":kayak:"
+ ],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "cartwheel": {
+ "unicode": "1f938",
+ "unicode_alternates": "",
+ "name": "person doing cartwheel",
+ "shortname": ":cartwheel:",
+ "category": "unicode9",
+ "emoji_order": "58",
+ "aliases": [
+ ":person_doing_cartwheel:"
+ ],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "juggling": {
+ "unicode": "1f939",
+ "unicode_alternates": "",
+ "name": "juggling",
+ "shortname": ":juggling:",
+ "category": "unicode9",
+ "emoji_order": "59",
+ "aliases": [
+ ":juggler:"
+ ],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "wrestlers": {
+ "unicode": "1f93c",
+ "unicode_alternates": "",
+ "name": "wrestlers",
+ "shortname": ":wrestlers:",
+ "category": "unicode9",
+ "emoji_order": "60",
+ "aliases": [
+ ":wrestling:"
+ ],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "boxing_glove": {
+ "unicode": "1f94a",
+ "unicode_alternates": "",
+ "name": "boxing glove",
+ "shortname": ":boxing_glove:",
+ "category": "unicode9",
+ "emoji_order": "61",
+ "aliases": [
+ ":boxing_gloves:"
+ ],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "martial_arts_uniform": {
+ "unicode": "1f94b",
+ "unicode_alternates": "",
+ "name": "martial arts uniform",
+ "shortname": ":martial_arts_uniform:",
+ "category": "unicode9",
+ "emoji_order": "62",
+ "aliases": [
+ ":karate_uniform:"
+ ],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "water_polo": {
+ "unicode": "1f93d",
+ "unicode_alternates": "",
+ "name": "water polo",
+ "shortname": ":water_polo:",
+ "category": "unicode9",
+ "emoji_order": "63",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "handball": {
+ "unicode": "1f93e",
+ "unicode_alternates": "",
+ "name": "handball",
+ "shortname": ":handball:",
+ "category": "unicode9",
+ "emoji_order": "64",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "goal": {
+ "unicode": "1f945",
+ "unicode_alternates": "",
+ "name": "goal net",
+ "shortname": ":goal:",
+ "category": "unicode9",
+ "emoji_order": "65",
+ "aliases": [
+ ":goal_net:"
+ ],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "fencer": {
+ "unicode": "1f93a",
+ "unicode_alternates": "",
+ "name": "fencer",
+ "shortname": ":fencer:",
+ "category": "unicode9",
+ "emoji_order": "68",
+ "aliases": [
+ ":fencing:"
+ ],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "first_place": {
+ "unicode": "1f947",
+ "unicode_alternates": "",
+ "name": "first place medal",
+ "shortname": ":first_place:",
+ "category": "unicode9",
+ "emoji_order": "69",
+ "aliases": [
+ ":first_place_medal:"
+ ],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "second_place": {
+ "unicode": "1f948",
+ "unicode_alternates": "",
+ "name": "second place medal",
+ "shortname": ":second_place:",
+ "category": "unicode9",
+ "emoji_order": "70",
+ "aliases": [
+ ":second_place_medal:"
+ ],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "third_place": {
+ "unicode": "1f949",
+ "unicode_alternates": "",
+ "name": "third place medal",
+ "shortname": ":third_place:",
+ "category": "unicode9",
+ "emoji_order": "71",
+ "aliases": [
+ ":third_place_medal:"
+ ],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "drum": {
+ "unicode": "1f941",
+ "unicode_alternates": "",
+ "name": "drum with drumsticks",
+ "shortname": ":drum:",
+ "category": "unicode9",
+ "emoji_order": "72",
+ "aliases": [
+ ":drum_with_drumsticks:"
+ ],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "shrimp": {
+ "unicode": "1f990",
+ "unicode_alternates": "",
+ "name": "shrimp",
+ "shortname": ":shrimp:",
+ "category": "unicode9",
+ "emoji_order": "73",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "squid": {
+ "unicode": "1f991",
+ "unicode_alternates": "",
+ "name": "squid",
+ "shortname": ":squid:",
+ "category": "unicode9",
+ "emoji_order": "74",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "milk": {
+ "unicode": "1f95b",
+ "unicode_alternates": "",
+ "name": "glass of milk",
+ "shortname": ":milk:",
+ "category": "unicode9",
+ "emoji_order": "76",
+ "aliases": [
+ ":glass_of_milk:"
+ ],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "peanuts": {
+ "unicode": "1f95c",
+ "unicode_alternates": "",
+ "name": "peanuts",
+ "shortname": ":peanuts:",
+ "category": "unicode9",
+ "emoji_order": "77",
+ "aliases": [
+ ":shelled_peanut:"
+ ],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "kiwi": {
+ "unicode": "1f95d",
+ "unicode_alternates": "",
+ "name": "kiwifruit",
+ "shortname": ":kiwi:",
+ "category": "unicode9",
+ "emoji_order": "78",
+ "aliases": [
+ ":kiwifruit:"
+ ],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "pancakes": {
+ "unicode": "1f95e",
+ "unicode_alternates": "",
+ "name": "pancakes",
+ "shortname": ":pancakes:",
+ "category": "unicode9",
+ "emoji_order": "79",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "gay_pride_flag": {
+ "unicode": "1f3f3-1f308",
+ "unicode_alternates": "",
+ "name": "gay_pride_flag",
+ "shortname": ":gay_pride_flag:",
+ "category": "unicode9",
+ "emoji_order": "311",
+ "aliases": [
+ ":rainbow_flag:"
+ ],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "prince_tone1": {
+ "unicode": "1f934-1f3fb",
+ "unicode_alternates": "",
+ "name": "prince tone 1",
+ "shortname": ":prince_tone1:",
+ "category": "unicode9",
+ "emoji_order": "10000",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "prince_tone2": {
+ "unicode": "1f934-1f3fc",
+ "unicode_alternates": "",
+ "name": "prince tone 2",
+ "shortname": ":prince_tone2:",
+ "category": "unicode9",
+ "emoji_order": "10001",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "prince_tone3": {
+ "unicode": "1f934-1f3fd",
+ "unicode_alternates": "",
+ "name": "prince tone 3",
+ "shortname": ":prince_tone3:",
+ "category": "unicode9",
+ "emoji_order": "10002",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "prince_tone4": {
+ "unicode": "1f934-1f3fe",
+ "unicode_alternates": "",
+ "name": "prince tone 4",
+ "shortname": ":prince_tone4:",
+ "category": "unicode9",
+ "emoji_order": "10003",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "prince_tone5": {
+ "unicode": "1f934-1f3ff",
+ "unicode_alternates": "",
+ "name": "prince tone 5",
+ "shortname": ":prince_tone5:",
+ "category": "unicode9",
+ "emoji_order": "10004",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "mrs_clause_tone1": {
+ "unicode": "1f936-1f3fb",
+ "unicode_alternates": "",
+ "name": "mother christmas tone 1",
+ "shortname": ":mrs_clause_tone1:",
+ "category": "unicode9",
+ "emoji_order": "10005",
+ "aliases": [
+ ":mother_christmas_tone1:"
+ ],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "mrs_clause_tone2": {
+ "unicode": "1f936-1f3fc",
+ "unicode_alternates": "",
+ "name": "mother christmas tone 2",
+ "shortname": ":mrs_clause_tone2:",
+ "category": "unicode9",
+ "emoji_order": "10006",
+ "aliases": [
+ ":mother_christmas_tone2:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "mrs_clause_tone3": {
+ "unicode": "1f936-1f3fd",
+ "unicode_alternates": "",
+ "name": "mother christmas tone 3",
+ "shortname": ":mrs_clause_tone3:",
+ "category": "unicode9",
+ "emoji_order": "10007",
+ "aliases": [
+ ":mother_christmas_tone3:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "mrs_clause_tone4": {
+ "unicode": "1f936-1f3fe",
+ "unicode_alternates": "",
+ "name": "mother christmas tone 4",
+ "shortname": ":mrs_clause_tone4:",
+ "category": "unicode9",
+ "emoji_order": "10008",
+ "aliases": [
+ ":mother_christmas_tone4:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "mrs_clause_tone5": {
+ "unicode": "1f936-1f3ff",
+ "unicode_alternates": "",
+ "name": "mother christmas tone 5",
+ "shortname": ":mrs_clause_tone5:",
+ "category": "unicode9",
+ "emoji_order": "10009",
+ "aliases": [
+ ":mother_christmas_tone5:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "man_in_tuxedo_tone1": {
+ "unicode": "1f935-1f3fb",
+ "unicode_alternates": "",
+ "name": "man in tuxedo tone 1",
+ "shortname": ":man_in_tuxedo_tone1:",
+ "category": "unicode9",
+ "emoji_order": "10010",
+ "aliases": [
+ ":tuxedo_tone1:"
+ ],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "man_in_tuxedo_tone2": {
+ "unicode": "1f935-1f3fc",
+ "unicode_alternates": "",
+ "name": "man in tuxedo tone 2",
+ "shortname": ":man_in_tuxedo_tone2:",
+ "category": "unicode9",
+ "emoji_order": "10011",
+ "aliases": [
+ ":tuxedo_tone2:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "man_in_tuxedo_tone3": {
+ "unicode": "1f935-1f3fd",
+ "unicode_alternates": "",
+ "name": "man in tuxedo tone 3",
+ "shortname": ":man_in_tuxedo_tone3:",
+ "category": "unicode9",
+ "emoji_order": "10012",
+ "aliases": [
+ ":tuxedo_tone3:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "man_in_tuxedo_tone4": {
+ "unicode": "1f935-1f3fe",
+ "unicode_alternates": "",
+ "name": "man in tuxedo tone 4",
+ "shortname": ":man_in_tuxedo_tone4:",
+ "category": "unicode9",
+ "emoji_order": "10013",
+ "aliases": [
+ ":tuxedo_tone4:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "man_in_tuxedo_tone5": {
+ "unicode": "1f935-1f3ff",
+ "unicode_alternates": "",
+ "name": "man in tuxedo tone 5",
+ "shortname": ":man_in_tuxedo_tone5:",
+ "category": "unicode9",
+ "emoji_order": "10014",
+ "aliases": [
+ ":tuxedo_tone5:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "shrug_tone1": {
+ "unicode": "1f937-1f3fb",
+ "unicode_alternates": "",
+ "name": "shrug tone 1",
+ "shortname": ":shrug_tone1:",
+ "category": "unicode9",
+ "emoji_order": "10015",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "shrug_tone2": {
+ "unicode": "1f937-1f3fc",
+ "unicode_alternates": "",
+ "name": "shrug tone 2",
+ "shortname": ":shrug_tone2:",
+ "category": "unicode9",
+ "emoji_order": "10016",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "shrug_tone3": {
+ "unicode": "1f937-1f3fd",
+ "unicode_alternates": "",
+ "name": "shrug tone 3",
+ "shortname": ":shrug_tone3:",
+ "category": "unicode9",
+ "emoji_order": "10017",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "shrug_tone4": {
+ "unicode": "1f937-1f3fe",
+ "unicode_alternates": "",
+ "name": "shrug tone 4",
+ "shortname": ":shrug_tone4:",
+ "category": "unicode9",
+ "emoji_order": "10018",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "shrug_tone5": {
+ "unicode": "1f937-1f3ff",
+ "unicode_alternates": "",
+ "name": "shrug tone 5",
+ "shortname": ":shrug_tone5:",
+ "category": "unicode9",
+ "emoji_order": "10019",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "face_palm_tone1": {
+ "unicode": "1f926-1f3fb",
+ "unicode_alternates": "",
+ "name": "face palm tone 1",
+ "shortname": ":face_palm_tone1:",
+ "category": "unicode9",
+ "emoji_order": "10020",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "face_palm_tone2": {
+ "unicode": "1f926-1f3fc",
+ "unicode_alternates": "",
+ "name": "face palm tone 2",
+ "shortname": ":face_palm_tone2:",
+ "category": "unicode9",
+ "emoji_order": "10021",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "face_palm_tone3": {
+ "unicode": "1f926-1f3fd",
+ "unicode_alternates": "",
+ "name": "face palm tone 3",
+ "shortname": ":face_palm_tone3:",
+ "category": "unicode9",
+ "emoji_order": "10022",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "face_palm_tone4": {
+ "unicode": "1f926-1f3fe",
+ "unicode_alternates": "",
+ "name": "face palm tone 4",
+ "shortname": ":face_palm_tone4:",
+ "category": "unicode9",
+ "emoji_order": "10023",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "face_palm_tone5": {
+ "unicode": "1f926-1f3ff",
+ "unicode_alternates": "",
+ "name": "face palm tone 5",
+ "shortname": ":face_palm_tone5:",
+ "category": "unicode9",
+ "emoji_order": "10024",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "pregnant_woman_tone1": {
+ "unicode": "1f930-1f3fb",
+ "unicode_alternates": "",
+ "name": "pregnant woman tone 1",
+ "shortname": ":pregnant_woman_tone1:",
+ "category": "unicode9",
+ "emoji_order": "10025",
+ "aliases": [
+ ":expecting_woman_tone1:"
+ ],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "pregnant_woman_tone2": {
+ "unicode": "1f930-1f3fc",
+ "unicode_alternates": "",
+ "name": "pregnant woman tone 2",
+ "shortname": ":pregnant_woman_tone2:",
+ "category": "unicode9",
+ "emoji_order": "10026",
+ "aliases": [
+ ":expecting_woman_tone2:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "pregnant_woman_tone3": {
+ "unicode": "1f930-1f3fd",
+ "unicode_alternates": "",
+ "name": "pregnant woman tone 3",
+ "shortname": ":pregnant_woman_tone3:",
+ "category": "unicode9",
+ "emoji_order": "10027",
+ "aliases": [
+ ":expecting_woman_tone3:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "pregnant_woman_tone4": {
+ "unicode": "1f930-1f3fe",
+ "unicode_alternates": "",
+ "name": "pregnant woman tone 4",
+ "shortname": ":pregnant_woman_tone4:",
+ "category": "unicode9",
+ "emoji_order": "10028",
+ "aliases": [
+ ":expecting_woman_tone4:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "pregnant_woman_tone5": {
+ "unicode": "1f930-1f3ff",
+ "unicode_alternates": "",
+ "name": "pregnant woman tone 5",
+ "shortname": ":pregnant_woman_tone5:",
+ "category": "unicode9",
+ "emoji_order": "10029",
+ "aliases": [
+ ":expecting_woman_tone5:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "man_dancing_tone1": {
+ "unicode": "1f57a-1f3fb",
+ "unicode_alternates": "",
+ "name": "man dancing tone 1",
+ "shortname": ":man_dancing_tone1:",
+ "category": "unicode9",
+ "emoji_order": "10030",
+ "aliases": [
+ ":male_dancer_tone1:"
+ ],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "man_dancing_tone2": {
+ "unicode": "1f57a-1f3fc",
+ "unicode_alternates": "",
+ "name": "man dancing tone 2",
+ "shortname": ":man_dancing_tone2:",
+ "category": "unicode9",
+ "emoji_order": "10031",
+ "aliases": [
+ ":male_dancer_tone2:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "man_dancing_tone3": {
+ "unicode": "1f57a-1f3fd",
+ "unicode_alternates": "",
+ "name": "man dancing tone 3",
+ "shortname": ":man_dancing_tone3:",
+ "category": "unicode9",
+ "emoji_order": "10032",
+ "aliases": [
+ ":male_dancer_tone3:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "man_dancing_tone4": {
+ "unicode": "1f57a-1f3fe",
+ "unicode_alternates": "",
+ "name": "man dancing tone 4",
+ "shortname": ":man_dancing_tone4:",
+ "category": "unicode9",
+ "emoji_order": "10033",
+ "aliases": [
+ ":male_dancer_tone4:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "man_dancing_tone5": {
+ "unicode": "1f57a-1f3ff",
+ "unicode_alternates": "",
+ "name": "man dancing tone 5",
+ "shortname": ":man_dancing_tone5:",
+ "category": "unicode9",
+ "emoji_order": "10034",
+ "aliases": [
+ ":male_dancer_tone5:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "selfie_tone1": {
+ "unicode": "1f933-1f3fb",
+ "unicode_alternates": "",
+ "name": "selfie tone 1",
+ "shortname": ":selfie_tone1:",
+ "category": "unicode9",
+ "emoji_order": "10035",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "selfie_tone2": {
+ "unicode": "1f933-1f3fc",
+ "unicode_alternates": "",
+ "name": "selfie tone 2",
+ "shortname": ":selfie_tone2:",
+ "category": "unicode9",
+ "emoji_order": "10036",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "selfie_tone3": {
+ "unicode": "1f933-1f3fd",
+ "unicode_alternates": "",
+ "name": "selfie tone 3",
+ "shortname": ":selfie_tone3:",
+ "category": "unicode9",
+ "emoji_order": "10037",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "selfie_tone4": {
+ "unicode": "1f933-1f3fe",
+ "unicode_alternates": "",
+ "name": "selfie tone 4",
+ "shortname": ":selfie_tone4:",
+ "category": "unicode9",
+ "emoji_order": "10038",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "selfie_tone5": {
+ "unicode": "1f933-1f3ff",
+ "unicode_alternates": "",
+ "name": "selfie tone 5",
+ "shortname": ":selfie_tone5:",
+ "category": "unicode9",
+ "emoji_order": "10039",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "fingers_crossed_tone1": {
+ "unicode": "1f91e-1f3fb",
+ "unicode_alternates": "",
+ "name": "hand with index and middle fingers crossed tone 1",
+ "shortname": ":fingers_crossed_tone1:",
+ "category": "unicode9",
+ "emoji_order": "10040",
+ "aliases": [
+ ":hand_with_index_and_middle_fingers_crossed_tone1:"
+ ],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "fingers_crossed_tone2": {
+ "unicode": "1f91e-1f3fc",
+ "unicode_alternates": "",
+ "name": "hand with index and middle fingers crossed tone 2",
+ "shortname": ":fingers_crossed_tone2:",
+ "category": "unicode9",
+ "emoji_order": "10041",
+ "aliases": [
+ ":hand_with_index_and_middle_fingers_crossed_tone2:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "fingers_crossed_tone3": {
+ "unicode": "1f91e-1f3fd",
+ "unicode_alternates": "",
+ "name": "hand with index and middle fingers crossed tone 3",
+ "shortname": ":fingers_crossed_tone3:",
+ "category": "unicode9",
+ "emoji_order": "10042",
+ "aliases": [
+ ":hand_with_index_and_middle_fingers_crossed_tone3:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "fingers_crossed_tone4": {
+ "unicode": "1f91e-1f3fe",
+ "unicode_alternates": "",
+ "name": "hand with index and middle fingers crossed tone 4",
+ "shortname": ":fingers_crossed_tone4:",
+ "category": "unicode9",
+ "emoji_order": "10043",
+ "aliases": [
+ ":hand_with_index_and_middle_fingers_crossed_tone4:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "fingers_crossed_tone5": {
+ "unicode": "1f91e-1f3ff",
+ "unicode_alternates": "",
+ "name": "hand with index and middle fingers crossed tone 5",
+ "shortname": ":fingers_crossed_tone5:",
+ "category": "unicode9",
+ "emoji_order": "10044",
+ "aliases": [
+ ":hand_with_index_and_middle_fingers_crossed_tone5:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "call_me_tone1": {
+ "unicode": "1f919-1f3fb",
+ "unicode_alternates": "",
+ "name": "call me hand tone 1",
+ "shortname": ":call_me_tone1:",
+ "category": "unicode9",
+ "emoji_order": "10045",
+ "aliases": [
+ ":call_me_hand_tone1:"
+ ],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "call_me_tone2": {
+ "unicode": "1f919-1f3fc",
+ "unicode_alternates": "",
+ "name": "call me hand tone 2",
+ "shortname": ":call_me_tone2:",
+ "category": "unicode9",
+ "emoji_order": "10046",
+ "aliases": [
+ ":call_me_hand_tone2:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "call_me_tone3": {
+ "unicode": "1f919-1f3fd",
+ "unicode_alternates": "",
+ "name": "call me hand tone 3",
+ "shortname": ":call_me_tone3:",
+ "category": "unicode9",
+ "emoji_order": "10047",
+ "aliases": [
+ ":call_me_hand_tone3:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "call_me_tone4": {
+ "unicode": "1f919-1f3fe",
+ "unicode_alternates": "",
+ "name": "call me hand tone 4",
+ "shortname": ":call_me_tone4:",
+ "category": "unicode9",
+ "emoji_order": "10048",
+ "aliases": [
+ ":call_me_hand_tone4:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "call_me_tone5": {
+ "unicode": "1f919-1f3ff",
+ "unicode_alternates": "",
+ "name": "call me hand tone 5",
+ "shortname": ":call_me_tone5:",
+ "category": "unicode9",
+ "emoji_order": "10049",
+ "aliases": [
+ ":call_me_hand_tone5:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "left_facing_fist_tone1": {
+ "unicode": "1f91b-1f3fb",
+ "unicode_alternates": "",
+ "name": "left facing fist tone 1",
+ "shortname": ":left_facing_fist_tone1:",
+ "category": "unicode9",
+ "emoji_order": "10050",
+ "aliases": [
+ ":left_fist_tone1:"
+ ],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "left_facing_fist_tone2": {
+ "unicode": "1f91b-1f3fc",
+ "unicode_alternates": "",
+ "name": "left facing fist tone 2",
+ "shortname": ":left_facing_fist_tone2:",
+ "category": "unicode9",
+ "emoji_order": "10051",
+ "aliases": [
+ ":left_fist_tone2:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "left_facing_fist_tone3": {
+ "unicode": "1f91b-1f3fd",
+ "unicode_alternates": "",
+ "name": "left facing fist tone 3",
+ "shortname": ":left_facing_fist_tone3:",
+ "category": "unicode9",
+ "emoji_order": "10052",
+ "aliases": [
+ ":left_fist_tone3:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "left_facing_fist_tone4": {
+ "unicode": "1f91b-1f3fe",
+ "unicode_alternates": "",
+ "name": "left facing fist tone 4",
+ "shortname": ":left_facing_fist_tone4:",
+ "category": "unicode9",
+ "emoji_order": "10053",
+ "aliases": [
+ ":left_fist_tone4:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "left_facing_fist_tone5": {
+ "unicode": "1f91b-1f3ff",
+ "unicode_alternates": "",
+ "name": "left facing fist tone 5",
+ "shortname": ":left_facing_fist_tone5:",
+ "category": "unicode9",
+ "emoji_order": "10054",
+ "aliases": [
+ ":left_fist_tone5:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "right_facing_fist_tone1": {
+ "unicode": "1f91c-1f3fb",
+ "unicode_alternates": "",
+ "name": "right facing fist tone 1",
+ "shortname": ":right_facing_fist_tone1:",
+ "category": "unicode9",
+ "emoji_order": "10055",
+ "aliases": [
+ ":right_fist_tone1:"
+ ],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "right_facing_fist_tone2": {
+ "unicode": "1f91c-1f3fc",
+ "unicode_alternates": "",
+ "name": "right facing fist tone 2",
+ "shortname": ":right_facing_fist_tone2:",
+ "category": "unicode9",
+ "emoji_order": "10056",
+ "aliases": [
+ ":right_fist_tone2:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "right_facing_fist_tone3": {
+ "unicode": "1f91c-1f3fd",
+ "unicode_alternates": "",
+ "name": "right facing fist tone 3",
+ "shortname": ":right_facing_fist_tone3:",
+ "category": "unicode9",
+ "emoji_order": "10057",
+ "aliases": [
+ ":right_fist_tone3:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "right_facing_fist_tone4": {
+ "unicode": "1f91c-1f3fe",
+ "unicode_alternates": "",
+ "name": "right facing fist tone 4",
+ "shortname": ":right_facing_fist_tone4:",
+ "category": "unicode9",
+ "emoji_order": "10058",
+ "aliases": [
+ ":right_fist_tone4:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "right_facing_fist_tone5": {
+ "unicode": "1f91c-1f3ff",
+ "unicode_alternates": "",
+ "name": "right facing fist tone 5",
+ "shortname": ":right_facing_fist_tone5:",
+ "category": "unicode9",
+ "emoji_order": "10059",
+ "aliases": [
+ ":right_fist_tone5:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "raised_back_of_hand_tone1": {
+ "unicode": "1f91a-1f3fb",
+ "unicode_alternates": "",
+ "name": "raised back of hand tone 1",
+ "shortname": ":raised_back_of_hand_tone1:",
+ "category": "unicode9",
+ "emoji_order": "10060",
+ "aliases": [
+ ":back_of_hand_tone1:"
+ ],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "raised_back_of_hand_tone2": {
+ "unicode": "1f91a-1f3fc",
+ "unicode_alternates": "",
+ "name": "raised back of hand tone 2",
+ "shortname": ":raised_back_of_hand_tone2:",
+ "category": "unicode9",
+ "emoji_order": "10061",
+ "aliases": [
+ ":back_of_hand_tone2:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "raised_back_of_hand_tone3": {
+ "unicode": "1f91a-1f3fd",
+ "unicode_alternates": "",
+ "name": "raised back of hand tone 3",
+ "shortname": ":raised_back_of_hand_tone3:",
+ "category": "unicode9",
+ "emoji_order": "10062",
+ "aliases": [
+ ":back_of_hand_tone3:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "raised_back_of_hand_tone4": {
+ "unicode": "1f91a-1f3fe",
+ "unicode_alternates": "",
+ "name": "raised back of hand tone 4",
+ "shortname": ":raised_back_of_hand_tone4:",
+ "category": "unicode9",
+ "emoji_order": "10063",
+ "aliases": [
+ ":back_of_hand_tone4:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "raised_back_of_hand_tone5": {
+ "unicode": "1f91a-1f3ff",
+ "unicode_alternates": "",
+ "name": "raised back of hand tone 5",
+ "shortname": ":raised_back_of_hand_tone5:",
+ "category": "unicode9",
+ "emoji_order": "10064",
+ "aliases": [
+ ":back_of_hand_tone5:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "handshake_tone1": {
+ "unicode": "1f91d-1f3fb",
+ "unicode_alternates": "",
+ "name": "handshake tone 1",
+ "shortname": ":handshake_tone1:",
+ "category": "unicode9",
+ "emoji_order": "10065",
+ "aliases": [
+ ":shaking_hands_tone1:"
+ ],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "handshake_tone2": {
+ "unicode": "1f91d-1f3fc",
+ "unicode_alternates": "",
+ "name": "handshake tone 2",
+ "shortname": ":handshake_tone2:",
+ "category": "unicode9",
+ "emoji_order": "10066",
+ "aliases": [
+ ":shaking_hands_tone2:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "handshake_tone3": {
+ "unicode": "1f91d-1f3fd",
+ "unicode_alternates": "",
+ "name": "handshake tone 3",
+ "shortname": ":handshake_tone3:",
+ "category": "unicode9",
+ "emoji_order": "10067",
+ "aliases": [
+ ":shaking_hands_tone3:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "handshake_tone4": {
+ "unicode": "1f91d-1f3fe",
+ "unicode_alternates": "",
+ "name": "handshake tone 4",
+ "shortname": ":handshake_tone4:",
+ "category": "unicode9",
+ "emoji_order": "10068",
+ "aliases": [
+ ":shaking_hands_tone4:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "handshake_tone5": {
+ "unicode": "1f91d-1f3ff",
+ "unicode_alternates": "",
+ "name": "handshake tone 5",
+ "shortname": ":handshake_tone5:",
+ "category": "unicode9",
+ "emoji_order": "10069",
+ "aliases": [
+ ":shaking_hands_tone5:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "cartwheel_tone1": {
+ "unicode": "1f938-1f3fb",
+ "unicode_alternates": "",
+ "name": "person doing cartwheel tone 1",
+ "shortname": ":cartwheel_tone1:",
+ "category": "unicode9",
+ "emoji_order": "10070",
+ "aliases": [
+ ":person_doing_cartwheel_tone1:"
+ ],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "cartwheel_tone2": {
+ "unicode": "1f938-1f3fc",
+ "unicode_alternates": "",
+ "name": "person doing cartwheel tone 2",
+ "shortname": ":cartwheel_tone2:",
+ "category": "unicode9",
+ "emoji_order": "10071",
+ "aliases": [
+ ":person_doing_cartwheel_tone2:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "cartwheel_tone3": {
+ "unicode": "1f938-1f3fd",
+ "unicode_alternates": "",
+ "name": "person doing cartwheel tone 3",
+ "shortname": ":cartwheel_tone3:",
+ "category": "unicode9",
+ "emoji_order": "10072",
+ "aliases": [
+ ":person_doing_cartwheel_tone3:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "cartwheel_tone4": {
+ "unicode": "1f938-1f3fe",
+ "unicode_alternates": "",
+ "name": "person doing cartwheel tone 4",
+ "shortname": ":cartwheel_tone4:",
+ "category": "unicode9",
+ "emoji_order": "10073",
+ "aliases": [
+ ":person_doing_cartwheel_tone4:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "cartwheel_tone5": {
+ "unicode": "1f938-1f3ff",
+ "unicode_alternates": "",
+ "name": "person doing cartwheel tone 5",
+ "shortname": ":cartwheel_tone5:",
+ "category": "unicode9",
+ "emoji_order": "10074",
+ "aliases": [
+ ":person_doing_cartwheel_tone5:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "wrestlers_tone1": {
+ "unicode": "1f93c-1f3fb",
+ "unicode_alternates": "",
+ "name": "wrestlers tone 1",
+ "shortname": ":wrestlers_tone1:",
+ "category": "unicode9",
+ "emoji_order": "10080",
+ "aliases": [
+ ":wrestling_tone1:"
+ ],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "wrestlers_tone2": {
+ "unicode": "1f93c-1f3fc",
+ "unicode_alternates": "",
+ "name": "wrestlers tone 2",
+ "shortname": ":wrestlers_tone2:",
+ "category": "unicode9",
+ "emoji_order": "10081",
+ "aliases": [
+ ":wrestling_tone2:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "wrestlers_tone3": {
+ "unicode": "1f93c-1f3fd",
+ "unicode_alternates": "",
+ "name": "wrestlers tone 3",
+ "shortname": ":wrestlers_tone3:",
+ "category": "unicode9",
+ "emoji_order": "10082",
+ "aliases": [
+ ":wrestling_tone3:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "wrestlers_tone4": {
+ "unicode": "1f93c-1f3fe",
+ "unicode_alternates": "",
+ "name": "wrestlers tone 4",
+ "shortname": ":wrestlers_tone4:",
+ "category": "unicode9",
+ "emoji_order": "10083",
+ "aliases": [
+ ":wrestling_tone4:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "wrestlers_tone5": {
+ "unicode": "1f93c-1f3ff",
+ "unicode_alternates": "",
+ "name": "wrestlers tone 5",
+ "shortname": ":wrestlers_tone5:",
+ "category": "unicode9",
+ "emoji_order": "10084",
+ "aliases": [
+ ":wrestling_tone5:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "water_polo_tone1": {
+ "unicode": "1f93d-1f3fb",
+ "unicode_alternates": "",
+ "name": "water polo tone 1",
+ "shortname": ":water_polo_tone1:",
+ "category": "unicode9",
+ "emoji_order": "10085",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "water_polo_tone2": {
+ "unicode": "1f93d-1f3fc",
+ "unicode_alternates": "",
+ "name": "water polo tone 2",
+ "shortname": ":water_polo_tone2:",
+ "category": "unicode9",
+ "emoji_order": "10086",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "water_polo_tone3": {
+ "unicode": "1f93d-1f3fd",
+ "unicode_alternates": "",
+ "name": "water polo tone 3",
+ "shortname": ":water_polo_tone3:",
+ "category": "unicode9",
+ "emoji_order": "10087",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "water_polo_tone4": {
+ "unicode": "1f93d-1f3fe",
+ "unicode_alternates": "",
+ "name": "water polo tone 4",
+ "shortname": ":water_polo_tone4:",
+ "category": "unicode9",
+ "emoji_order": "10088",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "water_polo_tone5": {
+ "unicode": "1f93d-1f3ff",
+ "unicode_alternates": "",
+ "name": "water polo tone 5",
+ "shortname": ":water_polo_tone5:",
+ "category": "unicode9",
+ "emoji_order": "10089",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "handball_tone1": {
+ "unicode": "1f93e-1f3fb",
+ "unicode_alternates": "",
+ "name": "handball tone 1",
+ "shortname": ":handball_tone1:",
+ "category": "unicode9",
+ "emoji_order": "10090",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "handball_tone2": {
+ "unicode": "1f93e-1f3fc",
+ "unicode_alternates": "",
+ "name": "handball tone 2",
+ "shortname": ":handball_tone2:",
+ "category": "unicode9",
+ "emoji_order": "10091",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "handball_tone3": {
+ "unicode": "1f93e-1f3fd",
+ "unicode_alternates": "",
+ "name": "handball tone 3",
+ "shortname": ":handball_tone3:",
+ "category": "unicode9",
+ "emoji_order": "10092",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "handball_tone4": {
+ "unicode": "1f93e-1f3fe",
+ "unicode_alternates": "",
+ "name": "handball tone 4",
+ "shortname": ":handball_tone4:",
+ "category": "unicode9",
+ "emoji_order": "10093",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "handball_tone5": {
+ "unicode": "1f93e-1f3ff",
+ "unicode_alternates": "",
+ "name": "handball tone 5",
+ "shortname": ":handball_tone5:",
+ "category": "unicode9",
+ "emoji_order": "10094",
+ "aliases": [],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "juggling_tone1": {
+ "unicode": "1f939-1f3fb",
+ "unicode_alternates": "",
+ "name": "juggling tone 1",
+ "shortname": ":juggling_tone1:",
+ "category": "unicode9",
+ "emoji_order": "10095",
+ "aliases": [
+ ":juggler_tone1:"
+ ],
+ "aliases_ascii": [],
+ "keywords": []
+ },
+ "juggling_tone2": {
+ "unicode": "1f939-1f3fc",
+ "unicode_alternates": "",
+ "name": "juggling tone 2",
+ "shortname": ":juggling_tone2:",
+ "category": "unicode9",
+ "emoji_order": "10096",
+ "aliases": [
+ ":juggler_tone2:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "juggling_tone3": {
+ "unicode": "1f939-1f3fd",
+ "unicode_alternates": "",
+ "name": "juggling tone 3",
+ "shortname": ":juggling_tone3:",
+ "category": "unicode9",
+ "emoji_order": "10097",
+ "aliases": [
+ ":juggler_tone3:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "juggling_tone4": {
+ "unicode": "1f939-1f3fe",
+ "unicode_alternates": "",
+ "name": "juggling tone 4",
+ "shortname": ":juggling_tone4:",
+ "category": "unicode9",
+ "emoji_order": "10098",
+ "aliases": [
+ ":juggler_tone4:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ },
+ "juggling_tone5": {
+ "unicode": "1f939-1f3ff",
+ "unicode_alternates": "",
+ "name": "juggling tone 5",
+ "shortname": ":juggling_tone5:",
+ "category": "unicode9",
+ "emoji_order": "10099",
+ "aliases": [
+ ":juggler_tone5:"
+ ],
+ "aliases_ascii": [],
+ "keywords": [],
+ "duplicate": true
+ }
+}
diff --git a/sfx2/inc/SfxRedactionHelper.hxx b/sfx2/inc/SfxRedactionHelper.hxx
new file mode 100644
index 000000000..07e752d17
--- /dev/null
+++ b/sfx2/inc/SfxRedactionHelper.hxx
@@ -0,0 +1,146 @@
+/* -*- 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/.
+ */
+
+#ifndef INCLUDED_CUI_SOURCE_INC_SFXREDACTIONHELPER_HXX
+#define INCLUDED_CUI_SOURCE_INC_SFXREDACTIONHELPER_HXX
+
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/lang/XComponent.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/drawing/XDrawPage.hpp>
+
+#include <sal/types.h>
+#include <rtl/ustring.hxx>
+#include <tools/gen.hxx>
+
+#include <string_view>
+#include <vector>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::uno;
+
+class SfxRequest;
+class SfxStringItem;
+class GDIMetaFile;
+class DocumentToGraphicRenderer;
+class SfxViewFrame;
+struct RedactionTarget;
+
+namespace i18nutil
+{
+struct SearchOptions2;
+}
+
+struct PageMargins
+{
+ // Page margins in mm100th
+ sal_Int32 nTop;
+ sal_Int32 nBottom;
+ sal_Int32 nLeft;
+ sal_Int32 nRight;
+};
+
+/*
+ * Mostly a bunch of static methods to handle the redaction functionality at
+ * different points of the process.
+ **/
+class SfxRedactionHelper
+{
+public:
+ /// Checks to see if the request has a parameter of IsRedactMode:bool=true
+ static bool isRedactMode(const SfxRequest& rReq);
+ /*
+ * Returns the value of the given string param as an OUString
+ * Returns empty OUString if no param
+ * */
+ static OUString getStringParam(const SfxRequest& rReq, sal_uInt16 nParamId);
+ /*
+ * Creates metafiles from the pages of the given document,
+ * and pushes into the given vector.
+ * */
+ static void getPageMetaFilesFromDoc(std::vector<GDIMetaFile>& aMetaFiles,
+ std::vector<::Size>& aPageSizes, sal_Int32 nPages,
+ DocumentToGraphicRenderer& aRenderer);
+ /*
+ * Creates one shape and one draw page for each gdimetafile,
+ * and inserts the shapes into the newly created draw pages.
+ * */
+ static void
+ 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);
+ /*
+ * Makes the Redaction toolbar visible to the user.
+ * Meant to be called after converting a document to a Draw doc
+ * for redaction purposes.
+ * */
+ static void showRedactionToolbar(const SfxViewFrame* pViewFrame);
+
+ /*
+ * Used to get the page margins from the original/source Writer document. Then we apply these values to the
+ * pages inserted into Draw for redaction.
+ * */
+ static PageMargins
+ getPageMarginsForWriter(const css::uno::Reference<css::frame::XModel>& xModel);
+
+ /*
+ * Used to get the page margins from the original/source Calc document. Then we apply these values to the
+ * pages inserted into Draw for redaction.
+ * */
+ static PageMargins getPageMarginsForCalc(const css::uno::Reference<css::frame::XModel>& xModel);
+
+ /*
+ * Used to find the text portions to be redacted. Returns a list of rectangles to cover those
+ * areas to be redacted. Probably the most crucial part of the auto-redaction process.
+ * */
+ static void searchInMetaFile(const RedactionTarget& rRedactionTarget, const GDIMetaFile& rMtf,
+ std::vector<tools::Rectangle>& aRedactionRectangles,
+ const uno::Reference<XComponent>& xComponent);
+
+ /*
+ * Draws a redaction rectangle on the draw page referenced with its page number (0-based)
+ * */
+ static void addRedactionRectToPage(const uno::Reference<XComponent>& xComponent,
+ const uno::Reference<drawing::XDrawPage>& xPage,
+ const std::vector<tools::Rectangle>& aNewRectangles);
+
+ /*
+ * Search for the given term through the gdimetafile, which has the whole content of a draw page,
+ * and draw redaction rectangles to the appropriate positions with suitable sizes.
+ * */
+ static void autoRedactPage(const RedactionTarget& rRedactionTarget,
+ const GDIMetaFile& rGDIMetaFile,
+ const uno::Reference<drawing::XDrawPage>& xPage,
+ const uno::Reference<XComponent>& xComponent);
+
+ /// Fill the search options based on the given redaction target
+ static void fillSearchOptions(i18nutil::SearchOptions2& rSearchOpt,
+ const RedactionTarget& rTarget);
+
+private:
+ static constexpr std::u16string_view m_aPredefinedTargets[6] = {
+ u"\\b(?:\\d[ -]*?){13,16}\\b", //Credit card numbers
+ u"\\b[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}\\b", //Email addresses
+ u"\\b(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)"
+ "\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)"
+ "\\b", //IP addresses
+ u"([12]\\d{3}[./-](0[1-9]|1[0-2])[./"
+ "-](0[1-9]|[12]\\d|3[01]))|((0[1-9]|[12]\\d|3[01])[./-](0[1-9]|1[0-2])[./"
+ "-][12]\\d{3})", //Dates (numerical)
+ u"\\s*[a-zA-Z]{2}(?:\\s*\\d\\s*){6}[a-zA-Z]?\\s*", //National Insurance Number (UK)
+ u"([1-9])(?!\\1{2}-\\1{2}-\\1{4})[1-9]{2}-[1-9]{2}-[1-9]{4}" //Social Security Number (US)
+ };
+};
+
+#endif // INCLUDED_CUI_SOURCE_INC_SFXREDACTIONHELPER_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/sfx2/inc/arrdecl.hxx b/sfx2/inc/arrdecl.hxx
new file mode 100644
index 000000000..b777fad7a
--- /dev/null
+++ b/sfx2/inc/arrdecl.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_INC_ARRDECL_HXX
+#define INCLUDED_SFX2_INC_ARRDECL_HXX
+
+#include <vector>
+#include <memory>
+
+class SfxFilter;
+typedef ::std::vector<std::shared_ptr<const SfxFilter>> SfxFilterList_Impl;
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/inc/autoredactdialog.hxx b/sfx2/inc/autoredactdialog.hxx
new file mode 100644
index 000000000..28c2c561f
--- /dev/null
+++ b/sfx2/inc/autoredactdialog.hxx
@@ -0,0 +1,175 @@
+/* -*- 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/.
+ */
+
+#ifndef INCLUDED_SFX2_INC_AUTOREDACTDIALOG_HXX
+#define INCLUDED_SFX2_INC_AUTOREDACTDIALOG_HXX
+
+#include <memory>
+#include <sal/config.h>
+#include <sfx2/basedlgs.hxx>
+#include <sfx2/objsh.hxx>
+
+namespace weld
+{
+class Button;
+}
+namespace weld
+{
+class ComboBox;
+}
+namespace weld
+{
+class Label;
+}
+namespace weld
+{
+class Window;
+}
+namespace weld
+{
+class TreeView;
+}
+
+enum RedactionTargetType
+{
+ REDACTION_TARGET_TEXT,
+ REDACTION_TARGET_REGEX,
+ REDACTION_TARGET_PREDEFINED,
+ REDACTION_TARGET_UNKNOWN
+};
+
+/// Keeps information for a single redaction target
+struct RedactionTarget
+{
+ OUString sName;
+ RedactionTargetType sType;
+ OUString sContent;
+ bool bCaseSensitive;
+ bool bWholeWords;
+ sal_uInt32 nID;
+};
+
+/// Used to display the targets list
+class TargetsTable
+{
+ std::unique_ptr<weld::TreeView> m_xControl;
+ int GetRowByTargetName(std::u16string_view sName);
+
+public:
+ TargetsTable(std::unique_ptr<weld::TreeView> xControl);
+ void InsertTarget(RedactionTarget* pTarget);
+ RedactionTarget* GetTargetByName(std::u16string_view sName);
+ OUString GetNameProposal() const;
+
+ int get_selected_index() const { return m_xControl->get_selected_index(); }
+ std::vector<int> get_selected_rows() const { return m_xControl->get_selected_rows(); }
+ void clear() { m_xControl->clear(); }
+ void remove(int nRow) { m_xControl->remove(nRow); }
+ void select(int nRow) { m_xControl->select(nRow); }
+ OUString get_id(int nRow) const { return m_xControl->get_id(nRow); }
+
+ // Sync data on the targets box with the data on the target
+ void setRowData(int nRowIndex, const RedactionTarget* pTarget);
+
+ void connect_row_activated(const Link<weld::TreeView&, bool>& rLink)
+ {
+ m_xControl->connect_row_activated(rLink);
+ };
+};
+
+namespace sfx2
+{
+class FileDialogHelper;
+}
+
+enum class StartFileDialogType
+{
+ Open,
+ SaveAs
+};
+
+class SfxAutoRedactDialog final : public SfxDialogController
+{
+ SfxObjectShellLock m_xDocShell;
+ std::vector<std::pair<std::unique_ptr<RedactionTarget>, OUString>> m_aTableTargets;
+ std::unique_ptr<sfx2::FileDialogHelper> m_pFileDlg;
+ bool m_bIsValidState;
+ bool m_bTargetsCopied;
+
+ std::unique_ptr<weld::Label> m_xRedactionTargetsLabel;
+ std::unique_ptr<TargetsTable> m_xTargetsBox;
+ std::unique_ptr<weld::Button> m_xLoadBtn;
+ std::unique_ptr<weld::Button> m_xSaveBtn;
+ std::unique_ptr<weld::Button> m_xAddBtn;
+ std::unique_ptr<weld::Button> m_xEditBtn;
+ std::unique_ptr<weld::Button> m_xDeleteBtn;
+
+ DECL_LINK(Load, weld::Button&, void);
+ DECL_LINK(Save, weld::Button&, void);
+ DECL_LINK(AddHdl, weld::Button&, void);
+ DECL_LINK(EditHdl, weld::Button&, void);
+ DECL_LINK(DeleteHdl, weld::Button&, void);
+ DECL_LINK(DoubleClickEditHdl, weld::TreeView&, bool);
+ DECL_LINK(LoadHdl, sfx2::FileDialogHelper*, void);
+ DECL_LINK(SaveHdl, sfx2::FileDialogHelper*, void);
+
+ void StartFileDialog(StartFileDialogType nType, const OUString& rTitle);
+ /// Carry out proper addition both to the targets box, and to the tabletargets vector.
+ void addTarget(std::unique_ptr<RedactionTarget> pTarget);
+ /// Clear all targets both visually and from the targets vector
+ void clearTargets();
+
+public:
+ SfxAutoRedactDialog(weld::Window* pParent);
+ virtual ~SfxAutoRedactDialog() override;
+
+ /// Check if the dialog has any valid redaction targets.
+ bool hasTargets() const;
+ /// Check if the dialog is in a valid state.
+ bool isValidState() const { return m_bIsValidState; }
+ /** Copies targets vector
+ * Does a shallow copy.
+ * Returns true if successful.
+ */
+ bool getTargets(std::vector<std::pair<RedactionTarget, OUString>>& r_aTargets);
+};
+
+class SfxAddTargetDialog final : public weld::GenericDialogController
+{
+private:
+ std::unique_ptr<weld::Entry> m_xName;
+ std::unique_ptr<weld::ComboBox> m_xType;
+ std::unique_ptr<weld::Label> m_xLabelContent;
+ std::unique_ptr<weld::Entry> m_xContent;
+ std::unique_ptr<weld::Label> m_xLabelPredefContent;
+ std::unique_ptr<weld::ComboBox> m_xPredefContent;
+ std::unique_ptr<weld::CheckButton> m_xCaseSensitive;
+ std::unique_ptr<weld::CheckButton> m_xWholeWords;
+
+ DECL_LINK(SelectTypeHdl, weld::ComboBox&, void);
+
+public:
+ SfxAddTargetDialog(weld::Window* pWindow, const OUString& rName);
+ SfxAddTargetDialog(weld::Window* pWindow, const OUString& sName,
+ const RedactionTargetType& eTargetType, const OUString& sContent,
+ bool bCaseSensitive, bool bWholeWords);
+
+ OUString getName() const { return m_xName->get_text(); }
+ RedactionTargetType getType() const;
+ OUString getContent() const;
+ bool isCaseSensitive() const
+ {
+ return m_xCaseSensitive->get_state() == TriState::TRISTATE_TRUE;
+ }
+ bool isWholeWords() const { return m_xWholeWords->get_state() == TriState::TRISTATE_TRUE; }
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/sfx2/inc/bitmaps.hlst b/sfx2/inc/bitmaps.hlst
new file mode 100644
index 000000000..af7f9705e
--- /dev/null
+++ b/sfx2/inc/bitmaps.hlst
@@ -0,0 +1,94 @@
+/* -*- 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/.
+ */
+#pragma once
+
+#include <rtl/ustring.hxx>
+
+inline constexpr OUStringLiteral BMP_HELP_TOOLBOX_INDEX_ON = u"sfx2/res/indexon_small.png";
+inline constexpr OUStringLiteral BMP_HELP_TOOLBOX_INDEX_OFF = u"sfx2/res/indexoff_small.png";
+inline constexpr OUStringLiteral BMP_HELP_TOOLBOX_START = u"res/sc06303.png";
+inline constexpr OUStringLiteral BMP_HELP_TOOLBOX_PREV = u"res/sc06301.png";
+inline constexpr OUStringLiteral BMP_HELP_TOOLBOX_NEXT = u"res/sc06300.png";
+inline constexpr OUStringLiteral BMP_HELP_TOOLBOX_PRINT = u"res/sc05504.png";
+inline constexpr OUStringLiteral BMP_HELP_TOOLBOX_BOOKMARKS = u"sfx2/res/favourite.png";
+inline constexpr OUStringLiteral BMP_HELP_TOOLBOX_SEARCHDIALOG = u"sfx2/res/sc05961.png";
+inline constexpr OUStringLiteral BMP_HELP_TOOLBOX_COPY = u"sfx2/res/sc05711.png";
+inline constexpr OUStringLiteral BMP_HELP_CONTENT_BOOK_OPEN = u"sfx2/res/hlpbookopen.png";
+inline constexpr OUStringLiteral BMP_HELP_CONTENT_BOOK_CLOSED = u"sfx2/res/hlpbookclosed.png";
+inline constexpr OUStringLiteral BMP_HELP_CONTENT_DOC = u"sfx2/res/hlpdoc.png";
+
+//start, sfx2/source/sidebar/Theme.cxx
+inline constexpr OUStringLiteral CLOSEDOC = u"sfx2/res/closedoc.png";
+#define GRIP "sfx2/res/grip.png"
+#define OPEN_MORE "sfx2/res/symphony/open_more.png"
+#define MOREBUTTON "sfx2/res/symphony/morebutton.png"
+#define SIDEBAR_ANIMATION_LARGE "sfx2/res/symphony/sidebar-animation-large.png"
+#define SIDEBAR_GALLERY_LARGE "sfx2/res/symphony/sidebar-gallery-large.png"
+#define SIDEBAR_NAVIGATOR_LARGE "sfx2/res/symphony/sidebar-navigator-large.png"
+#define SIDEBAR_PROPERTY_LARGE "sfx2/res/symphony/sidebar-property-large.png"
+#define SIDEBAR_PROPERTY_SMALL "sfx2/res/symphony/sidebar-property-small.png"
+#define SIDEBAR_STYLE_LARGE "sfx2/res/symphony/sidebar-style-large.png"
+#define SIDEBAR_TEMPLATE_LARGE "sfx2/res/symphony/sidebar-template-large.png"
+#define SIDEBAR_TRANSITION_LARGE "sfx2/res/symphony/sidebar-transition-large.png"
+#define SIDEBAR_FUNCTIONS_LARGE "sfx2/res/symphony/sidebar-functions-large.png"
+//end, sfx2/source/sidebar/Theme.cxx
+
+inline constexpr OUStringLiteral SFX_THUMBNAIL_TEXT = u"res/ott_96_8.png";
+inline constexpr OUStringLiteral SFX_THUMBNAIL_SHEET = u"res/ots_96_8.png";
+inline constexpr OUStringLiteral SFX_THUMBNAIL_PRESENTATION = u"res/otp_96_8.png";
+inline constexpr OUStringLiteral SFX_THUMBNAIL_DRAWING = u"res/otg_96_8.png";
+
+inline constexpr OUStringLiteral SFX_FILE_THUMBNAIL_TEXT = u"res/writer128.png";
+inline constexpr OUStringLiteral SFX_FILE_THUMBNAIL_SHEET = u"res/calc128.png";
+inline constexpr OUStringLiteral SFX_FILE_THUMBNAIL_PRESENTATION = u"res/impress128.png";
+inline constexpr OUStringLiteral SFX_FILE_THUMBNAIL_DRAWING = u"res/draw128.png";
+inline constexpr OUStringLiteral SFX_FILE_THUMBNAIL_DATABASE = u"res/base128.png";
+inline constexpr OUStringLiteral SFX_FILE_THUMBNAIL_MATH = u"res/math128.png";
+inline constexpr OUStringLiteral SFX_FILE_THUMBNAIL_DEFAULT = u"res/main128.png";
+
+inline constexpr OUStringLiteral SFX_FILE_OVERLAY_TEXT = u"res/odt_48_8.png";
+inline constexpr OUStringLiteral SFX_FILE_OVERLAY_SHEET = u"res/ods_48_8.png";
+inline constexpr OUStringLiteral SFX_FILE_OVERLAY_PRESENTATION = u"res/odp_48_8.png";
+inline constexpr OUStringLiteral SFX_FILE_OVERLAY_DRAWING = u"res/odg_48_8.png";
+inline constexpr OUStringLiteral SFX_FILE_OVERLAY_DATABASE = u"res/odb_48_8.png";
+inline constexpr OUStringLiteral SFX_FILE_OVERLAY_MATH = u"res/odf_48_8.png";
+inline constexpr OUStringLiteral SFX_FILE_OVERLAY_DEFAULT = u"res/mainapp_48_8.png";
+
+inline constexpr OUStringLiteral SFX_THUMBNAIL_BASE_192 = u"res/base_thumbnail_192.png";
+inline constexpr OUStringLiteral SFX_THUMBNAIL_BASE_256 = u"res/base_thumbnail_256.png";
+
+inline constexpr OUStringLiteral BMP_RECENTDOC_REMOVE = u"res/recentdoc_remove.png";
+inline constexpr OUStringLiteral BMP_RECENTDOC_REMOVE_HIGHLIGHTED = u"res/recentdoc_remove_highlighted.png";
+inline constexpr OUStringLiteral BMP_DEFAULT = u"res/templatestar.png";
+
+inline constexpr OUStringLiteral BMP_128X128_CALC_DOC = u"sfx2/res/128x128_calc_doc-p.png";
+inline constexpr OUStringLiteral BMP_128X128_DRAW_DOC = u"sfx2/res/128x128_draw_doc-p.png";
+inline constexpr OUStringLiteral BMP_128X128_IMPRESS_DOC = u"sfx2/res/128x128_impress_doc-p.png";
+inline constexpr OUStringLiteral BMP_128X128_MATH_DOC = u"sfx2/res/128x128_math_doc-p.png";
+inline constexpr OUStringLiteral BMP_128X128_WRITER_DOC = u"sfx2/res/128x128_writer_doc-p.png";
+
+inline constexpr OUStringLiteral SIDEBAR_CLOSE_INDICATOR = u"cmd/lc_decrementlevel.png";
+inline constexpr OUStringLiteral BMP_ACTION_DEFAULT_WRITER = u"res/odt_16_8.png";
+inline constexpr OUStringLiteral BMP_ACTION_DEFAULT_CALC = u"res/ods_16_8.png";
+inline constexpr OUStringLiteral BMP_ACTION_DEFAULT_IMPRESS = u"res/odp_16_8.png";
+inline constexpr OUStringLiteral BMP_ACTION_DEFAULT_DRAW = u"res/odg_16_8.png";
+inline constexpr OUStringLiteral BMP_ACTION_IMPORT = u"cmd/sc_dataimport.png";
+inline constexpr OUStringLiteral BMP_ACTION_EXTENSIONS = u"cmd/sc_additionsdialog.png";
+inline constexpr OUStringLiteral BMP_ACTION_DELETE_CATEGORY = u"cmd/sc_delete.png";
+inline constexpr OUStringLiteral BMP_ACTION_NEW_CATEGORY = u"cmd/sc_add.png";
+inline constexpr OUStringLiteral BMP_ACTION_RENAME = u"cmd/sc_editdoc.png";
+
+inline constexpr OUStringLiteral BMP_MENU_OPEN = u"cmd/sc_opentemplate.png";
+inline constexpr OUStringLiteral BMP_MENU_EDIT = u"cmd/sc_opentemplate.png";
+inline constexpr OUStringLiteral BMP_MENU_MOVE = u"cmd/sc_move.png";
+inline constexpr OUStringLiteral BMP_MENU_RENAME = u"cmd/sc_editdoc.png";
+inline constexpr OUStringLiteral BMP_MENU_DELETE = u"cmd/sc_delete.png";
+inline constexpr OUStringLiteral BMP_MENU_EXPORT = u"cmd/sc_exportto.png";
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/sfx2/inc/bitset.hxx b/sfx2/inc/bitset.hxx
new file mode 100644
index 000000000..3573e9c23
--- /dev/null
+++ b/sfx2/inc/bitset.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_INC_BITSET_HXX
+#define INCLUDED_SFX2_INC_BITSET_HXX
+
+#include <sal/types.h>
+
+#include <memory>
+
+class IndexBitSet
+{
+private:
+ sal_uInt16 nBlocks;
+ std::unique_ptr<sal_uInt32[]> pBitmap;
+
+ IndexBitSet& operator|=( sal_uInt16 nBit );
+ IndexBitSet& operator-=( sal_uInt16 nBit );
+ bool Contains( sal_uInt16 nBit ) const;
+
+ IndexBitSet(IndexBitSet const &) = delete;
+ void operator =(IndexBitSet const &) = delete;
+
+public:
+ IndexBitSet();
+ ~IndexBitSet();
+
+ sal_uInt16 GetFreeIndex();
+ void ReleaseIndex(sal_uInt16 i){*this-=i;}
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/inc/bluthsndapi.hxx b/sfx2/inc/bluthsndapi.hxx
new file mode 100644
index 000000000..c95bccf89
--- /dev/null
+++ b/sfx2/inc/bluthsndapi.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_INC_BLUTHSNDAPI_HXX
+#define INCLUDED_SFX2_INC_BLUTHSNDAPI_HXX
+
+#include <com/sun/star/frame/XFrame.hpp>
+#include <sfx2/mailmodelapi.hxx>
+
+
+// class SfxBluetoothModel_Impl -----------------------------------------------
+
+class SfxBluetoothModel:public SfxMailModel
+{
+public:
+ SendMailResult SaveAndSend( const css::uno::Reference< css::frame::XFrame >& xFrame );
+ SendMailResult Send();
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/inc/charmapcontrol.hxx b/sfx2/inc/charmapcontrol.hxx
new file mode 100644
index 000000000..e2c1e1e07
--- /dev/null
+++ b/sfx2/inc/charmapcontrol.hxx
@@ -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 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+#include <sfx2/charwin.hxx>
+#include <svtools/toolbarmenu.hxx>
+#include <deque>
+
+class CharmapPopup;
+
+namespace com::sun::star::frame { class XFrame; }
+
+class SfxCharmapCtrl final : public WeldToolbarPopup
+{
+public:
+ explicit SfxCharmapCtrl(CharmapPopup* pControl, weld::Widget* pParent);
+ virtual ~SfxCharmapCtrl() override;
+
+ virtual void GrabFocus() override;
+
+private:
+ rtl::Reference<CharmapPopup> m_xControl;
+
+ ScopedVclPtr<VirtualDevice> m_xVirDev;
+
+ std::deque<OUString> m_aRecentCharList;
+ std::deque<OUString> m_aRecentCharFontList;
+ std::deque<OUString> m_aFavCharList;
+ std::deque<OUString> m_aFavCharFontList;
+
+ SvxCharView m_aRecentCharView[16];
+ SvxCharView m_aFavCharView[16];
+ std::unique_ptr<weld::Label> m_xRecentLabel;
+ std::unique_ptr<weld::Button> m_xDlgBtn;
+ std::unique_ptr<weld::CustomWeld> m_xRecentCharView[16];
+ std::unique_ptr<weld::CustomWeld> m_xFavCharView[16];
+
+ DECL_LINK(CharClickHdl, SvxCharView*, void);
+ DECL_LINK(OpenDlgHdl, weld::Button&, void);
+
+ void getFavCharacterList();
+ void updateFavCharControl();
+
+ void getRecentCharacterList(); //gets both recent char and recent char font list
+ void updateRecentCharControl();
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/inc/charmappopup.hxx b/sfx2/inc/charmappopup.hxx
new file mode 100644
index 000000000..eb847dc57
--- /dev/null
+++ b/sfx2/inc/charmappopup.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 .
+ */
+
+#pragma once
+
+#include <svtools/popupwindowcontroller.hxx>
+
+class CharmapPopup final : public svt::PopupWindowController
+{
+public:
+ CharmapPopup(const css::uno::Reference<css::uno::XComponentContext>& rContext);
+ virtual ~CharmapPopup() override;
+
+ virtual VclPtr<vcl::Window> createVclPopupWindow(vcl::Window* pParent) override;
+ virtual std::unique_ptr<WeldToolbarPopup> weldPopupWindow() override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+
+ // XInitialization
+ virtual void SAL_CALL initialize(const css::uno::Sequence<css::uno::Any>& rArguments) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/inc/checkin.hxx b/sfx2/inc/checkin.hxx
new file mode 100644
index 000000000..78535f7aa
--- /dev/null
+++ b/sfx2/inc/checkin.hxx
@@ -0,0 +1,31 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include <vcl/weld.hxx>
+
+class SfxCheckinDialog final : public weld::GenericDialogController
+{
+private:
+ std::unique_ptr<weld::TextView> m_xCommentED;
+ std::unique_ptr<weld::CheckButton> m_xMajorCB;
+ std::unique_ptr<weld::Button> m_xOKBtn;
+
+ DECL_LINK(OKHdl, weld::Button&, void);
+
+public:
+ SfxCheckinDialog(weld::Window* pParent);
+ virtual ~SfxCheckinDialog() override;
+
+ OUString GetComment() const;
+ bool IsMajor() const;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/inc/commandpopup/CommandPopup.hxx b/sfx2/inc/commandpopup/CommandPopup.hxx
new file mode 100644
index 000000000..a34425f0d
--- /dev/null
+++ b/sfx2/inc/commandpopup/CommandPopup.hxx
@@ -0,0 +1,114 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 <vcl/weld.hxx>
+
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/frame/XFrame.hpp>
+#include <com/sun/star/container/XIndexAccess.hpp>
+#include <com/sun/star/i18n/XCharacterClassification.hpp>
+#include <com/sun/star/util/XURLTransformer.hpp>
+
+#include <functional>
+#include <unordered_set>
+
+struct CurrentEntry final
+{
+ OUString m_aCommandURL;
+ OUString m_aTooltip;
+
+ CurrentEntry(OUString const& rCommandURL, OUString const& rTooltip)
+ : m_aCommandURL(rCommandURL)
+ , m_aTooltip(rTooltip)
+ {
+ }
+};
+
+struct MenuContent final
+{
+ OUString m_aCommandURL;
+ OUString m_aMenuLabel;
+ OUString m_aSearchableMenuLabel;
+ OUString m_aFullLabelWithPath;
+ OUString m_aTooltip;
+ std::vector<MenuContent> m_aSubMenuContent;
+};
+
+class MenuContentHandler final
+{
+private:
+ css::uno::Reference<css::uno::XComponentContext> m_xContext;
+ css::uno::Reference<css::frame::XFrame> m_xFrame;
+ css::uno::Reference<css::i18n::XCharacterClassification> m_xCharacterClassification;
+ css::uno::Reference<css::util::XURLTransformer> m_xURLTransformer;
+
+ MenuContent m_aMenuContent;
+ OUString m_sModuleLongName;
+ OUString toLower(OUString const& rString);
+ std::unordered_set<OUString> m_aAdded;
+
+public:
+ MenuContentHandler(css::uno::Reference<css::frame::XFrame> const& xFrame);
+
+ void gatherMenuContent(css::uno::Reference<css::container::XIndexAccess> const& xIndexAccess,
+ MenuContent& rMenuContent);
+
+ void findInMenu(OUString const& rText, std::unique_ptr<weld::TreeView>& rpCommandTreeView,
+ std::vector<CurrentEntry>& rCommandList);
+
+private:
+ void 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);
+
+ void addCommandIfPossible(MenuContent const& rMenuContent,
+ const std::unique_ptr<weld::TreeView>& rpCommandTreeView,
+ std::vector<CurrentEntry>& rCommandList);
+};
+
+class CommandListBox final
+{
+private:
+ std::unique_ptr<weld::Builder> mxBuilder;
+ std::unique_ptr<weld::Popover> mxPopover;
+ std::unique_ptr<weld::Entry> mpEntry;
+ std::unique_ptr<weld::TreeView> mpCommandTreeView;
+
+ std::vector<CurrentEntry> maCommandList;
+ std::unique_ptr<MenuContentHandler> mpMenuContentHandler;
+
+ DECL_LINK(QueryTooltip, const weld::TreeIter&, OUString);
+ DECL_LINK(RowActivated, weld::TreeView&, bool);
+ DECL_LINK(ModifyHdl, weld::Entry&, void);
+ DECL_LINK(TreeViewKeyPress, const KeyEvent&, bool);
+
+ void dispatchCommandAndClose(OUString const& rCommand);
+
+public:
+ CommandListBox(weld::Window* pParent, css::uno::Reference<css::frame::XFrame> const& xFrame);
+ void connect_closed(const Link<weld::Popover&, void>& rLink)
+ {
+ mxPopover->connect_closed(rLink);
+ }
+};
+
+class CommandPopupHandler final
+{
+private:
+ std::unique_ptr<CommandListBox> mpListBox;
+
+public:
+ void showPopup(weld::Window* pParent, css::uno::Reference<css::frame::XFrame> const& xFrame);
+ DECL_LINK(PopupModeEnd, weld::Popover&, void);
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/inc/dinfdlg.hrc b/sfx2/inc/dinfdlg.hrc
new file mode 100644
index 000000000..b0b72d868
--- /dev/null
+++ b/sfx2/inc/dinfdlg.hrc
@@ -0,0 +1,87 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this 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_INC_DINFDLG_HRC
+#define INCLUDED_SFX2_INC_DINFDLG_HRC
+
+#include <unotools/resmgr.hxx>
+
+#define NC_(Context, String) TranslateId(Context, reinterpret_cast<char const *>(u8##String))
+
+const TranslateId SFX_CB_PROPERTY_STRINGARRAY[] =
+{
+ NC_("SFX_CB_PROPERTY_STRINGARRAY", "Checked by"),
+ NC_("SFX_CB_PROPERTY_STRINGARRAY", "Client"),
+ NC_("SFX_CB_PROPERTY_STRINGARRAY", "Date completed"),
+ NC_("SFX_CB_PROPERTY_STRINGARRAY", "Department"),
+ NC_("SFX_CB_PROPERTY_STRINGARRAY", "Destinations"),
+ NC_("SFX_CB_PROPERTY_STRINGARRAY", "Disposition"),
+ NC_("SFX_CB_PROPERTY_STRINGARRAY", "Division"),
+ NC_("SFX_CB_PROPERTY_STRINGARRAY", "Document number"),
+ NC_("SFX_CB_PROPERTY_STRINGARRAY", "Editor"),
+ NC_("SFX_CB_PROPERTY_STRINGARRAY", "Email"),
+ NC_("SFX_CB_PROPERTY_STRINGARRAY", "Forward to"),
+ NC_("SFX_CB_PROPERTY_STRINGARRAY", "Group"),
+ NC_("SFX_CB_PROPERTY_STRINGARRAY", "Info"),
+ NC_("SFX_CB_PROPERTY_STRINGARRAY", "Language"),
+ NC_("SFX_CB_PROPERTY_STRINGARRAY", "Mailstop"),
+ NC_("SFX_CB_PROPERTY_STRINGARRAY", "Matter"),
+ NC_("SFX_CB_PROPERTY_STRINGARRAY", "Office"),
+ NC_("SFX_CB_PROPERTY_STRINGARRAY", "Owner"),
+ NC_("SFX_CB_PROPERTY_STRINGARRAY", "Project"),
+ NC_("SFX_CB_PROPERTY_STRINGARRAY", "Publisher"),
+ NC_("SFX_CB_PROPERTY_STRINGARRAY", "Purpose"),
+ NC_("SFX_CB_PROPERTY_STRINGARRAY", "Received from"),
+ NC_("SFX_CB_PROPERTY_STRINGARRAY", "Recorded by"),
+ NC_("SFX_CB_PROPERTY_STRINGARRAY", "Recorded date"),
+ NC_("SFX_CB_PROPERTY_STRINGARRAY", "Reference"),
+ NC_("SFX_CB_PROPERTY_STRINGARRAY", "Source"),
+ NC_("SFX_CB_PROPERTY_STRINGARRAY", "Status"),
+ NC_("SFX_CB_PROPERTY_STRINGARRAY", "Telephone number"),
+ NC_("SFX_CB_PROPERTY_STRINGARRAY", "Typist"),
+ NC_("SFX_CB_PROPERTY_STRINGARRAY", "URL")
+};
+
+enum CustomProperties : sal_Int32 {
+ Custom_Type_Unknown = 0,
+ Custom_Type_Text = 1,
+ Custom_Type_Number = 2,
+ Custom_Type_Date = 3,
+ Custom_Type_Boolean = 4,
+ Custom_Type_Duration = 5,
+ Custom_Type_Datetime = 6
+};
+
+const std::pair<TranslateId, CustomProperties> SFX_LB_PROPERTY_STRINGARRAY[] =
+{
+ { NC_("SFX_CB_PROPERTY_STRINGARRAY", "Text") , Custom_Type_Text },
+ { NC_("SFX_CB_PROPERTY_STRINGARRAY", "DateTime") , Custom_Type_Datetime },
+ { NC_("SFX_CB_PROPERTY_STRINGARRAY", "Date") , Custom_Type_Date },
+ { NC_("SFX_CB_PROPERTY_STRINGARRAY", "Duration") , Custom_Type_Duration },
+ { NC_("SFX_CB_PROPERTY_STRINGARRAY", "Number") , Custom_Type_Number },
+ { NC_("SFX_CB_PROPERTY_STRINGARRAY", "Yes or no") , Custom_Type_Boolean }
+};
+
+// accessibility descriptions that use %PRODUCTNAME, we set these explicitly because querying a11y descs
+// in order to change %PRODUCTNAME at runtime is expensive, so limit doing that as much as possible.
+#define STR_A11Y_DESC_USERDATA NC_("documentinfopage|extended_tip|userdatacb", "Saves the user's full name with the file. You can edit the name by choosing Tools - Options - %PRODUCTNAME - User Data.")
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/inc/doctempl.hrc b/sfx2/inc/doctempl.hrc
new file mode 100644
index 000000000..d6b98a0e2
--- /dev/null
+++ b/sfx2/inc/doctempl.hrc
@@ -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_INC_DOCTEMPL_HRC
+#define INCLUDED_SFX2_INC_DOCTEMPL_HRC
+
+#include <unotools/resmgr.hxx>
+
+#define NC_(Context, String) TranslateId(Context, reinterpret_cast<char const *>(u8##String))
+
+const TranslateId TEMPLATE_LONG_NAMES_ARY[] =
+{
+ NC_("TEMPLATE_LONG_NAMES_ARY", "My Templates"),
+ NC_("TEMPLATE_LONG_NAMES_ARY", "Styles"),
+ NC_("TEMPLATE_LONG_NAMES_ARY", "Business Correspondence"),
+ NC_("TEMPLATE_LONG_NAMES_ARY", "Other Business Documents"),
+ NC_("TEMPLATE_LONG_NAMES_ARY", "Personal Correspondence and Documents"),
+ NC_("TEMPLATE_LONG_NAMES_ARY", "Forms and Contracts"),
+ NC_("TEMPLATE_LONG_NAMES_ARY", "Finances"),
+ NC_("TEMPLATE_LONG_NAMES_ARY", "Education"),
+ NC_("TEMPLATE_LONG_NAMES_ARY", "Presentation Backgrounds"),
+ NC_("TEMPLATE_LONG_NAMES_ARY", "Presentations"),
+ NC_("TEMPLATE_LONG_NAMES_ARY", "Miscellaneous"),
+ NC_("TEMPLATE_LONG_NAMES_ARY", "Labels")
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/inc/emojicontrol.hxx b/sfx2/inc/emojicontrol.hxx
new file mode 100644
index 000000000..50b70dc57
--- /dev/null
+++ b/sfx2/inc/emojicontrol.hxx
@@ -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/.
+ */
+
+#pragma once
+
+#include <sal/config.h>
+#include <svtools/toolbarmenu.hxx>
+#include <vcl/customweld.hxx>
+
+namespace com::sun::star::frame
+{
+class XFrame;
+}
+
+class EmojiPopup;
+class EmojiView;
+class ThumbnailViewItem;
+enum class FILTER_CATEGORY;
+
+class SfxEmojiControl final : public WeldToolbarPopup
+
+{
+public:
+ explicit SfxEmojiControl(const EmojiPopup* pControl, weld::Widget* pParent);
+ virtual ~SfxEmojiControl() override;
+
+ virtual void GrabFocus() override;
+
+private:
+ static void ConvertLabelToUnicode(weld::ToggleButton& rBtn);
+
+ FILTER_CATEGORY getFilter(const weld::Toggleable& rBtn) const;
+
+ DECL_LINK(ActivatePageHdl, weld::Toggleable&, void);
+ DECL_STATIC_LINK(SfxEmojiControl, InsertHdl, ThumbnailViewItem*, void);
+
+ std::unique_ptr<weld::ToggleButton> mxPeopleBtn;
+ std::unique_ptr<weld::ToggleButton> mxNatureBtn;
+ std::unique_ptr<weld::ToggleButton> mxFoodBtn;
+ std::unique_ptr<weld::ToggleButton> mxActivityBtn;
+ std::unique_ptr<weld::ToggleButton> mxTravelBtn;
+ std::unique_ptr<weld::ToggleButton> mxObjectsBtn;
+ std::unique_ptr<weld::ToggleButton> mxSymbolsBtn;
+ std::unique_ptr<weld::ToggleButton> mxFlagsBtn;
+ std::unique_ptr<weld::ToggleButton> mxUnicode9Btn;
+ std::unique_ptr<EmojiView> mxEmojiView;
+ std::unique_ptr<weld::CustomWeld> mxEmojiWeld;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/inc/emojipopup.hxx b/sfx2/inc/emojipopup.hxx
new file mode 100644
index 000000000..9961c8a84
--- /dev/null
+++ b/sfx2/inc/emojipopup.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 .
+ */
+
+#pragma once
+
+#include <svtools/popupwindowcontroller.hxx>
+
+class EmojiPopup final : public svt::PopupWindowController
+{
+public:
+ EmojiPopup(const css::uno::Reference<css::uno::XComponentContext>& rContext);
+ virtual ~EmojiPopup() override;
+
+ virtual VclPtr<vcl::Window> createVclPopupWindow(vcl::Window* pParent) override;
+ virtual std::unique_ptr<WeldToolbarPopup> weldPopupWindow() override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+
+ // XInitialization
+ virtual void SAL_CALL initialize(const css::uno::Sequence<css::uno::Any>& rArguments) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/inc/emojiview.hxx b/sfx2/inc/emojiview.hxx
new file mode 100644
index 000000000..8328f2f41
--- /dev/null
+++ b/sfx2/inc/emojiview.hxx
@@ -0,0 +1,76 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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/thumbnailview.hxx>
+
+//unicode item defines
+#define ITEM_MAX_WIDTH 30
+#define ITEM_MAX_HEIGHT 30
+#define ITEM_PADDING 5
+#define ITEM_MAX_TEXT_LENGTH 10
+
+enum class FILTER_CATEGORY
+{
+ PEOPLE,
+ NATURE,
+ FOOD,
+ ACTIVITY,
+ TRAVEL,
+ OBJECTS,
+ SYMBOLS,
+ FLAGS,
+ UNICODE9
+};
+
+// Display unicode emojis depending on the category
+class ViewFilter_Category final
+{
+public:
+ ViewFilter_Category(FILTER_CATEGORY rCategory)
+ : mCategory(rCategory)
+ {
+ }
+
+ bool operator()(const ThumbnailViewItem* pItem);
+
+ static bool isFilteredCategory(FILTER_CATEGORY filter, std::u16string_view rCategory);
+
+private:
+ FILTER_CATEGORY mCategory;
+};
+
+class EmojiView final : public ThumbnailView
+{
+public:
+ EmojiView(std::unique_ptr<weld::ScrolledWindow> xWindow);
+
+ virtual ~EmojiView() override;
+
+ // Fill view with emojis
+ void Populate();
+
+ void setInsertEmojiHdl(const Link<ThumbnailViewItem*, void>& rLink);
+
+ void AppendItem(const OUString& rTitle, const OUString& rCategory, const OUString& rName);
+
+private:
+ virtual bool MouseButtonDown(const MouseEvent& rMEvt) override;
+
+ virtual void SetDrawingArea(weld::DrawingArea* pDrawingArea) override;
+
+ virtual bool KeyInput(const KeyEvent& rKEvt) override;
+
+ std::string msJSONData;
+
+ Link<ThumbnailViewItem*, void> maInsertEmojiHdl;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/inc/emojiviewitem.hxx b/sfx2/inc/emojiviewitem.hxx
new file mode 100644
index 000000000..61a330673
--- /dev/null
+++ b/sfx2/inc/emojiviewitem.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/.
+ */
+
+#pragma once
+
+#include <sfx2/thumbnailviewitem.hxx>
+
+class EmojiViewItem final : public ThumbnailViewItem
+{
+public:
+ EmojiViewItem(ThumbnailView& rView, sal_uInt16 nId);
+
+ virtual ~EmojiViewItem () override;
+
+ void setCategory (const OUString &rCategory) { msCategory = rCategory; }
+
+ const OUString& getCategory () const { return msCategory; }
+
+ virtual void Paint (drawinglayer::processor2d::BaseProcessor2D *pProcessor,
+ const ThumbnailItemAttributes *pAttrs) override;
+
+ virtual void calculateItemsPosition (const tools::Long nThumbnailHeight,
+ const tools::Long nPadding, sal_uInt32 nMaxTextLength,
+ const ThumbnailItemAttributes *pAttrs) override;
+private:
+ OUString msCategory;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/inc/fwkhelper.hxx b/sfx2/inc/fwkhelper.hxx
new file mode 100644
index 000000000..d206957c9
--- /dev/null
+++ b/sfx2/inc/fwkhelper.hxx
@@ -0,0 +1,31 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SFX2_INC_FWKHELPER_HXX
+#define INCLUDED_SFX2_INC_FWKHELPER_HXX
+
+#include <sal/config.h>
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/frame/XFrame.hpp>
+
+void RefreshToolbars(css::uno::Reference<css::frame::XFrame> const& rFrame);
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/inc/guisaveas.hxx b/sfx2/inc/guisaveas.hxx
new file mode 100644
index 000000000..c19f78e3f
--- /dev/null
+++ b/sfx2/inc/guisaveas.hxx
@@ -0,0 +1,87 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this 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_INC_GUISAVEAS_HXX
+#define INCLUDED_SFX2_INC_GUISAVEAS_HXX
+
+#include <sal/config.h>
+
+#include <string_view>
+
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/container/XNameAccess.hpp>
+#include <com/sun/star/container/XContainerQuery.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/frame/XModuleManager2.hpp>
+
+#include <sfx2/signaturestate.hxx>
+
+
+namespace com::sun::star::document { class XDocumentProperties; }
+
+namespace weld { class Window; }
+class ModelData_Impl;
+
+class SfxStoringHelper
+{
+ friend class ModelData_Impl;
+
+private:
+ css::uno::Reference< css::container::XNameAccess > m_xFilterCFG;
+ css::uno::Reference< css::container::XContainerQuery > m_xFilterQuery;
+ css::uno::Reference< css::frame::XModuleManager2 > m_xModuleManager;
+
+ css::uno::Reference< css::container::XNameAccess > const & GetFilterConfiguration();
+ css::uno::Reference< css::container::XContainerQuery > const & GetFilterQuery();
+ css::uno::Reference< css::frame::XModuleManager2 > const & GetModuleManager();
+
+public:
+ SfxStoringHelper();
+
+ bool GUIStoreModel(
+ const css::uno::Reference< css::frame::XModel >& xModel,
+ std::u16string_view aSlotName,
+ css::uno::Sequence< css::beans::PropertyValue >& aArgsSequence,
+ bool bPreselectPassword,
+ SignatureState nDocumentSignatureState );
+
+ static bool CheckFilterOptionsAppearance(
+ const css::uno::Reference< css::container::XNameAccess >& xFilterCFG,
+ const OUString& aFilterName );
+
+
+ static void SetDocInfoState(
+ const css::uno::Reference< css::frame::XModel >& xModel,
+ const css::uno::Reference< css::document::XDocumentProperties>& i_xOldDocInfo );
+
+ static bool WarnUnacceptableFormat(
+ const css::uno::Reference< css::frame::XModel >& xModel,
+ std::u16string_view aOldUIName,
+ const OUString& aDefExtension,
+ bool rDefaultIsAlien );
+
+ static css::uno::Reference<css::awt::XWindow> GetModelXWindow(const css::uno::Reference<css::frame::XModel>& rModel);
+ static weld::Window* GetModelWindow( const css::uno::Reference< css::frame::XModel >& xModel );
+
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/inc/inettbc.hxx b/sfx2/inc/inettbc.hxx
new file mode 100644
index 000000000..1e695a9e4
--- /dev/null
+++ b/sfx2/inc/inettbc.hxx
@@ -0,0 +1,64 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this 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_INC_INETTBC_HXX
+#define INCLUDED_SFX2_INC_INETTBC_HXX
+
+#include <rtl/ustring.hxx>
+#include <sfx2/tbxctrl.hxx>
+
+namespace weld { class ComboBox; };
+class URLBoxItemWindow;
+class SvtURLBox;
+
+class SfxURLToolBoxControl_Impl final : public SfxToolBoxControl
+{
+private:
+ bool m_bModified;
+
+ SvtURLBox* GetURLBox() const;
+ URLBoxItemWindow* GetURLBoxItemWindow() const;
+ void OpenURL( const OUString& rName ) const;
+
+ DECL_LINK(OpenHdl, weld::ComboBox&, bool);
+ DECL_LINK(SelectHdl, weld::ComboBox&, void);
+
+ struct ExecuteInfo
+ {
+ css::uno::Reference< css::frame::XDispatch > xDispatch;
+ css::util::URL aTargetURL;
+ css::uno::Sequence< css::beans::PropertyValue > aArgs;
+ };
+
+ DECL_STATIC_LINK( SfxURLToolBoxControl_Impl, ExecuteHdl_Impl, void*, void );
+
+public:
+
+ SFX_DECL_TOOLBOX_CONTROL();
+
+ SfxURLToolBoxControl_Impl( sal_uInt16 nSlotId, ToolBoxItemId nId, ToolBox& rBox );
+ virtual ~SfxURLToolBoxControl_Impl() override;
+
+ virtual VclPtr<InterimItemWindow> CreateItemWindow(vcl::Window* pParent) override;
+ virtual void StateChangedAtToolBoxControl( sal_uInt16 nSID, SfxItemState eState, const SfxPoolItem* pState ) override;
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/inc/notebookbar/NotebookbarTabControl.hxx b/sfx2/inc/notebookbar/NotebookbarTabControl.hxx
new file mode 100644
index 000000000..ebbffc4f9
--- /dev/null
+++ b/sfx2/inc/notebookbar/NotebookbarTabControl.hxx
@@ -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/.
+ */
+
+#pragma once
+
+#include <vcl/tabctrl.hxx>
+
+#include <com/sun/star/frame/XFrame.hpp>
+
+namespace com::sun::star::ui { class XUIConfigurationListener; }
+namespace com::sun::star::uno { class XComponentContext; }
+
+class NotebookbarTabControl final : public NotebookbarTabControlBase
+{
+friend class ChangedUIEventListener;
+
+public:
+ NotebookbarTabControl( Window* pParent );
+ ~NotebookbarTabControl() override;
+
+ virtual void KeyInput( const KeyEvent& rKEvt ) override;
+ virtual bool EventNotify( NotifyEvent& rNEvt ) override;
+ virtual void StateChanged(StateChangedType nStateChange) override;
+ virtual Size calculateRequisition() const override;
+
+private:
+ static void FillShortcutsToolBox(css::uno::Reference<css::uno::XComponentContext> const & xContext,
+ const css::uno::Reference<css::frame::XFrame>& xFrame,
+ const OUString& aModuleName,
+ ToolBox* pShortcuts
+ );
+ void ArrowStops( sal_uInt16 nCode );
+
+ DECL_LINK(OpenNotebookbarPopupMenu, NotebookBar*, void);
+
+ css::uno::Reference<css::ui::XUIConfigurationListener> m_pListener;
+ css::uno::Reference<css::frame::XFrame> m_xFrame;
+ bool m_bInitialized;
+ bool m_bInvalidate;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/inc/pch/precompiled_sfx.cxx b/sfx2/inc/pch/precompiled_sfx.cxx
new file mode 100644
index 000000000..b741a11c2
--- /dev/null
+++ b/sfx2/inc/pch/precompiled_sfx.cxx
@@ -0,0 +1,12 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 "precompiled_sfx.hxx"
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/inc/pch/precompiled_sfx.hxx b/sfx2/inc/pch/precompiled_sfx.hxx
new file mode 100644
index 000000000..1fa9bcf42
--- /dev/null
+++ b/sfx2/inc/pch/precompiled_sfx.hxx
@@ -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 has been autogenerated by update_pch.sh. It is possible to edit it
+ manually (such as when an include file has been moved/renamed/removed). All such
+ manual changes will be rewritten by the next run of update_pch.sh (which presumably
+ also fixes all possible problems, so it's usually better to use it).
+
+ Generated on 2022-01-26 09:15:03 using:
+ ./bin/update_pch sfx2 sfx --cutoff=3 --exclude:system --exclude:module --exclude:local
+
+ If after updating build fails, use the following command to locate conflicting headers:
+ ./bin/update_pch_bisect ./sfx2/inc/pch/precompiled_sfx.hxx "make sfx2.build" --find-conflicts
+*/
+
+#include <sal/config.h>
+#if PCH_LEVEL >= 1
+#include <algorithm>
+#include <assert.h>
+#include <cassert>
+#include <chrono>
+#include <condition_variable>
+#include <cstddef>
+#include <cstdlib>
+#include <deque>
+#include <functional>
+#include <initializer_list>
+#include <limits>
+#include <map>
+#include <memory>
+#include <mutex>
+#include <new>
+#include <optional>
+#include <ostream>
+#include <set>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <string>
+#include <string_view>
+#include <type_traits>
+#include <unordered_map>
+#include <utility>
+#include <vector>
+#include <boost/property_tree/json_parser.hpp>
+#include <boost/property_tree/ptree.hpp>
+#endif // PCH_LEVEL >= 1
+#if PCH_LEVEL >= 2
+#include <osl/conditn.hxx>
+#include <osl/diagnose.h>
+#include <osl/file.hxx>
+#include <osl/interlck.h>
+#include <osl/module.h>
+#include <osl/module.hxx>
+#include <osl/mutex.hxx>
+#include <osl/process.h>
+#include <osl/security.hxx>
+#include <osl/thread.h>
+#include <osl/thread.hxx>
+#include <osl/time.h>
+#include <rtl/alloc.h>
+#include <rtl/bootstrap.hxx>
+#include <rtl/byteseq.hxx>
+#include <rtl/character.hxx>
+#include <rtl/instance.hxx>
+#include <rtl/malformeduriexception.hxx>
+#include <rtl/math.hxx>
+#include <rtl/ref.hxx>
+#include <rtl/strbuf.hxx>
+#include <rtl/string.h>
+#include <rtl/string.hxx>
+#include <rtl/stringconcat.hxx>
+#include <rtl/stringutils.hxx>
+#include <rtl/tencinfo.h>
+#include <rtl/textcvt.h>
+#include <rtl/textenc.h>
+#include <rtl/uri.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <rtl/ustring.h>
+#include <rtl/ustring.hxx>
+#include <sal/backtrace.hxx>
+#include <sal/log.hxx>
+#include <sal/macros.h>
+#include <sal/saldllapi.h>
+#include <sal/types.h>
+#include <vcl/EnumContext.hxx>
+#include <vcl/InterimItemWindow.hxx>
+#include <vcl/Scanline.hxx>
+#include <vcl/alpha.hxx>
+#include <vcl/bitmapex.hxx>
+#include <vcl/builder.hxx>
+#include <vcl/commandevent.hxx>
+#include <vcl/commandinfoprovider.hxx>
+#include <vcl/customweld.hxx>
+#include <vcl/dibtools.hxx>
+#include <vcl/dllapi.h>
+#include <vcl/errcode.hxx>
+#include <vcl/event.hxx>
+#include <vcl/font.hxx>
+#include <vcl/gdimtf.hxx>
+#include <vcl/gfxlink.hxx>
+#include <vcl/gradient.hxx>
+#include <vcl/graph.hxx>
+#include <vcl/graphicfilter.hxx>
+#include <vcl/help.hxx>
+#include <vcl/idle.hxx>
+#include <vcl/image.hxx>
+#include <vcl/imapobj.hxx>
+#include <vcl/keycod.hxx>
+#include <vcl/keycodes.hxx>
+#include <vcl/layout.hxx>
+#include <vcl/outdev.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/syswin.hxx>
+#include <vcl/timer.hxx>
+#include <vcl/toolbox.hxx>
+#include <vcl/toolboxid.hxx>
+#include <vcl/vclenum.hxx>
+#include <vcl/vclptr.hxx>
+#include <vcl/virdev.hxx>
+#include <vcl/weld.hxx>
+#include <vcl/weldutils.hxx>
+#include <vcl/window.hxx>
+#include <vcl/windowstate.hxx>
+#endif // PCH_LEVEL >= 2
+#if PCH_LEVEL >= 3
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/polygon/b2dpolypolygon.hxx>
+#include <basegfx/range/b2drange.hxx>
+#include <basic/basicdllapi.h>
+#include <basic/basicmanagerrepository.hxx>
+#include <basic/basmgr.hxx>
+#include <basic/sbdef.hxx>
+#include <basic/sberrors.hxx>
+#include <basic/sbstar.hxx>
+#include <basic/sbxdef.hxx>
+#include <com/sun/star/awt/XTopWindow.hpp>
+#include <com/sun/star/awt/XWindow.hpp>
+#include <com/sun/star/awt/XWindowPeer.hpp>
+#include <com/sun/star/beans/NamedValue.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/beans/PropertyExistException.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/beans/StringPair.hpp>
+#include <com/sun/star/beans/XPropertyContainer.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/beans/XPropertySetInfo.hpp>
+#include <com/sun/star/container/XChild.hpp>
+#include <com/sun/star/container/XContainerQuery.hpp>
+#include <com/sun/star/container/XIndexAccess.hpp>
+#include <com/sun/star/container/XNameAccess.hpp>
+#include <com/sun/star/document/DocumentProperties.hpp>
+#include <com/sun/star/document/MacroExecMode.hpp>
+#include <com/sun/star/document/UpdateDocMode.hpp>
+#include <com/sun/star/document/XCmisDocument.hpp>
+#include <com/sun/star/document/XDocumentProperties.hpp>
+#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
+#include <com/sun/star/document/XExporter.hpp>
+#include <com/sun/star/document/XFilter.hpp>
+#include <com/sun/star/document/XTypeDetection.hpp>
+#include <com/sun/star/document/XViewDataSupplier.hpp>
+#include <com/sun/star/drawing/XDrawPagesSupplier.hpp>
+#include <com/sun/star/embed/ElementModes.hpp>
+#include <com/sun/star/embed/EmbedStates.hpp>
+#include <com/sun/star/embed/XEmbeddedObject.hpp>
+#include <com/sun/star/embed/XStorage.hpp>
+#include <com/sun/star/embed/XTransactedObject.hpp>
+#include <com/sun/star/frame/Desktop.hpp>
+#include <com/sun/star/frame/DispatchResultState.hpp>
+#include <com/sun/star/frame/Frame.hpp>
+#include <com/sun/star/frame/FrameSearchFlag.hpp>
+#include <com/sun/star/frame/ModuleManager.hpp>
+#include <com/sun/star/frame/UnknownModuleException.hpp>
+#include <com/sun/star/frame/XComponentLoader.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/XDispatchRecorderSupplier.hpp>
+#include <com/sun/star/frame/XDispatchResultListener.hpp>
+#include <com/sun/star/frame/XFrame.hpp>
+#include <com/sun/star/frame/XLayoutManager.hpp>
+#include <com/sun/star/frame/XLoadable.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/frame/XStatusListener.hpp>
+#include <com/sun/star/frame/XStorable.hpp>
+#include <com/sun/star/frame/XSynchronousFrameLoader.hpp>
+#include <com/sun/star/frame/XTitle.hpp>
+#include <com/sun/star/frame/XToolbarController.hpp>
+#include <com/sun/star/frame/status/ItemStatus.hpp>
+#include <com/sun/star/frame/status/Visibility.hpp>
+#include <com/sun/star/frame/theGlobalEventBroadcaster.hpp>
+#include <com/sun/star/graphic/XGraphic.hpp>
+#include <com/sun/star/graphic/XPrimitive2D.hpp>
+#include <com/sun/star/io/IOException.hpp>
+#include <com/sun/star/io/WrongFormatException.hpp>
+#include <com/sun/star/io/XInputStream.hpp>
+#include <com/sun/star/io/XOutputStream.hpp>
+#include <com/sun/star/io/XStream.hpp>
+#include <com/sun/star/lang/DisposedException.hpp>
+#include <com/sun/star/lang/EventObject.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <com/sun/star/lang/Locale.hpp>
+#include <com/sun/star/lang/NoSupportException.hpp>
+#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
+#include <com/sun/star/lang/XComponent.hpp>
+#include <com/sun/star/lang/XInitialization.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/lang/XTypeProvider.hpp>
+#include <com/sun/star/lang/XUnoTunnel.hpp>
+#include <com/sun/star/sdbc/XResultSet.hpp>
+#include <com/sun/star/sdbc/XRow.hpp>
+#include <com/sun/star/security/DocumentDigitalSignatures.hpp>
+#include <com/sun/star/security/DocumentSignatureInformation.hpp>
+#include <com/sun/star/style/XStyleFamiliesSupplier.hpp>
+#include <com/sun/star/task/ErrorCodeIOException.hpp>
+#include <com/sun/star/task/ErrorCodeRequest.hpp>
+#include <com/sun/star/task/InteractionHandler.hpp>
+#include <com/sun/star/task/XInteractionHandler.hpp>
+#include <com/sun/star/task/XInteractionRequest.hpp>
+#include <com/sun/star/task/XStatusIndicator.hpp>
+#include <com/sun/star/ucb/CommandAbortedException.hpp>
+#include <com/sun/star/ucb/ContentCreationException.hpp>
+#include <com/sun/star/ucb/NameClash.hpp>
+#include <com/sun/star/ucb/XCommandEnvironment.hpp>
+#include <com/sun/star/ucb/XContent.hpp>
+#include <com/sun/star/ucb/XContentAccess.hpp>
+#include <com/sun/star/ui/ContextChangeEventMultiplexer.hpp>
+#include <com/sun/star/ui/XSidebarPanel.hpp>
+#include <com/sun/star/ui/XUIElement.hpp>
+#include <com/sun/star/ui/dialogs/CommonFilePickerElementIds.hpp>
+#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/XExecutableDialog.hpp>
+#include <com/sun/star/ui/dialogs/XFilePicker3.hpp>
+#include <com/sun/star/ui/dialogs/XFilePickerControlAccess.hpp>
+#include <com/sun/star/uno/Any.h>
+#include <com/sun/star/uno/Any.hxx>
+#include <com/sun/star/uno/Exception.hpp>
+#include <com/sun/star/uno/Reference.h>
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/uno/Sequence.h>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/uno/Type.h>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/uno/XInterface.hpp>
+#include <com/sun/star/uri/UriReferenceFactory.hpp>
+#include <com/sun/star/uri/XVndSunStarScriptUrl.hpp>
+#include <com/sun/star/util/CloseVetoException.hpp>
+#include <com/sun/star/util/RevisionTag.hpp>
+#include <com/sun/star/util/URL.hpp>
+#include <com/sun/star/util/URLTransformer.hpp>
+#include <com/sun/star/util/XAccounting.hpp>
+#include <com/sun/star/util/XCloneable.hpp>
+#include <com/sun/star/util/XCloseable.hpp>
+#include <com/sun/star/util/XModifiable.hpp>
+#include <com/sun/star/util/XURLTransformer.hpp>
+#include <com/sun/star/view/XSelectionSupplier.hpp>
+#include <com/sun/star/xml/sax/Parser.hpp>
+#include <comphelper/compbase.hxx>
+#include <comphelper/comphelperdllapi.h>
+#include <comphelper/dispatchcommand.hxx>
+#include <comphelper/docpasswordhelper.hxx>
+#include <comphelper/documentconstants.hxx>
+#include <comphelper/fileformat.h>
+#include <comphelper/fileurl.hxx>
+#include <comphelper/interaction.hxx>
+#include <comphelper/interfacecontainer2.hxx>
+#include <comphelper/interfacecontainer3.hxx>
+#include <comphelper/lok.hxx>
+#include <comphelper/multicontainer2.hxx>
+#include <comphelper/namedvaluecollection.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/propertysequence.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <comphelper/sequence.hxx>
+#include <comphelper/sequenceashashmap.hxx>
+#include <comphelper/servicehelper.hxx>
+#include <comphelper/storagehelper.hxx>
+#include <comphelper/string.hxx>
+#include <comphelper/types.hxx>
+#include <cppu/unotype.hxx>
+#include <cppuhelper/cppuhelperdllapi.h>
+#include <cppuhelper/exc_hlp.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <cppuhelper/weak.hxx>
+#include <cppuhelper/weakref.hxx>
+#include <drawinglayer/drawinglayerdllapi.h>
+#include <drawinglayer/geometry/viewinformation2d.hxx>
+#include <drawinglayer/primitive2d/BufferedDecompositionPrimitive2D.hxx>
+#include <drawinglayer/primitive2d/CommonTypes.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonSelectionPrimitive2D.hxx>
+#include <drawinglayer/primitive2d/Primitive2DContainer.hxx>
+#include <drawinglayer/primitive2d/Primitive2DVisitor.hxx>
+#include <drawinglayer/primitive2d/baseprimitive2d.hxx>
+#include <drawinglayer/primitive2d/textlayoutdevice.hxx>
+#include <drawinglayer/processor2d/baseprocessor2d.hxx>
+#include <framework/fwkdllapi.h>
+#include <framework/interaction.hxx>
+#include <i18nlangtag/lang.h>
+#include <i18nlangtag/languagetag.hxx>
+#include <o3tl/cow_wrapper.hxx>
+#include <o3tl/deleter.hxx>
+#include <o3tl/safeint.hxx>
+#include <o3tl/string_view.hxx>
+#include <o3tl/typed_flags_set.hxx>
+#include <officecfg/Office/Common.hxx>
+#include <salhelper/simplereferenceobject.hxx>
+#include <sot/exchange.hxx>
+#include <sot/formats.hxx>
+#include <sot/storage.hxx>
+#include <svl/SfxBroadcaster.hxx>
+#include <svl/eitem.hxx>
+#include <svl/hint.hxx>
+#include <svl/intitem.hxx>
+#include <svl/itemiter.hxx>
+#include <svl/itempool.hxx>
+#include <svl/itemset.hxx>
+#include <svl/lockfilecommon.hxx>
+#include <svl/macitem.hxx>
+#include <svl/poolitem.hxx>
+#include <svl/slstitm.hxx>
+#include <svl/stritem.hxx>
+#include <svl/style.hxx>
+#include <svl/svdde.hxx>
+#include <svl/svldllapi.h>
+#include <svl/undo.hxx>
+#include <svl/urihelper.hxx>
+#include <svl/visitem.hxx>
+#include <svl/whiter.hxx>
+#include <svtools/ehdl.hxx>
+#include <svtools/imagemgr.hxx>
+#include <svtools/miscopt.hxx>
+#include <svtools/sfxecode.hxx>
+#include <svtools/soerr.hxx>
+#include <svtools/svtdllapi.h>
+#include <toolkit/helper/convert.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+#include <tools/color.hxx>
+#include <tools/datetime.hxx>
+#include <tools/debug.hxx>
+#include <tools/degree.hxx>
+#include <tools/diagnose_ex.h>
+#include <tools/fract.hxx>
+#include <tools/gen.hxx>
+#include <tools/globname.hxx>
+#include <tools/json_writer.hxx>
+#include <tools/link.hxx>
+#include <tools/long.hxx>
+#include <tools/poly.hxx>
+#include <tools/ref.hxx>
+#include <tools/solar.h>
+#include <tools/stream.hxx>
+#include <tools/svborder.hxx>
+#include <tools/toolsdllapi.h>
+#include <tools/urlobj.hxx>
+#include <typelib/typedescription.h>
+#include <ucbhelper/content.hxx>
+#include <uno/sequence2.h>
+#include <unotools/configmgr.hxx>
+#include <unotools/confignode.hxx>
+#include <unotools/eventcfg.hxx>
+#include <unotools/historyoptions.hxx>
+#include <unotools/localedatawrapper.hxx>
+#include <unotools/mediadescriptor.hxx>
+#include <unotools/moduleoptions.hxx>
+#include <unotools/options.hxx>
+#include <unotools/pathoptions.hxx>
+#include <unotools/resmgr.hxx>
+#include <unotools/saveopt.hxx>
+#include <unotools/securityoptions.hxx>
+#include <unotools/streamwrap.hxx>
+#include <unotools/syslocale.hxx>
+#include <unotools/tempfile.hxx>
+#include <unotools/ucbhelper.hxx>
+#include <unotools/ucbstreamhelper.hxx>
+#include <unotools/unotoolsdllapi.h>
+#include <unotools/useroptions.hxx>
+#include <unotools/viewoptions.hxx>
+#endif // PCH_LEVEL >= 3
+#if PCH_LEVEL >= 4
+#include <appbaslib.hxx>
+#include <appdata.hxx>
+#include <appopen.hxx>
+#include <eventsupplier.hxx>
+#include <helper.hxx>
+#include <helpids.h>
+#include <itemdel.hxx>
+#include <nochaos.hxx>
+#include <objshimp.hxx>
+#include <openflag.hxx>
+#include <openuriexternally.hxx>
+#include <openurlhint.hxx>
+#include <sfx2/StyleManager.hxx>
+#include <sfx2/app.hxx>
+#include <sfx2/basedlgs.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/childwin.hxx>
+#include <sfx2/classificationhelper.hxx>
+#include <sfx2/ctrlitem.hxx>
+#include <sfx2/devtools/DevelopmentToolDockingWindow.hxx>
+#include <sfx2/dinfdlg.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/dllapi.h>
+#include <sfx2/docfac.hxx>
+#include <sfx2/docfile.hxx>
+#include <sfx2/docfilt.hxx>
+#include <sfx2/dockwin.hxx>
+#include <sfx2/doctempl.hxx>
+#include <sfx2/event.hxx>
+#include <sfx2/fcontnr.hxx>
+#include <sfx2/filedlghelper.hxx>
+#include <sfx2/flatpak.hxx>
+#include <sfx2/frame.hxx>
+#include <sfx2/frmdescr.hxx>
+#include <sfx2/infobar.hxx>
+#include <sfx2/inputdlg.hxx>
+#include <sfx2/ipclient.hxx>
+#include <sfx2/linkmgr.hxx>
+#include <sfx2/lnkbase.hxx>
+#include <sfx2/lokhelper.hxx>
+#include <sfx2/module.hxx>
+#include <sfx2/msg.hxx>
+#include <sfx2/msgpool.hxx>
+#include <sfx2/newstyle.hxx>
+#include <sfx2/notebookbar/SfxNotebookBar.hxx>
+#include <sfx2/objface.hxx>
+#include <sfx2/objitem.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/passwd.hxx>
+#include <sfx2/printer.hxx>
+#include <sfx2/progress.hxx>
+#include <sfx2/request.hxx>
+#include <sfx2/sfxbasecontroller.hxx>
+#include <sfx2/sfxbasemodel.hxx>
+#include <sfx2/sfxdlg.hxx>
+#include <sfx2/sfxhelp.hxx>
+#include <sfx2/sfxresid.hxx>
+#include <sfx2/sfxuno.hxx>
+#include <sfx2/sidebar/Context.hxx>
+#include <sfx2/sidebar/Deck.hxx>
+#include <sfx2/sidebar/FocusManager.hxx>
+#include <sfx2/sidebar/Panel.hxx>
+#include <sfx2/sidebar/ResourceManager.hxx>
+#include <sfx2/sidebar/SidebarChildWindow.hxx>
+#include <sfx2/sidebar/SidebarController.hxx>
+#include <sfx2/sidebar/SidebarDockingWindow.hxx>
+#include <sfx2/sidebar/TabBar.hxx>
+#include <sfx2/sidebar/Theme.hxx>
+#include <sfx2/signaturestate.hxx>
+#include <sfx2/stbitem.hxx>
+#include <sfx2/styfitem.hxx>
+#include <sfx2/tabdlg.hxx>
+#include <sfx2/tbxctrl.hxx>
+#include <sfx2/templatedlg.hxx>
+#include <sfx2/thumbnailview.hxx>
+#include <sfx2/thumbnailviewitem.hxx>
+#include <sfx2/tplpitem.hxx>
+#include <sfx2/viewfac.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/viewsh.hxx>
+#include <sfxpicklist.hxx>
+#include <sfxtypes.hxx>
+#include <sfxurlrelocator.hxx>
+#include <splitwin.hxx>
+#include <statcach.hxx>
+#include <templdgi.hxx>
+#include <tplcitem.hxx>
+#include <workwin.hxx>
+#endif // PCH_LEVEL >= 4
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/inc/preventduplicateinteraction.hxx b/sfx2/inc/preventduplicateinteraction.hxx
new file mode 100644
index 000000000..62e59dd96
--- /dev/null
+++ b/sfx2/inc/preventduplicateinteraction.hxx
@@ -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 .
+ */
+
+#ifndef INCLUDED_FRAMEWORK_PREVENTDUPLICATEINTERACTION_HXX
+#define INCLUDED_FRAMEWORK_PREVENTDUPLICATEINTERACTION_HXX
+
+#include <vector>
+
+#include <com/sun/star/frame/Desktop.hpp>
+#include <com/sun/star/frame/TerminationVetoException.hpp>
+#include <com/sun/star/lang/XInitialization.hpp>
+#include <com/sun/star/task/XInteractionHandler2.hpp>
+#include <com/sun/star/task/XInteractionRequest.hpp>
+
+#include <comphelper/compbase.hxx>
+#include <cppuhelper/implbase.hxx>
+
+#include <toolkit/helper/vclunohelper.hxx>
+#include <vcl/wrkwin.hxx>
+#include <vcl/svapp.hxx>
+#include <mutex>
+
+namespace com::sun::star::uno {
+ class XComponentContext;
+}
+
+namespace sfx2 {
+
+inline void closedialogs(SystemWindow& rTopLevel, bool bCloseRoot)
+{
+ for (vcl::Window *pChild = rTopLevel.GetWindow(GetWindowType::FirstTopWindowChild); pChild; pChild = rTopLevel.GetWindow(GetWindowType::NextTopWindowSibling))
+ closedialogs(dynamic_cast<SystemWindow&>(*pChild), true);
+ if (bCloseRoot)
+ rTopLevel.Close();
+}
+
+// This is intended to be the parent for any warning dialogs launched
+// during the load of a document so that those dialogs are modal to
+// this window and don't block any existing windows.
+//
+// If there are dialog children open on exit then veto termination,
+// close the topmost dialog and retry termination.
+class WarningDialogsParent final :
+ public comphelper::WeakComponentImplHelper<css::frame::XTerminateListener>
+{
+private:
+ VclPtr<WorkWindow> m_xWin;
+ css::uno::Reference<css::awt::XWindow> m_xInterface;
+
+private:
+
+ DECL_STATIC_LINK(WarningDialogsParent, TerminateDesktop, void*, void);
+
+ void closewarningdialogs()
+ {
+ if (!m_xWin)
+ return;
+ SolarMutexGuard aSolarGuard;
+ closedialogs(dynamic_cast<SystemWindow&>(*m_xWin), false);
+ }
+
+public:
+
+ using comphelper::WeakComponentImplHelperBase::disposing;
+ virtual void SAL_CALL disposing(const css::lang::EventObject&) override
+ {
+ }
+
+ // XTerminateListener
+ virtual void SAL_CALL queryTermination(const css::lang::EventObject&) override
+ {
+ closewarningdialogs();
+ Application::PostUserEvent(LINK(this, WarningDialogsParent, TerminateDesktop));
+ throw css::frame::TerminationVetoException();
+ }
+
+ virtual void SAL_CALL notifyTermination(const css::lang::EventObject&) override
+ {
+ }
+
+public:
+ WarningDialogsParent()
+ {
+ SolarMutexGuard aSolarGuard;
+ m_xWin = VclPtr<WorkWindow>::Create(nullptr, WB_STDWORK);
+ m_xWin->SetText("dialog parent for warning dialogs during load");
+ m_xInterface = VCLUnoHelper::GetInterface(m_xWin);
+ }
+
+ virtual ~WarningDialogsParent() override
+ {
+ closewarningdialogs();
+ m_xWin.disposeAndClear();
+ }
+
+ const css::uno::Reference<css::awt::XWindow>& GetDialogParent() const
+ {
+ return m_xInterface;
+ }
+};
+
+class WarningDialogsParentScope
+{
+private:
+ css::uno::Reference<css::frame::XDesktop> m_xDesktop;
+ rtl::Reference<WarningDialogsParent> m_xListener;
+
+public:
+ WarningDialogsParentScope(const css::uno::Reference<css::uno::XComponentContext>& rContext)
+ : m_xDesktop(css::frame::Desktop::create(rContext), css::uno::UNO_QUERY_THROW)
+ , m_xListener(new WarningDialogsParent)
+ {
+ m_xDesktop->addTerminateListener(m_xListener);
+ }
+
+ const css::uno::Reference<css::awt::XWindow>& GetDialogParent() const
+ {
+ return m_xListener->GetDialogParent();
+ }
+
+ ~WarningDialogsParentScope()
+ {
+ m_xDesktop->removeTerminateListener(m_xListener);
+ }
+};
+
+/**
+ @short Prevent us from showing the same interaction more than once during
+ the same transaction.
+
+ @descr Every interaction provided to this helper will be safed ... handled by the internal
+ used UUIInteractionHandler (!) and never be handled a second time!
+
+ On the other side there exists some interactions, which allow a retry.
+ So this helper allow to set a list of interactions combined with a retry value.
+ */
+class PreventDuplicateInteraction final :
+ public ::cppu::WeakImplHelper<css::lang::XInitialization, css::task::XInteractionHandler2>
+{
+ mutable std::mutex m_aLock;
+
+ // structs, types etc.
+ public:
+
+ struct InteractionInfo
+ {
+ public:
+ /// describe the interaction.
+ css::uno::Type m_aInteraction;
+ /// after max count was reached this interaction will be blocked.
+ sal_Int32 m_nMaxCount;
+ /// count how often this interaction was called.
+ sal_Int32 m_nCallCount;
+ /** hold the last intercepted request (matching the set interaction type) alive
+ so it can be used for further checks */
+ css::uno::Reference< css::task::XInteractionRequest > m_xRequest;
+
+ public:
+
+ InteractionInfo(const css::uno::Type& aInteraction)
+ : m_aInteraction(aInteraction)
+ , m_nMaxCount (1 )
+ , m_nCallCount (0 )
+ {}
+ };
+
+ // member
+ private:
+
+ /// Used to create needed uno services at runtime.
+ css::uno::Reference< css::uno::XComponentContext > m_xContext;
+
+ /** The outside interaction handler, which is used to handle every incoming interaction,
+ if it's not blocked. */
+ css::uno::Reference< css::task::XInteractionHandler > m_xHandler;
+
+ std::unique_ptr<WarningDialogsParentScope> m_xWarningDialogsParent;
+
+ /** This list describe which and how incoming interactions must be handled.
+ Further it contains all collected information after this interaction
+ object was used.*/
+ std::vector< InteractionInfo > m_lInteractionRules;
+
+
+ // uno interface
+ public:
+
+ virtual void SAL_CALL initialize(const css::uno::Sequence<css::uno::Any>& rArguments) override;
+
+ /**
+ @interface XInteractionHandler
+ @short called from outside to handle a problem
+ @descr We filter the incoming interactions. some of them
+ will be forwarded to the generic UI interaction handler.
+ So we must not implement it twice. Some other ones
+ will be aborted only.
+
+ @threadsafe yes
+ */
+ virtual void SAL_CALL handle(const css::uno::Reference< css::task::XInteractionRequest >& xRequest) override;
+
+
+ /**
+ @interface XInteractionHandler2
+ @short called from outside to handle a problem
+ @descr We filter the incoming interactions. some of them
+ will be forwarded to the generic UI interaction handler.
+ So we must not implement it twice. Some other ones
+ will be aborted only.
+
+ @threadsafe yes
+ */
+ virtual sal_Bool SAL_CALL handleInteractionRequest( const css::uno::Reference< css::task::XInteractionRequest >& xRequest ) override;
+
+
+ /**
+ @interface XInterface
+ @short called to query another interface of the component
+ @descr Will allow to query for XInteractionHandler2 if and only if m_xHandler supports this interface, too.
+
+ @threadsafe yes
+ */
+ virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type& aType ) override;
+
+ // c++ interface
+ public:
+
+
+ /**
+ @short ctor to guarantee right initialized instances of this class
+ @descr It uses the given uno service manager to create the global
+ generic UI interaction handler for later internal using.
+
+ @param xSMGR
+ uno service manager for creating services internally
+
+ @threadsafe not necessary
+ */
+ PreventDuplicateInteraction(const css::uno::Reference< css::uno::XComponentContext >& rxContext);
+
+
+ /**
+ @short dtor to free used memory.
+ */
+ virtual ~PreventDuplicateInteraction() override;
+
+
+ /**
+ @short set the outside interaction handler, which must be used internally
+ if the interaction will not be blocked by the set list of rules.
+
+ @note This overwrites the settings of e.g. useDefaultUUIHandler()!
+
+ @param xHandler
+ the new interaction handler
+ */
+ void setHandler(const css::uno::Reference< css::task::XInteractionHandler >& xHandler);
+
+
+ /**
+ @short instead of setting an outside interaction handler, this method
+ make sure the default UUI interaction handler of the office is used.
+
+ @note This overwrites the settings of e.g. setHandler()!
+ */
+ void useDefaultUUIHandler();
+
+
+ /**
+ @short add a new interaction to the list of interactions, which
+ must be handled by this helper.
+
+ @descr This method must be called immediately after a new instance of this helper was
+ created. Without such list of InteractionRules, this instances does nothing!
+ On the other side there is no possibility to remove rules.
+ So the same instance can't be used within different transactions.
+ It's a OneWay-object .-)
+
+ @param aInteractionInfo
+ describe the type of interaction, hos often it can be called etcpp.
+
+ @threadsafe yes
+ */
+ void addInteractionRule(const PreventDuplicateInteraction::InteractionInfo& aInteractionInfo);
+
+
+ /**
+ @short return the info struct for the specified interaction.
+
+ @param aInteraction
+ specify the interaction.
+
+ @param pReturn
+ provides information about:
+ - the count how often this interaction was handled during the
+ lifetime of this helper.
+ - the interaction itself, so it can be analyzed further
+
+ @return [boolean]
+ true if the queried interaction could be found.
+ false otherwise.
+
+ @threadsafe yes
+ */
+ bool getInteractionInfo(const css::uno::Type& aInteraction,
+ PreventDuplicateInteraction::InteractionInfo* pReturn ) const;
+};
+
+} // namespace sfx2
+
+#endif // INCLUDED_FRAMEWORK_PREVENTDUPLICATEINTERACTION_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/inc/recentdocsview.hxx b/sfx2/inc/recentdocsview.hxx
new file mode 100644
index 000000000..3ee34eabc
--- /dev/null
+++ b/sfx2/inc/recentdocsview.hxx
@@ -0,0 +1,112 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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/thumbnailview.hxx>
+#include <vcl/bitmapex.hxx>
+
+#include <o3tl/typed_flags_set.hxx>
+
+#include <com/sun/star/util/URL.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+
+class INetURLObject;
+struct ImplSVEvent;
+namespace com::sun::star::frame { class XDispatch; }
+
+namespace sfx2
+{
+
+class RecentDocsView;
+
+struct LoadRecentFile
+{
+ css::util::URL aTargetURL;
+ css::uno::Sequence< css::beans::PropertyValue > aArgSeq;
+ css::uno::Reference< css::frame::XDispatch > xDispatch;
+ RecentDocsView* pView;
+};
+
+enum class ApplicationType
+{
+ TYPE_NONE = 0,
+ TYPE_WRITER = 1 << 0,
+ TYPE_CALC = 1 << 1,
+ TYPE_IMPRESS = 1 << 2,
+ TYPE_DRAW = 1 << 3,
+ TYPE_DATABASE = 1 << 4,
+ TYPE_MATH = 1 << 5,
+ TYPE_OTHER = 1 << 6
+};
+
+} // namespace sfx2
+
+namespace o3tl {
+
+template<> struct typed_flags<sfx2::ApplicationType> : is_typed_flags<sfx2::ApplicationType, 0x7f> {};
+
+} // namespace o3tl
+
+namespace sfx2
+{
+
+class RecentDocsView final : public ThumbnailView
+{
+public:
+ RecentDocsView(std::unique_ptr<weld::ScrolledWindow> xWindow, std::unique_ptr<weld::Menu> xMenu);
+ virtual ~RecentDocsView() override;
+
+ void insertItem(const OUString &rURL, const OUString &rTitle, const OUString& rThumbnail, bool isReadOnly, sal_uInt16 nId);
+
+ static bool typeMatchesExtension(ApplicationType type, std::u16string_view rExt);
+
+ ApplicationType mnFileTypes;
+
+ virtual void Clear() override;
+
+ /// Update the information in the view.
+ virtual void Reload() override;
+
+ // launch load of recently used file
+ void PostLoadRecentUsedFile(LoadRecentFile* pLoadRecentFile);
+
+ // received on load of recently used file
+ void DispatchedLoadRecentUsedFile();
+
+private:
+ virtual bool MouseButtonDown( const MouseEvent& rMEvt ) override;
+
+ virtual bool MouseButtonUp( const MouseEvent& rMEvt ) override;
+
+ virtual void OnItemDblClicked(ThumbnailViewItem *pItem) override;
+
+ virtual void Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect) override;
+
+ virtual void LoseFocus() override;
+
+ bool isAcceptedFile(const INetURLObject& rURL) const;
+
+ DECL_LINK( ExecuteHdl_Impl, void*, void );
+
+ tools::Long mnItemMaxSize;
+ size_t mnLastMouseDownItem;
+
+ /// Image that appears when there is no recent document.
+ BitmapEx maWelcomeImage;
+ OUString maWelcomeLine1;
+ OUString maWelcomeLine2;
+
+ sfx2::LoadRecentFile* mpLoadRecentFile;
+ ImplSVEvent* m_nExecuteHdlId;
+};
+
+} // namespace sfx2
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/inc/saveastemplatedlg.hxx b/sfx2/inc/saveastemplatedlg.hxx
new file mode 100644
index 000000000..a4df61853
--- /dev/null
+++ b/sfx2/inc/saveastemplatedlg.hxx
@@ -0,0 +1,58 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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_INC_SAVEASTEMPLATEDLG_HXX
+#define INCLUDED_SFX2_INC_SAVEASTEMPLATEDLG_HXX
+
+#include <sal/config.h>
+#include <sfx2/doctempl.hxx>
+#include <vcl/weld.hxx>
+
+#include <com/sun/star/frame/XModel.hpp>
+
+// class SfxSaveAsTemplateDialog -------------------------------------------------------------------
+class SfxSaveAsTemplateDialog final : public weld::GenericDialogController
+{
+private:
+ std::unique_ptr<weld::TreeView> m_xLBCategory;
+ std::unique_ptr<weld::CheckButton> m_xCBXDefault;
+ std::unique_ptr<weld::Entry> m_xTemplateNameEdit;
+ std::unique_ptr<weld::Button> m_xOKButton;
+
+ OUString msSelectedCategory;
+ OUString msTemplateName;
+ sal_uInt16 mnRegionPos;
+
+ std::vector<OUString> msCategories;
+
+ SfxDocumentTemplates maDocTemplates;
+
+ css::uno::Reference<css::frame::XModel> m_xModel;
+
+public:
+ DECL_LINK(OkClickHdl, weld::Button&, void);
+ DECL_LINK(TemplateNameEditHdl, weld::Entry&, void);
+ DECL_LINK(SelectCategoryHdl, weld::TreeView&, void);
+
+ void initialize();
+ void SetCategoryLBEntries(const std::vector<OUString>& names);
+
+ /*Check whether template name is unique or not in a region*/
+ bool IsTemplateNameUnique();
+
+ bool SaveTemplate();
+
+public:
+ SfxSaveAsTemplateDialog(weld::Window* pParent,
+ const css::uno::Reference<css::frame::XModel>& rModel);
+};
+
+#endif // INCLUDED_SFX2_INC_SAVEASTEMPLATEDLG_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/inc/sfxbasecontroller_internal.hxx b/sfx2/inc/sfxbasecontroller_internal.hxx
new file mode 100644
index 000000000..0de26d5e9
--- /dev/null
+++ b/sfx2/inc/sfxbasecontroller_internal.hxx
@@ -0,0 +1,19 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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_INC_SFXBASECONTROLLER_INTERNAL_HXX
+#define INCLUDED_SFX2_INC_SFXBASECONTROLLER_INTERNAL_HXX
+
+#include <sal/types.h>
+
+extern sal_uInt32 Get10ThSec();
+
+#endif // INCLUDED_SFX2_INC_SFXBASECONTROLLER_INTERNAL_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/inc/sidebar/ContextChangeBroadcaster.hxx b/sfx2/inc/sidebar/ContextChangeBroadcaster.hxx
new file mode 100644
index 000000000..49770da76
--- /dev/null
+++ b/sfx2/inc/sidebar/ContextChangeBroadcaster.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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this 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_INC_SIDEBAR_CONTEXTCHANGEBROADCASTER_HXX
+#define INCLUDED_SFX2_INC_SIDEBAR_CONTEXTCHANGEBROADCASTER_HXX
+
+#include <com/sun/star/frame/XFrame.hpp>
+
+
+namespace sfx2::sidebar {
+
+
+/** This class is a helper for broadcasting context changes that are
+ tied to shells being activated or deactivated.
+*/
+class ContextChangeBroadcaster
+{
+public:
+ ContextChangeBroadcaster();
+ ~ContextChangeBroadcaster();
+
+ void Initialize (const OUString& rsContextName);
+
+ void Activate (const css::uno::Reference<css::frame::XFrame>& rxFrame);
+ void Deactivate (const css::uno::Reference<css::frame::XFrame>& rxFrame);
+
+ /** Enable or disable the broadcaster.
+ @param bIsEnabled
+ The new value of the "enabled" state.
+ @return
+ The old value of the "enabled" state is returned.
+ */
+ bool SetBroadcasterEnabled (const bool bIsEnabled);
+
+private:
+ OUString msContextName;
+ bool mbIsBroadcasterEnabled;
+
+ void BroadcastContextChange (
+ const css::uno::Reference<css::frame::XFrame>& rxFrame,
+ const OUString& rsModuleName,
+ const OUString& rsContextName);
+ static OUString GetModuleName (
+ const css::uno::Reference<css::frame::XFrame>& rxFrame);
+};
+
+
+} // end of namespace ::sd::sidebar
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/inc/sidebar/ContextList.hxx b/sfx2/inc/sidebar/ContextList.hxx
new file mode 100644
index 000000000..b3ecafa53
--- /dev/null
+++ b/sfx2/inc/sidebar/ContextList.hxx
@@ -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 .
+ */
+#pragma once
+
+#include <sfx2/sidebar/Context.hxx>
+#include <rtl/ustring.hxx>
+
+#include <vector>
+
+namespace sfx2::sidebar {
+
+/** Per context data for deck and panel descriptors.
+*/
+class ContextList
+{
+public:
+ ContextList();
+
+ class Entry
+ {
+ public:
+ Context maContext;
+ bool mbIsInitiallyVisible;
+ OUString msMenuCommand;
+ };
+
+ /** Return <TRUE/> when the given context matches any of the stored contexts.
+ */
+ const Entry* GetMatch (
+ const Context& rContext) const;
+ Entry* GetMatch (
+ const Context& rContext);
+
+ void AddContextDescription (
+ const Context& rContext,
+ const bool bIsInitiallyVisible,
+ const OUString& rsMenuCommand);
+
+ void ToggleVisibilityForContext( const Context& rContext,const bool bIsInitiallyVisible );
+ const ::std::vector<Entry>& GetEntries() const {return maEntries;};
+
+private:
+ ::std::vector<Entry> maEntries;
+
+ ::std::vector<Entry>::const_iterator FindBestMatch (const Context& rContext) const;
+};
+
+} // end of namespace sfx2::sidebar
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/inc/sidebar/ControllerFactory.hxx b/sfx2/inc/sidebar/ControllerFactory.hxx
new file mode 100644
index 000000000..8d45a2e3b
--- /dev/null
+++ b/sfx2/inc/sidebar/ControllerFactory.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 .
+ */
+#pragma once
+
+#include <com/sun/star/uno/Reference.hxx>
+#include <vcl/toolbox.hxx>
+
+namespace com::sun::star::awt { class XWindow; }
+namespace com::sun::star::frame { class XController; }
+namespace com::sun::star::frame { class XFrame; }
+namespace com::sun::star::frame { class XToolbarController; }
+
+class ToolBox;
+
+namespace weld {
+ class Builder;
+ class Toolbar;
+}
+
+namespace sfx2::sidebar {
+
+/** Convenience class for the easy creation of toolbox controllers.
+*/
+class ControllerFactory
+{
+public:
+ static css::uno::Reference<css::frame::XToolbarController> CreateToolBoxController(
+ ToolBox* pToolBox,
+ const ToolBoxItemId nItemId,
+ const OUString& rsCommandName,
+ const css::uno::Reference<css::frame::XFrame>& rxFrame,
+ const css::uno::Reference<css::frame::XController>& rxController,
+ const css::uno::Reference<css::awt::XWindow>& rxParentWindow,
+ const sal_Int32 nItemWidth, bool bSideBar);
+
+ static css::uno::Reference<css::frame::XToolbarController> CreateToolBoxController(
+ weld::Toolbar& rToolbar,
+ weld::Builder& rBuilder,
+ const OUString& rsCommandName,
+ const css::uno::Reference<css::frame::XFrame>& rxFrame,
+ const css::uno::Reference<css::frame::XController>& rxController,
+ bool bSideBar);
+
+private:
+ static css::uno::Reference<css::frame::XToolbarController> CreateToolBarController(
+ const css::uno::Reference<css::awt::XWindow>& rToolbar,
+ const OUString& rsCommandName,
+ const css::uno::Reference<css::frame::XFrame>& rxFrame,
+ const css::uno::Reference<css::frame::XController>& rxController,
+ const sal_Int32 nWidth, bool bSideBar);
+};
+
+
+} // end of namespace sfx2::sidebar
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/inc/sidebar/DeckDescriptor.hxx b/sfx2/inc/sidebar/DeckDescriptor.hxx
new file mode 100644
index 000000000..5381b4ea2
--- /dev/null
+++ b/sfx2/inc/sidebar/DeckDescriptor.hxx
@@ -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 .
+ */
+#pragma once
+
+#include <sidebar/ContextList.hxx>
+
+#include <sfx2/sidebar/Deck.hxx>
+
+namespace sfx2::sidebar
+{
+class DeckDescriptor
+{
+public:
+ OUString msTitle;
+ OUString msId;
+ OUString msIconURL;
+ OUString msHighContrastIconURL;
+ OUString msTitleBarIconURL;
+ OUString msHighContrastTitleBarIconURL;
+ OUString msHelpText;
+ ContextList maContextList;
+ bool mbIsEnabled;
+ sal_Int32 mnOrderIndex;
+ bool mbExperimental;
+
+ OUString msNodeName; // some impress deck nodes names are different from their Id
+
+ VclPtr<Deck> mpDeck;
+
+ DeckDescriptor();
+ DeckDescriptor(const DeckDescriptor& rOther);
+ ~DeckDescriptor();
+};
+
+} // end of namespace sfx2::sidebar
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/inc/sidebar/DeckLayouter.hxx b/sfx2/inc/sidebar/DeckLayouter.hxx
new file mode 100644
index 000000000..a40868f45
--- /dev/null
+++ b/sfx2/inc/sidebar/DeckLayouter.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 .
+ */
+#pragma once
+
+#include <sfx2/sidebar/Panel.hxx>
+
+namespace tools { class Rectangle; }
+
+namespace sfx2::sidebar {
+
+class DeckTitleBar;
+class SidebarDockingWindow;
+
+/** Helper for layouting the direct and indirect children of a
+ deck like title bars, panels, and scroll bars.
+*/
+namespace DeckLayouter
+{
+ void LayoutDeck (
+ const SidebarDockingWindow* pDockingWindow,
+ const tools::Rectangle& rContentArea,
+ sal_Int32& rMinimalWidth,
+ sal_Int32& rMinimalHeight,
+ SharedPanelContainer& rPanels,
+ DeckTitleBar& pDeckTitleBar,
+ weld::ScrolledWindow& pVerticalScrollBar);
+}
+
+} // end of namespace sfx2::sidebar
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/inc/sidebar/DeckTitleBar.hxx b/sfx2/inc/sidebar/DeckTitleBar.hxx
new file mode 100644
index 000000000..7ed23a8bf
--- /dev/null
+++ b/sfx2/inc/sidebar/DeckTitleBar.hxx
@@ -0,0 +1,57 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include <sidebar/TitleBar.hxx>
+#include <vcl/customweld.hxx>
+
+namespace sfx2::sidebar
+{
+class Deck;
+class GripWidget;
+
+class DeckTitleBar final : public TitleBar
+{
+public:
+ DeckTitleBar(const OUString& rsTitle, weld::Builder& rBuilder,
+ const std::function<void()>& rCloserAction);
+ virtual ~DeckTitleBar() override;
+
+ virtual void SetTitle(const OUString& rsTitle) override;
+ virtual OUString GetTitle() const override;
+
+ void SetCloserVisible(const bool bIsCloserVisible);
+ tools::Rectangle GetDragArea() const;
+
+ virtual void DataChanged() override;
+
+private:
+ virtual void HandleToolBoxItemClick() override;
+
+ std::unique_ptr<GripWidget> mxGripWidget;
+ std::unique_ptr<weld::CustomWeld> mxGripWeld;
+ std::unique_ptr<weld::Label> mxLabel;
+
+ const std::function<void()> maCloserAction;
+ bool mbIsCloserVisible;
+};
+
+} // end of namespace sfx2::sidebar
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/inc/sidebar/PanelDescriptor.hxx b/sfx2/inc/sidebar/PanelDescriptor.hxx
new file mode 100644
index 000000000..1033379f7
--- /dev/null
+++ b/sfx2/inc/sidebar/PanelDescriptor.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 .
+ */
+#pragma once
+
+#include <sidebar/ContextList.hxx>
+
+namespace sfx2::sidebar {
+
+class PanelDescriptor
+{
+public:
+ OUString msTitle;
+ bool mbIsTitleBarOptional;
+ OUString msId;
+ OUString msDeckId;
+ OUString msTitleBarIconURL;
+ OUString msHighContrastTitleBarIconURL;
+ ContextList maContextList;
+ OUString msImplementationURL;
+ sal_Int32 mnOrderIndex;
+ bool mbShowForReadOnlyDocuments;
+ bool mbWantsCanvas;
+ bool mbWantsAWT;
+ bool mbExperimental;
+
+ OUString msNodeName; // some impress panel nodes names are different from their Id
+
+ PanelDescriptor();
+ PanelDescriptor (const PanelDescriptor& rPanelDescriptor);
+ ~PanelDescriptor();
+};
+
+} // end of namespace sfx2::sidebar
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/inc/sidebar/PanelTitleBar.hxx b/sfx2/inc/sidebar/PanelTitleBar.hxx
new file mode 100644
index 000000000..308a0c8f9
--- /dev/null
+++ b/sfx2/inc/sidebar/PanelTitleBar.hxx
@@ -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 .
+ */
+#pragma once
+
+#include <sidebar/TitleBar.hxx>
+#include <com/sun/star/frame/XToolbarController.hpp>
+
+namespace com::sun::star::frame { class XController; }
+namespace com::sun::star::frame { class XFrame; }
+
+namespace sfx2::sidebar {
+
+class Panel;
+
+class PanelTitleBar final : public TitleBar
+{
+public:
+ PanelTitleBar(const OUString& rsTitle, weld::Builder& rBuilder, Panel* pPanel);
+ virtual ~PanelTitleBar() override;
+
+ virtual void SetTitle (const OUString& rsTitle) override;
+ virtual OUString GetTitle() const override;
+
+ void SetMoreOptionsCommand(const OUString& rsCommandName,
+ const css::uno::Reference<css::frame::XFrame>& rxFrame,
+ const css::uno::Reference<css::frame::XController>& rxController);
+
+ void UpdateExpandedState();
+
+ weld::Expander& GetExpander()
+ {
+ return *mxExpander;
+ }
+
+ const OUString& GetMoreOptionsCommand() const { return msMoreOptionsCommand; }
+
+private:
+ virtual void HandleToolBoxItemClick() override;
+
+ DECL_LINK(ExpandHdl, weld::Expander&, void);
+
+ std::unique_ptr<weld::Expander> mxExpander;
+
+ css::uno::Reference<css::frame::XToolbarController> mxController;
+
+ Panel* mpPanel;
+ OString msIdent;
+ OUString msMoreOptionsCommand;
+};
+
+} // end of namespace sfx2::sidebar
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/inc/sidebar/SidebarToolBox.hxx b/sfx2/inc/sidebar/SidebarToolBox.hxx
new file mode 100644
index 000000000..acae7475e
--- /dev/null
+++ b/sfx2/inc/sidebar/SidebarToolBox.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_SIDEBAR_SIDEBARTOOLBOX_HXX
+#define INCLUDED_SFX2_SIDEBAR_SIDEBARTOOLBOX_HXX
+
+#include <vcl/builder.hxx>
+#include <vcl/toolbox.hxx>
+#include <map>
+
+namespace com::sun::star::frame { class XToolbarController; }
+
+namespace sfx2::sidebar {
+
+/** The sidebar tool box has two responsibilities:
+ 1. Coordinated location, size, and other states with its parent
+ background window.
+ 2. Create and handle tool bar controller for its items.
+*/
+class SidebarToolBox : public ToolBox
+{
+public:
+ SidebarToolBox(vcl::Window* pParentWindow);
+ virtual ~SidebarToolBox() override;
+ virtual void dispose() override;
+
+ virtual ToolBoxButtonSize GetDefaultButtonSize() const;
+
+ using ToolBox::InsertItem;
+ virtual void InsertItem(const OUString& rCommand,
+ const css::uno::Reference<css::frame::XFrame>& rFrame,
+ ToolBoxItemBits nBits,
+ const Size& rRequestedSize,
+ ImplToolItems::size_type nPos = APPEND) override;
+
+ virtual bool EventNotify(NotifyEvent& rEvent) override;
+ virtual void KeyInput( const KeyEvent& rKEvt ) override;
+
+ void InitToolBox(VclBuilder::stringmap& rMap);
+
+protected:
+ typedef std::map<ToolBoxItemId, css::uno::Reference<css::frame::XToolbarController>> ControllerContainer;
+ ControllerContainer maControllers;
+ bool mbAreHandlersRegistered;
+ bool mbUseDefaultButtonSize;
+ bool mbSideBar;
+
+ DECL_LINK(DropDownClickHandler, ToolBox*, void);
+ DECL_LINK(ClickHandler, ToolBox*, void);
+ DECL_LINK(DoubleClickHandler, ToolBox*, void);
+ DECL_LINK(SelectHandler, ToolBox*, void);
+ DECL_LINK(ChangedIconSizeHandler, LinkParamNone*, void );
+
+ css::uno::Reference<css::frame::XToolbarController> GetControllerForItemId(const ToolBoxItemId nItemId) const;
+
+ void CreateController(const ToolBoxItemId nItemId,
+ const css::uno::Reference<css::frame::XFrame>& rxFrame,
+ const sal_Int32 nItemWidth, bool bSideBar);
+ void RegisterHandlers();
+};
+
+
+} // end of namespace sfx2::sidebar
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/inc/sidebar/TitleBar.hxx b/sfx2/inc/sidebar/TitleBar.hxx
new file mode 100644
index 000000000..1f589508d
--- /dev/null
+++ b/sfx2/inc/sidebar/TitleBar.hxx
@@ -0,0 +1,70 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include <sfx2/sidebar/Theme.hxx>
+#include <vcl/weld.hxx>
+
+namespace sfx2::sidebar {
+
+class TitleBar
+{
+public:
+ TitleBar(weld::Builder& rBuilder, Theme::ThemeItem eThemeItem);
+ virtual ~TitleBar();
+
+ virtual void SetTitle (const OUString& rsTitle) = 0;
+ virtual OUString GetTitle() const = 0;
+
+ virtual void DataChanged();
+
+ void Show(bool bShow);
+ bool GetVisible() const;
+
+ Size get_preferred_size() const;
+
+ void SetIcon(const css::uno::Reference<css::graphic::XGraphic>& rIcon);
+
+ weld::Toolbar& GetToolBox()
+ {
+ return *mxToolBox;
+ }
+ const weld::Toolbar& GetToolBox() const
+ {
+ return *mxToolBox;
+ }
+
+protected:
+ weld::Builder& mrBuilder;
+ std::unique_ptr<weld::Box> mxTitlebar;
+ std::unique_ptr<weld::Image> mxAddonImage;
+ std::unique_ptr<weld::Toolbar> mxToolBox;
+ Theme::ThemeItem meThemeItem;
+
+ virtual void HandleToolBoxItemClick() = 0;
+
+ DECL_LINK(SelectionHandler, const OString&, void);
+
+private:
+ void SetBackground();
+};
+
+} // end of namespace sfx2::sidebar
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/inc/sidebar/Tools.hxx b/sfx2/inc/sidebar/Tools.hxx
new file mode 100644
index 000000000..fb3dc54b4
--- /dev/null
+++ b/sfx2/inc/sidebar/Tools.hxx
@@ -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 .
+ */
+#pragma once
+
+#include <vcl/image.hxx>
+
+#include <com/sun/star/util/URL.hpp>
+
+namespace com::sun::star::frame { class XController; }
+namespace com::sun::star::frame { class XDispatch; }
+namespace com::sun::star::frame { class XFrame; }
+
+
+namespace sfx2::sidebar {
+
+class Tools
+{
+public:
+ static css::uno::Reference<css::graphic::XGraphic> GetImage(
+ const OUString& rsImageURL,
+ const OUString& rsHighContrastImageURL,
+ const css::uno::Reference<css::frame::XFrame>& rxFrame);
+
+ static css::uno::Reference<css::graphic::XGraphic> GetImage(
+ const OUString& rsURL,
+ const css::uno::Reference<css::frame::XFrame>& rxFrame);
+
+ static css::util::URL GetURL (const OUString& rsCommand);
+ static css::uno::Reference<css::frame::XDispatch> GetDispatch (
+ const css::uno::Reference<css::frame::XFrame>& rxFrame,
+ const css::util::URL& rURL);
+
+ static OUString GetModuleName (
+ const css::uno::Reference<css::frame::XController>& rxFrame);
+};
+
+
+} // end of namespace sfx2::sidebar
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/inc/sidebar/UnoDeck.hxx b/sfx2/inc/sidebar/UnoDeck.hxx
new file mode 100644
index 000000000..42c975c44
--- /dev/null
+++ b/sfx2/inc/sidebar/UnoDeck.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/.
+ *
+ */
+
+#pragma once
+
+
+#include <com/sun/star/ui/XDeck.hpp>
+
+#include <cppuhelper/implbase.hxx>
+
+#include <sfx2/sidebar/ResourceManager.hxx>
+
+namespace com::sun::star::frame { class XFrame; }
+namespace com::sun::star::ui { class XPanels; }
+namespace sfx2::sidebar { class SidebarController; }
+
+/** get the decks
+*/
+class SfxUnoDeck final : public cppu::WeakImplHelper<css::ui::XDeck>
+{
+
+public:
+
+ SfxUnoDeck(const css::uno::Reference<css::frame::XFrame>& , const OUString&);
+
+ virtual OUString SAL_CALL getId() override;
+
+ virtual OUString SAL_CALL getTitle() override;
+ virtual void SAL_CALL setTitle( const OUString& newTitle ) override;
+
+ virtual sal_Bool SAL_CALL isActive() override;
+ virtual void SAL_CALL activate( const sal_Bool bActivate ) override;
+
+ virtual css::uno::Reference<css::ui::XPanels> SAL_CALL getPanels() override;
+
+ virtual sal_Int32 SAL_CALL getOrderIndex() override;
+ virtual void SAL_CALL setOrderIndex( const sal_Int32 newOrderIndex ) override;
+ virtual void SAL_CALL moveFirst() override;
+ virtual void SAL_CALL moveLast() override;
+ virtual void SAL_CALL moveUp() override;
+ virtual void SAL_CALL moveDown() override;
+
+private:
+
+ const css::uno::Reference<css::frame::XFrame> xFrame;
+ sfx2::sidebar::SidebarController* getSidebarController();
+
+ const OUString mDeckId;
+
+ sal_Int32 GetMaxOrderIndex(const sfx2::sidebar::ResourceManager::DeckContextDescriptorContainer& rDecks);
+ sal_Int32 GetMinOrderIndex(const sfx2::sidebar::ResourceManager::DeckContextDescriptorContainer& rDecks);
+
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/inc/sidebar/UnoDecks.hxx b/sfx2/inc/sidebar/UnoDecks.hxx
new file mode 100644
index 000000000..95ce0be62
--- /dev/null
+++ b/sfx2/inc/sidebar/UnoDecks.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/.
+ *
+ */
+
+#pragma once
+
+#include <com/sun/star/ui/XDecks.hpp>
+
+#include <cppuhelper/implbase.hxx>
+
+namespace com::sun::star::frame { class XFrame; }
+namespace sfx2::sidebar { class SidebarController; }
+
+/** get the decks
+*/
+class SfxUnoDecks final : public cppu::WeakImplHelper<css::ui::XDecks>
+{
+
+public:
+
+ SfxUnoDecks(const css::uno::Reference<css::frame::XFrame>&);
+
+// XNameAccess
+
+ 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;
+
+// XIndexAccess
+
+ virtual sal_Int32 SAL_CALL getCount() override;
+
+ virtual css::uno::Any SAL_CALL getByIndex( sal_Int32 Index ) override;
+
+// XElementAccess
+ virtual css::uno::Type SAL_CALL getElementType() override;
+ virtual sal_Bool SAL_CALL hasElements() override;
+
+private:
+
+ const css::uno::Reference<css::frame::XFrame> xFrame;
+ sfx2::sidebar::SidebarController* getSidebarController();
+
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/inc/sidebar/UnoPanel.hxx b/sfx2/inc/sidebar/UnoPanel.hxx
new file mode 100644
index 000000000..4e148befd
--- /dev/null
+++ b/sfx2/inc/sidebar/UnoPanel.hxx
@@ -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/.
+ *
+ */
+
+#pragma once
+
+#include <com/sun/star/ui/XPanel.hpp>
+
+
+#include <cppuhelper/implbase.hxx>
+
+#include <sfx2/sidebar/Panel.hxx>
+#include <sfx2/sidebar/Deck.hxx>
+#include <sfx2/sidebar/ResourceManager.hxx>
+
+namespace com::sun::star::awt { class XWindow; }
+namespace com::sun::star::frame { class XFrame; }
+namespace sfx2::sidebar { class SidebarController; }
+
+
+/** get the Panel
+*/
+class SfxUnoPanel final : public cppu::WeakImplHelper<css::ui::XPanel>
+{
+
+public:
+
+ SfxUnoPanel(const css::uno::Reference<css::frame::XFrame>& , const OUString&, const OUString&);
+
+ virtual OUString SAL_CALL getId() override;
+
+ virtual OUString SAL_CALL getTitle() override;
+ virtual void SAL_CALL setTitle( const OUString& newTitle ) override;
+
+ virtual sal_Bool SAL_CALL isExpanded() override;
+ virtual void SAL_CALL expand( const sal_Bool bCollapseOther ) override;
+ virtual void SAL_CALL collapse( ) override;
+
+ virtual sal_Int32 SAL_CALL getOrderIndex() override;
+ virtual void SAL_CALL setOrderIndex( const sal_Int32 newOrderIndex ) override;
+ virtual void SAL_CALL moveFirst() override;
+ virtual void SAL_CALL moveLast() override;
+ virtual void SAL_CALL moveUp() override;
+ virtual void SAL_CALL moveDown() override;
+
+ virtual css::uno::Reference<css::awt::XWindow> SAL_CALL getDialog() override;
+
+private:
+
+ const css::uno::Reference<css::frame::XFrame> xFrame;
+ sfx2::sidebar::SidebarController* getSidebarController();
+
+ const OUString mPanelId;
+ const OUString mDeckId;
+
+ VclPtr<sfx2::sidebar::Deck> mpDeck;
+ std::weak_ptr<sfx2::sidebar::Panel> mxPanel;
+
+ sal_Int32 GetMaxOrderIndex(const sfx2::sidebar::ResourceManager::PanelContextDescriptorContainer& rPanels);
+ sal_Int32 GetMinOrderIndex(const sfx2::sidebar::ResourceManager::PanelContextDescriptorContainer& rPanels);
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/inc/sidebar/UnoPanels.hxx b/sfx2/inc/sidebar/UnoPanels.hxx
new file mode 100644
index 000000000..487043dee
--- /dev/null
+++ b/sfx2/inc/sidebar/UnoPanels.hxx
@@ -0,0 +1,57 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+#pragma once
+
+
+#include <com/sun/star/ui/XPanels.hpp>
+
+#include <cppuhelper/implbase.hxx>
+
+namespace com::sun::star::frame { class XFrame; }
+namespace sfx2::sidebar { class SidebarController; }
+
+/** get the decks
+*/
+class SfxUnoPanels final : public cppu::WeakImplHelper<css::ui::XPanels>
+{
+
+public:
+
+ SfxUnoPanels(const css::uno::Reference<css::frame::XFrame>& , const OUString&);
+
+// XPanels
+ virtual OUString SAL_CALL getDeckId() override;
+
+// XNameAccess
+
+ 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;
+
+// XIndexAccess
+
+ virtual sal_Int32 SAL_CALL getCount() override;
+
+ virtual css::uno::Any SAL_CALL getByIndex( sal_Int32 Index ) override;
+
+// XElementAccess
+ virtual css::uno::Type SAL_CALL getElementType() override;
+ virtual sal_Bool SAL_CALL hasElements() override;
+
+private:
+
+ const css::uno::Reference<css::frame::XFrame> xFrame;
+ sfx2::sidebar::SidebarController* getSidebarController();
+ const OUString& mDeckId;
+
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/inc/sidebar/UnoSidebar.hxx b/sfx2/inc/sidebar/UnoSidebar.hxx
new file mode 100644
index 000000000..20ffa22b1
--- /dev/null
+++ b/sfx2/inc/sidebar/UnoSidebar.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/.
+ *
+ */
+
+#ifndef INCLUDED_SFX2_SIDEBAR_SIDEBAR_HXX
+#define INCLUDED_SFX2_SIDEBAR_SIDEBAR_HXX
+
+#include <com/sun/star/ui/XSidebarProvider.hpp>
+
+#include <cppuhelper/implbase.hxx>
+
+namespace com::sun::star::frame { class XFrame; }
+namespace com::sun::star::ui { class XDecks; }
+namespace com::sun::star::ui { class XSidebar; }
+namespace sfx2::sidebar { class SidebarController; }
+
+/** get the sidebar for a given frame
+*/
+class SfxUnoSidebar final : public cppu::WeakImplHelper<css::ui::XSidebarProvider>
+{
+
+private:
+
+ const css::uno::Reference<css::frame::XFrame> xFrame;
+ sfx2::sidebar::SidebarController* getSidebarController();
+
+public:
+
+ SfxUnoSidebar(const css::uno::Reference<css::frame::XFrame>&);
+
+ virtual void SAL_CALL showDecks (const sal_Bool bVisible) override;
+
+
+ virtual void SAL_CALL setVisible (const sal_Bool bVisible) override;
+
+ virtual sal_Bool SAL_CALL isVisible() override;
+
+ virtual css::uno::Reference<css::frame::XFrame> SAL_CALL getFrame() override;
+
+ virtual css::uno::Reference<css::ui::XDecks> SAL_CALL getDecks() override;
+
+ virtual css::uno::Reference<css::ui::XSidebar> SAL_CALL getSidebar() override;
+
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/inc/sorgitm.hxx b/sfx2/inc/sorgitm.hxx
new file mode 100644
index 000000000..d67d89062
--- /dev/null
+++ b/sfx2/inc/sorgitm.hxx
@@ -0,0 +1,44 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this 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_INC_SORGITM_HXX
+#define INCLUDED_SFX2_INC_SORGITM_HXX
+
+#include <svl/stritem.hxx>
+
+// class SfxScriptOrganizerItem ---------------------------------------------
+
+class SfxScriptOrganizerItem final : public SfxStringItem
+{
+private:
+ OUString aLanguage;
+
+public:
+ static SfxPoolItem* CreateDefault();
+ SfxScriptOrganizerItem();
+
+ virtual SfxScriptOrganizerItem* Clone( SfxItemPool* pPool = nullptr ) const override;
+ virtual bool operator==( const SfxPoolItem& ) const override;
+ virtual bool QueryValue( css::uno::Any& rVal, sal_uInt8 nMemberId = 0 ) const override;
+ virtual bool PutValue( const css::uno::Any& rVal, sal_uInt8 nMemberId ) override;
+ const OUString& getLanguage() const { return aLanguage; };
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/inc/srchdlg.hxx b/sfx2/inc/srchdlg.hxx
new file mode 100644
index 000000000..3d8317477
--- /dev/null
+++ b/sfx2/inc/srchdlg.hxx
@@ -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 .
+ */
+#ifndef INCLUDED_SFX2_INC_SRCHDLG_HXX
+#define INCLUDED_SFX2_INC_SRCHDLG_HXX
+
+#include <vcl/weld.hxx>
+
+namespace sfx2 {
+
+
+// SearchDialog
+
+
+class SearchDialog final : public weld::GenericDialogController
+{
+private:
+ Link<SearchDialog&,void> m_aFindHdl;
+ Link<LinkParamNone*,void> m_aCloseHdl;
+
+ OUString m_sConfigName;
+
+ std::unique_ptr<weld::ComboBox> m_xSearchEdit;
+ std::unique_ptr<weld::CheckButton> m_xWholeWordsBox;
+ std::unique_ptr<weld::CheckButton> m_xMatchCaseBox;
+ std::unique_ptr<weld::CheckButton> m_xWrapAroundBox;
+ std::unique_ptr<weld::CheckButton> m_xBackwardsBox;
+ std::unique_ptr<weld::Button> m_xFindBtn;
+
+ void LoadConfig();
+ void SaveConfig();
+
+ DECL_LINK(FindHdl, weld::Button&, void);
+
+public:
+ SearchDialog(weld::Window* pWindow, const OUString& rConfigName);
+ static void runAsync(const std::shared_ptr<SearchDialog>& rController);
+ virtual ~SearchDialog() override;
+
+ void SetFindHdl( const Link<SearchDialog&,void>& rLink ) { m_aFindHdl = rLink; }
+ void SetCloseHdl( const Link<LinkParamNone*,void>& rLink ) { m_aCloseHdl = rLink; }
+
+ OUString GetSearchText() const { return m_xSearchEdit->get_active_text(); }
+ void SetSearchText( const OUString& _rText ) { m_xSearchEdit->set_entry_text( _rText ); }
+ bool IsOnlyWholeWords() const { return m_xWholeWordsBox->get_active(); }
+ bool IsMarchCase() const { return m_xMatchCaseBox->get_active(); }
+ bool IsWrapAround() const { return m_xWrapAroundBox->get_active(); }
+ bool IsSearchBackwards() const { return m_xBackwardsBox->get_active(); }
+
+ void SetFocusOnEdit();
+};
+
+} // namespace sfx2
+
+
+#endif // INCLUDED_SFX2_INC_SRCHDLG_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/inc/strings.hxx b/sfx2/inc/strings.hxx
new file mode 100644
index 000000000..67df9d7df
--- /dev/null
+++ b/sfx2/inc/strings.hxx
@@ -0,0 +1,57 @@
+/* -*- 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/.
+ */
+#pragma once
+
+#include <rtl/ustring.hxx>
+
+inline constexpr OUStringLiteral STR_HTML_GENERATOR
+ = u"%PRODUCTNAME %PRODUCTVERSION%PRODUCTEXTENSION (%1)";
+
+// Do not translate STR_TEMPLATE_NAME*_DEF names!!
+// STR_TEMPLATE_NAME*_DEF strings must EXACTLY fit dc:title tag in meta.xml of each template file
+inline constexpr OUStringLiteral STR_TEMPLATE_NAME1_DEF = u"Grey Elegant";
+inline constexpr OUStringLiteral STR_TEMPLATE_NAME2_DEF = u"Beehive";
+inline constexpr OUStringLiteral STR_TEMPLATE_NAME3_DEF = u"Blue Curve";
+inline constexpr OUStringLiteral STR_TEMPLATE_NAME4_DEF = u"Blueprint Plans";
+inline constexpr OUStringLiteral STR_TEMPLATE_NAME5_DEF = u"Candy";
+inline constexpr OUStringLiteral STR_TEMPLATE_NAME6_DEF = u"Yellow Idea";
+inline constexpr OUStringLiteral STR_TEMPLATE_NAME7_DEF = u"DNA";
+inline constexpr OUStringLiteral STR_TEMPLATE_NAME8_DEF = u"Focus";
+inline constexpr OUStringLiteral STR_TEMPLATE_NAME9_DEF = u"Forestbird";
+inline constexpr OUStringLiteral STR_TEMPLATE_NAME10_DEF = u"Freshes";
+inline constexpr OUStringLiteral STR_TEMPLATE_NAME11_DEF = u"Inspiration";
+inline constexpr OUStringLiteral STR_TEMPLATE_NAME12_DEF = u"Lights";
+inline constexpr OUStringLiteral STR_TEMPLATE_NAME13_DEF = u"Growing Liberty";
+inline constexpr OUStringLiteral STR_TEMPLATE_NAME14_DEF = u"Metropolis";
+inline constexpr OUStringLiteral STR_TEMPLATE_NAME15_DEF = u"Midnightblue";
+inline constexpr OUStringLiteral STR_TEMPLATE_NAME16_DEF = u"Nature Illustration";
+inline constexpr OUStringLiteral STR_TEMPLATE_NAME17_DEF = u"Pencil";
+inline constexpr OUStringLiteral STR_TEMPLATE_NAME18_DEF = u"Piano";
+inline constexpr OUStringLiteral STR_TEMPLATE_NAME19_DEF = u"Portfolio";
+inline constexpr OUStringLiteral STR_TEMPLATE_NAME20_DEF = u"Progress";
+inline constexpr OUStringLiteral STR_TEMPLATE_NAME21_DEF = u"Sunset";
+inline constexpr OUStringLiteral STR_TEMPLATE_NAME22_DEF = u"Vintage";
+inline constexpr OUStringLiteral STR_TEMPLATE_NAME23_DEF = u"Vivid";
+inline constexpr OUStringLiteral STR_TEMPLATE_NAME24_DEF = u"CV";
+inline constexpr OUStringLiteral STR_TEMPLATE_NAME25_DEF = u"Resume";
+inline constexpr OUStringLiteral STR_TEMPLATE_NAME26_DEF = u"Default";
+inline constexpr OUStringLiteral STR_TEMPLATE_NAME27_DEF = u"Modern";
+inline constexpr OUStringLiteral STR_TEMPLATE_NAME28_DEF = u"Modern business letter sans-serif";
+inline constexpr OUStringLiteral STR_TEMPLATE_NAME29_DEF = u"Modern business letter serif";
+inline constexpr OUStringLiteral STR_TEMPLATE_NAME30_DEF = u"Businesscard with logo";
+inline constexpr OUStringLiteral STR_TEMPLATE_NAME31_DEF = u"Simple";
+inline constexpr OUStringLiteral STR_TEMPLATE_NAME32_DEF = u"BPMN";
+
+inline constexpr OUStringLiteral CMIS_TYPE_STRING = u"String";
+#define CMIS_TYPE_INTEGER "Integer"
+#define CMIS_TYPE_DECIMAL "Decimal"
+#define CMIS_TYPE_DATETIME "Datetime"
+#define CMIS_TYPE_BOOL "Bool"
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/sfx2/inc/templatecontaineritem.hxx b/sfx2/inc/templatecontaineritem.hxx
new file mode 100644
index 000000000..019c1b909
--- /dev/null
+++ b/sfx2/inc/templatecontaineritem.hxx
@@ -0,0 +1,27 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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/templateproperties.hxx>
+
+class TemplateContainerItem final
+{
+public:
+ sal_uInt16 mnId;
+ sal_uInt16 mnRegionId;
+ OUString maTitle;
+ std::vector<TemplateItemProperties> maTemplates;
+
+ TemplateContainerItem(sal_uInt16 nId);
+
+ ~TemplateContainerItem();
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/inc/templatedefaultview.hxx b/sfx2/inc/templatedefaultview.hxx
new file mode 100644
index 000000000..1771ad3d8
--- /dev/null
+++ b/sfx2/inc/templatedefaultview.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/.
+ */
+
+#pragma once
+
+#include <sfx2/templatelocalview.hxx>
+
+class TemplateDefaultView final : public TemplateLocalView
+{
+public:
+ TemplateDefaultView(std::unique_ptr<weld::ScrolledWindow> xWindow,
+ std::unique_ptr<weld::Menu> xMenu);
+
+ virtual void showAllTemplates() override;
+
+ virtual bool KeyInput(const KeyEvent& rKEvt) override;
+
+ virtual bool MouseButtonDown(const MouseEvent& rMEvt) override;
+
+ void createContextMenu();
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/inc/templateviewitem.hxx b/sfx2/inc/templateviewitem.hxx
new file mode 100644
index 000000000..25f73b9d8
--- /dev/null
+++ b/sfx2/inc/templateviewitem.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/.
+ */
+
+#pragma once
+
+#include <sfx2/thumbnailviewitem.hxx>
+
+class TemplateViewItem final : public ThumbnailViewItem
+{
+public:
+
+ TemplateViewItem(ThumbnailView& rView, sal_uInt16 nId);
+
+ virtual ~TemplateViewItem () override;
+
+ void setPath (const OUString &rPath) { maPath = rPath; }
+
+ const OUString& getPath () const { return maPath; }
+
+ void showDefaultIcon(bool bVal) { mbIsDefaultTemplate = bVal; }
+
+ bool IsDefaultTemplate() const { return mbIsDefaultTemplate; }
+
+ tools::Rectangle getDefaultIconArea() const;
+
+ virtual void Paint (drawinglayer::processor2d::BaseProcessor2D *pProcessor,
+ const ThumbnailItemAttributes *pAttrs) override;
+
+ sal_uInt16 mnRegionId;
+ sal_uInt16 mnDocId;
+
+private:
+
+ OUString maPath;
+ BitmapEx maDefaultBitmap;
+ bool mbIsDefaultTemplate;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/inc/unoctitm.hxx b/sfx2/inc/unoctitm.hxx
new file mode 100644
index 000000000..5dc93943f
--- /dev/null
+++ b/sfx2/inc/unoctitm.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 .
+ */
+#pragma once
+
+#include <memory>
+#include <com/sun/star/frame/XNotifyingDispatch.hpp>
+#include <com/sun/star/lang/XUnoTunnel.hpp>
+#include <comphelper/multiinterfacecontainer4.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <cppuhelper/weakref.hxx>
+
+#include <svl/lstner.hxx>
+#include <sfx2/ctrlitem.hxx>
+#include <mutex>
+
+namespace com::sun::star::frame { class XFrame; }
+namespace com::sun::star::frame { class XNotifyingDispatch; }
+namespace com::sun::star::frame { class XStatusListener; }
+namespace com::sun::star::frame { struct FeatureStateEvent; }
+
+class SfxBindings;
+class SfxDispatcher;
+class SfxSlot;
+
+typedef comphelper::OMultiTypeInterfaceContainerHelperVar4<OUString, css::frame::XStatusListener>
+ SfxStatusDispatcher_Impl_ListenerContainer;
+
+class SfxStatusDispatcher : public cppu::WeakImplHelper<css::frame::XNotifyingDispatch>
+{
+protected:
+ std::mutex maMutex;
+ SfxStatusDispatcher_Impl_ListenerContainer maListeners;
+
+public:
+
+ SfxStatusDispatcher();
+
+ // XDispatch
+ virtual void SAL_CALL dispatchWithNotification( const css::util::URL& aURL,
+ const css::uno::Sequence< css::beans::PropertyValue >& aArgs,
+ const css::uno::Reference< css::frame::XDispatchResultListener >& rListener ) override;
+ 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;
+
+ // Something else
+ void ReleaseAll();
+ void sendStatusChanged(const OUString& rURL, const css::frame::FeatureStateEvent& rEvent);
+ std::vector<OUString> getContainedTypes() { std::unique_lock g(maMutex); return maListeners.getContainedTypes(g); };
+};
+
+class SfxSlotServer;
+class SfxDispatchController_Impl;
+class SfxOfficeDispatch final : public cppu::ImplInheritanceHelper<SfxStatusDispatcher, css::lang::XUnoTunnel>
+{
+friend class SfxDispatchController_Impl;
+ std::unique_ptr<SfxDispatchController_Impl> pImpl;
+public:
+ SfxOfficeDispatch( SfxBindings& rBind,
+ SfxDispatcher* pDispat,
+ const SfxSlot* pSlot,
+ const css::util::URL& rURL );
+ SfxOfficeDispatch( SfxDispatcher* pDispat,
+ const SfxSlot* pSlot,
+ const css::util::URL& rURL );
+ virtual ~SfxOfficeDispatch() override;
+
+ virtual void SAL_CALL dispatchWithNotification( const css::util::URL& aURL,
+ const css::uno::Sequence< css::beans::PropertyValue >& aArgs,
+ const css::uno::Reference< css::frame::XDispatchResultListener >& rListener ) override;
+ 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;
+
+ // XUnoTunnel
+ virtual sal_Int64 SAL_CALL getSomething( const css::uno::Sequence< sal_Int8 >& aIdentifier ) override ;
+ static const css::uno::Sequence< sal_Int8 >& getUnoTunnelId();
+
+ static bool IsMasterUnoCommand( const css::util::URL& aURL );
+ static OUString GetMasterUnoCommand( const css::util::URL& aURL );
+
+ void SetFrame(const css::uno::Reference< css::frame::XFrame >& xFrame);
+
+ void SetMasterUnoCommand( bool bSet );
+
+ SfxDispatcher* GetDispatcher_Impl();
+
+ sal_uInt16 GetId() const;
+};
+
+class SfxDispatchController_Impl final
+ : public SfxControllerItem
+ , public SfxListener
+{
+ css::util::URL aDispatchURL;
+ SfxDispatcher* pDispatcher;
+ SfxBindings* pBindings;
+ const SfxPoolItem* pLastState;
+ SfxOfficeDispatch* pDispatch;
+ bool bMasterSlave;
+ bool bVisible;
+ css::uno::WeakReference< css::frame::XFrame > xFrame;
+
+ static void addParametersToArgs( const css::util::URL& aURL,
+ css::uno::Sequence< css::beans::PropertyValue >& rArgs );
+ static MapUnit GetCoreMetric( SfxItemPool const & rPool, sal_uInt16 nSlot );
+
+ void sendStatusChanged(const OUString& rURL, const css::frame::FeatureStateEvent& rEvent);
+
+public:
+ SfxDispatchController_Impl( SfxOfficeDispatch* pDisp,
+ SfxBindings* pBind,
+ SfxDispatcher* pDispat,
+ const SfxSlot* pSlot,
+ const css::util::URL& rURL );
+ virtual ~SfxDispatchController_Impl() override;
+
+ virtual void Notify(SfxBroadcaster& rBC, const SfxHint& rHint) override;
+
+ static OUString getSlaveCommand( const css::util::URL& rURL );
+
+ void StateChanged( sal_uInt16 nSID, SfxItemState eState, const SfxPoolItem* pState, SfxSlotServer const * pServ );
+ virtual void StateChangedAtToolBoxControl( sal_uInt16 nSID, SfxItemState eState, const SfxPoolItem* pState ) override;
+ void setMasterSlaveCommand( bool bSet );
+ /// @throws css::uno::RuntimeException
+ void dispatch( const css::util::URL& aURL,
+ const css::uno::Sequence< css::beans::PropertyValue >& aArgs,
+ const css::uno::Reference< css::frame::XDispatchResultListener >& rListener );
+ /// @throws css::uno::RuntimeException
+ void addStatusListener(const css::uno::Reference< css::frame::XStatusListener > & xControl, const css::util::URL& aURL);
+ void UnBindController();
+ SfxDispatcher* GetDispatcher();
+ void SetFrame(const css::uno::Reference< css::frame::XFrame >& xFrame);
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/qa/complex/sfx2/DocumentEvents.java b/sfx2/qa/complex/sfx2/DocumentEvents.java
new file mode 100644
index 000000000..32c7ba5ae
--- /dev/null
+++ b/sfx2/qa/complex/sfx2/DocumentEvents.java
@@ -0,0 +1,221 @@
+package complex.sfx2;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.openoffice.test.OfficeConnection;
+import org.openoffice.test.tools.OfficeDocument;
+
+import com.sun.star.document.DocumentEvent;
+import com.sun.star.document.XDocumentEventBroadcaster;
+import com.sun.star.document.XDocumentEventListener;
+import com.sun.star.lang.EventObject;
+import com.sun.star.lang.XEventListener;
+import com.sun.star.lang.XMultiServiceFactory;
+import com.sun.star.uno.UnoRuntime;
+import com.sun.star.util.CloseVetoException;
+import com.sun.star.util.XCloseListener;
+import com.sun.star.util.XCloseable;
+
+public class DocumentEvents
+{
+ private static final OfficeConnection m_connection = new OfficeConnection();
+
+ @BeforeClass
+ public static void setUpConnection() throws Exception
+ {
+ m_connection.setUp();
+ }
+
+
+ @AfterClass
+ public static void tearDownConnection() throws InterruptedException, com.sun.star.uno.Exception
+ {
+ m_connection.tearDown();
+ }
+
+ @Before
+ public void beforeTest() throws Exception
+ {
+ final XMultiServiceFactory xMSF1 = UnoRuntime.queryInterface(
+ XMultiServiceFactory.class, m_connection.getComponentContext().getServiceManager() );
+ m_document = OfficeDocument.blankTextDocument( xMSF1 );
+ }
+
+ @After
+ public void afterTest()
+ {
+ if ( m_document != null )
+ {
+ assertTrue( "closing the test document failed", m_document.close() );
+ m_document = null;
+ }
+ }
+
+ /**
+ * sets up the environment for a test which checks the behavior upon closing a doc
+ */
+ private void impl_setupDocCloseTest()
+ {
+ m_observedCloseEvents.clear();
+
+ final XDocumentEventBroadcaster docEventBroadcaster = UnoRuntime.queryInterface(
+ XDocumentEventBroadcaster.class, m_document.getDocument() );
+ docEventBroadcaster.addDocumentEventListener( new DocumentEventListener() );
+
+ final XCloseable docCloseable = UnoRuntime.queryInterface( XCloseable.class,
+ m_document.getDocument() );
+ docCloseable.addCloseListener( new CloseListener() );
+
+ m_document.getDocument().addEventListener( new DocDisposeListener() );
+ }
+
+ /**
+ * sets up the environment for a test which checks the behavior upon closing a doc
+ */
+ private void impl_tearDownDocCloseTest( final String i_docCloseMethod )
+ {
+ synchronized( m_document )
+ {
+ try
+ {
+ m_document.wait(10000);
+ }
+ catch (InterruptedException ex)
+ {
+ // don't continue the test if somebody interrupted us ...
+ return;
+ }
+ }
+
+ m_document = null;
+ synchronized( m_observedCloseEvents )
+ {
+ assertArrayEquals(
+ "wrong order of events when closing a doc " + i_docCloseMethod,
+ new CloseEventType[] { CloseEventType.OnUnload, CloseEventType.NotifyClosing, CloseEventType.Disposing },
+ m_observedCloseEvents.toArray( new CloseEventType[0] )
+ );
+ }
+ }
+
+ @Test
+ public void testCloseWinEvents() throws Exception
+ {
+ impl_setupDocCloseTest();
+ m_document.getCurrentView().dispatch( ".uno:CloseWin" );
+ impl_tearDownDocCloseTest( "via .uno:CloseWin" );
+ }
+
+ //@Test
+ public void testCloseDocEvents() throws Exception
+ {
+ impl_setupDocCloseTest();
+ m_document.getCurrentView().dispatch( ".uno:CloseDoc" );
+ impl_tearDownDocCloseTest( "via .uno:CloseDoc" );
+ }
+
+ //@Test
+ public void testCloseByAPI()
+ {
+ impl_setupDocCloseTest();
+ // closing the doc by API is synchronous, so do this in a separate thread, else we will get a deadlock
+ // when the document tries to call back our listener (well, I admit I didn't understand *why* we get this
+ // deadlock ... :-\ )
+ (new DocCloser()).start();
+ impl_tearDownDocCloseTest( "by API" );
+ }
+
+ private class DocumentEventListener implements XDocumentEventListener
+ {
+
+ public void documentEventOccured( DocumentEvent i_documentEvent )
+ {
+ if ( i_documentEvent.EventName.equals( "OnUnload" ) )
+ {
+ synchronized( m_observedCloseEvents )
+ {
+ m_observedCloseEvents.add( CloseEventType.OnUnload );
+ }
+ }
+ }
+
+ public void disposing(EventObject eo)
+ {
+ // not interested in
+ }
+ }
+
+ private class CloseListener implements XCloseListener
+ {
+
+ public void queryClosing(EventObject eo, boolean bln) throws CloseVetoException
+ {
+ // not interested in
+ }
+
+ public void notifyClosing(EventObject eo)
+ {
+ synchronized( m_observedCloseEvents )
+ {
+ m_observedCloseEvents.add( CloseEventType.NotifyClosing );
+ }
+ }
+
+ public void disposing(EventObject eo)
+ {
+ // not interested in
+ }
+ }
+
+ private class DocDisposeListener implements XEventListener
+ {
+ public void disposing(EventObject eo)
+ {
+ synchronized( m_observedCloseEvents )
+ {
+ m_observedCloseEvents.add( CloseEventType.Disposing );
+ }
+ synchronized ( m_document )
+ {
+ m_document.notifyAll();
+ }
+ }
+ }
+
+ private class DocCloser extends Thread
+ {
+ @Override
+ public void run()
+ {
+ try
+ {
+ final XCloseable docCloseable = UnoRuntime.queryInterface(XCloseable.class, m_document.getDocument());
+ docCloseable.close(true);
+ }
+ catch (CloseVetoException ex)
+ {
+ Logger.getLogger(DocumentEvents.class.getName()).log(Level.SEVERE, null, ex);
+ }
+ }
+ }
+
+ private enum CloseEventType
+ {
+ OnUnload,
+ NotifyClosing,
+ Disposing
+ }
+
+ private OfficeDocument m_document = null;
+ final private ArrayList< CloseEventType > m_observedCloseEvents = new ArrayList<DocumentEvents.CloseEventType>();
+}
diff --git a/sfx2/qa/complex/sfx2/DocumentMetadataAccess.java b/sfx2/qa/complex/sfx2/DocumentMetadataAccess.java
new file mode 100644
index 000000000..5697bba07
--- /dev/null
+++ b/sfx2/qa/complex/sfx2/DocumentMetadataAccess.java
@@ -0,0 +1,1228 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package complex.sfx2;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import helper.StreamSimulator;
+
+import lib.TestParameters;
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.openoffice.test.OfficeConnection;
+
+import com.sun.star.beans.Pair;
+import com.sun.star.beans.PropertyValue;
+import com.sun.star.beans.StringPair;
+import com.sun.star.beans.XPropertySet;
+import com.sun.star.container.XEnumeration;
+import com.sun.star.container.XEnumerationAccess;
+import com.sun.star.frame.XStorable;
+import com.sun.star.io.XInputStream;
+import com.sun.star.lang.IllegalArgumentException;
+import com.sun.star.lang.XComponent;
+import com.sun.star.lang.XMultiServiceFactory;
+import com.sun.star.lang.XServiceInfo;
+import com.sun.star.rdf.BlankNode;
+import com.sun.star.rdf.FileFormat;
+import com.sun.star.rdf.Literal;
+import com.sun.star.rdf.Statement;
+import com.sun.star.rdf.URI;
+import com.sun.star.rdf.URIs;
+import com.sun.star.rdf.XBlankNode;
+import com.sun.star.rdf.XDocumentMetadataAccess;
+import com.sun.star.rdf.XDocumentRepository;
+import com.sun.star.rdf.XLiteral;
+import com.sun.star.rdf.XMetadatable;
+import com.sun.star.rdf.XNamedGraph;
+import com.sun.star.rdf.XNode;
+import com.sun.star.rdf.XQuerySelectResult;
+import com.sun.star.rdf.XRepository;
+import com.sun.star.rdf.XRepositorySupplier;
+import com.sun.star.rdf.XURI;
+import com.sun.star.text.XText;
+import com.sun.star.text.XTextDocument;
+import com.sun.star.text.XTextRange;
+import com.sun.star.uno.UnoRuntime;
+import com.sun.star.uno.XComponentContext;
+import com.sun.star.util.XCloseable;
+import complex.sfx2.tools.TestDocument;
+
+/**
+ * Test case for interface com.sun.star.rdf.XDocumentMetadataAccess
+ * Currently, this service is implemented in
+ * sfx2/source/doc/DocumentMetadataAccess.cxx
+ *
+ * Actually, this is not a service, so we need to create a document and
+ * go from there...
+ *
+ */
+public class DocumentMetadataAccess
+{
+ XMultiServiceFactory xMSF;
+ XComponentContext xContext;
+ String tempDir;
+
+ String nsRDF = "http://www.w3.org/1999/02/22-rdf-syntax-ns#";
+ String nsRDFS = "http://www.w3.org/2000/01/rdf-schema#";
+ String nsPkg="http://docs.oasis-open.org/opendocument/meta/package/common#";
+ String nsODF ="http://docs.oasis-open.org/opendocument/meta/package/odf#";
+
+ XURI foo;
+ XURI bar;
+ XURI baz;
+
+ static XURI rdf_type;
+ static XURI rdfs_label;
+ static XURI pkg_Document;
+ static XURI pkg_hasPart;
+ static XURI pkg_MetadataFile;
+ static XURI odf_ContentFile;
+ static XURI odf_StylesFile;
+ static XURI odf_Element;
+ static XBlankNode blank1;
+ static XBlankNode blank2;
+ static XBlankNode blank3;
+ static XBlankNode blank4;
+ static String manifestPath = "manifest.rdf";
+ static String contentPath = "content.xml";
+ static String stylesPath = "styles.xml";
+ static String fooPath = "foo.rdf";
+ static String fooBarPath = "meta/foo/bar.rdf";
+
+ XRepository xRep;
+ XRepositorySupplier xRS;
+ XDocumentMetadataAccess xDMA;
+
+ /**
+ * The test parameters
+ */
+ private static TestParameters param = null;
+
+ @Before public void before() throws Exception
+ {
+ xMSF = getMSF();
+ param = new TestParameters();
+ param.put("ServiceFactory", xMSF); // important for param.getMSF()
+
+ assertNotNull("could not create MultiServiceFactory.", xMSF);
+ XPropertySet xPropertySet = UnoRuntime.queryInterface(XPropertySet.class, xMSF);
+ Object defaultCtx = xPropertySet.getPropertyValue("DefaultContext");
+ xContext = UnoRuntime.queryInterface(XComponentContext.class, defaultCtx);
+ assertNotNull("could not get component context.", xContext);
+
+ tempDir = util.utils.getOfficeTemp/*Dir*/(xMSF);
+ System.out.println("tempdir: " + tempDir);
+
+ foo = URI.create(xContext, "uri:foo");
+ assertNotNull("foo", foo);
+ bar = URI.create(xContext, "uri:bar");
+ assertNotNull("bar", bar);
+ baz = URI.create(xContext, "uri:baz");
+ assertNotNull("baz", baz);
+
+ blank1 = BlankNode.create(xContext, "_:1");
+ assertNotNull("blank1", blank1);
+ blank2 = BlankNode.create(xContext, "_:2");
+ assertNotNull("blank2", blank2);
+ blank3 = BlankNode.create(xContext, "_:3");
+ assertNotNull("blank3", blank3);
+ blank4 = BlankNode.create(xContext, "_:4");
+ assertNotNull("blank4", blank4);
+ rdf_type = URI.createKnown(xContext, URIs.RDF_TYPE);
+ assertNotNull("rdf_type", rdf_type);
+ rdfs_label = URI.createKnown(xContext, URIs.RDFS_LABEL);
+ assertNotNull("rdfs_label", rdfs_label);
+ pkg_Document = URI.createKnown(xContext, URIs.PKG_DOCUMENT);
+ assertNotNull("pkg_Document", pkg_Document);
+ pkg_hasPart = URI.createKnown(xContext, URIs.PKG_HASPART);
+ assertNotNull("pkg_hasPart", pkg_hasPart);
+ pkg_MetadataFile = URI.createKnown(xContext, URIs.PKG_METADATAFILE);
+ assertNotNull("pkg_MetadataFile", pkg_MetadataFile);
+ odf_ContentFile = URI.createKnown(xContext, URIs.ODF_CONTENTFILE);
+ assertNotNull("odf_ContentFile", odf_ContentFile);
+ odf_StylesFile = URI.createKnown(xContext, URIs.ODF_STYLESFILE);
+ assertNotNull("odf_StylesFile", odf_StylesFile);
+ odf_Element = URI.createKnown(xContext, URIs.ODF_ELEMENT);
+ assertNotNull("odf_Element", odf_Element);
+ }
+
+ @After public void after()
+ {
+ xRep = null;
+ xRS = null;
+ xDMA = null;
+ }
+
+ @Test public void check() throws Exception
+ {
+ XComponent xComp = null;
+ XComponent xComp2 = null;
+ try {
+ XEnumeration xStmtsEnum;
+ XNamedGraph xManifest;
+
+ System.out.println("Creating document with Repository...");
+
+ // we cannot create a XDMA directly, we must create
+ // a document and get it from there :(
+ // create document
+ PropertyValue[] loadProps = new PropertyValue[1];
+ loadProps[0] = new PropertyValue();
+ loadProps[0].Name = "Hidden";
+ loadProps[0].Value = true;
+ xComp = util.DesktopTools.openNewDoc(xMSF, "swriter", loadProps);
+ XTextDocument xText = UnoRuntime.queryInterface(XTextDocument.class, xComp);
+
+ XRepositorySupplier xRepoSupplier = UnoRuntime.queryInterface(XRepositorySupplier.class, xComp);
+ assertNotNull("xRS null", xRepoSupplier);
+ XDocumentMetadataAccess xDocMDAccess = UnoRuntime.queryInterface(XDocumentMetadataAccess.class, xRepoSupplier);
+ assertNotNull("xDMA null", xDocMDAccess);
+ xRep = xRepoSupplier.getRDFRepository();
+ assertNotNull("xRep null", xRep);
+
+ System.out.println("...done");
+
+ System.out.println("Checking that new repository is initialized...");
+
+ XURI xBaseURI = xDocMDAccess;
+ String baseURI = xBaseURI.getStringValue();
+ assertNotNull("new: baseURI", xBaseURI );
+ assertTrue("new: baseURI", !xBaseURI.getStringValue().equals(""));
+
+ assertTrue("new: # graphs", 1 == xRep.getGraphNames().length);
+ XURI manifest = URI.createNS(xContext, xBaseURI.getStringValue(),
+ manifestPath);
+ xManifest = xRep.getGraph(manifest);
+ assertTrue("new: manifest graph", null != xManifest);
+
+ Statement[] manifestStmts = getManifestStmts(xBaseURI);
+ xStmtsEnum = xRep.getStatements(null, null, null);
+ assertTrue("new: manifest graph", eq(xStmtsEnum, manifestStmts));
+
+ System.out.println("...done");
+
+ System.out.println("Checking some invalid args...");
+
+ String content = "behold, for I am the content.";
+ new TestRange(content);
+
+ try {
+ xDocMDAccess.getElementByURI(null);
+ fail("getElementByURI: null allowed");
+ } catch (IllegalArgumentException e) {
+ // ignore
+ }
+ try {
+ xDocMDAccess.getMetadataGraphsWithType(null);
+ fail("getMetadataGraphsWithType: null URI allowed");
+ } catch (IllegalArgumentException e) {
+ // ignore
+ }
+ try {
+ xDocMDAccess.addMetadataFile("", new XURI[0]);
+ fail("addMetadataFile: empty filename allowed");
+ } catch (IllegalArgumentException e) {
+ // ignore
+ }
+ try {
+ xDocMDAccess.addMetadataFile("/foo", new XURI[0]);
+ fail("addMetadataFile: absolute filename allowed");
+ } catch (IllegalArgumentException e) {
+ // ignore
+ }
+ try {
+ xDocMDAccess.addMetadataFile("fo\"o", new XURI[0]);
+ fail("addMetadataFile: invalid filename allowed");
+ } catch (IllegalArgumentException e) {
+ // ignore
+ }
+ try {
+ xDocMDAccess.addMetadataFile("../foo", new XURI[0]);
+ fail("addMetadataFile: filename with .. allowed");
+ } catch (IllegalArgumentException e) {
+ // ignore
+ }
+ try {
+ xDocMDAccess.addMetadataFile("foo/../../bar", new XURI[0]);
+ fail("addMetadataFile: filename with nest .. allowed");
+ } catch (IllegalArgumentException e) {
+ // ignore
+ }
+ try {
+ xDocMDAccess.addMetadataFile("foo/././bar", new XURI[0]);
+ fail("addMetadataFile: filename with nest . allowed");
+ } catch (IllegalArgumentException e) {
+ // ignore
+ }
+ try {
+ xDocMDAccess.addMetadataFile("content.xml", new XURI[0]);
+ fail("addMetadataFile: content.xml allowed");
+ } catch (IllegalArgumentException e) {
+ // ignore
+ }
+ try {
+ xDocMDAccess.addMetadataFile("styles.xml", new XURI[0]);
+ fail("addMetadataFile: styles.xml allowed");
+ } catch (IllegalArgumentException e) {
+ // ignore
+ }
+ try {
+ xDocMDAccess.addMetadataFile("meta.xml", new XURI[0]);
+ fail("addMetadataFile: meta.xml allowed");
+ } catch (IllegalArgumentException e) {
+ // ignore
+ }
+ try {
+ xDocMDAccess.addMetadataFile("settings.xml", new XURI[0]);
+ fail("addMetadataFile: settings.xml allowed");
+ } catch (IllegalArgumentException e) {
+ // ignore
+ }
+ try {
+ xDocMDAccess.importMetadataFile(FileFormat.RDF_XML, null, "foo",
+ foo, new XURI[0]);
+ fail("importMetadataFile: null stream allowed");
+ } catch (IllegalArgumentException e) {
+ // ignore
+ }
+
+ final String sEmptyRDF = TestDocument.getUrl("empty.rdf");
+ try {
+ XInputStream xFooIn = new StreamSimulator(sEmptyRDF, true, param);
+ xDocMDAccess.importMetadataFile(FileFormat.RDF_XML, xFooIn, "",
+ foo, new XURI[0]);
+ fail("importMetadataFile: empty filename allowed");
+ } catch (IllegalArgumentException e) {
+ // ignore
+ }
+ try {
+ XInputStream xFooIn =
+ new StreamSimulator(sEmptyRDF, true, param);
+ xDocMDAccess.importMetadataFile(FileFormat.RDF_XML, xFooIn, "meta.xml",
+ foo, new XURI[0]);
+ fail("importMetadataFile: meta.xml filename allowed");
+ } catch (IllegalArgumentException e) {
+ // ignore
+ }
+ try {
+ XInputStream xFooIn =
+ new StreamSimulator(sEmptyRDF, true, param);
+ xDocMDAccess.importMetadataFile(FileFormat.RDF_XML,
+ xFooIn, "foo", null, new XURI[0]);
+ fail("importMetadataFile: null base URI allowed");
+ } catch (IllegalArgumentException e) {
+ // ignore
+ }
+ try {
+ XInputStream xFooIn =
+ new StreamSimulator(sEmptyRDF, true, param);
+ xDocMDAccess.importMetadataFile(FileFormat.RDF_XML,
+ xFooIn, "foo", rdf_type, new XURI[0]);
+ fail("importMetadataFile: non-absolute base URI allowed");
+ } catch (IllegalArgumentException e) {
+ // ignore
+ }
+ try {
+ xDocMDAccess.removeMetadataFile(null);
+ fail("removeMetadataFile: null URI allowed");
+ } catch (IllegalArgumentException e) {
+ // ignore
+ }
+ try {
+ xDocMDAccess.addContentOrStylesFile("");
+ fail("addContentOrStylesFile: empty filename allowed");
+ } catch (IllegalArgumentException e) {
+ // ignore
+ }
+ try {
+ xDocMDAccess.addContentOrStylesFile("/content.xml");
+ fail("addContentOrStylesFile: absolute filename allowed");
+ } catch (IllegalArgumentException e) {
+ // ignore
+ }
+ try {
+ xDocMDAccess.addContentOrStylesFile("foo.rdf");
+ fail("addContentOrStylesFile: invalid filename allowed");
+ } catch (IllegalArgumentException e) {
+ // ignore
+ }
+ try {
+ xDocMDAccess.removeContentOrStylesFile("");
+ fail("removeContentOrStylesFile: empty filename allowed");
+ } catch (IllegalArgumentException e) {
+ // ignore
+ }
+ try {
+ xDocMDAccess.loadMetadataFromStorage(null, foo, null);
+ fail("loadMetadataFromStorage: null storage allowed");
+ } catch (IllegalArgumentException e) {
+ // ignore
+ }
+ try {
+ xDocMDAccess.storeMetadataToStorage(null/*, base*/);
+ fail("storeMetadataToStorage: null storage allowed");
+ } catch (IllegalArgumentException e) {
+ // ignore
+ }
+ try {
+ xDocMDAccess.loadMetadataFromMedium(new PropertyValue[0]);
+ fail("loadMetadataFromMedium: empty medium allowed");
+ } catch (IllegalArgumentException e) {
+ // ignore
+ }
+ try {
+ xDocMDAccess.storeMetadataToMedium(new PropertyValue[0]);
+ fail("storeMetadataToMedium: empty medium allowed");
+ } catch (IllegalArgumentException e) {
+ // ignore
+ }
+
+ System.out.println("...done");
+
+ System.out.println("Checking file addition/removal...");
+
+ xDocMDAccess.removeContentOrStylesFile(contentPath);
+ xStmtsEnum = xManifest.getStatements(null, null, null);
+ assertTrue("removeContentOrStylesFile (content)",
+ eq(xStmtsEnum, new Statement[] {
+ manifestStmts[0], manifestStmts[2], manifestStmts[4]
+ }));
+
+ xDocMDAccess.addContentOrStylesFile(contentPath);
+ xStmtsEnum = xManifest.getStatements(null, null, null);
+ assertTrue("addContentOrStylesFile (content)",
+ eq(xStmtsEnum, manifestStmts));
+
+ xDocMDAccess.removeContentOrStylesFile(stylesPath);
+ xStmtsEnum = xManifest.getStatements(null, null, null);
+ assertTrue("removeContentOrStylesFile (styles)",
+ eq(xStmtsEnum, new Statement[] {
+ manifestStmts[0], manifestStmts[1], manifestStmts[3]
+ }));
+
+ xDocMDAccess.addContentOrStylesFile(stylesPath);
+ xStmtsEnum = xManifest.getStatements(null, null, null);
+ assertTrue("addContentOrStylesFile (styles)",
+ eq(xStmtsEnum, manifestStmts));
+
+ XURI xFoo = URI.createNS(xContext, xBaseURI.getStringValue(),
+ fooPath);
+ Statement xM_BaseHaspartFoo =
+ new Statement(xBaseURI, pkg_hasPart, xFoo, manifest);
+ Statement xM_FooTypeMetadata =
+ new Statement(xFoo, rdf_type, pkg_MetadataFile, manifest);
+ Statement xM_FooTypeBar =
+ new Statement(xFoo, rdf_type, bar, manifest);
+ xDocMDAccess.addMetadataFile(fooPath, new XURI[] { bar });
+ xStmtsEnum = xManifest.getStatements(null, null, null);
+ assertTrue("addMetadataFile",
+ eq(xStmtsEnum, merge(manifestStmts, new Statement[] {
+ xM_BaseHaspartFoo, xM_FooTypeMetadata, xM_FooTypeBar
+ })));
+
+ XURI[] graphsBar = xDocMDAccess.getMetadataGraphsWithType(bar);
+ assertTrue("getMetadataGraphsWithType",
+ graphsBar.length == 1 && eq(graphsBar[0], xFoo));
+
+
+ xDocMDAccess.removeMetadataFile(xFoo);
+ xStmtsEnum = xManifest.getStatements(null, null, null);
+ assertTrue("removeMetadataFile",
+ eq(xStmtsEnum, manifestStmts));
+
+ System.out.println("...done");
+
+ System.out.println("Checking mapping...");
+
+ XEnumerationAccess xTextEnum = UnoRuntime.queryInterface(XEnumerationAccess.class, xText.getText());
+ Object o = xTextEnum.createEnumeration().nextElement();
+ XMetadatable xMeta1 = UnoRuntime.queryInterface(XMetadatable.class, o);
+
+ XMetadatable xMeta;
+ xMeta = xDocMDAccess.getElementByURI(xMeta1);
+ assertTrue("getElementByURI: null", null != xMeta);
+ String XmlId = xMeta.getMetadataReference().Second;
+ String XmlId1 = xMeta1.getMetadataReference().Second;
+ assertTrue("getElementByURI: no xml id", !XmlId.equals(""));
+ assertTrue("getElementByURI: different xml id", XmlId.equals(XmlId1));
+
+ System.out.println("...done");
+
+ System.out.println("Checking storing and loading...");
+
+ XURI xFoobar = URI.createNS(xContext, xBaseURI.getStringValue(),
+ fooBarPath);
+ Statement[] metadataStmts = getMetadataFileStmts(xBaseURI,
+ fooBarPath);
+ xDocMDAccess.addMetadataFile(fooBarPath, new XURI[0]);
+ xStmtsEnum = xRep.getStatements(null, null, null);
+ assertTrue("addMetadataFile",
+ eq(xStmtsEnum, merge(manifestStmts, metadataStmts )));
+
+ Statement xFoobar_FooBarFoo =
+ new Statement(foo, bar, foo, xFoobar);
+ xRep.getGraph(xFoobar).addStatement(foo, bar, foo);
+ xStmtsEnum = xRep.getStatements(null, null, null);
+ assertTrue("addStatement",
+ eq(xStmtsEnum, merge(manifestStmts, merge(metadataStmts,
+ new Statement[] { xFoobar_FooBarFoo }))));
+
+ PropertyValue noMDNoContentFile = new PropertyValue();
+ noMDNoContentFile.Name = "URL";
+ noMDNoContentFile.Value = TestDocument.getUrl("CUSTOM.odt");
+ PropertyValue noMDFile = new PropertyValue();
+ noMDFile.Name = "URL";
+ noMDFile.Value = TestDocument.getUrl("TEST.odt");
+ PropertyValue file = new PropertyValue();
+ file.Name = "URL";
+ file.Value = tempDir + "TESTDMA.odt";
+ PropertyValue mimetype = new PropertyValue();
+ mimetype.Name = "MediaType";
+ mimetype.Value = "application/vnd.oasis.opendocument.text";
+ PropertyValue[] argsEmptyNoContent = { mimetype, noMDNoContentFile};
+ PropertyValue[] argsEmpty = { mimetype, noMDFile };
+ PropertyValue[] args = { mimetype, file };
+
+ xStmtsEnum = xRep.getStatements(null, null, null);
+ XURI[] graphs = xRep.getGraphNames();
+
+ xDocMDAccess.storeMetadataToMedium(args);
+
+ // this should re-init
+ xDocMDAccess.loadMetadataFromMedium(argsEmptyNoContent);
+ xRep = xRepoSupplier.getRDFRepository();
+ assertTrue("xRep null", null != xRep);
+ assertTrue("baseURI still tdoc?",
+ !baseURI.equals(xDocMDAccess.getStringValue()));
+ Statement[] manifestStmts2 = getManifestStmts(xDocMDAccess);
+ xStmtsEnum = xRep.getStatements(null, null, null);
+ // there is no content or styles file in here, so we have just
+ // the package stmt
+ assertTrue("loadMetadataFromMedium (no metadata, no content)",
+ eq(xStmtsEnum, new Statement[] { manifestStmts2[0] }));
+
+ // this should re-init
+ xDocMDAccess.loadMetadataFromMedium(argsEmpty);
+ xRep = xRepoSupplier.getRDFRepository();
+ assertTrue("xRep null", null != xRep);
+ assertTrue("baseURI still tdoc?",
+ !baseURI.equals(xDocMDAccess.getStringValue()));
+ Statement[] manifestStmts3 = getManifestStmts(xDocMDAccess);
+
+ xStmtsEnum = xRep.getStatements(null, null, null);
+ assertTrue("loadMetadataFromMedium (no metadata)",
+ eq(xStmtsEnum, manifestStmts3));
+
+ xDocMDAccess.loadMetadataFromMedium(args);
+ xRep = xRepoSupplier.getRDFRepository();
+ assertTrue("xRep null", null != xRep);
+ Statement[] manifestStmts4 = getManifestStmts(xDocMDAccess);
+ Statement[] metadataStmts4 = getMetadataFileStmts(xDocMDAccess,
+ fooBarPath);
+
+ xStmtsEnum = xRep.getStatements(null, null, null);
+ assertTrue("some graph(s) not reloaded",
+ graphs.length == xRep.getGraphNames().length);
+
+ XURI xFoobar4 = URI.createNS(xContext, xDocMDAccess.getStringValue(),
+ fooBarPath);
+ Statement xFoobar_FooBarFoo4 =
+ new Statement(foo, bar, foo, xFoobar4);
+ assertTrue("loadMetadataFromMedium (re-load)",
+ eq(xStmtsEnum, merge(manifestStmts4, merge(metadataStmts4,
+ new Statement[] { xFoobar_FooBarFoo4 }))));
+
+ System.out.println("...done");
+
+ System.out.println("Checking storing and loading via model...");
+
+ String f = tempDir + "TESTPARA.odt";
+
+ XStorable xStor = UnoRuntime.queryInterface(XStorable.class, xRepoSupplier);
+
+ xStor.storeToURL(f, new PropertyValue[0]);
+
+ xComp2 = util.DesktopTools.loadDoc(xMSF, f, loadProps);
+
+ XDocumentMetadataAccess xDMA2 = UnoRuntime.queryInterface(XDocumentMetadataAccess.class, xComp2);
+ assertTrue("xDMA2 null", null != xDMA2);
+
+ XRepositorySupplier xRS2 = UnoRuntime.queryInterface(XRepositorySupplier.class, xComp2);
+ assertTrue("xRS2 null", null != xRS2);
+
+ XRepository xRep2 = xRS2.getRDFRepository();
+ assertTrue("xRep2 null", null != xRep2);
+
+ Statement[] manifestStmts5 = getManifestStmts(xDMA2);
+ Statement[] metadataStmts5 = getMetadataFileStmts(xDMA2,
+ fooBarPath);
+ XURI xFoobar5 = URI.createNS(xContext, xDMA2.getStringValue(),
+ fooBarPath);
+ Statement xFoobar_FooBarFoo5 =
+ new Statement(foo, bar, foo, xFoobar5);
+ xStmtsEnum = xRep.getStatements(null, null, null);
+ XEnumeration xStmtsEnum2 = xRep2.getStatements(null, null, null);
+ assertTrue("load: repository differs",
+ eq(xStmtsEnum2, merge(manifestStmts5, merge(metadataStmts5,
+ new Statement[] { xFoobar_FooBarFoo5 }))));
+
+ System.out.println("...done");
+
+ } finally {
+ close(xComp);
+ close(xComp2);
+ }
+ }
+
+ @Test public void checkRDFa() throws Exception
+ {
+ XComponent xComp = null;
+ try {
+ final String file = TestDocument.getUrl("TESTRDFA.odt");
+ xComp = loadRDFa(file);
+ if (xComp != null)
+ {
+ final String sNewFile = tempDir + "TESTRDFA.odt";
+ storeRDFa(xComp, sNewFile);
+ close(xComp);
+
+ xComp = loadRDFa(sNewFile);
+ }
+ } finally {
+ close(xComp);
+ }
+ }
+
+ @Test
+ public void checkTdf123293() throws Exception
+ {
+ XComponent xComp = null;
+ try {
+ xComp = util.DesktopTools.loadDocUsingStream(xMSF, TestDocument.getPath("TESTRDFA.odt"));
+
+ // Metadata was discarded when loading from stream, make sure it's there now
+ XRepositorySupplier xRepoSupplier = UnoRuntime.queryInterface(XRepositorySupplier.class, xComp);
+ assertNotNull("No metadata loaded", xRepoSupplier);
+ } finally {
+ close(xComp);
+ }
+ }
+
+ private void storeRDFa(XComponent xComp, String file) throws com.sun.star.io.IOException
+ {
+ System.out.println("Storing test document...");
+
+ XStorable xStor = UnoRuntime.queryInterface(XStorable.class, xComp);
+
+ xStor.storeToURL(file, new PropertyValue[0]);
+
+ System.out.println("...done");
+ }
+
+ private XComponent loadRDFa(String file) throws Exception
+ {
+ XComponent xComp = null;
+
+ System.out.println("Loading test document...");
+
+ PropertyValue[] loadProps = new PropertyValue[1];
+ loadProps[0] = new PropertyValue();
+ loadProps[0].Name = "Hidden";
+ loadProps[0].Value = true;
+
+
+
+ xComp = util.DesktopTools.loadDoc(xMSF, file, loadProps);
+
+ XRepositorySupplier xRepoSupplier = UnoRuntime.queryInterface(XRepositorySupplier.class, xComp);
+ assertTrue("xRS null", null != xRepoSupplier);
+
+ XDocumentRepository xDocRepository = UnoRuntime.queryInterface(XDocumentRepository.class, xRepoSupplier.getRDFRepository());
+ assertTrue("xRep null", null != xDocRepository);
+
+ XTextDocument xTextDoc = UnoRuntime.queryInterface(XTextDocument.class, xComp);
+
+ XText xText = xTextDoc.getText();
+
+ XEnumerationAccess xEA = UnoRuntime.queryInterface(XEnumerationAccess.class, xText);
+ XEnumeration xEnum = xEA.createEnumeration();
+
+ System.out.println("...done");
+
+ System.out.println("Checking RDFa in loaded test document...");
+
+ XMetadatable xPara;
+ Pair<Statement[], Boolean> result;
+
+ Statement x_FooBarLit1 = new Statement(foo, bar, mkLit("1"), null);
+ xPara = UnoRuntime.queryInterface(XMetadatable.class, xEnum.nextElement());
+ result = xDocRepository.getStatementRDFa(xPara);
+ assertTrue("RDFa: 1",
+ !result.Second &&
+ eq(result.First, new Statement[] {
+ x_FooBarLit1
+ }));
+
+ Statement x_FooBarLit2 = new Statement(foo, bar, mkLit("2"), null);
+ xPara = UnoRuntime.queryInterface(XMetadatable.class, xEnum.nextElement());
+ result = xDocRepository.getStatementRDFa(xPara);
+ assertTrue("RDFa: 2",
+ !result.Second &&
+ eq(result.First, new Statement[] {
+ x_FooBarLit2
+ }));
+
+ Statement x_BlankBarLit3 =
+ new Statement(blank1, bar, mkLit("3"), null);
+ xPara = UnoRuntime.queryInterface(XMetadatable.class, xEnum.nextElement());
+ result = xDocRepository.getStatementRDFa(xPara);
+ assertTrue("RDFa: 3",
+ !result.Second &&
+ eq(result.First, new Statement[] {
+ x_BlankBarLit3
+ }));
+ XBlankNode b3 = UnoRuntime.queryInterface(XBlankNode.class, result.First[0].Subject);
+
+ Statement x_BlankBarLit4 =
+ new Statement(blank2, bar, mkLit("4"), null);
+ xPara = UnoRuntime.queryInterface(XMetadatable.class, xEnum.nextElement());
+ result = xDocRepository.getStatementRDFa(xPara);
+ assertTrue("RDFa: 4",
+ !result.Second &&
+ eq(result.First, new Statement[] {
+ x_BlankBarLit4
+ }));
+ XBlankNode b4 = UnoRuntime.queryInterface(XBlankNode.class, result.First[0].Subject);
+
+ Statement x_BlankBarLit5 =
+ new Statement(blank1, bar, mkLit("5"), null);
+ xPara = UnoRuntime.queryInterface(XMetadatable.class, xEnum.nextElement());
+ result = xDocRepository.getStatementRDFa(xPara);
+ assertTrue("RDFa: 5",
+ !result.Second &&
+ eq(result.First, new Statement[] {
+ x_BlankBarLit5
+ }));
+ XBlankNode b5 = UnoRuntime.queryInterface(XBlankNode.class, result.First[0].Subject);
+
+ assertTrue("RDFa: 3 != 4",
+ !b3.getStringValue().equals(b4.getStringValue()));
+ assertTrue("RDFa: 3 == 5",
+ b3.getStringValue().equals(b5.getStringValue()));
+
+ Statement x_FooBarLit6 = new Statement(foo, bar, mkLit("6"), null);
+ Statement x_FooBazLit6 = new Statement(foo, baz, mkLit("6"), null);
+ xPara = UnoRuntime.queryInterface(XMetadatable.class, xEnum.nextElement());
+ result = xDocRepository.getStatementRDFa(xPara);
+ assertTrue("RDFa: 6",
+ !result.Second &&
+ eq(result.First, new Statement[] {
+ x_FooBarLit6, x_FooBazLit6
+ }));
+
+ Statement x_FooBarLit7 = new Statement(foo, bar, mkLit("7"), null);
+ Statement x_FooBazLit7 = new Statement(foo, baz, mkLit("7"), null);
+ Statement x_FooFooLit7 = new Statement(foo, foo, mkLit("7"), null);
+ xPara = UnoRuntime.queryInterface(XMetadatable.class, xEnum.nextElement());
+ result = xDocRepository.getStatementRDFa(xPara);
+ assertTrue("RDFa: 7",
+ !result.Second &&
+ eq(result.First, new Statement[] {
+ x_FooBarLit7, x_FooBazLit7, x_FooFooLit7
+ }));
+
+ XNode lit = mkLit("a fooish bar");
+ XNode lit_type= mkLit("a fooish bar", bar);
+ Statement x_FooBarLit = new Statement(foo, bar, lit, null);
+ Statement x_FooBarLittype = new Statement(foo, bar, lit_type, null);
+
+ xPara = UnoRuntime.queryInterface(XMetadatable.class, xEnum.nextElement());
+ result = xDocRepository.getStatementRDFa(xPara);
+ assertTrue("RDFa: 8",
+ result.Second &&
+ eq(result.First, new Statement[] {
+ x_FooBarLit
+ }));
+
+ xPara = UnoRuntime.queryInterface(XMetadatable.class, xEnum.nextElement());
+ result = xDocRepository.getStatementRDFa(xPara);
+ assertTrue("RDFa: 9",
+ result.Second &&
+ eq(result.First, new Statement[] {
+ x_FooBarLit
+ }));
+
+ xPara = UnoRuntime.queryInterface(XMetadatable.class, xEnum.nextElement());
+ result = xDocRepository.getStatementRDFa(xPara);
+ assertTrue("RDFa: 10",
+ result.Second &&
+ eq(result.First, new Statement[] {
+ x_FooBarLittype
+ }));
+
+ Statement x_FooBarLit11
+ = new Statement(foo, bar, mkLit("11", bar), null);
+ xPara = UnoRuntime.queryInterface(XMetadatable.class, xEnum.nextElement());
+ result = xDocRepository.getStatementRDFa(xPara);
+ assertTrue("RDFa: 11",
+ !result.Second &&
+ eq(result.First, new Statement[] {
+ x_FooBarLit11
+ }));
+
+ XURI xFile = URI.createNS(xContext, file, "/" + contentPath);
+ Statement x_FileBarLit12 =
+ new Statement(xFile, bar, mkLit("12"), null);
+ xPara = UnoRuntime.queryInterface(XMetadatable.class, xEnum.nextElement());
+ result = xDocRepository.getStatementRDFa(xPara);
+ assertTrue("RDFa: 12",
+ !result.Second &&
+ eq(result.First, new Statement[] {
+ x_FileBarLit12
+ }));
+
+ xPara = UnoRuntime.queryInterface(XMetadatable.class, xEnum.nextElement());
+ result = xDocRepository.getStatementRDFa(xPara);
+ assertTrue("RDFa: 13",
+ result.Second &&
+ eq(result.First, new Statement[] {
+ x_FooBarLit
+ }));
+
+ new Statement(foo, rdfs_label, mkLit("14"), null);
+ xPara = UnoRuntime.queryInterface(XMetadatable.class, xEnum.nextElement());
+ result = xDocRepository.getStatementRDFa(xPara);
+ assertTrue("RDFa: 14",
+ result.Second &&
+ eq(result.First, new Statement[] {
+ /* x_FooLabelLit14 */ x_FooBarLit
+ }));
+
+ xPara = UnoRuntime.queryInterface(XMetadatable.class, xEnum.nextElement());
+ result = xDocRepository.getStatementRDFa(xPara);
+ assertTrue("RDFa: 15", eq(result.First, new Statement[] { } ));
+
+ xPara = UnoRuntime.queryInterface(XMetadatable.class, xEnum.nextElement());
+ result = xDocRepository.getStatementRDFa(xPara);
+ assertTrue("RDFa: 16", eq(result.First, new Statement[] { } ));
+
+ xPara = UnoRuntime.queryInterface(XMetadatable.class, xEnum.nextElement());
+ result = xDocRepository.getStatementRDFa(xPara);
+ assertTrue("RDFa: 17", eq(result.First, new Statement[] { } ));
+
+ xPara = UnoRuntime.queryInterface(XMetadatable.class, xEnum.nextElement());
+ result = xDocRepository.getStatementRDFa(xPara);
+ assertTrue("RDFa: 18", eq(result.First, new Statement[] { } ));
+
+ xPara = UnoRuntime.queryInterface(XMetadatable.class, xEnum.nextElement());
+ result = xDocRepository.getStatementRDFa(xPara);
+ assertTrue("RDFa: 19", eq(result.First, new Statement[] { } ));
+
+ xPara = UnoRuntime.queryInterface(
+ XMetadatable.class, xEnum.nextElement());
+ result = xDocRepository.getStatementRDFa(xPara);
+ assertTrue("RDFa: 20", eq(result.First, new Statement[] { } ));
+
+ xPara = UnoRuntime.queryInterface(
+ XMetadatable.class, xEnum.nextElement());
+ result = xDocRepository.getStatementRDFa(xPara);
+ assertTrue("RDFa: 21", eq(result.First, new Statement[] { } ));
+
+ System.out.println("...done");
+
+ return xComp;
+ }
+
+
+// utilities -------------------------------------------------------------
+
+ static void close(XComponent i_comp)
+ {
+ try {
+ XCloseable xClos = UnoRuntime.queryInterface(XCloseable.class, i_comp);
+ if (xClos != null)
+ {
+ xClos.close(true);
+ }
+ } catch (Exception e) {
+ }
+ }
+
+ XLiteral mkLit(String i_content)
+ {
+ return Literal.create(xContext, i_content);
+ }
+
+ XLiteral mkLit(String i_content, XURI i_uri)
+ {
+ return Literal.createWithType(xContext, i_content, i_uri);
+ }
+
+ static Statement[] merge(Statement[] i_A1, Statement[] i_A2)
+ {
+ // bah, java sucks...
+ Statement[] ret = new Statement[i_A1.length + i_A2.length];
+ for (int i = 0; i < i_A1.length; ++i) {
+ ret[i] = i_A1[i];
+ }
+ for (int i = 0; i < i_A2.length; ++i) {
+ ret[i+i_A1.length] = i_A2[i];
+ }
+ return ret;
+ }
+
+ public static String toS(XNode n) {
+ if (null == n)
+ {
+ return "< null >";
+ }
+ return n.getStringValue();
+ }
+
+ static boolean isBlank(XNode i_node)
+ {
+ XBlankNode blank = UnoRuntime.queryInterface(XBlankNode.class, i_node);
+ return blank != null;
+ }
+
+
+ static Statement[] toSeq(XEnumeration i_Enum) throws Exception
+ {
+ java.util.Collection<Statement> c = new java.util.ArrayList<Statement>();
+ while (i_Enum.hasMoreElements()) {
+ Statement s = (Statement) i_Enum.nextElement();
+ c.add(s);
+ }
+ // java sucks
+ Object[] arr = c.toArray();
+ Statement[] ret = new Statement[arr.length];
+ for (int i = 0; i < arr.length; ++i) {
+ ret[i] = (Statement) arr[i];
+ }
+ return ret;
+ }
+
+ static XNode[][] toSeqs(XEnumeration i_Enum) throws Exception
+ {
+ java.util.Collection<XNode[]> c = new java.util.ArrayList<XNode[]>();
+ while (i_Enum.hasMoreElements()) {
+ XNode[] s = (XNode[]) i_Enum.nextElement();
+ c.add(s);
+ }
+ Object[] arr = c.toArray();
+ XNode[][] ret = new XNode[arr.length][];
+ for (int i = 0; i < arr.length; ++i) {
+ ret[i] = (XNode[]) arr[i];
+ }
+ return ret;
+ }
+
+ private static class BindingComp implements java.util.Comparator<XNode[]>
+ {
+ public int compare(XNode[] left, XNode[] right)
+ {
+ if (left.length != right.length)
+ {
+ throw new RuntimeException();
+ }
+ for (int i = 0; i < left.length; ++i) {
+ int eq = (left[i].getStringValue().compareTo(
+ right[i].getStringValue()));
+ if (eq != 0)
+ {
+ return eq;
+ }
+ }
+ return 0;
+ }
+ }
+
+ private static class StmtComp implements java.util.Comparator<Statement>
+ {
+ public int compare(Statement left, Statement right)
+ {
+ int eq;
+ if ((eq = cmp(left.Graph, right.Graph )) != 0) return eq;
+ if ((eq = cmp(left.Subject, right.Subject )) != 0) return eq;
+ if ((eq = cmp(left.Predicate, right.Predicate)) != 0) return eq;
+ if ((eq = cmp(left.Object, right.Object )) != 0) return eq;
+ return 0;
+ }
+
+ private int cmp(XNode i_Left, XNode i_Right)
+ {
+ if (isBlank(i_Left)) {
+ return isBlank(i_Right) ? 0 : 1;
+ } else {
+ if (isBlank(i_Right)) {
+ return -1;
+ } else {
+ return toS(i_Left).compareTo(toS(i_Right));
+ }
+ }
+ }
+ }
+
+ static boolean eq(Statement i_Left, Statement i_Right)
+ {
+ XURI lG = i_Left.Graph;
+ XURI rG = i_Right.Graph;
+ if (!eq(lG, rG)) {
+ System.out.println("Graphs differ: " + toS(lG) + " != " + toS(rG));
+ return false;
+ }
+ if (!eq(i_Left.Subject, i_Right.Subject)) {
+ System.out.println("Subjects differ: " +
+ i_Left.Subject.getStringValue() + " != " +
+ i_Right.Subject.getStringValue());
+ return false;
+ }
+ if (!eq(i_Left.Predicate, i_Right.Predicate)) {
+ System.out.println("Predicates differ: " +
+ i_Left.Predicate.getStringValue() + " != " +
+ i_Right.Predicate.getStringValue());
+ return false;
+ }
+ if (!eq(i_Left.Object, i_Right.Object)) {
+ System.out.println("Objects differ: " +
+ i_Left.Object.getStringValue() + " != " +
+ i_Right.Object.getStringValue());
+ return false;
+ }
+ return true;
+ }
+
+ static boolean eq(Statement[] i_Result, Statement[] i_Expected)
+ {
+ if (i_Result.length != i_Expected.length) {
+ System.out.println("eq: different lengths: " + i_Result.length + " " +
+ i_Expected.length);
+ return false;
+ }
+ Statement[] expected = i_Expected.clone();
+ java.util.Arrays.sort(i_Result, new StmtComp());
+ java.util.Arrays.sort(expected, new StmtComp());
+ for (int i = 0; i < expected.length; ++i)
+ {
+ // This is better for debug!
+ final Statement a = i_Result[i];
+ final Statement b = expected[i];
+ final boolean cond = eq(a, b);
+ if (!cond) return false;
+ }
+ return true;
+ }
+
+ static boolean eq(XEnumeration i_Enum, Statement[] i_Expected)
+ throws Exception
+ {
+ Statement[] current = toSeq(i_Enum);
+ return eq(current, i_Expected);
+ }
+
+ static boolean eq(XNode i_Left, XNode i_Right)
+ {
+ if (i_Left == null) {
+ return (i_Right == null);
+ } else {
+ return (i_Right != null) &&
+ (i_Left.getStringValue().equals(i_Right.getStringValue())
+ // FIXME: hack: blank nodes considered equal
+ || (isBlank(i_Left) && isBlank(i_Right)));
+ }
+ }
+
+ static boolean eq(XQuerySelectResult i_Result,
+ String[] i_Vars, XNode[][] i_Bindings) throws Exception
+ {
+ String[] vars = i_Result.getBindingNames();
+ XEnumeration iter = i_Result;
+ XNode[][] bindings = toSeqs(iter);
+ if (vars.length != i_Vars.length) {
+ System.out.println("var lengths differ");
+ return false;
+ }
+ if (bindings.length != i_Bindings.length) {
+ System.out.println("binding lengths differ: " + i_Bindings.length +
+ " vs " + bindings.length );
+ return false;
+ }
+ java.util.Arrays.sort(bindings, new BindingComp());
+ java.util.Arrays.sort(i_Bindings, new BindingComp());
+ for (int i = 0; i < i_Bindings.length; ++i) {
+ if (i_Bindings[i].length != i_Vars.length) {
+ System.out.println("TEST ERROR!");
+ throw new Exception();
+ }
+ if (bindings[i].length != i_Vars.length) {
+ System.out.println("binding length and var length differ");
+ return false;
+ }
+ for (int j = 0; j < i_Vars.length; ++j) {
+ if (!eq(bindings[i][j], i_Bindings[i][j])) {
+ System.out.println("bindings differ: " +
+ toS(bindings[i][j]) + " != " + toS(i_Bindings[i][j]));
+ return false;
+ }
+ }
+ }
+ for (int i = 0; i < i_Vars.length; ++i) {
+ if (!vars[i].equals(i_Vars[i])) {
+ System.out.println("variable names differ: " +
+ vars[i] + " != " + i_Vars[i]);
+ return false;
+ }
+ }
+ return true;
+ }
+
+ static boolean eq(StringPair i_Left, StringPair i_Right)
+ {
+ return ((i_Left.First).equals(i_Right.First)) &&
+ ((i_Left.Second).equals(i_Right.Second));
+ }
+
+ static String mkNamespace(String i_prefix, String i_namespace)
+ {
+ return "PREFIX " + i_prefix + ": <" + i_namespace + ">\n";
+ }
+
+ static String mkNss()
+ {
+ String namespaces = mkNamespace("rdf",
+ "http://www.w3.org/1999/02/22-rdf-syntax-ns#");
+ namespaces += mkNamespace("pkg",
+ "http://docs.oasis-open.org/opendocument/meta/package/common#");
+ namespaces += mkNamespace("odf",
+ "http://docs.oasis-open.org/opendocument/meta/package/odf#");
+ return namespaces;
+ }
+
+ Statement[] getManifestStmts(XURI xBaseURI) throws Exception
+ {
+ XURI xManifest = URI.createNS(xContext, xBaseURI.getStringValue(),
+ manifestPath);
+ XURI xContent = URI.createNS(xContext, xBaseURI.getStringValue(),
+ contentPath);
+ XURI xStyles = URI.createNS(xContext, xBaseURI.getStringValue(),
+ stylesPath);
+ Statement xM_BaseTypeDoc =
+ new Statement(xBaseURI, rdf_type, pkg_Document, xManifest);
+ Statement xM_BaseHaspartContent =
+ new Statement(xBaseURI, pkg_hasPart, xContent, xManifest);
+ Statement xM_BaseHaspartStyles =
+ new Statement(xBaseURI, pkg_hasPart, xStyles, xManifest);
+ Statement xM_ContentTypeContent =
+ new Statement(xContent, rdf_type, odf_ContentFile, xManifest);
+ Statement xM_StylesTypeStyles =
+ new Statement(xStyles, rdf_type, odf_StylesFile, xManifest);
+ return new Statement[] {
+ xM_BaseTypeDoc, xM_BaseHaspartContent, xM_BaseHaspartStyles,
+ xM_ContentTypeContent, xM_StylesTypeStyles
+ };
+ }
+
+ Statement[] getMetadataFileStmts(XURI xBaseURI, String Path)
+ throws Exception
+ {
+ XURI xManifest = URI.createNS(xContext, xBaseURI.getStringValue(),
+ manifestPath);
+ XURI xGraph = URI.createNS(xContext, xBaseURI.getStringValue(), Path);
+ Statement xM_BaseHaspartGraph =
+ new Statement(xBaseURI, pkg_hasPart, xGraph, xManifest);
+ Statement xM_GraphTypeMetadata =
+ new Statement(xGraph, rdf_type, pkg_MetadataFile, xManifest);
+ return new Statement[] { xM_BaseHaspartGraph, xM_GraphTypeMetadata };
+ }
+
+ class TestRange implements XTextRange, XMetadatable, XServiceInfo
+ {
+ String m_Stream;
+ String m_XmlId;
+ String m_Text;
+ TestRange(String i_Str) { m_Text = i_Str; }
+
+ public String getStringValue() { return ""; }
+ public String getNamespace() { return ""; }
+ public String getLocalName() { return ""; }
+
+ public StringPair getMetadataReference()
+ {
+ return new StringPair(m_Stream, m_XmlId);
+ }
+ public void setMetadataReference(StringPair i_Ref)
+ throws IllegalArgumentException
+ {
+ m_Stream = i_Ref.First;
+ m_XmlId = i_Ref.Second;
+ }
+ public void ensureMetadataReference()
+ {
+ m_Stream = "content.xml";
+ m_XmlId = "42";
+ }
+
+ public String getImplementationName() { return null; }
+ public String[] getSupportedServiceNames() { return null; }
+ public boolean supportsService(String i_Svc)
+ {
+ return i_Svc.equals("com.sun.star.text.Paragraph");
+ }
+
+ public XText getText() { return null; }
+ public XTextRange getStart() { return null; }
+ public XTextRange getEnd() { return null; }
+ public String getString() { return m_Text; }
+ public void setString(String i_Str) { m_Text = i_Str; }
+ }
+
+
+
+ private XMultiServiceFactory getMSF()
+ {
+ return UnoRuntime.queryInterface(XMultiServiceFactory.class, connection.getComponentContext().getServiceManager());
+ }
+
+ // setup and close connections
+ @BeforeClass public static void setUpConnection() throws Exception {
+ System.out.println( "------------------------------------------------------------" );
+ System.out.println( "starting class: " + DocumentMetadataAccess.class.getName() );
+ System.out.println( "------------------------------------------------------------" );
+ connection.setUp();
+ }
+
+ @AfterClass public static void tearDownConnection()
+ throws InterruptedException, com.sun.star.uno.Exception
+ {
+ System.out.println( "------------------------------------------------------------" );
+ System.out.println( "finishing class: " + DocumentMetadataAccess.class.getName() );
+ System.out.println( "------------------------------------------------------------" );
+ connection.tearDown();
+ }
+
+ private static final OfficeConnection connection = new OfficeConnection();
+
+}
+
diff --git a/sfx2/qa/complex/sfx2/DocumentProperties.java b/sfx2/qa/complex/sfx2/DocumentProperties.java
new file mode 100644
index 000000000..c5f84b1b7
--- /dev/null
+++ b/sfx2/qa/complex/sfx2/DocumentProperties.java
@@ -0,0 +1,516 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package complex.sfx2;
+
+
+import complex.sfx2.tools.TestDocument;
+import com.sun.star.uno.UnoRuntime;
+import com.sun.star.uno.XComponentContext;
+import com.sun.star.lang.XInitialization;
+
+import com.sun.star.lang.XMultiServiceFactory;
+import com.sun.star.lang.Locale;
+import com.sun.star.lang.EventObject;
+import com.sun.star.util.Date;
+import com.sun.star.util.DateTime;
+import com.sun.star.util.Time;
+import com.sun.star.util.Duration;
+import com.sun.star.util.XModifyListener;
+import com.sun.star.util.XModifyBroadcaster;
+import com.sun.star.beans.XPropertyContainer;
+import com.sun.star.beans.XPropertySet;
+import com.sun.star.beans.PropertyValue;
+import com.sun.star.beans.NamedValue;
+import com.sun.star.beans.PropertyAttribute;
+import com.sun.star.beans.UnknownPropertyException;
+import com.sun.star.beans.IllegalTypeException;
+
+import com.sun.star.document.XDocumentProperties;
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.openoffice.test.OfficeConnection;
+import static org.junit.Assert.*;
+
+/**
+ * Test case for the service com.sun.star.document.DocumentProperties.
+ * Currently, this service is implemented in
+ * sfx2/source/doc/SfxDocumentMetaData.cxx.
+ *
+ */
+public class DocumentProperties
+{
+ @After public void cleanup() {
+ // nothing to do
+ }
+
+ // for testing modifications
+ private class Listener implements XModifyListener {
+ private boolean m_Called;
+
+ Listener() {
+ m_Called = false;
+ }
+
+ private boolean reset() {
+ boolean oldCalled = m_Called;
+ m_Called = false;
+ return oldCalled;
+ }
+
+ public void modified(EventObject e) {
+ m_Called = true;
+ }
+
+ public void disposing(EventObject e) {
+ }
+ }
+
+ @Test public void check() throws Exception
+ {
+ XMultiServiceFactory xMSF = getMSF();
+ assertNotNull("could not create MultiServiceFactory.", xMSF);
+ XPropertySet xPropertySet = UnoRuntime.queryInterface(XPropertySet.class, xMSF);
+ Object defaultCtx = xPropertySet.getPropertyValue("DefaultContext");
+ XComponentContext xContext = UnoRuntime.queryInterface(XComponentContext.class, defaultCtx);
+ assertNotNull("could not get component context.", xContext);
+
+ // TODO: Path to temp
+ String temp = util.utils.getOfficeTemp/*Dir*/(xMSF);
+ System.out.println("tempdir: " + temp);
+
+ PropertyValue[] noArgs = { };
+ PropertyValue mimetype = new PropertyValue();
+ mimetype.Name = "MediaType";
+ mimetype.Value = "application/vnd.oasis.opendocument.text";
+ PropertyValue[] mimeArgs = { mimetype };
+ PropertyValue cfile = new PropertyValue();
+ cfile.Name = "URL";
+ cfile.Value = temp + "EMPTY.odt";
+ PropertyValue[] mimeEmptyArgs = { mimetype, cfile };
+
+ System.out.println("Creating service DocumentProperties...");
+
+ Object oDP =
+ xMSF.createInstance("com.sun.star.document.DocumentProperties");
+ XDocumentProperties xDP = UnoRuntime.queryInterface(XDocumentProperties.class, oDP);
+
+ System.out.println("...done");
+
+
+ System.out.println("Checking initialize ...");
+
+ XDocumentProperties xDP2 = UnoRuntime.queryInterface(XDocumentProperties.class, xMSF.createInstance("com.sun.star.document.DocumentProperties"));
+ XInitialization xInit = UnoRuntime.queryInterface(XInitialization.class, xDP2);
+ xInit.initialize(new Object[] { });
+
+ System.out.println("...done");
+
+ System.out.println("Checking storing default-initialized meta data ...");
+
+ xDP2.storeToMedium("", mimeEmptyArgs);
+
+ System.out.println("...done");
+
+ System.out.println("Checking loading default-initialized meta data ...");
+
+ xDP2.loadFromMedium("", mimeEmptyArgs);
+ assertEquals("Author", "", xDP2.getAuthor());
+
+ System.out.println("...done");
+
+ System.out.println("Checking loading from test document...");
+
+ String file = TestDocument.getUrl("TEST.odt");
+ xDP.loadFromMedium(file, noArgs);
+/* XInputStream xStream =
+ new StreamSimulator("./testdocuments/TEST.odt", true, param);
+ Object oSF =
+ xMSF.createInstance("com.sun.star.embed.StorageFactory");
+ XSingleServiceFactory xSF = (XSingleServiceFactory)
+ UnoRuntime.queryInterface(XSingleServiceFactory.class, oSF);
+ Object oStor = xSF.createInstanceWithArguments(
+ new Object[] { xStream });
+ XStorage xStor = (XStorage) UnoRuntime.queryInterface(
+ XStorage.class, oStor);
+ xDP.loadFromStorage(xStor);*/
+
+ System.out.println("...done");
+
+ System.out.println("Checking meta-data import...");
+
+ assertEquals("Author", "Karl-Heinz Mustermann", xDP.getAuthor());
+ assertEquals(
+ "Generator",
+ "StarOffice/8$Solaris_x86 OpenOffice.org_project/680m232$Build-9227",
+ xDP.getGenerator());
+ assertEquals("CreationDate", 2007, xDP.getCreationDate().Year);
+ assertEquals("Title", "Urgent Memo", xDP.getTitle());
+ assertEquals("Subject", "Wichtige Mitteilung", xDP.getSubject());
+ assertEquals(
+ "Description",
+ "Modern internal company memorandum in full-blocked style",
+ xDP.getDescription());
+ assertEquals(
+ "ModifiedBy", "Karl-Heinz Mustermann", xDP.getModifiedBy());
+ assertEquals(
+ "ModificationDate", 10, xDP.getModificationDate().Month);
+ assertEquals(
+ "PrintedBy", "Karl-Heinz Mustermann", xDP.getPrintedBy());
+ assertEquals("PrintDate", 29, xDP.getPrintDate().Day);
+ assertEquals("TemplateName", "Modern Memo", xDP.getTemplateName());
+ assertTrue("TemplateURL",
+ xDP.getTemplateURL().endsWith("memmodern.ott"));
+ assertEquals("TemplateDate", 17, xDP.getTemplateDate().Hours);
+ assertTrue(
+ "AutoloadURL", xDP.getAutoloadURL().endsWith("/TEST.odt"));
+ assertEquals("AutoloadSecs", 0, xDP.getAutoloadSecs());
+ assertEquals("DefaultTarget", "_blank", xDP.getDefaultTarget());
+ assertEquals("EditingCycles", 3, xDP.getEditingCycles());
+ assertEquals("EditingDuration", 320, xDP.getEditingDuration());
+
+ String[] kws = xDP.getKeywords();
+ assertTrue("Keywords", fromArray(kws).containsAll(
+ fromArray(new Object[] { "Asien", "Memo", "Reis" })));
+
+ NamedValue[] ds = xDP.getDocumentStatistics();
+ assertTrue("DocumentStatistics:WordCount", containsNV(ds,
+ new NamedValue("WordCount", Integer.valueOf(23))));
+ assertTrue("DocumentStatistics:PageCount", containsNV(ds,
+ new NamedValue("PageCount", Integer.valueOf(1))));
+
+ XPropertyContainer udpc = xDP.getUserDefinedProperties();
+ XPropertySet udps = UnoRuntime.queryInterface( XPropertySet.class, udpc );
+ assertEquals(
+ "UserDefined 1", "Dies ist ein wichtiger Hinweis",
+ udps.getPropertyValue("Hinweis"));
+ assertEquals(
+ "UserDefined 2", "Kann Spuren von N\u00FCssen enthalten",
+ udps.getPropertyValue("Warnung"));
+
+ System.out.println("...done");
+
+ System.out.println("Checking meta-data updates...");
+
+ String str;
+ DateTime dt = new DateTime();
+ Locale l = new Locale();
+ int i;
+
+ str = "me";
+ xDP.setAuthor(str);
+ assertEquals("setAuthor", str, xDP.getAuthor());
+ str = "the computa";
+ xDP.setGenerator(str);
+ assertEquals("setGenerator", str, xDP.getGenerator());
+ dt.Year = 2038;
+ dt.Month = 1;
+ dt.Day = 1;
+ xDP.setCreationDate(dt);
+ assertEquals(
+ "setCreationDate", dt.Year, xDP.getCreationDate().Year);
+ str = "El t'itulo";
+ xDP.setTitle(str);
+ assertEquals("setTitle", str, xDP.getTitle());
+ str = "Ein verkommenes Subjekt";
+ xDP.setSubject(str);
+ assertEquals("setSubject", str, xDP.getSubject());
+ str = "Este descripci'on no es importante";
+ xDP.setDescription(str);
+ assertEquals("setDescription", str, xDP.getDescription());
+ l.Language = "en";
+ l.Country = "GB";
+ xDP.setLanguage(l);
+ Locale l2 = xDP.getLanguage();
+ assertEquals("setLanguage Lang", l.Language, l2.Language);
+ assertEquals("setLanguage Cty", l.Country, l2.Country);
+ str = "myself";
+ xDP.setModifiedBy(str);
+ assertEquals("setModifiedBy", str, xDP.getModifiedBy());
+ dt.Year = 2042;
+ xDP.setModificationDate(dt);
+ assertEquals(
+ "setModificationDate", dt.Year, xDP.getModificationDate().Year);
+ str = "i did not do it";
+ xDP.setPrintedBy(str);
+ assertEquals("setPrintedBy", str, xDP.getPrintedBy());
+ dt.Year = 2024;
+ xDP.setPrintDate(dt);
+ assertEquals("setPrintDate", dt.Year, xDP.getPrintDate().Year);
+ str = "blah";
+ xDP.setTemplateName(str);
+ assertEquals("setTemplateName", str, xDP.getTemplateName());
+ str = "gopher://some-hole-in-the-ground/";
+ xDP.setTemplateURL(str);
+ assertEquals("setTemplateURL", str, xDP.getTemplateURL());
+ dt.Year = 2043;
+ xDP.setTemplateDate(dt);
+ assertEquals(
+ "setTemplateDate", dt.Year, xDP.getTemplateDate().Year);
+ str = "http://nowhere/";
+ xDP.setAutoloadURL(str);
+ assertEquals("setAutoloadURL", str, xDP.getAutoloadURL());
+ i = 3661; // this might not work (due to conversion via double...)
+ xDP.setAutoloadSecs(i);
+ assertEquals("setAutoloadSecs", i, xDP.getAutoloadSecs());
+ str = "_blank";
+ xDP.setDefaultTarget(str);
+ assertEquals("setDefaultTarget", str, xDP.getDefaultTarget());
+ i = 42;
+ xDP.setEditingCycles((short) i);
+ assertEquals("setEditingCycles", i, xDP.getEditingCycles());
+ i = 84;
+ xDP.setEditingDuration(i);
+ assertEquals("setEditingDuration", i, xDP.getEditingDuration());
+ str = "";
+
+ String[] kws2 = new String[] {
+ "keywordly", "keywordlike", "keywordalicious" };
+ xDP.setKeywords(kws2);
+ kws = xDP.getKeywords();
+ assertTrue("setKeywords", fromArray(kws).containsAll(fromArray(kws2)));
+
+ NamedValue[] ds2 = new NamedValue[] {
+ new NamedValue("SyllableCount", Integer.valueOf(9)),
+ new NamedValue("FrameCount", Integer.valueOf(2)),
+ new NamedValue("SentenceCount", Integer.valueOf(7)) };
+ xDP.setDocumentStatistics(ds2);
+ ds = xDP.getDocumentStatistics();
+ assertTrue("setDocumentStatistics:SyllableCount", containsNV(ds,
+ new NamedValue("SyllableCount", Integer.valueOf(9))));
+ assertTrue("setDocumentStatistics:FrameCount", containsNV(ds,
+ new NamedValue("FrameCount", Integer.valueOf(2))));
+ assertTrue("setDocumentStatistics:SentenceCount", containsNV(ds,
+ new NamedValue("SentenceCount", Integer.valueOf(7))));
+
+ System.out.println("...done");
+
+ System.out.println("Checking user-defined meta-data updates...");
+
+ // actually, this tests the PropertyBag service
+ // but maybe the DocumentProperties service will be implemented
+ // differently some day...
+ boolean b = true;
+ double d = 3.1415;
+ // note that Time is only supported for backward compatibility!
+ Time t = new Time();
+ t.Hours = 1;
+ t.Minutes = 16;
+ Date date = new Date();
+ date.Year = 2071;
+ date.Month = 2;
+ date.Day = 3;
+ dt.Year = 2065;
+ Duration dur = new Duration();
+ dur.Negative = true;
+ dur.Years = 1001;
+ dur.Months = 999;
+ dur.Days = 888;
+ dur.Hours = 777;
+ dur.Minutes = 666;
+ dur.Seconds = 555;
+ dur.NanoSeconds = 444444444;
+
+ udpc.addProperty("Frobnicate", PropertyAttribute.REMOVABLE, b);
+ udpc.addProperty("FrobDuration", PropertyAttribute.REMOVABLE, dur);
+ udpc.addProperty("FrobDuration2", PropertyAttribute.REMOVABLE, t);
+ udpc.addProperty("FrobEndDate", PropertyAttribute.REMOVABLE, date);
+ udpc.addProperty("FrobStartTime", PropertyAttribute.REMOVABLE, dt);
+ udpc.addProperty("Pi", PropertyAttribute.REMOVABLE, new Double(d));
+ udpc.addProperty("Foo", PropertyAttribute.REMOVABLE, "bar");
+ udpc.addProperty("Removed", PropertyAttribute.REMOVABLE, "bar");
+ // #i94175#: empty property name is valid ODF 1.1
+ udpc.addProperty("", PropertyAttribute.REMOVABLE, "eeeeek");
+ try {
+ udpc.removeProperty("Info 1");
+ udpc.removeProperty("Removed");
+ } catch (UnknownPropertyException e) {
+ fail("removeProperty failed");
+ }
+
+ try {
+ udpc.addProperty("Forbidden", PropertyAttribute.REMOVABLE,
+ new String[] { "foo", "bar" });
+ fail("inserting value of non-supported type did not fail");
+ } catch (IllegalTypeException e) {
+ // ignore
+ }
+
+ assertEquals(
+ "UserDefined bool", b, udps.getPropertyValue("Frobnicate"));
+ assertTrue("UserDefined duration", eqDuration(dur, (Duration)
+ udps.getPropertyValue("FrobDuration")));
+ assertTrue("UserDefined time", eqTime(t, (Time)
+ udps.getPropertyValue("FrobDuration2")));
+ assertTrue("UserDefined date", eqDate(date, (Date)
+ udps.getPropertyValue("FrobEndDate")));
+ assertTrue("UserDefined datetime", eqDateTime(dt, (DateTime)
+ udps.getPropertyValue("FrobStartTime")));
+ assertEquals("UserDefined float", d, udps.getPropertyValue("Pi"));
+ assertEquals(
+ "UserDefined string", "bar", udps.getPropertyValue("Foo"));
+ assertEquals(
+ "UserDefined empty name", "eeeeek", udps.getPropertyValue(""));
+
+ try {
+ udps.getPropertyValue("Removed");
+ fail("UserDefined remove didn't");
+ } catch (UnknownPropertyException e) {
+ // ok
+ }
+
+ System.out.println("...done");
+
+ System.out.println("Checking storing meta-data to file...");
+
+ xDP.storeToMedium(temp + "TEST.odt", mimeArgs);
+
+ System.out.println("...done");
+
+ System.out.println("Checking loading meta-data from stored file...");
+
+ xDP.loadFromMedium(temp + "TEST.odt", noArgs);
+
+ System.out.println("...done");
+
+ System.out.println("Checking user-defined meta-data from stored file...");
+
+ udpc = xDP.getUserDefinedProperties();
+ udps = UnoRuntime.queryInterface( XPropertySet.class, udpc );
+
+ assertEquals(
+ "UserDefined bool", b, udps.getPropertyValue("Frobnicate"));
+ assertTrue("UserDefined duration", eqDuration(dur, (Duration)
+ udps.getPropertyValue("FrobDuration")));
+ // this is now a Duration!
+ Duration t_dur = new Duration(false, (short)0, (short)0, (short)0,
+ t.Hours, t.Minutes, t.Seconds,
+ t.NanoSeconds);
+ assertTrue("UserDefined time", eqDuration(t_dur, (Duration)
+ udps.getPropertyValue("FrobDuration2")));
+ assertTrue("UserDefined date", eqDate(date, (Date)
+ udps.getPropertyValue("FrobEndDate")));
+ assertTrue("UserDefined datetime", eqDateTime(dt, (DateTime)
+ udps.getPropertyValue("FrobStartTime")));
+ assertEquals("UserDefined float", d, udps.getPropertyValue("Pi"));
+ assertEquals(
+ "UserDefined string", "bar", udps.getPropertyValue("Foo"));
+
+ try {
+ udps.getPropertyValue("Removed");
+ fail("UserDefined remove didn't");
+ } catch (UnknownPropertyException e) {
+ // ok
+ }
+
+ System.out.println("...done");
+
+ System.out.println("Checking notification listener interface...");
+
+ Listener listener = new Listener();
+ XModifyBroadcaster xMB = UnoRuntime.queryInterface( XModifyBroadcaster.class, xDP );
+ xMB.addModifyListener(listener);
+ xDP.setAuthor("not me");
+ assertTrue("Listener Author", listener.reset());
+ udpc.addProperty("Listener", PropertyAttribute.REMOVABLE, "foo");
+ assertTrue("Listener UserDefined Add", listener.reset());
+ udps.setPropertyValue("Listener", "bar");
+ assertTrue("Listener UserDefined Set", listener.reset());
+ udpc.removeProperty("Listener");
+ assertTrue("Listener UserDefined Remove", listener.reset());
+ xMB.removeModifyListener(listener);
+ udpc.addProperty("Listener2", PropertyAttribute.REMOVABLE, "foo");
+ assertTrue("Removed Listener UserDefined Add", !listener.reset());
+
+ System.out.println("...done");
+ }
+
+ // grrr...
+ boolean eqDateTime(DateTime a, DateTime b) {
+ return a.Year == b.Year && a.Month == b.Month && a.Day == b.Day
+ && a.Hours == b.Hours && a.Minutes == b.Minutes
+ && a.Seconds == b.Seconds
+ && a.NanoSeconds == b.NanoSeconds;
+ }
+
+ boolean eqDate(Date a, Date b) {
+ return a.Year == b.Year && a.Month == b.Month && a.Day == b.Day;
+ }
+
+ boolean eqTime(Time a, Time b) {
+ return a.Hours == b.Hours && a.Minutes == b.Minutes
+ && a.Seconds == b.Seconds
+ && a.NanoSeconds == b.NanoSeconds;
+ }
+
+ boolean eqDuration(Duration a, Duration b) {
+ return a.Years == b.Years && a.Months == b.Months && a.Days == b.Days
+ && a.Hours == b.Hours && a.Minutes == b.Minutes
+ && a.Seconds == b.Seconds
+ && a.NanoSeconds == b.NanoSeconds
+ && a.Negative == b.Negative;
+ }
+
+ java.util.Collection<Object> fromArray(Object[] os) {
+ java.util.Collection<Object> ret = new java.util.HashSet<Object>();
+ for (int i = 0; i < os.length; ++i) {
+ ret.add(os[i]);
+ }
+ return ret;
+ }
+
+ // bah, structs do not have proper equals(), and uno.Type is not comparable
+ public static boolean containsNV (NamedValue[] nvs, NamedValue nv) {
+ for (int i = 0; i < nvs.length; ++i) {
+ if (nvs[i].Name.equals(nv.Name) && nvs[i].Value.equals(nv.Value)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private XMultiServiceFactory getMSF()
+ {
+ final XMultiServiceFactory xMSF1 = UnoRuntime.queryInterface( XMultiServiceFactory.class, connection.getComponentContext().getServiceManager() );
+ return xMSF1;
+ }
+
+ // setup and close connections
+ @BeforeClass public static void setUpConnection() throws Exception {
+ System.out.println( "------------------------------------------------------------" );
+ System.out.println( "starting class: " + DocumentProperties.class.getName() );
+ System.out.println( "------------------------------------------------------------" );
+ connection.setUp();
+ }
+
+ @AfterClass public static void tearDownConnection()
+ throws InterruptedException, com.sun.star.uno.Exception
+ {
+ System.out.println( "------------------------------------------------------------" );
+ System.out.println( "finishing class: " + DocumentProperties.class.getName() );
+ System.out.println( "------------------------------------------------------------" );
+ connection.tearDown();
+ }
+
+ private static final OfficeConnection connection = new OfficeConnection();
+
+}
+
diff --git a/sfx2/qa/complex/sfx2/GlobalEventBroadcaster.java b/sfx2/qa/complex/sfx2/GlobalEventBroadcaster.java
new file mode 100644
index 000000000..7c42cdf1f
--- /dev/null
+++ b/sfx2/qa/complex/sfx2/GlobalEventBroadcaster.java
@@ -0,0 +1,249 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+package complex.sfx2;
+
+import com.sun.star.awt.XWindow;
+import com.sun.star.document.XEventBroadcaster;
+import com.sun.star.document.XEventListener;
+import com.sun.star.lang.XMultiServiceFactory;
+import com.sun.star.sheet.XSpreadsheetDocument;
+import com.sun.star.text.XTextDocument;
+import com.sun.star.uno.UnoRuntime;
+import complex.sfx2.tools.WriterHelper;
+
+import java.util.ArrayList;
+
+import util.UITools;
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.openoffice.test.OfficeConnection;
+import static org.junit.Assert.*;
+
+
+/**
+ * This testcase checks the GlobalEventBroadcaster
+ * it will add an XEventListener and verify the Events
+ * raised when opening/changing and closing Office Documents
+ */
+public class GlobalEventBroadcaster {
+ XMultiServiceFactory m_xMSF = null;
+ XEventBroadcaster m_xEventBroadcaster = null;
+ ArrayList<String> notifyEvents = new ArrayList<String>();
+ // XTextDocument xTextDoc;
+ XSpreadsheetDocument xSheetDoc;
+ XEventListener m_xEventListener = new EventListenerImpl();
+
+ @Before public void initialize() {
+ m_xMSF = getMSF();
+ System.out.println("check whether there is a valid MultiServiceFactory");
+
+ assertNotNull("## Couldn't get MultiServiceFactory make sure your Office is started", m_xMSF);
+
+ System.out.println("... done");
+
+ System.out.println(
+ "Create an instance of com.sun.star.frame.GlobalEventBroadcaster");
+
+ Object GlobalEventBroadcaster = null;
+
+ try {
+ GlobalEventBroadcaster = m_xMSF.createInstance(
+ "com.sun.star.frame.GlobalEventBroadcaster");
+ } catch (com.sun.star.uno.Exception e) {
+ fail("## Exception while creating instance");
+ }
+
+ System.out.println("... done");
+
+ System.out.println("check whether the created instance is valid");
+
+ assertNotNull("couldn't create service", GlobalEventBroadcaster);
+
+ System.out.println("... done");
+
+ System.out.println(
+ "try to query the XEventBroadcaster from the gained Object");
+ m_xEventBroadcaster = UnoRuntime.queryInterface(XEventBroadcaster.class, GlobalEventBroadcaster);
+
+ if (util.utils.isVoid(m_xEventBroadcaster)) {
+ fail("couldn't get XEventBroadcaster");
+ }
+
+ System.out.println("... done");
+
+ System.out.println("adding Listener");
+ m_xEventBroadcaster.addEventListener(m_xEventListener);
+ System.out.println("... done");
+ }
+
+ @Test public void checkWriter() throws Exception {
+ System.out.println("-- Checking Writer --");
+
+ WriterHelper wHelper = new WriterHelper(m_xMSF);
+ String[] expected;
+ System.out.println("opening an empty writer doc");
+ notifyEvents.clear();
+ {
+ XTextDocument xTextDoc = wHelper.openEmptyDoc();
+ util.utils.waitForEventIdle(m_xMSF);
+ expected = new String[] { "OnUnfocus", "OnCreate", "OnViewCreated", "OnFocus" };
+
+ assertTrue("Wrong events fired when opening empty doc",
+ proveExpectation(expected));
+ System.out.println("... done");
+
+ System.out.println("changing the writer doc");
+ notifyEvents.clear();
+ xTextDoc.getText().setString("GlobalEventBroadcaster");
+ util.utils.waitForEventIdle(m_xMSF);
+ expected = new String[] { "OnModifyChanged" };
+
+ assertTrue("Wrong events fired when changing doc",
+ proveExpectation(expected));
+ System.out.println("... done");
+
+ System.out.println("closing the empty writer doc");
+ notifyEvents.clear();
+ wHelper.closeDoc(xTextDoc);
+ util.utils.waitForEventIdle(m_xMSF);
+ }
+ expected = new String[] { "OnUnfocus", "OnFocus", "OnViewClosed", "OnUnload" };
+
+ assertTrue("Wrong events fired when closing empty doc",
+ proveExpectation(expected));
+ System.out.println("... done");
+
+ System.out.println("opening a writer doc via Window-New Window");
+ notifyEvents.clear();
+ {
+ XTextDocument xTextDoc = wHelper.openFromDialog(".uno:NewWindow", "", false);
+
+ util.utils.waitForEventIdle(m_xMSF);
+ expected = new String[] { "OnUnfocus", "OnCreate", "OnViewCreated", "OnFocus", "OnUnfocus", "OnViewCreated", "OnFocus", };
+
+ assertTrue("Wrong events fired when opening a writer doc via Window-New Window",
+ proveExpectation(expected));
+ System.out.println("... done");
+
+ System.out.println("closing the created writer doc");
+ notifyEvents.clear();
+
+ wHelper.closeDoc(xTextDoc);
+ util.utils.waitForEventIdle(m_xMSF);
+ }
+ expected = new String[] { "OnViewClosed", "OnUnfocus", "OnFocus", "OnViewClosed", "OnUnload" };
+
+ assertTrue("Wrong events fired when closing Window-New Window",
+ proveExpectation(expected));
+
+ System.out.println("... done");
+ // TODO: It seems not possible to close the document without interactive question
+ // there the follow test will not be execute
+ if (false) {
+ System.out.println("Opening document with label wizard");
+ XTextDocument xTextDoc = wHelper.openFromDialog("private:factory/swriter?slot=21051", "", false);
+ util.utils.waitForEventIdle(m_xMSF);
+ XWindow xWindow = UnoRuntime.queryInterface(XWindow.class, wHelper.getToolkit().getActiveTopWindow());
+ UITools ut = new UITools(xWindow);
+ notifyEvents.clear();
+ System.out.println("pressing button 'New Document'");
+ try{
+ ut.clickButton ("New Document");
+ } catch (Exception e) {
+ System.out.println("Couldn't press Button");
+ }
+ System.out.println("... done");
+ util.utils.waitForEventIdle(m_xMSF);
+ expected = new String[] { "OnViewClosed", "OnCreate", "OnFocus", "OnModifyChanged" };
+
+ assertTrue("Wrong events fired when starting labels wizard",
+ proveExpectation(expected));
+
+ System.out.println("Try to close document...");
+ wHelper.closeDoc(xTextDoc);
+ util.utils.waitForEventIdle(m_xMSF);
+ wHelper.closeFromDialog();
+ util.utils.waitForEventIdle(m_xMSF);
+ xTextDoc = null;
+ }
+
+ System.out.println("-- Done Writer --");
+ }
+
+ @After public void cleanup() {
+ System.out.println("removing Listener");
+ m_xEventBroadcaster.removeEventListener(m_xEventListener);
+ System.out.println("... done");
+ }
+
+ private boolean proveExpectation(String[] expected) {
+ boolean locRes = true;
+ boolean failure = false;
+
+ System.out.println("Fired Events:");
+ for (int k=0;k<notifyEvents.size();k++) {
+ System.out.println("\t- "+notifyEvents.get(k));
+ }
+
+ for (int i = 0; i < expected.length; i++) {
+ locRes = notifyEvents.contains(expected[i]);
+
+ if (!locRes) {
+ System.out.println("The event " + expected[i] + " isn't fired");
+ failure = true;
+ }
+ }
+
+ return !failure;
+ }
+
+ private class EventListenerImpl implements XEventListener {
+ public void disposing(com.sun.star.lang.EventObject eventObject) {
+ System.out.println("disposing: " + eventObject.Source.toString());
+ }
+
+ public void notifyEvent(com.sun.star.document.EventObject eventObject) {
+ notifyEvents.add(eventObject.EventName);
+ }
+ }
+
+ private XMultiServiceFactory getMSF()
+ {
+ return UnoRuntime.queryInterface(XMultiServiceFactory.class, connection.getComponentContext().getServiceManager());
+ }
+
+ // setup and close connections
+ @BeforeClass public static void setUpConnection() throws Exception {
+ System.out.println("setUpConnection()");
+ connection.setUp();
+ }
+
+ @AfterClass public static void tearDownConnection()
+ throws InterruptedException, com.sun.star.uno.Exception
+ {
+ System.out.println("tearDownConnection() CheckGlobalEventBroadcaster_writer1");
+ connection.tearDown();
+ }
+
+ private static final OfficeConnection connection = new OfficeConnection();
+
+}
diff --git a/sfx2/qa/complex/sfx2/UndoManager.java b/sfx2/qa/complex/sfx2/UndoManager.java
new file mode 100644
index 000000000..12f13ada1
--- /dev/null
+++ b/sfx2/qa/complex/sfx2/UndoManager.java
@@ -0,0 +1,1459 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package complex.sfx2;
+
+import com.sun.star.accessibility.XAccessible;
+import com.sun.star.accessibility.XAccessibleAction;
+import com.sun.star.awt.Point;
+import com.sun.star.awt.Size;
+import com.sun.star.awt.XControl;
+import com.sun.star.awt.XControlModel;
+import com.sun.star.awt.XToolkitExperimental;
+import com.sun.star.beans.NamedValue;
+import com.sun.star.beans.XPropertySet;
+import com.sun.star.container.NoSuchElementException;
+import com.sun.star.container.XChild;
+import com.sun.star.container.XIndexContainer;
+import com.sun.star.container.XNameContainer;
+import com.sun.star.container.XNameReplace;
+import com.sun.star.container.XSet;
+import com.sun.star.document.EmptyUndoStackException;
+import com.sun.star.document.UndoContextNotClosedException;
+import com.sun.star.document.UndoFailedException;
+import com.sun.star.document.UndoManagerEvent;
+import com.sun.star.document.XEmbeddedScripts;
+import com.sun.star.document.XEventsSupplier;
+import com.sun.star.document.XUndoAction;
+import com.sun.star.lang.EventObject;
+import com.sun.star.lang.IndexOutOfBoundsException;
+import com.sun.star.lang.XEventListener;
+
+import java.lang.reflect.InvocationTargetException;
+
+import org.openoffice.test.tools.OfficeDocument;
+
+import com.sun.star.document.XUndoManagerSupplier;
+import com.sun.star.document.XUndoManager;
+import com.sun.star.document.XUndoManagerListener;
+import com.sun.star.drawing.XControlShape;
+import com.sun.star.drawing.XDrawPage;
+import com.sun.star.drawing.XDrawPageSupplier;
+import com.sun.star.drawing.XShapes;
+import com.sun.star.lang.XComponent;
+import com.sun.star.lang.XMultiServiceFactory;
+import com.sun.star.lang.XServiceInfo;
+import com.sun.star.lang.XSingleComponentFactory;
+import com.sun.star.lang.XTypeProvider;
+import com.sun.star.script.ScriptEventDescriptor;
+import com.sun.star.script.XEventAttacherManager;
+import com.sun.star.script.XLibraryContainer;
+import com.sun.star.task.XJob;
+import com.sun.star.uno.Exception;
+import com.sun.star.uno.Type;
+import com.sun.star.uno.UnoRuntime;
+import com.sun.star.uno.XComponentContext;
+import com.sun.star.util.InvalidStateException;
+import com.sun.star.util.NotLockedException;
+import com.sun.star.view.XControlAccess;
+
+import complex.sfx2.undo.CalcDocumentTest;
+import complex.sfx2.undo.ChartDocumentTest;
+import complex.sfx2.undo.DocumentTest;
+import complex.sfx2.undo.DrawDocumentTest;
+import complex.sfx2.undo.ImpressDocumentTest;
+import complex.sfx2.undo.WriterDocumentTest;
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Stack;
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+import org.openoffice.test.OfficeConnection;
+import org.openoffice.test.tools.DocumentType;
+import org.openoffice.test.tools.SpreadsheetDocument;
+
+/**
+ * Unit test for the UndoManager API
+ *
+ */
+public class UndoManager
+{
+
+ @Before
+ public void beforeTest() throws com.sun.star.uno.Exception
+ {
+ m_currentTestCase = null;
+ m_currentDocument = null;
+ m_undoListener = null;
+
+ // at our service factory, insert a new factory for our CallbackComponent
+ // this will allow the Basic code in our test documents to call back into this test case
+ // here, by just instantiating this service
+ final XSet globalFactory = UnoRuntime.queryInterface( XSet.class, getORB() );
+ m_callbackFactory = new CallbackComponentFactory();
+ globalFactory.insert( m_callbackFactory );
+ }
+
+
+ @Test
+ public void checkWriterUndo() throws Exception
+ {
+ m_currentTestCase = new WriterDocumentTest( getORB() );
+ impl_checkUndo();
+ }
+
+
+//FIXME fails fdo#35663 @Test
+ public void checkCalcUndo() throws java.lang.Exception
+ {
+ m_currentTestCase = new CalcDocumentTest( getORB() );
+ impl_checkUndo();
+ }
+
+
+ @Test
+ public void checkDrawUndo() throws Exception
+ {
+ m_currentTestCase = new DrawDocumentTest( getORB() );
+ impl_checkUndo();
+ }
+
+
+ @Test
+ public void checkImpressUndo() throws Exception
+ {
+ m_currentTestCase = new ImpressDocumentTest( getORB() );
+ impl_checkUndo();
+ }
+
+
+ @Test
+ public void checkChartUndo() throws Exception
+ {
+ m_currentTestCase = new ChartDocumentTest( getORB() );
+ impl_checkUndo();
+ }
+
+
+ @Test
+ public void checkBrokenScripts() throws com.sun.star.uno.Exception, InterruptedException
+ {
+ System.out.println( "testing: broken scripts" );
+
+ m_currentDocument = OfficeDocument.blankDocument( getORB(), DocumentType.CALC );
+ m_undoListener = new UndoListener();
+ getUndoManager().addUndoManagerListener( m_undoListener );
+
+ impl_setupBrokenBasicScript();
+ final String scriptURI = "vnd.sun.star.script:default.callbacks.brokenScript?language=Basic&location=document";
+
+
+ // scenario 1: Pressing a button which is bound to execute the script
+ // (This is one of the many cases where SfxObjectShell::CallXScript is invoked)
+
+ // set up the button
+ final XPropertySet buttonModel = impl_setupButton();
+ buttonModel.setPropertyValue( "Label", "exec broken script" );
+ impl_assignScript( buttonModel, "XActionListener", "actionPerformed",
+ scriptURI );
+
+ // switch the doc's view to form alive mode (so the button will actually work)
+ m_currentDocument.getCurrentView().dispatch( ".uno:SwitchControlDesignMode" );
+ XToolkitExperimental xToolkit = UnoRuntime.queryInterface(
+ XToolkitExperimental.class,
+ getORB().createInstance("com.sun.star.awt.Toolkit"));
+ xToolkit.processEventsToIdle();
+
+ // click the button
+ m_callbackCalled = false;
+ impl_clickButton( buttonModel );
+ // the macro is executed asynchronously by the button, so wait at most 2 seconds for the callback to be
+ // triggered
+ impl_waitFor( m_callbackCondition, 20000 );
+ // check the callback has actually been called
+ assertTrue( "clicking the test button did not work as expected - basic script not called", m_callbackCalled );
+
+ // again, since the script is executed asynchronously, we might arrive here while its execution
+ // is not completely finished. Give OOo another (at most) 2 seconds to finish it.
+ m_undoListener.waitForAllContextsClosed( 20000 );
+ // assure that the Undo Context Depth of the doc is still "0": The Basic script entered such a
+ // context, and didn't close it (thus it is broken), but the application framework should have
+ // auto-closed the context after the macro finished.
+ assertEquals( "undo context was not auto-closed as expected", 0, m_undoListener.getCurrentUndoContextDepth() );
+
+
+ // scenario 2: dispatching the script URL. Technically, this is equivalent to configuring the
+ // script into a menu or toolbar, and selecting the respective menu/toolbar item
+ m_callbackCalled = false;
+ m_currentDocument.getCurrentView().dispatch( scriptURI );
+ assertTrue( "dispatching the Script URL did not work as expected - basic script not called", m_callbackCalled );
+ // same as above: The script didn't close the context, but the OOo framework should have
+ assertEquals( "undo context was not auto-closed as expected", 0, m_undoListener.getCurrentUndoContextDepth() );
+
+
+ // scenario 3: assigning the script to some document event, and triggering this event
+ final XEventsSupplier eventSupplier = UnoRuntime.queryInterface( XEventsSupplier.class, m_currentDocument.getDocument() );
+ final XNameReplace events = UnoRuntime.queryInterface( XNameReplace.class, eventSupplier.getEvents() );
+ final NamedValue[] scriptDescriptor = new NamedValue[] {
+ new NamedValue( "EventType", "Script" ),
+ new NamedValue( "Script", scriptURI )
+ };
+ events.replaceByName( "OnViewCreated", scriptDescriptor );
+
+ // note: this may be prevented from working by setting
+ // Office::Common::Security::Scripting::AllowedDocumentEventURLs
+ // (checked in SfxEvents_Impl::isScriptURLAllowed())
+ m_callbackCalled = false;
+ m_currentDocument.getCurrentView().dispatch( ".uno:NewWindow" );
+ assertTrue( "triggering an event did not work as expected - basic script not called", m_callbackCalled );
+ // same as above: The script didn't close the context, but the OOo framework should have
+ assertEquals( "undo context was not auto-closed as expected", 0, m_undoListener.getCurrentUndoContextDepth() );
+
+
+ // scenario 4: let the script enter an Undo context, but not close it, as usual.
+ // Additionally, let the script close the document - the OOo framework code which cares for
+ // auto-closing of Undo contexts should survive this, ideally ...
+ m_closeAfterCallback = true;
+ m_callbackCalled = false;
+ m_currentDocument.getCurrentView().dispatch( scriptURI );
+ assertTrue( m_callbackCalled );
+ assertTrue( "The Basic script should have closed the document.", m_undoListener.isDisposed() );
+ m_currentDocument = null;
+ }
+
+
+ @Test
+ public void checkSerialization() throws com.sun.star.uno.Exception, InterruptedException
+ {
+ System.out.println( "testing: request serialization" );
+
+ m_currentDocument = OfficeDocument.blankDocument( getORB(), DocumentType.CALC );
+ final XUndoManager undoManager = getUndoManager();
+
+ final int threadCount = 10;
+ final int actionsPerThread = 10;
+ final int actionCount = threadCount * actionsPerThread;
+
+ // add some actions to the UndoManager, each knowing its position on the stack
+ final Object lock = new Object();
+ final Integer actionsUndone[] = new Integer[] { 0 };
+ for ( int i=actionCount; i>0; )
+ undoManager.addUndoAction( new CountingUndoAction( --i, lock, actionsUndone ) );
+
+ // some concurrent threads which undo the actions
+ Thread[] threads = new Thread[threadCount];
+ for ( int i=0; i<threadCount; ++i )
+ {
+ threads[i] = new Thread()
+ {
+ @Override
+ public void run()
+ {
+ for ( int j=0; j<actionsPerThread; ++j )
+ {
+ try { undoManager.undo(); }
+ catch ( final Exception e )
+ {
+ fail( "Those dummy actions are not expected to fail." );
+ return;
+ }
+ }
+ }
+ };
+ }
+
+ // start the threads
+ for ( int i=0; i<threadCount; ++i )
+ threads[i].start();
+
+ // wait for them to be finished
+ for ( int i=0; i<threadCount; ++i )
+ threads[i].join();
+
+ // ensure all actions have been undone
+ assertEquals( "not all actions have been undone", actionCount, actionsUndone[0].intValue() );
+ }
+
+
+ @After
+ public void afterTest()
+ {
+ if ( m_currentTestCase != null )
+ m_currentTestCase.closeDocument();
+ else if ( m_currentDocument != null )
+ m_currentDocument.close();
+ m_currentTestCase = null;
+ m_currentDocument = null;
+ m_callbackFactory.dispose();
+ }
+
+
+ /**
+ * @return returns the undo manager belonging to a given document
+ */
+ private XUndoManager getUndoManager()
+ {
+ final XUndoManagerSupplier suppUndo = UnoRuntime.queryInterface( XUndoManagerSupplier.class, m_currentDocument.getDocument() );
+ final XUndoManager undoManager = suppUndo.getUndoManager();
+ assertTrue( UnoRuntime.areSame( undoManager.getParent(), m_currentDocument.getDocument() ) );
+ return undoManager;
+ }
+
+
+ private void impl_waitFor( final Object i_condition, final int i_milliSeconds ) throws InterruptedException
+ {
+ synchronized( i_condition )
+ {
+ i_condition.wait( i_milliSeconds );
+ }
+ }
+
+
+ private void impl_setupBrokenBasicScript()
+ {
+ try
+ {
+ final XEmbeddedScripts embeddedScripts = UnoRuntime.queryInterface( XEmbeddedScripts.class, m_currentDocument.getDocument() );
+ final XLibraryContainer basicLibs = embeddedScripts.getBasicLibraries();
+ final XNameContainer basicLib = basicLibs.createLibrary( "default" );
+
+ final String brokenScriptCode =
+ "Option Explicit\n" +
+ "\n" +
+ "Sub brokenScript\n" +
+ " Dim callback as Object\n" +
+ " ThisComponent.UndoManager.enterUndoContext( \"" + getCallbackUndoContextTitle() + "\" )\n" +
+ "\n" +
+ " callback = createUnoService( \"" + getCallbackComponentServiceName() + "\" )\n" +
+ " Dim emptyArgs() as new com.sun.star.beans.NamedValue\n" +
+ " Dim result as String\n" +
+ " result = callback.execute( emptyArgs() )\n" +
+ " If result = \"close\" Then\n" +
+ " ThisComponent.close( TRUE )\n" +
+ " End If\n" +
+ "End Sub\n" +
+ "\n";
+
+ basicLib.insertByName( "callbacks", brokenScriptCode );
+ }
+ catch( com.sun.star.uno.Exception e )
+ {
+ fail( "caught an exception while setting up the script: " + e.toString() );
+ }
+ }
+
+
+ private XPropertySet impl_setupButton() throws com.sun.star.uno.Exception
+ {
+ // let the document create a shape
+ final XMultiServiceFactory docAsFactory = UnoRuntime.queryInterface( XMultiServiceFactory.class,
+ m_currentDocument.getDocument() );
+ final XControlShape xShape = UnoRuntime.queryInterface( XControlShape.class,
+ docAsFactory.createInstance( "com.sun.star.drawing.ControlShape" ) );
+
+ // position and size of the shape
+ xShape.setSize( new Size( 28 * 100, 10 * 100 ) );
+ xShape.setPosition( new Point( 10 * 100, 10 * 100 ) );
+
+ // create the form component (the model of a form control)
+ final String sQualifiedComponentName = "com.sun.star.form.component.CommandButton";
+ final XControlModel controlModel = UnoRuntime.queryInterface( XControlModel.class,
+ getORB().createInstance( sQualifiedComponentName ) );
+
+ // knitt both
+ xShape.setControl( controlModel );
+
+ // add the shape to the shapes collection of the document
+ SpreadsheetDocument spreadsheetDoc = (SpreadsheetDocument)m_currentDocument;
+ final XDrawPageSupplier suppDrawPage = UnoRuntime.queryInterface( XDrawPageSupplier.class,
+ spreadsheetDoc.getSheet( 0 ) );
+ final XDrawPage insertIntoPage = suppDrawPage.getDrawPage();
+
+ final XShapes sheetShapes = UnoRuntime.queryInterface( XShapes.class, insertIntoPage );
+ sheetShapes.add( xShape );
+
+ return UnoRuntime.queryInterface( XPropertySet.class, controlModel );
+ }
+
+
+ private void impl_assignScript( final XPropertySet i_controlModel, final String i_interfaceName,
+ final String i_interfaceMethod, final String i_scriptURI ) throws Exception
+ {
+ final XChild modelAsChild = UnoRuntime.queryInterface( XChild.class, i_controlModel );
+ final XIndexContainer parentForm = UnoRuntime.queryInterface( XIndexContainer.class, modelAsChild.getParent() );
+
+ final XEventAttacherManager manager = UnoRuntime.queryInterface( XEventAttacherManager.class, parentForm );
+
+ int containerPosition = -1;
+ for ( int i = 0; i < parentForm.getCount(); ++i )
+ {
+ final XPropertySet child = UnoRuntime.queryInterface( XPropertySet.class, parentForm.getByIndex( i ) );
+ if ( UnoRuntime.areSame( child, i_controlModel ) )
+ {
+ containerPosition = i;
+ break;
+ }
+ }
+ assertFalse( "could not find the given control model within its parent", containerPosition == -1 );
+ manager.registerScriptEvent( containerPosition, new ScriptEventDescriptor(
+ i_interfaceName,
+ i_interfaceMethod,
+ "",
+ "Script",
+ i_scriptURI
+ ) );
+ }
+
+
+ private void impl_clickButton( final XPropertySet i_buttonModel ) throws NoSuchElementException, IndexOutOfBoundsException
+ {
+ final XControlAccess controlAccess = UnoRuntime.queryInterface( XControlAccess.class,
+ m_currentDocument.getCurrentView().getController() );
+ final XControl control = controlAccess.getControl( UnoRuntime.queryInterface( XControlModel.class, i_buttonModel ) );
+ final XAccessible accessible = UnoRuntime.queryInterface( XAccessible.class, control );
+ final XAccessibleAction controlActions = UnoRuntime.queryInterface( XAccessibleAction.class, accessible.getAccessibleContext() );
+ for ( int i=0; i<controlActions.getAccessibleActionCount(); ++i )
+ {
+ if (controlActions.getAccessibleActionDescription(i).equals("press"))
+ {
+ controlActions.doAccessibleAction(i);
+ return;
+ }
+ }
+ fail("did not find the accessible action named 'press'");
+ }
+
+
+ private static class UndoListener implements XUndoManagerListener
+ {
+ public void undoActionAdded( UndoManagerEvent i_event )
+ {
+ assertFalse( "|undoActionAdded| called after document was disposed", m_isDisposed );
+
+ ++m_undoActionsAdded;
+ m_mostRecentlyAddedAction = i_event.UndoActionTitle;
+ }
+
+ public void actionUndone( UndoManagerEvent i_event )
+ {
+ assertFalse( "|actionUndone| called after document was disposed", m_isDisposed );
+
+ ++m_undoCount;
+ m_mostRecentlyUndone = i_event.UndoActionTitle;
+ }
+
+ public void actionRedone( UndoManagerEvent i_event )
+ {
+ assertFalse( "|actionRedone| called after document was disposed", m_isDisposed );
+
+ ++m_redoCount;
+ }
+
+ public void allActionsCleared( EventObject eo )
+ {
+ assertFalse( "|allActionsCleared| called after document was disposed", m_isDisposed );
+
+ m_wasCleared = true;
+ }
+
+ public void redoActionsCleared( EventObject eo )
+ {
+ assertFalse( "|redoActionsCleared| called after document was disposed", m_isDisposed );
+
+ m_redoWasCleared = true;
+ }
+
+ public void resetAll( EventObject i_event )
+ {
+ assertFalse( "|resetAll| called after document was disposed", m_isDisposed );
+
+ m_managerWasReset = true;
+ m_activeUndoContexts.clear();
+ }
+
+ public void enteredContext( UndoManagerEvent i_event )
+ {
+ assertFalse( "|enteredContext| called after document was disposed", m_isDisposed );
+
+ m_activeUndoContexts.push( i_event.UndoActionTitle );
+ assertEquals( "different opinions on the context nesting level (after entering)",
+ m_activeUndoContexts.size(), i_event.UndoContextDepth );
+ }
+
+ public void enteredHiddenContext( UndoManagerEvent i_event )
+ {
+ assertFalse( "|enteredHiddenContext| called after document was disposed", m_isDisposed );
+
+ m_activeUndoContexts.push( i_event.UndoActionTitle );
+ assertEquals( "different opinions on the context nesting level (after entering hidden)",
+ m_activeUndoContexts.size(), i_event.UndoContextDepth );
+ }
+
+ public void leftContext( UndoManagerEvent i_event )
+ {
+ assertFalse( "|leftContext| called after document was disposed", m_isDisposed );
+
+ assertEquals( "nested undo context descriptions do not match", m_activeUndoContexts.pop(), i_event.UndoActionTitle );
+ assertEquals( "different opinions on the context nesting level (after leaving)",
+ m_activeUndoContexts.size(), i_event.UndoContextDepth );
+ m_leftContext = true;
+ impl_notifyContextDepth();
+ }
+
+ public void leftHiddenContext( UndoManagerEvent i_event )
+ {
+ assertFalse( "|leftHiddenContext| called after document was disposed", m_isDisposed );
+ assertEquals( "|leftHiddenContext| is not expected to notify an action title", 0, i_event.UndoActionTitle.length() );
+
+ m_activeUndoContexts.pop();
+ assertEquals( "different opinions on the context nesting level (after leaving)",
+ m_activeUndoContexts.size(), i_event.UndoContextDepth );
+ m_leftHiddenContext = true;
+ impl_notifyContextDepth();
+ }
+
+ public void cancelledContext( UndoManagerEvent i_event )
+ {
+ assertFalse( "|cancelledContext| called after document was disposed", m_isDisposed );
+ assertEquals( "|cancelledContext| is not expected to notify an action title", 0, i_event.UndoActionTitle.length() );
+
+ m_activeUndoContexts.pop();
+ assertEquals( "different opinions on the context nesting level (after cancelling)",
+ m_activeUndoContexts.size(), i_event.UndoContextDepth );
+ m_cancelledContext = true;
+ impl_notifyContextDepth();
+ }
+
+ public void disposing( EventObject i_event )
+ {
+ m_isDisposed = true;
+ }
+
+ public void waitForAllContextsClosed( final int i_milliSeconds ) throws InterruptedException
+ {
+ synchronized ( m_allContextsClosedCondition )
+ {
+ if ( m_activeUndoContexts.empty() )
+ return;
+ m_allContextsClosedCondition.wait( i_milliSeconds );
+ }
+ }
+
+ private void impl_notifyContextDepth()
+ {
+ synchronized ( m_allContextsClosedCondition )
+ {
+ if ( m_activeUndoContexts.empty() )
+ {
+ m_allContextsClosedCondition.notifyAll();
+ }
+ }
+ }
+
+ private int getUndoActionsAdded() { return m_undoActionsAdded; }
+ private int getUndoActionCount() { return m_undoCount; }
+ private int getRedoActionCount() { return m_redoCount; }
+ private String getCurrentUndoContextTitle() { return m_activeUndoContexts.peek(); }
+ private String getMostRecentlyAddedActionTitle() { return m_mostRecentlyAddedAction; }
+ private String getMostRecentlyUndoneTitle() { return m_mostRecentlyUndone; }
+ private int getCurrentUndoContextDepth() { return m_activeUndoContexts.size(); }
+ private boolean isDisposed() { return m_isDisposed; }
+ private boolean wasContextLeft() { return m_leftContext; }
+ private boolean wasHiddenContextLeft() { return m_leftHiddenContext; }
+ private boolean hasContextBeenCancelled() { return m_cancelledContext; }
+ private boolean wereStacksCleared() { return m_wasCleared; }
+ private boolean wasRedoStackCleared() { return m_redoWasCleared; }
+ private boolean wasManagerReset() { return m_managerWasReset; }
+
+ void reset()
+ {
+ m_undoActionsAdded = m_undoCount = m_redoCount = 0;
+ m_activeUndoContexts.clear();
+ m_mostRecentlyAddedAction = m_mostRecentlyUndone = null;
+ // m_isDisposed is not cleared, intentionally
+ m_leftContext = m_leftHiddenContext = m_cancelledContext = m_wasCleared = m_redoWasCleared = m_managerWasReset = false;
+ }
+
+ private int m_undoActionsAdded = 0;
+ private int m_undoCount = 0;
+ private int m_redoCount = 0;
+ private boolean m_isDisposed = false;
+ private boolean m_leftContext = false;
+ private boolean m_leftHiddenContext = false;
+ private boolean m_cancelledContext = false;
+ private boolean m_wasCleared = false;
+ private boolean m_redoWasCleared = false;
+ private boolean m_managerWasReset = false;
+ private Stack< String >
+ m_activeUndoContexts = new Stack<String>();
+ private String m_mostRecentlyAddedAction = null;
+ private String m_mostRecentlyUndone = null;
+ private final Object m_allContextsClosedCondition = new Object();
+ }
+
+
+ private void impl_checkUndo() throws Exception
+ {
+ System.out.println( "testing: " + m_currentTestCase.getDocumentDescription() );
+ m_currentDocument = m_currentTestCase.getDocument();
+ m_currentTestCase.initializeDocument();
+ m_currentTestCase.verifyInitialDocumentState();
+
+ final XUndoManager undoManager = getUndoManager();
+ undoManager.clear();
+ assertFalse( "clearing the Undo manager should result in the impossibility to undo anything", undoManager.isUndoPossible() );
+ assertFalse( "clearing the Undo manager should result in the impossibility to redo anything", undoManager.isRedoPossible() );
+
+ m_undoListener = new UndoListener();
+ undoManager.addUndoManagerListener( m_undoListener );
+
+ impl_testSingleModification( undoManager );
+ impl_testMultipleModifications( undoManager );
+ impl_testCustomUndoActions( undoManager );
+ impl_testLocking( undoManager );
+ impl_testNestedContexts( undoManager );
+ impl_testErrorHandling( undoManager );
+ impl_testContextHandling( undoManager );
+ impl_testStackHandling( undoManager );
+ impl_testClearance( undoManager );
+ impl_testHiddenContexts( undoManager );
+
+ // close the document, ensure the Undo manager listener gets notified
+ m_currentTestCase.closeDocument();
+ m_currentTestCase = null;
+ m_currentDocument = null;
+ assertTrue( "document is closed, but the UndoManagerListener has not been notified of the disposal", m_undoListener.isDisposed() );
+ }
+
+
+ private void impl_testSingleModification( final XUndoManager i_undoManager ) throws com.sun.star.uno.Exception
+ {
+ m_currentTestCase.doSingleModification();
+ m_currentTestCase.verifySingleModificationDocumentState();
+
+ // undo the modification, ensure the listener got the proper notifications
+ assertEquals( "We did not yet do an undo!", 0, m_undoListener.getUndoActionCount() );
+ i_undoManager.undo();
+ assertEquals( "A simple undo does not result in the proper Undo count.",
+ 1, m_undoListener.getUndoActionCount() );
+
+ // verify the document is in its initial state, again
+ m_currentTestCase.verifyInitialDocumentState();
+
+ // redo the modification, ensure the listener got the proper notifications
+ assertEquals( "not yet made a redo!", 0, m_undoListener.getRedoActionCount() );
+ i_undoManager.redo();
+ assertEquals( "made a redo, but got no notification of it!", 1, m_undoListener.getRedoActionCount() );
+ // ensure the document is in the proper state, again
+ m_currentTestCase.verifySingleModificationDocumentState();
+
+ // now do an Undo via the UI (aka the dispatch API), and see if this works, and notifies the listener as
+ // expected
+ m_currentTestCase.getDocument().getCurrentView().dispatch( ".uno:Undo" );
+ m_currentTestCase.verifyInitialDocumentState();
+ assertEquals( "UI-Undo does not notify the listener", 2, m_undoListener.getUndoActionCount() );
+ }
+
+
+ private void impl_testMultipleModifications( final XUndoManager i_undoManager ) throws com.sun.star.uno.Exception
+ {
+ m_undoListener.reset();
+ assertEquals( "unexpected initial undo context depth", 0, m_undoListener.getCurrentUndoContextDepth() );
+ i_undoManager.enterUndoContext( "Batch Changes" );
+ assertEquals( "unexpected undo context depth after entering a context",
+ 1, m_undoListener.getCurrentUndoContextDepth() );
+ assertEquals( "entering an Undo context has not been notified properly",
+ "Batch Changes", m_undoListener.getCurrentUndoContextTitle() );
+
+ final int modifications = m_currentTestCase.doMultipleModifications();
+ assertEquals( "unexpected number of undo actions while doing batch changes to the document",
+ modifications, m_undoListener.getUndoActionsAdded() );
+ assertEquals( "seems the document operations touched the undo context depth",
+ 1, m_undoListener.getCurrentUndoContextDepth() );
+
+ i_undoManager.leaveUndoContext();
+ assertEquals( "unexpected undo context depth after leaving the last context",
+ 0, m_undoListener.getCurrentUndoContextDepth() );
+ assertEquals( "no Undo done, yet - still the listener has been notified of an Undo action",
+ 0, m_undoListener.getUndoActionCount() );
+
+ i_undoManager.undo();
+ assertEquals( "Just did an undo - the listener should have been notified", 1, m_undoListener.getUndoActionCount() );
+ m_currentTestCase.verifyInitialDocumentState();
+ }
+
+
+ private void impl_testCustomUndoActions( final XUndoManager i_undoManager ) throws com.sun.star.uno.Exception
+ {
+ i_undoManager.clear();
+ m_undoListener.reset();
+ assertFalse( "undo stack not empty after clearing the undo manager", i_undoManager.isUndoPossible() );
+ assertFalse( "redo stack not empty after clearing the undo manager", i_undoManager.isRedoPossible() );
+ assertArrayEquals( ">0 descriptions for an empty undo stack?",
+ new String[0], i_undoManager.getAllUndoActionTitles() );
+ assertArrayEquals( ">0 descriptions for an empty redo stack?",
+ new String[0], i_undoManager.getAllRedoActionTitles() );
+
+ // add two actions, one directly, one within a context
+ final CustomUndoAction action1 = new CustomUndoAction( "UndoAction1" );
+ i_undoManager.addUndoAction( action1 );
+ assertEquals( "Adding an undo action not observed by the listener", 1, m_undoListener.getUndoActionsAdded() );
+ assertEquals( "Adding an undo action did not notify the proper title",
+ action1.getTitle(), m_undoListener.getMostRecentlyAddedActionTitle() );
+ final String contextTitle = "Undo Context";
+ i_undoManager.enterUndoContext( contextTitle );
+ final CustomUndoAction action2 = new CustomUndoAction( "UndoAction2" );
+ i_undoManager.addUndoAction( action2 );
+ assertEquals( "Adding an undo action not observed by the listener",
+ 2, m_undoListener.getUndoActionsAdded() );
+ assertEquals( "Adding an undo action did not notify the proper title",
+ action2.getTitle(), m_undoListener.getMostRecentlyAddedActionTitle() );
+ i_undoManager.leaveUndoContext();
+
+ // see if the manager has proper descriptions
+ assertArrayEquals( "unexpected Redo descriptions after adding two actions",
+ new String[0], i_undoManager.getAllRedoActionTitles() );
+ assertArrayEquals( "unexpected Undo descriptions after adding two actions",
+ new String[]{contextTitle, action1.getTitle()}, i_undoManager.getAllUndoActionTitles() );
+
+ // undo one action
+ i_undoManager.undo();
+ assertEquals( "improper action title notified during programmatic Undo",
+ contextTitle, m_undoListener.getMostRecentlyUndoneTitle() );
+ assertTrue( "nested custom undo action has not been undone as expected", action2.undoCalled() );
+ assertFalse( "nested custom undo action has not been undone as expected", action1.undoCalled() );
+ assertArrayEquals( "unexpected Redo descriptions after undoing a nested custom action",
+ new String[]{contextTitle}, i_undoManager.getAllRedoActionTitles() );
+ assertArrayEquals( "unexpected Undo descriptions after undoing a nested custom action",
+ new String[]{action1.getTitle()}, i_undoManager.getAllUndoActionTitles() );
+
+ // undo the second action, via UI dispatches
+ m_currentTestCase.getDocument().getCurrentView().dispatch( ".uno:Undo" );
+ assertEquals( "improper action title notified during UI Undo", action1.getTitle(), m_undoListener.getMostRecentlyUndoneTitle() );
+ assertTrue( "nested custom undo action has not been undone as expected", action1.undoCalled() );
+ assertArrayEquals( "unexpected Redo descriptions after undoing the second custom action",
+ new String[]{action1.getTitle(), contextTitle}, i_undoManager.getAllRedoActionTitles() );
+ assertArrayEquals( "unexpected Undo descriptions after undoing the second custom action",
+ new String[0], i_undoManager.getAllUndoActionTitles() );
+
+ // check the actions are disposed when the stacks are cleared
+ i_undoManager.clear();
+ assertTrue( action1.disposed() && action2.disposed() );
+ }
+
+
+ private void impl_testLocking( final XUndoManager i_undoManager ) throws com.sun.star.uno.Exception
+ {
+ i_undoManager.reset();
+ m_undoListener.reset();
+
+ // implicit Undo actions, triggered by changes to the document
+ assertFalse( "unexpected initial locking state", i_undoManager.isLocked() );
+ i_undoManager.lock();
+ assertTrue( "just locked the manager, why does it lie?", i_undoManager.isLocked() );
+ m_currentTestCase.doSingleModification();
+ assertEquals( "when the Undo manager is locked, no implicit additions should happen",
+ 0, m_undoListener.getUndoActionsAdded() );
+ assertTrue( "Undo manager gets unlocked as a side effect of performing a simple operation", i_undoManager.isLocked() );
+ i_undoManager.unlock();
+ assertEquals( "unlock is not expected to add collected actions - they should be discarded",
+ 0, m_undoListener.getUndoActionsAdded() );
+ assertFalse( "just unlocked the manager, why does it lie?", i_undoManager.isLocked() );
+
+ // explicit Undo actions
+ i_undoManager.lock();
+ i_undoManager.addUndoAction( new CustomUndoAction() );
+ i_undoManager.unlock();
+ assertEquals( "explicit Undo actions are expected to be ignored when the manager is locked",
+ 0, m_undoListener.getUndoActionsAdded() );
+
+ // Undo contexts while being locked
+ i_undoManager.lock();
+ i_undoManager.enterUndoContext( "Dummy Context" );
+ i_undoManager.enterHiddenUndoContext();
+ assertEquals( "entering Undo contexts should be ignored when the manager is locked", 0, m_undoListener.getCurrentUndoContextDepth() );
+ i_undoManager.leaveUndoContext();
+ i_undoManager.leaveUndoContext();
+ i_undoManager.unlock();
+
+ // |unlock| error handling
+ assertFalse( "internal error: manager should not be locked at this point in time", i_undoManager.isLocked() );
+ boolean caughtExpected = false;
+ try { i_undoManager.unlock(); } catch ( final NotLockedException e ) { caughtExpected = true; }
+ assertTrue( "unlocking the manager when it is not locked should throw", caughtExpected );
+ }
+
+
+ private void impl_testContextHandling( final XUndoManager i_undoManager ) throws com.sun.star.uno.Exception
+ {
+
+ // part I: non-empty contexts
+ i_undoManager.reset();
+ m_undoListener.reset();
+
+ // put one action on the undo and one on the redo stack, as precondition for the following tests
+ final XUndoAction undoAction1 = new CustomUndoAction( "Undo Action 1" );
+ i_undoManager.addUndoAction( undoAction1 );
+ final XUndoAction undoAction2 = new CustomUndoAction( "Undo Action 2" );
+ i_undoManager.addUndoAction( undoAction2 );
+ i_undoManager.undo();
+ assertTrue( "precondition for context handling tests not met (1)", i_undoManager.isUndoPossible() );
+ assertTrue( "precondition for context handling tests not met (2)", i_undoManager.isRedoPossible() );
+ assertArrayEquals( new String[] { undoAction1.getTitle() }, i_undoManager.getAllUndoActionTitles() );
+ assertArrayEquals( new String[] { undoAction2.getTitle() }, i_undoManager.getAllRedoActionTitles() );
+
+ final String[] expectedRedoActionComments = new String[] { undoAction2.getTitle() };
+ assertArrayEquals( expectedRedoActionComments, i_undoManager.getAllRedoActionTitles() );
+
+ // enter a context
+ i_undoManager.enterUndoContext( "Undo Context" );
+ // this should not (yet) touch the redo stack
+ assertArrayEquals( expectedRedoActionComments, i_undoManager.getAllRedoActionTitles() );
+ assertEquals( "unexpected undo context depth after entering a context", 1, m_undoListener.getCurrentUndoContextDepth() );
+ // add a single action
+ XUndoAction undoAction3 = new CustomUndoAction( "Undo Action 3" );
+ i_undoManager.addUndoAction( undoAction3 );
+ // still, the redo stack should be untouched - added at a lower level does not affect it at all
+ assertArrayEquals( expectedRedoActionComments, i_undoManager.getAllRedoActionTitles() );
+
+ // while the context is open, its title should already contribute to the stack, ...
+ assertEquals( "Undo Context", i_undoManager.getCurrentUndoActionTitle() );
+ // ... getAllUndo/RedoActionTitles should operate on the top level, not on the level defined by the open
+ // context, ...
+ assertArrayEquals( new String[] { "Undo Context", undoAction1.getTitle() },
+ i_undoManager.getAllUndoActionTitles() );
+ // ... but Undo and Redo should be impossible as long as the context is open
+ assertFalse( i_undoManager.isUndoPossible() );
+ assertFalse( i_undoManager.isRedoPossible() );
+
+ // leave the context, check the listener has been notified properly, and the notified context depth is correct
+ i_undoManager.leaveUndoContext();
+ assertTrue( m_undoListener.wasContextLeft() );
+ assertFalse( m_undoListener.wasHiddenContextLeft() );
+ assertFalse( m_undoListener.hasContextBeenCancelled() );
+ assertEquals( "unexpected undo context depth leaving a non-empty context", 0, m_undoListener.getCurrentUndoContextDepth() );
+ // leaving a non-empty context should have cleared the redo stack
+ assertArrayEquals( new String[0], i_undoManager.getAllRedoActionTitles() );
+ assertTrue( m_undoListener.wasRedoStackCleared() );
+
+
+ // part II: empty contexts
+ i_undoManager.reset();
+ m_undoListener.reset();
+
+ // enter a context, leave it immediately without adding an action to it
+ i_undoManager.enterUndoContext( "Undo Context" );
+ i_undoManager.leaveUndoContext();
+ assertFalse( m_undoListener.wasContextLeft() );
+ assertFalse( m_undoListener.wasHiddenContextLeft() );
+ assertTrue( m_undoListener.hasContextBeenCancelled() );
+ assertFalse( "leaving an empty context should silently remove it, and not contribute to the stack",
+ i_undoManager.isUndoPossible() );
+ }
+
+
+ private void impl_testNestedContexts( final XUndoManager i_undoManager ) throws com.sun.star.uno.Exception
+ {
+ i_undoManager.reset();
+ m_undoListener.reset();
+ i_undoManager.enterUndoContext( "context 1" );
+ i_undoManager.enterUndoContext( "context 1.1" );
+ final CustomUndoAction action1 = new CustomUndoAction( "action 1.1.1" );
+ i_undoManager.addUndoAction( action1 );
+ i_undoManager.enterUndoContext( "context 1.1.2" );
+ final CustomUndoAction action2 = new CustomUndoAction( "action 1.1.2.1" );
+ i_undoManager.addUndoAction( action2 );
+ i_undoManager.leaveUndoContext();
+ final CustomUndoAction action3 = new CustomUndoAction( "action 1.1.3" );
+ i_undoManager.addUndoAction( action3 );
+ i_undoManager.leaveUndoContext();
+ i_undoManager.leaveUndoContext();
+ final CustomUndoAction action4 = new CustomUndoAction( "action 1.2" );
+ i_undoManager.addUndoAction( action4 );
+
+ i_undoManager.undo();
+ assertEquals( "undoing a single action notifies a wrong title", action4.getTitle(), m_undoListener.getMostRecentlyUndoneTitle() );
+ assertTrue( "custom Undo not called", action4.undoCalled() );
+ assertFalse( "too many custom Undos called", action1.undoCalled() || action2.undoCalled() || action3.undoCalled() );
+ i_undoManager.undo();
+ assertTrue( "nested actions not properly undone", action1.undoCalled() && action2.undoCalled() && action3.undoCalled() );
+ }
+
+
+ private void impl_testErrorHandling( final XUndoManager i_undoManager ) throws com.sun.star.uno.Exception
+ {
+ i_undoManager.reset();
+ m_undoListener.reset();
+
+ // try retrieving the comments for the current Undo/Redo - this should fail
+ boolean caughtExpected = false;
+ try { i_undoManager.getCurrentUndoActionTitle(); }
+ catch( final EmptyUndoStackException e ) { caughtExpected = true; }
+ assertTrue( "trying the title of the current Undo action is expected to fail for an empty stack", caughtExpected );
+
+ caughtExpected = false;
+ try { i_undoManager.getCurrentRedoActionTitle(); }
+ catch( final EmptyUndoStackException e ) { caughtExpected = true; }
+ assertTrue( "trying the title of the current Redo action is expected to fail for an empty stack", caughtExpected );
+
+ caughtExpected = false;
+ try { i_undoManager.undo(); } catch ( final EmptyUndoStackException e ) { caughtExpected = true; }
+ assertTrue( "undo should throw if no Undo action is on the stack", caughtExpected );
+
+ caughtExpected = false;
+ try { i_undoManager.redo(); } catch ( final EmptyUndoStackException e ) { caughtExpected = true; }
+ assertTrue( "redo should throw if no Redo action is on the stack", caughtExpected );
+
+ caughtExpected = false;
+ try { i_undoManager.leaveUndoContext(); } catch ( final InvalidStateException e ) { caughtExpected = true; }
+ assertTrue( "leaveUndoContext should throw if no context is currently open", caughtExpected );
+
+ caughtExpected = false;
+ try { i_undoManager.addUndoAction( null ); } catch ( com.sun.star.lang.IllegalArgumentException e ) { caughtExpected = true; }
+ assertTrue( "adding a NULL action should be rejected", caughtExpected );
+
+ i_undoManager.reset();
+ i_undoManager.addUndoAction( new CustomUndoAction() );
+ i_undoManager.addUndoAction( new CustomUndoAction() );
+ i_undoManager.undo();
+ i_undoManager.enterUndoContext( "Undo Context" );
+ // those methods should fail when a context is open:
+ final String[] methodNames = new String[] { "undo", "redo", "clear", "clearRedo" };
+ for ( int i=0; i<methodNames.length; ++i )
+ {
+ caughtExpected = false;
+ try
+ {
+ Method method = i_undoManager.getClass().getMethod( methodNames[i], new Class[0] );
+ method.invoke( i_undoManager, new Object[0] );
+ }
+ catch ( IllegalAccessException ex ) { }
+ catch ( IllegalArgumentException ex ) { }
+ catch ( InvocationTargetException ex )
+ {
+ Throwable targetException = ex.getTargetException();
+ caughtExpected = ( targetException instanceof UndoContextNotClosedException );
+ }
+ catch ( NoSuchMethodException ex ) { }
+ catch ( SecurityException ex ) { }
+
+ assertTrue( methodNames[i] + " should be rejected when there is an open context", caughtExpected );
+ }
+ i_undoManager.leaveUndoContext();
+
+ // try Undo actions which fail in their Undo/Redo
+ for ( int i=0; i<4; ++i )
+ {
+ final boolean undo = ( i < 2 );
+ final boolean doByAPI = ( i % 2 ) == 0;
+
+ i_undoManager.reset();
+ i_undoManager.addUndoAction( new CustomUndoAction() );
+ i_undoManager.addUndoAction( new FailingUndoAction( undo ? FAIL_UNDO : FAIL_REDO ) );
+ i_undoManager.addUndoAction( new CustomUndoAction() );
+ i_undoManager.undo();
+ if ( !undo )
+ i_undoManager.undo();
+ // assert preconditions for the below test
+ assertTrue( i_undoManager.isUndoPossible() );
+ assertTrue( i_undoManager.isRedoPossible() );
+
+ boolean caughtUndoFailed = false;
+ try
+ {
+ if ( undo )
+ if ( doByAPI )
+ i_undoManager.undo();
+ else
+ m_currentTestCase.getDocument().getCurrentView().dispatch( ".uno:Undo" );
+ else
+ if ( doByAPI )
+ i_undoManager.redo();
+ else
+ m_currentTestCase.getDocument().getCurrentView().dispatch( ".uno:Redo" );
+ }
+ catch ( UndoFailedException e )
+ {
+ caughtUndoFailed = true;
+ }
+ if ( doByAPI )
+ assertTrue( "Exceptions in XUndoAction.undo should be propagated at the API", caughtUndoFailed );
+ else
+ assertFalse( "Undo/Redo by UI should not let escape Exceptions", caughtUndoFailed );
+ if ( undo )
+ {
+ assertFalse( "a failing Undo should clear the Undo stack", i_undoManager.isUndoPossible() );
+ assertTrue( "a failing Undo should /not/ clear the Redo stack", i_undoManager.isRedoPossible() );
+ }
+ else
+ {
+ assertTrue( "a failing Redo should /not/ clear the Undo stack", i_undoManager.isUndoPossible() );
+ assertFalse( "a failing Redo should clear the Redo stack", i_undoManager.isRedoPossible() );
+ }
+ }
+ }
+
+
+ private void impl_testStackHandling( final XUndoManager i_undoManager ) throws com.sun.star.uno.Exception
+ {
+ i_undoManager.reset();
+ m_undoListener.reset();
+
+ assertFalse( i_undoManager.isUndoPossible() );
+ assertFalse( i_undoManager.isRedoPossible() );
+
+ i_undoManager.addUndoAction( new CustomUndoAction() );
+ assertTrue( i_undoManager.isUndoPossible() );
+ assertFalse( i_undoManager.isRedoPossible() );
+ i_undoManager.addUndoAction( new CustomUndoAction() );
+ assertTrue( i_undoManager.isUndoPossible() );
+ assertFalse( i_undoManager.isRedoPossible() );
+ i_undoManager.undo();
+ assertTrue( i_undoManager.isUndoPossible() );
+ assertTrue( i_undoManager.isRedoPossible() );
+ i_undoManager.undo();
+ assertFalse( i_undoManager.isUndoPossible() );
+ assertTrue( i_undoManager.isRedoPossible() );
+ i_undoManager.addUndoAction( new CustomUndoAction() );
+ assertTrue( i_undoManager.isUndoPossible() );
+ assertFalse( "adding a new action should have cleared the Redo stack", i_undoManager.isRedoPossible() );
+ }
+
+
+ private void impl_testClearance( final XUndoManager i_undoManager ) throws com.sun.star.uno.Exception
+ {
+ i_undoManager.reset();
+ m_undoListener.reset();
+
+ // add an action, clear the stack, verify the listener has been called
+ i_undoManager.addUndoAction( new CustomUndoAction() );
+ assertFalse( "clearance listener unexpectedly called", m_undoListener.wereStacksCleared() );
+ assertFalse( "redo-clearance listener unexpectedly called", m_undoListener.wasRedoStackCleared() );
+ i_undoManager.clear();
+ assertTrue( "clearance listener not called as expected", m_undoListener.wereStacksCleared() );
+ assertFalse( "redo-clearance listener unexpectedly called (2)", m_undoListener.wasRedoStackCleared() );
+
+ // ensure the listener is also called if the stack is actually empty at the moment of the call
+ m_undoListener.reset();
+ assertFalse( i_undoManager.isUndoPossible() );
+ i_undoManager.clear();
+ assertTrue( "clearance listener is also expected to be called if the stack was empty before", m_undoListener.wereStacksCleared() );
+
+ // ensure the proper listeners are called for clearRedo
+ m_undoListener.reset();
+ i_undoManager.clearRedo();
+ assertFalse( m_undoListener.wereStacksCleared() );
+ assertTrue( m_undoListener.wasRedoStackCleared() );
+
+ // ensure the redo listener is also called upon implicit redo stack clearance
+ m_undoListener.reset();
+ i_undoManager.addUndoAction( new CustomUndoAction() );
+ i_undoManager.addUndoAction( new CustomUndoAction() );
+ i_undoManager.undo();
+ assertTrue( i_undoManager.isUndoPossible() );
+ assertTrue( i_undoManager.isRedoPossible() );
+ i_undoManager.addUndoAction( new CustomUndoAction() );
+ assertFalse( i_undoManager.isRedoPossible() );
+ assertTrue( "implicit clearance of the Redo stack does not notify listeners", m_undoListener.wasRedoStackCleared() );
+
+ // test resetting the manager
+ m_undoListener.reset();
+ i_undoManager.addUndoAction( new CustomUndoAction() );
+ i_undoManager.addUndoAction( new CustomUndoAction() );
+ i_undoManager.undo();
+ assertTrue( i_undoManager.isUndoPossible() );
+ assertTrue( i_undoManager.isRedoPossible() );
+ i_undoManager.reset();
+ assertFalse( i_undoManager.isUndoPossible() );
+ assertFalse( i_undoManager.isRedoPossible() );
+ assertTrue( "|reset| does not properly notify", m_undoListener.wasManagerReset() );
+
+ // resetting the manager, with open undo contexts
+ m_undoListener.reset();
+ i_undoManager.addUndoAction( new CustomUndoAction() );
+ i_undoManager.enterUndoContext( "Undo Context" );
+ i_undoManager.addUndoAction( new CustomUndoAction() );
+ i_undoManager.enterHiddenUndoContext();
+ i_undoManager.reset();
+ assertTrue( "|reset| while contexts are open does not properly notify", m_undoListener.wasManagerReset() );
+ // verify the manager really has the proper context depth now
+ i_undoManager.enterUndoContext( "Undo Context" );
+ assertEquals( "seems that |reset| did not really close the open contexts", 1, m_undoListener.getCurrentUndoContextDepth() );
+ }
+
+
+ private void impl_testHiddenContexts( final XUndoManager i_undoManager ) throws com.sun.star.uno.Exception
+ {
+ i_undoManager.reset();
+ m_undoListener.reset();
+ assertFalse( "precondition for testing hidden undo contexts not met", i_undoManager.isUndoPossible() );
+
+ // entering a hidden context should be rejected if the stack is empty
+ boolean caughtExpected = false;
+ try { i_undoManager.enterHiddenUndoContext(); }
+ catch ( final EmptyUndoStackException e ) { caughtExpected = true; }
+ assertTrue( "entering hidden contexts should be denied on an empty stack", caughtExpected );
+
+ // but it should be allowed if the context is not empty
+ final CustomUndoAction undoAction0 = new CustomUndoAction( "Step 0" );
+ i_undoManager.addUndoAction( undoAction0 );
+ final CustomUndoAction undoAction1 = new CustomUndoAction( "Step 1" );
+ i_undoManager.addUndoAction( undoAction1 );
+ i_undoManager.enterHiddenUndoContext();
+ final CustomUndoAction hiddenUndoAction = new CustomUndoAction( "hidden context action" );
+ i_undoManager.addUndoAction( hiddenUndoAction );
+ i_undoManager.leaveUndoContext();
+ assertFalse( "leaving a hidden should not call |leftUndocontext|", m_undoListener.wasContextLeft() );
+ assertTrue( "leaving a hidden does not call |leftHiddenUndocontext|", m_undoListener.wasHiddenContextLeft() );
+ assertFalse( "leaving a non-empty hidden context claims to have cancelled it", m_undoListener.hasContextBeenCancelled() );
+ assertEquals( "leaving a hidden context is not properly notified", 0, m_undoListener.getCurrentUndoContextDepth() );
+ assertArrayEquals( "unexpected Undo stack after leaving a hidden context",
+ new String[] { undoAction1.getTitle(), undoAction0.getTitle() },
+ i_undoManager.getAllUndoActionTitles() );
+
+ // and then calling |undo| once should not only undo everything in the hidden context, but also
+ // the previous action - but not more
+ i_undoManager.undo();
+ assertTrue( "Undo after leaving a hidden context does not actually undo the context actions",
+ hiddenUndoAction.undoCalled() );
+ assertTrue( "Undo after leaving a hidden context does not undo the predecessor action",
+ undoAction1.undoCalled() );
+ assertFalse( "Undo after leaving a hidden context undoes too much",
+ undoAction0.undoCalled() );
+
+ // leaving an empty hidden context should call the proper notification method
+ m_undoListener.reset();
+ i_undoManager.enterHiddenUndoContext();
+ i_undoManager.leaveUndoContext();
+ assertFalse( m_undoListener.wasContextLeft() );
+ assertFalse( m_undoListener.wasHiddenContextLeft() );
+ assertTrue( m_undoListener.hasContextBeenCancelled() );
+
+ // nesting hidden and normal contexts
+ m_undoListener.reset();
+ i_undoManager.reset();
+ final CustomUndoAction action0 = new CustomUndoAction( "action 0" );
+ i_undoManager.addUndoAction( action0 );
+ i_undoManager.enterUndoContext( "context 1" );
+ final CustomUndoAction action1 = new CustomUndoAction( "action 1" );
+ i_undoManager.addUndoAction( action1 );
+ i_undoManager.enterHiddenUndoContext();
+ final CustomUndoAction action2 = new CustomUndoAction( "action 2" );
+ i_undoManager.addUndoAction( action2 );
+ i_undoManager.enterUndoContext( "context 2" );
+ // is entering a hidden context rejected even at the nesting level > 0 (the above test was for nesting level == 0)?
+ caughtExpected = false;
+ try { i_undoManager.enterHiddenUndoContext(); }
+ catch( final EmptyUndoStackException e ) { caughtExpected = true; }
+ assertTrue( "at a nesting level > 0, denied hidden contexts does not work as expected", caughtExpected );
+ final CustomUndoAction action3 = new CustomUndoAction( "action 3" );
+ i_undoManager.addUndoAction( action3 );
+ i_undoManager.enterHiddenUndoContext();
+ assertEquals( "mixed hidden/normal context do are not properly notified", 4, m_undoListener.getCurrentUndoContextDepth() );
+ i_undoManager.leaveUndoContext();
+ assertTrue( "the left context was empty - why wasn't 'cancelled' notified?", m_undoListener.hasContextBeenCancelled() );
+ assertFalse( m_undoListener.wasContextLeft() );
+ assertFalse( m_undoListener.wasHiddenContextLeft() );
+ i_undoManager.leaveUndoContext();
+ i_undoManager.leaveUndoContext();
+ i_undoManager.leaveUndoContext();
+ i_undoManager.undo();
+ assertFalse( "one action too much has been undone", action0.undoCalled() );
+ assertTrue( action1.undoCalled() );
+ assertTrue( action2.undoCalled() );
+ assertTrue( action3.undoCalled() );
+ }
+
+
+ private XComponentContext getContext()
+ {
+ return m_connection.getComponentContext();
+ }
+
+
+ private XMultiServiceFactory getORB()
+ {
+ final XMultiServiceFactory xMSF1 = UnoRuntime.queryInterface(
+ XMultiServiceFactory.class, getContext().getServiceManager() );
+ return xMSF1;
+ }
+
+
+ @BeforeClass
+ public static void setUpConnection() throws java.lang.Exception
+ {
+ System.out.println( "--------------------------------------------------------------------------------" );
+ System.out.println( "starting class: " + UndoManager.class.getName() );
+ System.out.println( "connecting ..." );
+ m_connection.setUp();
+ }
+
+
+ @AfterClass
+ public static void tearDownConnection() throws InterruptedException, com.sun.star.uno.Exception
+ {
+ System.out.println();
+ System.out.println( "tearing down connection" );
+ m_connection.tearDown();
+ System.out.println( "finished class: " + UndoManager.class.getName() );
+ System.out.println( "--------------------------------------------------------------------------------" );
+ }
+
+
+ private static class CustomUndoAction implements XUndoAction, XComponent
+ {
+ CustomUndoAction()
+ {
+ m_title = "Custom Undo Action";
+ }
+
+ CustomUndoAction( final String i_title )
+ {
+ m_title = i_title;
+ }
+
+ public String getTitle()
+ {
+ return m_title;
+ }
+
+ public void undo() throws UndoFailedException
+ {
+ m_undoCalled = true;
+ }
+
+ public void redo() throws UndoFailedException
+ {
+ }
+
+ public void dispose()
+ {
+ m_disposed = true;
+ }
+
+ public void addEventListener( XEventListener xl )
+ {
+ fail( "addEventListener is not expected to be called in the course of this test" );
+ }
+
+ public void removeEventListener( XEventListener xl )
+ {
+ fail( "removeEventListener is not expected to be called in the course of this test" );
+ }
+
+ boolean undoCalled() { return m_undoCalled; }
+ boolean disposed() { return m_disposed; }
+
+ private final String m_title;
+ private boolean m_undoCalled = false;
+ private boolean m_disposed = false;
+ }
+
+ private static short FAIL_UNDO = 1;
+ private static short FAIL_REDO = 2;
+
+ private static class FailingUndoAction implements XUndoAction
+ {
+ FailingUndoAction( final short i_failWhich )
+ {
+ m_failWhich = i_failWhich;
+ }
+
+ public String getTitle()
+ {
+ return "failing undo";
+ }
+
+ public void undo() throws UndoFailedException
+ {
+ if ( m_failWhich != FAIL_REDO )
+ impl_throw();
+ }
+
+ public void redo() throws UndoFailedException
+ {
+ if ( m_failWhich != FAIL_UNDO )
+ impl_throw();
+ }
+
+ private void impl_throw() throws UndoFailedException
+ {
+ throw new UndoFailedException();
+ }
+
+ private final short m_failWhich;
+ }
+
+
+ private static class CountingUndoAction implements XUndoAction
+ {
+ CountingUndoAction( final int i_expectedOrder, final Object i_lock, final Integer[] i_actionsUndoneCounter )
+ {
+ m_expectedOrder = i_expectedOrder;
+ m_lock = i_lock;
+ m_actionsUndoneCounter = i_actionsUndoneCounter;
+ }
+
+ public String getTitle()
+ {
+ return "Counting Undo Action";
+ }
+
+ public void undo() throws UndoFailedException
+ {
+ synchronized( m_lock )
+ {
+ assertEquals( "Undo action called out of order", m_expectedOrder, m_actionsUndoneCounter[0].intValue() );
+ ++m_actionsUndoneCounter[0];
+ }
+ }
+
+ public void redo() throws UndoFailedException
+ {
+ fail( "CountingUndoAction.redo is not expected to be called in this test." );
+ }
+ private final int m_expectedOrder;
+ private final Object m_lock;
+ private Integer[] m_actionsUndoneCounter;
+ }
+
+
+ private static String getCallbackUndoContextTitle()
+ {
+ return "Some Unfinished Undo Context";
+ }
+
+
+ private static String getCallbackComponentServiceName()
+ {
+ return "org.openoffice.complex.sfx2.Callback";
+ }
+
+
+ /**
+ * a factory for a callback component which, at OOo runtime, is inserted into OOo's "component repository"
+ */
+ private class CallbackComponentFactory implements XSingleComponentFactory, XServiceInfo, XComponent
+ {
+ public Object createInstanceWithContext( XComponentContext i_context ) throws com.sun.star.uno.Exception
+ {
+ return new CallbackComponent();
+ }
+
+ public Object createInstanceWithArgumentsAndContext( Object[] i_arguments, XComponentContext i_context ) throws com.sun.star.uno.Exception
+ {
+ return createInstanceWithContext( i_context );
+ }
+
+ public String getImplementationName()
+ {
+ return "org.openoffice.complex.sfx2.CallbackComponent";
+ }
+
+ public boolean supportsService( String i_serviceName )
+ {
+ return i_serviceName.equals( getCallbackComponentServiceName() );
+ }
+
+ public String[] getSupportedServiceNames()
+ {
+ return new String[] { getCallbackComponentServiceName() };
+ }
+
+ public void dispose()
+ {
+ final EventObject event = new EventObject( this );
+
+ final ArrayList<XEventListener> eventListenersCopy = (ArrayList<XEventListener>)m_eventListeners.clone();
+ final Iterator<XEventListener> iter = eventListenersCopy.iterator();
+ while ( iter.hasNext() )
+ {
+ iter.next().disposing( event );
+ }
+ }
+
+ public void addEventListener( XEventListener i_listener )
+ {
+ if ( i_listener != null )
+ m_eventListeners.add( i_listener );
+ }
+
+ public void removeEventListener( XEventListener i_listener )
+ {
+ m_eventListeners.remove( i_listener );
+ }
+
+ private final ArrayList<XEventListener> m_eventListeners = new ArrayList<XEventListener>();
+ }
+
+
+ private class CallbackComponent implements XJob, XTypeProvider
+ {
+ CallbackComponent()
+ {
+ }
+
+ public Object execute( NamedValue[] i_parameters ) throws com.sun.star.lang.IllegalArgumentException, com.sun.star.uno.Exception
+ {
+ // this method is called from within the Basic script which is to check whether the OOo framework
+ // properly cleans up unfinished Undo contexts. It is called immediately after the context has been
+ // entered, so verify the expected Undo manager state.
+ assertEquals( getCallbackUndoContextTitle(), m_undoListener.getCurrentUndoContextTitle() );
+ assertEquals( 1, m_undoListener.getCurrentUndoContextDepth() );
+
+ synchronized( m_callbackCondition )
+ {
+ m_callbackCalled = true;
+ m_callbackCondition.notifyAll();
+ }
+ return m_closeAfterCallback ? "close" : "";
+ }
+
+ public Type[] getTypes()
+ {
+ final Class<?> interfaces[] = getClass().getInterfaces();
+ Type types[] = new Type[ interfaces.length ];
+ for ( int i = 0; i < interfaces.length; ++i )
+ types[i] = new Type(interfaces[i]);
+ return types;
+ }
+
+ public byte[] getImplementationId()
+ {
+ return new byte[0];
+ }
+ }
+
+ private static final OfficeConnection m_connection = new OfficeConnection();
+ private DocumentTest m_currentTestCase;
+ private OfficeDocument m_currentDocument;
+ private UndoListener m_undoListener;
+ private CallbackComponentFactory m_callbackFactory = null;
+ private boolean m_callbackCalled = false;
+ private boolean m_closeAfterCallback = false;
+ private final Object m_callbackCondition = new Object();
+}
diff --git a/sfx2/qa/complex/sfx2/testdocuments/CUSTOM.odt b/sfx2/qa/complex/sfx2/testdocuments/CUSTOM.odt
new file mode 100644
index 000000000..831a8f451
--- /dev/null
+++ b/sfx2/qa/complex/sfx2/testdocuments/CUSTOM.odt
Binary files differ
diff --git a/sfx2/qa/complex/sfx2/testdocuments/TEST.odt b/sfx2/qa/complex/sfx2/testdocuments/TEST.odt
new file mode 100644
index 000000000..7c6f0b60f
--- /dev/null
+++ b/sfx2/qa/complex/sfx2/testdocuments/TEST.odt
Binary files differ
diff --git a/sfx2/qa/complex/sfx2/testdocuments/TESTRDFA.odt b/sfx2/qa/complex/sfx2/testdocuments/TESTRDFA.odt
new file mode 100644
index 000000000..d59739142
--- /dev/null
+++ b/sfx2/qa/complex/sfx2/testdocuments/TESTRDFA.odt
Binary files differ
diff --git a/sfx2/qa/complex/sfx2/testdocuments/empty.rdf b/sfx2/qa/complex/sfx2/testdocuments/empty.rdf
new file mode 100644
index 000000000..af62bab39
--- /dev/null
+++ b/sfx2/qa/complex/sfx2/testdocuments/empty.rdf
@@ -0,0 +1,13 @@
+<?xml version="1.0"?>
+
+<RDF
+ xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:s="http://www.w3.org/2000/01/rdf-schema#">
+
+<!--
+ This is the RDF Schema for the RDF data model as described in the
+ Resource Description Framework (RDF) Model and Syntax Specification
+ http://www.w3.org/TR/REC-rdf-syntax -->
+
+</RDF>
diff --git a/sfx2/qa/complex/sfx2/tools/TestDocument.java b/sfx2/qa/complex/sfx2/tools/TestDocument.java
new file mode 100644
index 000000000..462c97fb9
--- /dev/null
+++ b/sfx2/qa/complex/sfx2/tools/TestDocument.java
@@ -0,0 +1,34 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package complex.sfx2.tools;
+
+import java.io.File;
+import org.openoffice.test.OfficeFileUrl;
+import org.openoffice.test.Argument;
+
+public final class TestDocument {
+ public static String getUrl(String name) {
+ return OfficeFileUrl.getAbsolute(new File(Argument.get("tdoc"), name));
+ }
+ public static String getPath(String name) {
+ return new File(Argument.get("tdoc"), name).toString();
+ }
+
+ private TestDocument() {}
+}
diff --git a/sfx2/qa/complex/sfx2/tools/WriterHelper.java b/sfx2/qa/complex/sfx2/tools/WriterHelper.java
new file mode 100644
index 000000000..3180d3607
--- /dev/null
+++ b/sfx2/qa/complex/sfx2/tools/WriterHelper.java
@@ -0,0 +1,210 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+package complex.sfx2.tools;
+
+import com.sun.star.accessibility.AccessibleRole;
+import com.sun.star.accessibility.XAccessible;
+import com.sun.star.accessibility.XAccessibleAction;
+import com.sun.star.accessibility.XAccessibleContext;
+import com.sun.star.awt.XExtendedToolkit;
+import com.sun.star.awt.XWindow;
+import com.sun.star.beans.PropertyValue;
+import com.sun.star.frame.XController;
+import com.sun.star.frame.XDesktop;
+import com.sun.star.frame.XDispatch;
+import com.sun.star.frame.XDispatchProvider;
+import com.sun.star.frame.XModel;
+import com.sun.star.lang.XComponent;
+import com.sun.star.lang.XMultiServiceFactory;
+import com.sun.star.text.XTextDocument;
+import com.sun.star.uno.UnoRuntime;
+import com.sun.star.util.URL;
+import com.sun.star.util.XCloseable;
+import com.sun.star.util.XURLTransformer;
+
+import util.AccessibilityTools;
+import util.WriterTools;
+
+/**
+ * Methods to open Writer docs
+ *
+ */
+public class WriterHelper {
+ private XMultiServiceFactory m_xMSF = null;
+
+ /**
+ * Creates a new instance of WriterHelper
+ *
+ * @param xMSF
+ * The MultiServiceFactory gained from the office
+ */
+ public WriterHelper(XMultiServiceFactory xMSF) {
+ this.m_xMSF = xMSF;
+ }
+
+ /**
+ * Opens an empty document
+ *
+ * @return a reference to the opened document is returned
+ */
+ public XTextDocument openEmptyDoc() {
+ return WriterTools.createTextDoc(m_xMSF);
+ }
+
+ /**
+ * Closes a given XTextDocument
+ *
+ * @param xTextDoc
+ * the text document to be closed
+ * @return if an error occurs the errormessage is returned and an empty
+ * String if not
+ */
+ public String closeDoc(XTextDocument xTextDoc) {
+ XCloseable closer = UnoRuntime.queryInterface(XCloseable.class,
+ xTextDoc);
+ String err = "";
+
+ try {
+ closer.close(true);
+ } catch (com.sun.star.util.CloseVetoException e) {
+ err = "couldn't close document " + e;
+ System.out.println(err);
+ }
+
+ return err;
+ }
+
+ private XTextDocument xLocalDoc = null;
+
+ /**
+ * a TextDocument is opened by pressing a button in a dialog given by
+ * uno-URL
+ *
+ * @param url
+ * the uno-URL of the dialog to be opened
+ * @param createButton
+ * the language dependent label of the button to be pressed
+ * @param destroyLocal
+ * if true the document that has been opened to dispatch the
+ * dialog is closed before the method returns, otherwise this
+ * document remains open
+ * @return returns the created Textdocument
+ */
+ public XTextDocument openFromDialog(String url, String createButton,
+ boolean destroyLocal) throws Exception {
+ xLocalDoc = WriterTools.createTextDoc(m_xMSF);
+ XComponent comp = UnoRuntime
+ .queryInterface(XComponent.class, xLocalDoc);
+
+ XModel aModel = UnoRuntime.queryInterface(XModel.class, comp);
+
+ XController xController = aModel.getCurrentController();
+
+ // Opening Dialog
+ XDispatchProvider xDispProv = UnoRuntime.queryInterface(
+ XDispatchProvider.class, xController.getFrame());
+ XURLTransformer xParser = UnoRuntime.queryInterface(
+ XURLTransformer.class,
+ m_xMSF.createInstance("com.sun.star.util.URLTransformer"));
+
+ // Because it's an in/out parameter
+ // we must use an array of URL objects.
+ URL[] aParseURL = new URL[] { new URL() };
+ aParseURL[0].Complete = url;
+ xParser.parseStrict(aParseURL);
+
+ XDispatch xDispatcher = xDispProv.queryDispatch(aParseURL[0], "",
+ com.sun.star.frame.FrameSearchFlag.SELF
+ | com.sun.star.frame.FrameSearchFlag.CHILDREN);
+ if (xDispatcher != null) {
+ PropertyValue[] dispatchArguments = new PropertyValue[0];
+ xDispatcher.dispatch(aParseURL[0], dispatchArguments);
+ }
+
+ if (createButton.length() > 1) {
+ XExtendedToolkit tk = getToolkit();
+ Object atw = tk.getActiveTopWindow();
+
+ XWindow xWindow = UnoRuntime.queryInterface(XWindow.class, atw);
+
+ XAccessible xRoot = AccessibilityTools.getAccessibleObject(xWindow);
+ XAccessibleContext buttonContext = AccessibilityTools
+ .getAccessibleObjectForRole(xRoot,
+ AccessibleRole.PUSH_BUTTON, createButton);
+
+ XAccessibleAction buttonAction = UnoRuntime.queryInterface(
+ XAccessibleAction.class, buttonContext);
+
+ try {
+ System.out
+ .println("Name: " + buttonContext.getAccessibleName());
+ buttonAction.doAccessibleAction(0);
+ } catch (com.sun.star.lang.IndexOutOfBoundsException e) {
+ System.out.println("Couldn't press button");
+ }
+
+ util.utils.waitForEventIdle(m_xMSF);
+ }
+
+ XDesktop xDesktop = getDesktop();
+
+ XTextDocument returnDoc = UnoRuntime.queryInterface(
+ XTextDocument.class, xDesktop.getCurrentComponent());
+
+ if (destroyLocal) {
+ closeDoc(xLocalDoc);
+ xLocalDoc = null;
+ }
+
+ return returnDoc;
+ }
+
+ public void closeFromDialog() {
+ closeDoc(xLocalDoc);
+ xLocalDoc = null;
+ }
+
+ /**
+ * creates an instance of com.sun.star.awt.Toolkit to query the
+ * XExtendedToolkit interface
+ *
+ * @return returns the gained XExtendedToolkit Interface
+ */
+ public XExtendedToolkit getToolkit() throws com.sun.star.uno.Exception {
+ Object toolkit = m_xMSF.createInstance("com.sun.star.awt.Toolkit");
+
+ XExtendedToolkit tk = UnoRuntime.queryInterface(XExtendedToolkit.class,
+ toolkit);
+
+ return tk;
+ }
+
+ /**
+ * creates an instance of com.sun.star.frame.Desktop to query the XDesktop
+ * interface
+ *
+ * @return returns the gained XDesktop interface
+ */
+ private XDesktop getDesktop() throws com.sun.star.uno.Exception {
+ Object desk = m_xMSF.createInstance("com.sun.star.frame.Desktop");
+
+ XDesktop xDesktop = UnoRuntime.queryInterface(XDesktop.class, desk);
+
+ return xDesktop;
+ }
+} \ No newline at end of file
diff --git a/sfx2/qa/complex/sfx2/undo/CalcDocumentTest.java b/sfx2/qa/complex/sfx2/undo/CalcDocumentTest.java
new file mode 100644
index 000000000..a0dec8d06
--- /dev/null
+++ b/sfx2/qa/complex/sfx2/undo/CalcDocumentTest.java
@@ -0,0 +1,113 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package complex.sfx2.undo;
+
+import org.openoffice.test.tools.SpreadsheetDocument;
+import com.sun.star.table.XCellRange;
+import com.sun.star.lang.XMultiServiceFactory;
+import com.sun.star.table.XCell;
+import com.sun.star.uno.UnoRuntime;
+import org.openoffice.test.tools.DocumentType;
+import static org.junit.Assert.*;
+
+/**
+ * implements the {@link DocumentTest} interface on top of a spreadsheet document
+ */
+public class CalcDocumentTest extends DocumentTestBase
+{
+ public CalcDocumentTest( final XMultiServiceFactory i_orb ) throws Exception
+ {
+ super( i_orb, DocumentType.CALC );
+ }
+
+ public String getDocumentDescription()
+ {
+ return "spreadsheet document";
+ }
+
+ public void initializeDocument() throws com.sun.star.uno.Exception
+ {
+ final XCell cellA1 = getCellA1();
+ cellA1.setValue( INIT_VALUE );
+ assertEquals( "initializing the cell value didn't work", cellA1.getValue(), INIT_VALUE, 0 );
+
+ XCellRange range = UnoRuntime.queryInterface( XCellRange.class,
+ ((SpreadsheetDocument)m_document).getSheet(0) );
+
+ for ( int i=0; i<12; ++i )
+ {
+ XCell cell = range.getCellByPosition( 1, i );
+ cell.setFormula( "" );
+ }
+ }
+
+ public void doSingleModification() throws com.sun.star.uno.Exception
+ {
+ final XCell cellA1 = getCellA1();
+ assertEquals( "initial cell value not as expected", INIT_VALUE, cellA1.getValue(), 0 );
+ cellA1.setValue( MODIFIED_VALUE );
+ assertEquals( "modified cell value not as expected", MODIFIED_VALUE, cellA1.getValue(), 0 );
+ }
+
+ public void verifyInitialDocumentState() throws com.sun.star.uno.Exception
+ {
+ final XCell cellA1 = getCellA1();
+ assertEquals( "cell A1 doesn't have its initial value", INIT_VALUE, cellA1.getValue(), 0 );
+
+ XCellRange range = UnoRuntime.queryInterface( XCellRange.class,
+ ((SpreadsheetDocument)m_document).getSheet(0) );
+ for ( int i=0; i<12; ++i )
+ {
+ final XCell cell = range.getCellByPosition( 1, i );
+ assertEquals( "Cell B" + (i+1) + " not having its initial value (an empty string)", "", cell.getFormula() );
+ }
+ }
+
+ public void verifySingleModificationDocumentState() throws com.sun.star.uno.Exception
+ {
+ final XCell cellA1 = getCellA1();
+ assertEquals( "cell A1 doesn't have the value which we gave it", MODIFIED_VALUE, cellA1.getValue(), 0 );
+ }
+
+ public int doMultipleModifications() throws com.sun.star.uno.Exception
+ {
+ XCellRange range = UnoRuntime.queryInterface( XCellRange.class,
+ ((SpreadsheetDocument)m_document).getSheet(0) );
+
+ final String[] months = new String[] {
+ "January", "February", "March", "April", "May", "June", "July", "August",
+ "September", "October", "November", "December" };
+ for ( int i=0; i<12; ++i )
+ {
+ final XCell cell = range.getCellByPosition( 1, i );
+ cell.setFormula( months[i] );
+ }
+ return 12;
+ }
+
+ private XCell getCellA1() throws com.sun.star.uno.Exception
+ {
+ XCellRange range = UnoRuntime.queryInterface( XCellRange.class,
+ ((SpreadsheetDocument)m_document).getSheet(0) );
+ return range.getCellByPosition( 0, 0 );
+ }
+
+ private static final double INIT_VALUE = 100.0;
+ private static final double MODIFIED_VALUE = 200.0;
+}
diff --git a/sfx2/qa/complex/sfx2/undo/ChartDocumentTest.java b/sfx2/qa/complex/sfx2/undo/ChartDocumentTest.java
new file mode 100644
index 000000000..5d5ea95b2
--- /dev/null
+++ b/sfx2/qa/complex/sfx2/undo/ChartDocumentTest.java
@@ -0,0 +1,257 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package complex.sfx2.undo;
+
+import com.sun.star.chart2.XAxis;
+import com.sun.star.chart2.XCoordinateSystem;
+import com.sun.star.chart2.XCoordinateSystemContainer;
+import com.sun.star.awt.Size;
+import com.sun.star.beans.XPropertySet;
+import com.sun.star.chart2.XChartDocument;
+import com.sun.star.chart2.XDiagram;
+import com.sun.star.container.XIndexAccess;
+import com.sun.star.document.UndoFailedException;
+import com.sun.star.document.XUndoAction;
+import com.sun.star.document.XUndoManager;
+import com.sun.star.document.XUndoManagerSupplier;
+import com.sun.star.drawing.XShape;
+import com.sun.star.embed.EmbedStates;
+import com.sun.star.embed.EmbedVerbs;
+import com.sun.star.embed.XEmbeddedObject;
+import com.sun.star.lang.IndexOutOfBoundsException;
+import com.sun.star.lang.WrappedTargetException;
+import com.sun.star.lang.XMultiServiceFactory;
+import com.sun.star.text.XTextContent;
+import com.sun.star.text.XTextRange;
+import com.sun.star.uno.UnoRuntime;
+import com.sun.star.view.XSelectionSupplier;
+import org.openoffice.test.tools.DocumentType;
+import org.openoffice.test.tools.OfficeDocument;
+import static org.junit.Assert.*;
+
+public class ChartDocumentTest implements DocumentTest
+{
+ public ChartDocumentTest( final XMultiServiceFactory i_orb ) throws com.sun.star.uno.Exception
+ {
+ m_textDocument = OfficeDocument.blankDocument( i_orb, DocumentType.WRITER );
+
+ // create an OLE shape in the document
+ final XMultiServiceFactory factory = UnoRuntime.queryInterface( XMultiServiceFactory.class, m_textDocument.getDocument() );
+ final String shapeServiceName = "com.sun.star.text.TextEmbeddedObject";
+ final XPropertySet shapeProps = UnoRuntime.queryInterface( XPropertySet.class, factory.createInstance( shapeServiceName ) );
+ shapeProps.setPropertyValue("CLSID", "12dcae26-281f-416f-a234-c3086127382e");
+
+ final XShape shape = UnoRuntime.queryInterface( XShape.class, shapeProps );
+ shape.setSize( new Size( 16000, 9000 ) );
+
+ final XTextContent chartTextContent = UnoRuntime.queryInterface( XTextContent.class, shapeProps );
+
+ final XSelectionSupplier selSupplier = UnoRuntime.queryInterface( XSelectionSupplier.class,
+ m_textDocument.getCurrentView().getController() );
+ final Object selection = selSupplier.getSelection();
+ final XTextRange textRange = getAssociatedTextRange( selection );
+ if ( textRange == null )
+ throw new RuntimeException( "can't locate a text range" );
+
+ // insert the chart
+ textRange.getText().insertTextContent(textRange, chartTextContent, false);
+
+ // retrieve the chart model
+ XChartDocument chartDoc = UnoRuntime.queryInterface( XChartDocument.class, shapeProps.getPropertyValue( "Model" ) );
+ // insert default chart for the test to use.
+ com.sun.star.chart2.XChartDocument xCD2 =
+ UnoRuntime.queryInterface(com.sun.star.chart2.XChartDocument.class, chartDoc);
+ xCD2.createDefaultChart();
+ m_chartDocument = new OfficeDocument( i_orb, chartDoc );
+
+ // actually activate the object
+ final XEmbeddedObject embeddedChart = UnoRuntime.queryInterface( XEmbeddedObject.class,
+ shapeProps.getPropertyValue( "EmbeddedObject" ) );
+ embeddedChart.doVerb( EmbedVerbs.MS_OLEVERB_SHOW );
+
+ final int state = embeddedChart.getCurrentState();
+ if ( state != EmbedStates.UI_ACTIVE )
+ fail( "unable to activate the embedded chart" );
+ }
+
+ public String getDocumentDescription()
+ {
+ return "chart document";
+ }
+
+ public void initializeDocument() throws com.sun.star.uno.Exception
+ {
+ final XPropertySet wallProperties = impl_getWallProperties();
+ wallProperties.setPropertyValue( "FillStyle", com.sun.star.drawing.FillStyle.SOLID );
+ wallProperties.setPropertyValue( "FillColor", 0x00FFFFFF );
+ }
+
+ public void closeDocument()
+ {
+ m_textDocument.close();
+ }
+
+ private XPropertySet impl_getWallProperties()
+ {
+ final XChartDocument chartDoc = UnoRuntime.queryInterface( XChartDocument.class, m_chartDocument.getDocument() );
+ final XDiagram diagram = chartDoc.getFirstDiagram();
+ final XPropertySet wallProperties = diagram.getWall();
+ return wallProperties;
+ }
+
+ private XPropertySet impl_getYAxisProperties() throws IndexOutOfBoundsException
+ {
+ XPropertySet axisProperties = null;
+ final XChartDocument chartDoc = UnoRuntime.queryInterface( XChartDocument.class, m_chartDocument.getDocument() );
+ final XDiagram diagram = chartDoc.getFirstDiagram();
+ final XCoordinateSystemContainer coordContainer = UnoRuntime.queryInterface( XCoordinateSystemContainer.class, diagram );
+ final XCoordinateSystem[] coordSystems = coordContainer.getCoordinateSystems();
+ final XCoordinateSystem coordSystem = coordSystems[0];
+ final XAxis primaryYAxis = coordSystem.getAxisByDimension( 1, 0 );
+ axisProperties = UnoRuntime.queryInterface( XPropertySet.class, primaryYAxis );
+ return axisProperties;
+ }
+
+ private XUndoManager impl_getUndoManager()
+ {
+ final XUndoManagerSupplier undoManagerSupp = UnoRuntime.queryInterface( XUndoManagerSupplier.class, m_chartDocument.getDocument() );
+ final XUndoManager undoManager = undoManagerSupp.getUndoManager();
+ return undoManager;
+ }
+
+ public void doSingleModification() throws com.sun.star.uno.Exception
+ {
+ final XPropertySet wallProperties = impl_getWallProperties();
+
+ // simulate an Undo action, as long as the chart implementation doesn't add Undo actions itself
+ final XUndoManager undoManager = impl_getUndoManager();
+ undoManager.addUndoAction( new PropertyUndoAction( wallProperties, "FillColor", 0xCCFF44 ) );
+ // (the UndoAction will actually set the property value)
+ }
+
+ public void verifyInitialDocumentState() throws com.sun.star.uno.Exception
+ {
+ final XPropertySet wallProperties = impl_getWallProperties();
+ assertEquals( 0x00FFFFFF, ((Integer)wallProperties.getPropertyValue( "FillColor" )).intValue() );
+ }
+
+ public void verifySingleModificationDocumentState() throws com.sun.star.uno.Exception
+ {
+ final XPropertySet wallProperties = impl_getWallProperties();
+ assertEquals( 0xCCFF44, ((Integer)wallProperties.getPropertyValue( "FillColor" )).intValue() );
+ }
+
+ public int doMultipleModifications() throws com.sun.star.uno.Exception
+ {
+ final XPropertySet axisProperties = impl_getYAxisProperties();
+
+ final XUndoManager undoManager = impl_getUndoManager();
+ undoManager.addUndoAction( new PropertyUndoAction( axisProperties, "LineWidth", 300 ) );
+ undoManager.addUndoAction( new PropertyUndoAction( axisProperties, "LineColor", 0x000000 ) );
+
+ return 2;
+ }
+
+ public OfficeDocument getDocument()
+ {
+ return m_chartDocument;
+ }
+
+ private XTextRange getAssociatedTextRange( final Object i_object ) throws WrappedTargetException, IndexOutOfBoundsException
+ {
+ // possible cases:
+ // 1. a container of other objects - e.g. selection of 0 to n text portions, or 1 to n drawing objects
+ final XIndexAccess indexer = UnoRuntime.queryInterface( XIndexAccess.class, i_object );
+ if ((indexer != null) && indexer.getCount() > 0) {
+ final int count = indexer.getCount();
+ for (int i = 0; i < count; ++i) {
+ final XTextRange range = getAssociatedTextRange( indexer.getByIndex(i) );
+ if (range != null) {
+ return range;
+ }
+ }
+ }
+ // 2. another TextContent, having an anchor we can use
+ final XTextContent textContent = UnoRuntime.queryInterface(XTextContent.class, i_object);
+ if (textContent != null) {
+ final XTextRange range = textContent.getAnchor();
+ if (range != null) {
+ return range;
+ }
+ }
+
+ // an object which supports XTextRange directly
+ final XTextRange range = UnoRuntime.queryInterface(XTextRange.class, i_object);
+ if (range != null) {
+ return range;
+ }
+
+ return null;
+ }
+
+ private static class PropertyUndoAction implements XUndoAction
+ {
+ PropertyUndoAction( final XPropertySet i_component, final String i_propertyName, final Object i_newValue ) throws com.sun.star.uno.Exception
+ {
+ m_component = i_component;
+ m_propertyName = i_propertyName;
+ m_newValue = i_newValue;
+
+ m_oldValue = i_component.getPropertyValue( m_propertyName );
+ i_component.setPropertyValue( m_propertyName, m_newValue );
+ }
+
+ public String getTitle()
+ {
+ return "some dummy Undo Action";
+ }
+
+ public void undo() throws UndoFailedException
+ {
+ try
+ {
+ m_component.setPropertyValue( m_propertyName, m_oldValue );
+ }
+ catch ( com.sun.star.uno.Exception ex )
+ {
+ throw new UndoFailedException( "", this, ex );
+ }
+ }
+
+ public void redo() throws UndoFailedException
+ {
+ try
+ {
+ m_component.setPropertyValue( m_propertyName, m_newValue );
+ }
+ catch ( com.sun.star.uno.Exception ex )
+ {
+ throw new UndoFailedException( "", this, ex );
+ }
+ }
+
+ private final XPropertySet m_component;
+ private final String m_propertyName;
+ private final Object m_oldValue;
+ private final Object m_newValue;
+ }
+
+ private final OfficeDocument m_textDocument;
+ private final OfficeDocument m_chartDocument;
+}
diff --git a/sfx2/qa/complex/sfx2/undo/DocumentTest.java b/sfx2/qa/complex/sfx2/undo/DocumentTest.java
new file mode 100644
index 000000000..dd5fe4cf7
--- /dev/null
+++ b/sfx2/qa/complex/sfx2/undo/DocumentTest.java
@@ -0,0 +1,77 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package complex.sfx2.undo;
+
+import org.openoffice.test.tools.OfficeDocument;
+
+/**
+ * wrapper around an OfficeDocument, for running a standardized test procedure (related do Undo functionality)
+ * on the document.
+ *
+ */
+public interface DocumentTest
+{
+ /**
+ * returns a human-readable description for the document/type which the tests operates on
+ */
+ public String getDocumentDescription();
+
+ /**
+ * initializes the document to a state where the subsequent tests can be ran
+ */
+ public void initializeDocument() throws com.sun.star.uno.Exception;
+
+ /**
+ * closes the document which the test is based on
+ */
+ public void closeDocument();
+
+ /**
+ * does a simple modification to the document, which results in one Undo action being auto-generated
+ * by the OOo implementation
+ */
+ public void doSingleModification() throws com.sun.star.uno.Exception;
+
+ /**
+ * verifies the document is in the same state as after {@link #initializeDocument}
+ */
+ public void verifyInitialDocumentState() throws com.sun.star.uno.Exception;
+
+ /**
+ * verifies the document is in the state as expected after {@link #doSingleModification}
+ */
+ public void verifySingleModificationDocumentState() throws com.sun.star.uno.Exception;
+
+ /**
+ * does multiple modifications do the document, which would normally result in multiple Undo actions.
+ *
+ * The test framework will encapsulate the call into an {@link com.sun.star.document.XUndoManager#enterUndoContext} and
+ * {@link com.sun.star.document.XUndoManager#leaveUndoContext} call.
+ *
+ * @return
+ * the number of modifications done to the document. The caller assumes (and asserts) that the number
+ * of actions on the Undo stack equals this number.
+ */
+ public int doMultipleModifications() throws com.sun.star.uno.Exception;
+
+ /**
+ * returns the document which the test operates on
+ */
+ public OfficeDocument getDocument();
+}
diff --git a/sfx2/qa/complex/sfx2/undo/DocumentTestBase.java b/sfx2/qa/complex/sfx2/undo/DocumentTestBase.java
new file mode 100644
index 000000000..d7a8c111b
--- /dev/null
+++ b/sfx2/qa/complex/sfx2/undo/DocumentTestBase.java
@@ -0,0 +1,44 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package complex.sfx2.undo;
+
+import com.sun.star.lang.XMultiServiceFactory;
+import com.sun.star.uno.Exception;
+import org.openoffice.test.tools.DocumentType;
+import org.openoffice.test.tools.OfficeDocument;
+
+abstract class DocumentTestBase implements DocumentTest
+{
+ DocumentTestBase( final XMultiServiceFactory i_orb, final DocumentType i_docType ) throws Exception
+ {
+ m_document = OfficeDocument.blankDocument( i_orb, i_docType );
+ }
+
+ public OfficeDocument getDocument()
+ {
+ return m_document;
+ }
+
+ public void closeDocument()
+ {
+ m_document.close();
+ }
+
+ protected final OfficeDocument m_document;
+}
diff --git a/sfx2/qa/complex/sfx2/undo/DrawDocumentTest.java b/sfx2/qa/complex/sfx2/undo/DrawDocumentTest.java
new file mode 100644
index 000000000..ef4ff7fa0
--- /dev/null
+++ b/sfx2/qa/complex/sfx2/undo/DrawDocumentTest.java
@@ -0,0 +1,35 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package complex.sfx2.undo;
+
+import com.sun.star.lang.XMultiServiceFactory;
+import org.openoffice.test.tools.DocumentType;
+
+public class DrawDocumentTest extends DrawingOrPresentationDocumentTest
+{
+ public DrawDocumentTest( XMultiServiceFactory i_orb ) throws com.sun.star.uno.Exception
+ {
+ super( i_orb, DocumentType.DRAWING );
+ }
+
+ public String getDocumentDescription()
+ {
+ return "drawing document";
+ }
+}
diff --git a/sfx2/qa/complex/sfx2/undo/DrawingOrPresentationDocumentTest.java b/sfx2/qa/complex/sfx2/undo/DrawingOrPresentationDocumentTest.java
new file mode 100644
index 000000000..37b5d0d9c
--- /dev/null
+++ b/sfx2/qa/complex/sfx2/undo/DrawingOrPresentationDocumentTest.java
@@ -0,0 +1,207 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package complex.sfx2.undo;
+
+import com.sun.star.awt.Rectangle;
+import com.sun.star.document.XUndoManager;
+import com.sun.star.document.XUndoManagerSupplier;
+import com.sun.star.document.XUndoAction;
+import com.sun.star.awt.Point;
+import com.sun.star.awt.Size;
+import com.sun.star.beans.XPropertySet;
+import com.sun.star.drawing.CircleKind;
+import com.sun.star.drawing.XDrawPages;
+import com.sun.star.drawing.XDrawPagesSupplier;
+import com.sun.star.drawing.XShape;
+import com.sun.star.drawing.XShapes;
+import com.sun.star.lang.XMultiServiceFactory;
+import com.sun.star.uno.UnoRuntime;
+import org.openoffice.test.tools.DocumentType;
+import static org.junit.Assert.*;
+
+/**
+ * implements the {@link DocumentTest} interface on top of a drawing document
+ */
+public abstract class DrawingOrPresentationDocumentTest extends DocumentTestBase
+{
+ public DrawingOrPresentationDocumentTest( XMultiServiceFactory i_orb, final DocumentType i_docType ) throws com.sun.star.uno.Exception
+ {
+ super( i_orb, i_docType );
+ }
+
+ public void initializeDocument() throws com.sun.star.uno.Exception
+ {
+ // remove all shapes - Impress has two default shapes in a new doc; just get rid of them
+ final XShapes firstPageShapes = getFirstPageShapes();
+ while ( firstPageShapes.getCount() > 0 )
+ firstPageShapes.remove( UnoRuntime.queryInterface( XShape.class, firstPageShapes.getByIndex( 0 ) ) );
+ }
+
+ public void doSingleModification() throws com.sun.star.uno.Exception
+ {
+ // add a simple centered shape to the first page
+ Rectangle pagePlayground = impl_getFirstPagePlayground();
+ impl_createCircleShape(
+ ( pagePlayground.X + ( pagePlayground.Width - BIG_CIRCLE_SIZE ) / 2 ),
+ ( pagePlayground.Y + ( pagePlayground.Height - BIG_CIRCLE_SIZE ) / 2 ),
+ BIG_CIRCLE_SIZE,
+ FILL_COLOR
+ );
+ }
+
+ public void verifyInitialDocumentState() throws com.sun.star.uno.Exception
+ {
+ final XShapes firstPageShapes = getFirstPageShapes();
+ assertEquals( "there should be no shapes at all", 0, firstPageShapes.getCount() );
+ }
+
+ public void verifySingleModificationDocumentState() throws com.sun.star.uno.Exception
+ {
+ final XShapes firstPageShapes = getFirstPageShapes();
+ assertEquals( "there should be one shape, not more, not less", 1, firstPageShapes.getCount() );
+
+ final Object shape = firstPageShapes.getByIndex(0);
+ verifyShapeGeometry( shape, BIG_CIRCLE_SIZE, BIG_CIRCLE_SIZE );
+ final XPropertySet shapeProps = UnoRuntime.queryInterface( XPropertySet.class, shape );
+ assertEquals( "wrong circle type", CIRCLE_TYPE.getValue(), ((CircleKind)shapeProps.getPropertyValue( "CircleKind" )).getValue() );
+ //assertEquals( "wrong circle fill color", FILL_COLOR, ((Integer)shapeProps.getPropertyValue( "FillColor" )).intValue() );
+ // disable this particular check: A bug in the drawing layer API restores the FillColor to its
+ // default value upon re-insertion. This is issue #i115080#
+ }
+
+ public int doMultipleModifications() throws com.sun.star.uno.Exception
+ {
+ // add a simple centered shape to the first page
+ Rectangle pagePlayground = impl_getFirstPagePlayground();
+ impl_createCircleShape(
+ pagePlayground.X,
+ pagePlayground.Y,
+ SMALL_CIRCLE_SIZE,
+ ALTERNATE_FILL_COLOR
+ );
+ impl_createCircleShape(
+ pagePlayground.X + pagePlayground.Width - SMALL_CIRCLE_SIZE,
+ pagePlayground.Y,
+ SMALL_CIRCLE_SIZE,
+ ALTERNATE_FILL_COLOR
+ );
+ impl_createCircleShape(
+ pagePlayground.X,
+ pagePlayground.Y + pagePlayground.Height - SMALL_CIRCLE_SIZE,
+ SMALL_CIRCLE_SIZE,
+ ALTERNATE_FILL_COLOR
+ );
+ impl_createCircleShape(
+ pagePlayground.X + pagePlayground.Width - SMALL_CIRCLE_SIZE,
+ pagePlayground.Y + pagePlayground.Height - SMALL_CIRCLE_SIZE,
+ SMALL_CIRCLE_SIZE,
+ ALTERNATE_FILL_COLOR
+ );
+ return 4;
+ }
+
+ private void impl_createCircleShape( final int i_x, final int i_y, final int i_size, final int i_color ) throws com.sun.star.uno.Exception
+ {
+ final XPropertySet shapeProps = getDocument().createInstance( "com.sun.star.drawing.EllipseShape", XPropertySet.class );
+ shapeProps.setPropertyValue( "CircleKind", CIRCLE_TYPE );
+ shapeProps.setPropertyValue( "FillColor", i_color );
+
+ final XShape shape = UnoRuntime.queryInterface( XShape.class, shapeProps );
+ final Size shapeSize = new Size( i_size, i_size );
+ shape.setSize( shapeSize );
+ final Point shapePos = new Point( i_x, i_y );
+ shape.setPosition( shapePos );
+
+ final XShapes pageShapes = UnoRuntime.queryInterface( XShapes.class, getFirstPageShapes() );
+ pageShapes.add( shape );
+
+ // Sadly, Draw/Impress currently do not create Undo actions for programmatic changes to the document.
+ // Which renders the test here slightly useless ... unless we fake the Undo actions ourself.
+ final XUndoManagerSupplier suppUndoManager = UnoRuntime.queryInterface( XUndoManagerSupplier.class, getDocument().getDocument() );
+ final XUndoManager undoManager = suppUndoManager.getUndoManager();
+ undoManager.addUndoAction( new ShapeInsertionUndoAction( shape, pageShapes ) );
+ }
+
+ private Rectangle impl_getFirstPagePlayground() throws com.sun.star.uno.Exception
+ {
+ final XShapes firstPageShapes = getFirstPageShapes();
+ final XPropertySet firstPageProps = UnoRuntime.queryInterface( XPropertySet.class, firstPageShapes );
+ final int pageWidth = ((Integer)firstPageProps.getPropertyValue( "Width" )).intValue();
+ final int pageHeight = ((Integer)firstPageProps.getPropertyValue( "Height" )).intValue();
+ final int borderLeft = ((Integer)firstPageProps.getPropertyValue( "BorderLeft" )).intValue();
+ final int borderTop = ((Integer)firstPageProps.getPropertyValue( "BorderTop" )).intValue();
+ final int borderRight = ((Integer)firstPageProps.getPropertyValue( "BorderRight" )).intValue();
+ final int borderBottom = ((Integer)firstPageProps.getPropertyValue( "BorderBottom" )).intValue();
+ return new Rectangle( borderLeft, borderTop, pageWidth - borderLeft - borderRight, pageHeight - borderTop - borderBottom );
+ }
+
+ /**
+ * returns the XShapes interface of the first page of our drawing document
+ */
+ private XShapes getFirstPageShapes() throws com.sun.star.uno.Exception
+ {
+ final XDrawPagesSupplier suppPages = UnoRuntime.queryInterface( XDrawPagesSupplier.class, getDocument().getDocument() );
+ final XDrawPages pages = suppPages.getDrawPages();
+ return UnoRuntime.queryInterface( XShapes.class, pages.getByIndex( 0 ) );
+ }
+
+ /**
+ * verifies the given shape has the given size
+ */
+ private void verifyShapeGeometry( final Object i_shapeObject, final int i_expectedWidth, final int i_expectedHeight )
+ {
+ final XShape shape = UnoRuntime.queryInterface( XShape.class, i_shapeObject );
+ final Size shapeSize = shape.getSize();
+ assertEquals( "unexpected shape width", i_expectedWidth, shapeSize.Width );
+ assertEquals( "unexpected shape height", i_expectedHeight, shapeSize.Height );
+ }
+
+ private static class ShapeInsertionUndoAction implements XUndoAction
+ {
+ ShapeInsertionUndoAction( final XShape i_shape, final XShapes i_shapeCollection )
+ {
+ m_shape = i_shape;
+ m_shapeCollection = i_shapeCollection;
+ }
+
+ public String getTitle()
+ {
+ return "insert shape";
+ }
+
+ public void undo()
+ {
+ m_shapeCollection.remove( m_shape );
+ }
+
+ public void redo()
+ {
+ m_shapeCollection.add( m_shape );
+ }
+
+ private final XShape m_shape;
+ private final XShapes m_shapeCollection;
+ }
+
+ private static CircleKind CIRCLE_TYPE = CircleKind.FULL;
+ private static int FILL_COLOR = 0xCC2244;
+ private static int ALTERNATE_FILL_COLOR = 0x44CC22;
+ private static int BIG_CIRCLE_SIZE = 5000;
+ private static int SMALL_CIRCLE_SIZE = 2000;
+}
diff --git a/sfx2/qa/complex/sfx2/undo/ImpressDocumentTest.java b/sfx2/qa/complex/sfx2/undo/ImpressDocumentTest.java
new file mode 100644
index 000000000..8f9cb5771
--- /dev/null
+++ b/sfx2/qa/complex/sfx2/undo/ImpressDocumentTest.java
@@ -0,0 +1,35 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package complex.sfx2.undo;
+
+import com.sun.star.lang.XMultiServiceFactory;
+import org.openoffice.test.tools.DocumentType;
+
+public class ImpressDocumentTest extends DrawingOrPresentationDocumentTest
+{
+ public ImpressDocumentTest( XMultiServiceFactory i_orb ) throws com.sun.star.uno.Exception
+ {
+ super( i_orb, DocumentType.PRESENTATION );
+ }
+
+ public String getDocumentDescription()
+ {
+ return "presentation document";
+ }
+}
diff --git a/sfx2/qa/complex/sfx2/undo/WriterDocumentTest.java b/sfx2/qa/complex/sfx2/undo/WriterDocumentTest.java
new file mode 100644
index 000000000..624c2d7d2
--- /dev/null
+++ b/sfx2/qa/complex/sfx2/undo/WriterDocumentTest.java
@@ -0,0 +1,121 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package complex.sfx2.undo;
+
+import com.sun.star.text.XTextRange;
+import com.sun.star.beans.XPropertySet;
+import com.sun.star.table.XCell;
+import com.sun.star.table.XCellRange;
+import com.sun.star.text.XTextCursor;
+import com.sun.star.text.XTextTable;
+import com.sun.star.text.XText;
+import com.sun.star.text.XTextDocument;
+import com.sun.star.lang.XMultiServiceFactory;
+import com.sun.star.uno.UnoRuntime;
+import org.openoffice.test.tools.DocumentType;
+import static org.junit.Assert.*;
+
+/**
+ * implements the {@link DocumentTest} interface on top of a spreadsheet document
+ */
+public class WriterDocumentTest extends DocumentTestBase
+{
+ public WriterDocumentTest( final XMultiServiceFactory i_orb ) throws com.sun.star.uno.Exception
+ {
+ super( i_orb, DocumentType.WRITER );
+ }
+
+ public String getDocumentDescription()
+ {
+ return "text document";
+ }
+
+ public void initializeDocument() throws com.sun.star.uno.Exception
+ {
+ // TODO?
+ }
+
+ public void doSingleModification() throws com.sun.star.uno.Exception
+ {
+ final XTextDocument textDoc = UnoRuntime.queryInterface( XTextDocument.class, getDocument().getDocument() );
+ final XText docText = textDoc.getText();
+ docText.setString( s_blindText );
+ }
+
+ public void verifyInitialDocumentState() throws com.sun.star.uno.Exception
+ {
+ final XTextDocument textDoc = UnoRuntime.queryInterface( XTextDocument.class, getDocument().getDocument() );
+ final XText docText = textDoc.getText();
+ assertEquals( "document should be empty", "", docText.getString() );
+ }
+
+ public void verifySingleModificationDocumentState() throws com.sun.star.uno.Exception
+ {
+ final XTextDocument textDoc = UnoRuntime.queryInterface( XTextDocument.class, getDocument().getDocument() );
+ final XText docText = textDoc.getText();
+ assertEquals( "blind text not found", s_blindText, docText.getString() );
+ }
+
+ public int doMultipleModifications() throws com.sun.star.uno.Exception
+ {
+ final XTextDocument textDoc = UnoRuntime.queryInterface( XTextDocument.class, getDocument().getDocument() );
+ final XText docText = textDoc.getText();
+
+ int expectedUndoActions = 0;
+
+ // create a cursor
+ final XTextCursor cursor = docText.createTextCursor();
+
+ // create a table
+ final XTextTable textTable = UnoRuntime.queryInterface( XTextTable.class,
+ getDocument().createInstance( "com.sun.star.text.TextTable" ) );
+ textTable.initialize( 3, 3 );
+ final XPropertySet tableProps = UnoRuntime.queryInterface( XPropertySet.class, textTable );
+ tableProps.setPropertyValue( "BackColor", 0xCCFF44 );
+
+ // insert the table into the doc
+ docText.insertTextContent( cursor, textTable, false );
+ ++expectedUndoActions; //FIXME this will create 2 actions! currently the event is sent for every individual action; should it be sent for top-level actions only? how many internal actions are created is an implementation detail!
+ ++expectedUndoActions;
+
+ // write some content into the center cell
+ final XCellRange cellRange = UnoRuntime.queryInterface( XCellRange.class, textTable );
+ final XCell centerCell = cellRange.getCellByPosition( 1, 1 );
+ final XTextRange cellText = UnoRuntime.queryInterface( XTextRange.class, centerCell );
+ cellText.setString( "Undo Manager API Test" );
+ ++expectedUndoActions;
+
+ // give it another color
+ final XPropertySet cellProps = UnoRuntime.queryInterface( XPropertySet.class, centerCell );
+ cellProps.setPropertyValue( "BackColor", 0x44CCFF );
+ ++expectedUndoActions;
+
+ return expectedUndoActions;
+ }
+
+ private static final String s_blindText =
+ "Lorem ipsum dolor. Sit amet penatibus. A cum turpis. Aenean ac eu. " +
+ "Ligula est urna nulla vestibulum ullamcorper. Nec sit in amet tincidunt mus. " +
+ "Tellus sagittis mi. Suscipit cursus in vestibulum in eros ipsum felis cursus lectus " +
+ "nunc quis condimentum in risus nec wisi aenean luctus hendrerit magna habitasse commodo orci. " +
+ "Nisl etiam quis. Vestibulum justo eleifend aliquet luctus sed turpis volutpat ullamcorper " +
+ "aliquam penatibus sagittis pede tincidunt egestas. Nibh massa lectus. Sem mattis purus morbi " +
+ "scelerisque turpis donec urna phasellus. Quis at lacus. Viverra mauris mollis. " +
+ "Dolor tincidunt condimentum.";
+}
diff --git a/sfx2/qa/cppunit/data/reload-page.odg b/sfx2/qa/cppunit/data/reload-page.odg
new file mode 100644
index 000000000..0e9cf0864
--- /dev/null
+++ b/sfx2/qa/cppunit/data/reload-page.odg
Binary files differ
diff --git a/sfx2/qa/cppunit/doc.cxx b/sfx2/qa/cppunit/doc.cxx
new file mode 100644
index 000000000..7e62652b5
--- /dev/null
+++ b/sfx2/qa/cppunit/doc.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/.
+ */
+
+#include <test/bootstrapfixture.hxx>
+#include <unotest/macros_test.hxx>
+
+#include <com/sun/star/frame/Desktop.hpp>
+#include <com/sun/star/view/XSelectionSupplier.hpp>
+#include <com/sun/star/drawing/XDrawPagesSupplier.hpp>
+
+#include <comphelper/propertyvalue.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/sfxbasemodel.hxx>
+#include <osl/file.hxx>
+
+using namespace com::sun::star;
+
+namespace
+{
+/// Covers sfx2/source/doc/ fixes.
+class Test : public test::BootstrapFixture, public unotest::MacrosTest
+{
+private:
+ uno::Reference<lang::XComponent> mxComponent;
+
+public:
+ void setUp() override;
+ void tearDown() override;
+ uno::Reference<lang::XComponent>& getComponent() { return mxComponent; }
+};
+
+void Test::setUp()
+{
+ test::BootstrapFixture::setUp();
+
+ mxDesktop.set(frame::Desktop::create(mxComponentContext));
+}
+
+void Test::tearDown()
+{
+ if (mxComponent.is())
+ mxComponent->dispose();
+
+ test::BootstrapFixture::tearDown();
+}
+
+CPPUNIT_TEST_FIXTURE(Test, testNoGrabBagShape)
+{
+ // Load a document and select the first shape.
+ css::uno::Sequence<css::beans::PropertyValue> aArgs{ comphelper::makePropertyValue("ReadOnly",
+ true) };
+ getComponent() = loadFromDesktop("private:factory/simpress", "", aArgs);
+ uno::Reference<frame::XModel> xModel(getComponent(), uno::UNO_QUERY);
+ uno::Reference<drawing::XDrawPagesSupplier> xDrawPagesSupplier(xModel, uno::UNO_QUERY);
+ uno::Reference<container::XIndexAccess> xDrawPage(
+ xDrawPagesSupplier->getDrawPages()->getByIndex(0), uno::UNO_QUERY);
+ uno::Any aShape = xDrawPage->getByIndex(0);
+ uno::Reference<view::XSelectionSupplier> xController(xModel->getCurrentController(),
+ uno::UNO_QUERY);
+ xController->select(aShape);
+
+ // See if it has a signing certificate associated.
+ auto pBaseModel = dynamic_cast<SfxBaseModel*>(xModel.get());
+ CPPUNIT_ASSERT(pBaseModel);
+ SfxObjectShell* pObjectShell = pBaseModel->GetObjectShell();
+
+ // Without the accompanying fix in place, this test would have failed with:
+ // An uncaught exception of type com.sun.star.beans.UnknownPropertyException
+ // which was not caught later, resulting in a crash.
+ pObjectShell->GetSignPDFCertificate();
+}
+
+CPPUNIT_TEST_FIXTURE(Test, testTempFilePath)
+{
+ // Create a test file in a directory that contains the URL-encoded "testÿ" string.
+ getComponent() = loadFromDesktop("private:factory/swriter");
+ auto pBaseModel = dynamic_cast<SfxBaseModel*>(getComponent().get());
+ CPPUNIT_ASSERT(pBaseModel);
+ OUString aTargetDir
+ = m_directories.getURLFromWorkdir(u"CppunitTest/sfx2_doc.test.user/test%25C3%25Bf");
+ osl::Directory::create(aTargetDir);
+ OUString aTargetFile = aTargetDir + "/test.odt";
+ css::uno::Sequence<css::beans::PropertyValue> aArgs{ comphelper::makePropertyValue(
+ "FilterName", OUString("writer8")) };
+ pBaseModel->storeAsURL(aTargetFile, aArgs);
+ getComponent()->dispose();
+
+ // Load it and export to PDF.
+ getComponent() = loadFromDesktop(aTargetFile);
+ pBaseModel = dynamic_cast<SfxBaseModel*>(getComponent().get());
+ OUString aPdfTarget = aTargetDir + "/test.pdf";
+ css::uno::Sequence<css::beans::PropertyValue> aPdfArgs{ comphelper::makePropertyValue(
+ "FilterName", OUString("writer_pdf_Export")) };
+ // Without the accompanying fix in place, this test would have failed on Windows with:
+ // An uncaught exception of type com.sun.star.io.IOException
+ // because we first tried to create a temp file next to test.odt in a directory named
+ // "test%25C3%25Bf" instead of a directory named "test%C3%Bf".
+ pBaseModel->storeToURL(aPdfTarget, aPdfArgs);
+}
+}
+
+CPPUNIT_PLUGIN_IMPLEMENT();
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/qa/cppunit/misc/hello.odt b/sfx2/qa/cppunit/misc/hello.odt
new file mode 100644
index 000000000..23ce6a4db
--- /dev/null
+++ b/sfx2/qa/cppunit/misc/hello.odt
Binary files differ
diff --git a/sfx2/qa/cppunit/test_classification.cxx b/sfx2/qa/cppunit/test_classification.cxx
new file mode 100644
index 000000000..45d294788
--- /dev/null
+++ b/sfx2/qa/cppunit/test_classification.cxx
@@ -0,0 +1,126 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 <test/bootstrapfixture.hxx>
+#include <unotest/macros_test.hxx>
+
+#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
+#include <com/sun/star/frame/Desktop.hpp>
+#include <com/sun/star/frame/DispatchHelper.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+
+#include <comphelper/processfactory.hxx>
+#include <comphelper/propertysequence.hxx>
+
+using namespace ::com::sun::star;
+
+namespace
+{
+
+/// Tests the handling of the .uno:ClassificationApply command in various applications.
+class ClassificationTest : public test::BootstrapFixture, public unotest::MacrosTest
+{
+ uno::Reference<lang::XComponent> mxComponent;
+ void testClassification();
+
+public:
+ virtual void setUp() override;
+ virtual void tearDown() override;
+ void testWriter();
+ void testCalc();
+ void testImpress();
+
+ CPPUNIT_TEST_SUITE(ClassificationTest);
+ CPPUNIT_TEST(testWriter);
+ CPPUNIT_TEST(testCalc);
+ CPPUNIT_TEST(testImpress);
+ CPPUNIT_TEST_SUITE_END();
+};
+
+void ClassificationTest::setUp()
+{
+ test::BootstrapFixture::setUp();
+
+ mxDesktop.set(frame::Desktop::create(mxComponentContext));
+}
+
+void ClassificationTest::tearDown()
+{
+ if (mxComponent.is())
+ mxComponent->dispose();
+
+ test::BootstrapFixture::tearDown();
+}
+
+void ClassificationTest::testClassification()
+{
+ uno::Sequence<beans::PropertyValue> aPropertyValues(comphelper::InitPropertySequence(
+ {
+ {"Name", uno::Any(OUString("Non-Business"))},
+ {"Type", uno::Any(OUString("urn:bails:ExportControl:"))},
+ }));
+ dispatchCommand(mxComponent, ".uno:ClassificationApply", aPropertyValues);
+
+ uno::Reference<document::XDocumentPropertiesSupplier> xDocumentPropertiesSupplier(mxComponent, uno::UNO_QUERY);
+ CPPUNIT_ASSERT(xDocumentPropertiesSupplier.is());
+ uno::Reference<document::XDocumentProperties> xDocumentProperties = xDocumentPropertiesSupplier->getDocumentProperties();
+ uno::Reference<beans::XPropertySet> xPropertySet(xDocumentProperties->getUserDefinedProperties(), uno::UNO_QUERY);
+ uno::Any aAny = xPropertySet->getPropertyValue("urn:bails:ExportControl:BusinessAuthorizationCategory:Identifier");
+ CPPUNIT_ASSERT_EQUAL(OUString("urn:example:tscp:1:non-business"), aAny.get<OUString>());
+
+ aPropertyValues = comphelper::InitPropertySequence(
+ {
+ {"Name", uno::Any(OUString("Confidential"))},
+ {"Type", uno::Any(OUString("urn:bails:NationalSecurity:"))},
+ });
+ dispatchCommand(mxComponent, ".uno:ClassificationApply", aPropertyValues);
+ aAny = xPropertySet->getPropertyValue("urn:bails:NationalSecurity:BusinessAuthorizationCategory:Identifier");
+ CPPUNIT_ASSERT_EQUAL(OUString("urn:example:tscp:1:confidential"), aAny.get<OUString>());
+
+ aPropertyValues = comphelper::InitPropertySequence(
+ {
+ {"Name", uno::Any(OUString("Internal Only"))},
+ {"Type", uno::Any(OUString("urn:bails:IntellectualProperty:"))},
+ });
+ dispatchCommand(mxComponent, ".uno:ClassificationApply", aPropertyValues);
+ aAny = xPropertySet->getPropertyValue("urn:bails:IntellectualProperty:BusinessAuthorizationCategory:Identifier");
+ CPPUNIT_ASSERT_EQUAL(OUString("urn:example:tscp:1:internal-only"), aAny.get<OUString>());
+}
+
+void ClassificationTest::testWriter()
+{
+ // Test SID_CLASSIFICATION_APPLY handling in SwDocShell::Execute().
+ mxComponent = loadFromDesktop("private:factory/swriter", "com.sun.star.text.TextDocument");
+ // This resulted in a beans::UnknownPropertyException when the request wasn't handled.
+ testClassification();
+}
+
+void ClassificationTest::testCalc()
+{
+ // Test SID_CLASSIFICATION_APPLY handling in ScFormatShell::ExecuteStyle().
+ mxComponent = loadFromDesktop("private:factory/scalc", "com.sun.star.sheet.SpreadsheetDocument");
+ // This resulted in a beans::UnknownPropertyException when the request wasn't handled.
+ testClassification();
+}
+
+void ClassificationTest::testImpress()
+{
+ // Test SID_CLASSIFICATION_APPLY handling in sd::DrawViewShell::FuTemporary().
+ mxComponent = loadFromDesktop("private:factory/simpress", "com.sun.star.presentation.PresentationDocument");
+ // This resulted in a beans::UnknownPropertyException when the request wasn't handled.
+ testClassification();
+}
+
+CPPUNIT_TEST_SUITE_REGISTRATION(ClassificationTest);
+
+}
+
+CPPUNIT_PLUGIN_IMPLEMENT();
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/qa/cppunit/test_controlleritem.cxx b/sfx2/qa/cppunit/test_controlleritem.cxx
new file mode 100644
index 000000000..75d220536
--- /dev/null
+++ b/sfx2/qa/cppunit/test_controlleritem.cxx
@@ -0,0 +1,58 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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/types.h>
+#include <cppunit/TestAssert.h>
+#include <cppunit/TestFixture.h>
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/plugin/TestPlugIn.h>
+
+#include <sfx2/ctrlitem.hxx>
+
+namespace {
+
+class ControllerItemTest
+ : public ::CppUnit::TestFixture
+{
+public:
+ void test();
+
+ CPPUNIT_TEST_SUITE(ControllerItemTest);
+ CPPUNIT_TEST(test);
+ CPPUNIT_TEST_SUITE_END();
+
+private:
+};
+
+bool bDeleted = false;
+
+class FooController : public SfxControllerItem {
+public:
+ FooController() : SfxControllerItem() {}
+ virtual ~FooController() override { bDeleted = true; }
+};
+
+void ControllerItemTest::test()
+{
+ FooController *pController(new FooController());
+
+ // TESTME: binding, un-binding, re-binding, IsBound, SetId etc.
+
+ pController->dispose();
+ delete pController;
+ CPPUNIT_ASSERT( bDeleted );
+}
+
+CPPUNIT_TEST_SUITE_REGISTRATION(ControllerItemTest);
+
+}
+
+CPPUNIT_PLUGIN_IMPLEMENT();
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/qa/cppunit/test_metadatable.cxx b/sfx2/qa/cppunit/test_metadatable.cxx
new file mode 100644
index 000000000..459f635d0
--- /dev/null
+++ b/sfx2/qa/cppunit/test_metadatable.cxx
@@ -0,0 +1,248 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this 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 <cppunit/TestAssert.h>
+#include <cppunit/TestFixture.h>
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/plugin/TestPlugIn.h>
+
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+
+#include <sfx2/Metadatable.hxx>
+#include <sfx2/XmlIdRegistry.hxx>
+
+#include <memory>
+
+
+using namespace ::com::sun::star;
+
+
+namespace {
+
+class MetadatableTest
+ : public ::CppUnit::TestFixture
+{
+public:
+ void test();
+
+ CPPUNIT_TEST_SUITE(MetadatableTest);
+ CPPUNIT_TEST(test);
+ CPPUNIT_TEST_SUITE_END();
+
+private:
+};
+
+class MockMetadatable
+ : public ::sfx2::Metadatable
+{
+private:
+ ::sfx2::IXmlIdRegistry & m_rRegistry;
+
+public:
+ MockMetadatable(::sfx2::IXmlIdRegistry & i_rReg,
+ bool const i_isInClip = false)
+ : m_rRegistry(i_rReg)
+ , m_bInClipboard(i_isInClip), m_bInUndo(false), m_bInContent(true) {}
+ bool m_bInClipboard;
+ bool m_bInUndo;
+ bool m_bInContent;
+ virtual bool IsInClipboard() const override { return m_bInClipboard; }
+ virtual bool IsInUndo() const override { return m_bInUndo; }
+ virtual bool IsInContent() const override { return m_bInContent; }
+ virtual ::sfx2::IXmlIdRegistry& GetRegistry() override { return m_rRegistry; }
+ virtual css::uno::Reference< css::rdf::XMetadatable > MakeUnoObject() override { return nullptr; }
+};
+
+void MetadatableTest::test()
+{
+ std::unique_ptr< ::sfx2::IXmlIdRegistry > const pReg(
+ ::sfx2::createXmlIdRegistry(false) );
+ std::unique_ptr< ::sfx2::IXmlIdRegistry > const pRegClip(
+ ::sfx2::createXmlIdRegistry(true) );
+
+ MockMetadatable m1(*pReg);
+ MockMetadatable m2(*pReg);
+ MockMetadatable m3(*pReg);
+ MockMetadatable m4(*pReg);
+ MockMetadatable m5(*pReg);
+ OUString empty;
+ OUString content( "content.xml" );
+ OUString sid3( "id3" );
+ OUString sid4( "id4" );
+ beans::StringPair id1(content, "id1");
+ beans::StringPair id2(content, "id2");
+ beans::StringPair id3(content, sid3);
+ beans::StringPair id4("styles.xml", sid4);
+ beans::StringPair id3e(empty, sid3);
+ beans::StringPair id4e(empty, sid4);
+ m1.SetMetadataReference(id1);
+ CPPUNIT_ASSERT_MESSAGE("set failed", bool(m1.GetMetadataReference() == id1));
+ try {
+ m2.SetMetadataReference(id1);
+ CPPUNIT_ASSERT_MESSAGE("set duplicate succeeded", false);
+ } catch (const lang::IllegalArgumentException &) { }
+ m1.SetMetadataReference(id1);
+ CPPUNIT_ASSERT_MESSAGE("set failed (existing)",
+ bool(m1.GetMetadataReference() == id1));
+ m1.EnsureMetadataReference();
+ CPPUNIT_ASSERT_MESSAGE("ensure failed (existing)",
+ bool(m1.GetMetadataReference() == id1));
+
+ m2.EnsureMetadataReference();
+ beans::StringPair m2id(m2.GetMetadataReference());
+ CPPUNIT_ASSERT_MESSAGE("ensure failed", !m2id.Second.isEmpty());
+ m2.EnsureMetadataReference();
+ CPPUNIT_ASSERT_MESSAGE("ensure failed (idempotent)",
+ bool(m2.GetMetadataReference() == m2id));
+
+ m1.m_bInUndo = true;
+ CPPUNIT_ASSERT_MESSAGE("move to undo failed",
+ m1.GetMetadataReference().Second.isEmpty());
+
+ m1.m_bInUndo = false;
+ CPPUNIT_ASSERT_MESSAGE("move from undo failed",
+ bool(m1.GetMetadataReference() == id1));
+
+ m1.m_bInUndo = true;
+ try {
+ m2.SetMetadataReference(id1); // steal!
+ } catch (lang::IllegalArgumentException &) {
+ CPPUNIT_FAIL("set duplicate to undo failed");
+ }
+ m1.m_bInUndo = false;
+ CPPUNIT_ASSERT_MESSAGE("move from undo: duplicate",
+ m1.GetMetadataReference().Second.isEmpty());
+
+ m3.RegisterAsCopyOf(m2);
+ CPPUNIT_ASSERT_MESSAGE("copy: source", bool(m2.GetMetadataReference() == id1));
+ CPPUNIT_ASSERT_MESSAGE("copy: duplicate",
+ m3.GetMetadataReference().Second.isEmpty());
+ m4.RegisterAsCopyOf(m3);
+ CPPUNIT_ASSERT_MESSAGE("copy: source", bool(m2.GetMetadataReference() == id1));
+ CPPUNIT_ASSERT_MESSAGE("copy: duplicate",
+ m3.GetMetadataReference().Second.isEmpty());
+ CPPUNIT_ASSERT_MESSAGE("copy: duplicate",
+ m4.GetMetadataReference().Second.isEmpty());
+ m2.m_bInUndo = true;
+ CPPUNIT_ASSERT_MESSAGE("duplicate to undo",
+ bool(m3.GetMetadataReference() == id1));
+ CPPUNIT_ASSERT_MESSAGE("duplicate to undo",
+ m2.GetMetadataReference().Second.isEmpty());
+ m2.m_bInUndo = false;
+ CPPUNIT_ASSERT_MESSAGE("duplicate from undo",
+ bool(m2.GetMetadataReference() == id1));
+ CPPUNIT_ASSERT_MESSAGE("duplicate from undo",
+ m3.GetMetadataReference().Second.isEmpty());
+
+ m4.EnsureMetadataReference(); // new!
+ beans::StringPair m4id(m4.GetMetadataReference());
+ CPPUNIT_ASSERT_MESSAGE("ensure on duplicate",
+ !m4id.Second.isEmpty());
+ CPPUNIT_ASSERT_MESSAGE("ensure on duplicate",
+ !(m4id == id1));
+
+ MockMetadatable mc1(*pRegClip, true); // in clipboard
+ MockMetadatable mc2(*pRegClip, true);
+ MockMetadatable mc3(*pRegClip, true);
+ MockMetadatable mc4(*pRegClip, true);
+ MockMetadatable m2p(*pReg);
+ MockMetadatable m3p(*pReg);
+
+ mc1.SetMetadataReference(id2);
+ CPPUNIT_ASSERT_MESSAGE("set failed", bool(mc1.GetMetadataReference() == id2));
+ try {
+ mc2.SetMetadataReference(id2);
+ CPPUNIT_FAIL("set duplicate succeeded");
+ } catch (const lang::IllegalArgumentException &) { }
+ mc1.SetMetadataReference(id2);
+ CPPUNIT_ASSERT_MESSAGE("set failed (existing)",
+ bool(mc1.GetMetadataReference() == id2));
+ mc1.EnsureMetadataReference();
+ CPPUNIT_ASSERT_MESSAGE("ensure failed (existing)",
+ bool(mc1.GetMetadataReference() == id2));
+ mc2.EnsureMetadataReference();
+ beans::StringPair mc2id(mc2.GetMetadataReference());
+ CPPUNIT_ASSERT_MESSAGE("ensure failed", !mc2id.Second.isEmpty());
+ mc2.EnsureMetadataReference();
+ CPPUNIT_ASSERT_MESSAGE("ensure failed (idempotent)",
+ bool(mc2.GetMetadataReference() == mc2id));
+ mc2.RemoveMetadataReference();
+ CPPUNIT_ASSERT_MESSAGE("remove failed",
+ mc2.GetMetadataReference().Second.isEmpty());
+
+ // set up mc2 as copy of m2 and mc3 as copy of m3
+ mc3.RegisterAsCopyOf(m3);
+ CPPUNIT_ASSERT_MESSAGE("copy to clipboard (latent)",
+ mc3.GetMetadataReference().Second.isEmpty() );
+ mc2.RegisterAsCopyOf(m2);
+ CPPUNIT_ASSERT_MESSAGE("copy to clipboard (non-latent)",
+ bool(mc2.GetMetadataReference() == id1));
+ // paste mc2 to m2p and mc3 to m3p
+ m2p.RegisterAsCopyOf(mc2);
+ CPPUNIT_ASSERT_MESSAGE("paste from clipboard (non-latent)",
+ m2p.GetMetadataReference().Second.isEmpty() );
+ m3p.RegisterAsCopyOf(mc3);
+ CPPUNIT_ASSERT_MESSAGE("paste from clipboard (latent)",
+ m3p.GetMetadataReference().Second.isEmpty() );
+ // delete m2, m2p, m3
+ m2.RemoveMetadataReference();
+ CPPUNIT_ASSERT_MESSAGE("remove failed",
+ m2.GetMetadataReference().Second.isEmpty());
+ CPPUNIT_ASSERT_MESSAGE("paste-remove (non-latent)",
+ bool(m2p.GetMetadataReference() == id1));
+ m2p.RemoveMetadataReference();
+ CPPUNIT_ASSERT_MESSAGE("remove failed",
+ m2p.GetMetadataReference().Second.isEmpty());
+ CPPUNIT_ASSERT_MESSAGE("paste-remove2 (non-latent)",
+ bool(m3.GetMetadataReference() == id1));
+ m3.RemoveMetadataReference();
+ CPPUNIT_ASSERT_MESSAGE("remove failed",
+ m3.GetMetadataReference().Second.isEmpty());
+ CPPUNIT_ASSERT_MESSAGE("paste-remove (latent)",
+ bool(m3p.GetMetadataReference() == id1));
+ // delete mc2
+ mc2.SetMetadataReference(beans::StringPair());
+ CPPUNIT_ASSERT_MESSAGE("in clipboard becomes non-latent",
+ mc3.GetMetadataReference().Second.isEmpty() );
+ // paste mc2
+ m2p.RegisterAsCopyOf(mc2);
+ CPPUNIT_ASSERT_MESSAGE("remove-paste",
+ m2p.GetMetadataReference().Second.isEmpty());
+ CPPUNIT_ASSERT_MESSAGE("remove-paste (stolen)",
+ bool(m3p.GetMetadataReference() == id1));
+
+ // auto-detect stream
+ m5.SetMetadataReference(id3e);
+ CPPUNIT_ASSERT_MESSAGE("auto-detect (content)",
+ bool(m5.GetMetadataReference() == id3));
+ m5.m_bInContent = false;
+ m5.SetMetadataReference(id4e);
+ CPPUNIT_ASSERT_MESSAGE("auto-detect (styles)",
+ bool(m5.GetMetadataReference() == id4));
+}
+
+
+CPPUNIT_TEST_SUITE_REGISTRATION(MetadatableTest);
+
+}
+
+CPPUNIT_PLUGIN_IMPLEMENT();
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/qa/cppunit/test_misc.cxx b/sfx2/qa/cppunit/test_misc.cxx
new file mode 100644
index 000000000..1bf90cfbc
--- /dev/null
+++ b/sfx2/qa/cppunit/test_misc.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/.
+ */
+
+#include <sal/types.h>
+
+#ifndef _WIN32
+#include <sys/stat.h>
+#include <unistd.h>
+#endif
+#include <memory>
+
+#include <cppunit/TestAssert.h>
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/plugin/TestPlugIn.h>
+
+#include <com/sun/star/beans/PropertyState.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/document/DocumentProperties.hpp>
+#include <com/sun/star/packages/zip/ZipFileAccess.hpp>
+#include <com/sun/star/frame/Desktop.hpp>
+#include <com/sun/star/frame/XStorable.hpp>
+
+#include <test/bootstrapfixture.hxx>
+#include <test/xmltesttools.hxx>
+#include <unotest/macros_test.hxx>
+
+#include <unotools/ucbstreamhelper.hxx>
+#include <comphelper/propertysequence.hxx>
+#include <comphelper/processfactory.hxx>
+#include <sfx2/app.hxx>
+#include <osl/file.hxx>
+
+
+using namespace ::com::sun::star;
+
+
+namespace {
+
+class MiscTest
+ : public test::BootstrapFixture
+ , public unotest::MacrosTest
+ , public XmlTestTools
+{
+public:
+ virtual void setUp() override;
+
+ virtual void registerNamespaces(xmlXPathContextPtr& pXmlXpathCtx) override
+ {
+ // ODF
+ xmlXPathRegisterNs(pXmlXpathCtx, BAD_CAST("office"), BAD_CAST("urn:oasis:names:tc:opendocument:xmlns:office:1.0"));
+ xmlXPathRegisterNs(pXmlXpathCtx, BAD_CAST("meta"), BAD_CAST("urn:oasis:names:tc:opendocument:xmlns:meta:1.0"));
+ xmlXPathRegisterNs(pXmlXpathCtx, BAD_CAST("dc"), BAD_CAST("http://purl.org/dc/elements/1.1/"));
+ // used in testCustomMetadata
+ xmlXPathRegisterNs(pXmlXpathCtx, BAD_CAST("foo"), BAD_CAST("http://foo.net"));
+ xmlXPathRegisterNs(pXmlXpathCtx, BAD_CAST("baz"), BAD_CAST("http://baz.net"));
+ }
+};
+
+void MiscTest::setUp()
+{
+ m_xContext = comphelper::getProcessComponentContext();
+ mxDesktop.set(frame::Desktop::create(m_xContext));
+ SfxApplication::GetOrCreate();
+}
+
+CPPUNIT_TEST_FIXTURE(MiscTest, testODFCustomMetadata)
+{
+ uno::Reference<document::XDocumentProperties> const xProps(
+ ::com::sun::star::document::DocumentProperties::create(m_xContext));
+
+ OUString const url(m_directories.getURLFromSrc(u"/sfx2/qa/complex/sfx2/testdocuments/CUSTOM.odt"));
+ xProps->loadFromMedium(url, uno::Sequence<beans::PropertyValue>());
+ CPPUNIT_ASSERT_EQUAL(OUString(""), xProps->getAuthor());
+ uno::Sequence<beans::PropertyValue> mimeArgs({
+ beans::PropertyValue("MediaType", -1, uno::Any(OUString("application/vnd.oasis.opendocument.text")), beans::PropertyState_DIRECT_VALUE)
+ });
+ utl::TempFile aTempFile;
+ xProps->storeToMedium(aTempFile.GetURL(), mimeArgs);
+
+ // check that custom metadata is preserved
+ uno::Reference<packages::zip::XZipFileAccess2> const xZip(
+ packages::zip::ZipFileAccess::createWithURL(m_xContext, aTempFile.GetURL()));
+ uno::Reference<io::XInputStream> const xInputStream(xZip->getByName("meta.xml"), uno::UNO_QUERY);
+ std::unique_ptr<SvStream> const pStream(utl::UcbStreamHelper::CreateStream(xInputStream, true));
+ xmlDocUniquePtr pXmlDoc = parseXmlStream(pStream.get());
+ assertXPathContent(pXmlDoc, "/office:document-meta/office:meta/bork", "bork");
+ assertXPath(pXmlDoc, "/office:document-meta/office:meta/foo:bar", 1);
+ assertXPath(pXmlDoc, "/office:document-meta/office:meta/foo:bar/baz:foo", 1);
+ assertXPath(pXmlDoc, "/office:document-meta/office:meta/foo:bar/baz:foo[@baz:bar='foo']");
+ assertXPathContent(pXmlDoc, "/office:document-meta/office:meta/foo:bar/foo:baz", "bar");
+
+ aTempFile.EnableKillingFile();
+}
+
+CPPUNIT_TEST_FIXTURE(MiscTest, testNoThumbnail)
+{
+ // Load a document.
+ const OUString aURL(m_directories.getURLFromSrc(u"/sfx2/qa/cppunit/misc/hello.odt"));
+ uno::Reference<lang::XComponent> xComponent
+ = loadFromDesktop(aURL, "com.sun.star.text.TextDocument");
+
+ // Save it with the NoThumbnail option and assert that it has no thumbnail.
+#ifndef _WIN32
+ mode_t nMask = umask(022);
+#endif
+ uno::Reference<frame::XStorable> xStorable(xComponent, uno::UNO_QUERY);
+ CPPUNIT_ASSERT(xStorable.is());
+ utl::TempFile aTempFile;
+ aTempFile.EnableKillingFile();
+ uno::Sequence<beans::PropertyValue> aProperties(
+ comphelper::InitPropertySequence({ { "NoThumbnail", uno::Any(true) } }));
+ osl::File::remove(aTempFile.GetURL());
+ xStorable->storeToURL(aTempFile.GetURL(), aProperties);
+ uno::Reference<packages::zip::XZipFileAccess2> xZipFile
+ = packages::zip::ZipFileAccess::createWithURL(m_xContext, aTempFile.GetURL());
+ CPPUNIT_ASSERT(!xZipFile->hasByName("Thumbnails/thumbnail.png"));
+
+#ifndef _WIN32
+ // Check permissions of the URL after store.
+ osl::DirectoryItem aItem;
+ CPPUNIT_ASSERT_EQUAL(osl::DirectoryItem::E_None,
+ osl::DirectoryItem::get(aTempFile.GetURL(), aItem));
+
+ osl::FileStatus aStatus(osl_FileStatus_Mask_Attributes);
+ CPPUNIT_ASSERT_EQUAL(osl::DirectoryItem::E_None, aItem.getFileStatus(aStatus));
+
+ // The following checks used to fail in the past, osl_File_Attribute_GrpRead was not set even if
+ // umask requested so:
+ CPPUNIT_ASSERT(aStatus.getAttributes() & osl_File_Attribute_GrpRead);
+ CPPUNIT_ASSERT(aStatus.getAttributes() & osl_File_Attribute_OthRead);
+
+ // Now "save as" again to trigger the "overwrite" case.
+ xStorable->storeToURL(aTempFile.GetURL(), {});
+ CPPUNIT_ASSERT_EQUAL(osl::DirectoryItem::E_None, aItem.getFileStatus(aStatus));
+ // The following check used to fail in the past, result had temp file
+ // permissions.
+ CPPUNIT_ASSERT(aStatus.getAttributes() & osl_File_Attribute_GrpRead);
+
+ umask(nMask);
+#endif
+
+ xComponent->dispose();
+}
+
+CPPUNIT_TEST_FIXTURE(MiscTest, testHardLinks)
+{
+#ifndef _WIN32
+ OUString aSourceDir = m_directories.getURLFromSrc(u"/sfx2/qa/cppunit/misc/");
+ OUString aTargetDir = m_directories.getURLFromWorkdir(u"/CppunitTest/sfx2_misc.test.user/");
+ const OUString aURL(aTargetDir + "hello.odt");
+ osl::File::copy(aSourceDir + "hello.odt", aURL);
+ OUString aTargetPath;
+ osl::FileBase::getSystemPathFromFileURL(aURL, aTargetPath);
+ OString aOld = aTargetPath.toUtf8();
+ aTargetPath += ".2";
+ OString aNew = aTargetPath.toUtf8();
+ int nRet = link(aOld.getStr(), aNew.getStr());
+ CPPUNIT_ASSERT_EQUAL(0, nRet);
+
+ uno::Reference<lang::XComponent> xComponent = loadFromDesktop(aURL, "com.sun.star.text.TextDocument");
+
+ uno::Reference<frame::XStorable> xStorable(xComponent, uno::UNO_QUERY);
+ xStorable->store();
+
+ struct stat buf;
+ // coverity[fs_check_call] - this is legitimate in the context of this test
+ nRet = stat(aOld.getStr(), &buf);
+ CPPUNIT_ASSERT_EQUAL(0, nRet);
+ // This failed: hard link count was 1, the hard link broke on store.
+ CPPUNIT_ASSERT(buf.st_nlink > 1);
+
+ // Test that symlinks are preserved as well.
+ nRet = remove(aNew.getStr());
+ CPPUNIT_ASSERT_EQUAL(0, nRet);
+ nRet = symlink(aOld.getStr(), aNew.getStr());
+ CPPUNIT_ASSERT_EQUAL(0, nRet);
+ xStorable->storeToURL(aURL + ".2", {});
+ nRet = lstat(aNew.getStr(), &buf);
+ CPPUNIT_ASSERT_EQUAL(0, nRet);
+ // This failed, the hello.odt.2 symlink was replaced with a real file.
+ CPPUNIT_ASSERT(bool(S_ISLNK(buf.st_mode)));
+
+ xComponent->dispose();
+#endif
+}
+
+CPPUNIT_TEST_FIXTURE(MiscTest, testOverwrite)
+{
+ // tdf#60237 - try to overwrite an existing file using the different settings of the Overwrite option
+ utl::TempFile aTempFile;
+ aTempFile.EnableKillingFile();
+ uno::Reference<lang::XComponent> xComponent
+ = loadFromDesktop(aTempFile.GetURL(), "com.sun.star.text.TextDocument");
+ uno::Reference<frame::XStorable> xStorable(xComponent, uno::UNO_QUERY);
+ CPPUNIT_ASSERT(xStorable.is());
+
+ // overwrite the file using the default case of the Overwrite option (true)
+ CPPUNIT_ASSERT_NO_THROW(xStorable->storeToURL(aTempFile.GetURL(), {}));
+
+ // explicitly overwrite the file using the Overwrite option
+ CPPUNIT_ASSERT_NO_THROW(xStorable->storeToURL(
+ aTempFile.GetURL(),
+ comphelper::InitPropertySequence({ { "Overwrite", uno::Any(true) } })));
+
+ try
+ {
+ // overwrite an existing file with the Overwrite flag set to false
+ xStorable->storeToURL(aTempFile.GetURL(), comphelper::InitPropertySequence(
+ { { "Overwrite", uno::Any(false) } }));
+ CPPUNIT_ASSERT_MESSAGE("We expect an exception on overwriting an existing file", false);
+ }
+ catch (const css::uno::Exception&)
+ {
+ }
+
+ xComponent->dispose();
+}
+
+}
+
+CPPUNIT_PLUGIN_IMPLEMENT();
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/qa/cppunit/view.cxx b/sfx2/qa/cppunit/view.cxx
new file mode 100644
index 000000000..70bb5fd4f
--- /dev/null
+++ b/sfx2/qa/cppunit/view.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 <test/bootstrapfixture.hxx>
+#include <unotest/macros_test.hxx>
+
+#include <com/sun/star/frame/Desktop.hpp>
+#include <com/sun/star/drawing/XDrawView.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+
+#include <sfx2/app.hxx>
+#include <sfx2/sfxsids.hrc>
+#include <sfx2/viewfrm.hxx>
+#include <svl/itemset.hxx>
+#include <svl/intitem.hxx>
+#include <sfx2/request.hxx>
+#include <sfx2/bindings.hxx>
+
+using namespace com::sun::star;
+
+constexpr OUStringLiteral DATA_DIRECTORY = u"/sfx2/qa/cppunit/data/";
+
+/// Covers sfx2/source/view/ fixes.
+class Sfx2ViewTest : public test::BootstrapFixture, public unotest::MacrosTest
+{
+private:
+ uno::Reference<lang::XComponent> mxComponent;
+
+public:
+ void setUp() override;
+ void tearDown() override;
+ uno::Reference<lang::XComponent>& getComponent() { return mxComponent; }
+};
+
+void Sfx2ViewTest::setUp()
+{
+ test::BootstrapFixture::setUp();
+
+ mxDesktop.set(frame::Desktop::create(mxComponentContext));
+}
+
+void Sfx2ViewTest::tearDown()
+{
+ if (mxComponent.is())
+ mxComponent->dispose();
+
+ test::BootstrapFixture::tearDown();
+}
+
+CPPUNIT_TEST_FIXTURE(Sfx2ViewTest, testReloadPage)
+{
+ // Load a document, which has 2 pages.
+ OUString aURL = m_directories.getURLFromSrc(DATA_DIRECTORY) + "reload-page.odg";
+ getComponent() = loadFromDesktop(aURL);
+
+ // Reload, and request to start on page 2.
+ SfxViewFrame* pFrame = SfxViewFrame::Current();
+ SfxAllItemSet aSet(SfxGetpApp()->GetPool());
+ aSet.Put(SfxInt32Item(SID_PAGE_NUMBER, 1));
+ SfxRequest aReq(SID_RELOAD, SfxCallMode::SLOT, aSet);
+ pFrame->ExecReload_Impl(aReq);
+ uno::Reference<frame::XModel> xModel = SfxObjectShell::Current()->GetBaseModel();
+ getComponent() = xModel;
+
+ // Check the current page after reload.
+ uno::Reference<drawing::XDrawView> xController(xModel->getCurrentController(), uno::UNO_QUERY);
+ uno::Reference<beans::XPropertySet> xPage(xController->getCurrentPage(), uno::UNO_QUERY);
+ sal_Int32 nPage{};
+ xPage->getPropertyValue("Number") >>= nPage;
+
+ // Without the accompanying fix in place, this test would have failed with:
+ // - Expected: 2
+ // - Actual : 1
+ // i.e. the document was opened on page 1, not page 2, SID_PAGE_NUMBER was ignored.
+ CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(2), nPage);
+}
+
+CPPUNIT_PLUGIN_IMPLEMENT();
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/qa/python/check_sidebar.py b/sfx2/qa/python/check_sidebar.py
new file mode 100644
index 000000000..59cc955b8
--- /dev/null
+++ b/sfx2/qa/python/check_sidebar.py
@@ -0,0 +1,169 @@
+# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-offset: 4 -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+import unittest
+import unohelper
+import os
+from org.libreoffice.unotest import UnoInProcess
+
+from com.sun.star.ui import XSidebarProvider
+from com.sun.star.ui import XDecks
+from com.sun.star.ui import XDeck
+from com.sun.star.ui import XPanels
+from com.sun.star.ui import XPanel
+
+class CheckSidebar(unittest.TestCase):
+
+ @classmethod
+ def setUpClass(cls):
+ cls._uno = UnoInProcess()
+ cls._uno.setUp()
+
+ @classmethod
+ def tearDownClass(cls):
+ cls._uno.tearDown()
+
+ def test_check_sidebar(self):
+
+ xDoc = self.__class__._uno.openEmptyDoc( url = "private:factory/scalc", bHidden = False, bReadOnly = False)
+ xController = xDoc.getCurrentController()
+
+ xSidebar = xController.getSidebar()
+ assert(xSidebar)
+
+ xSidebar.setVisible(True)
+ isVisible = xSidebar.isVisible()
+ self.assertTrue ( xSidebar.isVisible() )
+
+ # TODO: does not work in unit test context
+# xSidebar.setVisible(False)
+# isVisible = xSidebar.isVisible()
+# assert( not isVisible )
+# xSidebar.setVisible(True)
+
+ xSidebar.showDecks(False)
+ xSidebar.showDecks(True)
+
+ xDecks = xSidebar.getDecks()
+
+ first_deck_name = "PropertyDeck";
+
+ deck_element_names = xDecks.getElementNames()
+ assert ( first_deck_name in deck_element_names )
+ assert ( xDecks.hasByName(first_deck_name) )
+
+ decks_count = len(xDecks)
+ self.assertEqual ( 5, decks_count )
+
+ xDeck = xDecks[first_deck_name]
+ assert ( xDeck )
+ assert ( xDeck.getId() == first_deck_name )
+
+ new_deck_title = "New title"
+ xDeck.setTitle(new_deck_title)
+ assert ( xDeck.getTitle() == new_deck_title )
+
+ xDeck.moveFirst()
+ initial_index = xDeck.getOrderIndex()
+ self.assertEqual(100, initial_index)
+
+ xDeck.moveLast()
+ assert ( xDeck.getOrderIndex() > initial_index )
+
+ initial_index = xDeck.getOrderIndex()
+ xDeck.moveFirst()
+ assert ( xDeck.getOrderIndex() < initial_index )
+
+ initial_index = xDeck.getOrderIndex()
+ xDeck.moveDown()
+ assert ( xDeck.getOrderIndex() > initial_index )
+
+ initial_index = xDeck.getOrderIndex()
+ xDeck.moveUp()
+ assert ( xDeck.getOrderIndex() < initial_index )
+
+ xPanels = xDeck.getPanels()
+
+ panels_count = len(xPanels)
+ self.assertEqual ( panels_count, 5 )
+
+ first_panel_name = self.getFirstPanel(xPanels)
+
+ panel_element_names = xPanels.getElementNames()
+ assert ( first_panel_name in panel_element_names )
+ assert ( xPanels.hasByName(first_panel_name) )
+
+ xPanel = xPanels[first_panel_name]
+ assert ( xPanel )
+ assert ( xPanel.getId() == first_panel_name )
+
+ new_title = "New title"
+ xPanel.setTitle(new_title)
+ assert ( xPanel.getTitle() == new_title )
+
+ initial_index = xPanel.getOrderIndex()
+ xPanel.moveLast()
+ assert ( xPanel.getOrderIndex() > initial_index )
+
+ initial_index = xPanel.getOrderIndex()
+ xPanel.moveFirst()
+ assert ( xPanel.getOrderIndex() < initial_index )
+
+ initial_index = xPanel.getOrderIndex()
+ xPanel.moveDown()
+ assert ( xPanel.getOrderIndex() > initial_index )
+
+ initial_index = xPanel.getOrderIndex()
+ xPanel.moveUp()
+ assert ( xPanel.getOrderIndex() < initial_index )
+
+ xPanel.collapse()
+ assert( not xPanel.isExpanded() )
+
+ last_panel_name = self.getLastPanel(xPanels)
+
+ other_panel = xPanels[last_panel_name]
+ other_panel.expand(False)
+ assert( other_panel.isExpanded() )
+
+ xPanel.expand(True)
+ assert( xPanel.isExpanded() )
+ assert( not other_panel.isExpanded() )
+
+ # close the document
+ xDoc.dispose()
+
+ def getFirstPanel(self, xPanels):
+
+ panel_name = ""
+ cur_index = 10000
+
+ for panel in xPanels:
+ if panel.getOrderIndex() < cur_index:
+ panel_name = panel.getId()
+ cur_index = panel.getOrderIndex()
+
+ return panel_name
+
+ def getLastPanel(self, xPanels):
+
+ panel_name = ""
+ cur_index = 0
+
+ for panel in xPanels:
+ if panel.getOrderIndex() > cur_index:
+ panel_name = panel.getId()
+ cur_index = panel.getOrderIndex()
+
+ return panel_name
+
+if __name__ == "__main__":
+ unittest.main()
+
+# vim: set shiftwidth=4 softtabstop=4 expandtab:
diff --git a/sfx2/qa/python/check_sidebar_registry.py b/sfx2/qa/python/check_sidebar_registry.py
new file mode 100644
index 000000000..47b8eecde
--- /dev/null
+++ b/sfx2/qa/python/check_sidebar_registry.py
@@ -0,0 +1,89 @@
+# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-offset: 4 -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+import unittest
+import unohelper
+import os
+from org.libreoffice.unotest import UnoInProcess
+import uno
+
+class CheckSidebarRegistry(unittest.TestCase):
+
+ @classmethod
+ def setUpClass(cls):
+ cls._uno = UnoInProcess()
+ cls._uno.setUp()
+
+ @classmethod
+ def tearDownClass(cls):
+ cls._uno.tearDown()
+
+ def test_sidebar_registry(self):
+
+ # assert(result) after whole processing to list defective nodes at once
+ result = True
+
+ #open registry node in Sidebar.xcu
+ config_provider = self.createUnoService("com.sun.star.configuration.ConfigurationProvider")
+
+ param = uno.createUnoStruct('com.sun.star.beans.PropertyValue')
+ param.Name = "nodepath"
+
+
+ # Deck names consistency
+
+ param.Value = "org.openoffice.Office.UI.Sidebar/Content/DeckList"
+
+ sidebar_decks_settings = config_provider.createInstanceWithArguments("com.sun.star.configuration.ConfigurationAccess",
+ (param, ))
+ for nodeName in sidebar_decks_settings:
+
+ node = sidebar_decks_settings[nodeName]
+
+ if (node.Id != nodeName):
+ print("\nNon-consistent sidebar.xcu Deck registry names", nodeName, node.Id)
+ result = False
+
+ # panel names consistency
+
+ param.Value = "org.openoffice.Office.UI.Sidebar/Content/PanelList"
+
+ sidebar_panels_settings = config_provider.createInstanceWithArguments("com.sun.star.configuration.ConfigurationAccess",
+ (param, ))
+ for nodeName in sidebar_panels_settings:
+
+ node = sidebar_panels_settings[nodeName]
+
+ if (node.Id != nodeName):
+ print("\nNon-consistent sidebar.xcu Panel registry names", nodeName, node.Id)
+ result = False
+
+ # is panel bound to an existing Deck ?
+ FoundDeckId = False
+ for deckNodeName in sidebar_decks_settings:
+ deck_node = sidebar_decks_settings[deckNodeName]
+ if (node.DeckId == deck_node.Id):
+ FoundDeckId = True
+ if not FoundDeckId:
+ print("\nNon existing DeckId for the panel ",node.Id)
+ result = False
+
+ # trigger the overall result. details of each error have already be printed
+ assert(result)
+
+
+ def createUnoService(self, serviceName):
+
+ sm = uno.getComponentContext().ServiceManager
+ return sm.createInstanceWithContext(serviceName, uno.getComponentContext())
+
+if __name__ == "__main__":
+ unittest.main()
+
+# vim: set shiftwidth=4 softtabstop=4 expandtab:
diff --git a/sfx2/qa/uitest/doc/data/pdf-sign.pdf b/sfx2/qa/uitest/doc/data/pdf-sign.pdf
new file mode 100644
index 000000000..8dedb6998
--- /dev/null
+++ b/sfx2/qa/uitest/doc/data/pdf-sign.pdf
Binary files differ
diff --git a/sfx2/qa/uitest/doc/objserv.py b/sfx2/qa/uitest/doc/objserv.py
new file mode 100644
index 000000000..627469ae9
--- /dev/null
+++ b/sfx2/qa/uitest/doc/objserv.py
@@ -0,0 +1,24 @@
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+from uitest.framework import UITestCase
+from uitest.uihelper.common import get_url_for_data_file
+
+
+# Test for sfx2/source/doc/objserv.cxx.
+class Test(UITestCase):
+
+ def testPdfSigning(self):
+ # Start Impress.
+ with self.ui_test.load_file(get_url_for_data_file("pdf-sign.pdf")) as impress_doc:
+
+ # Now use File -> Digital signatures -> Digital signatures.
+ with self.ui_test.execute_dialog_through_command(".uno:Signature", close_button="close"):
+ # Without the accompanying fix in place, this test would have failed with:
+ # uno.com.sun.star.uno.RuntimeException: Could not find child with id: close vcl/source/uitest/uiobject.cxx:452
+ pass
+
+# vim: set shiftwidth=4 softtabstop=4 expandtab:
diff --git a/sfx2/qa/unit/data/sfx2-dialogs-test.txt b/sfx2/qa/unit/data/sfx2-dialogs-test.txt
new file mode 100644
index 000000000..18dcbacf6
--- /dev/null
+++ b/sfx2/qa/unit/data/sfx2-dialogs-test.txt
@@ -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 contains all dialogs that the unit tests in the module
+# will work on if it is in script mode. It will read one-by-one,
+# try to open it and create a screenshot that will be saved in
+# workdir/screenshots using the pattern of the ui-file name.
+#
+# Syntax:
+# - empty lines are allowed
+# - lines starting with '#' are treated as comment
+# - all other lines should contain a *.ui filename in the same
+# notation as in the dialog constructors (see code)
+
+#
+# The 'known' dialogs which have a hard-coded representation
+# in registerKnownDialogsByID/createDialogByID
+#
+
+# No known dialogs in sfx2 for now
+
+#
+# Dialogs without a hard-coded representation. These will
+# be visualized using a fallback based on weld::Builder
+#
+
+# currently deactivated, leads to problems and the test to not work
+# This is typically a hint that these should be hard-coded in the
+# test case since they need some document and model data to work
+
+sfx/ui/documentpropertiesdialog.ui
+sfx/ui/descriptioninfopage.ui
+sfx/ui/documentinfopage.ui
+sfx/ui/custominfopage.ui
+sfx/ui/cmisinfopage.ui
+sfx/ui/documentfontspage.ui
+sfx/ui/managestylepage.ui
+sfx/ui/optprintpage.ui
+sfx/ui/securityinfopage.ui
+sfx/ui/helpcontentpage.ui
+sfx/ui/helpindexpage.ui
+sfx/ui/helpsearchpage.ui
+sfx/ui/helpbookmarkpage.ui
+sfx/ui/licensedialog.ui
+sfx/ui/linkeditdialog.ui
+sfx/ui/bookmarkdialog.ui
+sfx/ui/checkin.ui
+sfx/ui/editdurationdialog.ui
+sfx/ui/inputdialog.ui
+sfx/ui/newstyle.ui
+sfx/ui/password.ui
+sfx/ui/versionsofdialog.ui
+sfx/ui/versioncommentdialog.ui
+sfx/ui/versionscmis.ui
+sfx/ui/loadtemplatedialog.ui
+sfx/ui/templatedlg.ui
+sfx/ui/printeroptionsdialog.ui
+sfx/ui/searchdialog.ui
+sfx/ui/alienwarndialog.ui
+sfx/ui/errorfindemaildialog.ui
+sfx/ui/querysavedialog.ui
+sfx/ui/floatingrecord.ui
+sfx/ui/helpcontrol.ui
+sfx/ui/startcenter.ui
+sfx/ui/cmisline.ui
+sfx/ui/autoredactdialog.ui
diff --git a/sfx2/qa/unit/sfx2-dialogs-test.cxx b/sfx2/qa/unit/sfx2-dialogs-test.cxx
new file mode 100644
index 000000000..6e23237bc
--- /dev/null
+++ b/sfx2/qa/unit/sfx2-dialogs-test.cxx
@@ -0,0 +1,58 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 <test/screenshot_test.hxx>
+#include <vcl/abstdlg.hxx>
+
+using namespace ::com::sun::star;
+
+/// Test opening a dialog in sfx2
+class Sfx2DialogsTest : public ScreenshotTest
+{
+private:
+ /// helper method to populate KnownDialogs, called in setUp(). Needs to be
+ /// written and has to add entries to KnownDialogs
+ virtual void registerKnownDialogsByID(mapType& rKnownDialogs) override;
+
+ /// dialog creation for known dialogs by ID. Has to be implemented for
+ /// each registered known dialog
+ virtual VclPtr<VclAbstractDialog> createDialogByID(sal_uInt32 nID) override;
+
+public:
+ Sfx2DialogsTest();
+
+ // try to open a dialog
+ void openAnyDialog();
+
+ CPPUNIT_TEST_SUITE(Sfx2DialogsTest);
+ CPPUNIT_TEST(openAnyDialog);
+ CPPUNIT_TEST_SUITE_END();
+};
+
+Sfx2DialogsTest::Sfx2DialogsTest() {}
+
+void Sfx2DialogsTest::registerKnownDialogsByID(mapType& /*rKnownDialogs*/)
+{
+ // fill map of known dialogs
+}
+
+VclPtr<VclAbstractDialog> Sfx2DialogsTest::createDialogByID(sal_uInt32 /*nID*/) { return nullptr; }
+
+void Sfx2DialogsTest::openAnyDialog()
+{
+ /// process input file containing the UXMLDescriptions of the dialogs to dump
+ processDialogBatchFile(u"sfx2/qa/unit/data/sfx2-dialogs-test.txt");
+}
+
+CPPUNIT_TEST_SUITE_REGISTRATION(Sfx2DialogsTest);
+
+CPPUNIT_PLUGIN_IMPLEMENT();
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/qa/unoapi/knownissues.xcl b/sfx2/qa/unoapi/knownissues.xcl
new file mode 100644
index 000000000..1d87f84c9
--- /dev/null
+++ b/sfx2/qa/unoapi/knownissues.xcl
@@ -0,0 +1,5 @@
+### i23244 ###
+sfx.FrameLoader::com::sun::star::frame::XSynchronousFrameLoader
+
+### i79149 ###
+sfx.StandaloneDocumentInfo::com::sun::star::document::DocumentInfo
diff --git a/sfx2/qa/unoapi/sfx.sce b/sfx2/qa/unoapi/sfx.sce
new file mode 100644
index 000000000..d75b95b31
--- /dev/null
+++ b/sfx2/qa/unoapi/sfx.sce
@@ -0,0 +1,4 @@
+-o sfx.AppDispatchProvider
+#i113306 -o sfx.DocumentTemplates
+-o sfx.FrameLoader
+-o sfx.SfxMacroLoader
diff --git a/sfx2/qa/unoapi/testdocuments/SfxStandaloneDocInfoObject.sdw b/sfx2/qa/unoapi/testdocuments/SfxStandaloneDocInfoObject.sdw
new file mode 100644
index 000000000..c4b5672f9
--- /dev/null
+++ b/sfx2/qa/unoapi/testdocuments/SfxStandaloneDocInfoObject.sdw
Binary files differ
diff --git a/sfx2/qa/unoapi/testdocuments/report.stw b/sfx2/qa/unoapi/testdocuments/report.stw
new file mode 100644
index 000000000..5b8efafa1
--- /dev/null
+++ b/sfx2/qa/unoapi/testdocuments/report.stw
Binary files differ
diff --git a/sfx2/qa/unoapi/testdocuments/report2.stw b/sfx2/qa/unoapi/testdocuments/report2.stw
new file mode 100644
index 000000000..9ee0a7ee0
--- /dev/null
+++ b/sfx2/qa/unoapi/testdocuments/report2.stw
Binary files differ
diff --git a/sfx2/sdi/appslots.sdi b/sfx2/sdi/appslots.sdi
new file mode 100644
index 000000000..5503c624e
--- /dev/null
+++ b/sfx2/sdi/appslots.sdi
@@ -0,0 +1,339 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+interface Application
+{
+ SID_AUTOPILOTMENU // ole(no) api(final/play/rec)
+ [
+ ]
+ SID_ABOUT // ole(no) api(final/play/rec)
+ [
+ ExecMethod = MiscExec_Impl ;
+ ]
+ SID_SETOPTIONS
+ [
+ ExecMethod = MiscExec_Impl ;
+ ]
+ SID_QUITAPP // ole(req) api(final/play)
+ [
+ ExecMethod = MiscExec_Impl ;
+ StateMethod = MiscState_Impl ;
+ ]
+ SID_LOGOUT // ole(req) api(final/play)
+ [
+ ]
+ SID_HELPINDEX // ole(no) api(final/todo)
+ [
+ ExecMethod = MiscExec_Impl ;
+ ]
+ SID_EXTENDEDHELP // ole(no) api(final/play/norec)
+ [
+ ExecMethod = MiscExec_Impl ;
+ StateMethod = MiscState_Impl ;
+ ]
+ SID_HELPBALLOONS // ole(no) api(final/play/rec)
+ [
+ ExecMethod = MiscExec_Impl ;
+ StateMethod = MiscState_Impl ;
+ ]
+ SID_HELPTIPS // ole(no) api(final/play/rec)
+ [
+ ExecMethod = MiscExec_Impl ;
+ StateMethod = MiscState_Impl ;
+ ]
+ SID_TIPOFTHEDAY // ole(no) api(final/play/rec)
+ [
+ ExecMethod = MiscExec_Impl ;
+ ]
+ SID_CONFIG // ole(no) api(final/play/rec)
+ [
+ ExecMethod = MiscExec_Impl ;
+ StateMethod = MiscState_Impl ;
+ ]
+ SID_TOOLBOXOPTIONS // ole(no) api(final/play)
+ [
+ ExecMethod = MiscExec_Impl ;
+ StateMethod = MiscState_Impl ;
+ ]
+ SID_CONFIGSTATUSBAR // ole(no) api(final/play)
+ [
+ ExecMethod = MiscExec_Impl ;
+ StateMethod = MiscState_Impl ;
+ ]
+ SID_CONFIGMENU // ole(no) api(final/play)
+ [
+ ExecMethod = MiscExec_Impl ;
+ StateMethod = MiscState_Impl ;
+ ]
+ SID_CONFIGACCEL // ole(no) api(final/play)
+ [
+ ExecMethod = MiscExec_Impl ;
+ StateMethod = MiscState_Impl ;
+ ]
+ SID_CONFIGEVENT // ole(no) api(final/play)
+ [
+ ExecMethod = MiscExec_Impl ;
+ StateMethod = MiscState_Impl ;
+ ]
+ SID_TEMPLATE_MANAGER // ole(no) api(final/play/rec)
+ [
+ ExecMethod = MiscExec_Impl ;
+ StateMethod = MiscState_Impl ;
+ ]
+ SID_TEMPLATE_ADDRESSBOOKSOURCE // ole(no) api(final/play/rec)
+ [
+ ExecMethod = MiscExec_Impl ;
+ StateMethod = MiscState_Impl ;
+ ]
+ SID_ATTR_UNDO_COUNT // ole(no) api(final/play)
+ [
+ ExecMethod = PropExec_Impl ;
+ StateMethod = PropState_Impl ;
+ ]
+ SID_BASICSTOP // ole(no) api(final/play/norec)
+ [
+ ExecMethod = MiscExec_Impl ;
+ StateMethod = MiscState_Impl ;
+ ]
+ SID_BASICBREAK // ole(no) api(final/play/norec)
+ [
+ ExecMethod = MiscExec_Impl ;
+ StateMethod = MiscState_Impl ;
+ ]
+ SID_SEND_FEEDBACK
+ [
+ ExecMethod = MiscExec_Impl ;
+ ]
+ SID_Q_AND_A
+ [
+ ExecMethod = MiscExec_Impl ;
+ ]
+ SID_DOCUMENTATION
+ [
+ ExecMethod = MiscExec_Impl ;
+ ]
+ SID_DONATION
+ [
+ ExecMethod = MiscExec_Impl ;
+ ]
+ SID_GETINVOLVED
+ [
+ ExecMethod = MiscExec_Impl ;
+ ]
+ SID_WHATSNEW
+ [
+ ExecMethod = MiscExec_Impl ;
+ ]
+ SID_HYPHENATIONMISSING
+ [
+ ExecMethod = MiscExec_Impl ;
+ ]
+ SID_SHOW_LICENSE
+ [
+ ExecMethod = MiscExec_Impl ;
+ ]
+ SID_SHOW_CREDITS
+ [
+ ExecMethod = MiscExec_Impl ;
+ ]
+ SID_RECENTFILELIST
+ [
+ ]
+ SID_TOOLBAR_MODE
+ [
+ ExecMethod = MiscExec_Impl ;
+ StateMethod = MiscState_Impl ;
+ ]
+ SID_TOOLBAR_MODE_UI
+ [
+ ExecMethod = MiscExec_Impl ;
+ ]
+ SID_AVAILABLE_TOOLBARS
+ [
+ ExecMethod = MiscExec_Impl ;
+ StateMethod = MiscState_Impl ;
+ ]
+ SID_ZOOM_ENTIRE_PAGE
+ [
+ ExecMethod = MiscExec_Impl ;
+ StateMethod = MiscState_Impl ;
+ ]
+ SID_ZOOM_OPTIMAL
+ [
+ ExecMethod = MiscExec_Impl ;
+ StateMethod = MiscState_Impl ;
+ ]
+ SID_ZOOM_PAGE_WIDTH
+ [
+ ExecMethod = MiscExec_Impl ;
+ StateMethod = MiscState_Impl ;
+ ]
+ SID_ZOOM_50_PERCENT
+ [
+ ExecMethod = MiscExec_Impl ;
+ StateMethod = MiscState_Impl ;
+ ]
+ SID_ZOOM_75_PERCENT
+ [
+ ExecMethod = MiscExec_Impl ;
+ StateMethod = MiscState_Impl ;
+ ]
+ SID_ZOOM_100_PERCENT
+ [
+ ExecMethod = MiscExec_Impl ;
+ StateMethod = MiscState_Impl ;
+ ]
+ SID_ZOOM_150_PERCENT
+ [
+ ExecMethod = MiscExec_Impl ;
+ StateMethod = MiscState_Impl ;
+ ]
+ SID_ZOOM_200_PERCENT
+ [
+ ExecMethod = MiscExec_Impl ;
+ StateMethod = MiscState_Impl ;
+ ]
+ SID_MENUBAR
+ [
+ ExecMethod = MiscExec_Impl ;
+ StateMethod = MiscState_Impl ;
+ ]
+ SID_SAFE_MODE
+ [
+ ExecMethod = MiscExec_Impl ;
+ StateMethod = MiscState_Impl ;
+ ]
+ SID_DEVELOPMENT_TOOLS_DOCKING_WINDOW
+ [
+ ExecMethod = MiscExec_Impl ;
+ StateMethod = MiscState_Impl ;
+ ]
+ SID_INSPECT_SELECTED_OBJECT
+ [
+ ExecMethod = MiscExec_Impl ;
+ StateMethod = MiscState_Impl ;
+ ]
+ SID_TOOLBAR_LOCK
+ [
+ ExecMethod = MiscExec_Impl ;
+ StateMethod = MiscState_Impl ;
+ ]
+}
+
+
+
+shell SfxApplication
+{
+ import Application;
+ import Documents "Documents";
+
+ SID_NEWDOCDIRECT // ole(no) api(no)
+ [
+ ExecMethod = NewDocDirectExec_Impl ;
+ StateMethod = NewDocDirectState_Impl ;
+ ]
+ SID_CLOSEDOCS
+ [
+ ExecMethod = MiscExec_Impl ;
+ StateMethod = MiscState_Impl ;
+ ]
+ SID_BASICIDE_APPEAR // status(final|play)
+ [
+ ExecMethod = OfaExec_Impl;
+ StateMethod = OfaState_Impl;
+ ]
+ SID_SCRIPTORGANIZER // status(final|play)
+ [
+ ExecMethod = OfaExec_Impl;
+ StateMethod = OfaState_Impl;
+ ]
+ SID_MACROORGANIZER // status(final|play)
+ [
+ ExecMethod = OfaExec_Impl;
+ StateMethod = OfaState_Impl;
+ ]
+ SID_RUNMACRO // status(final|play)
+ [
+ ExecMethod = OfaExec_Impl;
+ StateMethod = OfaState_Impl;
+ ]
+ SID_BASICCHOOSER // status(final|play)
+ [
+ ExecMethod = OfaExec_Impl;
+ ]
+ SID_INET_DLG // status(final)
+ [
+ ExecMethod = OfaExec_Impl;
+ ]
+
+
+
+ SID_OFFICE_CHECK_PLZ
+ [
+ ExecMethod = OfaExec_Impl;
+ ]
+ SID_NEWSD // status(final|play)
+ [
+ ExecMethod = OfaExec_Impl;
+ StateMethod = OfaState_Impl;
+ ]
+ FN_LABEL // status(final|play)
+ [
+ ExecMethod = OfaExec_Impl;
+ StateMethod = OfaState_Impl;
+ ]
+ FN_BUSINESS_CARD // status(final|play)
+
+ [
+ ExecMethod = OfaExec_Impl;
+ StateMethod = OfaState_Impl;
+ ]
+ FN_XFORMS_INIT // #i31958# - new XForms document
+ [
+ ExecMethod = OfaExec_Impl;
+ StateMethod = OfaState_Impl;
+ ]
+ SID_COMP_BIBLIOGRAPHY
+ [
+ ExecMethod = OfaExec_Impl;
+ ]
+ SID_ADDRESS_DATA_SOURCE
+ [
+ ExecMethod = OfaExec_Impl;
+ ]
+ SID_AUTO_CORRECT_DLG
+ [
+ ExecMethod = OfaExec_Impl ;
+ StateMethod = OfaState_Impl;
+ ]
+ SID_OPTIONS_TREEDIALOG
+ [
+ ExecMethod = OfaExec_Impl ;
+ ]
+ SID_MORE_DICTIONARIES
+ [
+ ExecMethod = OfaExec_Impl ;
+ ]
+}
+
+shell SfxModule
+{
+}
+
+// eof ------------------------------------------------------------------------
+
diff --git a/sfx2/sdi/docslots.sdi b/sfx2/sdi/docslots.sdi
new file mode 100644
index 000000000..444dca508
--- /dev/null
+++ b/sfx2/sdi/docslots.sdi
@@ -0,0 +1,276 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+interface Documents
+{
+ SID_NEWDOC // ole(opt) api(final/play/rec)
+ [
+ ExecMethod = NewDocExec_Impl ;
+ ]
+ SID_OPENDOC // ole(no) api(final/play/rec)
+ [
+ ExecMethod = OpenDocExec_Impl ;
+ ]
+ SID_OPENREMOTE
+ [
+ ExecMethod = OpenRemoteExec_Impl ;
+ ]
+ SID_SIGNPDF
+ [
+ ExecMethod = SignPDFExec_Impl ;
+ ]
+ SID_OPENHYPERLINK // ole(no) api(final/play/rec)
+ [
+ ExecMethod = OpenDocExec_Impl ;
+ ]
+ SID_OPENURL // ole(no) api(no)
+ [
+ ExecMethod = OpenDocExec_Impl ;
+ ]
+ SID_OPENTEMPLATE // ole(no) api(final/play/rec)
+ [
+ ExecMethod = OpenDocExec_Impl ;
+ ]
+ SID_CLOSEDOCS // ole(req) api(final/play/rec)
+ [
+ ExecMethod = MiscExec_Impl ;
+ StateMethod = MiscState_Impl ;
+ ]
+ SID_SAVEDOCS // ole(no) api(final/play/rec)
+ [
+ ExecMethod = MiscExec_Impl ;
+ StateMethod = MiscState_Impl ;
+ ]
+}
+
+
+interface Document
+{
+ SID_DOCTITLE // ole(opt) api(final/norec)
+ [
+ ExecMethod = ExecProps_Impl ;
+ StateMethod = StateProps_Impl ;
+ ]
+ SID_DOCPATH // ole(req) api(final/play/norec)
+ [
+ ExecMethod = ExecProps_Impl ;
+ StateMethod = StateProps_Impl ;
+ ]
+ SID_DOCFULLNAME // ole(req) api(final/play/norec)
+ [
+ ExecMethod = ExecProps_Impl ;
+ StateMethod = StateProps_Impl ;
+ ]
+ SID_CLOSEDOC // ole(req) api(final/play/rec)
+ [
+ ExecMethod = ExecFile_Impl ;
+ StateMethod = GetState_Impl ;
+ ]
+ SID_CLOSING // ole(no) api(final/play/norec)
+ [
+ StateMethod = StateProps_Impl ;
+ ]
+ SID_ACTIVATE // ole(no) api(final/play/norec)
+ [
+ ExecMethod = ExecView_Impl ;
+ StateMethod = StateView_Impl ;
+ ]
+}
+
+interface OfficeDocument : Document
+{
+ SID_DOCINFO_AUTHOR // ole(opt) api(todo)
+ [
+ ExecMethod = ExecProps_Impl ;
+ StateMethod = StateProps_Impl ;
+ ]
+ SID_DOCINFO_COMMENTS // ole(opt) api(todo)
+ [
+ ExecMethod = ExecProps_Impl ;
+ StateMethod = StateProps_Impl ;
+ ]
+ SID_DOCINFO_KEYWORDS // ole(opt) api(todo)
+ [
+ ExecMethod = ExecProps_Impl ;
+ StateMethod = StateProps_Impl ;
+ ]
+ SID_DOC_READONLY // ole(opt) api(final/play/norec)
+ [
+ ExecMethod = ExecProps_Impl ;
+ StateMethod = StateProps_Impl ;
+ ]
+ SID_DOC_SAVED // ole(req) api(final/play/norec)
+ [
+ ExecMethod = ExecProps_Impl ;
+ StateMethod = StateProps_Impl ;
+ ]
+ SID_DOC_MODIFIED // ole(no) api(final/noplay/norec)
+ [
+ StateMethod = GetState_Impl ;
+ ]
+ SID_MODIFIED // ole(no) api(final/noplay/norec)
+ [
+ ExecMethod = ExecProps_Impl ;
+ StateMethod = GetState_Impl ;
+ ]
+ SID_SAVESIMPLE []
+ SID_SAVEDOC // ole(req) api(final/play/rec)
+ [
+ ExecMethod = ExecFile_Impl ;
+ StateMethod = GetState_Impl ;
+ ]
+ SID_SAVEASDOC // ole(req) api(final/play/rec)
+ [
+ ExecMethod = ExecFile_Impl ;
+ StateMethod = GetState_Impl ;
+ ]
+ SID_SAVEACOPY // ole(req) api(final/play/rec)
+ [
+ ExecMethod = ExecFile_Impl ;
+ StateMethod = GetState_Impl ;
+ ]
+ SID_SAVEASREMOTE // ole(req) api(final/play/rec)
+ [
+ ExecMethod = ExecFile_Impl ;
+ StateMethod = GetState_Impl ;
+ ]
+ SID_DOCTEMPLATE // ole(no) api(final/play/rec)
+ [
+ ExecMethod = ExecFile_Impl ;
+ StateMethod = GetState_Impl ;
+ ]
+ SID_PRINTDOC //ole(req) api(final/play/norec)
+ [
+ ExecMethod = PrintExec_Impl ;
+ StateMethod = NoState ;
+ ]
+ SID_PRINTOUT // ole(opt) api(final/play/norec)
+ [
+ ExecMethod = PrintExec_Impl ;
+ StateMethod = PrintState_Impl ;
+ ]
+ SID_DOC_LOADING // ole(no) api(final/play/norec)
+ [
+ StateMethod = StateProps_Impl ;
+ ]
+ SID_IMG_LOADING // ole(no) api(final/play/norec)
+ [
+ StateMethod = StateProps_Impl ;
+ ]
+ SID_VERSION
+ [
+ ExecMethod = ExecFile_Impl;
+ StateMethod = GetState_Impl;
+ ]
+ SID_SIGNATURE
+ [
+ ExecMethod = ExecFile_Impl;
+ StateMethod = GetState_Impl;
+ ]
+ SID_MACRO_SIGNATURE
+ [
+ ExecMethod = ExecFile_Impl;
+ StateMethod = GetState_Impl;
+ ]
+ SID_CHECKOUT
+ [
+ ExecMethod = ExecFile_Impl;
+ StateMethod = GetState_Impl;
+ ]
+ SID_CANCELCHECKOUT
+ [
+ ExecMethod = ExecFile_Impl;
+ StateMethod = GetState_Impl;
+ ]
+ SID_CHECKIN
+ [
+ ExecMethod = ExecFile_Impl;
+ StateMethod = GetState_Impl;
+ ]
+ SID_DOC_REPAIR
+ [
+ StateMethod = GetState_Impl;
+ ]
+}
+
+
+
+shell SfxObjectShell
+{
+ import OfficeDocument;
+
+ SID_DOCINFO // ole(no) api(final/play)
+ [
+ ExecMethod = ExecFile_Impl ;
+ StateMethod = GetState_Impl ;
+ ]
+ SID_DOCINFO_TITLE // ole(no) api(final/play/rec)
+ [
+ StateMethod = GetState_Impl ;
+ ]
+ SID_EXPORTDOC
+ [
+ ExecMethod = ExecFile_Impl ;
+ StateMethod = GetState_Impl ;
+ ]
+ SID_EXPORTDOCASPDF
+ [
+ ExecMethod = ExecFile_Impl ;
+ StateMethod = GetState_Impl ;
+ ]
+ SID_DIRECTEXPORTDOCASPDF
+ [
+ ExecMethod = ExecFile_Impl ;
+ StateMethod = GetState_Impl ;
+ ]
+ SID_EXPORTDOCASEPUB
+ [
+ ExecMethod = ExecFile_Impl ;
+ StateMethod = GetState_Impl ;
+ ]
+ SID_DIRECTEXPORTDOCASEPUB
+ [
+ ExecMethod = ExecFile_Impl ;
+ StateMethod = GetState_Impl ;
+ ]
+ SID_REDACTDOC
+ [
+ ExecMethod = ExecFile_Impl ;
+ StateMethod = GetState_Impl ;
+ ]
+ SID_AUTOREDACTDOC
+ [
+ ExecMethod = ExecFile_Impl ;
+ StateMethod = GetState_Impl ;
+ ]
+ SID_ADDITIONS_DIALOG
+ [
+ ExecMethod = ExecFile_Impl ;
+ StateMethod = GetState_Impl ;
+ ]
+ SID_SIGNATURE
+ [
+ ExecMethod = ExecFile_Impl;
+ StateMethod = GetState_Impl;
+ ]
+ SID_MACRO_SIGNATURE
+ [
+ ExecMethod = ExecFile_Impl;
+ StateMethod = GetState_Impl;
+ ]
+} ;
diff --git a/sfx2/sdi/frmslots.sdi b/sfx2/sdi/frmslots.sdi
new file mode 100644
index 000000000..1126a0f19
--- /dev/null
+++ b/sfx2/sdi/frmslots.sdi
@@ -0,0 +1,318 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+interface Window
+{
+ SID_HYPERLINK_DIALOG
+ [
+ ExecMethod = ChildWindowExecute ;
+ StateMethod = ChildWindowState ;
+ ]
+ SID_RUBY_DIALOG
+ [
+ ExecMethod = ChildWindowExecute ;
+ StateMethod = ChildWindowState ;
+ ]
+ SID_NAVIGATOR // status(final|play)
+ [
+ ExecMethod = ChildWindowExecute ;
+ StateMethod = ChildWindowState ;
+ ]
+ SID_INFOBAR // status(final|play)
+ [
+ ExecMethod = ChildWindowExecute ;
+ StateMethod = ChildWindowState ;
+ ]
+ SID_SIDEBAR // status(final|play)
+ [
+ ExecMethod = ChildWindowExecute ;
+ StateMethod = ChildWindowState ;
+ ]
+ SID_SIDEBAR_DECK
+ [
+ ExecMethod = ChildWindowExecute ;
+ ]
+ SID_BROWSER // ole(no) api()
+ [
+ ExecMethod = ChildWindowExecute ;
+ StateMethod = ChildWindowState ;
+ ]
+ SID_VIEW_DATA_SOURCE_BROWSER// ole(no) api()
+ [
+ ExecMethod = ChildWindowExecute ;
+ StateMethod = ChildWindowState ;
+ ]
+ SID_SEARCH_DLG // ole(no) api(final/play/rec)
+ [
+ ExecMethod = ChildWindowExecute ;
+ StateMethod = ChildWindowState ;
+ ]
+ SID_TOGGLESTATUSBAR // ole(no) api(final/play/rec)
+ [
+ ExecMethod = MiscExec_Impl ;
+ StateMethod = MiscState_Impl ;
+ ]
+ SID_RECORDMACRO // ole(no) api(final/play/norec)
+ [
+ ExecMethod = MiscExec_Impl ;
+ StateMethod = MiscState_Impl ;
+ ]
+ SID_STOP_RECORDING // ole(no) api(final/play/norec)
+ [
+ ExecMethod = MiscExec_Impl ;
+ StateMethod = MiscState_Impl ;
+ ]
+ SID_STYLE_DESIGNER // ole(no) api(final/play/rec)
+ [
+ ExecMethod = ChildWindowExecute ;
+ ]
+ SID_RECORDING_FLOATWINDOW // ole(no) api(final/play/rec)
+ [
+ ExecMethod = ChildWindowExecute ;
+ StateMethod = ChildWindowState ;
+ ]
+
+ // Pre-defined docking window slots (usable by internal docking windows)
+ SID_DOCKWIN_0
+ [
+ ExecMethod = ChildWindowExecute ;
+ StateMethod = ChildWindowState ;
+ ]
+ SID_DOCKWIN_1
+ [
+ ExecMethod = ChildWindowExecute ;
+ StateMethod = ChildWindowState ;
+ ]
+ SID_DOCKWIN_2
+ [
+ ExecMethod = ChildWindowExecute ;
+ StateMethod = ChildWindowState ;
+ ]
+ SID_DOCKWIN_3
+ [
+ ExecMethod = ChildWindowExecute ;
+ StateMethod = ChildWindowState ;
+ ]
+ SID_DOCKWIN_4
+ [
+ ExecMethod = ChildWindowExecute ;
+ StateMethod = ChildWindowState ;
+ ]
+ SID_DOCKWIN_5
+ [
+ ExecMethod = ChildWindowExecute ;
+ StateMethod = ChildWindowState ;
+ ]
+ SID_DOCKWIN_6
+ [
+ ExecMethod = ChildWindowExecute ;
+ StateMethod = ChildWindowState ;
+ ]
+ SID_DOCKWIN_7
+ [
+ ExecMethod = ChildWindowExecute ;
+ StateMethod = ChildWindowState ;
+ ]
+ SID_DOCKWIN_8
+ [
+ ExecMethod = ChildWindowExecute ;
+ StateMethod = ChildWindowState ;
+ ]
+ SID_DOCKWIN_9
+ [
+ ExecMethod = ChildWindowExecute ;
+ StateMethod = ChildWindowState ;
+ ]
+/* Be careful!
+ You also have to make changes in:
+ - sfx2/sdi/sfx.sdi
+ - sfx2/source/dialog/dockwin.cxx
+ - sfx2/sdi/frmslots.sdi
+ - sfx2/inc/sfx2/sfxsids.hrc
+*/
+ // Window.GetFrameWindow( "Name" )
+ SID_FILLFRAME
+ [
+ ExecMethod = ExecView_Impl;
+ ]
+
+ // Only for Designers: Slot must be present
+ SID_STYLE_NEW
+ [
+ ]
+
+ // Only for Designers: Slot must be present
+ SID_STYLE_DRAGHIERARCHIE
+ [
+ ]
+ SID_CLEARHISTORY // ole(no) api(final/play/rec)
+ [
+ ExecMethod = ExecHistory_Impl ;
+ StateMethod = StateHistory_Impl ;
+ ]
+ SID_UNDO // ole(no) api(final/play/rec)
+ [
+ ExecMethod = ExecHistory_Impl ;
+ StateMethod = StateHistory_Impl ;
+ ]
+ SID_REDO // ole(no) api(final/play/rec)
+ [
+ ExecMethod = ExecHistory_Impl ;
+ StateMethod = StateHistory_Impl ;
+ ]
+ SID_REPEAT // ole(no) api(final/play/rec)
+ [
+ ExecMethod = ExecHistory_Impl ;
+ StateMethod = StateHistory_Impl ;
+ ]
+ SID_CURRENT_URL // ole(no) api(no)
+ [
+ StateMethod = MiscState_Impl ;
+ ]
+ // Stringlist with the verbs ( SFX only )
+ SID_OBJECT // ole(no) api(no)
+ [
+ StateMethod = GetState_Impl ;
+ ExecMethod = ExecView_Impl ;
+ ]
+ SID_TERMINATE_INPLACEACTIVATION
+ [
+ ExecMethod = ExecView_Impl ;
+ ]
+}
+
+interface BrowseWindow : Window
+{
+ SID_ACTIVATE // ole(no) api(final/play/rec)
+ [
+ // Implementations in Subclasses
+ ]
+ SID_NEWWINDOW // ole(no) api(play/rec)
+ [
+ ExecMethod = ExecView_Impl ;
+ StateMethod = StateView_Impl ;
+ ]
+ SID_EDITDOC // ole(?opt) api(play/rec)
+ [
+ ExecMethod = ExecReload_Impl ;
+ StateMethod = StateReload_Impl ;
+ ]
+ SID_READONLYDOC // ole(?opt) api(play/rec)
+ [
+ ExecMethod = ExecReload_Impl ;
+ StateMethod = StateReload_Impl ;
+ ]
+ SID_RELOAD // ole(?opt) api(play/rec)
+ [
+ ExecMethod = ExecReload_Impl ;
+ StateMethod = StateReload_Impl ;
+ ]
+ SID_VIEWSHELL // ole(no) api(no)
+ [
+ ExecMethod = ExecView_Impl ;
+ StateMethod = StateView_Impl ;
+ ]
+ SID_VIEWSHELL0 // ole(no) api(no)
+ [
+ ExecMethod = ExecView_Impl ;
+ StateMethod = StateView_Impl ;
+ ]
+ SID_VIEWSHELL1 // ole(no) api(no)
+ [
+ ExecMethod = ExecView_Impl ;
+ StateMethod = StateView_Impl ;
+ ]
+ SID_VIEWSHELL2 // ole(no) api(no)
+ [
+ ExecMethod = ExecView_Impl ;
+ StateMethod = StateView_Impl ;
+ ]
+ SID_VIEWSHELL3 // ole(no) api(no)
+ [
+ ExecMethod = ExecView_Impl ;
+ StateMethod = StateView_Impl ;
+ ]
+ SID_VIEWSHELL4 // ole(no) api(no)
+ [
+ ExecMethod = ExecView_Impl ;
+ StateMethod = StateView_Impl ;
+ ]
+}
+
+interface TopWindow : BrowseWindow
+{
+ SID_NEWDOCDIRECT // ole(no) api(no)
+ [
+ ExecMethod = Exec_Impl ;
+ StateMethod = GetState_Impl ;
+ ]
+ SID_WIN_FULLSCREEN // ole(no) api(final/play/rec)
+ [
+ ExecMethod = MiscExec_Impl ;
+ StateMethod = MiscState_Impl ;
+ ]
+ SID_COMMAND_POPUP
+ [
+ ExecMethod = MiscExec_Impl ;
+ StateMethod = MiscState_Impl ;
+ ]
+ SID_CLOSEWIN // ole(no) api(final/play/rec)
+ [
+ ExecMethod = Exec_Impl ;
+ StateMethod = GetState_Impl ;
+ ]
+ SID_BROWSE_FORWARD // ole(no) api()
+ [
+ ExecMethod = INetExecute_Impl ;
+ StateMethod = INetState_Impl ;
+ ]
+ SID_BROWSE_BACKWARD // ole(no) api()
+ [
+ ExecMethod = INetExecute_Impl ;
+ StateMethod = INetState_Impl ;
+ ]
+ SID_CREATELINK // ole(no) api()
+ [
+ ExecMethod = INetExecute_Impl ;
+ StateMethod = INetState_Impl ;
+ ]
+ SID_FOCUSURLBOX // ole(no) api(final/play/rec)
+ [
+ ExecMethod = INetExecute_Impl ;
+ StateMethod = INetState_Impl ;
+ ]
+}
+
+
+
+shell SfxViewFrame
+{
+ import TopWindow;
+
+ SID_ACTIVATE // ole(no) api(final/play/rec)
+ [
+ ExecMethod = Exec_Impl ;
+ StateMethod = GetState_Impl ;
+ ]
+
+ SID_SHOWPOPUPS
+ [
+ ExecMethod = Exec_Impl ;
+ StateMethod = GetState_Impl ;
+ ]
+}
diff --git a/sfx2/sdi/sfx.sdi b/sfx2/sdi/sfx.sdi
new file mode 100644
index 000000000..834c898a5
--- /dev/null
+++ b/sfx2/sdi/sfx.sdi
@@ -0,0 +1,5790 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+SfxBoolItem _SwitchViewShell0 SID_VIEWSHELL0
+
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+ Asynchron;
+
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::View;
+]
+
+
+SfxBoolItem _SwitchViewShell1 SID_VIEWSHELL1
+
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+ Asynchron;
+
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::View;
+]
+
+
+SfxBoolItem _SwitchViewShell2 SID_VIEWSHELL2
+
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+ Asynchron;
+
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::View;
+]
+
+
+SfxBoolItem _SwitchViewShell3 SID_VIEWSHELL3
+
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+ Asynchron;
+
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::View;
+]
+
+
+SfxBoolItem _SwitchViewShell4 SID_VIEWSHELL4
+
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+ Asynchron;
+
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::View;
+]
+
+
+SfxVoidItem About SID_ABOUT
+()
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Application;
+]
+
+SfxVoidItem Activate SID_ACTIVATE
+()
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = TRUE,
+ RecordPerSet;
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::View;
+]
+
+
+SfxBoolItem ActiveHelp SID_HELPBALLOONS
+
+[
+ AutoUpdate = TRUE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Application;
+]
+
+
+SfxUInt16Item ActualStyleFamily SID_STYLE_FAMILY
+
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::Document;
+]
+
+
+SfxStringItem NewDoc SID_NEWDOC
+(SfxStringItem Region SID_TEMPLATE_REGIONNAME,SfxStringItem Name SID_TEMPLATE_NAME)
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+ Asynchron;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Application;
+]
+
+
+SfxVoidItem AddBookmark SID_CREATELINK
+()
+[
+ AutoUpdate = FALSE,
+ FastCall = TRUE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::Explorer;
+]
+
+
+SfxStringItem AddDirect SID_NEWDOCDIRECT
+()
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+ Asynchron;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Application;
+]
+
+
+SfxVoidItem CharmapControl SID_CHARMAP_CONTROL
+
+[
+ AutoUpdate = FALSE,
+ FastCall = TRUE,
+ ReadOnlyDoc = FALSE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Special;
+]
+
+
+SfxVoidItem AddressBookSource SID_TEMPLATE_ADDRESSBOOKSOURCE
+()
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+ Asynchron;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Template;
+]
+
+
+SfxVoidItem AddWatch SID_BASICIDE_ADDWATCH
+()
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+ Asynchron;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Macro;
+]
+
+
+SfxStringItem Author SID_DOCINFO_AUTHOR
+
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = ;
+]
+
+SfxStringItem AutoPilotMenu SID_AUTOPILOTMENU
+()
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Application;
+]
+
+
+SfxVoidItem Backspace SID_BACKSPACE
+()
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::Edit;
+]
+
+
+SfxVoidItem BasicBreak SID_BASICBREAK
+()
+[
+ AutoUpdate = TRUE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ NoRecord;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Macro;
+]
+
+
+SfxVoidItem BasicIDEAppear SID_BASICIDE_APPEAR
+(SfxStringItem Document SID_BASICIDE_ARG_DOCUMENT,SfxStringItem LibName SID_BASICIDE_ARG_LIBNAME,
+ SfxStringItem Name SID_BASICIDE_ARG_NAME,SfxStringItem Type SID_BASICIDE_ARG_TYPE,
+ SfxUInt32Item Line SID_BASICIDE_ARG_LINE,SfxUInt16Item Column1 SID_BASICIDE_ARG_COLUMN1,
+ SfxUInt16Item Column2 SID_BASICIDE_ARG_COLUMN2)
+[
+ AutoUpdate = FALSE,
+ FastCall = TRUE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Application;
+]
+
+
+SfxVoidItem BasicStepInto SID_BASICSTEPINTO
+()
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+ Asynchron;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Macro;
+]
+
+
+SfxVoidItem BasicStepOut SID_BASICSTEPOUT
+()
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+ Asynchron;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Macro;
+]
+
+
+SfxVoidItem BasicStepOver SID_BASICSTEPOVER
+()
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+ Asynchron;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Macro;
+]
+
+
+SfxVoidItem BasicStop SID_BASICSTOP
+()
+[
+ AutoUpdate = TRUE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ NoRecord;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Macro;
+]
+
+
+SfxBoolItem Beamer SID_BROWSER
+
+[
+ AutoUpdate = TRUE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::View;
+]
+
+
+SfxVoidItem BreakPointsChanged SID_BASICIDE_BRKPNTSCHANGED
+()
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+ Asynchron;
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::Macro;
+]
+
+
+SfxStringItem BrowseBackward SID_BROWSE_BACKWARD
+(SfxUInt16Item nSteps SID_BROWSE_BACKWARD)
+[
+ AutoUpdate = TRUE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+ Asynchron;
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::Navigator;
+]
+
+
+SfxStringItem BrowseForward SID_BROWSE_FORWARD
+(SfxUInt16Item nSteps SID_BROWSE_FORWARD)
+[
+ AutoUpdate = TRUE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+ Asynchron;
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::Navigator;
+]
+
+
+SfxBoolItem BrowseView SID_BROWSER_MODE
+
+[
+ AutoUpdate = TRUE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::View;
+]
+
+SfxTemplateItem CharStyle SID_STYLE_FAMILY1
+
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::Document;
+]
+
+
+SfxVoidItem ChooseMacro SID_BASICIDE_CHOOSEMACRO
+()
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+ Asynchron;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Macro;
+]
+
+
+SfxVoidItem ClearHistory SID_CLEARHISTORY
+()
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = FALSE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Options;
+]
+
+SfxVoidItem CloseDocs SID_CLOSEDOCS
+()
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+ Asynchron;
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::Application;
+]
+
+
+SfxStringItem CloseDoc SID_CLOSEDOC
+(SfxBoolItem saveChanges SID_CLOSEDOC_SAVE,SfxStringItem fileName SID_CLOSEDOC_FILENAME)
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+ Asynchron;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Document;
+]
+
+
+SfxVoidItem CloseWin SID_CLOSEWIN
+()
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+ Asynchron;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::View;
+]
+
+
+SfxBoolItem Closing SID_CLOSING
+
+[
+ AutoUpdate = TRUE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::Intern;
+]
+
+
+SfxStringItem Comments SID_DOCINFO_COMMENTS
+
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = ;
+]
+
+
+SfxVoidItem CompareDocuments SID_DOCUMENT_COMPARE
+(SfxStringItem URL SID_FILE_NAME,SfxStringItem FilterName SID_FILTER_NAME,SfxStringItem Password SID_PASSWORD,SfxStringItem FilterOptions SID_FILE_FILTEROPTIONS,SfxInt16Item Version SID_VERSION, SfxBoolItem NoAcceptDialog SID_NO_ACCEPT_DIALOG)
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = FALSE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Edit;
+]
+
+
+SfxVoidItem CompileBasic SID_BASICCOMPILE
+()
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+ Asynchron;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Macro;
+]
+
+
+SfxVoidItem ConfigureDialog SID_CONFIG
+(SfxStringItem ResourceURL SID_CONFIG)
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+ Asynchron;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Options;
+]
+
+
+SfxStringItem Context SID_CONTEXT
+
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::View;
+]
+
+
+SfxVoidItem Copy SID_COPY
+()
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Edit;
+]
+
+
+SfxVoidItem CreateMacro SID_BASICIDE_CREATEMACRO
+(SfxMacroInfoItem aMacroInfo SID_BASICIDE_ARG_MACROINFO)
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+ Asynchron;
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::Macro;
+]
+
+
+SfxStringItem CurrentURL SID_CURRENT_URL
+
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+ Asynchron;
+
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = ;
+]
+
+
+SfxVoidItem CursorEndOfScreen SID_CURSORENDOFSCREEN
+()
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::Intern;
+]
+
+
+SfxVoidItem CursorTopOfScreen SID_CURSORTOPOFSCREEN
+()
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::Intern;
+]
+
+SfxVoidItem Cut SID_CUT
+()
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = FALSE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Edit;
+]
+
+
+SfxStringItem DefaultFilePath SID_DEFAULTFILEPATH
+
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = ;
+]
+
+
+SfxStringItem DefaultFileName SID_DEFAULTFILENAME
+
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = ;
+]
+
+
+SfxVoidItem Delete SID_DELETE
+()
+[
+ AutoUpdate = FALSE,
+ FastCall = TRUE,
+ ReadOnlyDoc = FALSE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Edit;
+]
+
+
+SfxVoidItem DeleteCurrent SID_BASICIDE_DELETECURRENT
+()
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+ Asynchron;
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::Macro;
+]
+
+
+SfxVoidItem DeleteStyle SID_STYLE_DELETE
+(SfxStringItem Param SID_STYLE_DELETE,SfxUInt16Item Family SID_STYLE_FAMILY)
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = FALSE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::Template;
+]
+
+
+SfxVoidItem DesignerDialog SID_STYLE_DESIGNER
+
+[
+ AutoUpdate = TRUE,
+ FastCall = TRUE,
+ ReadOnlyDoc = FALSE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::View;
+]
+
+SfxVoidItem DragHierarchy SID_STYLE_DRAGHIERARCHIE
+()
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = ;
+]
+
+
+SfxVoidItem EditDoc SID_EDITDOC
+(SfxBoolItem Editable SID_EDITDOC)
+[
+ AutoUpdate = TRUE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+ Asynchron;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Document;
+]
+
+
+SfxVoidItem ReadOnlyDoc SID_READONLYDOC
+(SfxBoolItem Editable SID_EDITDOC)
+[
+ AutoUpdate = TRUE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+ Asynchron;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Document;
+]
+
+
+SfxVoidItem EditMacro SID_BASICIDE_EDITMACRO
+(SfxMacroInfoItem aMacroInfo SID_BASICIDE_ARG_MACROINFO)
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+ Asynchron;
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::Macro;
+]
+
+
+SfxVoidItem EditStyle SID_STYLE_EDIT
+(SfxStringItem Param SID_STYLE_EDIT,SfxUInt16Item Family SID_STYLE_FAMILY)
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = FALSE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Template;
+]
+
+
+SfxVoidItem HideStyle SID_STYLE_HIDE
+(SfxStringItem Param SID_STYLE_HIDE,SfxUInt16Item Family SID_STYLE_FAMILY)
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = FALSE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::Template;
+]
+
+
+SfxVoidItem ShowStyle SID_STYLE_SHOW
+(SfxStringItem Param SID_STYLE_SHOW,SfxUInt16Item Family SID_STYLE_FAMILY)
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = FALSE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::Template;
+]
+
+
+SfxVoidItem ExecuteSearch FID_SEARCH_NOW
+(SvxSearchItem SearchItem SID_SEARCH_ITEM, SfxBoolItem Quiet SID_SEARCH_QUIET )
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::Edit;
+]
+
+
+SfxVoidItem RepeatSearch SID_BASICIDE_REPEAT_SEARCH
+(SvxSearchItem SearchItem SID_SEARCH_ITEM, SfxBoolItem Quiet SID_SEARCH_QUIET)
+[
+ AutoUpdate = FALSE,
+ FastCall = TRUE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+ Asynchron;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Edit;
+]
+
+
+SfxBoolItem ExtendedHelp SID_EXTENDEDHELP
+
+[
+ AutoUpdate = TRUE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Application;
+]
+
+
+SfxStringItem FileName SID_FILE_NAME
+
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = ;
+]
+
+
+SfxStringItem FocusUrlBox SID_FOCUSURLBOX
+()
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::Explorer;
+]
+
+
+SfxTemplateItem FrameStyle SID_STYLE_FAMILY3
+
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::Document;
+]
+
+SfxStringItem FullName SID_DOCFULLNAME
+
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = ;
+]
+
+SfxVoidItem CommandPopup SID_COMMAND_POPUP
+[
+ AutoUpdate = TRUE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+ Asynchron;
+
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::View;
+]
+
+SfxBoolItem FullScreen SID_WIN_FULLSCREEN
+
+[
+ AutoUpdate = TRUE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+ Asynchron;
+
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::View;
+]
+
+
+SfxVoidItem ZoomPageWidth SID_ZOOM_PAGE_WIDTH
+
+[
+ AutoUpdate = TRUE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+ Asynchron;
+
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::View;
+]
+
+
+
+SfxVoidItem ZoomOptimal SID_ZOOM_OPTIMAL
+
+[
+ AutoUpdate = TRUE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+ Asynchron;
+
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::View;
+]
+
+
+
+SfxVoidItem ZoomPage SID_ZOOM_ENTIRE_PAGE
+
+[
+ AutoUpdate = TRUE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+ Asynchron;
+
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::View;
+]
+
+
+
+SfxVoidItem Zoom200Percent SID_ZOOM_200_PERCENT
+
+[
+ AutoUpdate = TRUE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+ Asynchron;
+
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::View;
+]
+
+
+
+SfxVoidItem Zoom150Percent SID_ZOOM_150_PERCENT
+
+[
+ AutoUpdate = TRUE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+ Asynchron;
+
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::View;
+]
+
+
+
+SfxVoidItem Zoom100Percent SID_ZOOM_100_PERCENT
+
+[
+ AutoUpdate = TRUE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+ Asynchron;
+
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::View;
+]
+
+
+
+SfxVoidItem Zoom75Percent SID_ZOOM_75_PERCENT
+
+[
+ AutoUpdate = TRUE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+ Asynchron;
+
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::View;
+]
+
+
+SfxVoidItem Zoom50Percent SID_ZOOM_50_PERCENT
+
+[
+ AutoUpdate = TRUE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+ Asynchron;
+
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::View;
+]
+
+
+SfxVoidItem GetFrameWindow SID_FILLFRAME
+(SfxStringItem WindowName SID_FILLFRAME)
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = ;
+]
+
+
+SfxVoidItem GoDown SID_CURSORDOWN
+(SfxInt16Item By SID_CURSORDOWN)
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = TRUE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Navigator;
+]
+
+
+SfxVoidItem GoDownBlock SID_CURSORPAGEDOWN
+(SfxInt16Item By SID_CURSORPAGEDOWN,SfxBoolItem Sel FN_PARAM_2)
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = TRUE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::Navigator;
+]
+
+
+SfxVoidItem GoDownBlockSel SID_CURSORPAGEDOWN_SEL
+(SfxInt16Item By FN_PARAM_1)
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = TRUE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::Navigator;
+]
+
+
+SfxVoidItem GoDownSel SID_CURSORDOWN_SEL
+(SfxInt16Item By FN_PARAM_1)
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = TRUE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::Navigator;
+]
+
+
+SfxVoidItem GoLeft SID_CURSORLEFT
+(SfxInt16Item By SID_CURSORLEFT,SfxBoolItem Sel FN_PARAM_2)
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = TRUE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::Navigator;
+]
+
+SfxVoidItem GoLeftBlockSel SID_CURSORPAGELEFT_SEL
+(SfxInt16Item By FN_PARAM_1)
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = TRUE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::Navigator;
+]
+
+
+SfxVoidItem GoLeftSel SID_CURSORLEFT_SEL
+(SfxInt16Item By FN_PARAM_1)
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = TRUE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::Navigator;
+]
+
+
+SfxVoidItem GoRight SID_CURSORRIGHT
+(SfxInt16Item By SID_CURSORRIGHT,SfxBoolItem Sel FN_PARAM_2)
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = TRUE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::Navigator;
+]
+
+
+SfxVoidItem GoRightSel SID_CURSORRIGHT_SEL
+(SfxInt16Item By FN_PARAM_1)
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = TRUE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::Navigator;
+]
+
+
+SfxVoidItem GoToEndOfData SID_CURSORENDOFFILE
+(SfxBoolItem Sel FN_PARAM_2)
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = TRUE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::Navigator;
+]
+
+
+SfxVoidItem GoToEndOfDataSel SID_CURSORENDOFFILE_SEL
+()
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = TRUE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::Navigator;
+]
+
+
+SfxVoidItem GoToEndOfRow SID_CURSOREND
+(SfxBoolItem Sel FN_PARAM_2)
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = TRUE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::Navigator;
+]
+
+
+SfxVoidItem GoToEndOfRowSel SID_CURSOREND_SEL
+()
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = TRUE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::Navigator;
+]
+
+
+SfxVoidItem GoToStart SID_CURSORTOPOFFILE
+(SfxBoolItem Sel FN_PARAM_2)
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = TRUE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::Navigator;
+]
+
+
+SfxVoidItem GoToStartOfRow SID_CURSORHOME
+()
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = TRUE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::Navigator;
+]
+
+
+SfxVoidItem GoToStartOfRowSel SID_CURSORHOME_SEL
+()
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = TRUE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::Navigator;
+]
+
+
+SfxVoidItem GoToStartSel SID_CURSORTOPOFFILE_SEL
+()
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = TRUE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::Navigator;
+]
+
+
+SfxVoidItem GoUp SID_CURSORUP
+(SfxInt16Item By SID_CURSORUP,SfxBoolItem Sel FN_PARAM_2)
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = TRUE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::Navigator;
+]
+
+
+SfxVoidItem GoUpBlock SID_CURSORPAGEUP
+(SfxInt16Item By SID_CURSORPAGEUP,SfxBoolItem Sel FN_PARAM_2)
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = TRUE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::Navigator;
+]
+
+
+SfxVoidItem GoUpBlockSel SID_CURSORPAGEUP_SEL
+(SfxInt16Item By FN_PARAM_1)
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = TRUE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::Navigator;
+]
+
+
+SfxVoidItem GoUpSel SID_CURSORUP_SEL
+(SfxInt16Item By FN_PARAM_1)
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = TRUE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::Navigator;
+]
+
+
+SfxVoidItem HelpIndex SID_HELPINDEX
+()
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Application;
+]
+
+
+SfxBoolItem HelpTip SID_HELPTIPS
+
+[
+ AutoUpdate = TRUE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::Application;
+]
+
+SfxVoidItem TipOfTheDay SID_TIPOFTHEDAY
+
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = FALSE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Application;
+]
+
+SfxVoidItem EmojiControl SID_EMOJI_CONTROL
+
+[
+ AutoUpdate = FALSE,
+ FastCall = TRUE,
+ ReadOnlyDoc = FALSE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Special;
+]
+
+
+SfxVoidItem HideCurPage SID_BASICIDE_HIDECURPAGE
+()
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+ Asynchron;
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::Macro;
+]
+
+
+SfxBoolItem HyperlinkDialog SID_HYPERLINK_DIALOG
+
+[
+ AutoUpdate = TRUE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Edit;
+]
+
+
+SfxVoidItem InsertDoc SID_INSERTDOC
+(SfxStringItem Name SID_INSERTDOC,SfxStringItem Filter FN_PARAM_1)
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = FALSE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+ Asynchron;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Insert;
+]
+
+
+SfxVoidItem InsertObjectFloatingFrame SID_INSERT_FLOATINGFRAME
+(
+ SfxStringItem Name FN_PARAM_1,
+ SfxStringItem URL FN_PARAM_2,
+ SvxSizeItem Margin FN_PARAM_3,
+ SfxByteItem ScrollingMode FN_PARAM_4,
+ SfxBoolItem IsBorder FN_PARAM_5
+)
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = FALSE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+ Asynchron;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Insert;
+]
+
+
+SfxBoolItem IsLoading SID_DOC_LOADING
+
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::View;
+]
+
+
+SfxBoolItem IsLoadingImages SID_IMG_LOADING
+
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::View;
+]
+
+
+SfxBoolItem PrintOut SID_PRINTOUT
+(SfxInt16Item copies SID_PRINT_COPIES,SfxBoolItem silent SID_SILENT)
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::Document;
+]
+
+
+SfxVoidItem JumpToMark SID_JUMPTOMARK
+(SfxStringItem Bookmark SID_JUMPTOMARK)
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::Navigator;
+]
+
+
+SfxStringItem Keywords SID_DOCINFO_KEYWORDS
+
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = ;
+]
+
+
+SfxStringItem LibLoaded SID_BASICIDE_LIBLOADED
+(SfxUnoAnyItem Document SID_BASICIDE_ARG_DOCUMENT_MODEL,SfxStringItem LibName SID_BASICIDE_ARG_LIBNAME)
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::Macro;
+]
+
+
+SfxStringItem LibRemoved SID_BASICIDE_LIBREMOVED
+(SfxUnoAnyItem Document SID_BASICIDE_ARG_DOCUMENT_MODEL,SfxStringItem LibName SID_BASICIDE_ARG_LIBNAME)
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::Macro;
+]
+
+
+SfxStringItem LibSelect SID_BASICIDE_LIBSELECTED
+(SfxUnoAnyItem Document SID_BASICIDE_ARG_DOCUMENT_MODEL,SfxStringItem LibName SID_BASICIDE_ARG_LIBNAME)
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::Macro;
+]
+
+
+SfxStringItem LibSelector SID_BASICIDE_LIBSELECTOR
+
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Macro;
+]
+
+
+SfxVoidItem LoadAccel SID_CONFIGACCEL
+(SfxStringItem FileName SID_CFGFILE)
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+ Asynchron;
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::Options;
+]
+
+
+SfxVoidItem LoadBasic SID_BASICLOAD
+()
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+ Asynchron;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Insert;
+]
+
+
+SfxVoidItem LoadEvents SID_CONFIGEVENT
+(SfxStringItem FileName SID_CFGFILE)
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+ Asynchron;
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::Options;
+]
+
+
+SfxVoidItem LoadMenu SID_CONFIGMENU
+(SfxStringItem FileName SID_CFGFILE)
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+ Asynchron;
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::Options;
+]
+
+
+SfxVoidItem LoadStatusBar SID_CONFIGSTATUSBAR
+(SfxStringItem FileName SID_CFGFILE)
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+ Asynchron;
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::Options;
+]
+
+
+SfxVoidItem LoadToolBox SID_TOOLBOXOPTIONS
+()
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+ Asynchron;
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::Options;
+]
+
+
+SfxStringItem Logout SID_LOGOUT
+()
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+ Asynchron;
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::Application;
+]
+
+
+SfxScriptOrganizerItem ScriptOrganizer SID_SCRIPTORGANIZER
+(SfxScriptOrganizerItem ScriptOrganizer SID_SCRIPTORGANIZER)
+[
+ AutoUpdate = FALSE,
+ FastCall = TRUE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Macro;
+]
+
+
+SfxScriptOrganizerItem MacroOrganizer SID_MACROORGANIZER
+(SfxUInt16Item TabId SID_MACROORGANIZER)
+[
+ AutoUpdate = FALSE,
+ FastCall = TRUE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Macro;
+]
+
+//----------------------------------------------------FALSE
+SfxBoolItem ShowLines SID_SHOWLINES
+
+[
+ AutoUpdate = TRUE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = TRUE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Macro;
+]
+
+
+SfxVoidItem RunMacro SID_RUNMACRO
+()
+[
+ AutoUpdate = FALSE,
+ FastCall = TRUE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Macro;
+]
+
+SfxVoidItem GotoLine SID_GOTOLINE
+
+[
+ AutoUpdate = TRUE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = TRUE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Macro;
+]
+
+
+SfxVoidItem MacroDialog SID_BASICCHOOSER
+()
+[
+ AutoUpdate = FALSE,
+ FastCall = TRUE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Macro;
+]
+
+SfxVoidItem MatchGroup SID_BASICIDE_MATCHGROUP
+()
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+ Asynchron;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Macro;
+]
+
+SfxVoidItem MergeDocuments SID_DOCUMENT_MERGE
+(SfxStringItem URL SID_FILE_NAME,SfxInt16Item Version SID_VERSION)
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = FALSE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Edit;
+]
+
+
+SfxUInt16Item MetricUnit SID_ATTR_METRIC
+
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = ;
+]
+
+SfxBoolItem Modified SID_MODIFIED
+
+[
+ AutoUpdate = TRUE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::View;
+]
+
+
+SfxBoolItem ModifiedStatus SID_DOC_MODIFIED
+
+[
+ AutoUpdate = TRUE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::View;
+]
+
+
+SfxVoidItem ModuleDialog SID_BASICIDE_MODULEDLG
+()
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+ Asynchron;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Macro;
+]
+
+
+SfxVoidItem NameChangedOnTab SID_BASICIDE_NAMECHANGEDONTAB
+(SfxUInt16Item TabId SID_BASICIDE_ARG_TABID,SfxStringItem NewName SID_BASICIDE_ARG_MODULENAME)
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+ Asynchron;
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::Macro;
+]
+
+
+SfxBoolItem Navigator SID_NAVIGATOR
+[
+ AutoUpdate = TRUE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Navigator;
+]
+
+SfxBoolItem InfoBar SID_INFOBAR
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::Document;
+]
+
+SfxBoolItem Sidebar SID_SIDEBAR
+[
+ AutoUpdate = TRUE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+ Asynchron;
+
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::View;
+]
+
+SfxBoolItem Menubar SID_MENUBAR
+[
+ AutoUpdate = TRUE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+ Asynchron;
+
+ AccelConfig = FALSE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::View;
+]
+
+SfxVoidItem Notebookbar SID_NOTEBOOKBAR
+(SfxStringItem File SID_NOTEBOOKBAR)
+[
+ AutoUpdate = TRUE,
+ FastCall = TRUE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+ Asynchron;
+
+ AccelConfig = FALSE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::View;
+]
+
+
+SfxVoidItem RestoreEditingView SID_RESTORE_EDITING_VIEW
+
+[
+ AutoUpdate = TRUE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::View;
+]
+
+
+SfxVoidItem NewDialog SID_BASICIDE_NEWDIALOG
+()
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+ Asynchron;
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::Macro;
+]
+
+
+SfxVoidItem NewModule SID_BASICIDE_NEWMODULE
+()
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+ Asynchron;
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::Macro;
+]
+
+SfxVoidItem NewStyle SID_STYLE_NEW
+(SfxStringItem Param SID_STYLE_NEW,SfxUInt16Item Family SID_STYLE_FAMILY)
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = FALSE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::Template;
+]
+
+
+SfxVoidItem NewWindow SID_NEWWINDOW
+()
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::View;
+]
+
+
+SfxVoidItem ObjectCatalog SID_BASICIDE_OBJCAT
+()
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+ Asynchron;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Macro;
+]
+
+
+SfxVoidItem ObjectMenue SID_OBJECT
+(SfxInt16Item VerbID SID_OBJECT)
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+ Asynchron;
+
+
+ AccelConfig = FALSE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::Edit;
+]
+
+
+SfxVoidItem Open SID_OPENDOC
+(SfxStringItem URL SID_FILE_NAME,SfxStringItem FilterName SID_FILTER_NAME,SfxStringItem OpenFlags SID_OPTIONS,SfxStringItem Password SID_PASSWORD,SfxStringItem FilterOptions SID_FILE_FILTEROPTIONS,SfxInt16Item Version SID_VERSION,SfxStringItem Referer SID_REFERER,SfxStringItem SuggestedSaveAsDir SID_DEFAULTFILEPATH,SfxStringItem SuggestedSaveAsName SID_DEFAULTFILENAME)
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+ Asynchron;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Application;
+]
+
+SfxVoidItem OpenRemote SID_OPENREMOTE
+(SfxStringItem URL SID_FILE_NAME,SfxStringItem FilterName SID_FILTER_NAME,SfxStringItem OpenFlags SID_OPTIONS,SfxStringItem Password SID_PASSWORD,SfxStringItem FilterOptions SID_FILE_FILTEROPTIONS,SfxInt16Item Version SID_VERSION,SfxStringItem Referer SID_REFERER,SfxStringItem SuggestedSaveAsDir SID_DEFAULTFILEPATH,SfxStringItem SuggestedSaveAsName SID_DEFAULTFILENAME)
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+ Asynchron;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Application;
+]
+
+SfxVoidItem SignPDF SID_SIGNPDF
+(SfxStringItem URL SID_FILE_NAME,SfxStringItem FilterName SID_FILTER_NAME,SfxStringItem OpenFlags SID_OPTIONS,SfxStringItem Password SID_PASSWORD,SfxStringItem FilterOptions SID_FILE_FILTEROPTIONS,SfxInt16Item Version SID_VERSION,SfxStringItem Referer SID_REFERER,SfxStringItem SuggestedSaveAsDir SID_DEFAULTFILEPATH,SfxStringItem SuggestedSaveAsName SID_DEFAULTFILENAME)
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+ Asynchron;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Application;
+]
+
+SfxVoidItem WebHtml SID_WEBHTML
+()
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+ Asynchron;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Application;
+]
+
+
+SfxVoidItem OpenHyperlink SID_OPENHYPERLINK
+(SfxStringItem URL SID_FILE_NAME,SfxStringItem FilterName SID_FILTER_NAME,SfxStringItem OpenFlags SID_OPTIONS,SfxStringItem Password SID_PASSWORD,SfxStringItem FilterOptions SID_FILE_FILTEROPTIONS,SfxInt16Item Version SID_VERSION,SfxStringItem Referer SID_REFERER)
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+ Asynchron;
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::Application;
+]
+
+
+SfxStringItem DocInfoTitle SID_DOCINFO_TITLE
+
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = ;
+]
+
+
+SfxVoidItem OpenTemplate SID_OPENTEMPLATE
+(SfxStringItem Region SID_TEMPLATE_REGIONNAME,SfxStringItem Name SID_TEMPLATE_NAME)
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+ Asynchron;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Template;
+]
+
+SfxVoidItem CheckOut SID_CHECKOUT
+()
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+ Asynchron;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Document;
+]
+
+SfxVoidItem CancelCheckOut SID_CANCELCHECKOUT
+()
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+ Asynchron;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Document;
+]
+
+SfxVoidItem CheckIn SID_CHECKIN
+(SfxStringItem VersionComment SID_DOCINFO_COMMENTS,SfxBoolItem VersionMajor SID_DOCINFO_MAJOR)
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+ Asynchron;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Document;
+]
+
+
+SfxStringItem OpenUrl SID_OPENURL
+
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+ Asynchron;
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Application;
+]
+
+
+SfxVoidItem Options SID_OPTIONS
+()
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::Options;
+]
+
+SfxVoidItem TemplateManager SID_TEMPLATE_MANAGER
+()
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+ Asynchron;
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::Application;
+]
+
+
+SfxTemplateItem PageStyle SID_STYLE_FAMILY4
+
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::Document;
+]
+
+
+SfxTemplateItem ParaStyle SID_STYLE_FAMILY2
+
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::Document;
+]
+
+
+SfxVoidItem Paste SID_PASTE
+(SfxUInt16Item AnchorType FN_PARAM_1, SfxBoolItem IgnoreComments FN_PARAM_2)
+[
+ AutoUpdate = FALSE,
+ FastCall = TRUE,
+ ReadOnlyDoc = FALSE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Edit;
+]
+
+
+SvxClipboardFormatItem ClipboardFormatItems SID_CLIPBOARD_FORMAT_ITEMS
+(SfxUInt32Item SelectedFormat SID_CLIPBOARD_FORMAT_ITEMS)
+[
+ AutoUpdate = FALSE,
+ FastCall = TRUE,
+ ReadOnlyDoc = FALSE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::Edit;
+]
+
+
+SfxVoidItem PasteSpecial SID_PASTE_SPECIAL
+(SfxUInt32Item Format SID_PASTE_SPECIAL)
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = FALSE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+ Asynchron;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Edit;
+]
+
+SfxVoidItem PasteOnlyText SID_PASTE_ONLY_TEXT
+
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Edit;
+]
+
+SfxVoidItem PasteOnlyFormula SID_PASTE_ONLY_FORMULA
+
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Edit;
+]
+
+SfxVoidItem PasteOnlyValue SID_PASTE_ONLY_VALUE
+
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Edit;
+]
+
+SfxVoidItem PasteTransposed SID_PASTE_TRANSPOSED
+
+[
+ AutoUpdate = FALSE,
+ FastCall = TRUE,
+ ReadOnlyDoc = FALSE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Edit;
+]
+
+SfxVoidItem PasteAsLink SID_PASTE_AS_LINK
+
+[
+ AutoUpdate = FALSE,
+ FastCall = TRUE,
+ ReadOnlyDoc = FALSE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Edit;
+]
+
+SfxVoidItem PasteTextImportDialog SID_PASTE_TEXTIMPORT_DIALOG
+
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Edit;
+]
+
+SfxStringItem DocPath SID_DOCPATH
+
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = ;
+]
+
+
+SfxVoidItem Print SID_PRINTDOC
+(SfxStringItem PrinterName SID_PRINTER_NAME,SfxStringItem FileName SID_FILE_NAME,SfxInt16Item Copies SID_PRINT_COPIES,SfxStringItem RangeText SID_PRINT_PAGES,SfxBoolItem Selection SID_SELECTION,SfxBoolItem Asynchron SID_ASYNCHRON,SfxBoolItem Collate SID_PRINT_COLLATE,SfxBoolItem Silent SID_SILENT)
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+ Asynchron;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Document;
+]
+
+
+SfxVoidItem PrintDefault SID_PRINTDOCDIRECT
+()
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+ Asynchron;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Document;
+]
+
+
+SfxStringItem Printer SID_PRINTER_NAME
+
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+ Asynchron;
+
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = ;
+]
+
+
+SfxVoidItem PrinterSetup SID_SETUPPRINTER
+(SfxStringItem PrinterName SID_PRINTER_NAME)
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+ Asynchron;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Document;
+]
+
+
+SfxBoolItem PrintPreview SID_PRINTPREVIEW
+
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = TRUE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+ Asynchron;
+
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::View;
+]
+
+
+SfxVoidItem SetDocumentProperties SID_DOCINFO
+(SfxDocumentInfoItem Properties SID_DOCINFO)
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Document;
+]
+
+
+SfxStringItem Quit SID_QUITAPP
+()
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+ Asynchron;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Application;
+]
+
+
+SfxBoolItem ReadOnly SID_DOC_READONLY
+
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = ;
+]
+
+
+SfxBoolItem MacroRecorder SID_RECORDMACRO
+
+[
+ AutoUpdate = TRUE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ NoRecord;
+
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Macro;
+]
+
+
+SfxVoidItem StopRecording SID_STOP_RECORDING
+(SfxBoolItem DontRecord FN_PARAM_1)
+[
+ AutoUpdate = TRUE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ NoRecord;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Macro;
+]
+
+
+SfxBoolItem MacroRecordingFloat SID_RECORDING_FLOATWINDOW
+
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ NoRecord;
+
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::Macro;
+]
+
+
+SfxStringItem Redo SID_REDO
+( SfxUInt16Item Redo SID_REDO, SfxBoolItem Repair SID_REPAIRPACKAGE )
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = FALSE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Edit;
+]
+
+SfxBoolItem DocumentRepair SID_DOC_REPAIR
+
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = FALSE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+ Asynchron;
+
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = ;
+]
+
+SfxBoolItem Reload SID_RELOAD
+()
+[
+ AutoUpdate = TRUE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+ Asynchron;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Document;
+]
+
+
+SfxVoidItem RemoveWatch SID_BASICIDE_REMOVEWATCH
+()
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+ Asynchron;
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::Macro;
+]
+
+
+SfxVoidItem RenameCurrent SID_BASICIDE_RENAMECURRENT
+()
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+ Asynchron;
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::Macro;
+]
+
+
+SfxVoidItem Repaint SID_REPAINT
+()
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = TRUE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::View;
+]
+
+
+SfxStringItem Repeat SID_REPEAT
+()
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = FALSE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Edit;
+]
+
+
+SfxBoolItem RubyDialog SID_RUBY_DIALOG
+
+[
+ AutoUpdate = TRUE,
+ FastCall = TRUE,
+ ReadOnlyDoc = FALSE,
+ Toggle = TRUE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Format;
+]
+
+
+SfxVoidItem RunBasic SID_BASICRUN
+()
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+ Asynchron;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Macro;
+]
+
+SfxVoidItem SaveSimple SID_SAVESIMPLE
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = FALSE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ NoRecord;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Document;
+]
+
+SfxStringItem Save SID_SAVEDOC
+(SfxStringItem VersionComment SID_DOCINFO_COMMENTS,SfxStringItem Author SID_DOCINFO_AUTHOR,SfxBoolItem DontTerminateEdit FN_PARAM_1,SfxBoolItem NoFileSync SID_NO_FILE_SYNC)
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = FALSE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+ Asynchron;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Document;
+]
+
+
+SfxVoidItem SaveAll SID_SAVEDOCS
+()
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = FALSE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Application;
+]
+
+
+SfxStringItem SaveAs SID_SAVEASDOC
+(SfxStringItem URL SID_FILE_NAME,SfxStringItem FilterName SID_FILTER_NAME,SfxStringItem Password SID_PASSWORD,SfxBoolItem PasswordInteraction SID_PASSWORDINTERACTION,SfxStringItem FilterOptions SID_FILE_FILTEROPTIONS,SfxStringItem VersionComment SID_DOCINFO_COMMENTS,SfxStringItem VersionAuthor SID_DOCINFO_AUTHOR,SfxBoolItem Overwrite SID_OVERWRITE,SfxBoolItem Unpacked SID_UNPACK,SfxBoolItem SaveTo SID_SAVETO,SfxBoolItem NoFileSync SID_NO_FILE_SYNC,SfxBoolItem NoThumbnail SID_NO_THUMBNAIL)
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Document;
+]
+
+SfxStringItem SaveAsRemote SID_SAVEASREMOTE
+(SfxStringItem URL SID_FILE_NAME,SfxStringItem FilterName SID_FILTER_NAME,SfxStringItem Password SID_PASSWORD,SfxBoolItem PasswordInteraction SID_PASSWORDINTERACTION,SfxStringItem FilterOptions SID_FILE_FILTEROPTIONS,SfxStringItem VersionComment SID_DOCINFO_COMMENTS,SfxStringItem VersionAuthor SID_DOCINFO_AUTHOR,SfxBoolItem Overwrite SID_OVERWRITE,SfxBoolItem Unpacked SID_UNPACK,SfxBoolItem SaveTo SID_SAVETO)
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Document;
+]
+
+
+SfxVoidItem SaveAsTemplate SID_DOCTEMPLATE
+(SfxStringItem TemplateRegion SID_TEMPLATE_REGIONNAME,SfxStringItem TemplateName SID_TEMPLATE_NAME)
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Template;
+]
+
+
+
+SfxVoidItem SaveACopy SID_SAVEACOPY
+(SfxStringItem URL SID_FILE_NAME,SfxStringItem FilterName SID_FILTER_NAME,SfxBoolItem Overwrite SID_OVERWRITE, SfxStringItem FilterOptions SID_FILE_FILTEROPTIONS,SfxBoolItem SaveACopy SID_SAVEACOPYITEM)
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Document;
+]
+
+SfxVoidItem SaveBasicAs SID_BASICSAVEAS
+()
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+ Asynchron;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Document;
+]
+
+
+SfxVoidItem ExportDialog SID_EXPORT_DIALOG
+()
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+ Asynchron;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Document;
+]
+
+
+SfxVoidItem ImportDialog SID_IMPORT_DIALOG
+()
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+ Asynchron;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Document;
+]
+
+
+SfxBoolItem Saved SID_DOC_SAVED
+
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = ;
+]
+
+
+SfxVoidItem SbxDeleted SID_BASICIDE_SBXDELETED
+(SbxItem Sbx SID_BASICIDE_ARG_SBX)
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::Macro;
+]
+
+
+SfxVoidItem SbxInserted SID_BASICIDE_SBXINSERTED
+(SbxItem Sbx SID_BASICIDE_ARG_SBX)
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::Macro;
+]
+
+
+SfxVoidItem SbxRenamed SID_BASICIDE_SBXRENAMED
+()
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::Macro;
+]
+
+
+SfxVoidItem ScrollBodyPageDown SID_MAIL_SCROLLBODY_PAGEDOWN
+()
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::Explorer;
+]
+
+
+SfxBoolItem SearchDialog SID_SEARCH_DLG
+[
+ AutoUpdate = TRUE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Edit;
+]
+
+
+SfxUInt16Item SearchOptions SID_SEARCH_OPTIONS
+
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::Intern;
+]
+
+
+SvxSearchItem SearchProperties SID_SEARCH_ITEM
+
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = ;
+]
+
+
+SfxVoidItem SelectAll SID_SELECTALL
+()
+[
+ AutoUpdate = FALSE,
+ FastCall = TRUE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Edit;
+]
+
+
+SfxVoidItem SendFax FN_FAX
+()
+[
+ AutoUpdate = FALSE,
+ FastCall = TRUE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+ Asynchron;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Document;
+]
+
+
+SfxStringItem SendMail SID_MAIL_SENDDOC
+(SfxStringItem Recipient SID_MAIL_RECIPIENT,SfxStringItem Subject SID_MAIL_SUBJECT,SfxStringItem MailText SID_MAIL_TEXT,SfxUInt16Item Priority SID_MAIL_PRIORITY )
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+ Asynchron;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Document;
+]
+
+SfxStringItem SendMailDocAsPDF SID_MAIL_SENDDOCASPDF
+(SfxStringItem Recipient SID_MAIL_RECIPIENT,SfxStringItem Subject SID_MAIL_SUBJECT,SfxStringItem MailText SID_MAIL_TEXT,SfxUInt16Item Priority SID_MAIL_PRIORITY)
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+ Asynchron;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Document;
+]
+
+SfxStringItem SendViaBluetooth SID_BLUETOOTH_SENDDOC
+()
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+ Asynchron;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Document;
+]
+
+
+SfxStringItem SendMailDocAsFormat SID_MAIL_SENDDOCASFORMAT
+(SfxStringItem Recipient SID_MAIL_RECIPIENT,SfxStringItem Subject SID_MAIL_SUBJECT,SfxStringItem MailText SID_MAIL_TEXT,SfxUInt16Item Priority SID_MAIL_PRIORITY,SfxStringItem TypeName SID_TYPE_NAME)
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+ Asynchron;
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::Document;
+]
+
+SfxStringItem SendMailDocAsMS SID_MAIL_SENDDOCASMS
+(SfxStringItem Recipient SID_MAIL_RECIPIENT,SfxStringItem Subject SID_MAIL_SUBJECT,SfxStringItem MailText SID_MAIL_TEXT,SfxUInt16Item Priority SID_MAIL_PRIORITY)
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+ Asynchron;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Document;
+]
+
+SfxStringItem SendMailDocAsOOo SID_MAIL_SENDDOCASOOO
+(SfxStringItem Recipient SID_MAIL_RECIPIENT,SfxStringItem Subject SID_MAIL_SUBJECT,SfxStringItem MailText SID_MAIL_TEXT,SfxUInt16Item Priority SID_MAIL_PRIORITY)
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+ Asynchron;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Document;
+]
+
+
+SfxVoidItem SetOptions SID_SETOPTIONS
+()
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+ Asynchron;
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = ;
+]
+
+
+SfxBoolItem ShowPopups SID_SHOWPOPUPS
+
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::Options;
+]
+
+
+SfxVoidItem ShowSbx SID_BASICIDE_SHOWSBX
+(SbxItem Sbx SID_BASICIDE_ARG_SBX)
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+ Asynchron;
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::View;
+]
+
+
+SfxBoolItem SourceView SID_SOURCEVIEW
+
+[
+ AutoUpdate = TRUE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = TRUE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Options;
+]
+
+
+SfxBoolItem StatusBarVisible SID_TOGGLESTATUSBAR
+
+[
+ AutoUpdate = TRUE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = TRUE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::View;
+]
+
+
+SfxStringItem StatusGetDate SID_BASICIDE_STAT_DATE
+
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::View;
+]
+
+
+SfxStringItem StatusGetPosition SID_BASICIDE_STAT_POS
+
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::View;
+]
+
+
+SfxStringItem StatusGetTitle SID_BASICIDE_STAT_TITLE
+
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::View;
+]
+
+
+SfxVoidItem StoreAllModuleSources SID_BASICIDE_STOREALLMODULESOURCES
+()
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::Macro;
+]
+
+
+SfxVoidItem StoreModuleSource SID_BASICIDE_STOREMODULESOURCE
+(SfxMacroInfoItem aMacroInfo SID_BASICIDE_ARG_MACROINFO)
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::Macro;
+]
+
+SfxVoidItem StyleEndPreview SID_STYLE_END_PREVIEW
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = FALSE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ NoRecord;
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = ;
+]
+
+SfxVoidItem StylePreview SID_STYLE_PREVIEW
+(SfxStringItem ParamName SID_STYLE_PREVIEW,SfxUInt16Item FamilyType SID_STYLE_FAMILY)
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = FALSE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ NoRecord;
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = ;
+]
+
+SfxVoidItem SidebarDeck SID_SIDEBAR_DECK
+(SfxStringItem SidebarDeck SID_SIDEBAR_DECK)
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = FALSE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = TRUE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = ;
+]
+
+SfxTemplateItem StyleApply SID_STYLE_APPLY
+(SfxStringItem Template SID_STYLE_APPLY,SfxUInt16Item Family SID_STYLE_FAMILY,SfxStringItem FamilyName SID_STYLE_FAMILYNAME,SfxStringItem Style SID_APPLY_STYLE)
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = FALSE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Document;
+]
+
+
+SfxVoidItem StyleNewByExample SID_STYLE_NEW_BY_EXAMPLE
+(SfxStringItem Param SID_STYLE_NEW_BY_EXAMPLE,SfxUInt16Item Family SID_STYLE_FAMILY)
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = FALSE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Template;
+]
+
+
+SfxVoidItem StyleUpdateByExample SID_STYLE_UPDATE_BY_EXAMPLE
+(SfxStringItem Param SID_STYLE_UPDATE_BY_EXAMPLE,SfxUInt16Item Family SID_STYLE_FAMILY)
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = FALSE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Template;
+]
+
+
+SfxBoolItem StyleWatercanMode SID_STYLE_WATERCAN
+
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = FALSE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::Format;
+]
+
+SfxVoidItem ClassificationApply SID_CLASSIFICATION_APPLY
+(SfxStringItem Name SID_CLASSIFICATION_APPLY, SfxStringItem Type SID_TYPE_NAME)
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = FALSE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Document;
+]
+
+SfxWatermarkItem Watermark SID_WATERMARK
+(SfxStringItem Text SID_WATERMARK, SfxStringItem Font SID_WATERMARK_FONT,
+ SfxInt16Item Angle SID_WATERMARK_ANGLE, SfxInt16Item Transparency SID_WATERMARK_TRANSPARENCY,
+ SfxUInt32Item Color SID_WATERMARK_COLOR
+)
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = FALSE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Document;
+]
+
+SfxUInt16Item SwitchViewShell SID_VIEWSHELL
+
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+ Asynchron;
+
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::View;
+]
+
+SfxTemplateItem ListStyle SID_STYLE_FAMILY5
+
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::Document;
+]
+
+SfxTemplateItem TableStyle SID_STYLE_FAMILY6
+
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::Document;
+]
+
+SfxStringItem Title SID_DOCTITLE
+
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = ;
+]
+
+SfxVoidItem ToggleBreakPoint SID_BASICIDE_TOGGLEBRKPNT
+()
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+ Asynchron;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Macro;
+]
+
+
+SfxVoidItem BasicIDEShowWindow SID_BASICIDE_SHOWWINDOW
+(SfxStringItem Document SID_BASICIDE_ARG_DOCUMENT,SfxStringItem LibName SID_BASICIDE_ARG_LIBNAME,
+ SfxStringItem Name SID_BASICIDE_ARG_NAME,SfxStringItem Type SID_BASICIDE_ARG_TYPE,
+ SfxUInt32Item Line SID_BASICIDE_ARG_LINE,SfxUInt16Item Column1 SID_BASICIDE_ARG_COLUMN1,
+ SfxUInt16Item Column2 SID_BASICIDE_ARG_COLUMN2)
+[
+ AutoUpdate = FALSE,
+ FastCall = TRUE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::Application;
+]
+
+
+SfxStringItem Undo SID_UNDO
+( SfxUInt16Item Undo SID_UNDO, SfxBoolItem Repair SID_REPAIRPACKAGE )
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = FALSE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Edit;
+]
+
+
+SfxBoolItem FormatPaintbrush SID_FORMATPAINTBRUSH ( SfxBoolItem PersistentCopy SID_FORMATPAINTBRUSH )
+[
+ AutoUpdate = TRUE,
+ FastCall = FALSE,
+ ReadOnlyDoc = FALSE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Edit;
+]
+
+
+SfxUInt16Item UndoCount SID_ATTR_UNDO_COUNT
+
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = ;
+]
+
+
+SfxVoidItem UpdateAllModuleSources SID_BASICIDE_UPDATEALLMODULESOURCES
+()
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::Macro;
+]
+
+
+SfxVoidItem UpdateModuleSource SID_BASICIDE_UPDATEMODULESOURCE
+(SfxMacroInfoItem aMacroInfo SID_BASICIDE_ARG_MACROINFO)
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::Macro;
+]
+
+
+SfxVoidItem ManageBreakPoints SID_BASICIDE_MANAGEBRKPNTS
+()
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+ Asynchron;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Macro;
+]
+
+
+SfxVoidItem ToggleBreakPointEnabled SID_BASICIDE_TOGGLEBRKPNTENABLED
+()
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+ Asynchron;
+
+ AccelConfig = TRUE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::Macro;
+]
+
+SfxVoidItem VersionDialog SID_VERSION
+()
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ NoRecord;
+ Asynchron;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Document;
+]
+
+SfxUInt16Item Signature SID_SIGNATURE
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ NoRecord;
+ Asynchron;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Document;
+]
+
+
+SfxUInt16Item MacroSignature SID_MACRO_SIGNATURE
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ NoRecord;
+ Asynchron;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Document;
+]
+
+
+SfxBoolItem ViewDataSourceBrowser SID_VIEW_DATA_SOURCE_BROWSER
+
+[
+ AutoUpdate = TRUE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::View;
+]
+
+SfxVoidItem ZoomPlus SID_ZOOM_IN
+()
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::View;
+]
+
+
+SfxVoidItem ZoomNext SID_ZOOM_NEXT
+()
+[
+ AutoUpdate = TRUE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::View;
+]
+
+
+SfxVoidItem ZoomMinus SID_ZOOM_OUT
+()
+[
+ AutoUpdate = TRUE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::View;
+]
+
+
+SfxVoidItem ZoomPrevious SID_ZOOM_PREV
+()
+[
+ AutoUpdate = TRUE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::View;
+]
+
+
+SfxVoidItem ZoomToolBox SID_ZOOM_TOOLBOX
+
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::View;
+]
+
+
+SfxVoidItem ExportTo SID_EXPORTDOC
+(SfxStringItem URL SID_FILE_NAME,SfxStringItem FilterName SID_FILTER_NAME,SfxBoolItem Overwrite SID_OVERWRITE, SfxStringItem FilterOptions SID_FILE_FILTEROPTIONS,SfxBoolItem SaveACopy SID_SAVEACOPYITEM)
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Document;
+]
+
+
+SfxVoidItem ExportToPDF SID_EXPORTDOCASPDF
+(SfxStringItem URL SID_FILE_NAME, SfxStringItem FilterName SID_FILTER_NAME)
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Document;
+]
+
+SfxVoidItem ExportDirectToPDF SID_DIRECTEXPORTDOCASPDF
+(SfxStringItem URL SID_FILE_NAME, SfxStringItem FilterName SID_FILTER_NAME,
+ SfxBoolItem IsRedactMode SID_IS_REDACT_MODE, SfxStringItem RedactionStyle SID_REDACTION_STYLE)
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Document;
+]
+
+SfxVoidItem ExportToEPUB SID_EXPORTDOCASEPUB
+(SfxStringItem URL SID_FILE_NAME, SfxStringItem FilterName SID_FILTER_NAME)
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Document;
+]
+
+SfxVoidItem ExportDirectToEPUB SID_DIRECTEXPORTDOCASEPUB
+(SfxStringItem URL SID_FILE_NAME, SfxStringItem FilterName SID_FILTER_NAME)
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Document;
+]
+
+SfxVoidItem RedactDoc SID_REDACTDOC
+(SfxStringItem URL SID_FILE_NAME, SfxStringItem FilterName SID_FILTER_NAME)
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Document;
+]
+
+SfxVoidItem AutoRedactDoc SID_AUTOREDACTDOC
+(SfxStringItem URL SID_FILE_NAME, SfxStringItem FilterName SID_FILTER_NAME)
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Document;
+]
+
+SfxImageItem ImageOrientation SID_IMAGE_ORIENTATION
+
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::View;
+]
+
+SfxVoidItem SendFeedback SID_SEND_FEEDBACK
+()
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Application;
+]
+SfxVoidItem QuestionAnswers SID_Q_AND_A
+()
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ GroupId = SfxGroupId::Application;
+]
+SfxVoidItem Documentation SID_DOCUMENTATION
+()
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ GroupId = SfxGroupId::Application;
+]
+SfxVoidItem Donation SID_DONATION
+()
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ GroupId = SfxGroupId::Application;
+]
+SfxVoidItem GetInvolved SID_GETINVOLVED
+()
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ GroupId = SfxGroupId::Application;
+]
+SfxVoidItem WhatsNew SID_WHATSNEW
+()
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ GroupId = SfxGroupId::Application;
+]
+SfxVoidItem HyphenationMissing SID_HYPHENATIONMISSING
+()
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ GroupId = SfxGroupId::Application;
+]
+SfxVoidItem ShowLicense SID_SHOW_LICENSE
+()
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Application;
+]
+
+
+SfxVoidItem ShowCredits SID_SHOW_CREDITS
+()
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Application;
+]
+
+
+SfxVoidItem InternetDialog SID_INET_DLG
+()
+[
+ AutoUpdate = FALSE,
+ FastCall = TRUE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Options;
+]
+
+
+SfxVoidItem CheckPLZ SID_OFFICE_CHECK_PLZ
+(SfxStringItem PLZ SID_OFFICE_CHECK_PLZ)
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::Special;
+]
+
+
+SfxVoidItem AutoPilotAddressDataSource SID_ADDRESS_DATA_SOURCE
+()
+[
+ AutoUpdate = FALSE,
+ FastCall = TRUE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Application;
+]
+
+
+SfxVoidItem InsertBusinessCard FN_BUSINESS_CARD
+( SfxUnoFrameItem Frame SID_FILLFRAME, SfxBoolItem Hidden SID_HIDDEN )
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = FALSE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+ Asynchron;
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Insert;
+]
+
+
+SfxVoidItem InsertLabels FN_LABEL
+( SfxUnoFrameItem Frame SID_FILLFRAME, SfxBoolItem Hidden SID_HIDDEN )
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = FALSE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+ Asynchron;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Insert;
+]
+
+
+SfxVoidItem NewXForms FN_XFORMS_INIT
+( SfxUnoFrameItem Frame SID_FILLFRAME, SfxBoolItem Hidden SID_HIDDEN )
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = FALSE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+ Asynchron;
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::Insert;
+]
+
+
+SfxVoidItem NewPresentation SID_NEWSD
+( SfxUnoFrameItem Frame SID_FILLFRAME, SfxBoolItem Hidden SID_HIDDEN )
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = FALSE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::Application;
+]
+
+
+SfxVoidItem BibliographyComponent SID_COMP_BIBLIOGRAPHY
+()
+[
+ AutoUpdate = FALSE,
+ FastCall = TRUE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Application;
+]
+
+SfxVoidItem AutoCorrectDlg SID_AUTO_CORRECT_DLG
+(SfxBoolItem ShowSwOptions SID_AUTO_CORRECT_DLG, SfxBoolItem OpenSmartTag SID_OPEN_SMARTTAGOPTIONS)
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Options;
+]
+
+SfxVoidItem OptionsTreeDialog SID_OPTIONS_TREEDIALOG
+()
+[
+ AutoUpdate = FALSE,
+ FastCall = TRUE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Options;
+]
+
+SfxVoidItem TerminateInplaceActivation SID_TERMINATE_INPLACEACTIVATION
+()
+[
+ AutoUpdate = FALSE,
+ FastCall = TRUE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+]
+
+
+SfxVoidItem RecentFileList SID_RECENTFILELIST
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = FALSE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::Application;
+]
+
+SfxVoidItem ToolbarMode SID_TOOLBAR_MODE
+(SfxStringItem Mode SID_TOOLBAR_MODE)
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = FALSE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::Application;
+]
+
+SfxVoidItem ToolbarModeUI SID_TOOLBAR_MODE_UI
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = FALSE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Application;
+]
+
+SfxVoidItem AvailableToolbars SID_AVAILABLE_TOOLBARS
+(SfxStringItem Toolbar SID_AVAILABLE_TOOLBARS)
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = FALSE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::Application;
+]
+
+SfxVoidItem AVMediaPlayer SID_AVMEDIA_PLAYER
+()
+[
+ AutoUpdate = TRUE,
+ FastCall = TRUE,
+ ReadOnlyDoc = TRUE,
+ Toggle = TRUE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Application;
+]
+
+SfxVoidItem InsertAVMedia SID_INSERT_AVMEDIA
+(
+ SfxStringItem URL SID_INSERT_AVMEDIA,
+ SvxSizeItem Size FN_PARAM_1,
+ SfxBoolItem IsLink FN_PARAM_2
+)
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = FALSE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Application;
+]
+
+SfxVoidItem MoreDictionaries SID_MORE_DICTIONARIES
+()
+[
+ AutoUpdate = FALSE,
+ FastCall = TRUE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = TRUE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = FALSE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Options;
+]
+
+SfxVoidItem ActivateStyleApply SID_ACTIVATE_STYLE_APPLY
+()
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = FALSE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+ Asynchron;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Template;
+]
+
+
+SfxBoolItem DockingWindow0 SID_DOCKWIN_0
+
+[
+ AutoUpdate = TRUE,
+ FastCall = TRUE,
+ ReadOnlyDoc = FALSE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::Application;
+]
+
+
+SfxBoolItem DockingWindow1 SID_DOCKWIN_1
+
+[
+ AutoUpdate = TRUE,
+ FastCall = TRUE,
+ ReadOnlyDoc = FALSE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::Application;
+]
+
+
+SfxBoolItem DockingWindow2 SID_DOCKWIN_2
+
+[
+ AutoUpdate = TRUE,
+ FastCall = TRUE,
+ ReadOnlyDoc = FALSE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::Application;
+]
+
+
+SfxBoolItem DockingWindow3 SID_DOCKWIN_3
+
+[
+ AutoUpdate = TRUE,
+ FastCall = TRUE,
+ ReadOnlyDoc = FALSE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::Application;
+]
+
+
+SfxBoolItem DockingWindow4 SID_DOCKWIN_4
+
+[
+ AutoUpdate = TRUE,
+ FastCall = TRUE,
+ ReadOnlyDoc = FALSE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::Application;
+]
+
+
+SfxBoolItem DockingWindow5 SID_DOCKWIN_5
+
+[
+ AutoUpdate = TRUE,
+ FastCall = TRUE,
+ ReadOnlyDoc = FALSE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::Application;
+]
+
+
+SfxBoolItem DockingWindow6 SID_DOCKWIN_6
+
+[
+ AutoUpdate = TRUE,
+ FastCall = TRUE,
+ ReadOnlyDoc = FALSE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::Application;
+]
+
+
+SfxBoolItem DockingWindow7 SID_DOCKWIN_7
+
+[
+ AutoUpdate = TRUE,
+ FastCall = TRUE,
+ ReadOnlyDoc = FALSE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::Application;
+]
+
+
+SfxBoolItem DockingWindow8 SID_DOCKWIN_8
+
+[
+ AutoUpdate = TRUE,
+ FastCall = TRUE,
+ ReadOnlyDoc = FALSE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::Application;
+]
+
+
+SfxBoolItem DockingWindow9 SID_DOCKWIN_9
+
+[
+ AutoUpdate = TRUE,
+ FastCall = TRUE,
+ ReadOnlyDoc = FALSE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::Application;
+]
+
+
+SfxVoidItem PasteUnformatted SID_PASTE_UNFORMATTED
+()
+[
+ AutoUpdate = FALSE,
+ FastCall = TRUE,
+ ReadOnlyDoc = FALSE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+ Asynchron;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::Edit;
+]
+
+
+// call thesaurus dialog from context menu
+SfxVoidItem ThesaurusFromContext SID_THES
+(SfxStringItem WordReplace FN_PARAM_THES_WORD_REPLACE)
+[
+ AutoUpdate = FALSE,
+ FastCall = TRUE,
+ ReadOnlyDoc = FALSE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = FALSE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::Text;
+]
+
+
+SvxZoomItem Zoom SID_ATTR_ZOOM
+
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+ Asynchron;
+
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::View;
+]
+
+SfxVoidItem SafeMode SID_SAFE_MODE
+()
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Application;
+]
+
+SfxBoolItem DevelopmentToolsDockingWindow SID_DEVELOPMENT_TOOLS_DOCKING_WINDOW
+[
+ AutoUpdate = TRUE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = TRUE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Application;
+]
+
+SfxVoidItem InspectSelectedObject SID_INSPECT_SELECTED_OBJECT
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::View;
+]
+
+SfxVoidItem UnicodeNotationToggle SID_UNICODE_NOTATION_TOGGLE
+()
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = FALSE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = TRUE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = FALSE,
+ GroupId = SfxGroupId::Options;
+]
+
+SfxVoidItem AdditionsDialog SID_ADDITIONS_DIALOG
+(SfxStringItem AdditionsTag FN_PARAM_ADDITIONS_TAG)
+[
+ AutoUpdate = FALSE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = FALSE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = TRUE,
+ MenuConfig = TRUE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Application;
+]
+
+SfxBoolItem ToolbarLock SID_TOOLBAR_LOCK
+[
+ AutoUpdate = TRUE,
+ FastCall = FALSE,
+ ReadOnlyDoc = TRUE,
+ Toggle = TRUE,
+ Container = FALSE,
+ RecordAbsolute = FALSE,
+ RecordPerSet;
+
+ AccelConfig = FALSE,
+ MenuConfig = FALSE,
+ ToolBoxConfig = TRUE,
+ GroupId = SfxGroupId::Application;
+]
diff --git a/sfx2/sdi/sfxitems.sdi b/sfx2/sdi/sfxitems.sdi
new file mode 100644
index 000000000..231548e5a
--- /dev/null
+++ b/sfx2/sdi/sfxitems.sdi
@@ -0,0 +1,118 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+ item void SfxVoidItem;
+ item BOOL SfxBoolItem;
+ item INT32 SfxUInt16Item;
+ item INT16 SfxInt16Item;
+ item INT32 SfxUInt32Item;
+ item INT32 SfxInt32Item;
+ item String SfxStringItem;
+ item BYTE SfxByteItem;
+ item INT16 SfxEnumItem;
+
+ item String SbxItem; //! Dummy
+ item String SfxObjectItem; //! Dummy
+ item String SfxTemplateItem; //! Dummy
+ item String SfxMacroInfoItem; //! Dummy
+ item String SfxImageItem; //! Dummy
+ item String SfxObjectShellItem //! Dummy
+ item String SfxUnoAnyItem //! Dummy
+ item String SfxUnoFrameItem //! Dummy
+ item String SfxWatermarkItem //! Dummy
+
+ struct Point
+ {
+ INT32 X MID_X;
+ INT32 Y MID_Y;
+ };
+ item Point SfxPointItem;
+
+ struct Rectangle
+ {
+ INT32 Left MID_RECT_LEFT;
+ INT32 Top MID_RECT_TOP;
+ INT32 Width MID_WIDTH;
+ INT32 Height MID_HEIGHT;
+ };
+ item Rectangle SfxRectangleItem;
+
+ struct DocInfo
+ {
+ BOOL UseUserData MID_DOCINFO_USEUSERDATA;
+ BOOL DeleteUserData MID_DOCINFO_DELETEUSERDATA;
+ String Title MID_DOCINFO_TITLE;
+ String Subject MID_DOCINFO_SUBJECT;
+ String KeyWords MID_DOCINFO_KEYWORDS;
+ String Description MID_DOCINFO_DESCRIPTION;
+ BOOL AutoReload MID_DOCINFO_AUTOLOADENABLED;
+ INT32 AutoReloadTime MID_DOCINFO_AUTOLOADSECS;
+ String AutoReloadURL MID_DOCINFO_AUTOLOADURL;
+ String AutoReloadFrame MID_DOCINFO_DEFAULTTARGET;
+ };
+ item DocInfo SfxDocumentInfoItem;
+
+ struct SvxSearch
+ {
+ INT16 StyleFamily MID_SEARCH_STYLEFAMILY;
+ UINT16 CellType MID_SEARCH_CELLTYPE;
+ BOOL RowDirection MID_SEARCH_ROWDIRECTION;
+ BOOL AllTables MID_SEARCH_ALLTABLES;
+ BOOL SearchFiltered MID_SEARCH_SEARCHFILTERED;
+ BOOL Backward MID_SEARCH_BACKWARD;
+ BOOL Pattern MID_SEARCH_PATTERN;
+ BOOL Content MID_SEARCH_CONTENT;
+ BOOL AsianOptions MID_SEARCH_ASIANOPTIONS;
+ INT16 AlgorithmType MID_SEARCH_ALGORITHMTYPE;
+ INT32 SearchFlags MID_SEARCH_FLAGS;
+ String SearchString MID_SEARCH_SEARCHSTRING;
+ String ReplaceString MID_SEARCH_REPLACESTRING;
+ INT16 Locale MID_SEARCH_LOCALE;
+ INT32 ChangedChars MID_SEARCH_CHANGEDCHARS;
+ INT32 DeletedChars MID_SEARCH_DELETEDCHARS;
+ INT32 InsertedChars MID_SEARCH_INSERTEDCHARS;
+ INT32 TransliterateFlags MID_SEARCH_TRANSLITERATEFLAGS;
+ INT16 Command MID_SEARCH_COMMAND;
+ INT32 SearchStartPointX MID_SEARCH_STARTPOINTX;
+ INT32 SearchStartPointY MID_SEARCH_STARTPOINTY;
+ BOOL SearchFormatted MID_SEARCH_SEARCHFORMATTED;
+ INT16 AlgorithmType2 MID_SEARCH_ALGORITHMTYPE2;
+ };
+ item SvxSearch SvxSearchItem;
+
+ struct SvxSize
+ {
+ INT32 Width MID_SIZE_WIDTH;
+ INT32 Height MID_SIZE_HEIGHT;
+ };
+ item SvxSize SvxSizeItem;
+
+ struct SfxScriptOrganizer
+ {
+ String Language MID_SCRIPT_ORGANIZER_LANGUAGE;
+ };
+ item SfxScriptOrganizer SfxScriptOrganizerItem;
+ item String SvxClipboardFormatItem; //! Dummy
+
+ struct SvxZoom
+ {
+ UINT16 Value MID_VALUE;
+ INT16 ValueSet MID_VALUESET;
+ INT16 Type MID_TYPE;
+ };
+ item SvxZoom SvxZoomItem;
diff --git a/sfx2/sdi/sfxslots.sdi b/sfx2/sdi/sfxslots.sdi
new file mode 100644
index 000000000..75f2137dd
--- /dev/null
+++ b/sfx2/sdi/sfxslots.sdi
@@ -0,0 +1,33 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+module
+StarApplicationFrame
+[
+SlotIdFile( "sfx2/sfxsids.hrc" )
+]
+{
+ include "sfxitems.sdi"
+ include "sfx.sdi"
+ include "docslots.sdi"
+ include "viwslots.sdi"
+ include "frmslots.sdi"
+ include "appslots.sdi"
+
+}
+
diff --git a/sfx2/sdi/viwslots.sdi b/sfx2/sdi/viwslots.sdi
new file mode 100644
index 000000000..111180b30
--- /dev/null
+++ b/sfx2/sdi/viwslots.sdi
@@ -0,0 +1,93 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+interface View
+{
+
+ SID_MAIL_SENDDOC // ole(no) api(todo)
+ [
+ ExecMethod = ExecMisc_Impl ;
+ StateMethod = GetState_Impl ;
+ ]
+ SID_MAIL_SENDDOCASPDF // ole(no) api(todo)
+ [
+ ExecMethod = ExecMisc_Impl ;
+ StateMethod = GetState_Impl ;
+ ]
+ SID_WEBHTML // ole(no) api(todo)
+ [
+ ExecMethod = ExecMisc_Impl ;
+ StateMethod = GetState_Impl ;
+ ]
+ SID_MAIL_SENDDOCASFORMAT // ole(no) api(todo)
+ [
+ ExecMethod = ExecMisc_Impl ;
+ StateMethod = GetState_Impl ;
+ ]
+ SID_MAIL_SENDDOCASMS // ole(no) api(todo)
+ [
+ ExecMethod = ExecMisc_Impl ;
+ StateMethod = GetState_Impl ;
+ ]
+ SID_MAIL_SENDDOCASOOO // ole(no) api(todo)
+ [
+ ExecMethod = ExecMisc_Impl ;
+ StateMethod = GetState_Impl ;
+ ]
+ SID_BLUETOOTH_SENDDOC // ole(no) api(todo)
+ [
+ ExecMethod = ExecMisc_Impl ;
+ StateMethod = GetState_Impl ;
+ ]
+
+ SID_PRINTDOC // ole(no) api(play/rec)
+ [
+ ExecMethod = ExecPrint_Impl ;
+ StateMethod = GetState_Impl ;
+ ]
+ SID_PRINTDOCDIRECT // ole(no) api(final/play/rec)
+ [
+ ExecMethod = ExecPrint_Impl ;
+ StateMethod = GetState_Impl ;
+ ]
+ SID_PRINTER_NAME // ole(no) api(play/rec)
+ [
+ ExecMethod = ExecPrint_Impl ;
+ ]
+ SID_SETUPPRINTER // ole(no) api(play/rec)
+ [
+ ExecMethod = ExecPrint_Impl ;
+ StateMethod = GetState_Impl ;
+ ]
+ SID_ACTIVATE_STYLE_APPLY
+ [
+ ExecMethod = ExecMisc_Impl ;
+ ]
+ SID_STYLE_FAMILY
+ [
+ ExecMethod = ExecMisc_Impl ;
+ StateMethod = GetState_Impl ;
+ ]
+}
+
+
+
+shell SfxViewShell
+{
+ import View;
+}
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: */
diff --git a/sfx2/uiconfig/ui/addtargetdialog.ui b/sfx2/uiconfig/ui/addtargetdialog.ui
new file mode 100644
index 000000000..09c704779
--- /dev/null
+++ b/sfx2/uiconfig/ui/addtargetdialog.ui
@@ -0,0 +1,278 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.22.1 -->
+<interface domain="sfx">
+ <requires lib="gtk+" version="3.20"/>
+ <object class="GtkDialog" id="AddTargetDialog">
+ <property name="can_focus">False</property>
+ <property name="border_width">6</property>
+ <property name="title" translatable="yes" context="addtargetdialog|AddTargetDialog">Add Target</property>
+ <property name="modal">True</property>
+ <property name="default_width">0</property>
+ <property name="default_height">0</property>
+ <property name="type_hint">dialog</property>
+ <child>
+ <placeholder/>
+ </child>
+ <child internal-child="vbox">
+ <object class="GtkBox" id="dialog-vbox1">
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">12</property>
+ <child internal-child="action_area">
+ <object class="GtkButtonBox" id="dialog-action_area">
+ <property name="can_focus">False</property>
+ <property name="layout_style">end</property>
+ <child>
+ <object class="GtkButton" id="close">
+ <property name="label" translatable="yes" context="stock">_OK</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use-underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="cancel">
+ <property name="label" translatable="yes" context="stock">_Cancel</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use-underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="help">
+ <property name="label" translatable="yes" context="stock">_Help</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use-underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ <property name="secondary">True</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">end</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <!-- n-columns=1 n-rows=1 -->
+ <object class="GtkGrid">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="row_spacing">6</property>
+ <property name="column_spacing">12</property>
+ <child>
+ <object class="GtkLabel" id="label_name">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes" context="addtargetdialog|label_name">Name:</property>
+ <property name="use_underline">True</property>
+ <accessibility>
+ <relation type="label-for" target="name"/>
+ </accessibility>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="name">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hexpand">True</property>
+ <property name="activates_default">True</property>
+ <property name="truncate-multiline">True</property>
+ <accessibility>
+ <relation type="labelled-by" target="label_name"/>
+ </accessibility>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label_type">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes" context="addtargetdialog|label_type">Type:</property>
+ <property name="use_underline">True</property>
+ <accessibility>
+ <relation type="label-for" target="type"/>
+ </accessibility>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label_content">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes" context="addtargetdialog|label_content">Content:</property>
+ <property name="use_underline">True</property>
+ <accessibility>
+ <relation type="label-for" target="content"/>
+ </accessibility>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="content">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hexpand">True</property>
+ <property name="activates_default">True</property>
+ <property name="truncate-multiline">True</property>
+ <accessibility>
+ <relation type="labelled-by" target="label_content"/>
+ </accessibility>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBoxText" id="type">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="active">0</property>
+ <items>
+ <item id="text" translatable="yes" context="addtargetdialog|type">Text</item>
+ <item id="regex" translatable="yes" context="addtargetdialog|type">Regular expression</item>
+ <item id="predefined" translatable="yes" context="addtargetdialog|type">Predefined</item>
+ </items>
+ <accessibility>
+ <relation type="labelled-by" target="label_type"/>
+ </accessibility>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label_content_predef">
+ <property name="sensitive">False</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes" context="addtargetdialog|label_content_predef">Content:</property>
+ <property name="use_underline">True</property>
+ <accessibility>
+ <relation type="label-for" target="content_predef"/>
+ </accessibility>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBoxText" id="content_predef">
+ <property name="sensitive">False</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="active">0</property>
+ <items>
+ <item id="creditcard" translatable="yes" context="addtargetdialog|content_predef">Credit card numbers</item>
+ <item id="email" translatable="yes" context="addtargetdialog|content_predef">Email addresses</item>
+ <item id="ip" translatable="yes" context="addtargetdialog|content_predef">IP addresses</item>
+ <item id="numdates" translatable="yes" context="addtargetdialog|content_predef">Dates (Numerical)</item>
+ <item id="uknin" translatable="yes" context="addtargetdialog|content_predef">National Insurance Number (UK)</item>
+ <item id="usssn" translatable="yes" context="addtargetdialog|content_predef">Social Security Number (US)</item>
+ </items>
+ <accessibility>
+ <relation type="labelled-by" target="label_content_predef"/>
+ </accessibility>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">3</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkCheckButton" id="checkboxCaseSensitive">
+ <property name="label" translatable="yes" context="addtargetdialog|checkboxCaseSensitive">Match case</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_underline">True</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="checkboxWholeWords">
+ <property name="label" translatable="yes" context="addtargetdialog|checkboxWholeWords">Whole words only</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_underline">True</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">end</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <action-widgets>
+ <action-widget response="-5">close</action-widget>
+ <action-widget response="-6">cancel</action-widget>
+ <action-widget response="-11">help</action-widget>
+ </action-widgets>
+ </object>
+</interface>
diff --git a/sfx2/uiconfig/ui/alienwarndialog.ui b/sfx2/uiconfig/ui/alienwarndialog.ui
new file mode 100644
index 000000000..d142433c5
--- /dev/null
+++ b/sfx2/uiconfig/ui/alienwarndialog.ui
@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.22.1 -->
+<interface domain="sfx">
+ <requires lib="gtk+" version="3.20"/>
+ <object class="GtkMessageDialog" id="AlienWarnDialog">
+ <property name="can_focus">False</property>
+ <property name="title" translatable="yes" context="alienwarndialog|AlienWarnDialog">Confirm File Format</property>
+ <property name="resizable">False</property>
+ <property name="modal">True</property>
+ <property name="type_hint">dialog</property>
+ <property name="skip_taskbar_hint">True</property>
+ <property name="message_type">question</property>
+ <property name="text" translatable="yes" context="alienwarndialog|AlienWarnDialog">This document may contain formatting or content that cannot be saved in the currently selected file format “%FORMATNAME”.</property>
+ <property name="secondary_text" translatable="yes" context="alienwarndialog|AlienWarnDialog">Use the default ODF file format to be sure that the document is saved correctly.</property>
+ <child internal-child="vbox">
+ <object class="GtkBox" id="messagedialog-vbox">
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">12</property>
+ <child internal-child="action_area">
+ <object class="GtkButtonBox" id="messagedialog-action_area">
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkButton" id="cancel">
+ <property name="label" translatable="yes" context="alienwarndialog|cancel">Use %DEFAULTEXTENSION _Format</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ <property name="non_homogeneous">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="save">
+ <property name="label" translatable="yes" context="alienwarndialog|save">_Use %FORMATNAME Format</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="has_default">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ <property name="non_homogeneous">True</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">end</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="ask">
+ <property name="label" translatable="yes" context="alienwarndialog|ask">_Ask when not saving in ODF or default format</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_underline">True</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <action-widgets>
+ <action-widget response="-6">cancel</action-widget>
+ <action-widget response="-5">save</action-widget>
+ </action-widgets>
+ </object>
+</interface>
diff --git a/sfx2/uiconfig/ui/autoredactdialog.ui b/sfx2/uiconfig/ui/autoredactdialog.ui
new file mode 100644
index 000000000..b4eda210b
--- /dev/null
+++ b/sfx2/uiconfig/ui/autoredactdialog.ui
@@ -0,0 +1,322 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.36.0 -->
+<interface domain="sfx">
+ <requires lib="gtk+" version="3.20"/>
+ <object class="GtkTreeStore" id="liststore1">
+ <columns>
+ <!-- column-name sTargetName -->
+ <column type="gchararray"/>
+ <!-- column-name sType -->
+ <column type="gchararray"/>
+ <!-- column-name sContent -->
+ <column type="gchararray"/>
+ <!-- column-name bIsCaseSensitive -->
+ <column type="gchararray"/>
+ <!-- column-name bWholeWords -->
+ <column type="gchararray"/>
+ <!-- column-name id -->
+ <column type="gchararray"/>
+ </columns>
+ </object>
+ <object class="GtkDialog" id="AutoRedactDialog">
+ <property name="can_focus">False</property>
+ <property name="border_width">6</property>
+ <property name="title" translatable="yes" context="autoredactdialog|AutoRedactDialog">Automatic Redaction</property>
+ <property name="modal">True</property>
+ <property name="type_hint">dialog</property>
+ <child internal-child="vbox">
+ <object class="GtkBox" id="dialog-vbox1">
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">12</property>
+ <child internal-child="action_area">
+ <object class="GtkButtonBox">
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkButton" id="help">
+ <property name="label" translatable="yes" context="stock">_Help</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use-underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ <property name="secondary">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="ok">
+ <property name="label" translatable="yes" context="stock">_OK</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use-underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="cancel">
+ <property name="label" translatable="yes" context="stock">_Cancel</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use-underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="pack_type">end</property>
+ <property name="position">5</property>
+ </packing>
+ </child>
+ <child>
+ <!-- n-columns=1 n-rows=1 -->
+ <object class="GtkGrid">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="row_spacing">6</property>
+ <property name="column_spacing">12</property>
+ <child>
+ <object class="GtkScrolledWindow">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkTreeView" id="targets">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="margin-end">5</property>
+ <property name="margin_bottom">5</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="model">liststore1</property>
+ <property name="search_column">0</property>
+ <property name="show_expanders">False</property>
+ <child internal-child="selection">
+ <object class="GtkTreeSelection" id="Macro Library List-selection2"/>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="treeviewcolumn0">
+ <property name="resizable">True</property>
+ <property name="spacing">6</property>
+ <property name="title" translatable="yes" context="autoredactdialog|target">Target Name</property>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderer0"/>
+ <attributes>
+ <attribute name="text">0</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="treeviewcolumn1">
+ <property name="resizable">True</property>
+ <property name="spacing">6</property>
+ <property name="title" translatable="yes" context="autoredactdialog|description">Type</property>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderer1"/>
+ <attributes>
+ <attribute name="text">1</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="treeviewcolumn2">
+ <property name="resizable">True</property>
+ <property name="spacing">6</property>
+ <property name="title" translatable="yes" context="autoredactdialog|target">Content</property>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderer2"/>
+ <attributes>
+ <attribute name="text">2</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="treeviewcolumn3">
+ <property name="resizable">True</property>
+ <property name="spacing">6</property>
+ <property name="title" translatable="yes" context="autoredactdialog|target">Match case</property>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderer3"/>
+ <attributes>
+ <attribute name="text">3</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="treeviewcolumn4">
+ <property name="resizable">True</property>
+ <property name="spacing">6</property>
+ <property name="title" translatable="yes" context="autoredactdialog|target">Whole words</property>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderer4"/>
+ <attributes>
+ <attribute name="text">4</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ <accessibility>
+ <relation type="labelled-by" target="labelRedactionTargets"/>
+ </accessibility>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="labelRedactionTargets">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="halign">start</property>
+ <property name="valign">center</property>
+ <property name="margin-end">1</property>
+ <property name="margin_bottom">1</property>
+ <property name="label" translatable="yes" context="menuassignpage|contentslabel">_Redaction Targets</property>
+ <property name="use_underline">True</property>
+ <accessibility>
+ <relation type="label-for" target="targets"/>
+ </accessibility>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButtonBox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">6</property>
+ <property name="layout_style">start</property>
+ <child>
+ <object class="GtkButton" id="btnLoadTargets">
+ <property name="label" translatable="yes" context="autoredactdialog|btnLoadTargets">Load Targets</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="btnSaveTargets">
+ <property name="label" translatable="yes" context="autoredactdialog|btnSaveTargets">Save Targets</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSeparator" id="separator">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="can_focus">False</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ <property name="non_homogeneous">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="add">
+ <property name="label" translatable="yes" context="autoredactdialog|add">Add Target</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="edit">
+ <property name="label" translatable="yes" context="autoredactdialog|edit">Edit Target</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">4</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="delete">
+ <property name="label" translatable="yes" context="autoredactdialog|delete">Delete Target</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">5</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <action-widgets>
+ <action-widget response="-11">help</action-widget>
+ <action-widget response="-5">ok</action-widget>
+ <action-widget response="-6">cancel</action-widget>
+ </action-widgets>
+ </object>
+</interface>
diff --git a/sfx2/uiconfig/ui/bookmarkdialog.ui b/sfx2/uiconfig/ui/bookmarkdialog.ui
new file mode 100644
index 000000000..dfa59e2c2
--- /dev/null
+++ b/sfx2/uiconfig/ui/bookmarkdialog.ui
@@ -0,0 +1,141 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.22.1 -->
+<interface domain="sfx">
+ <requires lib="gtk+" version="3.20"/>
+ <object class="GtkDialog" id="BookmarkDialog">
+ <property name="can_focus">False</property>
+ <property name="border_width">6</property>
+ <property name="title" translatable="yes" context="bookmarkdialog|BookmarkDialog">Add to Bookmarks</property>
+ <property name="modal">True</property>
+ <property name="default_width">0</property>
+ <property name="default_height">0</property>
+ <property name="type_hint">dialog</property>
+ <child>
+ <placeholder/>
+ </child>
+ <child internal-child="vbox">
+ <object class="GtkBox" id="dialog-vbox1">
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">12</property>
+ <child internal-child="action_area">
+ <object class="GtkButtonBox" id="dialog-action_area1">
+ <property name="can_focus">False</property>
+ <property name="layout_style">end</property>
+ <child>
+ <object class="GtkButton" id="ok">
+ <property name="label" translatable="yes" context="stock">_OK</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="has_default">True</property>
+ <property name="receives_default">True</property>
+ <property name="use-underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="cancel">
+ <property name="label" translatable="yes" context="stock">_Cancel</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use-underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="help">
+ <property name="label" translatable="yes" context="stock">_Help</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="receives_default">True</property>
+ <property name="use-underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ <property name="secondary">True</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">end</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="alttitle">
+ <property name="can_focus">False</property>
+ <property name="no_show_all">True</property>
+ <property name="label" translatable="yes" context="bookmarkdialog|alttitle">Rename Bookmark</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="box1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="valign">start</property>
+ <property name="hexpand">True</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkLabel" id="label2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes" context="bookmarkdialog|label2">Bookmark:</property>
+ <property name="use_underline">True</property>
+ <property name="mnemonic_widget">entry</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hexpand">True</property>
+ <property name="truncate-multiline">True</property>
+ <property name="activates_default">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <action-widgets>
+ <action-widget response="-5">ok</action-widget>
+ <action-widget response="-6">cancel</action-widget>
+ <action-widget response="-11">help</action-widget>
+ </action-widgets>
+ </object>
+</interface>
diff --git a/sfx2/uiconfig/ui/bookmarkmenu.ui b/sfx2/uiconfig/ui/bookmarkmenu.ui
new file mode 100644
index 000000000..30c03835f
--- /dev/null
+++ b/sfx2/uiconfig/ui/bookmarkmenu.ui
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.20.0 -->
+<interface domain="sfx">
+ <requires lib="gtk+" version="3.20"/>
+ <object class="GtkMenu" id="menu">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkMenuItem" id="display">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes" context="bookmarkmenu|display">Display</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkSeparatorMenuItem" id="menuitem1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="rename">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes" context="bookmarkmenu|rename">Rename...</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="delete">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes" context="bookmarkmenu|delete">Delete</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ </object>
+</interface>
diff --git a/sfx2/uiconfig/ui/charmapcontrol.ui b/sfx2/uiconfig/ui/charmapcontrol.ui
new file mode 100644
index 000000000..f1d0128b7
--- /dev/null
+++ b/sfx2/uiconfig/ui/charmapcontrol.ui
@@ -0,0 +1,514 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.36.0 -->
+<interface domain="sfx">
+ <requires lib="gtk+" version="3.20"/>
+ <object class="GtkPopover" id="charmapctrl">
+ <property name="can_focus">False</property>
+ <property name="no_show_all">True</property>
+ <property name="border_width">4</property>
+ <property name="constrain-to">none</property>
+ <child>
+ <object class="GtkBox" id="container">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="label1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes" context="charmapcontrol|label1">Favorites</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <!-- n-columns=1 n-rows=1 -->
+ <object class="GtkGrid" id="grid1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="halign">start</property>
+ <property name="row_spacing">3</property>
+ <property name="column_spacing">3</property>
+ <child>
+ <object class="GtkDrawingArea" id="favchar1">
+ <property name="width_request">35</property>
+ <property name="height_request">35</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="events">GDK_BUTTON_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_STRUCTURE_MASK</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkDrawingArea" id="favchar2">
+ <property name="width_request">35</property>
+ <property name="height_request">35</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="events">GDK_BUTTON_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_STRUCTURE_MASK</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkDrawingArea" id="favchar4">
+ <property name="width_request">35</property>
+ <property name="height_request">35</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="events">GDK_BUTTON_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_STRUCTURE_MASK</property>
+ </object>
+ <packing>
+ <property name="left_attach">3</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkDrawingArea" id="favchar3">
+ <property name="width_request">35</property>
+ <property name="height_request">35</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="events">GDK_BUTTON_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_STRUCTURE_MASK</property>
+ </object>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkDrawingArea" id="favchar5">
+ <property name="width_request">35</property>
+ <property name="height_request">35</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="events">GDK_BUTTON_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_STRUCTURE_MASK</property>
+ </object>
+ <packing>
+ <property name="left_attach">4</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkDrawingArea" id="favchar6">
+ <property name="width_request">35</property>
+ <property name="height_request">35</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="events">GDK_BUTTON_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_STRUCTURE_MASK</property>
+ </object>
+ <packing>
+ <property name="left_attach">5</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkDrawingArea" id="favchar7">
+ <property name="width_request">35</property>
+ <property name="height_request">35</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="events">GDK_BUTTON_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_STRUCTURE_MASK</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkDrawingArea" id="favchar8">
+ <property name="width_request">35</property>
+ <property name="height_request">35</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="events">GDK_BUTTON_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_STRUCTURE_MASK</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkDrawingArea" id="favchar9">
+ <property name="width_request">35</property>
+ <property name="height_request">35</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="events">GDK_BUTTON_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_STRUCTURE_MASK</property>
+ </object>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkDrawingArea" id="favchar10">
+ <property name="width_request">35</property>
+ <property name="height_request">35</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="events">GDK_BUTTON_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_STRUCTURE_MASK</property>
+ </object>
+ <packing>
+ <property name="left_attach">3</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkDrawingArea" id="favchar11">
+ <property name="width_request">35</property>
+ <property name="height_request">35</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="events">GDK_BUTTON_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_STRUCTURE_MASK</property>
+ </object>
+ <packing>
+ <property name="left_attach">4</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkDrawingArea" id="favchar12">
+ <property name="width_request">35</property>
+ <property name="height_request">35</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="events">GDK_BUTTON_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_STRUCTURE_MASK</property>
+ </object>
+ <packing>
+ <property name="left_attach">5</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkDrawingArea" id="favchar13">
+ <property name="width_request">35</property>
+ <property name="height_request">35</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="events">GDK_BUTTON_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_STRUCTURE_MASK</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkDrawingArea" id="favchar14">
+ <property name="width_request">35</property>
+ <property name="height_request">35</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="events">GDK_BUTTON_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_STRUCTURE_MASK</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkDrawingArea" id="favchar15">
+ <property name="width_request">35</property>
+ <property name="height_request">35</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="events">GDK_BUTTON_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_STRUCTURE_MASK</property>
+ </object>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="top_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkDrawingArea" id="favchar16">
+ <property name="width_request">35</property>
+ <property name="height_request">35</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="events">GDK_BUTTON_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_STRUCTURE_MASK</property>
+ </object>
+ <packing>
+ <property name="left_attach">3</property>
+ <property name="top_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes" context="charmapcontrol|label2">Recent</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <!-- n-columns=1 n-rows=1 -->
+ <object class="GtkGrid" id="grid2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="halign">start</property>
+ <property name="row_spacing">3</property>
+ <property name="column_spacing">3</property>
+ <child>
+ <object class="GtkDrawingArea" id="viewchar1">
+ <property name="width_request">35</property>
+ <property name="height_request">35</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="events">GDK_BUTTON_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_STRUCTURE_MASK</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkDrawingArea" id="viewchar2">
+ <property name="width_request">35</property>
+ <property name="height_request">35</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="events">GDK_BUTTON_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_STRUCTURE_MASK</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkDrawingArea" id="viewchar4">
+ <property name="width_request">35</property>
+ <property name="height_request">35</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="events">GDK_BUTTON_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_STRUCTURE_MASK</property>
+ </object>
+ <packing>
+ <property name="left_attach">3</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkDrawingArea" id="viewchar3">
+ <property name="width_request">35</property>
+ <property name="height_request">35</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="events">GDK_BUTTON_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_STRUCTURE_MASK</property>
+ </object>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkDrawingArea" id="viewchar5">
+ <property name="width_request">35</property>
+ <property name="height_request">35</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="events">GDK_BUTTON_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_STRUCTURE_MASK</property>
+ </object>
+ <packing>
+ <property name="left_attach">4</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkDrawingArea" id="viewchar6">
+ <property name="width_request">35</property>
+ <property name="height_request">35</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="events">GDK_BUTTON_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_STRUCTURE_MASK</property>
+ </object>
+ <packing>
+ <property name="left_attach">5</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkDrawingArea" id="viewchar16">
+ <property name="width_request">35</property>
+ <property name="height_request">35</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="events">GDK_BUTTON_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_STRUCTURE_MASK</property>
+ </object>
+ <packing>
+ <property name="left_attach">3</property>
+ <property name="top_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkDrawingArea" id="viewchar15">
+ <property name="width_request">35</property>
+ <property name="height_request">35</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="events">GDK_BUTTON_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_STRUCTURE_MASK</property>
+ </object>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="top_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkDrawingArea" id="viewchar14">
+ <property name="width_request">35</property>
+ <property name="height_request">35</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="events">GDK_BUTTON_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_STRUCTURE_MASK</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkDrawingArea" id="viewchar13">
+ <property name="width_request">35</property>
+ <property name="height_request">35</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="events">GDK_BUTTON_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_STRUCTURE_MASK</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkDrawingArea" id="viewchar12">
+ <property name="width_request">35</property>
+ <property name="height_request">35</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="events">GDK_BUTTON_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_STRUCTURE_MASK</property>
+ </object>
+ <packing>
+ <property name="left_attach">5</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkDrawingArea" id="viewchar11">
+ <property name="width_request">35</property>
+ <property name="height_request">35</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="events">GDK_BUTTON_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_STRUCTURE_MASK</property>
+ </object>
+ <packing>
+ <property name="left_attach">4</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkDrawingArea" id="viewchar10">
+ <property name="width_request">35</property>
+ <property name="height_request">35</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="events">GDK_BUTTON_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_STRUCTURE_MASK</property>
+ </object>
+ <packing>
+ <property name="left_attach">3</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkDrawingArea" id="viewchar9">
+ <property name="width_request">35</property>
+ <property name="height_request">35</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="events">GDK_BUTTON_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_STRUCTURE_MASK</property>
+ </object>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkDrawingArea" id="viewchar8">
+ <property name="width_request">35</property>
+ <property name="height_request">35</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="events">GDK_BUTTON_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_STRUCTURE_MASK</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkDrawingArea" id="viewchar7">
+ <property name="width_request">35</property>
+ <property name="height_request">35</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="events">GDK_BUTTON_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_STRUCTURE_MASK</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="specialchardlg">
+ <property name="label" translatable="yes" context="charmapcontrol|specialchardlg">More Characters…</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">4</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+</interface>
diff --git a/sfx2/uiconfig/ui/charviewmenu.ui b/sfx2/uiconfig/ui/charviewmenu.ui
new file mode 100644
index 000000000..714d33c6e
--- /dev/null
+++ b/sfx2/uiconfig/ui/charviewmenu.ui
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.20.2 -->
+<interface domain="sfx">
+ <requires lib="gtk+" version="3.20"/>
+ <object class="GtkMenu" id="charviewmenu">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkMenuItem" id="clearchar">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes" context="charviewmenu|clearchar">Remove</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="clearallchar">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes" context="charviewmenu|clearallchar">Clear All</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ </object>
+</interface>
diff --git a/sfx2/uiconfig/ui/checkin.ui b/sfx2/uiconfig/ui/checkin.ui
new file mode 100644
index 000000000..fb0bf0ec8
--- /dev/null
+++ b/sfx2/uiconfig/ui/checkin.ui
@@ -0,0 +1,154 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.22.1 -->
+<interface domain="sfx">
+ <requires lib="gtk+" version="3.20"/>
+ <object class="GtkDialog" id="CheckinDialog">
+ <property name="can_focus">False</property>
+ <property name="border_width">6</property>
+ <property name="title" translatable="yes" context="checkin|CheckinDialog">Check-In</property>
+ <property name="resizable">False</property>
+ <property name="modal">True</property>
+ <property name="window_position">center-on-parent</property>
+ <property name="default_width">0</property>
+ <property name="default_height">0</property>
+ <property name="type_hint">dialog</property>
+ <child>
+ <placeholder/>
+ </child>
+ <child internal-child="vbox">
+ <object class="GtkBox" id="dialog-vbox1">
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">12</property>
+ <child internal-child="action_area">
+ <object class="GtkButtonBox" id="dialog-action_area1">
+ <property name="can_focus">False</property>
+ <property name="layout_style">end</property>
+ <child>
+ <object class="GtkButton" id="ok">
+ <property name="label" translatable="yes" context="stock">_OK</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="has_default">True</property>
+ <property name="receives_default">True</property>
+ <property name="use-underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="cancel">
+ <property name="label" translatable="yes" context="stock">_Cancel</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use-underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="help">
+ <property name="label" translatable="yes" context="stock">_Help</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use-underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ <property name="secondary">True</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">end</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <!-- n-columns=1 n-rows=1 -->
+ <object class="GtkGrid" id="grid1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="row_spacing">6</property>
+ <child>
+ <object class="GtkCheckButton" id="MajorVersion">
+ <property name="label" translatable="yes" context="checkin|MajorVersion">New major version</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_underline">True</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes" context="checkin|label2">Version comment:</property>
+ <property name="use_underline">True</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkTextView" id="VersionComment">
+ <property name="width_request">300</property>
+ <property name="height_request">150</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="wrap_mode">word</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <action-widgets>
+ <action-widget response="-5">ok</action-widget>
+ <action-widget response="-6">cancel</action-widget>
+ <action-widget response="-11">help</action-widget>
+ </action-widgets>
+ </object>
+</interface>
diff --git a/sfx2/uiconfig/ui/classificationbox.ui b/sfx2/uiconfig/ui/classificationbox.ui
new file mode 100644
index 000000000..4441818c2
--- /dev/null
+++ b/sfx2/uiconfig/ui/classificationbox.ui
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.22.1 -->
+<interface domain="sfx">
+ <requires lib="gtk+" version="3.20"/>
+ <object class="GtkBox" id="ClassificationBox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin-start">6</property>
+ <property name="margin-end">6</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="use_underline">True</property>
+ <property name="mnemonic_widget">combobox</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBoxText" id="combobox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+</interface>
diff --git a/sfx2/uiconfig/ui/cmisinfopage.ui b/sfx2/uiconfig/ui/cmisinfopage.ui
new file mode 100644
index 000000000..b7a410804
--- /dev/null
+++ b/sfx2/uiconfig/ui/cmisinfopage.ui
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.22.1 -->
+<interface domain="sfx">
+ <requires lib="gtk+" version="3.20"/>
+ <!-- n-columns=1 n-rows=1 -->
+ <object class="GtkGrid" id="CmisInfoPage">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="border_width">6</property>
+ <property name="row_spacing">6</property>
+ <child>
+ <object class="GtkScrolledWindow" id="CmisScroll">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="hscrollbar_policy">never</property>
+ <property name="vscrollbar_policy">always</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkViewport" id="viewport1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkBox" id="CmisWindow">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ </object>
+</interface>
diff --git a/sfx2/uiconfig/ui/cmisline.ui b/sfx2/uiconfig/ui/cmisline.ui
new file mode 100644
index 000000000..b933d5c06
--- /dev/null
+++ b/sfx2/uiconfig/ui/cmisline.ui
@@ -0,0 +1,134 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.22.1 -->
+<interface domain="sfx">
+ <requires lib="gtk+" version="3.20"/>
+ <object class="GtkAdjustment" id="adjustment1">
+ <property name="upper">86400000</property>
+ <property name="step_increment">1000</property>
+ <property name="page_increment">60000</property>
+ </object>
+ <object class="GtkFrame" id="CmisFrame">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="border_width">6</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <!-- n-columns=1 n-rows=1 -->
+ <object class="GtkGrid" id="grid1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="row_spacing">6</property>
+ <property name="column_spacing">12</property>
+ <property name="margin-start">12</property>
+ <property name="margin-top">6</property>
+ <child>
+ <object class="GtkLabel" id="name">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="width_chars">30</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="type">
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes" context="cmisline|type">Type</property>
+ <property name="width_chars">8</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkRadioButton" id="yes">
+ <property name="label" translatable="yes" context="cmisline|yes">Yes</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_underline">True</property>
+ <property name="active">True</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">3</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkRadioButton" id="no">
+ <property name="label" translatable="yes" context="cmisline|no">No</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_underline">True</property>
+ <property name="draw_indicator">True</property>
+ <property name="group">yes</property>
+ </object>
+ <packing>
+ <property name="left_attach">4</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="value">
+ <property name="can_focus">True</property>
+ <property name="max_length">300</property>
+ <property name="truncate-multiline">True</property>
+ <property name="width_chars">55</property>
+ </object>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkMenuButton" id="date">
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="draw_indicator">True</property>
+ <property name="label" translatable="no"></property>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">5</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="time">
+ <property name="can_focus">True</property>
+ <property name="truncate-multiline">True</property>
+ <property name="adjustment">adjustment1</property>
+ </object>
+ <packing>
+ <property name="left_attach">6</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="key">
+ <property name="can_focus">False</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ </object>
+ </child>
+ </object>
+</interface>
diff --git a/sfx2/uiconfig/ui/commandpopup.ui b/sfx2/uiconfig/ui/commandpopup.ui
new file mode 100644
index 000000000..08e82423e
--- /dev/null
+++ b/sfx2/uiconfig/ui/commandpopup.ui
@@ -0,0 +1,93 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.38.2 -->
+<interface domain="sfx">
+ <requires lib="gtk+" version="3.20"/>
+ <object class="GtkTreeStore" id="liststore1">
+ <columns>
+ <!-- column-name icon -->
+ <column type="GdkPixbuf"/>
+ <!-- column-name text -->
+ <column type="gchararray"/>
+ <!-- column-name tooltip -->
+ <column type="gchararray"/>
+ <!-- column-name id -->
+ <column type="gchararray"/>
+ </columns>
+ </object>
+ <object class="GtkPopover" id="CommandPopup">
+ <property name="can-focus">False</property>
+ <property name="position">bottom</property>
+ <property name="constrain-to">none</property>
+ <child>
+ <object class="GtkBox" id="container">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="border-width">6</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkEntry" id="command_entry">
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="truncate-multiline">True</property>
+ <property name="placeholder-text" translatable="yes" context="commandpopup|entry">Search command</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow">
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="shadow-type">in</property>
+ <child>
+ <object class="GtkTreeView" id="command_treeview">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="model">liststore1</property>
+ <property name="headers-visible">False</property>
+ <property name="headers-clickable">False</property>
+ <property name="enable-search">False</property>
+ <property name="search-column">0</property>
+ <property name="hover-selection">True</property>
+ <property name="show-expanders">False</property>
+ <property name="tooltip-column">2</property>
+ <property name="activate-on-single-click">True</property>
+ <child internal-child="selection">
+ <object class="GtkTreeSelection"/>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="column">
+ <property name="sizing">fixed</property>
+ <child>
+ <object class="GtkCellRendererPixbuf" id="cellrenderericon"/>
+ <attributes>
+ <attribute name="pixbuf">0</attribute>
+ </attributes>
+ </child>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderertext"/>
+ <attributes>
+ <attribute name="text">1</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+</interface>
diff --git a/sfx2/uiconfig/ui/custominfopage.ui b/sfx2/uiconfig/ui/custominfopage.ui
new file mode 100644
index 000000000..645db84af
--- /dev/null
+++ b/sfx2/uiconfig/ui/custominfopage.ui
@@ -0,0 +1,142 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.36.0 -->
+<interface domain="sfx">
+ <requires lib="gtk+" version="3.20"/>
+ <!-- n-columns=1 n-rows=1 -->
+ <object class="GtkGrid" id="CustomInfoPage">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="border_width">6</property>
+ <property name="row_spacing">6</property>
+ <child>
+ <object class="GtkButton" id="add">
+ <property name="label" translatable="yes" context="custominfopage|add">Add _Property</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="halign">end</property>
+ <property name="use_underline">True</property>
+ <child internal-child="accessible">
+ <object class="AtkObject" id="add-atkobject">
+ <property name="AtkObject::accessible-description" translatable="yes" context="custominfopage|extended_tip|add">Click to add a new row to the Properties list.</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="headerbox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkLabel" id="name">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes" context="custominfopage|name">Name</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="type">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes" context="custominfopage|type">Type</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="value">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes" context="custominfopage|value">Value</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="scroll">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="hscrollbar_policy">never</property>
+ <property name="vscrollbar_policy">always</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkViewport">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <child>
+ <object class="GtkBox" id="box">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkBox" id="properties">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="valign">start</property>
+ <property name="border_width">3</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">6</property>
+ <child>
+ <placeholder/>
+ </child>
+ <child internal-child="accessible">
+ <object class="AtkObject" id="properties-atkobject">
+ <property name="AtkObject::accessible-description" translatable="yes" context="custominfopage|extended_tip|properties">Enter your custom contents. You can change the name, type, and contents of each row. You can add or remove rows. The items will be exported as metadata to other file formats.</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child internal-child="accessible">
+ <object class="AtkObject" id="CustomInfoPage-atkobject">
+ <property name="AtkObject::accessible-description" translatable="yes" context="custominfopage|extended_tip|CustomInfoPage">Allows you to assign custom information fields to your document.</property>
+ </object>
+ </child>
+ </object>
+</interface>
diff --git a/sfx2/uiconfig/ui/deck.ui b/sfx2/uiconfig/ui/deck.ui
new file mode 100644
index 000000000..ca3c96dcf
--- /dev/null
+++ b/sfx2/uiconfig/ui/deck.ui
@@ -0,0 +1,132 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.38.2 -->
+<interface domain="sfx">
+ <requires lib="gtk+" version="3.20"/>
+ <!-- n-columns=1 n-rows=2 -->
+ <object class="GtkGrid" id="Deck">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="vexpand">True</property>
+ <child>
+ <object class="GtkScrolledWindow" id="scrolledwindow">
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <child>
+ <object class="GtkViewport">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <child>
+ <object class="GtkBox" id="contents">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left-attach">0</property>
+ <property name="top-attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="titlebar">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkDrawingArea" id="grip">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="valign">center</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkImage" id="addonimage">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="valign">center</property>
+ <property name="icon-name">missing-image</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="valign">center</property>
+ <property name="hexpand">True</property>
+ <property name="ellipsize">end</property>
+ <property name="xalign">0</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToolbar" id="toolbar">
+ <property name="can-focus">True</property>
+ <property name="no-show-all">True</property>
+ <property name="toolbar-style">icons</property>
+ <property name="show-arrow">False</property>
+ <property name="icon_size">2</property>
+ <child>
+ <object class="GtkToolButton" id="button">
+ <property name="visible">True</property>
+ <property name="tooltip-text" translatable="yes" context="deck|SFX_STR_SIDEBAR_CLOSE_DECK">Close Sidebar Deck</property>
+ <property name="use-underline">True</property>
+ <property name="icon-name">window-close-symbolic</property>
+ <child internal-child="accessible">
+ <object class="AtkObject" id="button-atkobject">
+ <property name="AtkObject::accessible-name" translatable="yes" context="deck|SFX_STR_SIDEBAR_CLOSE_DECK">Close Sidebar Deck</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">False</property>
+ </packing>
+ </child>
+ <style>
+ <class name="small-button"/>
+ </style>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="left-attach">0</property>
+ <property name="top-attach">0</property>
+ </packing>
+ </child>
+ </object>
+</interface>
diff --git a/sfx2/uiconfig/ui/descriptioninfopage.ui b/sfx2/uiconfig/ui/descriptioninfopage.ui
new file mode 100644
index 000000000..86545c576
--- /dev/null
+++ b/sfx2/uiconfig/ui/descriptioninfopage.ui
@@ -0,0 +1,154 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.36.0 -->
+<interface domain="sfx">
+ <requires lib="gtk+" version="3.20"/>
+ <!-- n-columns=1 n-rows=1 -->
+ <object class="GtkGrid" id="DescriptionInfoPage">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="border_width">6</property>
+ <property name="row_spacing">6</property>
+ <property name="column_spacing">12</property>
+ <child>
+ <object class="GtkLabel" id="label27">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes" context="descriptioninfopage|label27">_Title:</property>
+ <property name="use_underline">True</property>
+ <property name="mnemonic_widget">title</property>
+ <property name="xalign">1</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label28">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes" context="descriptioninfopage|label28">_Subject:</property>
+ <property name="use_underline">True</property>
+ <property name="mnemonic_widget">subject</property>
+ <property name="xalign">1</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label29">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes" context="descriptioninfopage|label29">_Keywords:</property>
+ <property name="use_underline">True</property>
+ <property name="mnemonic_widget">keywords</property>
+ <property name="xalign">1</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label30">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="valign">start</property>
+ <property name="label" translatable="yes" context="descriptioninfopage|label30">_Comments:</property>
+ <property name="use_underline">True</property>
+ <property name="mnemonic_widget">comments</property>
+ <property name="xalign">1</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="title">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hexpand">True</property>
+ <property name="truncate-multiline">True</property>
+ <child internal-child="accessible">
+ <object class="AtkObject" id="title-atkobject">
+ <property name="AtkObject::accessible-description" translatable="yes" context="descriptioninfopage|extended_tip|title">Enter a title for the document.</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="subject">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hexpand">True</property>
+ <property name="truncate-multiline">True</property>
+ <child internal-child="accessible">
+ <object class="AtkObject" id="subject-atkobject">
+ <property name="AtkObject::accessible-description" translatable="yes" context="descriptioninfopage|extended_tip|subject">Enter a subject for the document. You can use a subject to group documents with similar contents.</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="keywords">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hexpand">True</property>
+ <property name="truncate-multiline">True</property>
+ <child internal-child="accessible">
+ <object class="AtkObject" id="keywords-atkobject">
+ <property name="AtkObject::accessible-description" translatable="yes" context="descriptioninfopage|extended_tip|keywords">Enter the words that you want to use to index the content of your document. Keywords must be separated by commas. A keyword can contain white space characters or semicolons.</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="scrolledwindow1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkTextView" id="comments">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <child internal-child="accessible">
+ <object class="AtkObject" id="comments-atkobject">
+ <property name="AtkObject::accessible-description" translatable="yes" context="descriptioninfopage|extended_tip|comments">Enter comments to help identify the document.</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">3</property>
+ </packing>
+ </child>
+ <child internal-child="accessible">
+ <object class="AtkObject" id="DescriptionInfoPage-atkobject">
+ <property name="AtkObject::accessible-description" translatable="yes" context="descriptioninfopage|extended_tip|DescriptionInfoPage">Contains descriptive information about the document.</property>
+ </object>
+ </child>
+ </object>
+</interface>
diff --git a/sfx2/uiconfig/ui/developmenttool.ui b/sfx2/uiconfig/ui/developmenttool.ui
new file mode 100644
index 000000000..18a334afb
--- /dev/null
+++ b/sfx2/uiconfig/ui/developmenttool.ui
@@ -0,0 +1,661 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.38.2 -->
+<interface domain="sfx">
+ <requires lib="gtk+" version="3.20"/>
+ <object class="GtkTextBuffer"/>
+ <object class="GtkTreeStore" id="liststore1">
+ <columns>
+ <!-- column-name text -->
+ <column type="gchararray"/>
+ <!-- column-name id -->
+ <column type="gchararray"/>
+ <!-- column-name weight1 -->
+ <column type="gint"/>
+ </columns>
+ </object>
+ <object class="GtkTreeStore" id="object_inspector_interfaces_liststore">
+ <columns>
+ <!-- column-name text -->
+ <column type="gchararray"/>
+ <!-- column-name id -->
+ <column type="gchararray"/>
+ <!-- column-name weight1 -->
+ <column type="gint"/>
+ </columns>
+ </object>
+ <object class="GtkTreeStore" id="object_inspector_methods_liststore">
+ <columns>
+ <!-- column-name text -->
+ <column type="gchararray"/>
+ <!-- column-name out -->
+ <column type="gchararray"/>
+ <!-- column-name in -->
+ <column type="gchararray"/>
+ <!-- column-name impl -->
+ <column type="gchararray"/>
+ <!-- column-name id -->
+ <column type="gchararray"/>
+ <!-- column-name weight1 -->
+ <column type="gint"/>
+ <!-- column-name weight2 -->
+ <column type="gint"/>
+ <!-- column-name weight3 -->
+ <column type="gint"/>
+ <!-- column-name weight4 -->
+ <column type="gint"/>
+ </columns>
+ </object>
+ <object class="GtkTreeStore" id="object_inspector_properties_liststore">
+ <columns>
+ <!-- column-name text -->
+ <column type="gchararray"/>
+ <!-- column-name value -->
+ <column type="gchararray"/>
+ <!-- column-name type -->
+ <column type="gchararray"/>
+ <!-- column-name info -->
+ <column type="gchararray"/>
+ <!-- column-name id -->
+ <column type="gchararray"/>
+ <!-- column-name weight1 -->
+ <column type="gint"/>
+ <!-- column-name weight2 -->
+ <column type="gint"/>
+ <!-- column-name weight3 -->
+ <column type="gint"/>
+ <!-- column-name weight4 -->
+ <column type="gint"/>
+ </columns>
+ </object>
+ <object class="GtkTreeStore" id="object_inspector_services_liststore">
+ <columns>
+ <!-- column-name text -->
+ <column type="gchararray"/>
+ <!-- column-name id -->
+ <column type="gchararray"/>
+ <!-- column-name weight1 -->
+ <column type="gint"/>
+ </columns>
+ </object>
+ <object class="GtkBox" id="DevelopmentTool">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkPaned">
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="position">270</property>
+ <property name="wide-handle">True</property>
+ <child>
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="margin-start">6</property>
+ <property name="margin-end">6</property>
+ <property name="margin-top">6</property>
+ <property name="margin-bottom">6</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="border-width">3</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkToolbar" id="dom_toolbar">
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="toolbar-style">both-horiz</property>
+ <property name="show-arrow">False</property>
+ <child>
+ <object class="GtkToggleToolButton" id="dom_current_selection_toggle">
+ <property name="visible">True</property>
+ <property name="tooltip-text" translatable="yes" context="developmenttool|dom_current_selection_toggle-tooltip">Current Selection In Document</property>
+ <property name="label" translatable="yes" context="developmenttool|dom_current_selection_toggle">Current Selection</property>
+ <property name="use-underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToolButton" id="dom_refresh_button">
+ <property name="visible">True</property>
+ <property name="tooltip-text" translatable="yes" context="developmenttool|dom_refresh_button-tooltip">Refresh Document Model Tree View</property>
+ <property name="label" translatable="yes" context="developmenttool|dom_refresh_button">Refresh</property>
+ <property name="use-underline">True</property>
+ <property name="icon-name">cmd/lc_reload.png</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">True</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow">
+ <property name="width-request">200</property>
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="shadow-type">in</property>
+ <child>
+ <object class="GtkTreeView" id="leftside_treeview_id">
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="model">liststore1</property>
+ <property name="search-column">0</property>
+ <property name="enable-tree-lines">True</property>
+ <child internal-child="selection">
+ <object class="GtkTreeSelection"/>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="treecolumn">
+ <property name="resizable">True</property>
+ <property name="title" translatable="yes" context="developmenttool|object">Object</property>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderertext2"/>
+ <attributes>
+ <attribute name="text">0</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="resize">False</property>
+ <property name="shrink">True</property>
+ </packing>
+ </child>
+ <child>
+ <!-- n-columns=1 n-rows=2 -->
+ <object class="GtkGrid">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="margin-start">6</property>
+ <property name="margin-end">6</property>
+ <property name="margin-top">6</property>
+ <property name="margin-bottom">6</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <child>
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <child>
+ <object class="GtkToolbar" id="object_inspector_toolbar">
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="toolbar-style">icons</property>
+ <property name="show-arrow">False</property>
+ <child>
+ <object class="GtkToolButton" id="back">
+ <property name="visible">True</property>
+ <property name="tooltip-text" translatable="yes" context="developmenttool|tooltip-back">Back</property>
+ <property name="label" translatable="yes" context="developmenttool|back">Back</property>
+ <property name="use-underline">True</property>
+ <property name="icon-name">cmd/lc_prevrecord.png</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToolButton" id="inspect">
+ <property name="visible">True</property>
+ <property name="tooltip-text" translatable="yes" context="developmenttool|tooltip-inspect">Inspect</property>
+ <property name="label" translatable="yes" context="developmenttool|inspect">Inspect</property>
+ <property name="use-underline">True</property>
+ <property name="icon-name">cmd/lc_recsearch.png</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToolButton" id="refresh">
+ <property name="visible">True</property>
+ <property name="tooltip-text" translatable="yes" context="developmenttool|tooltip-refresh">Refresh</property>
+ <property name="label" translatable="yes" context="developmenttool|refresh">Refresh</property>
+ <property name="use-underline">True</property>
+ <property name="icon-name">cmd/lc_reload.png</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">True</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="class_name_label">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="margin-end">6</property>
+ <property name="hexpand">False</property>
+ <property name="vexpand">False</property>
+ <property name="label" translatable="yes" context="developmenttool|classname">Class name:</property>
+ <accessibility>
+ <relation type="label-for" target="class_name_value_id"/>
+ </accessibility>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="class_name_value_id">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="halign">start</property>
+ <property name="hexpand">True</property>
+ <property name="selectable">True</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <accessibility>
+ <relation type="labelled-by" target="class_name_label"/>
+ </accessibility>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="left-attach">0</property>
+ <property name="top-attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkNotebook" id="object_inspector_notebookbar">
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <child>
+ <object class="GtkScrolledWindow">
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="shadow-type">in</property>
+ <child>
+ <object class="GtkTreeView" id="interfaces_treeview_id">
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">True</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="model">object_inspector_interfaces_liststore</property>
+ <property name="search-column">0</property>
+ <property name="enable-tree-lines">True</property>
+ <child internal-child="selection">
+ <object class="GtkTreeSelection"/>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="interfaces_treeviewcolumn1">
+ <property name="resizable">True</property>
+ <property name="title" translatable="yes" context="developmenttool|name">Name</property>
+ <property name="clickable">True</property>
+ <property name="sort-indicator">True</property>
+ <child>
+ <object class="GtkCellRendererText" id="obj_insp_cellrenderertext4"/>
+ <attributes>
+ <attribute name="text">0</attribute>
+ <attribute name="weight">2</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="tab">
+ <object class="GtkLabel" id="object_inspector_interfaces_tab">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="label" translatable="yes" context="developmenttool|interfaces">Interfaces</property>
+ </object>
+ <packing>
+ <property name="tab-fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow">
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="shadow-type">in</property>
+ <child>
+ <object class="GtkTreeView" id="services_treeview_id">
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">True</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="model">object_inspector_services_liststore</property>
+ <property name="search-column">0</property>
+ <property name="enable-tree-lines">True</property>
+ <child internal-child="selection">
+ <object class="GtkTreeSelection"/>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="services_treeviewcolumn1">
+ <property name="resizable">True</property>
+ <property name="title" translatable="yes" context="developmenttool|name">Name</property>
+ <property name="clickable">True</property>
+ <property name="sort-indicator">True</property>
+ <child>
+ <object class="GtkCellRendererText" id="obj_insp_cellrenderertext7"/>
+ <attributes>
+ <attribute name="text">0</attribute>
+ <attribute name="weight">2</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child type="tab">
+ <object class="GtkLabel" id="object_inspector_services_tab">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="label" translatable="yes" context="developmenttool|services">Services</property>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ <property name="tab-fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkPaned" id="object_inspector_paned">
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkScrolledWindow">
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="shadow-type">in</property>
+ <child>
+ <object class="GtkTreeView" id="properties_treeview_id">
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">True</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="model">object_inspector_properties_liststore</property>
+ <property name="search-column">0</property>
+ <property name="enable-tree-lines">True</property>
+ <child internal-child="selection">
+ <object class="GtkTreeSelection"/>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="properties_treeviewcolumn1">
+ <property name="resizable">True</property>
+ <property name="title" translatable="yes" context="developmenttool|name">Name</property>
+ <property name="clickable">True</property>
+ <property name="sort-indicator">True</property>
+ <child>
+ <object class="GtkCellRendererText" id="obj_insp_cellrenderertext1"/>
+ <attributes>
+ <attribute name="text">0</attribute>
+ <attribute name="weight">5</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="properties_treeviewcolumn2">
+ <property name="resizable">True</property>
+ <property name="title" translatable="yes" context="developmenttool|value">Value</property>
+ <property name="clickable">True</property>
+ <property name="sort-indicator">True</property>
+ <child>
+ <object class="GtkCellRendererText" id="obj_insp_cellrenderertext2"/>
+ <attributes>
+ <attribute name="text">1</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="properties_treeviewcolumn3">
+ <property name="resizable">True</property>
+ <property name="title" translatable="yes" context="developmenttool|type">Type</property>
+ <property name="clickable">True</property>
+ <property name="sort-indicator">True</property>
+ <child>
+ <object class="GtkCellRendererText" id="obj_insp_cellrenderertext3"/>
+ <attributes>
+ <attribute name="text">2</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="properties_treeviewcolumn4">
+ <property name="resizable">True</property>
+ <property name="title" translatable="yes" context="developmenttool|info">Info</property>
+ <property name="clickable">True</property>
+ <property name="sort-indicator">True</property>
+ <child>
+ <object class="GtkCellRendererText" id="obj_insp_cellrenderertext5"/>
+ <attributes>
+ <attribute name="text">3</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="resize">True</property>
+ <property name="shrink">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow">
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="shadow-type">in</property>
+ <child>
+ <object class="GtkTextView" id="object_inspector_text_view">
+ <property name="height-request">100</property>
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="editable">False</property>
+ <property name="wrap-mode">word</property>
+ <property name="cursor-visible">False</property>
+ <property name="accepts-tab">False</property>
+ <property name="monospace">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="resize">True</property>
+ <property name="shrink">True</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child type="tab">
+ <object class="GtkLabel" id="object_inspector_properties_tab">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="label" translatable="yes" context="developmenttool|properties">Properties</property>
+ </object>
+ <packing>
+ <property name="position">2</property>
+ <property name="tab-fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow">
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="shadow-type">in</property>
+ <child>
+ <object class="GtkTreeView" id="methods_treeview_id">
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">True</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="model">object_inspector_methods_liststore</property>
+ <property name="search-column">0</property>
+ <property name="enable-tree-lines">True</property>
+ <child internal-child="selection">
+ <object class="GtkTreeSelection"/>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="methods_treeviewcolumn1">
+ <property name="resizable">True</property>
+ <property name="title" translatable="yes" context="developmenttool|method">Method</property>
+ <property name="clickable">True</property>
+ <property name="sort-indicator">True</property>
+ <child>
+ <object class="GtkCellRendererText" id="obj_insp_cellrenderertext10"/>
+ <attributes>
+ <attribute name="text">0</attribute>
+ <attribute name="weight">5</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="methods_treeviewcolumn2">
+ <property name="resizable">True</property>
+ <property name="title" translatable="yes" context="developmenttool|returntype">Return Type</property>
+ <property name="clickable">True</property>
+ <property name="sort-indicator">True</property>
+ <child>
+ <object class="GtkCellRendererText" id="obj_insp_cellrenderertext11"/>
+ <attributes>
+ <attribute name="text">1</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="methods_treeviewcolumn3">
+ <property name="resizable">True</property>
+ <property name="title" translatable="yes" context="developmenttool|parameters">Parameters</property>
+ <property name="clickable">True</property>
+ <property name="sort-indicator">True</property>
+ <child>
+ <object class="GtkCellRendererText" id="obj_insp_cellrenderertext12"/>
+ <attributes>
+ <attribute name="text">2</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="methods_treeviewcolumn4">
+ <property name="resizable">True</property>
+ <property name="title" translatable="yes" context="developmenttool|implementation_class">Implementation Class</property>
+ <property name="clickable">True</property>
+ <property name="sort-indicator">True</property>
+ <child>
+ <object class="GtkCellRendererText" id="obj_insp_cellrenderertext13"/>
+ <attributes>
+ <attribute name="text">3</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ <child type="tab">
+ <object class="GtkLabel" id="object_inspector_methods_tab">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="label" translatable="yes" context="developmenttool|methods">Methods</property>
+ </object>
+ <packing>
+ <property name="position">3</property>
+ <property name="tab-fill">False</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="left-attach">0</property>
+ <property name="top-attach">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="resize">True</property>
+ <property name="shrink">True</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+</interface>
diff --git a/sfx2/uiconfig/ui/devtoolsmenu.ui b/sfx2/uiconfig/ui/devtoolsmenu.ui
new file mode 100644
index 000000000..e877b5b07
--- /dev/null
+++ b/sfx2/uiconfig/ui/devtoolsmenu.ui
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.38.2 -->
+<interface domain="sfx">
+ <requires lib="gtk+" version="3.20"/>
+ <object class="GtkMenu" id="inspect_menu">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <child>
+ <object class="GtkMenuItem" id="inspect">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="label" translatable="yes" context="devtoolsmenu|inspect">Inspect</property>
+ <property name="use-underline">True</property>
+ </object>
+ </child>
+ </object>
+</interface>
diff --git a/sfx2/uiconfig/ui/documentfontspage.ui b/sfx2/uiconfig/ui/documentfontspage.ui
new file mode 100644
index 000000000..5c79b0b8d
--- /dev/null
+++ b/sfx2/uiconfig/ui/documentfontspage.ui
@@ -0,0 +1,162 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.38.2 -->
+<interface domain="sfx">
+ <requires lib="gtk+" version="3.20"/>
+ <!-- n-columns=1 n-rows=2 -->
+ <object class="GtkGrid" id="DocumentFontsPage">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="border-width">12</property>
+ <property name="row-spacing">6</property>
+ <property name="column-spacing">12</property>
+ <child>
+ <object class="GtkFrame" id="fontEmbeddingFrame">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="label-xalign">0</property>
+ <property name="shadow-type">none</property>
+ <child>
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="margin-start">12</property>
+ <property name="margin-top">6</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkCheckButton" id="embedFonts">
+ <property name="label" translatable="yes" context="documentfontspage|embedFonts">_Embed fonts in the document</property>
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">False</property>
+ <property name="use-underline">True</property>
+ <property name="draw-indicator">True</property>
+ <child internal-child="accessible">
+ <object class="AtkObject" id="embedFonts-atkobject">
+ <property name="AtkObject::accessible-description" translatable="yes" context="documentfontspage|extended_tip|embedFonts">Mark this box to embed document fonts into the document file, for portability between different computer systems.</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="embedUsedFonts">
+ <property name="label" translatable="yes" context="documentfontspage|embedUsedFonts">_Only embed fonts that are used in documents</property>
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">False</property>
+ <property name="use-underline">True</property>
+ <property name="draw-indicator">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="fontEmbeddingLabel">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="label" translatable="yes" context="documentfontspage|fontEmbeddingLabel">Font Embedding</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left-attach">0</property>
+ <property name="top-attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFrame" id="scriptframe">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="label-xalign">0</property>
+ <property name="shadow-type">none</property>
+ <child>
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="margin-start">12</property>
+ <property name="margin-top">6</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkCheckButton" id="embedLatinScriptFonts">
+ <property name="label" translatable="yes" context="documentfontspage|embedLatinScriptFonts">_Latin fonts</property>
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">False</property>
+ <property name="use-underline">True</property>
+ <property name="draw-indicator">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="embedAsianScriptFonts">
+ <property name="label" translatable="yes" context="documentfontspage|embedAsianScriptFonts">_Asian fonts</property>
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">False</property>
+ <property name="use-underline">True</property>
+ <property name="draw-indicator">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="embedComplexScriptFonts">
+ <property name="label" translatable="yes" context="documentfontspage|embedComplexScriptFonts">_Complex fonts</property>
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">False</property>
+ <property name="use-underline">True</property>
+ <property name="draw-indicator">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="scriptlabel">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="label" translatable="yes" context="documentfontspage|fontScriptFrameLabel">Font scripts to embed</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left-attach">0</property>
+ <property name="top-attach">1</property>
+ </packing>
+ </child>
+ <child internal-child="accessible">
+ <object class="AtkObject" id="DocumentFontsPage-atkobject">
+ <property name="AtkObject::accessible-description" translatable="yes" context="documentfontspage|extended_tip|DocumentFontsPage">Embed document fonts in the current file.</property>
+ </object>
+ </child>
+ </object>
+</interface>
diff --git a/sfx2/uiconfig/ui/documentinfopage.ui b/sfx2/uiconfig/ui/documentinfopage.ui
new file mode 100644
index 000000000..5d86e541e
--- /dev/null
+++ b/sfx2/uiconfig/ui/documentinfopage.ui
@@ -0,0 +1,483 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.38.2 -->
+<interface domain="sfx">
+ <requires lib="gtk+" version="3.20"/>
+ <!-- n-columns=3 n-rows=14 -->
+ <object class="GtkGrid" id="DocumentInfoPage">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="border-width">12</property>
+ <property name="row-spacing">6</property>
+ <property name="column-spacing">12</property>
+ <child>
+ <object class="GtkLabel" id="label13">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="halign">end</property>
+ <property name="label" translatable="yes" context="documentinfopage|label13">_Created:</property>
+ <property name="use-underline">True</property>
+ <property name="mnemonic-widget">showcreate</property>
+ </object>
+ <packing>
+ <property name="left-attach">0</property>
+ <property name="top-attach">4</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label14">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="halign">end</property>
+ <property name="label" translatable="yes" context="documentinfopage|label14">_Modified:</property>
+ <property name="use-underline">True</property>
+ <property name="mnemonic-widget">showmodify</property>
+ </object>
+ <packing>
+ <property name="left-attach">0</property>
+ <property name="top-attach">5</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label15">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="halign">end</property>
+ <property name="label" translatable="yes" context="documentinfopage|label15">_Digitally signed:</property>
+ <property name="use-underline">True</property>
+ <property name="mnemonic-widget">showsigned</property>
+ </object>
+ <packing>
+ <property name="left-attach">0</property>
+ <property name="top-attach">7</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label16">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="halign">end</property>
+ <property name="label" translatable="yes" context="documentinfopage|label16">Last pri_nted:</property>
+ <property name="use-underline">True</property>
+ <property name="mnemonic-widget">showprint</property>
+ </object>
+ <packing>
+ <property name="left-attach">0</property>
+ <property name="top-attach">8</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label17">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="halign">end</property>
+ <property name="label" translatable="yes" context="documentinfopage|label17">Total _editing time:</property>
+ <property name="use-underline">True</property>
+ <property name="mnemonic-widget">showedittime</property>
+ </object>
+ <packing>
+ <property name="left-attach">0</property>
+ <property name="top-attach">9</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label18">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="halign">end</property>
+ <property name="label" translatable="yes" context="documentinfopage|label18">Re_vision number:</property>
+ <property name="use-underline">True</property>
+ <property name="mnemonic-widget">showrevision</property>
+ </object>
+ <packing>
+ <property name="left-attach">0</property>
+ <property name="top-attach">10</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="showcreate">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="selectable">True</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="left-attach">1</property>
+ <property name="top-attach">4</property>
+ <property name="width">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="showmodify">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="selectable">True</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="left-attach">1</property>
+ <property name="top-attach">5</property>
+ <property name="width">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="showsigned">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="label" translatable="yes" context="documentinfopage|showsigned">Multiply signed document</property>
+ <property name="selectable">True</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="left-attach">1</property>
+ <property name="top-attach">7</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="showprint">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="selectable">True</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="left-attach">1</property>
+ <property name="top-attach">8</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="showedittime">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="selectable">True</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="left-attach">1</property>
+ <property name="top-attach">9</property>
+ <property name="width">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="showrevision">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="selectable">True</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="left-attach">1</property>
+ <property name="top-attach">10</property>
+ <property name="width">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="userdatacb">
+ <property name="label" translatable="yes" context="documentinfopage|userdatacb">_Apply user data</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">False</property>
+ <property name="no-show-all">True</property>
+ <property name="use-underline">True</property>
+ <property name="draw-indicator">True</property>
+ </object>
+ <packing>
+ <property name="left-attach">1</property>
+ <property name="top-attach">11</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="reset">
+ <property name="label" translatable="yes" context="documentinfopage|reset">Reset Properties</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">True</property>
+ <property name="no-show-all">True</property>
+ <property name="valign">center</property>
+ <child internal-child="accessible">
+ <object class="AtkObject" id="reset-atkobject">
+ <property name="AtkObject::accessible-description" translatable="yes" context="documentinfopage|extended_tip|reset">Resets the editing time to zero, the creation date to the current date and time, and the version number to 1. The modification and printing dates are also deleted.</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left-attach">2</property>
+ <property name="top-attach">11</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="signature">
+ <property name="label" translatable="yes" context="documentinfopage|signature">Di_gital Signatures...</property>
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="has-focus">True</property>
+ <property name="receives-default">True</property>
+ <property name="valign">center</property>
+ <property name="use-underline">True</property>
+ </object>
+ <packing>
+ <property name="left-attach">2</property>
+ <property name="top-attach">6</property>
+ <property name="height">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label11">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="halign">end</property>
+ <property name="label" translatable="yes" context="documentinfopage|label11">_Size:</property>
+ <property name="use-underline">True</property>
+ <property name="mnemonic-widget">showsize</property>
+ </object>
+ <packing>
+ <property name="left-attach">0</property>
+ <property name="top-attach">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="showsize">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="label" translatable="yes" context="documentinfopage|showsize">unknown</property>
+ <property name="selectable">True</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="left-attach">1</property>
+ <property name="top-attach">3</property>
+ <property name="width">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label8">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="halign">end</property>
+ <property name="label" translatable="yes" context="documentinfopage|label8">_Location:</property>
+ <property name="use-underline">True</property>
+ <property name="mnemonic-widget">showlocation</property>
+ </object>
+ <packing>
+ <property name="left-attach">0</property>
+ <property name="top-attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label7">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="halign">end</property>
+ <property name="label" translatable="yes" context="documentinfopage|label7">_Type:</property>
+ <property name="use-underline">True</property>
+ <property name="mnemonic-widget">showtype</property>
+ </object>
+ <packing>
+ <property name="left-attach">0</property>
+ <property name="top-attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="showtype">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="selectable">True</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="left-attach">1</property>
+ <property name="top-attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="changepass">
+ <property name="label" translatable="yes" context="documentinfopage|changepass">Change _Password</property>
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">True</property>
+ <property name="valign">start</property>
+ <property name="use-underline">True</property>
+ </object>
+ <packing>
+ <property name="left-attach">2</property>
+ <property name="top-attach">0</property>
+ <property name="height">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="templateft">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="halign">end</property>
+ <property name="label" translatable="yes" context="documentinfopage|templateft">Template:</property>
+ <property name="use-underline">True</property>
+ <property name="mnemonic-widget">showtemplate</property>
+ </object>
+ <packing>
+ <property name="left-attach">0</property>
+ <property name="top-attach">6</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="showtemplate">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="selectable">True</property>
+ <property name="max-width-chars">56</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="left-attach">1</property>
+ <property name="top-attach">6</property>
+ </packing>
+ </child>
+ <child>
+ <!-- n-columns=2 n-rows=1 -->
+ <object class="GtkGrid" id="grid1">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="column-spacing">12</property>
+ <child>
+ <object class="GtkImage" id="icon">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="valign">center</property>
+ <property name="icon-name">missing-image</property>
+ </object>
+ <packing>
+ <property name="left-attach">0</property>
+ <property name="top-attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="nameed">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="selectable">True</property>
+ <property name="single-line-mode">True</property>
+ <property name="max-width-chars">56</property>
+ <child internal-child="accessible">
+ <object class="AtkObject" id="nameed-atkobject">
+ <property name="AtkObject::accessible-description" translatable="yes" context="documentinfopage|extended_tip|nameed">Displays the file name.</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left-attach">1</property>
+ <property name="top-attach">0</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="left-attach">1</property>
+ <property name="top-attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLinkButton" id="showlocation">
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">True</property>
+ <property name="relief">none</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="left-attach">1</property>
+ <property name="top-attach">2</property>
+ <property name="width">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="thumbnailsavecb">
+ <property name="label" translatable="yes" context="documentinfopage|thumbnailsavecb">Save preview image with this document</property>
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">False</property>
+ <property name="no-show-all">True</property>
+ <property name="draw-indicator">True</property>
+ </object>
+ <packing>
+ <property name="left-attach">1</property>
+ <property name="top-attach">12</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <child>
+ <object class="GtkCheckButton" id="image-preferred-dpi-checkbutton">
+ <property name="label" translatable="yes" context="documentinfopage|image-preferred-dpi-checkbutton">Image preferred DPI</property>
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">False</property>
+ <property name="hexpand">True</property>
+ <property name="draw-indicator">True</property>
+ <accessibility>
+ <relation type="label-for" target="image-preferred-dpi-combobox"/>
+ </accessibility>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBoxText" id="image-preferred-dpi-combobox">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="has-entry">True</property>
+ <items>
+ <item>96</item>
+ <item>150</item>
+ <item>200</item>
+ <item>300</item>
+ <item>600</item>
+ </items>
+ <accessibility>
+ <relation type="labelled-by" target="image-preferred-dpi-checkbutton"/>
+ </accessibility>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="left-attach">1</property>
+ <property name="top-attach">13</property>
+ </packing>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child internal-child="accessible">
+ <object class="AtkObject" id="DocumentInfoPage-atkobject">
+ <property name="AtkObject::accessible-description" translatable="yes" context="documentinfopage|extended_tip|DocumentInfoPage">Contains basic information about the current file.</property>
+ </object>
+ </child>
+ </object>
+</interface>
diff --git a/sfx2/uiconfig/ui/documentpropertiesdialog.ui b/sfx2/uiconfig/ui/documentpropertiesdialog.ui
new file mode 100644
index 000000000..5b5c1a8e7
--- /dev/null
+++ b/sfx2/uiconfig/ui/documentpropertiesdialog.ui
@@ -0,0 +1,349 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.22.1 -->
+<interface domain="sfx">
+ <requires lib="gtk+" version="3.20"/>
+ <object class="GtkDialog" id="DocumentPropertiesDialog">
+ <property name="can_focus">False</property>
+ <property name="border_width">6</property>
+ <property name="title" translatable="yes" context="documentpropertiesdialog|DocumentPropertiesDialog">Properties of “%1”</property>
+ <property name="modal">True</property>
+ <property name="default_width">0</property>
+ <property name="default_height">0</property>
+ <property name="type_hint">dialog</property>
+ <child>
+ <placeholder/>
+ </child>
+ <child internal-child="vbox">
+ <object class="GtkBox" id="dialog-vbox1">
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">12</property>
+ <child internal-child="action_area">
+ <object class="GtkButtonBox" id="dialog-action_area1">
+ <property name="can_focus">False</property>
+ <property name="layout_style">end</property>
+ <child>
+ <object class="GtkButton" id="reset">
+ <property name="label" translatable="yes" context="stock">_Reset</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use-underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="ok">
+ <property name="label" translatable="yes" context="stock">_OK</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="has_default">True</property>
+ <property name="receives_default">True</property>
+ <property name="use-underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="cancel">
+ <property name="label" translatable="yes" context="stock">_Cancel</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use-underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="help">
+ <property name="label" translatable="yes" context="stock">_Help</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use-underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">3</property>
+ <property name="secondary">True</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">end</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkNotebook" id="tabcontrol">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="enable_popup">True</property>
+ <child>
+ <!-- n-columns=1 n-rows=1 -->
+ <object class="GtkGrid">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ </child>
+ <child type="tab">
+ <object class="GtkLabel" id="general">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes" context="documentpropertiesdialog|general">General </property>
+ </object>
+ <packing>
+ <property name="tab_fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <!-- n-columns=1 n-rows=1 -->
+ <object class="GtkGrid">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child type="tab">
+ <object class="GtkLabel" id="description">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes" context="documentpropertiesdialog|description">Description</property>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ <property name="tab_fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <!-- n-columns=1 n-rows=1 -->
+ <object class="GtkGrid">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child type="tab">
+ <object class="GtkLabel" id="customprops">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes" context="documentpropertiesdialog|customprops">Custom Properties</property>
+ </object>
+ <packing>
+ <property name="position">2</property>
+ <property name="tab_fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <!-- n-columns=1 n-rows=1 -->
+ <object class="GtkGrid">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ <child type="tab">
+ <object class="GtkLabel" id="cmisprops">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes" context="documentpropertiesdialog|cmisprops">CMIS Properties</property>
+ </object>
+ <packing>
+ <property name="position">3</property>
+ <property name="tab_fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <!-- n-columns=1 n-rows=1 -->
+ <object class="GtkGrid">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="position">4</property>
+ </packing>
+ </child>
+ <child type="tab">
+ <object class="GtkLabel" id="security">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes" context="documentpropertiesdialog|security">Security</property>
+ </object>
+ <packing>
+ <property name="position">4</property>
+ <property name="tab_fill">False</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <action-widgets>
+ <action-widget response="101">reset</action-widget>
+ <action-widget response="-5">ok</action-widget>
+ <action-widget response="-6">cancel</action-widget>
+ <action-widget response="-11">help</action-widget>
+ </action-widgets>
+ </object>
+</interface>
diff --git a/sfx2/uiconfig/ui/editdocumentdialog.ui b/sfx2/uiconfig/ui/editdocumentdialog.ui
new file mode 100644
index 000000000..aa1cc1d2b
--- /dev/null
+++ b/sfx2/uiconfig/ui/editdocumentdialog.ui
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.20.2 -->
+<interface domain="sfx">
+ <requires lib="gtk+" version="3.20"/>
+ <object class="GtkMessageDialog" id="EditDocumentDialog">
+ <property name="can_focus">False</property>
+ <property name="title" translatable="yes" context="editdocumentdialog|EditDocumentDialog">Confirm editing of document</property>
+ <property name="resizable">False</property>
+ <property name="modal">True</property>
+ <property name="type_hint">dialog</property>
+ <property name="skip_taskbar_hint">True</property>
+ <property name="message_type">question</property>
+ <property name="text" translatable="yes" context="editdocumentdialog|EditDocumentDialog">Are you sure you want to edit the document?</property>
+ <property name="secondary_text" translatable="yes" context="editdocumentdialog|EditDocumentDialog">The original file can be signed without editing the document. Existing signatures on the document will be lost in case of saving an edited version.</property>
+ <child internal-child="vbox">
+ <object class="GtkBox" id="messagedialog-vbox">
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">12</property>
+ <child internal-child="action_area">
+ <object class="GtkButtonBox" id="messagedialog-action_area">
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkButton" id="edit">
+ <property name="label" translatable="yes" context="editdocumentdialog|edit">Edit Document</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="has_default">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ <property name="non_homogeneous">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="cancel">
+ <property name="label" translatable="yes" context="editdocumentdialog|cancel">Cancel</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ <property name="non_homogeneous">True</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">end</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <action-widgets>
+ <action-widget response="-5">edit</action-widget>
+ <action-widget response="-6">cancel</action-widget>
+ </action-widgets>
+ </object>
+</interface>
diff --git a/sfx2/uiconfig/ui/editdurationdialog.ui b/sfx2/uiconfig/ui/editdurationdialog.ui
new file mode 100644
index 000000000..b6ebe0553
--- /dev/null
+++ b/sfx2/uiconfig/ui/editdurationdialog.ui
@@ -0,0 +1,364 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.20.4 -->
+<interface domain="sfx">
+ <requires lib="gtk+" version="3.20"/>
+ <object class="GtkAdjustment" id="adjustment1">
+ <property name="upper">2147483647</property>
+ <property name="step_increment">1</property>
+ <property name="page_increment">10</property>
+ </object>
+ <object class="GtkAdjustment" id="adjustment2">
+ <property name="upper">2147483647</property>
+ <property name="step_increment">1</property>
+ <property name="page_increment">10</property>
+ </object>
+ <object class="GtkAdjustment" id="adjustment3">
+ <property name="upper">2147483647</property>
+ <property name="step_increment">1</property>
+ <property name="page_increment">10</property>
+ </object>
+ <object class="GtkAdjustment" id="adjustment4">
+ <property name="upper">2147483647</property>
+ <property name="step_increment">1</property>
+ <property name="page_increment">10</property>
+ </object>
+ <object class="GtkAdjustment" id="adjustment5">
+ <property name="upper">2147483647</property>
+ <property name="step_increment">1</property>
+ <property name="page_increment">10</property>
+ </object>
+ <object class="GtkAdjustment" id="adjustment6">
+ <property name="upper">2147483647</property>
+ <property name="step_increment">1</property>
+ <property name="page_increment">10</property>
+ </object>
+ <object class="GtkAdjustment" id="adjustment7">
+ <property name="upper">2147483647</property>
+ <property name="step_increment">1</property>
+ <property name="page_increment">10</property>
+ </object>
+ <object class="GtkDialog" id="EditDurationDialog">
+ <property name="can_focus">False</property>
+ <property name="border_width">6</property>
+ <property name="title" translatable="yes" context="editdurationdialog|EditDurationDialog">Edit Duration</property>
+ <property name="resizable">False</property>
+ <property name="modal">True</property>
+ <property name="default_width">0</property>
+ <property name="default_height">0</property>
+ <property name="type_hint">dialog</property>
+ <child internal-child="vbox">
+ <object class="GtkBox" id="dialog-vbox1">
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">12</property>
+ <child internal-child="action_area">
+ <object class="GtkButtonBox" id="dialog-action_area1">
+ <property name="can_focus">False</property>
+ <property name="layout_style">end</property>
+ <child>
+ <object class="GtkButton" id="ok">
+ <property name="label" translatable="yes" context="stock">_OK</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="has_default">True</property>
+ <property name="receives_default">True</property>
+ <property name="use-underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="cancel">
+ <property name="label" translatable="yes" context="stock">_Cancel</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use-underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="help">
+ <property name="label" translatable="yes" context="stock">_Help</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use-underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ <property name="secondary">True</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">end</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFrame" id="storageframe">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <!-- n-columns=1 n-rows=1 -->
+ <object class="GtkGrid" id="grid1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="row_spacing">6</property>
+ <property name="column_spacing">12</property>
+ <property name="margin-start">12</property>
+ <property name="margin-top">6</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <child>
+ <object class="GtkCheckButton" id="negative">
+ <property name="label" translatable="yes" context="editdurationdialog|negative">_Negative</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_underline">True</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ <property name="width">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes" context="editdurationdialog|label1">_Years:</property>
+ <property name="use_underline">True</property>
+ <property name="mnemonic_widget">years</property>
+ <property name="xalign">1</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes" context="editdurationdialog|label">_Months:</property>
+ <property name="use_underline">True</property>
+ <property name="mnemonic_widget">months</property>
+ <property name="xalign">1</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label3">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes" context="editdurationdialog|label3">_Days:</property>
+ <property name="use_underline">True</property>
+ <property name="mnemonic_widget">days</property>
+ <property name="xalign">1</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label4">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes" context="editdurationdialog|label4">H_ours:</property>
+ <property name="use_underline">True</property>
+ <property name="mnemonic_widget">hours</property>
+ <property name="xalign">1</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">4</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label5">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes" context="editdurationdialog|label5">Min_utes:</property>
+ <property name="use_underline">True</property>
+ <property name="mnemonic_widget">minutes</property>
+ <property name="xalign">1</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">5</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label6">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes" context="editdurationdialog|label6">_Seconds:</property>
+ <property name="use_underline">True</property>
+ <property name="mnemonic_widget">seconds</property>
+ <property name="xalign">1</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">6</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label7">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes" context="editdurationdialog|label7">Millise_conds:</property>
+ <property name="use_underline">True</property>
+ <property name="mnemonic_widget">milliseconds</property>
+ <property name="xalign">1</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">7</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="years">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="activates_default">True</property>
+ <property name="truncate-multiline">True</property>
+ <property name="adjustment">adjustment1</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="months">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="activates_default">True</property>
+ <property name="truncate-multiline">True</property>
+ <property name="adjustment">adjustment2</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="days">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="activates_default">True</property>
+ <property name="truncate-multiline">True</property>
+ <property name="adjustment">adjustment3</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="hours">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="activates_default">True</property>
+ <property name="truncate-multiline">True</property>
+ <property name="adjustment">adjustment4</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">4</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="minutes">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="activates_default">True</property>
+ <property name="truncate-multiline">True</property>
+ <property name="adjustment">adjustment5</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">5</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="seconds">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="activates_default">True</property>
+ <property name="truncate-multiline">True</property>
+ <property name="adjustment">adjustment6</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">6</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="milliseconds">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="activates_default">True</property>
+ <property name="truncate-multiline">True</property>
+ <property name="adjustment">adjustment7</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">7</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes" context="editdurationdialog|label2">Duration</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <action-widgets>
+ <action-widget response="-5">ok</action-widget>
+ <action-widget response="-6">cancel</action-widget>
+ <action-widget response="-11">help</action-widget>
+ </action-widgets>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+</interface>
diff --git a/sfx2/uiconfig/ui/emojicontrol.ui b/sfx2/uiconfig/ui/emojicontrol.ui
new file mode 100644
index 000000000..6bedfc612
--- /dev/null
+++ b/sfx2/uiconfig/ui/emojicontrol.ui
@@ -0,0 +1,181 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.22.2 -->
+<interface domain="sfx">
+ <requires lib="gtk+" version="3.20"/>
+ <object class="GtkPopover" id="emojictrl">
+ <property name="can_focus">False</property>
+ <property name="constrain-to">none</property>
+ <child>
+ <object class="GtkBox" id="container">
+ <property name="width_request">350</property>
+ <property name="height_request">200</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <!-- n-columns=1 n-rows=1 -->
+ <object class="GtkGrid">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="column_spacing">3</property>
+ <child>
+ <object class="GtkToggleButton" id="people">
+ <property name="label" translatable="no">1f603</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="active">True</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToggleButton" id="nature">
+ <property name="label" translatable="no">1f43c</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToggleButton" id="food">
+ <property name="label" translatable="no">1f34f</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToggleButton" id="activity">
+ <property name="label" translatable="no">1f3c8</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">3</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToggleButton" id="travel">
+ <property name="label" translatable="no">1f697</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">4</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToggleButton" id="objects">
+ <property name="label" translatable="no">1f4f1</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">5</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToggleButton" id="symbols">
+ <property name="label" translatable="no">1f499</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">6</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToggleButton" id="flags">
+ <property name="label" translatable="no">1f1ee-1f1f3</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">7</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToggleButton" id="unicode9">
+ <property name="label" translatable="no">1f939</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">8</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="emoji_win">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="hscrollbar_policy">never</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkViewport">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <child>
+ <object class="GtkDrawingArea" id="emoji_view">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="events">GDK_BUTTON_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_STRUCTURE_MASK</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+</interface>
diff --git a/sfx2/uiconfig/ui/errorfindemaildialog.ui b/sfx2/uiconfig/ui/errorfindemaildialog.ui
new file mode 100644
index 000000000..2318a716d
--- /dev/null
+++ b/sfx2/uiconfig/ui/errorfindemaildialog.ui
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.18.3 -->
+<interface domain="sfx">
+ <requires lib="gtk+" version="3.20"/>
+ <object class="GtkMessageDialog" id="ErrorFindEmailDialog">
+ <property name="can_focus">False</property>
+ <property name="title" translatable="yes" context="errorfindemaildialog|ErrorFindEmailDialog">No email configuration</property>
+ <property name="resizable">False</property>
+ <property name="modal">True</property>
+ <property name="type_hint">dialog</property>
+ <property name="skip_taskbar_hint">True</property>
+ <property name="message_type">error</property>
+ <property name="buttons">ok</property>
+ <property name="text" translatable="yes" context="errorfindemaildialog|ErrorFindEmailDialog">%PRODUCTNAME was unable to find a working email configuration.</property>
+ <property name="secondary_text" translatable="yes" context="errorfindemaildialog|ErrorFindEmailDialog">Please save this document locally instead and attach it from within your email client.</property>
+ <child internal-child="vbox">
+ <object class="GtkBox" id="messagedialog-vbox">
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">12</property>
+ <child internal-child="action_area">
+ <object class="GtkButtonBox" id="messagedialog-action_area">
+ <property name="can_focus">False</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">end</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+</interface>
diff --git a/sfx2/uiconfig/ui/extrabutton.ui b/sfx2/uiconfig/ui/extrabutton.ui
new file mode 100644
index 000000000..cec9c2b67
--- /dev/null
+++ b/sfx2/uiconfig/ui/extrabutton.ui
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.36.0 -->
+<interface domain="sfx">
+ <requires lib="gtk+" version="3.20"/>
+ <object class="GtkBox" id="ExtraButton">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkButton" id="button">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="receives_default">True</property>
+ <property name="always_show_image">True</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+</interface>
diff --git a/sfx2/uiconfig/ui/floatingrecord.ui b/sfx2/uiconfig/ui/floatingrecord.ui
new file mode 100644
index 000000000..570fba2a9
--- /dev/null
+++ b/sfx2/uiconfig/ui/floatingrecord.ui
@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.22.1 -->
+<interface domain="sfx">
+ <requires lib="gtk+" version="3.20"/>
+ <object class="GtkDialog" id="FloatingRecord">
+ <property name="can_focus">False</property>
+ <property name="title" translatable="yes" context="floatingrecord|FloatingRecord">Record Macro</property>
+ <property name="default_width">0</property>
+ <property name="default_height">0</property>
+ <property name="type_hint">utility</property>
+ <property name="deletable">False</property>
+ <child>
+ <placeholder/>
+ </child>
+ <child internal-child="vbox">
+ <object class="GtkBox">
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <child internal-child="action_area">
+ <object class="GtkButtonBox">
+ <property name="can_focus">False</property>
+ <property name="no_show_all">True</property>
+ <property name="layout_style">end</property>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToolbar" id="toolbar">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="toolbar_style">text</property>
+ <property name="show_arrow">False</property>
+ <child>
+ <object class="GtkToolButton" id=".uno:StopRecording">
+ <property name="visible">True</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="use_underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="homogeneous">True</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+</interface>
diff --git a/sfx2/uiconfig/ui/helpbookmarkpage.ui b/sfx2/uiconfig/ui/helpbookmarkpage.ui
new file mode 100644
index 000000000..d7219ec54
--- /dev/null
+++ b/sfx2/uiconfig/ui/helpbookmarkpage.ui
@@ -0,0 +1,103 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.22.2 -->
+<interface domain="sfx">
+ <requires lib="gtk+" version="3.20"/>
+ <object class="GtkTreeStore" id="liststore2">
+ <columns>
+ <!-- column-name text -->
+ <column type="gchararray"/>
+ <!-- column-name id -->
+ <column type="gchararray"/>
+ </columns>
+ </object>
+ <object class="GtkBox" id="HelpBookmarkPage">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="border_width">6</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <!-- n-columns=1 n-rows=1 -->
+ <object class="GtkGrid" id="grid1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="row_spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="label1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes" context="helpbookmarkpage|label1">_Bookmarks</property>
+ <property name="use_underline">True</property>
+ <property name="mnemonic_widget">bookmarks</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkTreeView" id="bookmarks">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="model">liststore2</property>
+ <property name="headers_visible">False</property>
+ <property name="headers_clickable">False</property>
+ <property name="search_column">0</property>
+ <property name="show_expanders">False</property>
+ <child internal-child="selection">
+ <object class="GtkTreeSelection"/>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="treeviewcolumn2">
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderertext2"/>
+ <attributes>
+ <attribute name="text">0</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="display">
+ <property name="label" translatable="yes" context="helpbookmarkpage|display">_Display</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="halign">end</property>
+ <property name="use_underline">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+</interface>
diff --git a/sfx2/uiconfig/ui/helpcontentpage.ui b/sfx2/uiconfig/ui/helpcontentpage.ui
new file mode 100644
index 000000000..42012f9bc
--- /dev/null
+++ b/sfx2/uiconfig/ui/helpcontentpage.ui
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.22.2 -->
+<interface domain="sfx">
+ <requires lib="gtk+" version="3.20"/>
+ <object class="GtkTreeStore" id="liststore2">
+ <columns>
+ <!-- column-name expander -->
+ <column type="GdkPixbuf"/>
+ <!-- column-name text -->
+ <column type="gchararray"/>
+ <!-- column-name id -->
+ <column type="gchararray"/>
+ </columns>
+ </object>
+ <!-- n-columns=1 n-rows=1 -->
+ <object class="GtkGrid" id="HelpContentPage">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="border_width">6</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkScrolledWindow">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkTreeView" id="content">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="model">liststore2</property>
+ <property name="headers_visible">False</property>
+ <property name="headers_clickable">False</property>
+ <property name="search_column">0</property>
+ <child internal-child="selection">
+ <object class="GtkTreeSelection" id="treeview-selection2"/>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="treeviewcolumn2">
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkCellRendererPixbuf" id="cellrenderertext4"/>
+ <attributes>
+ <attribute name="pixbuf">0</attribute>
+ </attributes>
+ </child>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderertext2">
+ <property name="xalign">0</property>
+ </object>
+ <attributes>
+ <attribute name="text">1</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ </object>
+</interface>
diff --git a/sfx2/uiconfig/ui/helpcontrol.ui b/sfx2/uiconfig/ui/helpcontrol.ui
new file mode 100644
index 000000000..8d30bdfc9
--- /dev/null
+++ b/sfx2/uiconfig/ui/helpcontrol.ui
@@ -0,0 +1,248 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.22.2 -->
+<interface domain="sfx">
+ <requires lib="gtk+" version="3.20"/>
+ <object class="GtkBox" id="HelpControl">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <child>
+ <object class="GtkBox" id="box1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="border_width">6</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkComboBoxText" id="active">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkNotebook" id="tabcontrol">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="enable_popup">True</property>
+ <child>
+ <!-- n-columns=1 n-rows=1 -->
+ <object class="GtkGrid">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ </child>
+ <child type="tab">
+ <object class="GtkLabel" id="contents">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes" context="helpcontrol|contents">Contents</property>
+ </object>
+ <packing>
+ <property name="tab_fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <!-- n-columns=1 n-rows=1 -->
+ <object class="GtkGrid">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child type="tab">
+ <object class="GtkLabel" id="index">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes" context="helpcontrol|index">Index</property>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ <property name="tab_fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <!-- n-columns=1 n-rows=1 -->
+ <object class="GtkGrid">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child type="tab">
+ <object class="GtkLabel" id="find">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes" context="helpcontrol|find">Find</property>
+ </object>
+ <packing>
+ <property name="position">2</property>
+ <property name="tab_fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <!-- n-columns=1 n-rows=1 -->
+ <object class="GtkGrid">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ <child type="tab">
+ <object class="GtkLabel" id="bookmarks">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes" context="helpcontrol|bookmarks">Bookmarks</property>
+ </object>
+ <packing>
+ <property name="position">3</property>
+ <property name="tab_fill">False</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+</interface>
diff --git a/sfx2/uiconfig/ui/helpindexpage.ui b/sfx2/uiconfig/ui/helpindexpage.ui
new file mode 100644
index 000000000..8850f8ccf
--- /dev/null
+++ b/sfx2/uiconfig/ui/helpindexpage.ui
@@ -0,0 +1,135 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.22.2 -->
+<interface domain="sfx">
+ <requires lib="gtk+" version="3.20"/>
+ <object class="GtkTreeStore" id="liststore2">
+ <columns>
+ <!-- column-name text -->
+ <column type="gchararray"/>
+ <!-- column-name id -->
+ <column type="gchararray"/>
+ </columns>
+ </object>
+ <object class="GtkBox" id="HelpIndexPage">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="border_width">6</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <!-- n-columns=1 n-rows=1 -->
+ <object class="GtkGrid" id="grid1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="row_spacing">6</property>
+ <child>
+ <object class="GtkButton" id="display">
+ <property name="label" translatable="yes" context="helpindexpage|display">_Display</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="halign">end</property>
+ <property name="use_underline">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFrame">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <!-- n-columns=1 n-rows=1 -->
+ <object class="GtkGrid" id="terms">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="vexpand">True</property>
+ <property name="row_spacing">3</property>
+ <property name="margin-top">6</property>
+ <child>
+ <object class="GtkScrolledWindow">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkTreeView" id="termlist">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="model">liststore2</property>
+ <property name="headers_visible">False</property>
+ <property name="headers_clickable">False</property>
+ <property name="search_column">0</property>
+ <property name="show_expanders">False</property>
+ <child internal-child="selection">
+ <object class="GtkTreeSelection"/>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="treeviewcolumn2">
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderertext2"/>
+ <attributes>
+ <attribute name="text">0</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="termentry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hexpand">True</property>
+ <property name="truncate-multiline">True</property>
+ <property name="activates_default">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes" context="helpindexpage|label1">_Search Term</property>
+ <property name="use_underline">True</property>
+ <property name="xalign">0</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+</interface>
diff --git a/sfx2/uiconfig/ui/helpmanual.ui b/sfx2/uiconfig/ui/helpmanual.ui
new file mode 100644
index 000000000..0da32ebb4
--- /dev/null
+++ b/sfx2/uiconfig/ui/helpmanual.ui
@@ -0,0 +1,79 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.22.1 -->
+<interface domain="sfx">
+ <requires lib="gtk+" version="3.20"/>
+ <object class="GtkMessageDialog" id="onlinehelpmanual">
+ <property name="can_focus">False</property>
+ <property name="title" translatable="yes" context="helpmanual|onlinehelpmanual">%PRODUCTNAME Help Not Installed</property>
+ <property name="resizable">False</property>
+ <property name="modal">True</property>
+ <property name="type_hint">dialog</property>
+ <property name="skip_taskbar_hint">True</property>
+ <property name="text" translatable="yes" context="helpmanual|onlinehelpmanual">The %PRODUCTNAME built-in help for current UI language ($UILOCALE) is not installed on your computer.</property>
+ <property name="secondary_text" translatable="yes" context="helpmanual|onlinehelpmanual">You may either install it from our website or your system’s repositories, or read an online version.</property>
+ <child internal-child="vbox">
+ <object class="GtkBox" id="internal_box">
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <child internal-child="action_area">
+ <object class="GtkButtonBox" id="btnbox">
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkButton" id="website">
+ <property name="label" translatable="yes" context="helpmanual|website">Read Help Online</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="cancel">
+ <property name="label" translatable="yes" context="stock">_Cancel</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="has_default">True</property>
+ <property name="receives_default">True</property>
+ <property name="use-underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="hidedialog">
+ <property name="label" translatable="yes" context="helpmanual|hidedialog">Do not show this dialog again</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <action-widgets>
+ <action-widget response="-5">website</action-widget>
+ <action-widget response="-6">cancel</action-widget>
+ </action-widgets>
+ </object>
+</interface>
diff --git a/sfx2/uiconfig/ui/helpsearchpage.ui b/sfx2/uiconfig/ui/helpsearchpage.ui
new file mode 100644
index 000000000..664bda924
--- /dev/null
+++ b/sfx2/uiconfig/ui/helpsearchpage.ui
@@ -0,0 +1,175 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.22.2 -->
+<interface domain="sfx">
+ <requires lib="gtk+" version="3.20"/>
+ <object class="GtkTreeStore" id="liststore2">
+ <columns>
+ <!-- column-name text -->
+ <column type="gchararray"/>
+ <!-- column-name id -->
+ <column type="gchararray"/>
+ </columns>
+ </object>
+ <object class="GtkBox" id="HelpSearchPage">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="border_width">6</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <!-- n-columns=1 n-rows=1 -->
+ <object class="GtkGrid" id="grid1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="row_spacing">6</property>
+ <child>
+ <object class="GtkButton" id="display">
+ <property name="label" translatable="yes" context="helpsearchpage|display">_Display</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="halign">end</property>
+ <property name="use_underline">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">5</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes" context="helpsearchpage|label1">_Search term</property>
+ <property name="use_underline">True</property>
+ <property name="mnemonic_widget">search</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkTreeView" id="results">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="model">liststore2</property>
+ <property name="headers_visible">False</property>
+ <property name="headers_clickable">False</property>
+ <property name="search_column">0</property>
+ <property name="show_expanders">False</property>
+ <child internal-child="selection">
+ <object class="GtkTreeSelection"/>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="treeviewcolumn2">
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderertext2"/>
+ <attributes>
+ <attribute name="text">0</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">4</property>
+ </packing>
+ </child>
+ <child>
+ <!-- n-columns=1 n-rows=1 -->
+ <object class="GtkGrid" id="grid2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="column_spacing">12</property>
+ <child>
+ <object class="GtkComboBoxText" id="search">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="has_entry">True</property>
+ <child internal-child="entry">
+ <object class="GtkEntry" id="combobox-entry">
+ <property name="truncate-multiline">True</property>
+ <property name="can_focus">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="find">
+ <property name="label" translatable="yes" context="helpsearchpage|find">_Find</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_underline">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="completewords">
+ <property name="label" translatable="yes" context="helpsearchpage|completewords">_Complete words only</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_underline">True</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="headings">
+ <property name="label" translatable="yes" context="helpsearchpage|headings">Find in _headings only</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_underline">True</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">3</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+</interface>
diff --git a/sfx2/uiconfig/ui/helpwindow.ui b/sfx2/uiconfig/ui/helpwindow.ui
new file mode 100644
index 000000000..6241c287c
--- /dev/null
+++ b/sfx2/uiconfig/ui/helpwindow.ui
@@ -0,0 +1,199 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.22.2 -->
+<interface domain="sfx">
+ <requires lib="gtk+" version="3.20"/>
+ <object class="GtkPaned" id="HelpWindow">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="wide_handle">True</property>
+ <child>
+ <object class="GtkBox" id="helppanewindow">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="resize">False</property>
+ <property name="shrink">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="helptextpane">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkBox" id="helptexttoolbar">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkToolbar" id="toolbar">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="toolbar_style">icons</property>
+ <child>
+ <object class="GtkToolButton" id="index">
+ <property name="visible">True</property>
+ <property name="icon_name">sfx2/res/indexon_small.png</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSeparatorToolItem" id="separator1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToolButton" id="backward">
+ <property name="visible">True</property>
+ <property name="tooltip_text" translatable="yes" context="helpwindow|backward|tooltip_text">Previous Page</property>
+ <property name="icon_name">res/sc06301.png</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToolButton" id="forward">
+ <property name="visible">True</property>
+ <property name="tooltip_text" translatable="yes" context="helpwindow|forward|tooltip_text">Next Page</property>
+ <property name="icon_name">res/sc06300.png</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToolButton" id="start">
+ <property name="visible">True</property>
+ <property name="tooltip_text" translatable="yes" context="helpwindow|start|tooltip_text">First Page</property>
+ <property name="icon_name">res/sc06303.png</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSeparatorToolItem" id="separator2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToolButton" id="print">
+ <property name="visible">True</property>
+ <property name="tooltip_text" translatable="yes" context="helpwindow|print|tooltip_text">Print</property>
+ <property name="icon_name">res/sc05504.png</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToolButton" id="bookmarks">
+ <property name="visible">True</property>
+ <property name="tooltip_text" translatable="yes" context="helpwindow|bookmarks|tooltip_text">Add to Bookmarks</property>
+ <property name="icon_name">sfx2/res/favourite.png</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToolButton" id="searchdialog">
+ <property name="visible">True</property>
+ <property name="tooltip_text" translatable="yes" context="helpwindow|searchdialog|tooltip_text">Find on this Page</property>
+ <property name="icon_name">sfx2/res/sc05961.png</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">False</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="checkbutton">
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="no_show_all">True</property>
+ <property name="use_underline">True</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">end</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="helptextwindow">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="resize">True</property>
+ <property name="shrink">False</property>
+ </packing>
+ </child>
+ </object>
+ <object class="GtkMenu" id="menu">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+</interface>
diff --git a/sfx2/uiconfig/ui/infobar.ui b/sfx2/uiconfig/ui/infobar.ui
new file mode 100644
index 000000000..a7cf21662
--- /dev/null
+++ b/sfx2/uiconfig/ui/infobar.ui
@@ -0,0 +1,187 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.36.0 -->
+<interface domain="sfx">
+ <requires lib="gtk+" version="3.20"/>
+ <object class="GtkBox" id="InfoBar">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkScrolledWindow">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="hscrollbar_policy">never</property>
+ <property name="vscrollbar_policy">never</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkViewport">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <!-- n-columns=1 n-rows=1 -->
+ <object class="GtkGrid">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="border_width">2</property>
+ <property name="column_spacing">6</property>
+ <child>
+ <object class="GtkBox" id="right">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="valign">start</property>
+ <property name="spacing">3</property>
+ <child>
+ <object class="GtkBox" id="buttonbox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="valign">center</property>
+ <property name="spacing">6</property>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToolbar" id="closebar">
+ <property name="can_focus">True</property>
+ <property name="no_show_all">True</property>
+ <property name="valign">center</property>
+ <property name="toolbar_style">icons</property>
+ <property name="show_arrow">False</property>
+ <property name="icon_size">1</property>
+ <child>
+ <object class="GtkToolButton" id="close">
+ <property name="visible">True</property>
+ <property name="tooltip_text" translatable="yes" context="infobar|close|tooltip_text">Close Infobar</property>
+ <property name="icon_name">window-close-symbolic</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">True</property>
+ </packing>
+ </child>
+ <style>
+ <class name="small-button"/>
+ </style>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="left">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="valign">start</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkImage" id="image">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="valign">center</property>
+ <property name="icon-name">missing-image</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="primary">
+ <property name="can_focus">False</property>
+ <property name="no_show_all">True</property>
+ <property name="valign">center</property>
+ <property name="vexpand">True</property>
+ <property name="label">label</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ <child internal-child="accessible">
+ <object class="AtkObject" id="primary-atkobject">
+ <property name="AtkObject::accessible-role">static</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow">
+ <property name="width_request">60</property>
+ <property name="height_request">6</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="hscrollbar_policy">never</property>
+ <property name="vscrollbar_policy">never</property>
+ <child>
+ <object class="GtkTextView" id="secondary">
+ <property name="width_request">60</property>
+ <property name="height_request">6</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="editable">False</property>
+ <property name="wrap_mode">word-char</property>
+ <property name="cursor_visible">False</property>
+ <property name="accepts_tab">False</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ <object class="GtkSizeGroup" id="sizegroup1">
+ <property name="mode">vertical</property>
+ <widgets>
+ <widget name="right"/>
+ <widget name="left"/>
+ </widgets>
+ </object>
+</interface>
diff --git a/sfx2/uiconfig/ui/inputdialog.ui b/sfx2/uiconfig/ui/inputdialog.ui
new file mode 100644
index 000000000..9d8680fd9
--- /dev/null
+++ b/sfx2/uiconfig/ui/inputdialog.ui
@@ -0,0 +1,128 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.20.2 -->
+<interface domain="sfx">
+ <requires lib="gtk+" version="3.20"/>
+ <object class="GtkDialog" id="InputDialog">
+ <property name="can_focus">False</property>
+ <property name="border_width">6</property>
+ <property name="modal">True</property>
+ <property name="default_width">0</property>
+ <property name="default_height">0</property>
+ <property name="type_hint">dialog</property>
+ <child internal-child="vbox">
+ <object class="GtkBox" id="dialog-vbox1">
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">12</property>
+ <child internal-child="action_area">
+ <object class="GtkButtonBox" id="dialog-action_area1">
+ <property name="can_focus">False</property>
+ <property name="layout_style">end</property>
+ <child>
+ <object class="GtkButton" id="help">
+ <property name="label" translatable="yes" context="stock">_Help</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use-underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ <property name="secondary">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="ok">
+ <property name="label" translatable="yes" context="stock">_OK</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="has_default">True</property>
+ <property name="receives_default">True</property>
+ <property name="use-underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="cancel">
+ <property name="label" translatable="yes" context="stock">_Cancel</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use-underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">end</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <!-- n-columns=1 n-rows=1 -->
+ <object class="GtkGrid" id="grid2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="valign">start</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="row_spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes" context="inputdialog|label">Height:</property>
+ <property name="use_underline">True</property>
+ <property name="mnemonic_widget">entry</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hexpand">True</property>
+ <property name="truncate-multiline">True</property>
+ <property name="activates_default">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <action-widgets>
+ <action-widget response="-11">help</action-widget>
+ <action-widget response="-5">ok</action-widget>
+ <action-widget response="-6">cancel</action-widget>
+ </action-widgets>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+</interface>
diff --git a/sfx2/uiconfig/ui/licensedialog.ui b/sfx2/uiconfig/ui/licensedialog.ui
new file mode 100644
index 000000000..71d504f93
--- /dev/null
+++ b/sfx2/uiconfig/ui/licensedialog.ui
@@ -0,0 +1,98 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.20.2 -->
+<interface domain="sfx">
+ <requires lib="gtk+" version="3.20"/>
+ <object class="GtkDialog" id="LicenseDialog">
+ <property name="can_focus">False</property>
+ <property name="border_width">6</property>
+ <property name="title" translatable="yes" context="licensedialog|LicenseDialog">Licensing and Legal information</property>
+ <property name="resizable">False</property>
+ <property name="modal">True</property>
+ <property name="default_width">0</property>
+ <property name="default_height">0</property>
+ <property name="type_hint">dialog</property>
+ <child internal-child="vbox">
+ <object class="GtkBox" id="dialog-vbox1">
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">12</property>
+ <child internal-child="action_area">
+ <object class="GtkButtonBox" id="dialog-action_area1">
+ <property name="can_focus">False</property>
+ <property name="layout_style">end</property>
+ <child>
+ <object class="GtkButton" id="ok">
+ <property name="label" translatable="yes" context="licensedialog|show">_Show License</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ <property name="secondary">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="close">
+ <property name="label" translatable="yes" context="stock">_Close</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="has_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="has_default">True</property>
+ <property name="receives_default">True</property>
+ <property name="use-underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">end</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes" context="licensedialog|label">%PRODUCTNAME is made available subject to the terms of the Mozilla Public License, v. 2.0. A copy of the MPL can be obtained at http://mozilla.org/MPL/2.0/.
+
+Third Party Code Additional copyright notices and license terms applicable to portions of the Software are set forth in the LICENSE.html file; choose Show License to see exact details in English.
+
+All trademarks and registered trademarks mentioned herein are the property of their respective owners.
+
+Copyright © 2000–2022 LibreOffice contributors. All rights reserved.
+
+This product was created by %OOOVENDOR, based on OpenOffice.org, which is Copyright 2000, 2011 Oracle and/or its affiliates. %OOOVENDOR acknowledges all community members, please see http://www.libreoffice.org/ for more details.</property>
+ <property name="wrap">True</property>
+ <property name="max_width_chars">80</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <action-widgets>
+ <action-widget response="-5">ok</action-widget>
+ <action-widget response="-7">close</action-widget>
+ </action-widgets>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+</interface>
diff --git a/sfx2/uiconfig/ui/linefragment.ui b/sfx2/uiconfig/ui/linefragment.ui
new file mode 100644
index 000000000..422a33241
--- /dev/null
+++ b/sfx2/uiconfig/ui/linefragment.ui
@@ -0,0 +1,240 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.22.1 -->
+<interface domain="sfx">
+ <requires lib="gtk+" version="3.20"/>
+ <object class="GtkAdjustment" id="adjustment1">
+ <property name="upper">86400000</property>
+ <property name="step_increment">1000</property>
+ <property name="page_increment">60000</property>
+ </object>
+ <object class="GtkImage" id="image1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="icon_name">sfx2/res/deleterow.png</property>
+ </object>
+ <!-- n-columns=1 n-rows=1 -->
+ <object class="GtkGrid" id="lineentry">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="no_show_all">True</property>
+ <property name="valign">start</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="column_spacing">4</property>
+ <child>
+ <object class="GtkComboBoxText" id="namebox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="has_entry">True</property>
+ <child internal-child="entry">
+ <object class="GtkEntry">
+ <property name="can_focus">True</property>
+ <property name="truncate-multiline">True</property>
+ <property name="width_chars">27</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBoxText" id="typebox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="remove">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="tooltip_text" translatable="yes" context="linefragment|STR_SFX_REMOVE_PROPERTY">Remove Property</property>
+ <property name="image">image1</property>
+ <property name="always-show-image">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">3</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkEntry" id="valueedit">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="truncate-multiline">True</property>
+ <property name="width_chars">32</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="datetimebox">
+ <property name="can_focus">False</property>
+ <property name="no_show_all">True</property>
+ <property name="hexpand">True</property>
+ <property name="spacing">3</property>
+ <child>
+ <object class="GtkMenuButton" id="date">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="draw_indicator">True</property>
+ <property name="label" translatable="no"></property>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="time">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="truncate-multiline">True</property>
+ <property name="adjustment">adjustment1</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="durationbox">
+ <property name="can_focus">False</property>
+ <property name="no_show_all">True</property>
+ <property name="spacing">3</property>
+ <child>
+ <object class="GtkEntry" id="duration">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hexpand">True</property>
+ <property name="truncate-multiline">True</property>
+ <property name="editable">False</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="durationbutton">
+ <property name="label" translatable="yes" context="linefragment|SFX_ST_EDIT">...</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="yesno">
+ <property name="can_focus">True</property>
+ <property name="no_show_all">True</property>
+ <property name="hscrollbar_policy">never</property>
+ <property name="vscrollbar_policy">never</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkViewport">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="homogeneous">True</property>
+ <child>
+ <object class="GtkRadioButton" id="yes">
+ <property name="label" translatable="yes" context="linefragment|yes">Yes</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="active">True</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkRadioButton" id="no">
+ <property name="label" translatable="yes" context="linefragment|no">No</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="draw_indicator">True</property>
+ <property name="group">yes</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ </object>
+ <object class="GtkSizeGroup" id="sizegroup">
+ <widgets>
+ <widget name="namebox"/>
+ <widget name="valueedit"/>
+ <widget name="datetimebox"/>
+ <widget name="durationbox"/>
+ <widget name="yesno"/>
+ </widgets>
+ </object>
+</interface>
diff --git a/sfx2/uiconfig/ui/linkeditdialog.ui b/sfx2/uiconfig/ui/linkeditdialog.ui
new file mode 100644
index 000000000..0fbe755ff
--- /dev/null
+++ b/sfx2/uiconfig/ui/linkeditdialog.ui
@@ -0,0 +1,220 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.22.1 -->
+<interface domain="sfx">
+ <requires lib="gtk+" version="3.20"/>
+ <object class="GtkDialog" id="LinkEditDialog">
+ <property name="can_focus">False</property>
+ <property name="border_width">6</property>
+ <property name="title" translatable="yes" context="linkeditdialog|title">Modify DDE Link</property>
+ <property name="modal">True</property>
+ <property name="default_width">0</property>
+ <property name="default_height">0</property>
+ <property name="type_hint">dialog</property>
+ <child>
+ <placeholder/>
+ </child>
+ <child internal-child="vbox">
+ <object class="GtkBox" id="dialog-vbox1">
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">12</property>
+ <child internal-child="action_area">
+ <object class="GtkButtonBox" id="dialog-action_area1">
+ <property name="can_focus">False</property>
+ <property name="layout_style">end</property>
+ <child>
+ <object class="GtkButton" id="ok">
+ <property name="label" translatable="yes" context="stock">_OK</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="has_default">True</property>
+ <property name="receives_default">True</property>
+ <property name="use-underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="cancel">
+ <property name="label" translatable="yes" context="stock">_Cancel</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use-underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="help">
+ <property name="label" translatable="yes" context="stock">_Help</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use-underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ <property name="secondary">True</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">end</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFrame" id="frame1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <!-- n-columns=1 n-rows=1 -->
+ <object class="GtkGrid" id="grid1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="row_spacing">6</property>
+ <property name="column_spacing">12</property>
+ <property name="margin-start">12</property>
+ <property name="margin-top">6</property>
+ <child>
+ <object class="GtkLabel" id="label2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes" context="linkeditdialog|label2">_Application:</property>
+ <property name="use_underline">True</property>
+ <property name="mnemonic_widget">app</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label3">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes" context="linkeditdialog|label3">_File:</property>
+ <property name="use_underline">True</property>
+ <property name="mnemonic_widget">file</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label4">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes" context="linkeditdialog|label4">_Category:</property>
+ <property name="use_underline">True</property>
+ <property name="mnemonic_widget">category</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="app">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hexpand">True</property>
+ <property name="truncate-multiline">True</property>
+ <property name="activates_default">True</property>
+ <child internal-child="accessible">
+ <object class="AtkObject" id="app-atkobject">
+ <property name="AtkObject::accessible-description" translatable="yes" context="linkeditdialog|extended_tip|app">Lists the application that last saved the source file. The office suite applications have the server name soffice.</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="file">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hexpand">True</property>
+ <property name="truncate-multiline">True</property>
+ <property name="activates_default">True</property>
+ <child internal-child="accessible">
+ <object class="AtkObject" id="file-atkobject">
+ <property name="AtkObject::accessible-description" translatable="yes" context="linkeditdialog|extended_tip|file">Path to the source file. Relative paths must be expressed by full URI, for example, with file://.</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="category">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hexpand">True</property>
+ <property name="truncate-multiline">True</property>
+ <property name="activates_default">True</property>
+ <child internal-child="accessible">
+ <object class="AtkObject" id="category-atkobject">
+ <property name="AtkObject::accessible-description" translatable="yes" context="linkeditdialog|extended_tip|category">Lists the section or object that the link refers to in the source file. If you want, you can enter a new section or object here.</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">2</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes" context="linkeditdialog|label1">Modify Link</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <action-widgets>
+ <action-widget response="-5">ok</action-widget>
+ <action-widget response="-6">cancel</action-widget>
+ <action-widget response="-11">help</action-widget>
+ </action-widgets>
+ </object>
+</interface>
diff --git a/sfx2/uiconfig/ui/loadtemplatedialog.ui b/sfx2/uiconfig/ui/loadtemplatedialog.ui
new file mode 100644
index 000000000..36ed46a32
--- /dev/null
+++ b/sfx2/uiconfig/ui/loadtemplatedialog.ui
@@ -0,0 +1,453 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.36.0 -->
+<interface domain="sfx">
+ <requires lib="gtk+" version="3.20"/>
+ <object class="GtkTreeStore" id="liststore1">
+ <columns>
+ <!-- column-name text -->
+ <column type="gchararray"/>
+ <!-- column-name id -->
+ <column type="gchararray"/>
+ </columns>
+ </object>
+ <object class="GtkTreeStore" id="liststore2">
+ <columns>
+ <!-- column-name text -->
+ <column type="gchararray"/>
+ <!-- column-name id -->
+ <column type="gchararray"/>
+ </columns>
+ </object>
+ <object class="GtkDialog" id="LoadTemplateDialog">
+ <property name="can_focus">False</property>
+ <property name="border_width">6</property>
+ <property name="title" translatable="yes" context="loadtemplatedialog|LoadTemplateDialog">New</property>
+ <property name="modal">True</property>
+ <property name="type_hint">dialog</property>
+ <child internal-child="vbox">
+ <object class="GtkBox" id="dialog-vbox1">
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">12</property>
+ <child internal-child="action_area">
+ <object class="GtkButtonBox" id="dialog-action_area1">
+ <property name="can_focus">False</property>
+ <property name="layout_style">end</property>
+ <child>
+ <object class="GtkButton" id="fromfile">
+ <property name="label" translatable="yes" context="loadtemplatedialog|fromfile">From File...</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="no_show_all">True</property>
+ <property name="use_underline">True</property>
+ <property name="tooltip_text" translatable="yes" context="loadtemplatedialog|fromfile|tooltip_text">Copy styles from selected external document to current document.</property>
+ <child internal-child="accessible">
+ <object class="AtkObject" id="fromfile-atkobject">
+ <property name="AtkObject::accessible-description" translatable="yes" context="loadtemplatedialog|extended_tip|fromfile">Locate the file containing the styles that you want to load, and then click Open.</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="cancel">
+ <property name="label" translatable="yes" context="stock">_Cancel</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="receives_default">True</property>
+ <property name="use-underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="ok">
+ <property name="label" translatable="yes" context="stock">_OK</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="has_default">True</property>
+ <property name="receives_default">True</property>
+ <property name="use-underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="help">
+ <property name="label" translatable="yes" context="stock">_Help</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="receives_default">True</property>
+ <property name="use-underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">3</property>
+ <property name="secondary">True</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">end</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <!-- n-columns=1 n-rows=1 -->
+ <object class="GtkGrid" id="grid1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="row_spacing">6</property>
+ <property name="column_spacing">12</property>
+ <property name="column_homogeneous">True</property>
+ <child>
+ <object class="GtkFrame" id="frame1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkScrolledWindow">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="shadow_type">in</property>
+ <property name="margin-start">12</property>
+ <property name="margin-top">6</property>
+ <child>
+ <object class="GtkTreeView" id="categories">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="vexpand">True</property>
+ <property name="model">liststore1</property>
+ <property name="headers_visible">False</property>
+ <property name="headers_clickable">False</property>
+ <property name="search_column">0</property>
+ <property name="show_expanders">False</property>
+ <child internal-child="selection">
+ <object class="GtkTreeSelection" id="treeview-selection1"/>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="treeviewcolumn1">
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderertext1"/>
+ <attributes>
+ <attribute name="text">0</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ <child internal-child="accessible">
+ <object class="AtkObject" id="categories-atkobject">
+ <property name="AtkObject::accessible-description" translatable="yes" context="loadtemplatedialog|extended_tip|categories">Lists the available template categories. Click a category to view its contents in the Templates list.</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes" context="loadtemplatedialog|label1">Categories</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFrame" id="frame2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkScrolledWindow">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="shadow_type">in</property>
+ <property name="margin-start">12</property>
+ <property name="margin-top">6</property>
+ <child>
+ <object class="GtkTreeView" id="templates">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="vexpand">True</property>
+ <property name="model">liststore2</property>
+ <property name="headers_visible">False</property>
+ <property name="headers_clickable">False</property>
+ <property name="search_column">0</property>
+ <property name="show_expanders">False</property>
+ <child internal-child="selection">
+ <object class="GtkTreeSelection" id="treeview-selection2"/>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="treeviewcolumn2">
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderertext2"/>
+ <attributes>
+ <attribute name="text">0</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ <child internal-child="accessible">
+ <object class="AtkObject" id="templates-atkobject">
+ <property name="AtkObject::accessible-description" translatable="yes" context="loadtemplatedialog|extended_tip|templates">Lists the available templates for the selected category.</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes" context="loadtemplatedialog|label2">Templates</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ <property name="tooltip_text" translatable="yes" context="loadtemplatedialog|label2|tooltip_text">Templates in the selected category</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <!-- n-columns=1 n-rows=1 -->
+ <object class="GtkGrid" id="optionsgrid">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="row_spacing">6</property>
+ <property name="column_spacing">12</property>
+ <property name="column_homogeneous">True</property>
+ <child>
+ <object class="GtkCheckButton" id="text">
+ <property name="label" translatable="yes" context="loadtemplatedialog|text">_Paragraph and Character</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="no_show_all">True</property>
+ <property name="use_underline">True</property>
+ <property name="tooltip_text" translatable="yes" context="loadtemplatedialog|text|tooltip_text">Copy paragraph and character styles to current document.</property>
+ <property name="draw_indicator">True</property>
+ <child internal-child="accessible">
+ <object class="AtkObject" id="text-atkobject">
+ <property name="AtkObject::accessible-description" translatable="yes" context="loadtemplatedialog|extended_tip|text">Loads the paragraph and the character styles from the selected document into the current document.</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="frame">
+ <property name="label" translatable="yes" context="loadtemplatedialog|frame">_Frame</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="no_show_all">True</property>
+ <property name="use_underline">True</property>
+ <property name="tooltip_text" translatable="yes" context="loadtemplatedialog|frame|tooltip_text">Copy frame styles to current document.</property>
+ <property name="draw_indicator">True</property>
+ <child internal-child="accessible">
+ <object class="AtkObject" id="frame-atkobject">
+ <property name="AtkObject::accessible-description" translatable="yes" context="loadtemplatedialog|extended_tip|frame">Loads the frame styles from the selected document into the current document.</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="pages">
+ <property name="label" translatable="yes" context="loadtemplatedialog|pages">Pa_ge</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="no_show_all">True</property>
+ <property name="use_underline">True</property>
+ <property name="tooltip_text" translatable="yes" context="loadtemplatedialog|pages|tooltip_text">Copy page styles to current document.</property>
+ <property name="draw_indicator">True</property>
+ <child internal-child="accessible">
+ <object class="AtkObject" id="pages-atkobject">
+ <property name="AtkObject::accessible-description" translatable="yes" context="loadtemplatedialog|extended_tip|pages">Loads the page styles from the selected document into the current document.</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="numbering">
+ <property name="label" translatable="yes" context="loadtemplatedialog|numbering">_List</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="no_show_all">True</property>
+ <property name="use_underline">True</property>
+ <property name="tooltip_text" translatable="yes" context="loadtemplatedialog|numbering|tooltip_text">Copy list styles to current document.</property>
+ <property name="draw_indicator">True</property>
+ <child internal-child="accessible">
+ <object class="AtkObject" id="numbering-atkobject">
+ <property name="AtkObject::accessible-description" translatable="yes" context="loadtemplatedialog|extended_tip|numbering">Loads the list styles from the selected document into the current document.</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="overwrite">
+ <property name="label" translatable="yes" context="loadtemplatedialog|overwrite">_Overwrite</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="no_show_all">True</property>
+ <property name="use_underline">True</property>
+ <property name="draw_indicator">True</property>
+ <property name="tooltip_text" translatable="yes" context="loadtemplatedialog|overwrite|tooltip_text">Overwrite styles with same name</property>
+ <child internal-child="accessible">
+ <object class="AtkObject" id="overwrite-atkobject">
+ <property name="AtkObject::accessible-description" translatable="yes" context="loadtemplatedialog|extended_tip|overwrite">Replaces styles in the current document that have the same name as the styles you are loading.</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">1</property>
+ <property name="width">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="alttitle">
+ <property name="can_focus">False</property>
+ <property name="no_show_all">True</property>
+ <property name="label" translatable="yes" context="loadtemplatedialog|alttitle">Load Styles from Template</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">3</property>
+ <property name="width">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkExpander" id="expander">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="resize_toplevel">True</property>
+ <child>
+ <!-- n-columns=1 n-rows=1 -->
+ <object class="GtkGrid" id="previewgrid">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_top">6</property>
+ <child>
+ <object class="GtkDrawingArea" id="image">
+ <property name="can_focus">False</property>
+ <property name="no_show_all">True</property>
+ <property name="halign">start</property>
+ <property name="valign">start</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label3">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes" context="loadtemplatedialog|label3">Pre_view</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child internal-child="accessible">
+ <object class="AtkObject" id="expander-atkobject">
+ <property name="AtkObject::accessible-description" translatable="yes" context="loadtemplatedialog|extended_tip|expander">Shows or hides a preview of a selected template.</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">2</property>
+ <property name="width">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <action-widgets>
+ <action-widget response="100">fromfile</action-widget>
+ <action-widget response="-6">cancel</action-widget>
+ <action-widget response="-5">ok</action-widget>
+ <action-widget response="-11">help</action-widget>
+ </action-widgets>
+ <child type="titlebar">
+ <placeholder/>
+ </child>
+ <child internal-child="accessible">
+ <object class="AtkObject" id="LoadTemplateDialog-atkobject">
+ <property name="AtkObject::accessible-description" translatable="yes" context="loadtemplatedialog|extended_tip|LoadTemplateDialog">Imports formatting styles from another document or template into the current document.</property>
+ </object>
+ </child>
+ </object>
+</interface>
diff --git a/sfx2/uiconfig/ui/managestylepage.ui b/sfx2/uiconfig/ui/managestylepage.ui
new file mode 100644
index 000000000..d2910e643
--- /dev/null
+++ b/sfx2/uiconfig/ui/managestylepage.ui
@@ -0,0 +1,241 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.22.1 -->
+<interface domain="sfx">
+ <requires lib="gtk+" version="3.20"/>
+ <object class="GtkBox" id="ManageStylePage">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="border_width">6</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkFrame" id="frame1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <!-- n-columns=1 n-rows=1 -->
+ <object class="GtkGrid" id="grid1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="row_spacing">6</property>
+ <property name="column_spacing">12</property>
+ <property name="margin-start">12</property>
+ <property name="margin-top">6</property>
+ <child>
+ <object class="GtkLabel" id="nameft">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes" context="managestylepage|nameft">_Name:</property>
+ <property name="use_underline">True</property>
+ <property name="mnemonic_widget">name</property>
+ <property name="xalign">1</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="nextstyleft">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes" context="managestylepage|nextstyleft">Ne_xt style:</property>
+ <property name="use_underline">True</property>
+ <property name="mnemonic_widget">nextstyle</property>
+ <property name="xalign">1</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="linkedwithft">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes" context="managestylepage|linkedwithft">Inherit from:</property>
+ <property name="use_underline">True</property>
+ <property name="mnemonic_widget">linkedwith</property>
+ <property name="xalign">1</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="categoryft">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes" context="managestylepage|categoryft">_Category:</property>
+ <property name="use_underline">True</property>
+ <property name="mnemonic_widget">category</property>
+ <property name="xalign">1</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">4</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBoxText" id="nextstyle">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="valign">center</property>
+ <property name="hexpand">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="editstyle">
+ <property name="label" translatable="yes" context="managestylepage|editstyle">Edit Style</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ </object>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="top_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBoxText" id="linkedwith">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="valign">center</property>
+ <property name="hexpand">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="editlinkstyle">
+ <property name="label" translatable="yes" context="managestylepage|editlinkstyle">Edit Style</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ </object>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="top_attach">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBoxText" id="category">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="valign">center</property>
+ <property name="hexpand">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">4</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="autoupdate">
+ <property name="label" translatable="yes" context="managestylepage|autoupdate">_AutoUpdate</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="no_show_all">True</property>
+ <property name="use_underline">True</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="name">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="valign">center</property>
+ <property name="hexpand">True</property>
+ <property name="editable">False</property>
+ <property name="truncate-multiline">True</property>
+ <property name="width_chars">52</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes" context="managestylepage|label1">Style</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFrame" id="frame2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkLabel" id="desc">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="wrap">True</property>
+ <property name="max_width_chars">52</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0</property>
+ <property name="margin-start">12</property>
+ <property name="margin-top">6</property>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes" context="managestylepage|label2">Contains</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+</interface>
diff --git a/sfx2/uiconfig/ui/navigator.ui b/sfx2/uiconfig/ui/navigator.ui
new file mode 100644
index 000000000..e7dd8ca09
--- /dev/null
+++ b/sfx2/uiconfig/ui/navigator.ui
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.38.2 -->
+<interface domain="sfx">
+ <requires lib="gtk+" version="3.20"/>
+ <object class="GtkBox" id="Navigator">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+</interface>
diff --git a/sfx2/uiconfig/ui/newstyle.ui b/sfx2/uiconfig/ui/newstyle.ui
new file mode 100644
index 000000000..91ccd6174
--- /dev/null
+++ b/sfx2/uiconfig/ui/newstyle.ui
@@ -0,0 +1,212 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.22.1 -->
+<interface domain="sfx">
+ <requires lib="gtk+" version="3.20"/>
+ <object class="GtkTreeStore" id="liststore1">
+ <columns>
+ <!-- column-name text -->
+ <column type="gchararray"/>
+ <!-- column-name id -->
+ <column type="gchararray"/>
+ </columns>
+ </object>
+ <object class="GtkDialog" id="CreateStyleDialog">
+ <property name="can_focus">False</property>
+ <property name="border_width">6</property>
+ <property name="title" translatable="yes" context="newstyle|CreateStyleDialog">New Style from Selection</property>
+ <property name="modal">True</property>
+ <property name="default_width">0</property>
+ <property name="default_height">0</property>
+ <property name="type_hint">dialog</property>
+ <child>
+ <placeholder/>
+ </child>
+ <child internal-child="vbox">
+ <object class="GtkBox" id="dialog-vbox3">
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">12</property>
+ <child internal-child="action_area">
+ <object class="GtkButtonBox" id="dialog-action_area3">
+ <property name="can_focus">False</property>
+ <property name="layout_style">end</property>
+ <child>
+ <object class="GtkButton" id="ok">
+ <property name="label" translatable="yes" context="stock">_OK</property>
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="has_default">True</property>
+ <property name="receives_default">True</property>
+ <property name="use-underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="cancel">
+ <property name="label" translatable="yes" context="stock">_Cancel</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use-underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="help">
+ <property name="label" translatable="yes" context="stock">_Help</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use-underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ <property name="secondary">True</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">end</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFrame" id="frame1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <!-- n-columns=1 n-rows=1 -->
+ <object class="GtkGrid" id="stylegrid">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="row_spacing">3</property>
+ <property name="margin-start">12</property>
+ <property name="margin-top">6</property>
+ <child>
+ <object class="GtkScrolledWindow">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkTreeView" id="styles">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="model">liststore1</property>
+ <property name="headers_visible">False</property>
+ <property name="headers_clickable">False</property>
+ <property name="search_column">0</property>
+ <property name="show_expanders">False</property>
+ <child internal-child="selection">
+ <object class="GtkTreeSelection" id="treeview-selection1"/>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="treeviewcolumn1">
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderertext1"/>
+ <attributes>
+ <attribute name="text">0</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ <child internal-child="accessible">
+ <object class="AtkObject" id="styles-atkobject">
+ <property name="AtkObject::accessible-name" translatable="yes" context="newstyle|styles-atkobject">Custom styles for selected category</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="stylename">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hexpand">True</property>
+ <property name="activates_default">True</property>
+ <property name="truncate-multiline">True</property>
+ <child internal-child="accessible">
+ <object class="AtkObject" id="stylename-atkobject">
+ <property name="AtkObject::accessible-name" translatable="yes" context="newstyle|stylename-atkobject">Style name</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="categorylabel">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="mnemonic-widget">styles</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes" context="newstyle|categorylabel">Custom styles for current document</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes" context="newstyle|label1">Enter new style name:</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <action-widgets>
+ <action-widget response="-5">ok</action-widget>
+ <action-widget response="-6">cancel</action-widget>
+ <action-widget response="-11">help</action-widget>
+ </action-widgets>
+ </object>
+</interface>
diff --git a/sfx2/uiconfig/ui/notebookbar.ui b/sfx2/uiconfig/ui/notebookbar.ui
new file mode 100644
index 000000000..cc8656720
--- /dev/null
+++ b/sfx2/uiconfig/ui/notebookbar.ui
@@ -0,0 +1,95 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.18.3 -->
+<interface domain="sfx">
+ <requires lib="gtk+" version="3.20"/>
+ <requires lib="LibreOffice" version="1.0"/>
+ <!-- n-columns=1 n-rows=1 -->
+ <object class="GtkGrid" id="NotebookBar">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkBox" id="box1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="sfxlo-NotebookbarTabControl" id="notebook1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <child>
+ <object class="GtkBox" id="box63">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkBox" id="box2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkButton" id="Open">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="action_name">.uno:Open</property>
+ <property name="relief">none</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="OpenRemote">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="action_name">.uno:OpenRemote</property>
+ <property name="relief">none</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <child type="tab">
+ <object class="GtkLabel" id="label9">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes" context="notebookbar|label9">File</property>
+ <property name="use_underline">True</property>
+ </object>
+ <packing>
+ <property name="tab_fill">False</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ </object>
+</interface>
diff --git a/sfx2/uiconfig/ui/notebookbarpopup.ui b/sfx2/uiconfig/ui/notebookbarpopup.ui
new file mode 100644
index 000000000..297c5093d
--- /dev/null
+++ b/sfx2/uiconfig/ui/notebookbarpopup.ui
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.18.3 -->
+<interface domain="sfx">
+ <requires lib="gtk+" version="3.20"/>
+ <object class="GtkWindow" id="Popup">
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="border_width">4</property>
+ <property name="resizable">False</property>
+ <property name="destroy_with_parent">True</property>
+ <property name="type_hint">popup-menu</property>
+ <property name="deletable">False</property>
+ <child>
+ <object class="GtkBox" id="box">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin-end">6</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="spacing">6</property>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ </child>
+ </object>
+</interface>
diff --git a/sfx2/uiconfig/ui/optprintpage.ui b/sfx2/uiconfig/ui/optprintpage.ui
new file mode 100644
index 000000000..00fb0494b
--- /dev/null
+++ b/sfx2/uiconfig/ui/optprintpage.ui
@@ -0,0 +1,682 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.38.1 -->
+<interface domain="sfx">
+ <requires lib="gtk+" version="3.20"/>
+ <object class="GtkAdjustment" id="adjustment1">
+ <property name="lower">2</property>
+ <property name="upper">256</property>
+ <property name="value">64</property>
+ <property name="step-increment">1</property>
+ <property name="page-increment">10</property>
+ </object>
+ <!-- n-columns=2 n-rows=4 -->
+ <object class="GtkGrid" id="OptPrintPage">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="border-width">6</property>
+ <property name="row-spacing">12</property>
+ <property name="column-spacing">24</property>
+ <child>
+ <object class="GtkFrame" id="frame1">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="label-xalign">0</property>
+ <property name="shadow-type">none</property>
+ <child>
+ <!-- n-columns=1 n-rows=2 -->
+ <object class="GtkGrid" id="grid2">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="margin-start">12</property>
+ <property name="margin-top">6</property>
+ <property name="row-spacing">3</property>
+ <child>
+ <object class="GtkRadioButton" id="printer">
+ <property name="label" translatable="yes" context="optprintpage|printer">_Printer</property>
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">False</property>
+ <property name="use-underline">True</property>
+ <property name="active">True</property>
+ <property name="draw-indicator">True</property>
+ <child internal-child="accessible">
+ <object class="AtkObject" id="printer-atkobject">
+ <property name="AtkObject::accessible-description" translatable="yes" context="extended_tip|printer">Specifies whether the print settings apply to direct printing or to printing to a file.</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left-attach">0</property>
+ <property name="top-attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkRadioButton" id="file">
+ <property name="label" translatable="yes" context="optprintpage|file">Print to _file</property>
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">False</property>
+ <property name="use-underline">True</property>
+ <property name="draw-indicator">True</property>
+ <property name="group">printer</property>
+ <child internal-child="accessible">
+ <object class="AtkObject" id="file-atkobject">
+ <property name="AtkObject::accessible-description" translatable="yes" context="extended_tip|file">Specifies whether the print settings apply to direct printing or to printing to a file.</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left-attach">0</property>
+ <property name="top-attach">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label4">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="label" translatable="yes" context="optprintpage|label4">Settings for</property>
+ <property name="use-underline">True</property>
+ <property name="xalign">0</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left-attach">0</property>
+ <property name="top-attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFrame" id="frame3">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="label-xalign">0</property>
+ <property name="shadow-type">none</property>
+ <child>
+ <!-- n-columns=1 n-rows=3 -->
+ <object class="GtkGrid" id="grid3">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="margin-start">12</property>
+ <property name="margin-top">6</property>
+ <property name="row-spacing">3</property>
+ <child>
+ <object class="GtkCheckButton" id="papersize">
+ <property name="label" translatable="yes" context="optprintpage|papersize">P_aper size</property>
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">False</property>
+ <property name="use-underline">True</property>
+ <property name="draw-indicator">True</property>
+ <child internal-child="accessible">
+ <object class="AtkObject" id="papersize-atkobject">
+ <property name="AtkObject::accessible-description" translatable="yes" context="extended_tip|papersize">Mark this check box if a certain paper size is needed for printing the current document.</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left-attach">0</property>
+ <property name="top-attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="paperorient">
+ <property name="label" translatable="yes" context="optprintpage|paperorient">Pap_er orientation</property>
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">False</property>
+ <property name="use-underline">True</property>
+ <property name="draw-indicator">True</property>
+ <child internal-child="accessible">
+ <object class="AtkObject" id="paperorient-atkobject">
+ <property name="AtkObject::accessible-description" translatable="yes" context="extended_tip|paperorient">Mark this check box if you need a certain paper orientation for printing the current document.</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left-attach">0</property>
+ <property name="top-attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="trans">
+ <property name="label" translatable="yes" context="optprintpage|trans">_Transparency</property>
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">False</property>
+ <property name="use-underline">True</property>
+ <property name="draw-indicator">True</property>
+ <child internal-child="accessible">
+ <object class="AtkObject" id="trans-atkobject">
+ <property name="AtkObject::accessible-description" translatable="yes" context="extended_tip|trans">Mark this check box if you always want to be warned if transparent objects are contained in the document.</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left-attach">0</property>
+ <property name="top-attach">2</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label2">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="label" translatable="yes" context="optprintpage|label2">Warnings</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left-attach">1</property>
+ <property name="top-attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFrame" id="frame2">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="label-xalign">0</property>
+ <property name="shadow-type">none</property>
+ <child>
+ <!-- n-columns=1 n-rows=2 -->
+ <object class="GtkGrid" id="grid4">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="margin-start">12</property>
+ <property name="margin-top">6</property>
+ <property name="row-spacing">3</property>
+ <child>
+ <object class="GtkCheckButton" id="reducegrad">
+ <property name="label" translatable="yes" context="optprintpage|reducegrad">Reduce _gradient</property>
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">False</property>
+ <property name="use-underline">True</property>
+ <property name="draw-indicator">True</property>
+ <child internal-child="accessible">
+ <object class="AtkObject" id="reducegrad-atkobject">
+ <property name="AtkObject::accessible-description" translatable="yes" context="extended_tip|reducegrad">If this field is marked, gradients are printed with reduced quality.</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left-attach">0</property>
+ <property name="top-attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <!-- n-columns=1 n-rows=2 -->
+ <object class="GtkGrid" id="grid8">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="margin-start">18</property>
+ <property name="row-spacing">3</property>
+ <child>
+ <!-- n-columns=2 n-rows=1 -->
+ <object class="GtkGrid" id="grid6">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="column-spacing">3</property>
+ <child>
+ <object class="GtkRadioButton" id="reducegradstripes">
+ <property name="label" translatable="yes" context="optprintpage|reducegradstripes">Gradient _stripes:</property>
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">False</property>
+ <property name="use-underline">True</property>
+ <property name="active">True</property>
+ <property name="draw-indicator">True</property>
+ <child internal-child="accessible">
+ <object class="AtkObject" id="reducegradstripes-atkobject">
+ <property name="AtkObject::accessible-description" translatable="yes" context="extended_tip|reducegradstripes">Specifies the maximum number of gradient stripes for printing.</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left-attach">0</property>
+ <property name="top-attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="reducegradstep">
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="activates-default">True</property>
+ <property name="adjustment">adjustment1</property>
+ <property name="truncate-multiline">True</property>
+ <child internal-child="accessible">
+ <object class="AtkObject" id="reducegradstep-atkobject">
+ <property name="AtkObject::accessible-description" translatable="yes" context="extended_tip|reducegradstep">Specifies the maximum number of gradient stripes for printing.</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left-attach">1</property>
+ <property name="top-attach">0</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="left-attach">0</property>
+ <property name="top-attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkRadioButton" id="reducegradcolor">
+ <property name="label" translatable="yes" context="optprintpage|reducegradcolor">Intermediate _color</property>
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">False</property>
+ <property name="use-underline">True</property>
+ <property name="draw-indicator">True</property>
+ <property name="group">reducegradstripes</property>
+ <child internal-child="accessible">
+ <object class="AtkObject" id="reducegradcolor-atkobject">
+ <property name="AtkObject::accessible-description" translatable="yes" context="extended_tip|reducegradcolor">Specifies that gradients are only printed in a single intermediate color.</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left-attach">0</property>
+ <property name="top-attach">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="left-attach">0</property>
+ <property name="top-attach">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label1">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="label" translatable="yes" context="optprintpage|label1">Reduce Gradient</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ </object>
+ </child>
+ <child internal-child="accessible">
+ <object class="AtkObject" id="frame2-atkobject">
+ <property name="AtkObject::accessible-description" translatable="yes" context="extended_tip|frame2">Defines which warnings appear before printing begins.</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left-attach">1</property>
+ <property name="top-attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFrame" id="frame4">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="label-xalign">0</property>
+ <property name="shadow-type">none</property>
+ <child>
+ <!-- n-columns=1 n-rows=2 -->
+ <object class="GtkGrid" id="grid1">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="margin-start">12</property>
+ <property name="margin-top">6</property>
+ <property name="row-spacing">3</property>
+ <child>
+ <object class="GtkCheckButton" id="reducebitmap">
+ <property name="label" translatable="yes" context="optprintpage|reducebitmap">Reduce _bitmaps</property>
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">False</property>
+ <property name="use-underline">True</property>
+ <property name="draw-indicator">True</property>
+ <child internal-child="accessible">
+ <object class="AtkObject" id="reducebitmap-atkobject">
+ <property name="AtkObject::accessible-description" translatable="yes" context="extended_tip|reducebitmap">Specifies that bitmaps are printed with reduced quality. The resolution can only be reduced and not increased.</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left-attach">0</property>
+ <property name="top-attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <!-- n-columns=1 n-rows=3 -->
+ <object class="GtkGrid" id="grid11">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="margin-start">18</property>
+ <property name="row-spacing">3</property>
+ <child>
+ <object class="GtkRadioButton" id="reducebitmapnormal">
+ <property name="label" translatable="yes" context="optprintpage|reducebitmapnormal">N_ormal print quality</property>
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">False</property>
+ <property name="use-underline">True</property>
+ <property name="draw-indicator">True</property>
+ <property name="group">reducebitmapoptimal</property>
+ <child internal-child="accessible">
+ <object class="AtkObject" id="reducebitmapnormal-atkobject">
+ <property name="AtkObject::accessible-description" translatable="yes" context="extended_tip|reducebitmapnormal">High print quality corresponds to a resolution of 300dpi. Normal print quality corresponds to a resolution of 200dpi. </property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left-attach">0</property>
+ <property name="top-attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <!-- n-columns=2 n-rows=1 -->
+ <object class="GtkGrid" id="grid12">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="column-spacing">3</property>
+ <child>
+ <object class="GtkRadioButton" id="reducebitmapresol">
+ <property name="label" translatable="yes" context="optprintpage|reducebitmapresol">Reso_lution:</property>
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">False</property>
+ <property name="use-underline">True</property>
+ <property name="draw-indicator">True</property>
+ <property name="group">reducebitmapoptimal</property>
+ <child internal-child="accessible">
+ <object class="AtkObject" id="reducebitmapresol-atkobject">
+ <property name="AtkObject::accessible-description" translatable="yes" context="extended_tip|reducebitmapresol">Specifies the maximum print quality in dpi. The resolution can only be reduced and not increased.</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left-attach">0</property>
+ <property name="top-attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBoxText" id="reducebitmapdpi">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <items>
+ <item translatable="yes" context="optprintpage|reducebitmapdpi">72 DPI</item>
+ <item translatable="yes" context="optprintpage|reducebitmapdpi">96 DPI</item>
+ <item translatable="yes" context="optprintpage|reducebitmapdpi">150 DPI (Fax)</item>
+ <item translatable="yes" context="optprintpage|reducebitmapdpi">200 DPI (default)</item>
+ <item translatable="yes" context="optprintpage|reducebitmapdpi">300 DPI</item>
+ <item translatable="yes" context="optprintpage|reducebitmapdpi">600 DPI</item>
+ </items>
+ <child internal-child="accessible">
+ <object class="AtkObject" id="reducebitmapdpi-atkobject">
+ <property name="AtkObject::accessible-description" translatable="yes" context="extended_tip|reducebitmapdpi">Specifies the maximum print quality in dpi. The resolution can only be reduced and not increased.</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left-attach">1</property>
+ <property name="top-attach">0</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="left-attach">0</property>
+ <property name="top-attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkRadioButton" id="reducebitmapoptimal">
+ <property name="label" translatable="yes" context="optprintpage|reducebitmapoptimal">_High print quality</property>
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">False</property>
+ <property name="use-underline">True</property>
+ <property name="active">True</property>
+ <property name="draw-indicator">True</property>
+ <child internal-child="accessible">
+ <object class="AtkObject" id="reducebitmapoptimal-atkobject">
+ <property name="AtkObject::accessible-description" translatable="yes" context="extended_tip|reducebitmapoptimal">High print quality corresponds to a resolution of 300dpi. Normal print quality corresponds to a resolution of 200dpi. </property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left-attach">0</property>
+ <property name="top-attach">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="left-attach">0</property>
+ <property name="top-attach">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label3">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="label" translatable="yes" context="optprintpage|label1">Reduce Bitmaps</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left-attach">0</property>
+ <property name="top-attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFrame" id="frame5">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="label-xalign">0</property>
+ <property name="shadow-type">none</property>
+ <child>
+ <!-- n-columns=1 n-rows=2 -->
+ <object class="GtkGrid" id="grid5">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="margin-start">12</property>
+ <property name="margin-top">6</property>
+ <property name="row-spacing">3</property>
+ <child>
+ <object class="GtkCheckButton" id="reducetrans">
+ <property name="label" translatable="yes" context="optprintpage|reducetrans">_Reduce transparency</property>
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">False</property>
+ <property name="use-underline">True</property>
+ <property name="draw-indicator">True</property>
+ <child internal-child="accessible">
+ <object class="AtkObject" id="reducetrans-atkobject">
+ <property name="AtkObject::accessible-description" translatable="yes" context="extended_tip|reducetrans">If you mark this field the transparent objects will be printed like normal, non-transparent objects, depending on your selection in the following two option buttons.</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left-attach">0</property>
+ <property name="top-attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <!-- n-columns=1 n-rows=2 -->
+ <object class="GtkGrid" id="grid10">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="margin-start">18</property>
+ <property name="row-spacing">3</property>
+ <child>
+ <object class="GtkRadioButton" id="reducetransauto">
+ <property name="label" translatable="yes" context="optprintpage|reducetransauto">Auto_matically</property>
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">False</property>
+ <property name="use-underline">True</property>
+ <property name="active">True</property>
+ <property name="draw-indicator">True</property>
+ <child internal-child="accessible">
+ <object class="AtkObject" id="reducetransauto-atkobject">
+ <property name="AtkObject::accessible-description" translatable="yes" context="extended_tip|reducetransauto">Specifies that the transparency is only printed if the transparent area covers less than a quarter of the entire page.</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left-attach">0</property>
+ <property name="top-attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkRadioButton" id="reducetransnone">
+ <property name="label" translatable="yes" context="optprintpage|reducetransnone">_No transparency</property>
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">False</property>
+ <property name="use-underline">True</property>
+ <property name="draw-indicator">True</property>
+ <property name="group">reducetransauto</property>
+ <child internal-child="accessible">
+ <object class="AtkObject" id="reducetransnone-atkobject">
+ <property name="AtkObject::accessible-description" translatable="yes" context="extended_tip|reducetransnone">With this option transparency is never printed.</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left-attach">0</property>
+ <property name="top-attach">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="left-attach">0</property>
+ <property name="top-attach">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label5">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="label" translatable="yes" context="optprintpage|label1">Reduce Transparency</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left-attach">0</property>
+ <property name="top-attach">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFrame" id="frame6">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="label-xalign">0</property>
+ <property name="shadow-type">none</property>
+ <child>
+ <!-- n-columns=1 n-rows=3 -->
+ <object class="GtkGrid" id="grid7">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="margin-start">12</property>
+ <property name="margin-top">6</property>
+ <property name="row-spacing">3</property>
+ <child>
+ <object class="GtkCheckButton" id="pdf">
+ <property name="label" translatable="yes" context="optprintpage|pdf">_PDF as standard print job format</property>
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">False</property>
+ <property name="use-underline">True</property>
+ <property name="draw-indicator">True</property>
+ </object>
+ <packing>
+ <property name="left-attach">0</property>
+ <property name="top-attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="converttogray">
+ <property name="label" translatable="yes" context="optprintpage|converttogray">Con_vert colors to grayscale</property>
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">False</property>
+ <property name="use-underline">True</property>
+ <property name="draw-indicator">True</property>
+ <child internal-child="accessible">
+ <object class="AtkObject" id="converttogray-atkobject">
+ <property name="AtkObject::accessible-description" translatable="yes" context="extended_tip|converttogray">Specifies that all colors are printed only as grayscale.</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left-attach">0</property>
+ <property name="top-attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="reducebitmaptrans">
+ <property name="label" translatable="yes" context="optprintpage|reducebitmaptrans">Include transparent objects</property>
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">False</property>
+ <property name="use-underline">True</property>
+ <property name="draw-indicator">True</property>
+ <child internal-child="accessible">
+ <object class="AtkObject" id="reducebitmaptrans-atkobject">
+ <property name="AtkObject::accessible-description" translatable="yes" context="extended_tip|reducebitmaptrans">If this field is marked, the reduction in print quality for bitmaps also applies to the transparent areas of objects.</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left-attach">0</property>
+ <property name="top-attach">2</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label6">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="label" translatable="yes" context="optprintpage|label4">Defaults</property>
+ <property name="use-underline">True</property>
+ <property name="xalign">0</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left-attach">0</property>
+ <property name="top-attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child internal-child="accessible">
+ <object class="AtkObject" id="OptPrintPage-atkobject">
+ <property name="AtkObject::accessible-description" translatable="yes" context="extended_tip|OptPrintPage">Specifies the print setting options.</property>
+ </object>
+ </child>
+ </object>
+</interface>
diff --git a/sfx2/uiconfig/ui/panel.ui b/sfx2/uiconfig/ui/panel.ui
new file mode 100644
index 000000000..981a2f2d6
--- /dev/null
+++ b/sfx2/uiconfig/ui/panel.ui
@@ -0,0 +1,121 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.38.2 -->
+<interface domain="sfx">
+ <requires lib="gtk+" version="3.20"/>
+ <object class="GtkBox" id="Panel">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkBox" id="titlebar">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkImage" id="addonimage">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="valign">center</property>
+ <property name="icon-name">missing-image</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkExpander" id="expander">
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="valign">center</property>
+ <property name="hexpand">True</property>
+ <child>
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="use-underline">True</property>
+ <property name="xalign">0</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToolbar" id="toolbar">
+ <property name="can-focus">True</property>
+ <property name="no-show-all">True</property>
+ <property name="toolbar-style">icons</property>
+ <property name="show-arrow">False</property>
+ <property name="icon_size">2</property>
+ <child>
+ <object class="GtkToolButton" id="button">
+ <property name="visible">True</property>
+ <property name="tooltip-text" translatable="yes" context="panel|SFX_STR_SIDEBAR_MORE_OPTIONS">More Options</property>
+ <property name="use-underline">True</property>
+ <property name="icon-name">sfx2/res/symphony/morebutton.png</property>
+ <child internal-child="accessible">
+ <object class="AtkObject" id="button-atkobject">
+ <property name="AtkObject::accessible-name" translatable="yes" context="panel|SFX_STR_SIDEBAR_MORE_OPTIONS">More Options</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">False</property>
+ </packing>
+ </child>
+ <style>
+ <class name="small-button"/>
+ </style>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="contents">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+</interface>
diff --git a/sfx2/uiconfig/ui/password.ui b/sfx2/uiconfig/ui/password.ui
new file mode 100644
index 000000000..ff7cfe9f1
--- /dev/null
+++ b/sfx2/uiconfig/ui/password.ui
@@ -0,0 +1,366 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.38.2 -->
+<interface domain="sfx">
+ <requires lib="gtk+" version="3.20"/>
+ <object class="GtkDialog" id="PasswordDialog">
+ <property name="can-focus">False</property>
+ <property name="border-width">6</property>
+ <property name="title" translatable="yes" context="password|PasswordDialog">Enter Password</property>
+ <property name="modal">True</property>
+ <property name="window-position">center</property>
+ <property name="default-width">0</property>
+ <property name="default-height">0</property>
+ <property name="type-hint">dialog</property>
+ <child internal-child="vbox">
+ <object class="GtkBox" id="dialog-vbox1">
+ <property name="can-focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">12</property>
+ <child internal-child="action_area">
+ <object class="GtkButtonBox" id="dialog-action_area1">
+ <property name="can-focus">False</property>
+ <property name="layout-style">end</property>
+ <child>
+ <object class="GtkButton" id="ok">
+ <property name="label" translatable="yes" context="stock">_OK</property>
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="can-focus">True</property>
+ <property name="can-default">True</property>
+ <property name="has-default">True</property>
+ <property name="receives-default">True</property>
+ <property name="use-underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="cancel">
+ <property name="label" translatable="yes" context="stock">_Cancel</property>
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">True</property>
+ <property name="use-underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="help">
+ <property name="label" translatable="yes" context="stock">_Help</property>
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">True</property>
+ <property name="use-underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ <property name="secondary">True</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack-type">end</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="box1">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkFrame" id="password1frame">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="label-xalign">0</property>
+ <property name="shadow-type">none</property>
+ <child>
+ <!-- n-columns=2 n-rows=3 -->
+ <object class="GtkGrid" id="grid1">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="margin-start">12</property>
+ <property name="margin-top">6</property>
+ <property name="row-spacing">6</property>
+ <property name="column-spacing">12</property>
+ <child>
+ <object class="GtkLabel" id="userft">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="label" translatable="yes" context="password|userft">User:</property>
+ <property name="use-underline">True</property>
+ <property name="mnemonic-widget">usered</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="left-attach">0</property>
+ <property name="top-attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="pass1ft">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="label" translatable="yes" context="password|pass1ft">Password:</property>
+ <property name="use-underline">True</property>
+ <property name="mnemonic-widget">pass1ed</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="left-attach">0</property>
+ <property name="top-attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="confirm1ft">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="label" translatable="yes" context="password|confirm1ft">Confirm:</property>
+ <property name="use-underline">True</property>
+ <property name="mnemonic-widget">confirm1ed</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="left-attach">0</property>
+ <property name="top-attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="usered">
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="hexpand">True</property>
+ <property name="activates-default">True</property>
+ <property name="truncate-multiline">True</property>
+ </object>
+ <packing>
+ <property name="left-attach">1</property>
+ <property name="top-attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="pass1ed">
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="hexpand">True</property>
+ <property name="visibility">False</property>
+ <property name="activates-default">True</property>
+ <property name="truncate-multiline">True</property>
+ <property name="input-purpose">password</property>
+ <child internal-child="accessible">
+ <object class="AtkObject" id="pass1ed-atkobject">
+ <property name="AtkObject::accessible-name" translatable="yes" context="password|pass1ed-atkobject">Password</property>
+ <property name="AtkObject::accessible-description" translatable="yes" context="password|extended_tip|pass1ed">Type a password. A password is case sensitive.</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left-attach">1</property>
+ <property name="top-attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="confirm1ed">
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="hexpand">True</property>
+ <property name="visibility">False</property>
+ <property name="activates-default">True</property>
+ <property name="truncate-multiline">True</property>
+ <property name="input-purpose">password</property>
+ <child internal-child="accessible">
+ <object class="AtkObject" id="confirm1ed-atkobject">
+ <property name="AtkObject::accessible-description" translatable="yes" context="password|extended_tip|confirm1ed">Re-enter the password.</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left-attach">1</property>
+ <property name="top-attach">2</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label1">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="label" translatable="yes" context="password|label1">Password</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFrame" id="password2frame">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="label-xalign">0</property>
+ <property name="shadow-type">none</property>
+ <child>
+ <!-- n-columns=2 n-rows=2 -->
+ <object class="GtkGrid" id="grid2">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="margin-start">12</property>
+ <property name="margin-top">6</property>
+ <property name="row-spacing">6</property>
+ <property name="column-spacing">12</property>
+ <child>
+ <object class="GtkLabel" id="pass2ft">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="label" translatable="yes" context="password|pass2ft">Password:</property>
+ <property name="use-underline">True</property>
+ <property name="mnemonic-widget">pass2ed</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="left-attach">0</property>
+ <property name="top-attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="confirm2ft">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="label" translatable="yes" context="password|confirm2ft">Confirm:</property>
+ <property name="use-underline">True</property>
+ <property name="mnemonic-widget">confirm2ed</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="left-attach">0</property>
+ <property name="top-attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="pass2ed">
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="hexpand">True</property>
+ <property name="visibility">False</property>
+ <property name="activates-default">True</property>
+ <property name="truncate-multiline">True</property>
+ <property name="input-purpose">password</property>
+ <child internal-child="accessible">
+ <object class="AtkObject" id="pass2ed-atkobject">
+ <property name="AtkObject::accessible-description" translatable="yes" context="password|extended_tip|pass2ed">Type a password. A password is case sensitive.</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left-attach">1</property>
+ <property name="top-attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="confirm2ed">
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="hexpand">True</property>
+ <property name="visibility">False</property>
+ <property name="activates-default">True</property>
+ <property name="truncate-multiline">True</property>
+ <property name="input-purpose">password</property>
+ <child internal-child="accessible">
+ <object class="AtkObject" id="confirm2ed-atkobject">
+ <property name="AtkObject::accessible-description" translatable="yes" context="password|extended_tip|confirm2ed">Re-enter the password.</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left-attach">1</property>
+ <property name="top-attach">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label2">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="label" translatable="yes" context="password|label2">Second Password</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="minlenft">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="margin-start">6</property>
+ <property name="xalign">0</property>
+ <child internal-child="accessible">
+ <object class="AtkObject" id="minlenft-atkobject">
+ <property name="AtkObject::accessible-role" translatable="no">static</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="onlyascii">
+ <property name="can-focus">False</property>
+ <property name="no-show-all">True</property>
+ <property name="margin-start">6</property>
+ <property name="label" translatable="yes" context="password|onlyascii">Only Basic Latin characters can be entered</property>
+ <property name="xalign">0</property>
+ <child internal-child="accessible">
+ <object class="AtkObject" id="onlyascii-atkobject">
+ <property name="AtkObject::accessible-role" translatable="no">static</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <action-widgets>
+ <action-widget response="-6">cancel</action-widget>
+ <action-widget response="-11">help</action-widget>
+ </action-widgets>
+ </object>
+</interface>
diff --git a/sfx2/uiconfig/ui/printeroptionsdialog.ui b/sfx2/uiconfig/ui/printeroptionsdialog.ui
new file mode 100644
index 000000000..2176e68a8
--- /dev/null
+++ b/sfx2/uiconfig/ui/printeroptionsdialog.ui
@@ -0,0 +1,91 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.20.4 -->
+<interface domain="sfx">
+ <requires lib="gtk+" version="3.20"/>
+ <object class="GtkDialog" id="PrinterOptionsDialog">
+ <property name="can_focus">False</property>
+ <property name="border_width">6</property>
+ <property name="title" translatable="yes" context="printeroptionsdialog|PrinterOptionsDialog">Printer Options</property>
+ <property name="resizable">False</property>
+ <property name="modal">True</property>
+ <property name="default_width">0</property>
+ <property name="default_height">0</property>
+ <property name="type_hint">dialog</property>
+ <child internal-child="vbox">
+ <object class="GtkBox" id="dialog-vbox1">
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">12</property>
+ <child internal-child="action_area">
+ <object class="GtkButtonBox" id="dialog-action_area1">
+ <property name="can_focus">False</property>
+ <property name="layout_style">end</property>
+ <child>
+ <object class="GtkButton" id="ok">
+ <property name="label" translatable="yes" context="stock">_OK</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="has_default">True</property>
+ <property name="receives_default">True</property>
+ <property name="use-underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="cancel">
+ <property name="label" translatable="yes" context="stock">_Cancel</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="receives_default">True</property>
+ <property name="use-underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="help">
+ <property name="label" translatable="yes" context="stock">_Help</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use-underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ <property name="secondary">True</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">end</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ </child>
+ <action-widgets>
+ <action-widget response="-5">ok</action-widget>
+ <action-widget response="-6">cancel</action-widget>
+ <action-widget response="-11">help</action-widget>
+ </action-widgets>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+</interface>
diff --git a/sfx2/uiconfig/ui/querysavedialog.ui b/sfx2/uiconfig/ui/querysavedialog.ui
new file mode 100644
index 000000000..4e78257e5
--- /dev/null
+++ b/sfx2/uiconfig/ui/querysavedialog.ui
@@ -0,0 +1,86 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.20.2 -->
+<interface domain="sfx">
+ <requires lib="gtk+" version="3.20"/>
+ <object class="GtkMessageDialog" id="QuerySaveDialog">
+ <property name="can_focus">False</property>
+ <property name="title" translatable="yes" context="querysavedialog|QuerySaveDialog">Save Document?</property>
+ <property name="resizable">False</property>
+ <property name="modal">True</property>
+ <property name="type_hint">dialog</property>
+ <property name="skip_taskbar_hint">True</property>
+ <property name="message_type">warning</property>
+ <property name="text" translatable="yes" context="querysavedialog|QuerySaveDialog">Save changes to document “$(DOC)” before closing?</property>
+ <property name="secondary_text" translatable="yes" context="querysavedialog|QuerySaveDialog">Your changes will be lost if you don’t save them.</property>
+ <child internal-child="vbox">
+ <object class="GtkBox" id="messagedialog-vbox">
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">12</property>
+ <child internal-child="action_area">
+ <object class="GtkButtonBox" id="messagedialog-action_area">
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkButton" id="discard">
+ <property name="label" translatable="yes" context="querysavedialog|discard">Do_n’t Save</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_underline">True</property>
+ <style>
+ <class name="destructive-action"/>
+ </style>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="cancel">
+ <property name="label" translatable="yes" context="stock">_Cancel</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use-underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="save">
+ <property name="label" translatable="yes" context="querysavedialog|save">_Save</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="has_default">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">end</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <action-widgets>
+ <action-widget response="-9">discard</action-widget>
+ <action-widget response="-6">cancel</action-widget>
+ <action-widget response="-8">save</action-widget>
+ </action-widgets>
+ </object>
+</interface>
diff --git a/sfx2/uiconfig/ui/safemodequerydialog.ui b/sfx2/uiconfig/ui/safemodequerydialog.ui
new file mode 100644
index 000000000..774c38084
--- /dev/null
+++ b/sfx2/uiconfig/ui/safemodequerydialog.ui
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.20.2 -->
+<interface domain="sfx">
+ <requires lib="gtk+" version="3.20"/>
+ <object class="GtkMessageDialog" id="SafeModeQueryDialog">
+ <property name="can_focus">False</property>
+ <property name="title" translatable="yes" context="safemodequerydialog|SafeModeQueryDialog">Enter Safe Mode</property>
+ <property name="resizable">False</property>
+ <property name="modal">True</property>
+ <property name="type_hint">dialog</property>
+ <property name="message_type">question</property>
+ <property name="text" translatable="yes" context="safemodequerydialog|label">Are you sure you want to restart %PRODUCTNAME and enter safe mode?</property>
+ <child internal-child="vbox">
+ <object class="GtkBox">
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">2</property>
+ <child internal-child="action_area">
+ <object class="GtkButtonBox">
+ <property name="can_focus">False</property>
+ <property name="homogeneous">True</property>
+ <child>
+ <object class="GtkButton" id="cancel">
+ <property name="label" translatable="yes" context="stock">_Cancel</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="has_default">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="ok">
+ <property name="label" translatable="yes" context="safemodequerydialog|restart">_Restart</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <action-widgets>
+ <action-widget response="-6">cancel</action-widget>
+ <action-widget response="-5">ok</action-widget>
+ </action-widgets>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+</interface>
diff --git a/sfx2/uiconfig/ui/saveastemplatedlg.ui b/sfx2/uiconfig/ui/saveastemplatedlg.ui
new file mode 100644
index 000000000..dc4d02404
--- /dev/null
+++ b/sfx2/uiconfig/ui/saveastemplatedlg.ui
@@ -0,0 +1,263 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.36.0 -->
+<interface domain="sfx">
+ <requires lib="gtk+" version="3.20"/>
+ <object class="GtkTreeStore" id="categorylist">
+ <columns>
+ <!-- column-name gchararray1 -->
+ <column type="gchararray"/>
+ </columns>
+ </object>
+ <object class="GtkDialog" id="SaveAsTemplateDialog">
+ <property name="can_focus">False</property>
+ <property name="border_width">6</property>
+ <property name="title" translatable="yes" context="saveastemplatedlg|SaveAsTemplateDialog">Save As Template</property>
+ <property name="modal">True</property>
+ <property name="default_width">0</property>
+ <property name="default_height">0</property>
+ <property name="type_hint">normal</property>
+ <child internal-child="vbox">
+ <object class="GtkBox" id="dialog-vbox1">
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">2</property>
+ <child internal-child="action_area">
+ <object class="GtkButtonBox" id="dialog-action_area1">
+ <property name="can_focus">False</property>
+ <property name="layout_style">end</property>
+ <child>
+ <object class="GtkButton" id="help">
+ <property name="label" translatable="yes" context="stock">_Help</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use-underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ <property name="secondary">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="ok">
+ <property name="label" translatable="yes" context="stock">_OK</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="has_default">True</property>
+ <property name="receives_default">True</property>
+ <property name="use-underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="cancel">
+ <property name="label" translatable="yes" context="stock">_Cancel</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use-underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">end</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <!-- n-columns=1 n-rows=1 -->
+ <object class="GtkGrid" id="grid1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_bottom">6</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="row_spacing">12</property>
+ <property name="column_spacing">12</property>
+ <child>
+ <object class="GtkBox" id="box1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="create_label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes" context="saveastemplatedlg|create_label">Enter Template _Name:</property>
+ <property name="tooltip_text" translatable="yes" context="saveastemplatedlg|create_label">Enter a name for the template.</property>
+ <property name="use_underline">True</property>
+ <property name="mnemonic_widget">name_entry</property>
+ <property name="xalign">0</property>
+ <attributes>
+ <attribute name="weight" value="normal"/>
+ </attributes>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="name_entry">
+ <property name="width_request">300</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="activates_default">True</property>
+ <property name="truncate-multiline">True</property>
+ <child internal-child="accessible">
+ <object class="AtkObject" id="name_entry-atkobject">
+ <property name="AtkObject::accessible-description" translatable="yes" context="saveastemplatedlg|extended_tip|name_entry">Enter a name for the template.</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="box2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="select_label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes" context="saveastemplatedlg|select_label">Select Template _Category:</property>
+ <property name="tooltip_text" translatable="yes" context="saveastemplatedlg|select_label">Save template in selected category.</property>
+ <property name="use_underline">True</property>
+ <property name="mnemonic_widget">categorylb</property>
+ <property name="xalign">0</property>
+ <attributes>
+ <attribute name="weight" value="normal"/>
+ </attributes>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="scroll">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkTreeView" id="categorylb">
+ <property name="height_request">146</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="model">categorylist</property>
+ <property name="headers_visible">False</property>
+ <property name="headers_clickable">False</property>
+ <property name="search_column">0</property>
+ <property name="show_expanders">False</property>
+ <child internal-child="selection">
+ <object class="GtkTreeSelection" id="treeview-selection1"/>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="treeviewcolumn1">
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderertext1"/>
+ <attributes>
+ <attribute name="text">0</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ <child internal-child="accessible">
+ <object class="AtkObject" id="categorylb-atkobject">
+ <property name="AtkObject::accessible-description" translatable="yes" context="saveastemplatedlg|extended_tip|categorylb">Select a category in which to save the new template.</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="defaultcb">
+ <property name="label" translatable="yes" context="saveastemplatedlg|defaultcb">_Set as default template</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_underline">True</property>
+ <property name="draw_indicator">True</property>
+ <child internal-child="accessible">
+ <object class="AtkObject" id="defaultcb-atkobject">
+ <property name="AtkObject::accessible-description" translatable="yes" context="saveastemplatedlg|extended_tip|defaultcb">The new template will be used as the default template.</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <action-widgets>
+ <action-widget response="-11">help</action-widget>
+ <action-widget response="-5">ok</action-widget>
+ <action-widget response="-6">cancel</action-widget>
+ </action-widgets>
+ <child type="titlebar">
+ <placeholder/>
+ </child>
+ <child internal-child="accessible">
+ <object class="AtkObject" id="SaveAsTemplateDialog-atkobject">
+ <property name="AtkObject::accessible-description" translatable="yes" context="saveastemplatedlg|extended_tip|SaveAsTemplateDialog">Saves the current document as a template.</property>
+ </object>
+ </child>
+ </object>
+</interface>
diff --git a/sfx2/uiconfig/ui/searchdialog.ui b/sfx2/uiconfig/ui/searchdialog.ui
new file mode 100644
index 000000000..a99b20e76
--- /dev/null
+++ b/sfx2/uiconfig/ui/searchdialog.ui
@@ -0,0 +1,200 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.22.1 -->
+<interface domain="sfx">
+ <requires lib="gtk+" version="3.20"/>
+ <object class="GtkDialog" id="SearchDialog">
+ <property name="can_focus">False</property>
+ <property name="border_width">6</property>
+ <property name="title" translatable="yes" context="searchdialog|SearchDialog">Find on this Page</property>
+ <property name="default_width">0</property>
+ <property name="default_height">0</property>
+ <property name="type_hint">dialog</property>
+ <child>
+ <placeholder/>
+ </child>
+ <child internal-child="vbox">
+ <object class="GtkBox" id="dialog-vbox3">
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">12</property>
+ <child internal-child="action_area">
+ <object class="GtkButtonBox" id="dialog-action_area3">
+ <property name="can_focus">False</property>
+ <property name="layout_style">end</property>
+ <child>
+ <object class="GtkButton" id="ok">
+ <property name="label" translatable="yes" context="searchdialog|search">_Find</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="has_default">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="close">
+ <property name="label" translatable="yes" context="stock">_Close</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use-underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">end</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="box1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">12</property>
+ <child>
+ <!-- n-columns=1 n-rows=1 -->
+ <object class="GtkGrid" id="grid1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="row_spacing">6</property>
+ <child>
+ <object class="GtkComboBoxText" id="searchterm">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="valign">center</property>
+ <property name="hexpand">True</property>
+ <property name="has_entry">True</property>
+ <child internal-child="entry">
+ <object class="GtkEntry" id="combobox-entry1">
+ <property name="can_focus">True</property>
+ <property name="truncate-multiline">True</property>
+ <property name="activates_default">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes" context="searchdialog|label1">_Search for:</property>
+ <property name="use_underline">True</property>
+ <property name="mnemonic_widget">searchterm</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <!-- n-columns=1 n-rows=1 -->
+ <object class="GtkGrid" id="grid13">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="row_spacing">6</property>
+ <property name="column_spacing">12</property>
+ <child>
+ <object class="GtkCheckButton" id="matchcase">
+ <property name="label" translatable="yes" context="searchdialog|matchcase">Ma_tch case</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_underline">True</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="wholewords">
+ <property name="label" translatable="yes" context="searchdialog|wholewords">Whole wor_ds only</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_underline">True</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="backwards">
+ <property name="label" translatable="yes" context="searchdialog|backwards">Bac_kwards</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_underline">True</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="wrap">
+ <property name="label" translatable="yes" context="searchdialog|wrap">Wrap _around</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_underline">True</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <action-widgets>
+ <action-widget response="101">ok</action-widget>
+ <action-widget response="-7">close</action-widget>
+ </action-widgets>
+ </object>
+</interface>
diff --git a/sfx2/uiconfig/ui/securityinfopage.ui b/sfx2/uiconfig/ui/securityinfopage.ui
new file mode 100644
index 000000000..0b2a5df23
--- /dev/null
+++ b/sfx2/uiconfig/ui/securityinfopage.ui
@@ -0,0 +1,125 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.38.2 -->
+<interface domain="sfx">
+ <requires lib="gtk+" version="3.20"/>
+ <object class="GtkFrame" id="SecurityInfoPage">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="border-width">12</property>
+ <property name="label-xalign">0</property>
+ <property name="shadow-type">none</property>
+ <child>
+ <!-- n-columns=1 n-rows=3 -->
+ <object class="GtkGrid" id="grid9">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="margin-start">12</property>
+ <property name="margin-top">6</property>
+ <property name="row-spacing">6</property>
+ <child>
+ <object class="GtkCheckButton" id="readonly">
+ <property name="label" translatable="yes" context="securityinfopage|readonly">_Open file read-only</property>
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">False</property>
+ <property name="halign">start</property>
+ <property name="use-underline">True</property>
+ <property name="draw-indicator">True</property>
+ <child internal-child="accessible">
+ <object class="AtkObject" id="readonly-atkobject">
+ <property name="AtkObject::accessible-description" translatable="yes" context="securityinfopage|extended_tip|readonly">Select to allow this document to be opened in read-only mode only.</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left-attach">0</property>
+ <property name="top-attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="recordchanges">
+ <property name="label" translatable="yes" context="securityinfopage|recordchanges">Record _changes</property>
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">False</property>
+ <property name="use-underline">True</property>
+ <property name="draw-indicator">True</property>
+ <child internal-child="accessible">
+ <object class="AtkObject" id="recordchanges-atkobject">
+ <property name="AtkObject::accessible-description" translatable="yes" context="securityinfopage|extended_tip|recordchanges">Select to enable recording changes. This is the same as Edit - Track Changes - Record.</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left-attach">0</property>
+ <property name="top-attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="box2">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkButton" id="protect">
+ <property name="label" translatable="yes" context="securityinfopage|protect">Protect...</property>
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">True</property>
+ <child internal-child="accessible">
+ <object class="AtkObject" id="protect-atkobject">
+ <property name="AtkObject::accessible-description" translatable="yes" context="securityinfopage|extended_tip|protect">Protects the change recording state with a password. If change recording is protected for the current document, the button is named Unprotect. Click Unprotect and type the correct password to disable the protection.</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="unprotect">
+ <property name="label" translatable="yes" context="securityinfopage|unprotect">_Unprotect...</property>
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">True</property>
+ <property name="use-underline">True</property>
+ <child internal-child="accessible">
+ <object class="AtkObject" id="unprotect-atkobject">
+ <property name="AtkObject::accessible-description" translatable="yes" context="securityinfopage|extended_tip|unprotect">Protects the change recording state with a password. If change recording is protected for the current document, the button is named Unprotect. Click Unprotect and type the correct password to disable the protection.</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="left-attach">0</property>
+ <property name="top-attach">2</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label47">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="label" translatable="yes" context="securityinfopage|label47">File Sharing Options</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ </object>
+ </child>
+ <child internal-child="accessible">
+ <object class="AtkObject" id="SecurityInfoPage-atkobject">
+ <property name="AtkObject::accessible-description" translatable="yes" context="securityinfopage|extended_tip|SecurityInfoPage">Sets password options for the current document.</property>
+ </object>
+ </child>
+ </object>
+</interface>
diff --git a/sfx2/uiconfig/ui/singletabdialog.ui b/sfx2/uiconfig/ui/singletabdialog.ui
new file mode 100644
index 000000000..c1ddd4f31
--- /dev/null
+++ b/sfx2/uiconfig/ui/singletabdialog.ui
@@ -0,0 +1,87 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.22.1 -->
+<interface domain="sfx">
+ <requires lib="gtk+" version="3.20"/>
+ <object class="GtkDialog" id="SingleTabDialog">
+ <property name="can_focus">False</property>
+ <property name="border_width">6</property>
+ <property name="resizable">False</property>
+ <property name="modal">True</property>
+ <property name="type_hint">dialog</property>
+ <child>
+ <placeholder/>
+ </child>
+ <child internal-child="vbox">
+ <object class="GtkBox" id="dialog-vbox1">
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">12</property>
+ <child internal-child="action_area">
+ <object class="GtkButtonBox" id="dialog-action_area1">
+ <property name="can_focus">False</property>
+ <property name="layout_style">end</property>
+ <child>
+ <object class="GtkButton" id="ok">
+ <property name="label" translatable="yes" context="stock">_OK</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="has_default">True</property>
+ <property name="receives_default">True</property>
+ <property name="use-underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="cancel">
+ <property name="label" translatable="yes" context="stock">_Cancel</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use-underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="help">
+ <property name="label" translatable="yes" context="stock">_Help</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use-underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ <property name="secondary">True</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">end</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ </child>
+ <action-widgets>
+ <action-widget response="-5">ok</action-widget>
+ <action-widget response="-6">cancel</action-widget>
+ <action-widget response="-11">help</action-widget>
+ </action-widgets>
+ </object>
+</interface>
diff --git a/sfx2/uiconfig/ui/startcenter.ui b/sfx2/uiconfig/ui/startcenter.ui
new file mode 100644
index 000000000..e65eba9e9
--- /dev/null
+++ b/sfx2/uiconfig/ui/startcenter.ui
@@ -0,0 +1,618 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.38.2 -->
+<interface domain="sfx">
+ <requires lib="gtk+" version="3.20"/>
+ <object class="GtkImage" id="calc_all_image">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="icon-name">res/ods_32_8.png</property>
+ </object>
+ <object class="GtkMenu" id="clearmenu">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <child>
+ <object class="GtkMenuItem" id="clear_all">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="label" translatable="yes" context="startcenter|clear_all">Clear Recent Documents</property>
+ </object>
+ </child>
+ </object>
+ <object class="GtkImage" id="database_all_image">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="icon-name">res/odb_32_8.png</property>
+ </object>
+ <object class="GtkImage" id="draw_all_image">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="icon-name">res/odg_32_8.png</property>
+ </object>
+ <object class="GtkMenu" id="filtermenu">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <child>
+ <object class="GtkMenuItem" id="filter_writer">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="label" translatable="yes" context="startcenter|filter_writer">Writer Templates</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="filter_calc">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="label" translatable="yes" context="startcenter|filter_calc">Calc Templates</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="filter_impress">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="label" translatable="yes" context="startcenter|filter_impress">Impress Templates</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="filter_draw">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="label" translatable="yes" context="startcenter|filter_draw">Draw Templates</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkSeparatorMenuItem" id="menuitem3">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="manage">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="label" translatable="yes" context="startcenter|manage">Manage Templates</property>
+ </object>
+ </child>
+ </object>
+ <object class="GtkImage" id="impress_all_image">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="icon-name">res/odp_32_8.png</property>
+ </object>
+ <object class="GtkMenu" id="localmenu">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ </object>
+ <object class="GtkImage" id="math_all_image">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="icon-name">res/odf_32_8.png</property>
+ </object>
+ <object class="GtkImage" id="open_all_image">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="icon-name">framework/res/folder_32.png</property>
+ </object>
+ <object class="GtkImage" id="open_all_image1">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="icon-name">framework/res/recent-documents.png</property>
+ </object>
+ <object class="GtkImage" id="open_all_image2">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="icon-name">framework/res/remote-documents.png</property>
+ </object>
+ <object class="GtkMenu" id="recentmenu">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ </object>
+ <object class="GtkImage" id="templates_all_image">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="icon-name">framework/res/templates_32.png</property>
+ </object>
+ <object class="GtkImage" id="writer_all_image">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="icon-name">res/odt_32_8.png</property>
+ </object>
+ <object class="GtkBox" id="StartCenter">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <child>
+ <object class="GtkBox" id="box1">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="border-width">0</property>
+ <child>
+ <object class="GtkFrame" id="frame1">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="label-xalign">0</property>
+ <property name="shadow-type">none</property>
+ <child>
+ <object class="GtkBox" id="all_buttons_box">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkBox" id="buttons_box">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="vexpand">True</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">3</property>
+ <child>
+ <object class="GtkButton" id="open_all">
+ <property name="label" translatable="yes" context="startcenter|open_all">_Open File</property>
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">True</property>
+ <property name="margin-start">6</property>
+ <property name="margin-end">6</property>
+ <property name="margin-top">6</property>
+ <property name="image">open_all_image</property>
+ <property name="relief">none</property>
+ <property name="use-underline">True</property>
+ <property name="xalign">0</property>
+ <property name="always-show-image">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="open_remote">
+ <property name="label" translatable="yes" context="startcenter|open_remote">Remote File_s</property>
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">True</property>
+ <property name="margin-start">6</property>
+ <property name="margin-end">6</property>
+ <property name="image">open_all_image2</property>
+ <property name="relief">none</property>
+ <property name="use-underline">True</property>
+ <property name="xalign">0</property>
+ <property name="always-show-image">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSeparator" id="separator3">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="margin-start">6</property>
+ <property name="margin-end">6</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkMenuButton" id="open_recent">
+ <property name="label" translatable="yes" context="startcenter|open_recent">_Recent Documents</property>
+ <property name="name">MenuToggleButton</property>
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">True</property>
+ <property name="margin-start">6</property>
+ <property name="margin-end">6</property>
+ <property name="image">open_all_image1</property>
+ <property name="relief">none</property>
+ <property name="use-underline">True</property>
+ <property name="xalign">0</property>
+ <property name="always-show-image">True</property>
+ <property name="draw-indicator">True</property>
+ <property name="popup">clearmenu</property>
+ <property name="use-popover">False</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkMenuButton" id="templates_all">
+ <property name="label" translatable="yes" context="startcenter|templates_all">T_emplates</property>
+ <property name="name">MenuToggleButton</property>
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">True</property>
+ <property name="margin-start">6</property>
+ <property name="margin-end">6</property>
+ <property name="image">templates_all_image</property>
+ <property name="relief">none</property>
+ <property name="use-underline">True</property>
+ <property name="xalign">0</property>
+ <property name="always-show-image">True</property>
+ <property name="draw-indicator">True</property>
+ <property name="popup">filtermenu</property>
+ <property name="use-popover">False</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">4</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSeparator" id="separator1">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="margin-start">6</property>
+ <property name="margin-end">6</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">5</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="create_label">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="margin-start">12</property>
+ <property name="margin-bottom">6</property>
+ <property name="label" translatable="yes" context="startcenter|create_label">Create:</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">6</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="writer_all">
+ <property name="label" translatable="yes" context="startcenter|writer_all">_Writer Document</property>
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">True</property>
+ <property name="margin-start">6</property>
+ <property name="margin-end">6</property>
+ <property name="image">writer_all_image</property>
+ <property name="relief">none</property>
+ <property name="use-underline">True</property>
+ <property name="xalign">0</property>
+ <property name="always-show-image">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">7</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="calc_all">
+ <property name="label" translatable="yes" context="startcenter|calc_all">_Calc Spreadsheet</property>
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">True</property>
+ <property name="margin-start">6</property>
+ <property name="margin-end">6</property>
+ <property name="image">calc_all_image</property>
+ <property name="relief">none</property>
+ <property name="use-underline">True</property>
+ <property name="xalign">0</property>
+ <property name="always-show-image">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">8</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="impress_all">
+ <property name="label" translatable="yes" context="startcenter|impress_all">_Impress Presentation</property>
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">True</property>
+ <property name="margin-start">6</property>
+ <property name="margin-end">6</property>
+ <property name="image">impress_all_image</property>
+ <property name="relief">none</property>
+ <property name="use-underline">True</property>
+ <property name="xalign">0</property>
+ <property name="always-show-image">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">9</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="draw_all">
+ <property name="label" translatable="yes" context="startcenter|draw_all">_Draw Drawing</property>
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">True</property>
+ <property name="margin-start">6</property>
+ <property name="margin-end">6</property>
+ <property name="image">draw_all_image</property>
+ <property name="relief">none</property>
+ <property name="use-underline">True</property>
+ <property name="xalign">0</property>
+ <property name="always-show-image">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">10</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="math_all">
+ <property name="label" translatable="yes" context="startcenter|math_all">_Math Formula</property>
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">True</property>
+ <property name="margin-start">6</property>
+ <property name="margin-end">6</property>
+ <property name="image">math_all_image</property>
+ <property name="relief">none</property>
+ <property name="use-underline">True</property>
+ <property name="xalign">0</property>
+ <property name="always-show-image">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">11</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="database_all">
+ <property name="label" translatable="yes" context="startcenter|database_all">_Base Database</property>
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">True</property>
+ <property name="margin-start">6</property>
+ <property name="margin-end">6</property>
+ <property name="image">database_all_image</property>
+ <property name="relief">none</property>
+ <property name="use-underline">True</property>
+ <property name="xalign">0</property>
+ <property name="always-show-image">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">12</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="althelplabel">
+ <property name="can-focus">False</property>
+ <property name="no-show-all">True</property>
+ <property name="label" translatable="yes" context="startcenter|althelplabel">He_lp</property>
+ <property name="use-underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">15</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkDrawingArea" id="daBrand">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="valign">center</property>
+ <property name="margin-start">12</property>
+ <property name="margin-end">12</property>
+ <property name="margin-bottom">12</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSeparator" id="separator2">
+ <property name="height-request">2</property>
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="small_buttons_box">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="margin-start">6</property>
+ <property name="margin-end">6</property>
+ <property name="margin-top">6</property>
+ <property name="margin-bottom">6</property>
+ <property name="spacing">6</property>
+ <property name="homogeneous">True</property>
+ <child>
+ <object class="GtkButton" id="help">
+ <property name="label" translatable="yes" context="stock">_Help</property>
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">True</property>
+ <property name="relief">none</property>
+ <property name="use-underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="extensions">
+ <property name="label" translatable="yes" context="startcenter|extensions">E_xtensions</property>
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">True</property>
+ <property name="relief">none</property>
+ <property name="use-underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label1">
+ <property name="can-focus">False</property>
+ <property name="label" translatable="yes" context="startcenter|label1">Application</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="box2">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkLabel" id="all_recent_label">
+ <property name="can-focus">False</property>
+ <property name="label" translatable="yes" context="startcenter|all_recent_label">Recent Files List</property>
+ <property name="use-underline">True</property>
+ <property name="mnemonic-widget">all_recent</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="local_view_label">
+ <property name="can-focus">False</property>
+ <property name="label" translatable="yes" context="startcenter|local_view_label">Templates List</property>
+ <property name="use-underline">True</property>
+ <property name="mnemonic-widget">local_view</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="scrollrecent">
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="shadow-type">in</property>
+ <child>
+ <object class="GtkViewport">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <child>
+ <object class="GtkDrawingArea" id="all_recent">
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="events">GDK_BUTTON_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_STRUCTURE_MASK</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="scrolllocal">
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="shadow-type">in</property>
+ <child>
+ <object class="GtkViewport">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <child>
+ <object class="GtkDrawingArea" id="local_view">
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="events">GDK_BUTTON_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_STRUCTURE_MASK</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+</interface>
diff --git a/sfx2/uiconfig/ui/stylecontextmenu.ui b/sfx2/uiconfig/ui/stylecontextmenu.ui
new file mode 100644
index 000000000..b667a2ef3
--- /dev/null
+++ b/sfx2/uiconfig/ui/stylecontextmenu.ui
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.20.0 -->
+<interface domain="sfx">
+ <requires lib="gtk+" version="3.20"/>
+ <object class="GtkMenu" id="menu">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkMenuItem" id="new">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes" context="stylecontextmenu|new">New...</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="edit">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes" context="stylecontextmenu|edit">Modify...</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="hide">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes" context="stylecontextmenu|hide">Hide</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="show">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes" context="stylecontextmenu|show">Show</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="delete">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes" context="stylecontextmenu|delete">Delete...</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ </object>
+</interface>
diff --git a/sfx2/uiconfig/ui/tabbar.ui b/sfx2/uiconfig/ui/tabbar.ui
new file mode 100644
index 000000000..66e9ad4e9
--- /dev/null
+++ b/sfx2/uiconfig/ui/tabbar.ui
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.38.1 -->
+<interface domain="sfx">
+ <requires lib="gtk+" version="3.20"/>
+ <object class="GtkBox" id="TabBar">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+</interface>
diff --git a/sfx2/uiconfig/ui/tabbarcontents.ui b/sfx2/uiconfig/ui/tabbarcontents.ui
new file mode 100644
index 000000000..eb35998c1
--- /dev/null
+++ b/sfx2/uiconfig/ui/tabbarcontents.ui
@@ -0,0 +1,157 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.38.2 -->
+<interface domain="sfx">
+ <requires lib="gtk+" version="3.20"/>
+ <object class="GtkImage" id="image6">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="icon-name">open-menu-symbolic</property>
+ <property name="icon_size">2</property>
+ </object>
+ <object class="GtkMenu" id="mainmenu">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <child>
+ <object class="GtkSeparatorMenuItem" id="separator1">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="locktaskpanel">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="label" translatable="yes" context="tabbar|locktaskpanel">Dock</property>
+ <property name="use-underline">True</property>
+ <accelerator key="F10" signal="activate" modifiers="GDK_SHIFT_MASK | GDK_CONTROL_MASK"/>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="unlocktaskpanel">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="label" translatable="yes" context="tabbar|unlocktaskpanel">Undock</property>
+ <property name="use-underline">True</property>
+ <accelerator key="F10" signal="activate" modifiers="GDK_SHIFT_MASK | GDK_CONTROL_MASK"/>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="hidesidebar">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="label" translatable="yes" context="tabbar|hidesidebar">Close Sidebar</property>
+ <property name="use-underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="customization">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="label" translatable="yes" context="tabbar|customization">Customization</property>
+ <property name="use-underline">True</property>
+ <child type="submenu">
+ <object class="GtkMenu" id="submenu">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <child>
+ <object class="GtkSeparatorMenuItem" id="separator2">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="restoredefault">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="label" translatable="yes" context="tabbar|restoredefault">Restore Default</property>
+ <property name="use-underline">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <object class="GtkWindow" id="window">
+ <property name="can-focus">False</property>
+ <child>
+ <object class="GtkBox" id="toplevel">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkBox" id="TabBarContents">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="border-width">2</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkMenuButton" id="menubutton">
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">True</property>
+ <property name="tooltip-text" translatable="yes" context="tabbar|menubutton|tool_tip">Sidebar Settings</property>
+ <property name="halign">center</property>
+ <property name="valign">center</property>
+ <property name="margin-bottom">3</property>
+ <property name="image">image6</property>
+ <property name="relief">none</property>
+ <property name="always-show-image">True</property>
+ <property name="popup">mainmenu</property>
+ <property name="use-popover">False</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToolbar" id="measure">
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="halign">center</property>
+ <property name="orientation">vertical</property>
+ <property name="toolbar-style">icons</property>
+ <property name="show-arrow">False</property>
+ <child>
+ <object class="GtkToggleToolButton" id="toggle">
+ <property name="visible">True</property>
+ <property name="use-underline">True</property>
+ <property name="icon-name">sfx2/res/symphony/sidebar-property-large.png</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">True</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+</interface>
diff --git a/sfx2/uiconfig/ui/tabbutton.ui b/sfx2/uiconfig/ui/tabbutton.ui
new file mode 100644
index 000000000..fc8f17508
--- /dev/null
+++ b/sfx2/uiconfig/ui/tabbutton.ui
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.38.1 -->
+<interface domain="sfx">
+ <requires lib="gtk+" version="3.20"/>
+ <object class="GtkToolbar" id="button">
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="halign">center</property>
+ <property name="hexpand">False</property>
+ <property name="orientation">vertical</property>
+ <property name="toolbar-style">icons</property>
+ <property name="show-arrow">False</property>
+ <child>
+ <object class="GtkToggleToolButton" id="toggle">
+ <property name="visible">True</property>
+ <property name="use-underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">True</property>
+ </packing>
+ </child>
+ </object>
+</interface>
diff --git a/sfx2/uiconfig/ui/templatecategorydlg.ui b/sfx2/uiconfig/ui/templatecategorydlg.ui
new file mode 100644
index 000000000..ecf5bd92c
--- /dev/null
+++ b/sfx2/uiconfig/ui/templatecategorydlg.ui
@@ -0,0 +1,227 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.22.1 -->
+<interface domain="sfx">
+ <requires lib="gtk+" version="3.20"/>
+ <object class="GtkTreeStore" id="categorylist">
+ <columns>
+ <!-- column-name text -->
+ <column type="gchararray"/>
+ <!-- column-name id -->
+ <column type="gchararray"/>
+ </columns>
+ </object>
+ <object class="GtkDialog" id="TemplatesCategoryDialog">
+ <property name="can_focus">False</property>
+ <property name="border_width">6</property>
+ <property name="title" translatable="yes" context="templatecategorydlg|TemplatesCategoryDialog">Select Category</property>
+ <property name="modal">True</property>
+ <property name="default_width">0</property>
+ <property name="default_height">0</property>
+ <property name="type_hint">normal</property>
+ <child>
+ <placeholder/>
+ </child>
+ <child internal-child="vbox">
+ <object class="GtkBox" id="dialog-vbox1">
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">2</property>
+ <child internal-child="action_area">
+ <object class="GtkButtonBox" id="dialog-action_area1">
+ <property name="can_focus">False</property>
+ <property name="layout_style">end</property>
+ <child>
+ <object class="GtkButton" id="help">
+ <property name="label" translatable="yes" context="stock">_Help</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use-underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ <property name="secondary">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="ok">
+ <property name="label" translatable="yes" context="stock">_OK</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="has_default">True</property>
+ <property name="receives_default">True</property>
+ <property name="use-underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="cancel">
+ <property name="label" translatable="yes" context="stock">_Cancel</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use-underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">end</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <!-- n-columns=1 n-rows=1 -->
+ <object class="GtkGrid" id="grid1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_bottom">6</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="row_spacing">12</property>
+ <property name="column_spacing">12</property>
+ <child>
+ <object class="GtkBox" id="box1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="select_label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes" context="templatecategorydlg|select_label">Select from Existing Category</property>
+ <property name="use_underline">True</property>
+ <property name="mnemonic_widget">categorylb</property>
+ <property name="xalign">0</property>
+ <attributes>
+ <attribute name="weight" value="normal"/>
+ </attributes>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkTreeView" id="categorylb">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="vexpand">True</property>
+ <property name="model">categorylist</property>
+ <property name="headers_visible">False</property>
+ <property name="headers_clickable">False</property>
+ <property name="search_column">0</property>
+ <property name="show_expanders">False</property>
+ <child internal-child="selection">
+ <object class="GtkTreeSelection" id="treeview-selection1"/>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="treeviewcolumn1">
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderertext1"/>
+ <attributes>
+ <attribute name="text">0</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="box2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="create_label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes" context="templatecategorydlg|create_label">or Create a New Category</property>
+ <property name="use_underline">True</property>
+ <property name="mnemonic_widget">category_entry</property>
+ <property name="xalign">0</property>
+ <attributes>
+ <attribute name="weight" value="normal"/>
+ </attributes>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="category_entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hexpand">True</property>
+ <property name="truncate-multiline">True</property>
+ <property name="activates_default">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <action-widgets>
+ <action-widget response="-11">help</action-widget>
+ <action-widget response="-5">ok</action-widget>
+ <action-widget response="-6">cancel</action-widget>
+ </action-widgets>
+ </object>
+</interface>
diff --git a/sfx2/uiconfig/ui/templatedlg.ui b/sfx2/uiconfig/ui/templatedlg.ui
new file mode 100644
index 000000000..7f7fe31d6
--- /dev/null
+++ b/sfx2/uiconfig/ui/templatedlg.ui
@@ -0,0 +1,514 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.38.2 -->
+<interface domain="sfx">
+ <requires lib="gtk+" version="3.20"/>
+ <object class="GtkMenu" id="contextmenu">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ </object>
+ <object class="GtkImage" id="image8">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="icon-name">svx/res/galicon.png</property>
+ </object>
+ <object class="GtkImage" id="image9">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="icon-name">svx/res/listview.png</property>
+ </object>
+ <object class="GtkMenu" id="menu1">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ </object>
+ <object class="GtkTreeStore" id="tree_store">
+ <columns>
+ <!-- column-name default_img -->
+ <column type="GdkPixbuf"/>
+ <!-- column-name name -->
+ <column type="gchararray"/>
+ <!-- column-name category -->
+ <column type="gchararray"/>
+ <!-- column-name application -->
+ <column type="gchararray"/>
+ <!-- column-name modified -->
+ <column type="gchararray"/>
+ <!-- column-name size -->
+ <column type="gchararray"/>
+ <!-- column-name id -->
+ <column type="gchararray"/>
+ </columns>
+ </object>
+ <object class="GtkDialog" id="TemplateDialog">
+ <property name="can-focus">False</property>
+ <property name="border-width">6</property>
+ <property name="title" translatable="yes" context="templatedlg|TemplateDialog">Templates</property>
+ <property name="resizable">False</property>
+ <property name="modal">True</property>
+ <property name="default-width">0</property>
+ <property name="default-height">0</property>
+ <property name="type-hint">normal</property>
+ <child internal-child="vbox">
+ <object class="GtkBox" id="dialog-vbox1">
+ <property name="can-focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">2</property>
+ <child internal-child="action_area">
+ <object class="GtkButtonBox" id="dialog-action_area1">
+ <property name="can-focus">False</property>
+ <property name="layout-style">end</property>
+ <child>
+ <object class="GtkButton" id="help">
+ <property name="label" translatable="yes" context="stock">_Help</property>
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">True</property>
+ <property name="use-underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ <property name="secondary">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="hidedialogcb">
+ <property name="label" translatable="yes" context="templatedlg|hidedialogcb">Show this dialog at startup</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">False</property>
+ <property name="use-underline">True</property>
+ <property name="draw-indicator">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ <property name="secondary">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="ok">
+ <property name="label" translatable="yes" context="stock">_OK</property>
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="can-default">True</property>
+ <property name="has-default">True</property>
+ <property name="receives-default">True</property>
+ <property name="use-underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="close">
+ <property name="label" translatable="yes" context="stock">_Close</property>
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="can-default">True</property>
+ <property name="receives-default">True</property>
+ <property name="use-underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack-type">end</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="box1">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkBox" id="filter_box">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkEntry" id="search_filter">
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="tooltip-text" translatable="yes" context="templatedlg|search_filter|tooltip_text">Search</property>
+ <property name="hexpand">True</property>
+ <property name="activates-default">True</property>
+ <property name="truncate-multiline">True</property>
+ <property name="placeholder-text" translatable="yes" context="templatedlg|search_filter">Search...</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="box5">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkBox" id="box7">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <child>
+ <object class="GtkMenuButton" id="action_menu">
+ <property name="label" translatable="yes" context="templatedlg|action_menu|label">_Manage</property>
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">False</property>
+ <property name="use-underline">True</property>
+ <property name="always-show-image">True</property>
+ <property name="draw-indicator">True</property>
+ <property name="popup">menu1</property>
+ <property name="use-popover">False</property>
+ <child>
+ <placeholder/>
+ </child>
+ <child internal-child="accessible">
+ <object class="AtkObject" id="action_menu-atkobject">
+ <property name="AtkObject::accessible-description" translatable="yes" context="templatedlg|extended_tip|action_menu">Provides commands to create, rename and delete categories, reset default templates, and refresh the template manager.</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack-type">end</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="box6">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkComboBoxText" id="filter_application">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="tooltip-text" translatable="yes" context="templatedlg|filter_application|tooltip_text">Filter by Application</property>
+ <items>
+ <item id="0" translatable="yes" context="templatedlg|applist">All Applications</item>
+ <item id="0" translatable="yes" context="templatedlg|applist">Text Documents</item>
+ <item id="0" translatable="yes" context="templatedlg|applist">Spreadsheets</item>
+ <item id="0" translatable="yes" context="templatedlg|applist">Presentations</item>
+ <item id="0" translatable="yes" context="templatedlg|applist">Drawings</item>
+ </items>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBoxText" id="filter_folder">
+ <property name="width-request">250</property>
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="tooltip-text" translatable="yes" context="templatedlg|filter_folder|tooltip_text">Filter by Category</property>
+ <items>
+ <item id="0" translatable="yes" context="templatedlg|folderlist">All Categories</item>
+ </items>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack-type">end</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label1">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="label" translatable="yes" context="templatedlg|label1">Filter</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack-type">end</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack-type">end</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFrame" id="thumbnailviewframe">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="label-xalign">0</property>
+ <property name="shadow-type">none</property>
+ <child>
+ <object class="GtkBox" id="thumbnailview_box">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkScrolledWindow" id="scrolllocal">
+ <property name="can-focus">True</property>
+ <property name="no-show-all">True</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="shadow-type">in</property>
+ <child>
+ <object class="GtkViewport">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <child>
+ <object class="GtkDrawingArea" id="template_view">
+ <property name="can-focus">True</property>
+ <property name="events">GDK_BUTTON_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_STRUCTURE_MASK</property>
+ <property name="no-show-all">True</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow">
+ <property name="can-focus">True</property>
+ <property name="no-show-all">True</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="shadow-type">in</property>
+ <child>
+ <object class="GtkTreeView" id="tree_list">
+ <property name="can-focus">True</property>
+ <property name="no-show-all">True</property>
+ <property name="has-tooltip">True</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="model">tree_store</property>
+ <property name="enable-search">False</property>
+ <property name="search-column">0</property>
+ <property name="show-expanders">False</property>
+ <child internal-child="selection">
+ <object class="GtkTreeSelection" id="treeview-selection1"/>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn">
+ <child>
+ <object class="GtkCellRendererPixbuf" id="cellrenderer1"/>
+ <attributes>
+ <attribute name="pixbuf">0</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="treeviewcolumn1">
+ <property name="resizable">True</property>
+ <property name="spacing">6</property>
+ <property name="title" translatable="yes" context="templatedlg|treeviewcolumn1">Name</property>
+ <property name="expand">True</property>
+ <property name="clickable">True</property>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderer2">
+ <property name="ypad">3</property>
+ </object>
+ <attributes>
+ <attribute name="text">1</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="treeviewcolumn2">
+ <property name="resizable">True</property>
+ <property name="spacing">6</property>
+ <property name="title" translatable="yes" context="templatedlg|treeviewcolumn2">Category</property>
+ <property name="clickable">True</property>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderer3">
+ <property name="ypad">3</property>
+ </object>
+ <attributes>
+ <attribute name="text">2</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="treeviewcolumn3">
+ <property name="resizable">True</property>
+ <property name="title" translatable="yes" context="templatedlg|treeviewcolumn3">Application</property>
+ <property name="clickable">True</property>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderer4">
+ <property name="ypad">3</property>
+ </object>
+ <attributes>
+ <attribute name="text">3</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="treeviewcolumn4">
+ <property name="resizable">True</property>
+ <property name="title" translatable="yes" context="templatedlg|treeviewcolumn4">Modified</property>
+ <property name="clickable">True</property>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderer5">
+ <property name="ypad">3</property>
+ </object>
+ <attributes>
+ <attribute name="text">4</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="treeviewcolumn5">
+ <property name="resizable">True</property>
+ <property name="title" translatable="yes" context="templatedlg|treeviewcolumn5">Size</property>
+ <property name="clickable">True</property>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderer6">
+ <property name="ypad">3</property>
+ </object>
+ <attributes>
+ <attribute name="text">5</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="thumbnailviewlabel">
+ <property name="can-focus">False</property>
+ <property name="label" translatable="yes" context="templatedlg|thumbnailviewlabel">Template List</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="box2">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <child>
+ <object class="GtkToggleButton" id="thumbnail_view_btn">
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">True</property>
+ <property name="tooltip-text" translatable="yes" context="templatedlg|thumbnail_view_btn|tooltip_text">Thumbnail View</property>
+ <property name="image">image8</property>
+ <property name="relief">none</property>
+ <property name="always-show-image">True</property>
+ <property name="active">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToggleButton" id="list_view_btn">
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">True</property>
+ <property name="tooltip-text" translatable="yes" context="templatedlg|list_view_btn|tooltip_text">List View</property>
+ <property name="image">image9</property>
+ <property name="relief">none</property>
+ <property name="always-show-image">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack-type">end</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <action-widgets>
+ <action-widget response="-11">help</action-widget>
+ <action-widget response="-5">ok</action-widget>
+ <action-widget response="-7">close</action-widget>
+ </action-widgets>
+ </object>
+</interface>
diff --git a/sfx2/uiconfig/ui/templatepanel.ui b/sfx2/uiconfig/ui/templatepanel.ui
new file mode 100644
index 000000000..f4e8d98b2
--- /dev/null
+++ b/sfx2/uiconfig/ui/templatepanel.ui
@@ -0,0 +1,315 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.36.0 -->
+<interface domain="sfx">
+ <requires lib="gtk+" version="3.20"/>
+ <object class="GtkTreeStore" id="liststore1">
+ <columns>
+ <!-- column-name text -->
+ <column type="gchararray"/>
+ <!-- column-name id -->
+ <column type="gchararray"/>
+ </columns>
+ </object>
+ <object class="GtkTreeStore" id="liststore2">
+ <columns>
+ <!-- column-name text -->
+ <column type="gchararray"/>
+ <!-- column-name id -->
+ <column type="gchararray"/>
+ </columns>
+ </object>
+ <!-- n-columns=1 n-rows=1 -->
+ <object class="GtkGrid" id="TemplatePanel">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <child>
+ <object class="GtkBox" id="box1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="border_width">6</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">3</property>
+ <child>
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkToolbar" id="left">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="toolbar_style">text</property>
+ <property name="show_arrow">False</property>
+ <property name="icon_size">2</property>
+ <child>
+ <object class="GtkToggleToolButton" id="2">
+ <property name="use_underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToggleToolButton" id="1">
+ <property name="use_underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToggleToolButton" id="3">
+ <property name="use_underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToggleToolButton" id="4">
+ <property name="use_underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToggleToolButton" id="5">
+ <property name="use_underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToggleToolButton" id="6">
+ <property name="use_underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToggleToolButton" id="65535">
+ <property name="use_underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">False</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToolbar" id="right">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="toolbar_style">icons</property>
+ <property name="show_arrow">False</property>
+ <property name="icon_size">2</property>
+ <child>
+ <object class="GtkToggleToolButton" id="watercan">
+ <property name="visible">True</property>
+ <property name="tooltip_text" translatable="yes" context="templatepanel|STR_STYLE_FILL_FORMAT_MODE">Fill Format Mode</property>
+ <property name="use_underline">True</property>
+ <property name="icon_name">res/sc05554.png</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToolButton" id="new">
+ <property name="visible">True</property>
+ <property name="tooltip_text" translatable="yes" context="templatepanel|STR_STYLE_NEW_STYLE_FROM_SELECTION">New Style from Selection</property>
+ <property name="use_underline">True</property>
+ <property name="icon_name">res/sc05555.png</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkMenuToolButton" id="newmenu">
+ <property name="no_show_all">True</property>
+ <property name="tooltip_text" translatable="yes" context="templatepanel|STR_STYLE_NEW_STYLE_ACTION">Styles actions</property>
+ <property name="use_underline">True</property>
+ <property name="icon_name">res/sc05555.png</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToolButton" id="update">
+ <property name="visible">True</property>
+ <property name="tooltip_text" translatable="yes" context="templatepanel|STR_STYLE_UPDATE_STYLE">Update Style</property>
+ <property name="use_underline">True</property>
+ <property name="icon_name">res/sc05556.png</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">False</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">end</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow">
+ <property name="can_focus">True</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkTreeView" id="flatview">
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="no_show_all">True</property>
+ <property name="has_tooltip">True</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="model">liststore1</property>
+ <property name="headers_visible">False</property>
+ <property name="search_column">0</property>
+ <property name="show_expanders">False</property>
+ <child internal-child="selection">
+ <object class="GtkTreeSelection" id="Macro Library List-selection2"/>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="treeviewcolumn1">
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderertext1"/>
+ <attributes>
+ <attribute name="text">0</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ <child internal-child="accessible">
+ <object class="AtkObject" id="flatview-atkobject">
+ <property name="AtkObject::accessible-name" translatable="yes" context="templatepanel|STR_STYLE_ELEMTLIST">Style List</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow">
+ <property name="can_focus">True</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkTreeView" id="treeview">
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="no_show_all">True</property>
+ <property name="has_tooltip">True</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="model">liststore2</property>
+ <property name="headers_visible">False</property>
+ <property name="reorderable">True</property>
+ <property name="search_column">0</property>
+ <property name="enable_tree_lines">True</property>
+ <child internal-child="selection">
+ <object class="GtkTreeSelection" id="Macro Library List-selection1"/>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="treeviewcolumn2">
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderertext2"/>
+ <attributes>
+ <attribute name="text">0</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ <child internal-child="accessible">
+ <object class="AtkObject" id="treeview-atkobject">
+ <property name="AtkObject::accessible-name" translatable="yes" context="templatepanel|STR_STYLE_ELEMTLIST">Style List</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="showpreview">
+ <property name="label" translatable="yes" context="commontemplate|STR_PREVIEW_CHECKBOX">Show previews</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_underline">True</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBoxText" id="filter">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">4</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ </object>
+ <object class="GtkMenu" id="toolmenu">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+</interface>
diff --git a/sfx2/uiconfig/ui/urlbox.ui b/sfx2/uiconfig/ui/urlbox.ui
new file mode 100644
index 000000000..29fe22b9f
--- /dev/null
+++ b/sfx2/uiconfig/ui/urlbox.ui
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.22.1 -->
+<interface domain="sfx">
+ <requires lib="gtk+" version="3.20"/>
+ <object class="GtkBox" id="URLBox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkComboBoxText" id="urlbox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="has_entry">True</property>
+ <child internal-child="entry">
+ <object class="GtkEntry">
+ <property name="truncate-multiline">True</property>
+ <property name="can_focus">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+</interface>
diff --git a/sfx2/uiconfig/ui/versioncommentdialog.ui b/sfx2/uiconfig/ui/versioncommentdialog.ui
new file mode 100644
index 000000000..421847357
--- /dev/null
+++ b/sfx2/uiconfig/ui/versioncommentdialog.ui
@@ -0,0 +1,168 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.36.0 -->
+<interface domain="sfx">
+ <requires lib="gtk+" version="3.20"/>
+ <object class="GtkDialog" id="VersionCommentDialog">
+ <property name="can_focus">False</property>
+ <property name="border_width">6</property>
+ <property name="title" translatable="yes" context="versioncommentdialog|VersionCommentDialog">Insert Version Comment</property>
+ <property name="resizable">False</property>
+ <property name="modal">True</property>
+ <property name="default_width">0</property>
+ <property name="default_height">0</property>
+ <property name="type_hint">dialog</property>
+ <child internal-child="vbox">
+ <object class="GtkBox" id="dialog-vbox1">
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">12</property>
+ <child internal-child="action_area">
+ <object class="GtkButtonBox" id="dialog-action_area1">
+ <property name="can_focus">False</property>
+ <property name="layout_style">end</property>
+ <child>
+ <object class="GtkButton" id="ok">
+ <property name="label" translatable="yes" context="stock">_OK</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="has_default">True</property>
+ <property name="receives_default">True</property>
+ <property name="use-underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="close">
+ <property name="label" translatable="yes" context="stock">_Close</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="receives_default">True</property>
+ <property name="use-underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="cancel">
+ <property name="label" translatable="yes" context="stock">_Cancel</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use-underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="help">
+ <property name="label" translatable="yes" context="stock">_Help</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use-underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">3</property>
+ <property name="secondary">True</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">end</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <!-- n-columns=1 n-rows=1 -->
+ <object class="GtkGrid" id="grid1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="row_spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="timestamp">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes" context="versioncommentdialog|timestamp">Date and time: </property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="author">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes" context="versioncommentdialog|author">Saved by: </property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="scrolledwindow1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkTextView" id="textview">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <action-widgets>
+ <action-widget response="-5">ok</action-widget>
+ <action-widget response="-7">close</action-widget>
+ <action-widget response="-6">cancel</action-widget>
+ <action-widget response="-11">help</action-widget>
+ </action-widgets>
+ <child type="titlebar">
+ <placeholder/>
+ </child>
+ <child internal-child="accessible">
+ <object class="AtkObject" id="VersionCommentDialog-atkobject">
+ <property name="AtkObject::accessible-description" translatable="yes" context="versioncommentdialog|extended_tip|VersionCommentDialog">Enter a comment here when you are saving a new version. If you clicked Show to open this dialog, you cannot edit the comment.</property>
+ </object>
+ </child>
+ </object>
+</interface>
diff --git a/sfx2/uiconfig/ui/versionscmis.ui b/sfx2/uiconfig/ui/versionscmis.ui
new file mode 100644
index 000000000..16eff4f32
--- /dev/null
+++ b/sfx2/uiconfig/ui/versionscmis.ui
@@ -0,0 +1,256 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.22.1 -->
+<interface domain="sfx">
+ <requires lib="gtk+" version="3.20"/>
+ <object class="GtkTreeStore" id="liststore3">
+ <columns>
+ <!-- column-name text -->
+ <column type="gchararray"/>
+ <!-- column-name text2 -->
+ <column type="gchararray"/>
+ <!-- column-name text3 -->
+ <column type="gchararray"/>
+ <!-- column-name id -->
+ <column type="gchararray"/>
+ </columns>
+ </object>
+ <object class="GtkDialog" id="VersionsCmisDialog">
+ <property name="can_focus">False</property>
+ <property name="border_width">6</property>
+ <property name="modal">True</property>
+ <property name="type_hint">dialog</property>
+ <child>
+ <placeholder/>
+ </child>
+ <child internal-child="vbox">
+ <object class="GtkBox" id="dialog-vbox1">
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">12</property>
+ <child internal-child="action_area">
+ <object class="GtkButtonBox" id="dialog-action_area1">
+ <property name="can_focus">False</property>
+ <property name="layout_style">end</property>
+ <child>
+ <object class="GtkButton" id="delete">
+ <property name="label" translatable="yes" context="stock">_Delete</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="compare">
+ <property name="label" translatable="yes" context="versionscmis|compare">_Compare</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="show">
+ <property name="label" translatable="yes" context="versionscmis|show">_Show...</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="close">
+ <property name="label" translatable="yes" context="stock">_Close</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use-underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="open">
+ <property name="label" translatable="yes" context="versionscmis|open">_Open</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">4</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="help">
+ <property name="label" translatable="yes" context="stock">_Help</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use-underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">6</property>
+ <property name="secondary">True</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">end</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <!-- n-columns=1 n-rows=1 -->
+ <object class="GtkGrid" id="grid1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="row_spacing">12</property>
+ <child>
+ <object class="GtkFrame" id="frame2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <!-- n-columns=1 n-rows=1 -->
+ <object class="GtkGrid" id="grid3">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="margin-start">12</property>
+ <property name="margin-top">6</property>
+ <child>
+ <object class="GtkScrolledWindow">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkTreeView" id="versions">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="model">liststore3</property>
+ <property name="headers_visible">True</property>
+ <property name="search_column">0</property>
+ <property name="show_expanders">False</property>
+ <child internal-child="selection">
+ <object class="GtkTreeSelection" id="versions-selection2"/>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="treeviewcolumn1">
+ <property name="resizable">True</property>
+ <property name="spacing">6</property>
+ <property name="title" translatable="yes" context="versionscmis|datetime">Date and time</property>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderer1"/>
+ <attributes>
+ <attribute name="text">0</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="treeviewcolumn2">
+ <property name="resizable">True</property>
+ <property name="spacing">6</property>
+ <property name="title" translatable="yes" context="versionscmis|savedby">Saved by</property>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderer2"/>
+ <attributes>
+ <attribute name="text">1</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="treeviewcolumn3">
+ <property name="resizable">True</property>
+ <property name="spacing">6</property>
+ <property name="title" translatable="yes" context="versionscmis|comments">Comments</property>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderer3"/>
+ <attributes>
+ <attribute name="text">2</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes" context="versionscmis|label2">Existing Versions</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <action-widgets>
+ <action-widget response="103">delete</action-widget>
+ <action-widget response="102">compare</action-widget>
+ <action-widget response="101">show</action-widget>
+ <action-widget response="-7">close</action-widget>
+ <action-widget response="-5">open</action-widget>
+ <action-widget response="-11">help</action-widget>
+ </action-widgets>
+ </object>
+</interface>
diff --git a/sfx2/uiconfig/ui/versionsofdialog.ui b/sfx2/uiconfig/ui/versionsofdialog.ui
new file mode 100644
index 000000000..c217cbf59
--- /dev/null
+++ b/sfx2/uiconfig/ui/versionsofdialog.ui
@@ -0,0 +1,371 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.36.0 -->
+<interface domain="sfx">
+ <requires lib="gtk+" version="3.20"/>
+ <object class="GtkTreeStore" id="liststore3">
+ <columns>
+ <!-- column-name text -->
+ <column type="gchararray"/>
+ <!-- column-name text2 -->
+ <column type="gchararray"/>
+ <!-- column-name text3 -->
+ <column type="gchararray"/>
+ <!-- column-name id -->
+ <column type="gchararray"/>
+ </columns>
+ </object>
+ <object class="GtkDialog" id="VersionsOfDialog">
+ <property name="can_focus">False</property>
+ <property name="border_width">6</property>
+ <property name="modal">True</property>
+ <property name="type_hint">dialog</property>
+ <child internal-child="vbox">
+ <object class="GtkBox" id="dialog-vbox1">
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">12</property>
+ <child internal-child="action_area">
+ <object class="GtkButtonBox" id="dialog-action_area1">
+ <property name="can_focus">False</property>
+ <property name="layout_style">end</property>
+ <child>
+ <object class="GtkButton" id="cmis">
+ <property name="label" translatable="yes" context="versionsofdialog|cmis">CMIS</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="delete">
+ <property name="label" translatable="yes" context="stock">_Delete</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_underline">True</property>
+ <child internal-child="accessible">
+ <object class="AtkObject" id="delete-atkobject">
+ <property name="AtkObject::accessible-description" translatable="yes" context="versionsofdialog|extended_tip|delete">Deletes the selected version.</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="compare">
+ <property name="label" translatable="yes" context="versionsofdialog|compare">_Compare</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_underline">True</property>
+ <child internal-child="accessible">
+ <object class="AtkObject" id="compare-atkobject">
+ <property name="AtkObject::accessible-description" translatable="yes" context="versionsofdialog|extended_tip|compare">Compare the changes that were made in each version.</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="show">
+ <property name="label" translatable="yes" context="versionsofdialog|show">_Show...</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_underline">True</property>
+ <child internal-child="accessible">
+ <object class="AtkObject" id="show-atkobject">
+ <property name="AtkObject::accessible-description" translatable="yes" context="versionsofdialog|extended_tip|show">Displays the entire comment for the selected version.</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="close">
+ <property name="label" translatable="yes" context="stock">_Close</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="has_default">True</property>
+ <property name="receives_default">True</property>
+ <property name="use-underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">4</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="open">
+ <property name="label" translatable="yes" context="versionsofdialog|open">_Open</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_underline">True</property>
+ <child internal-child="accessible">
+ <object class="AtkObject" id="open-atkobject">
+ <property name="AtkObject::accessible-description" translatable="yes" context="versionsofdialog|extended_tip|open">Opens the selected version in a read-only window.</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">5</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="help">
+ <property name="label" translatable="yes" context="stock">_Help</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use-underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">6</property>
+ <property name="secondary">True</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">end</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <!-- n-columns=1 n-rows=1 -->
+ <object class="GtkGrid" id="grid1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="row_spacing">12</property>
+ <child>
+ <object class="GtkFrame" id="frame1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <!-- n-columns=1 n-rows=1 -->
+ <object class="GtkGrid" id="grid2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="column_spacing">12</property>
+ <property name="margin-start">12</property>
+ <property name="margin-top">6</property>
+ <child>
+ <object class="GtkButton" id="save">
+ <property name="label" translatable="yes" context="versionsofdialog|save">Save _New Version</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_underline">True</property>
+ <child internal-child="accessible">
+ <object class="AtkObject" id="save-atkobject">
+ <property name="AtkObject::accessible-description" translatable="yes" context="versionsofdialog|extended_tip|save">Saves the current state of the document as a new version. If you want, you can also enter comments in the Insert Version Comment dialog before you save the new version.</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="always">
+ <property name="label" translatable="yes" context="versionsofdialog|always">_Always save a new version on closing</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_underline">True</property>
+ <property name="draw_indicator">True</property>
+ <child internal-child="accessible">
+ <object class="AtkObject" id="always-atkobject">
+ <property name="AtkObject::accessible-description" translatable="yes" context="versionsofdialog|extended_tip|always">If you have made changes to your document then a new version is automatically saved when you close the document.</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes" context="versionsofdialog|label1">New Versions</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFrame" id="frame2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <!-- n-columns=1 n-rows=1 -->
+ <object class="GtkGrid" id="grid3">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="margin-start">12</property>
+ <property name="margin-top">6</property>
+ <child>
+ <object class="GtkScrolledWindow">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkTreeView" id="versions">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="model">liststore3</property>
+ <property name="search_column">0</property>
+ <property name="show_expanders">False</property>
+ <child internal-child="selection">
+ <object class="GtkTreeSelection" id="versions-selection2"/>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="treeviewcolumn1">
+ <property name="resizable">True</property>
+ <property name="spacing">6</property>
+ <property name="title" translatable="yes" context="versionsofdialog|datetime">Date and time</property>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderer1"/>
+ <attributes>
+ <attribute name="text">0</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="treeviewcolumn2">
+ <property name="resizable">True</property>
+ <property name="spacing">6</property>
+ <property name="title" translatable="yes" context="versionsofdialog|savedby">Saved by</property>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderer2"/>
+ <attributes>
+ <attribute name="text">1</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="treeviewcolumn3">
+ <property name="resizable">True</property>
+ <property name="spacing">6</property>
+ <property name="title" translatable="yes" context="versionsofdialog|comments">Comments</property>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderer3"/>
+ <attributes>
+ <attribute name="text">2</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ <child internal-child="accessible">
+ <object class="AtkObject" id="versions-atkobject">
+ <property name="AtkObject::accessible-description" translatable="yes" context="versionsofdialog|extended_tip|versions">Lists the existing versions of the current document, the date and the time they were created, the author and the associated comments.</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes" context="versionsofdialog|label2">Existing Versions</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <action-widgets>
+ <action-widget response="104">cmis</action-widget>
+ <action-widget response="103">delete</action-widget>
+ <action-widget response="102">compare</action-widget>
+ <action-widget response="101">show</action-widget>
+ <action-widget response="-7">close</action-widget>
+ <action-widget response="-5">open</action-widget>
+ <action-widget response="-11">help</action-widget>
+ </action-widgets>
+ <child type="titlebar">
+ <placeholder/>
+ </child>
+ <child internal-child="accessible">
+ <object class="AtkObject" id="VersionsOfDialog-atkobject">
+ <property name="AtkObject::accessible-description" translatable="yes" context="versionsofdialog|extended_tip|VersionsOfDialog">Saves and organizes multiple versions of the current document in the same file. You can also open, delete and compare previous versions.</property>
+ </object>
+ </child>
+ </object>
+</interface>
diff --git a/sfx2/util/sfx.component b/sfx2/util/sfx.component
new file mode 100644
index 000000000..7970342db
--- /dev/null
+++ b/sfx2/util/sfx.component
@@ -0,0 +1,100 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ -->
+
+<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@"
+ xmlns="http://openoffice.org/2010/uno-components">
+ <implementation name="com.sun.star.comp.sfx2.BackingComp"
+ constructor="com_sun_star_comp_sfx2_BackingComp_get_implementation">
+ <service name="com.sun.star.frame.StartModule"/>
+ <service name="com.sun.star.frame.ProtocolHandler"/>
+ <optional/>
+ </implementation>
+ <implementation name="SfxDocumentMetaData"
+ constructor="SfxDocumentMetaData_get_implementation">
+ <service name="com.sun.star.document.DocumentProperties"/>
+ </implementation>
+ <implementation name="CompatWriterDocPropsImpl"
+ constructor="CompatWriterDocPropsImpl_get_implementation">
+ <service name="com.sun.star.writer.DocumentProperties"/>
+ </implementation>
+ <implementation name="com.sun.star.comp.desktop.QuickstartWrapper"
+ constructor="com_sun_star_comp_desktop_QuickstartWrapper_get_implementation"
+ single-instance="true">
+ <service name="com.sun.star.office.Quickstart"/>
+ <singleton name="com.sun.star.office.theQuickstart"/>
+ </implementation>
+ <implementation name="com.sun.star.comp.document.OwnSubFilter"
+ constructor="com_sun_star_comp_document_OwnSubFilter_get_implementation">
+ <service name="com.sun.star.comp.document.OwnSubFilter"/>
+ <service name="com.sun.star.document.OwnSubFilter"/>
+ </implementation>
+ <implementation name="com.sun.star.comp.embed.PackageStructureCreator"
+ constructor="com_sun_star_comp_embed_PackageStructureCreator_get_implementation">
+ <service name="com.sun.star.comp.embed.PackageStructureCreator"/>
+ <service name="com.sun.star.embed.PackageStructureCreator"/>
+ </implementation>
+ <implementation name="com.sun.star.comp.office.FrameLoader"
+ constructor="com_sun_star_comp_office_FrameLoader_get_implementation">
+ <service name="com.sun.star.frame.SynchronousFrameLoader"/>
+ <service name="com.sun.star.frame.OfficeFrameLoader"/>
+ </implementation>
+ <implementation name="com.sun.star.comp.sfx2.AppDispatchProvider"
+ constructor="com_sun_star_comp_sfx2_AppDispatchProvider_get_implementation">
+ <service name="com.sun.star.frame.ProtocolHandler"/>
+ <service name="com.sun.star.frame.AppDispatchProvider"/>
+ </implementation>
+ <implementation name="com.sun.star.comp.sfx2.ApplicationDialogLibraryContainer"
+ constructor="com_sun_star_comp_sfx2_ApplicationDialogLibraryContainer_get_implementation">
+ <service name="com.sun.star.script.ApplicationDialogLibraryContainer"/>
+ </implementation>
+ <implementation name="com.sun.star.comp.sfx2.ApplicationScriptLibraryContainer"
+ constructor="com_sun_star_comp_sfx2_ApplicationScriptLibraryContainer_get_implementation">
+ <service name="com.sun.star.script.ApplicationScriptLibraryContainer"/>
+ </implementation>
+ <implementation name="com.sun.star.comp.sfx2.DocumentTemplates"
+ constructor="com_sun_star_comp_sfx2_DocumentTemplates_get_implementation">
+ <service name="com.sun.star.frame.DocumentTemplates"/>
+ </implementation>
+ <implementation name="com.sun.star.comp.sfx2.GlobalEventBroadcaster"
+ constructor="com_sun_star_comp_sfx2_GlobalEventBroadcaster_get_implementation"
+ single-instance="true">
+ <service name="com.sun.star.frame.GlobalEventBroadcaster"/>
+ <singleton name="com.sun.star.frame.theGlobalEventBroadcaster"/>
+ </implementation>
+ <implementation name="com.sun.star.comp.sfx2.IFrameObject"
+ constructor="com_sun_star_comp_sfx2_IFrameObject_get_implementation">
+ <service name="com.sun.star.frame.SpecialEmbeddedObject"/>
+ </implementation>
+ <implementation name="com.sun.star.comp.sfx2.SfxMacroLoader"
+ constructor="com_sun_star_comp_sfx2_SfxMacroLoader_get_implementation">
+ <service name="com.sun.star.frame.ProtocolHandler"/>
+ </implementation>
+ <implementation name="com.sun.star.comp.sfx2.ClassificationCategoriesController"
+ constructor="com_sun_star_sfx2_ClassificationCategoriesController_get_implementation">
+ <service name="com.sun.star.frame.ToolbarController"/>
+ </implementation>
+ <implementation name="com.sun.star.comp.sfx2.InsertEmojiToolBoxControl"
+ constructor="com_sun_star_comp_sfx2_InsertEmojiToolBoxControl_get_implementation">
+ <service name="com.sun.star.frame.ToolbarController"/>
+ </implementation>
+ <implementation name="com.sun.star.comp.sfx2.InsertSymbolToolBoxControl"
+ constructor="com_sun_star_comp_sfx2_InsertSymbolToolBoxControl_get_implementation">
+ <service name="com.sun.star.frame.ToolbarController"/>
+ </implementation>
+</component>
diff --git a/sfx2/util/sfx.component.extended b/sfx2/util/sfx.component.extended
new file mode 100644
index 000000000..2695e06a0
--- /dev/null
+++ b/sfx2/util/sfx.component.extended
@@ -0,0 +1,7 @@
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+com.sun.star.comp.sfx2.BackingComp