From 267c6f2ac71f92999e969232431ba04678e7437e Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Mon, 15 Apr 2024 07:54:39 +0200 Subject: Adding upstream version 4:24.2.0. Signed-off-by: Daniel Baumann --- framework/AllLangMoTarget_fwk.mk | 13 + framework/CppunitTest_framework_dispatch.mk | 42 + framework/CppunitTest_framework_loadenv.mk | 43 + framework/CppunitTest_framework_services.mk | 43 + framework/IwyuFilter_framework.yaml | 121 + framework/JunitTest_framework_complex.mk | 83 + framework/JunitTest_framework_unoapi.mk | 24 + framework/Library_fwk.mk | 247 ++ framework/Makefile | 7 + framework/Module_framework.mk | 49 + framework/Package_dtd.mk | 32 + framework/README.md | 7 + framework/UIConfig_startmodule.mk | 16 + framework/dtd/accelerator.dtd | 34 + framework/dtd/event.dtd | 35 + framework/dtd/groupuinames.dtd | 26 + framework/dtd/image.dtd | 57 + framework/dtd/menubar.dtd | 39 + framework/dtd/statusbar.dtd | 39 + framework/dtd/toolbar.dtd | 53 + framework/inc/acceleratorconst.h | 32 + framework/inc/addonmenu.hxx | 80 + framework/inc/bitmaps.hlst | 39 + framework/inc/classes/actiontriggercontainer.hxx | 64 + framework/inc/classes/actiontriggerpropertyset.hxx | 113 + .../classes/actiontriggerseparatorpropertyset.hxx | 96 + framework/inc/classes/converter.hxx | 45 + framework/inc/classes/fwkresid.hxx | 27 + framework/inc/classes/protocolhandlercache.hxx | 146 + framework/inc/classes/resource.hxx | 30 + .../inc/classes/rootactiontriggercontainer.hxx | 95 + framework/inc/classes/taskcreator.hxx | 60 + framework/inc/dispatch/closedispatcher.hxx | 261 ++ .../inc/dispatch/dispatchinformationprovider.hxx | 64 + framework/inc/dispatch/dispatchprovider.hxx | 117 + framework/inc/dispatch/interceptionhelper.hxx | 256 ++ framework/inc/dispatch/mailtodispatcher.hxx | 96 + framework/inc/dispatch/oxt_handler.hxx | 90 + framework/inc/dispatch/popupmenudispatcher.hxx | 117 + framework/inc/dispatch/servicehandler.hxx | 100 + framework/inc/dispatch/startmoduledispatcher.hxx | 126 + framework/inc/dispatch/systemexec.hxx | 99 + .../inc/helper/dockingareadefaultacceptor.hxx | 68 + framework/inc/helper/mischelper.hxx | 263 ++ framework/inc/helper/ocomponentaccess.hxx | 161 + framework/inc/helper/ocomponentenumeration.hxx | 160 + framework/inc/helper/oframes.hxx | 190 + framework/inc/helper/persistentwindowstate.hxx | 166 + framework/inc/helper/propertysetcontainer.hxx | 74 + framework/inc/helper/shareablemutex.hxx | 90 + framework/inc/helper/statusindicator.hxx | 103 + framework/inc/helper/statusindicatorfactory.hxx | 270 ++ framework/inc/helper/tagwindowasmodified.hxx | 94 + framework/inc/helper/titlebarupdate.hxx | 142 + .../inc/helper/uiconfigelementwrapperbase.hxx | 141 + framework/inc/helper/uielementwrapperbase.hxx | 112 + framework/inc/helper/vclstatusindicator.hxx | 89 + framework/inc/helper/wakeupthread.hxx | 53 + framework/inc/interaction/quietinteraction.hxx | 110 + framework/inc/jobs/configaccess.hxx | 88 + framework/inc/jobs/helponstartup.hxx | 189 + framework/inc/jobs/job.hxx | 202 + framework/inc/jobs/jobconst.hxx | 44 + framework/inc/jobs/jobdata.hxx | 209 + framework/inc/jobs/jobresult.hxx | 103 + framework/inc/jobs/joburl.hxx | 108 + framework/inc/jobs/shelljob.hxx | 125 + framework/inc/menuconfiguration.hxx | 102 + framework/inc/pch/precompiled_fwk.cxx | 12 + framework/inc/pch/precompiled_fwk.hxx | 302 ++ framework/inc/properties.h | 138 + framework/inc/protocols.h | 124 + framework/inc/recording/dispatchrecorder.hxx | 90 + .../inc/recording/dispatchrecordersupplier.hxx | 83 + framework/inc/services.h | 42 + framework/inc/services/layoutmanager.hxx | 274 ++ .../inc/services/mediatypedetectionhelper.hxx | 67 + framework/inc/services/uriabbreviation.hxx | 55 + framework/inc/statusbarconfiguration.hxx | 49 + framework/inc/stdtypes.h | 57 + framework/inc/strings.hrc | 80 + framework/inc/targets.h | 38 + framework/inc/taskcreatordefs.hxx | 63 + framework/inc/threadhelp/transactionguard.hxx | 48 + framework/inc/toolboxconfiguration.hxx | 49 + framework/inc/uiconfiguration/globalsettings.hxx | 52 + .../inc/uiconfiguration/graphicnameaccess.hxx | 54 + framework/inc/uiconfiguration/imagemanager.hxx | 94 + framework/inc/uiconfiguration/imagetype.hxx | 35 + .../inc/uiconfiguration/windowstateproperties.hxx | 41 + .../inc/uielement/FixedImageToolbarController.hxx | 55 + .../inc/uielement/FixedTextToolbarController.hxx | 52 + framework/inc/uielement/addonstoolbarwrapper.hxx | 58 + .../inc/uielement/buttontoolbarcontroller.hxx | 94 + .../inc/uielement/comboboxtoolbarcontroller.hxx | 62 + framework/inc/uielement/commandinfo.hxx | 45 + .../inc/uielement/complextoolbarcontroller.hxx | 97 + framework/inc/uielement/constitemcontainer.hxx | 91 + .../inc/uielement/dropdownboxtoolbarcontroller.hxx | 63 + framework/inc/uielement/edittoolbarcontroller.hxx | 63 + framework/inc/uielement/fontmenucontroller.hxx | 63 + framework/inc/uielement/fontsizemenucontroller.hxx | 66 + framework/inc/uielement/footermenucontroller.hxx | 39 + .../inc/uielement/genericstatusbarcontroller.hxx | 57 + framework/inc/uielement/headermenucontroller.hxx | 58 + .../inc/uielement/imagebuttontoolbarcontroller.hxx | 54 + framework/inc/uielement/itemcontainer.hxx | 77 + .../inc/uielement/langselectionmenucontroller.hxx | 84 + framework/inc/uielement/macrosmenucontroller.hxx | 53 + framework/inc/uielement/menubarmanager.hxx | 194 + framework/inc/uielement/menubarmerger.hxx | 110 + framework/inc/uielement/menubarwrapper.hxx | 74 + framework/inc/uielement/newmenucontroller.hxx | 84 + framework/inc/uielement/progressbarwrapper.hxx | 82 + framework/inc/uielement/rootitemcontainer.hxx | 119 + .../inc/uielement/spinfieldtoolbarcontroller.hxx | 73 + framework/inc/uielement/statusbar.hxx | 52 + framework/inc/uielement/statusbaritem.hxx | 76 + framework/inc/uielement/statusbarmanager.hxx | 104 + framework/inc/uielement/statusbarmerger.hxx | 75 + framework/inc/uielement/statusbarwrapper.hxx | 57 + .../uielement/statusindicatorinterfacewrapper.hxx | 53 + framework/inc/uielement/styletoolbarcontroller.hxx | 78 + .../uielement/togglebuttontoolbarcontroller.hxx | 76 + framework/inc/uielement/toolbarmanager.hxx | 255 ++ framework/inc/uielement/toolbarmerger.hxx | 138 + .../inc/uielement/toolbarmodemenucontroller.hxx | 59 + framework/inc/uielement/toolbarsmenucontroller.hxx | 89 + framework/inc/uielement/toolbarwrapper.hxx | 90 + framework/inc/uielement/uicommanddescription.hxx | 95 + framework/inc/uielement/uielement.hxx | 106 + framework/inc/uielement/uielementtypenames.hxx | 30 + .../configurationaccessfactorymanager.hxx | 83 + framework/inc/uifactory/factoryconfiguration.hxx | 96 + framework/inc/uifactory/menubarfactory.hxx | 73 + .../inc/xml/acceleratorconfigurationreader.hxx | 133 + .../inc/xml/acceleratorconfigurationwriter.hxx | 78 + framework/inc/xml/imagesconfiguration.hxx | 54 + framework/inc/xml/imagesdocumenthandler.hxx | 132 + framework/inc/xml/menudocumenthandler.hxx | 221 + framework/inc/xml/saxnamespacefilter.hxx | 74 + framework/inc/xml/statusbardocumenthandler.hxx | 133 + framework/inc/xml/toolboxconfigurationdefines.hxx | 65 + framework/inc/xml/toolboxdocumenthandler.hxx | 149 + framework/inc/xml/xmlnamespaces.hxx | 52 + .../complex/ModuleManager/CheckXModuleManager.java | 330 ++ framework/qa/complex/XTitle/CheckXTitle.java | 325 ++ .../AcceleratorsConfigurationTest.java | 789 ++++ framework/qa/complex/accelerators/KeyMapping.java | 143 + framework/qa/complex/api_internal/CheckAPI.java | 180 + framework/qa/complex/api_internal/api.lst | 249 ++ framework/qa/complex/api_internal/tests.sce | 2 + framework/qa/complex/api_internal/worksforme.sce | 54 + .../qa/complex/broken_document/LoadDocument.java | 108 + .../qa/complex/broken_document/TestDocument.java | 33 + .../broken_document/test_documents/dbf.dbf.emf | 1 + .../CheckContextMenuInterceptor.java | 304 ++ .../ContextMenuInterceptor.java | 132 + .../complex/contextMenuInterceptor/space-metal.jpg | Bin 0 -> 4313 bytes framework/qa/complex/desktop/DesktopTerminate.java | 144 + framework/qa/complex/dispatches/Interceptor.java | 338 ++ .../qa/complex/dispatches/checkdispatchapi.java | 401 ++ .../disposing/GetServiceWhileDisposingOffice.java | 82 + .../qa/complex/framework/autosave/AutoSave.java | 451 ++ .../complex/framework/autosave/ConfigHelper.java | 94 + .../qa/complex/framework/autosave/Protocol.java | 890 ++++ .../qa/complex/framework/recovery/CrashThread.java | 72 + .../framework/recovery/KlickButtonThread.java | 46 + .../complex/framework/recovery/RecoveryTest.java | 611 +++ .../complex/framework/recovery/RecoveryTools.java | 301 ++ .../qa/complex/imageManager/CheckImageManager.java | 201 + framework/qa/complex/imageManager/_XComponent.java | 167 + .../qa/complex/imageManager/_XImageManager.java | 105 + .../qa/complex/imageManager/_XInitialization.java | 78 + .../qa/complex/imageManager/_XTypeProvider.java | 89 + .../qa/complex/imageManager/_XUIConfiguration.java | 66 + .../imageManager/_XUIConfigurationPersistence.java | 84 + .../loadAllDocuments/CheckXComponentLoader.java | 526 +++ .../loadAllDocuments/CheckXComponentLoader.props | 1 + .../loadAllDocuments/InteractionHandler.java | 122 + .../complex/loadAllDocuments/StatusIndicator.java | 145 + .../loadAllDocuments/testdocuments/Calc_6.sxc | Bin 0 -> 9547 bytes .../loadAllDocuments/testdocuments/Writer6.sxw | Bin 0 -> 5754 bytes .../loadAllDocuments/testdocuments/draw1.sxd | Bin 0 -> 11821 bytes .../loadAllDocuments/testdocuments/imp1.sxi | Bin 0 -> 35135 bytes .../testdocuments/password_check.sxw | Bin 0 -> 5128 bytes .../complex/loadAllDocuments/testdocuments/pic.gif | Bin 0 -> 1433 bytes .../complex/loadAllDocuments/testdocuments/pic.jpg | Bin 0 -> 2651 bytes .../qa/complex/path_settings/PathSettingsTest.java | 721 ++++ .../path_substitution/PathSubstitutionTest.java | 294 ++ framework/qa/cppunit/data/double-loading.odt | Bin 0 -> 13729 bytes framework/qa/cppunit/data/empty.fodp | 2 + framework/qa/cppunit/dispatchtest.cxx | 204 + framework/qa/cppunit/loadenv.cxx | 78 + framework/qa/cppunit/services.cxx | 138 + framework/qa/unoapi/framework.sce | 51 + framework/qa/unoapi/knownissues.xcl | 77 + framework/qa/unoapi/testdocuments/Calc_Link.sxc | Bin 0 -> 5410 bytes framework/qa/unoapi/testdocuments/Writer_link.sxw | Bin 0 -> 5188 bytes .../qa/unoapi/testdocuments/XTypeDetection.sxw | Bin 0 -> 4995 bytes framework/qa/unoapi/testdocuments/delete.cfg | Bin 0 -> 2799 bytes framework/source/accelerators/acceleratorcache.cxx | 123 + .../accelerators/acceleratorconfiguration.cxx | 1325 ++++++ .../documentacceleratorconfiguration.cxx | 196 + .../globalacceleratorconfiguration.cxx | 124 + framework/source/accelerators/keymapping.cxx | 211 + .../moduleacceleratorconfiguration.cxx | 157 + framework/source/accelerators/presethandler.cxx | 731 ++++ framework/source/accelerators/storageholder.cxx | 447 ++ framework/source/classes/framecontainer.cxx | 293 ++ framework/source/classes/taskcreator.cxx | 89 + framework/source/dispatch/closedispatcher.cxx | 615 +++ framework/source/dispatch/dispatchdisabler.cxx | 170 + .../dispatch/dispatchinformationprovider.cxx | 130 + framework/source/dispatch/dispatchprovider.cxx | 585 +++ framework/source/dispatch/interceptionhelper.cxx | 271 ++ .../source/dispatch/isstartmoduledispatch.hxx | 32 + framework/source/dispatch/loaddispatcher.cxx | 148 + framework/source/dispatch/mailtodispatcher.cxx | 233 ++ framework/source/dispatch/oxt_handler.cxx | 175 + framework/source/dispatch/popupmenudispatcher.cxx | 269 ++ framework/source/dispatch/servicehandler.cxx | 260 ++ .../source/dispatch/startmoduledispatcher.cxx | 147 + framework/source/dispatch/systemexec.cxx | 156 + .../source/dispatch/windowcommanddispatch.cxx | 158 + .../source/fwe/classes/actiontriggercontainer.cxx | 134 + .../fwe/classes/actiontriggerpropertyset.cxx | 371 ++ .../classes/actiontriggerseparatorpropertyset.cxx | 245 ++ framework/source/fwe/classes/addonmenu.cxx | 299 ++ framework/source/fwe/classes/addonsoptions.cxx | 1952 +++++++++ framework/source/fwe/classes/framelistanalyzer.cxx | 260 ++ framework/source/fwe/classes/fwkresid.cxx | 24 + .../fwe/classes/rootactiontriggercontainer.cxx | 237 ++ .../source/fwe/classes/sfxhelperfunctions.cxx | 158 + framework/source/fwe/dispatch/interaction.cxx | 225 + .../source/fwe/helper/actiontriggerhelper.cxx | 376 ++ framework/source/fwe/helper/configimporter.cxx | 74 + framework/source/fwe/helper/documentundoguard.cxx | 195 + .../source/fwe/helper/propertysetcontainer.cxx | 160 + framework/source/fwe/helper/titlehelper.cxx | 684 +++ framework/source/fwe/helper/undomanagerhelper.cxx | 1094 +++++ framework/source/fwe/xml/menuconfiguration.cxx | 156 + framework/source/fwe/xml/menudocumenthandler.cxx | 885 ++++ framework/source/fwe/xml/saxnamespacefilter.cxx | 163 + .../source/fwe/xml/statusbarconfiguration.cxx | 105 + .../source/fwe/xml/statusbardocumenthandler.cxx | 616 +++ framework/source/fwe/xml/toolboxconfiguration.cxx | 106 + .../source/fwe/xml/toolboxdocumenthandler.cxx | 713 ++++ framework/source/fwe/xml/xmlnamespaces.cxx | 152 + framework/source/fwi/classes/converter.cxx | 117 + .../source/fwi/classes/protocolhandlercache.cxx | 257 ++ framework/source/fwi/helper/mischelper.cxx | 154 + framework/source/fwi/helper/shareablemutex.cxx | 49 + framework/source/fwi/jobs/configaccess.cxx | 184 + .../source/fwi/threadhelp/transactionmanager.cxx | 219 + .../source/fwi/uielement/constitemcontainer.cxx | 276 ++ framework/source/fwi/uielement/itemcontainer.cxx | 206 + .../source/fwi/uielement/rootitemcontainer.cxx | 301 ++ .../source/helper/dockingareadefaultacceptor.cxx | 129 + framework/source/helper/ocomponentaccess.cxx | 167 + framework/source/helper/ocomponentenumeration.cxx | 117 + framework/source/helper/oframes.cxx | 369 ++ framework/source/helper/persistentwindowstate.cxx | 265 ++ framework/source/helper/statusindicator.cxx | 128 + framework/source/helper/statusindicatorfactory.cxx | 577 +++ framework/source/helper/tagwindowasmodified.cxx | 146 + framework/source/helper/titlebarupdate.cxx | 316 ++ .../source/helper/uiconfigelementwrapperbase.cxx | 481 +++ framework/source/helper/uielementwrapperbase.cxx | 203 + framework/source/helper/vclstatusindicator.cxx | 140 + framework/source/helper/wakeupthread.cxx | 60 + .../source/inc/accelerators/acceleratorcache.hxx | 115 + .../inc/accelerators/acceleratorconfiguration.hxx | 312 ++ framework/source/inc/accelerators/keymapping.hxx | 126 + .../source/inc/accelerators/presethandler.hxx | 378 ++ .../source/inc/accelerators/storageholder.hxx | 182 + framework/source/inc/dispatch/dispatchdisabler.hxx | 96 + framework/source/inc/dispatch/loaddispatcher.hxx | 120 + .../source/inc/dispatch/windowcommanddispatch.hxx | 109 + framework/source/inc/loadenv/actionlockguard.hxx | 141 + framework/source/inc/loadenv/loadenv.hxx | 551 +++ framework/source/inc/loadenv/loadenvexception.hxx | 87 + framework/source/inc/loadenv/targethelper.hxx | 90 + framework/source/inc/pattern/frame.hxx | 79 + framework/source/inc/pattern/window.hxx | 62 + framework/source/interaction/quietinteraction.cxx | 129 + framework/source/jobs/helponstartup.cxx | 336 ++ framework/source/jobs/job.cxx | 857 ++++ framework/source/jobs/jobdata.cxx | 545 +++ framework/source/jobs/jobdispatch.cxx | 474 +++ framework/source/jobs/jobexecutor.cxx | 385 ++ framework/source/jobs/jobresult.cxx | 179 + framework/source/jobs/joburl.cxx | 247 ++ framework/source/jobs/shelljob.cxx | 168 + framework/source/layoutmanager/helpers.cxx | 341 ++ framework/source/layoutmanager/helpers.hxx | 67 + framework/source/layoutmanager/layoutmanager.cxx | 3070 ++++++++++++++ .../source/layoutmanager/toolbarlayoutmanager.cxx | 4130 +++++++++++++++++++ .../source/layoutmanager/toolbarlayoutmanager.hxx | 285 ++ framework/source/layoutmanager/uielement.cxx | 99 + framework/source/loadenv/loadenv.cxx | 1819 ++++++++ framework/source/loadenv/targethelper.cxx | 64 + framework/source/recording/dispatchrecorder.cxx | 435 ++ .../source/recording/dispatchrecordersupplier.cxx | 161 + .../services/ContextChangeEventMultiplexer.cxx | 367 ++ framework/source/services/autorecovery.cxx | 4342 ++++++++++++++++++++ framework/source/services/desktop.cxx | 1772 ++++++++ framework/source/services/dispatchhelper.cxx | 218 + framework/source/services/frame.cxx | 3337 +++++++++++++++ .../source/services/mediatypedetectionhelper.cxx | 92 + framework/source/services/modulemanager.cxx | 355 ++ framework/source/services/pathsettings.cxx | 1421 +++++++ framework/source/services/sessionlistener.cxx | 414 ++ framework/source/services/substitutepathvars.cxx | 695 ++++ framework/source/services/taskcreatorsrv.cxx | 357 ++ framework/source/services/uriabbreviation.cxx | 75 + framework/source/services/urltransformer.cxx | 307 ++ .../uiconfiguration/CommandImageResolver.cxx | 152 + .../uiconfiguration/CommandImageResolver.hxx | 53 + framework/source/uiconfiguration/ImageList.cxx | 201 + framework/source/uiconfiguration/ImageList.hxx | 75 + .../source/uiconfiguration/globalsettings.cxx | 262 ++ .../source/uiconfiguration/graphicnameaccess.cxx | 80 + framework/source/uiconfiguration/imagemanager.cxx | 173 + .../source/uiconfiguration/imagemanagerimpl.cxx | 1210 ++++++ .../source/uiconfiguration/imagemanagerimpl.hxx | 182 + .../source/uiconfiguration/moduleuicfgsupplier.cxx | 180 + .../moduleuiconfigurationmanager.cxx | 1660 ++++++++ .../uiconfiguration/uicategorydescription.cxx | 395 ++ .../uiconfiguration/uiconfigurationmanager.cxx | 1382 +++++++ .../uiconfiguration/windowstateconfiguration.cxx | 1391 +++++++ .../uielement/FixedImageToolbarController.cxx | 138 + .../uielement/FixedTextToolbarController.cxx | 127 + .../source/uielement/addonstoolbarwrapper.cxx | 163 + .../source/uielement/buttontoolbarcontroller.cxx | 273 ++ .../source/uielement/comboboxtoolbarcontroller.cxx | 333 ++ .../source/uielement/complextoolbarcontroller.cxx | 326 ++ .../source/uielement/controlmenucontroller.cxx | 329 ++ .../uielement/dropdownboxtoolbarcontroller.cxx | 282 ++ .../source/uielement/edittoolbarcontroller.cxx | 217 + framework/source/uielement/fontmenucontroller.cxx | 219 + .../source/uielement/fontsizemenucontroller.cxx | 286 ++ .../source/uielement/footermenucontroller.cxx | 73 + .../uielement/genericstatusbarcontroller.cxx | 157 + .../source/uielement/generictoolbarcontroller.cxx | 434 ++ .../source/uielement/headermenucontroller.cxx | 237 ++ .../uielement/imagebuttontoolbarcontroller.cxx | 148 + .../uielement/langselectionmenucontroller.cxx | 293 ++ .../uielement/langselectionstatusbarcontroller.cxx | 362 ++ .../source/uielement/macrosmenucontroller.cxx | 171 + framework/source/uielement/menubarmanager.cxx | 1592 +++++++ framework/source/uielement/menubarmerger.cxx | 430 ++ framework/source/uielement/menubarwrapper.cxx | 285 ++ framework/source/uielement/newmenucontroller.cxx | 466 +++ .../source/uielement/objectmenucontroller.cxx | 137 + .../source/uielement/popuptoolbarcontroller.cxx | 794 ++++ framework/source/uielement/progressbarwrapper.cxx | 310 ++ .../source/uielement/recentfilesmenucontroller.cxx | 479 +++ .../source/uielement/resourcemenucontroller.cxx | 581 +++ .../uielement/spinfieldtoolbarcontroller.cxx | 452 ++ framework/source/uielement/statusbar.cxx | 89 + framework/source/uielement/statusbaritem.cxx | 234 ++ framework/source/uielement/statusbarmanager.cxx | 656 +++ framework/source/uielement/statusbarmerger.cxx | 237 ++ framework/source/uielement/statusbarwrapper.cxx | 165 + .../uielement/statusindicatorinterfacewrapper.cxx | 102 + .../source/uielement/styletoolbarcontroller.cxx | 244 ++ .../source/uielement/subtoolbarcontroller.cxx | 547 +++ .../source/uielement/thesaurusmenucontroller.cxx | 186 + .../uielement/togglebuttontoolbarcontroller.cxx | 270 ++ framework/source/uielement/toolbarmanager.cxx | 2340 +++++++++++ framework/source/uielement/toolbarmerger.cxx | 648 +++ .../source/uielement/toolbarmodemenucontroller.cxx | 296 ++ .../source/uielement/toolbarsmenucontroller.cxx | 791 ++++ framework/source/uielement/toolbarwrapper.cxx | 363 ++ .../source/uielement/uicommanddescription.cxx | 725 ++++ .../source/uifactory/addonstoolbarfactory.cxx | 206 + .../source/uifactory/factoryconfiguration.cxx | 289 ++ framework/source/uifactory/menubarfactory.cxx | 172 + framework/source/uifactory/statusbarfactory.cxx | 84 + framework/source/uifactory/toolbarfactory.cxx | 84 + framework/source/uifactory/uicontrollerfactory.cxx | 341 ++ .../source/uifactory/uielementfactorymanager.cxx | 553 +++ .../uifactory/windowcontentfactorymanager.cxx | 204 + .../source/xml/acceleratorconfigurationreader.cxx | 258 ++ .../source/xml/acceleratorconfigurationwriter.cxx | 123 + framework/source/xml/imagesconfiguration.cxx | 104 + framework/source/xml/imagesdocumenthandler.cxx | 366 ++ framework/uiconfig/startmodule/menubar/menubar.xml | 78 + framework/util/fwk.component | 325 ++ framework/util/fwk.component.autorecovery | 7 + 391 files changed, 102296 insertions(+) create mode 100644 framework/AllLangMoTarget_fwk.mk create mode 100644 framework/CppunitTest_framework_dispatch.mk create mode 100644 framework/CppunitTest_framework_loadenv.mk create mode 100644 framework/CppunitTest_framework_services.mk create mode 100644 framework/IwyuFilter_framework.yaml create mode 100644 framework/JunitTest_framework_complex.mk create mode 100644 framework/JunitTest_framework_unoapi.mk create mode 100644 framework/Library_fwk.mk create mode 100644 framework/Makefile create mode 100644 framework/Module_framework.mk create mode 100644 framework/Package_dtd.mk create mode 100644 framework/README.md create mode 100644 framework/UIConfig_startmodule.mk create mode 100644 framework/dtd/accelerator.dtd create mode 100644 framework/dtd/event.dtd create mode 100644 framework/dtd/groupuinames.dtd create mode 100644 framework/dtd/image.dtd create mode 100644 framework/dtd/menubar.dtd create mode 100644 framework/dtd/statusbar.dtd create mode 100644 framework/dtd/toolbar.dtd create mode 100644 framework/inc/acceleratorconst.h create mode 100644 framework/inc/addonmenu.hxx create mode 100644 framework/inc/bitmaps.hlst create mode 100644 framework/inc/classes/actiontriggercontainer.hxx create mode 100644 framework/inc/classes/actiontriggerpropertyset.hxx create mode 100644 framework/inc/classes/actiontriggerseparatorpropertyset.hxx create mode 100644 framework/inc/classes/converter.hxx create mode 100644 framework/inc/classes/fwkresid.hxx create mode 100644 framework/inc/classes/protocolhandlercache.hxx create mode 100644 framework/inc/classes/resource.hxx create mode 100644 framework/inc/classes/rootactiontriggercontainer.hxx create mode 100644 framework/inc/classes/taskcreator.hxx create mode 100644 framework/inc/dispatch/closedispatcher.hxx create mode 100644 framework/inc/dispatch/dispatchinformationprovider.hxx create mode 100644 framework/inc/dispatch/dispatchprovider.hxx create mode 100644 framework/inc/dispatch/interceptionhelper.hxx create mode 100644 framework/inc/dispatch/mailtodispatcher.hxx create mode 100644 framework/inc/dispatch/oxt_handler.hxx create mode 100644 framework/inc/dispatch/popupmenudispatcher.hxx create mode 100644 framework/inc/dispatch/servicehandler.hxx create mode 100644 framework/inc/dispatch/startmoduledispatcher.hxx create mode 100644 framework/inc/dispatch/systemexec.hxx create mode 100644 framework/inc/helper/dockingareadefaultacceptor.hxx create mode 100644 framework/inc/helper/mischelper.hxx create mode 100644 framework/inc/helper/ocomponentaccess.hxx create mode 100644 framework/inc/helper/ocomponentenumeration.hxx create mode 100644 framework/inc/helper/oframes.hxx create mode 100644 framework/inc/helper/persistentwindowstate.hxx create mode 100644 framework/inc/helper/propertysetcontainer.hxx create mode 100644 framework/inc/helper/shareablemutex.hxx create mode 100644 framework/inc/helper/statusindicator.hxx create mode 100644 framework/inc/helper/statusindicatorfactory.hxx create mode 100644 framework/inc/helper/tagwindowasmodified.hxx create mode 100644 framework/inc/helper/titlebarupdate.hxx create mode 100644 framework/inc/helper/uiconfigelementwrapperbase.hxx create mode 100644 framework/inc/helper/uielementwrapperbase.hxx create mode 100644 framework/inc/helper/vclstatusindicator.hxx create mode 100644 framework/inc/helper/wakeupthread.hxx create mode 100644 framework/inc/interaction/quietinteraction.hxx create mode 100644 framework/inc/jobs/configaccess.hxx create mode 100644 framework/inc/jobs/helponstartup.hxx create mode 100644 framework/inc/jobs/job.hxx create mode 100644 framework/inc/jobs/jobconst.hxx create mode 100644 framework/inc/jobs/jobdata.hxx create mode 100644 framework/inc/jobs/jobresult.hxx create mode 100644 framework/inc/jobs/joburl.hxx create mode 100644 framework/inc/jobs/shelljob.hxx create mode 100644 framework/inc/menuconfiguration.hxx create mode 100644 framework/inc/pch/precompiled_fwk.cxx create mode 100644 framework/inc/pch/precompiled_fwk.hxx create mode 100644 framework/inc/properties.h create mode 100644 framework/inc/protocols.h create mode 100644 framework/inc/recording/dispatchrecorder.hxx create mode 100644 framework/inc/recording/dispatchrecordersupplier.hxx create mode 100644 framework/inc/services.h create mode 100644 framework/inc/services/layoutmanager.hxx create mode 100644 framework/inc/services/mediatypedetectionhelper.hxx create mode 100644 framework/inc/services/uriabbreviation.hxx create mode 100644 framework/inc/statusbarconfiguration.hxx create mode 100644 framework/inc/stdtypes.h create mode 100644 framework/inc/strings.hrc create mode 100644 framework/inc/targets.h create mode 100644 framework/inc/taskcreatordefs.hxx create mode 100644 framework/inc/threadhelp/transactionguard.hxx create mode 100644 framework/inc/toolboxconfiguration.hxx create mode 100644 framework/inc/uiconfiguration/globalsettings.hxx create mode 100644 framework/inc/uiconfiguration/graphicnameaccess.hxx create mode 100644 framework/inc/uiconfiguration/imagemanager.hxx create mode 100644 framework/inc/uiconfiguration/imagetype.hxx create mode 100644 framework/inc/uiconfiguration/windowstateproperties.hxx create mode 100644 framework/inc/uielement/FixedImageToolbarController.hxx create mode 100644 framework/inc/uielement/FixedTextToolbarController.hxx create mode 100644 framework/inc/uielement/addonstoolbarwrapper.hxx create mode 100644 framework/inc/uielement/buttontoolbarcontroller.hxx create mode 100644 framework/inc/uielement/comboboxtoolbarcontroller.hxx create mode 100644 framework/inc/uielement/commandinfo.hxx create mode 100644 framework/inc/uielement/complextoolbarcontroller.hxx create mode 100644 framework/inc/uielement/constitemcontainer.hxx create mode 100644 framework/inc/uielement/dropdownboxtoolbarcontroller.hxx create mode 100644 framework/inc/uielement/edittoolbarcontroller.hxx create mode 100644 framework/inc/uielement/fontmenucontroller.hxx create mode 100644 framework/inc/uielement/fontsizemenucontroller.hxx create mode 100644 framework/inc/uielement/footermenucontroller.hxx create mode 100644 framework/inc/uielement/genericstatusbarcontroller.hxx create mode 100644 framework/inc/uielement/headermenucontroller.hxx create mode 100644 framework/inc/uielement/imagebuttontoolbarcontroller.hxx create mode 100644 framework/inc/uielement/itemcontainer.hxx create mode 100644 framework/inc/uielement/langselectionmenucontroller.hxx create mode 100644 framework/inc/uielement/macrosmenucontroller.hxx create mode 100644 framework/inc/uielement/menubarmanager.hxx create mode 100644 framework/inc/uielement/menubarmerger.hxx create mode 100644 framework/inc/uielement/menubarwrapper.hxx create mode 100644 framework/inc/uielement/newmenucontroller.hxx create mode 100644 framework/inc/uielement/progressbarwrapper.hxx create mode 100644 framework/inc/uielement/rootitemcontainer.hxx create mode 100644 framework/inc/uielement/spinfieldtoolbarcontroller.hxx create mode 100644 framework/inc/uielement/statusbar.hxx create mode 100644 framework/inc/uielement/statusbaritem.hxx create mode 100644 framework/inc/uielement/statusbarmanager.hxx create mode 100644 framework/inc/uielement/statusbarmerger.hxx create mode 100644 framework/inc/uielement/statusbarwrapper.hxx create mode 100644 framework/inc/uielement/statusindicatorinterfacewrapper.hxx create mode 100644 framework/inc/uielement/styletoolbarcontroller.hxx create mode 100644 framework/inc/uielement/togglebuttontoolbarcontroller.hxx create mode 100644 framework/inc/uielement/toolbarmanager.hxx create mode 100644 framework/inc/uielement/toolbarmerger.hxx create mode 100644 framework/inc/uielement/toolbarmodemenucontroller.hxx create mode 100644 framework/inc/uielement/toolbarsmenucontroller.hxx create mode 100644 framework/inc/uielement/toolbarwrapper.hxx create mode 100644 framework/inc/uielement/uicommanddescription.hxx create mode 100644 framework/inc/uielement/uielement.hxx create mode 100644 framework/inc/uielement/uielementtypenames.hxx create mode 100644 framework/inc/uifactory/configurationaccessfactorymanager.hxx create mode 100644 framework/inc/uifactory/factoryconfiguration.hxx create mode 100644 framework/inc/uifactory/menubarfactory.hxx create mode 100644 framework/inc/xml/acceleratorconfigurationreader.hxx create mode 100644 framework/inc/xml/acceleratorconfigurationwriter.hxx create mode 100644 framework/inc/xml/imagesconfiguration.hxx create mode 100644 framework/inc/xml/imagesdocumenthandler.hxx create mode 100644 framework/inc/xml/menudocumenthandler.hxx create mode 100644 framework/inc/xml/saxnamespacefilter.hxx create mode 100644 framework/inc/xml/statusbardocumenthandler.hxx create mode 100644 framework/inc/xml/toolboxconfigurationdefines.hxx create mode 100644 framework/inc/xml/toolboxdocumenthandler.hxx create mode 100644 framework/inc/xml/xmlnamespaces.hxx create mode 100644 framework/qa/complex/ModuleManager/CheckXModuleManager.java create mode 100644 framework/qa/complex/XTitle/CheckXTitle.java create mode 100644 framework/qa/complex/accelerators/AcceleratorsConfigurationTest.java create mode 100644 framework/qa/complex/accelerators/KeyMapping.java create mode 100644 framework/qa/complex/api_internal/CheckAPI.java create mode 100644 framework/qa/complex/api_internal/api.lst create mode 100644 framework/qa/complex/api_internal/tests.sce create mode 100644 framework/qa/complex/api_internal/worksforme.sce create mode 100644 framework/qa/complex/broken_document/LoadDocument.java create mode 100644 framework/qa/complex/broken_document/TestDocument.java create mode 100644 framework/qa/complex/broken_document/test_documents/dbf.dbf.emf create mode 100644 framework/qa/complex/contextMenuInterceptor/CheckContextMenuInterceptor.java create mode 100644 framework/qa/complex/contextMenuInterceptor/ContextMenuInterceptor.java create mode 100644 framework/qa/complex/contextMenuInterceptor/space-metal.jpg create mode 100644 framework/qa/complex/desktop/DesktopTerminate.java create mode 100644 framework/qa/complex/dispatches/Interceptor.java create mode 100644 framework/qa/complex/dispatches/checkdispatchapi.java create mode 100644 framework/qa/complex/disposing/GetServiceWhileDisposingOffice.java create mode 100644 framework/qa/complex/framework/autosave/AutoSave.java create mode 100644 framework/qa/complex/framework/autosave/ConfigHelper.java create mode 100644 framework/qa/complex/framework/autosave/Protocol.java create mode 100644 framework/qa/complex/framework/recovery/CrashThread.java create mode 100644 framework/qa/complex/framework/recovery/KlickButtonThread.java create mode 100644 framework/qa/complex/framework/recovery/RecoveryTest.java create mode 100644 framework/qa/complex/framework/recovery/RecoveryTools.java create mode 100644 framework/qa/complex/imageManager/CheckImageManager.java create mode 100644 framework/qa/complex/imageManager/_XComponent.java create mode 100644 framework/qa/complex/imageManager/_XImageManager.java create mode 100644 framework/qa/complex/imageManager/_XInitialization.java create mode 100644 framework/qa/complex/imageManager/_XTypeProvider.java create mode 100644 framework/qa/complex/imageManager/_XUIConfiguration.java create mode 100644 framework/qa/complex/imageManager/_XUIConfigurationPersistence.java create mode 100644 framework/qa/complex/loadAllDocuments/CheckXComponentLoader.java create mode 100644 framework/qa/complex/loadAllDocuments/CheckXComponentLoader.props create mode 100644 framework/qa/complex/loadAllDocuments/InteractionHandler.java create mode 100644 framework/qa/complex/loadAllDocuments/StatusIndicator.java create mode 100644 framework/qa/complex/loadAllDocuments/testdocuments/Calc_6.sxc create mode 100644 framework/qa/complex/loadAllDocuments/testdocuments/Writer6.sxw create mode 100644 framework/qa/complex/loadAllDocuments/testdocuments/draw1.sxd create mode 100644 framework/qa/complex/loadAllDocuments/testdocuments/imp1.sxi create mode 100644 framework/qa/complex/loadAllDocuments/testdocuments/password_check.sxw create mode 100644 framework/qa/complex/loadAllDocuments/testdocuments/pic.gif create mode 100644 framework/qa/complex/loadAllDocuments/testdocuments/pic.jpg create mode 100644 framework/qa/complex/path_settings/PathSettingsTest.java create mode 100644 framework/qa/complex/path_substitution/PathSubstitutionTest.java create mode 100644 framework/qa/cppunit/data/double-loading.odt create mode 100644 framework/qa/cppunit/data/empty.fodp create mode 100644 framework/qa/cppunit/dispatchtest.cxx create mode 100644 framework/qa/cppunit/loadenv.cxx create mode 100644 framework/qa/cppunit/services.cxx create mode 100644 framework/qa/unoapi/framework.sce create mode 100644 framework/qa/unoapi/knownissues.xcl create mode 100644 framework/qa/unoapi/testdocuments/Calc_Link.sxc create mode 100644 framework/qa/unoapi/testdocuments/Writer_link.sxw create mode 100644 framework/qa/unoapi/testdocuments/XTypeDetection.sxw create mode 100644 framework/qa/unoapi/testdocuments/delete.cfg create mode 100644 framework/source/accelerators/acceleratorcache.cxx create mode 100644 framework/source/accelerators/acceleratorconfiguration.cxx create mode 100644 framework/source/accelerators/documentacceleratorconfiguration.cxx create mode 100644 framework/source/accelerators/globalacceleratorconfiguration.cxx create mode 100644 framework/source/accelerators/keymapping.cxx create mode 100644 framework/source/accelerators/moduleacceleratorconfiguration.cxx create mode 100644 framework/source/accelerators/presethandler.cxx create mode 100644 framework/source/accelerators/storageholder.cxx create mode 100644 framework/source/classes/framecontainer.cxx create mode 100644 framework/source/classes/taskcreator.cxx create mode 100644 framework/source/dispatch/closedispatcher.cxx create mode 100644 framework/source/dispatch/dispatchdisabler.cxx create mode 100644 framework/source/dispatch/dispatchinformationprovider.cxx create mode 100644 framework/source/dispatch/dispatchprovider.cxx create mode 100644 framework/source/dispatch/interceptionhelper.cxx create mode 100644 framework/source/dispatch/isstartmoduledispatch.hxx create mode 100644 framework/source/dispatch/loaddispatcher.cxx create mode 100644 framework/source/dispatch/mailtodispatcher.cxx create mode 100644 framework/source/dispatch/oxt_handler.cxx create mode 100644 framework/source/dispatch/popupmenudispatcher.cxx create mode 100644 framework/source/dispatch/servicehandler.cxx create mode 100644 framework/source/dispatch/startmoduledispatcher.cxx create mode 100644 framework/source/dispatch/systemexec.cxx create mode 100644 framework/source/dispatch/windowcommanddispatch.cxx create mode 100644 framework/source/fwe/classes/actiontriggercontainer.cxx create mode 100644 framework/source/fwe/classes/actiontriggerpropertyset.cxx create mode 100644 framework/source/fwe/classes/actiontriggerseparatorpropertyset.cxx create mode 100644 framework/source/fwe/classes/addonmenu.cxx create mode 100644 framework/source/fwe/classes/addonsoptions.cxx create mode 100644 framework/source/fwe/classes/framelistanalyzer.cxx create mode 100644 framework/source/fwe/classes/fwkresid.cxx create mode 100644 framework/source/fwe/classes/rootactiontriggercontainer.cxx create mode 100644 framework/source/fwe/classes/sfxhelperfunctions.cxx create mode 100644 framework/source/fwe/dispatch/interaction.cxx create mode 100644 framework/source/fwe/helper/actiontriggerhelper.cxx create mode 100644 framework/source/fwe/helper/configimporter.cxx create mode 100644 framework/source/fwe/helper/documentundoguard.cxx create mode 100644 framework/source/fwe/helper/propertysetcontainer.cxx create mode 100644 framework/source/fwe/helper/titlehelper.cxx create mode 100644 framework/source/fwe/helper/undomanagerhelper.cxx create mode 100644 framework/source/fwe/xml/menuconfiguration.cxx create mode 100644 framework/source/fwe/xml/menudocumenthandler.cxx create mode 100644 framework/source/fwe/xml/saxnamespacefilter.cxx create mode 100644 framework/source/fwe/xml/statusbarconfiguration.cxx create mode 100644 framework/source/fwe/xml/statusbardocumenthandler.cxx create mode 100644 framework/source/fwe/xml/toolboxconfiguration.cxx create mode 100644 framework/source/fwe/xml/toolboxdocumenthandler.cxx create mode 100644 framework/source/fwe/xml/xmlnamespaces.cxx create mode 100644 framework/source/fwi/classes/converter.cxx create mode 100644 framework/source/fwi/classes/protocolhandlercache.cxx create mode 100644 framework/source/fwi/helper/mischelper.cxx create mode 100644 framework/source/fwi/helper/shareablemutex.cxx create mode 100644 framework/source/fwi/jobs/configaccess.cxx create mode 100644 framework/source/fwi/threadhelp/transactionmanager.cxx create mode 100644 framework/source/fwi/uielement/constitemcontainer.cxx create mode 100644 framework/source/fwi/uielement/itemcontainer.cxx create mode 100644 framework/source/fwi/uielement/rootitemcontainer.cxx create mode 100644 framework/source/helper/dockingareadefaultacceptor.cxx create mode 100644 framework/source/helper/ocomponentaccess.cxx create mode 100644 framework/source/helper/ocomponentenumeration.cxx create mode 100644 framework/source/helper/oframes.cxx create mode 100644 framework/source/helper/persistentwindowstate.cxx create mode 100644 framework/source/helper/statusindicator.cxx create mode 100644 framework/source/helper/statusindicatorfactory.cxx create mode 100644 framework/source/helper/tagwindowasmodified.cxx create mode 100644 framework/source/helper/titlebarupdate.cxx create mode 100644 framework/source/helper/uiconfigelementwrapperbase.cxx create mode 100644 framework/source/helper/uielementwrapperbase.cxx create mode 100644 framework/source/helper/vclstatusindicator.cxx create mode 100644 framework/source/helper/wakeupthread.cxx create mode 100644 framework/source/inc/accelerators/acceleratorcache.hxx create mode 100644 framework/source/inc/accelerators/acceleratorconfiguration.hxx create mode 100644 framework/source/inc/accelerators/keymapping.hxx create mode 100644 framework/source/inc/accelerators/presethandler.hxx create mode 100644 framework/source/inc/accelerators/storageholder.hxx create mode 100644 framework/source/inc/dispatch/dispatchdisabler.hxx create mode 100644 framework/source/inc/dispatch/loaddispatcher.hxx create mode 100644 framework/source/inc/dispatch/windowcommanddispatch.hxx create mode 100644 framework/source/inc/loadenv/actionlockguard.hxx create mode 100644 framework/source/inc/loadenv/loadenv.hxx create mode 100644 framework/source/inc/loadenv/loadenvexception.hxx create mode 100644 framework/source/inc/loadenv/targethelper.hxx create mode 100644 framework/source/inc/pattern/frame.hxx create mode 100644 framework/source/inc/pattern/window.hxx create mode 100644 framework/source/interaction/quietinteraction.cxx create mode 100644 framework/source/jobs/helponstartup.cxx create mode 100644 framework/source/jobs/job.cxx create mode 100644 framework/source/jobs/jobdata.cxx create mode 100644 framework/source/jobs/jobdispatch.cxx create mode 100644 framework/source/jobs/jobexecutor.cxx create mode 100644 framework/source/jobs/jobresult.cxx create mode 100644 framework/source/jobs/joburl.cxx create mode 100644 framework/source/jobs/shelljob.cxx create mode 100644 framework/source/layoutmanager/helpers.cxx create mode 100644 framework/source/layoutmanager/helpers.hxx create mode 100644 framework/source/layoutmanager/layoutmanager.cxx create mode 100644 framework/source/layoutmanager/toolbarlayoutmanager.cxx create mode 100644 framework/source/layoutmanager/toolbarlayoutmanager.hxx create mode 100644 framework/source/layoutmanager/uielement.cxx create mode 100644 framework/source/loadenv/loadenv.cxx create mode 100644 framework/source/loadenv/targethelper.cxx create mode 100644 framework/source/recording/dispatchrecorder.cxx create mode 100644 framework/source/recording/dispatchrecordersupplier.cxx create mode 100644 framework/source/services/ContextChangeEventMultiplexer.cxx create mode 100644 framework/source/services/autorecovery.cxx create mode 100644 framework/source/services/desktop.cxx create mode 100644 framework/source/services/dispatchhelper.cxx create mode 100644 framework/source/services/frame.cxx create mode 100644 framework/source/services/mediatypedetectionhelper.cxx create mode 100644 framework/source/services/modulemanager.cxx create mode 100644 framework/source/services/pathsettings.cxx create mode 100644 framework/source/services/sessionlistener.cxx create mode 100644 framework/source/services/substitutepathvars.cxx create mode 100644 framework/source/services/taskcreatorsrv.cxx create mode 100644 framework/source/services/uriabbreviation.cxx create mode 100644 framework/source/services/urltransformer.cxx create mode 100644 framework/source/uiconfiguration/CommandImageResolver.cxx create mode 100644 framework/source/uiconfiguration/CommandImageResolver.hxx create mode 100644 framework/source/uiconfiguration/ImageList.cxx create mode 100644 framework/source/uiconfiguration/ImageList.hxx create mode 100644 framework/source/uiconfiguration/globalsettings.cxx create mode 100644 framework/source/uiconfiguration/graphicnameaccess.cxx create mode 100644 framework/source/uiconfiguration/imagemanager.cxx create mode 100644 framework/source/uiconfiguration/imagemanagerimpl.cxx create mode 100644 framework/source/uiconfiguration/imagemanagerimpl.hxx create mode 100644 framework/source/uiconfiguration/moduleuicfgsupplier.cxx create mode 100644 framework/source/uiconfiguration/moduleuiconfigurationmanager.cxx create mode 100644 framework/source/uiconfiguration/uicategorydescription.cxx create mode 100644 framework/source/uiconfiguration/uiconfigurationmanager.cxx create mode 100644 framework/source/uiconfiguration/windowstateconfiguration.cxx create mode 100644 framework/source/uielement/FixedImageToolbarController.cxx create mode 100644 framework/source/uielement/FixedTextToolbarController.cxx create mode 100644 framework/source/uielement/addonstoolbarwrapper.cxx create mode 100644 framework/source/uielement/buttontoolbarcontroller.cxx create mode 100644 framework/source/uielement/comboboxtoolbarcontroller.cxx create mode 100644 framework/source/uielement/complextoolbarcontroller.cxx create mode 100644 framework/source/uielement/controlmenucontroller.cxx create mode 100644 framework/source/uielement/dropdownboxtoolbarcontroller.cxx create mode 100644 framework/source/uielement/edittoolbarcontroller.cxx create mode 100644 framework/source/uielement/fontmenucontroller.cxx create mode 100644 framework/source/uielement/fontsizemenucontroller.cxx create mode 100644 framework/source/uielement/footermenucontroller.cxx create mode 100644 framework/source/uielement/genericstatusbarcontroller.cxx create mode 100644 framework/source/uielement/generictoolbarcontroller.cxx create mode 100644 framework/source/uielement/headermenucontroller.cxx create mode 100644 framework/source/uielement/imagebuttontoolbarcontroller.cxx create mode 100644 framework/source/uielement/langselectionmenucontroller.cxx create mode 100644 framework/source/uielement/langselectionstatusbarcontroller.cxx create mode 100644 framework/source/uielement/macrosmenucontroller.cxx create mode 100644 framework/source/uielement/menubarmanager.cxx create mode 100644 framework/source/uielement/menubarmerger.cxx create mode 100644 framework/source/uielement/menubarwrapper.cxx create mode 100644 framework/source/uielement/newmenucontroller.cxx create mode 100644 framework/source/uielement/objectmenucontroller.cxx create mode 100644 framework/source/uielement/popuptoolbarcontroller.cxx create mode 100644 framework/source/uielement/progressbarwrapper.cxx create mode 100644 framework/source/uielement/recentfilesmenucontroller.cxx create mode 100644 framework/source/uielement/resourcemenucontroller.cxx create mode 100644 framework/source/uielement/spinfieldtoolbarcontroller.cxx create mode 100644 framework/source/uielement/statusbar.cxx create mode 100644 framework/source/uielement/statusbaritem.cxx create mode 100644 framework/source/uielement/statusbarmanager.cxx create mode 100644 framework/source/uielement/statusbarmerger.cxx create mode 100644 framework/source/uielement/statusbarwrapper.cxx create mode 100644 framework/source/uielement/statusindicatorinterfacewrapper.cxx create mode 100644 framework/source/uielement/styletoolbarcontroller.cxx create mode 100644 framework/source/uielement/subtoolbarcontroller.cxx create mode 100644 framework/source/uielement/thesaurusmenucontroller.cxx create mode 100644 framework/source/uielement/togglebuttontoolbarcontroller.cxx create mode 100644 framework/source/uielement/toolbarmanager.cxx create mode 100644 framework/source/uielement/toolbarmerger.cxx create mode 100644 framework/source/uielement/toolbarmodemenucontroller.cxx create mode 100644 framework/source/uielement/toolbarsmenucontroller.cxx create mode 100644 framework/source/uielement/toolbarwrapper.cxx create mode 100644 framework/source/uielement/uicommanddescription.cxx create mode 100644 framework/source/uifactory/addonstoolbarfactory.cxx create mode 100644 framework/source/uifactory/factoryconfiguration.cxx create mode 100644 framework/source/uifactory/menubarfactory.cxx create mode 100644 framework/source/uifactory/statusbarfactory.cxx create mode 100644 framework/source/uifactory/toolbarfactory.cxx create mode 100644 framework/source/uifactory/uicontrollerfactory.cxx create mode 100644 framework/source/uifactory/uielementfactorymanager.cxx create mode 100644 framework/source/uifactory/windowcontentfactorymanager.cxx create mode 100644 framework/source/xml/acceleratorconfigurationreader.cxx create mode 100644 framework/source/xml/acceleratorconfigurationwriter.cxx create mode 100644 framework/source/xml/imagesconfiguration.cxx create mode 100644 framework/source/xml/imagesdocumenthandler.cxx create mode 100644 framework/uiconfig/startmodule/menubar/menubar.xml create mode 100644 framework/util/fwk.component create mode 100644 framework/util/fwk.component.autorecovery (limited to 'framework') diff --git a/framework/AllLangMoTarget_fwk.mk b/framework/AllLangMoTarget_fwk.mk new file mode 100644 index 0000000000..cbeff181bd --- /dev/null +++ b/framework/AllLangMoTarget_fwk.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,fwk)) + +$(eval $(call gb_AllLangMoTarget_set_polocation,fwk,framework)) + +# vim: set noet sw=4 ts=4: diff --git a/framework/CppunitTest_framework_dispatch.mk b/framework/CppunitTest_framework_dispatch.mk new file mode 100644 index 0000000000..b9571ebe3a --- /dev/null +++ b/framework/CppunitTest_framework_dispatch.mk @@ -0,0 +1,42 @@ +# -*- 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,framework_dispatch)) + +$(eval $(call gb_CppunitTest_add_exception_objects,framework_dispatch, \ + framework/qa/cppunit/dispatchtest \ +)) + +$(eval $(call gb_CppunitTest_use_sdk_api,framework_dispatch)) + +$(eval $(call gb_CppunitTest_use_libraries,framework_dispatch, \ + comphelper \ + cppu \ + cppuhelper \ + fwk \ + sal \ + subsequenttest \ + utl \ + tl \ + test \ + unotest \ +)) + +$(eval $(call gb_CppunitTest_use_external,framework_dispatch,boost_headers)) + +$(eval $(call gb_CppunitTest_use_sdk_api,framework_dispatch)) + +$(eval $(call gb_CppunitTest_use_ure,framework_dispatch)) +$(eval $(call gb_CppunitTest_use_vcl,framework_dispatch)) + +$(eval $(call gb_CppunitTest_use_rdb,framework_dispatch,services)) + +$(eval $(call gb_CppunitTest_use_configuration,framework_dispatch)) + +# vim: set noet sw=4 ts=4: diff --git a/framework/CppunitTest_framework_loadenv.mk b/framework/CppunitTest_framework_loadenv.mk new file mode 100644 index 0000000000..4f5f4b8e83 --- /dev/null +++ b/framework/CppunitTest_framework_loadenv.mk @@ -0,0 +1,43 @@ +# -*- 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,framework_loadenv)) + +$(eval $(call gb_CppunitTest_add_exception_objects,framework_loadenv, \ + framework/qa/cppunit/loadenv \ +)) + +$(eval $(call gb_CppunitTest_use_sdk_api,framework_loadenv)) + +$(eval $(call gb_CppunitTest_use_libraries,framework_loadenv, \ + comphelper \ + cppu \ + cppuhelper \ + sal \ + salhelper \ + subsequenttest \ + test \ + unotest \ + utl \ + tl \ + vcl \ +)) + +$(eval $(call gb_CppunitTest_use_external,framework_loadenv,boost_headers)) + +$(eval $(call gb_CppunitTest_use_sdk_api,framework_loadenv)) + +$(eval $(call gb_CppunitTest_use_ure,framework_loadenv)) +$(eval $(call gb_CppunitTest_use_vcl,framework_loadenv)) + +$(eval $(call gb_CppunitTest_use_rdb,framework_loadenv,services)) + +$(eval $(call gb_CppunitTest_use_configuration,framework_loadenv)) + +# vim: set noet sw=4 ts=4: diff --git a/framework/CppunitTest_framework_services.mk b/framework/CppunitTest_framework_services.mk new file mode 100644 index 0000000000..2e6e4a50a3 --- /dev/null +++ b/framework/CppunitTest_framework_services.mk @@ -0,0 +1,43 @@ +# -*- 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,framework_services)) + +$(eval $(call gb_CppunitTest_add_exception_objects,framework_services, \ + framework/qa/cppunit/services \ +)) + +$(eval $(call gb_CppunitTest_use_sdk_api,framework_services)) + +$(eval $(call gb_CppunitTest_use_libraries,framework_services, \ + comphelper \ + cppu \ + cppuhelper \ + sal \ + salhelper \ + subsequenttest \ + test \ + unotest \ + utl \ + tl \ + vcl \ +)) + +$(eval $(call gb_CppunitTest_use_external,framework_services,boost_headers)) + +$(eval $(call gb_CppunitTest_use_sdk_api,framework_services)) + +$(eval $(call gb_CppunitTest_use_ure,framework_services)) +$(eval $(call gb_CppunitTest_use_vcl,framework_services)) + +$(eval $(call gb_CppunitTest_use_rdb,framework_services,services)) + +$(eval $(call gb_CppunitTest_use_configuration,framework_services)) + +# vim: set noet sw=4 ts=4: diff --git a/framework/IwyuFilter_framework.yaml b/framework/IwyuFilter_framework.yaml new file mode 100644 index 0000000000..3d69d43151 --- /dev/null +++ b/framework/IwyuFilter_framework.yaml @@ -0,0 +1,121 @@ +--- +assumeFilename: framework/source/services/autorecovery.cxx +excludelist: + framework/inc/stdtypes.h: + # Don't propose hxx -> h change in URE libs + - cppuhelper/interfacecontainer.hxx + framework/inc/services/layoutmanager.hxx: + # Don't propose hxx -> h change in URE libs + - cppuhelper/interfacecontainer.hxx + # Don't propose hxx -> h change in URE libs + - cppuhelper/interfacecontainer.hxx + framework/inc/uielement/statusbarmanager.hxx: + # Don't propose hxx -> h change in URE libs + - cppuhelper/interfacecontainer.hxx + framework/inc/uielement/toolbarmanager.hxx: + # Don't propose hxx -> h change in URE libs + - cppuhelper/interfacecontainer.hxx + framework/inc/statusbarconfiguration.hxx: + # Needed for css shortcut + - sal/types.h + framework/inc/toolboxconfiguration.hxx: + # Needed for css shortcut + - sal/types.h + framework/source/uiconfiguration/imagemanagerimpl.hxx: + # Don't propose hxx -> h change in URE libs + - cppuhelper/interfacecontainer.hxx + framework/source/dispatch/closedispatcher.cxx: + # Needed on MAC + - com/sun/star/beans/XFastPropertySet.hpp + - services.h + framework/source/dispatch/servicehandler.cxx: + # Actually used + - com/sun/star/lang/XMultiServiceFactory.hpp + framework/source/fwe/classes/framelistanalyzer.cxx: + # Actually used + - com/sun/star/frame/XFrame.hpp + framework/source/fwe/classes/addonmenu.cxx: + # Actually used + - com/sun/star/uno/Reference.hxx + - com/sun/star/beans/PropertyValue.hpp + framework/source/fwe/helper/configimporter.cxx: + # Actually used + - com/sun/star/ui/XUIConfigurationManager3.hpp + framework/source/fwe/helper/undomanagerhelper.cxx: + # Actually used + - com/sun/star/document/XUndoManager.hpp + framework/source/fwe/xml/toolboxconfiguration.cxx: + # Actually used + - com/sun/star/io/XInputStream.hpp + framework/source/fwe/xml/statusbarconfiguration.cxx: + # Actually used + - com/sun/star/io/XInputStream.hpp + framework/source/helper/persistentwindowstate.cxx: + # Actually used + - com/sun/star/awt/XWindow.hpp + framework/source/layoutmanager/uielement.cxx: + # Actually used + - com/sun/star/ui/DockingArea.hpp + framework/source/layoutmanager/helpers.cxx: + # Actually used + - com/sun/star/ui/DockingArea.hpp + - com/sun/star/ui/XUIElement.hpp + framework/source/services/substitutepathvars.cxx: + # Don't propose hxx -> h change in URE libs + - osl/thread.hxx + # Needed for template + - com/sun/star/container/XNameAccess.hpp + framework/source/uifactory/menubarfactory.cxx: + # Actually used + - com/sun/star/uno/XComponentContext.hpp + framework/source/xml/imagesconfiguration.cxx: + # Actually used + - com/sun/star/io/XInputStream.hpp + framework/source/uielement/genericstatusbarcontroller.cxx: + # Actually used + - com/sun/star/ui/XStatusbarItem.hpp + framework/source/uielement/complextoolbarcontroller.cxx: + # Actually used + - com/sun/star/util/XURLTransformer.hpp + - com/sun/star/beans/PropertyValue.hpp + - com/sun/star/frame/XFrame.hpp + framework/source/uielement/generictoolbarcontroller.cxx: + # Actually used + - com/sun/star/beans/PropertyValue.hpp + framework/source/uielement/dropdownboxtoolbarcontroller.cxx: + # Actually used + - com/sun/star/beans/PropertyValue.hpp + framework/source/uielement/comboboxtoolbarcontroller.cxx: + # Actually used + - com/sun/star/beans/PropertyValue.hpp + framework/source/uielement/controlmenucontroller.cxx: + # Needed for template + - com/sun/star/frame/XFrame.hpp + framework/source/uielement/edittoolbarcontroller.cxx: + # Actually used + - com/sun/star/beans/PropertyValue.hpp + framework/source/uielement/fontmenucontroller.cxx: + # Needed for template + - com/sun/star/frame/XFrame.hpp + framework/source/uielement/imagebuttontoolbarcontroller.cxx: + # Actually used + - com/sun/star/uno/XComponentContext.hpp + framework/source/uielement/menubarmanager.cxx: + # Needed on WIN32 + - com/sun/star/lang/SystemDependent.hpp + # Actually used + - com/sun/star/frame/XDispatch.hpp + - com/sun/star/uno/XComponentContext.hpp + framework/source/uielement/menubarwrapper.cxx: + # Actually used + - com/sun/star/container/XNameAccess.hpp + framework/source/uielement/spinfieldtoolbarcontroller.cxx: + # Needed on WIN32 + - o3tl/char16_t2wchar_t.hxx + framework/source/uielement/styletoolbarcontroller.cxx: + # Actually used + - com/sun/star/util/XURLTransformer.hpp + framework/source/uielement/toolbarsmenucontroller.cxx: + # Actually used + - com/sun/star/beans/PropertyValue.hpp + - com/sun/star/frame/XLayoutManager.hpp diff --git a/framework/JunitTest_framework_complex.mk b/framework/JunitTest_framework_complex.mk new file mode 100644 index 0000000000..30f24b8104 --- /dev/null +++ b/framework/JunitTest_framework_complex.mk @@ -0,0 +1,83 @@ +# -*- 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,framework_complex)) + +$(eval $(call gb_JunitTest_set_defs,framework_complex,\ + $$(DEFS) \ + -Dorg.openoffice.test.arg.tdoc=$(SRCDIR)/framework/qa/complex/broken_document/test_documents \ +)) + +$(eval $(call gb_JunitTest_use_unoapi_jars,framework_complex)) + +$(eval $(call gb_JunitTest_add_sourcefiles,framework_complex,\ + framework/qa/complex/disposing/GetServiceWhileDisposingOffice \ + framework/qa/complex/path_substitution/PathSubstitutionTest \ + framework/qa/complex/loadAllDocuments/InteractionHandler \ + framework/qa/complex/loadAllDocuments/CheckXComponentLoader \ + framework/qa/complex/loadAllDocuments/StatusIndicator \ + framework/qa/complex/broken_document/TestDocument \ + framework/qa/complex/broken_document/LoadDocument \ + framework/qa/complex/framework/autosave/AutoSave \ + framework/qa/complex/framework/autosave/Protocol \ + framework/qa/complex/framework/autosave/ConfigHelper \ + framework/qa/complex/framework/recovery/KlickButtonThread \ + framework/qa/complex/framework/recovery/RecoveryTools \ + framework/qa/complex/framework/recovery/RecoveryTest \ + framework/qa/complex/framework/recovery/CrashThread \ + framework/qa/complex/accelerators/AcceleratorsConfigurationTest \ + framework/qa/complex/accelerators/KeyMapping \ + framework/qa/complex/contextMenuInterceptor/CheckContextMenuInterceptor \ + framework/qa/complex/contextMenuInterceptor/ContextMenuInterceptor \ + framework/qa/complex/path_settings/PathSettingsTest \ + framework/qa/complex/desktop/DesktopTerminate \ + framework/qa/complex/imageManager/_XComponent \ + framework/qa/complex/imageManager/CheckImageManager \ + framework/qa/complex/imageManager/_XTypeProvider \ + framework/qa/complex/imageManager/_XInitialization \ + framework/qa/complex/imageManager/_XImageManager \ + framework/qa/complex/imageManager/_XUIConfigurationPersistence \ + framework/qa/complex/imageManager/_XUIConfiguration \ + framework/qa/complex/api_internal/CheckAPI \ + framework/qa/complex/dispatches/checkdispatchapi \ + framework/qa/complex/dispatches/Interceptor \ + framework/qa/complex/ModuleManager/CheckXModuleManager \ + framework/qa/complex/XTitle/CheckXTitle \ +)) + +$(eval $(call gb_JunitTest_add_classes,framework_complex,\ + complex.dispatches.checkdispatchapi \ + complex.XTitle.CheckXTitle \ +)) +# these were disabled in the old build system too, please check +# carefully before reenabling +# complex.ModuleManager.CheckXModuleManager \ + complex.accelerators.AcceleratorsConfigurationTest \ + complex.api_internal.CheckAPI \ + complex.broken_document.LoadDocument \ + complex.desktop.DesktopTerminate \ + complex.disposing.GetServiceWhileDisposingOffice \ + complex.framework.autosave.AutoSave \ + complex.framework.recovery.RecoveryTest \ + complex.imageManager.CheckImageManager \ + complex.loadAllDocuments.CheckXComponentLoader \ + complex.path_settings.PathSettingsTest \ + complex.path_substitution.PathSubstitutionTest \ + +# vim: set noet sw=4 ts=4: diff --git a/framework/JunitTest_framework_unoapi.mk b/framework/JunitTest_framework_unoapi.mk new file mode 100644 index 0000000000..025051899f --- /dev/null +++ b/framework/JunitTest_framework_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,framework_unoapi)) + +$(eval $(call gb_JunitTest_set_unoapi_test_defaults,framework_unoapi)) + +# vim: set noet sw=4 ts=4: diff --git a/framework/Library_fwk.mk b/framework/Library_fwk.mk new file mode 100644 index 0000000000..cb89380890 --- /dev/null +++ b/framework/Library_fwk.mk @@ -0,0 +1,247 @@ +# -*- 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,fwk)) + +$(eval $(call gb_Library_add_defs,fwk,\ + -DFWK_DLLIMPLEMENTATION \ +)) + +$(eval $(call gb_Library_set_componentfile,fwk,framework/util/fwk,services)) + +$(eval $(call gb_Library_set_include,fwk,\ + -I$(SRCDIR)/framework/source/inc \ + -I$(SRCDIR)/framework/inc \ + -I$(WORKDIR)/CustomTarget/officecfg/registry \ + $$(INCLUDE) \ +)) + +$(eval $(call gb_Library_set_precompiled_header,fwk,framework/inc/pch/precompiled_fwk)) + +$(eval $(call gb_Library_use_external,fwk,boost_headers)) + +$(eval $(call gb_Library_use_custom_headers,fwk,\ + officecfg/registry \ +)) + +$(eval $(call gb_Library_use_sdk_api,fwk)) + +$(eval $(call gb_Library_use_libraries,fwk,\ + comphelper \ + cppu \ + cppuhelper \ + $(call gb_Helper_optional,BREAKPAD, \ + crashreport) \ + i18nlangtag \ + sal \ + salhelper \ + sot \ + svl \ + svt \ + tk \ + tl \ + ucbhelper \ + utl \ + vcl \ +)) + +ifneq ($(ENABLE_WASM_STRIP_RECOVERYUI),TRUE) +$(eval $(call gb_Library_add_exception_objects,fwk,\ + framework/source/services/autorecovery \ +)) + +$(eval $(call gb_Library_add_componentimpl,fwk,autorecovery)) +endif + +$(eval $(call gb_Library_add_exception_objects,fwk,\ + framework/source/accelerators/acceleratorcache \ + framework/source/accelerators/acceleratorconfiguration \ + framework/source/accelerators/documentacceleratorconfiguration \ + framework/source/accelerators/globalacceleratorconfiguration \ + framework/source/accelerators/keymapping \ + framework/source/accelerators/moduleacceleratorconfiguration \ + framework/source/accelerators/presethandler \ + framework/source/accelerators/storageholder \ + framework/source/classes/framecontainer \ + framework/source/classes/taskcreator \ + framework/source/dispatch/closedispatcher \ + framework/source/dispatch/dispatchinformationprovider \ + framework/source/dispatch/dispatchprovider \ + framework/source/dispatch/interceptionhelper \ + framework/source/dispatch/loaddispatcher \ + framework/source/dispatch/startmoduledispatcher \ + framework/source/dispatch/windowcommanddispatch \ + framework/source/dispatch/dispatchdisabler \ + framework/source/dispatch/mailtodispatcher \ + framework/source/dispatch/oxt_handler \ + framework/source/dispatch/popupmenudispatcher \ + framework/source/dispatch/servicehandler \ + framework/source/dispatch/systemexec \ + framework/source/helper/dockingareadefaultacceptor \ + framework/source/helper/ocomponentaccess \ + framework/source/helper/ocomponentenumeration \ + framework/source/helper/oframes \ + framework/source/helper/persistentwindowstate \ + framework/source/helper/statusindicator \ + framework/source/helper/statusindicatorfactory \ + framework/source/helper/tagwindowasmodified \ + framework/source/helper/titlebarupdate \ + framework/source/helper/uiconfigelementwrapperbase \ + framework/source/helper/uielementwrapperbase \ + framework/source/helper/vclstatusindicator \ + framework/source/helper/wakeupthread \ + framework/source/interaction/quietinteraction \ + framework/source/jobs/helponstartup \ + framework/source/jobs/shelljob \ + framework/source/fwe/classes/actiontriggercontainer \ + framework/source/fwe/classes/actiontriggerpropertyset \ + framework/source/fwe/classes/actiontriggerseparatorpropertyset \ + framework/source/fwe/classes/addonmenu \ + framework/source/fwe/classes/addonsoptions \ + framework/source/fwe/classes/framelistanalyzer \ + framework/source/fwe/classes/fwkresid \ + framework/source/fwe/classes/rootactiontriggercontainer \ + framework/source/fwe/classes/sfxhelperfunctions \ + framework/source/fwe/dispatch/interaction \ + framework/source/fwe/helper/actiontriggerhelper \ + framework/source/fwe/helper/configimporter \ + framework/source/fwe/helper/propertysetcontainer \ + framework/source/fwe/helper/titlehelper \ + framework/source/fwe/helper/documentundoguard \ + framework/source/fwe/helper/undomanagerhelper \ + framework/source/fwe/xml/menuconfiguration \ + framework/source/fwe/xml/menudocumenthandler \ + framework/source/fwe/xml/saxnamespacefilter \ + framework/source/fwe/xml/statusbarconfiguration \ + framework/source/fwe/xml/statusbardocumenthandler \ + framework/source/fwe/xml/toolboxconfiguration \ + framework/source/fwe/xml/toolboxdocumenthandler \ + framework/source/fwe/xml/xmlnamespaces \ + framework/source/fwi/classes/converter \ + framework/source/fwi/classes/protocolhandlercache \ + framework/source/fwi/helper/mischelper \ + framework/source/fwi/helper/shareablemutex \ + framework/source/fwi/jobs/configaccess \ + framework/source/fwi/threadhelp/transactionmanager \ + framework/source/fwi/uielement/constitemcontainer \ + framework/source/fwi/uielement/itemcontainer \ + framework/source/fwi/uielement/rootitemcontainer \ + framework/source/jobs/job \ + framework/source/jobs/jobdata \ + framework/source/jobs/jobdispatch \ + framework/source/jobs/jobexecutor \ + framework/source/jobs/jobresult \ + framework/source/jobs/joburl \ + framework/source/layoutmanager/helpers \ + framework/source/layoutmanager/layoutmanager \ + framework/source/layoutmanager/toolbarlayoutmanager \ + framework/source/layoutmanager/uielement \ + framework/source/loadenv/loadenv \ + framework/source/loadenv/targethelper \ + framework/source/recording/dispatchrecorder \ + framework/source/recording/dispatchrecordersupplier \ + framework/source/services/ContextChangeEventMultiplexer \ + framework/source/services/desktop \ + framework/source/services/dispatchhelper \ + framework/source/services/frame \ + framework/source/services/modulemanager \ + framework/source/services/pathsettings \ + framework/source/services/sessionlistener \ + framework/source/services/substitutepathvars \ + framework/source/services/taskcreatorsrv \ + framework/source/services/urltransformer \ + framework/source/services/mediatypedetectionhelper \ + framework/source/services/uriabbreviation \ + framework/source/uiconfiguration/CommandImageResolver \ + framework/source/uiconfiguration/ImageList \ + framework/source/uiconfiguration/globalsettings \ + framework/source/uiconfiguration/graphicnameaccess \ + framework/source/uiconfiguration/imagemanager \ + framework/source/uiconfiguration/imagemanagerimpl \ + framework/source/uiconfiguration/moduleuicfgsupplier \ + framework/source/uiconfiguration/moduleuiconfigurationmanager \ + framework/source/uiconfiguration/uicategorydescription \ + framework/source/uiconfiguration/uiconfigurationmanager \ + framework/source/uiconfiguration/windowstateconfiguration \ + framework/source/uielement/addonstoolbarwrapper \ + framework/source/uielement/buttontoolbarcontroller \ + framework/source/uielement/comboboxtoolbarcontroller \ + framework/source/uielement/complextoolbarcontroller \ + framework/source/uielement/controlmenucontroller \ + framework/source/uielement/dropdownboxtoolbarcontroller \ + framework/source/uielement/edittoolbarcontroller \ + framework/source/uielement/FixedImageToolbarController \ + framework/source/uielement/FixedTextToolbarController \ + framework/source/uielement/genericstatusbarcontroller \ + framework/source/uielement/generictoolbarcontroller \ + framework/source/uielement/imagebuttontoolbarcontroller \ + framework/source/uielement/langselectionstatusbarcontroller \ + framework/source/uielement/menubarmanager \ + framework/source/uielement/menubarmerger \ + framework/source/uielement/menubarwrapper \ + framework/source/uielement/objectmenucontroller \ + framework/source/uielement/popuptoolbarcontroller \ + framework/source/uielement/progressbarwrapper \ + framework/source/uielement/recentfilesmenucontroller \ + framework/source/uielement/resourcemenucontroller \ + framework/source/uielement/spinfieldtoolbarcontroller \ + framework/source/uielement/statusbar \ + framework/source/uielement/statusbaritem \ + framework/source/uielement/statusbarmanager \ + framework/source/uielement/statusbarmerger \ + framework/source/uielement/statusbarwrapper \ + framework/source/uielement/statusindicatorinterfacewrapper \ + framework/source/uielement/styletoolbarcontroller \ + framework/source/uielement/subtoolbarcontroller \ + framework/source/uielement/thesaurusmenucontroller \ + framework/source/uielement/togglebuttontoolbarcontroller \ + framework/source/uielement/toolbarmanager \ + framework/source/uielement/toolbarmerger \ + framework/source/uielement/toolbarwrapper \ + framework/source/uielement/uicommanddescription \ + framework/source/uielement/fontmenucontroller \ + framework/source/uielement/fontsizemenucontroller \ + framework/source/uielement/footermenucontroller \ + framework/source/uielement/headermenucontroller \ + framework/source/uielement/langselectionmenucontroller \ + framework/source/uielement/macrosmenucontroller \ + framework/source/uielement/newmenucontroller \ + framework/source/uielement/toolbarmodemenucontroller \ + framework/source/uielement/toolbarsmenucontroller \ + framework/source/uifactory/addonstoolbarfactory \ + framework/source/uifactory/factoryconfiguration \ + framework/source/uifactory/menubarfactory \ + framework/source/uifactory/statusbarfactory \ + framework/source/uifactory/toolbarfactory \ + framework/source/uifactory/uicontrollerfactory \ + framework/source/uifactory/uielementfactorymanager \ + framework/source/uifactory/windowcontentfactorymanager \ + framework/source/xml/acceleratorconfigurationreader \ + framework/source/xml/acceleratorconfigurationwriter \ + framework/source/xml/imagesconfiguration \ + framework/source/xml/imagesdocumenthandler \ +)) + +ifeq ($(OS),WNT) +$(eval $(call gb_Library_use_system_win32_libs,fwk,\ + advapi32 \ +)) +endif + +# vim: set noet sw=4 ts=4: diff --git a/framework/Makefile b/framework/Makefile new file mode 100644 index 0000000000..ccb1c85a04 --- /dev/null +++ b/framework/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/framework/Module_framework.mk b/framework/Module_framework.mk new file mode 100644 index 0000000000..924989d067 --- /dev/null +++ b/framework/Module_framework.mk @@ -0,0 +1,49 @@ +# -*- 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,framework)) + +$(eval $(call gb_Module_add_targets,framework,\ + Library_fwk \ + Package_dtd \ + UIConfig_startmodule \ +)) + +$(eval $(call gb_Module_add_slowcheck_targets,framework,\ + CppunitTest_framework_dispatch \ + CppunitTest_framework_loadenv \ +)) + +# Not sure why this is not stable on macOS. +ifneq ($(OS),MACOSX) +$(eval $(call gb_Module_add_slowcheck_targets,framework,\ + CppunitTest_framework_services \ +)) +endif + +$(eval $(call gb_Module_add_l10n_targets,framework,\ + AllLangMoTarget_fwk \ +)) + +$(eval $(call gb_Module_add_subsequentcheck_targets,framework,\ + JunitTest_framework_complex \ + JunitTest_framework_unoapi \ +)) + +# vim: set noet sw=4 ts=4: diff --git a/framework/Package_dtd.mk b/framework/Package_dtd.mk new file mode 100644 index 0000000000..50be1653ba --- /dev/null +++ b/framework/Package_dtd.mk @@ -0,0 +1,32 @@ +# -*- 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_Package_Package,framework_dtd,$(SRCDIR)/framework/dtd)) + +$(eval $(call gb_Package_add_files,framework_dtd,$(LIBO_SHARE_FOLDER)/dtd/officedocument/1_0,\ + accelerator.dtd \ + event.dtd \ + groupuinames.dtd \ + image.dtd \ + menubar.dtd \ + statusbar.dtd \ + toolbar.dtd \ +)) + +# vim: set noet sw=4 ts=4: diff --git a/framework/README.md b/framework/README.md new file mode 100644 index 0000000000..74711a9a69 --- /dev/null +++ b/framework/README.md @@ -0,0 +1,7 @@ +# UNO Framework + +Toolbars, menus, UNO stuff, including accelerators and interaction, etc. + +## See Also + + diff --git a/framework/UIConfig_startmodule.mk b/framework/UIConfig_startmodule.mk new file mode 100644 index 0000000000..e92ce07bd9 --- /dev/null +++ b/framework/UIConfig_startmodule.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_UIConfig_UIConfig,modules/StartModule)) + +$(eval $(call gb_UIConfig_add_menubarfiles,modules/StartModule,\ + framework/uiconfig/startmodule/menubar/menubar \ +)) + +# vim: set noet sw=4 ts=4: diff --git a/framework/dtd/accelerator.dtd b/framework/dtd/accelerator.dtd new file mode 100644 index 0000000000..912b652d3c --- /dev/null +++ b/framework/dtd/accelerator.dtd @@ -0,0 +1,34 @@ + + + + + + + + + diff --git a/framework/dtd/event.dtd b/framework/dtd/event.dtd new file mode 100644 index 0000000000..5af4a3dd26 --- /dev/null +++ b/framework/dtd/event.dtd @@ -0,0 +1,35 @@ + + + + + + + + + diff --git a/framework/dtd/groupuinames.dtd b/framework/dtd/groupuinames.dtd new file mode 100644 index 0000000000..218939cf9b --- /dev/null +++ b/framework/dtd/groupuinames.dtd @@ -0,0 +1,26 @@ + + + + + + + diff --git a/framework/dtd/image.dtd b/framework/dtd/image.dtd new file mode 100644 index 0000000000..fb69a5e5f4 --- /dev/null +++ b/framework/dtd/image.dtd @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/framework/dtd/menubar.dtd b/framework/dtd/menubar.dtd new file mode 100644 index 0000000000..b3469f7324 --- /dev/null +++ b/framework/dtd/menubar.dtd @@ -0,0 +1,39 @@ + + + + + + + + + + + + diff --git a/framework/dtd/statusbar.dtd b/framework/dtd/statusbar.dtd new file mode 100644 index 0000000000..40a5609b7c --- /dev/null +++ b/framework/dtd/statusbar.dtd @@ -0,0 +1,39 @@ + + + + + + + + + + + diff --git a/framework/dtd/toolbar.dtd b/framework/dtd/toolbar.dtd new file mode 100644 index 0000000000..7f8ee24e8a --- /dev/null +++ b/framework/dtd/toolbar.dtd @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + diff --git a/framework/inc/acceleratorconst.h b/framework/inc/acceleratorconst.h new file mode 100644 index 0000000000..667dc06c07 --- /dev/null +++ b/framework/inc/acceleratorconst.h @@ -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 . + */ + +#pragma once + +#include + +namespace framework{ + +// same items with a name space alias +inline constexpr OUString AL_ELEMENT_ACCELERATORLIST = u"accel:acceleratorlist"_ustr; +inline constexpr OUString AL_ELEMENT_ITEM = u"accel:item"_ustr; + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/addonmenu.hxx b/framework/inc/addonmenu.hxx new file mode 100644 index 0000000000..d6f244cdca --- /dev/null +++ b/framework/inc/addonmenu.hxx @@ -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 . + */ +#pragma once + +#include +#include + +namespace com::sun::star::beans { struct PropertyValue; } +namespace com::sun::star::frame { class XFrame; } +namespace com::sun::star::uno { template class Sequence; } +namespace com::sun::star::uno { template class Reference; } + +class MenuBar; +class PopupMenu; + +#define ADDONMENU_ITEMID_START 2000 +#define ADDONMENU_ITEMID_END 3000 + +namespace framework +{ + +class AddonMenuManager +{ + public: + static bool HasAddonMenuElements(); + + // Check if the context string matches the provided xModel context + static bool IsCorrectContext(std::u16string_view rModuleIdentifier, std::u16string_view rContext); + + // Create the Add-Ons menu + static VclPtr CreateAddonMenu( const css::uno::Reference< css::frame::XFrame >& rFrame ); + + // Merge the Add-Ons help menu items into the given menu bar at a defined pos + static void MergeAddonHelpMenu( const css::uno::Reference< css::frame::XFrame >& rFrame, + MenuBar const * pMergeMenuBar ); + + // Merge the addon popup menus into the given menu bar at the provided pos. + static void MergeAddonPopupMenus( const css::uno::Reference< css::frame::XFrame >& rFrame, + sal_uInt16 nMergeAtPos, + MenuBar* pMergeMenuBar ); + + // Returns the next position to insert a menu item/sub menu + static sal_uInt16 GetNextPos( sal_uInt16 nPos ); + + // Build up the menu item and sub menu into the provided pCurrentMenu. The sub menus should be of type nSubMenuType. + static void BuildMenu( PopupMenu* pCurrentMenu, + sal_uInt16 nInsPos, + sal_uInt16& nUniqueMenuId, + const css::uno::Sequence< css::uno::Sequence< css::beans::PropertyValue > >& aAddonMenuDefinition, + const css::uno::Reference< css::frame::XFrame >& rFrame, + const OUString& rModuleIdentifier ); + + // Retrieve the menu entry property values from a sequence + static void GetMenuEntry( const css::uno::Sequence< css::beans::PropertyValue >& rAddonMenuEntry, + OUString& rTitle, + OUString& rURL, + OUString& rTarget, + OUString& rContext, + css::uno::Sequence< css::uno::Sequence< css::beans::PropertyValue > >& rAddonSubMenu ); +}; + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/bitmaps.hlst b/framework/inc/bitmaps.hlst new file mode 100644 index 0000000000..986005bbdc --- /dev/null +++ b/framework/inc/bitmaps.hlst @@ -0,0 +1,39 @@ +/* -*- 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 + +inline constexpr OUString BMP_SAVEMODIFIED_SMALL = u"res/savemodified_small.png"_ustr; +inline constexpr OUString BMP_SAVEMODIFIED_LARGE = u"res/savemodified_large.png"_ustr; +inline constexpr OUString BMP_SAVEMODIFIED_EXTRALARGE = u"res/savemodified_extralarge.png"_ustr; + +inline constexpr OUString RID_SVXBMP_BUTTON = u"res/sx10594.png"_ustr; +inline constexpr OUString RID_SVXBMP_RADIOBUTTON = u"res/sx10595.png"_ustr; +inline constexpr OUString RID_SVXBMP_CHECKBOX = u"res/sx10596.png"_ustr; +inline constexpr OUString RID_SVXBMP_FIXEDTEXT = u"res/sx10597.png"_ustr; +inline constexpr OUString RID_SVXBMP_GROUPBOX = u"res/sx10598.png"_ustr; +inline constexpr OUString RID_SVXBMP_EDITBOX = u"res/sx10599.png"_ustr; +inline constexpr OUString RID_SVXBMP_LISTBOX = u"res/sx10600.png"_ustr; +inline constexpr OUString RID_SVXBMP_COMBOBOX = u"res/sx10601.png"_ustr; +inline constexpr OUString RID_SVXBMP_IMAGEBUTTON = u"res/sx10604.png"_ustr; +inline constexpr OUString RID_SVXBMP_IMAGECONTROL = u"res/sx10710.png"_ustr; +inline constexpr OUString RID_SVXBMP_FILECONTROL = u"res/sx10605.png"_ustr; +inline constexpr OUString RID_SVXBMP_DATEFIELD = u"res/sx10704.png"_ustr; +inline constexpr OUString RID_SVXBMP_TIMEFIELD = u"res/sx10705.png"_ustr; +inline constexpr OUString RID_SVXBMP_NUMERICFIELD = u"res/sx10706.png"_ustr; +inline constexpr OUString RID_SVXBMP_CURRENCYFIELD = u"res/sx10707.png"_ustr; +inline constexpr OUString RID_SVXBMP_PATTERNFIELD = u"res/sx10708.png"_ustr; +inline constexpr OUString RID_SVXBMP_FORMATTEDFIELD = u"res/sx10728.png"_ustr; +inline constexpr OUString RID_SVXBMP_SCROLLBAR = u"res/sx10768.png"_ustr; +inline constexpr OUString RID_SVXBMP_SPINBUTTON = u"res/sx10769.png"_ustr; +inline constexpr OUString RID_SVXBMP_NAVIGATIONBAR = u"res/sx10607.png"_ustr; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/framework/inc/classes/actiontriggercontainer.hxx b/framework/inc/classes/actiontriggercontainer.hxx new file mode 100644 index 0000000000..25511d52b3 --- /dev/null +++ b/framework/inc/classes/actiontriggercontainer.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 . + */ + +#pragma once + +#include +#include +#include +#include + +inline constexpr OUString SERVICENAME_ACTIONTRIGGERCONTAINER = u"com.sun.star.ui.ActionTriggerContainer"_ustr; +inline constexpr OUString IMPLEMENTATIONNAME_ACTIONTRIGGERCONTAINER = u"com.sun.star.comp.ui.ActionTriggerContainer"_ustr; + +namespace framework +{ + +class ActionTriggerContainer final : public PropertySetContainer, + public css::lang::XMultiServiceFactory, + public css::lang::XServiceInfo, + public css::lang::XTypeProvider +{ + public: + ActionTriggerContainer(); + virtual ~ActionTriggerContainer() override; + + // 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; + + // XMultiServiceFactory + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL createInstance( const OUString& aServiceSpecifier ) override; + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL createInstanceWithArguments( const OUString& ServiceSpecifier, const css::uno::Sequence< css::uno::Any >& Arguments ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getAvailableServiceNames() override; + + // 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; + + // XTypeProvider + virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes( ) override; + virtual css::uno::Sequence< sal_Int8 > SAL_CALL getImplementationId( ) override; +}; + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/classes/actiontriggerpropertyset.hxx b/framework/inc/classes/actiontriggerpropertyset.hxx new file mode 100644 index 0000000000..da553fa9e1 --- /dev/null +++ b/framework/inc/classes/actiontriggerpropertyset.hxx @@ -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 . + */ + +#pragma once + +#include +#include +#include +#include + +#include +#include +#include + +inline constexpr OUString SERVICENAME_ACTIONTRIGGER = u"com.sun.star.ui.ActionTrigger"_ustr; +inline constexpr OUString IMPLEMENTATIONNAME_ACTIONTRIGGER = u"com.sun.star.comp.ui.ActionTrigger"_ustr; + +namespace framework +{ + +class ActionTriggerPropertySet final : private cppu::BaseMutex, + public css::lang::XServiceInfo , + public css::lang::XTypeProvider, + public ::cppu::OBroadcastHelper , + public ::cppu::OPropertySetHelper , // -> XPropertySet, XFastPropertySet, XMultiPropertySet + public ::cppu::OWeakObject +{ + public: + ActionTriggerPropertySet(); + virtual ~ActionTriggerPropertySet() override; + + // 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; + + // 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; + + // XTypeProvider + virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes( ) override; + virtual css::uno::Sequence< sal_Int8 > SAL_CALL getImplementationId( ) override; + + private: + + // OPropertySetHelper + + virtual sal_Bool SAL_CALL convertFastPropertyValue( css::uno::Any& aConvertedValue, + css::uno::Any& aOldValue, + sal_Int32 nHandle, + const css::uno::Any& aValue ) override; + + virtual void SAL_CALL setFastPropertyValue_NoBroadcast( sal_Int32 nHandle, const css::uno::Any& aValue ) override; + + using cppu::OPropertySetHelper::getFastPropertyValue; + virtual void SAL_CALL getFastPropertyValue( css::uno::Any& aValue, sal_Int32 nHandle ) const override; + + virtual ::cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper() override; + + virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo() override; + + static css::uno::Sequence< css::beans::Property > impl_getStaticPropertyDescriptor(); + + // helper + + /// @throws css::lang::IllegalArgumentException + bool impl_tryToChangeProperty( const OUString& aCurrentValue , + const css::uno::Any& aNewValue , + css::uno::Any& aOldValue , + css::uno::Any& aConvertedValue ); + + /// @throws css::lang::IllegalArgumentException + bool impl_tryToChangeProperty( const css::uno::Reference< css::awt::XBitmap >& xBitmap, + const css::uno::Any& aNewValue , + css::uno::Any& aOldValue , + css::uno::Any& aConvertedValue ); + + /// @throws css::lang::IllegalArgumentException + bool impl_tryToChangeProperty( const css::uno::Reference< css::uno::XInterface >& xInterface, + const css::uno::Any& aNewValue , + css::uno::Any& aOldValue , + css::uno::Any& aConvertedValue ); + + // members + + OUString m_aCommandURL; + OUString m_aHelpURL; + OUString m_aText; + css::uno::Reference< css::awt::XBitmap > m_xBitmap; + css::uno::Reference< css::uno::XInterface > m_xActionTriggerContainer; +}; + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/classes/actiontriggerseparatorpropertyset.hxx b/framework/inc/classes/actiontriggerseparatorpropertyset.hxx new file mode 100644 index 0000000000..0d0b62b490 --- /dev/null +++ b/framework/inc/classes/actiontriggerseparatorpropertyset.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 . + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +inline constexpr OUString SERVICENAME_ACTIONTRIGGERSEPARATOR = u"com.sun.star.ui.ActionTriggerSeparator"_ustr; +inline constexpr OUString IMPLEMENTATIONNAME_ACTIONTRIGGERSEPARATOR = u"com.sun.star.comp.ui.ActionTriggerSeparator"_ustr; + +namespace framework +{ + +class ActionTriggerSeparatorPropertySet final : private cppu::BaseMutex, + public css::lang::XServiceInfo , + public css::lang::XTypeProvider, + public ::cppu::OBroadcastHelper , + public ::cppu::OPropertySetHelper , // -> XPropertySet, XFastPropertySet, XMultiPropertySet + public ::cppu::OWeakObject +{ + public: + ActionTriggerSeparatorPropertySet(); + virtual ~ActionTriggerSeparatorPropertySet() override; + + // 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; + + // 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; + + // XTypeProvider + virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes( ) override; + virtual css::uno::Sequence< sal_Int8 > SAL_CALL getImplementationId( ) override; + + private: + + // OPropertySetHelper + + virtual sal_Bool SAL_CALL convertFastPropertyValue( css::uno::Any& aConvertedValue, + css::uno::Any& aOldValue, + sal_Int32 nHandle, + const css::uno::Any& aValue ) override; + + virtual void SAL_CALL setFastPropertyValue_NoBroadcast( sal_Int32 nHandle, const css::uno::Any& aValue ) override; + + using cppu::OPropertySetHelper::getFastPropertyValue; + virtual void SAL_CALL getFastPropertyValue( css::uno::Any& aValue, sal_Int32 nHandle ) const override; + + virtual ::cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper() override; + + virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo() override; + + static css::uno::Sequence< css::beans::Property > impl_getStaticPropertyDescriptor(); + + // helper + + /// @throws css::lang::IllegalArgumentException + /// @throws css::uno::RuntimeException + bool impl_tryToChangeProperty( sal_Int16 aCurrentValue , + const css::uno::Any& aNewValue , + css::uno::Any& aOldValue , + css::uno::Any& aConvertedValue ); + + // members + + sal_Int16 m_nSeparatorType; +}; + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/classes/converter.hxx b/framework/inc/classes/converter.hxx new file mode 100644 index 0000000000..17198ec2c0 --- /dev/null +++ b/framework/inc/classes/converter.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 . + */ + +#pragma once + +#include + +#include +#include +#include +#include + +namespace framework{ + +class Converter +{ + public: + // Seq <=> Seq + static css::uno::Sequence< css::beans::NamedValue > convert_seqPropVal2seqNamedVal ( const css::uno::Sequence< css::beans::PropertyValue >& lSource ); + + // Seq => Vector + static std::vector convert_seqOUString2OUStringList( const css::uno::Sequence< OUString >& lSource ); + + static OUString convert_DateTime2ISO8601 ( const DateTime& aSource ); +}; + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/classes/fwkresid.hxx b/framework/inc/classes/fwkresid.hxx new file mode 100644 index 0000000000..5023f34a23 --- /dev/null +++ b/framework/inc/classes/fwkresid.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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 +#include + +OUString FwkResId(TranslateId aId); + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/classes/protocolhandlercache.hxx b/framework/inc/classes/protocolhandlercache.hxx new file mode 100644 index 0000000000..84035642eb --- /dev/null +++ b/framework/inc/classes/protocolhandlercache.hxx @@ -0,0 +1,146 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 +#include + +#include + +#include +#include + +namespace framework{ + +inline constexpr OUString PACKAGENAME_PROTOCOLHANDLER = u"Office.ProtocolHandler"_ustr; /// name of our configuration package + +#define CFG_PATH_SEPARATOR "/" /// separator for configuration paths + +#define PROPERTY_PROTOCOLS "Protocols" /// properties of a protocol handler + +/** + Programmer can register his own services to handle different protocols. + Don't forget: It doesn't mean "handling of documents" ... these services could handle protocols ... + e.g. "mailto:", "file://", ".java:" + This struct holds the information about one such registered protocol handler. + A list of handler objects is defined as ProtocolHandlerHash. see below +*/ +struct ProtocolHandler +{ + /* member */ + public: + + /// the uno implementation name of this handler + OUString m_sUNOName; + /// list of URL pattern which defines the protocols which this handler is registered for + std::vector m_lProtocols; +}; + +/** + This hash use registered pattern of all protocol handlers as keys and provide her + uno implementation names as value. Overloading of the index operator makes it possible + to search for a key by using a full qualified URL on list of all possible pattern keys. +*/ +typedef std::unordered_map PatternHash; + +/** + This hash holds protocol handler structs by her names. +*/ +typedef std::unordered_map HandlerHash; + +/** + @short this hash makes it easy to find a protocol handler by using his uno implementation name. + @descr It holds two lists of information: + - first holds all handler by her uno implementation names and + can be used to get her other properties + - another one maps her registered pattern to her uno names to + perform search on such data + But this lists a static for all instances of this class. So it's possible to + create new objects without opening configuration twice and free memory automatically + if last object will gone. + + @attention We implement a singleton concept - so we don't need any mutex member here. + Because to safe access on static member we must use a static global lock + here too. + + @devstatus ready to use + @threadsafe yes +*/ + +class HandlerCFGAccess; +class HandlerCache final +{ + /* member */ + private: + + /// list of all registered handler registered by her uno implementation names + static std::optional s_pHandler; + /// maps URL pattern to handler names + static std::optional s_pPattern; + /// informs about config updates + static HandlerCFGAccess* s_pConfig; + /// ref count to construct/destruct internal member lists on demand by using singleton mechanism + static sal_Int32 m_nRefCount; + + /* interface */ + public: + + HandlerCache(); + ~HandlerCache(); + + bool search( const OUString& sURL, ProtocolHandler* pReturn ) const; + bool search( const css::util::URL& aURL, ProtocolHandler* pReturn ) const; + + void takeOver(HandlerHash aHandler, PatternHash aPattern); +}; + +/** + @short implements configuration access for handler configuration + @descr We use the ConfigItem mechanism to read/write values from/to configuration. + We set a data container pointer for filling or reading ... this class use it temp. + After successfully calling of read(), we can use filled container directly or merge it with an existing one. + After successfully calling of write() all values of given data container are flushed to our configuration - + but current implementation doesn't support writing really. + + @base ::utl::ConfigItem + base mechanism for configuration access + + @devstatus ready to use + @threadsafe no +*/ +class HandlerCFGAccess final : public ::utl::ConfigItem +{ + private: + HandlerCache* m_pCache; + + virtual void ImplCommit() override; + + /* interface */ + public: + HandlerCFGAccess( const OUString& sPackage ); + void read ( HandlerHash& rHandlerHash, PatternHash& rPatternHash ); + + void setCache(HandlerCache* pCache) {m_pCache = pCache;}; + virtual void Notify(const css::uno::Sequence< OUString >& lPropertyNames) override; +}; + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/classes/resource.hxx b/framework/inc/classes/resource.hxx new file mode 100644 index 0000000000..64d89d119e --- /dev/null +++ b/framework/inc/classes/resource.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 . + */ + +#pragma once + +#define MENUITEM_TOOLBAR_VISIBLEBUTTON 1 +#define MENUITEM_TOOLBAR_CUSTOMIZETOOLBAR 2 +#define MENUITEM_TOOLBAR_UNDOCKTOOLBAR 3 +#define MENUITEM_TOOLBAR_DOCKTOOLBAR 4 +#define MENUITEM_TOOLBAR_DOCKALLTOOLBAR 5 +#define MENUITEM_TOOLBAR_LOCKTOOLBARPOSITION 6 +#define MENUITEM_TOOLBAR_CLOSE 7 + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/classes/rootactiontriggercontainer.hxx b/framework/inc/classes/rootactiontriggercontainer.hxx new file mode 100644 index 0000000000..87ba5b518f --- /dev/null +++ b/framework/inc/classes/rootactiontriggercontainer.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 . + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +inline constexpr OUString IMPLEMENTATIONNAME_ROOTACTIONTRIGGERCONTAINER = u"com.sun.star.comp.ui.RootActionTriggerContainer"_ustr; + +namespace framework +{ + +class RootActionTriggerContainer final : public PropertySetContainer, + public css::lang::XMultiServiceFactory, + public css::lang::XServiceInfo, + public css::lang::XTypeProvider, + public css::container::XNamed +{ + public: + RootActionTriggerContainer(css::uno::Reference xMenu, const OUString* pMenuIdentifier); + virtual ~RootActionTriggerContainer() override; + + // 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; + + // XMultiServiceFactory + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL createInstance( const OUString& aServiceSpecifier ) override; + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL createInstanceWithArguments( const OUString& ServiceSpecifier, const css::uno::Sequence< css::uno::Any >& Arguments ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getAvailableServiceNames() override; + + // XIndexContainer + virtual void SAL_CALL insertByIndex( sal_Int32 Index, const css::uno::Any& Element ) override; + + virtual void SAL_CALL removeByIndex( sal_Int32 Index ) override; + + // XIndexReplace + virtual void SAL_CALL replaceByIndex( sal_Int32 Index, const css::uno::Any& Element ) 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; + + // 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; + + // XTypeProvider + virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes( ) override; + virtual css::uno::Sequence< sal_Int8 > SAL_CALL getImplementationId( ) override; + + // XNamed + virtual OUString SAL_CALL getName( ) override; + virtual void SAL_CALL setName( const OUString& aName ) override; + + private: + void FillContainer(); + + bool m_bContainerCreated; + css::uno::Reference m_xMenu; + const OUString* m_pMenuIdentifier; +}; + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/classes/taskcreator.hxx b/framework/inc/classes/taskcreator.hxx new file mode 100644 index 0000000000..d57cc406e3 --- /dev/null +++ b/framework/inc/classes/taskcreator.hxx @@ -0,0 +1,60 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 +#include + +#include +#include +#include + +namespace framework{ + +/* + @short a helper to create new tasks or sub frames for "_blank" or FrameSearchFlag::CREATE + @descr There are different places to create new (task)frames and it's not easy to service this code. + That's the reason for this helper. It encapsulates asynchronous/synchronous creation + by providing a simple interface. + + @devstatus ready to use + @threadsafe yes +*/ +class TaskCreator final +{ + + // member + private: + + css::uno::Reference< css::uno::XComponentContext > m_xContext; + + // interface + public: + + TaskCreator( css::uno::Reference< css::uno::XComponentContext > xContext ); + ~TaskCreator( ); + + css::uno::Reference< css::frame::XFrame > createTask( const OUString& sName, const utl::MediaDescriptor& rDescriptor ); + +}; // class TaskCreator + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/dispatch/closedispatcher.hxx b/framework/inc/dispatch/closedispatcher.hxx new file mode 100644 index 0000000000..2217afa9c8 --- /dev/null +++ b/framework/inc/dispatch/closedispatcher.hxx @@ -0,0 +1,261 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +class SystemWindow; + +namespace framework{ + +/** + @short helper to dispatch the URLs ".uno:CloseDoc"/".uno:CloseWin"/".uno:CloseFrame" + to close a frame/document or the whole application implicitly in case it was the last frame + + @descr These URLs implements a special functionality to close a document or the whole frame ... + and handle the state, it was the last frame or document. Then we create the + default backing document which can be used to open new ones using the file open dialog + or some other menu entries. Or we terminate the whole application in case this backing mode should not + be used. + */ +class CloseDispatcher final : public ::cppu::WeakImplHelper< + css::frame::XNotifyingDispatch, // => XDispatch + css::frame::XDispatchInformationProvider > +{ + + // types + + private: + + /** @short describe, which request must be done here. + @descr The incoming URLs {.uno:CloseDoc/CloseWin and CloseFrame + can be classified so and checked later performant.}*/ + enum EOperation + { + E_CLOSE_DOC, + E_CLOSE_FRAME, + E_CLOSE_WIN + }; + + // member + + private: + + /** @short reference to a uno service manager, + which can be used to create own needed + uno resources. */ + css::uno::Reference< css::uno::XComponentContext > m_xContext; + + /** @short reference to the target frame, which should be + closed by this dispatch. */ + css::uno::WeakReference< css::frame::XFrame > m_xCloseFrame; + + /** @short used for asynchronous callbacks within the main thread. + @descr Internally we work asynchronously. Because our callees + are not aware that their request can kill its own environment... */ + std::unique_ptr m_aAsyncCallback; + + /** @short used inside asynchronous callback to decide, + which operation must be executed. */ + EOperation m_eOperation; + + /** @short for asynchronous operations we must hold us self alive! */ + css::uno::Reference< css::uno::XInterface > m_xSelfHold; + + /** @short holded alive for internally asynchronous operations! */ + css::uno::Reference< css::frame::XDispatchResultListener > m_xResultListener; + + VclPtr m_pSysWindow; + + // native interface + + public: + + /** @short connect a new CloseDispatcher instance to its frame. + @descr One CloseDispatcher instance is bound to own frame only. + That makes an implementation (e.g. of listener support) + much more easier .-) + + @param rxContext + an un oservice manager, which is needed to create uno resource + internally. + + @param xFrame + the frame where the corresponding dispatch was started. + + @param sTarget + help us to find the right target for this close operation. + */ + CloseDispatcher(css::uno::Reference< css::uno::XComponentContext > xContext , + const css::uno::Reference< css::frame::XFrame >& xFrame , + std::u16string_view sTarget); + + /** @short does nothing real. */ + virtual ~CloseDispatcher() override; + + // uno interface + + public: + + // XNotifyingDispatch + virtual void SAL_CALL dispatchWithNotification( const css::util::URL& aURL , + const css::uno::Sequence< css::beans::PropertyValue >& lArguments, + const css::uno::Reference< css::frame::XDispatchResultListener >& xListener ) 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; + + // XDispatchInformationProvider + virtual css::uno::Sequence< sal_Int16 > SAL_CALL getSupportedCommandGroups ( ) override; + virtual css::uno::Sequence< css::frame::DispatchInformation > SAL_CALL getConfigurableDispatchInformation( sal_Int16 nCommandGroup ) override; + + // internal helper + + private: + + /** @short a callback for asynchronous started operations. + + @descr As already mentioned, we make internally all operations + asynchronous. Otherwise our callis kill its own environment + during they call us... + */ + DECL_LINK( impl_asyncCallback, LinkParamNone*, void ); + + /** @short prepare m_xCloseFrame so it should be closeable without problems. + + @descr That's needed to be sure, that the document can't disagree + later with e.g. an office termination. + The problem: Closing of documents can show UI. If the user + ignores it and open/close other documents, we can't know + which state the office has after closing of this frame. + + @param bCloseAllOtherViewsToo + if there are other top level frames, which + contains views to the same document then our m_xCloseFrame, + they are forced to be closed too. + We need it to implement the CLOSE_DOC semantic. + + @return [boolean] + sal_True if closing was successful. + */ + bool implts_prepareFrameForClosing(const css::uno::Reference< css::frame::XFrame >& xFrame, + bool bCloseAllOtherViewsToo, + bool& bControllerSuspended ); + + /** @short close the member m_xCloseFrame. + + @descr This method does not look for any document + inside this frame. Such views must be cleared + before (e.g. by calling implts_closeView()! + + Otherwise e.g. the XController->suspend() + call is not made and no UI warn the user about + losing document changes. Because the + frame is closed... + + @return [bool] + sal_True if closing was successful. + */ + bool implts_closeFrame(); + + /** @short set the special BackingComponent (now StartModule) + as new component of our m_xCloseFrame. + + @return [bool] + sal_True if operation was successful. + */ + bool implts_establishBackingMode(); + + /** @short calls XDesktop->terminate(). + + @descr No office code has to be called + afterwards! Because the process is dying... + The only exception is a might be registered + listener at this instance here. + Because he should know, that such things will happen :-) + + @return [bool] + sal_True if termination of the application was started ... + */ + bool implts_terminateApplication(); + + /** @short notify a DispatchResultListener. + + @descr We check the listener reference before we use it. + So this method can be called every time! + + @parama xListener + the listener, which should be notified. + Can be null! + + @param nState + directly used as css::frame::DispatchResultState value. + + @param aResult + not used yet really ... + */ + void implts_notifyResultListener(const css::uno::Reference< css::frame::XDispatchResultListener >& xListener, + sal_Int16 nState , + const css::uno::Any& aResult ); + + /** @short try to find the right target frame where this close request + must be really done. + + @descr The problem behind: closing some resources depends sometimes from the + context where its dispatched. Sometimes the start frame of the dispatch + has to be closed itself (target=_self) ... sometimes its parent frame + has to be closed - BUT(!) it means a parent frame containing a top level + window. _top can't be used then for dispatch - because it address TopFrames + not frames containing top level windows. So normally _magic (which btw does not + exists at the moment .-) ) should be used. So we interpret target= + as _magic ! + + @param xFrame + start point for search of right dispatch frame. + + @param sTarget + give us an idea how this target frame must be searched. + */ + + static css::uno::Reference< css::frame::XFrame > static_impl_searchRightTargetFrame(const css::uno::Reference< css::frame::XFrame >& xFrame , + std::u16string_view sTarget); + +}; // class CloseDispatcher + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/dispatch/dispatchinformationprovider.hxx b/framework/inc/dispatch/dispatchinformationprovider.hxx new file mode 100644 index 0000000000..e5bdeddc1f --- /dev/null +++ b/framework/inc/dispatch/dispatchinformationprovider.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 . + */ + +#pragma once + +#include +#include + +#include +#include +#include + +namespace framework{ + +/*-************************************************************************************************************ + @short a helper to merge dispatch information of different sources together. +*//*-*************************************************************************************************************/ +class DispatchInformationProvider final : public ::cppu::WeakImplHelper< css::frame::XDispatchInformationProvider > +{ + + // member + private: + + css::uno::Reference< css::uno::XComponentContext > m_xContext; + css::uno::WeakReference< css::frame::XFrame > m_xFrame; + + // interface + public: + + DispatchInformationProvider(css::uno::Reference< css::uno::XComponentContext > xContext , + const css::uno::Reference< css::frame::XFrame >& xFrame); + + virtual ~DispatchInformationProvider() override; + + virtual css::uno::Sequence< sal_Int16 > SAL_CALL getSupportedCommandGroups() override; + + virtual css::uno::Sequence< css::frame::DispatchInformation > SAL_CALL getConfigurableDispatchInformation(sal_Int16 nCommandGroup) override; + + // helper + private: + + css::uno::Sequence< css::uno::Reference< css::frame::XDispatchInformationProvider > > implts_getAllSubProvider(); + +}; // class DispatchInformationProvider + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/dispatch/dispatchprovider.hxx b/framework/inc/dispatch/dispatchprovider.hxx new file mode 100644 index 0000000000..c6656948eb --- /dev/null +++ b/framework/inc/dispatch/dispatchprovider.hxx @@ -0,0 +1,117 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 + +#include +#include +#include +#include +#include + +#include +#include + +namespace framework{ + +/** + @descr We know some special dispatch objects with different functionality. + The can be created internally by the following DispatchProvider. + Here we define some identifier to force creation of the right one. +*/ +enum EDispatchHelper +{ + E_DEFAULTDISPATCHER , + E_CREATEDISPATCHER , + E_BLANKDISPATCHER , + E_SELFDISPATCHER , + E_CLOSEDISPATCHER , + E_STARTMODULEDISPATCHER +}; + +/** + @short implement a helper for XDispatchProvider interface + @descr The result of a queryDispatch() call depends from the owner, which use an instance of this class. + (frame, desktop) All of them must provides different functionality. + E.g: - task can be created by the desktop only + - a task can have a beamer as direct child + - a normal frame never can create a new one by himself + + @attention Use this class as member only! Never use it as baseclass. + XInterface will be ambiguous and we hold a weakreference to our OWNER - not to our SUPERCLASS! + + @base OWeakObject + provides ref count and weak mechanism + + @devstatus ready to use + @threadsafe yes +*/ +class DispatchProvider final : public ::cppu::WeakImplHelper< css::frame::XDispatchProvider > +{ + /* member */ + private: + /// reference to global service manager to create new services + css::uno::Reference< css::uno::XComponentContext > m_xContext; + /// weakreference to owner frame (Don't use a hard reference. Owner can't delete us then!) + css::uno::WeakReference< css::frame::XFrame > m_xFrame; + /// cache of some other dispatch provider which are registered inside configuration to handle special URL protocols + HandlerCache m_aProtocolHandlerCache; + std::unordered_map> + m_aProtocolHandlers; + + /* interface */ + public: + + DispatchProvider( css::uno::Reference< css::uno::XComponentContext > xContext , + const css::uno::Reference< css::frame::XFrame >& xFrame ); + + 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; + + void ClearProtocolHandlers() { m_aProtocolHandlers.clear(); } + + /* helper */ + private: + // Let him protected! So nobody can use us as base ... + virtual ~DispatchProvider() override; + + css::uno::Reference< css::frame::XDispatch > implts_getOrCreateDispatchHelper ( EDispatchHelper eHelper , + const css::uno::Reference< css::frame::XFrame >& xOwner , + const OUString& sTarget = OUString() , + sal_Int32 nSearchFlags = 0 ); + bool implts_isLoadableContent ( const css::util::URL& aURL ); + css::uno::Reference< css::frame::XDispatch > implts_queryDesktopDispatch ( const css::uno::Reference< css::frame::XFrame >& xDesktop , + const css::util::URL& aURL , + const OUString& sTargetFrameName , + sal_Int32 nSearchFlags ); + css::uno::Reference< css::frame::XDispatch > implts_queryFrameDispatch ( const css::uno::Reference< css::frame::XFrame >& xFrame , + const css::util::URL& aURL , + const OUString& sTargetFrameName , + sal_Int32 nSearchFlags ); + css::uno::Reference< css::frame::XDispatch > implts_searchProtocolHandler ( const css::util::URL& aURL ); + +}; // class DispatchProvider + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/dispatch/interceptionhelper.hxx b/framework/inc/dispatch/interceptionhelper.hxx new file mode 100644 index 0000000000..89339b1f84 --- /dev/null +++ b/framework/inc/dispatch/interceptionhelper.hxx @@ -0,0 +1,256 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +namespace framework{ + +class DispatchProvider; + +/** @short implements a helper to support interception with additional functionality. + + @descr This helper implements the complete XDispatchProviderInterception interface with + master/slave functionality AND using of optional features like registration of URL pattern! + + @attention Don't use this class as direct member - use it dynamically. Do not derive from this class. + We hold a weakreference to our owner not to our superclass. + */ +class InterceptionHelper final : public ::cppu::WeakImplHelper< + css::frame::XDispatchProvider, + css::frame::XDispatchProviderInterception, + css::lang::XEventListener > +{ + + // structs, helper + + /** @short bind an interceptor component to its URL pattern registration. */ + struct InterceptorInfo + { + /** @short reference to the interceptor component. */ + css::uno::Reference< css::frame::XDispatchProvider > xInterceptor; + + /** @short it's registration for URL patterns. + + @descr If the interceptor component does not support the optional interface + XInterceptorInfo, it will be registered for one pattern "*" by default. + That would make it possible to handle it in the same manner then real + registered interceptor objects and we must not implement any special code. */ + css::uno::Sequence< OUString > lURLPattern; + }; + + /** @short implements a list of items of type InterceptorInfo, and provides some special + functions on it. + + @descr Because interceptor objects can be registered for URL patterns, + it supports a wildcard search on all list items. + */ + class InterceptorList : public ::std::deque< InterceptorInfo > + { + public: + + /** @short search for an interceptor inside this list using it's reference. + + @param xInterceptor + points to the interceptor object, which should be located inside this list. + + @return An iterator object, which points directly to the located item inside this list. + In case no interceptor could be found, it points to the end of this list! + */ + iterator findByReference(const css::uno::Reference< css::frame::XDispatchProviderInterceptor >& xInterceptor) + { + iterator pIt; + for (pIt=begin(); pIt!=end(); ++pIt) + { + if (pIt->xInterceptor == xInterceptor) + return pIt; + } + return end(); + } + + /** @short search for an interceptor inside this list using it's reference. + + @param sURL + URL which should match with a registered pattern. + + @return An iterator object, which points directly to the located item inside this list. + In case no interceptor could be found, it points to the end of this list! + */ + iterator findByPattern(std::u16string_view sURL) + { + for (iterator pIt=begin(); pIt!=end(); ++pIt) + { + for (const OUString& pattern : pIt->lURLPattern) + { + WildCard aPattern(pattern); + if (aPattern.Matches(sURL)) + return pIt; + } + } + return end(); + } + }; + + // member + + private: + + /** @short reference to the frame, which uses this instance to implement its own interception. + + @descr We hold a weak reference only, to make disposing operations easy. */ + css::uno::WeakReference< css::frame::XFrame > m_xOwnerWeak; + + /** @short this interception helper implements the top level master of an interceptor list ... + but this member is the lowest possible slave! */ + rtl::Reference< DispatchProvider > m_xSlave; + + /** @short contains all registered interceptor objects. */ + InterceptorList m_lInterceptionRegs; + + // native interface + + public: + + /** @short creates a new interception helper instance. + + @param xOwner + points to the frame, which use this instances to support its own interception interfaces. + + @param xSlave + an outside creates dispatch provider, which has to be used here as lowest slave "interceptor". + */ + InterceptionHelper(const css::uno::Reference< css::frame::XFrame >& xOwner, + rtl::Reference< DispatchProvider > xSlave); + + private: + + /** @short standard destructor. + + @descr This method destruct an instance of this class and clear some member. + This method is protected, because it's not allowed to use this class as a direct member! + You MUST use a dynamical instance (pointer). That's the reason for a protected dtor. + */ + virtual ~InterceptionHelper() override; + + // uno interface + + public: + + // XDispatchProvider + + /** @short query for a dispatch, which implements the requested feature. + + @descr We search inside our list of interception registrations, to locate + any interested interceptor. In case no interceptor exists or nobody is + interested on this URL our lowest slave will be used. + + @param aURL + describes the requested dispatch functionality. + + @param sTargetFrameName + the name of the target frame or a special name like "_blank", "_top" ... + Won't be used here ... but may by one of our registered interceptor objects + or our slave. + + @param nSearchFlags + optional search parameter for targeting, if sTargetFrameName isn't a special one. + + @return A valid dispatch object, if any interceptor or at least our slave is interested on the given URL; + or NULL otherwise. + */ + virtual css::uno::Reference< css::frame::XDispatch > SAL_CALL queryDispatch(const css::util::URL& aURL , + const OUString& sTargetFrameName, + sal_Int32 nSearchFlags ) override; + + // XDispatchProvider + + /** @short implements an optimized queryDispatch() for remote. + + @descr It capsulate more than one queryDispatch() requests and return a list of dispatch objects + as result. Because both lists (in and out) correspond together, it's not allowed to + pack it - means suppress NULL references! + + @param lDescriptor + a list of queryDispatch() arguments. + + @return A list of dispatch objects. + */ + virtual css::uno::Sequence< css::uno::Reference< css::frame::XDispatch > > SAL_CALL queryDispatches(const css::uno::Sequence< css::frame::DispatchDescriptor >& lDescriptor) override; + + // XDispatchProviderInterception + + /** @short register an interceptor. + + @descr Somebody can register himself to intercept all or some special dispatches. + It's depend from his supported interfaces. If he implement XInterceptorInfo + he his called for some special URLs only - otherwise we call it for every request! + + @attention We don't check for double registrations here! + + @param xInterceptor + reference to interceptor, which wishes to be registered here. + + @throw A RuntimeException if the given reference is NULL! + */ + virtual void SAL_CALL registerDispatchProviderInterceptor(const css::uno::Reference< css::frame::XDispatchProviderInterceptor >& xInterceptor) override; + + // XDispatchProviderInterception + + /** @short release an interceptor. + + @descr Remove the registered interceptor from our internal list + and delete all special information about it. + + @param xInterceptor + reference to the interceptor, which wishes to be deregistered. + + @throw A RuntimeException if the given reference is NULL! + */ + virtual void SAL_CALL releaseDispatchProviderInterceptor( const css::uno::Reference< css::frame::XDispatchProviderInterceptor >& xInterceptor ) override; + + // XEventListener + + /** @short Is called from our owner frame, in case he will be disposed. + + @descr We have to release all references to him then. + Normally we will die by ref count too... + */ + virtual void SAL_CALL disposing(const css::lang::EventObject& aEvent) override; + + rtl::Reference GetSlave() const { return m_xSlave; } + +}; // class InterceptionHelper + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/dispatch/mailtodispatcher.hxx b/framework/inc/dispatch/mailtodispatcher.hxx new file mode 100644 index 0000000000..d6fe52d93b --- /dev/null +++ b/framework/inc/dispatch/mailtodispatcher.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 . + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace framework{ + +/** + @short protocol handler for "mailto:" URLs + @descr It's a special dispatch object which is used registered for "mailto:*" URLs and + will be automatically used from the framework dispatch mechanism if such URL occurred. + + @base OWeakObject + provides XWeak and ref count mechanism + + @devstatus ready to use +*/ +class MailToDispatcher final : public ::cppu::WeakImplHelper< + css::lang::XServiceInfo, + css::frame::XDispatchProvider, + css::frame::XNotifyingDispatch> // => XDispatch +{ + /* member */ + private: + + /// reference to global uno service manager which had created us + css::uno::Reference< css::uno::XComponentContext > m_xContext; + + /* interface */ + public: + + // ctor/dtor + MailToDispatcher( css::uno::Reference< css::uno::XComponentContext > xContext ); + virtual ~MailToDispatcher( ) override; + + /* interface 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; + + // XDispatchProvider + virtual css::uno::Reference< css::frame::XDispatch > SAL_CALL queryDispatch ( const css::util::URL& aURL , + const OUString& sTarget , + sal_Int32 nFlags ) override; + virtual css::uno::Sequence< css::uno::Reference< css::frame::XDispatch > > SAL_CALL queryDispatches( const css::uno::Sequence< css::frame::DispatchDescriptor >& lDescriptor ) override; + + // XNotifyingDispatch + virtual void SAL_CALL dispatchWithNotification( const css::util::URL& aURL , + const css::uno::Sequence< css::beans::PropertyValue >& lArguments, + const css::uno::Reference< css::frame::XDispatchResultListener >& xListener ) 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; + + /* internal */ + private: + /// @throws css::uno::RuntimeException + bool implts_dispatch( const css::util::URL& aURL ); + +}; // class MailToDispatcher + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/dispatch/oxt_handler.hxx b/framework/inc/dispatch/oxt_handler.hxx new file mode 100644 index 0000000000..09b80ea8ca --- /dev/null +++ b/framework/inc/dispatch/oxt_handler.hxx @@ -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 . + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace framework{ + +/*-************************************************************************************************************ + @short handler to detect and play sounds ("wav" and "au" only!) + @descr Register this implementation as a content handler to detect and/or play wav- and au-sounds. + It doesn't depend from the target platform. But one instance of this class + can play one sound at the same time only. Means every new dispatch request will stop the + might still running one. So we support one operation/one URL/one listener at the same time + only. + + @devstatus ready + @threadsafe yes +*//*-*************************************************************************************************************/ +class Oxt_Handler final : public ::cppu::WeakImplHelper< + css::lang::XServiceInfo, + css::frame::XNotifyingDispatch, // => XDispatch + css::document::XExtendedFilterDetection > +{ + + public: + + Oxt_Handler( css::uno::Reference< css::uno::XComponentContext > ); + virtual ~Oxt_Handler( ) override; + + /* interface 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; + + // XNotifyingDispatch + + virtual void SAL_CALL dispatchWithNotification(const css::util::URL& aURL , + const css::uno::Sequence< css::beans::PropertyValue >& lArguments, + const css::uno::Reference< css::frame::XDispatchResultListener >& xListener ) override; + + // XDispatch + + virtual void SAL_CALL dispatch ( const css::util::URL& aURL , + const css::uno::Sequence< css::beans::PropertyValue >& lArguments ) override; + // not supported ! + 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 {}; + + // XExtendedFilterDetection + virtual OUString SAL_CALL detect ( css::uno::Sequence< css::beans::PropertyValue >& lDescriptor ) override; + + private: + std::mutex m_mutex; + + css::uno::Reference< css::uno::XComponentContext > m_xContext; /// global uno service factory to create new services + +}; + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/dispatch/popupmenudispatcher.hxx b/framework/inc/dispatch/popupmenudispatcher.hxx new file mode 100644 index 0000000000..0d22beb864 --- /dev/null +++ b/framework/inc/dispatch/popupmenudispatcher.hxx @@ -0,0 +1,117 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace framework{ + +/*-************************************************************************************************************ + @short helper for desktop only(!) to create new tasks on demand for dispatches + @descr Use this class as member only! Never use it as baseclass. + XInterface will be ambiguous and we hold a weakcss::uno::Reference to our OWNER - not to our SUPERCLASS! + + @implements XInterface + XDispatch + XLoadEventListener + XFrameActionListener + XEventListener + @base OWeakObject + + @devstatus ready to use +*//*-*************************************************************************************************************/ +class PopupMenuDispatcher final : public ::cppu::WeakImplHelper< + css::lang::XServiceInfo, + css::frame::XDispatchProvider, + css::frame::XDispatch, + css::frame::XFrameActionListener, + css::lang::XInitialization > +{ + + // public methods + + public: + + // constructor / destructor + PopupMenuDispatcher( css::uno::Reference< css::uno::XComponentContext > xContext ); + + /* interface 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 >& lArguments ) override; + // XDispatchProvider + virtual css::uno::Reference< css::frame::XDispatch > SAL_CALL queryDispatch( + const css::util::URL& aURL , + const OUString& sTarget , + sal_Int32 nFlags ) override; + + virtual css::uno::Sequence< css::uno::Reference< css::frame::XDispatch > > SAL_CALL queryDispatches( + const css::uno::Sequence< css::frame::DispatchDescriptor >& lDescriptor ) override; + + // XDispatch + virtual void SAL_CALL dispatch( const css::util::URL& aURL, + const css::uno::Sequence< css::beans::PropertyValue >& seqProperties ) 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; + + // XFrameActionListener + virtual void SAL_CALL frameAction( const css::frame::FrameActionEvent& aEvent ) override; + + // XEventListener + void SAL_CALL disposing( const css::lang::EventObject& aEvent ) override; + + private: + virtual ~PopupMenuDispatcher() override; + + void impl_RetrievePopupControllerQuery(); + + css::uno::WeakReference< css::frame::XFrame > m_xWeakFrame; /// css::uno::WeakReference to frame (Don't use a hard css::uno::Reference. Owner can't delete us then!) + css::uno::Reference< css::container::XNameAccess > m_xPopupCtrlQuery; /// reference to query for popup controller + css::uno::Reference< css::uri::XUriReferenceFactory > m_xUriRefFactory; /// reference to the uri reference factory + css::uno::Reference< css::uno::XComponentContext > m_xContext; /// factory shared with our owner to create new services! + bool m_bAlreadyDisposed; /// Protection against multiple disposing calls. + bool m_bActivateListener; /// dispatcher is listener for frame activation + +}; // class PopupMenuDispatcher + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/dispatch/servicehandler.hxx b/framework/inc/dispatch/servicehandler.hxx new file mode 100644 index 0000000000..0b1ee8ab93 --- /dev/null +++ b/framework/inc/dispatch/servicehandler.hxx @@ -0,0 +1,100 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace framework{ + +/** + @short protocol handler for "service:*" URLs + @descr It's a special dispatch/provider object which is registered for such URL pattern and will + be automatically used by the framework dispatch mechanism if such URL occurred. + His job is to create any registered uno components which must be coded inside + dispatched URL (may with some optional given parameters). After that such created + service must be hold his self alive. Such mechanism can be useful for UI components + (e.g. Dialogs, Wizards) only. + + @base OWeakObject + provides XWeak and ref count mechanism + + @devstatus ready to use +*/ +class ServiceHandler final : public ::cppu::WeakImplHelper< + css::lang::XServiceInfo , + css::frame::XDispatchProvider , + css::frame::XNotifyingDispatch > // => XDispatch +{ + /* member */ + private: + + /// reference to global uno service manager which had created us + css::uno::Reference< css::uno::XComponentContext > m_xContext; + + /* interface */ + public: + + // ctor/dtor + ServiceHandler( css::uno::Reference< css::uno::XComponentContext > ); + virtual ~ServiceHandler( ) override; + + /* interface 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; + + // XDispatchProvider + virtual css::uno::Reference< css::frame::XDispatch > SAL_CALL queryDispatch ( const css::util::URL& aURL , + const OUString& sTarget , + sal_Int32 nFlags ) override; + virtual css::uno::Sequence< css::uno::Reference< css::frame::XDispatch > > SAL_CALL queryDispatches( const css::uno::Sequence< css::frame::DispatchDescriptor >& lDescriptor ) override; + + // XNotifyingDispatch + virtual void SAL_CALL dispatchWithNotification( const css::util::URL& aURL , + const css::uno::Sequence< css::beans::PropertyValue >& lArguments, + const css::uno::Reference< css::frame::XDispatchResultListener >& xListener ) 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; + + /* internal */ + private: + /// @throws css::uno::RuntimeException + css::uno::Reference< css::uno::XInterface > implts_dispatch( const css::util::URL& aURL ); + +}; // class ServiceHandler + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/dispatch/startmoduledispatcher.hxx b/framework/inc/dispatch/startmoduledispatcher.hxx new file mode 100644 index 0000000000..c575004d49 --- /dev/null +++ b/framework/inc/dispatch/startmoduledispatcher.hxx @@ -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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 +#include +#include +#include +#include +#include + +#include + +namespace framework{ + +/** + @short helper to handle all URLs related to the StartModule + */ +class StartModuleDispatcher final : public ::cppu::WeakImplHelper< + css::frame::XNotifyingDispatch, // => XDispatch + css::frame::XDispatchInformationProvider > +{ + + // member + + private: + + /** @short reference to a uno service manager, + which can be used to create own needed + uno resources. */ + css::uno::Reference< css::uno::XComponentContext > m_xContext; + + // native interface + + public: + + /** @short connect a new StartModuleDispatcher instance to its "owner frame". + + @descr Such "owner frame" is used as context for all related operations. + + @param xContext + a UNO service manager, which is needed to create UNO resource + internally. + + @param xFrame + the frame where the corresponding dispatch was started. + */ + StartModuleDispatcher(css::uno::Reference< css::uno::XComponentContext > xContext); + + /** @short does nothing real. */ + virtual ~StartModuleDispatcher() override; + + // uno interface + + public: + + // XNotifyingDispatch + virtual void SAL_CALL dispatchWithNotification( const css::util::URL& aURL , + const css::uno::Sequence< css::beans::PropertyValue >& lArguments, + const css::uno::Reference< css::frame::XDispatchResultListener >& xListener ) 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; + + // XDispatchInformationProvider + virtual css::uno::Sequence< sal_Int16 > SAL_CALL getSupportedCommandGroups ( ) override; + virtual css::uno::Sequence< css::frame::DispatchInformation > SAL_CALL getConfigurableDispatchInformation( sal_Int16 nCommandGroup ) override; + + // internal helper + + private: + + /** @short check if StartModule can be shown. + */ + bool implts_isBackingModePossible(); + + /** @short open the special BackingComponent (now StartModule) + */ + void implts_establishBackingMode(); + + /** @short notify a DispatchResultListener. + + @descr We check the listener reference before we use it. + So this method can be called every time! + + @parama xListener + the listener, which should be notified. + Can be null! + + @param nState + directly used as css::frame::DispatchResultState value. + + @param aResult + not used yet really ... + */ + void implts_notifyResultListener(const css::uno::Reference< css::frame::XDispatchResultListener >& xListener, + sal_Int16 nState , + const css::uno::Any& aResult ); + +}; // class StartModuleDispatcher + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/dispatch/systemexec.hxx b/framework/inc/dispatch/systemexec.hxx new file mode 100644 index 0000000000..67a36b7246 --- /dev/null +++ b/framework/inc/dispatch/systemexec.hxx @@ -0,0 +1,99 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace framework{ + +/** + @short protocol handler for "systemexec:*" URLs + @descr It's a special dispatch/provider object which is registered for such URL pattern and will + be automatically used by the framework dispatch mechanism if such URL occurred. + It forwards all URL's to the underlying operating system. + So it would e.g. be possible to open HTML files outside the office within a web browser. + + @base OWeakObject + provides XWeak and ref count mechanism + + @devstatus ready to use +*/ +class SystemExec final : public ::cppu::WeakImplHelper< + css::lang::XServiceInfo , + css::frame::XDispatchProvider , + css::frame::XNotifyingDispatch > // => XDispatch +{ + /* member */ + private: + + /// reference to global uno service manager which had created us + css::uno::Reference< css::uno::XComponentContext > m_xContext; + + /* interface */ + public: + + // ctor/dtor + SystemExec( css::uno::Reference< css::uno::XComponentContext > xContext ); + virtual ~SystemExec( ) override; + + /* interface 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; + + // XDispatchProvider + virtual css::uno::Reference< css::frame::XDispatch > SAL_CALL queryDispatch ( const css::util::URL& aURL , + const OUString& sTarget , + sal_Int32 nFlags ) override; + virtual css::uno::Sequence< css::uno::Reference< css::frame::XDispatch > > SAL_CALL queryDispatches( const css::uno::Sequence< css::frame::DispatchDescriptor >& lDescriptor ) override; + + // XNotifyingDispatch + virtual void SAL_CALL dispatchWithNotification( const css::util::URL& aURL , + const css::uno::Sequence< css::beans::PropertyValue >& lArguments, + const css::uno::Reference< css::frame::XDispatchResultListener >& xListener ) 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; + + /* internal */ + private: + + void impl_notifyResultListener(const css::uno::Reference< css::frame::XDispatchResultListener >& xListener, + const sal_Int16 nState ); + +}; // class SystemExec + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/helper/dockingareadefaultacceptor.hxx b/framework/inc/helper/dockingareadefaultacceptor.hxx new file mode 100644 index 0000000000..f591e12ef7 --- /dev/null +++ b/framework/inc/helper/dockingareadefaultacceptor.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 . + */ + +#pragma once + +#include +#include + +#include +#include + +namespace framework{ + +class DockingAreaDefaultAcceptor final : public ::cppu::WeakImplHelper< css::ui::XDockingAreaAcceptor > +{ + public: + + // constructor / destructor + + /*-**************************************************************************************************** + @short constructor to initialize this instance + @descr A docking area acceptor + But we need an instance to create more than one enumerations to the same tasklist! + + @seealso class Desktop + @seealso class OTasksEnumeration + + @param "xOwner" is a reference to our owner and must be the desktop! + @param "pTasks" is a pointer to the taskcontainer of the desktop. We need it to create a new enumeration. + @onerror Do nothing and reset this object to default with an empty list. + *//*-*****************************************************************************************************/ + + DockingAreaDefaultAcceptor( const css::uno::Reference< css::frame::XFrame >& xOwner ); + virtual ~DockingAreaDefaultAcceptor() override; + + // XDockingAreaAcceptor + + virtual css::uno::Reference< css::awt::XWindow > SAL_CALL getContainerWindow() override; + virtual sal_Bool SAL_CALL requestDockingAreaSpace( const css::awt::Rectangle& RequestedSpace ) override; + virtual void SAL_CALL setDockingAreaSpace( const css::awt::Rectangle& BorderSpace ) override; + + // variables + // (should be private everyway!) + + private: + + css::uno::WeakReference< css::frame::XFrame > m_xOwner; /// weak reference to our frame object! +}; + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/helper/mischelper.hxx b/framework/inc/helper/mischelper.hxx new file mode 100644 index 0000000000..04284e8ff8 --- /dev/null +++ b/framework/inc/helper/mischelper.hxx @@ -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 . + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +namespace framework +{ + +// menu ids for language status bar control +enum LangMenuIDs +{ + MID_LANG_SEL_1 = 1, // need to start with 1 since xPopupMenu->execute will return 0 if the menu is cancelled + MID_LANG_SEL_2, + MID_LANG_SEL_3, + MID_LANG_SEL_4, + MID_LANG_SEL_5, + MID_LANG_SEL_6, + MID_LANG_SEL_7, + MID_LANG_SEL_8, + MID_LANG_SEL_9, + MID_LANG_SEL_NONE, + MID_LANG_SEL_RESET, + MID_LANG_SEL_MORE, + MID_LANG_DEF_NONE, + MID_LANG_DEF_RESET, + MID_LANG_DEF_MORE, + + MID_LANG_PARA_SEPARATOR, + MID_LANG_PARA_STRING, + + MID_LANG_PARA_1, + MID_LANG_PARA_2, + MID_LANG_PARA_3, + MID_LANG_PARA_4, + MID_LANG_PARA_5, + MID_LANG_PARA_6, + MID_LANG_PARA_7, + MID_LANG_PARA_8, + MID_LANG_PARA_9, + MID_LANG_PARA_NONE, + MID_LANG_PARA_RESET, + MID_LANG_PARA_MORE, +}; + +inline bool IsScriptTypeMatchingToLanguage( SvtScriptType nScriptType, LanguageType nLang ) +{ + return bool(nScriptType & SvtLanguageOptions::GetScriptTypeOfLanguage( nLang )); +} + +inline void RetrieveTypeNameFromResourceURL( std::u16string_view aResourceURL, OUString& aType, OUString& aName ) +{ + static constexpr std::u16string_view RESOURCEURL_PREFIX = u"private:resource/"; + + if (o3tl::starts_with(aResourceURL, RESOURCEURL_PREFIX )) + { + size_t nIdx = RESOURCEURL_PREFIX.size(); + while (nIdx < aResourceURL.size() && aResourceURL[nIdx]=='/') + ++nIdx; + if (nIdx >= aResourceURL.size()) + return; + aType = o3tl::getToken(aResourceURL, u'/', nIdx); + if (nIdx == std::u16string_view::npos) + return; + while (nIdx < aResourceURL.size() && aResourceURL[nIdx]=='/') + ++nIdx; + if (nIdx >= aResourceURL.size()) + return; + aName = o3tl::getToken(aResourceURL, u'/', nIdx); + } +} + +class LanguageGuessingHelper +{ + mutable css::uno::Reference< css::linguistic2::XLanguageGuessing > m_xLanguageGuesser; + css::uno::Reference< css::uno::XComponentContext > m_xContext; + +public: + LanguageGuessingHelper(css::uno::Reference< css::uno::XComponentContext > _xContext) : m_xContext(std::move(_xContext)){} + + css::uno::Reference< css::linguistic2::XLanguageGuessing > const & GetGuesser() const; +}; + +void FillLangItems( std::set< OUString > &rLangItems, + const css::uno::Reference< css::frame::XFrame > &rxFrame, + const LanguageGuessingHelper & rLangGuessHelper, + SvtScriptType nScriptType, + const OUString & rCurLang, + const OUString & rKeyboardLang, + const OUString & rGuessedTextLang ); + +//It's common for an object to want to create and own a Broadcaster and set +//itself as a Listener on its own Broadcaster member. + +//However, calling addListener on a Broadcaster means that the Broadcaster adds +//a reference to the Listener leading to an ownership cycle where the Listener +//owns the Broadcaster which "owns" the Listener. + +//The WeakContainerListener allows breaking this cycle and retrofitting +//afflicted implementations fairly easily. + +//OriginalListener owns the Broadcaster which "owns" the WeakContainerListener +//which forwards the events to the OriginalListener without taking ownership of +//it. +class WeakContainerListener final : public ::cppu::WeakImplHelper +{ + private: + css::uno::WeakReference mxOwner; + + public: + WeakContainerListener(css::uno::Reference const & xOwner) + : mxOwner(xOwner) + { + } + + // container.XContainerListener + virtual void SAL_CALL elementInserted(const css::container::ContainerEvent& rEvent) override + { + css::uno::Reference xOwner(mxOwner.get(), + css::uno::UNO_QUERY); + if (xOwner.is()) + xOwner->elementInserted(rEvent); + } + + virtual void SAL_CALL elementRemoved(const css::container::ContainerEvent& rEvent) override + { + css::uno::Reference xOwner(mxOwner.get(), + css::uno::UNO_QUERY); + if (xOwner.is()) + xOwner->elementRemoved(rEvent); + } + + virtual void SAL_CALL elementReplaced(const css::container::ContainerEvent& rEvent) override + { + css::uno::Reference xOwner(mxOwner.get(), + css::uno::UNO_QUERY); + if (xOwner.is()) + xOwner->elementReplaced(rEvent); + } + + // lang.XEventListener + virtual void SAL_CALL disposing(const css::lang::EventObject& rEvent) override + { + css::uno::Reference xOwner(mxOwner.get(), + css::uno::UNO_QUERY); + if (xOwner.is()) + xOwner->disposing(rEvent); + + } +}; + +class WeakChangesListener final : public ::cppu::WeakImplHelper +{ + private: + css::uno::WeakReference mxOwner; + + public: + WeakChangesListener(css::uno::Reference const & xOwner) + : mxOwner(xOwner) + { + } + + // util.XChangesListener + virtual void SAL_CALL changesOccurred(const css::util::ChangesEvent& rEvent) override + { + css::uno::Reference xOwner(mxOwner.get(), + css::uno::UNO_QUERY); + if (xOwner.is()) + xOwner->changesOccurred(rEvent); + } + + // lang.XEventListener + virtual void SAL_CALL disposing(const css::lang::EventObject& rEvent) override + { + css::uno::Reference xOwner(mxOwner.get(), + css::uno::UNO_QUERY); + if (xOwner.is()) + xOwner->disposing(rEvent); + + } +}; + +class WeakDocumentEventListener final : public ::cppu::WeakImplHelper +{ + private: + css::uno::WeakReference mxOwner; + + public: + WeakDocumentEventListener(css::uno::Reference const & xOwner) + : mxOwner(xOwner) + { + } + + virtual void SAL_CALL documentEventOccured(const css::document::DocumentEvent& rEvent) override + { + css::uno::Reference xOwner(mxOwner.get(), + css::uno::UNO_QUERY); + if (xOwner.is()) + xOwner->documentEventOccured(rEvent); + + } + + // lang.XEventListener + virtual void SAL_CALL disposing(const css::lang::EventObject& rEvent) override + { + css::uno::Reference xOwner(mxOwner.get(), + css::uno::UNO_QUERY); + if (xOwner.is()) + xOwner->disposing(rEvent); + + } +}; + +css::uno::Reference +GetFirstListenerWith_Impl( + css::uno::Reference const & xComponentContext, + css::uno::Reference const& xEventFocus, + std::function const&)> const& rPredicate); + +extern auto (*g_pGetMultiplexerListener)( + css::uno::Reference const & xComponentContext, + css::uno::Reference const&, + std::function const&)> const&) + -> css::uno::Reference; + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/helper/ocomponentaccess.hxx b/framework/inc/helper/ocomponentaccess.hxx new file mode 100644 index 0000000000..9c444953a0 --- /dev/null +++ b/framework/inc/helper/ocomponentaccess.hxx @@ -0,0 +1,161 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 +#include +#include +#include +#include + +#include +#include +#include + +namespace framework{ + +/*-************************************************************************************************************ + @short implement XEnumerationAccess interface as helper to create many oneway enumeration of components + @descr We share mutex and framecontainer with our owner and have full access to its child tasks. + (Our owner can be the Desktop only!) We create oneway enumerations on demand. These "lists" + can be used for one time only. Step during the list from first to last element. + (The type of created enumerations is OComponentEnumeration.) + + @implements XInterface + XTypeProvider + XEnumerationAccess + XElementAccess + + @base OWeakObject + + @devstatus ready to use +*//*-*************************************************************************************************************/ + +class OComponentAccess final : public ::cppu::WeakImplHelper< css::container::XEnumerationAccess > +{ + + // public methods + + public: + + // constructor / destructor + + /*-**************************************************************************************************** + @short constructor to initialize this instance + @descr A desktop will create an enumeration-access-object. An enumeration is a oneway-list and a + snapshot of the components of current tasks under the desktop. + But we need an instance to create more than one enumerations at different times! + + @seealso class Desktop + @seealso class OComponentEnumeration + + @param "xOwner" is a reference to our owner and must be the desktop! + @onerror Do nothing and reset this object to default with an empty list. + *//*-*****************************************************************************************************/ + + OComponentAccess( const css::uno::Reference< css::frame::XDesktop >& xOwner ); + + // XEnumerationAccess + + /*-**************************************************************************************************** + @short create a new enumeration of components + @descr You can call this method to get a new snapshot from all components of all tasks of the desktop as an enumeration. + + @seealso interface XEnumerationAccess + @seealso interface XEnumeration + @seealso class Desktop + @return If the desktop and some components exist => a valid reference to an enumeration
+ An NULL-reference, other way. + *//*-*****************************************************************************************************/ + + virtual css::uno::Reference< css::container::XEnumeration > SAL_CALL createEnumeration() override; + + // XElementAccess + + /*-**************************************************************************************************** + @short get the type of elements in enumeration + @seealso interface XElementAccess + @seealso class OComponentEnumeration + @return The uno-type XComponent. + *//*-*****************************************************************************************************/ + + virtual css::uno::Type SAL_CALL getElementType() override; + + /*-**************************************************************************************************** + @short get state of componentlist of enumeration. + @seealso interface XElementAccess + @return sal_True ,if more than 0 elements exist. + @return sal_False ,otherwise. + *//*-*****************************************************************************************************/ + + virtual sal_Bool SAL_CALL hasElements() override; + + // protected methods + + private: + + /*-**************************************************************************************************** + @short standard destructor + @descr This method destruct an instance of this class and clear some member. + Don't use an instance of this class as normal member. Use it dynamically with a pointer. + We hold a weakreference to our owner and not to our superclass! + That's the reason for a protected dtor. + + @seealso class Desktop + *//*-*****************************************************************************************************/ + + virtual ~OComponentAccess() override; + + + /*-**************************************************************************************************** + @short recursive method (!) to collect all components of all frames from the subtree of given node + @descr This is necessary to create the enumeration. + + @seealso method createEnumeration + + @param "xNode" , root of subtree and start point of search + @param "seqComponents", result list of search. We can't use a return value, we search recursive + and must collect all information. + *//*-*****************************************************************************************************/ + + void impl_collectAllChildComponents( const css::uno::Reference< css::frame::XFramesSupplier >& xNode , + std::vector< css::uno::Reference< css::lang::XComponent > >& seqComponents ); + + /*-**************************************************************************************************** + @short get the component of a frame + @descr The component of a frame can be the window, the controller or the model. + + @seealso method createEnumeration + + @param "xFrame", frame which contains the component + @return A reference to the component of given frame. + + @onerror A null reference is returned. + *//*-*****************************************************************************************************/ + + css::uno::Reference< css::lang::XComponent > impl_getFrameComponent( const css::uno::Reference< css::frame::XFrame >& xFrame ) const; + + css::uno::WeakReference< css::frame::XDesktop > m_xOwner; /// weak reference to the desktop object! + +}; // class OComponentAccess + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/helper/ocomponentenumeration.hxx b/framework/inc/helper/ocomponentenumeration.hxx new file mode 100644 index 0000000000..5791b38779 --- /dev/null +++ b/framework/inc/helper/ocomponentenumeration.hxx @@ -0,0 +1,160 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 +#include +#include + +#include +#include + +namespace framework{ + +/*-************************************************************************************************************ + @short implement a helper for a oneway enumeration of components + @descr You can step during this list only for one time! It's a snapshot. + Don't forget to release the reference. You are the owner of an instance of this implementation. + You can't use this as a baseclass. Please use it as a dynamical object for return. + + @implements XInterface + XTypeProvider + XEventListener + XEnumeration + + @base OWeakObject + + @devstatus ready to use + @threadsafe yes +*//*-*************************************************************************************************************/ + +class OComponentEnumeration final : public ::cppu::WeakImplHelper< css::container::XEnumeration,css::lang::XEventListener > +{ + + // public methods + + public: + + // constructor / destructor + + /*-**************************************************************************************************** + @short constructor to initialize this enumeration + @descr An enumeration is a list with oneway-access! You can get every member only for one time. + This method allow to initialize this oneway list with values. + @param "seqComponents" is a sequence of interfaces, which are components. + @onerror Do nothing and reset this object to default with an empty list. + *//*-*****************************************************************************************************/ + + OComponentEnumeration( std::vector< css::uno::Reference< css::lang::XComponent > >&& seqComponents ); + + // XEventListener + + /*-**************************************************************************************************** + @short last chance to release all references and free memory + @descr This method is called, if the enumeration is used completely and has no more elements. + Then we must destroy our list and release all references to other objects. + + @seealso interface XEventListener + + @param "aEvent" describe the source of this event. + *//*-*****************************************************************************************************/ + + virtual void SAL_CALL disposing( const css::lang::EventObject& aEvent ) override; + + // XEnumeration + + /*-**************************************************************************************************** + @short check count of accessible elements of enumeration + @descr You can call this method to get information about accessible elements in future. + Elements you have already gotten are not accessible! + + @seealso interface XEnumeration + @return sal_True = if more elements accessible
+ sal_False = other way + + @onerror sal_False
+ (List is empty and there no accessible elements ...) + *//*-*****************************************************************************************************/ + + virtual sal_Bool SAL_CALL hasMoreElements() override; + + /*-**************************************************************************************************** + @short give the next element, if some exist + @descr If a call "hasMoreElements()" return true, you can get the next element of list. + + @seealso interface XEnumeration + @return A Reference to a component, safed in an Any-structure. + + @onerror If end of enumeration is arrived or there are no elements in list => a NoSuchElementException is thrown. + *//*-*****************************************************************************************************/ + + virtual css::uno::Any SAL_CALL nextElement() override; + + // protected methods + + private: + + /*-**************************************************************************************************** + @short standard destructor + @descr This method destruct an instance of this class and clear some member. + We make it protected, because it's not supported to use this class as normal instance! + You must create it dynamical in memory and use a pointer. + *//*-*****************************************************************************************************/ + + virtual ~OComponentEnumeration() override; + + /*-**************************************************************************************************** + @short reset instance to default values + + @descr There are two ways to delete an instance of this class.
+ 1) delete with destructor
+ 2) dispose from parent or factory or...
+ This method does the same for both ways! It frees used memory and releases references... + + @seealso method dispose() + @seealso destructor ~TaskEnumeration() + *//*-*****************************************************************************************************/ + + void impl_resetObject(); + + + // debug methods + // (should be private everyway!) + + /*-**************************************************************************************************** + @short debug-method to check incoming parameter of some other methods of this class + @descr The following methods are used to check parameters for other methods + of this class. The return value is used directly for an ASSERT(...). + + @seealso ASSERT in implementation! + + @param references to checking variables + @return sal_False on invalid parameter
+ sal_True otherway + *//*-*****************************************************************************************************/ + + sal_uInt32 m_nPosition; /// current position in enumeration + std::vector< css::uno::Reference< css::lang::XComponent > > m_seqComponents; /// list of current components + +}; // class OComponentEnumeration + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/helper/oframes.hxx b/framework/inc/helper/oframes.hxx new file mode 100644 index 0000000000..d29063407a --- /dev/null +++ b/framework/inc/helper/oframes.hxx @@ -0,0 +1,190 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 + +#include +#include + +#include +#include + +namespace framework{ + +/** + @short implement XFrames, XIndexAccess and XElementAccess interfaces as helper for services + @descr Use this class as helper for these interfaces. We share mutex and framecontainer with our owner. + The framecontainer is a member of it from type "FrameContainer". That means; + we have the same information as our owner. In current implementation we use mutex and lock-mechanism + to prevent against compete access. In future we plan support of semaphore! + + @devstatus deprecated + + @ATTENTION Don't use this class as direct member - use it dynamically. Do not derive from this class. + We hold a weakreference to our owner not to our superclass. +*/ +class OFrames final : public ::cppu::WeakImplHelper< css::frame::XFrames > +{ + public: + + /** + @short standard ctor + @descr These initialize a new instance of this class with all needed information for work. + We share framecontainer with owner implementation! It's a threadsafe container. + @param xOwner , reference to our owner. We hold a wekreference to prevent us against cross-references! + @param pFrameContainer , pointer to shared framecontainer of owner. It's valid only, if weakreference is valid! + */ + OFrames( const css::uno::Reference< css::frame::XFrame >& xOwner , + FrameContainer* pFrameContainer ); + + // XFrames + + /** + @short append frame to container + @descr We share the container with our owner. We can do this only, if no lock is set on container. + Valid references are accepted only! + + @seealso class FrameContainer + + @param "xFrame", reference to an existing frame to append. + @onerror We do nothing in release or throw an assert in debug version. + */ + virtual void SAL_CALL append( const css::uno::Reference< css::frame::XFrame >& xFrame ) override; + + /** + @short remove frame from container + @descr This is the companion to append(). We only accept valid references and don't work, if + a lock is set. + + @seealso class FrameContainer + + @param "xFrame", reference to an existing frame to remove. + @onerror We do nothing in release or throw an assert in debug version. + */ + virtual void SAL_CALL remove( const css::uno::Reference< css::frame::XFrame >& xFrame ) override; + + /** + @short return list of all applicable frames for given flags + @descr Call these to get a list of all frames, which are match with given search flags. + @param "nSearchFlag", flags to search right frames. + @return A list of founded frames. + + @onerror An empty list is returned. + */ + virtual css::uno::Sequence< css::uno::Reference< css::frame::XFrame > > SAL_CALL queryFrames( sal_Int32 nSearchFlags ) override; + + // XIndexAccess + + /** + @short get count of all current frames in container + @descr This is the beginning of full index-access. With a count you can step over all items in container. + Next call should be getByIndex(). But these mechanism works only, if no lock in container is set! + + @seealso class FrameContainer + @seealso method getByIndex() + @return Count of current items in container. + + @onerror If a lock is set, we return 0 for prevent further access! + */ + virtual sal_Int32 SAL_CALL getCount() override; + + /** + @short get specified container item by index + @descr If you called getCount() successful - this method return the specified element as an Any. + You must observe the range from 0 to count-1! Otherwise an IndexOutOfBoundsException is thrown. + + @seealso class FrameContainer + @seealso method getCount() + + @param "nIndex", valid index to get container item. + @return A container item (specified by index) wrapped in an Any. + + @onerror If a lock is set, we return an empty Any! + @onerror If index out of range, an IndexOutOfBoundsException is thrown. + */ + virtual css::uno::Any SAL_CALL getByIndex( sal_Int32 nIndex ) override; + + // XElementAccess + + /** + @short get uno-type of all container items + @descr In current implementation type is fixed to XFrame! + (container-lock is ignored) + @return A uno-type descriptor. + */ + virtual css::uno::Type SAL_CALL getElementType() override; + + /** + @short get fill state of current container + @descr Call these to get information about, if items exist in container or not. + (container-lock is ignored) + @return sal_True, if container contains some items. + @return sal_False, otherwise. + + @onerror We return sal_False. + */ + virtual sal_Bool SAL_CALL hasElements() override; + + private: + + /** + @short standard destructor + @descr This method destruct an instance of this class and clear some member. + This method is protected, because it's not allowed to use this class as a member! + You MUST use a dynamical instance (pointer). That's the reason for a protected dtor. + */ + virtual ~OFrames() override; + + /** + @short reset instance to default values + @descr There are two ways to delete an instance of this class.
+ 1) delete with destructor
+ 2) dispose from parent or factory ore ...
+ This method do the same for both ways! It free used memory and release references ... + + @seealso method dispose() (if it exist!) + @seealso destructor ~TaskEnumeration() + */ + void impl_resetObject(); + + /** + @short append one sequence to another + @descr There is no operation to add to sequences! Use this helper-method to do this. + + @seealso class Sequence + + @param "seqDestination", reference to sequence on which operation will append the other sequence. + @param "seqSource" , reference to sequence for append. + @return "seqDestination" is parameter AND return value at the same time. + */ + void impl_appendSequence( css::uno::Sequence< css::uno::Reference< css::frame::XFrame > >& seqDestination , + const css::uno::Sequence< css::uno::Reference< css::frame::XFrame > >& seqSource ); + + static bool impldbg_checkParameter_queryFrames ( sal_Int32 nSearchFlags ); + + css::uno::WeakReference< css::frame::XFrame > m_xOwner; /// reference to owner of this instance (Hold no hard reference!) + FrameContainer* m_pFrameContainer; /// with owner shared list to hold all direct children of an XFramesSupplier + bool m_bRecursiveSearchProtection; /// flag to protect against recursive searches of frames at parents +}; + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/helper/persistentwindowstate.hxx b/framework/inc/helper/persistentwindowstate.hxx new file mode 100644 index 0000000000..8d062488fe --- /dev/null +++ b/framework/inc/helper/persistentwindowstate.hxx @@ -0,0 +1,166 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 + +#include + +#include +#include +#include +#include + +#include +#include + +namespace framework{ + +/*-************************************************************************************************************ + @short listener for closing document frames to make her window state persistent + @descr It's a feature of our office. If a document window was created by ourself (and not from + any external process e.g. the office bean) we save and restore the window state of it + corresponding to the document service factory. That means: one instance of this class will be + a listener on one frame which container window was created by ourself. + We listen for frame action events and every time a component will detached from a frame + we store its current position and size to the configuration. Every time a new component is + attached to a frame first time(!) we restore this information again. + + @base OWeakObject + implements ref counting for this class. + + @devstatus ready + @threadsafe yes +*//*-*************************************************************************************************************/ +class PersistentWindowState final : public ::cppu::WeakImplHelper< + css::lang::XInitialization, + css::frame::XFrameActionListener > // => XEventListener +{ + + // member + + private: + + /// may we need a uno service manager to create own services + css::uno::Reference< css::uno::XComponentContext > m_xContext; + + /// reference to the frame which was created by the office himself + css::uno::WeakReference< css::frame::XFrame > m_xFrame; + + /// we call SetWindowState one times only for the same frame! + bool m_bWindowStateAlreadySet; + + // interface + + public: + + // ctor/dtor + PersistentWindowState(css::uno::Reference< css::uno::XComponentContext > xContext); + virtual ~PersistentWindowState( ) override; + + // XInitialization + virtual void SAL_CALL initialize(const css::uno::Sequence< css::uno::Any >& lArguments) override; + + // XFrameActionListener + virtual void SAL_CALL frameAction(const css::frame::FrameActionEvent& aEvent) override; + + // XEventListener + virtual void SAL_CALL disposing(const css::lang::EventObject& aEvent) override; + + // helper + + private: + + /** @short identify the application module, which is used behind the component + of our frame. + + @param rxContext + needed to create needed uno resources. + + @param xFrame + contains the component, which must be identified. + + @return [string] + a module identifier for the current frame component. + */ + static OUString implst_identifyModule(const css::uno::Reference< css::uno::XComponentContext >& rxContext, + const css::uno::Reference< css::frame::XFrame >& xFrame); + + /** @short retrieve the window state from the configuration. + + @param rxContext + needed to create the configuration access. + + @param sModuleName + identifies the application module, where the + information should be obtained for. + + @return [string] + contains the information about position and size. + */ + static OUString implst_getWindowStateFromConfig(const css::uno::Reference< css::uno::XComponentContext >& rxContext , + std::u16string_view sModuleName); + + /** @short retrieve the window state from the container window. + + @param xWindow + must point to the container window of the frame. + We use it VCL part here - because the toolkit doesn't + provide the right functionality! + + @return [string] + contains the information about position and size. + */ + static OUString implst_getWindowStateFromWindow(const css::uno::Reference< css::awt::XWindow >& xWindow); + + /** @short restore the position and size on the container window. + + @param rxContext + needed to create the configuration access. + + @param sModuleName + identifies the application module, where the + information should be set on. + + @param sWindowState + contains the information about position and size. + */ + static void implst_setWindowStateOnConfig(const css::uno::Reference< css::uno::XComponentContext >& rxContext, + std::u16string_view sModuleName , + const OUString& sWindowState ); + + /** @short restore the position and size on the container window. + + @param xWindow + must point to the container window of the frame. + We use it VCL part here - because the toolkit doesn't + provide the right functionality! + + @param sWindowState + contains the information about position and size. + */ + static void implst_setWindowStateOnWindow(const css::uno::Reference< css::awt::XWindow >& xWindow , + std::u16string_view sWindowState); + +}; // class PersistentWindowState + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/helper/propertysetcontainer.hxx b/framework/inc/helper/propertysetcontainer.hxx new file mode 100644 index 0000000000..849899784c --- /dev/null +++ b/framework/inc/helper/propertysetcontainer.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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 + +#include +#include +#include +#include +#include + +namespace framework +{ + +class PropertySetContainer : public css::container::XIndexContainer , + public ::cppu::OWeakObject +{ + public: + PropertySetContainer(); + virtual ~PropertySetContainer() override; + + // 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; + + // XIndexContainer + virtual void SAL_CALL insertByIndex( sal_Int32 Index, const css::uno::Any& Element ) override; + + virtual void SAL_CALL removeByIndex( sal_Int32 Index ) override; + + // XIndexReplace + virtual void SAL_CALL replaceByIndex( sal_Int32 Index, const css::uno::Any& Element ) 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 + { + return cppu::UnoType::get(); + } + + virtual sal_Bool SAL_CALL hasElements() override; + + private: + typedef std::vector< css::uno::Reference< css::beans::XPropertySet > > PropertySetVector; + PropertySetVector m_aPropertySetVector; + std::mutex m_aMutex; +}; + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/helper/shareablemutex.hxx b/framework/inc/helper/shareablemutex.hxx new file mode 100644 index 0000000000..d43b3e938d --- /dev/null +++ b/framework/inc/helper/shareablemutex.hxx @@ -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 . + */ + +#pragma once + +#include +#include + +namespace framework +{ + +/** + * This acts like a rtl::Reference + */ +class ShareableMutex +{ + public: + ShareableMutex(); + ShareableMutex( const ShareableMutex& rShareableMutex ); + ShareableMutex& operator=( const ShareableMutex& rShareableMutex ); + + ~ShareableMutex() { m_pMutexRef->release(); } + + /** acquire the shared mutex */ + void acquire(); + /** release the shared mutex */ + void release(); + + private: + /* ShareableMutex::MutexRef will destroy itself when the last ShareableMutex pointing to it is destroyed */ + struct MutexRef + { + MutexRef() : m_refCount(0) {} + void acquire() + { + osl_atomic_increment( &m_refCount ); + } + + void release() + { + if ( osl_atomic_decrement( &m_refCount ) == 0 ) + delete this; + } + + oslInterlockedCount m_refCount; + osl::Mutex m_oslMutex; + }; + + MutexRef* m_pMutexRef; +}; + +class ShareGuard +{ + public: + explicit ShareGuard( ShareableMutex& rShareMutex ) : + m_rShareMutex( rShareMutex ) + { + m_rShareMutex.acquire(); + } + + ~ShareGuard() + { + m_rShareMutex.release(); + } + + private: + ShareGuard& operator=( const ShareGuard& ) = delete; + + ShareableMutex& m_rShareMutex; +}; + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/helper/statusindicator.hxx b/framework/inc/helper/statusindicator.hxx new file mode 100644 index 0000000000..8d97738d00 --- /dev/null +++ b/framework/inc/helper/statusindicator.hxx @@ -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 . + */ + +#pragma once + +// include files of own module +#include + +// include UNO interfaces +#include + +// include all others +#include +#include + +namespace framework{ + +/** + @short implement a status indicator object + + @descr With this indicator you can show a message and a progress... + but you share the output device with other indicator objects, + if this instances was created by the same factory. + Then the last created object has full access to device. + All others change her internal data structure only. + + All objects of this StatusIndicator class calls a C++ interface + on the StatusIndicatorFactory (where they were created). + The factory holds all data structures and paints the progress. + + @devstatus ready to use + @threadsafe yes +*/ +class StatusIndicator final : public ::cppu::WeakImplHelper< css::task::XStatusIndicator > +{ + + // member + private: + + /** @short weak reference to our factory + @descr All our interface calls will be forwarded + to a suitable c++ interface on this factory. + But we don't hold our factory alive. They + correspond with e.g. with a Frame service and + will be owned by him. If the frame will be closed + he close our factory too ... + */ + css::uno::WeakReference< css::task::XStatusIndicatorFactory > m_xFactory; + + sal_Int32 m_nRange; + // We want the callback percentages to increase monotonically + int m_nLastCallbackPercent; + + // c++ interface + public: + + /** @short initialize new instance of this class. + + @param pFactory + pointer to our factory + */ + StatusIndicator(StatusIndicatorFactory* pFactory); + + /** @short does nothing real... + */ + virtual ~StatusIndicator() override; + + // uno interface + public: + + // XStatusIndicator + virtual void SAL_CALL start(const OUString& sText , + sal_Int32 nRange) override; + + virtual void SAL_CALL end() override; + + virtual void SAL_CALL reset() override; + + virtual void SAL_CALL setText(const OUString& sText) override; + + virtual void SAL_CALL setValue(sal_Int32 nValue) override; + +}; // class StatusIndicator + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/helper/statusindicatorfactory.hxx b/framework/inc/helper/statusindicatorfactory.hxx new file mode 100644 index 0000000000..c492a645f5 --- /dev/null +++ b/framework/inc/helper/statusindicatorfactory.hxx @@ -0,0 +1,270 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 + +#include +#include + +// include files of own module +#include + +// include uno interfaces +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace framework{ + +/** + @descr This struct hold some information about all currently running progress processes. + Because the can be used on a stack, we must cache her states but must paint only + the top most one. + */ +struct IndicatorInfo +{ + + // member + public: + + /** @short points to the indicator child, where we hold its states + alive here. */ + css::uno::Reference< css::task::XStatusIndicator > m_xIndicator; + + /** @short the last set text for this indicator */ + OUString m_sText; + + /** @short the last set value for this indicator */ + sal_Int32 m_nValue; + + // interface + public: + + /** @short initialize new instance of this class + + @param xIndicator + the new child indicator of our factory. + + @param sText + its initial text. + + @param nRange + the max range for this indicator. + */ + IndicatorInfo(const css::uno::Reference< css::task::XStatusIndicator >& xIndicator, + const OUString& sText ) + { + m_xIndicator = xIndicator; + m_sText = sText; + m_nValue = 0; + } + + /** @short Used to locate an info struct inside a stl structure... + + @descr The indicator object itself is used as key. Its values + are not interesting then. Because more than one child + indicator can use the same values... + */ + bool operator==(const css::uno::Reference< css::task::XStatusIndicator >& xIndicator) const + { + return (m_xIndicator == xIndicator); + } +}; + +/** @descr Define a list of child indicator objects and its data. */ +typedef ::std::vector< IndicatorInfo > IndicatorStack; + +/** @short implement a factory service to create new status indicator objects + + @descr Internally it uses: + - a vcl based + - or a uno based and by the frame layouted + progress implementation. + + This factory create different indicators and control his access + to a shared output device! Only the last activated component + can write its state to this device. All other requests will be + cached only. + + @devstatus ready to use + @threadsafe yes + */ +class StatusIndicatorFactory final : public ::cppu::WeakImplHelper< + css::lang::XServiceInfo + , css::lang::XInitialization + , css::task::XStatusIndicatorFactory + , css::util::XUpdatable > +{ + + // member + private: + std::mutex m_mutex; + + /** stack with all current indicator children. */ + IndicatorStack m_aStack; + + /** uno service manager to create own needed uno resources. */ + css::uno::Reference< css::uno::XComponentContext > m_xContext; + + /** most active indicator child, which could work with our shared indicator window only. */ + css::uno::Reference< css::task::XStatusIndicator > m_xActiveChild; + + /** used to show the progress on the frame (layouted!) or + as a plugged vcl window. */ + css::uno::Reference< css::task::XStatusIndicator > m_xProgress; + + /** points to the frame, where we show the progress (in case + m_xProgress points to a frame progress. */ + css::uno::WeakReference< css::frame::XFrame > m_xFrame; + + /** points to an outside window, where we show the progress (in case + we are plugged into such window). */ + css::uno::WeakReference< css::awt::XWindow > m_xPluggWindow; + + /** Notify us if a fix time is over. We use it to implement an + intelligent "Reschedule" ... */ + rtl::Reference m_pWakeUp; + + /** Our WakeUpThread calls us in our interface method "XUpdatable::update(). + There we set this member m_bAllowReschedule to sal_True. Next time if our impl_reschedule() + method is called, we know, that an Application::Reschedule() should be made. + Because the last made Reschedule can be was taken long time ago ... may be.*/ + bool m_bAllowReschedule; + + /** enable/disable automatic showing of our parent window. */ + bool m_bAllowParentShow; + + /** enable/disable rescheduling. Default=enabled*/ + bool m_bDisableReschedule; + + /** prevent recursive calling of Application::Reschedule(). */ + static sal_Int32 m_nInReschedule; + + // interface + + public: + StatusIndicatorFactory(css::uno::Reference< css::uno::XComponentContext > xContext); + + virtual OUString SAL_CALL getImplementationName() override + { + return "com.sun.star.comp.framework.StatusIndicatorFactory"; + } + + virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override + { + return cppu::supportsService(this, ServiceName); + } + + virtual css::uno::Sequence SAL_CALL getSupportedServiceNames() override + { + return { "com.sun.star.task.StatusIndicatorFactory" }; + } + + // XInitialization + virtual void SAL_CALL initialize(const css::uno::Sequence< css::uno::Any >& lArguments) override; + + // XStatusIndicatorFactory + virtual css::uno::Reference< css::task::XStatusIndicator > SAL_CALL createStatusIndicator() override; + + // XUpdatable + virtual void SAL_CALL update() override; + + // similar (XStatusIndicator) + void start(const css::uno::Reference< css::task::XStatusIndicator >& xChild, + const OUString& sText , + sal_Int32 nRange); + + void reset(const css::uno::Reference< css::task::XStatusIndicator >& xChild); + + void end(const css::uno::Reference< css::task::XStatusIndicator >& xChild); + + void setText(const css::uno::Reference< css::task::XStatusIndicator >& xChild, + const OUString& sText ); + + void setValue(const css::uno::Reference< css::task::XStatusIndicator >& xChild, + sal_Int32 nValue); + + // specials + + private: + + virtual ~StatusIndicatorFactory() override; + + // helper + + /** @short show the parent window of this progress ... + if it's allowed to do so. + + @descr By default we show the parent window automatically + if this progress is used. + If that isn't a valid operation, the user of this + progress can suppress this feature by initializing + us with a special parameter. + + @seealso initialize() + */ + void implts_makeParentVisibleIfAllowed(); + + /** @short creates a new internal used progress. + @descr This factory does not paint the progress itself. + It uses helper for that. They can be vcl based or + layouted by the frame and provided as a uno interface. + */ + void impl_createProgress(); + + /** @short shows the internal used progress. + @descr This factory does not paint the progress itself. + It uses helper for that. They can be vcl based or + layouted by the frame and provided as a uno interface. + */ + void impl_showProgress(); + + /** @short hides the internal used progress. + @descr This factory does not paint the progress itself. + It uses helper for that. They can be vcl based or + layouted by the frame and provided as a uno interface. + */ + void impl_hideProgress(); + + /** @short try to "share the current thread in an intelligent manner" :-) + + @param Overwrites our algorithm for Reschedule and force it to be sure + that our progress was painted right. + */ + void impl_reschedule(bool bForceUpdate); + + void impl_startWakeUpThread(); + void impl_stopWakeUpThread(); + +}; // class StatusIndicatorFactory + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/helper/tagwindowasmodified.hxx b/framework/inc/helper/tagwindowasmodified.hxx new file mode 100644 index 0000000000..e8c9fff22d --- /dev/null +++ b/framework/inc/helper/tagwindowasmodified.hxx @@ -0,0 +1,94 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 +#include +#include +#include +#include + +#include +#include + +namespace framework{ + +/*-************************************************************************************************************ + @short listen for modify events on model and tag frame container window so it can react accordingly + @descr Used e.g. by our MAC port where such state is shown separately on some controls of the + title bar. + + @base OWeakObject + implements ref counting for this class. + + @devstatus draft + @threadsafe yes +*//*-*************************************************************************************************************/ +class TagWindowAsModified final : public ::cppu::WeakImplHelper< + css::lang::XInitialization, + css::frame::XFrameActionListener, // => XEventListener + css::util::XModifyListener > // => XEventListener +{ + + // member + + private: + + /// reference to the frame, where we listen for new loaded documents for updating our own xModel reference + css::uno::Reference< css::frame::XFrame > m_xFrame; + + /// reference to the frame container window, where we must set the tag + VclPtr m_xWindow; + + /// we list on the model for modify events + css::uno::Reference< css::util::XModifiable > m_xModel; + + // interface + + public: + + // ctor/dtor + TagWindowAsModified(); + virtual ~TagWindowAsModified( ) override; + + // XInterface, XTypeProvider + + // XInitialization + virtual void SAL_CALL initialize(const css::uno::Sequence< css::uno::Any >& lArguments) override; + + // XModifyListener + virtual void SAL_CALL modified(const css::lang::EventObject& aEvent) override; + + // XFrameActionListener + virtual void SAL_CALL frameAction(const css::frame::FrameActionEvent& aEvent) override; + + // XEventListener + virtual void SAL_CALL disposing(const css::lang::EventObject& aEvent) override; + + private: + + // @todo document me + void impl_update(const css::uno::Reference< css::frame::XFrame >& xFrame); + +}; // class TagWindowAsModified + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/helper/titlebarupdate.hxx b/framework/inc/helper/titlebarupdate.hxx new file mode 100644 index 0000000000..11aba17dc4 --- /dev/null +++ b/framework/inc/helper/titlebarupdate.hxx @@ -0,0 +1,142 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 +#include +#include +#include +#include +#include +#include + +namespace framework{ + +/*-************************************************************************************************************ + @short helps our frame on setting title/icon on the titlebar (including updates) + + @devstatus draft + @threadsafe yes +*//*-*************************************************************************************************************/ +class TitleBarUpdate final : public ::cppu::WeakImplHelper< + css::lang::XInitialization + , css::frame::XTitleChangeListener // => XEventListener + , css::frame::XFrameActionListener > // => XEventListener +{ + + // structs, types + + private: + + struct TModuleInfo + { + /// internal id of this module + OUString sID; + /// configured icon for this module + ::sal_Int32 nIcon; + }; + + // member + + private: + + /// may we need a uno service manager to create own services + css::uno::Reference< css::uno::XComponentContext > m_xContext; + + /// reference to the frame which was created by the office himself + css::uno::WeakReference< css::frame::XFrame > m_xFrame; + + // interface + + public: + + // ctor/dtor + TitleBarUpdate(css::uno::Reference< css::uno::XComponentContext > xContext); + virtual ~TitleBarUpdate( ) override; + + // XInterface, XTypeProvider + + // XInitialization + virtual void SAL_CALL initialize(const css::uno::Sequence< css::uno::Any >& lArguments) override; + + // XFrameActionListener + virtual void SAL_CALL frameAction(const css::frame::FrameActionEvent& aEvent) override; + + // XTitleChangeListener + virtual void SAL_CALL titleChanged(const css::frame::TitleChangedEvent& aEvent) override; + + // XEventListener + virtual void SAL_CALL disposing(const css::lang::EventObject& aEvent) override; + + // helper + + private: + + /** @short identify the application module, which is used behind the component + of our frame. + + @param xFrame + contains the component, which must be identified. + + @param rInfo + describe the module in its details. + Is set only if return value is true. + + @return [sal_Bool] + sal_True in case module could be identified and all needed values could be read. + sal_False otherwise. + */ + bool implst_getModuleInfo(const css::uno::Reference< css::frame::XFrame >& xFrame, + TModuleInfo& rInfo ); + + /** @short set a new icon and title on the title bar of our connected frame window. + + @descr It does not check if an update is really needed. That has to be done outside. + It retrieves all needed information and update the title bar - nothing less - + nothing more. + */ + void impl_forceUpdate(); + + /** @short identify the current component (inside the connected frame) + and set the right module icon on the title bar. + + @param xFrame + the frame which contains the component and where the icon must be set + on the window title bar. + */ + void impl_updateIcon(const css::uno::Reference< css::frame::XFrame >& xFrame); + + /** @short gets the current title from the frame and set it on the window. + + @param xFrame + the frame which contains the component and where the title must be set + on the window title bar. + */ + void impl_updateTitle(const css::uno::Reference< css::frame::XFrame >& xFrame); + + //Hook to set GNOME3/Windows 7 applicationID for toplevel frames + //http://msdn.microsoft.com/en-us/library/dd378459(v=VS.85).aspx + //http://live.gnome.org/GnomeShell/ApplicationBased + void impl_updateApplicationID(const css::uno::Reference< css::frame::XFrame >& xFrame); +}; // class TitleBarUpdate + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/helper/uiconfigelementwrapperbase.hxx b/framework/inc/helper/uiconfigelementwrapperbase.hxx new file mode 100644 index 0000000000..5b561bef5d --- /dev/null +++ b/framework/inc/helper/uiconfigelementwrapperbase.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 . + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace framework +{ + +typedef ::cppu::WeakImplHelper< + css::ui::XUIElement, + css::ui::XUIElementSettings, + css::lang::XInitialization, + css::lang::XComponent, + css::util::XUpdatable, + css::ui::XUIConfigurationListener > UIConfigElementWrapperBase_BASE; + +class UIConfigElementWrapperBase : private cppu::BaseMutex, + public ::cppu::OBroadcastHelper , + public ::cppu::OPropertySetHelper , + public UIConfigElementWrapperBase_BASE +{ + + // public methods + + public: + UIConfigElementWrapperBase( sal_Int16 nType ); + virtual ~UIConfigElementWrapperBase() override; + + // XInterface + virtual void SAL_CALL acquire() noexcept override + { OWeakObject::acquire(); } + virtual void SAL_CALL release() noexcept override + { OWeakObject::release(); } + virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type& type) override; + + // XTypeProvider + virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes( ) override; + + // XComponent + virtual void SAL_CALL dispose() override = 0; + 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; + + // XInitialization + virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override; + + // XUIElementSettings + virtual void SAL_CALL updateSettings() override = 0; + virtual css::uno::Reference< css::container::XIndexAccess > SAL_CALL getSettings( sal_Bool bWriteable ) override; + virtual void SAL_CALL setSettings( const css::uno::Reference< css::container::XIndexAccess >& UISettings ) override; + + // XUIElement + virtual css::uno::Reference< css::frame::XFrame > SAL_CALL getFrame() override; + virtual OUString SAL_CALL getResourceURL() override; + virtual ::sal_Int16 SAL_CALL getType() override; + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL getRealInterface() override = 0; + + // XUpdatable + virtual void SAL_CALL update() override; + + // XUIConfigurationListener + virtual void SAL_CALL elementInserted( const css::ui::ConfigurationEvent& Event ) override; + virtual void SAL_CALL elementRemoved( const css::ui::ConfigurationEvent& Event ) override; + virtual void SAL_CALL elementReplaced( const css::ui::ConfigurationEvent& Event ) override; + + // XEventListener + using cppu::OPropertySetHelper::disposing; + virtual void SAL_CALL disposing( const css::lang::EventObject& aEvent ) override; + + // protected methods + + protected: + + // OPropertySetHelper + virtual sal_Bool SAL_CALL convertFastPropertyValue ( css::uno::Any& aConvertedValue , + css::uno::Any& aOldValue , + sal_Int32 nHandle , + const css::uno::Any& aValue ) override; + virtual void SAL_CALL setFastPropertyValue_NoBroadcast( sal_Int32 nHandle , + const css::uno::Any& aValue ) override; + using cppu::OPropertySetHelper::getFastPropertyValue; + virtual void SAL_CALL getFastPropertyValue( css::uno::Any& aValue , + sal_Int32 nHandle ) const override; + virtual ::cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper() override; + virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo() override; + + virtual void impl_fillNewData(); + + static css::uno::Sequence< css::beans::Property > impl_getStaticPropertyDescriptor(); + + sal_Int16 m_nType; + bool m_bPersistent : 1, + m_bInitialized : 1, + m_bConfigListener : 1, + m_bConfigListening : 1, + m_bDisposed : 1, + m_bNoClose : 1; + OUString m_aResourceURL; + css::uno::Reference< css::ui::XUIConfigurationManager > m_xConfigSource; + css::uno::Reference< css::container::XIndexAccess > m_xConfigData; + css::uno::WeakReference< css::frame::XFrame > m_xWeakFrame; + css::uno::Reference< css::awt::XMenuBar > m_xMenuBar; + comphelper::OMultiTypeInterfaceContainerHelper2 m_aListenerContainer; /// container for ALL Listener +}; + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/helper/uielementwrapperbase.hxx b/framework/inc/helper/uielementwrapperbase.hxx new file mode 100644 index 0000000000..573755f202 --- /dev/null +++ b/framework/inc/helper/uielementwrapperbase.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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace framework +{ + +typedef ::cppu::WeakImplHelper< + css::ui::XUIElement, + css::lang::XInitialization, + css::lang::XComponent, + css::util::XUpdatable > UIElementWrapperBase_BASE; + +class UIElementWrapperBase : private cppu::BaseMutex, + public ::cppu::OBroadcastHelper , + public ::cppu::OPropertySetHelper , + public UIElementWrapperBase_BASE +{ + + // public methods + + public: + UIElementWrapperBase( sal_Int16 nType ); + virtual ~UIElementWrapperBase() override; + + // XInterface + virtual void SAL_CALL acquire() noexcept override + { OWeakObject::acquire(); } + virtual void SAL_CALL release() noexcept override + { OWeakObject::release(); } + virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type& type) override; + + // XTypeProvider + virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes( ) override; + + // XComponent + virtual void SAL_CALL dispose() override = 0; + 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; + + // XInitialization + virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override; + + // XUpdatable + virtual void SAL_CALL update() override; + + // XUIElement + virtual css::uno::Reference< css::frame::XFrame > SAL_CALL getFrame() override; + virtual OUString SAL_CALL getResourceURL() override; + virtual ::sal_Int16 SAL_CALL getType() override; + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL getRealInterface() override = 0; + + // protected methods + + protected: + + // OPropertySetHelper + virtual sal_Bool SAL_CALL convertFastPropertyValue ( css::uno::Any& aConvertedValue , + css::uno::Any& aOldValue , + sal_Int32 nHandle , + const css::uno::Any& aValue ) override; + virtual void SAL_CALL setFastPropertyValue_NoBroadcast( sal_Int32 nHandle , + const css::uno::Any& aValue ) override; + using cppu::OPropertySetHelper::getFastPropertyValue; + virtual void SAL_CALL getFastPropertyValue( css::uno::Any& aValue , + sal_Int32 nHandle ) const override; + virtual ::cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper() override; + virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo() override; + + static css::uno::Sequence< css::beans::Property > impl_getStaticPropertyDescriptor(); + + comphelper::OMultiTypeInterfaceContainerHelper2 m_aListenerContainer; /// container for ALL Listener + OUString m_aResourceURL; + css::uno::WeakReference< css::frame::XFrame > m_xWeakFrame; + sal_Int16 m_nType; + bool m_bInitialized : 1, + m_bDisposed; +}; + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/helper/vclstatusindicator.hxx b/framework/inc/helper/vclstatusindicator.hxx new file mode 100644 index 0000000000..6afae95650 --- /dev/null +++ b/framework/inc/helper/vclstatusindicator.hxx @@ -0,0 +1,89 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 +#include + +#include +#include + +namespace framework { + +// declaration + +class VCLStatusIndicator final : public ::cppu::WeakImplHelper< css::task::XStatusIndicator > +{ + + // member + + private: + + /** points to the parent window of this progress and + hold it alive. */ + css::uno::Reference< css::awt::XWindow > m_xParentWindow; + + /** shows the progress. + + @attention This member is not synchronized using our own mutex! + It's guarded by the solarmutex only. Otherwise + we have to lock two of them, which can force a deadlock... + */ + VclPtr m_pStatusBar; + + /** knows the current range of the progress. */ + sal_Int32 m_nRange; + + /** knows the current value of the progress. */ + sal_Int32 m_nValue; + + // interface + + public: + + /// ctor + VCLStatusIndicator(css::uno::Reference< css::awt::XWindow > xParentWindow); + + /// dtor + virtual ~VCLStatusIndicator() override; + + /// XStatusIndicator + virtual void SAL_CALL start(const OUString& sText , + sal_Int32 nRange) override; + + virtual void SAL_CALL reset() override; + + virtual void SAL_CALL end() override; + + virtual void SAL_CALL setText(const OUString& sText) override; + + virtual void SAL_CALL setValue(sal_Int32 nValue) override; + + // helper + + private: + + static void impl_recalcLayout(vcl::Window* pStatusBar , + vcl::Window const * pParentWindow); +}; + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/helper/wakeupthread.hxx b/framework/inc/helper/wakeupthread.hxx new file mode 100644 index 0000000000..cdc8700a52 --- /dev/null +++ b/framework/inc/helper/wakeupthread.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 + +#include +#include +#include +#include +#include + +namespace com::sun::star::util +{ +class XUpdatable; +} + +namespace framework +{ +class WakeUpThread final : public salhelper::Thread +{ + css::uno::WeakReference updatable_; + std::condition_variable condition_; + std::mutex mutex_; + bool terminate_; + + void execute() override; + +public: + WakeUpThread(css::uno::Reference const& updatable); + + void stop(); +}; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/interaction/quietinteraction.hxx b/framework/inc/interaction/quietinteraction.hxx new file mode 100644 index 0000000000..ce0969bd14 --- /dev/null +++ b/framework/inc/interaction/quietinteraction.hxx @@ -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 . + */ + +#pragma once + +#include +#include + +#include + +namespace framework{ + +/** + @short handle interactions non visible + @descr Sometimes it's necessary to use a non visible interaction handler. + He can't do anything, which a visible one can handle. + But it can be used to intercept problems e.g. during loading of documents. + + In current implementation we solve conflicts for following situations only: + - InteractiveIOException + - InteractiveAugmentedIOException + All other requests will be aborted. + */ +class QuietInteraction final : public ::cppu::WeakImplHelper< + css::task::XInteractionHandler > +{ + // member + private: + + /// in case an unknown interaction was aborted - we save it for our external user! + css::uno::Any m_aRequest; + + // uno interface + public: + + // XInterface, XTypeProvider + + /** + @interface XInteractionHandler + @short called from outside to handle a problem + @descr The only interaction we can handle here is to + decide which of two ambiguous filters should be really used. + We use the user selected one every time. + All other request will be aborted and can break the code, + which use this interaction handler. + + But you can use another method of this class to check for + some special interactions too: IO Exceptions + May a ComponentLoader needs that to throw suitable exception + on his own interface. + + @threadsafe yes + */ + virtual void SAL_CALL handle( const css::uno::Reference< css::task::XInteractionRequest >& xRequest ) override; + + // c++ interface + public: + /** + @short ctor to guarantee right initialized instances of this class + @threadsafe not necessary + */ + QuietInteraction(); + + /** + @short return the handled interaction request + @descr We saved any obtained interaction request internally. + Maybe the outside user of this class is interested + on that. Especially we got an unknown interaction + and aborted it hard. + + @return [com.sun.star.uno.Any] + the packed interaction request + Can be empty if no interaction was used! + + @threadsafe yes + */ + css::uno::Any getRequest() const; + + /** + @short returns information if interaction was used + @descr It can be useful to know the reason for a failed operation. + + @return [boolean] + for used interaction + otherwise + + @threadsafe yes + */ + bool wasUsed() const; +}; + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/jobs/configaccess.hxx b/framework/inc/jobs/configaccess.hxx new file mode 100644 index 0000000000..2efc22cb8e --- /dev/null +++ b/framework/inc/jobs/configaccess.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 . + */ + +#pragma once + +#include +#include +#include + +namespace framework{ + +/** + @short implements a simple configuration access + @descr Sometimes it's better to have direct config access + instead of using specialized config items of the svtools + project. This class can wrap such configuration access. + */ +class ConfigAccess final +{ + + public: + + /** represent the possible modes of the internal wrapped configuration access */ + enum EOpenMode + { + /// config isn't used yet + E_CLOSED, + /// config access is open for reading only + E_READONLY, + /// config access is open for reading/writing data + E_READWRITE + }; + + // member + + private: + mutable std::mutex m_mutex; + + /** + reference to the uno service manager + It's necessary to instantiate own needed services. + */ + css::uno::Reference< css::uno::XComponentContext > m_xContext; + + /** hold an opened configuration alive */ + css::uno::Reference< css::uno::XInterface > m_xConfig; + + /** knows the root of the opened config access point */ + OUString m_sRoot; + + /** represent the current open mode */ + EOpenMode m_eMode; + + // native interface methods + + void closeImpl(); + public: + + ConfigAccess( css::uno::Reference< css::uno::XComponentContext > xContext, + OUString sRoot ); + ~ConfigAccess(); + + void open ( EOpenMode eMode ); + void close ( ); + EOpenMode getMode( ) const; + + const css::uno::Reference< css::uno::XInterface >& cfg(); +}; + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/jobs/helponstartup.hxx b/framework/inc/jobs/helponstartup.hxx new file mode 100644 index 0000000000..aaaf35492f --- /dev/null +++ b/framework/inc/jobs/helponstartup.hxx @@ -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 . + */ + +#pragma once + +#include + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace framework{ + +/** @short implements a job component, which handle the special + feature to show a suitable help page for every (visible!) + loaded document. + */ +class HelpOnStartup final : public ::cppu::WeakImplHelper< css::lang::XServiceInfo,css::lang::XEventListener,css::task::XJob > +{ + + // member + private: + std::mutex m_mutex; + + /** @short reference to a uno service manager. */ + css::uno::Reference< css::uno::XComponentContext > m_xContext; + + /** @short such module manager is used to classify new opened documents. */ + css::uno::Reference< css::frame::XModuleManager2 > m_xModuleManager; + + /** @short is needed to locate a might open help frame. */ + css::uno::Reference< css::frame::XDesktop2 > m_xDesktop; + + /** @short provides read access to the underlying configuration. */ + css::uno::Reference< css::container::XNameAccess > m_xConfig; + + /** @short knows the current locale of this office session, + which is needed to build complete help URLs. + */ + OUString m_sLocale; + + /** @short knows the current operating system of this office session, + which is needed to build complete help URLs. + */ + OUString m_sSystem; + + // native interface + public: + + /** @short create new instance of this class. + + @param xContext + reference to the uno service manager, which created this instance. + Can be used later to create own needed uno resources on demand. + */ + HelpOnStartup(css::uno::Reference< css::uno::XComponentContext > xContext); + + /** @short does nothing real ... + + @descr But it should exists as virtual function, + so this class can't make trouble + related to inline/symbols etcpp.! + */ + virtual ~HelpOnStartup() override; + + // uno interface + public: + + /* interface 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; + + // css.task.XJob + virtual css::uno::Any SAL_CALL execute(const css::uno::Sequence< css::beans::NamedValue >& lArguments) override; + + // css.lang.XEventListener + virtual void SAL_CALL disposing(const css::lang::EventObject& aEvent) override; + + // helper + private: + + /** @short analyze the given job arguments, try to locate a model reference + and try to classify this model. + + @descr As a result of this operation a module identifier will be returned. + It can be used against the module configuration then to retrieve further information. + + @param lArguments + the list of job arguments which is given on our interface method execute(). + + @return [string] + a module identifier ... or an empty value if no model could be located ... + or if it could not be classified successfully. + */ + OUString its_getModuleIdFromEnv(const css::uno::Sequence< css::beans::NamedValue >& lArguments); + + /** @short tries to locate the open help module and return + the url of the currently shown help content. + + @descr It returns an empty string, if the help isn't still + open at calling time. + + @return The URL of the current shown help content; + or an empty value if the help isn't still open. + */ + OUString its_getCurrentHelpURL(); + + /** @short checks if the given help url match to a default help url + of any office module. + + @param sHelpURL + the help url for checking. + + @return [bool] + sal_True if the given URL is any default one ... + sal_False otherwise. + */ + bool its_isHelpUrlADefaultOne(std::u16string_view sHelpURL); + + /** @short checks, if the help module should be shown automatically for the + currently opened office module. + + @descr This value is read from the module configuration. + In case the help should be shown, this method returns + a help URL, which can be used to show the right help content. + + @param sModule + identifies the used office module. + + @return [string] + A valid help URL in case the help content should be shown; + an empty value if such automatism was disabled for the specified office module. + */ + OUString its_checkIfHelpEnabledAndGetURL(const OUString& sModule); + + /** @short create a help URL for the given parameters. + + @param sBaseURL + must be the base URL for a requested help content + e.g. "vnd.sun.star.help://swriter/" + or "vnd.sun.star.help://swriter/67351" + + @param sLocale + the current office locale + e.g. "en-US" + + @param sSystem + the current operating system + e.g. "WIN" + + @return The URL which was generated. + e.g. + e.g. "vnd.sun.star.help://swriter/?Language=en-US&System=WIN" + or "vnd.sun.star.help://swriter/67351?Language=en-US&System=WIN" + */ + static OUString ist_createHelpURL(std::u16string_view sBaseURL, + std::u16string_view sLocale , + std::u16string_view sSystem ); +}; + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/jobs/job.hxx b/framework/inc/jobs/job.hxx new file mode 100644 index 0000000000..8afc1966f6 --- /dev/null +++ b/framework/inc/jobs/job.hxx @@ -0,0 +1,202 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 + +#include +#include +#include +#include +#include + +#include +#include + +namespace framework{ + +/** + @short it represent a job; execute it and control its lifetime + + @descr This implementation can be used to wrap jobs, execute it + synchronously or asynchronous, control its lifetime + and differe between jobs with and without configuration. + */ +class Job final : public ::cppu::WeakImplHelper< + css::task::XJobListener + , css::frame::XTerminateListener + , css::util::XCloseListener > +{ + + // structs + + private: + + /** different possible states for the internal wrapped job. + It can be started, stopped by a queryClosing() request or + disposed() by a notifyClosing() request ... + */ + enum ERunState + { + E_NEW, + E_RUNNING, + E_STOPPED_OR_FINISHED, + E_DISPOSED + }; + + // member + + private: + + /** + hold all necessary information about this job. + It can be used for both modes: with and without configuration. + */ + JobData m_aJobCfg; + + /** + We need it to create own services on demand. + */ + css::uno::Reference< css::uno::XComponentContext > m_xContext; + + /** + Hold the (may asynchronous) job alive. + */ + css::uno::Reference< css::uno::XInterface > m_xJob; + + /** + Used to wait for finishing of asynchronous started jobs. + */ + ::osl::Condition m_aAsyncWait; + + /** + For some special cases we must know the environment, in which + this job runs. Means the frame inside which we may was triggered. + We use it too, to listen for closing events of this resource. + + Please note: If m_xFrame is set - m_xModel should be NULL. + Only one environment can be supported really. + */ + css::uno::Reference< css::frame::XFrame > m_xFrame; + + /** + For some special cases we must know the environment, in which + this job runs. Means the document inside which we may was triggered. + We use it too, to listen for closing events of this resource. + + Please note: If m_xModel is set - m_xFrame should be NULL. + Only one environment can be supported really. + */ + css::uno::Reference< css::frame::XModel > m_xModel; + + /** + We are registered at this instance to listen for office shutdown events. + It's necessary suppress it (if possible) or to react in the right way. + */ + css::uno::Reference< css::frame::XDesktop2 > m_xDesktop; + + /** + A job can return a dispatch result event after finishing its work. + We have to transport it to any outside interested listener then. + (see m_xResultSourceFake for further information too!) + */ + css::uno::Reference< css::frame::XDispatchResultListener > m_xResultListener; + + /** + We can't set ourself as source of a dispatch result event ... nor our job. + Because the listener (set as m_xResultListener) expect the original instance, + where it was registered. This original instance is the user of this class. + It must be set explicitly and will be used to fake the source of the event! + */ + css::uno::Reference< css::uno::XInterface > m_xResultSourceFake; + + /** + Holds the state, if we are listen for desktop/frame or model closing events or not. + The used references are not really enough to detect a valid listener connection. + That's why we use this additional information here too. + */ + bool m_bListenOnDesktop; + bool m_bListenOnFrame; + bool m_bListenOnModel; + + /** + In case we got a close request from our desktop/frame/model (on which we listen) ... and + the ownership was delivered there ... we have to close ourself and this object + in case the internal wrapped and running job finish his work. + */ + bool m_bPendingCloseFrame; + bool m_bPendingCloseModel; + + /** + indicates in which state the internal job currently exist. + + We can use this information to throw any suitable veto exception + to prevent the environment against dying or suppress superfluous dispose() + calls at the job. + */ + ERunState m_eRunState; + + // native interface + + public: + + Job( const css::uno::Reference< css::uno::XComponentContext >& xContext , + css::uno::Reference< css::frame::XFrame > xFrame ); + Job( const css::uno::Reference< css::uno::XComponentContext >& xContext , + css::uno::Reference< css::frame::XModel > xModel ); + virtual ~Job( ) override; + + void setDispatchResultFake( const css::uno::Reference< css::frame::XDispatchResultListener >& xListener , + const css::uno::Reference< css::uno::XInterface >& xSourceFake ); + void setJobData ( const JobData& aData ); + void execute ( const css::uno::Sequence< css::beans::NamedValue >& lDynamicArgs ); + void die ( ); + + private: + + css::uno::Sequence< css::beans::NamedValue > impl_generateJobArgs ( const css::uno::Sequence< css::beans::NamedValue >& lDynamicArgs ); + void impl_reactForJobResult( const css::uno::Any& aResult ); + void impl_startListening ( ); + void impl_stopListening ( ); + + // uno interface + + public: + + // XJobListener + virtual void SAL_CALL jobFinished( const css::uno::Reference< css::task::XAsyncJob >& xJob, + const css::uno::Any& aResult ) override; + + // XTerminateListener + virtual void SAL_CALL queryTermination ( const css::lang::EventObject& aEvent ) override; + virtual void SAL_CALL notifyTermination( const css::lang::EventObject& aEvent ) override; + + // XCloseListener + virtual void SAL_CALL queryClosing ( const css::lang::EventObject& aEvent , + sal_Bool bGetsOwnership ) override; + virtual void SAL_CALL notifyClosing( const css::lang::EventObject& aEvent ) override; + + // XEventListener + virtual void SAL_CALL disposing( const css::lang::EventObject& aEvent ) override; +}; + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/jobs/jobconst.hxx b/framework/inc/jobs/jobconst.hxx new file mode 100644 index 0000000000..569c2d4c93 --- /dev/null +++ b/framework/inc/jobs/jobconst.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 . + */ + +#pragma once + +#include + +namespace framework +{ +/** + @short defines all constant values used within a job environment. + + @descr The protocol between job executor/dispatch and jobs is fix and well defined. + But why every implemented job should write such constant values directly into + it's code. Typos can occur or code will be changed by new developers ... + Shared set of constant values can help to improve the maintenance of this code. + */ +class JobConst +{ +public: + static constexpr OUString ANSWER_DEACTIVATE_JOB = u"Deactivate"_ustr; + static constexpr OUString ANSWER_SAVE_ARGUMENTS = u"SaveArguments"_ustr; + static constexpr OUString ANSWER_SEND_DISPATCHRESULT = u"SendDispatchResult"_ustr; +}; + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/jobs/jobdata.hxx b/framework/inc/jobs/jobdata.hxx new file mode 100644 index 0000000000..050bce6a84 --- /dev/null +++ b/framework/inc/jobs/jobdata.hxx @@ -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 . + */ + +#pragma once + +#include +#include + +#include + +#include +#include +#include + +namespace framework{ + +/** + @short holds all necessary information about a job and + handle it's configuration (if any exist!) + @descr It can be used from different use cases as a container + (or proxy) for all config data of a registered job. + But it doesn't implement any execute functionality! + */ +class JobData final +{ + public: + + /** These values can be used to differe between jobs with and jobs without + a configuration. Of course an "unknown state" should be available too, + to detect a missing initialization. + */ + enum EMode + { + /// indicates a missing initialization + E_UNKNOWN_MODE, + /// indicates a job with configuration (They alias represent the config key name.) + E_ALIAS, + /// indicates a job without configuration (The pure UNO implementation is used only.) + E_SERVICE, + /// indicates a job with configuration, which was triggered by an event + E_EVENT + }; + + /** These values represent the environment type, in which a job can run. + A job must known, from which binding it will be started. Because + it's initialization data depends from that! + */ + enum EEnvironment + { + /// indicates a missing initialization + E_UNKNOWN_ENVIRONMENT, + /// this job is used by the global JobExecutor service + E_EXECUTION, + /// this job is used by the global dispatch framework + E_DISPATCH, + /// this job is used by the global event broadcaster + E_DOCUMENTEVENT + }; + + /** Some jobs can be registered to "logical events", which are generated on demand if another document event + occurs. E.g. "onDocumentOpened" in case "OnNew" or "OnLoad" was notified to the JobExecutor instance. + And normally the original event is transported as parameter set to the executed job. But then such job + can't differ between e.g. "OnNew" and "onDocumentOpened". + That's why we must know, for which type of event the job was really triggered .-) + + The information "sDocEvent" from this struct must be set on the member JobData::m_sEvent from outside + user of such Jobdata structure. + */ + struct TJob2DocEventBinding + { + OUString m_sJobName; + OUString m_sDocEvent; + + TJob2DocEventBinding(OUString sJobName , + OUString sDocEvent) + : m_sJobName (std::move(sJobName )) + , m_sDocEvent(std::move(sDocEvent)) + {} + }; + + // member + + private: + + /** + reference to the uno service manager. + We need it for creating of own uno services ... e.g. for + opening the configuration. + */ + css::uno::Reference< css::uno::XComponentContext > m_xContext; + + /** + An instance of this class can be used in two different modes: + - as a configured job + - as a job without any configuration + First mode is triggered by an alias, which points to the + configuration entries. Second mode is specified by a uno service + or implementation name. Then we do the same things (use the same interfaces) + but don't handle any configuration data. + The effect: This mode can be detected by this member. + */ + EMode m_eMode; + + /** + Because jobs can be bind to different mechanism inside office, a job + should know inside which environment it runs. E.g. a job can be executed + by the global JobExecutor service (triggered by an event) or e.g. as part + of the global dispatch framework (triggered by an UI control e.g. a menu entry). + */ + EEnvironment m_eEnvironment; + + /** + the alias name of this job. + Is used as entry of configuration set for job registration, to find all + necessary properties of it... + */ + OUString m_sAlias; + + /** + the uno implementation name of this job. + It's read from the configuration. Don't set it from outside! + */ + OUString m_sService; + + /** + the module context list of this job. + It's read from the configuration. Don't set it from outside! + */ + OUString m_sContext; + + /** + a job can be registered for an event. + It can be an empty value! But it will be set from outside any times. + Because it's not clear which job this instance should represent if an event + (instead of an alias) comes in. Because there can be multiple registrations + for this event. We use this information only, to merge it with the job specific + arguments. A job can be called so, with a) its own config data and b) some dynamic + environment data. + */ + OUString m_sEvent; + + /** + job specific configuration items... unknown for us! + It's read from the configuration. Don't set it from outside! + */ + std::vector< css::beans::NamedValue > m_lArguments; + + // native interface + + public: + + JobData( css::uno::Reference< css::uno::XComponentContext > xContext ); + JobData( const JobData& rCopy ); + ~JobData( ); + + JobData& operator=( const JobData& rCopy ); + + EMode getMode () const; + EEnvironment getEnvironment () const; + OUString getEnvironmentDescriptor() const; + OUString getService () const; + OUString getEvent () const; + css::uno::Sequence< css::beans::NamedValue > getConfig () const; + std::vector< css::beans::NamedValue > getJobConfig () const; + + bool hasConfig () const; + bool hasCorrectContext ( std::u16string_view rModuleIdent ) const; + + void setEnvironment ( EEnvironment eEnvironment ); + void setAlias ( const OUString& sAlias ); + void setService ( const OUString& sService ); + void setEvent ( const OUString& sEvent , + const OUString& sAlias ); + void setJobConfig ( std::vector< css::beans::NamedValue >&& lArguments ); + void disableJob ( ); + + static std::vector< OUString > getEnabledJobsForEvent( const css::uno::Reference< css::uno::XComponentContext >& rxContext, + std::u16string_view sEvent ); + + static void appendEnabledJobsForEvent( const css::uno::Reference< css::uno::XComponentContext >& rxContext, + const OUString& sEvent , + ::std::vector< JobData::TJob2DocEventBinding >& lJobs ); + + // private helper + + private: + + void impl_reset(); +}; + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/jobs/jobresult.hxx b/framework/inc/jobs/jobresult.hxx new file mode 100644 index 0000000000..a9d3f189ba --- /dev/null +++ b/framework/inc/jobs/jobresult.hxx @@ -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 . + */ + +#pragma once + +#include +#include + +#include + +namespace framework{ + +/** + @short represent a result of a finished job execution + @descr Such result instance transport all necessary + data from the code place where the job was finished + to the outside code, where e.g. listener must be notified. + */ +class JobResult final +{ + + // types + + public: + + /** + These enum values are used to build a flag mask of possible set + parts of an analyzed pure job execution result. + An user of this class can decide, if a member of us can be valid + or not. So it can indicate, if a job used the special part inside + its returned result protocol. + To be usable as flags - it must be values of set {0,1,2,4,8,16 ...}! + */ + enum EParts + { + E_NOPART = 0, + E_ARGUMENTS = 1, + E_DEACTIVATE = 2, + E_DISPATCHRESULT = 4 + }; + + // member + + private: + + /** + an user of us must know, which (possible) parts of + a "pure result" was really set by an executed job. + Means which other members of this class are valid. + This mask can be used to find it out. + */ + sal_uInt32 m_eParts; + + /** + a job can have persistent data + They are part of the pure result and will be used to + write it to the configuration. But that's part of any + user of us. We provide this information here only. + */ + std::vector< css::beans::NamedValue > m_lArguments; + + /** + represent the part "DispatchResult" + It's a fulfilled event type, which was given + back by the executed job. Any user of us can send + it to its registered result listener directly. + */ + css::frame::DispatchResultEvent m_aDispatchResult; + + // native interface + + public: + + JobResult( const css::uno::Any& aResult ); + JobResult( const JobResult& rCopy ); + ~JobResult( ); + + JobResult& operator=( const JobResult& rCopy ); + + bool existPart ( sal_uInt32 eParts ) const; + std::vector< css::beans::NamedValue > getArguments ( ) const; + css::frame::DispatchResultEvent getDispatchResult( ) const; +}; + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/jobs/joburl.hxx b/framework/inc/jobs/joburl.hxx new file mode 100644 index 0000000000..23486ec2f4 --- /dev/null +++ b/framework/inc/jobs/joburl.hxx @@ -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 . + */ + +#pragma once + +#include + +namespace framework{ + +#define JOBURL_EVENT_STR "event=" +#define JOBURL_EVENT_LEN 6 + +#define JOBURL_ALIAS_STR "alias=" +#define JOBURL_ALIAS_LEN 6 + +#define JOBURL_SERVICE_STR "service=" +#define JOBURL_SERVICE_LEN 8 + +#define JOBURL_PART_SEPARATOR ';' +#define JOBURL_PARTARGS_SEPARATOR ',' + +/** + @short can be used to parse, validate and work with job URL's + @descr Job URLs are specified by the following syntax: + vnd.sun.star.job:{[event=]|[alias=]|[service=]} + This class can analyze this structure and separate it into his different parts. + After doing that these parts are accessible by the methods of this class. + */ +class JobURL +{ + + // types + + private: + + /** + possible states of a job URL + Note: These values are used in combination. So they must be + values in form 2^n. + */ + enum ERequest + { + /// mark a job URL as not valid + E_UNKNOWN = 0, + /// it's an event + E_EVENT = 1, + /// it's an alias + E_ALIAS = 2, + /// it's a service + E_SERVICE = 4 + }; + + // types + + private: + + /** knows the state of this URL instance */ + sal_uInt32 m_eRequest; + + /** holds the event part of a job URL */ + OUString m_sEvent; + + /** holds the alias part of a job URL */ + OUString m_sAlias; + + /** holds the service part of a job URL */ + OUString m_sService; + + // native interface + + public: + + JobURL ( const OUString& sURL ); + bool isValid ( ) const; + bool getEvent ( OUString& sEvent ) const; + bool getAlias ( OUString& sAlias ) const; + bool getService ( OUString& sService ) const; + + // private helper + + private: + + static bool implst_split( std::u16string_view sPart , + const char* pPartIdentifier , + sal_Int32 nPartLength , + OUString& rPartValue , + OUString& rPartArguments ); +}; + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/jobs/shelljob.hxx b/framework/inc/jobs/shelljob.hxx new file mode 100644 index 0000000000..9152da5159 --- /dev/null +++ b/framework/inc/jobs/shelljob.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 . + */ + +#pragma once + +#include + +#include +#include +#include + +namespace framework{ + +/** @short implements a job component which can be used + to execute system shell commands. + + @descr Because the job will be implemented generic + it can be bound to any event where jobs can be + registered for. Further there is a generic + way to configure the shell command and it's list + of arguments. + */ +class ShellJob final : public ::cppu::WeakImplHelper< css::lang::XServiceInfo,css::task::XJob > +{ + + // member + private: + + /** @short reference to a uno service manager. */ + css::uno::Reference< css::uno::XComponentContext > m_xContext; + + // native interface + public: + + /** @short create new instance of this class. + + @param xContext + reference to the uno service manager, which created this instance. + Can be used later to create own needed uno resources on demand. + */ + ShellJob(css::uno::Reference< css::uno::XComponentContext > xContext); + + /** @short does nothing real ... + + @descr But it should exists as virtual function, + so this class can't make trouble + related to inline/symbols etcpp.! + */ + virtual ~ShellJob() override; + + // uno interface + public: + + /* interface 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; + + // css.task.XJob + virtual css::uno::Any SAL_CALL execute(const css::uno::Sequence< css::beans::NamedValue >& lArguments) override; + + // helper + private: + + /** generate a return value for method execute() + which will force deactivation of this job for further requests. + + @return an Any following the job protocol for deactivation. + */ + static css::uno::Any impl_generateAnswer4Deactivation(); + + /** substitute all might existing placeholder variables + within the configured command. + + The command is part of the job configuration. + These will make changes more easy (no code changes required). + Further the command can use placeholder as they are supported + by the global substitution service (e.g. $(prog) etcpp) + + @param sCommand + the command containing placeholder variables. + + @return the substituted command. + */ + OUString impl_substituteCommandVariables(const OUString& sCommand); + + /** executes the command. + + @param sCommand + the absolute command as URL or system path (without any argument !). + + @param lArguments + the complete list of arguments configured for these job. + + @param bCheckExitCode + bind the execution result to the exit code of the started process. + If it's set to false we return false only in case executable couldn't be found + or couldn't be started. + + @return sal_True if command was executed successfully; sal_False otherwise. + */ + bool impl_execute(const OUString& sCommand , + const css::uno::Sequence< OUString >& lArguments , + bool bCheckExitCode); +}; + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/menuconfiguration.hxx b/framework/inc/menuconfiguration.hxx new file mode 100644 index 0000000000..9389ed3e39 --- /dev/null +++ b/framework/inc/menuconfiguration.hxx @@ -0,0 +1,102 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 +#include + +namespace com::sun::star::container { class XIndexAccess; } +namespace com::sun::star::frame { class XDispatchProvider; } +namespace com::sun::star::io { class XInputStream; } +namespace com::sun::star::io { class XOutputStream; } +namespace com::sun::star::uno { class XComponentContext; } + +namespace framework +{ + +struct MenuAttributes +{ +private: + oslInterlockedCount refCount; + + MenuAttributes(OUString sFrame, OUString sImageIdStr) + : refCount(0) + , aTargetFrame(std::move(sFrame)) + , aImageId(std::move(sImageIdStr)) + { + } + + MenuAttributes(css::uno::WeakReference _xDispatchProvider) + : refCount(0) + , xDispatchProvider(std::move(_xDispatchProvider)) + { + } + + MenuAttributes(const MenuAttributes&) = delete; + +public: + OUString aTargetFrame; + OUString aImageId; + css::uno::WeakReference xDispatchProvider; + + static void* CreateAttribute(const OUString& rFrame, const OUString& rImageIdStr); + static void* CreateAttribute(const css::uno::WeakReference& rDispatchProvider); + static void ReleaseAttribute(void* nAttributePtr); + + void acquire() + { + osl_atomic_increment(&refCount); + } + + void release() + { + if (!osl_atomic_decrement(&refCount)) + delete this; + } +}; + +class MenuConfiguration final +{ +public: + MenuConfiguration( + // use const when giving a UNO reference by reference + css::uno::Reference< css::uno::XComponentContext > rxContext ); + + ~MenuConfiguration(); + + /// @throws css::lang::WrappedTargetException + /// @throws css::uno::RuntimeException + css::uno::Reference< css::container::XIndexAccess > CreateMenuBarConfigurationFromXML( + css::uno::Reference< css::io::XInputStream > const & rInputStream ); + + /// @throws css::lang::WrappedTargetException + /// @throws css::uno::RuntimeException + void StoreMenuBarConfigurationToXML( + css::uno::Reference< css::container::XIndexAccess > const & rMenuBarConfiguration, + css::uno::Reference< css::io::XOutputStream > const & rOutputStream, + bool bIsMenuBar ); + +private: + css::uno::Reference< css::uno::XComponentContext> m_xContext; +}; + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/pch/precompiled_fwk.cxx b/framework/inc/pch/precompiled_fwk.cxx new file mode 100644 index 0000000000..e61e15d1e8 --- /dev/null +++ b/framework/inc/pch/precompiled_fwk.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_fwk.hxx" + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/pch/precompiled_fwk.hxx b/framework/inc/pch/precompiled_fwk.hxx new file mode 100644 index 0000000000..286a892477 --- /dev/null +++ b/framework/inc/pch/precompiled_fwk.hxx @@ -0,0 +1,302 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If 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 2023-07-19 09:22:12 using: + ./bin/update_pch framework fwk --cutoff=7 --exclude:system --include:module --include:local + + If after updating build fails, use the following command to locate conflicting headers: + ./bin/update_pch_bisect ./framework/inc/pch/precompiled_fwk.hxx "make framework.build" --find-conflicts +*/ + +#include +#if PCH_LEVEL >= 1 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#endif // PCH_LEVEL >= 1 +#if PCH_LEVEL >= 2 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#endif // PCH_LEVEL >= 2 +#if PCH_LEVEL >= 3 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#endif // PCH_LEVEL >= 3 +#if PCH_LEVEL >= 4 +#include +#include +#include +#include +#include +#include +#endif // PCH_LEVEL >= 4 + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/properties.h b/framework/inc/properties.h new file mode 100644 index 0000000000..c4eef829af --- /dev/null +++ b/framework/inc/properties.h @@ -0,0 +1,138 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 + +namespace framework{ + +/** properties for "Frame" class */ + +inline constexpr OUString FRAME_PROPNAME_ASCII_DISPATCHRECORDERSUPPLIER = u"DispatchRecorderSupplier"_ustr; +inline constexpr OUString FRAME_PROPNAME_ASCII_ISHIDDEN = u"IsHidden"_ustr; +inline constexpr OUString FRAME_PROPNAME_ASCII_LAYOUTMANAGER = u"LayoutManager"_ustr; +inline constexpr OUString FRAME_PROPNAME_ASCII_TITLE = u"Title"_ustr; +inline constexpr OUString FRAME_PROPNAME_ASCII_INDICATORINTERCEPTION = u"IndicatorInterception"_ustr; +inline constexpr OUString FRAME_PROPNAME_ASCII_URL = u"URL"_ustr; + +// Please add new entries alphabetical sorted and correct all other handles! +// Start counting with 0, so it can be used as direct index into an array too. +#define FRAME_PROPHANDLE_DISPATCHRECORDERSUPPLIER 0 +#define FRAME_PROPHANDLE_ISHIDDEN 1 +#define FRAME_PROPHANDLE_LAYOUTMANAGER 2 +#define FRAME_PROPHANDLE_TITLE 3 +#define FRAME_PROPHANDLE_INDICATORINTERCEPTION 4 +#define FRAME_PROPHANDLE_URL 5 + +/** properties for "LayoutManager" class */ + +inline constexpr OUString LAYOUTMANAGER_PROPNAME_ASCII_MENUBARCLOSER = u"MenuBarCloser"_ustr; +inline constexpr OUString LAYOUTMANAGER_PROPNAME_ASCII_AUTOMATICTOOLBARS = u"AutomaticToolbars"_ustr; +inline constexpr OUString LAYOUTMANAGER_PROPNAME_ASCII_REFRESHVISIBILITY = u"RefreshContextToolbarVisibility"_ustr; +inline constexpr OUString LAYOUTMANAGER_PROPNAME_ASCII_HIDECURRENTUI = u"HideCurrentUI"_ustr; +inline constexpr OUString LAYOUTMANAGER_PROPNAME_ASCII_LOCKCOUNT = u"LockCount"_ustr; +inline constexpr OUString LAYOUTMANAGER_PROPNAME_ASCII_PRESERVE_CONTENT_SIZE = u"PreserveContentSize"_ustr; +inline constexpr OUString LAYOUTMANAGER_PROPNAME_ASCII_REFRESHTOOLTIP = u"RefreshContextToolbarToolTip"_ustr; + +#define LAYOUTMANAGER_PROPNAME_MENUBARCLOSER LAYOUTMANAGER_PROPNAME_ASCII_MENUBARCLOSER + +#define LAYOUTMANAGER_PROPHANDLE_MENUBARCLOSER 0 +#define LAYOUTMANAGER_PROPHANDLE_AUTOMATICTOOLBARS 1 +#define LAYOUTMANAGER_PROPHANDLE_REFRESHVISIBILITY 2 +#define LAYOUTMANAGER_PROPHANDLE_HIDECURRENTUI 3 +#define LAYOUTMANAGER_PROPHANDLE_LOCKCOUNT 4 +#define LAYOUTMANAGER_PROPHANDLE_PRESERVE_CONTENT_SIZE 5 +#define LAYOUTMANAGER_PROPHANDLE_REFRESHTOOLTIP 6 + +/** properties for "UICommandDescription" class */ +inline constexpr OUString UICOMMANDDESCRIPTION_NAMEACCESS_COMMANDIMAGELIST = u"private:resource/image/commandimagelist"_ustr; +inline constexpr OUString UICOMMANDDESCRIPTION_NAMEACCESS_COMMANDROTATEIMAGELIST = u"private:resource/image/commandrotateimagelist"_ustr; +inline constexpr OUString UICOMMANDDESCRIPTION_NAMEACCESS_COMMANDMIRRORIMAGELIST = u"private:resource/image/commandmirrorimagelist"_ustr; + + +#define UICOMMANDDESCRIPTION_PROPERTIES_TOGGLEBUTTON 8 + +/** properties for "AutoRecovery" class */ + +inline constexpr OUString AUTORECOVERY_PROPNAME_ASCII_EXISTS_RECOVERYDATA = u"ExistsRecoveryData"_ustr; +inline constexpr OUString AUTORECOVERY_PROPNAME_ASCII_EXISTS_SESSIONDATA = u"ExistsSessionData"_ustr; +inline constexpr OUString AUTORECOVERY_PROPNAME_ASCII_CRASHED = u"Crashed"_ustr; + +#define AUTORECOVERY_PROPNAME_EXISTS_RECOVERYDATA AUTORECOVERY_PROPNAME_ASCII_EXISTS_RECOVERYDATA +#define AUTORECOVERY_PROPNAME_EXISTS_SESSIONDATA AUTORECOVERY_PROPNAME_ASCII_EXISTS_SESSIONDATA +#define AUTORECOVERY_PROPNAME_CRASHED AUTORECOVERY_PROPNAME_ASCII_CRASHED + +#define AUTORECOVERY_PROPHANDLE_EXISTS_RECOVERYDATA 0 +#define AUTORECOVERY_PROPHANDLE_EXISTS_SESSIONDATA 1 +#define AUTORECOVERY_PROPHANDLE_CRASHED 2 + +/** properties for Filter config */ + +inline constexpr OUString FILTER_PROPNAME_ASCII_DOCUMENTSERVICE = u"DocumentService"_ustr; + +/** properties for office module config (Setup.xcu) */ + +inline constexpr OUString OFFICEFACTORY_PROPNAME_ASCII_UINAME = u"ooSetupFactoryUIName"_ustr; +inline constexpr OUString OFFICEFACTORY_PROPNAME_ASCII_ICON = u"ooSetupFactoryIcon"_ustr; + +/** provides some helper methods to implement property sets. */ + +class PropHelper +{ + public: + + /** checks if given property will be changed by this settings. + * We compare the content of the given any values. If they are different we return sal_True - sal_False otherwise. + * + * @param aCurrentValue contains the current value for this property + * @param aNewValue contains the new value for this property + * @param aOldValue returns the current value, in case something will be changed + * @param aChangedValue returns the new value, in case something will be changed + * + * @return if value of this property will be changed; otherwise. + */ + + static bool willPropertyBeChanged( const css::uno::Any& aCurrentValue , + const css::uno::Any& aNewValue , + css::uno::Any& aOldValue , + css::uno::Any& aChangedValue ) + { + bool bChanged = false; + + // clear return parameter to be sure, to put out only valid values ... + aOldValue.clear(); + aChangedValue.clear(); + + // if value change ... + bChanged = aCurrentValue != aNewValue; + if (bChanged) + { + // ... set information of change. + aOldValue = aCurrentValue; + aChangedValue = aNewValue; + } + + return bChanged; + } +}; + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/protocols.h b/framework/inc/protocols.h new file mode 100644 index 0000000000..bbadb7e718 --- /dev/null +++ b/framework/inc/protocols.h @@ -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 . + */ + +/*TODO outline this implementation :-) */ + +#pragma once + +#include + +#include + +#include + +namespace framework{ + +/** + some protocols must be checked during loading or dispatching URLs manually + It can be necessary to decide, if a URL represent a non visible content or + a real visible component. + */ + +// indicates a loadable content in general! +#define SPECIALPROTOCOL_PRIVATE "private:" +// indicates loading of components using a model directly +#define SPECIALPROTOCOL_PRIVATE_OBJECT u"private:object" +// indicates loading of components using a stream only +#define SPECIALPROTOCOL_PRIVATE_STREAM u"private:stream" +// indicates creation of empty documents +#define SPECIALPROTOCOL_PRIVATE_FACTORY u"private:factory" +// internal protocol of the sfx project for generic dispatch functionality +#define SPECIALPROTOCOL_SLOT u"slot:" +// external representation of the slot protocol using names instead of id's +#define SPECIALPROTOCOL_UNO u".uno:" +// special sfx protocol to execute macros +#define SPECIALPROTOCOL_MACRO u"macro:" +// generic way to start uno services during dispatch +#define SPECIALPROTOCOL_SERVICE u"service:" +// for sending mails +#define SPECIALPROTOCOL_MAILTO u"mailto:" +// for sending news +#define SPECIALPROTOCOL_NEWS u"news:" + +/** well known protocols */ +enum class EProtocol +{ + PrivateObject, + PrivateStream, + PrivateFactory, + Slot, + Uno, + Macro, + Service, + MailTo, + News +}; + +class ProtocolCheck +{ + public: + + /** + it checks if given URL match the required protocol only + It should be used instead of specifyProtocol() if only this question + is interesting to perform the code. We must not check for all possible protocols here... + */ + static bool isProtocol( std::u16string_view sURL, EProtocol eRequired ) + { + bool bRet = false; + switch(eRequired) + { + case EProtocol::PrivateObject: + bRet = o3tl::starts_with(sURL, SPECIALPROTOCOL_PRIVATE_OBJECT); + break; + case EProtocol::PrivateStream: + bRet = o3tl::starts_with(sURL, SPECIALPROTOCOL_PRIVATE_STREAM); + break; + case EProtocol::PrivateFactory: + bRet = o3tl::starts_with(sURL, SPECIALPROTOCOL_PRIVATE_FACTORY); + break; + case EProtocol::Slot: + bRet = o3tl::starts_with(sURL, SPECIALPROTOCOL_SLOT); + break; + case EProtocol::Uno: + bRet = o3tl::starts_with(sURL, SPECIALPROTOCOL_UNO); + break; + case EProtocol::Macro: + bRet = o3tl::starts_with(sURL, SPECIALPROTOCOL_MACRO); + break; + case EProtocol::Service: + bRet = o3tl::starts_with(sURL, SPECIALPROTOCOL_SERVICE); + break; + case EProtocol::MailTo: + bRet = o3tl::starts_with(sURL, SPECIALPROTOCOL_MAILTO); + break; + case EProtocol::News: + bRet = o3tl::starts_with(sURL, SPECIALPROTOCOL_NEWS); + break; + default: + bRet = false; + break; + } + return bRet; + } +}; + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/recording/dispatchrecorder.hxx b/framework/inc/recording/dispatchrecorder.hxx new file mode 100644 index 0000000000..ec03189d3c --- /dev/null +++ b/framework/inc/recording/dispatchrecorder.hxx @@ -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 . + */ + +#pragma once + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace framework{ + +class DispatchRecorder final + : public ::cppu::WeakImplHelper< + css::lang::XServiceInfo + , css::frame::XDispatchRecorder + , css::container::XIndexReplace > +{ + // private member + private: + ::std::vector < css::frame::DispatchStatement > m_aStatements; + sal_Int32 m_nRecordingID; + css::uno::Reference< css::script::XTypeConverter > m_xConverter; + + // public interface + public: + DispatchRecorder( const css::uno::Reference< css::uno::XComponentContext >& xSMGR ); + virtual ~DispatchRecorder() override; + + /* interface 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; + + // XDispatchRecorder + virtual void SAL_CALL startRecording ( const css::uno::Reference< css::frame::XFrame >& xFrame ) override; + virtual void SAL_CALL recordDispatch ( const css::util::URL& aURL, const css::uno::Sequence< css::beans::PropertyValue >& lArguments ) override; + virtual void SAL_CALL recordDispatchAsComment( const css::util::URL& aURL, const css::uno::Sequence< css::beans::PropertyValue >& lArguments ) override; + virtual void SAL_CALL endRecording () override; + virtual OUString SAL_CALL getRecordedMacro () override; + + virtual css::uno::Type SAL_CALL getElementType() override; + + virtual sal_Bool SAL_CALL hasElements() override; + + virtual sal_Int32 SAL_CALL getCount() override; + + virtual css::uno::Any SAL_CALL getByIndex(sal_Int32) override; + + virtual void SAL_CALL replaceByIndex(sal_Int32, const css::uno::Any&) override; + + // private functions + private: + void implts_recordMacro( std::u16string_view aURL, + const css::uno::Sequence< css::beans::PropertyValue >& lArguments, + bool bAsComment, OUStringBuffer& ); + void AppendToBuffer( const css::uno::Any& aValue, OUStringBuffer& aArgumentBuffer ); + +}; // class DispatcRecorder + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/recording/dispatchrecordersupplier.hxx b/framework/inc/recording/dispatchrecordersupplier.hxx new file mode 100644 index 0000000000..d01cec161d --- /dev/null +++ b/framework/inc/recording/dispatchrecordersupplier.hxx @@ -0,0 +1,83 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 +#include +#include +#include + +#include + +namespace framework{ + +/** @short implement a supplier for dispatch recorder + @descr This supplier can be set on property "DispatchRecorderSupplier" on a frame. + By using of this supplier and his internal XDispatchRecorder it's possible to + record XDispatch::dispatch() requests. + + @threadsafe yes + */ +class DispatchRecorderSupplier final : public ::cppu::WeakImplHelper< + css::lang::XServiceInfo , + css::frame::XDispatchRecorderSupplier > +{ + + // member + + private: + + /** provided dispatch recorder of this supplier instance + + @life Is controlled from outside. Because this variable is set + from there and not created internally. But we release our + reference to it if we die. + */ + css::uno::Reference< css::frame::XDispatchRecorder > m_xDispatchRecorder; + + // uno interface + + public: + + /* interface 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; + + // XDispatchRecorderSupplier + + virtual void SAL_CALL setDispatchRecorder( const css::uno::Reference< css::frame::XDispatchRecorder >& xRecorder ) override; + virtual css::uno::Reference< css::frame::XDispatchRecorder > SAL_CALL getDispatchRecorder( ) override; + virtual void SAL_CALL dispatchAndRecord ( const css::util::URL& aURL , + const css::uno::Sequence< css::beans::PropertyValue >& lArguments , + const css::uno::Reference< css::frame::XDispatch >& xDispatcher ) override; + + // native interface + + public: + + DispatchRecorderSupplier(); + virtual ~DispatchRecorderSupplier() override; + +}; // class DispatchRecorderSupplier + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/services.h b/framework/inc/services.h new file mode 100644 index 0000000000..c87008f8eb --- /dev/null +++ b/framework/inc/services.h @@ -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 . + */ + +#pragma once + +#include + +namespace framework{ + +// used servicenames by framework + +inline constexpr OUString SERVICENAME_FILTERFACTORY = u"com.sun.star.document.FilterFactory"_ustr; +inline constexpr OUString SERVICENAME_CFGUPDATEACCESS = u"com.sun.star.configuration.ConfigurationUpdateAccess"_ustr; // provides read/write access to the configuration +inline constexpr OUString SERVICENAME_CFGREADACCESS = u"com.sun.star.configuration.ConfigurationAccess"_ustr; // provides readonly access to the configuration +inline constexpr OUString SERVICENAME_JOB = u"com.sun.star.task.Job"_ustr; +inline constexpr OUString SERVICENAME_PROTOCOLHANDLER = u"com.sun.star.frame.ProtocolHandler"_ustr; +inline constexpr OUString SERVICENAME_POPUPMENUCONTROLLER = u"com.sun.star.frame.PopupMenuController"_ustr; + +// used implementationnames by framework + +#define IMPLEMENTATIONNAME_QUICKLAUNCHER "com.sun.star.comp.desktop.QuickstartWrapper" +inline constexpr OUString IMPLEMENTATIONNAME_FWK_TASKCREATOR = u"com.sun.star.comp.framework.TaskCreator"_ustr; + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/services/layoutmanager.hxx b/framework/inc/services/layoutmanager.hxx new file mode 100644 index 0000000000..f2f32a53a1 --- /dev/null +++ b/framework/inc/services/layoutmanager.hxx @@ -0,0 +1,274 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +class MenuBar; + +namespace framework +{ + class MenuBarWrapper; + class ProgressBarWrapper; + class ToolbarLayoutManager; + class GlobalSettings; + namespace detail + { + class InfoHelperBuilder; + } + typedef ::cppu::WeakImplHelper < css::lang::XServiceInfo + , css::frame::XLayoutManager2 + , css::awt::XWindowListener + > LayoutManager_Base; + typedef ::comphelper::OPropertyContainer LayoutManager_PBase; + class LayoutManager final : public LayoutManager_Base , + private cppu::BaseMutex, + public ::cppu::OBroadcastHelper , + public LayoutManager_PBase + { + public: + LayoutManager( const css::uno::Reference< css::uno::XComponentContext >& xContext ); + virtual ~LayoutManager() override; + + /** declaration of XInterface, XTypeProvider, XServiceInfo */ + DECLARE_XINTERFACE() + DECLARE_XTYPEPROVIDER() + virtual OUString SAL_CALL getImplementationName() override + { + return "com.sun.star.comp.framework.LayoutManager"; + } + + virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override + { + return cppu::supportsService(this, ServiceName); + } + + virtual css::uno::Sequence SAL_CALL getSupportedServiceNames() override + { + css::uno::Sequence< OUString > aSeq { "com.sun.star.frame.LayoutManager" }; + return aSeq; + } + + // XLayoutManager + virtual void SAL_CALL attachFrame( const css::uno::Reference< css::frame::XFrame >& Frame ) override; + virtual void SAL_CALL reset() override; + virtual css::awt::Rectangle SAL_CALL getCurrentDockingArea( ) override; + virtual css::uno::Reference< css::ui::XDockingAreaAcceptor > SAL_CALL getDockingAreaAcceptor() override; + virtual void SAL_CALL setDockingAreaAcceptor( const css::uno::Reference< css::ui::XDockingAreaAcceptor >& xDockingAreaAcceptor ) final override; + virtual void SAL_CALL createElement( const OUString& aName ) override; + virtual void SAL_CALL destroyElement( const OUString& aName ) override; + virtual sal_Bool SAL_CALL requestElement( const OUString& ResourceURL ) override; + virtual css::uno::Reference< css::ui::XUIElement > SAL_CALL getElement( const OUString& aName ) override; + virtual css::uno::Sequence< css::uno::Reference< css::ui::XUIElement > > SAL_CALL getElements( ) override; + virtual sal_Bool SAL_CALL showElement( const OUString& aName ) override; + virtual sal_Bool SAL_CALL hideElement( const OUString& aName ) override; + virtual sal_Bool SAL_CALL dockWindow( const OUString& aName, css::ui::DockingArea DockingArea, const css::awt::Point& Pos ) override; + virtual sal_Bool SAL_CALL dockAllWindows( ::sal_Int16 nElementType ) override; + virtual sal_Bool SAL_CALL floatWindow( const OUString& aName ) override; + virtual sal_Bool SAL_CALL lockWindow( const OUString& ResourceURL ) override; + virtual sal_Bool SAL_CALL unlockWindow( const OUString& ResourceURL ) override; + virtual void SAL_CALL setElementSize( const OUString& aName, const css::awt::Size& aSize ) override; + virtual void SAL_CALL setElementPos( const OUString& aName, const css::awt::Point& aPos ) override; + virtual void SAL_CALL setElementPosSize( const OUString& aName, const css::awt::Point& aPos, const css::awt::Size& aSize ) override; + virtual sal_Bool SAL_CALL isElementVisible( const OUString& aName ) override; + virtual sal_Bool SAL_CALL isElementFloating( const OUString& aName ) override; + virtual sal_Bool SAL_CALL isElementDocked( const OUString& aName ) override; + virtual sal_Bool SAL_CALL isElementLocked( const OUString& ResourceURL ) override; + virtual css::awt::Size SAL_CALL getElementSize( const OUString& aName ) override; + virtual css::awt::Point SAL_CALL getElementPos( const OUString& aName ) override; + virtual void SAL_CALL lock( ) override; + virtual void SAL_CALL unlock( ) override; + virtual void SAL_CALL doLayout( ) override; + virtual void SAL_CALL setVisible( sal_Bool bVisible ) override; + virtual sal_Bool SAL_CALL isVisible() override; + + // XMenuBarMergingAcceptor + + virtual sal_Bool SAL_CALL setMergedMenuBar( const css::uno::Reference< css::container::XIndexAccess >& xMergedMenuBar ) override; + virtual void SAL_CALL removeMergedMenuBar( ) override; + + // XWindowListener + virtual void SAL_CALL windowResized( const css::awt::WindowEvent& aEvent ) override; + virtual void SAL_CALL windowMoved( const css::awt::WindowEvent& aEvent ) override; + virtual void SAL_CALL windowShown( const css::lang::EventObject& aEvent ) override; + virtual void SAL_CALL windowHidden( const css::lang::EventObject& aEvent ) override; + + // XFrameActionListener + virtual void SAL_CALL frameAction( const css::frame::FrameActionEvent& aEvent ) override; + + // XEventListener + using cppu::OPropertySetHelper::disposing; + virtual void SAL_CALL disposing( const css::lang::EventObject& aEvent ) override; + + // XUIConfigurationListener + virtual void SAL_CALL elementInserted( const css::ui::ConfigurationEvent& Event ) override; + virtual void SAL_CALL elementRemoved( const css::ui::ConfigurationEvent& Event ) override; + virtual void SAL_CALL elementReplaced( const css::ui::ConfigurationEvent& Event ) override; + + // XLayoutManagerEventBroadcaster + virtual void SAL_CALL addLayoutManagerEventListener( const css::uno::Reference< css::frame::XLayoutManagerListener >& aLayoutManagerListener ) override; + virtual void SAL_CALL removeLayoutManagerEventListener( const css::uno::Reference< css::frame::XLayoutManagerListener >& aLayoutManagerListener ) override; + + DECL_LINK( MenuBarClose, void *, void); + DECL_LINK( WindowEventListener, VclWindowEvent&, void ); + + // called from ToolbarLayoutManager + void requestLayout(); + + /// Reading of settings - shared with ToolbarLayoutManager. + static bool readWindowStateData( const OUString& rName, UIElement& rElementData, + const css::uno::Reference< css::container::XNameAccess > &rPersistentWindowState, + std::unique_ptr &rGlobalSettings, bool &bInGlobalSettings, + const css::uno::Reference< css::uno::XComponentContext > &rComponentContext ); + + private: + DECL_LINK(AsyncLayoutHdl, Timer *, void); + + // menu bar + + void implts_createMenuBar( const OUString& rMenuBarName ); + void impl_clearUpMenuBar(); + void implts_reset( bool bAttach ); + void implts_updateMenuBarClose(); + bool implts_resetMenuBar(); + void implts_createMSCompatibleMenuBar(const OUString& rName); + + // locking + + void implts_lock(); + bool implts_unlock(); + + // query + + css::uno::Reference< css::ui::XUIElement > implts_findElement( std::u16string_view aName ); + + bool implts_readWindowStateData( const OUString& rName, UIElement& rElementData ); + void implts_writeWindowStateData( const OUString& rName, const UIElement& rElementData ); + void implts_destroyElements(); + void implts_toggleFloatingUIElementsVisibility( bool bActive ); + void implts_reparentChildWindows(); + css::uno::Reference< css::ui::XUIElement > implts_createDockingWindow( const OUString& aElementName ); + + bool implts_isEmbeddedLayoutManager() const; + css::uno::Reference< css::ui::XUIElement > implts_createElement( const OUString& aName ); + + // layouting methods + bool implts_resizeContainerWindow( const css::awt::Size& rContainerSize, const css::awt::Point& rComponentPos ); + ::Size implts_getContainerWindowOutputSize(); + + void implts_setDockingAreaWindowSizes(); + css::awt::Rectangle implts_calcDockingAreaSizes(); + bool implts_doLayout( bool bForceRequestBorderSpace, bool bOuterResize ); + void implts_doLayout_notify( bool bOuterResize ); + + // internal methods to control status/progress bar + ::Size implts_getStatusBarSize(); + void implts_destroyStatusBar(); + void implts_createStatusBar( const OUString& rStatusBarName ); + void implts_createProgressBar(); + void implts_destroyProgressBar(); + void implts_setStatusBarPosSize( const ::Point& rPos, const ::Size& rSize ); + bool implts_showStatusBar( bool bStoreState=false ); + bool implts_hideStatusBar( bool bStoreState=false ); + void implts_readStatusBarState( const OUString& rStatusBarName ); + bool implts_showProgressBar(); + bool implts_hideProgressBar(); + void implts_backupProgressBarWrapper(); + void implts_setOffset( const sal_Int32 nBottomOffset ); + + /// @throws css::uno::RuntimeException + void implts_setInplaceMenuBar( + const css::uno::Reference< css::container::XIndexAccess >& xMergedMenuBar ); + /// @throws css::uno::RuntimeException + void implts_resetInplaceMenuBar(); + + void implts_setVisibleState( bool bShow ); + void implts_updateUIElementsVisibleState( bool bShow ); + void implts_setCurrentUIVisibility( bool bShow ); + void implts_notifyListeners(short nEvent, const css::uno::Any& rInfoParam); + + // OPropertySetHelper + + virtual void SAL_CALL setFastPropertyValue_NoBroadcast( sal_Int32 nHandle , + const css::uno::Any& aValue ) override; + virtual ::cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper() override; + virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo() override; + + css::uno::Reference< css::uno::XComponentContext > m_xContext; /** reference to factory, which has created this instance. */ + css::uno::Reference< css::util::XURLTransformer > m_xURLTransformer; + css::uno::Reference< css::frame::XFrame > m_xFrame; + css::uno::Reference< css::ui::XUIConfigurationManager > m_xModuleCfgMgr; + css::uno::Reference< css::ui::XUIConfigurationManager > m_xDocCfgMgr; + css::uno::Reference< css::awt::XWindow > m_xContainerWindow; + css::uno::Reference< css::awt::XTopWindow2 > m_xContainerTopWindow; + sal_Int32 m_nLockCount; + bool m_bInplaceMenuSet; + bool m_bMenuVisible; + bool m_bVisible; + bool m_bParentWindowVisible; + bool m_bMustDoLayout; + bool m_bAutomaticToolbars; + bool m_bHideCurrentUI; + bool m_bGlobalSettings; + bool m_bPreserveContentSize; + bool m_bMenuBarCloseButton; + css::awt::Rectangle m_aDockingArea; + css::uno::Reference< css::ui::XDockingAreaAcceptor > m_xDockingAreaAcceptor; + rtl::Reference< MenuBarManager > m_xInplaceMenuBar; + rtl::Reference< MenuBarWrapper > m_xMenuBar; + UIElement m_aStatusBarElement; + UIElement m_aProgressBarElement; + rtl::Reference< ProgressBarWrapper > m_xProgressBarBackup; + css::uno::Reference< css::frame::XModuleManager2 > m_xModuleManager; + css::uno::Reference< css::ui::XUIElementFactoryManager > m_xUIElementFactoryManager; + css::uno::Reference< css::container::XNameAccess > m_xPersistentWindowState; + css::uno::Reference< css::container::XNameAccess > m_xPersistentWindowStateSupplier; + std::unique_ptr m_pGlobalSettings; + OUString m_aModuleIdentifier; + Timer m_aAsyncLayoutTimer; + comphelper::OMultiTypeInterfaceContainerHelper2 m_aListenerContainer; // container for ALL Listener + rtl::Reference< ToolbarLayoutManager > m_xToolbarManager; + + friend class detail::InfoHelperBuilder; + }; + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/services/mediatypedetectionhelper.hxx b/framework/inc/services/mediatypedetectionhelper.hxx new file mode 100644 index 0000000000..fd89d5c686 --- /dev/null +++ b/framework/inc/services/mediatypedetectionhelper.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 . + */ + +#pragma once + +#include +#include + +#include + +// namespaces + +namespace framework{ + +/*-************************************************************************************************************ + @implements XInterface + XTypeProvider + XServiceInfo + XStringMapping + @base OWeakObject + + @devstatus deprecated +*//*-*************************************************************************************************************/ + +class MediaTypeDetectionHelper final : public ::cppu::WeakImplHelper< css::util::XStringMapping, css::lang::XServiceInfo> +{ + + // public methods + + public: + + MediaTypeDetectionHelper(); + virtual ~MediaTypeDetectionHelper() override; + + /* interface 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; + + // XStringMapping + + /*-**************************************************************************************************** + *//*-*****************************************************************************************************/ + + virtual sal_Bool SAL_CALL mapStrings(css::uno::Sequence< OUString >& seqParameter) override; + +}; + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/services/uriabbreviation.hxx b/framework/inc/services/uriabbreviation.hxx new file mode 100644 index 0000000000..d33255b3aa --- /dev/null +++ b/framework/inc/services/uriabbreviation.hxx @@ -0,0 +1,55 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 +#include +#include + +#include + +namespace framework +{ +class UriAbbreviation final + : public ::cppu::WeakImplHelper +{ +public: + explicit UriAbbreviation(css::uno::Reference const& context); + + /* interface XServiceInfo */ + virtual OUString SAL_CALL getImplementationName() override; + virtual sal_Bool SAL_CALL supportsService(const OUString& sServiceName) override; + virtual css::uno::Sequence SAL_CALL getSupportedServiceNames() override; + + // css::util::XStringAbbreviation: + virtual OUString SAL_CALL + abbreviateString(const css::uno::Reference& xStringWidth, + ::sal_Int32 nWidth, const OUString& aString) override; + +private: + UriAbbreviation(UriAbbreviation const&) = delete; + UriAbbreviation& operator=(UriAbbreviation const&) = delete; + + virtual ~UriAbbreviation() override {} +}; + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/statusbarconfiguration.hxx b/framework/inc/statusbarconfiguration.hxx new file mode 100644 index 0000000000..a65a9ab4a8 --- /dev/null +++ b/framework/inc/statusbarconfiguration.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 . + */ +#pragma once + +#include + +namespace com::sun::star::container { class XIndexAccess; } +namespace com::sun::star::container { class XIndexContainer; } +namespace com::sun::star::io { class XInputStream; } +namespace com::sun::star::io { class XOutputStream; } +namespace com::sun::star::uno { class XComponentContext; } +namespace com::sun::star::uno { template class Reference; } + +namespace framework +{ + +class StatusBarConfiguration +{ + public: + static bool LoadStatusBar( + const css::uno::Reference< css::uno::XComponentContext >& xContext, + const css::uno::Reference< css::io::XInputStream >& xInputStream, + const css::uno::Reference< css::container::XIndexContainer >& rStatusbarConfiguration ); + + static bool StoreStatusBar( + const css::uno::Reference< css::uno::XComponentContext >& xContext, + const css::uno::Reference< css::io::XOutputStream >& xOutputStream, + const css::uno::Reference< css::container::XIndexAccess >& rStatusbarConfiguration ); +}; + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/stdtypes.h b/framework/inc/stdtypes.h new file mode 100644 index 0000000000..265daba778 --- /dev/null +++ b/framework/inc/stdtypes.h @@ -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 + +namespace framework{ + +/** + Own hash functions used for stl-structures ... e.g. hash tables/maps ... +*/ + +struct KeyEventHashCode +{ + size_t operator()( const css::awt::KeyEvent& aEvent ) const + { + return static_cast(aEvent.KeyCode + + //aEvent.KeyChar + + //aEvent.KeyFunc + + aEvent.Modifiers); + } +}; + +struct KeyEventEqualsFunc +{ + bool operator()(const css::awt::KeyEvent& rKey1, + const css::awt::KeyEvent& rKey2) const + { + return ( + (rKey1.KeyCode == rKey2.KeyCode ) && + //(rKey1.KeyChar == rKey2.KeyChar ) && + //(rKey1.KeyFunc == rKey2.KeyFunc ) && + (rKey1.Modifiers == rKey2.Modifiers) + ); + } +}; + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/strings.hrc b/framework/inc/strings.hrc new file mode 100644 index 0000000000..086fe14a4e --- /dev/null +++ b/framework/inc/strings.hrc @@ -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 . + */ + +#pragma once + +#define NC_(Context, String) TranslateId(Context, u8##String) + +#define STR_MENU_HEADFOOTALL NC_("STR_MENU_HEADFOOTALL", "All" ) +#define STR_UPDATEDOC NC_("STR_UPDATEDOC", "~Update" ) +#define STR_CLOSEDOC_ANDRETURN NC_("STR_CLOSEDOC_ANDRETURN", "~Close & Return to " ) +#define STR_TOOLBAR_VISIBLE_BUTTONS NC_("STR_TOOLBAR_VISIBLE_BUTTONS", "Visible ~Buttons") +#define STR_TOOLBAR_CUSTOMIZE_TOOLBAR NC_("STR_TOOLBAR_CUSTOMIZE_TOOLBAR", "~Customize Toolbar...") +#define STR_TOOLBAR_UNDOCK_TOOLBAR NC_("STR_TOOLBAR_UNDOCK_TOOLBAR", "U~ndock Toolbar" ) +#define STR_TOOLBAR_DOCK_TOOLBAR NC_("STR_TOOLBAR_DOCK_TOOLBAR", "~Dock Toolbar" ) +#define STR_TOOLBAR_DOCK_ALL_TOOLBARS NC_("STR_TOOLBAR_DOCK_ALL_TOOLBARS", "Dock ~All Toolbars" ) +#define STR_TOOLBAR_LOCK_TOOLBAR NC_("STR_TOOLBAR_LOCK_TOOLBAR", "~Lock Toolbar Position" ) +#define STR_TOOLBAR_CLOSE_TOOLBAR NC_("STR_TOOLBAR_CLOSE_TOOLBAR", "Close ~Toolbar" ) +#define STR_SAVECOPYDOC NC_("STR_SAVECOPYDOC", "Save Copy ~as..." ) +#define STR_NODOCUMENT NC_("STR_NODOCUMENT", "No Documents") +#define STR_CLEAR_RECENT_FILES NC_("STR_CLEAR_RECENT_FILES", "Clear List") +#define STR_CLEAR_RECENT_FILES_HELP NC_("STR_CLEAR_RECENT_FILES_HELP", "Clears the list with the most recently opened files. This action can not be undone.") +#define STR_REMOTE_TITLE NC_("STR_REMOTE_TITLE", " (Remote)") +#define STR_EMDASH_SEPARATOR NC_("STR_EMDASH_SEPARATOR", " — ") +#define STR_SAFEMODE_TITLE NC_("STR_SAFEMODE_TITLE", " (Safe Mode)") +#define STR_TOOLBAR_TITLE_ADDON NC_("STR_TOOLBAR_TITLE_ADDON", "Add-On %num%") +#define STR_FULL_DISC_RETRY_BUTTON NC_("STR_FULL_DISC_RETRY_BUTTON", "Retry" ) +#define STR_FULL_DISC_MSG NC_("STR_FULL_DISC_MSG", "%PRODUCTNAME could not save important internal information due to insufficient free disk space at the following location:\n%PATH\n\nYou will not be able to continue working with %PRODUCTNAME without allocating more free disk space at that location.\n\nPress the 'Retry' button after you have allocated more free disk space to retry saving the data.\n\n" ) +#define STR_RESTORE_TOOLBARS NC_("STR_RESTORE_TOOLBARS", "~Reset" ) +#define STR_LOCK_TOOLBARS NC_("STR_LOCK_TOOLBARS", "~Lock Toolbars" ) +#define STR_CORRUPT_UICFG_SHARE NC_("STR_CORRUPT_UICFG_SHARE", "An error occurred while loading the user interface configuration data. The application will be terminated now.\nPlease try to reinstall the application." ) +#define STR_CORRUPT_UICFG_USER NC_("STR_CORRUPT_UICFG_USER", "An error occurred while loading the user interface configuration data. The application will be terminated now.\nPlease try to remove your user profile for the application." ) +#define STR_CORRUPT_UICFG_GENERAL NC_("STR_CORRUPT_UICFG_GENERAL", "An error occurred while loading the user interface configuration data. The application will be terminated now.\nPlease try to remove your user profile for the application first or try to reinstall the application." ) +#define STR_UNTITLED_DOCUMENT NC_("STR_UNTITLED_DOCUMENT", "Untitled" ) +// To translators: for displaying 'Multiple Languages' in the language statusbar control +#define STR_LANGSTATUS_MULTIPLE_LANGUAGES NC_("STR_LANGSTATUS_MULTIPLE_LANGUAGES", "Multiple Languages" ) +#define STR_LANGSTATUS_NONE NC_("STR_LANGSTATUS_NONE", "None (Do not check spelling)" ) +#define STR_RESET_TO_DEFAULT_LANGUAGE NC_("STR_RESET_TO_DEFAULT_LANGUAGE", "Reset to Default Language" ) +#define STR_LANGSTATUS_MORE NC_("STR_LANGSTATUS_MORE", "More..." ) +#define STR_SET_LANGUAGE_FOR_PARAGRAPH NC_("STR_SET_LANGUAGE_FOR_PARAGRAPH", "Set Language for Paragraph" ) +#define STR_LANGSTATUS_HINT NC_("STR_LANGSTATUS_HINT", "Text Language. Right-click to set character or paragraph language" ) + +#define RID_STR_PROPTITLE_EDIT NC_("RID_STR_PROPTITLE_EDIT", "Text Box") +#define RID_STR_PROPTITLE_CHECKBOX NC_("RID_STR_PROPTITLE_CHECKBOX", "Check Box") +#define RID_STR_PROPTITLE_COMBOBOX NC_("RID_STR_PROPTITLE_COMBOBOX", "Combo Box") +#define RID_STR_PROPTITLE_LISTBOX NC_("RID_STR_PROPTITLE_LISTBOX", "List Box") +#define RID_STR_PROPTITLE_DATEFIELD NC_("RID_STR_PROPTITLE_DATEFIELD", "Date Field") +#define RID_STR_PROPTITLE_TIMEFIELD NC_("RID_STR_PROPTITLE_TIMEFIELD", "Time Field") +#define RID_STR_PROPTITLE_NUMERICFIELD NC_("RID_STR_PROPTITLE_NUMERICFIELD", "Numeric Field") +#define RID_STR_PROPTITLE_CURRENCYFIELD NC_("RID_STR_PROPTITLE_CURRENCYFIELD", "Currency Field") +#define RID_STR_PROPTITLE_PATTERNFIELD NC_("RID_STR_PROPTITLE_PATTERNFIELD", "Pattern Field") +#define RID_STR_PROPTITLE_FORMATTED NC_("RID_STR_PROPTITLE_FORMATTED", "Formatted Field") + +#define RID_STR_PROPTITLE_PUSHBUTTON NC_("RID_STR_PROPTITLE_PUSHBUTTON", "Push Button") +#define RID_STR_PROPTITLE_RADIOBUTTON NC_("RID_STR_PROPTITLE_RADIOBUTTON", "Option Button") +#define RID_STR_PROPTITLE_FIXEDTEXT NC_("RID_STR_PROPTITLE_FIXEDTEXT", "Label Field") +#define RID_STR_PROPTITLE_GROUPBOX NC_("RID_STR_PROPTITLE_GROUPBOX", "Group Box") +#define RID_STR_PROPTITLE_IMAGEBUTTON NC_("RID_STR_PROPTITLE_IMAGEBUTTON", "Image Button") +#define RID_STR_PROPTITLE_IMAGECONTROL NC_("RID_STR_PROPTITLE_IMAGECONTROL", "Image Control") +#define RID_STR_PROPTITLE_FILECONTROL NC_("RID_STR_PROPTITLE_FILECONTROL", "File Selection") +#define RID_STR_PROPTITLE_SCROLLBAR NC_("RID_STR_PROPTITLE_SCROLLBAR", "Scrollbar") +#define RID_STR_PROPTITLE_SPINBUTTON NC_("RID_STR_PROPTITLE_SPINBUTTON", "Spin Button") +#define RID_STR_PROPTITLE_NAVBAR NC_("RID_STR_PROPTITLE_NAVBAR", "Navigation Bar") + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/targets.h b/framework/inc/targets.h new file mode 100644 index 0000000000..331eff0dce --- /dev/null +++ b/framework/inc/targets.h @@ -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 . + */ + +#pragma once + +#include + +namespace framework{ + +// Values for special frame search ... sTargetFrameName of findFrame() or queryDispatch() or loadComponentFromURL() + +inline constexpr OUString SPECIALTARGET_SELF = u"_self"_ustr; // The frame himself is searched. +#define SPECIALTARGET_PARENT u"_parent" // The direct parent frame is searched. +inline constexpr OUString SPECIALTARGET_TOP = u"_top"_ustr; // Search at our parents for the first task (if any exist) or a frame without a parent. +inline constexpr OUString SPECIALTARGET_BLANK = u"_blank"_ustr; // Create a new task. +inline constexpr OUString SPECIALTARGET_DEFAULT = u"_default"_ustr; // Create a new task or recycle an existing one +inline constexpr OUString SPECIALTARGET_BEAMER = u"_beamer"_ustr; // special frame in hierarchy +inline constexpr OUString SPECIALTARGET_HELPTASK = u"OFFICE_HELP_TASK"_ustr; // special name for our help task + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/taskcreatordefs.hxx b/framework/inc/taskcreatordefs.hxx new file mode 100644 index 0000000000..e7eca6838a --- /dev/null +++ b/framework/inc/taskcreatordefs.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 . + */ + +#pragma once + +#include + +/// [XFrame] if it's set, it will be used as parent frame for the new created frame. +inline constexpr OUString ARGUMENT_PARENTFRAME = u"ParentFrame"_ustr; // XFrame + +/** [OUString] if it's not a special name (beginning with "_" ... which are not allowed here!) + it will be set as the API name of the new created frame. + */ +inline constexpr OUString ARGUMENT_FRAMENAME = u"FrameName"_ustr; // OUString + +/// [bool] If it's set to true we will make the new created frame visible. +inline constexpr OUString ARGUMENT_MAKEVISIBLE = u"MakeVisible"_ustr; // bool + +/** [bool] If not "ContainerWindow" property is set it force creation of a + top level window as new container window. + */ +inline constexpr OUString ARGUMENT_CREATETOPWINDOW = u"CreateTopWindow"_ustr; // bool + +/// [Rectangle] Place the new created frame on this place and resize the container window. +inline constexpr OUString ARGUMENT_POSSIZE = u"PosSize"_ustr; // Rectangle + +/// [XWindow] an outside created window, used as container window of the new created frame. +inline constexpr OUString ARGUMENT_CONTAINERWINDOW = u"ContainerWindow"_ustr; // XWindow + +/** [bool] enable/disable special mode, where this frame will be part of + the persistent window state feature suitable for any office module window + */ +inline constexpr OUString ARGUMENT_SUPPORTPERSISTENTWINDOWSTATE + = u"SupportPersistentWindowState"_ustr; // bool + +/** [bool] enable/disable special mode, where the title bar of our + the new created frame will be updated automatically. + Default = ON ! + */ +inline constexpr OUString ARGUMENT_ENABLE_TITLEBARUPDATE = u"EnableTitleBarUpdate"_ustr; // bool + +/** [bool] enable/disable if the frame is explicitly requested to be hidden + Default = OFF ! + */ +inline constexpr OUString ARGUMENT_HIDDENFORCONVERSION = u"HiddenForConversion"_ustr; // bool + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/threadhelp/transactionguard.hxx b/framework/inc/threadhelp/transactionguard.hxx new file mode 100644 index 0000000000..24c273be62 --- /dev/null +++ b/framework/inc/threadhelp/transactionguard.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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 + +namespace framework{ + +class TransactionGuard +{ + public: + TransactionGuard( TransactionManager& rManager, EExceptionMode eMode ) + : m_pManager( &rManager ) + { + m_pManager->registerTransaction( eMode ); + } + + ~TransactionGuard() + { + m_pManager->unregisterTransaction(); + } + TransactionGuard(const TransactionGuard&) = delete; + TransactionGuard& operator=(const TransactionGuard&) = delete; + + private: + TransactionManager* m_pManager; +}; + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/toolboxconfiguration.hxx b/framework/inc/toolboxconfiguration.hxx new file mode 100644 index 0000000000..66bedb153e --- /dev/null +++ b/framework/inc/toolboxconfiguration.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 . + */ +#pragma once + +#include + +namespace com::sun::star::container { class XIndexAccess; } +namespace com::sun::star::container { class XIndexContainer; } +namespace com::sun::star::io { class XInputStream; } +namespace com::sun::star::io { class XOutputStream; } +namespace com::sun::star::uno { class XComponentContext; } +namespace com::sun::star::uno { template class Reference; } + +namespace framework +{ + +class ToolBoxConfiguration +{ + public: + static bool LoadToolBox( + const css::uno::Reference< css::uno::XComponentContext >& rxContext, + const css::uno::Reference< css::io::XInputStream >& rInputStream, + const css::uno::Reference< css::container::XIndexContainer >& rToolbarConfiguration ); + + static bool StoreToolBox( + const css::uno::Reference< css::uno::XComponentContext >& rxContext, + const css::uno::Reference< css::io::XOutputStream >& rOutputStream, + const css::uno::Reference< css::container::XIndexAccess >& rToolbarConfiguration ); +}; + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/uiconfiguration/globalsettings.hxx b/framework/inc/uiconfiguration/globalsettings.hxx new file mode 100644 index 0000000000..feb5fe8ea3 --- /dev/null +++ b/framework/inc/uiconfiguration/globalsettings.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 . + */ + +#pragma once + +#include + +namespace framework +{ + +class GlobalSettings +{ + public: + GlobalSettings( css::uno::Reference< css::uno::XComponentContext > xContext ); + ~GlobalSettings(); + + enum StateInfo + { + STATEINFO_LOCKED, + STATEINFO_DOCKED + }; + + // settings access + bool HasToolbarStatesInfo() const; + bool GetToolbarStateInfo( StateInfo eStateInfo, css::uno::Any& aValue ); + + private: + GlobalSettings(const GlobalSettings&) = delete; + GlobalSettings& operator=(const GlobalSettings& ) = delete; + + css::uno::Reference< css::uno::XComponentContext > m_xContext; +}; + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/uiconfiguration/graphicnameaccess.hxx b/framework/inc/uiconfiguration/graphicnameaccess.hxx new file mode 100644 index 0000000000..bba9aaed92 --- /dev/null +++ b/framework/inc/uiconfiguration/graphicnameaccess.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 . + */ + +#pragma once + +#include + +#include +#include +#include + +namespace framework +{ + class GraphicNameAccess final : public ::cppu::WeakImplHelper< css::container::XNameAccess > + { + public: + GraphicNameAccess(); + virtual ~GraphicNameAccess() override; + + void addElement( const OUString& rName, const css::uno::Reference< css::graphic::XGraphic >& rElement ); + + // 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; + + // XElementAccess + virtual sal_Bool SAL_CALL hasElements() override; + virtual css::uno::Type SAL_CALL getElementType( ) override; + + private: + typedef std::unordered_map> NameGraphicHashMap; + NameGraphicHashMap m_aNameToElementMap; + css::uno::Sequence< OUString > m_aSeq; + }; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/uiconfiguration/imagemanager.hxx b/framework/inc/uiconfiguration/imagemanager.hxx new file mode 100644 index 0000000000..aacc8bbd3c --- /dev/null +++ b/framework/inc/uiconfiguration/imagemanager.hxx @@ -0,0 +1,94 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 + +#include +#include +#include + +#include +#include +#include + +namespace framework +{ + class ImageManagerImpl; + class ImageManager final : public ::cppu::WeakImplHelper< css::ui::XImageManager, css::lang::XServiceInfo> + { + public: + ImageManager( const css::uno::Reference< css::uno::XComponentContext >& rxContext, bool bForModule ); + virtual ~ImageManager() override; + + virtual OUString SAL_CALL getImplementationName() override + { + return "com.sun.star.comp.framework.ImageManager"; + } + + virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override + { + return cppu::supportsService(this, ServiceName); + } + + virtual css::uno::Sequence SAL_CALL getSupportedServiceNames() override + { + css::uno::Sequence< OUString > aSeq { "com.sun.star.ui.ImageManager" }; + return aSeq; + } + + // 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 >& aListener ) override; + + // XInitialization + virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override; + + // XImageManager + virtual void SAL_CALL reset() override; + virtual css::uno::Sequence< OUString > SAL_CALL getAllImageNames( ::sal_Int16 nImageType ) override; + virtual sal_Bool SAL_CALL hasImage( ::sal_Int16 nImageType, const OUString& aCommandURL ) override; + virtual css::uno::Sequence< css::uno::Reference< css::graphic::XGraphic > > SAL_CALL getImages( ::sal_Int16 nImageType, const css::uno::Sequence< OUString >& aCommandURLSequence ) override; + virtual void SAL_CALL replaceImages( ::sal_Int16 nImageType, const css::uno::Sequence< OUString >& aCommandURLSequence, const css::uno::Sequence< css::uno::Reference< css::graphic::XGraphic > >& aGraphicsSequence ) override; + virtual void SAL_CALL removeImages( ::sal_Int16 nImageType, const css::uno::Sequence< OUString >& aResourceURLSequence ) override; + virtual void SAL_CALL insertImages( ::sal_Int16 nImageType, const css::uno::Sequence< OUString >& aCommandURLSequence, const css::uno::Sequence< css::uno::Reference< css::graphic::XGraphic > >& aGraphicSequence ) override; + + // XUIConfiguration + virtual void SAL_CALL addConfigurationListener( const css::uno::Reference< css::ui::XUIConfigurationListener >& Listener ) override; + virtual void SAL_CALL removeConfigurationListener( const css::uno::Reference< css::ui::XUIConfigurationListener >& Listener ) override; + + // XUIConfigurationPersistence + virtual void SAL_CALL reload() override; + virtual void SAL_CALL store() override; + virtual void SAL_CALL storeToStorage( const css::uno::Reference< css::embed::XStorage >& Storage ) override; + virtual sal_Bool SAL_CALL isModified() override; + virtual sal_Bool SAL_CALL isReadOnly() override; + + // Non-UNO methods + /// @throws css::uno::RuntimeException + void setStorage( const css::uno::Reference< css::embed::XStorage >& Storage ); + + private: + ::std::unique_ptr m_pImpl; + }; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/uiconfiguration/imagetype.hxx b/framework/inc/uiconfiguration/imagetype.hxx new file mode 100644 index 0000000000..54ac4b1682 --- /dev/null +++ b/framework/inc/uiconfiguration/imagetype.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 . + */ + +#pragma once + +#include +#include + +namespace framework +{ + +typedef std::unordered_map< OUString, + OUString > CommandToImageNameMap; +typedef std::unordered_map< OUString, + bool > CommandMap; + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/uiconfiguration/windowstateproperties.hxx b/framework/inc/uiconfiguration/windowstateproperties.hxx new file mode 100644 index 0000000000..f400dd0b54 --- /dev/null +++ b/framework/inc/uiconfiguration/windowstateproperties.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 + +inline constexpr OUString WINDOWSTATE_PROPERTY_LOCKED = u"Locked"_ustr; +inline constexpr OUString WINDOWSTATE_PROPERTY_DOCKED = u"Docked"_ustr; +inline constexpr OUString WINDOWSTATE_PROPERTY_VISIBLE = u"Visible"_ustr; +inline constexpr OUString WINDOWSTATE_PROPERTY_DOCKINGAREA = u"DockingArea"_ustr; +inline constexpr OUString WINDOWSTATE_PROPERTY_DOCKPOS = u"DockPos"_ustr; +inline constexpr OUString WINDOWSTATE_PROPERTY_DOCKSIZE = u"DockSize"_ustr; +inline constexpr OUString WINDOWSTATE_PROPERTY_POS = u"Pos"_ustr; +inline constexpr OUString WINDOWSTATE_PROPERTY_SIZE = u"Size"_ustr; +inline constexpr OUString WINDOWSTATE_PROPERTY_UINAME = u"UIName"_ustr; +inline constexpr OUString WINDOWSTATE_PROPERTY_INTERNALSTATE = u"InternalState"_ustr; +inline constexpr OUString WINDOWSTATE_PROPERTY_STYLE = u"Style"_ustr; +inline constexpr OUString WINDOWSTATE_PROPERTY_CONTEXT = u"ContextSensitive"_ustr; +inline constexpr OUString WINDOWSTATE_PROPERTY_HIDEFROMENU = u"HideFromToolbarMenu"_ustr; +inline constexpr OUString WINDOWSTATE_PROPERTY_NOCLOSE = u"NoClose"_ustr; +inline constexpr OUString WINDOWSTATE_PROPERTY_SOFTCLOSE = u"SoftClose"_ustr; +inline constexpr OUString WINDOWSTATE_PROPERTY_CONTEXTACTIVE = u"ContextActive"_ustr; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/uielement/FixedImageToolbarController.hxx b/framework/inc/uielement/FixedImageToolbarController.hxx new file mode 100644 index 0000000000..ffe40713b5 --- /dev/null +++ b/framework/inc/uielement/FixedImageToolbarController.hxx @@ -0,0 +1,55 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 + +#include +#include + +class ToolBox; + +namespace framework +{ +class FixedImageControl; + +class FixedImageToolbarController final : public ComplexToolbarController +{ +public: + FixedImageToolbarController(const css::uno::Reference& rxContext, + const css::uno::Reference& rFrame, + ToolBox* pToolBar, ToolBoxItemId nID, const OUString& aCommand); + + // XComponent + virtual void SAL_CALL dispose() override; + + void CheckAndUpdateImages(); + +private: + virtual void executeControlCommand(const css::frame::ControlCommand& rControlCommand) override; + + DECL_LINK(MiscOptionsChanged, LinkParamNone*, void); + + VclPtr m_pFixedImageControl; + sal_Int16 m_eSymbolSize; +}; +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/uielement/FixedTextToolbarController.hxx b/framework/inc/uielement/FixedTextToolbarController.hxx new file mode 100644 index 0000000000..bcf1d093f3 --- /dev/null +++ b/framework/inc/uielement/FixedTextToolbarController.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 . + */ + +#pragma once + +#include +#include + +#include + +class ToolBox; + +namespace framework +{ +class FixedTextControl; + +class FixedTextToolbarController final : public ComplexToolbarController +{ +public: + FixedTextToolbarController(const css::uno::Reference& rxContext, + const css::uno::Reference& rFrame, + ToolBox* pToolBar, ToolBoxItemId nID, const OUString& aCommand); + + // XComponent + virtual void SAL_CALL dispose() override; + +private: + virtual void executeControlCommand(const css::frame::ControlCommand& rControlCommand) override; + virtual css::uno::Sequence + getExecuteArgs(sal_Int16 KeyModifier) const override; + + VclPtr m_pFixedTextControl; +}; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/uielement/addonstoolbarwrapper.hxx b/framework/inc/uielement/addonstoolbarwrapper.hxx new file mode 100644 index 0000000000..fc09465eea --- /dev/null +++ b/framework/inc/uielement/addonstoolbarwrapper.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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 +#include +#include +#include + +namespace framework +{ +class ToolBarManager; + +class AddonsToolBarWrapper final : public UIElementWrapperBase +{ + public: + AddonsToolBarWrapper( const css::uno::Reference< css::uno::XComponentContext >& xContext ); + virtual ~AddonsToolBarWrapper() override; + + // XComponent + virtual void SAL_CALL dispose() override; + + // XInitialization + virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override; + + // XUIElement + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL getRealInterface() override; + + // cf. ToolbarLayoutManager + void populateImages(); + + private: + css::uno::Reference< css::uno::XComponentContext > m_xContext; + rtl::Reference< ToolBarManager > m_xToolBarManager; + css::uno::Sequence< css::uno::Sequence< css::beans::PropertyValue > > m_aConfigData; + bool m_bCreatedImages; +}; + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/uielement/buttontoolbarcontroller.hxx b/framework/inc/uielement/buttontoolbarcontroller.hxx new file mode 100644 index 0000000000..759775ef8b --- /dev/null +++ b/framework/inc/uielement/buttontoolbarcontroller.hxx @@ -0,0 +1,94 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +class ToolBox; + +namespace framework +{ + +class ButtonToolbarController final : public css::frame::XStatusListener, + public css::frame::XToolbarController, + public css::lang::XInitialization, + public css::util::XUpdatable, + public css::lang::XComponent, + public ::cppu::BaseMutex, + public ::cppu::OWeakObject +{ + public: + ButtonToolbarController( css::uno::Reference< css::uno::XComponentContext > xContext, + ToolBox* pToolBar, + OUString aCommand ); + virtual ~ButtonToolbarController() override; + + // 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; + + // XInitialization + virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override; + + // XUpdatable + virtual void SAL_CALL update() 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 >& aListener ) override; + + // XEventListener + virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override; + + // XStatusListener + virtual void SAL_CALL statusChanged( const css::frame::FeatureStateEvent& Event ) override; + + // XToolbarController + virtual void SAL_CALL execute( sal_Int16 KeyModifier ) override; + virtual void SAL_CALL click() override; + virtual void SAL_CALL doubleClick() override; + virtual css::uno::Reference< css::awt::XWindow > SAL_CALL createPopupWindow() override; + virtual css::uno::Reference< css::awt::XWindow > SAL_CALL createItemWindow( const css::uno::Reference< css::awt::XWindow >& Parent ) override; + + private: + bool m_bInitialized : 1, + m_bDisposed : 1; + OUString m_aCommandURL; + css::uno::Reference< css::frame::XFrame > m_xFrame; + css::uno::Reference< css::uno::XComponentContext > m_xContext; + css::uno::Reference< css::util::XURLTransformer > m_xURLTransformer; + VclPtr m_pToolbar; +}; + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/uielement/comboboxtoolbarcontroller.hxx b/framework/inc/uielement/comboboxtoolbarcontroller.hxx new file mode 100644 index 0000000000..126aab6500 --- /dev/null +++ b/framework/inc/uielement/comboboxtoolbarcontroller.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 . + */ + +#pragma once + +#include + +#include + +namespace framework +{ + +class ComboBoxControl; + +class ComboboxToolbarController final : public ComplexToolbarController + +{ + public: + ComboboxToolbarController( const css::uno::Reference< css::uno::XComponentContext >& rxContext, + const css::uno::Reference< css::frame::XFrame >& rFrame, + ToolBox* pToolBar, + ToolBoxItemId nID, + sal_Int32 nWidth, + const OUString& aCommand ); + virtual ~ComboboxToolbarController() override; + + // XComponent + virtual void SAL_CALL dispose() override; + + // called from ComboBoxControl + void Select(); + void Modify(); + void GetFocus(); + void LoseFocus(); + void Activate(); + + private: + virtual void executeControlCommand( const css::frame::ControlCommand& rControlCommand ) override; + virtual css::uno::Sequence< css::beans::PropertyValue> getExecuteArgs(sal_Int16 KeyModifier) const override; + + VclPtr m_pComboBox; +}; + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/uielement/commandinfo.hxx b/framework/inc/uielement/commandinfo.hxx new file mode 100644 index 0000000000..a93c693e6a --- /dev/null +++ b/framework/inc/uielement/commandinfo.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 . + */ + +#pragma once + +#include +#include + +#include +#include + +namespace framework +{ + +struct CommandInfo +{ + CommandInfo() : nId( 0 ), + nImageInfo( 0 ) {} + + ::std::vector< ToolBoxItemId > aIds; + ToolBoxItemId nId; + sal_Int16 nImageInfo; +}; + +typedef std::unordered_map CommandToInfoMap; + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/uielement/complextoolbarcontroller.hxx b/framework/inc/uielement/complextoolbarcontroller.hxx new file mode 100644 index 0000000000..0aa10471be --- /dev/null +++ b/framework/inc/uielement/complextoolbarcontroller.hxx @@ -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 . + */ + +#pragma once + +#include +#include +#include +#include +#include + +#include +#include +#include + +class ToolBox; + +namespace framework +{ + +class ComplexToolbarController : public svt::ToolboxController + +{ + public: + ComplexToolbarController( const css::uno::Reference< css::uno::XComponentContext >& rxContext, + const css::uno::Reference< css::frame::XFrame >& rFrame, + ToolBox* pToolBar, + ToolBoxItemId nID, + const OUString& aCommand ); + virtual ~ComplexToolbarController() override; + + // XComponent + virtual void SAL_CALL dispose() override; + + // XToolbarController + virtual void SAL_CALL execute( sal_Int16 KeyModifier ) override; + + // XStatusListener + virtual void SAL_CALL statusChanged( const css::frame::FeatureStateEvent& Event ) override; + + DECL_STATIC_LINK( ComplexToolbarController, ExecuteHdl_Impl, void*, void ); + DECL_STATIC_LINK( ComplexToolbarController, Notify_Impl, void*, void); + + struct ExecuteInfo + { + css::uno::Reference< css::frame::XDispatch > xDispatch; + css::util::URL aTargetURL; + css::uno::Sequence< css::beans::PropertyValue > aArgs; + }; + + struct NotifyInfo + { + OUString aEventName; + css::uno::Reference< css::frame::XControlNotificationListener > xNotifyListener; + css::util::URL aSourceURL; + css::uno::Sequence< css::beans::NamedValue > aInfoSeq; + }; + + protected: + css::uno::Reference< css::frame::XDispatch > getDispatchFromCommand( const OUString& aCommand ) const; + void addNotifyInfo( const OUString& aEventName, + const css::uno::Reference< css::frame::XDispatch >& xDispatch, + const css::uno::Sequence< css::beans::NamedValue >& rInfo ); + + virtual void executeControlCommand( const css::frame::ControlCommand& rControlCommand ) = 0; + virtual css::uno::Sequence< css::beans::PropertyValue> getExecuteArgs(sal_Int16 KeyModifier) const; + const css::util::URL& getInitializedURL(); + void notifyFocusGet(); + void notifyFocusLost(); + void notifyTextChanged( const OUString& aText ); + + VclPtr m_xToolbar; + ToolBoxItemId m_nID; + bool m_bMadeInvisible; + mutable css::util::URL m_aURL; + css::uno::Reference< css::util::XURLTransformer > m_xURLTransformer; +}; + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/uielement/constitemcontainer.hxx b/framework/inc/uielement/constitemcontainer.hxx new file mode 100644 index 0000000000..2cec0b9a30 --- /dev/null +++ b/framework/inc/uielement/constitemcontainer.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 . + */ + +#pragma once + +#include +#include +#include +#include + +#include +#include +#include + +#include + +namespace framework +{ + +class RootItemContainer; +class ItemContainer; +class ConstItemContainer final : public ::cppu::WeakImplHelper< + css::container::XIndexAccess, + css::beans::XFastPropertySet, + css::beans::XPropertySet > +{ + friend class RootItemContainer; + friend class ItemContainer; + + public: + ConstItemContainer(); + ConstItemContainer( const ItemContainer& rtemContainer ); + ConstItemContainer( const css::uno::Reference< css::container::XIndexAccess >& rSourceContainer, bool bFastCopy = false ); + virtual ~ConstItemContainer() 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 + { + return cppu::UnoType>::get(); + } + + virtual sal_Bool SAL_CALL hasElements() override; + + // XPropertySet + virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo( ) 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; + virtual void SAL_CALL addPropertyChangeListener( const OUString& aPropertyName, const css::uno::Reference< css::beans::XPropertyChangeListener >& xListener ) 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& PropertyName, const css::uno::Reference< css::beans::XVetoableChangeListener >& aListener ) override; + virtual void SAL_CALL removeVetoableChangeListener( const OUString& PropertyName, const css::uno::Reference< css::beans::XVetoableChangeListener >& aListener ) 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; + + private: + ::cppu::IPropertyArrayHelper& getInfoHelper(); + css::uno::Sequence< css::beans::Property > impl_getStaticPropertyDescriptor(); + + void copyItemContainer( const std::vector< css::uno::Sequence< css::beans::PropertyValue > >& rSourceVector ); + css::uno::Reference< css::container::XIndexAccess > deepCopyContainer( const css::uno::Reference< css::container::XIndexAccess >& rSubContainer ); + + std::vector< css::uno::Sequence< css::beans::PropertyValue > > m_aItemVector; + OUString m_aUIName; +}; + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/uielement/dropdownboxtoolbarcontroller.hxx b/framework/inc/uielement/dropdownboxtoolbarcontroller.hxx new file mode 100644 index 0000000000..cb01578629 --- /dev/null +++ b/framework/inc/uielement/dropdownboxtoolbarcontroller.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 . + */ + +#pragma once + +#include + +#include +#include + +class ToolBox; + +namespace framework +{ + +class ListBoxControl; + +class DropdownToolbarController final : public ComplexToolbarController + +{ + public: + DropdownToolbarController( const css::uno::Reference< css::uno::XComponentContext >& rxContext, + const css::uno::Reference< css::frame::XFrame >& rFrame, + ToolBox* pToolBar, + ToolBoxItemId nID, + sal_Int32 nWidth, + const OUString& aCommand ); + virtual ~DropdownToolbarController() override; + + // XComponent + virtual void SAL_CALL dispose() override; + + // called from ListBoxControl + void Select(); + void GetFocus(); + void LoseFocus(); + + private: + virtual void executeControlCommand( const css::frame::ControlCommand& rControlCommand ) override; + virtual css::uno::Sequence< css::beans::PropertyValue> getExecuteArgs(sal_Int16 KeyModifier) const override; + + VclPtr m_pListBoxControl; +}; + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/uielement/edittoolbarcontroller.hxx b/framework/inc/uielement/edittoolbarcontroller.hxx new file mode 100644 index 0000000000..c45aa38937 --- /dev/null +++ b/framework/inc/uielement/edittoolbarcontroller.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 . + */ + +#pragma once + +#include + +#include + +class ToolBox; + +namespace framework +{ + +class EditControl; + +class EditToolbarController final : public ComplexToolbarController + +{ + public: + EditToolbarController( const css::uno::Reference< css::uno::XComponentContext >& rxContext, + const css::uno::Reference< css::frame::XFrame >& rFrame, + ToolBox* pToolBar, + ToolBoxItemId nID, + sal_Int32 nWidth, + const OUString& aCommand ); + virtual ~EditToolbarController() override; + + // XComponent + virtual void SAL_CALL dispose() override; + + // called from EditControl + void Modify(); + void GetFocus(); + void LoseFocus(); + void Activate(); + + private: + virtual void executeControlCommand( const css::frame::ControlCommand& rControlCommand ) override; + virtual css::uno::Sequence< css::beans::PropertyValue> getExecuteArgs(sal_Int16 KeyModifier) const override; + + VclPtr m_pEditControl; +}; + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/uielement/fontmenucontroller.hxx b/framework/inc/uielement/fontmenucontroller.hxx new file mode 100644 index 0000000000..b98e8c7b1d --- /dev/null +++ b/framework/inc/uielement/fontmenucontroller.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 . + */ + +#pragma once + +#include + +#include +#include + +namespace framework +{ + class FontMenuController final : public svt::PopupMenuControllerBase + { + using svt::PopupMenuControllerBase::disposing; + + public: + FontMenuController( const css::uno::Reference< css::uno::XComponentContext >& xContext ); + virtual ~FontMenuController() override; + + /* interface 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; + + // XPopupMenuController + virtual void SAL_CALL updatePopupMenu() override; + + // XStatusListener + virtual void SAL_CALL statusChanged( const css::frame::FeatureStateEvent& Event ) override; + + // XMenuListener + virtual void SAL_CALL itemActivated( const css::awt::MenuEvent& rEvent ) override; + + // XEventListener + virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override; + + private: + virtual void impl_setPopupMenu() override; + void fillPopupMenu( const css::uno::Sequence< OUString >& rFontNameSeq, css::uno::Reference< css::awt::XPopupMenu > const & rPopupMenu ); + + OUString m_aFontFamilyName; + css::uno::Reference< css::frame::XDispatch > m_xFontListDispatch; + }; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/uielement/fontsizemenucontroller.hxx b/framework/inc/uielement/fontsizemenucontroller.hxx new file mode 100644 index 0000000000..d04bc637a3 --- /dev/null +++ b/framework/inc/uielement/fontsizemenucontroller.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 +#include +#include + +#include +#include +#include + +namespace framework +{ + class FontSizeMenuController final : public svt::PopupMenuControllerBase + { + using svt::PopupMenuControllerBase::disposing; + + public: + FontSizeMenuController( const css::uno::Reference< css::uno::XComponentContext >& xContext ); + virtual ~FontSizeMenuController() override; + + /* interface 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; + + // XPopupMenuController + virtual void SAL_CALL updatePopupMenu() override; + + // XStatusListener + virtual void SAL_CALL statusChanged( const css::frame::FeatureStateEvent& Event ) override; + + // XEventListener + virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override; + + private: + virtual void impl_setPopupMenu() override; + void setCurHeight( tools::Long nHeight, css::uno::Reference< css::awt::XPopupMenu > const & rPopupMenu ); + void fillPopupMenu( css::uno::Reference< css::awt::XPopupMenu > const & rPopupMenu ); + OUString retrievePrinterName( css::uno::Reference< css::frame::XFrame > const & rFrame ); + + std::vector m_aHeightArray; + css::frame::status::FontHeight m_aFontHeight; + css::uno::Reference< css::frame::XDispatch > m_xCurrentFontDispatch; + }; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/uielement/footermenucontroller.hxx b/framework/inc/uielement/footermenucontroller.hxx new file mode 100644 index 0000000000..304ffeec3c --- /dev/null +++ b/framework/inc/uielement/footermenucontroller.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 . + */ + +#pragma once + +#include + +namespace framework +{ + class FooterMenuController final : public HeaderMenuController + { + public: + FooterMenuController( const css::uno::Reference< css::uno::XComponentContext >& xContext ); + virtual ~FooterMenuController() override; + + /* interface 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; + }; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/uielement/genericstatusbarcontroller.hxx b/framework/inc/uielement/genericstatusbarcontroller.hxx new file mode 100644 index 0000000000..e0eae738fd --- /dev/null +++ b/framework/inc/uielement/genericstatusbarcontroller.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 + +#include + +namespace framework +{ + +struct AddonStatusbarItemData; + +class GenericStatusbarController final : public svt::StatusbarController +{ + public: + GenericStatusbarController( const css::uno::Reference< css::uno::XComponentContext >& rxContext, + const css::uno::Reference< css::frame::XFrame >& rFrame, + const css::uno::Reference< css::ui::XStatusbarItem >& rxItem, + AddonStatusbarItemData *pItemData ); + virtual ~GenericStatusbarController() override; + + // XComponent + virtual void SAL_CALL dispose() override; + // XStatusListener + virtual void SAL_CALL statusChanged( const css::frame::FeatureStateEvent& Event ) override; + + virtual void SAL_CALL paint( const css::uno::Reference< css::awt::XGraphics >& xGraphics, + const css::awt::Rectangle& rOutputRectangle, + ::sal_Int32 nStyle ) override; + + private: + bool m_bEnabled; + bool m_bOwnerDraw; + AddonStatusbarItemData* m_pItemData; + css::uno::Reference< css::graphic::XGraphic > m_xGraphic; +}; + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/uielement/headermenucontroller.hxx b/framework/inc/uielement/headermenucontroller.hxx new file mode 100644 index 0000000000..cdd48fea49 --- /dev/null +++ b/framework/inc/uielement/headermenucontroller.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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 + +#include + +namespace framework +{ + class HeaderMenuController : public svt::PopupMenuControllerBase + { + using svt::PopupMenuControllerBase::disposing; + + public: + HeaderMenuController( const css::uno::Reference< css::uno::XComponentContext >& xContext, bool _bFooter = false ); + virtual ~HeaderMenuController() override; + + /* interface 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; + + // XPopupMenuController + virtual void SAL_CALL updatePopupMenu() override; + + // XStatusListener + virtual void SAL_CALL statusChanged( const css::frame::FeatureStateEvent& Event ) override; + + // XEventListener + virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override; + + private: + void fillPopupMenu( const css::uno::Reference< css::frame::XModel >& rModel, css::uno::Reference< css::awt::XPopupMenu > const & rPopupMenu ); + + css::uno::Reference< css::frame::XModel > m_xModel; + bool m_bFooter; + }; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/uielement/imagebuttontoolbarcontroller.hxx b/framework/inc/uielement/imagebuttontoolbarcontroller.hxx new file mode 100644 index 0000000000..0e8ecbae6c --- /dev/null +++ b/framework/inc/uielement/imagebuttontoolbarcontroller.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 . + */ + +#pragma once + +#include + +#include +#include + +class ToolBox; + +namespace framework +{ + +class ImageButtonToolbarController final : public ComplexToolbarController + +{ + public: + ImageButtonToolbarController( const css::uno::Reference< css::uno::XComponentContext >& rxContext, + const css::uno::Reference< css::frame::XFrame >& rFrame, + ToolBox* pToolBar, + ToolBoxItemId nID, + const OUString& aCommand ); + virtual ~ImageButtonToolbarController() override; + + // XComponent + virtual void SAL_CALL dispose() override; + + private: + virtual void executeControlCommand( const css::frame::ControlCommand& rControlCommand ) override; + + bool ReadImageFromURL( bool bBigImage, const OUString& aImageURL, Image& aImage ); +}; + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/uielement/itemcontainer.hxx b/framework/inc/uielement/itemcontainer.hxx new file mode 100644 index 0000000000..927beea584 --- /dev/null +++ b/framework/inc/uielement/itemcontainer.hxx @@ -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 . + */ + +#pragma once + +#include + +#include +#include + +#include + +#include + +namespace framework +{ +class ConstItemContainer; +class ItemContainer final : public ::cppu::WeakImplHelper< css::container::XIndexContainer> +{ + friend class ConstItemContainer; + + public: + ItemContainer( const ShareableMutex& ); + ItemContainer( const ConstItemContainer& rConstItemContainer, const ShareableMutex& rMutex ); + ItemContainer( const css::uno::Reference< css::container::XIndexAccess >& rItemAccessContainer, const ShareableMutex& rMutex ); + virtual ~ItemContainer() override; + + // XInterface, XTypeProvider + + // XIndexContainer + virtual void SAL_CALL insertByIndex( sal_Int32 Index, const css::uno::Any& Element ) override; + + virtual void SAL_CALL removeByIndex( sal_Int32 Index ) override; + + // XIndexReplace + virtual void SAL_CALL replaceByIndex( sal_Int32 Index, const css::uno::Any& Element ) 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 + { + return cppu::UnoType>::get(); + } + + virtual sal_Bool SAL_CALL hasElements() override; + + private: + void copyItemContainer( const std::vector< css::uno::Sequence< css::beans::PropertyValue > >& rSourceVector, const ShareableMutex& rMutex ); + css::uno::Reference< css::container::XIndexAccess > deepCopyContainer( const css::uno::Reference< css::container::XIndexAccess >& rSubContainer, const ShareableMutex& rMutex ); + + mutable ShareableMutex m_aShareMutex; + std::vector< css::uno::Sequence< css::beans::PropertyValue > > m_aItemVector; +}; + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/uielement/langselectionmenucontroller.hxx b/framework/inc/uielement/langselectionmenucontroller.hxx new file mode 100644 index 0000000000..070054a2f4 --- /dev/null +++ b/framework/inc/uielement/langselectionmenucontroller.hxx @@ -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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 + +#include +#include + +#include + +namespace framework +{ + class LanguageSelectionMenuController final : public svt::PopupMenuControllerBase + { + using svt::PopupMenuControllerBase::disposing; + + public: + LanguageSelectionMenuController( const css::uno::Reference< css::uno::XComponentContext >& xContext ); + virtual ~LanguageSelectionMenuController() override; + + /* interface 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; + + // XPopupMenuController + virtual void SAL_CALL updatePopupMenu() override; + + // XStatusListener + virtual void SAL_CALL statusChanged( const css::frame::FeatureStateEvent& Event ) override; + + // XEventListener + virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override; + + private: + // XInitialization + virtual void initializeImpl( std::unique_lock& rGuard, const css::uno::Sequence< css::uno::Any >& aArguments ) override; + + virtual void impl_setPopupMenu() override; + enum Mode + { + MODE_SetLanguageSelectionMenu, + MODE_SetLanguageParagraphMenu, + MODE_SetLanguageAllTextMenu + }; + + bool m_bShowMenu; + OUString m_aLangStatusCommandURL; + css::uno::Reference< css::frame::XDispatch > m_xLanguageDispatch; + OUString m_aMenuCommandURL_Lang; + css::uno::Reference< css::frame::XDispatch > m_xMenuDispatch_Lang; + OUString m_aMenuCommandURL_Font; + css::uno::Reference< css::frame::XDispatch > m_xMenuDispatch_Font; + OUString m_aMenuCommandURL_CharDlgForParagraph; + css::uno::Reference< css::frame::XDispatch > m_xMenuDispatch_CharDlgForParagraph; + OUString m_aCurLang; + SvtScriptType m_nScriptType; + OUString m_aKeyboardLang; + OUString m_aGuessedTextLang; + LanguageGuessingHelper m_aLangGuessHelper; + + void fillPopupMenu( css::uno::Reference< css::awt::XPopupMenu > const & rPopupMenu, const Mode rMode ); + }; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/uielement/macrosmenucontroller.hxx b/framework/inc/uielement/macrosmenucontroller.hxx new file mode 100644 index 0000000000..7419f02a70 --- /dev/null +++ b/framework/inc/uielement/macrosmenucontroller.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 + +namespace framework +{ + class MacrosMenuController final : public svt::PopupMenuControllerBase + { + using svt::PopupMenuControllerBase::disposing; + + css::uno::Reference< css::uno::XComponentContext > m_xContext; + + public: + MacrosMenuController( const css::uno::Reference< css::uno::XComponentContext >& xContext ); + virtual ~MacrosMenuController() override; + + /* interface 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; + + // XStatusListener + virtual void SAL_CALL statusChanged( const css::frame::FeatureStateEvent& Event ) override; + + // XEventListener + virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override; + + private: + void fillPopupMenu(const css::uno::Reference& rPopupMenu); + void addScriptItems(const css::uno::Reference& rPopupMenu, sal_uInt16 startItemId); + }; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/uielement/menubarmanager.hxx b/framework/inc/uielement/menubarmanager.hxx new file mode 100644 index 0000000000..7036be08a2 --- /dev/null +++ b/framework/inc/uielement/menubarmanager.hxx @@ -0,0 +1,194 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace framework +{ + +struct PopupControllerEntry +{ + css::uno::WeakReference< css::frame::XDispatchProvider > m_xDispatchProvider; +}; + +typedef std::unordered_map< OUString, PopupControllerEntry > PopupControllerCache; + +class MenuBarManager final : + public comphelper::WeakComponentImplHelper< + css::frame::XStatusListener, + css::frame::XFrameActionListener, + css::ui::XUIConfigurationListener, + css::awt::XSystemDependentMenuPeer> +{ + public: + MenuBarManager( + const css::uno::Reference< css::uno::XComponentContext >& xContext, + const css::uno::Reference< css::frame::XFrame >& rFrame, + const css::uno::Reference< css::util::XURLTransformer >& _xURLTransformer, + const css::uno::Reference< css::frame::XDispatchProvider >& rDispatchProvider, + const OUString& aModuleIdentifier, + Menu* pMenu, + bool bDelete, + bool bHasMenuBar = true ); + + virtual ~MenuBarManager() override; + + // XStatusListener + virtual void SAL_CALL statusChanged( const css::frame::FeatureStateEvent& Event ) override; + + // XFrameActionListener + virtual void SAL_CALL frameAction( const css::frame::FrameActionEvent& Action ) override; + + // XEventListener + virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override; + + // XUIConfigurationListener + virtual void SAL_CALL elementInserted( const css::ui::ConfigurationEvent& Event ) override; + virtual void SAL_CALL elementRemoved( const css::ui::ConfigurationEvent& Event ) override; + virtual void SAL_CALL elementReplaced( const css::ui::ConfigurationEvent& Event ) override; + + // XSystemDependentMenuPeer + virtual css::uno::Any SAL_CALL getMenuHandle( const css::uno::Sequence< sal_Int8 >& ProcessId, sal_Int16 SystemType ) override; + + DECL_LINK( Select, Menu *, bool ); + + Menu* GetMenuBar() const { return m_pVCLMenu; } + + // Configuration methods + static void FillMenuWithConfiguration( sal_uInt16& nId, Menu* pMenu, + const OUString& rModuleIdentifier, + const css::uno::Reference< css::container::XIndexAccess >& rItemContainer, + const css::uno::Reference< css::util::XURLTransformer >& rTransformer ); + static void FillMenu( sal_uInt16& nId, + Menu* pMenu, + const OUString& rModuleIdentifier, + const css::uno::Reference< css::container::XIndexAccess >& rItemContainer, + const css::uno::Reference< css::frame::XDispatchProvider >& rDispatchProvider ); + + void FillMenuManager( Menu* pMenu, + const css::uno::Reference< css::frame::XFrame >& rFrame, + const css::uno::Reference< css::frame::XDispatchProvider >& rDispatchProvider, + const OUString& rModuleIdentifier, + bool bDelete ); + void SetItemContainer( const css::uno::Reference< css::container::XIndexAccess >& rItemContainer ); + void GetPopupController( PopupControllerCache& rPopupController ); + + private: + DECL_LINK( Activate, Menu *, bool ); + DECL_LINK( Deactivate, Menu *, bool ); + DECL_LINK( AsyncSettingsHdl, Timer *, void ); + + void disposing(std::unique_lock&) override; + void RemoveListener(); + void RequestImages(); + void RetrieveImageManagers(); + static bool MustBeHidden( PopupMenu* pPopupMenu, const css::uno::Reference< css::util::XURLTransformer >& rTransformer ); + OUString RetrieveLabelFromCommand(const OUString& rCmdURL); + + struct MenuItemHandler + { + MenuItemHandler( sal_uInt16 aItemId, + rtl::Reference< MenuBarManager > xManager, + css::uno::Reference< css::frame::XDispatch > xDispatch ) : + nItemId( aItemId ), + bMadeInvisible ( false ), + xSubMenuManager(std::move( xManager )), + xMenuItemDispatch(std::move( xDispatch )) {} + + sal_uInt16 nItemId; + bool bMadeInvisible; + OUString aTargetFrame; + OUString aMenuItemURL; + OUString aParsedItemURL; + rtl::Reference< MenuBarManager > xSubMenuManager; + css::uno::Reference< css::frame::XDispatch > xMenuItemDispatch; + css::uno::Reference< css::frame::XPopupMenuController > xPopupMenuController; + css::uno::Reference< css::awt::XPopupMenu > xPopupMenu; + vcl::KeyCode aKeyCode; + }; + + void RetrieveShortcuts( std::vector< std::unique_ptr >& aMenuShortCuts ); + static void FillMenuImages( css::uno::Reference< css::frame::XFrame > const & xFrame, Menu* _pMenu, bool bShowMenuImages ); + static void impl_RetrieveShortcutsFromConfiguration( const css::uno::Reference< css::ui::XAcceleratorConfiguration >& rAccelCfg, + const css::uno::Sequence< OUString >& rCommands, + std::vector< std::unique_ptr >& aMenuShortCuts ); + static void MergeAddonMenus( Menu* pMenuBar, const MergeMenuInstructionContainer&, const OUString& aModuleIdentifier ); + + MenuItemHandler* GetMenuItemHandler( sal_uInt16 nItemId ); + bool CreatePopupMenuController( MenuItemHandler* pMenuItemHandler, + const css::uno::Reference< css::frame::XDispatchProvider >& rDispatchProvider, + const OUString& rModuleIdentifier ); + void AddMenu(MenuBarManager* pSubMenuManager,const OUString& _sItemCommand,sal_uInt16 _nItemId); + sal_uInt16 FillItemCommand(OUString& _rItemCommand, Menu* _pMenu,sal_uInt16 _nIndex) const; + void SetHdl(); + + bool m_bDeleteMenu; + bool m_bActive; + bool m_bShowMenuImages; + bool m_bRetrieveImages; + bool m_bAcceleratorCfg; + bool m_bHasMenuBar; + OUString m_aModuleIdentifier; + VclPtr m_pVCLMenu; + css::uno::Reference< css::frame::XFrame > m_xFrame; + css::uno::Reference< css::frame::XUIControllerFactory > m_xPopupMenuControllerFactory; + ::std::vector< std::unique_ptr > m_aMenuItemHandlerVector; + css::uno::Reference< css::frame::XDispatchProvider > m_xDispatchProvider; + css::uno::Reference< css::ui::XImageManager > m_xDocImageManager; + css::uno::Reference< css::ui::XImageManager > m_xModuleImageManager; + css::uno::Reference< css::ui::XAcceleratorConfiguration > m_xDocAcceleratorManager; + css::uno::Reference< css::ui::XAcceleratorConfiguration > m_xModuleAcceleratorManager; + css::uno::Reference< css::ui::XAcceleratorConfiguration > m_xGlobalAcceleratorManager; + css::uno::Reference< css::uno::XComponentContext > m_xContext; + css::uno::Reference< css::util::XURLTransformer > m_xURLTransformer; + css::uno::Reference< css::container::XIndexAccess > m_xDeferredItemContainer; + OUString m_sIconTheme; + Timer m_aAsyncSettingsTimer; +}; + +} // namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/uielement/menubarmerger.hxx b/framework/inc/uielement/menubarmerger.hxx new file mode 100644 index 0000000000..16bb8ec676 --- /dev/null +++ b/framework/inc/uielement/menubarmerger.hxx @@ -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 . + */ + +#pragma once + +#include + +#include +#include + +#include +#include + +namespace framework +{ + +struct AddonMenuItem; +typedef ::std::vector< AddonMenuItem > AddonMenuContainer; + +struct AddonMenuItem +{ + OUString aTitle; + OUString aURL; + OUString aContext; + AddonMenuContainer aSubMenu; +}; + +enum RPResultInfo +{ + RP_OK, + RP_POPUPMENU_NOT_FOUND, + RP_MENUITEM_NOT_FOUND, + RP_MENUITEM_INSTEAD_OF_POPUPMENU_FOUND +}; + +struct ReferencePathInfo +{ + VclPtr pPopupMenu; + sal_uInt16 nPos; + sal_Int32 nLevel; + RPResultInfo eResult; +}; + +namespace MenuBarMerger +{ + bool IsCorrectContext( + std::u16string_view aContext, std::u16string_view aModuleIdentifier ); + + void RetrieveReferencePath( std::u16string_view, + std::vector< OUString >& aReferencePath ); + ReferencePathInfo FindReferencePath( const std::vector< OUString >& aReferencePath, Menu* pMenu ); + sal_uInt16 FindMenuItem( std::u16string_view rCmd, + Menu const * pMenu ); + void GetMenuEntry( const css::uno::Sequence< css::beans::PropertyValue >& rAddonMenuEntry, + AddonMenuItem& aAddonMenu ); + void GetSubMenu( const css::uno::Sequence< css::uno::Sequence< css::beans::PropertyValue > >& rSubMenuEntries, + AddonMenuContainer& rSubMenu ); + bool ProcessMergeOperation( Menu* pMenu, + sal_uInt16 nPos, + sal_uInt16& rItemId, + std::u16string_view rMergeCommand, + std::u16string_view rMergeCommandParameter, + const OUString& rModuleIdentifier, + const AddonMenuContainer& rAddonMenuItems ); + bool ProcessFallbackOperation( const ReferencePathInfo& aRefPathInfo, + sal_uInt16& rItemId, + std::u16string_view rMergeCommand, + std::u16string_view rMergeFallback, + const ::std::vector< OUString >& rReferencePath, + std::u16string_view rModuleIdentifier, + const AddonMenuContainer& rAddonMenuItems ); + bool MergeMenuItems( Menu* pMenu, + sal_uInt16 nPos, + sal_uInt16 nModIndex, + sal_uInt16& rItemId, + const OUString& rModuleIdentifier, + const AddonMenuContainer& rAddonMenuItems ); + bool ReplaceMenuItem( Menu* pMenu, + sal_uInt16 nPos, + sal_uInt16& rItemId, + const OUString& rModuleIdentifier, + const AddonMenuContainer& rAddonMenuItems ); + bool RemoveMenuItems( Menu* pMenu, + sal_uInt16 nPos, + std::u16string_view rMergeCommandParameter ); + bool CreateSubMenu( Menu* pSubMenu, + sal_uInt16& nItemId, + const OUString& rModuleIdentifier, + const AddonMenuContainer& rAddonSubMenu ); +} + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/uielement/menubarwrapper.hxx b/framework/inc/uielement/menubarwrapper.hxx new file mode 100644 index 0000000000..848af5c693 --- /dev/null +++ b/framework/inc/uielement/menubarwrapper.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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 +#include + +#include +#include + +namespace framework +{ + +typedef cppu::ImplInheritanceHelper< UIConfigElementWrapperBase, css::container::XNameAccess> MenuBarWrapper_Base; +class MenuBarWrapper final : public MenuBarWrapper_Base + +{ + public: + MenuBarWrapper( css::uno::Reference< css::uno::XComponentContext > xContext ); + virtual ~MenuBarWrapper() override; + + MenuBarManager* GetMenuBarManager() const { return m_xMenuBarManager.get(); } + + // XComponent + virtual void SAL_CALL dispose() override; + + // XInitialization + virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override; + + // XUIElement + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL getRealInterface() override; + + // XUIElementSettings + virtual void SAL_CALL updateSettings( ) override; + + // XElementAccess + virtual css::uno::Type SAL_CALL getElementType() override; + virtual sal_Bool SAL_CALL hasElements() 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; + + private: + virtual void impl_fillNewData() override; + void fillPopupControllerCache(); + + bool m_bRefreshPopupControllerCache : 1; + rtl::Reference< MenuBarManager > m_xMenuBarManager; + PopupControllerCache m_aPopupControllerCache; + css::uno::Reference< css::uno::XComponentContext > m_xContext; +}; + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/uielement/newmenucontroller.hxx b/framework/inc/uielement/newmenucontroller.hxx new file mode 100644 index 0000000000..45b5e8f886 --- /dev/null +++ b/framework/inc/uielement/newmenucontroller.hxx @@ -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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 + +#include +#include +#include + +namespace framework +{ + class NewMenuController final : public svt::PopupMenuControllerBase + { + using svt::PopupMenuControllerBase::disposing; + + public: + NewMenuController( const css::uno::Reference< css::uno::XComponentContext >& xContext ); + virtual ~NewMenuController() override; + + /* interface 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; + + // XStatusListener + virtual void SAL_CALL statusChanged( const css::frame::FeatureStateEvent& Event ) override; + + // XMenuListener + virtual void SAL_CALL itemSelected( const css::awt::MenuEvent& rEvent ) override; + virtual void SAL_CALL itemActivated( const css::awt::MenuEvent& rEvent ) override; + + // XEventListener + virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override; + + private: + // XInitialization + virtual void initializeImpl( std::unique_lock& rGuard, const css::uno::Sequence< css::uno::Any >& aArguments ) override; + + virtual void impl_setPopupMenu() override; + + void fillPopupMenu( css::uno::Reference< css::awt::XPopupMenu > const & rPopupMenu ); + void retrieveShortcutsFromConfiguration( const css::uno::Reference< css::ui::XAcceleratorConfiguration >& rAccelCfg, + const css::uno::Sequence< OUString >& rCommands, + std::vector< vcl::KeyCode >& aMenuShortCuts ); + void setAccelerators(); + void determineAndSetNewDocAccel(const css::awt::KeyEvent& rKeyCode); + void setMenuImages( PopupMenu* pPopupMenu, bool bSetImages ); + + private: + // members + bool m_bShowImages : 1, + m_bNewMenu : 1, + m_bModuleIdentified : 1, + m_bAcceleratorCfg : 1; + OUString m_aIconTheme; + OUString m_aTargetFrame; + OUString m_aModuleIdentifier; + OUString m_aEmptyDocURL; + css::uno::Reference< css::uno::XComponentContext > m_xContext; + css::uno::Reference< css::ui::XAcceleratorConfiguration > m_xDocAcceleratorManager; + css::uno::Reference< css::ui::XAcceleratorConfiguration > m_xModuleAcceleratorManager; + css::uno::Reference< css::ui::XAcceleratorConfiguration > m_xGlobalAcceleratorManager; + }; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/uielement/progressbarwrapper.hxx b/framework/inc/uielement/progressbarwrapper.hxx new file mode 100644 index 0000000000..5b6ca9019e --- /dev/null +++ b/framework/inc/uielement/progressbarwrapper.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 . + */ + +#pragma once + +#include + +#include + +#include + +namespace framework{ + +class ProgressBarWrapper final : public UIElementWrapperBase +{ + public: + + // constructor / destructor + + ProgressBarWrapper(); + virtual ~ProgressBarWrapper() override; + + // public interfaces + void setStatusBar( const css::uno::Reference< css::awt::XWindow >& rStatusBar, bool bOwnsInstance = false ); + css::uno::Reference< css::awt::XWindow > getStatusBar() const; + + // wrapped methods of css::task::XStatusIndicator + /// @throws css::uno::RuntimeException + void start( const OUString& Text, ::sal_Int32 Range ); + /// @throws css::uno::RuntimeException + void end(); + /// @throws css::uno::RuntimeException + void setText( const OUString& Text ); + /// @throws css::uno::RuntimeException + void setValue( ::sal_Int32 Value ); + /// @throws css::uno::RuntimeException + void reset(); + + // UNO interfaces + // XComponent + virtual void SAL_CALL dispose() override; + + // XInitialization + virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override; + + // XUpdatable + virtual void SAL_CALL update() override; + + // XUIElement + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL getRealInterface() override; + + // variables + // (should be private everyway!) + + private: + css::uno::Reference< css::awt::XWindow > m_xStatusBar; // Reference to our status bar XWindow + css::uno::WeakReference< css::uno::XInterface > m_xProgressBarIfacWrapper; + bool m_bOwnsInstance; // Indicator that we are owner of the XWindow + sal_Int32 m_nRange; + sal_Int32 m_nValue; + OUString m_aText; +}; // class ProgressBarWrapper + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/uielement/rootitemcontainer.hxx b/framework/inc/uielement/rootitemcontainer.hxx new file mode 100644 index 0000000000..159137f0f6 --- /dev/null +++ b/framework/inc/uielement/rootitemcontainer.hxx @@ -0,0 +1,119 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +namespace framework +{ +class ConstItemContainer; + +typedef ::cppu::WeakImplHelper< + css::container::XIndexContainer, + css::lang::XSingleComponentFactory > RootItemContainer_BASE; + +class RootItemContainer final : private cppu::BaseMutex, + public ::cppu::OBroadcastHelper , + public ::cppu::OPropertySetHelper , + public RootItemContainer_BASE +{ + friend class ConstItemContainer; + + public: + RootItemContainer(); + RootItemContainer( const css::uno::Reference< css::container::XIndexAccess >& rItemAccessContainer ); + virtual ~RootItemContainer() override; + + // XInterface + virtual void SAL_CALL acquire() noexcept override + { OWeakObject::acquire(); } + virtual void SAL_CALL release() noexcept override + { OWeakObject::release(); } + virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type& type) override; + + // XTypeProvider + virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes( ) override; + + // XIndexContainer + virtual void SAL_CALL insertByIndex( sal_Int32 Index, const css::uno::Any& Element ) override; + + virtual void SAL_CALL removeByIndex( sal_Int32 Index ) override; + + // XIndexReplace + virtual void SAL_CALL replaceByIndex( sal_Int32 Index, const css::uno::Any& Element ) 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 + { + return cppu::UnoType>::get(); + } + + virtual sal_Bool SAL_CALL hasElements() override; + + // XSingleComponentFactory + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL createInstanceWithContext( const css::uno::Reference< css::uno::XComponentContext >& Context ) override; + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL createInstanceWithArgumentsAndContext( const css::uno::Sequence< css::uno::Any >& Arguments, const css::uno::Reference< css::uno::XComponentContext >& Context ) override; + + private: + // OPropertySetHelper + virtual sal_Bool SAL_CALL convertFastPropertyValue ( css::uno::Any& aConvertedValue , + css::uno::Any& aOldValue , + sal_Int32 nHandle , + const css::uno::Any& aValue ) override; + virtual void SAL_CALL setFastPropertyValue_NoBroadcast( sal_Int32 nHandle , + const css::uno::Any& aValue ) override; + using cppu::OPropertySetHelper::getFastPropertyValue; + virtual void SAL_CALL getFastPropertyValue( css::uno::Any& aValue , + sal_Int32 nHandle ) const override; + virtual ::cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper() override; + virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo() override; + + static css::uno::Sequence< css::beans::Property > impl_getStaticPropertyDescriptor(); + + RootItemContainer& operator=( const RootItemContainer& ) = delete; + RootItemContainer( const RootItemContainer& ) = delete; + + css::uno::Reference< css::container::XIndexAccess > deepCopyContainer( const css::uno::Reference< css::container::XIndexAccess >& rSubContainer ); + + mutable ShareableMutex m_aShareMutex; + std::vector< css::uno::Sequence< css::beans::PropertyValue > > m_aItemVector; + OUString m_aUIName; +}; + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/uielement/spinfieldtoolbarcontroller.hxx b/framework/inc/uielement/spinfieldtoolbarcontroller.hxx new file mode 100644 index 0000000000..609f4ec012 --- /dev/null +++ b/framework/inc/uielement/spinfieldtoolbarcontroller.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 . + */ + +#pragma once + +#include + +#include +#include + +class ToolBox; + +namespace framework +{ + +class SpinfieldControl; + +class SpinfieldToolbarController final : public ComplexToolbarController + +{ + public: + SpinfieldToolbarController( const css::uno::Reference< css::uno::XComponentContext >& rxContext, + const css::uno::Reference< css::frame::XFrame >& rFrame, + ToolBox* pToolBar, + ToolBoxItemId nID, + sal_Int32 nWidth, + const OUString& aCommand ); + virtual ~SpinfieldToolbarController() override; + + // XComponent + virtual void SAL_CALL dispose() override; + + // called from SpinfieldControl + void Modify(); + void GetFocus(); + void LoseFocus(); + void Activate(); + + OUString FormatOutputString(double fValue); + private: + virtual void executeControlCommand( const css::frame::ControlCommand& rControlCommand ) override; + virtual css::uno::Sequence< css::beans::PropertyValue> getExecuteArgs(sal_Int16 KeyModifier) const override; + + bool impl_getValue( const css::uno::Any& rAny, sal_Int32& nValue, double& fValue, bool& bFloat ); + + bool m_bFloat; + double m_nMax; + double m_nMin; + double m_nValue; + double m_nStep; + VclPtr m_pSpinfieldControl; + OUString m_aOutFormat; +}; + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/uielement/statusbar.hxx b/framework/inc/uielement/statusbar.hxx new file mode 100644 index 0000000000..6d33396dbe --- /dev/null +++ b/framework/inc/uielement/statusbar.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 . + */ + +#pragma once + +#include + +#include + +namespace framework +{ + +class FrameworkStatusBar final : public StatusBar +{ + public: + + FrameworkStatusBar( vcl::Window* pParent, + WinBits nWinBits ); + + void SetStatusBarManager( StatusBarManager* pStatusBarManager ); + + virtual void StateChanged( StateChangedType nType ) override; + virtual void DataChanged( const DataChangedEvent& rDCEvt ) override; + virtual void UserDraw(const UserDrawEvent& rUDEvt) override; + virtual void Command( const CommandEvent &rEvt ) override; + virtual void MouseMove( const MouseEvent& rMEvt ) override; + virtual void MouseButtonDown( const MouseEvent& rMEvt ) override; + virtual void MouseButtonUp( const MouseEvent& rMEvt ) override; + + private: + StatusBarManager* m_pMgr; +}; + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/uielement/statusbaritem.hxx b/framework/inc/uielement/statusbaritem.hxx new file mode 100644 index 0000000000..b9597bd2ab --- /dev/null +++ b/framework/inc/uielement/statusbaritem.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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 +#include +#include + +class StatusBar; + +namespace framework +{ + +struct AddonStatusbarItemData; + +typedef comphelper::WeakComponentImplHelper< css::ui::XStatusbarItem > StatusbarItem_Base; + +class StatusbarItem final : public StatusbarItem_Base +{ +public: + explicit StatusbarItem( + StatusBar *pStatusBar, + sal_uInt16 nId, + OUString aCommand ); + virtual ~StatusbarItem() override; + + void disposing(std::unique_lock&) override; + + // css::ui::XStatusbarItem Attributes + virtual OUString SAL_CALL getCommand() override; + virtual ::sal_uInt16 SAL_CALL getItemId() override; + virtual ::sal_uInt32 SAL_CALL getWidth() override; + virtual ::sal_uInt16 SAL_CALL getStyle() override; + virtual ::sal_Int32 SAL_CALL getOffset() override; + virtual css::awt::Rectangle SAL_CALL getItemRect() override; + virtual OUString SAL_CALL getText() override; + virtual void SAL_CALL setText( const OUString& rText ) override; + virtual OUString SAL_CALL getHelpText() override; + virtual void SAL_CALL setHelpText( const OUString& rHelpText ) override; + virtual OUString SAL_CALL getQuickHelpText() override; + virtual void SAL_CALL setQuickHelpText( const OUString& rQuickHelpText ) override; + virtual OUString SAL_CALL getAccessibleName() override; + virtual void SAL_CALL setAccessibleName( const OUString& rAccessibleName ) override; + virtual sal_Bool SAL_CALL getVisible() override; + virtual void SAL_CALL setVisible( sal_Bool bVisible ) override; + + // css::ui::XStatusbarItem Methods + virtual void SAL_CALL repaint( ) override; + +private: + VclPtr m_pStatusBar; + sal_uInt16 m_nId; + sal_uInt16 m_nStyle; + OUString m_aCommand; + +}; + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/uielement/statusbarmanager.hxx b/framework/inc/uielement/statusbarmanager.hxx new file mode 100644 index 0000000000..c893e4abdc --- /dev/null +++ b/framework/inc/uielement/statusbarmanager.hxx @@ -0,0 +1,104 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace framework +{ + +class FrameworkStatusBar; +class StatusBarManager final: public ::cppu::WeakImplHelper< + css::frame::XFrameActionListener, + css::lang::XComponent, + css::ui::XUIConfigurationListener > + +{ + friend class FrameworkStatusBar; + + public: + StatusBarManager( css::uno::Reference< css::uno::XComponentContext > xContext, + css::uno::Reference< css::frame::XFrame > xFrame, + StatusBar* pStatusBar ); + virtual ~StatusBarManager() override; + + StatusBar* GetStatusBar() const; + + // XFrameActionListener + virtual void SAL_CALL frameAction( const css::frame::FrameActionEvent& Action ) override; + + // XEventListener + virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override; + + // XUIConfigurationListener + virtual void SAL_CALL elementInserted( const css::ui::ConfigurationEvent& Event ) override; + virtual void SAL_CALL elementRemoved( const css::ui::ConfigurationEvent& Event ) override; + virtual void SAL_CALL elementReplaced( const css::ui::ConfigurationEvent& Event ) override; + + // XComponent + void SAL_CALL dispose() override; + void SAL_CALL addEventListener( const css::uno::Reference< XEventListener >& xListener ) override; + void SAL_CALL removeEventListener( const css::uno::Reference< XEventListener >& xListener ) override; + + void FillStatusBar( const css::uno::Reference< css::container::XIndexAccess >& rStatusBarData ); + + private: + void DataChanged( const DataChangedEvent& rDCEvt ); + void UserDraw( const UserDrawEvent& rUDEvt ); + void Command( const CommandEvent& rEvt ); + void MouseMove( const MouseEvent& rMEvt ); + void MouseButtonDown( const MouseEvent& rMEvt ); + void MouseButtonUp( const MouseEvent& rMEvt ); + DECL_LINK(Click, StatusBar*, void); + DECL_LINK(DoubleClick, StatusBar*, void); + + void RemoveControllers(); + void CreateControllers(); + void UpdateControllers(); + void MouseButton( const MouseEvent& rMEvt ,sal_Bool ( SAL_CALL css::frame::XStatusbarController::*_pMethod )(const css::awt::MouseEvent&)); + + typedef std::map< sal_uInt16, css::uno::Reference< css::frame::XStatusbarController > > StatusBarControllerMap; + + bool m_bDisposed : 1, + m_bFrameActionRegistered : 1, + m_bUpdateControllers : 1; + VclPtr m_pStatusBar; + css::uno::Reference< css::frame::XFrame > m_xFrame; + StatusBarControllerMap m_aControllerMap; + std::mutex m_mutex; + comphelper::OInterfaceContainerHelper4 m_aListenerContainer; + css::uno::Reference< css::uno::XComponentContext > m_xContext; + css::uno::Reference< css::frame::XUIControllerFactory > m_xStatusbarControllerFactory; +}; + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/uielement/statusbarmerger.hxx b/framework/inc/uielement/statusbarmerger.hxx new file mode 100644 index 0000000000..1eaec5a88f --- /dev/null +++ b/framework/inc/uielement/statusbarmerger.hxx @@ -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 . + */ +#pragma once + +#include + +#include + +#include +#include +#include +#include + +namespace framework +{ + +struct AddonStatusbarItemData +{ + OUString aLabel; +}; + +struct AddonStatusbarItem +{ + OUString aCommandURL; + OUString aLabel; + OUString aContext; + StatusBarItemBits nItemBits; + sal_Int16 nWidth; +}; + +typedef ::std::vector< AddonStatusbarItem > AddonStatusbarItemContainer; + +namespace StatusbarMerger +{ + bool IsCorrectContext( std::u16string_view aContext ); + + bool ConvertSeqSeqToVector( const css::uno::Sequence< css::uno::Sequence< css::beans::PropertyValue > >& rSequence, + AddonStatusbarItemContainer& rContainer ); + + sal_uInt16 FindReferencePos( StatusBar* pStatusbar, + std::u16string_view rReferencePoint ); + + bool ProcessMergeOperation( StatusBar* pStatusbar, + sal_uInt16 nPos, + sal_uInt16& rItemId, + std::u16string_view rMergeCommand, + std::u16string_view rMergeCommandParameter, + const AddonStatusbarItemContainer& rItems ); + + bool ProcessMergeFallback( StatusBar* pStatusbar, + sal_uInt16& rItemId, + std::u16string_view rMergeCommand, + std::u16string_view rMergeFallback, + const AddonStatusbarItemContainer& rItems ); +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/uielement/statusbarwrapper.hxx b/framework/inc/uielement/statusbarwrapper.hxx new file mode 100644 index 0000000000..c281b4f486 --- /dev/null +++ b/framework/inc/uielement/statusbarwrapper.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 +#include +#include +#include + +namespace framework +{ +class StatusBarManager; + +class StatusBarWrapper final : public UIConfigElementWrapperBase +{ + public: + StatusBarWrapper( + css::uno::Reference< css::uno::XComponentContext > xContext ); + virtual ~StatusBarWrapper() override; + + // XComponent + virtual void SAL_CALL dispose() override; + + // XInitialization + virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override; + + // XUIElement + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL getRealInterface() override; + + // XUIElementSettings + virtual void SAL_CALL updateSettings() override; + + private: + rtl::Reference< StatusBarManager > m_xStatusBarManager; + css::uno::Reference< css::uno::XComponentContext > m_xContext; +}; + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/uielement/statusindicatorinterfacewrapper.hxx b/framework/inc/uielement/statusindicatorinterfacewrapper.hxx new file mode 100644 index 0000000000..abfae53e75 --- /dev/null +++ b/framework/inc/uielement/statusindicatorinterfacewrapper.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 +#include + +#include +#include +#include + +namespace framework +{ + +class StatusIndicatorInterfaceWrapper final : public ::cppu::WeakImplHelper< css::task::XStatusIndicator> +{ + public: + StatusIndicatorInterfaceWrapper( const css::uno::Reference< css::lang::XComponent >& rStatusIndicatorImpl ); + virtual ~StatusIndicatorInterfaceWrapper() override; + + // XStatusIndicator + + virtual void SAL_CALL start ( const OUString& sText , + sal_Int32 nRange ) override; + virtual void SAL_CALL end ( ) override; + virtual void SAL_CALL reset ( ) override; + virtual void SAL_CALL setText ( const OUString& sText ) override; + virtual void SAL_CALL setValue( sal_Int32 nValue ) override; + + private: + css::uno::WeakReference< css::lang::XComponent > m_xStatusIndicatorImpl; +}; + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/uielement/styletoolbarcontroller.hxx b/framework/inc/uielement/styletoolbarcontroller.hxx new file mode 100644 index 0000000000..45b1ad7ecc --- /dev/null +++ b/framework/inc/uielement/styletoolbarcontroller.hxx @@ -0,0 +1,78 @@ +/* -*- 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 +#include + +namespace framework { + +/** + * A dispatcher that serves as a proxy for style commands with arguments + * i.e. .uno:StyleApply?... in order to provide useful status updates to + * generic UI elements such as toolbars or menubar. It listens to special + * status commands, and computes a boolean status out of them. Then it + * forwards that boolean status to the listener, as if it was the status + * of the original command. + * + * Note that the implementation is minimal: Although the UI element appears + * to be the owner of the dispatcher, it's still responsible, as usual, to + * call removeStatusListener same amount of times as addStatusListener, + * otherwise the dispatcher might not be destructed. In addition this + * implementation might hold a hard reference on the owner, and it's the + * responsibility of the owner to destroy the dispatcher first, in order + * to break the cycle. + */ +class StyleDispatcher final : public cppu::WeakImplHelper< css::frame::XDispatch, css::frame::XStatusListener > +{ +public: + StyleDispatcher( const css::uno::Reference< css::frame::XFrame >& rFrame, + css::uno::Reference< css::util::XURLTransformer > xUrlTransformer, + const css::util::URL& rURL ); + + // XDispatch + void SAL_CALL dispatch( const css::util::URL& rURL, const css::uno::Sequence< css::beans::PropertyValue >& rArguments ) override; + void SAL_CALL addStatusListener( const css::uno::Reference< css::frame::XStatusListener >& rListener, const css::util::URL& rURL ) override; + void SAL_CALL removeStatusListener( const css::uno::Reference< css::frame::XStatusListener >& rListener, const css::util::URL& rURL ) override; + + // XStatusListener + void SAL_CALL statusChanged( const css::frame::FeatureStateEvent& rEvent ) override; + + // XEventListener + void SAL_CALL disposing( const css::lang::EventObject& rSource ) override; + +private: + OUString m_aStyleName, m_aCommand, m_aStatusCommand; + css::uno::Reference< css::util::XURLTransformer > m_xUrlTransformer; + css::uno::Reference< css::frame::XDispatchProvider > m_xFrame; + css::uno::Reference< css::frame::XDispatch > m_xStatusDispatch; + css::uno::Reference< css::frame::XStatusListener > m_xOwner; +}; + +class StyleToolbarController final : public svt::ToolboxController +{ +public: + StyleToolbarController( const css::uno::Reference< css::uno::XComponentContext >& rContext, + const css::uno::Reference< css::frame::XFrame >& rFrame, + const OUString& rCommand ); + + // XUpdatable + void SAL_CALL update() override; + + // XStatusListener + void SAL_CALL statusChanged( const css::frame::FeatureStateEvent& rEvent ) override; + + // XComponent + void SAL_CALL dispose() override; +}; + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/framework/inc/uielement/togglebuttontoolbarcontroller.hxx b/framework/inc/uielement/togglebuttontoolbarcontroller.hxx new file mode 100644 index 0000000000..fe11433434 --- /dev/null +++ b/framework/inc/uielement/togglebuttontoolbarcontroller.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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 + +#include +#include + +class Menu; +class ToolBox; + +namespace framework +{ + +class ToggleButtonToolbarController final : public ComplexToolbarController + +{ + public: + class DropdownMenuItem + { + public: + OUString mLabel; + OUString mTipHelpText; + }; + + enum class Style + { + DropDownButton, + ToggleDropDownButton + }; + + ToggleButtonToolbarController( const css::uno::Reference< css::uno::XComponentContext >& rxContext, + const css::uno::Reference< css::frame::XFrame >& rFrame, + ToolBox* pToolBar, + ToolBoxItemId nID, + Style eStyle, + const OUString& aCommand ); + virtual ~ToggleButtonToolbarController() override; + + // XComponent + virtual void SAL_CALL dispose() override; + + // XToolbarController + virtual css::uno::Reference< css::awt::XWindow > SAL_CALL createPopupWindow() override; + + private: + virtual void executeControlCommand( const css::frame::ControlCommand& rControlCommand ) override; + virtual css::uno::Sequence< css::beans::PropertyValue> getExecuteArgs(sal_Int16 KeyModifier) const override; + + DECL_LINK( MenuSelectHdl, Menu *, bool); + + OUString m_aCurrentSelection; + std::vector< DropdownMenuItem > m_aDropdownMenuList; +}; + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/uielement/toolbarmanager.hxx b/framework/inc/uielement/toolbarmanager.hxx new file mode 100644 index 0000000000..dc80fbcfa8 --- /dev/null +++ b/framework/inc/uielement/toolbarmanager.hxx @@ -0,0 +1,255 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +class PopupMenu; +class ToolBox; +class Menu; + +namespace framework +{ + +class ImageOrientationController; +class ToolBarManager; + +class ToolBarManagerImpl +{ +public: + virtual ~ToolBarManagerImpl() = default; + virtual void Init() = 0; + virtual void Destroy() = 0; + virtual css::uno::Reference GetInterface() = 0; + virtual void InsertItem(ToolBoxItemId nId, + const OUString& rCommandURL, + const OUString& rTooltip, + const OUString& rLabel, + ToolBoxItemBits nItemBits) = 0; + virtual void InsertSeparator() = 0; + virtual void InsertSpace() = 0; + virtual void InsertBreak() = 0; + virtual ToolBoxItemId GetItemId(sal_uInt16 nPos) = 0; + virtual ToolBoxItemId GetCurItemId() = 0; + virtual OUString GetItemCommand(ToolBoxItemId nId) = 0; + virtual sal_uInt16 GetItemCount() = 0; + virtual void SetItemCheckable(ToolBoxItemId nId) = 0; + virtual void HideItem(ToolBoxItemId nId, const OUString& rCommandURL) = 0; + virtual bool IsItemVisible(ToolBoxItemId nId, const OUString& rCommandURL) = 0; + virtual void Clear() = 0; + virtual void SetName(const OUString& rName) = 0; + virtual void SetHelpId(const OUString& rHelpId) = 0; + virtual bool WillUsePopupMode() = 0; + virtual bool IsReallyVisible() = 0; + virtual void SetIconSize(ToolBoxButtonSize eSize) = 0; + virtual vcl::ImageType GetImageSize() = 0; + virtual void ConnectCallbacks(ToolBarManager* pManager) = 0; + virtual void SetMenuType(ToolBoxMenuType eType) = 0; + virtual void MergeToolbar(ToolBoxItemId & rItemId, + sal_uInt16 nFirstItem, + const OUString& rModuleIdentifier, + CommandToInfoMap& rCommandMap, + MergeToolbarInstruction& rInstruction) = 0; + virtual void SetItemImage(ToolBoxItemId nId, + const OUString& rCommandURL, + const Image& rImage) = 0; + virtual void UpdateSize() = 0; + virtual void SetItemWindow(ToolBoxItemId nItemId, vcl::Window* pNewWindow) = 0; +}; + +typedef ::cppu::WeakImplHelper< + css::frame::XFrameActionListener, + css::lang::XComponent, + css::ui::XUIConfigurationListener + > ToolbarManager_Base; + +class ToolBarManager final : public ToolbarManager_Base +{ + public: + ToolBarManager( const css::uno::Reference< css::uno::XComponentContext >& rxContext, + const css::uno::Reference< css::frame::XFrame >& rFrame, + OUString aResourceName, + ToolBox* pToolBar ); + ToolBarManager( const css::uno::Reference< css::uno::XComponentContext >& rxContext, + const css::uno::Reference< css::frame::XFrame >& rFrame, + OUString aResourceName, + weld::Toolbar* pToolBar, + weld::Builder* pBuilder ); + virtual ~ToolBarManager() override; + + ToolBox* GetToolBar() const; + + // XFrameActionListener + virtual void SAL_CALL frameAction( const css::frame::FrameActionEvent& Action ) override; + + // XEventListener + virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override; + + // XUIConfigurationListener + virtual void SAL_CALL elementInserted( const css::ui::ConfigurationEvent& Event ) override; + virtual void SAL_CALL elementRemoved( const css::ui::ConfigurationEvent& Event ) override; + virtual void SAL_CALL elementReplaced( const css::ui::ConfigurationEvent& Event ) override; + + // XComponent + void SAL_CALL dispose() override; + void SAL_CALL addEventListener( const css::uno::Reference< XEventListener >& xListener ) override; + void SAL_CALL removeEventListener( const css::uno::Reference< XEventListener >& xListener ) override; + + void CheckAndUpdateImages(); + void RequestImages(); + void FillToolbar( const css::uno::Reference< css::container::XIndexAccess >& rToolBarData, + const css::uno::Reference< css::container::XIndexAccess >& rContextData, + const OUString& rContextToolbarName ); + void FillAddonToolbar( const css::uno::Sequence< css::uno::Sequence< css::beans::PropertyValue > >& rAddonToolbar ); + void FillOverflowToolbar( ToolBox const * pParent ); + void notifyRegisteredControllers( const OUString& aUIElementName, const OUString& aCommand ); + void Destroy(); + + enum ExecuteCommand + { + EXEC_CMD_CLOSETOOLBAR, + EXEC_CMD_UNDOCKTOOLBAR, + EXEC_CMD_DOCKTOOLBAR, + EXEC_CMD_DOCKALLTOOLBARS + }; + + enum ClickAction + { + Click, + DblClick, + Execute + }; + + struct ExecuteInfo + { + OUString aToolbarResName; + ExecuteCommand nCmd; + css::uno::Reference< css::frame::XLayoutManager > xLayoutManager; + css::uno::Reference< css::awt::XWindow > xWindow; + }; + + public: + void OnClick(bool bUseExecute = false); + void OnDropdownClick(bool bCreatePopupWindow = true); + + DECL_LINK(DropdownClick, ToolBox *, void); + DECL_LINK(DoubleClick, ToolBox *, void); + DECL_LINK(Select, ToolBox *, void); + DECL_LINK( StateChanged, StateChangedType const *, void ); + DECL_LINK( DataChanged, DataChangedEvent const *, void ); + DECL_LINK( MiscOptionsChanged, LinkParamNone*, void ); + + DECL_LINK( MenuButton, ToolBox *, void ); + DECL_LINK( MenuPreExecute, ToolBox *, void ); + DECL_LINK( MenuSelect, Menu *, bool ); + DECL_LINK(AsyncUpdateControllersHdl, Timer *, void); + DECL_LINK( OverflowEventListener, VclWindowEvent&, void ); + DECL_STATIC_LINK( ToolBarManager, ExecuteHdl_Impl, void*, void ); + + private: + void Init(); + void FillToolbarFromContainer(const css::uno::Reference< css::container::XIndexAccess >& rItemContainer, + const OUString& rResourceName, ToolBoxItemId& nId, ToolBoxItemId& nAddonId); + void ToggleButton(const OUString& rResourceName, std::u16string_view rCommand); + void AddCustomizeMenuItems(ToolBox const * pToolBar); + void InitImageManager(); + void RemoveControllers(); + void CreateControllers(); + void UpdateControllers(); + //for update controller via Support Visible + void UpdateController( const css::uno::Reference< css::frame::XToolbarController >& xController); + //end + void AddFrameActionListener(); + void RefreshImages(); + ToolBoxItemBits ConvertStyleToToolboxItemBits( sal_Int32 nStyle ); + css::uno::Reference< css::frame::XModel > GetModelFromFrame() const; + bool IsPluginMode() const; + void HandleClick(ClickAction eAction); + void setToolBarImage(const Image& _aImage,const CommandToInfoMap::const_iterator& _pIter); + void impl_elementChanged(bool _bRemove,const css::ui::ConfigurationEvent& Event ); + + typedef std::unordered_map< ToolBoxItemId, css::uno::Reference< css::frame::XStatusListener > > ToolBarControllerMap; + typedef ::std::vector< css::uno::Reference< css::frame::XSubToolbarController > > SubToolBarControllerVector; + typedef std::unordered_map SubToolBarToSubToolBarControllerMap; + + bool m_bDisposed : 1, + m_bFrameActionRegistered : 1, + m_bUpdateControllers : 1; + + sal_Int16 m_eSymbolSize; + sal_uInt16 m_nContextMinPos; + + std::unique_ptr m_pImpl; + VclPtr m_pToolBar; + weld::Toolbar* m_pWeldedToolBar; + + OUString m_aModuleIdentifier; + OUString m_aResourceName; + OUString m_aContextResourceName; + + css::uno::Reference< css::util::XURLTransformer > m_xURLTransformer; + css::uno::Reference< css::frame::XFrame > m_xFrame; + ToolBarControllerMap m_aControllerMap; + std::mutex m_mutex; + comphelper::OInterfaceContainerHelper4 m_aListenerContainer; + css::uno::Reference< css::uno::XComponentContext > m_xContext; + css::uno::Reference< css::frame::XUIControllerFactory > m_xToolbarControllerFactory; + css::uno::Reference< css::ui::XImageManager > m_xModuleImageManager; + css::uno::Reference< css::ui::XImageManager > m_xDocImageManager; + + CommandToInfoMap m_aCommandMap; + SubToolBarToSubToolBarControllerMap m_aSubToolBarControllerMap; + Timer m_aAsyncUpdateControllersTimer; + OUString m_sIconTheme; + + rtl::Reference< ToolBarManager > m_aOverflowManager; + rtl::Reference< ImageOrientationController > m_aImageController; +}; + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/uielement/toolbarmerger.hxx b/framework/inc/uielement/toolbarmerger.hxx new file mode 100644 index 0000000000..b84ca2005e --- /dev/null +++ b/framework/inc/uielement/toolbarmerger.hxx @@ -0,0 +1,138 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 + +#include + +#include + +#include +#include + +#include +#include +#include + +namespace framework +{ + +struct AddonsParams +{ + OUString aControlType; + sal_uInt16 nWidth; +}; + +struct AddonToolbarItem +{ + OUString aCommandURL; + OUString aLabel; + OUString aTarget; + OUString aContext; + OUString aControlType; + sal_uInt16 nWidth; +}; + +typedef ::std::vector< AddonToolbarItem > AddonToolbarItemContainer; + +struct ReferenceToolbarPathInfo +{ + ToolBox::ImplToolItems::size_type nPos; + bool bResult; +}; + +class ToolBarMerger +{ + public: + static bool IsCorrectContext( std::u16string_view aContext, std::u16string_view aModuleIdentifier ); + + static void ConvertSeqSeqToVector( const css::uno::Sequence< css::uno::Sequence< css::beans::PropertyValue > >& rSequence, + AddonToolbarItemContainer& rContainer ); + + static void ConvertSequenceToValues( const css::uno::Sequence< css::beans::PropertyValue >& rSequence, + OUString& rCommandURL, + OUString& rLabel, + OUString& rTarget, + OUString& rContext, + OUString& rControlType, + sal_uInt16& rWidth ); + + static ReferenceToolbarPathInfo FindReferencePoint( const ToolBox* pToolbar, sal_uInt16 nFirstItem, + std::u16string_view rReferencePoint ); + + static bool ProcessMergeOperation( ToolBox* pToolbar, + ToolBox::ImplToolItems::size_type nPos, + ToolBoxItemId& rItemId, + CommandToInfoMap& rCommandMap, + std::u16string_view rModuleIdentifier, + std::u16string_view rMergeCommand, + std::u16string_view rMergeCommandParameter, + const AddonToolbarItemContainer& rItems ); + + static bool ProcessMergeFallback( ToolBox* pToolbar, + ToolBoxItemId& rItemId, + CommandToInfoMap& rCommandMap, + std::u16string_view rModuleIdentifier, + std::u16string_view rMergeCommand, + std::u16string_view rMergeFallback, + const AddonToolbarItemContainer& rItems ); + + static void MergeItems( ToolBox* pToolbar, + ToolBox::ImplToolItems::size_type nPos, + sal_uInt16 nModIndex, + ToolBoxItemId& rItemId, + CommandToInfoMap& rCommandMap, + std::u16string_view rModuleIdentifier, + const AddonToolbarItemContainer& rAddonToolbarItems ); + + static void ReplaceItem( ToolBox* pToolbar, + ToolBox::ImplToolItems::size_type nPos, + ToolBoxItemId& rItemId, + CommandToInfoMap& rCommandMap, + std::u16string_view rModuleIdentifier, + const AddonToolbarItemContainer& rAddonToolbarItems ); + + static void RemoveItems( ToolBox* pToolbar, + ToolBox::ImplToolItems::size_type nPos, + std::u16string_view rMergeCommandParameter ); + + static rtl::Reference<::cppu::OWeakObject> CreateController( + const css::uno::Reference< css::uno::XComponentContext > & rxContext, + const css::uno::Reference< css::frame::XFrame > & xFrame, + ToolBox* pToolbar, + const OUString& rCommandURL, + ToolBoxItemId nId, + sal_uInt16 nWidth, + std::u16string_view rControlType ); + + static void CreateToolbarItem( ToolBox* pToolbox, + ToolBox::ImplToolItems::size_type nPos, + ToolBoxItemId nItemId, + const AddonToolbarItem& rAddonToolbarItem ); + + private: + ToolBarMerger( const ToolBarMerger& ) = delete; + ToolBarMerger& operator=( const ToolBarMerger& ) = delete; +}; + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/uielement/toolbarmodemenucontroller.hxx b/framework/inc/uielement/toolbarmodemenucontroller.hxx new file mode 100644 index 0000000000..cee2b0dac3 --- /dev/null +++ b/framework/inc/uielement/toolbarmodemenucontroller.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 . + */ + +#pragma once + +#include + +namespace framework +{ + class ToolbarModeMenuController final : public svt::PopupMenuControllerBase + { + using svt::PopupMenuControllerBase::disposing; + + public: + ToolbarModeMenuController( const css::uno::Reference< css::uno::XComponentContext >& xContext ); + virtual ~ToolbarModeMenuController() override; + + /* interface 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; + + // XPopupMenuController + virtual void SAL_CALL setPopupMenu( const css::uno::Reference< css::awt::XPopupMenu >& PopupMenu ) override; + + // XStatusListener + virtual void SAL_CALL statusChanged( const css::frame::FeatureStateEvent& Event ) override; + + // XMenuListener + virtual void SAL_CALL itemSelected( const css::awt::MenuEvent& rEvent ) override; + virtual void SAL_CALL itemActivated( const css::awt::MenuEvent& rEvent ) override; + + // XEventListener + virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override; + + private: + void fillPopupMenu( css::uno::Reference< css::awt::XPopupMenu > const & rPopupMenu ); + + css::uno::Reference< css::uno::XComponentContext > m_xContext; + }; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/uielement/toolbarsmenucontroller.hxx b/framework/inc/uielement/toolbarsmenucontroller.hxx new file mode 100644 index 0000000000..48ae2eacd3 --- /dev/null +++ b/framework/inc/uielement/toolbarsmenucontroller.hxx @@ -0,0 +1,89 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 +#include +#include +#include + +#include +#include +#include + +#include + +namespace framework +{ + class ToolbarsMenuController final : public svt::PopupMenuControllerBase + { + using svt::PopupMenuControllerBase::disposing; + + public: + ToolbarsMenuController( const css::uno::Reference< css::uno::XComponentContext >& xContext ); + virtual ~ToolbarsMenuController() override; + + /* interface 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; + + // XPopupMenuController + virtual void SAL_CALL setPopupMenu( const css::uno::Reference< css::awt::XPopupMenu >& PopupMenu ) override; + + // XStatusListener + virtual void SAL_CALL statusChanged( const css::frame::FeatureStateEvent& Event ) override; + + // XMenuListener + virtual void SAL_CALL itemSelected( const css::awt::MenuEvent& rEvent ) override; + virtual void SAL_CALL itemActivated( const css::awt::MenuEvent& rEvent ) override; + + // XEventListener + virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override; + + struct ExecuteInfo + { + css::uno::Reference< css::frame::XDispatch > xDispatch; + css::util::URL aTargetURL; + css::uno::Sequence< css::beans::PropertyValue > aArgs; + }; + + DECL_STATIC_LINK( ToolbarsMenuController, ExecuteHdl_Impl, void*, void ); + + private: + // XInitialization + virtual void initializeImpl( std::unique_lock& rGuard, const css::uno::Sequence< css::uno::Any >& aArguments ) override; + + void fillPopupMenu( css::uno::Reference< css::awt::XPopupMenu > const & rPopupMenu ); + css::uno::Sequence< css::uno::Sequence< css::beans::PropertyValue > > getLayoutManagerToolbars( const css::uno::Reference< css::frame::XLayoutManager >& rLayoutManager ); + css::uno::Reference< css::frame::XDispatch > getDispatchFromCommandURL( const OUString& rCommandURL ); + void addCommand( css::uno::Reference< css::awt::XPopupMenu > const & rPopupMenu, const OUString& rCommandURL, const OUString& aLabel ); + + css::uno::Reference< css::uno::XComponentContext > m_xContext; + css::uno::Reference< css::container::XNameAccess > m_xPersistentWindowState; + css::uno::Reference< css::ui::XUIConfigurationManager > m_xModuleCfgMgr; + css::uno::Reference< css::ui::XUIConfigurationManager > m_xDocCfgMgr; + bool m_bResetActive; + std::vector< OUString > m_aCommandVector; + IntlWrapper m_aIntlWrapper; + }; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/uielement/toolbarwrapper.hxx b/framework/inc/uielement/toolbarwrapper.hxx new file mode 100644 index 0000000000..6325c0b541 --- /dev/null +++ b/framework/inc/uielement/toolbarwrapper.hxx @@ -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 . + */ + +#pragma once + +#include + +#include +#include +#include +#include +#include +#include + +namespace weld +{ + class Builder; + class Container; + class Toolbar; +} + +namespace framework +{ +class ToolBarManager; + +class ToolBarWrapper final : public cppu::ImplInheritanceHelper +{ + public: + ToolBarWrapper( const css::uno::Reference< css::uno::XComponentContext >& xContext ); + virtual ~ToolBarWrapper() override; + + // XComponent + virtual void SAL_CALL dispose() override; + + // XInitialization + virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override; + + // XUIElement + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL getRealInterface() override; + + // XUpdatable + virtual void SAL_CALL update() override; + + // XUIElementSettings + virtual void SAL_CALL updateSettings() override; + + // XUIFunctionListener + virtual void SAL_CALL functionExecute( const OUString& aUIElementName, const OUString& aCommand ) override; + + // XContextChangeEventListener + virtual void SAL_CALL notifyContextChangeEvent( const css::ui::ContextChangeEventObject& aEvent ) override; + + // XEventListener + using cppu::OPropertySetHelper::disposing; + virtual void SAL_CALL disposing( const css::lang::EventObject& aEvent ) override; + + private: + virtual void SAL_CALL setFastPropertyValue_NoBroadcast( sal_Int32 nHandle, const css::uno::Any& aValue ) override; + virtual void impl_fillNewData() override; + + rtl::Reference< ToolBarManager > m_xToolBarManager; + css::uno::Reference< css::uno::XComponentContext > m_xContext; + css::uno::Reference< css::ui::XUIElement > m_xSubElement; + + std::unique_ptr m_xBuilder; + std::unique_ptr m_xTopLevel; + std::unique_ptr m_xWeldedToolbar; +}; + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/uielement/uicommanddescription.hxx b/framework/inc/uielement/uicommanddescription.hxx new file mode 100644 index 0000000000..aeeb1f9aa2 --- /dev/null +++ b/framework/inc/uielement/uicommanddescription.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 . + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace framework +{ +typedef comphelper::WeakComponentImplHelper< css::lang::XServiceInfo, + css::container::XNameAccess > UICommandDescription_BASE; + +class UICommandDescription : public UICommandDescription_BASE +{ + public: + UICommandDescription( const css::uno::Reference< css::uno::XComponentContext >& rxContext ); + virtual ~UICommandDescription() override; + + virtual OUString SAL_CALL getImplementationName() override + { + return "com.sun.star.comp.framework.UICommandDescription"; + } + + virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override + { + return cppu::supportsService(this, ServiceName); + } + + virtual css::uno::Sequence SAL_CALL getSupportedServiceNames() override + { + css::uno::Sequence< OUString > aSeq { "com.sun.star.frame.UICommandDescription" }; + return aSeq; + } + +private: + // 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; + + // XElementAccess + virtual css::uno::Type SAL_CALL getElementType() override; + virtual sal_Bool SAL_CALL hasElements() override; + +public: + typedef std::unordered_map< OUString, + OUString > ModuleToCommandFileMap; + + typedef std::unordered_map< OUString, + css::uno::Reference< css::container::XNameAccess > > UICommandsHashMap; + + protected: + UICommandDescription( const css::uno::Reference< css::uno::XComponentContext>& rxContext, bool ); + void impl_fillElements(const char* _pName); + void ensureGenericUICommandsForLanguage(const LanguageTag& rLanguage); + + OUString m_aPrivateResourceURL; + css::uno::Reference< css::uno::XComponentContext > m_xContext; + ModuleToCommandFileMap m_aModuleToCommandFileMap; + std::map m_aUICommandsHashMap; + std::map > m_xGenericUICommands; + css::uno::Reference< css::frame::XModuleManager2 > m_xModuleManager; +}; + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/uielement/uielement.hxx b/framework/inc/uielement/uielement.hxx new file mode 100644 index 0000000000..8e6f0c7de4 --- /dev/null +++ b/framework/inc/uielement/uielement.hxx @@ -0,0 +1,106 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 +#include +#include +#include + +#include +#include +#include + +namespace framework +{ + +struct DockedData +{ + DockedData() : m_aPos( SAL_MAX_INT32, SAL_MAX_INT32 ), + m_nDockedArea( css::ui::DockingArea_DOCKINGAREA_TOP ), + m_bLocked( false ) {} + + css::awt::Point m_aPos; + css::ui::DockingArea m_nDockedArea; + bool m_bLocked; +}; + +struct FloatingData +{ + FloatingData() : m_aPos( SAL_MAX_INT32, SAL_MAX_INT32 ), + m_nLines( 1 ), + m_bIsHorizontal( true ) {} + + css::awt::Point m_aPos; + css::awt::Size m_aSize; + ToolBox::ImplToolItems::size_type m_nLines; + bool m_bIsHorizontal; +}; + +struct UIElement +{ + UIElement() : m_bFloating( false ), + m_bVisible( true ), + m_bUserActive( false ), + m_bMasterHide( false ), + m_bContextSensitive( false ), + m_bNoClose( false ), + m_bStateRead( false ), + m_nStyle( ButtonType::SYMBOLONLY ) + {} + + UIElement( OUString aName, + OUString aType, + css::uno::Reference< css::ui::XUIElement > xUIElement + ) : m_aType(std::move( aType )), + m_aName(std::move( aName )), + m_xUIElement(std::move( xUIElement )), + m_bFloating( false ), + m_bVisible( true ), + m_bUserActive( false ), + m_bMasterHide( false ), + m_bContextSensitive( false ), + m_bNoClose( false ), + m_bStateRead( false ), + m_nStyle( ButtonType::SYMBOLONLY ) {} + + bool operator< ( const UIElement& aUIElement ) const; + + OUString m_aType; + OUString m_aName; + OUString m_aUIName; + css::uno::Reference< css::ui::XUIElement > m_xUIElement; + bool m_bFloating, + m_bVisible, + m_bUserActive, + m_bMasterHide, + m_bContextSensitive; + bool m_bNoClose, + m_bStateRead; + ButtonType m_nStyle; + DockedData m_aDockedData; + FloatingData m_aFloatingData; +}; + +typedef std::vector< UIElement > UIElementVector; + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/uielement/uielementtypenames.hxx b/framework/inc/uielement/uielementtypenames.hxx new file mode 100644 index 0000000000..a248a0d9f2 --- /dev/null +++ b/framework/inc/uielement/uielementtypenames.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 . + */ + +#pragma once + +#define UIELEMENTTYPE_MENUBAR_NAME "menubar" +#define UIELEMENTTYPE_POPUPMENU_NAME "popupmenu" +#define UIELEMENTTYPE_TOOLBAR_NAME "toolbar" +#define UIELEMENTTYPE_STATUSBAR_NAME "statusbar" +#define UIELEMENTTYPE_FLOATINGWINDOW_NAME "floater" +#define UIELEMENTTYPE_PROGRESSBAR_NAME "progressbar" +#define UIELEMENTTYPE_TOOLPANEL_NAME "toolpanel" + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/uifactory/configurationaccessfactorymanager.hxx b/framework/inc/uifactory/configurationaccessfactorymanager.hxx new file mode 100644 index 0000000000..af0345b496 --- /dev/null +++ b/framework/inc/uifactory/configurationaccessfactorymanager.hxx @@ -0,0 +1,83 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +namespace framework { + +class ConfigurationAccess_FactoryManager final : public ::cppu::WeakImplHelper< css::container::XContainerListener> +{ + public: + ConfigurationAccess_FactoryManager( const css::uno::Reference< css::uno::XComponentContext>& rxContext, OUString _sRoot ); + virtual ~ConfigurationAccess_FactoryManager() override; + + void readConfigurationData(); + + OUString getFactorySpecifierFromTypeNameModule( std::u16string_view rType, std::u16string_view rName, std::u16string_view rModule ) const; + void addFactorySpecifierToTypeNameModule( std::u16string_view rType, std::u16string_view rName, std::u16string_view rModule, const OUString& aServiceSpecifier ); + void removeFactorySpecifierFromTypeNameModule( std::u16string_view rType, std::u16string_view rName, std::u16string_view rModule ); + css::uno::Sequence< css::uno::Sequence< css::beans::PropertyValue > > getFactoriesDescription() const; + + // container.XContainerListener + virtual void SAL_CALL elementInserted( const css::container::ContainerEvent& Event ) override; + virtual void SAL_CALL elementRemoved( const css::container::ContainerEvent& Event ) override; + virtual void SAL_CALL elementReplaced( const css::container::ContainerEvent& Event ) override; + + // lang.XEventListener + virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override; + + private: + class FactoryManagerMap : public std::unordered_map + { + }; + + bool impl_getElementProps( const css::uno::Any& rElement, OUString& rType, OUString& rName, OUString& rModule, OUString& rServiceSpecifier ) const; + + mutable std::mutex m_aMutex; + OUString m_aPropType; + OUString m_aPropName; + OUString m_aPropModule; + OUString m_aPropFactory; + OUString m_sRoot; + FactoryManagerMap m_aFactoryManagerMap; + css::uno::Reference< css::lang::XMultiServiceFactory > m_xConfigProvider; + css::uno::Reference< css::container::XNameAccess > m_xConfigAccess; + css::uno::Reference< css::container::XContainerListener > m_xConfigListener; + bool m_bConfigAccessInitialized; +}; + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/uifactory/factoryconfiguration.hxx b/framework/inc/uifactory/factoryconfiguration.hxx new file mode 100644 index 0000000000..4e5ea6c4b0 --- /dev/null +++ b/framework/inc/uifactory/factoryconfiguration.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 . + */ + +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +// Namespace + +namespace framework +{ + +// Configuration access class for PopupMenuControllerFactory implementation + +class ConfigurationAccess_ControllerFactory final : // interfaces + public ::cppu::WeakImplHelper< css::container::XContainerListener> +{ +public: + ConfigurationAccess_ControllerFactory( const css::uno::Reference< css::uno::XComponentContext >& rxContext, OUString _sRoot ); + virtual ~ConfigurationAccess_ControllerFactory() override; + + void readConfigurationData(); + + OUString getServiceFromCommandModule( std::u16string_view rCommandURL, std::u16string_view rModule ) const; + OUString getValueFromCommandModule( std::u16string_view rCommandURL, std::u16string_view rModule ) const; + void addServiceToCommandModule( std::u16string_view rCommandURL, std::u16string_view rModule, const OUString& rServiceSpecifier ); + void removeServiceFromCommandModule( std::u16string_view rCommandURL, std::u16string_view rModule ); + + // container.XContainerListener + virtual void SAL_CALL elementInserted( const css::container::ContainerEvent& Event ) override; + virtual void SAL_CALL elementRemoved( const css::container::ContainerEvent& Event ) override; + virtual void SAL_CALL elementReplaced( const css::container::ContainerEvent& Event ) override; + + // lang.XEventListener + virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override; + +private: + void updateConfigurationDataImpl(); + + struct ControllerInfo + { + OUString m_aImplementationName; + OUString m_aValue; + ControllerInfo(OUString _aImplementationName, OUString _aValue) : m_aImplementationName(std::move(_aImplementationName)),m_aValue(std::move(_aValue)){} + ControllerInfo(){} + }; + class MenuControllerMap : public std::unordered_map< OUString, + ControllerInfo > + { + }; + + bool impl_getElementProps( const css::uno::Any& aElement, OUString& aCommand, OUString& aModule, OUString& aServiceSpecifier,OUString& aValue ) const; + + mutable std::mutex m_mutex; + OUString m_aPropCommand; + OUString m_aPropModule; + OUString m_aPropController; + OUString m_aPropValue; + OUString m_sRoot; + MenuControllerMap m_aMenuControllerMap; + css::uno::Reference< css::lang::XMultiServiceFactory > m_xConfigProvider; + css::uno::Reference< css::container::XNameAccess > m_xConfigAccess; + css::uno::Reference< css::container::XContainerListener > m_xConfigAccessListener; + bool m_bConfigAccessInitialized; +}; + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/uifactory/menubarfactory.hxx b/framework/inc/uifactory/menubarfactory.hxx new file mode 100644 index 0000000000..a3ec76daa1 --- /dev/null +++ b/framework/inc/uifactory/menubarfactory.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 . + */ + +#pragma once + +#include +#include +#include +#include + +#include +#include +#include + +namespace framework +{ +typedef ::cppu::WeakImplHelper< + css::lang::XServiceInfo, + css::ui::XUIElementFactory> MenuBarFactory_BASE; + + class MenuBarFactory : public MenuBarFactory_BASE + { + public: + MenuBarFactory( css::uno::Reference< css::uno::XComponentContext > xContext ); + virtual ~MenuBarFactory() override; + + virtual OUString SAL_CALL getImplementationName() override + { + return "com.sun.star.comp.framework.MenuBarFactory"; + } + + virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override + { + return cppu::supportsService(this, ServiceName); + } + + virtual css::uno::Sequence SAL_CALL getSupportedServiceNames() override + { + css::uno::Sequence< OUString > aSeq { "com.sun.star.ui.UIElementFactory" }; + return aSeq; + } + + // XUIElementFactory + virtual css::uno::Reference< css::ui::XUIElement > SAL_CALL createUIElement( const OUString& ResourceURL, const css::uno::Sequence< css::beans::PropertyValue >& Args ) override; + + static void CreateUIElement(const OUString& ResourceURL + ,const css::uno::Sequence< css::beans::PropertyValue >& Args + ,std::u16string_view ResourceType + ,const css::uno::Reference< css::ui::XUIElement >& _xMenuBar + ,const css::uno::Reference< css::uno::XComponentContext >& _rxContext); + + protected: + css::uno::Reference< css::uno::XComponentContext > m_xContext; + }; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/xml/acceleratorconfigurationreader.hxx b/framework/inc/xml/acceleratorconfigurationreader.hxx new file mode 100644 index 0000000000..7754630137 --- /dev/null +++ b/framework/inc/xml/acceleratorconfigurationreader.hxx @@ -0,0 +1,133 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 + +#include + +#include + +#include +#include + +namespace framework{ + +class AcceleratorConfigurationReader final : public ::cppu::WeakImplHelper< css::xml::sax::XDocumentHandler > +{ + + // const, types + + private: + + /** @short classification of XML elements. */ + enum EXMLElement + { + E_ELEMENT_ACCELERATORLIST, + E_ELEMENT_ITEM + }; + + /** @short classification of XML attributes. */ + enum EXMLAttribute + { + E_ATTRIBUTE_KEYCODE, + E_ATTRIBUTE_MOD_SHIFT, + E_ATTRIBUTE_MOD_MOD1, + E_ATTRIBUTE_MOD_MOD2, + E_ATTRIBUTE_MOD_MOD3, + E_ATTRIBUTE_URL + }; + + // member + + private: + + /** @short reference to the outside container, where this + reader/writer must work on. */ + AcceleratorCache& m_rContainer; + + /** @short used to detect if an accelerator list + occurs recursive inside xml. */ + bool m_bInsideAcceleratorList; + + /** @short used to detect if an accelerator item + occurs recursive inside xml. */ + bool m_bInsideAcceleratorItem; + + /** @short provide information about the parsing state. + + @descr We use it to find out the line and column, where + an error occurred. + */ + css::uno::Reference< css::xml::sax::XLocator > m_xLocator; + + // interface + + public: + + /** @short connect this new reader/writer instance + to an outside container, which should be used + flushed to the underlying XML configuration or + filled from there. + + @param rContainer + a reference to the outside container. + */ + AcceleratorConfigurationReader(AcceleratorCache& rContainer); + + /** @short does nothing real ... */ + virtual ~AcceleratorConfigurationReader() override; + + // XDocumentHandler + virtual void SAL_CALL startDocument() override; + + virtual void SAL_CALL endDocument() override; + + virtual void SAL_CALL startElement(const OUString& sElement , + const css::uno::Reference< css::xml::sax::XAttributeList >& xAttributeList) override; + + virtual void SAL_CALL endElement(const OUString& sElement) override; + + virtual void SAL_CALL characters(const OUString& sChars) override; + + virtual void SAL_CALL ignorableWhitespace(const OUString& sWhitespaces) override; + + virtual void SAL_CALL processingInstruction(const OUString& sTarget, + const OUString& sData ) override; + + virtual void SAL_CALL setDocumentLocator(const css::uno::Reference< css::xml::sax::XLocator >& xLocator) override; + + // helper + + private: + + /** TODO document me */ + static EXMLElement implst_classifyElement(std::u16string_view sElement); + + /** TODO document me */ + static EXMLAttribute implst_classifyAttribute(std::u16string_view sAttribute); + + /** TODO document me */ + OUString implts_getErrorLineString(); +}; + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/xml/acceleratorconfigurationwriter.hxx b/framework/inc/xml/acceleratorconfigurationwriter.hxx new file mode 100644 index 0000000000..ae5580675b --- /dev/null +++ b/framework/inc/xml/acceleratorconfigurationwriter.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 . + */ + +#pragma once + +#include + +#include +#include + +namespace framework{ + +class AcceleratorConfigurationWriter final +{ + + // member + + private: + + /** @short needed to write the xml configuration. */ + css::uno::Reference< css::xml::sax::XDocumentHandler > m_xConfig; + + /** @short reference to the outside container, where this + writer must work on. */ + const AcceleratorCache& m_rContainer; + + // interface + + public: + + /** @short connect this new writer instance + to an outside container, which should be + flushed to the underlying XML configuration. + + @param rContainer + a reference to the outside container. + + @param xConfig + used to write the configuration there. + */ + AcceleratorConfigurationWriter(const AcceleratorCache& rContainer, + css::uno::Reference< css::xml::sax::XDocumentHandler > xConfig ); + + /** @short does nothing real ... */ + ~AcceleratorConfigurationWriter(); + + /** @short TODO */ + void flush(); + + // helper + + private: + + /** @short TODO */ + void impl_ts_writeKeyCommandPair(const css::awt::KeyEvent& aKey , + const OUString& sCommand, + const css::uno::Reference< css::xml::sax::XDocumentHandler >& xConfig ); +}; + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/xml/imagesconfiguration.hxx b/framework/inc/xml/imagesconfiguration.hxx new file mode 100644 index 0000000000..4b17b7c98d --- /dev/null +++ b/framework/inc/xml/imagesconfiguration.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 . + */ + +#pragma once + +#include +#include +#include + +#include + +namespace framework +{ + +struct ImageItemDescriptor +{ + OUString aCommandURL; // URL command to dispatch +}; + +typedef std::vector ImageItemDescriptorList; + +class ImagesConfiguration +{ + public: + static bool LoadImages( + const css::uno::Reference< css::uno::XComponentContext >& rxContext, + const css::uno::Reference< css::io::XInputStream >& rInputStream, + ImageItemDescriptorList& rItems ); + + static bool StoreImages( + const css::uno::Reference< css::uno::XComponentContext >& rxContext, + const css::uno::Reference< css::io::XOutputStream >& rOutputStream, + const ImageItemDescriptorList& rItems ); +}; + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/xml/imagesdocumenthandler.hxx b/framework/inc/xml/imagesdocumenthandler.hxx new file mode 100644 index 0000000000..ccdbe0e5b4 --- /dev/null +++ b/framework/inc/xml/imagesdocumenthandler.hxx @@ -0,0 +1,132 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 + +#include +#include +#include + +#include + +namespace framework{ + +// Hash code function for using in all hash maps of follow implementation. + +class OReadImagesDocumentHandler final : public ::cppu::WeakImplHelper< css::xml::sax::XDocumentHandler > +{ + public: + enum Image_XML_Entry + { + IMG_ELEMENT_IMAGECONTAINER, + IMG_ELEMENT_IMAGES, + IMG_ELEMENT_ENTRY, + IMG_ELEMENT_EXTERNALIMAGES, + IMG_ELEMENT_EXTERNALENTRY, + IMG_ATTRIBUTE_HREF, + IMG_ATTRIBUTE_MASKCOLOR, + IMG_ATTRIBUTE_COMMAND, + IMG_ATTRIBUTE_BITMAPINDEX, + IMG_ATTRIBUTE_MASKURL, + IMG_ATTRIBUTE_MASKMODE, + IMG_ATTRIBUTE_HIGHCONTRASTURL, + IMG_ATTRIBUTE_HIGHCONTRASTMASKURL, + IMG_XML_ENTRY_COUNT + }; + + enum Image_XML_Namespace + { + IMG_NS_IMAGE, + IMG_NS_XLINK + }; + + OReadImagesDocumentHandler( ImageItemDescriptorList& aItems ); + virtual ~OReadImagesDocumentHandler() override; + + // 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; + + private: + OUString getErrorLineString(); + + class ImageHashMap : public std::unordered_map< OUString, Image_XML_Entry > + { + }; + + bool m_bImageContainerStartFound; + bool m_bImageContainerEndFound; + bool m_bImagesStartFound; + ImageHashMap m_aImageMap; + ImageItemDescriptorList& m_rImageList; + css::uno::Reference< css::xml::sax::XLocator > m_xLocator; +}; + +class OWriteImagesDocumentHandler final +{ + public: + OWriteImagesDocumentHandler( + const ImageItemDescriptorList& aItems, + css::uno::Reference< css::xml::sax::XDocumentHandler > const & + rWriteDocumentHandler); + ~OWriteImagesDocumentHandler(); + + /// @throws css::xml::sax::SAXException + /// @throws css::uno::RuntimeException + void WriteImagesDocument(); + + private: + /// @throws css::xml::sax::SAXException + /// @throws css::uno::RuntimeException + void WriteImageList( const ImageItemDescriptorList* ); + + /// @throws css::xml::sax::SAXException + /// @throws css::uno::RuntimeException + void WriteImage( const ImageItemDescriptor* ); + + const ImageItemDescriptorList& m_rImageItemList; + css::uno::Reference< css::xml::sax::XDocumentHandler > m_xWriteDocumentHandler; + css::uno::Reference< css::xml::sax::XAttributeList > m_xEmptyList; + OUString m_aXMLImageNS; + OUString m_aAttributeXlinkType; + OUString m_aAttributeValueSimple; +}; + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/xml/menudocumenthandler.hxx b/framework/inc/xml/menudocumenthandler.hxx new file mode 100644 index 0000000000..db72eb9171 --- /dev/null +++ b/framework/inc/xml/menudocumenthandler.hxx @@ -0,0 +1,221 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 +#include +#include +#include +#include +#include + +#include +#include + +namespace framework{ + +class ReadMenuDocumentHandlerBase : public ::cppu::WeakImplHelper< css::xml::sax::XDocumentHandler > +{ + public: + ReadMenuDocumentHandlerBase(); + virtual ~ReadMenuDocumentHandlerBase() override; + + // XDocumentHandler + virtual void SAL_CALL startDocument() override = 0; + + virtual void SAL_CALL endDocument() override = 0; + + virtual void SAL_CALL startElement( + const OUString& aName, + const css::uno::Reference< css::xml::sax::XAttributeList > &xAttribs) override = 0; + + virtual void SAL_CALL endElement(const OUString& aName) override = 0; + + virtual void SAL_CALL characters(const OUString& aChars) override = 0; + + 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; + + protected: + OUString getErrorLineString(); + + css::uno::Reference< css::xml::sax::XLocator > m_xLocator; + css::uno::Reference< css::xml::sax::XDocumentHandler> m_xReader; + void initPropertyCommon( css::uno::Sequence< css::beans::PropertyValue > &rProps, + const OUString &rCommandURL, const OUString &rHelpId, + const OUString &rLabel, sal_Int16 nItemStyleBits ); + private: + OUString m_aType; + OUString m_aLabel; + OUString m_aContainer; + OUString m_aHelpURL; + OUString m_aCommandURL; + OUString m_aStyle; +}; + +class OReadMenuDocumentHandler final : public ReadMenuDocumentHandlerBase +{ + public: + OReadMenuDocumentHandler( + const css::uno::Reference< css::container::XIndexContainer >& rItemContainer ); + virtual ~OReadMenuDocumentHandler() override; + + // 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; + + private: + int m_nElementDepth; + enum class ReaderMode { None, MenuBar, MenuPopup }; + ReaderMode m_eReaderMode; + css::uno::Reference< css::container::XIndexContainer > m_xMenuBarContainer; + css::uno::Reference< css::lang::XSingleComponentFactory > m_xContainerFactory; +}; // OReadMenuDocumentHandler + +class OReadMenuBarHandler final : public ReadMenuDocumentHandlerBase +{ + public: + OReadMenuBarHandler( + const css::uno::Reference< css::container::XIndexContainer >& rMenuBarContainer, + const css::uno::Reference< css::lang::XSingleComponentFactory >& rContainerFactory ); + virtual ~OReadMenuBarHandler() override; + + // 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; + + private: + int m_nElementDepth; + bool m_bMenuMode; + css::uno::Reference< css::container::XIndexContainer > m_xMenuBarContainer; + css::uno::Reference< css::lang::XSingleComponentFactory > m_xContainerFactory; +}; // OReadMenuBarHandler + +class OReadMenuHandler final : public ReadMenuDocumentHandlerBase +{ + public: + OReadMenuHandler( const css::uno::Reference< css::container::XIndexContainer >& rMenuContainer, + const css::uno::Reference< css::lang::XSingleComponentFactory >& rContainerFactory ); + virtual ~OReadMenuHandler() override; + + // 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; + + private: + int m_nElementDepth; + bool m_bMenuPopupMode; + css::uno::Reference< css::container::XIndexContainer > m_xMenuContainer; + css::uno::Reference< css::lang::XSingleComponentFactory > m_xContainerFactory; +}; // OReadMenuHandler + +class OReadMenuPopupHandler final : public ReadMenuDocumentHandlerBase +{ + public: + OReadMenuPopupHandler( const css::uno::Reference< css::container::XIndexContainer >& rMenuContainer, + const css::uno::Reference< css::lang::XSingleComponentFactory >& rContainerFactory ); + virtual ~OReadMenuPopupHandler() override; + + // 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; + + private: + enum NextElementClose { ELEM_CLOSE_NONE, ELEM_CLOSE_MENUITEM, ELEM_CLOSE_MENUSEPARATOR }; + + int m_nElementDepth; + bool m_bMenuMode; + css::uno::Reference< css::container::XIndexContainer > m_xMenuContainer; + css::uno::Reference< css::lang::XSingleComponentFactory > m_xContainerFactory; + css::uno::Reference< css::uno::XComponentContext > m_xComponentContext; + NextElementClose m_nNextElementExpected; +}; // OReadMenuPopupHandler + +class OWriteMenuDocumentHandler final +{ + public: + OWriteMenuDocumentHandler( + const css::uno::Reference< css::container::XIndexAccess >& rMenuBarContainer, + const css::uno::Reference< css::xml::sax::XDocumentHandler >& rDocumentHandler, + bool bIsMenuBar ); + ~OWriteMenuDocumentHandler(); + + /// @throws css::xml::sax::SAXException + /// @throws css::uno::RuntimeException + void WriteMenuDocument(); + private: + /// @throws css::xml::sax::SAXException + /// @throws css::uno::RuntimeException + void WriteMenu( const css::uno::Reference< css::container::XIndexAccess >& rSubMenuContainer ); + + void WriteMenuItem( const OUString& aCommandURL, const OUString& aLabel, const OUString& aHelpURL, sal_Int16 nStyle ); + void WriteMenuSeparator(); + + css::uno::Reference< css::container::XIndexAccess > m_xMenuBarContainer; + css::uno::Reference< css::xml::sax::XDocumentHandler > m_xWriteDocumentHandler; + css::uno::Reference< css::xml::sax::XAttributeList > m_xEmptyList; + bool m_bIsMenuBar; +}; + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/xml/saxnamespacefilter.hxx b/framework/inc/xml/saxnamespacefilter.hxx new file mode 100644 index 0000000000..04f5bd4fe3 --- /dev/null +++ b/framework/inc/xml/saxnamespacefilter.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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 +#include +#include +#include + +#include + +namespace framework +{ + +// workaround for incremental linking bugs in MSVC2015 +class SAL_DLLPUBLIC_TEMPLATE SaxNamespaceFilter_Base : public cppu::WeakImplHelper< css::xml::sax::XDocumentHandler > {}; + +class SaxNamespaceFilter final : public SaxNamespaceFilter_Base +{ + public: + SaxNamespaceFilter( css::uno::Reference< css::xml::sax::XDocumentHandler > const & rSax1DocumentHandler ); + virtual ~SaxNamespaceFilter() override; + + // 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; + + private: + typedef ::std::stack< XMLNamespaces > NamespaceStack; + + OUString getErrorLineString(); + + css::uno::Reference< css::xml::sax::XLocator > m_xLocator; + css::uno::Reference< css::xml::sax::XDocumentHandler> xDocumentHandler; + NamespaceStack m_aNamespaceStack; +}; + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/xml/statusbardocumenthandler.hxx b/framework/inc/xml/statusbardocumenthandler.hxx new file mode 100644 index 0000000000..ba1ef7c364 --- /dev/null +++ b/framework/inc/xml/statusbardocumenthandler.hxx @@ -0,0 +1,133 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 + +#include + +#include +#include + +#include + +namespace framework{ + +// Hash code function for using in all hash maps of follow implementation. + +// workaround for incremental linking bugs in MSVC2015 +class SAL_DLLPUBLIC_TEMPLATE OReadStatusBarDocumentHandler_Base : public cppu::WeakImplHelper< css::xml::sax::XDocumentHandler > {}; + +class OReadStatusBarDocumentHandler final : public OReadStatusBarDocumentHandler_Base +{ + public: + enum StatusBar_XML_Entry + { + SB_ELEMENT_STATUSBAR, + SB_ELEMENT_STATUSBARITEM, + SB_ATTRIBUTE_URL, + SB_ATTRIBUTE_ALIGN, + SB_ATTRIBUTE_STYLE, + SB_ATTRIBUTE_AUTOSIZE, + SB_ATTRIBUTE_OWNERDRAW, + SB_ATTRIBUTE_WIDTH, + SB_ATTRIBUTE_OFFSET, + SB_ATTRIBUTE_HELPURL, + SB_ATTRIBUTE_MANDATORY, + SB_XML_ENTRY_COUNT + }; + + enum StatusBar_XML_Namespace + { + SB_NS_STATUSBAR, + SB_NS_XLINK + }; + + OReadStatusBarDocumentHandler( const css::uno::Reference< css::container::XIndexContainer >& aStatusBarItems ); + virtual ~OReadStatusBarDocumentHandler() override; + + // 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; + + private: + OUString getErrorLineString(); + + class StatusBarHashMap : public std::unordered_map + { + }; + + bool m_bStatusBarStartFound; + bool m_bStatusBarItemStartFound; + StatusBarHashMap m_aStatusBarMap; + css::uno::Reference< css::container::XIndexContainer > m_aStatusBarItems; + css::uno::Reference< css::xml::sax::XLocator > m_xLocator; +}; + +class OWriteStatusBarDocumentHandler final +{ + public: + OWriteStatusBarDocumentHandler( + const css::uno::Reference< css::container::XIndexAccess >& rStatusBarItems, + const css::uno::Reference< css::xml::sax::XDocumentHandler >& rWriteDocHandler ); + ~OWriteStatusBarDocumentHandler(); + + /// @throws css::xml::sax::SAXException + /// @throws css::uno::RuntimeException + void WriteStatusBarDocument(); + + private: + /// @throws css::xml::sax::SAXException + /// @throws css::uno::RuntimeException + void WriteStatusBarItem( + const OUString& rCommandURL, + sal_Int16 nOffset, + sal_Int16 nStyle, + sal_Int16 nWidth ); + + css::uno::Reference< css::container::XIndexAccess > m_aStatusBarItems; + css::uno::Reference< css::xml::sax::XDocumentHandler > m_xWriteDocumentHandler; + css::uno::Reference< css::xml::sax::XAttributeList > m_xEmptyList; + OUString m_aXMLStatusBarNS; + OUString m_aXMLXlinkNS; + OUString m_aAttributeURL; +}; + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/xml/toolboxconfigurationdefines.hxx b/framework/inc/xml/toolboxconfigurationdefines.hxx new file mode 100644 index 0000000000..39f77f3f4c --- /dev/null +++ b/framework/inc/xml/toolboxconfigurationdefines.hxx @@ -0,0 +1,65 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 + +#define XMLNS_TOOLBAR "http://openoffice.org/2001/toolbar" +#define XMLNS_XLINK "http://www.w3.org/1999/xlink" +inline constexpr OUString XMLNS_TOOLBAR_PREFIX = u"toolbar:"_ustr; +#define XMLNS_XLINK_PREFIX "xlink:" + +#define XMLNS_FILTER_SEPARATOR "^" + +#define ELEMENT_TOOLBAR "toolbar" +#define ELEMENT_TOOLBARITEM "toolbaritem" +#define ELEMENT_TOOLBARSPACE "toolbarspace" +#define ELEMENT_TOOLBARBREAK "toolbarbreak" +#define ELEMENT_TOOLBARSEPARATOR "toolbarseparator" + +#define ATTRIBUTE_UINAME "uiname" + +#define ATTRIBUTE_TEXT "text" +#define ATTRIBUTE_URL "href" +#define ATTRIBUTE_VISIBLE "visible" +#define ATTRIBUTE_ITEMSTYLE "style" + +inline constexpr OUString ELEMENT_NS_TOOLBAR = u"toolbar:toolbar"_ustr; +inline constexpr OUString ELEMENT_NS_TOOLBARITEM = u"toolbar:toolbaritem"_ustr; +inline constexpr OUString ELEMENT_NS_TOOLBARSPACE = u"toolbar:toolbarspace"_ustr; +inline constexpr OUString ELEMENT_NS_TOOLBARBREAK = u"toolbar:toolbarbreak"_ustr; +inline constexpr OUString ELEMENT_NS_TOOLBARSEPARATOR = u"toolbar:toolbarseparator"_ustr; + +inline constexpr OUString ATTRIBUTE_XMLNS_TOOLBAR = u"xmlns:toolbar"_ustr; +inline constexpr OUString ATTRIBUTE_XMLNS_XLINK = u"xmlns:xlink"_ustr; + +#define ATTRIBUTE_BOOLEAN_TRUE "true" +inline constexpr OUString ATTRIBUTE_BOOLEAN_FALSE = u"false"_ustr; + +inline constexpr OUString ATTRIBUTE_ITEMSTYLE_RADIO = u"radio"_ustr; +inline constexpr OUString ATTRIBUTE_ITEMSTYLE_AUTO = u"auto"_ustr; +inline constexpr OUString ATTRIBUTE_ITEMSTYLE_LEFT = u"left"_ustr; +inline constexpr OUString ATTRIBUTE_ITEMSTYLE_AUTOSIZE = u"autosize"_ustr; +inline constexpr OUString ATTRIBUTE_ITEMSTYLE_DROPDOWN = u"dropdown"_ustr; +inline constexpr OUString ATTRIBUTE_ITEMSTYLE_REPEAT = u"repeat"_ustr; +inline constexpr OUString ATTRIBUTE_ITEMSTYLE_TEXT = u"text"_ustr; +inline constexpr OUString ATTRIBUTE_ITEMSTYLE_DROPDOWNONLY = u"dropdownonly"_ustr; +inline constexpr OUString ATTRIBUTE_ITEMSTYLE_IMAGE = u"image"_ustr; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/xml/toolboxdocumenthandler.hxx b/framework/inc/xml/toolboxdocumenthandler.hxx new file mode 100644 index 0000000000..f134054267 --- /dev/null +++ b/framework/inc/xml/toolboxdocumenthandler.hxx @@ -0,0 +1,149 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 + +#include + +#include +#include + +#include + +namespace framework{ + +// Hash code function for using in all hash maps of follow implementation. + +// workaround for incremental linking bugs in MSVC2015 +class SAL_DLLPUBLIC_TEMPLATE OReadToolBoxDocumentHandler_Base : public cppu::WeakImplHelper< css::xml::sax::XDocumentHandler > {}; + +class OReadToolBoxDocumentHandler final : public OReadToolBoxDocumentHandler_Base +{ + public: + enum ToolBox_XML_Entry + { + TB_ELEMENT_TOOLBAR, + TB_ELEMENT_TOOLBARITEM, + TB_ELEMENT_TOOLBARSPACE, + TB_ELEMENT_TOOLBARBREAK, + TB_ELEMENT_TOOLBARSEPARATOR, + TB_ATTRIBUTE_TEXT, + TB_ATTRIBUTE_URL, + TB_ATTRIBUTE_VISIBLE, + TB_ATTRIBUTE_STYLE, + TB_ATTRIBUTE_UINAME, + TB_XML_ENTRY_COUNT + }; + + enum ToolBox_XML_Namespace + { + TB_NS_TOOLBAR, + TB_NS_XLINK + }; + + OReadToolBoxDocumentHandler( const css::uno::Reference< css::container::XIndexContainer >& rItemContainer ); + virtual ~OReadToolBoxDocumentHandler() override; + + // 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; + + private: + OUString getErrorLineString(); + + class ToolBoxHashMap : public std::unordered_map + { + }; + + bool m_bToolBarStartFound : 1; + bool m_bToolBarItemStartFound : 1; + bool m_bToolBarSpaceStartFound : 1; + bool m_bToolBarBreakStartFound : 1; + bool m_bToolBarSeparatorStartFound : 1; + ToolBoxHashMap m_aToolBoxMap; + css::uno::Reference< css::container::XIndexContainer > m_rItemContainer; + css::uno::Reference< css::xml::sax::XLocator > m_xLocator; + + OUString m_aType; + OUString m_aLabel; + OUString m_aStyle; + OUString m_aIsVisible; + OUString m_aCommandURL; +}; + +class OWriteToolBoxDocumentHandler final +{ + public: + OWriteToolBoxDocumentHandler( + const css::uno::Reference< css::container::XIndexAccess >& rItemAccess, + css::uno::Reference< css::xml::sax::XDocumentHandler > const & rDocumentHandler ); + ~OWriteToolBoxDocumentHandler(); + + /// @throws css::xml::sax::SAXException + /// @throws css::uno::RuntimeException + void WriteToolBoxDocument(); + + private: + /// @throws css::xml::sax::SAXException + /// @throws css::uno::RuntimeException + void WriteToolBoxItem( const OUString& aCommandURL, const OUString& aLabel, sal_Int16 nStyle, bool bVisible ); + + /// @throws css::xml::sax::SAXException + /// @throws css::uno::RuntimeException + void WriteToolBoxSpace(); + + /// @throws css::xml::sax::SAXException + /// @throws css::uno::RuntimeException + void WriteToolBoxBreak(); + + /// @throws css::xml::sax::SAXException + /// @throws css::uno::RuntimeException + void WriteToolBoxSeparator(); + + css::uno::Reference< css::xml::sax::XDocumentHandler > m_xWriteDocumentHandler; + css::uno::Reference< css::xml::sax::XAttributeList > m_xEmptyList; + css::uno::Reference< css::container::XIndexAccess > m_rItemAccess; + OUString m_aXMLToolbarNS; + OUString m_aXMLXlinkNS; + OUString m_aAttributeURL; +}; + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/inc/xml/xmlnamespaces.hxx b/framework/inc/xml/xmlnamespaces.hxx new file mode 100644 index 0000000000..1c01b03cce --- /dev/null +++ b/framework/inc/xml/xmlnamespaces.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 . + */ + +#pragma once + +#include + +#include + +namespace framework +{ + +class XMLNamespaces final +{ + public: + /// @throws css::xml::sax::SAXException + void addNamespace( const OUString& aName, const OUString& aValue ); + + /// @throws css::xml::sax::SAXException + OUString applyNSToAttributeName( const OUString& ) const; + /// @throws css::xml::sax::SAXException + OUString applyNSToElementName( const OUString& ) const; + + private: + typedef ::std::map< OUString, OUString > NamespaceMap; + + /// @throws css::xml::sax::SAXException + OUString const & getNamespaceValue( const OUString& aNamespace ) const; + + OUString m_aDefaultNamespace; + NamespaceMap m_aNamespaceMap; +}; + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/qa/complex/ModuleManager/CheckXModuleManager.java b/framework/qa/complex/ModuleManager/CheckXModuleManager.java new file mode 100644 index 0000000000..1015d00c2a --- /dev/null +++ b/framework/qa/complex/ModuleManager/CheckXModuleManager.java @@ -0,0 +1,330 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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.ModuleManager; + +import com.sun.star.beans.*; +import com.sun.star.frame.*; +import com.sun.star.lang.*; +import com.sun.star.uno.AnyConverter; +import com.sun.star.uno.UnoRuntime; +import com.sun.star.util.*; +import com.sun.star.container.*; + + + +// ---------- junit imports ----------------- +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.*; + + + +/** @short todo document me + */ +public class CheckXModuleManager +{ + + // some const + + + // member + + /** points to the global uno service manager. */ + private XMultiServiceFactory m_xSmgr = null; + + /** the module manager for testing. */ + private XModuleManager m_xMM = null; + + /** a special frame used to load documents there. */ + private XComponentLoader m_xLoader = null; + + + // test environment + + + /** @short Create the environment for following tests. + + @descr Use either a component loader from desktop or + from frame + */ + @Before public void before() + throws java.lang.Exception + { + // get uno service manager from global test environment + m_xSmgr = getMSF(); + + // create module manager + m_xMM = UnoRuntime.queryInterface(XModuleManager.class, m_xSmgr.createInstance("com.sun.star.frame.ModuleManager")); + + // create desktop instance to create a special frame to load documents there. + XFrame xDesktop = UnoRuntime.queryInterface(XFrame.class, m_xSmgr.createInstance("com.sun.star.frame.Desktop")); + + m_xLoader = UnoRuntime.queryInterface(XComponentLoader.class, xDesktop.findFrame("_blank", 0)); + } + + + /** @short close the environment. + */ + @After public void after() + throws java.lang.Exception + { + XCloseable xClose = UnoRuntime.queryInterface(XCloseable.class, m_xLoader); + xClose.close(false); + + m_xLoader = null; + m_xMM = null; + m_xSmgr = null; + } + + + /** @todo document me + */ + @Test public void checkModuleIdentification() + throws java.lang.Exception + { + impl_identifyModulesBasedOnDocs("com.sun.star.text.TextDocument" ); + impl_identifyModulesBasedOnDocs("com.sun.star.text.WebDocument" ); + impl_identifyModulesBasedOnDocs("com.sun.star.text.GlobalDocument" ); + impl_identifyModulesBasedOnDocs("com.sun.star.formula.FormulaProperties" ); + impl_identifyModulesBasedOnDocs("com.sun.star.sheet.SpreadsheetDocument" ); + impl_identifyModulesBasedOnDocs("com.sun.star.drawing.DrawingDocument" ); + impl_identifyModulesBasedOnDocs("com.sun.star.presentation.PresentationDocument"); + impl_identifyModulesBasedOnDocs("com.sun.star.sdb.OfficeDatabaseDocument" ); + // TODO: fails + // impl_identifyModulesBasedOnDocs("com.sun.star.chart.ChartDocument" ); + } + + + /** @todo document me + */ + @Test public void checkModuleConfigurationReadable() + throws java.lang.Exception + { + } + + + /** @todo document me + */ + @Test public void checkModuleConfigurationWriteable() + throws java.lang.Exception + { + // modules supporting real documents + impl_checkReadOnlyPropsOfModule("com.sun.star.text.TextDocument" ); + impl_checkReadOnlyPropsOfModule("com.sun.star.text.WebDocument" ); + impl_checkReadOnlyPropsOfModule("com.sun.star.text.GlobalDocument" ); + impl_checkReadOnlyPropsOfModule("com.sun.star.formula.FormulaProperties" ); + impl_checkReadOnlyPropsOfModule("com.sun.star.sheet.SpreadsheetDocument" ); + impl_checkReadOnlyPropsOfModule("com.sun.star.drawing.DrawingDocument" ); + impl_checkReadOnlyPropsOfModule("com.sun.star.presentation.PresentationDocument"); + impl_checkReadOnlyPropsOfModule("com.sun.star.sdb.OfficeDatabaseDocument" ); + impl_checkReadOnlyPropsOfModule("com.sun.star.chart.ChartDocument" ); + + // other modules + impl_checkReadOnlyPropsOfModule("com.sun.star.sdb.FormDesign" ); + impl_checkReadOnlyPropsOfModule("com.sun.star.sdb.TextReportDesign" ); + impl_checkReadOnlyPropsOfModule("com.sun.star.sdb.RelationDesign" ); + impl_checkReadOnlyPropsOfModule("com.sun.star.sdb.QueryDesign" ); + impl_checkReadOnlyPropsOfModule("com.sun.star.sdb.TableDesign" ); + impl_checkReadOnlyPropsOfModule("com.sun.star.sdb.DataSourceBrowser"); + impl_checkReadOnlyPropsOfModule("com.sun.star.frame.Bibliography" ); + impl_checkReadOnlyPropsOfModule("com.sun.star.script.BasicIDE" ); + impl_checkReadOnlyPropsOfModule("com.sun.star.frame.StartModule" ); + } + + + /** @todo document me + */ + @Test public void checkModuleConfigurationQueries() + throws java.lang.Exception + { + impl_searchModulesByDocumentService("com.sun.star.text.TextDocument" ); + impl_searchModulesByDocumentService("com.sun.star.text.WebDocument" ); + impl_searchModulesByDocumentService("com.sun.star.text.GlobalDocument" ); + impl_searchModulesByDocumentService("com.sun.star.formula.FormulaProperties" ); + impl_searchModulesByDocumentService("com.sun.star.sheet.SpreadsheetDocument" ); + impl_searchModulesByDocumentService("com.sun.star.drawing.DrawingDocument" ); + impl_searchModulesByDocumentService("com.sun.star.presentation.PresentationDocument"); + impl_searchModulesByDocumentService("com.sun.star.sdb.OfficeDatabaseDocument" ); + impl_searchModulesByDocumentService("com.sun.star.chart.ChartDocument" ); + } + + + /** @todo document me + */ + private void impl_searchModulesByDocumentService(String sDocumentService) + throws java.lang.Exception + { + System.out.println("search modules matching document service '"+sDocumentService+"' ..."); + + NamedValue[] lProps = new NamedValue[1]; + lProps[0] = new NamedValue(); + lProps[0].Name = "ooSetupFactoryDocumentService"; + lProps[0].Value = sDocumentService; + + XContainerQuery xMM = UnoRuntime.queryInterface(XContainerQuery.class, m_xMM); + XEnumeration xResult = xMM.createSubSetEnumerationByProperties(lProps); + while(xResult.hasMoreElements()) + { + PropertyValue[] lModuleProps = (PropertyValue[])AnyConverter.toArray(xResult.nextElement()); + int c = lModuleProps.length; + int i = 0; + String sFoundModule = ""; + String sFoundDocService = ""; + for (i=0; i 0) + { + System.out.println("** get command by " + sKeys[i] + " **"); + + String sCmdFromCache = ""; // get a value using XAcceleratorConfiguration API + String sCmdFromConfiguration = ""; // get a value using configuration API + + // GET shortcuts/commands using XAcceleratorConfiguration API + sCmdFromCache = xAccelCfg.getCommandByKeyEvent(convertShortcut2AWTKey(sKeys[i])); + System.out.println(sKeys[i] + "-->" + sCmdFromCache + ", by XAcceleratorConfiguration API"); + + // GET shortcuts/commands using configuration API + sCmdFromConfiguration = getCommandFromConfiguration(xAccess, sKeys[i]); + System.out.println(sKeys[i] + "-->" + sCmdFromConfiguration + ", by configuration API"); + + assertTrue("values are different by XAcceleratorConfiguration API and configuration API!", sCmdFromCache.equals(sCmdFromConfiguration)); + + String sLocale = "es"; + setOfficeLocale(sLocale); + sCmdFromConfiguration = getCommandFromConfiguration(xAccess, sKeys[i]); + System.out.println(sKeys[i] + "-->" + sCmdFromConfiguration + ", by configuration API" + " for locale:" + getOfficeLocale()); + + sLocale = "en-US"; + setOfficeLocale(sLocale); //reset to default locale + } + else + { + System.out.println(sKeys[i] + " doesn't exist!"); + } + } + } + + + /** @todo document me. + */ + private void impl_checkSetKeyCommands(XAcceleratorConfiguration xAccelCfg, XNameAccess xPrimaryAccess, XNameAccess xSecondaryAccess, String[] sKeys, String[] sCommands) + throws java.lang.Exception + { + System.out.println("check setKeyCommands..."); + + for (int i = 0; i < sKeys.length; ++i) + { + if (!xPrimaryAccess.hasByName(sKeys[i]) && !xSecondaryAccess.hasByName(sKeys[i])) + { + xAccelCfg.setKeyEvent(convertShortcut2AWTKey(sKeys[i]), sCommands[i]); + xAccelCfg.store(); + if (xPrimaryAccess.hasByName(sKeys[i])) + { + System.out.println("add " + sKeys[i] + " successfully!"); + } + else + { + System.out.println("add " + sKeys[i] + " failed!"); + } + } + else if (xPrimaryAccess.hasByName(sKeys[i])) + { + String sOriginalCommand = getCommandFromConfiguration(xPrimaryAccess, sKeys[i]); + if (!sCommands[i].equals(sOriginalCommand)) + { + xAccelCfg.setKeyEvent(convertShortcut2AWTKey(sKeys[i]), sCommands[i]); + xAccelCfg.store(); + + String sChangedCommand = getCommandFromConfiguration(xPrimaryAccess, sKeys[i]); + if (sCommands[i].equals(sChangedCommand)) + { + System.out.println("change " + sKeys[i] + " successfully!"); + } + else + { + System.out.println("change " + sKeys[i] + " failed!"); + } + } + else + { + System.out.println(sKeys[i] + " already exist!"); + } + } + else if (xSecondaryAccess.hasByName(sKeys[i])) + { + String sOriginalCommand = getCommandFromConfiguration(xSecondaryAccess, sKeys[i]); + if (!sCommands[i].equals(sOriginalCommand)) + { + xAccelCfg.setKeyEvent(convertShortcut2AWTKey(sKeys[i]), sCommands[i]); + xAccelCfg.store(); + + String sChangedCommand = getCommandFromConfiguration(xPrimaryAccess, sKeys[i]); + if (sCommands[i].equals(sChangedCommand)) + { + System.out.println("change " + sKeys[i] + " successfully!"); + } + else + { + System.out.println("change " + sKeys[i] + " failed!"); + } + } + else + { + System.out.println(sKeys[i] + " already exist!"); + } + } + } + } + + + /** @todo document me. + */ + private void impl_checkRemoveKeyCommands(XAcceleratorConfiguration xAccelCfg, XNameAccess xPrimaryAccess, XNameAccess xSecondaryAccess, String[] sKeys) + throws java.lang.Exception + { + System.out.println("check removeKeyCommands..."); + + for (int i = 0; i < sKeys.length; i++) + { + if (!xPrimaryAccess.hasByName(sKeys[i]) && !xSecondaryAccess.hasByName(sKeys[i])) + { + System.out.println(sKeys[i] + " doesn't exist!"); + } + else if (xPrimaryAccess.hasByName(sKeys[i])) + { + xAccelCfg.removeKeyEvent(convertShortcut2AWTKey(sKeys[i])); + xAccelCfg.store(); + if (!xPrimaryAccess.hasByName(sKeys[i])) + { + System.out.println("Remove " + sKeys[i] + " successfully!"); + } + else + { + System.out.println("Remove " + sKeys[i] + " failed!"); + } + } + else if (xSecondaryAccess.hasByName(sKeys[i])) + { + xAccelCfg.removeKeyEvent(convertShortcut2AWTKey(sKeys[i])); + xAccelCfg.store(); + if (!xSecondaryAccess.hasByName(sKeys[i])) + { + System.out.println("Remove " + sKeys[i] + " successfully!"); + } + else + { + System.out.println("Remove " + sKeys[i] + " failed!"); + } + } + } + } + + + /** @todo document me. + */ + private void impl_checkGetPreferredKeyEventsForCommandList(XAcceleratorConfiguration xAccelCfg, XNameAccess xPrimaryAccess, String[] sCommandList) + throws java.lang.Exception + { + System.out.println("check getPreferredKeyEventsForCommandList..."); + + Object[] oKeyEvents = xAccelCfg.getPreferredKeyEventsForCommandList(sCommandList); + for (int i = 0; i < oKeyEvents.length; i++) + { + System.out.println("get preferred key for command " + sCommandList[i] + ":"); + + KeyEvent aKeyEvent = (KeyEvent) AnyConverter.toObject(KeyEvent.class, oKeyEvents[i]); + String sKeyEvent = convertAWTKey2Shortcut(aKeyEvent); + System.out.println(sKeyEvent); + + String sCmdFromConfiguration = getCommandFromConfiguration(xPrimaryAccess, sKeyEvent); + System.out.println(sCmdFromConfiguration); + if (sCommandList[i].equals(sCmdFromConfiguration)) + { + System.out.println("get preferred key correctly!"); + } + else + { + System.out.println("get preferred key failed!"); + } + } + } + + + /** @todo document me. + */ + private String getCommandFromConfiguration(XNameAccess xAccess, String sKey) + throws java.lang.Exception + { + String sCommand = ""; + + if (xAccess.hasByName(sKey)) + { + XNameAccess xKey = UnoRuntime.queryInterface(XNameAccess.class, xAccess.getByName(sKey)); + XNameAccess xCommand = UnoRuntime.queryInterface(XNameAccess.class, xKey.getByName("Command")); + + String sLocale = getOfficeLocale(); + if (xCommand.hasByName(sLocale)) + { + sCommand = UnoRuntime.queryInterface(String.class, xCommand.getByName(sLocale)); + } + } + + return sCommand; + } + + + /** @todo document me. + */ + private void LoadDocumentAcceleratorConfiguration(String sDocCfgName) + throws java.lang.Exception + { + XSingleServiceFactory xStorageFactory = UnoRuntime.queryInterface(XSingleServiceFactory.class, m_xSmgr.createInstance("com.sun.star.embed.StorageFactory")); + + Object aArgs[] = new Object[2]; + aArgs[0] = sDocCfgName; + aArgs[1] = Integer.valueOf(com.sun.star.embed.ElementModes.READ); + XStorage xRootStorage = UnoRuntime.queryInterface(XStorage.class, xStorageFactory.createInstanceWithArguments(aArgs)); + + XStorage xUIConfig = xRootStorage.openStorageElement("Configurations2", com.sun.star.embed.ElementModes.READ); + + PropertyValue aProp = new PropertyValue(); + aProp.Name = "DocumentRoot"; + aProp.Value = xUIConfig; + Object[] lArgs = new Object[1]; + lArgs[0] = aProp; + + XInitialization xInit = UnoRuntime.queryInterface(XInitialization.class, m_xDocumentAccelCfg); + xInit.initialize(lArgs); + + // TODO: throws css::container::NoSuchElementException + try + { + String test = m_xDocumentAccelCfg.getCommandByKeyEvent(convertShortcut2AWTKey("F2")); + System.out.println(test); + } + catch(com.sun.star.container.NoSuchElementException e) + { + System.out.println("NoSuchElementException caught: " + e.getMessage()); + } + } + + + /** @todo document me. + */ + private void SaveDocumentAcceleratorConfiguration(String sDocCfgName) + throws java.lang.Exception + { + XSingleServiceFactory xStorageFactory = UnoRuntime.queryInterface(XSingleServiceFactory.class, m_xSmgr.createInstance("com.sun.star.embed.StorageFactory")); + + Object aArgs[] = new Object[2]; + aArgs[0] = sDocCfgName; + aArgs[1] = Integer.valueOf(com.sun.star.embed.ElementModes.WRITE); + XStorage xRootStorage = UnoRuntime.queryInterface(XStorage.class, xStorageFactory.createInstanceWithArguments(aArgs)); + + XStorage xUIConfig = xRootStorage.openStorageElement("Configurations2", com.sun.star.embed.ElementModes.WRITE); + + XUIConfigurationManager xCfgMgr = UnoRuntime.queryInterface(XUIConfigurationManager.class, m_xSmgr.createInstance("com.sun.star.ui.UIConfigurationManager")); + + XUIConfigurationStorage xUICfgStore = UnoRuntime.queryInterface(XUIConfigurationStorage.class, xCfgMgr); + xUICfgStore.setStorage(xUIConfig); + + XPropertySet xUIConfigProps = UnoRuntime.queryInterface(XPropertySet.class, xUIConfig); + xUIConfigProps.setPropertyValue("MediaType", "application/vnd.sun.xml.ui.configuration"); + + if (xCfgMgr != null) + { + XAcceleratorConfiguration xTargetAccMgr = UnoRuntime.queryInterface(XAcceleratorConfiguration.class, xCfgMgr.getShortCutManager()); + XUIConfigurationPersistence xCommit1 = UnoRuntime.queryInterface(XUIConfigurationPersistence.class, xTargetAccMgr); + XUIConfigurationPersistence xCommit2 = UnoRuntime.queryInterface(XUIConfigurationPersistence.class, xCfgMgr); + xCommit1.store(); + xCommit2.store(); + + XTransactedObject xCommit3 = UnoRuntime.queryInterface(XTransactedObject.class, xRootStorage); + xCommit3.commit(); + } + } + + + /** @todo document me. + */ + private com.sun.star.awt.KeyEvent convertShortcut2AWTKey(String sShortcut) + throws java.lang.Exception + { + com.sun.star.awt.KeyEvent aKeyEvent = new com.sun.star.awt.KeyEvent(); + KeyMapping aKeyMapping = new KeyMapping(); + String[] sShortcutSplits = sShortcut.split("_"); + + aKeyEvent.KeyCode = aKeyMapping.mapIdentifier2Code(sShortcutSplits[0]); + for (int i = 1; i < sShortcutSplits.length; i++) + { + if (sShortcutSplits[i].equals("SHIFT")) + { + aKeyEvent.Modifiers |= com.sun.star.awt.KeyModifier.SHIFT; + } + else if (sShortcutSplits[i].equals("MOD1")) + { + aKeyEvent.Modifiers |= com.sun.star.awt.KeyModifier.MOD1; + } + else if (sShortcutSplits[i].equals("MOD2")) + { + aKeyEvent.Modifiers |= com.sun.star.awt.KeyModifier.MOD2; + } + } + + return aKeyEvent; + } + + + /** @todo document me. + */ + private String convertAWTKey2Shortcut(com.sun.star.awt.KeyEvent aKeyEvent) + throws java.lang.Exception + { + String sShortcut; + + KeyMapping aKeyMapping = new KeyMapping(); + sShortcut = aKeyMapping.mapCode2Identifier(aKeyEvent.KeyCode); + + if ((aKeyEvent.Modifiers & com.sun.star.awt.KeyModifier.SHIFT) == com.sun.star.awt.KeyModifier.SHIFT) + { + sShortcut += "_SHIFT"; + } + if ((aKeyEvent.Modifiers & com.sun.star.awt.KeyModifier.MOD1) == com.sun.star.awt.KeyModifier.MOD1) + { + sShortcut += "_MOD1"; + } + if ((aKeyEvent.Modifiers & com.sun.star.awt.KeyModifier.MOD2) == com.sun.star.awt.KeyModifier.MOD2) + { + sShortcut += "_MOD2"; + } + + return sShortcut; + } + + + /** @todo document me. + */ + private String getOfficeLocale() + throws java.lang.Exception + { + String sLocale = ""; + + String sConfigPath = "org.openoffice.Setup"; + boolean bReadOnly = true; + XNameAccess xRootConfig = openConfig(sConfigPath, bReadOnly); + + if (xRootConfig != null) + { + XNameAccess xLocale = UnoRuntime.queryInterface(XNameAccess.class, xRootConfig.getByName("L10N")); + XPropertySet xSet = UnoRuntime.queryInterface(XPropertySet.class, xLocale); + sLocale = (String) xSet.getPropertyValue("ooLocale"); + } + + return sLocale; + } + + + /** @todo document me. + */ + private void setOfficeLocale(String sLocale) + throws java.lang.Exception + { + String sConfigPath = "org.openoffice.Setup"; + boolean bReadOnly = false; + XNameAccess xRootConfig = openConfig(sConfigPath, bReadOnly); + + if (xRootConfig != null) + { + XNameAccess xLocale = UnoRuntime.queryInterface(XNameAccess.class, xRootConfig.getByName("L10N")); + XPropertySet xSet = UnoRuntime.queryInterface(XPropertySet.class, xLocale); + xSet.setPropertyValue("ooLocale", sLocale); + XChangesBatch xBatch = UnoRuntime.queryInterface(XChangesBatch.class, xRootConfig); + xBatch.commitChanges(); + } + } + + + /** @todo document me. + */ + private XNameAccess openConfig( + String sConfigPath, + boolean bReadOnly) + throws java.lang.Exception + { + XMultiServiceFactory xConfigRoot = theDefaultProvider.get( + connection.getComponentContext()); + + PropertyValue[] lParams = new PropertyValue[2]; + lParams[0] = new PropertyValue(); + lParams[0].Name = "nodepath"; + lParams[0].Value = sConfigPath; + + lParams[1] = new PropertyValue(); + lParams[1].Name = "locale"; + lParams[1].Value = "*"; + + Object aConfig; + if (bReadOnly) + { + aConfig = xConfigRoot.createInstanceWithArguments("com.sun.star.configuration.ConfigurationAccess", lParams); + } + else + { + aConfig = xConfigRoot.createInstanceWithArguments("com.sun.star.configuration.ConfigurationUpdateAccess", lParams); + } + + XNameAccess xConfig = UnoRuntime.queryInterface(XNameAccess.class, aConfig); + + if (xConfig == null) + { + throw new com.sun.star.uno.Exception("Could not open configuration \"" + sConfigPath + "\""); + } + + return xConfig; + } + + 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()"); + connection.tearDown(); + } + private static final OfficeConnection connection = new OfficeConnection(); +} diff --git a/framework/qa/complex/accelerators/KeyMapping.java b/framework/qa/complex/accelerators/KeyMapping.java new file mode 100644 index 0000000000..fd3637ba2a --- /dev/null +++ b/framework/qa/complex/accelerators/KeyMapping.java @@ -0,0 +1,143 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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.accelerators; + +import java.util.HashMap; + +class KeyIdentifierInfo +{ + protected String sIdentifier; + protected Short nCode; + + KeyIdentifierInfo(String sID, Short nC) + { + sIdentifier = sID; + nCode = nC; + } +} + +class IdentifierHashMap extends HashMap +{ +} + +class CodeHashMap extends HashMap +{ +} + +public class KeyMapping +{ + private final IdentifierHashMap aIdentifierHashMap; + private final CodeHashMap aCodeHashMap; + + public KeyMapping() + { + KeyIdentifierInfo[] aInfoMap = { + new KeyIdentifierInfo("0", Short.valueOf(com.sun.star.awt.Key.NUM0)), + new KeyIdentifierInfo("1", Short.valueOf(com.sun.star.awt.Key.NUM1)), + new KeyIdentifierInfo("2", Short.valueOf(com.sun.star.awt.Key.NUM2)), + new KeyIdentifierInfo("3", Short.valueOf(com.sun.star.awt.Key.NUM3)), + new KeyIdentifierInfo("4", Short.valueOf(com.sun.star.awt.Key.NUM4)), + new KeyIdentifierInfo("5", Short.valueOf(com.sun.star.awt.Key.NUM5)), + new KeyIdentifierInfo("6", Short.valueOf(com.sun.star.awt.Key.NUM6)), + new KeyIdentifierInfo("7", Short.valueOf(com.sun.star.awt.Key.NUM7)), + new KeyIdentifierInfo("8", Short.valueOf(com.sun.star.awt.Key.NUM8)), + new KeyIdentifierInfo("9", Short.valueOf(com.sun.star.awt.Key.NUM9)), + new KeyIdentifierInfo("A", Short.valueOf(com.sun.star.awt.Key.A)), + new KeyIdentifierInfo("B", Short.valueOf(com.sun.star.awt.Key.B)), + new KeyIdentifierInfo("C", Short.valueOf(com.sun.star.awt.Key.C)), + new KeyIdentifierInfo("D", Short.valueOf(com.sun.star.awt.Key.D)), + new KeyIdentifierInfo("E", Short.valueOf(com.sun.star.awt.Key.E)), + new KeyIdentifierInfo("F", Short.valueOf(com.sun.star.awt.Key.F)), + new KeyIdentifierInfo("G", Short.valueOf(com.sun.star.awt.Key.G)), + new KeyIdentifierInfo("H", Short.valueOf(com.sun.star.awt.Key.H)), + new KeyIdentifierInfo("I", Short.valueOf(com.sun.star.awt.Key.I)), + new KeyIdentifierInfo("J", Short.valueOf(com.sun.star.awt.Key.J)), + new KeyIdentifierInfo("K", Short.valueOf(com.sun.star.awt.Key.K)), + new KeyIdentifierInfo("L", Short.valueOf(com.sun.star.awt.Key.L)), + new KeyIdentifierInfo("M", Short.valueOf(com.sun.star.awt.Key.M)), + new KeyIdentifierInfo("N", Short.valueOf(com.sun.star.awt.Key.N)), + new KeyIdentifierInfo("O", Short.valueOf(com.sun.star.awt.Key.O)), + new KeyIdentifierInfo("P", Short.valueOf(com.sun.star.awt.Key.P)), + new KeyIdentifierInfo("Q", Short.valueOf(com.sun.star.awt.Key.Q)), + new KeyIdentifierInfo("R", Short.valueOf(com.sun.star.awt.Key.R)), + new KeyIdentifierInfo("S", Short.valueOf(com.sun.star.awt.Key.S)), + new KeyIdentifierInfo("T", Short.valueOf(com.sun.star.awt.Key.T)), + new KeyIdentifierInfo("U", Short.valueOf(com.sun.star.awt.Key.U)), + new KeyIdentifierInfo("V", Short.valueOf(com.sun.star.awt.Key.V)), + new KeyIdentifierInfo("W", Short.valueOf(com.sun.star.awt.Key.W)), + new KeyIdentifierInfo("X", Short.valueOf(com.sun.star.awt.Key.X)), + new KeyIdentifierInfo("Y", Short.valueOf(com.sun.star.awt.Key.Y)), + new KeyIdentifierInfo("Z", Short.valueOf(com.sun.star.awt.Key.Z)), + new KeyIdentifierInfo("F1", Short.valueOf(com.sun.star.awt.Key.F1)), + new KeyIdentifierInfo("F2", Short.valueOf(com.sun.star.awt.Key.F2)), + new KeyIdentifierInfo("F3", Short.valueOf(com.sun.star.awt.Key.F3)), + new KeyIdentifierInfo("F4", Short.valueOf(com.sun.star.awt.Key.F4)), + new KeyIdentifierInfo("F5", Short.valueOf(com.sun.star.awt.Key.F5)), + new KeyIdentifierInfo("F6", Short.valueOf(com.sun.star.awt.Key.F6)), + new KeyIdentifierInfo("F7", Short.valueOf(com.sun.star.awt.Key.F7)), + new KeyIdentifierInfo("F8", Short.valueOf(com.sun.star.awt.Key.F8)), + new KeyIdentifierInfo("F9", Short.valueOf(com.sun.star.awt.Key.F9)), + new KeyIdentifierInfo("F10", Short.valueOf(com.sun.star.awt.Key.F10)), + new KeyIdentifierInfo("F11", Short.valueOf(com.sun.star.awt.Key.F11)), + new KeyIdentifierInfo("F12", Short.valueOf(com.sun.star.awt.Key.F12)), + new KeyIdentifierInfo("DOWN", Short.valueOf(com.sun.star.awt.Key.DOWN)), + new KeyIdentifierInfo("UP", Short.valueOf(com.sun.star.awt.Key.UP)), + new KeyIdentifierInfo("LEFT", Short.valueOf(com.sun.star.awt.Key.LEFT)), + new KeyIdentifierInfo("RIGHT", Short.valueOf(com.sun.star.awt.Key.RIGHT)), + new KeyIdentifierInfo("HOME", Short.valueOf(com.sun.star.awt.Key.HOME)), + new KeyIdentifierInfo("END", Short.valueOf(com.sun.star.awt.Key.END)), + new KeyIdentifierInfo("PAGEUP", Short.valueOf(com.sun.star.awt.Key.PAGEUP)), + new KeyIdentifierInfo("PAGEDOWN", Short.valueOf(com.sun.star.awt.Key.PAGEDOWN)), + new KeyIdentifierInfo("RETURN", Short.valueOf(com.sun.star.awt.Key.RETURN)), + new KeyIdentifierInfo("ESCAPE", Short.valueOf(com.sun.star.awt.Key.ESCAPE)), + new KeyIdentifierInfo("TAB", Short.valueOf(com.sun.star.awt.Key.TAB)), + new KeyIdentifierInfo("BACKSPACE", Short.valueOf(com.sun.star.awt.Key.BACKSPACE)), + new KeyIdentifierInfo("SPACE", Short.valueOf(com.sun.star.awt.Key.SPACE)), + new KeyIdentifierInfo("INSERT", Short.valueOf(com.sun.star.awt.Key.INSERT)), + new KeyIdentifierInfo("DELETE", Short.valueOf(com.sun.star.awt.Key.DELETE)), + new KeyIdentifierInfo("ADD", Short.valueOf(com.sun.star.awt.Key.ADD)), + new KeyIdentifierInfo("SUBTRACT", Short.valueOf(com.sun.star.awt.Key.SUBTRACT)), + new KeyIdentifierInfo("MULTIPLY", Short.valueOf(com.sun.star.awt.Key.MULTIPLY)), + new KeyIdentifierInfo("DIVIDE", Short.valueOf(com.sun.star.awt.Key.DIVIDE)), + new KeyIdentifierInfo("CUT", Short.valueOf(com.sun.star.awt.Key.CUT)), + new KeyIdentifierInfo("COPY", Short.valueOf(com.sun.star.awt.Key.COPY)), + new KeyIdentifierInfo("PASTE", Short.valueOf(com.sun.star.awt.Key.PASTE)), + new KeyIdentifierInfo("UNDO", Short.valueOf(com.sun.star.awt.Key.UNDO)), + new KeyIdentifierInfo("REPEAT", Short.valueOf(com.sun.star.awt.Key.REPEAT)) + }; + + aIdentifierHashMap = new IdentifierHashMap(); + aCodeHashMap = new CodeHashMap(); + for (int i = 0; i + *
  • NoOffice=yes - StarOffice is not started initially.
  • + * + */ +public class DesktopTerminate +{ + + private XMultiServiceFactory xMSF; + private static final int iOfficeCloseTime = 1000; + + /** + * Test if all available document types change the + * persistent Window Attributes + * + * The test follows basically these steps: + * - Create a configuration reader and a componentloader + * - Look for all document types in the configuration + * - Do for every doc type + * - start office + * - read configuration attribute settings + * - create a new document + * - resize the document and close it + * - close office + * - start office + * - read configuration attribute settings + * - create another new document + * - compare old settings with new ones: should be different + * - compare the document size with the resized document: should be equal + * - close office + * - Test finished + */ + @Test public void checkPersistentWindowState() + { + try + { + + System.out.println("Connect the first time."); + + if (!connect()) + { + return; + } + + if (!disconnect()) + { + return; + } + } + catch (Exception e) + { + e.printStackTrace(); + } + } + + private boolean connect() + { + try + { + xMSF = getMSF(); + util.utils.pause(10000); + } + catch (java.lang.Exception e) + { + System.out.println(e.getClass().getName()); + System.out.println("Message: " + e.getMessage()); + fail("Cannot connect the Office."); + return false; + } + return true; + } + + private boolean disconnect() + { + try + { + XDesktop desk = null; + desk = UnoRuntime.queryInterface(XDesktop.class, xMSF.createInstance("com.sun.star.frame.Desktop")); + desk.terminate(); + System.out.println("Waiting " + iOfficeCloseTime + " milliseconds for the Office to close down"); + util.utils.pause(iOfficeCloseTime); + xMSF = null; + } + catch (java.lang.Exception e) + { + e.printStackTrace(); + fail("Cannot dispose the Office."); + return false; + } + return true; + } + + + 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() + { + System.out.println("tearDownConnection()"); + // don't do a tearDown here, desktop is already terminated. + } + + private static final OfficeConnection connection = new OfficeConnection(); + +} diff --git a/framework/qa/complex/dispatches/Interceptor.java b/framework/qa/complex/dispatches/Interceptor.java new file mode 100644 index 0000000000..ecbac44216 --- /dev/null +++ b/framework/qa/complex/dispatches/Interceptor.java @@ -0,0 +1,338 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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.dispatches; + +// __________ Imports __________ + +// structs, const, ... +import com.sun.star.beans.PropertyValue; + +// exceptions +import com.sun.star.frame.DispatchDescriptor; +import com.sun.star.frame.XDispatch; +import com.sun.star.frame.XDispatchProvider; +import com.sun.star.frame.XDispatchProviderInterceptor; +import com.sun.star.frame.XInterceptorInfo; +import com.sun.star.frame.XStatusListener; + +import com.sun.star.lang.XMultiServiceFactory; +import com.sun.star.awt.XDataTransferProviderAccess; + +// helper +import com.sun.star.util.URL; +import com.sun.star.uno.UnoRuntime; + +// __________ Implementation __________ + +/** + * implements a configurable interceptor for dispatch events. + */ +public class Interceptor implements XDispatch, + XDispatchProviderInterceptor, + XInterceptorInfo +{ + /** contains the list of interception URL schema's (wildcards are allowed there!) + supported by this interceptor. It can be set from outside. + If no external URLs are set, the default "*" is used instead. + That would have the same effect as if this implementation would not support the + interface XInterceptorInfo ! + */ + private String[] m_lURLs4InterceptionInfo = null; + + + + /** These URL's will be blocked by this interceptor. + Can be set from outside. Every queryDispatch() for these + set of URL's will be answered with an empty dispatch object! + If no external URLs are set the default "*" is used instead. + So every incoming URL will be blocked .-) + */ + private String[] m_lURLs4Blocking = null; + + + + /** Every dispatch interceptor knows it's master and slave interceptor + of the dispatch chain. These values must be stupid handled .-) + They have to be set and reset in case the right interface methods are called. + Nothing more. It's not allowed to dispose() it. + The slave can be used inside queryDispatch() to forward requests, + which are not handled by this interceptor instance. + */ + private XDispatchProvider m_xSlave = null; + private XDispatchProvider m_xMaster = null; + + + + /** counts calls of setSlave...(). + So the outside API test can use this value to know if this interceptor + was really added to the interceptor chain of OOo. + */ + private int m_nRegistrationCount = 0; + + + + /** indicates if this interceptor object is currently part of the interceptor + chain of OOo. Only true if a valid slave or master dispatch is set on this + instance. + */ + private boolean m_bIsRegistered = false; + + /** points to the global uno service manager. */ + private XMultiServiceFactory m_xMSF = null; + + public Interceptor(XMultiServiceFactory xMSF) + { + m_xMSF = xMSF; + } + + + /** XInterceptorInfo */ + public synchronized String[] getInterceptedURLs() + { + return impl_getURLs4InterceptionInfo(); + } + + + + /** XDispatchProviderInterceptor */ + public synchronized XDispatchProvider getSlaveDispatchProvider() + { + System.out.println("Interceptor.getSlaveDispatchProvider() called"); + return m_xSlave; + } + + + + /** XDispatchProviderInterceptor */ + public synchronized XDispatchProvider getMasterDispatchProvider() + { + System.out.println("Interceptor.getMasterDispatchProvider() called"); + return m_xMaster; + } + + + + /** XDispatchProviderInterceptor */ + public synchronized void setSlaveDispatchProvider(XDispatchProvider xSlave) + { + System.out.println("Interceptor.setSlaveDispatchProvider("+xSlave+") called"); + + if (xSlave != null) + { + ++m_nRegistrationCount; + m_bIsRegistered = true; + } + else + { + m_bIsRegistered = false; + } + + m_xSlave = xSlave; + } + + /** XDispatchProviderInterceptor */ + public synchronized void setMasterDispatchProvider(XDispatchProvider xMaster) + { + System.out.println("Interceptor.setMasterDispatchProvider("+xMaster+") called"); + m_xMaster = xMaster; + } + + private XDataTransferProviderAccess m_xToolkit = null; + + /** A beautiful method whose only purpose is to take and release a + * solar mutex. If this hangs - you can see a beautiful deadlock + * when you attach your debugger to the main process. + */ + private void checkNoSolarMutexHeld() + { + try + { + if (m_xToolkit == null) + m_xToolkit = UnoRuntime.queryInterface( + XDataTransferProviderAccess.class, + m_xMSF.createInstance("com.sun.star.awt.Toolkit")); + + // A Method notable only for taking the solar mutex. + System.out.println("Check solarmutex not held - if so deadlock"); + m_xToolkit.getDragSource( null ); + System.out.println("Solarmutex not held."); + } catch (java.lang.Throwable ex) { + System.out.println("Failed to create and invoke toolkit method " + ex.toString()); + } + } + + /** XDispatchProvider + */ + public synchronized XDispatch queryDispatch(URL aURL, + String sTargetFrameName, + int nSearchFlags) + { + System.out.println("Interceptor.queryDispatch('"+aURL.Complete+"', '"+sTargetFrameName+"', "+nSearchFlags+") called"); + + checkNoSolarMutexHeld(); + + if (impl_isBlockedURL(aURL.Complete)) + { + System.out.println("Interceptor.queryDispatch(): URL blocked => returns NULL"); + return null; + } + + if (m_xSlave != null) + { + System.out.println("Interceptor.queryDispatch(): ask slave ..."); + return m_xSlave.queryDispatch(aURL, sTargetFrameName, nSearchFlags); + } + + System.out.println("Interceptor.queryDispatch(): no idea => returns this"); + return this; + } + + + + /** XDispatchProvider + */ + public XDispatch[] queryDispatches(DispatchDescriptor[] lRequests) + { + int i = 0; + int c = lRequests.length; + + XDispatch[] lResults = new XDispatch[c]; + for (i=0; i aCheckMap = new HashMap(c2); + for (i2 = 0; i2 < c2; ++i2) + { + DispatchInformation aInfo = lInfos[i2]; + if (aInfo.GroupId != nGroup) + { + // Error + fail("At least one DispatchInformation item does not match the requested group.\n\trequested group=[" + nGroup + + "] returned group=[" + aInfo.GroupId + "] command=\"" + aInfo.Command + "\""); // true => don't break this test + continue; + } + + if (aCheckMap.containsKey(aInfo.Command)) + { + // Error + fail("Found a duplicate item: group=[" + aInfo.GroupId + "] command=\"" + aInfo.Command + "\""); // true => don't break this test + continue; + } + + aCheckMap.put(aInfo.Command, aInfo.Command); + System.out.println("\t[" + aInfo.GroupId + "] \"" + aInfo.Command + "\""); + } + } + } + + private synchronized XFrame impl_createNewFrame() + { + XFrame xFrame = m_xDesktop.findFrame("_blank", 0); + xFrame.getContainerWindow().setVisible(true); + return xFrame; + } + + private synchronized void impl_closeFrame(XFrame xFrame) throws Exception + { + XCloseable xClose = UnoRuntime.queryInterface(XCloseable.class, xFrame); + xClose.close(false); + } + + 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()"); + connection.tearDown(); + } + private static final OfficeConnection connection = new OfficeConnection(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/qa/complex/disposing/GetServiceWhileDisposingOffice.java b/framework/qa/complex/disposing/GetServiceWhileDisposingOffice.java new file mode 100644 index 0000000000..fd7fffcb3d --- /dev/null +++ b/framework/qa/complex/disposing/GetServiceWhileDisposingOffice.java @@ -0,0 +1,82 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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.disposing; + +import com.sun.star.lang.XMultiServiceFactory; +import com.sun.star.uno.UnoRuntime; +import com.sun.star.frame.XDesktop; + +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import org.openoffice.test.OfficeConnection; + + +/** + * This test is for bug110698. The Office is closed and is continually connected + * while it closes. This did let the Office freeze. Now when the Office is + * closed, the connection is refused. + */ +public class GetServiceWhileDisposingOffice +{ + + @Test public void checkServiceWhileDisposing() throws Exception + { + XMultiServiceFactory xMSF = getMSF(); + XDesktop xDesktop = null; + + xDesktop = UnoRuntime.queryInterface(XDesktop.class, xMSF.createInstance("com.sun.star.frame.Desktop")); + int step = 0; + try + { + System.out.println("Start the termination of the Office."); + xDesktop.terminate(); + for (; step < 10000; step++) + { + xMSF.createInstance("com.sun.star.frame.Desktop"); + } + } + catch (com.sun.star.lang.DisposedException e) + { + System.out.println("DisposedException in step: " + step); + } + } + + + 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() + { + System.out.println("tearDownConnection()"); + // Office is already terminated. + } + private static final OfficeConnection connection = new OfficeConnection(); + +} diff --git a/framework/qa/complex/framework/autosave/AutoSave.java b/framework/qa/complex/framework/autosave/AutoSave.java new file mode 100644 index 0000000000..c6e7549d0e --- /dev/null +++ b/framework/qa/complex/framework/autosave/AutoSave.java @@ -0,0 +1,451 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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.framework.autosave; + + + +import com.sun.star.beans.PropertyValue; +import com.sun.star.frame.FeatureStateEvent; +import com.sun.star.frame.XDispatch; +import com.sun.star.frame.XDispatchProvider; +import com.sun.star.frame.XModel; +import com.sun.star.frame.XStatusListener; +import com.sun.star.frame.theAutoRecovery; +import com.sun.star.lang.XMultiServiceFactory; +import com.sun.star.sheet.FillDirection; +import com.sun.star.sheet.XCellSeries; +import com.sun.star.table.XCellRange; +import com.sun.star.util.XCloseable; +import com.sun.star.sheet.XSpreadsheet; +import com.sun.star.sheet.XSpreadsheetDocument; +import com.sun.star.sheet.XSpreadsheets; +import com.sun.star.uno.AnyConverter; +import com.sun.star.uno.Type; +import com.sun.star.uno.UnoRuntime; +import com.sun.star.uno.XInterface; +import com.sun.star.util.URL; +import com.sun.star.util.XURLTransformer; +import java.util.*; +import util.utils; + + +// ---------- junit imports ----------------- +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 util.SOfficeFactory; +import static org.junit.Assert.*; + + + +/** @short Check some use cases of the AutoSave feature + */ +public class AutoSave +{ + + private static class AutoSaveListener implements XStatusListener + { + private XDispatch m_xAutoSave; + private URL m_aRegistration; + private final Protocol m_aLog; + + private AutoSaveListener(XMultiServiceFactory xSMGR , + XDispatch xAutoSave, + Protocol aLog ) + { + m_aLog = aLog; + m_aLog.log(Protocol.TYPE_SCOPE_OPEN, "create listener for AutoSave notifications ..."); + + try + { + m_xAutoSave = xAutoSave; + + XURLTransformer xParser = UnoRuntime.queryInterface(XURLTransformer.class, xSMGR.createInstance("com.sun.star.util.URLTransformer")); + URL[] aURL = new URL[1]; + aURL[0] = new URL(); + aURL[0].Complete = "vnd.sun.star.autorecovery:/doAutoSave"; + xParser.parseStrict(aURL); + m_aRegistration = aURL[0]; + + m_xAutoSave.addStatusListener(this, m_aRegistration); + m_aLog.log(Protocol.TYPE_INFO, "successfully registered as AutoSave listener."); + } + catch(Throwable ex) + { + m_aLog.log(ex); + } + + m_aLog.log(Protocol.TYPE_SCOPE_CLOSE, ""); + } + + private void disableListener() + { + m_aLog.log(Protocol.TYPE_SCOPE_OPEN, "stop listening for AutoSave notifications ..."); + + XDispatch xAutoSave = null; + URL aRegURL = null; + synchronized (this) + { + xAutoSave = m_xAutoSave; + aRegURL = m_aRegistration; + } + + try + { + if ( + (xAutoSave != null) && + (aRegURL != null) + ) + xAutoSave.removeStatusListener(this, aRegURL); + } + catch(Throwable ex) + { + m_aLog.log(ex); + } + + m_aLog.log(Protocol.TYPE_SCOPE_CLOSE, ""); + } + + public void statusChanged(FeatureStateEvent aEvent) + { + m_aLog.log(Protocol.TYPE_SCOPE_OPEN, "statusChanged() called from AutoSave ..."); + + m_aLog.log("FeatureURL = \""+aEvent.FeatureURL.Complete+"\"" ); + m_aLog.log("FeatureDescriptor = \""+aEvent.FeatureDescriptor+"\"" ); + m_aLog.log("IsEnabled = \""+aEvent.IsEnabled+"\"" ); + m_aLog.log("Requery = \""+aEvent.Requery+"\"" ); + m_aLog.log("State:" ); + m_aLog.log(aEvent.State ); + + m_aLog.log(Protocol.TYPE_SCOPE_CLOSE, ""); + } + + public void disposing(com.sun.star.lang.EventObject aEvent) + { + m_aLog.log(Protocol.TYPE_INFO, "disposing() called from AutoSave."); + synchronized(this) + { + m_xAutoSave = null; + m_aRegistration = null; + } + } + } + + + // some const + + + // member + + private Protocol m_aLog; + + /** points to the global uno service manager. */ + private XMultiServiceFactory m_xSMGR = null; + + private SOfficeFactory m_aSOF; + + /** can be used to trigger/enable/disable the AutoSave feature. */ + private XDispatch m_xAutoSave = null; + + private XURLTransformer m_xURLParser = null; + + + // test environment + + + /** @short Create the environment for following tests. + + @descr create an empty test frame, where we can load + different components inside. + */ + @Before public void before() + { + m_aLog = new Protocol(Protocol.MODE_HTML | Protocol.MODE_STDOUT, Protocol.FILTER_NONE, utils.getUsersTempDir() + "/complex_log_ascii_01.html"); + + try + { + // get uno service manager from global test environment + m_xSMGR = getMSF(); + + // get another helper to e.g. create test documents + m_aSOF = SOfficeFactory.getFactory(m_xSMGR); + + // create AutoSave instance + m_xAutoSave = theAutoRecovery.get(connection.getComponentContext()); + + // prepare AutoSave + // make sure it will be started every 1 min + ConfigHelper aConfig = new ConfigHelper(connection.getComponentContext(), "org.openoffice.Office.Recovery", false); + aConfig.writeRelativeKey("AutoSave", "Enabled" , Boolean.TRUE ); + aConfig.writeRelativeKey("AutoSave", "TimeIntervall", Integer.valueOf(1)); // 1 min + aConfig.flush(); + aConfig = null; + + // is needed to parse dispatch commands + m_xURLParser = UnoRuntime.queryInterface(XURLTransformer.class, m_xSMGR.createInstance("com.sun.star.util.URLTransformer")); + + } + catch(java.lang.Throwable ex) + { + m_aLog.log(ex); + fail("Couldn't create test environment"); + } + } + + + /** @short close the environment. + */ + @After public void after() + { + // ??? + } + + + // create a calc document with content, which needs some time for saving + private XInterface createBigCalcDoc() + { + m_aLog.log(Protocol.TYPE_SCOPE_OPEN, "createBigCalcDoc() started ..."); + try + { + m_aLog.log("Create empty calc document for testing."); + XSpreadsheetDocument xSheetDoc = m_aSOF.createCalcDoc("_default"); + m_aLog.log("Retrieve first sheet from calc document."); + XSpreadsheets xSheets = xSheetDoc.getSheets(); + XSpreadsheet xSheet = (XSpreadsheet)AnyConverter.toObject( + new Type(XSpreadsheet.class), + xSheets.getByName( + xSheets.getElementNames()[0])); + m_aLog.log("Fill two cells with value and formula."); + xSheet.getCellByPosition(0, 0).setValue(1); + xSheet.getCellByPosition(0, 1).setFormula("=a1+1"); + m_aLog.log("Retrieve big range."); + XCellRange xRange = xSheet.getCellRangeByName("A1:Z9999"); + XCellSeries xSeries = UnoRuntime.queryInterface(XCellSeries.class, xRange); + m_aLog.log("Duplicate cells from top to bottom inside range."); + xSeries.fillAuto(FillDirection.TO_BOTTOM, 2); + m_aLog.log("Duplicate cells from left to right inside range."); + xSeries.fillAuto(FillDirection.TO_RIGHT , 1); + + m_aLog.log(Protocol.TYPE_SCOPE_CLOSE | Protocol.TYPE_OK, "createBigCalcDoc() finished."); + return xSheetDoc; + } + catch(Throwable ex) + { + m_aLog.log(ex); + } + + m_aLog.log(Protocol.TYPE_SCOPE_CLOSE, "createBigCalcDoc() finished."); + return null; + } + + + private void saveDoc(XInterface xDoc, + String sURL) + { + m_aLog.log(Protocol.TYPE_SCOPE_OPEN, "saveDoc('"+sURL+"') started ..."); + try + { + URL[] aURL = new URL[1]; + aURL[0] = new URL(); + aURL[0].Complete = ".uno:SaveAs"; + m_xURLParser.parseStrict(aURL); + + XModel xModel = UnoRuntime.queryInterface(XModel.class, xDoc); + XDispatchProvider xProvider = UnoRuntime.queryInterface(XDispatchProvider.class, xModel.getCurrentController()); + XDispatch xDispatch = xProvider.queryDispatch(aURL[0], "_self", 0); + + PropertyValue[] lArgs = new PropertyValue[3]; + lArgs[0] = new PropertyValue(); + lArgs[0].Name = "URL"; + lArgs[0].Value = sURL; + lArgs[1] = new PropertyValue(); + lArgs[1].Name = "Overwrite"; + lArgs[1].Value = Boolean.TRUE; + lArgs[2] = new PropertyValue(); + lArgs[2].Name = "StoreTo"; + lArgs[2].Value = Boolean.TRUE; + + xDispatch.dispatch(aURL[0], lArgs); + + m_aLog.log(Protocol.TYPE_OK, "saveDoc('"+sURL+"') = OK."); + } + catch(Throwable ex) + { + m_aLog.log(ex); + } + m_aLog.log(Protocol.TYPE_SCOPE_CLOSE, "saveDoc('"+sURL+"') finished."); + } + + + private void closeDoc(XInterface xDoc) + { + m_aLog.log(Protocol.TYPE_SCOPE_OPEN, "closeDoc() started ..."); + + try + { + Random aRandom = new Random(); + int nRetry = 5; + while(nRetry>0) + { + try + { + XCloseable xClose = UnoRuntime.queryInterface(XCloseable.class, xDoc); + if (xClose != null) + { + xClose.close(false); + m_aLog.log(Protocol.TYPE_OK, "closeDoc() = OK."); + nRetry = 0; + } + else + { + m_aLog.log(Protocol.TYPE_ERROR, "closeDoc() = ERROR. Doc doesn't provide needed interface!"); + } + } + catch(com.sun.star.util.CloseVetoException exVeto) + { + m_aLog.log(Protocol.TYPE_WARNING , "got CloseVetoException on calling doc.close()." ); + m_aLog.log(Protocol.TYPE_WARNING_INFO, "Please check the reason for that more in detail." ); + m_aLog.log(Protocol.TYPE_WARNING_INFO, "A message like \"Can not close while saving.\" was intended and doesn't show an error!"); + m_aLog.log(Protocol.TYPE_WARNING_INFO, exVeto.getMessage()); + } + + if (nRetry > 0) + { + --nRetry; + long nWait = aRandom.nextInt(30000); // 30 sec. + try + { + m_aLog.log(Protocol.TYPE_INFO, "sleep for "+nWait+" ms"); + synchronized(this) + { + wait(nWait); + } + } + catch(Throwable ex) + { + m_aLog.log(Protocol.TYPE_WARNING , "got exception for wait() !?"); + m_aLog.log(Protocol.TYPE_WARNING_INFO, ex.getMessage()); + } + } + } + } + catch(Throwable ex) + { + m_aLog.log(ex); + } + + m_aLog.log(Protocol.TYPE_SCOPE_CLOSE, "closeDoc() finished."); + } + + private class DocThread extends Thread + { + DocThread() + {} + + @Override + public void run() + { + impl_checkConcurrentAutoSaveToNormalUISave(); + } + } + + + /** @short check concurrent save requests to the same document + * at the same time. + * + * @descr First we simulate an UI save by dispatching the right URL + * to the document and at the same time we try to trigger an AutoSave + * from another thread. So these operations should be started at the same time. + * It should not crash. The AutoSave request must be postponed. + */ + @Test public void checkConcurrentAutoSaveToNormalUISave() + { + m_aLog.log(Protocol.TYPE_TESTMARK , "AutoSave"); + m_aLog.log(Protocol.TYPE_SCOPE_OPEN, "checkConcurrentAutoSaveToNormalUISave()"); + + AutoSaveListener xListener = new AutoSaveListener(m_xSMGR, m_xAutoSave, m_aLog); + + try + { + DocThread aThread = new DocThread(); + aThread.start(); + aThread.join(); + } + catch(Throwable ex) + {} + + xListener.disableListener(); + + m_aLog.log(Protocol.TYPE_SCOPE_CLOSE, "checkConcurrentAutoSaveToNormalUISave()"); + m_aLog.logStatistics(); + } + + public void impl_checkConcurrentAutoSaveToNormalUISave() + { + Random aRandom = new Random(); + + int i = 0; + int c = 5; + for (i=0; i"); + + // insert line number + if (m_nLine % MARK_DIFF == 0) + impl_generateColoredHTMLCell(sLine, Long.toString(m_nLine), BGCOLOR_LINECOL, FGCOLOR_LINECOL_MARKED, true); + else + impl_generateColoredHTMLCell(sLine, Long.toString(m_nLine), BGCOLOR_LINECOL, FGCOLOR_LINECOL_NORMAL, false); + + // add time stamp + impl_generateColoredHTMLCell(sLine, m_aStamp.toString()+" ", BGCOLOR_TIMESTAMP, FGCOLOR_TIMESTAMP, false); + + // add log type info + boolean bTypeCellFilled = false; + if ((m_nType & TYPE_ERROR_INFO) == TYPE_ERROR_INFO) + { + impl_generateColoredHTMLCell(sLine, "ERROR INFO", BGCOLOR_ERROR_INFO, FGCOLOR_ERROR_INFO, false); + bTypeCellFilled = true; + } + else + if ((m_nType & TYPE_ERROR) == TYPE_ERROR) + { + impl_generateColoredHTMLCell(sLine, "ERROR", BGCOLOR_ERROR, FGCOLOR_ERROR, true); + bTypeCellFilled = true; + } + else + if ((m_nType & TYPE_WARNING_INFO) == TYPE_WARNING_INFO) + { + impl_generateColoredHTMLCell(sLine, "WARNING INFO", BGCOLOR_WARNING_INFO, FGCOLOR_WARNING_INFO, false); + bTypeCellFilled = true; + } + else + if ((m_nType & TYPE_WARNING) == TYPE_WARNING) + { + impl_generateColoredHTMLCell(sLine, "WARNING", BGCOLOR_WARNING, FGCOLOR_WARNING, true); + bTypeCellFilled = true; + } + else + if ((m_nType & TYPE_OK) == TYPE_OK) + { + impl_generateColoredHTMLCell(sLine, "OK", BGCOLOR_OK, FGCOLOR_OK, true); + bTypeCellFilled = true; + } + else + if ((m_nType & TYPE_INFO) == TYPE_INFO) + { + impl_generateColoredHTMLCell(sLine, "INFO", BGCOLOR_INFO, FGCOLOR_INFO, false); + bTypeCellFilled = true; + } + else + if ((m_nType & TYPE_TESTMARK) == TYPE_TESTMARK) + { + impl_generateColoredHTMLCell(sLine, "TEST", BGCOLOR_TESTMARK, FGCOLOR_TESTMARK, true); + bTypeCellFilled = true; + } + else + if ((m_nType & TYPE_STATISTIC) == TYPE_STATISTIC) + { + impl_generateColoredHTMLCell(sLine, "STATISTIC", BGCOLOR_STATISTIC, FGCOLOR_STATISTIC, false); + bTypeCellFilled = true; + } + else + if ((m_nType & TYPE_LINK) == TYPE_LINK) + { + impl_generateColoredHTMLCell(sLine, "LINK", BGCOLOR_LINK, FGCOLOR_LINK, false); + bTypeCellFilled = true; + } + else + if ( + ((m_nType & TYPE_SCOPE_OPEN ) == TYPE_SCOPE_OPEN ) || + ((m_nType & TYPE_SCOPE_CLOSE) == TYPE_SCOPE_CLOSE) + ) + { + impl_generateColoredHTMLCell(sLine, "SCOPE", BGCOLOR_SCOPE, FGCOLOR_SCOPE, false); + bTypeCellFilled = true; + } + + // if no type information was added to the current column, we must + // write any content into this cell. Otherwise some browser + // shows a strange layout! + if (! bTypeCellFilled) + impl_generateColoredHTMLCell(sLine, " ", BGCOLOR_STANDARD, FGCOLOR_STANDARD, false); + + // add scope information + sLine.append(""); + for (int s=0; s{ "+m_nScope+""); + else + if ((m_nType & TYPE_SCOPE_CLOSE) == TYPE_SCOPE_CLOSE) + sLine.append(""+m_nScope+" }"); + sLine.append("\n"); + + // add message + sLine.append("" ); + sLine.append(m_sMessage); + sLine.append("\n" ); + + sLine.append("\n" ); + + return sLine.toString(); + } + + /** detect, if this line object represent an error */ + public synchronized boolean isError() + { + return ( + ((m_nType & TYPE_ERROR) == TYPE_ERROR) && + ((m_nType & TYPE_INFO ) != TYPE_INFO ) + ); + } + + /** detect, if this line object represent a warning */ + public synchronized boolean isWarning() + { + return ( + ((m_nType & TYPE_WARNING) == TYPE_WARNING) && + ((m_nType & TYPE_INFO ) != TYPE_INFO ) + ); + } + + /** detect, if this line object represent a marked position */ + public synchronized boolean isTestMark() + { + return ((m_nType & TYPE_TESTMARK) == TYPE_TESTMARK); + } + + /** + * create a colored table cell formatted as HTML. + * + * @param sCell + * an outside string buffer, which can be + * used to generate the + * needed HTML code there. + * + * @param sContent + * the text content of this cell. + * + * @param sBGColor + * a string, which represent the background color + * coded in HTML. + * + * @param sFGColor + * a string, which represent the foreground color + * coded in HTML. + * + * @param bBold + * enable/disable bold state for the text content. + */ + private void impl_generateColoredHTMLCell(StringBuffer sCell , + String sContent, + String sBGColor, + String sFGColor, + boolean bBold ) + { + sCell.append(""); + sCell.append(""); + if (bBold) + sCell.append(""); + sCell.append(sContent); + if (bBold) + sCell.append(""); + sCell.append("\n"); + } + } + + + /** + * ctor + * It creates a new instance of this class and initializes it in the right mode. + * + * @param nMode + * specify how the log should be generated. + * + * @param nFilter + * can be used to filter log messages by its type. + * + * @param sFileName + * the name of the log file (if nMode requires a log file) + */ + public Protocol(int nMode , + int nFilter , + String sFileName) + { + m_nMode = nMode; + m_nFilter = nFilter; + m_sFileName = sFileName; + m_nLine = 0; + m_nScope = 1; + m_nWarnings = 0; + m_nErrors = 0; + m_aStartTime = new Timestamp(System.currentTimeMillis()); + } + + + + + + /** + * log an unspecified message. + * + * Sometimes it's not necessary to set a special type for a message. + * The pure message seems to be enough. The type of such "pure messages" + * will be set to INFO. + * + * @param sMessage + * the pure message + * + * @see #log(int, String) + */ + public synchronized void log( /*IN*/ String sMessage ) + { + log(TYPE_INFO, sMessage); + } + + + /** + * log an exception. + * + * It uses all information available by this exception object + * to generate the log. So exceptions are printed out using a + * standard format. + * + * @param exThrowable + * the exception + */ + public synchronized void log( /*IN*/ Throwable exThrowable ) + { + log(TYPE_SCOPE_OPEN | TYPE_ERROR, "exception \""+exThrowable.getMessage()+"\""); + + StackTraceElement[] lStack = exThrowable.getStackTrace(); + for (int i=0; i 0 ) && + ((nType & TYPE_SCOPE_CLOSE) == TYPE_SCOPE_CLOSE) + ) + { + --m_nScope; + } + + // update statistic values + if (aLine.isTestMark()) + ++m_nTestMarks; + if (aLine.isWarning()) + ++m_nWarnings; + if (aLine.isError()) + ++m_nErrors; + + // no else - it's a bit field of possible reactions! + if ((m_nMode & MODE_STDOUT) == MODE_STDOUT) + System.out.print(sAsciiLog); + // no else - it's a bit field of possible reactions! + if ((m_nMode & MODE_STDERR) == MODE_STDERR) + System.err.print(sAsciiLog); + // no else - it's a bit field of possible reactions! + // But these both conditions must be handled together. + // Because we can't generate different types of log contents to the same log file. + // We prefer HTML if both types are set. + if ( + ((m_nMode & MODE_HTML ) == MODE_HTML ) || + ((m_nMode & MODE_ASCII) == MODE_ASCII) + ) + { + boolean bAppend = (m_nLine>1); + String sContent; + if ((m_nMode & MODE_HTML) == MODE_HTML) + { + if (! bAppend) + sContent = impl_generateHTMLHeader()+sHTMLLog; + else + sContent = sHTMLLog; + } + else + { + if (! bAppend) + sContent = impl_generateAsciiHeader()+sAsciiLog; + else + sContent = sAsciiLog; + } + + impl_writeToLogFile(m_sFileName, bAppend, sContent); + } + } + + + + + + /** + * log the current statistic values + * We write it into our protocol buffer and + * reset it. + */ + public synchronized void logStatistics() + { + Timestamp aEndTime = new Timestamp(System.currentTimeMillis()); + Timestamp aDiff = new Timestamp(aEndTime.getTime()-m_aStartTime.getTime()); + + int nLogType = TYPE_STATISTIC; + if (m_nErrors > 0) + nLogType = TYPE_ERROR_INFO; + else + if (m_nWarnings > 0) + nLogType = TYPE_WARNING_INFO; + + log(nLogType | TYPE_SCOPE_OPEN , "statistic:" ); + log(nLogType , "tests = "+m_nTestMarks ); + log(nLogType , "errors = "+m_nErrors ); + log(nLogType , "warnings = "+m_nWarnings ); + log(nLogType , "elapsed time = "+aDiff.toString()); + log(nLogType | TYPE_SCOPE_CLOSE, "" ); + + resetStatistics(); + } + + private synchronized void resetStatistics() + { + m_nTestMarks = 0; + m_nWarnings = 0; + m_nErrors = 0; + m_aStartTime = new Timestamp(System.currentTimeMillis()); + } + + + /** + * returns a generic html header for generating html log files + * + * It's used from the method finish() to generate a valid html formatted file. + * For that it's necessary to open some special html targets like e.g. . + * + * @return A string, which includes the whole header. + * + * @see #finish() + * @see #impl_generateHTMLFooter() + */ + private String impl_generateHTMLHeader() + { + return "\n\n"+m_sFileName+"\n\n\n\n"; + } + + private String impl_generateAsciiHeader() + { + return "********************************************************************************\n"; + } + + /** + * helper to log different representations of a property(array) + * + * @param sOut + * used to generate the log output there. + * + * @param lProps + * represent the property(array) to be logged. + */ + private void impl_logPropertyArray( /*OUT*/ StringBuffer sOut , + /*IN */ com.sun.star.beans.PropertyValue[] lProps ) + { + int i = 0; + int c = lProps.length; + + for (i=0; i] {"); + sOut.append("}"); + } + else + if (aAny instanceof com.sun.star.beans.PropertyValue[]) + { + sOut.append("[sequence< PropertyValue >] {"); + com.sun.star.beans.PropertyValue[] lSubProps = (com.sun.star.beans.PropertyValue[])com.sun.star.uno.AnyConverter.toArray(aAny); + impl_logPropertyArray(sOut, lSubProps); + sOut.append("}"); + } + else + if (aAny instanceof com.sun.star.beans.NamedValue[]) + { + sOut.append("[sequence< NamedValue >] {"); + com.sun.star.beans.NamedValue[] lSubProps = (com.sun.star.beans.NamedValue[])com.sun.star.uno.AnyConverter.toArray(aAny); + impl_logPropertyArray(sOut, lSubProps); + sOut.append("}"); + } + else + { + sOut.append("[unknown array] {-}"); + } + } + else + if (com.sun.star.uno.AnyConverter.isObject(aAny)) + { + sOut.append("[object] {"); + // TODO + sOut.append("}"); + } + + if ((m_nMode & MODE_HTML) == MODE_HTML) + sOut.append("
    "); + else + sOut.append("\n"); + } + catch(com.sun.star.lang.IllegalArgumentException exIll) + { + sOut.append("Got exception during property conversion.\n"); + sOut.append(exIll.getMessage()); + sOut.append("\n"); + } + } + + + /** + * Writes the given content to the specified log file. + */ + private void impl_writeToLogFile(String sFileName, + boolean bAppend , + String sContent ) + { + try + { + FileWriter aLogFile = new FileWriter(sFileName, bAppend); + aLogFile.write(sContent); + aLogFile.flush(); + aLogFile.close(); + aLogFile = null; + } + catch (java.io.IOException exIO) + { + System.err.println("Can't dump protocol into log file."); + System.err.println(exIO); + exIO.printStackTrace(); + } + } +} diff --git a/framework/qa/complex/framework/recovery/CrashThread.java b/framework/qa/complex/framework/recovery/CrashThread.java new file mode 100644 index 0000000000..8a16f83c6a --- /dev/null +++ b/framework/qa/complex/framework/recovery/CrashThread.java @@ -0,0 +1,72 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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.framework.recovery; + +import com.sun.star.frame.XController; +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.uno.UnoRuntime; +import com.sun.star.util.URL; +import com.sun.star.util.XURLTransformer; + +/** + * Thread to crash the office. This thread dies after the office process + * is nopt longer available. + */ +public class CrashThread extends Thread { + private final XComponent xDoc; + private final XMultiServiceFactory msf; + + public CrashThread(XComponent xDoc, XMultiServiceFactory msf) { + this.xDoc = xDoc; + this.msf = msf; + } + + @Override + public void run() { + try{ + XModel xModel = UnoRuntime.queryInterface(XModel.class, xDoc); + + XController xController = xModel.getCurrentController(); + XDispatchProvider xDispProv = UnoRuntime.queryInterface( + XDispatchProvider.class, + xController); + XURLTransformer xParser = UnoRuntime.queryInterface( + XURLTransformer.class, + msf.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[1]; + aParseURL[0] = new URL(); + aParseURL[0].Complete = ".uno:Crash"; + xParser.parseStrict(aParseURL); + + URL aURL = aParseURL[0]; + XDispatch xDispatcher = xDispProv.queryDispatch(aURL, "", 0); + + if (xDispatcher != null) { + xDispatcher.dispatch(aURL, null); + } + } catch (Exception e){} + } +} diff --git a/framework/qa/complex/framework/recovery/KlickButtonThread.java b/framework/qa/complex/framework/recovery/KlickButtonThread.java new file mode 100644 index 0000000000..90ecf68c44 --- /dev/null +++ b/framework/qa/complex/framework/recovery/KlickButtonThread.java @@ -0,0 +1,46 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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.framework.recovery; + +import com.sun.star.awt.XWindow; +import util.UITools; + +/** + * Thread to crash the office. This thread dies after the office process + * is no longer available. + */ +public class KlickButtonThread extends Thread { + private final XWindow xWindow; + private final String buttonName; + + public KlickButtonThread(XWindow xWindow, String buttonName) { + this.xWindow = xWindow; + this.buttonName = buttonName; + } + + @Override + public void run() { + try{ + UITools oUITools = new UITools(xWindow); + + oUITools.clickButton(buttonName); + + } catch (Exception e){} + } +} diff --git a/framework/qa/complex/framework/recovery/RecoveryTest.java b/framework/qa/complex/framework/recovery/RecoveryTest.java new file mode 100644 index 0000000000..ede07b4863 --- /dev/null +++ b/framework/qa/complex/framework/recovery/RecoveryTest.java @@ -0,0 +1,611 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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.framework.recovery; + +import com.sun.star.accessibility.XAccessibleContext; +import com.sun.star.awt.Rectangle; +import com.sun.star.awt.Size; +import com.sun.star.awt.XDialog; +import com.sun.star.awt.XWindow; +import com.sun.star.container.XEnumeration; +import com.sun.star.frame.XModel; +import com.sun.star.lang.XComponent; +import com.sun.star.lang.XMultiServiceFactory; +import com.sun.star.text.ControlCharacter; +import com.sun.star.text.XText; +import com.sun.star.text.XTextCursor; +import com.sun.star.text.XTextDocument; +import com.sun.star.uno.UnoRuntime; +import complexlib.ComplexTestCase; +import helper.OfficeProvider; +import java.awt.Dimension; +import java.awt.Point; +import java.awt.Toolkit; +import java.io.PrintWriter; +import java.util.HashMap; +import java.util.Random; +import util.DesktopTools; +import util.PropertyName; +import util.SOfficeFactory; +import util.UITools; +import util.utils; + +public class RecoveryTest extends ComplexTestCase { + + static XMultiServiceFactory xMSF; + static SOfficeFactory SOF; + static RecoveryTools rt; + /** + * If you divide the screen in four parts in the first of them the office + * windows should be placed. The range of the first quarter is stored in the variable. + */ + static Point windowMaxPosition; + /** + * The office windows starts in the first quarter of the screen. In this variable + * the maximum size for the windows was stored so the windows can be placed + * visible on the screen. + */ + static Size windowMaxSize; + + /** + * All office windows will be placed by this test on randomized positions. + * This positions was stored in this Hashmap. The keys are the frame names + * and the values are com sun.star.awt.Rectangle. + * @see com.sun.star.awt.Rectangle + */ + private final HashMap windowsPosSize = new HashMap(); + + /** + * A function to tell the framework, which test functions are available. + * @return All test methods. + * + * @todo: hidden documents + * @todo: running presentation + * @todo: modular dialogs like Hpyerlink-Dialog + * @todo: second view of a document + * @todo: remove recovery data before start test + * @todo: after a second start after the crash there should no documents recovered anymore + * @todo: enable remove of recovery files + */ + @Override + public String[] getTestMethodNames() { + return new String[]{"testCrash"}; + } + + /** Create the environment for following tests. + * Use either a component loader from desktop or + * from frame + */ + public void normalCrash(){ + cleanRecoveryData(); + startOffice(); + generateDesktop(); + makeCrash(); + int expectedDocumentCount = windowsPosSize.size() + 1; + handleRecoveryDialogAfterCrash(expectedDocumentCount); + startOffice(); + handleRecoveryDialog_QuickExit(expectedDocumentCount); + handleCrashReporterDialog(true); + checkDocumentCount(expectedDocumentCount); + } + + public void testCrash(){ + cleanRecoveryData(); + restoreBackupRecoveryData(); + startOffice(); + int expectedDocumentCount = 3; + handleRecoveryDialog_QuickExitAndSave(expectedDocumentCount); + } + + public void before() throws Exception { + + String msg ="\n\n\tPATH TO OFFICE BINARY MISSING!\n"; + msg +="\tPlease run your command with the following parameter:\n\n"; + msg +="\t-AppExecutionCommand=OFFICEBINARY CONNECTIONSTRING\n\n"; + msg +="Example Windows:\n"; + msg +="-AppExecutionCommand=C:\\office\\soffice.exe --accept=socket,host=localhost,port=8101;urp;\n\n"; + msg +="Example UNIX:\n"; + msg +="-AppExecutionCommand=/office/soffice \"--accept=socket,host=localhost,port=8101;urp;\"\n\n"; + msg+="NOTE: on UNIX be sure to have the connection string inside quotation mark!\n"; + + assure(msg, param.get("AppExecutionCommand") != null && ! param.get("AppExecutionCommand").equals("")); + System.out.println("HALLO" + param.get("AppExecutionCommand")); + msg = "\n\nONE PARAMETER IS MISSING!\n"; + msg += "Please append to your command the following parameter:\n\n"; + msg += "\t-NoOffice=true"; + assure(msg, param.getBool("NoOffice")); + + + rt = new RecoveryTools(param ,log); + + rt.removeParametersFromAppExecutionCommand(); + + log.println("start the office to test recovery feature..."); + + // make window ranges + makeWindowPositionRage(); + + } + + private void startOffice(){ + assure("Could not connect to office", connect()); + log.setWatcher(param.get("Watcher")); + } + + + private void checkDocumentCount(int expectedDocumentCount){ + XEnumeration allComp = DesktopTools.getAllComponents(xMSF); + int documentCount = 0; + + try{ + while (allComp.hasMoreElements()){ + allComp.nextElement(); + documentCount ++; + } + } + catch ( com.sun.star.container.NoSuchElementException e){} + catch ( com.sun.star.lang.WrappedTargetException e){} + + String msg ="The amount of documents to recover is different from the expected amount:\n"; + msg += "\texpected:\t" + expectedDocumentCount + "\n"; + msg += "\tto recover:\t" + documentCount; + + assure(msg, expectedDocumentCount == documentCount); + } + + /** + * This function starts an office instance. It uses the AppExecutionCommand parameter. + * @return TRUE if office is connected otherwise FALSE + */ + private boolean connect(){ + try { + + OfficeProvider oProvider = new OfficeProvider(); + xMSF = (XMultiServiceFactory)oProvider.getManager(param); + + SOF = SOfficeFactory.getFactory(xMSF); + + } + catch (java.lang.Exception e) { + log.println(e.getClass().getName()); + log.println("Message: " + e.getMessage()); + failed("Cannot connect the Office."); + return false; + } + return true; + } + + /** + * This function crashes the office + */ + private void makeCrash(){ + // get all documents + Object[] allDocs = DesktopTools.getAllOpenDocuments(xMSF); + + // get one of them for dispatching + XComponent xDoc = (XComponent) allDocs[0]; + log.println("make the crash in second thread"); + + CrashThread crash = new CrashThread(xDoc, xMSF); + crash.start(); + util.utils.shortWait(); + util.utils.shortWait(); + } + + /** + * This function uses accessibility to handle the dialog which appears while the + * office is crashed. It click the button "OK" to continue. + */ + private void handleRecoveryDialogAfterCrash(int expectedDocumentCount){ + try{ + + // if the office crashes, the recovery feature needs some time + // to save all docs. Therefore the recovery dialog could need some + // time to pop up. + log.println("waiting for recovery dialog..."); + + int counter = 0; + int maximum = param.getInt(PropertyName.THREAD_TIME_OUT) / utils.DEFAULT_SHORT_WAIT_MS; + + XDialog oDialog = rt.getActiveDialog(xMSF); + + while ( oDialog == null && (counter < maximum)) + { + util.utils.shortWait(); + oDialog = rt.getActiveDialog(xMSF); + counter ++; + } + + assure("could not get Recovery Window",(oDialog != null)); + + XWindow xWindow = UnoRuntime.queryInterface(XWindow.class, oDialog); + + UITools oUITools = new UITools(xWindow); + + oUITools.printAccessibleTree((PrintWriter) log, param.getBool(PropertyName.DEBUG_IS_ACTIVE)); + + String[] documents = oUITools.getListBoxItems("The following files will be recovered"); + log.println("there are " + documents.length + " documents to save"); + + String msg ="The amount of documents to recover is different from the expected amount:\n"; + msg += "\texpected:\t" + expectedDocumentCount + "\n"; + msg += "\tto recover:\t" + documents.length; + + assure(msg, expectedDocumentCount == documents.length); + + log.println("disable automatically launch of Office"); + oUITools.setCheckBoxValue("Launch StarOffice automatically", Integer.valueOf(0)); + + log.println("start saving..."); + oUITools.clickButton("OK"); + + rt.waitForClosedOffice(); + + } catch (Exception e){ + e.printStackTrace(); + failed("Could not handle crash-dialog: " + e.toString()); + } + } + + private void handleCrashReporterDialog(boolean cancel){ + try{ + + log.println("try to get Crash Reporter Dialog..."); + + XDialog oDialog = rt.getActiveDialog(xMSF); + assure("could not get CrashReporter Dialog", oDialog != null); + + XWindow xWindow = UnoRuntime.queryInterface(XWindow.class, oDialog); + + log.println(oDialog.getTitle()); + + UITools oUITools = new UITools(xWindow); + + if (cancel) { + log.println("clicking 'Cancel' button..."); + + rt.clickThreadButton(xWindow, "Cancel"); + } + else { + log.println("clicking 'Next' button..."); + oUITools.clickButton("Next>>"); + } + + } catch (Exception e){ + failed("Could not handle CrashReporter Dialog: " + e.toString()); + } + } + + private void handleRecoveryDialog_QuickExit(int expectedDocumentCount){ + log.println("handle Recovery Dialog at restart: quick exit"); + handleRecoveryDialogAtRestart(expectedDocumentCount, false, true); + handleAreYouSureDialog(true); + handleSaveDocumentsDialog(false); + + } + private void handleRecoveryDialog_QuickExitAndSave(int expectedDocumentCount){ + log.println("handle Recovery Dialog at restart: quick exit"); + handleRecoveryDialogAtRestart(expectedDocumentCount, false, true); + handleAreYouSureDialog(true); + handleSaveDocumentsDialog(true); + } + /** + * This function uses accessibility to handle the dialog which appears while the + * office is started after a crash. It waits until the "next>>" button is enabled + * and click it then to continue. + * @param expectedDocumentCount the amount of documents which must be displayed in the recovery dialog + * @param recover If the document should be recovered this variable must be true. If it is false + * the recovery process was stopped and the button cancel was clicked. + * @param cancel If the recovery is finished, this parameter decides to click the "Next" button + * or the click cancel. If the value is true, the cancel button was clicked. + */ + private void handleRecoveryDialogAtRestart(int expectedDocumentCount, boolean recover, boolean cancel){ + try{ + + log.println("try to get Recovery Dialog..."); + + XDialog oDialog = null; + oDialog = rt.getActiveDialogAfterStartup(xMSF); + + assure("could not get Recovery Dialog at start of office", (oDialog != null), ContinueWithTest.YES); + + XWindow xWindow = UnoRuntime.queryInterface(XWindow.class, oDialog); + log.println("got the following dialog: '" +oDialog.getTitle() + "'"); + + UITools oUITools = new UITools(xWindow); + + String listBoxName = "Status of recovered documents"; + String[] documents = oUITools.getListBoxItems(listBoxName); + log.println("there are " + documents.length + " documents to recover"); + log.println("The following files will be recovered:"); + for (int i=0;i"); + + util.utils.shortWait(); + + int counter = 0; + int maximum = param.getInt(PropertyName.THREAD_TIME_OUT) / utils.DEFAULT_SHORT_WAIT_MS; + + XAccessibleContext oButton = null; + while ((oButton == null) && (counter < maximum)){ + log.println("recovering..."); + + try{ + oButton = oUITools.getButton("Next >"); + } catch (NullPointerException e){ + // no fault: The title "Start Recovery" switches to "Next" + // while all documents are recoverd + } + util.utils.shortWait(); + counter++; + } + + if (cancel) { + log.println("clicking 'Cancel' button..."); + + rt.clickThreadButton(xWindow, "Cancel"); + + } + else { + log.println("clicking 'Next' button..."); + oUITools.clickButton("Next >"); + } + + util.utils.shortWait(); + + } else { + log.println("do not recover: clicking 'Cancel' button..."); + + rt.clickThreadButton(xWindow, "Cancel"); + } + + } catch (Exception e){ + failed("Could not handle recovery-dialog at restart: " + e.toString()); + } + + } + + /** + * This function uses accessibility to handle the dialog "Are you sure". + * It click "Yes" or "No", dependent on the value of the parameter Yes + * @param yes If value is TRUE the button "Yes" was clicked, otherwise the button + * "No". + */ + private void handleAreYouSureDialog(boolean yes) + { + try{ + if (yes){ + rt.handleModalDialog(xMSF, "Yes"); + } else{ + rt.handleModalDialog(xMSF, "Cancel"); + } + } catch (com.sun.star.accessibility.IllegalAccessibleComponentStateException e){ + failed("Could not handle 'Are you sure' dialog."); + } + } + + /** + * This function uses accessibility to handle the dialog "Are you sure". + * It click "Yes" or "No", depending on the value of the parameter Yes + * @param saveDocuments If value is TRUE the button "Yes" was clicked, otherwise the button + * "No". + */ + private void handleSaveDocumentsDialog(boolean saveDocuments) + { + try{ + if (!saveDocuments){ + rt.handleModalDialog(xMSF, "Cancel"); + } else{ + XWindow oDialog = null; + oDialog = rt.getActiveWindow(xMSF); + + assure("could not get 'Save Documents' Dialog: ", (oDialog != null), ContinueWithTest.YES); + + UITools oUITools = new UITools(oDialog); + + oUITools.printAccessibleTree((PrintWriter) log, param.getBool(PropertyName.DEBUG_IS_ACTIVE)); + + String listBoxName = "Documents"; + String[] documents = null; + try{ + documents = oUITools.getListBoxItems(listBoxName); + } catch (java.lang.Exception e){ + failed("could not get the document names from the 'Save Documents' dialog", ContinueWithTest.YES); + } + log.println("there are " + documents.length + " documents to save"); + log.println("The following documents will be saved:"); + for (int i=0;iRecoverTest. You will find here some helper + * functions. + */ +public class RecoveryTools { + + private final TestParameters param; + private final LogWriter log; + + /** + * Creates new OfficeWatcher + * @param param the test parameter + * @param log a log writer + */ + public RecoveryTools(TestParameters param, LogWriter log) { + this.param = param; + this.log = log; + + } + + /** + * get the active dialog from the top of the desktop + * @return a XDialog interface of the dialog + */ + public XDialog getActiveDialog( XMultiServiceFactory xMSF){ + XWindow xWin = getActiveWindow(xMSF); + return UnoRuntime.queryInterface(XDialog.class, xWin); + } + + public XWindow getActiveWindow( XMultiServiceFactory xMSF){ + XInterface xToolKit = null; + try { + xToolKit = (XInterface) xMSF.createInstance("com.sun.star.awt.Toolkit") ; + } catch (com.sun.star.uno.Exception e) { + return null; + } + + XExtendedToolkit tk = UnoRuntime.queryInterface(XExtendedToolkit.class, xToolKit); + Object atw = tk.getActiveTopWindow(); + return UnoRuntime.queryInterface(XWindow.class, atw); + } + + /** + * After a crash the office start with a recovery dialog. It could be that the office + * is connectable but not all services to get the dialog a loaded. This function + * tries to get the dialog until the OfficeWatcher kills the office. + * @return a XDialog interface of the dialog + */ + public XDialog getActiveDialogAfterStartup(XMultiServiceFactory xMSF){ + // while the office starts it takes some time to get the dialog. + + // the dialog is accessible AFTER the office has recoverd all documents. + // This could consumes more time then the TimeOut allow. + int counter = 0; + int multi = 5; + int pause = utils.DEFAULT_SHORT_WAIT_MS * 10; + int timeOut = param.getInt(PropertyName.THREAD_TIME_OUT)*5; + int maximum = (timeOut / pause) * multi; + + XDialog oDialog = getActiveDialog(xMSF); + + while (oDialog == null && (counter < maximum)){ + log.println("waiting until the office has recovered... remaining " + (timeOut * multi - pause * counter)/1000 + " seconds"); + util.utils.pause(pause); + oDialog = getActiveDialog(xMSF); + counter ++; + } + return oDialog; + } + + /** + * remove the content of the user backup folder and removes the Recovery.xcu. This + * was done from the Office via XSimpleFileAccess + * @throws com.sun.star.io.IOException the exception was thrown if something goes wrong. + */ + public void cleanRecoveryData() + throws com.sun.star.io.IOException + { + try{ + HashMap recFiles = getRecoveryFiles(); + + String recoveryFolder = recFiles.get("recoveryFolder"); + String recoveryXCU = recFiles.get("recoveryXCU"); + + log.println("try to remove content of '" + recoveryFolder + "'"); + + File rf = new File(recoveryFolder); + + boolean success = FileTools.cleanDir(rf); + log.println("removed " + recoveryFolder + ": " + success); + + log.println("try to remove '" + recoveryXCU + "'"); + + File xcu = new File(recoveryXCU); + if (xcu.isFile()){ + success = xcu.delete(); + log.println("removed " + recoveryXCU + " : " + success); + } + + } catch (Exception e){ + throw new com.sun.star.io.IOException("could not remove old recovery data", e); + } + } + + private HashMap getRecoveryFiles() + throws com.sun.star.io.IOException + { + try{ + log.println("try to get UnoProvider..."); + UnoProvider unoProv = new UnoProvider(); + XMultiServiceFactory xMSF = (XMultiServiceFactory) unoProv.getManager(param); + + String userPath = utils.expandMacro(xMSF, "${$ORIGIN/bootstraprc:UserInstallation}"); + System.out.println("userPath:'" + userPath + "'"); + + if (userPath.equals(""))userPath = utils.expandMacro(xMSF, "${$ORIGIN/bootstrap.ini:UserInstallation}"); + System.out.println("userPath:'" + userPath + "'"); + + if (userPath.equals("")) throw new com.sun.star.io.IOException("could not get user path at bootstrapping"); + + String recoveryFolder = utils.getSystemURL(userPath + "/user/backup"); + + String recoveryXCU = utils.getSystemURL(userPath + "/user/registry/data/org/openoffice/Office/Recovery.xcu"); + + HashMap recFiles = new HashMap(); + + recFiles.put("recoveryFolder", recoveryFolder); + recFiles.put("recoveryXCU", recoveryXCU); + return recFiles; + + } catch (Exception e){ + throw new com.sun.star.io.IOException("could not get recovery folder", e); + } + + } + + + /** + * This function waits until the office is closed. If the closing time reach + * the value of parameter THREAD_TIME_OUT the office was killed. + */ + public void waitForClosedOffice(){ + // check for the office process + helper.ProcessHandler ph = (helper.ProcessHandler) param.get("AppProvider"); + + int timeOut = param.getInt(PropertyName.THREAD_TIME_OUT)*5; + int pause = utils.DEFAULT_SHORT_WAIT_MS * 20; + int multi = 0; + while ((ph != null) && (ph.getExitCode()<0) && (pause*multi < timeOut)) { + log.println("waiting until the office is closed... remaining " + (timeOut - pause * multi)/1000 + " seconds"); + util.utils.pause(pause); + multi ++; + } + + // be sure that office is closed + if (ph != null) ph.kill(); + } + + + + /** + * The office must be started WITH restore functionality. + * Therefore the parameter '--norestore' + * was removed from the AppExecutionCommand parameter + */ + public void removeParametersFromAppExecutionCommand(){ + + //remove some params to start office + String office = (String) param.get("AppExecutionCommand"); + String[] params = {"--norestore"}; + + for (int i = 0; i < params.length; i++){ + int index = office.indexOf(params[i]); + int length = params[i].length(); + if (index != -1){ + office = office.substring(0, index) + office.substring(index + length); + log.println("removed '" + params[i] + "' from AppExecutionCommand: " + office); + } + } + param.put("AppExecutionCommand", office); + log.println("connect: " + (String) param.get("AppExecutionCommand")); + + } + + /** + * This function uses accessibility to handle modal dialogs like the + * "Are you sure" dialog. + * It clicks the named button given in parameter buttonName + * @param buttonName the name of the button which should be clicked + */ + public void handleModalDialog(XMultiServiceFactory xMSF, String buttonName) + throws com.sun.star.accessibility.IllegalAccessibleComponentStateException + { + + log.println("try to get modal Dialog..."); + + util.utils.shortWait(); + + XWindow oDialog = getActiveWindow(xMSF); + + if (oDialog == null) throw new com.sun.star.accessibility.IllegalAccessibleComponentStateException("could not get modal Dialog"); + + + UITools oUITools = new UITools(oDialog); + oUITools.printAccessibleTree((PrintWriter) log, param.getBool(PropertyName.DEBUG_IS_ACTIVE)); + + try{ + log.println("click ' " + buttonName + "' button.."); + oUITools.clickButton(buttonName); + } catch ( java.lang.Exception e){ + throw new com.sun.star.accessibility.IllegalAccessibleComponentStateException("Could not click '"+buttonName +"' at modal dialog", e); + } + util.utils.shortWait(); + } + + public void clickThreadButton(XWindow xWindow, String buttonName) + { + KlickButtonThread kbt = new KlickButtonThread(xWindow, buttonName); + kbt.start(); + util.utils.pause(utils.DEFAULT_SHORT_WAIT_MS * 10); + } + + public void copyRecoveryData(boolean backup) + throws com.sun.star.io.IOException, java.io.IOException + { + HashMap recFiles = null; + + try{ + recFiles = getRecoveryFiles(); + } catch ( com.sun.star.io.IOException e){ + throw new com.sun.star.io.IOException("Could not get recovery files", e); + } + + try{ + String recoveryFolder = recFiles.get("recoveryFolder"); + String recoveryXCU = recFiles.get("recoveryXCU"); + + File recFolder = new File(recoveryFolder); + File recFolderBackup = new File(recoveryFolder+".recoveryTest"); + + File recXCU = new File(recoveryXCU); + File recXCUBackup = new File(recoveryXCU + ".recoveryTest"); + + if (backup){ + FileTools.copyDirectory(recFolder, recFolderBackup); + FileTools.copyFile(recXCU, recXCUBackup); + } else { + FileTools.copyDirectory(recFolderBackup, recFolder); + FileTools.copyFile(recXCUBackup, recXCU); + + } + } catch (java.io.IOException e){ + // the new constructor that takes a Throwable is only available in Java1.6 + java.io.IOException newEx = new java.io.IOException("Could not copy recovery files"); + newEx.initCause(e); + throw newEx; + } + } + + +} diff --git a/framework/qa/complex/imageManager/CheckImageManager.java b/framework/qa/complex/imageManager/CheckImageManager.java new file mode 100644 index 0000000000..a88a96d30c --- /dev/null +++ b/framework/qa/complex/imageManager/CheckImageManager.java @@ -0,0 +1,201 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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.imageManager; + +import com.sun.star.lang.XComponent; +import com.sun.star.lang.XInitialization; +import com.sun.star.lang.XMultiServiceFactory; +import com.sun.star.lang.XTypeProvider; +import com.sun.star.uno.UnoRuntime; +import com.sun.star.ui.XImageManager; +import com.sun.star.ui.XModuleUIConfigurationManagerSupplier; +import com.sun.star.ui.XUIConfiguration; +import com.sun.star.ui.XUIConfigurationManager; +import com.sun.star.ui.XUIConfigurationPersistence; + + +// ---------- junit imports ----------------- +import lib.TestParameters; +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.*; + + +/** + * + */ +public class CheckImageManager { + boolean checkUIConfigManager = false; + XMultiServiceFactory xMSF = null; + /** + * The test parameters + */ + private TestParameters param = null; + + @Before public void before() + { + xMSF = getMSF(); + param = new TestParameters(); + param.put("ServiceFactory", xMSF); // some qadevOOo functions need the ServiceFactory + } + + + @Test public void checkImageManagerFromModule() + { + System.out.println(" **** ImageManager from ModuleUIConfigurationManager *** "); + XUIConfigurationManager xManager = null; + try { + Object o = xMSF.createInstance( + "com.sun.star.ui.ModuleUIConfigurationManagerSupplier"); + XModuleUIConfigurationManagerSupplier xMUICMS = + UnoRuntime.queryInterface(XModuleUIConfigurationManagerSupplier.class, o); + xManager = xMUICMS.getUIConfigurationManager( + "com.sun.star.text.TextDocument"); + } + catch(com.sun.star.uno.Exception e) { + fail("Exception. " + e.getMessage()); + } + XImageManager xImageManager = UnoRuntime.queryInterface(XImageManager.class, xManager.getImageManager()); + performChecks(xImageManager, "ModuleUIConfig"); + } + + public void checkImageManager() { + System.out.println(" **** ImageManager from UIConfigurationManager *** "); + XUIConfigurationManager xManager = null; + try { + xManager = UnoRuntime.queryInterface(XUIConfigurationManager.class, xMSF.createInstance("com.sun.star.comp.framework.UIConfigurationManager")); + } + catch(com.sun.star.uno.Exception e) { + fail("Exception. " + e.getMessage()); + } + + XImageManager xImageManager = UnoRuntime.queryInterface(XImageManager.class, xManager.getImageManager()); + performChecks(xImageManager, "UIConfig"); + } + + private void performChecks(XImageManager xImageManager, String testObjectName) { + util.dbg.printInterfaces(xImageManager); + + OXUIConfigurationListenerImpl configListener = new OXUIConfigurationListenerImpl(); + param.put("XUIConfiguration.XUIConfigurationListenerImpl", configListener); + + XInitialization xInit = UnoRuntime.queryInterface(XInitialization.class, xImageManager); + _XInitialization _xInit = new _XInitialization(param, xInit); + assertTrue(testObjectName + "::XInitialization.initialize", _xInit._initialize()); + + // xImageManager is already there, just write a test ;-) + _XImageManager _xImage = new _XImageManager(xImageManager); + assertTrue(testObjectName + "::XImageManager.getAllImageNames", _xImage._getAllImageNames()); + assertTrue(testObjectName + "::XImageManager.getImages", _xImage._getImages()); + assertTrue(testObjectName + "::XImageManager.hasImage", _xImage._hasImage()); + assertTrue(testObjectName + "::XImageManager.insertImages", _xImage._insertImages()); + assertTrue(testObjectName + "::XImageManager.removeImages", _xImage._removeImages()); + assertTrue(testObjectName + "::XImageManager.replaceImages", _xImage._replaceImages()); + assertTrue(testObjectName + "::XImageManager.reset", _xImage._reset()); + + XTypeProvider xType = UnoRuntime.queryInterface(XTypeProvider.class, xImageManager); + _XTypeProvider _xType = new _XTypeProvider(xType); + assertTrue(testObjectName + "::XTypeProvider.getImplementationId", _xType._getImplementationId()); + assertTrue(testObjectName + "::XTypeProvider.getTypes", _xType._getTypes()); + + XUIConfiguration xUIConfig = UnoRuntime.queryInterface(XUIConfiguration.class, xImageManager); + _XUIConfiguration _xUIConfig = new _XUIConfiguration(param, xUIConfig); + _xUIConfig.before(); + assertTrue(testObjectName + "::XUIConfig.addConfigurationListener", _xUIConfig._addConfigurationListener()); + assertTrue(testObjectName + "::XUIConfig.removeConfigurationListener", _xUIConfig._removeConfigurationListener()); + + XUIConfigurationPersistence xUIConfigPersistence = (XUIConfigurationPersistence)UnoRuntime.queryInterface(XUIConfiguration.class, xImageManager); + _XUIConfigurationPersistence _xUIConfigPersistence = new _XUIConfigurationPersistence(param, xUIConfigPersistence); + _xUIConfigPersistence.before(); + assertTrue(testObjectName + "::XUIConfigPersistence.isModified", _xUIConfigPersistence._isModified()); + assertTrue(testObjectName + "::XUIConfigPersistence.isReadOnly", _xUIConfigPersistence._isReadOnly()); + assertTrue(testObjectName + "::XUIConfigPersistence.reload", _xUIConfigPersistence._reload()); + assertTrue(testObjectName + "::XUIConfigPersistence.store", _xUIConfigPersistence._store()); + assertTrue(testObjectName + "::XUIConfigPersistence.storeToStorage", _xUIConfigPersistence._storeToStorage()); + + XComponent xComp = UnoRuntime.queryInterface(XComponent.class, xImageManager); + _XComponent _xComp = new _XComponent(param, xComp); + _xComp.before(); + assertTrue(testObjectName + "::XComponent.addEventListener", _xComp._addEventListener()); + assertTrue(testObjectName + "::XComponent.removeEventListener", _xComp._removeEventListener()); + assertTrue(testObjectName + "::XComponent.dispose", _xComp._dispose()); + } + + + private static class OXUIConfigurationListenerImpl implements _XUIConfiguration.XUIConfigurationListenerImpl { + private boolean triggered = false; + + public boolean actionWasTriggered() { + return triggered; + } + + public void disposing(com.sun.star.lang.EventObject eventObject) { + triggered = true; + } + + public void elementInserted(com.sun.star.ui.ConfigurationEvent configurationEvent) { + triggered = true; + } + + public void elementRemoved(com.sun.star.ui.ConfigurationEvent configurationEvent) { + triggered = true; + } + + public void elementReplaced(com.sun.star.ui.ConfigurationEvent configurationEvent) { + triggered = true; + } + + public void fireEvent() { + // remove for real action: + triggered = !triggered; + } + + public void reset() { + // remove comment for real function + //triggered = false; + } + + } + + 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()"); + connection.tearDown(); + } + private static final OfficeConnection connection = new OfficeConnection(); + +} diff --git a/framework/qa/complex/imageManager/_XComponent.java b/framework/qa/complex/imageManager/_XComponent.java new file mode 100644 index 0000000000..0d0a997b73 --- /dev/null +++ b/framework/qa/complex/imageManager/_XComponent.java @@ -0,0 +1,167 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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.imageManager; + +import com.sun.star.frame.XDesktop; +import com.sun.star.lang.EventObject; +import com.sun.star.lang.XComponent; +import com.sun.star.lang.XEventListener; +import lib.TestParameters; + +/** +* Testing com.sun.star.lang.XComponent +* interface methods : +*
      +*
    • dispose()
    • +*
    • addEventListener()
    • +*
    • removeEventListener()
    • +*
    +* After this interface test object must be recreated.

    +* Multithreaded test ability not implemented yet. +* @see com.sun.star.lang.XComponent +*/ +public class _XComponent { + + private XComponent oObj = null; + private XComponent altDispose = null; + private final TestParameters tEnv; + private boolean listenerDisposed[] = new boolean[2]; + private String[] Loutput = new String[2]; + + /** + * Listener which added but not removed, and its method must be called + * on dispose call. + */ + private class MyEventListener implements XEventListener { + private final int number; + private final String message; + private MyEventListener(int number, String message) { + this.message = message; + this.number = number; + } + public void disposing ( EventObject oEvent ) { + Loutput[number] = Thread.currentThread() + " is DISPOSING " + message + this; + listenerDisposed[number] = true; + } + } + + private final XEventListener listener1 = new MyEventListener(0, "EV1"); + private final XEventListener listener2 = new MyEventListener(1, "EV2"); + + public _XComponent(TestParameters tEnv, XComponent oObj) { + this.tEnv = tEnv; + this.oObj = oObj; + } + + /** + * For the cfgmgr2.OSetElement tests: dispose the owner element. + */ + public void before() { + // do not dispose this component, but parent instead + altDispose = (XComponent)tEnv.get("XComponent.DisposeThis"); + + } + + /** + * Adds two listeners.

    + * Has OK status if then the first listener will receive an event + * on dispose method call. + */ + public boolean _addEventListener() { + + listenerDisposed[0] = false; + listenerDisposed[1] = false; + + oObj.addEventListener( listener1 ); + oObj.addEventListener( listener2 ); + + return true; + } // finished _addEventListener() + + /** + * Removes the second of two added listeners.

    + * Method tests to be completed successfully : + *

      + *
    • addEventListener : method must add two listeners.
    • + *

    + * Has OK status if no events will be sent to the second listener on + * dispose method call. + */ + public boolean _removeEventListener() { + if (disposed) + { + System.out.println("Hint: already disposed."); + return false; + } + // the second listener should not be called + oObj.removeEventListener( listener2 ); + System.out.println(Thread.currentThread() + " is removing EL " + listener2); + return true; + } // finished _removeEventListener() + + private boolean disposed = false; + + /** + * Disposes the object and then check appropriate listeners were + * called or not.

    + * Method tests to be completed successfully : + *

      + *
    • removeEventListener : method must remove one of two + * listeners.
    • + *

    + * Has OK status if liseter removed wasn't called and other listener + * was. + */ + public boolean _dispose() { + disposed = false; + + System.out.println( "begin dispose" + Thread.currentThread()); + XDesktop oDesk = (XDesktop) tEnv.get("Desktop"); + if (oDesk !=null) { + oDesk.terminate(); + } + else { + if (altDispose == null) + { + oObj.dispose(); + } + else + { + altDispose.dispose(); + } + } + + util.utils.shortWait(); + if (Loutput[0]!=null){ + System.out.println(Loutput[0]); + } + if (Loutput[1]!=null) { + System.out.println(Loutput[1]); + } + System.out.println( "end dispose" + Thread.currentThread()); + disposed = true; + + // check that dispose() works OK. + return listenerDisposed[0] && !listenerDisposed[1]; + + } // finished _dispose() + +} // finished class _XComponent + + diff --git a/framework/qa/complex/imageManager/_XImageManager.java b/framework/qa/complex/imageManager/_XImageManager.java new file mode 100644 index 0000000000..c077bbf265 --- /dev/null +++ b/framework/qa/complex/imageManager/_XImageManager.java @@ -0,0 +1,105 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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.imageManager; + +import com.sun.star.graphic.XGraphic; +import com.sun.star.ui.ImageType; +import com.sun.star.ui.XImageManager; + + +/** + * + */ +public class _XImageManager { + + + private String[]imageNames = null; + private XGraphic[] xGraphicArray = null; + private final XImageManager oObj; + + public _XImageManager( XImageManager oObj) { + this.oObj = oObj; + } + + public boolean _getAllImageNames() { + short s = ImageType.COLOR_NORMAL + ImageType.SIZE_DEFAULT; + imageNames = oObj.getAllImageNames(s); + for (int i=0; i<(imageNames.length>10?10:imageNames.length); i++) + { + System.out.println("###### Image: " + imageNames[i]); + } + return imageNames != null; + } + + public boolean _getImages() { + short s = ImageType.COLOR_NORMAL + ImageType.SIZE_DEFAULT; + try { + xGraphicArray = oObj.getImages(s, imageNames); + } + catch(com.sun.star.lang.IllegalArgumentException e) { + } + return xGraphicArray != null; + } + + public boolean _hasImage() { + boolean result = true; + short s = ImageType.COLOR_NORMAL + ImageType.SIZE_DEFAULT; + try { // check the first image names, 10 at max + for (int i=0; i<(imageNames.length>10?10:imageNames.length); i++) + { + result &= oObj.hasImage(s, imageNames[i]); + } + } + catch(com.sun.star.lang.IllegalArgumentException e) { + result = false; + } + return result; + } + + public boolean _insertImages() { + try { + oObj.insertImages((short)imageNames.length, imageNames, xGraphicArray); + } + catch(com.sun.star.container.ElementExistException e) { + } + catch(com.sun.star.lang.IllegalArgumentException e) { + } + catch(com.sun.star.lang.IllegalAccessException e) { + } + return true; + } + + public boolean _removeImages() { + try { + oObj.removeImages((short)(imageNames.length-1), imageNames); + } + catch(com.sun.star.lang.IllegalArgumentException e) { + } + catch(com.sun.star.lang.IllegalAccessException e) { + } + return true; + } + + public boolean _replaceImages() { + return true; + } + + public boolean _reset() { + return true; + } +} diff --git a/framework/qa/complex/imageManager/_XInitialization.java b/framework/qa/complex/imageManager/_XInitialization.java new file mode 100644 index 0000000000..07508ef3f9 --- /dev/null +++ b/framework/qa/complex/imageManager/_XInitialization.java @@ -0,0 +1,78 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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.imageManager; + + + + +import com.sun.star.lang.XInitialization; +import lib.TestParameters; + +/** +* Testing com.sun.star.lang.XInitialization +* interface methods.

    +* This test needs the following object relations : +*

      +*
    • 'XInitialization.args' (of type Object[]): +* (Optional) : argument for initialize +* method. If omitted, zero length array is used.
    • +*

        +* Test is multithread compliant.

        +* Till the present time there was no need to recreate environment +* after this test completion. +*/ +public class _XInitialization { + + + private final TestParameters tEnv; + private XInitialization oObj = null; + + public _XInitialization(TestParameters tEnv, XInitialization oObj) { + + this.tEnv = tEnv; + this.oObj = oObj; + } + + /** + * Test calls the method with 0 length array and checks that + * no exceptions were thrown.

        + * Has OK status if no exceptions were thrown.

        + */ + public boolean _initialize() { + boolean result = true ; + + try { + Object[] args = (Object[]) tEnv.get("XInitialization.args"); + if (args==null) { + oObj.initialize(new Object[0]); + } else { + oObj.initialize(args); + } + + } catch (com.sun.star.uno.Exception e) { + System.out.println("Exception occurred while method calling.") ; + result = false ; + } + + return result ; + } // finished _initialize() + +} // finished class _XInitialization + + diff --git a/framework/qa/complex/imageManager/_XTypeProvider.java b/framework/qa/complex/imageManager/_XTypeProvider.java new file mode 100644 index 0000000000..318c056c88 --- /dev/null +++ b/framework/qa/complex/imageManager/_XTypeProvider.java @@ -0,0 +1,89 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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.imageManager; + + + +import com.sun.star.lang.XTypeProvider; +import com.sun.star.uno.Type; + +/** +* Testing com.sun.star.lang.XTypeProvider +* interface methods : +*

          +*
        • getTypes()
        • +*
        • getImplementationId()
        • +*

        +* Test is NOT multithread compliant.

        +* @see com.sun.star.lang.XTypeProvider +*/ +public class _XTypeProvider { + + + + private XTypeProvider oObj = null; + + public _XTypeProvider(XTypeProvider oObj) { + this.oObj = oObj; + } + + /** + * Just calls the method.

        + * Has OK status if no runtime exceptions occurred. + */ + public boolean _getImplementationId() { + boolean result = true; + System.out.println("testing getImplementationId() ... "); + + System.out.println("The ImplementationId is "+oObj.getImplementationId()); + result = true; + + return result; + + } // end getImplementationId() + + + /** + * Calls the method and checks the return value.

        + * Has OK status if one of the return value equals to the + * type com.sun.star.lang.XTypeProvider. + */ + public boolean _getTypes() { + boolean result = false; + System.out.println("getting Types..."); + Type[] types = oObj.getTypes(); + for (int i=0;i"); + } + + return result; + + } // end getTypes() + +} + diff --git a/framework/qa/complex/imageManager/_XUIConfiguration.java b/framework/qa/complex/imageManager/_XUIConfiguration.java new file mode 100644 index 0000000000..f2e7be33d4 --- /dev/null +++ b/framework/qa/complex/imageManager/_XUIConfiguration.java @@ -0,0 +1,66 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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.imageManager; + + +import com.sun.star.ui.XUIConfiguration; +import com.sun.star.ui.XUIConfigurationListener; +import lib.TestParameters; + + + +public class _XUIConfiguration { + + + private final TestParameters tEnv; + private final XUIConfiguration oObj; + private XUIConfigurationListenerImpl xListener = null; + + public interface XUIConfigurationListenerImpl + extends XUIConfigurationListener { + void reset(); + void fireEvent(); + boolean actionWasTriggered(); + } + + + public _XUIConfiguration(TestParameters tEnv, XUIConfiguration oObj) { + this.tEnv = tEnv; + this.oObj = oObj; + } + + public void before() { + xListener = (XUIConfigurationListenerImpl)tEnv.get( + "XUIConfiguration.XUIConfigurationListenerImpl"); + } + + public boolean _addConfigurationListener() { + oObj.addConfigurationListener(xListener); + xListener.fireEvent(); + return xListener.actionWasTriggered(); + } + + public boolean _removeConfigurationListener() { + oObj.removeConfigurationListener(xListener); + xListener.reset(); + xListener.fireEvent(); + return !xListener.actionWasTriggered(); + } + +} diff --git a/framework/qa/complex/imageManager/_XUIConfigurationPersistence.java b/framework/qa/complex/imageManager/_XUIConfigurationPersistence.java new file mode 100644 index 0000000000..23735a1797 --- /dev/null +++ b/framework/qa/complex/imageManager/_XUIConfigurationPersistence.java @@ -0,0 +1,84 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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.imageManager; + +import com.sun.star.embed.XStorage; +import com.sun.star.ui.XUIConfigurationPersistence; +import lib.TestParameters; + + + +public class _XUIConfigurationPersistence { + + + private final TestParameters tEnv; + private final XUIConfigurationPersistence oObj; + private XStorage xStore = null; + + public _XUIConfigurationPersistence(TestParameters tEnv, XUIConfigurationPersistence oObj) { + + this.tEnv = tEnv; + this.oObj = oObj; + } + + public void before() { + xStore = (XStorage)tEnv.get("XUIConfigurationStorage.Storage"); + } + + public boolean _reload() { + try { + oObj.reload(); + } + catch(com.sun.star.uno.Exception e) { + + } + return true; + } + + public boolean _store() { + try { + oObj.store(); + } + catch(com.sun.star.uno.Exception e) { + + } + return true; + } + + public boolean _storeToStorage() { + boolean result = true; + try { + oObj.storeToStorage(xStore); + } + catch(com.sun.star.uno.Exception e) { + result = false; + + } + return result; + } + + public boolean _isModified() { + return !oObj.isModified(); + } + + public boolean _isReadOnly() { + return !oObj.isReadOnly(); + } + +} diff --git a/framework/qa/complex/loadAllDocuments/CheckXComponentLoader.java b/framework/qa/complex/loadAllDocuments/CheckXComponentLoader.java new file mode 100644 index 0000000000..451ba1292c --- /dev/null +++ b/framework/qa/complex/loadAllDocuments/CheckXComponentLoader.java @@ -0,0 +1,526 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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.loadAllDocuments; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.fail; +import helper.URLHelper; + +import java.io.File; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +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.OfficeFileUrl; + +import com.sun.star.beans.PropertyValue; +import com.sun.star.frame.FrameSearchFlag; +import com.sun.star.frame.XComponentLoader; +import com.sun.star.frame.XFrame; +import com.sun.star.frame.XStorable; +import com.sun.star.io.XInputStream; +import com.sun.star.lang.XComponent; +import com.sun.star.lang.XMultiServiceFactory; +import com.sun.star.ucb.XSimpleFileAccess; +import com.sun.star.uno.UnoRuntime; +import com.sun.star.util.XCloseable; + + +/** @short Check the interface method XComponentLoader.loadComponentFromURL() + + @descr A prerequisite for this test is a server which allows access to files + that will be loaded via three different access methods: +

          +
        • 1. nfs (mounted directory / mapped network drive)
        • +
        • 2. ftp
        • +
        • 3. http
        • +
        +

        + The test will look for a list of files from the TestDocumentPath + on and load these files from the mounted directory, via ftp and http. + The parameters for this have to be "ftp_access" and "http_access". + If they are not given, tests for ftp and http will fail. + + @todo We need a further test for accessing UNC paths on windows! + */ +public class CheckXComponentLoader +{ + + // some const + + /** used to classify the result of a loadComponentFromURL() request. */ + private static final int RESULT_VALID_DOC = 1; + private static final int RESULT_EMPTY_DOC = 2; + private static final int RESULT_ILLEGALARGUMENTEXCEPTION = 3; + private static final int RESULT_IOEXCEPTION = 4; + private static final int RESULT_RUNTIMEEXCEPTION = 5; + private static final int RESULT_EXCEPTION = 6; + + /** used for testing password protected files. */ + private static final String SUFFIX_PASSWORD_TEMPFILE = "password_"; + private static final String PREFIX_PASSWORD_TEMPFILE = ".sxw"; + private static final String DEFAULT_PASSWORD = "DefaultPasswordForComponentLoaderTest"; + + + // member + + /** provides XComponentLoader interface too. */ + private XFrame m_xFrame = null; + + /** will be set to xDesktop OR xFrame. */ + private XComponentLoader m_xLoader = null; + + /** can be used to open local files as stream. */ + private XSimpleFileAccess m_xStreamProvider = null; + + /** directory for creating temp. files. */ + private String m_sTempPath = null; + + /** directory for searching files to load */ + private String m_sTestDocPath = null; + + /** files of m_sTestDocPath to test. */ + private static ArrayList m_lTestFiles = null; + + + // test environment + + + /** @short Create the environment for following tests. + + @descr Use either a component loader from desktop or + from frame + */ + @Before public void before() throws Exception + { + // get uno service manager from global test environment + /* points to the global uno service manager. */ + XMultiServiceFactory xMSF = getMSF(); + + // create stream provider + m_xStreamProvider = UnoRuntime.queryInterface(XSimpleFileAccess.class, xMSF.createInstance("com.sun.star.ucb.SimpleFileAccess")); + + // create desktop instance + /* provides XComponentLoader interface. */ + XFrame xDesktop = UnoRuntime.queryInterface(XFrame.class, xMSF.createInstance("com.sun.star.frame.Desktop")); + + // create frame instance + m_xFrame = xDesktop.findFrame("testFrame_componentLoader", + FrameSearchFlag.TASKS | FrameSearchFlag.CREATE); + assertNotNull("Couldn't create test frame.", m_xFrame); + + // define default loader for testing + // TODO think about using of bot loader instances! + m_xLoader = UnoRuntime.queryInterface(XComponentLoader.class, xDesktop); + assertNotNull("Desktop service doesn't support needed component loader interface.", m_xLoader); + + // get temp path for this environment + final String tempDirURL = util.utils.getOfficeTemp/*Dir*/(getMSF()); + m_sTempPath = graphical.FileHelper.getSystemPathFromFileURL(tempDirURL); + // m_sTempPath = "."+fs_sys; + + // get all files from the given directory + // TODO URLHelper should ignore directories! + m_lTestFiles = new ArrayList(); + final String sTestDocURL = OfficeFileUrl.getAbsolute(new File("testdocuments")); + m_sTestDocPath = graphical.FileHelper.getSystemPathFromFileURL(sTestDocURL); + File aBaseDir = new File(m_sTestDocPath); + List lDirContent = URLHelper.getSystemFilesFromDir(aBaseDir.getPath()); + Iterator lList = lDirContent.iterator(); + int nBasePathLength = m_sTestDocPath.length(); + while(lList.hasNext()) + { + File aFile = lList.next(); + + // ignore broken links and directories at all + if ( + (!aFile.exists()) || + (!aFile.isFile()) + ) + { + continue; + } + + String sCompletePath = aFile.getAbsolutePath(); + String sSubPath = sCompletePath.substring(nBasePathLength); + + m_lTestFiles.add(sSubPath); + } + } + + + /** @short close the environment. + */ + @After public void after() throws Exception + { + XCloseable xClose = UnoRuntime.queryInterface(XCloseable.class, m_xFrame); + xClose.close(false); + + m_xFrame = null; + m_xLoader = null; + } + + + /** @short Look for files in the given directory for loading. + */ + @Test public void checkUsingOfMediaDescriptor() + { + InteractionHandler xHandler = new InteractionHandler(); + StatusIndicator xIndicator = new StatusIndicator(); + + PropertyValue[] lProps = new PropertyValue[3]; + + lProps[0] = new PropertyValue(); + lProps[0].Name = "Hidden"; + lProps[0].Value = Boolean.TRUE; + + lProps[1] = new PropertyValue(); + lProps[1].Name = "InteractionHandler"; + lProps[1].Value = xHandler; + + lProps[2] = new PropertyValue(); + lProps[2].Name = "StatusIndicator"; + lProps[2].Value = xIndicator; + + Iterator aSnapshot = m_lTestFiles.iterator(); + while (aSnapshot.hasNext()) + { + File aSysFile = new File(m_sTestDocPath, aSnapshot.next()); + String sURL = URLHelper.getFileURLFromSystemPath(aSysFile); + + loadURL(m_xLoader, RESULT_VALID_DOC, sURL, "_blank", 0, lProps); + // It's not needed to reset this using states! + // It's done internally... + if (!xIndicator.wasUsed()) + { + System.out.println("External progress was not used for loading."); + } + if (xHandler.wasUsed()) + { + System.out.println("External interaction handler was not used for loading."); + } + } + } + + + /** TODO document me and move this method to a more global helper! */ + private String impl_getTempFileName(String sTempPath, + String sSuffix , + String sPrefix ) + { + File aDir = new File(sTempPath); + aDir.mkdirs(); + + // TODO: create a temp file which not exist! + for (int i=0; i<999999; ++i) + { + File aTempFile = new File(aDir, sSuffix+i+sPrefix); + if (!aTempFile.exists()) + { + return aTempFile.getAbsolutePath(); + } + } + + fail("Seems that all temp file names are currently in use!"); + return null; + } + + + /** TODO document me and move this method to a more global helper! */ + private void impl_createTempOfficeDocument(XComponentLoader xLoader , + String sSourceURL, + String sTargetURL, + String sFilter , + String sPassword ) throws Exception + { + PropertyValue[] lLoadProps = new PropertyValue[1]; + + lLoadProps[0] = new PropertyValue(); + lLoadProps[0].Name = "Hidden"; + lLoadProps[0].Value = Boolean.TRUE; + + PropertyValue[] lSaveProps = new PropertyValue[3]; + + lSaveProps[0] = new PropertyValue(); + lSaveProps[0].Name = "FilterName"; + lSaveProps[0].Value = sFilter; + + lSaveProps[1] = new PropertyValue(); + lSaveProps[1].Name = "PassWord"; + lSaveProps[1].Value = sPassword; + + lSaveProps[2] = new PropertyValue(); + lSaveProps[2].Name = "Overwrite"; + lSaveProps[2].Value = Boolean.TRUE; + + XComponent xDoc = null; + // load it + xDoc = xLoader.loadComponentFromURL(sSourceURL, "_blank", 0, lLoadProps); + assertNotNull("Could create office document, which should be saved as temp one.", xDoc); + + // save it as temp file + XStorable xStore = UnoRuntime.queryInterface(XStorable.class, xDoc); + xStore.storeAsURL(sTargetURL, lSaveProps); + + // Don't forget to close this file. Otherwise the temp file is locked! + XCloseable xClose = UnoRuntime.queryInterface(XCloseable.class, xDoc); + xClose.close(false); + } + + + /** @short Check the password handling. + + @descr The used password is the one given + as password for the ftp connection, + or - if none given a default one. + */ + @Test public void checkLoadingWithPassword() throws Exception + { + String sTempFile = impl_getTempFileName(m_sTempPath, SUFFIX_PASSWORD_TEMPFILE, PREFIX_PASSWORD_TEMPFILE); + File aTestFile = new File(sTempFile); + String sTestURL = URLHelper.getFileURLFromSystemPath(aTestFile); + + impl_createTempOfficeDocument(m_xLoader, "private:factory/swriter", sTestURL, "StarOffice XML (Writer)", DEFAULT_PASSWORD); + + PropertyValue[] lArgs1 = new PropertyValue[2]; + + lArgs1[0] = new PropertyValue(); + lArgs1[0].Name = "Hidden"; + lArgs1[0].Value = Boolean.TRUE; + + lArgs1[1] = new PropertyValue(); + lArgs1[1].Name = "Password"; + lArgs1[1].Value = DEFAULT_PASSWORD; + + PropertyValue[] lArgs2 = new PropertyValue[1]; + + lArgs2[0] = new PropertyValue(); + lArgs2[0].Name = "Hidden"; + lArgs2[0].Value = Boolean.TRUE; + + loadURL(m_xLoader, RESULT_VALID_DOC, sTestURL, "_blank", 0, lArgs1); +// TODO: wrong? loadURL(m_xLoader, RESULT_EMPTY_DOC, sTestURL, "_blank", 0, lArgs2); + } + + /** + * Check URL encoding. The first filename that matches "*.sxw" + * is used as source for several encodings. + */ + @Test public void checkURLEncoding() throws Exception { + PropertyValue[] lProps = new PropertyValue[1]; + + lProps[0] = new PropertyValue(); + lProps[0].Name = "Hidden"; + lProps[0].Value = Boolean.TRUE; + + // first get encoding of this system + InputStreamReader in = new InputStreamReader(System.in); + String sSystemEncoding = in.getEncoding(); + + System.out.println("This system's encoding: " + sSystemEncoding); + + assertNotNull("Found an empty directory. There are no files for testing.", m_lTestFiles); + + + // get a file name as byte array + Iterator aSnapshot = m_lTestFiles.iterator(); + byte[] baURL = null; + + while (aSnapshot.hasNext()) { + File aFile = new File(m_sTestDocPath, aSnapshot.next()); + String sFile = URLHelper.getFileURLFromSystemPath(aFile); + + // take the first sxw file as stream + if (sFile.endsWith(".sxw")) { + baURL = sFile.getBytes(); + + break; + } + } + + assertNotNull("Found no file to load. Cannot test.", baURL); + + //construct several different encoded strings + String[] sEncoding = new String[] { + "US-ASCII", "TRUE", // us ascii encoding + "ISO-8859-1", "TRUE", // iso encoding + "UTF-8", "TRUE", // 8 bit utf encoding + "UTF-16BE", "FALSE", // 16 bit big endian utf + "UTF-16LE", "FALSE", // 16 bit little endian utf + "UTF-16", "FALSE" // 16 bit, order specified by byte order mark + + }; + + for (int i = 0; i < sEncoding.length; i = i + 2) { + String encURL = new String(baURL, sEncoding[i]); + System.out.println("ENC[" + sEncoding[i] + "]"); + + if (sEncoding[i + 1].equals("TRUE")) { + loadURL(m_xLoader, RESULT_VALID_DOC, encURL, "_blank", 0, + lProps); + } else { + //with cws_loadenv01 changed to IllegalArgumentException + loadURL(m_xLoader, RESULT_ILLEGALARGUMENTEXCEPTION, encURL, "_blank", 0, + lProps); + } + } + } + + /** TODO document me + */ + @Test public void checkStreamLoading() throws Exception + { + PropertyValue[] lProps = new PropertyValue[2]; + + lProps[0] = new PropertyValue(); + lProps[0].Name = "Hidden"; + lProps[0].Value = Boolean.TRUE; + + lProps[1] = new PropertyValue(); + lProps[1].Name = "InputStream"; + + Iterator aSnapshot = m_lTestFiles.iterator(); + while (aSnapshot.hasNext()) + { + File aFile = new File(m_sTestDocPath, aSnapshot.next()); + String sURL = URLHelper.getFileURLFromSystemPath(aFile); + + XInputStream xStream = m_xStreamProvider.openFileRead(sURL); + lProps[1].Value = xStream; + + // check different version of "private:stream" URL! + loadURL(m_xLoader, RESULT_VALID_DOC, "private:stream" , "_blank", 0, lProps); + } + } + + /** + * Loads one URL with the given parameters using the method + * loadComponentFromURL(). Further it's possible to specify, which result is + * required and we check internally if it was reached. Logging of errors + * and success stories is done inside this method too. Of course we catch + * all possible exceptions and try to leave the office without any forgotten + * but opened documents. + */ + private void loadURL(XComponentLoader xLoader, int nRequiredResult, + String sURL, String sTarget, int nFlags, + PropertyValue[] lProps) { + int nResult = RESULT_EMPTY_DOC; + XComponent xDoc = null; + + try { + xDoc = xLoader.loadComponentFromURL(sURL, sTarget, nFlags, + lProps); + + if (xDoc != null) { + nResult = RESULT_VALID_DOC; + } else { + nResult = RESULT_EMPTY_DOC; + } + } catch (com.sun.star.lang.IllegalArgumentException exArgument) { + nResult = RESULT_ILLEGALARGUMENTEXCEPTION; + } catch (com.sun.star.io.IOException exIO) { + nResult = RESULT_IOEXCEPTION; + } catch (com.sun.star.uno.RuntimeException exRuntime) { + nResult = RESULT_RUNTIMEEXCEPTION; + } catch (Exception e) { + nResult = RESULT_EXCEPTION; + } + + try { + if (xDoc != null) { + xDoc.dispose(); + xDoc = null; + } + } catch (com.sun.star.uno.RuntimeException exClosing) { + System.out.println("exception during disposing of a document found!" + + " Doesn't influence test - but should be checked."); + } + + String sMessage = "URL[\"" + sURL + "\"]"; + + if (nResult == nRequiredResult) { + System.out.println(sMessage + " expected result [" + + convertResult2String(nResult) + "] "); + } else { + fail(sMessage + " unexpected result [" + + convertResult2String(nResult) + "] " + + "\nrequired was [" + + convertResult2String(nRequiredResult) + "]" + + "\nwe got [" + convertResult2String(nResult) + "]" + ); + } + } + + /** + * it match the int result value to a string, which can be used for logging + */ + private static String convertResult2String(int nResult) { + switch (nResult) { + case RESULT_VALID_DOC: + return "VALID_DOC"; + + case RESULT_EMPTY_DOC: + return "EMPTY_DOC"; + + case RESULT_ILLEGALARGUMENTEXCEPTION: + return "ILLEGALARGUMENTEXCEPTION"; + + case RESULT_IOEXCEPTION: + return "IOEXCEPTION"; + + case RESULT_RUNTIMEEXCEPTION: + return "RUNTIMEEXCEPTION"; + + case RESULT_EXCEPTION: + return "ALLOTHEREXCEPTION"; + } + + return "unknown!"; + } + + 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()"); + connection.tearDown(); + } + private static final OfficeConnection connection = new OfficeConnection(); + +} diff --git a/framework/qa/complex/loadAllDocuments/CheckXComponentLoader.props b/framework/qa/complex/loadAllDocuments/CheckXComponentLoader.props new file mode 100644 index 0000000000..92b56bef99 --- /dev/null +++ b/framework/qa/complex/loadAllDocuments/CheckXComponentLoader.props @@ -0,0 +1 @@ +ThreadTimeOut=600000 diff --git a/framework/qa/complex/loadAllDocuments/InteractionHandler.java b/framework/qa/complex/loadAllDocuments/InteractionHandler.java new file mode 100644 index 0000000000..24cf6a151f --- /dev/null +++ b/framework/qa/complex/loadAllDocuments/InteractionHandler.java @@ -0,0 +1,122 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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.loadAllDocuments; + +import com.sun.star.task.XInteractionHandler; +import com.sun.star.uno.AnyConverter; + + +/** + * Implements a simple interaction handler, + * which can abort all incoming interactions only ... but make it possible to + * log it. So it can be used for debug and test purposes. + */ +public class InteractionHandler implements XInteractionHandler +{ + + + /** + * @const RETRY_COUNT it defines the max count of + * retrying of an interaction + */ + private static final int RETRY_COUNT = 3; + + + + /** + * count using of RETRY continuations + */ + private int m_nTry ; + /** true if the interaction handler was used */ + private boolean m_bWasUsed ; + + + /** + * ctor + * It's initialize an object of this class with default values + * and set the protocol stack. So the outside code can check + * if this handler was used or not. + */ + public InteractionHandler() + { + m_nTry = 0 ; + m_bWasUsed = false; + } + + /** + * Called to start the interaction, because the outside code wish to solve + * a detected problem or to inform the user about something. + * We save the information here and can handle two well known continuations + * only. + * [abort and retry]. + * + * @param xRequest + * describe the interaction + */ + public void handle(com.sun.star.task.XInteractionRequest xRequest) + { + m_bWasUsed = true; + + // analyze the possible continuations. + // We can abort all incoming interactions only. + // But additional we can try to continue it several times too. + // Of course after e.g. three loops we have to stop and abort it. + com.sun.star.task.XInteractionContinuation[] lContinuations = xRequest.getContinuations(); + + com.sun.star.task.XInteractionAbort xAbort = null; + com.sun.star.task.XInteractionRetry xRetry = null; + com.sun.star.uno.Type xAbortType = new com.sun.star.uno.Type(com.sun.star.task.XInteractionAbort.class); + com.sun.star.uno.Type xRetryType = new com.sun.star.uno.Type(com.sun.star.task.XInteractionRetry.class); + + for (int i=0; i aListOfWorkingProperty; + + /** + * Initialize before the tests start: this has to be done only once. + * This methods sets the 'aPathSettings' and 'xPropertyInfoOfPathSettings' variables. + */ + @Before + public void before() throws Exception + { + try + { + aPathSettings = thePathSettings.get(connection.getComponentContext()); + assertNotNull("Can't instantiate com.sun.star.util.thePathSettings.", aPathSettings); + util.dbg.getSuppServices(aPathSettings); + final XPropertySet xPropSet_of_PathSettings = UnoRuntime.queryInterface(XPropertySet.class, aPathSettings); + + xPropertyInfoOfPathSettings = xPropSet_of_PathSettings.getPropertySetInfo().getProperties(); + aPathSettingNames = new String[xPropertyInfoOfPathSettings.length]; + aPathSettingValues = new String[xPropertyInfoOfPathSettings.length]; + + aListOfWorkingProperty = new ArrayList(); + + // get initial values and create new ones + for (int i = 0; i < xPropertyInfoOfPathSettings.length; i++) + { + final String sName = xPropertyInfoOfPathSettings[i].Name; + aPathSettingNames[i] = sName; + Object o = xPropSet_of_PathSettings.getPropertyValue(sName); + + String sValue = convertToString(o); + aPathSettingValues[i] = sValue; + aListOfWorkingProperty.add(xPropertyInfoOfPathSettings[i]); + } + } + catch (com.sun.star.uno.Exception e) + { + System.out.println(e.getClass().getName()); + System.out.println("Message: " + e.getMessage()); + } + } + + private String convertToString(Object o) + { + String sValue = ""; + try + { + if (AnyConverter.isString(o)) + { + sValue = AnyConverter.toString(o); + } + else if (AnyConverter.isArray(o)) + { + Object oValueList = AnyConverter.toArray(o); + String[] aValueList = (String[]) oValueList; + String sValues = ""; + for (int j = 0; j < aValueList.length; j++) + { + if (sValues.length() > 0) + { + sValues += ";"; + } + sValues += aValueList[j]; + } + sValue = sValues; + } + else + { + System.out.println("Can't convert Object to String"); + } + } + catch (com.sun.star.lang.IllegalArgumentException e) + { + /* ignore */ + } + return sValue; + } + + /** + * Simple existence test, if this fails, the Lists must update + */ + @Test + public void checkInternalListConsistence() + { + // check if all Properties are in the internal test list + for (int i = 0; i < xPropertyInfoOfPathSettings.length; i++) + { + final String sName = xPropertyInfoOfPathSettings[i].Name; + boolean bOccur = checkIfNameExistsInList(sName, availablePropNames, availablePropNameExtensions); + assertTrue("TEST IS WRONG, Name:='" + sName + "' doesn't exist in internal Test list.", bOccur); + } + + // check if all properties in the internal list also exist in real life + for (int i = 0; i < availablePropNames.length; i++) + { + final String aListName = availablePropNames[i]; + for (int j = 0; j < availablePropNameExtensions.length; j++) + { + final String aSubListName = availablePropNameExtensions[j]; + final String aName = aListName + aSubListName; + boolean bOccur = checkIfNameExistsInList(aName, aPathSettingNames, new String[] + { + "" + } /* list must not empty! */); + assertTrue("TEST IS WRONG, Name:='" + aName + "' from the internal test list do not occur in real life path settings.", bOccur); + } + } + } + + /** + * Simple O(n^n) check if a given String (_sNameMustOccur) exist in the given list(+SubList) values. + * @param _sNameMustOccur + * @param _aList + * @param _aSubList + * @return true, if name occur + */ + private boolean checkIfNameExistsInList(String _sNameMustOccur, String[] _aList, String[] _aSubList) + { + for (int i = 0; i < _aList.length; i++) + { + final String aListName = _aList[i]; + for (int j = 0; j < _aSubList.length; j++) + { + final String aSubListName = _aSubList[j]; + final String aName = aListName + aSubListName; + if (aName.equals(_sNameMustOccur)) + { + return true; + } + } + } + return false; + } + + /** + * Shows the path settings + */ + @Test + public void showPathSettings() throws UnknownPropertyException, WrappedTargetException + { + System.out.println("\n---- All properties ----"); + final XPropertySet xPropSet_of_PathSettings = UnoRuntime.queryInterface(XPropertySet.class, aPathSettings); + + for (int i = 0; i < aListOfWorkingProperty.size(); i++) + { + final String sName = aListOfWorkingProperty.get(i).Name; + System.out.print(sName); + Object o = xPropSet_of_PathSettings.getPropertyValue(sName); + + try + { + AnyConverter.toString(o); + } + catch (com.sun.star.lang.IllegalArgumentException e) + { + } + System.out.println(); + } + System.out.println("---- Finish showing properties ----\n"); + } + + private boolean checkPaths(Object _o, Object _o2) + { + String sLeftPath = ""; + String sRightPath = ""; + if (AnyConverter.isArray(_o)) + { + try + { + Object oValues = AnyConverter.toArray(_o); + sLeftPath = convertToString(oValues); + } + catch (com.sun.star.lang.IllegalArgumentException e) + { + } + } + else if (AnyConverter.isString(_o)) + { + try + { + sLeftPath = AnyConverter.toString(_o); + } + catch (com.sun.star.lang.IllegalArgumentException e) + { + } + } + + if (AnyConverter.isArray(_o2)) + { + try + { + Object oValues = AnyConverter.toArray(_o2); + sRightPath = convertToString(oValues); + } + catch (com.sun.star.lang.IllegalArgumentException e) + { + } + } + else if (AnyConverter.isString(_o2)) + { + try + { + sRightPath = AnyConverter.toString(_o2); + } + catch (com.sun.star.lang.IllegalArgumentException e) + { + } + } + return checkPaths(sLeftPath, sRightPath); + } + + /** + * Check 2 given paths if the _aOtherPath exists in _aPath, _aPath could be a list separated by ';' + * @param _aPath + * @param _aOtherPath + * @return true, if _aOtherPath found + */ + private boolean checkPaths(String _aPath, String _aOtherPath) + { + if (_aOtherPath.contains(";")) + { + StringTokenizer aToken = new StringTokenizer(_aOtherPath, ";"); + int nCount = 0; + int nFound = 0; + while (aToken.hasMoreElements()) + { + String sPath = (String)aToken.nextElement(); + nCount ++; + if (checkPaths(_aPath, sPath)) + { + nFound++; + } + } + if (nFound == nCount) + { + return true; + } + } + else if(_aPath.contains(";")) + { + StringTokenizer aToken = new StringTokenizer(_aPath, ";"); + while (aToken.hasMoreElements()) + { + String sToken = (String)aToken.nextElement(); + if (sToken.equals(_aOtherPath)) + { + return true; + } + } + return false; + } + else if (_aPath.equals(_aOtherPath)) + { + return true; + } + return false; + } + + /** + * This tests the XFastPropertySet interface implementation. + */ + @Test + public void checkXFastPropertySet() + { + System.out.println("---- Testing the XFastPropertySet interface ----"); + + + // do for all properties + // xPropertyInfoOfPathSettings.length + for (int i = 0; i < aListOfWorkingProperty.size(); i++) + { + final Property property = aListOfWorkingProperty.get(i); // xPropertyInfoOfPathSettings[i]; + String name = property.Name; + // get property name and initial value + System.out.println("Test property with name: " + name); + boolean bResult; + if (name.endsWith("_writable")) + { + bResult = checkStringProperty(property); + } + else if (name.endsWith("_user")) + { + bResult = checkStringListProperty(property); + } + else if (name.endsWith("_internal")) + { + bResult = checkStringListProperty(property); + } + else + { + // old path settings + bResult = checkStringProperty(property); + } + System.out.print(" Test of property " + name + " finished"); + if (bResult) + { + System.out.println(" [ok]"); + } + else + { + System.out.println(" [FAILED]"); + } + System.out.println(); + } + System.out.println("---- Test of XFastPropertySet finished ----\n"); + } + + private boolean checkStringListProperty(Property property) + { + // creating instances + boolean bResult = true; + XFastPropertySet xFPS = UnoRuntime.queryInterface(XFastPropertySet.class, aPathSettings); + + String name = property.Name; + int handle = property.Handle; + + Object oValue; + try + { + oValue = xFPS.getFastPropertyValue(handle); + } + catch (UnknownPropertyException ex) + { + return false; + } + catch (WrappedTargetException ex) + { + return false; + } + + if (!AnyConverter.isArray(oValue)) + { + System.out.println(" Internal error, type wrong. PathSetting property with name:" + name + " should be an array."); + return false; + } + + String val; + try + { + Object oValues = AnyConverter.toArray(oValue); + + + final String[] aValues = (String[])oValues; + + // aNewValues contains a deep copy of aValues + String[] aNewValues = new String[aValues.length]; + System.arraycopy(aValues, 0, aNewValues, 0, aValues.length); + if (aValues.length > 0) + { + val = aValues[0]; + } + else + { + val = null; + aNewValues = new String[1]; // create a String list + } + System.out.println(" Property has initial value: '" + val + "'"); + + // set to a new correct value + String newVal = changeToCorrectValue(val); + assertFalse("newVal must not equal val.", newVal.equals(val)); + + System.out.println(" Try to change to a correct value '" + newVal + "'"); + aNewValues[0] = newVal; + + try + { + try + { + xFPS.setFastPropertyValue(handle, aNewValues); + } + catch (com.sun.star.lang.WrappedTargetException e) + { + System.out.println(" FAIL: setFastPropertyValue(handle:=" + handle + ", name:='" + name + "')" + e.getMessage()); + bResult = false; + } + + // Property_internal can't change we will not arrive beyond this line + + // check the change + Object oObj = xFPS.getFastPropertyValue(handle); + if (!checkPaths(oObj, aNewValues)) + { + System.out.println(" FAIL: Did not change value on property " + name + "."); + bResult = false; + } + + // set back to initial setting + System.out.println(" Try to check"); + try + { + xFPS.setFastPropertyValue(handle, oValue); + } + catch (com.sun.star.beans.PropertyVetoException e) + { + // should not occur + System.out.println(" FAIL: PropertyVetoException caught: " + e.getMessage()); + bResult = false; + } + } + catch (com.sun.star.beans.PropertyVetoException e) + { + if (!name.endsWith("_internal")) + { + // should not occur + System.out.println(" FAIL: PropertyVetoException caught: " + e.getMessage()); + bResult = false; + } + else + { + System.out.println(" OK: PropertyVetoException caught: " + e.getMessage() + " it seems not allowed to change internal values."); + } + } + + // check if changed + Object checkVal3 = xFPS.getFastPropertyValue(handle); + if (!checkPaths(checkVal3, oValues)) + { + System.out.println(" FAIL: Can't change value back to original on property " + name); + bResult = false; + } + } + catch (com.sun.star.uno.Exception e) + { + System.out.println(" FAIL: getFastPropertyValue(handle:=" + handle + ", name:='" + name + "')" + e.getMessage()); + bResult = false; + } + return bResult; + } + + private boolean checkStringProperty(Property property) + { + boolean bResult = true; + XFastPropertySet xFPS = UnoRuntime.queryInterface(XFastPropertySet.class, aPathSettings); + String name = property.Name; + int handle = property.Handle; + Object oValue; + try + { + oValue = xFPS.getFastPropertyValue(handle); + } + catch (UnknownPropertyException ex) + { + return false; + } + catch (WrappedTargetException ex) + { + return false; + } + + + try + { + String val = ""; + val = AnyConverter.toString(oValue); + System.out.println(" Property has initial value: '" + val + "'"); + + // set to a new correct value + String newVal = changeToCorrectValue(val); + System.out.println(" Try to change to a correct value '" + newVal + "'"); + xFPS.setFastPropertyValue(handle, newVal); + + // check the change + String checkVal = (String) xFPS.getFastPropertyValue(handle); + if (!checkPaths(checkVal, newVal)) + { + System.out.println(" FAIL: Did not change value on property " + name + "."); + bResult = false; + } + /* + * Change the given String to an incorrect path URL. + */ + newVal = "fileblablabla"; + System.out.println(" Try to change to incorrect value '" + newVal + "'"); + try + { + xFPS.setFastPropertyValue(handle, newVal); + } + catch (com.sun.star.lang.IllegalArgumentException e) + { + System.out.println(" Correctly thrown Exception caught."); + } + + // check if changed + String checkVal2 = (String) xFPS.getFastPropertyValue(handle); + if (!checkPaths(checkVal2, checkVal)) + { + System.out.println(" FAIL: Value did change on property " + name + " though it should not have."); + bResult = false; + } + else + { + System.out.println(" OK: Incorrect value was not set."); + } + // set back to initial setting + System.out.println(" Set back to initial value."); + try + { + xFPS.setFastPropertyValue(handle, val); + } + catch (com.sun.star.lang.IllegalArgumentException e) + { + System.out.println(" IllegalArgumentException caught: " + e.getMessage()); + bResult = false; + } + // check if changed + String checkVal3 = (String) xFPS.getFastPropertyValue(handle); + if (!checkVal3.equals(val)) + { + if (!checkPaths(checkVal3, val)) + { + System.out.println(" FAIL: Can't change value back to original on property " + name); + System.out.println(" Value is: " + checkVal3); + + bResult = false; + } + else + { + System.out.println(" OK: the pathsettings contains the original path."); + System.out.println(" Value is: " + checkVal3); + System.out.println(" Value should be: " + val); + } + } + else + { + System.out.println(" OK: Change value back to original on property " + name); + } + } + catch (com.sun.star.uno.Exception e) + { + System.out.println(" FAIL: getFastPropertyValue(handle:=" + handle + ", name:='" + name + "')" + e.getMessage()); + bResult = false; + } + return bResult; + } + + + // ____________________ + /** + * This tests the XPropertySet interface implementation. + */ + + /** + * Change the given String to a correct path URL. + * @return The changed path URL. + */ + private String changeToCorrectValue(String path) + { + // the simplest possibility + if (path == null || path.equals("")) + { + String sTempDir = System.getProperty("java.io.tmpdir"); + sTempDir = util.utils.getFullURL(sTempDir); + return sTempDir; // "file:///tmp"; + } + return graphical.FileHelper.appendPath(path, "tmp"); + } + + /** + * Listener implementation which sets a flag when + * listener was called. + */ + public class MyChangeListener implements XPropertiesChangeListener, + XPropertyChangeListener, + XVetoableChangeListener + { + + private boolean propChanged = false; + private boolean propertiesChanged = false; + private boolean vetoableChanged = false; + + public void propertiesChange( + com.sun.star.beans.PropertyChangeEvent[] e) + { + propertiesChanged = true; + } + + public void vetoableChange(com.sun.star.beans.PropertyChangeEvent pE) + throws com.sun.star.beans.PropertyVetoException + { + vetoableChanged = true; + } + + public void propertyChange(com.sun.star.beans.PropertyChangeEvent pE) + { + propChanged = true; + } + + public void disposing(com.sun.star.lang.EventObject eventObject) + { + } + + public void resetListener() + { + propChanged = false; + propertiesChanged = false; + vetoableChanged = false; + } + + public boolean changePropertyEventFired() + { + return propChanged; + } + + public boolean changePropertiesEventFired() + { + return propertiesChanged; + } + + public boolean vetoableChangeEventFired() + { + return vetoableChanged; + } + } + + // 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()"); + connection.tearDown(); + } + private static final OfficeConnection connection = new OfficeConnection(); +} diff --git a/framework/qa/complex/path_substitution/PathSubstitutionTest.java b/framework/qa/complex/path_substitution/PathSubstitutionTest.java new file mode 100644 index 0000000000..96d59287c3 --- /dev/null +++ b/framework/qa/complex/path_substitution/PathSubstitutionTest.java @@ -0,0 +1,294 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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.path_substitution; + +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.util.ArrayList; + +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.lang.XMultiServiceFactory; +import com.sun.star.uno.UnoRuntime; +import com.sun.star.util.XStringSubstitution; + +/** + * + */ +public class PathSubstitutionTest +{ + + // all substitution variables + private VariableContainer substVars = null; + + /** + * Create an array with all substitution variables + */ + @Before public void initialize() + { + substVars = new VariableContainer(); + substVars.add("$(prog)", true, true); + substVars.add("$(inst)", true, true); + substVars.add("$(user)", true, true); + substVars.add("$(work)", true, true); + substVars.add("$(home)", true, true); + substVars.add("$(temp)", true, true); + substVars.add("$(lang)", false, false); + substVars.add("$(username)", false, false); + substVars.add("$(langid)", false, false); + substVars.add("$(vlang)", false, false); + // path won't resubstitute + substVars.add("$(path)", true, false); + } + + /** + * One actual test: as the method 'getTestMethodNames()' tells. + */ + @Test public void checkXStringSubstitution() + { + XMultiServiceFactory xMSF = getMSF(); + System.out.println("---- Testing the XStringSubstitution interface ----"); + System.out.println("Create instance of test object.\n"); + XStringSubstitution oObj = null; + try + { + Object x = xMSF.createInstance( + "com.sun.star.util.PathSubstitution"); + oObj = UnoRuntime.queryInterface(XStringSubstitution.class, x); + if (oObj == null) + { + throw new com.sun.star.uno.Exception(); + } + } + catch (com.sun.star.uno.Exception e) + { + System.out.println(e.getClass().getName()); + System.out.println("Message: " + e.getMessage()); + fail("Could not create an instance of the test object."); + return; + } + + for (int i = 0; i < substVars.size(); i++) + { + String var = substVars.getVariable(i); + System.out.println("Testing var '" + var + "'"); + try + { + String substVal = oObj.getSubstituteVariableValue(var); + System.out.println("\tvalue '" + substVal + "'"); + substVars.putValue(i, substVal); + + // simple check: let path in a string replace + String substString = var + "/additional/path"; + + System.out.println("Substitute '" + substString + "'"); + String newValue = oObj.substituteVariables(substString, true); + System.out.println("Return value '" + newValue + "'"); + // 2do: better check for correct substitution + assertTrue("Did not substitute '" + + substString + "' to '" + newValue + + "' correctly:", newValue.startsWith(substVal)); + + // simple check part two: + //make substitution backwards if possible + if (substVars.canReSubstitute(i)) + { + substString = substVal + "/additional/path"; + + System.out.println("Substitute backwards '" + substString + "'"); + newValue = oObj.reSubstituteVariables(substString); + System.out.println("Return value '" + newValue + "'"); + // 2do: better check for correct substitution + assertTrue("Did not reSubstitute '" + + substString + "' to '" + newValue + + "' correctly:", checkResubstitute(newValue, var)); + } + + // simple check part three: look if replace + //in middle of text works + substString = "file:///starting/" + var + "/path"; + + String sCanSubstAllPos; + if (substVars.onlySubstituteAtBegin(i)) + sCanSubstAllPos = "NO"; + else + sCanSubstAllPos = "YES"; + System.out.println("Variable can substitute within string: "+sCanSubstAllPos); + System.out.println("Substitute '" + substString + "'"); + newValue = oObj.substituteVariables(substString, false); + System.out.println("Return value '" + newValue + "'"); + boolean erg = true; + if (substVars.onlySubstituteAtBegin(i)) + { + // in this case it should not have worked + erg = newValue.indexOf(substVal) == -1; + } + else + { + erg = newValue.indexOf(substVal) != -1; + } + assertTrue("Did not substitute '" + + substString + "' to '" + newValue + + "' correctly:", erg); + + } + catch (com.sun.star.uno.Exception e) + { + System.out.println(e.getClass().getName()); + System.out.println("Message: " + e.getMessage()); + fail("Could not create an instance of the test object."); + return; + } + System.out.println("Finish testing '" + var + "'\n"); + } + + // check of greedy resubstitution + String prog = "$(prog)"; + String inst = "$(inst)"; + String instPth = substVars.getValue(inst); + String progPth = substVars.getValue(prog); + + if (progPth.startsWith(instPth) && instPth.startsWith(progPth)) + { + System.out.println("Greedy ReSubstitute"); + String substString = progPth + "/additional/path"; + String newVal = oObj.reSubstituteVariables(substString); + System.out.println("String '" + substString + + "' should be resubstituted with"); + System.out.println("Variable '" + prog + "' instead of Variable '" + + inst + "'"); + assertTrue("Did not reSubstitute '" + substString + + "' to '" + newVal + "' correctly:", + newVal.startsWith(prog)); + } + + System.out.println( + "---- Finish testing the XStringSubstitution interface ----"); + } + + /** + * test the resubstitution + * @return true, if resubstitution is correct. + */ + private boolean checkResubstitute(String subst, String original) + { + // simple: subst starts with original + if (subst.startsWith(original)) + { + return true; + } + else + { // hard: been resubstituted with a different variable. + for (int i = 0; i < substVars.size(); i++) + { + String var = substVars.getVariable(i); + if (subst.startsWith(var) && original.startsWith(original)) + { + return true; + } + } + } + return false; + } + + /** + * Class for containing the substitution variables with their + * values and some information. + */ + private static class VariableContainer + { + + public ArrayList varName; + public ArrayList varValue; + public ArrayList substAtBegin; + public ArrayList resubst; + + public VariableContainer() + { + varName = new ArrayList(); + varValue = new ArrayList(); + substAtBegin = new ArrayList(); + resubst = new ArrayList(); + } + + public void add(String var, boolean onlySubstAtBegin, + boolean canResubst) + { + varName.add(var); + this.substAtBegin.add(Boolean.valueOf(onlySubstAtBegin)); + this.resubst.add(Boolean.valueOf(canResubst)); + } + + public void putValue(int i, String val) + { + varValue.add(i, val); + } + + public int size() + { + return varName.size(); + } + + public String getVariable(int i) + { + return varName.get(i); + } + + public String getValue(String var) + { + return varValue.get(varName.indexOf(var)); + } + + public boolean onlySubstituteAtBegin(int i) + { + return substAtBegin.get(i).booleanValue(); + } + + public boolean canReSubstitute(int i) + { + return resubst.get(i).booleanValue(); + } + } + + 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()"); + connection.tearDown(); + } + private static final OfficeConnection connection = new OfficeConnection(); +} diff --git a/framework/qa/cppunit/data/double-loading.odt b/framework/qa/cppunit/data/double-loading.odt new file mode 100644 index 0000000000..ce990fc5c1 Binary files /dev/null and b/framework/qa/cppunit/data/double-loading.odt differ diff --git a/framework/qa/cppunit/data/empty.fodp b/framework/qa/cppunit/data/empty.fodp new file mode 100644 index 0000000000..3c2a4cf2cd --- /dev/null +++ b/framework/qa/cppunit/data/empty.fodp @@ -0,0 +1,2 @@ + + diff --git a/framework/qa/cppunit/dispatchtest.cxx b/framework/qa/cppunit/dispatchtest.cxx new file mode 100644 index 0000000000..7ba29ef9cb --- /dev/null +++ b/framework/qa/cppunit/dispatchtest.cxx @@ -0,0 +1,204 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.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 +#include + +#include +#include +#include + +#include +#include + +using namespace ::com::sun::star; + +namespace +{ +/// Sample interception implementation that asserts getInterceptedURLs() and queryDispatch() is in sync. +class MyInterceptor + : public cppu::WeakImplHelper +{ + std::mutex maMutex; + uno::Reference m_xMaster; + uno::Reference m_xSlave; + uno::Sequence m_aDisabledCommands; + int m_nExpected; + int m_nUnexpected; + +public: + MyInterceptor(); + + /// Number of queryDispatch() calls that operate on a command advertised by getInterceptedURLs(). + int getExpected(); + /// Number of queryDispatch() calls that operate on a command not advertised by getInterceptedURLs(). + int getUnexpected(); + + // frame::XInterceptorInfo + virtual uno::Sequence SAL_CALL getInterceptedURLs() override; + + // frame::XDispatchProviderInterceptor + virtual void SAL_CALL setMasterDispatchProvider( + const uno::Reference& xNewSupplier) override; + virtual uno::Reference SAL_CALL getMasterDispatchProvider() override; + virtual void SAL_CALL + setSlaveDispatchProvider(const uno::Reference& xNewSupplier) override; + virtual uno::Reference SAL_CALL getSlaveDispatchProvider() override; + + // frame::XDispatchProvider + virtual uno::Sequence> SAL_CALL + queryDispatches(const uno::Sequence& rRequests) override; + virtual uno::Reference + SAL_CALL queryDispatch(const util::URL& rURL, const OUString& rTargetFrameName, + sal_Int32 SearchFlags) override; +}; + +MyInterceptor::MyInterceptor() + : m_aDisabledCommands{ ".uno:Bold" } + , m_nExpected(0) + , m_nUnexpected(0) +{ +} + +int MyInterceptor::getExpected() +{ + std::unique_lock aGuard(maMutex); + int nRet = m_nExpected; + m_nExpected = 0; + return nRet; +} + +int MyInterceptor::getUnexpected() +{ + std::unique_lock aGuard(maMutex); + int nRet = m_nUnexpected; + m_nUnexpected = 0; + return nRet; +} + +uno::Sequence MyInterceptor::getInterceptedURLs() { return m_aDisabledCommands; } + +void MyInterceptor::setMasterDispatchProvider( + const uno::Reference& xNewSupplier) +{ + std::unique_lock aGuard(maMutex); + m_xMaster = xNewSupplier; +} + +uno::Reference MyInterceptor::getMasterDispatchProvider() +{ + std::unique_lock aGuard(maMutex); + return m_xMaster; +} + +void MyInterceptor::setSlaveDispatchProvider( + const uno::Reference& xNewSupplier) +{ + std::unique_lock aGuard(maMutex); + m_xSlave = xNewSupplier; +} + +uno::Reference MyInterceptor::getSlaveDispatchProvider() +{ + std::unique_lock aGuard(maMutex); + return m_xSlave; +} + +uno::Sequence> +MyInterceptor::queryDispatches(const uno::Sequence& rRequests) +{ + uno::Sequence> aResult(rRequests.getLength()); + auto aResultRange = asNonConstRange(aResult); + + for (sal_Int32 i = 0; i < rRequests.getLength(); ++i) + { + aResultRange[i] = queryDispatch(rRequests[i].FeatureURL, rRequests[i].FrameName, + rRequests[i].SearchFlags); + } + + return aResult; +} + +uno::Reference MyInterceptor::queryDispatch(const util::URL& rURL, + const OUString& /*rTargetFrameName*/, + sal_Int32 /*SearchFlags*/) +{ + std::unique_lock aGuard(maMutex); + if (std::find(std::cbegin(m_aDisabledCommands), std::cend(m_aDisabledCommands), rURL.Complete) + != std::cend(m_aDisabledCommands)) + ++m_nExpected; + else + ++m_nUnexpected; + + return uno::Reference(); +} + +/// Tests how InterceptionHelper invokes a registered interceptor. +class DispatchTest : public UnoApiTest +{ +public: + DispatchTest() + : UnoApiTest("/framework/qa/cppunit/data/") + { + } +}; + +CPPUNIT_TEST_FIXTURE(DispatchTest, testInterception) +{ + mxComponent = loadFromDesktop("private:factory/swriter", "com.sun.star.text.TextDocument"); + uno::Reference xModel(mxComponent, uno::UNO_QUERY); + CPPUNIT_ASSERT(xModel.is()); + + uno::Reference xRegistration( + xModel->getCurrentController()->getFrame(), uno::UNO_QUERY); + CPPUNIT_ASSERT(xRegistration.is()); + + rtl::Reference pInterceptor(new MyInterceptor()); + xRegistration->registerDispatchProviderInterceptor(pInterceptor); + + dispatchCommand(mxComponent, ".uno:Bold", {}); + CPPUNIT_ASSERT_GREATER(0, pInterceptor->getExpected()); + CPPUNIT_ASSERT_EQUAL(0, pInterceptor->getUnexpected()); + dispatchCommand(mxComponent, ".uno:Italic", {}); + // This was 1: MyInterceptor::queryDispatch() was called for .uno:Italic. + CPPUNIT_ASSERT_EQUAL(0, pInterceptor->getUnexpected()); +} + +CPPUNIT_TEST_FIXTURE(DispatchTest, testSfxOfficeDispatchDispose) +{ + // this test doesn't work with a new document because of aURL.Main check in SfxBaseController::dispatch() + loadFromFile(u"empty.fodp"); + uno::Reference xModel(mxComponent, uno::UNO_QUERY); + CPPUNIT_ASSERT(xModel.is()); + uno::Reference xController(xModel->getCurrentController()); + CPPUNIT_ASSERT(xController.is()); + uno::Reference xFrame(xController->getFrame(), uno::UNO_QUERY); + CPPUNIT_ASSERT(xFrame.is()); + + uno::Reference xParser(util::URLTransformer::create(mxComponentContext)); + util::URL url; + url.Complete = xModel->getURL() + "#dummy"; + xParser->parseStrict(url); + + uno::Reference xDisp(xFrame->queryDispatch(url, "", 0)); + CPPUNIT_ASSERT(xDisp.is()); + + mxComponent->dispose(); + + util::URL urlSlot; + urlSlot.Complete = "slot:5598"; + xParser->parseStrict(urlSlot); + // crashed with UAF + xDisp->dispatch(urlSlot, {}); +} +} + +CPPUNIT_PLUGIN_IMPLEMENT(); + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/qa/cppunit/loadenv.cxx b/framework/qa/cppunit/loadenv.cxx new file mode 100644 index 0000000000..c9aa2789d9 --- /dev/null +++ b/framework/qa/cppunit/loadenv.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/. + */ + +#include + +#include +#include + +#include +#include +#include + +using namespace ::com::sun::star; + +namespace +{ +/// Covers framework/source/loadenv/ fixes. +class Test : public UnoApiTest +{ +public: + Test() + : UnoApiTest("/framework/qa/cppunit/data/") + { + } +}; + +class DocumentOpener +{ +public: + DECL_STATIC_LINK(DocumentOpener, OpenDocument, void*, void); +}; + +IMPL_STATIC_LINK(DocumentOpener, OpenDocument, void*, pArg, void) +{ + CPPUNIT_ASSERT(pArg); + auto pURL = static_cast(pArg); + uno::Reference xComponentContext + = comphelper::getProcessComponentContext(); + uno::Reference xDesktop = frame::Desktop::create(xComponentContext); + xDesktop->loadComponentFromURL(*pURL, "_default", 0, {}); + delete pURL; +} + +CPPUNIT_TEST_FIXTURE(Test, testDoubleLoading) +{ + // Try to load the same document twice. This is similar to trying to execute the soffice process + // twice: in that case the 2nd instance forwards to the 1st instance and then uses the same code + // path. + for (int i = 0; i < 2; ++i) + { + auto pURL = std::make_unique(createFileURL(u"double-loading.odt")); + Application::PostUserEvent(LINK(nullptr, DocumentOpener, OpenDocument), pURL.release()); + } + Scheduler::ProcessEventsToIdle(); + + // Verify that the 2nd load didn't happen, since it's the same document. + uno::Reference xFrames = mxDesktop->getFrames(); + // Without the accompanying fix in place, this failed with: + // - Expected: 1 + // - Actual : 2 + // i.e. the document was loaded twice, into two separate frames/windows. + CPPUNIT_ASSERT_EQUAL(static_cast(1), xFrames->getCount()); + + // Close the document, now that we know we have a single one. + uno::Reference xFrame(xFrames->getByIndex(0), uno::UNO_QUERY); + xFrame->getController()->getModel()->dispose(); +} +} + +CPPUNIT_PLUGIN_IMPLEMENT(); + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/qa/cppunit/services.cxx b/framework/qa/cppunit/services.cxx new file mode 100644 index 0000000000..be6c0def70 --- /dev/null +++ b/framework/qa/cppunit/services.cxx @@ -0,0 +1,138 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.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 + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +using namespace ::com::sun::star; + +namespace +{ +/// Covers framework/source/services/ fixes. +class Test : public UnoApiTest +{ +public: + Test() + : UnoApiTest("/framework/qa/cppunit/data/") + { + } +}; + +/// Invokes XFrameImpl::loadComponentFromURL() on a thread. +class TestThread : public salhelper::Thread +{ + uno::Reference mxComponentLoader; + uno::Reference& mrComponent; + +public: + TestThread(const uno::Reference& xComponentLoader, + uno::Reference& rComponent); + void execute() override; +}; + +TestThread::TestThread(const uno::Reference& xComponentLoader, + uno::Reference& rComponent) + : salhelper::Thread("TestThread") + , mxComponentLoader(xComponentLoader) + , mrComponent(rComponent) +{ +} + +void TestThread::execute() +{ + sal_Int32 nSearchFlags = frame::FrameSearchFlag::AUTO; + uno::Sequence aArguments = { + comphelper::makePropertyValue("OnMainThread", true), + }; + // Note how this is invoking loadComponentFromURL() on a frame, not on the desktop, as usual. + mrComponent = mxComponentLoader->loadComponentFromURL("private:factory/swriter", "_self", + nSearchFlags, aArguments); +} + +CPPUNIT_TEST_FIXTURE(Test, testLoadComponentFromURL) +{ + // Without the accompanying fix in place, this test would have failed with: + // thread 1: comphelper::SolarMutex::doRelease end: m_nCount is 1 + // thread 2: vcl::SolarThreadExecutor::execute: before SolarMutexReleaser ctor + // thread 2: comphelper::SolarMutex::doRelease start: m_nCount is 1, bUnlockAll is 1 + // thread 2: comphelper::SolarMutex::doRelease: failed IsCurrentThread() check, will abort + // Notice how thread 2 attempts to release the solar mutex while thread 1 holds it. + + // Create a default window, so by the time the thread would post a user event, it doesn't need + // the solar mutex to process a SendMessageW() call on Windows. + ScopedVclPtrInstance xWindow(nullptr, WB_APP | WB_STDWORK); + // Variable is not used, it holds the default window. + (void)xWindow; + + rtl::Reference xThread; + { + // Start the thread that will load the component, but hold the solar mutex for now, so we + // can see if it blocks. + SolarMutexGuard guard; + uno::Reference xFrame = mxDesktop->findFrame("_blank", /*nSearchFlags=*/0); + uno::Reference xComponentLoader(xFrame, uno::UNO_QUERY); + xThread = new TestThread(xComponentLoader, mxComponent); + xThread->launch(); + // If loadComponentFromURL() doesn't lock the solar mutex, the test will abort here. + osl::Thread::wait(std::chrono::seconds(1)); + } + { + // Now release the solar mutex, so the thread can post the task on the main loop. + SolarMutexReleaser releaser; + osl::Thread::wait(std::chrono::seconds(1)); + } + { + // Spin the main loop. + SolarMutexGuard guard; + Scheduler::ProcessEventsToIdle(); + } + { + // Stop the thread. + SolarMutexReleaser releaser; + xThread->join(); + } +} + +CPPUNIT_TEST_FIXTURE(Test, testURLTransformer_parseSmart) +{ + // Without the accompanying fix in place, this test would have failed with + // "www.example.com:" treated as scheme, "/8080/foo/" as path, "bar?q=baz" + // as name, and "F" as fragment. + + css::util::URL aURL; + aURL.Complete = "www.example.com:8080/foo/bar?q=baz#F"; + css::uno::Reference xParser(css::util::URLTransformer::create(mxComponentContext)); + CPPUNIT_ASSERT(xParser->parseSmart(aURL, "http:")); + CPPUNIT_ASSERT_EQUAL(OUString("http://www.example.com:8080/foo/bar?q=baz#F"), aURL.Complete); + CPPUNIT_ASSERT_EQUAL(OUString("http://www.example.com:8080/foo/bar"), aURL.Main); + CPPUNIT_ASSERT_EQUAL(OUString("http://"), aURL.Protocol); + CPPUNIT_ASSERT(aURL.User.isEmpty()); + CPPUNIT_ASSERT(aURL.Password.isEmpty()); + CPPUNIT_ASSERT_EQUAL(OUString("www.example.com"), aURL.Server); + CPPUNIT_ASSERT_EQUAL(sal_Int16(8080), aURL.Port); + CPPUNIT_ASSERT_EQUAL(OUString("/foo/"), aURL.Path); + CPPUNIT_ASSERT_EQUAL(OUString("bar"), aURL.Name); + CPPUNIT_ASSERT_EQUAL(OUString("q=baz"), aURL.Arguments); + CPPUNIT_ASSERT_EQUAL(OUString("F"), aURL.Mark); +} +} + +CPPUNIT_PLUGIN_IMPLEMENT(); + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/qa/unoapi/framework.sce b/framework/qa/unoapi/framework.sce new file mode 100644 index 0000000000..ed1497d6df --- /dev/null +++ b/framework/qa/unoapi/framework.sce @@ -0,0 +1,51 @@ +# +# This file is part of the LibreOffice project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +# This file incorporates work covered by the following license notice: +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed +# with this work for additional information regarding copyright +# ownership. The ASF licenses this file to you under the Apache +# License, Version 2.0 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.apache.org/licenses/LICENSE-2.0 . +# +-o fwl.ContentHandlerFactory +-o fwl.FilterFactory +-o fwl.FrameLoaderFactory +-o fwl.SubstituteVariables +#i113245 -o fwl.TypeDetection +#i84346 -o fwl.PathSettings +-o fwk.DispatchRecorder +-o fwk.DispatchRecorderSupplier +-o fwk.FooterMenuController +-o fwk.StatusBarControllerFactory +-o fwk.ToolBarsMenuController +-o fwk.UICategoryDescription +#i84423 -o fwk.JobExecutor +#i84423 -o fwk.JobHandler +-o fwk.MailToDispatcher +-o fwk.ServiceHandler +-o fwk.URLTransformer +-o fwk.MacrosMenuController +#i112746 -o fwk.ModuleManager +-o fwk.UIElementFactoryManager +-o fwk.UICommandDescription +-o fwk.LayoutManager +-o fwk.UIConfigurationManager +-o fwk.MenuBarFactory +-o fwk.FontSizeMenuController +-o fwk.HeaderMenuController +-o fwk.ControlMenuController +-o fwk.FontMenuController +-o fwk.ModuleUIConfigurationManagerSupplier +-o fwk.ModuleUIConfigurationManager +#i84321 -o fwk.PopupMenuControllerFactory +#i88644 -o fwk.Frame +-o fwk.Desktop + diff --git a/framework/qa/unoapi/knownissues.xcl b/framework/qa/unoapi/knownissues.xcl new file mode 100644 index 0000000000..d1255e6115 --- /dev/null +++ b/framework/qa/unoapi/knownissues.xcl @@ -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 . +# + +### 112552 ### +fwk.JobHandler::com::sun::star::frame::XDispatchProvider +fwk.JobHandler::com::sun::star::lang::XInitialization + +### i8242 ### +fwk.Desktop::com::sun::star::lang::XComponent + +### i30570 ### +fwl.TypeDetection::com::sun::star::container::XNameReplace + +### i50165 ### +fwk.UIConfigurationManager::com::sun::star::ui::XUIConfigurationStorage +fwk.UIConfigurationManager::com::sun::star::ui::XUIConfigurationPersistence +fwk.UIConfigurationManager::com::sun::star::ui::XUIConfiguration +fwk.UIConfigurationManager::com::sun::star::ui::XUIConfigurationManager + +### i50578 ### +fwk.StatusBarControllerFactory::com::sun::star::lang::XMultiComponentFactory + +### i68698 ### +fwl.PathSettings::com::sun::star::beans::XMultiPropertySet + +### i74020 ### +fwl.FilterFactory::com::sun::star::lang::XMultiServiceFactory + +### i30570 ### +fwl.FilterFactory::com::sun::star::container::XNameReplace + +### i84321 ### +fwk.PopupMenuControllerFactory +# -> disabled in framework.sce + +### i84346 ### +fwl.PathSettings::com::sun::star::beans::XMultiPropertySet +#-> disabled in framework.sce + +### i84423 ### +fwk.JobExecutor +fwk.JobHandler +#-> disabled in framework.sce + +### i88635 ### +fwk.Desktop::com::sun::star::frame::XFramesSupplier + +### i87526 ### +fwk.Frame::com::sun::star::lang::XComponent + +### i87865 ### +fwk.Desktop::com::sun::star::frame::XDesktop + +### i88644 ### +fwk.Frame +#-> disabled in framework.sce + +### i90345 ### +fwk.URLTransformer::com::sun::star::util::XURLTransformer + +### i111180 ### +fwk.Desktop::com::sun::star::frame::XComponentLoader diff --git a/framework/qa/unoapi/testdocuments/Calc_Link.sxc b/framework/qa/unoapi/testdocuments/Calc_Link.sxc new file mode 100644 index 0000000000..086c04fe04 Binary files /dev/null and b/framework/qa/unoapi/testdocuments/Calc_Link.sxc differ diff --git a/framework/qa/unoapi/testdocuments/Writer_link.sxw b/framework/qa/unoapi/testdocuments/Writer_link.sxw new file mode 100644 index 0000000000..5e5c8bdcb8 Binary files /dev/null and b/framework/qa/unoapi/testdocuments/Writer_link.sxw differ diff --git a/framework/qa/unoapi/testdocuments/XTypeDetection.sxw b/framework/qa/unoapi/testdocuments/XTypeDetection.sxw new file mode 100644 index 0000000000..b241f4ed87 Binary files /dev/null and b/framework/qa/unoapi/testdocuments/XTypeDetection.sxw differ diff --git a/framework/qa/unoapi/testdocuments/delete.cfg b/framework/qa/unoapi/testdocuments/delete.cfg new file mode 100644 index 0000000000..31fef95a0d Binary files /dev/null and b/framework/qa/unoapi/testdocuments/delete.cfg differ diff --git a/framework/source/accelerators/acceleratorcache.cxx b/framework/source/accelerators/acceleratorcache.cxx new file mode 100644 index 0000000000..c0b819a2a9 --- /dev/null +++ b/framework/source/accelerators/acceleratorcache.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 + +#include + +#include + +namespace framework +{ +bool AcceleratorCache::hasKey(const css::awt::KeyEvent& aKey) const +{ + return (m_lKey2Commands.find(aKey) != m_lKey2Commands.end()); +} + +bool AcceleratorCache::hasCommand(const OUString& sCommand) const +{ + return (m_lCommand2Keys.find(sCommand) != m_lCommand2Keys.end()); +} + +AcceleratorCache::TKeyList AcceleratorCache::getAllKeys() const +{ + TKeyList lKeys; + lKeys.reserve(m_lKey2Commands.size()); + + for (auto const& key2Command : m_lKey2Commands) + { + lKeys.push_back(key2Command.first); + } + + return lKeys; +} + +void AcceleratorCache::setKeyCommandPair(const css::awt::KeyEvent& aKey, const OUString& sCommand) +{ + // register command for the specified key + m_lKey2Commands[aKey] = sCommand; + + // update optimized structure to bind multiple keys to one command + TKeyList& rKeyList = m_lCommand2Keys[sCommand]; + rKeyList.push_back(aKey); +} + +AcceleratorCache::TKeyList AcceleratorCache::getKeysByCommand(const OUString& sCommand) const +{ + TCommand2Keys::const_iterator pCommand = m_lCommand2Keys.find(sCommand); + if (pCommand == m_lCommand2Keys.end()) + throw css::container::NoSuchElementException(); + return pCommand->second; +} + +OUString AcceleratorCache::getCommandByKey(const css::awt::KeyEvent& aKey) const +{ + TKey2Commands::const_iterator pKey = m_lKey2Commands.find(aKey); + if (pKey == m_lKey2Commands.end()) + throw css::container::NoSuchElementException(); + return pKey->second; +} + +void AcceleratorCache::removeKey(const css::awt::KeyEvent& aKey) +{ + // check if key exists + TKey2Commands::const_iterator pKey = m_lKey2Commands.find(aKey); + if (pKey == m_lKey2Commands.end()) + return; + + // get its registered command + // Because we must know its place inside the optimized + // structure, which bind keys to commands, too! + OUString sCommand = pKey->second; + pKey = m_lKey2Commands.end(); // nobody should use an undefined value .-) + + // remove key from primary list + m_lKey2Commands.erase(aKey); + + // get keylist for that command + TCommand2Keys::iterator pCommand = m_lCommand2Keys.find(sCommand); + if (pCommand == m_lCommand2Keys.end()) + return; + TKeyList& lKeys = pCommand->second; + + // one or more keys assign + if (lKeys.size() == 1) + // remove key from optimized command list + m_lCommand2Keys.erase(sCommand); + else // only remove this key from the keylist + { + auto pKeys = ::std::find(lKeys.begin(), lKeys.end(), aKey); + + if (pKeys != lKeys.end()) + lKeys.erase(pKeys); + } +} + +void AcceleratorCache::removeCommand(const OUString& sCommand) +{ + const TKeyList& lKeys = getKeysByCommand(sCommand); + for (auto const& lKey : lKeys) + { + removeKey(lKey); + } +} + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/accelerators/acceleratorconfiguration.cxx b/framework/source/accelerators/acceleratorconfiguration.cxx new file mode 100644 index 0000000000..0dff986fa9 --- /dev/null +++ b/framework/source/accelerators/acceleratorconfiguration.cxx @@ -0,0 +1,1325 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +constexpr OUString PRESET_DEFAULT = u"default"_ustr; +constexpr OUString TARGET_CURRENT = u"current"_ustr; + +namespace framework +{ + constexpr OUString CFG_ENTRY_SECONDARY = u"SecondaryKeys"_ustr; + constexpr OUString CFG_PROP_COMMAND = u"Command"_ustr; + + static OUString lcl_getKeyString(const css::awt::KeyEvent& aKeyEvent) + { + const sal_Int32 nBeginIndex = 4; // "KEY_" is the prefix of an identifier... + OUString sKey(KeyMapping::get().mapCodeToIdentifier(aKeyEvent.KeyCode)); + if (sKey.getLength() < nBeginIndex) // dead key + return OUString(); + OUStringBuffer sKeyBuffer(sKey.subView(nBeginIndex)); + + if ( (aKeyEvent.Modifiers & css::awt::KeyModifier::SHIFT) == css::awt::KeyModifier::SHIFT ) + sKeyBuffer.append("_SHIFT"); + if ( (aKeyEvent.Modifiers & css::awt::KeyModifier::MOD1 ) == css::awt::KeyModifier::MOD1 ) + sKeyBuffer.append("_MOD1"); + if ( (aKeyEvent.Modifiers & css::awt::KeyModifier::MOD2 ) == css::awt::KeyModifier::MOD2 ) + sKeyBuffer.append("_MOD2"); + if ( (aKeyEvent.Modifiers & css::awt::KeyModifier::MOD3 ) == css::awt::KeyModifier::MOD3 ) + sKeyBuffer.append("_MOD3"); + + return sKeyBuffer.makeStringAndClear(); + } + +XMLBasedAcceleratorConfiguration::XMLBasedAcceleratorConfiguration(const css::uno::Reference< css::uno::XComponentContext >& xContext) + : m_xContext (xContext ) + , m_aPresetHandler(xContext ) +{ +} + +XMLBasedAcceleratorConfiguration::~XMLBasedAcceleratorConfiguration() +{ + SAL_WARN_IF(m_pWriteCache, "fwk.accelerators", "XMLBasedAcceleratorConfiguration::~XMLBasedAcceleratorConfiguration(): Changes not flushed. Ignore it ..."); +} + +css::uno::Sequence< css::awt::KeyEvent > SAL_CALL XMLBasedAcceleratorConfiguration::getAllKeyEvents() +{ + SolarMutexGuard g; + AcceleratorCache& rCache = impl_getCFG(); + AcceleratorCache::TKeyList lKeys = rCache.getAllKeys(); + return comphelper::containerToSequence(lKeys); +} + +OUString SAL_CALL XMLBasedAcceleratorConfiguration::getCommandByKeyEvent(const css::awt::KeyEvent& aKeyEvent) +{ + SolarMutexGuard g; + AcceleratorCache& rCache = impl_getCFG(); + if (!rCache.hasKey(aKeyEvent)) + throw css::container::NoSuchElementException( + OUString(), + static_cast< ::cppu::OWeakObject* >(this)); + return rCache.getCommandByKey(aKeyEvent); +} + +void SAL_CALL XMLBasedAcceleratorConfiguration::setKeyEvent(const css::awt::KeyEvent& aKeyEvent, + const OUString& sCommand ) +{ + if ( + (aKeyEvent.KeyCode == 0) && + (aKeyEvent.KeyChar == 0) && + (aKeyEvent.KeyFunc == 0) && + (aKeyEvent.Modifiers == 0) + ) + throw css::lang::IllegalArgumentException( + "Such key event seems not to be supported by any operating system.", + static_cast< ::cppu::OWeakObject* >(this), + 0); + + if (sCommand.isEmpty()) + throw css::lang::IllegalArgumentException( + "Empty command strings are not allowed here.", + static_cast< ::cppu::OWeakObject* >(this), + 1); + + SolarMutexGuard g; + AcceleratorCache& rCache = impl_getCFG(true); // sal_True => force getting of a writeable cache! + rCache.setKeyCommandPair(aKeyEvent, sCommand); +} + +void SAL_CALL XMLBasedAcceleratorConfiguration::removeKeyEvent(const css::awt::KeyEvent& aKeyEvent) +{ + SolarMutexGuard g; + AcceleratorCache& rCache = impl_getCFG(true); // true => force using of a writeable cache + if (!rCache.hasKey(aKeyEvent)) + throw css::container::NoSuchElementException( + OUString(), + static_cast< ::cppu::OWeakObject* >(this)); + rCache.removeKey(aKeyEvent); +} + +css::uno::Sequence< css::awt::KeyEvent > SAL_CALL XMLBasedAcceleratorConfiguration::getKeyEventsByCommand(const OUString& sCommand) +{ + if (sCommand.isEmpty()) + throw css::lang::IllegalArgumentException( + "Empty command strings are not allowed here.", + static_cast< ::cppu::OWeakObject* >(this), + 1); + + SolarMutexGuard g; + AcceleratorCache& rCache = impl_getCFG(); + if (!rCache.hasCommand(sCommand)) + throw css::container::NoSuchElementException( + OUString(), + static_cast< ::cppu::OWeakObject* >(this)); + + AcceleratorCache::TKeyList lKeys = rCache.getKeysByCommand(sCommand); + return comphelper::containerToSequence(lKeys); +} + +css::uno::Sequence< css::uno::Any > SAL_CALL XMLBasedAcceleratorConfiguration::getPreferredKeyEventsForCommandList(const css::uno::Sequence< OUString >& lCommandList) +{ + SolarMutexGuard g; + + sal_Int32 i = 0; + sal_Int32 c = lCommandList.getLength(); + css::uno::Sequence< css::uno::Any > lPreferredOnes (c); // don't pack list! + AcceleratorCache& rCache = impl_getCFG(); + + auto lPreferredOnesRange = asNonConstRange(lPreferredOnes); + for (i=0; i(this), + static_cast(i)); + + if (!rCache.hasCommand(rCommand)) + continue; + + AcceleratorCache::TKeyList lKeys = rCache.getKeysByCommand(rCommand); + if ( lKeys.empty() ) + continue; + + css::uno::Any& rAny = lPreferredOnesRange[i]; + rAny <<= *(lKeys.begin()); + } + + return lPreferredOnes; +} + +void SAL_CALL XMLBasedAcceleratorConfiguration::removeCommandFromAllKeyEvents(const OUString& sCommand) +{ + if (sCommand.isEmpty()) + throw css::lang::IllegalArgumentException( + "Empty command strings are not allowed here.", + static_cast< ::cppu::OWeakObject* >(this), + 0); + + SolarMutexGuard g; + AcceleratorCache& rCache = impl_getCFG(true); // sal_True => force getting of a writeable cache! + if (!rCache.hasCommand(sCommand)) + throw css::container::NoSuchElementException( + "Command does not exists inside this container.", + static_cast< ::cppu::OWeakObject* >(this)); + rCache.removeCommand(sCommand); +} + +void SAL_CALL XMLBasedAcceleratorConfiguration::reload() +{ + css::uno::Reference< css::io::XStream > xStream; + css::uno::Reference< css::io::XStream > xStreamNoLang; + { + SolarMutexGuard g; + xStream = m_aPresetHandler.openTarget(TARGET_CURRENT, + css::embed::ElementModes::READ); + try + { + xStreamNoLang = m_aPresetHandler.openPreset(PRESET_DEFAULT); + } + catch(const css::io::IOException&) {} // does not have to exist + } + + css::uno::Reference< css::io::XInputStream > xIn; + if (xStream.is()) + xIn = xStream->getInputStream(); + if (!xIn.is()) + throw css::io::IOException( + "Could not open accelerator configuration for reading.", + static_cast< ::cppu::OWeakObject* >(this)); + + // impl_ts_load() does not clear the cache + { + SolarMutexGuard g; + m_aReadCache = AcceleratorCache(); + } + + impl_ts_load(xIn); + + // Load also the general language independent default accelerators + // (ignoring the already defined accelerators) + if (xStreamNoLang.is()) + { + xIn = xStreamNoLang->getInputStream(); + if (xIn.is()) + impl_ts_load(xIn); + } +} + +void SAL_CALL XMLBasedAcceleratorConfiguration::store() +{ + css::uno::Reference< css::io::XStream > xStream; + { + SolarMutexGuard g; + xStream = m_aPresetHandler.openTarget(TARGET_CURRENT, + css::embed::ElementModes::READWRITE); // open or create! + } + + css::uno::Reference< css::io::XOutputStream > xOut; + if (xStream.is()) + xOut = xStream->getOutputStream(); + + if (!xOut.is()) + throw css::io::IOException( + "Could not open accelerator configuration for saving.", + static_cast< ::cppu::OWeakObject* >(this)); + + impl_ts_save(xOut); + + xOut.clear(); + xStream.clear(); + + m_aPresetHandler.commitUserChanges(); +} + +void SAL_CALL XMLBasedAcceleratorConfiguration::storeToStorage(const css::uno::Reference< css::embed::XStorage >& xStorage) +{ + // no fallback from read/write to readonly! + css::uno::Reference< css::io::XStream > xStream = xStorage->openStreamElement(TARGET_CURRENT, css::embed::ElementModes::READWRITE); + + css::uno::Reference< css::io::XOutputStream > xOut; + if (xStream.is()) + xOut = xStream->getOutputStream(); + + if (!xOut.is()) + throw css::io::IOException( + "Could not open accelerator configuration for saving.", + static_cast< ::cppu::OWeakObject* >(this)); + + impl_ts_save(xOut); + + // TODO inform listener about success, so it can flush the root and sub storage of this stream! +} + +sal_Bool SAL_CALL XMLBasedAcceleratorConfiguration::isModified() +{ + SolarMutexGuard g; + return (m_pWriteCache != nullptr); +} + +sal_Bool SAL_CALL XMLBasedAcceleratorConfiguration::isReadOnly() +{ + css::uno::Reference< css::io::XStream > xStream; + { + SolarMutexGuard g; + xStream = m_aPresetHandler.openTarget(TARGET_CURRENT, + css::embed::ElementModes::READWRITE); // open or create! + } + + css::uno::Reference< css::io::XOutputStream > xOut; + if (xStream.is()) + xOut = xStream->getOutputStream(); + return !(xOut.is()); +} + +void SAL_CALL XMLBasedAcceleratorConfiguration::setStorage(const css::uno::Reference< css::embed::XStorage >& /*xStorage*/) +{ + SAL_INFO("fwk.accelerators", "XMLBasedAcceleratorConfiguration::setStorage(): implement this HACK .-)"); +} + +sal_Bool SAL_CALL XMLBasedAcceleratorConfiguration::hasStorage() +{ + SAL_INFO("fwk.accelerators", "XMLBasedAcceleratorConfiguration::hasStorage(): implement this HACK .-)"); + return false; +} + +void SAL_CALL XMLBasedAcceleratorConfiguration::addConfigurationListener(const css::uno::Reference< css::ui::XUIConfigurationListener >& /*xListener*/) +{ + SAL_INFO("fwk.accelerators", "XMLBasedAcceleratorConfiguration::addConfigurationListener(): implement me"); +} + +void SAL_CALL XMLBasedAcceleratorConfiguration::removeConfigurationListener(const css::uno::Reference< css::ui::XUIConfigurationListener >& /*xListener*/) +{ + SAL_INFO("fwk.accelerators", "XMLBasedAcceleratorConfiguration::removeConfigurationListener(): implement me"); +} + +void SAL_CALL XMLBasedAcceleratorConfiguration::reset() +{ + { + SolarMutexGuard g; + m_aPresetHandler.copyPresetToTarget(PRESET_DEFAULT, TARGET_CURRENT); + } + + reload(); +} + +void SAL_CALL XMLBasedAcceleratorConfiguration::addResetListener(const css::uno::Reference< css::form::XResetListener >& /*xListener*/) +{ + SAL_INFO("fwk.accelerators", "XMLBasedAcceleratorConfiguration::addResetListener(): implement me"); +} + +void SAL_CALL XMLBasedAcceleratorConfiguration::removeResetListener(const css::uno::Reference< css::form::XResetListener >& /*xListener*/) +{ + SAL_INFO("fwk.accelerators", "XMLBasedAcceleratorConfiguration::removeResetListener(): implement me"); +} + +// IStorageListener +void XMLBasedAcceleratorConfiguration::changesOccurred() +{ + reload(); +} + +void XMLBasedAcceleratorConfiguration::impl_ts_load(const css::uno::Reference< css::io::XInputStream >& xStream) +{ + css::uno::Reference< css::uno::XComponentContext > xContext; + { + SolarMutexGuard g; + xContext = m_xContext; + m_pWriteCache.reset(); + } + + css::uno::Reference< css::io::XSeekable > xSeek(xStream, css::uno::UNO_QUERY); + if (xSeek.is()) + xSeek->seek(0); + + SolarMutexGuard g; + + // create the parser queue + // Note: Use special filter object between parser and reader + // to get filtered xml with right namespaces ... + // Use further a temp cache for reading! + rtl::Reference pReader = new AcceleratorConfigurationReader(m_aReadCache); + rtl::Reference pFilter = new SaxNamespaceFilter(pReader); + + // connect parser, filter and stream + css::uno::Reference< css::xml::sax::XParser > xParser = css::xml::sax::Parser::create(xContext); + xParser->setDocumentHandler(pFilter); + + css::xml::sax::InputSource aSource; + aSource.aInputStream = xStream; + + // TODO think about error handling + xParser->parseStream(aSource); +} + +void XMLBasedAcceleratorConfiguration::impl_ts_save(const css::uno::Reference< css::io::XOutputStream >& xStream) +{ + bool bChanged; + AcceleratorCache aCache; + css::uno::Reference< css::uno::XComponentContext > xContext; + { + SolarMutexGuard g; + bChanged = (m_pWriteCache != nullptr); + if (bChanged) + aCache = *m_pWriteCache; + else + aCache = m_aReadCache; + xContext = m_xContext; + } + + css::uno::Reference< css::io::XTruncate > xClearable(xStream, css::uno::UNO_QUERY_THROW); + xClearable->truncate(); + + // TODO can be removed if seek(0) is done by truncate() automatically! + css::uno::Reference< css::io::XSeekable > xSeek(xStream, css::uno::UNO_QUERY); + if (xSeek.is()) + xSeek->seek(0); + + // combine writer/cache/stream etcpp. + css::uno::Reference< css::xml::sax::XWriter > xWriter = css::xml::sax::Writer::create(xContext); + xWriter->setOutputStream(xStream); + + // write into the stream + css::uno::Reference< css::xml::sax::XDocumentHandler > xHandler(xWriter, css::uno::UNO_QUERY_THROW); + AcceleratorConfigurationWriter aWriter(aCache, xHandler); + aWriter.flush(); + + SolarMutexGuard g; + // take over all changes into the readonly cache ... + // and forget the copy-on-write copied cache + if (bChanged) + { + m_aReadCache = *m_pWriteCache; + m_pWriteCache.reset(); + } +} + +AcceleratorCache& XMLBasedAcceleratorConfiguration::impl_getCFG(bool bWriteAccessRequested) +{ + SolarMutexGuard g; + + //create copy of our readonly-cache, if write access is forced ... but + //not still possible! + if ( bWriteAccessRequested && !m_pWriteCache ) + { + m_pWriteCache.reset(new AcceleratorCache(m_aReadCache)); + } + + // in case, we have a writeable cache, we use it for reading too! + // Otherwise the API user can't find its own changes... + if (m_pWriteCache) + return *m_pWriteCache; + else + return m_aReadCache; +} + +OUString XMLBasedAcceleratorConfiguration::impl_ts_getLocale() const +{ + OUString sISOLocale = officecfg::Setup::L10N::ooLocale::get(); + + if (sISOLocale.isEmpty()) + return "en-US"; + return sISOLocale; +} + +/******************************************************************************* +* +* XCU based accelerator configuration +* +*******************************************************************************/ + +XCUBasedAcceleratorConfiguration::XCUBasedAcceleratorConfiguration(css::uno::Reference< css::uno::XComponentContext > xContext) + : m_xContext (std::move(xContext )) +{ + m_xCfg.set( + ::comphelper::ConfigurationHelper::openConfig( m_xContext, "org.openoffice.Office.Accelerators", ::comphelper::EConfigurationModes::AllLocales ), + css::uno::UNO_QUERY ); +} + +XCUBasedAcceleratorConfiguration::~XCUBasedAcceleratorConfiguration() +{ +} + +css::uno::Sequence< css::awt::KeyEvent > SAL_CALL XCUBasedAcceleratorConfiguration::getAllKeyEvents() +{ + SolarMutexGuard g; + + AcceleratorCache::TKeyList lKeys = impl_getCFG(true).getAllKeys(); //get keys from PrimaryKeys set + + AcceleratorCache::TKeyList lSecondaryKeys = impl_getCFG(false).getAllKeys(); //get keys from SecondaryKeys set + lKeys.reserve(lKeys.size()+lSecondaryKeys.size()); + for (auto const& secondaryKey : lSecondaryKeys) + lKeys.push_back(secondaryKey); + + return comphelper::containerToSequence(lKeys); +} + +OUString SAL_CALL XCUBasedAcceleratorConfiguration::getCommandByKeyEvent(const css::awt::KeyEvent& aKeyEvent) +{ + SolarMutexGuard g; + + AcceleratorCache& rPrimaryCache = impl_getCFG(true ); + AcceleratorCache& rSecondaryCache = impl_getCFG(false); + + if (!rPrimaryCache.hasKey(aKeyEvent) && !rSecondaryCache.hasKey(aKeyEvent)) + throw css::container::NoSuchElementException( + OUString(), + static_cast< ::cppu::OWeakObject* >(this)); + + if (rPrimaryCache.hasKey(aKeyEvent)) + return rPrimaryCache.getCommandByKey(aKeyEvent); + else + return rSecondaryCache.getCommandByKey(aKeyEvent); +} + +void SAL_CALL XCUBasedAcceleratorConfiguration::setKeyEvent(const css::awt::KeyEvent& aKeyEvent, + const OUString& sCommand ) +{ + SAL_INFO( "fwk.accelerators", "XCUBasedAcceleratorConfiguration::setKeyEvent" ); + + if ( + (aKeyEvent.KeyCode == 0) && + (aKeyEvent.KeyChar == 0) && + (aKeyEvent.KeyFunc == 0) && + (aKeyEvent.Modifiers == 0) + ) + throw css::lang::IllegalArgumentException( + "Such key event seems not to be supported by any operating system.", + static_cast< ::cppu::OWeakObject* >(this), + 0); + + if (sCommand.isEmpty()) + throw css::lang::IllegalArgumentException( + "Empty command strings are not allowed here.", + static_cast< ::cppu::OWeakObject* >(this), + 1); + + SolarMutexGuard g; + + AcceleratorCache& rPrimaryCache = impl_getCFG(true, true ); // sal_True => force getting of a writeable cache! + AcceleratorCache& rSecondaryCache = impl_getCFG(false, true); // sal_True => force getting of a writeable cache! + + if ( rPrimaryCache.hasKey(aKeyEvent) ) + { + OUString sOriginalCommand = rPrimaryCache.getCommandByKey(aKeyEvent); + if ( sCommand != sOriginalCommand ) + { + if (rSecondaryCache.hasCommand(sOriginalCommand)) + { + AcceleratorCache::TKeyList lSecondaryKeys = rSecondaryCache.getKeysByCommand(sOriginalCommand); + rSecondaryCache.removeKey(lSecondaryKeys[0]); + rPrimaryCache.setKeyCommandPair(lSecondaryKeys[0], sOriginalCommand); + } + + if (rPrimaryCache.hasCommand(sCommand)) + { + AcceleratorCache::TKeyList lPrimaryKeys = rPrimaryCache.getKeysByCommand(sCommand); + rPrimaryCache.removeKey(lPrimaryKeys[0]); + rSecondaryCache.setKeyCommandPair(lPrimaryKeys[0], sCommand); + } + + rPrimaryCache.setKeyCommandPair(aKeyEvent, sCommand); + } + } + + else if ( rSecondaryCache.hasKey(aKeyEvent) ) + { + OUString sOriginalCommand = rSecondaryCache.getCommandByKey(aKeyEvent); + if (sCommand != sOriginalCommand) + { + if (rPrimaryCache.hasCommand(sCommand)) + { + AcceleratorCache::TKeyList lPrimaryKeys = rPrimaryCache.getKeysByCommand(sCommand); + rPrimaryCache.removeKey(lPrimaryKeys[0]); + rSecondaryCache.setKeyCommandPair(lPrimaryKeys[0], sCommand); + } + + rSecondaryCache.removeKey(aKeyEvent); + rPrimaryCache.setKeyCommandPair(aKeyEvent, sCommand); + } + } + + else + { + if (rPrimaryCache.hasCommand(sCommand)) + { + AcceleratorCache::TKeyList lPrimaryKeys = rPrimaryCache.getKeysByCommand(sCommand); + rPrimaryCache.removeKey(lPrimaryKeys[0]); + rSecondaryCache.setKeyCommandPair(lPrimaryKeys[0], sCommand); + } + + rPrimaryCache.setKeyCommandPair(aKeyEvent, sCommand); + } +} + +void SAL_CALL XCUBasedAcceleratorConfiguration::removeKeyEvent(const css::awt::KeyEvent& aKeyEvent) +{ + SolarMutexGuard g; + + AcceleratorCache& rPrimaryCache = impl_getCFG(true, true ); + AcceleratorCache& rSecondaryCache = impl_getCFG(false, true); + + if (!rPrimaryCache.hasKey(aKeyEvent) && !rSecondaryCache.hasKey(aKeyEvent)) + throw css::container::NoSuchElementException( + OUString(), + static_cast< ::cppu::OWeakObject* >(this)); + + if (rPrimaryCache.hasKey(aKeyEvent)) + { + OUString sOriginalCommand = rPrimaryCache.getCommandByKey(aKeyEvent); + if (!sOriginalCommand.isEmpty()) + { + if (rSecondaryCache.hasCommand(sOriginalCommand)) + { + AcceleratorCache::TKeyList lSecondaryKeys = rSecondaryCache.getKeysByCommand(sOriginalCommand); + rSecondaryCache.removeKey(lSecondaryKeys[0]); + rPrimaryCache.setKeyCommandPair(lSecondaryKeys[0], sOriginalCommand); + } + + rPrimaryCache.removeKey(aKeyEvent); + } + + } + else + { + OUString sDelCommand = rSecondaryCache.getCommandByKey(aKeyEvent); + if (!sDelCommand.isEmpty()) + rSecondaryCache.removeKey(aKeyEvent); + } +} + +css::uno::Sequence< css::awt::KeyEvent > SAL_CALL XCUBasedAcceleratorConfiguration::getKeyEventsByCommand(const OUString& sCommand) +{ + if (sCommand.isEmpty()) + throw css::lang::IllegalArgumentException( + "Empty command strings are not allowed here.", + static_cast< ::cppu::OWeakObject* >(this), + 1); + + SolarMutexGuard g; + + AcceleratorCache& rPrimaryCache = impl_getCFG(true ); + AcceleratorCache& rSecondaryCache = impl_getCFG(false); + + if (!rPrimaryCache.hasCommand(sCommand) && !rSecondaryCache.hasCommand(sCommand)) + throw css::container::NoSuchElementException( + OUString(), + static_cast< ::cppu::OWeakObject* >(this)); + + AcceleratorCache::TKeyList lKeys = rPrimaryCache.getKeysByCommand(sCommand); + + AcceleratorCache::TKeyList lSecondaryKeys = rSecondaryCache.getKeysByCommand(sCommand); + for (auto const& secondaryKey : lSecondaryKeys) + lKeys.push_back(secondaryKey); + + return comphelper::containerToSequence(lKeys); +} + +static AcceleratorCache::TKeyList::const_iterator lcl_getPreferredKey(const AcceleratorCache::TKeyList& lKeys) +{ + return std::find_if(lKeys.begin(), lKeys.end(), [](const css::awt::KeyEvent& rAWTKey) { + return !::svt::AcceleratorExecute::st_AWTKey2VCLKey(rAWTKey).GetName().isEmpty(); }); +} + +css::uno::Sequence< css::uno::Any > SAL_CALL XCUBasedAcceleratorConfiguration::getPreferredKeyEventsForCommandList(const css::uno::Sequence< OUString >& lCommandList) +{ + SolarMutexGuard g; + + sal_Int32 i = 0; + sal_Int32 c = lCommandList.getLength(); + css::uno::Sequence< css::uno::Any > lPreferredOnes (c); // don't pack list! + AcceleratorCache& rCache = impl_getCFG(true); + + auto lPreferredOnesRange = asNonConstRange(lPreferredOnes); + for (i=0; i(this), + static_cast(i)); + + if (!rCache.hasCommand(rCommand)) + continue; + + AcceleratorCache::TKeyList lKeys = rCache.getKeysByCommand(rCommand); + if ( lKeys.empty() ) + continue; + + AcceleratorCache::TKeyList::const_iterator pPreferredKey = lcl_getPreferredKey(lKeys); + if (pPreferredKey != lKeys.end ()) + { + css::uno::Any& rAny = lPreferredOnesRange[i]; + rAny <<= *pPreferredKey; + } + } + + return lPreferredOnes; +} + +void SAL_CALL XCUBasedAcceleratorConfiguration::removeCommandFromAllKeyEvents(const OUString& sCommand) +{ + if (sCommand.isEmpty()) + throw css::lang::IllegalArgumentException( + "Empty command strings are not allowed here.", + static_cast< ::cppu::OWeakObject* >(this), + 0); + + SolarMutexGuard g; + + AcceleratorCache& rPrimaryCache = impl_getCFG(true, true ); + AcceleratorCache& rSecondaryCache = impl_getCFG(false, true); + + if (!rPrimaryCache.hasCommand(sCommand) && !rSecondaryCache.hasCommand(sCommand)) + throw css::container::NoSuchElementException( + "Command does not exists inside this container.", + static_cast< ::cppu::OWeakObject* >(this)); + + if (rPrimaryCache.hasCommand(sCommand)) + rPrimaryCache.removeCommand(sCommand); + if (rSecondaryCache.hasCommand(sCommand)) + rSecondaryCache.removeCommand(sCommand); +} + +void SAL_CALL XCUBasedAcceleratorConfiguration::reload() +{ + SAL_INFO( "fwk.accelerators", "XCUBasedAcceleratorConfiguration::reload()" ); + + SolarMutexGuard g; + + bool bPreferred; + css::uno::Reference< css::container::XNameAccess > xAccess; + + bPreferred = true; + m_aPrimaryReadCache = AcceleratorCache(); + m_pPrimaryWriteCache.reset(); + m_xCfg->getByName(CFG_ENTRY_PRIMARY) >>= xAccess; + impl_ts_load(bPreferred, xAccess); // load the preferred keys + + bPreferred = false; + m_aSecondaryReadCache = AcceleratorCache(); + m_pSecondaryWriteCache.reset(); + m_xCfg->getByName(CFG_ENTRY_SECONDARY) >>= xAccess; + impl_ts_load(bPreferred, xAccess); // load the secondary keys +} + +void SAL_CALL XCUBasedAcceleratorConfiguration::store() +{ + SAL_INFO( "fwk.accelerators", "XCUBasedAcceleratorConfiguration::store()" ); + + SolarMutexGuard g; + + bool bPreferred; + + bPreferred = true; + // on-demand creation of the primary write cache + impl_getCFG(bPreferred, true); + impl_ts_save(bPreferred); + + bPreferred = false; + // on-demand creation of the secondary write cache + impl_getCFG(bPreferred, true); + impl_ts_save(bPreferred); +} + +void SAL_CALL XCUBasedAcceleratorConfiguration::storeToStorage(const css::uno::Reference< css::embed::XStorage >& xStorage) +{ + // use m_aCache + old AcceleratorXMLWriter to store data directly on storage given as parameter ... + if (!xStorage.is()) + return; + + tools::Long nOpenModes = css::embed::ElementModes::READWRITE; + css::uno::Reference< css::embed::XStorage > xAcceleratorTypeStorage = xStorage->openStorageElement("accelerator", nOpenModes); + if (!xAcceleratorTypeStorage.is()) + return; + + css::uno::Reference< css::io::XStream > xStream = xAcceleratorTypeStorage->openStreamElement("current", nOpenModes); + css::uno::Reference< css::io::XOutputStream > xOut; + if (xStream.is()) + xOut = xStream->getOutputStream(); + if (!xOut.is()) + throw css::io::IOException( + "Could not open accelerator configuration for saving.", + static_cast< ::cppu::OWeakObject* >(this)); + + // the original m_aCache has been split into primary cache and secondary cache... + // we should merge them before storing to storage + AcceleratorCache aCache; + { + SolarMutexGuard g; + + if (m_pPrimaryWriteCache != nullptr) + aCache = *m_pPrimaryWriteCache; + else + aCache = m_aPrimaryReadCache; + + AcceleratorCache::TKeyList lKeys; + if (m_pSecondaryWriteCache!=nullptr) + { + lKeys = m_pSecondaryWriteCache->getAllKeys(); + for (auto const& lKey : lKeys) + aCache.setKeyCommandPair(lKey, m_pSecondaryWriteCache->getCommandByKey(lKey)); + } + else + { + lKeys = m_aSecondaryReadCache.getAllKeys(); + for (auto const& lKey : lKeys) + aCache.setKeyCommandPair(lKey, m_aSecondaryReadCache.getCommandByKey(lKey)); + } + } + + css::uno::Reference< css::io::XTruncate > xClearable(xOut, css::uno::UNO_QUERY_THROW); + xClearable->truncate(); + css::uno::Reference< css::io::XSeekable > xSeek(xOut, css::uno::UNO_QUERY); + if (xSeek.is()) + xSeek->seek(0); + + css::uno::Reference< css::xml::sax::XWriter > xWriter = css::xml::sax::Writer::create(m_xContext); + xWriter->setOutputStream(xOut); + + // write into the stream + css::uno::Reference< css::xml::sax::XDocumentHandler > xHandler(xWriter, css::uno::UNO_QUERY_THROW); + AcceleratorConfigurationWriter aWriter(aCache, xHandler); + aWriter.flush(); +} + +sal_Bool SAL_CALL XCUBasedAcceleratorConfiguration::isModified() +{ + return false; +} + +sal_Bool SAL_CALL XCUBasedAcceleratorConfiguration::isReadOnly() +{ + return false; +} + +void SAL_CALL XCUBasedAcceleratorConfiguration::setStorage(const css::uno::Reference< css::embed::XStorage >& /*xStorage*/) +{ + SAL_INFO("fwk.accelerators", "XCUBasedAcceleratorConfiguration::setStorage(): implement this HACK .-)"); +} + +sal_Bool SAL_CALL XCUBasedAcceleratorConfiguration::hasStorage() +{ + SAL_INFO("fwk.accelerators", "XCUBasedAcceleratorConfiguration::hasStorage(): implement this HACK .-)"); + return false; +} + +void SAL_CALL XCUBasedAcceleratorConfiguration::addConfigurationListener(const css::uno::Reference< css::ui::XUIConfigurationListener >& /*xListener*/) +{ + SAL_INFO("fwk.accelerators", "XCUBasedAcceleratorConfiguration::addConfigurationListener(): implement me"); +} + +void SAL_CALL XCUBasedAcceleratorConfiguration::removeConfigurationListener(const css::uno::Reference< css::ui::XUIConfigurationListener >& /*xListener*/) +{ + SAL_INFO("fwk.accelerators", "XCUBasedAcceleratorConfiguration::removeConfigurationListener(): implement me"); +} + +void SAL_CALL XCUBasedAcceleratorConfiguration::reset() +{ + css::uno::Reference< css::container::XNamed > xNamed(m_xCfg, css::uno::UNO_QUERY); + OUString sConfig = xNamed->getName(); + if ( sConfig == "Global" ) + { + m_xCfg.set( + ::comphelper::ConfigurationHelper::openConfig( m_xContext, CFG_ENTRY_GLOBAL, ::comphelper::EConfigurationModes::AllLocales ), + css::uno::UNO_QUERY ); + XCUBasedAcceleratorConfiguration::reload(); + } + else if ( sConfig == "Modules" ) + { + m_xCfg.set( + ::comphelper::ConfigurationHelper::openConfig( m_xContext, CFG_ENTRY_MODULES, ::comphelper::EConfigurationModes::AllLocales ), + css::uno::UNO_QUERY ); + XCUBasedAcceleratorConfiguration::reload(); + } +} + +void SAL_CALL XCUBasedAcceleratorConfiguration::addResetListener(const css::uno::Reference< css::form::XResetListener >& /*xListener*/) +{ + SAL_INFO("fwk.accelerators", "XCUBasedAcceleratorConfiguration::addResetListener(): implement me"); +} + +void SAL_CALL XCUBasedAcceleratorConfiguration::removeResetListener(const css::uno::Reference< css::form::XResetListener >& /*xListener*/) +{ + SAL_INFO("fwk.accelerators", "XCUBasedAcceleratorConfiguration::removeResetListener(): implement me"); +} + +void SAL_CALL XCUBasedAcceleratorConfiguration::changesOccurred(const css::util::ChangesEvent& aEvent) +{ + SAL_INFO( "fwk.accelerators", "XCUBasedAcceleratorConfiguration::changesOccurred()" ); + + css::uno::Reference< css::container::XHierarchicalNameAccess > xHAccess; + aEvent.Base >>= xHAccess; + if (! xHAccess.is ()) + return; + + css::util::ChangesEvent aReceivedEvents( aEvent ); + const sal_Int32 c = aReceivedEvents.Changes.getLength(); + for (sal_Int32 i=0; i']/Key['']/Command[]" will + // be interesting for use. Sometimes short path values are given also by the broadcaster ... but they must be ignored :-) + // So we try to split the path into 3 parts (module isn't important here, because we already know it ... because + // these instance is bound to a specific module configuration ... or it''s the global configuration where no module is given at all. + + OUString sOrgPath; + OUString sPath; + OUString sKey; + + aChange.Accessor >>= sOrgPath; + sPath = sOrgPath; + OUString sPrimarySecondary = ::utl::extractFirstFromConfigurationPath(sPath, &sPath); + OUString sGlobalModules = ::utl::extractFirstFromConfigurationPath(sPath, &sPath); + + if ( sGlobalModules == CFG_ENTRY_GLOBAL ) + { + sKey = ::utl::extractFirstFromConfigurationPath(sPath, &sPath); + if ( !sKey.isEmpty() && !sPath.isEmpty() ) + reloadChanged(sPrimarySecondary, sGlobalModules, OUString(), sKey); + } + else if ( sGlobalModules == CFG_ENTRY_MODULES ) + { + OUString sModule = ::utl::extractFirstFromConfigurationPath(sPath, &sPath); + sKey = ::utl::extractFirstFromConfigurationPath(sPath, &sPath); + + if ( !sKey.isEmpty() && !sPath.isEmpty() ) + { + reloadChanged(sPrimarySecondary, sGlobalModules, sModule, sKey); + } + } + } +} + +void SAL_CALL XCUBasedAcceleratorConfiguration::disposing(const css::lang::EventObject& /*aSource*/) +{ +} + +void XCUBasedAcceleratorConfiguration::impl_ts_load( bool bPreferred, const css::uno::Reference< css::container::XNameAccess >& xCfg ) +{ + AcceleratorCache aReadCache; + css::uno::Reference< css::container::XNameAccess > xAccess; + if ( m_sGlobalOrModules == "Global" ) + xCfg->getByName(CFG_ENTRY_GLOBAL) >>= xAccess; + else if ( m_sGlobalOrModules == "Modules" ) + { + css::uno::Reference< css::container::XNameAccess > xModules; + xCfg->getByName(CFG_ENTRY_MODULES) >>= xModules; + xModules->getByName(m_sModuleCFG) >>= xAccess; + } + + const OUString sIsoLang = impl_ts_getLocale(); + static constexpr OUStringLiteral sDefaultLocale(u"en-US"); + + css::uno::Reference< css::container::XNameAccess > xKey; + css::uno::Reference< css::container::XNameAccess > xCommand; + if (xAccess.is()) + { + css::uno::Sequence< OUString > lKeys = xAccess->getElementNames(); + sal_Int32 nKeys = lKeys.getLength(); + for ( sal_Int32 i=0; igetByName(sKey) >>= xKey; + xKey->getByName(CFG_PROP_COMMAND) >>= xCommand; + + const css::uno::Sequence< OUString > lLocales = xCommand->getElementNames(); + ::std::vector< OUString > aLocales { lLocales.begin(), lLocales.end() }; + + OUString sLocale; + for (auto const& locale : aLocales) + { + if ( locale == sIsoLang ) + { + sLocale = locale; + break; + } + } + + if (sLocale.isEmpty()) + { + for (auto const& locale : aLocales) + { + if ( locale == sDefaultLocale ) + { + sLocale = locale; + break; + } + } + + if (sLocale.isEmpty()) + continue; + } + + OUString sCommand; + xCommand->getByName(sLocale) >>= sCommand; + if (sCommand.isEmpty()) + continue; + + css::awt::KeyEvent aKeyEvent; + + sal_Int32 nIndex = 0; + std::u16string_view sKeyCommand = o3tl::getToken(sKey, 0, '_', nIndex); + aKeyEvent.KeyCode = KeyMapping::get().mapIdentifierToCode(OUString::Concat("KEY_") + sKeyCommand); + + const sal_Int32 nToken = 4; + bool bValid = true; + sal_Int32 k; + for (k = 0; k < nToken; ++k) + { + if (nIndex < 0) + break; + + std::u16string_view sToken = o3tl::getToken(sKey, 0, '_', nIndex); + if (sToken.empty()) + { + bValid = false; + break; + } + + if ( sToken == u"SHIFT" ) + aKeyEvent.Modifiers |= css::awt::KeyModifier::SHIFT; + else if ( sToken == u"MOD1" ) + aKeyEvent.Modifiers |= css::awt::KeyModifier::MOD1; + else if ( sToken == u"MOD2" ) + aKeyEvent.Modifiers |= css::awt::KeyModifier::MOD2; + else if ( sToken == u"MOD3" ) + aKeyEvent.Modifiers |= css::awt::KeyModifier::MOD3; + else + { + bValid = false; + break; + } + } + + if ( !aReadCache.hasKey(aKeyEvent) && bValid && kgetAllKeys(); + + for (auto const& primaryReadKey : lPrimaryReadKeys) + { + if (!m_pPrimaryWriteCache->hasKey(primaryReadKey)) + removeKeyFromConfiguration(primaryReadKey, true); + } + + for (auto const& primaryWriteKey : lPrimaryWriteKeys) + { + OUString sCommand = m_pPrimaryWriteCache->getCommandByKey(primaryWriteKey); + if (!m_aPrimaryReadCache.hasKey(primaryWriteKey)) + { + insertKeyToConfiguration(primaryWriteKey, sCommand, true); + } + else + { + OUString sReadCommand = m_aPrimaryReadCache.getCommandByKey(primaryWriteKey); + if (sReadCommand != sCommand) + insertKeyToConfiguration(primaryWriteKey, sCommand, true); + } + } + + // take over all changes into the original container + SolarMutexGuard g; + // coverity[check_after_deref] - confusing but correct + if (m_pPrimaryWriteCache) + { + m_aPrimaryReadCache = *m_pPrimaryWriteCache; + m_pPrimaryWriteCache.reset(); + } + } + + else + { + AcceleratorCache::TKeyList lSecondaryReadKeys = m_aSecondaryReadCache.getAllKeys(); + AcceleratorCache::TKeyList lSecondaryWriteKeys = m_pSecondaryWriteCache->getAllKeys(); + + for (auto const& secondaryReadKey : lSecondaryReadKeys) + { + if (!m_pSecondaryWriteCache->hasKey(secondaryReadKey)) + removeKeyFromConfiguration(secondaryReadKey, false); + } + + for (auto const& secondaryWriteKey : lSecondaryWriteKeys) + { + OUString sCommand = m_pSecondaryWriteCache->getCommandByKey(secondaryWriteKey); + if (!m_aSecondaryReadCache.hasKey(secondaryWriteKey)) + { + insertKeyToConfiguration(secondaryWriteKey, sCommand, false); + } + else + { + OUString sReadCommand = m_aSecondaryReadCache.getCommandByKey(secondaryWriteKey); + if (sReadCommand != sCommand) + insertKeyToConfiguration(secondaryWriteKey, sCommand, false); + } + } + + // take over all changes into the original container + SolarMutexGuard g; + // coverity[check_after_deref] - confusing but correct + if (m_pSecondaryWriteCache) + { + m_aSecondaryReadCache = *m_pSecondaryWriteCache; + m_pSecondaryWriteCache.reset(); + } + } + + ::comphelper::ConfigurationHelper::flush(m_xCfg); +} + +void XCUBasedAcceleratorConfiguration::insertKeyToConfiguration( const css::awt::KeyEvent& aKeyEvent, const OUString& sCommand, const bool bPreferred ) +{ + css::uno::Reference< css::container::XNameAccess > xAccess; + css::uno::Reference< css::container::XNameContainer > xContainer; + css::uno::Reference< css::lang::XSingleServiceFactory > xFac; + css::uno::Reference< css::uno::XInterface > xInst; + + if ( bPreferred ) + m_xCfg->getByName(CFG_ENTRY_PRIMARY) >>= xAccess; + else + m_xCfg->getByName(CFG_ENTRY_SECONDARY) >>= xAccess; + + if ( m_sGlobalOrModules == CFG_ENTRY_GLOBAL ) + xAccess->getByName(CFG_ENTRY_GLOBAL) >>= xContainer; + else if ( m_sGlobalOrModules == CFG_ENTRY_MODULES ) + { + css::uno::Reference< css::container::XNameContainer > xModules; + xAccess->getByName(CFG_ENTRY_MODULES) >>= xModules; + if ( !xModules->hasByName(m_sModuleCFG) ) + { + xFac.set(xModules, css::uno::UNO_QUERY); + xInst = xFac->createInstance(); + xModules->insertByName(m_sModuleCFG, css::uno::Any(xInst)); + } + xModules->getByName(m_sModuleCFG) >>= xContainer; + } + + const OUString sKey = lcl_getKeyString(aKeyEvent); + css::uno::Reference< css::container::XNameAccess > xKey; + css::uno::Reference< css::container::XNameContainer > xCommand; + if ( !xContainer->hasByName(sKey) ) + { + xFac.set(xContainer, css::uno::UNO_QUERY); + xInst = xFac->createInstance(); + xContainer->insertByName(sKey, css::uno::Any(xInst)); + } + xContainer->getByName(sKey) >>= xKey; + + xKey->getByName(CFG_PROP_COMMAND) >>= xCommand; + OUString sLocale = impl_ts_getLocale(); + if ( !xCommand->hasByName(sLocale) ) + xCommand->insertByName(sLocale, css::uno::Any(sCommand)); + else + xCommand->replaceByName(sLocale, css::uno::Any(sCommand)); +} + +void XCUBasedAcceleratorConfiguration::removeKeyFromConfiguration( const css::awt::KeyEvent& aKeyEvent, const bool bPreferred ) +{ + css::uno::Reference< css::container::XNameAccess > xAccess; + css::uno::Reference< css::container::XNameContainer > xContainer; + + if ( bPreferred ) + m_xCfg->getByName(CFG_ENTRY_PRIMARY) >>= xAccess; + else + m_xCfg->getByName(CFG_ENTRY_SECONDARY) >>= xAccess; + + if ( m_sGlobalOrModules == CFG_ENTRY_GLOBAL ) + xAccess->getByName(CFG_ENTRY_GLOBAL) >>= xContainer; + else if ( m_sGlobalOrModules == CFG_ENTRY_MODULES ) + { + css::uno::Reference< css::container::XNameAccess > xModules; + xAccess->getByName(CFG_ENTRY_MODULES) >>= xModules; + if ( !xModules->hasByName(m_sModuleCFG) ) + return; + xModules->getByName(m_sModuleCFG) >>= xContainer; + } + + const OUString sKey = lcl_getKeyString(aKeyEvent); + xContainer->removeByName(sKey); +} + +void XCUBasedAcceleratorConfiguration::reloadChanged( const OUString& sPrimarySecondary, std::u16string_view sGlobalModules, const OUString& sModule, const OUString& sKey ) +{ + css::uno::Reference< css::container::XNameAccess > xAccess; + css::uno::Reference< css::container::XNameContainer > xContainer; + + m_xCfg->getByName(sPrimarySecondary) >>= xAccess; + if ( sGlobalModules == CFG_ENTRY_GLOBAL ) + xAccess->getByName(CFG_ENTRY_GLOBAL) >>= xContainer; + else + { + css::uno::Reference< css::container::XNameAccess > xModules; + xAccess->getByName(CFG_ENTRY_MODULES) >>= xModules; + if ( !xModules->hasByName(sModule) ) + return; + xModules->getByName(sModule) >>= xContainer; + } + + css::awt::KeyEvent aKeyEvent; + + sal_Int32 nIndex = 0; + std::u16string_view sKeyIdentifier = o3tl::getToken(sKey, 0, '_', nIndex); + aKeyEvent.KeyCode = KeyMapping::get().mapIdentifierToCode(OUString::Concat("KEY_") + sKeyIdentifier); + + const int nToken = 4; + for (sal_Int32 i = 0; i < nToken; ++i) + { + if ( nIndex < 0 ) + break; + + std::u16string_view sToken = o3tl::getToken(sKey, 0, '_', nIndex); + if ( sToken == u"SHIFT" ) + aKeyEvent.Modifiers |= css::awt::KeyModifier::SHIFT; + else if ( sToken == u"MOD1" ) + aKeyEvent.Modifiers |= css::awt::KeyModifier::MOD1; + else if ( sToken == u"MOD2" ) + aKeyEvent.Modifiers |= css::awt::KeyModifier::MOD2; + else if ( sToken == u"MOD3" ) + aKeyEvent.Modifiers |= css::awt::KeyModifier::MOD3; + } + + css::uno::Reference< css::container::XNameAccess > xKey; + css::uno::Reference< css::container::XNameAccess > xCommand; + OUString sCommand; + + if (xContainer->hasByName(sKey)) + { + OUString sLocale = impl_ts_getLocale(); + xContainer->getByName(sKey) >>= xKey; + xKey->getByName(CFG_PROP_COMMAND) >>= xCommand; + xCommand->getByName(sLocale) >>= sCommand; + } + + if ( sPrimarySecondary == CFG_ENTRY_PRIMARY ) + { + if (sCommand.isEmpty()) + m_aPrimaryReadCache.removeKey(aKeyEvent); + else + m_aPrimaryReadCache.setKeyCommandPair(aKeyEvent, sCommand); + } + else if ( sPrimarySecondary == CFG_ENTRY_SECONDARY ) + { + if (sCommand.isEmpty()) + m_aSecondaryReadCache.removeKey(aKeyEvent); + else + m_aSecondaryReadCache.setKeyCommandPair(aKeyEvent, sCommand); + } +} + +AcceleratorCache& XCUBasedAcceleratorConfiguration::impl_getCFG(bool bPreferred, bool bWriteAccessRequested) +{ + SolarMutexGuard g; + + if (bPreferred) + { + //create copy of our readonly-cache, if write access is forced ... but + //not still possible! + if ( bWriteAccessRequested && !m_pPrimaryWriteCache ) + { + m_pPrimaryWriteCache.reset(new AcceleratorCache(m_aPrimaryReadCache)); + } + + // in case, we have a writeable cache, we use it for reading too! + // Otherwise the API user can't find its own changes... + if (m_pPrimaryWriteCache) + return *m_pPrimaryWriteCache; + else + return m_aPrimaryReadCache; + } + + else + { + //create copy of our readonly-cache, if write access is forced ... but + //not still possible! + if ( bWriteAccessRequested && !m_pSecondaryWriteCache ) + { + m_pSecondaryWriteCache.reset(new AcceleratorCache(m_aSecondaryReadCache)); + } + + // in case, we have a writeable cache, we use it for reading too! + // Otherwise the API user can't find its own changes... + if (m_pSecondaryWriteCache) + return *m_pSecondaryWriteCache; + else + return m_aSecondaryReadCache; + } +} + +OUString XCUBasedAcceleratorConfiguration::impl_ts_getLocale() const +{ + OUString sISOLocale = officecfg::Setup::L10N::ooLocale::get(); + + if (sISOLocale.isEmpty()) + return "en-US"; + return sISOLocale; +} + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/accelerators/documentacceleratorconfiguration.cxx b/framework/source/accelerators/documentacceleratorconfiguration.cxx new file mode 100644 index 0000000000..c86895f0de --- /dev/null +++ b/framework/source/accelerators/documentacceleratorconfiguration.cxx @@ -0,0 +1,196 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include + +#include + +#include +#include +#include +#include +#include + +using namespace framework; + +constexpr OUStringLiteral RESOURCETYPE_ACCELERATOR = u"accelerator"; + +namespace { + +/** + implements a read/write access to a document + based accelerator configuration. + */ + +typedef ::cppu::ImplInheritanceHelper< + XMLBasedAcceleratorConfiguration, + css::lang::XServiceInfo> DocumentAcceleratorConfiguration_BASE; + +class DocumentAcceleratorConfiguration : public DocumentAcceleratorConfiguration_BASE +{ +private: + + /** points to the root storage of the outside document, + where we can read/save our configuration data. */ + css::uno::Reference< css::embed::XStorage > m_xDocumentRoot; + +public: + + /** initialize this instance and fill the internal cache. + + @param xSMGR + reference to a uno service manager, which is used internally. + */ + DocumentAcceleratorConfiguration( + const css::uno::Reference< css::uno::XComponentContext >& xContext, + const css::uno::Sequence< css::uno::Any >& lArguments); + + virtual ~DocumentAcceleratorConfiguration() override; + + virtual OUString SAL_CALL getImplementationName() override + { + return "com.sun.star.comp.framework.DocumentAcceleratorConfiguration"; + } + + virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override + { + return cppu::supportsService(this, ServiceName); + } + + virtual css::uno::Sequence SAL_CALL getSupportedServiceNames() override + { + return {"com.sun.star.ui.DocumentAcceleratorConfiguration"}; + } + + // XUIConfigurationStorage + virtual void SAL_CALL setStorage(const css::uno::Reference< css::embed::XStorage >& xStorage) override; + + virtual sal_Bool SAL_CALL hasStorage() override; + + /** read all data into the cache. */ + void fillCache(); +}; + +DocumentAcceleratorConfiguration::DocumentAcceleratorConfiguration( + const css::uno::Reference< css::uno::XComponentContext >& xContext, + const css::uno::Sequence< css::uno::Any >& lArguments) + : DocumentAcceleratorConfiguration_BASE(xContext) +{ + SolarMutexGuard g; + css::uno::Reference xRoot; + if (lArguments.getLength() == 1 && (lArguments[0] >>= xRoot)) + { + m_xDocumentRoot = xRoot; + } + else + { + ::comphelper::SequenceAsHashMap lArgs(lArguments); + m_xDocumentRoot = lArgs.getUnpackedValueOrDefault( + "DocumentRoot", + css::uno::Reference< css::embed::XStorage >()); + } +} + +DocumentAcceleratorConfiguration::~DocumentAcceleratorConfiguration() +{ + m_aPresetHandler.removeStorageListener(this); +} + +void SAL_CALL DocumentAcceleratorConfiguration::setStorage(const css::uno::Reference< css::embed::XStorage >& xStorage) +{ + // Attention! xStorage must be accepted too, if it's NULL ! + + bool bForgetOldStorages; + { + SolarMutexGuard g; + bForgetOldStorages = m_xDocumentRoot.is(); + m_xDocumentRoot = xStorage; + } + + if (bForgetOldStorages) + /* forget all currently cached data AND(!) forget all currently used storages. */ + m_aPresetHandler.forgetCachedStorages(); + + if (xStorage.is()) + fillCache(); +} + +sal_Bool SAL_CALL DocumentAcceleratorConfiguration::hasStorage() +{ + SolarMutexGuard g; + return m_xDocumentRoot.is(); +} + +void DocumentAcceleratorConfiguration::fillCache() +{ + css::uno::Reference< css::embed::XStorage > xDocumentRoot; + { + SolarMutexGuard g; + xDocumentRoot = m_xDocumentRoot; + } + + // Sometimes we must live without a document root. + // E.g. if the document is readonly ... + if (!xDocumentRoot.is()) + return; + + // get current office locale ... but don't cache it. + // Otherwise we must be listener on the configuration layer + // which seems to superfluous for this small implementation .-) + LanguageTag aLanguageTag( impl_ts_getLocale()); + + // May be the current document does not contain any + // accelerator config? Handle it gracefully :-) + try + { + // Note: The used preset class is threadsafe by itself ... and live if we live! + // We do not need any mutex here. + + // open the folder, where the configuration exists + m_aPresetHandler.connectToResource( + PresetHandler::E_DOCUMENT, + RESOURCETYPE_ACCELERATOR, + u"", + xDocumentRoot, + aLanguageTag); + + DocumentAcceleratorConfiguration::reload(); + m_aPresetHandler.addStorageListener(this); + } + catch(const css::uno::Exception&) + {} +} + +} // namespace framework + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_framework_DocumentAcceleratorConfiguration_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence const &arguments) +{ + rtl::Reference inst = new DocumentAcceleratorConfiguration(context, arguments); + css::uno::XInterface *acquired_inst = cppu::acquire(inst.get()); + + inst->fillCache(); + + return acquired_inst; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/accelerators/globalacceleratorconfiguration.cxx b/framework/source/accelerators/globalacceleratorconfiguration.cxx new file mode 100644 index 0000000000..e54a05a03b --- /dev/null +++ b/framework/source/accelerators/globalacceleratorconfiguration.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 +#include +#include + +#include +#include + +#include +#include +#include + +using namespace framework; + +namespace { + +/** + implements a read/write access to the global + accelerator configuration. + */ +typedef ::cppu::ImplInheritanceHelper< + XCUBasedAcceleratorConfiguration, + css::lang::XServiceInfo > GlobalAcceleratorConfiguration_BASE; +class GlobalAcceleratorConfiguration : public GlobalAcceleratorConfiguration_BASE +{ +public: + + /** initialize this instance and fill the internal cache. + + @param xSMGR + reference to a uno service manager, which is used internally. + */ + explicit GlobalAcceleratorConfiguration(const css::uno::Reference< css::uno::XComponentContext >& xContext); + + virtual OUString SAL_CALL getImplementationName() override + { + return "com.sun.star.comp.framework.GlobalAcceleratorConfiguration"; + } + + virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override + { + return cppu::supportsService(this, ServiceName); + } + + virtual css::uno::Sequence SAL_CALL getSupportedServiceNames() override + { + return {"com.sun.star.ui.GlobalAcceleratorConfiguration"}; + } + + /// This has to be called after when the instance is acquire()'d. + void fillCache(); + +private: + + /** helper to listen for configuration changes without ownership cycle problems */ + css::uno::Reference< css::util::XChangesListener > m_xCfgListener; +}; + +GlobalAcceleratorConfiguration::GlobalAcceleratorConfiguration(const css::uno::Reference< css::uno::XComponentContext >& xContext) + : GlobalAcceleratorConfiguration_BASE(xContext) +{ + // force keyboard string registration. + KeyMapping::get(); +} + +void GlobalAcceleratorConfiguration::fillCache() +{ + /** read all data into the cache. */ + +#if 0 + // get current office locale ... but don't cache it. + // Otherwise we must be listener on the configuration layer + // which seems to superfluous for this small implementation .-) + // XXX: what is this good for? it was a comphelper::Locale but unused + LanguageTag aLanguageTag(m_sLocale); +#endif + + // May be there exists no accelerator config? Handle it gracefully :-) + try + { + m_sGlobalOrModules = CFG_ENTRY_GLOBAL; + XCUBasedAcceleratorConfiguration::reload(); + + css::uno::Reference< css::util::XChangesNotifier > xBroadcaster(m_xCfg, css::uno::UNO_QUERY_THROW); + m_xCfgListener = new WeakChangesListener(this); + xBroadcaster->addChangesListener(m_xCfgListener); + } + catch(const css::uno::RuntimeException&) + { throw; } + catch(const css::uno::Exception&) + {} +} + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_framework_GlobalAcceleratorConfiguration_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence const &) +{ + rtl::Reference xGAC = new GlobalAcceleratorConfiguration(context); + xGAC->fillCache(); + return cppu::acquire(xGAC.get()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/accelerators/keymapping.cxx b/framework/source/accelerators/keymapping.cxx new file mode 100644 index 0000000000..be0c4c27f2 --- /dev/null +++ b/framework/source/accelerators/keymapping.cxx @@ -0,0 +1,211 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include + +namespace framework +{ + +// helper + +KeyMapping::KeyIdentifierInfo const KeyMapping::KeyIdentifierMap[] = +{ + {css::awt::Key::NUM0 , "KEY_0" }, + {css::awt::Key::NUM1 , "KEY_1" }, + {css::awt::Key::NUM2 , "KEY_2" }, + {css::awt::Key::NUM3 , "KEY_3" }, + {css::awt::Key::NUM4 , "KEY_4" }, + {css::awt::Key::NUM5 , "KEY_5" }, + {css::awt::Key::NUM6 , "KEY_6" }, + {css::awt::Key::NUM7 , "KEY_7" }, + {css::awt::Key::NUM8 , "KEY_8" }, + {css::awt::Key::NUM9 , "KEY_9" }, + {css::awt::Key::A , "KEY_A" }, + {css::awt::Key::B , "KEY_B" }, + {css::awt::Key::C , "KEY_C" }, + {css::awt::Key::D , "KEY_D" }, + {css::awt::Key::E , "KEY_E" }, + {css::awt::Key::F , "KEY_F" }, + {css::awt::Key::G , "KEY_G" }, + {css::awt::Key::H , "KEY_H" }, + {css::awt::Key::I , "KEY_I" }, + {css::awt::Key::J , "KEY_J" }, + {css::awt::Key::K , "KEY_K" }, + {css::awt::Key::L , "KEY_L" }, + {css::awt::Key::M , "KEY_M" }, + {css::awt::Key::N , "KEY_N" }, + {css::awt::Key::O , "KEY_O" }, + {css::awt::Key::P , "KEY_P" }, + {css::awt::Key::Q , "KEY_Q" }, + {css::awt::Key::R , "KEY_R" }, + {css::awt::Key::S , "KEY_S" }, + {css::awt::Key::T , "KEY_T" }, + {css::awt::Key::U , "KEY_U" }, + {css::awt::Key::V , "KEY_V" }, + {css::awt::Key::W , "KEY_W" }, + {css::awt::Key::X , "KEY_X" }, + {css::awt::Key::Y , "KEY_Y" }, + {css::awt::Key::Z , "KEY_Z" }, + {css::awt::Key::F1 , "KEY_F1" }, + {css::awt::Key::F2 , "KEY_F2" }, + {css::awt::Key::F3 , "KEY_F3" }, + {css::awt::Key::F4 , "KEY_F4" }, + {css::awt::Key::F5 , "KEY_F5" }, + {css::awt::Key::F6 , "KEY_F6" }, + {css::awt::Key::F7 , "KEY_F7" }, + {css::awt::Key::F8 , "KEY_F8" }, + {css::awt::Key::F9 , "KEY_F9" }, + {css::awt::Key::F10 , "KEY_F10" }, + {css::awt::Key::F11 , "KEY_F11" }, + {css::awt::Key::F12 , "KEY_F12" }, + {css::awt::Key::F13 , "KEY_F13" }, + {css::awt::Key::F14 , "KEY_F14" }, + {css::awt::Key::F15 , "KEY_F15" }, + {css::awt::Key::F16 , "KEY_F16" }, + {css::awt::Key::F17 , "KEY_F17" }, + {css::awt::Key::F18 , "KEY_F18" }, + {css::awt::Key::F19 , "KEY_F19" }, + {css::awt::Key::F20 , "KEY_F20" }, + {css::awt::Key::F21 , "KEY_F21" }, + {css::awt::Key::F22 , "KEY_F22" }, + {css::awt::Key::F23 , "KEY_F23" }, + {css::awt::Key::F24 , "KEY_F24" }, + {css::awt::Key::F25 , "KEY_F25" }, + {css::awt::Key::F26 , "KEY_F26" }, + {css::awt::Key::DOWN , "KEY_DOWN" }, + {css::awt::Key::UP , "KEY_UP" }, + {css::awt::Key::LEFT , "KEY_LEFT" }, + {css::awt::Key::RIGHT , "KEY_RIGHT" }, + {css::awt::Key::HOME , "KEY_HOME" }, + {css::awt::Key::END , "KEY_END" }, + {css::awt::Key::PAGEUP , "KEY_PAGEUP" }, + {css::awt::Key::PAGEDOWN , "KEY_PAGEDOWN" }, + {css::awt::Key::RETURN , "KEY_RETURN" }, + {css::awt::Key::ESCAPE , "KEY_ESCAPE" }, + {css::awt::Key::TAB , "KEY_TAB" }, + {css::awt::Key::BACKSPACE , "KEY_BACKSPACE" }, + {css::awt::Key::SPACE , "KEY_SPACE" }, + {css::awt::Key::INSERT , "KEY_INSERT" }, + {css::awt::Key::DELETE , "KEY_DELETE" }, + {css::awt::Key::ADD , "KEY_ADD" }, + {css::awt::Key::SUBTRACT , "KEY_SUBTRACT" }, + {css::awt::Key::MULTIPLY , "KEY_MULTIPLY" }, + {css::awt::Key::DIVIDE , "KEY_DIVIDE" }, + {css::awt::Key::POINT , "KEY_POINT" }, + {css::awt::Key::COMMA , "KEY_COMMA" }, + {css::awt::Key::LESS , "KEY_LESS" }, + {css::awt::Key::GREATER , "KEY_GREATER" }, + {css::awt::Key::EQUAL , "KEY_EQUAL" }, + {css::awt::Key::OPEN , "KEY_OPEN" }, + {css::awt::Key::CUT , "KEY_CUT" }, + {css::awt::Key::COPY , "KEY_COPY" }, + {css::awt::Key::PASTE , "KEY_PASTE" }, + {css::awt::Key::UNDO , "KEY_UNDO" }, + {css::awt::Key::REPEAT , "KEY_REPEAT" }, + {css::awt::Key::FIND , "KEY_FIND" }, + {css::awt::Key::PROPERTIES , "KEY_PROPERTIES" }, + {css::awt::Key::FRONT , "KEY_FRONT" }, + {css::awt::Key::CONTEXTMENU , "KEY_CONTEXTMENU"}, + {css::awt::Key::HELP , "KEY_HELP" }, + {css::awt::Key::MENU , "KEY_MENU" }, + {css::awt::Key::HANGUL_HANJA , "KEY_HANGUL_HANJA"}, + {css::awt::Key::DECIMAL , "KEY_DECIMAL" }, + {css::awt::Key::TILDE , "KEY_TILDE" }, + {css::awt::Key::QUOTELEFT , "KEY_QUOTELEFT" }, + {css::awt::Key::BRACKETLEFT , "KEY_BRACKETLEFT" }, + {css::awt::Key::BRACKETRIGHT , "KEY_BRACKETRIGHT" }, + {css::awt::Key::SEMICOLON , "KEY_SEMICOLON" }, + {css::awt::Key::QUOTERIGHT , "KEY_QUOTERIGHT" }, + {css::awt::Key::RIGHTCURLYBRACKET, "KEY_RIGHTCURLYBRACKET" }, + {css::awt::Key::NUMBERSIGN, "KEY_NUMBERSIGN" }, + {css::awt::Key::COLON , "KEY_COLON" }, + {0 , "" } // mark the end of this array! +}; + +KeyMapping::KeyMapping() +{ + sal_Int32 i = 0; + while(KeyIdentifierMap[i].Code != 0) + { + OUString sIdentifier = OUString::createFromAscii(KeyIdentifierMap[i].Identifier); + sal_Int16 nCode = KeyIdentifierMap[i].Code; + + m_lIdentifierHash[sIdentifier] = nCode; + m_lCodeHash [nCode] = sIdentifier; + + ++i; + } +} + +KeyMapping & KeyMapping::get() { + static KeyMapping KEYS; + return KEYS; +} + +sal_uInt16 KeyMapping::mapIdentifierToCode(const OUString& sIdentifier) +{ + Identifier2CodeHash::const_iterator pIt = m_lIdentifierHash.find(sIdentifier); + if (pIt != m_lIdentifierHash.end()) + return pIt->second; + + // It's not well known identifier - but may be a pure key code formatted as string... + // Check and convert it! + sal_uInt16 nCode = 0; + if (!KeyMapping::impl_st_interpretIdentifierAsPureKeyCode(sIdentifier, nCode)) + throw css::lang::IllegalArgumentException( + "Can not map given identifier to a valid key code value.", + css::uno::Reference< css::uno::XInterface >(), + 0); + + return nCode; +} + +OUString KeyMapping::mapCodeToIdentifier(sal_uInt16 nCode) +{ + Code2IdentifierHash::const_iterator pIt = m_lCodeHash.find(nCode); + if (pIt != m_lCodeHash.end()) + return pIt->second; + + // If we have no well known identifier - use the pure code value! + return OUString::number(nCode); +} + +bool KeyMapping::impl_st_interpretIdentifierAsPureKeyCode(std::u16string_view sIdentifier, + sal_uInt16& rCode ) +{ + sal_Int32 nCode = o3tl::toInt32(sIdentifier); + if (nCode > 0) + { + rCode = static_cast(nCode); + return true; + } + + // 0 is normally an error of the called method toInt32() ... + // But we must be aware, that the identifier is "0"! + rCode = 0; + return sIdentifier == u"0"; +} + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/accelerators/moduleacceleratorconfiguration.cxx b/framework/source/accelerators/moduleacceleratorconfiguration.cxx new file mode 100644 index 0000000000..c8ce39fec4 --- /dev/null +++ b/framework/source/accelerators/moduleacceleratorconfiguration.cxx @@ -0,0 +1,157 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include + +#include + +#include +#include +#include + +#include + +#include + +using namespace framework; + +namespace { + +/** + implements a read/write access to a module + dependent accelerator configuration. + */ +typedef ::cppu::ImplInheritanceHelper< + XCUBasedAcceleratorConfiguration, + css::lang::XServiceInfo > ModuleAcceleratorConfiguration_BASE; + +class ModuleAcceleratorConfiguration : public ModuleAcceleratorConfiguration_BASE +{ +private: + /** identify the application module, where this accelerator + configuration cache should work on. */ + OUString m_sModule; + +public: + + /** initialize this instance and fill the internal cache. + + @param xSMGR + reference to a uno service manager, which is used internally. + */ + ModuleAcceleratorConfiguration( + const css::uno::Reference< css::uno::XComponentContext >& xContext, + const css::uno::Sequence< css::uno::Any >& lArguments); + + virtual OUString SAL_CALL getImplementationName() override + { + return "com.sun.star.comp.framework.ModuleAcceleratorConfiguration"; + } + + virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override + { + return cppu::supportsService(this, ServiceName); + } + + virtual css::uno::Sequence SAL_CALL getSupportedServiceNames() override + { + return {"com.sun.star.ui.ModuleAcceleratorConfiguration"}; + } + + /// This has to be called after when the instance is acquire()'d. + void fillCache(); + +private: + /** helper to listen for configuration changes without ownership cycle problems */ + css::uno::Reference< css::util::XChangesListener > m_xCfgListener; +}; + +ModuleAcceleratorConfiguration::ModuleAcceleratorConfiguration( + const css::uno::Reference< css::uno::XComponentContext >& xContext, + const css::uno::Sequence< css::uno::Any >& lArguments) + : ModuleAcceleratorConfiguration_BASE(xContext) +{ + SolarMutexGuard g; + + OUString sModule; + if (lArguments.getLength() == 1 && (lArguments[0] >>= sModule)) + { + m_sModule = sModule; + } else + { + ::comphelper::SequenceAsHashMap lArgs(lArguments); + m_sModule = lArgs.getUnpackedValueOrDefault("ModuleIdentifier", OUString()); + // OUString sLocale = lArgs.getUnpackedValueOrDefault("Locale", OUString("x-default")); + } + + if (m_sModule.isEmpty()) + throw css::uno::RuntimeException( + "The module dependent accelerator configuration service was initialized with an empty module identifier!", + static_cast< ::cppu::OWeakObject* >(this)); +} + +void ModuleAcceleratorConfiguration::fillCache() +{ + { + SolarMutexGuard g; + m_sModuleCFG = m_sModule; + } + +#if 0 + // get current office locale ... but don't cache it. + // Otherwise we must be listener on the configuration layer + // which seems to superfluous for this small implementation .-) + // XXX: what is this good for? it was a comphelper::Locale but unused + LanguageTag aLanguageTag(m_sLocale); +#endif + + // May be the current app module does not have any + // accelerator config? Handle it gracefully :-) + try + { + m_sGlobalOrModules = CFG_ENTRY_MODULES; + XCUBasedAcceleratorConfiguration::reload(); + + css::uno::Reference< css::util::XChangesNotifier > xBroadcaster(m_xCfg, css::uno::UNO_QUERY_THROW); + m_xCfgListener = new WeakChangesListener(this); + xBroadcaster->addChangesListener(m_xCfgListener); + } + catch(const css::uno::RuntimeException&) + { throw; } + catch(const css::uno::Exception&) + {} +} + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_framework_ModuleAcceleratorConfiguration_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence const &arguments) +{ + rtl::Reference inst = new ModuleAcceleratorConfiguration(context, arguments); + css::uno::XInterface *acquired_inst = cppu::acquire(inst.get()); + + inst->fillCache(); + + return acquired_inst; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/accelerators/presethandler.cxx b/framework/source/accelerators/presethandler.cxx new file mode 100644 index 0000000000..0572049ab7 --- /dev/null +++ b/framework/source/accelerators/presethandler.cxx @@ -0,0 +1,731 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +const ::sal_Int32 ID_CORRUPT_UICONFIG_SHARE = 1; +const ::sal_Int32 ID_CORRUPT_UICONFIG_USER = 2; +const ::sal_Int32 ID_CORRUPT_UICONFIG_GENERAL = 3; + +namespace framework +{ + +namespace { + +/** @short because a concurrent access to the same storage from different implementations + isn't supported, we have to share it with others. + + @descr This struct is allegedly shared and must be used within a + synchronized section. But it isn't. + */ +struct TSharedStorages final +{ + StorageHolder m_lStoragesShare; + StorageHolder m_lStoragesUser; + + TSharedStorages() + {}; +}; + +/** @short provides access to the: + a) shared root storages + b) shared "inbetween" storages + of the share and user layer. */ +TSharedStorages& SharedStorages() +{ + static TSharedStorages theStorages; + return theStorages; +} + +} + +PresetHandler::PresetHandler(css::uno::Reference< css::uno::XComponentContext > xContext) + : m_xContext(std::move(xContext)) + , m_eConfigType(E_GLOBAL) +{ +} + +PresetHandler::PresetHandler(const PresetHandler& rCopy) +{ + m_xContext = rCopy.m_xContext; + m_eConfigType = rCopy.m_eConfigType; + m_xWorkingStorageShare = rCopy.m_xWorkingStorageShare; + m_xWorkingStorageNoLang = rCopy.m_xWorkingStorageNoLang; + m_xWorkingStorageUser = rCopy.m_xWorkingStorageUser; + m_lDocumentStorages = rCopy.m_lDocumentStorages; + m_sRelPathShare = rCopy.m_sRelPathShare; + m_sRelPathUser = rCopy.m_sRelPathUser; +} + +PresetHandler::~PresetHandler() +{ + m_xWorkingStorageShare.clear(); + m_xWorkingStorageNoLang.clear(); + m_xWorkingStorageUser.clear(); + + /* #i46497# + Don't call forgetCachedStorages() here for shared storages. + Because we opened different sub storages by using openPath(). + And every already open path was reused and referenced (means it's + ref count was increased!) + So now we have to release our ref counts to these shared storages + only ... and not to free all used storages. + Otherwise we will disconnect all other open configuration access + objects which base on these storages. + */ + auto & sharedStorages = SharedStorages(); + sharedStorages.m_lStoragesShare.closePath(m_sRelPathShare); + sharedStorages.m_lStoragesUser.closePath (m_sRelPathUser ); + + /* On the other side closePath() is not needed for our special handled + document storage. Because it's not shared with others ... so we can + free it. + */ + m_lDocumentStorages.forgetCachedStorages(); +} + +void PresetHandler::forgetCachedStorages() +{ + SolarMutexGuard g; + + if (m_eConfigType == E_DOCUMENT) + { + m_xWorkingStorageShare.clear(); + m_xWorkingStorageNoLang.clear(); + m_xWorkingStorageUser.clear(); + } + + m_lDocumentStorages.forgetCachedStorages(); +} + +namespace { + +OUString lcl_getLocalizedMessage(::sal_Int32 nID) +{ + OUString sMessage("Unknown error."); + + switch(nID) + { + case ID_CORRUPT_UICONFIG_SHARE : + sMessage = FwkResId(STR_CORRUPT_UICFG_SHARE); + + break; + + case ID_CORRUPT_UICONFIG_USER : + sMessage = FwkResId(STR_CORRUPT_UICFG_USER); + break; + + case ID_CORRUPT_UICONFIG_GENERAL : + sMessage = FwkResId(STR_CORRUPT_UICFG_GENERAL); + break; + } + + return sMessage; +} + +void lcl_throwCorruptedUIConfigurationException( + css::uno::Any const & exception, sal_Int32 id) +{ + css::uno::Exception e; + bool ok = (exception >>= e); + OSL_ASSERT(ok); + throw css::configuration::CorruptedUIConfigurationException( + lcl_getLocalizedMessage(id), + css::uno::Reference< css::uno::XInterface >(), + exception.getValueTypeName() + ": \"" + e.Message + "\""); +} + +} + +css::uno::Reference< css::embed::XStorage > PresetHandler::getOrCreateRootStorageShare() +{ + auto & sharedStorages = SharedStorages(); + css::uno::Reference< css::embed::XStorage > xRoot = sharedStorages.m_lStoragesShare.getRootStorage(); + if (xRoot.is()) + return xRoot; + + css::uno::Reference< css::uno::XComponentContext > xContext; + { + SolarMutexGuard g; + xContext = m_xContext; + } + + css::uno::Reference< css::util::XPathSettings > xPathSettings = + css::util::thePathSettings::get( xContext ); + + OUString sShareLayer = xPathSettings->getBasePathShareLayer(); + + // "UIConfig" is a "multi path" ... use first part only here! + sal_Int32 nPos = sShareLayer.indexOf(';'); + if (nPos > 0) + sShareLayer = sShareLayer.copy(0, nPos); + + // Note: May be an user uses URLs without a final slash! Check it ... + nPos = sShareLayer.lastIndexOf('/'); + if (nPos != sShareLayer.getLength()-1) + sShareLayer += "/"; + + sShareLayer += "soffice.cfg"; + /* + // TODO remove me! + // Attention: This is temp. workaround ... We create a temp. storage file + // based of a system directory. This must be used so, till the storage implementation + // can work on directories too. + */ + css::uno::Sequence< css::uno::Any > lArgs{ + css::uno::Any(sShareLayer), + css::uno::Any(css::embed::ElementModes::READ | css::embed::ElementModes::NOCREATE) + }; + + css::uno::Reference< css::lang::XSingleServiceFactory > xStorageFactory = css::embed::FileSystemStorageFactory::create( xContext ); + css::uno::Reference< css::embed::XStorage > xStorage; + + try + { + xStorage.set(xStorageFactory->createInstanceWithArguments(lArgs), css::uno::UNO_QUERY_THROW); + } + catch(const css::uno::Exception&) + { + css::uno::Any ex(cppu::getCaughtException()); + lcl_throwCorruptedUIConfigurationException( + ex, ID_CORRUPT_UICONFIG_SHARE); + } + + sharedStorages.m_lStoragesShare.setRootStorage(xStorage); + + return xStorage; +} + +css::uno::Reference< css::embed::XStorage > PresetHandler::getOrCreateRootStorageUser() +{ + auto & sharedStorages = SharedStorages(); + css::uno::Reference< css::embed::XStorage > xRoot = sharedStorages.m_lStoragesUser.getRootStorage(); + if (xRoot.is()) + return xRoot; + + css::uno::Reference< css::uno::XComponentContext > xContext; + { + SolarMutexGuard g; + xContext = m_xContext; + } + + css::uno::Reference< css::util::XPathSettings > xPathSettings = + css::util::thePathSettings::get( xContext ); + + OUString sUserLayer = xPathSettings->getBasePathUserLayer(); + + // Note: May be an user uses URLs without a final slash! Check it ... + sal_Int32 nPos = sUserLayer.lastIndexOf('/'); + if (nPos != sUserLayer.getLength()-1) + sUserLayer += "/"; + + sUserLayer += "soffice.cfg"; // storage file + + css::uno::Sequence< css::uno::Any > lArgs{ css::uno::Any(sUserLayer), + css::uno::Any(css::embed::ElementModes::READWRITE) }; + + css::uno::Reference< css::lang::XSingleServiceFactory > xStorageFactory = css::embed::FileSystemStorageFactory::create( xContext ); + css::uno::Reference< css::embed::XStorage > xStorage; + + try + { + xStorage.set(xStorageFactory->createInstanceWithArguments(lArgs), css::uno::UNO_QUERY_THROW); + } + catch(const css::uno::Exception&) + { + css::uno::Any ex(cppu::getCaughtException()); + lcl_throwCorruptedUIConfigurationException( + ex, ID_CORRUPT_UICONFIG_USER); + } + + sharedStorages.m_lStoragesUser.setRootStorage(xStorage); + + return xStorage; +} + +css::uno::Reference< css::embed::XStorage > PresetHandler::getWorkingStorageUser() const +{ + SolarMutexGuard g; + return m_xWorkingStorageUser; +} + +css::uno::Reference< css::embed::XStorage > PresetHandler::getParentStorageShare() +{ + css::uno::Reference< css::embed::XStorage > xWorking; + { + SolarMutexGuard g; + xWorking = m_xWorkingStorageShare; + } + + return SharedStorages().m_lStoragesShare.getParentStorage(xWorking); +} + +css::uno::Reference< css::embed::XStorage > PresetHandler::getParentStorageUser() +{ + css::uno::Reference< css::embed::XStorage > xWorking; + { + SolarMutexGuard g; + xWorking = m_xWorkingStorageUser; + } + + return SharedStorages().m_lStoragesUser.getParentStorage(xWorking); +} + +void PresetHandler::connectToResource( PresetHandler::EConfigType eConfigType , + std::u16string_view sResource , + std::u16string_view sModule , + const css::uno::Reference< css::embed::XStorage >& xDocumentRoot, + const LanguageTag& rLanguageTag ) +{ + // TODO free all current open storages! + + { + SolarMutexGuard g; + m_eConfigType = eConfigType; + } + + css::uno::Reference< css::embed::XStorage > xShare; + css::uno::Reference< css::embed::XStorage > xNoLang; + css::uno::Reference< css::embed::XStorage > xUser; + + // special case for documents + // use outside root storage, if we run in E_DOCUMENT mode! + if (eConfigType == E_DOCUMENT) + { + if (!xDocumentRoot.is()) + throw css::uno::RuntimeException( + "There is valid root storage, where the UI configuration can work on."); + m_lDocumentStorages.setRootStorage(xDocumentRoot); + xShare = xDocumentRoot; + xUser = xDocumentRoot; + } + else + { + xShare = getOrCreateRootStorageShare(); + xUser = getOrCreateRootStorageUser(); + } + + // #...# + try + { + + // a) inside share layer we should not create any new structures... We have to use + // existing ones only! + // b) inside user layer we can (SOFT mode!) but sometimes we should not (HARD mode!) + // create new empty structures. We should prefer using of any existing structure. + sal_Int32 eShareMode = (css::embed::ElementModes::READ | css::embed::ElementModes::NOCREATE); + sal_Int32 eUserMode = css::embed::ElementModes::READWRITE; + + OUStringBuffer sRelPathBuf(1024); + OUString sRelPathShare; + OUString sRelPathUser; + switch(eConfigType) + { + case E_GLOBAL : + { + sRelPathShare = OUString::Concat("global/") + sResource; + sRelPathUser = sRelPathShare; + + xShare = impl_openPathIgnoringErrors(sRelPathShare, eShareMode, true ); + xUser = impl_openPathIgnoringErrors(sRelPathUser , eUserMode , false); + } + break; + + case E_MODULES : + { + sRelPathShare = OUString::Concat("modules/") + sModule + "/" + sResource; + sRelPathUser = sRelPathShare; + + xShare = impl_openPathIgnoringErrors(sRelPathShare, eShareMode, true ); + xUser = impl_openPathIgnoringErrors(sRelPathUser , eUserMode , false); + } + break; + + case E_DOCUMENT : + { + // A document does not have a share layer in real. + // It has one layer only, and this one should be opened READ_WRITE. + // So we open the user layer here only and set the share layer equals to it .-) + + sRelPathBuf.append(sResource); + sRelPathUser = sRelPathBuf.makeStringAndClear(); + sRelPathShare = sRelPathUser; + + try + { + xUser = m_lDocumentStorages.openPath(sRelPathUser , eUserMode ); + xShare = xUser; + } + catch(const css::uno::RuntimeException&) + { throw; } + catch(const css::uno::Exception&) + { xShare.clear(); xUser.clear(); } + } + break; + } + + // Non-localized global share + xNoLang = xShare; + + if ( + (rLanguageTag != LanguageTag(LANGUAGE_USER_PRIV_NOTRANSLATE)) && // localized level? + (eConfigType != E_DOCUMENT ) // no localization in document mode! + ) + { + // First try to find the right localized set inside share layer. + // Fallbacks are allowed there. + OUString aShareLocale( rLanguageTag.getBcp47()); + OUString sLocalizedSharePath(sRelPathShare); + bool bAllowFallbacks = true; + xShare = impl_openLocalizedPathIgnoringErrors(sLocalizedSharePath, eShareMode, true , aShareLocale, bAllowFallbacks); + + // The try to locate the right sub dir inside user layer ... without using fallbacks! + // Normally the corresponding sub dir should be created matching the specified locale. + // Because we allow creation of storages inside user layer by default. + OUString aUserLocale( rLanguageTag.getBcp47()); + OUString sLocalizedUserPath(sRelPathUser); + bAllowFallbacks = false; + xUser = impl_openLocalizedPathIgnoringErrors(sLocalizedUserPath, eUserMode, false, aUserLocale, bAllowFallbacks); + + sRelPathShare = sLocalizedSharePath; + sRelPathUser = sLocalizedUserPath; + } + + { + SolarMutexGuard g; + m_xWorkingStorageShare = xShare; + m_xWorkingStorageNoLang= xNoLang; + m_xWorkingStorageUser = xUser; + m_sRelPathShare = sRelPathShare; + m_sRelPathUser = sRelPathUser; + } + + } + catch(const css::uno::Exception&) + { + css::uno::Any ex(cppu::getCaughtException()); + lcl_throwCorruptedUIConfigurationException( + ex, ID_CORRUPT_UICONFIG_GENERAL); + } +} + +void PresetHandler::copyPresetToTarget(std::u16string_view sPreset, + std::u16string_view sTarget) +{ + // don't check our preset list, if element exists + // We try to open it and forward all errors to the user! + + css::uno::Reference< css::embed::XStorage > xWorkingShare; + css::uno::Reference< css::embed::XStorage > xWorkingNoLang; + css::uno::Reference< css::embed::XStorage > xWorkingUser; + { + SolarMutexGuard g; + xWorkingShare = m_xWorkingStorageShare; + xWorkingNoLang= m_xWorkingStorageNoLang; + xWorkingUser = m_xWorkingStorageUser; + } + + // e.g. module without any config data ?! + if ( + (!xWorkingShare.is()) || + (!xWorkingUser.is() ) + ) + { + return; + } + + OUString sPresetFile = OUString::Concat(sPreset) + ".xml"; + OUString sTargetFile = OUString::Concat(sTarget) + ".xml"; + + // remove existing elements before you try to copy the preset to that location ... + // Otherwise w will get an ElementExistException inside copyElementTo()! + css::uno::Reference< css::container::XNameAccess > xCheckingUser(xWorkingUser, css::uno::UNO_QUERY_THROW); + if (xCheckingUser->hasByName(sTargetFile)) + xWorkingUser->removeElement(sTargetFile); + + xWorkingShare->copyElementTo(sPresetFile, xWorkingUser, sTargetFile); + + // If our storages work in transacted mode, we have + // to commit all changes from bottom to top! + commitUserChanges(); +} + +css::uno::Reference< css::io::XStream > PresetHandler::openPreset(std::u16string_view sPreset) +{ + css::uno::Reference< css::embed::XStorage > xFolder; + { + SolarMutexGuard g; + xFolder = m_xWorkingStorageNoLang; + } + + // e.g. module without any config data ?! + if (!xFolder.is()) + return css::uno::Reference< css::io::XStream >(); + + OUString sFile = OUString::Concat(sPreset) + ".xml"; + + // inform user about errors (use original exceptions!) + css::uno::Reference< css::io::XStream > xStream = xFolder->openStreamElement(sFile, css::embed::ElementModes::READ); + return xStream; +} + +css::uno::Reference< css::io::XStream > PresetHandler::openTarget( + std::u16string_view sTarget, sal_Int32 const nMode) +{ + css::uno::Reference< css::embed::XStorage > xFolder; + { + SolarMutexGuard g; + xFolder = m_xWorkingStorageUser; + } + + // e.g. module without any config data ?! + if (!xFolder.is()) + return css::uno::Reference< css::io::XStream >(); + + OUString const sFile(OUString::Concat(sTarget) + ".xml"); + + return xFolder->openStreamElement(sFile, nMode); +} + +void PresetHandler::commitUserChanges() +{ + css::uno::Reference< css::embed::XStorage > xWorking; + EConfigType eCfgType; + { + SolarMutexGuard g; + xWorking = m_xWorkingStorageUser; + eCfgType = m_eConfigType; + } + + // e.g. module without any config data ?! + if (!xWorking.is()) + return; + + OUString sPath; + + switch(eCfgType) + { + case E_GLOBAL : + case E_MODULES : + { + auto & sharedStorages = SharedStorages(); + sPath = sharedStorages.m_lStoragesUser.getPathOfStorage(xWorking); + sharedStorages.m_lStoragesUser.commitPath(sPath); + sharedStorages.m_lStoragesUser.notifyPath(sPath); + } + break; + + case E_DOCUMENT : + { + sPath = m_lDocumentStorages.getPathOfStorage(xWorking); + m_lDocumentStorages.commitPath(sPath); + m_lDocumentStorages.notifyPath(sPath); + } + break; + } +} + +void PresetHandler::addStorageListener(XMLBasedAcceleratorConfiguration* pListener) +{ + OUString sRelPath; + EConfigType eCfgType; + { + SolarMutexGuard g; + sRelPath = m_sRelPathUser; // use user path ... because we don't work directly on the share layer! + eCfgType = m_eConfigType; + } + + if (sRelPath.isEmpty()) + return; + + switch(eCfgType) + { + case E_GLOBAL : + case E_MODULES : + { + SharedStorages().m_lStoragesUser.addStorageListener(pListener, sRelPath); + } + break; + + case E_DOCUMENT : + { + m_lDocumentStorages.addStorageListener(pListener, sRelPath); + } + break; + } +} + +void PresetHandler::removeStorageListener(XMLBasedAcceleratorConfiguration* pListener) +{ + OUString sRelPath; + EConfigType eCfgType; + { + SolarMutexGuard g; + sRelPath = m_sRelPathUser; // use user path ... because we don't work directly on the share layer! + eCfgType = m_eConfigType; + } + + if (sRelPath.isEmpty()) + return; + + switch(eCfgType) + { + case E_GLOBAL : + case E_MODULES : + { + SharedStorages().m_lStoragesUser.removeStorageListener(pListener, sRelPath); + } + break; + + case E_DOCUMENT : + { + m_lDocumentStorages.removeStorageListener(pListener, sRelPath); + } + break; + } +} + +css::uno::Reference< css::embed::XStorage > PresetHandler::impl_openPathIgnoringErrors(const OUString& sPath , + sal_Int32 eMode , + bool bShare) +{ + css::uno::Reference< css::embed::XStorage > xPath; + try + { + if (bShare) + xPath = SharedStorages().m_lStoragesShare.openPath(sPath, eMode); + else + xPath = SharedStorages().m_lStoragesUser.openPath(sPath, eMode); + } + catch(const css::uno::RuntimeException&) + { throw; } + catch(const css::uno::Exception&) + { xPath.clear(); } + return xPath; +} + +::std::vector< OUString >::const_iterator PresetHandler::impl_findMatchingLocalizedValue( + const ::std::vector< OUString >& lLocalizedValues, + OUString& rLanguageTag, + bool bAllowFallbacks ) +{ + ::std::vector< OUString >::const_iterator pFound = lLocalizedValues.end(); + if (bAllowFallbacks) + { + pFound = LanguageTag::getFallback(lLocalizedValues, rLanguageTag); + // if we found a valid locale ... take it over to our in/out parameter + // rLanguageTag + if (pFound != lLocalizedValues.end()) + { + rLanguageTag = *pFound; + } + } + else + { + pFound = std::find(lLocalizedValues.begin(), lLocalizedValues.end(), rLanguageTag); + } + + return pFound; +} + +css::uno::Reference< css::embed::XStorage > PresetHandler::impl_openLocalizedPathIgnoringErrors( + OUString& sPath , + sal_Int32 eMode , + bool bShare , + OUString& rLanguageTag , + bool bAllowFallback) +{ + css::uno::Reference< css::embed::XStorage > xPath = impl_openPathIgnoringErrors(sPath, eMode, bShare); + ::std::vector< OUString > lSubFolders = impl_getSubFolderNames(xPath); + ::std::vector< OUString >::const_iterator pLocaleFolder = impl_findMatchingLocalizedValue(lSubFolders, rLanguageTag, bAllowFallback); + + // no fallback ... creation not allowed => no storage + if ( + (pLocaleFolder == lSubFolders.end() ) && + ((eMode & css::embed::ElementModes::NOCREATE) == css::embed::ElementModes::NOCREATE) + ) + return css::uno::Reference< css::embed::XStorage >(); + + // it doesn't matter, if there is a locale fallback or not + // If creation of storages is allowed, we do it anyway. + // Otherwise we have no acc config at all, which can make other trouble. + OUString sLocalizedPath = sPath + "/"; + if (pLocaleFolder != lSubFolders.end()) + sLocalizedPath += *pLocaleFolder; + else + sLocalizedPath += rLanguageTag; + + css::uno::Reference< css::embed::XStorage > xLocalePath = impl_openPathIgnoringErrors(sLocalizedPath, eMode, bShare); + + if (xLocalePath.is()) + sPath = sLocalizedPath; + else + sPath.clear(); + + return xLocalePath; +} + +::std::vector< OUString > PresetHandler::impl_getSubFolderNames(const css::uno::Reference< css::embed::XStorage >& xFolder) +{ + if (!xFolder.is()) + return ::std::vector< OUString >(); + + ::std::vector< OUString > lSubFolders; + const css::uno::Sequence< OUString > lNames = xFolder->getElementNames(); + const OUString* pNames = lNames.getConstArray(); + sal_Int32 c = lNames.getLength(); + sal_Int32 i = 0; + + for (i=0; iisStorageElement(pNames[i])) + lSubFolders.push_back(pNames[i]); + } + catch(const css::uno::RuntimeException&) + { throw; } + catch(const css::uno::Exception&) + {} + } + + return lSubFolders; +} + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/accelerators/storageholder.cxx b/framework/source/accelerators/storageholder.cxx new file mode 100644 index 0000000000..6cef699bfb --- /dev/null +++ b/framework/source/accelerators/storageholder.cxx @@ -0,0 +1,447 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include + +#include + +#include + +#include +#include + +#include + +constexpr OUString PATH_SEPARATOR = u"/"_ustr; +#define PATH_SEPARATOR_UNICODE u'/' + +namespace framework +{ + +StorageHolder::StorageHolder() +{ +} + +StorageHolder::~StorageHolder() +{ + // TODO implement me + // dispose/clear etcpp. +} + +void StorageHolder::forgetCachedStorages() +{ + std::unique_lock g(m_mutex); + for (auto & lStorage : m_lStorages) + { + TStorageInfo& rInfo = lStorage.second; + // TODO think about listener ! + rInfo.Storage.clear(); + } + m_lStorages.clear(); +} + +void StorageHolder::setRootStorage(const css::uno::Reference< css::embed::XStorage >& xRoot) +{ + std::unique_lock g(m_mutex); + m_xRoot = xRoot; +} + +css::uno::Reference< css::embed::XStorage > StorageHolder::getRootStorage() const +{ + std::unique_lock g(m_mutex); + return m_xRoot; +} + +css::uno::Reference< css::embed::XStorage > StorageHolder::openPath(const OUString& sPath , + sal_Int32 nOpenMode) +{ + OUString sNormedPath = StorageHolder::impl_st_normPath(sPath); + std::vector lFolders = StorageHolder::impl_st_parsePath(sNormedPath); + + // SAFE -> ---------------------------------- + std::unique_lock aReadLock(m_mutex); + css::uno::Reference< css::embed::XStorage > xParent = m_xRoot; + aReadLock.unlock(); + // <- SAFE ---------------------------------- + + css::uno::Reference< css::embed::XStorage > xChild; + OUString sRelPath; + + for (auto const& lFolder : lFolders) + { + OUString sCheckPath (sRelPath + lFolder + PATH_SEPARATOR); + + // SAFE -> ------------------------------ + aReadLock.lock(); + + // If we found an already open storage ... we must increase + // its use count. Otherwise it will may be closed too early :-) + TPath2StorageInfo::iterator pCheck = m_lStorages.find(sCheckPath); + TStorageInfo* pInfo = nullptr; + if (pCheck != m_lStorages.end()) + { + pInfo = &(pCheck->second); + ++(pInfo->UseCount); + xChild = pInfo->Storage; + + aReadLock.unlock(); + // <- SAFE ------------------------------ + } + else + { + aReadLock.unlock(); + // <- SAFE ------------------------------ + + try + { + xChild = StorageHolder::openSubStorageWithFallback(xParent, lFolder, nOpenMode); // TODO think about delegating fallback decision to our own caller! + } + catch(const css::uno::RuntimeException&) + { throw; } + catch(const css::uno::Exception&) + { + /* TODO URGENT! + in case we found some "already existing storages" on the path before and increased its UseCount ... + and now we will get an exception on creating a new sub storage ... + we must decrease all UseCounts, which was touched before. Otherwise these storages can't be closed! + + Idea: Using of another structure member "PossibleUseCount" as vector of unique numbers. + Every thread use another unique number to identify all "owned candidates". + A flush method with the same unique number force increasing of the "UseCount" variable then + inside a synchronized block ... + */ + throw; + } + + std::unique_lock g(m_mutex); + pInfo = &(m_lStorages[sCheckPath]); + pInfo->Storage = xChild; + pInfo->UseCount = 1; + } + + xParent = xChild; + sRelPath += lFolder + PATH_SEPARATOR; + } + + // TODO think about return last storage as working storage ... but don't caching it inside this holder! + // => otherwise the same storage is may be commit more than once. + + return xChild; +} + +StorageHolder::TStorageList StorageHolder::getAllPathStorages(const OUString& sPath) +{ + OUString sNormedPath = StorageHolder::impl_st_normPath(sPath); + std::vector lFolders = StorageHolder::impl_st_parsePath(sNormedPath); + + StorageHolder::TStorageList lStoragesOfPath; + OUString sRelPath; + + std::unique_lock g(m_mutex); + + for (auto const& lFolder : lFolders) + { + OUString sCheckPath (sRelPath + lFolder + PATH_SEPARATOR); + + TPath2StorageInfo::iterator pCheck = m_lStorages.find(sCheckPath); + if (pCheck == m_lStorages.end()) + { + // at least one path element was not found + // Seems that this path isn't open ... + lStoragesOfPath.clear(); + return lStoragesOfPath; + } + + TStorageInfo& rInfo = pCheck->second; + lStoragesOfPath.push_back(rInfo.Storage); + + sRelPath += lFolder + PATH_SEPARATOR; + } + + return lStoragesOfPath; +} + +void StorageHolder::commitPath(const OUString& sPath) +{ + StorageHolder::TStorageList lStorages = getAllPathStorages(sPath); + + css::uno::Reference< css::embed::XTransactedObject > xCommit; + StorageHolder::TStorageList::reverse_iterator pIt; + for ( pIt = lStorages.rbegin(); // order of commit is important ... otherwise changes are not recognized! + pIt != lStorages.rend(); + ++pIt ) + { + xCommit.set(*pIt, css::uno::UNO_QUERY); + if (!xCommit.is()) + continue; + xCommit->commit(); + } + + // SAFE -> ------------------------------ + { + std::unique_lock aReadLock(m_mutex); + xCommit.set(m_xRoot, css::uno::UNO_QUERY); + } + // <- SAFE ------------------------------ + + if (xCommit.is()) + xCommit->commit(); +} + +void StorageHolder::closePath(const OUString& rPath) +{ + OUString sNormedPath = StorageHolder::impl_st_normPath(rPath); + std::vector lFolders = StorageHolder::impl_st_parsePath(sNormedPath); + + /* convert list of paths in the following way: + [0] = "path_1" => "path_1 + [1] = "path_2" => "path_1/path_2" + [2] = "path_3" => "path_1/path_2/path_3" + */ + OUString sParentPath; + for (auto & lFolder : lFolders) + { + OUString sCurrentRelPath(sParentPath + lFolder + PATH_SEPARATOR); + lFolder = sCurrentRelPath; + sParentPath = sCurrentRelPath; + } + + std::unique_lock g(m_mutex); + + std::vector::reverse_iterator pIt2; + for ( pIt2 = lFolders.rbegin(); + pIt2 != lFolders.rend(); + ++pIt2 ) + { + OUString sPath = *pIt2; + TPath2StorageInfo::iterator pPath = m_lStorages.find(sPath); + if (pPath == m_lStorages.end()) + continue; // ??? + + TStorageInfo& rInfo = pPath->second; + --rInfo.UseCount; + if (rInfo.UseCount < 1) + { + rInfo.Storage.clear(); + m_lStorages.erase(pPath); + } + } +} + +void StorageHolder::notifyPath(const OUString& sPath) +{ + OUString sNormedPath = StorageHolder::impl_st_normPath(sPath); + + std::unique_lock g(m_mutex); + + TPath2StorageInfo::iterator pIt1 = m_lStorages.find(sNormedPath); + if (pIt1 == m_lStorages.end()) + return; + + TStorageInfo& rInfo = pIt1->second; + for (auto const& listener : rInfo.Listener) + { + if (listener) + listener->changesOccurred(); + } +} + +void StorageHolder::addStorageListener( XMLBasedAcceleratorConfiguration* pListener, + const OUString& sPath ) +{ + OUString sNormedPath = StorageHolder::impl_st_normPath(sPath); + + std::unique_lock g(m_mutex); + + TPath2StorageInfo::iterator pIt1 = m_lStorages.find(sNormedPath); + if (pIt1 == m_lStorages.end()) + return; + + TStorageInfo& rInfo = pIt1->second; + TStorageListenerList::iterator pIt2 = ::std::find(rInfo.Listener.begin(), rInfo.Listener.end(), pListener); + if (pIt2 == rInfo.Listener.end()) + rInfo.Listener.push_back(pListener); +} + +void StorageHolder::removeStorageListener( XMLBasedAcceleratorConfiguration* pListener, + const OUString& sPath ) +{ + OUString sNormedPath = StorageHolder::impl_st_normPath(sPath); + + std::unique_lock g(m_mutex); + + TPath2StorageInfo::iterator pIt1 = m_lStorages.find(sNormedPath); + if (pIt1 == m_lStorages.end()) + return; + + TStorageInfo& rInfo = pIt1->second; + TStorageListenerList::iterator pIt2 = ::std::find(rInfo.Listener.begin(), rInfo.Listener.end(), pListener); + if (pIt2 != rInfo.Listener.end()) + rInfo.Listener.erase(pIt2); +} + +OUString StorageHolder::getPathOfStorage(const css::uno::Reference< css::embed::XStorage >& xStorage) +{ + std::unique_lock g(m_mutex); + + for (auto const& lStorage : m_lStorages) + { + const TStorageInfo& rInfo = lStorage.second; + if (rInfo.Storage == xStorage) + return lStorage.first; + } + + return OUString(); +} + +css::uno::Reference< css::embed::XStorage > StorageHolder::getParentStorage(const css::uno::Reference< css::embed::XStorage >& xChild) +{ + OUString sChildPath = getPathOfStorage(xChild); + return getParentStorage(sChildPath); +} + +css::uno::Reference< css::embed::XStorage > StorageHolder::getParentStorage(const OUString& sChildPath) +{ + // normed path = "a/b/c/" ... we search for "a/b/" + OUString sNormedPath = StorageHolder::impl_st_normPath(sChildPath); + std::vector lFolders = StorageHolder::impl_st_parsePath(sNormedPath); + sal_Int32 c = lFolders.size(); + + // a) "" => - => no parent + // b) "a/b/c/" => "a/b/" => return storage "a/b/" + // c) "a/" => "" => return root ! + + // a) + if (c < 1) + return css::uno::Reference< css::embed::XStorage >(); + + // SAFE -> ---------------------------------- + { + std::unique_lock aReadLock(m_mutex); + + // b) + if (c < 2) + return m_xRoot; + + // c) + OUStringBuffer sParentPath(64); + sal_Int32 i = 0; + for (i = 0; i < c - 1; ++i) + { + sParentPath.append(lFolders[i] + PATH_SEPARATOR); + } + + auto pParent = m_lStorages.find(sParentPath.makeStringAndClear()); + if (pParent != m_lStorages.end()) + return pParent->second.Storage; + } + // <- SAFE ---------------------------------- + + // ? + SAL_INFO("fwk", "StorageHolder::getParentStorage(): Unexpected situation. Cached storage item seems to be wrong."); + return css::uno::Reference< css::embed::XStorage >(); +} + +StorageHolder& StorageHolder::operator=(const StorageHolder& rCopy) +{ + std::unique_lock g(m_mutex); + m_xRoot = rCopy.m_xRoot; + m_lStorages = rCopy.m_lStorages; + return *this; +} + +css::uno::Reference< css::embed::XStorage > StorageHolder::openSubStorageWithFallback(const css::uno::Reference< css::embed::XStorage >& xBaseStorage , + const OUString& sSubStorage , + sal_Int32 eOpenMode) +{ + // a) try it first with user specified open mode + // ignore errors ... but save it for later use! + try + { + css::uno::Reference< css::embed::XStorage > xSubStorage = xBaseStorage->openStorageElement(sSubStorage, eOpenMode); + if (xSubStorage.is()) + return xSubStorage; + } + catch(const css::uno::RuntimeException&) + { + throw; + } + catch(const css::uno::Exception&) + { + // b) readonly already tried? => forward last error! + if ((eOpenMode & css::embed::ElementModes::WRITE) != css::embed::ElementModes::WRITE) // fallback possible ? + throw; + } + + // b) readonly already tried, throw error + if ((eOpenMode & css::embed::ElementModes::WRITE) != css::embed::ElementModes::WRITE) // fallback possible ? + throw css::uno::Exception(); + + // c) try it readonly + // don't catch exception here! Outside code wish to know, if operation failed or not. + // Otherwise they work on NULL references ... + sal_Int32 eNewMode = (eOpenMode & ~css::embed::ElementModes::WRITE); + css::uno::Reference< css::embed::XStorage > xSubStorage = xBaseStorage->openStorageElement(sSubStorage, eNewMode); + if (xSubStorage.is()) + return xSubStorage; + + // d) no chance! + SAL_INFO("fwk", "openSubStorageWithFallback(): Unexpected situation! Got no exception for missing storage ..."); + return css::uno::Reference< css::embed::XStorage >(); +} + +OUString StorageHolder::impl_st_normPath(const OUString& sPath) +{ + // path must start without "/" but end with "/"! + + OUString sNormedPath = sPath; + + // "/bla" => "bla" && "/" => "" (!) + (void)sNormedPath.startsWith(PATH_SEPARATOR, &sNormedPath); + + // "/" => "" || "" => "" ? + if (sNormedPath.isEmpty()) + return OUString(); + + // "bla" => "bla/" + if (sNormedPath.lastIndexOf(PATH_SEPARATOR_UNICODE) != (sNormedPath.getLength()-1)) + sNormedPath += PATH_SEPARATOR; + + return sNormedPath; +} + +std::vector StorageHolder::impl_st_parsePath(std::u16string_view sPath) +{ + std::vector lToken; + sal_Int32 i = 0; + while (true) + { + OUString sToken( o3tl::getToken(sPath, 0, PATH_SEPARATOR_UNICODE, i) ); + if (i < 0) + break; + lToken.push_back(sToken); + } + return lToken; +} + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/classes/framecontainer.cxx b/framework/source/classes/framecontainer.cxx new file mode 100644 index 0000000000..a23e3633e1 --- /dev/null +++ b/framework/source/classes/framecontainer.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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include + +#include +#include +#include + +namespace framework +{ +/**-*************************************************************************************************************** + @short initialize an empty container + @descr The container will be empty then - special features (e.g. the async quit mechanism) are disabled. + + @threadsafe not necessary - it's not a singleton + *****************************************************************************************************************/ +FrameContainer::FrameContainer() +/*DEPRECATEME + , m_bAsyncQuit ( sal_False ) // default must be "disabled"! + , m_aAsyncCall ( LINK( this, FrameContainer, implts_asyncQuit ) ) +*/ +{ +} + +/**-*************************************************************************************************************** + @short deinitialize may a filled container + @descr Special features (if the currently are running) will be disabled and we free all used other resources. + + @threadsafe not necessary - it's not a singleton + *****************************************************************************************************************/ +FrameContainer::~FrameContainer() +{ + // Don't forget to free memory! + m_aContainer.clear(); + m_xActiveFrame.clear(); +} + +/**-*************************************************************************************************************** + @short append a new frame to the container + @descr We accept the incoming frame only, if it is a valid reference and doesn't exist already. + + @param xFrame + frame, which should be added to this container + Must be a valid reference. + + @threadsafe yes + *****************************************************************************************************************/ +void FrameContainer::append(const css::uno::Reference& xFrame) +{ + if (xFrame.is() && !exist(xFrame)) + { + SolarMutexGuard g; + m_aContainer.push_back(xFrame); + } +} + +/**-*************************************************************************************************************** + @short remove a frame from the container + @descr In case we remove the last frame and our internal special feature (the async quit mechanism) + was enabled by the desktop instance, we start it. + + @param xFrame + frame, which should be deleted from this container + Must be a valid reference. + + @threadsafe yes + *****************************************************************************************************************/ +void FrameContainer::remove(const css::uno::Reference& xFrame) +{ + SolarMutexGuard g; + + TFrameContainer::iterator aSearchedItem + = ::std::find(m_aContainer.begin(), m_aContainer.end(), xFrame); + if (aSearchedItem != m_aContainer.end()) + { + m_aContainer.erase(aSearchedItem); + + // If removed frame was the current active frame - reset state variable. + if (m_xActiveFrame == xFrame) + m_xActiveFrame.clear(); + } +} + +/**-*************************************************************************************************************** + @short check if the given frame currently exist inside the container + @param xFrame + reference to the queried frame + + @return if frame is part of this container + otherwise + + @threadsafe yes + *****************************************************************************************************************/ +bool FrameContainer::exist(const css::uno::Reference& xFrame) const +{ + SolarMutexGuard g; + return (::std::find(m_aContainer.begin(), m_aContainer.end(), xFrame) != m_aContainer.end()); +} + +/**-*************************************************************************************************************** + @short delete all existing items of the container + @threadsafe yes + *****************************************************************************************************************/ +void FrameContainer::clear() +{ + SolarMutexGuard g; + // Clear the container ... + m_aContainer.clear(); + // ... and don't forget to reset the active frame. + // It's a reference to a valid container-item. + // But no container item => no active frame! + m_xActiveFrame.clear(); +} + +/**-*************************************************************************************************************** + @short returns count of all current existing frames + @deprecated This value can't be guaranteed for multithreading environments. + So it will be marked as deprecated and should be replaced by "getAllElements()". + + @return the count of existing container items + + @threadsafe yes + *****************************************************************************************************************/ +sal_uInt32 FrameContainer::getCount() const +{ + SolarMutexGuard g; + return static_cast(m_aContainer.size()); +} + +/**-*************************************************************************************************************** + @short returns one item of this container + @deprecated This value can't be guaranteed for multithreading environments. + So it will be marked as deprecated and should be replaced by "getAllElements()". + + @param nIndex + a value between 0 and (getCount()-1) to address one container item + + @return a reference to a frame inside the container, which match with given index + + @threadsafe yes + *****************************************************************************************************************/ +css::uno::Reference FrameContainer::operator[](sal_uInt32 nIndex) const +{ + css::uno::Reference xFrame; + try + { + // Get element form container WITH automatic test of ranges! + // If index not valid, an out_of_range exception is thrown. + SolarMutexGuard g; + xFrame = m_aContainer.at(nIndex); + } + catch (const std::out_of_range&) + { + // The index is not valid for current container-content - we must handle this case! + // We can return the default value ... + SAL_INFO("fwk", "FrameContainer::operator[]: Exception caught: std::out_of_range"); + } + return xFrame; +} + +/**-*************************************************************************************************************** + @short returns a snapshot of all currently existing frames inside this container + @descr Should be used to replace the deprecated functions getCount()/operator[]! + + @return a list of all frame references inside this container + + @threadsafe yes + *****************************************************************************************************************/ +css::uno::Sequence> FrameContainer::getAllElements() const +{ + SolarMutexGuard g; + return comphelper::containerToSequence(m_aContainer); +} + +/**-*************************************************************************************************************** + @short set the given frame as the new active one inside this container + @descr We accept this frame only, if it's already a part of this container. + + @param xFrame + reference to the new active frame + Must be a valid reference and already part of this container. + + @threadsafe yes + *****************************************************************************************************************/ +void FrameContainer::setActive(const css::uno::Reference& xFrame) +{ + if (!xFrame.is() || exist(xFrame)) + { + SolarMutexGuard g; + m_xActiveFrame = xFrame; + } +} + +/**-*************************************************************************************************************** + @short return the current active frame of this container + @descr Value can be null in case the frame was removed from the container and nobody + from outside decide which of all others should be the new one... + + @return a reference to the current active frame + Value can be NULL! + + @threadsafe yes + *****************************************************************************************************************/ +css::uno::Reference FrameContainer::getActive() const +{ + SolarMutexGuard g; + return m_xActiveFrame; +} + +/**-*************************************************************************************************************** + @short implements a simple search based on current container items + @descr It can be used for findFrame() and implements a deep down search. + + @param sName + target name, which is searched + + @return reference to the found frame or NULL if not. + + @threadsafe yes + *****************************************************************************************************************/ +css::uno::Reference +FrameContainer::searchOnAllChildrens(const OUString& sName) const +{ + SolarMutexGuard g; + // Step over all child frames. But if direct child isn't the right one search on his children first - before + // you go to next direct child of this container! + css::uno::Reference xSearchedFrame; + for (auto const& container : m_aContainer) + { + if (container->getName() == sName) + { + xSearchedFrame = container; + break; + } + else + { + xSearchedFrame = container->findFrame(sName, css::frame::FrameSearchFlag::CHILDREN); + if (xSearchedFrame.is()) + break; + } + } + return xSearchedFrame; +} + +/**-*************************************************************************************************************** + @short implements a simple search based on current container items + @descr It can be used for findFrame() and search on members of this container only! + + @param sName + target name, which is searched + + @return reference to the found frame or NULL if not. + + @threadsafe yes + *****************************************************************************************************************/ +css::uno::Reference +FrameContainer::searchOnDirectChildrens(std::u16string_view sName) const +{ + SolarMutexGuard g; + css::uno::Reference xSearchedFrame; + for (auto const& container : m_aContainer) + { + if (container->getName() == sName) + { + xSearchedFrame = container; + break; + } + } + return xSearchedFrame; +} + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/classes/taskcreator.cxx b/framework/source/classes/taskcreator.cxx new file mode 100644 index 0000000000..f84bcb7114 --- /dev/null +++ b/framework/source/classes/taskcreator.cxx @@ -0,0 +1,89 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace framework{ + +/*-**************************************************************************************************** + @short initialize instance with necessary information + @descr We need a valid uno service manager to create or instantiate new services. + All other information to create frames or tasks come in on right interface methods. + + @param xContext + points to the valid uno service manager +*//*-*****************************************************************************************************/ +TaskCreator::TaskCreator( css::uno::Reference< css::uno::XComponentContext > xContext ) + : m_xContext (std::move( xContext )) +{ +} + +/*-**************************************************************************************************** + @short deinitialize instance + @descr We should release all used resource which are not needed any longer. +*//*-*****************************************************************************************************/ +TaskCreator::~TaskCreator() +{ +} + +/*-**************************************************************************************************** + TODO document me +*//*-*****************************************************************************************************/ +css::uno::Reference< css::frame::XFrame > TaskCreator::createTask( const OUString& sName, const utl::MediaDescriptor& rDescriptor ) +{ + css::uno::Reference< css::lang::XSingleServiceFactory > xCreator; + + try + { + xCreator.set( m_xContext->getServiceManager()->createInstanceWithContext(IMPLEMENTATIONNAME_FWK_TASKCREATOR, m_xContext), css::uno::UNO_QUERY_THROW); + } + catch(const css::uno::Exception&) + {} + + // no catch here ... without a task creator service we can't open ANY document window within the office. + // That's IMHO not a good idea. Then we should accept the stacktrace showing us the real problem. + // BTW: The used fallback creator service (IMPLEMENTATIONNAME_FWK_TASKCREATOR) is implemented in the same + // library then these class here ... Why we should not be able to create it ? + if ( ! xCreator.is()) + xCreator = css::frame::TaskCreator::create(m_xContext); + + css::uno::Sequence< css::uno::Any > lArgs + { + css::uno::Any(css::beans::NamedValue(ARGUMENT_PARENTFRAME, css::uno::Any(css::uno::Reference< css::frame::XFrame >( css::frame::Desktop::create( m_xContext ), css::uno::UNO_QUERY_THROW)))) , + css::uno::Any(css::beans::NamedValue(ARGUMENT_CREATETOPWINDOW, css::uno::Any(true))), + css::uno::Any(css::beans::NamedValue(ARGUMENT_MAKEVISIBLE, css::uno::Any(false))), + css::uno::Any(css::beans::NamedValue(ARGUMENT_SUPPORTPERSISTENTWINDOWSTATE, css::uno::Any(true))), + css::uno::Any(css::beans::NamedValue(ARGUMENT_FRAMENAME, css::uno::Any(sName))), + css::uno::Any(css::beans::NamedValue(ARGUMENT_HIDDENFORCONVERSION, css::uno::Any(rDescriptor.getUnpackedValueOrDefault(ARGUMENT_HIDDENFORCONVERSION, false)))) + }; + css::uno::Reference< css::frame::XFrame > xTask(xCreator->createInstanceWithArguments(lArgs), css::uno::UNO_QUERY_THROW); + return xTask; +} + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/dispatch/closedispatcher.cxx b/framework/source/dispatch/closedispatcher.cxx new file mode 100644 index 0000000000..2fd3bc91e3 --- /dev/null +++ b/framework/source/dispatch/closedispatcher.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 +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +using namespace com::sun::star; + +namespace framework{ + +#ifdef fpf + #error "Who uses \"fpf\" as define. It will overwrite my namespace alias ..." +#endif +namespace fpf = ::framework::pattern::frame; + +constexpr OUString URL_CLOSEDOC = u".uno:CloseDoc"_ustr; +constexpr OUString URL_CLOSEWIN = u".uno:CloseWin"_ustr; +const char URL_CLOSEFRAME[] = ".uno:CloseFrame"; + +CloseDispatcher::CloseDispatcher(css::uno::Reference< css::uno::XComponentContext > xContext , + const css::uno::Reference< css::frame::XFrame >& xFrame , + std::u16string_view sTarget) + : m_xContext(std::move(xContext)) + , m_aAsyncCallback( + new vcl::EventPoster(LINK(this, CloseDispatcher, impl_asyncCallback))) + , m_eOperation(E_CLOSE_DOC) + , m_pSysWindow(nullptr) +{ + uno::Reference xTarget = static_impl_searchRightTargetFrame(xFrame, sTarget); + m_xCloseFrame = xTarget; + + // Try to retrieve the system window instance of the closing frame. + uno::Reference xWindow = xTarget->getContainerWindow(); + if (xWindow.is()) + { + VclPtr pWindow = VCLUnoHelper::GetWindow(xWindow); + if (pWindow->IsSystemWindow()) + m_pSysWindow = dynamic_cast(pWindow.get()); + } +} + +CloseDispatcher::~CloseDispatcher() +{ + SolarMutexGuard g; + m_aAsyncCallback.reset(); + m_pSysWindow.reset(); +} + +void SAL_CALL CloseDispatcher::dispatch(const css::util::URL& aURL , + const css::uno::Sequence< css::beans::PropertyValue >& lArguments) +{ + dispatchWithNotification(aURL, lArguments, css::uno::Reference< css::frame::XDispatchResultListener >()); +} + +css::uno::Sequence< sal_Int16 > SAL_CALL CloseDispatcher::getSupportedCommandGroups() +{ + return css::uno::Sequence< sal_Int16 >{css::frame::CommandGroup::VIEW, css::frame::CommandGroup::DOCUMENT}; +} + +css::uno::Sequence< css::frame::DispatchInformation > SAL_CALL CloseDispatcher::getConfigurableDispatchInformation(sal_Int16 nCommandGroup) +{ + if (nCommandGroup == css::frame::CommandGroup::VIEW) + { + /* Attention: Don't add .uno:CloseFrame here. Because it's not really + a configurable feature ... and further it does not have + a valid UIName entry inside the GenericCommands.xcu ... */ + css::uno::Sequence< css::frame::DispatchInformation > lViewInfos{ + { URL_CLOSEWIN, css::frame::CommandGroup::VIEW } + }; + return lViewInfos; + } + else if (nCommandGroup == css::frame::CommandGroup::DOCUMENT) + { + css::uno::Sequence< css::frame::DispatchInformation > lDocInfos{ + { URL_CLOSEDOC, css::frame::CommandGroup::DOCUMENT } + }; + return lDocInfos; + } + + return css::uno::Sequence< css::frame::DispatchInformation >(); +} + +void SAL_CALL CloseDispatcher::addStatusListener(const css::uno::Reference< css::frame::XStatusListener >& /*xListener*/, + const css::util::URL& /*aURL*/ ) +{ +} + +void SAL_CALL CloseDispatcher::removeStatusListener(const css::uno::Reference< css::frame::XStatusListener >& /*xListener*/, + const css::util::URL& /*aURL*/ ) +{ +} + +void SAL_CALL CloseDispatcher::dispatchWithNotification(const css::util::URL& aURL , + const css::uno::Sequence< css::beans::PropertyValue >& lArguments, + const css::uno::Reference< css::frame::XDispatchResultListener >& xListener ) +{ + // SAFE -> ---------------------------------- + SolarMutexClearableGuard aWriteLock; + + // This reference indicates, that we were already called before and + // our asynchronous process was not finished yet. + // We have to reject double calls. Otherwise we risk, + // that we try to close an already closed resource... + // And it is no problem to do nothing then. The UI user will try it again, if + // non of these jobs was successful. + if (m_xSelfHold.is()) + { + aWriteLock.clear(); + // <- SAFE ------------------------------ + + implts_notifyResultListener( + xListener, + css::frame::DispatchResultState::DONTKNOW, + css::uno::Any()); + return; + } + + // First we have to check, if this dispatcher is used right. Means if valid URLs are used. + // If not - we have to break this operation. But an optional listener must be informed. + // BTW: We save the information about the requested operation. Because + // we need it later. + if ( aURL.Complete == URL_CLOSEDOC ) + m_eOperation = E_CLOSE_DOC; + else if ( aURL.Complete == URL_CLOSEWIN ) + m_eOperation = E_CLOSE_WIN; + else if ( aURL.Complete == URL_CLOSEFRAME ) + m_eOperation = E_CLOSE_FRAME; + else + { + aWriteLock.clear(); + // <- SAFE ------------------------------ + + implts_notifyResultListener( + xListener, + css::frame::DispatchResultState::FAILURE, + css::uno::Any()); + return; + } + + if (m_pSysWindow && m_pSysWindow->GetCloseHdl().IsSet()) + { + // The closing frame has its own close handler. Call it instead. + m_pSysWindow->GetCloseHdl().Call(*m_pSysWindow); + + aWriteLock.clear(); + // <- SAFE ------------------------------ + + implts_notifyResultListener( + xListener, + css::frame::DispatchResultState::SUCCESS, + css::uno::Any()); + + return; + } + + // OK - URLs are the right ones. + // But we can't execute synchronously :-) + // May we are called from a generic key-input handler, + // which isn't aware that this call kill its own environment... + // Do it asynchronous everytimes! + + // But don't forget to hold ourselves alive. + // We are called back from an environment, which doesn't know a uno reference. + // They call us back by using our c++ interface. + + m_xResultListener = xListener; + m_xSelfHold.set(static_cast< ::cppu::OWeakObject* >(this), css::uno::UNO_QUERY); + + aWriteLock.clear(); + // <- SAFE ---------------------------------- + + bool bIsSynchron = false; + for (const css::beans::PropertyValue& rArg : lArguments ) + { + if ( rArg.Name == "SynchronMode" ) + { + rArg.Value >>= bIsSynchron; + break; + } + } + + if ( bIsSynchron ) + impl_asyncCallback(nullptr); + else + { + SolarMutexGuard g; + m_aAsyncCallback->Post(); + } +} + +/** + @short asynchronous callback + @descr We start all actions inside this object asynchronous + (see comments there). + Now we do the following: + - close all views to the same document, if needed and possible + - make the current frame empty + ! This step is necessary to handle errors during closing the + document inside the frame. May the document shows a dialog and + the user ignore it. Then the state of the office can be changed + during we try to close frame and document. + - check the environment (means count open frames - excluding our + current one) + - decide then, if we must close this frame only, establish the backing mode + or shutdown the whole application. +*/ +IMPL_LINK_NOARG(CloseDispatcher, impl_asyncCallback, LinkParamNone*, void) +{ + try + { + + // Allow calling of XController->suspend() everytimes. + // Dispatch is an UI functionality. We implement such dispatch object here. + // And further XController->suspend() was designed to bring an UI ... + bool bControllerSuspended = false; + + bool bCloseAllViewsToo; + EOperation eOperation; + css::uno::Reference< css::uno::XComponentContext > xContext; + css::uno::Reference< css::frame::XFrame > xCloseFrame; + css::uno::Reference< css::frame::XDispatchResultListener > xListener; + { + SolarMutexGuard g; + + // Closing of all views, related to the same document, is allowed + // only if the dispatched URL was ".uno:CloseDoc"! + bCloseAllViewsToo = (m_eOperation == E_CLOSE_DOC); + + eOperation = m_eOperation; + xContext = m_xContext; + xCloseFrame.set(m_xCloseFrame.get(), css::uno::UNO_QUERY); + xListener = m_xResultListener; + } + + // frame already dead ?! + // Nothing to do ! + if (! xCloseFrame.is()) + return; + + bool bCloseFrame = false; + bool bEstablishBackingMode = false; + bool bTerminateApp = false; + + // Analyze the environment a first time. + // If we found some special cases, we can + // make some decisions earlier! + css::uno::Reference< css::frame::XFramesSupplier > xDesktop( css::frame::Desktop::create(xContext), css::uno::UNO_QUERY_THROW); + FrameListAnalyzer aCheck1(xDesktop, xCloseFrame, FrameAnalyzerFlags::Help | FrameAnalyzerFlags::BackingComponent); + + // Check for existing UNO connections. + // NOTE: There is a race between checking this and connections being created/destroyed before + // we close the frame / terminate the app. + css::uno::Reference bridgeFac( css::bridge::BridgeFactory::create(xContext) ); + bool bHasActiveConnections = bridgeFac->getExistingBridges().hasElements(); + + // a) If the current frame (where the close dispatch was requested for) does not have + // any parent frame ... it will close this frame only. Such frame isn't part of the + // global desktop tree ... and such frames are used as "implementation details" only. + // E.g. the live previews of our wizards doing such things. And then the owner of the frame + // is responsible for closing the application or accepting closing of the application + // by others. + if ( ! xCloseFrame->getCreator().is()) + bCloseFrame = true; + + // b) The help window can't disagree with any request. + // Because it doesn't implement a controller - it uses a window only. + // Further it can't be the last open frame - if we do all other things + // right inside this CloseDispatcher implementation. + // => close it! + else if (aCheck1.m_bReferenceIsHelp) + bCloseFrame = true; + + // c) If we are already in "backing mode", we terminate the application, if no active UNO connections are found. + // If there is an active UNO connection, we only close the frame and leave the application alive. + // It doesn't matter, how many other frames (can be the help or hidden frames only) are open then. + else if (aCheck1.m_bReferenceIsBacking) { + if (bHasActiveConnections) + bCloseFrame = true; + else + bTerminateApp = true; + } + + // d) Otherwise we have to: close all views to the same document, close the + // document inside our own frame and decide then again, what has to be done! + else + { + if (implts_prepareFrameForClosing(m_xCloseFrame, bCloseAllViewsToo, bControllerSuspended)) + { + // OK; this frame is empty now. + // Check the environment again to decide, what is the next step. + FrameListAnalyzer aCheck2(xDesktop, xCloseFrame, FrameAnalyzerFlags::All); + + // c1) there is as minimum 1 frame open, which is visible and contains a document + // different from our one. And it's not the help! + // (tdf#30920 consider that closing a frame which is not the backing window (start center) while there is + // another frame that is the backing window open only closes the frame, and not terminate the app, so + // closing the license frame doesn't terminate the app if launched from the start center) + // => close our frame only - nothing else. + if (!aCheck2.m_lOtherVisibleFrames.empty() || (!aCheck2.m_bReferenceIsBacking && aCheck2.m_xBackingComponent.is())) + bCloseFrame = true; + else + + // c2) if we close the current view ... but not all other views + // to the same document, we must close the current frame only! + // Because implts_closeView() suspended this view only - does not + // close the frame. + if ( + (!bCloseAllViewsToo ) && + (!aCheck2.m_lModelFrames.empty()) + ) + bCloseFrame = true; + + else + // c3) there is no other (visible) frame open ... + // The help module will be ignored everytimes! + // But we have to decide if we must terminate the + // application or establish the backing mode now. + // And that depends from the dispatched URL ... + { + if (eOperation == E_CLOSE_FRAME) + { + if (bHasActiveConnections) + bCloseFrame = true; + else + bTerminateApp = true; + } + else if( SvtModuleOptions().IsModuleInstalled(SvtModuleOptions::EModule::STARTMODULE) ) + bEstablishBackingMode = true; + else if (bHasActiveConnections) + bCloseFrame = true; + else + bTerminateApp = true; + } + } + } + + // Do it now ... + bool bSuccess = false; + if (bCloseFrame) + bSuccess = implts_closeFrame(); + else if (bEstablishBackingMode) + #if defined MACOSX + { + // on mac close down, quickstarter keeps the process alive + // however if someone has shut down the quickstarter + // behave as any other platform + + bool bQuickstarterRunning = false; + // get quickstart service + try + { + css::uno::Reference< css::beans::XFastPropertySet > xSet( xContext->getServiceManager()->createInstanceWithContext(IMPLEMENTATIONNAME_QUICKLAUNCHER, xContext), css::uno::UNO_QUERY_THROW ); + css::uno::Any aVal( xSet->getFastPropertyValue( 0 ) ); + bool bState = false; + if( aVal >>= bState ) + bQuickstarterRunning = bState; + } + catch( const css::uno::Exception& ) + { + } + bSuccess = bQuickstarterRunning ? implts_terminateApplication() : implts_establishBackingMode(); + } + #else + bSuccess = implts_establishBackingMode(); + #endif + else if (bTerminateApp) + bSuccess = implts_terminateApplication(); + + if ( ! bSuccess && bControllerSuspended ) + { + css::uno::Reference< css::frame::XController > xController = xCloseFrame->getController(); + if (xController.is()) + xController->suspend(false); + } + + // inform listener + sal_Int16 nState = css::frame::DispatchResultState::FAILURE; + if (bSuccess) + nState = css::frame::DispatchResultState::SUCCESS; + implts_notifyResultListener(xListener, nState, css::uno::Any()); + + SolarMutexGuard g; + // This method was called asynchronous from our main thread by using a pointer. + // We reached this method only, by using a reference to ourself :-) + // Further this member is used to detect still running and not yet finished + // asynchronous operations. So it's time now to release this reference. + // But hold it temp alive. Otherwise we die before we can finish this method really :-)) + css::uno::Reference< css::uno::XInterface > xTempHold = m_xSelfHold; + m_xSelfHold.clear(); + m_xResultListener.clear(); + } + catch(const css::lang::DisposedException&) + { + } +} + +bool CloseDispatcher::implts_prepareFrameForClosing(const css::uno::Reference< css::frame::XFrame >& xFrame, + bool bCloseAllOtherViewsToo, + bool& bControllerSuspended ) +{ + // Frame already dead ... so this view is closed ... is closed ... is ... .-) + if (! xFrame.is()) + return true; + + // Close all views to the same document ... if forced to do so. + // But don't touch our own frame here! + // We must do so ... because the may be following controller->suspend() + // will show the "save/discard/cancel" dialog for the last view only! + if (bCloseAllOtherViewsToo) + { + css::uno::Reference< css::uno::XComponentContext > xContext; + { + SolarMutexGuard g; + xContext = m_xContext; + } + + css::uno::Reference< css::frame::XFramesSupplier > xDesktop( css::frame::Desktop::create( xContext ), css::uno::UNO_QUERY_THROW); + FrameListAnalyzer aCheck(xDesktop, xFrame, FrameAnalyzerFlags::All); + + size_t c = aCheck.m_lModelFrames.size(); + size_t i = 0; + for (i=0; i xController = xFrame->getController(); + if (xController.is()) // some views don't uses a controller .-( (e.g. the help window) + { + bControllerSuspended = xController->suspend(true); + if (! bControllerSuspended) + return false; + } + } + + // don't remove the component really by e.g. calling setComponent(null, null). + // It's enough to suspend the controller. + // If we close the frame later this controller doesn't show the same dialog again. + return true; +} + +bool CloseDispatcher::implts_closeFrame() +{ + css::uno::Reference< css::frame::XFrame > xFrame; + { + SolarMutexGuard g; + xFrame.set(m_xCloseFrame.get(), css::uno::UNO_QUERY); + } + + // frame already dead ? => so it's closed ... it's closed ... + if ( ! xFrame.is() ) + return true; + + // don't deliver ownership; our "UI user" will try it again if it failed. + // OK - he will get an empty frame then. But normally an empty frame + // should be closeable always :-) + if (!fpf::closeIt(xFrame)) + return false; + + { + SolarMutexGuard g; + m_xCloseFrame.clear(); + } + + return true; +} + +bool CloseDispatcher::implts_establishBackingMode() +{ + css::uno::Reference< css::uno::XComponentContext > xContext; + css::uno::Reference< css::frame::XFrame > xFrame; + { + SolarMutexGuard g; + xContext = m_xContext; + xFrame.set(m_xCloseFrame.get(), css::uno::UNO_QUERY); + } + + if (!xFrame.is()) + return false; + + css::uno::Reference < css::document::XActionLockable > xLock( xFrame, css::uno::UNO_QUERY ); + if ( xLock.is() && xLock->isActionLocked() ) + return false; + + css::uno::Reference< css::awt::XWindow > xContainerWindow = xFrame->getContainerWindow(); + + css::uno::Reference< css::frame::XController > xStartModule = css::frame::StartModule::createWithParentWindow( + xContext, xContainerWindow); + + // Attention: You MUST(!) call setComponent() before you call attachFrame(). + css::uno::Reference< css::awt::XWindow > xBackingWin(xStartModule, css::uno::UNO_QUERY); + xFrame->setComponent(xBackingWin, xStartModule); + xStartModule->attachFrame(xFrame); + xContainerWindow->setVisible(true); + + return true; +} + +bool CloseDispatcher::implts_terminateApplication() +{ + css::uno::Reference< css::uno::XComponentContext > xContext; + { + SolarMutexGuard g; + xContext = m_xContext; + } + + css::uno::Reference< css::frame::XDesktop2 > xDesktop = css::frame::Desktop::create( xContext ); + + return xDesktop->terminate(); +} + +void CloseDispatcher::implts_notifyResultListener(const css::uno::Reference< css::frame::XDispatchResultListener >& xListener, + sal_Int16 nState , + const css::uno::Any& aResult ) +{ + if (!xListener.is()) + return; + + css::frame::DispatchResultEvent aEvent( + css::uno::Reference< css::uno::XInterface >(static_cast< ::cppu::OWeakObject* >(this), css::uno::UNO_QUERY), + nState, + aResult); + + xListener->dispatchFinished(aEvent); +} + +css::uno::Reference< css::frame::XFrame > CloseDispatcher::static_impl_searchRightTargetFrame(const css::uno::Reference< css::frame::XFrame >& xFrame , + std::u16string_view sTarget) +{ + if (o3tl::equalsIgnoreAsciiCase(sTarget, u"_self")) + return xFrame; + + OSL_ENSURE(sTarget.empty(), "CloseDispatch used for unexpected target. Magic things will happen now .-)"); + + css::uno::Reference< css::frame::XFrame > xTarget = xFrame; + while(true) + { + // a) top frames will be closed + if (xTarget->isTop()) + return xTarget; + + // b) even child frame containing top level windows (e.g. query designer of database) will be closed + css::uno::Reference< css::awt::XWindow > xWindow = xTarget->getContainerWindow(); + css::uno::Reference< css::awt::XTopWindow > xTopWindowCheck(xWindow, css::uno::UNO_QUERY); + if (xTopWindowCheck.is()) + { + // b1) Note: Toolkit interface XTopWindow sometimes is used by real VCL-child-windows also .-) + // Be sure that these window is really a "top system window". + // Attention ! Checking Window->GetParent() isn't the right approach here. + // Because sometimes VCL create "implicit border windows" as parents even we created + // a simple XWindow using the toolkit only .-( + SolarMutexGuard aSolarLock; + VclPtr pWindow = VCLUnoHelper::GetWindow( xWindow ); + if ( pWindow && pWindow->IsSystemWindow() ) + return xTarget; + } + + // c) try to find better results on parent frame + // If no parent frame exists (because this frame is used outside the desktop tree) + // the given frame must be used directly. + css::uno::Reference< css::frame::XFrame > xParent = xTarget->getCreator(); + if ( ! xParent.is()) + return xTarget; + + // c1) check parent frame inside next loop ... + xTarget = xParent; + } +} + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/dispatch/dispatchdisabler.cxx b/framework/source/dispatch/dispatchdisabler.cxx new file mode 100644 index 0000000000..2341d0dd64 --- /dev/null +++ b/framework/source/dispatch/dispatchdisabler.cxx @@ -0,0 +1,170 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.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 + +#include +#include + +#include +#include + +using namespace css; +using namespace framework; + +DispatchDisabler::DispatchDisabler(const uno::Reference< uno::XComponentContext >& ) +{ +} + +// XInitialization +void SAL_CALL DispatchDisabler::initialize( const uno::Sequence< uno::Any >& aArguments ) +{ + uno::Sequence< OUString > aDisabledURLs; + if( aArguments.hasElements() && + ( aArguments[0] >>= aDisabledURLs ) ) + { + for( OUString const & url : std::as_const(aDisabledURLs) ) + maDisabledURLs.insert(url); + } +} + +// XDispatchProvider +uno::Reference< frame::XDispatch > SAL_CALL +DispatchDisabler::queryDispatch( const util::URL& rURL, + const OUString& rTargetFrameName, + ::sal_Int32 nSearchFlags ) +{ + // If present - disabled. + if( maDisabledURLs.find(rURL.Complete) != maDisabledURLs.end() || + !mxSlave.is() ) + return uno::Reference< frame::XDispatch >(); + else + return mxSlave->queryDispatch(rURL, rTargetFrameName, nSearchFlags); +} + +uno::Sequence< uno::Reference< frame::XDispatch > > SAL_CALL +DispatchDisabler::queryDispatches( const uno::Sequence< frame::DispatchDescriptor >& rRequests ) +{ + uno::Sequence< uno::Reference< frame::XDispatch > > aResult(rRequests.getLength()); + auto aResultRange = asNonConstRange(aResult); + for( sal_Int32 i = 0; i < rRequests.getLength(); ++i ) + aResultRange[i] = queryDispatch(rRequests[i].FeatureURL, + rRequests[i].FrameName, + rRequests[i].SearchFlags); + return aResult; +} + +// XDispatchProviderInterceptor +uno::Reference< frame::XDispatchProvider > SAL_CALL +DispatchDisabler::getSlaveDispatchProvider() +{ + return mxSlave; +} + +void SAL_CALL DispatchDisabler::setSlaveDispatchProvider( const uno::Reference< frame::XDispatchProvider >& xNewDispatchProvider ) +{ + mxSlave = xNewDispatchProvider; +} + +uno::Reference< frame::XDispatchProvider > SAL_CALL +DispatchDisabler::getMasterDispatchProvider() +{ + return mxMaster; +} +void SAL_CALL +DispatchDisabler::setMasterDispatchProvider( const uno::Reference< frame::XDispatchProvider >& xNewSupplier ) +{ + mxMaster = xNewSupplier; +} + +// XInterceptorInfo +uno::Sequence< OUString > SAL_CALL + DispatchDisabler::getInterceptedURLs() +{ + uno::Sequence< OUString > aDisabledURLs(maDisabledURLs.size()); + auto aDisabledURLsRange = asNonConstRange(aDisabledURLs); + sal_Int32 n = 0; + for (auto const& disabledURL : maDisabledURLs) + aDisabledURLsRange[n++] = disabledURL; + return aDisabledURLs; +} + +// XElementAccess +uno::Type SAL_CALL DispatchDisabler::getElementType() +{ + uno::Type aModuleType = cppu::UnoType::get(); + return aModuleType; +} + +::sal_Bool SAL_CALL DispatchDisabler::hasElements() +{ + return !maDisabledURLs.empty(); +} + +// XNameAccess +uno::Any SAL_CALL DispatchDisabler::getByName( const OUString& ) +{ + return uno::Any(); +} + +uno::Sequence< OUString > SAL_CALL DispatchDisabler::getElementNames() +{ + return getInterceptedURLs(); +} + +sal_Bool SAL_CALL DispatchDisabler::hasByName( const OUString& rName ) +{ + return maDisabledURLs.find(rName) != maDisabledURLs.end(); +} + +// XNameReplace +void SAL_CALL DispatchDisabler::replaceByName( const OUString& rName, const uno::Any& aElement ) +{ + removeByName( rName ); + insertByName( rName, aElement ); +} + +// XNameContainer +void DispatchDisabler::insertByName( const OUString& rName, const uno::Any& ) +{ + maDisabledURLs.insert(rName); +} + +void DispatchDisabler::removeByName( const OUString& rName ) +{ + auto it = maDisabledURLs.find(rName); + if( it != maDisabledURLs.end() ) + maDisabledURLs.erase(it); +} + +// XInterface, XTypeProvider, XServiceInfo + +OUString SAL_CALL DispatchDisabler::getImplementationName() +{ + return "com.sun.star.comp.framework.services.DispatchDisabler"; +} + +sal_Bool SAL_CALL DispatchDisabler::supportsService( const OUString& sServiceName ) +{ + return cppu::supportsService(this, sServiceName); +} + +css::uno::Sequence< OUString > SAL_CALL DispatchDisabler::getSupportedServiceNames() +{ + return { "com.sun.star.frame.DispatchDisabler" }; +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +framework_DispatchDisabler_get_implementation( + css::uno::XComponentContext* context, css::uno::Sequence const& ) +{ + return cppu::acquire(new framework::DispatchDisabler(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/dispatch/dispatchinformationprovider.cxx b/framework/source/dispatch/dispatchinformationprovider.cxx new file mode 100644 index 0000000000..a5a0bd6980 --- /dev/null +++ b/framework/source/dispatch/dispatchinformationprovider.cxx @@ -0,0 +1,130 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include + +#include + +#include + +#include +#include + +namespace framework{ + +DispatchInformationProvider::DispatchInformationProvider(css::uno::Reference< css::uno::XComponentContext > xContext , + const css::uno::Reference< css::frame::XFrame >& xFrame) + : m_xContext (std::move(xContext )) + , m_xFrame (xFrame ) +{ +} + +DispatchInformationProvider::~DispatchInformationProvider() +{ +} + +css::uno::Sequence< sal_Int16 > SAL_CALL DispatchInformationProvider::getSupportedCommandGroups() +{ + css::uno::Sequence< css::uno::Reference< css::frame::XDispatchInformationProvider > > lProvider = implts_getAllSubProvider(); + sal_Int32 c1 = lProvider.getLength(); + sal_Int32 i1 = 0; + + ::std::vector< sal_Int16 > lGroups; + + for (i1=0; i1 xProvider = lProvider[i1]; + if (!xProvider.is()) + continue; + + const css::uno::Sequence< sal_Int16 > lProviderGroups = xProvider->getSupportedCommandGroups(); + sal_Int32 c2 = lProviderGroups.getLength(); + sal_Int32 i2 = 0; + for (i2=0; i2::const_iterator pGroup = + ::std::find(lGroups.begin(), lGroups.end(), rGroup); + if (pGroup == lGroups.end()) + lGroups.push_back(rGroup); + } + } + + return ::comphelper::containerToSequence(lGroups); +} + +css::uno::Sequence< css::frame::DispatchInformation > SAL_CALL DispatchInformationProvider::getConfigurableDispatchInformation(sal_Int16 nCommandGroup) +{ + css::uno::Sequence< css::uno::Reference< css::frame::XDispatchInformationProvider > > lProvider = implts_getAllSubProvider(); + sal_Int32 c1 = lProvider.getLength(); + sal_Int32 i1 = 0; + + std::unordered_map lInfos; + + for (i1=0; i1 xProvider = lProvider[i1]; + if (!xProvider.is()) + continue; + + const css::uno::Sequence< css::frame::DispatchInformation > lProviderInfos = xProvider->getConfigurableDispatchInformation(nCommandGroup); + sal_Int32 c2 = lProviderInfos.getLength(); + sal_Int32 i2 = 0; + for (i2=0; i2 > DispatchInformationProvider::implts_getAllSubProvider() +{ + css::uno::Reference< css::frame::XFrame > xFrame(m_xFrame); + if (!xFrame.is()) + return css::uno::Sequence< css::uno::Reference< css::frame::XDispatchInformationProvider > >(); + + rtl::Reference xCloser = new CloseDispatcher(m_xContext, xFrame, u"_self"); // explicit "_self" ... not "" ... see implementation of close dispatcher itself! + + css::uno::Reference< css::frame::XDispatchInformationProvider > xController (xFrame->getController() , css::uno::UNO_QUERY); + css::uno::Reference< css::frame::XDispatchInformationProvider > xAppDispatcher = css::frame::AppDispatchProvider::create(m_xContext); + css::uno::Sequence< css::uno::Reference< css::frame::XDispatchInformationProvider > > lProvider{ + xController, xCloser, xAppDispatcher + }; + + return lProvider; +} + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/dispatch/dispatchprovider.cxx b/framework/source/dispatch/dispatchprovider.cxx new file mode 100644 index 0000000000..8328e9c422 --- /dev/null +++ b/framework/source/dispatch/dispatchprovider.cxx @@ -0,0 +1,585 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include + +#include +#include +#include "isstartmoduledispatch.hxx" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace framework{ + +/** + @short standard ctor/dtor + @descr These initialize a new instance of this class with needed information for work. + We hold a weakreference to our owner frame which start dispatches at us. + We can't use a normal reference because he hold a reference of us too ... + nobody can die so ...! + + @seealso using at owner + + @param rxContext + reference to servicemanager to create new services. + @param xFrame + reference to our owner frame. +*/ +DispatchProvider::DispatchProvider( css::uno::Reference< css::uno::XComponentContext > xContext , + const css::uno::Reference< css::frame::XFrame >& xFrame ) + : m_xContext (std::move( xContext )) + , m_xFrame ( xFrame ) +{ +} + +/** + @short protected(!) dtor for deinitializing + @descr We made it protected to prevent using of us as base class instead as a member. + */ +DispatchProvider::~DispatchProvider() +{ +} + +/** + @interface XDispatchProvider + @short search a dispatcher for given URL + @descr If no interceptor is set on owner, we search for right frame and dispatch URL to it. + If no frame was found, we do nothing. + But we don't do it directly here. We detect the type of our owner frame and calls + specialized queryDispatch() helper dependen from that. Because a Desktop handle some + requests in another way then a normal frame. + + @param aURL + URL to dispatch. + @param sTargetFrameName + name of searched frame. + @param nSearchFlags + flags for searching. + @return A reference to a dispatch object for this URL (if someone was found!). + + @threadsafe yes +*/ +css::uno::Reference< css::frame::XDispatch > SAL_CALL DispatchProvider::queryDispatch( const css::util::URL& aURL , + const OUString& sTargetFrameName , + sal_Int32 nSearchFlags ) +{ + css::uno::Reference< css::frame::XDispatch > xDispatcher; + + css::uno::Reference< css::frame::XFrame > xOwner(m_xFrame); + + css::uno::Reference< css::frame::XDesktop > xDesktopCheck( xOwner, css::uno::UNO_QUERY ); + + if (xDesktopCheck.is()) + xDispatcher = implts_queryDesktopDispatch(xOwner, aURL, sTargetFrameName, nSearchFlags); + else + xDispatcher = implts_queryFrameDispatch(xOwner, aURL, sTargetFrameName, nSearchFlags); + + return xDispatcher; +} + +/** + @interface XDispatchProvider + @short do the same like queryDispatch() ... but handle multiple dispatches at the same time + @descr It's an optimism. User give us a list of queries ... and we return a list of dispatcher. + If one of given queries couldn't be solved to a real existing dispatcher ... + we return a list with empty references in it! Order of both lists will be retained! + + @seealso method queryDispatch() + + @param lDescriptions + a list of all dispatch parameters for multiple requests + @return A reference a list of dispatch objects for these URLs - may with some values inside. + + @threadsafe yes +*/ +css::uno::Sequence< css::uno::Reference< css::frame::XDispatch > > SAL_CALL DispatchProvider::queryDispatches( const css::uno::Sequence< css::frame::DispatchDescriptor >& lDescriptions ) +{ + // Create return list - which must have same size then the given descriptor + // It's not allowed to pack it! + sal_Int32 nCount = lDescriptions.getLength(); + css::uno::Sequence< css::uno::Reference< css::frame::XDispatch > > lDispatcher( nCount ); + auto lDispatcherRange = asNonConstRange(lDispatcher); + // Step over all descriptors and try to get any dispatcher for it. + for( sal_Int32 i=0; i DispatchProvider::implts_queryDesktopDispatch( const css::uno::Reference< css::frame::XFrame >& xDesktop , + const css::util::URL& aURL , + const OUString& sTargetFrameName , + sal_Int32 nSearchFlags ) +{ + css::uno::Reference< css::frame::XDispatch > xDispatcher; + + // ignore wrong requests which are not supported + if ( + (sTargetFrameName==SPECIALTARGET_PARENT ) || // we have no parent by definition + (sTargetFrameName==SPECIALTARGET_BEAMER ) // beamer frames are allowed as child of tasks only - + // and they exist more than ones. We have no idea which our sub tasks is the right one + ) + { + return nullptr; + } + + // I) handle special cases which not right for using findFrame() first + + // I.I) "_blank" + // It's not the right place to create a new task here - because we are queried for a dispatch object + // only, which can handle such request. Such dispatcher should create the required task on demand. + // Normally the functionality for "_blank" is provided by findFrame() - but that would create it directly + // here. that's why we must "intercept" here. + + if (sTargetFrameName==SPECIALTARGET_BLANK) + { + if (implts_isLoadableContent(aURL)) + xDispatcher = implts_getOrCreateDispatchHelper( E_BLANKDISPATCHER, xDesktop ); + } + + // I.II) "_default" + // This is a combination of search an empty task for recycling - or create a new one. + + else if (sTargetFrameName==SPECIALTARGET_DEFAULT) + { + if (implts_isLoadableContent(aURL)) + xDispatcher = implts_getOrCreateDispatchHelper( E_DEFAULTDISPATCHER, xDesktop ); + + if (isStartModuleDispatch(aURL)) + xDispatcher = implts_getOrCreateDispatchHelper( E_STARTMODULEDISPATCHER, xDesktop ); + } + + // I.III) "_self", "", "_top" + // The desktop can't load any document - but he can handle some special protocols like "uno", "slot" ... + // Why is "top" here handled too? Because the desktop is the topest frame. Normally it's superfluous + // to use this target - but we can handle it in the same manner then "_self". + + else if ( + (sTargetFrameName==SPECIALTARGET_SELF) || + (sTargetFrameName==SPECIALTARGET_TOP ) || + (sTargetFrameName.isEmpty()) + ) + { + xDispatcher = implts_searchProtocolHandler(aURL); + } + + // I.IV) no further special targets exist + // Now we have to search for the right target frame by calling findFrame() - but should provide our code + // against creation of a new task if no frame could be found. + // I said it before - it's allowed for dispatch() only. + + else + { + sal_Int32 nRightFlags = nSearchFlags & ~css::frame::FrameSearchFlag::CREATE; + + // try to find any existing target and ask him for his dispatcher + css::uno::Reference< css::frame::XFrame > xFoundFrame = xDesktop->findFrame(sTargetFrameName, nRightFlags); + if (xFoundFrame.is()) + { + css::uno::Reference< css::frame::XDispatchProvider > xProvider( xFoundFrame, css::uno::UNO_QUERY ); + xDispatcher = xProvider->queryDispatch(aURL,SPECIALTARGET_SELF,0); + } + // if it couldn't be found - but creation was allowed + // use special dispatcher for creation or forwarding to the browser + else if (nSearchFlags & css::frame::FrameSearchFlag::CREATE) + xDispatcher = implts_getOrCreateDispatchHelper( E_CREATEDISPATCHER, xDesktop, sTargetFrameName, nSearchFlags ); + } + + return xDispatcher; +} + +css::uno::Reference< css::frame::XDispatch > DispatchProvider::implts_queryFrameDispatch( const css::uno::Reference< css::frame::XFrame >& xFrame , + const css::util::URL& aURL , + const OUString& sTargetFrameName , + sal_Int32 nSearchFlags ) +{ + css::uno::Reference< css::frame::XDispatch > xDispatcher; + + // 0) Some URLs are dispatched in a generic way (e.g. by the menu) using the default target "". + // But they are specified to use her own fix target. Detect such URLs here and use the correct target. + + // I) handle special cases which not right for using findFrame() first + + // I.I) "_blank", "_default" + // It's not the right place to create a new task here. Only the desktop can do that. + // Normally the functionality for "_blank" is provided by findFrame() - but that would create it directly + // here. that's why we must "intercept" here. + + if ( + (sTargetFrameName==SPECIALTARGET_BLANK ) || + (sTargetFrameName==SPECIALTARGET_DEFAULT) + ) + { + css::uno::Reference< css::frame::XDispatchProvider > xParent( xFrame->getCreator(), css::uno::UNO_QUERY ); + if (xParent.is()) + xDispatcher = xParent->queryDispatch(aURL, sTargetFrameName, 0); // it's a special target - ignore search flags + } + + // I.II) "_beamer" + // Special sub frame of a top frame only. Search or create it. ... OK it's currently a little bit HACKI. + // Only the sfx (means the controller) can create it. + + else if (sTargetFrameName==SPECIALTARGET_BEAMER) + { + css::uno::Reference< css::frame::XDispatchProvider > xBeamer( xFrame->findFrame( SPECIALTARGET_BEAMER, css::frame::FrameSearchFlag::CHILDREN | css::frame::FrameSearchFlag::SELF ), css::uno::UNO_QUERY ); + if (xBeamer.is()) + { + xDispatcher = xBeamer->queryDispatch(aURL, SPECIALTARGET_SELF, 0); + } + else + { + css::uno::Reference< css::frame::XDispatchProvider > xController( xFrame->getController(), css::uno::UNO_QUERY ); + if (xController.is()) + // force using of special target - but use original search flags + // May the caller used the CREATE flag or not! + xDispatcher = xController->queryDispatch(aURL, SPECIALTARGET_BEAMER, nSearchFlags); + } + } + + // I.IV) "_parent" + // Our parent frame (if it exist) should handle this URL. + + else if (sTargetFrameName==SPECIALTARGET_PARENT) + { + css::uno::Reference< css::frame::XDispatchProvider > xParent( xFrame->getCreator(), css::uno::UNO_QUERY ); + if (xParent.is()) + // SELF => we must address the parent directly... and not his parent or any other parent! + xDispatcher = xParent->queryDispatch(aURL, SPECIALTARGET_SELF, 0); + } + + // I.V) "_top" + // This request must be forwarded to any parent frame, till we reach a top frame. + // If no parent exist, we can handle itself. + + else if (sTargetFrameName==SPECIALTARGET_TOP) + { + if (xFrame->isTop()) + { + // If we are this top frame itself (means our owner frame) + // we should call ourself recursiv with a better target "_self". + // So we can share the same code! (see reaction for "_self" inside this method too.) + xDispatcher = queryDispatch(aURL,SPECIALTARGET_SELF,0); + } + else + { + css::uno::Reference< css::frame::XDispatchProvider > xParent( xFrame->getCreator(), css::uno::UNO_QUERY ); + // Normally if isTop() returned sal_False ... the parent frame MUST(!) exist ... + // But it seems to be better to check that here to prevent us against an access violation. + if (xParent.is()) + xDispatcher = xParent->queryDispatch(aURL, SPECIALTARGET_TOP, 0); + } + } + + // I.VI) "_self", "" + // Our owner frame should handle this URL. But we can't do it for all of them. + // So we ask the internal set controller first. If he disagree we try to find a registered + // protocol handler. If this failed too - we check for a loadable content and in case of true + // we load it into the frame by returning specialized dispatch object. + + else if ( + (sTargetFrameName==SPECIALTARGET_SELF) || + (sTargetFrameName.isEmpty()) + ) + { + // There exist a hard coded interception for special URLs. + if ( aURL.Complete == ".uno:CloseDoc" || aURL.Complete == ".uno:CloseWin" ) + { + css::uno::Reference< css::frame::XDispatchProvider > xParent( xFrame->getCreator(), css::uno::UNO_QUERY ); + // In case the frame is not a top one, is not based on system window and has a parent, + // the parent frame should be queried for the correct dispatcher. + // See i93473 + if ( + !WindowHelper::isTopWindow(xFrame->getContainerWindow()) && + !VCLUnoHelper::GetWindow(xFrame->getContainerWindow())->IsSystemWindow() && + xParent.is() + ) + xDispatcher = xParent->queryDispatch(aURL, SPECIALTARGET_SELF, 0); + else + xDispatcher = implts_getOrCreateDispatchHelper( E_CLOSEDISPATCHER, xFrame ); + } + else if ( aURL.Complete == ".uno:CloseFrame" ) + xDispatcher = implts_getOrCreateDispatchHelper( E_CLOSEDISPATCHER, xFrame ); + + if ( ! xDispatcher.is()) + { + // Ask our controller for his agreement for these dispatched URL ... + // because some URLs are internal and can be handled faster by SFX - which most is the current controller! + // But in case of e.g. the bibliography not all queries will be handled successfully here. + css::uno::Reference< css::frame::XDispatchProvider > xController( xFrame->getController(), css::uno::UNO_QUERY ); + if (xController.is()) + xDispatcher = xController->queryDispatch(aURL, SPECIALTARGET_SELF, 0); + } + + // If controller has no fun to dispatch these URL - we must search another right dispatcher. + // Search for any registered protocol handler first. + if (!xDispatcher.is()) + xDispatcher = implts_searchProtocolHandler(aURL); + + // Not for controller - not for protocol handler + // It should be a loadable content - may be a file. Check it ... + // This check is necessary to found out, that + // support for some protocols isn't installed by user. May be + // "ftp" isn't available. So we suppress creation of our self dispatcher. + // The result will be clear. He can't handle it - but he would try it. + if ( + ( ! xDispatcher.is() ) && + ( implts_isLoadableContent(aURL) ) + ) + { + xDispatcher = implts_getOrCreateDispatchHelper( E_SELFDISPATCHER, xFrame ); + } + } + + // I.VII) no further special handlings exist + // Now we have to search for the right target frame by calling findFrame() - but should provide our code + // against creation of a new task if no frame could be found. + // I said it before - it's allowed for dispatch() only. + + else + { + sal_Int32 nRightFlags = nSearchFlags & ~css::frame::FrameSearchFlag::CREATE; + + // try to find any existing target and ask him for his dispatcher + css::uno::Reference< css::frame::XFrame > xFoundFrame = xFrame->findFrame(sTargetFrameName, nRightFlags); + if (xFoundFrame.is()) + { + // Attention: Found target is our own owner frame! + // Don't ask him for his dispatcher. We know it already - it's our self dispatch helper. + // Otherwise we can start a never ending recursiv call. Why? + // Somewhere called our owner frame - he called some interceptor objects - and may by this dispatch provider + // is called. If wa use queryDispatch() on our owner frame again - we start this call stack again ... and again. + if (xFoundFrame==xFrame) + xDispatcher = implts_getOrCreateDispatchHelper( E_SELFDISPATCHER, xFrame ); + else + { + css::uno::Reference< css::frame::XDispatchProvider > xProvider( xFoundFrame, css::uno::UNO_QUERY ); + xDispatcher = xProvider->queryDispatch(aURL,SPECIALTARGET_SELF,0); + } + } + else + // if it couldn't be found - but creation was allowed + // forward request to the desktop. + // Note: The given target name must be used to set the name on new created task! + // Don't forward request by changing it to a special one e.g _blank. + // Use the CREATE flag only to prevent call against further searches. + // We already know it - the target must be created new. + if (nSearchFlags & css::frame::FrameSearchFlag::CREATE) + { + css::uno::Reference< css::frame::XDispatchProvider > xParent( xFrame->getCreator(), css::uno::UNO_QUERY ); + if (xParent.is()) + xDispatcher = xParent->queryDispatch(aURL, sTargetFrameName, css::frame::FrameSearchFlag::CREATE); + } + } + + return xDispatcher; +} + +/** + @short search for a registered protocol handler and ask him for a dispatch object + @descr We search a suitable handler inside our cfg package org.openoffice.Office.ProtocolHandler. + If we found anyone, we create and initialize it. Initialize means: we set our owner frame on it + as context information. He can use it or leave it. Of course - we are aware of handler implementations, + which doesn't support initialization. It's an optional feature. + + @param aURL + the dispatch URL for which may a handler is registered + + @return A dispatch object if a handler was found and agree with the given URL or otherwise. + + @threadsafe yes +*/ +css::uno::Reference< css::frame::XDispatch > DispatchProvider::implts_searchProtocolHandler( const css::util::URL& aURL ) +{ + css::uno::Reference< css::frame::XDispatch > xDispatcher; + ProtocolHandler aHandler; + + // This member is threadsafe by himself and lives if we live - we don't need any mutex here. + if (m_aProtocolHandlerCache.search(aURL,&aHandler)) + { + css::uno::Reference< css::frame::XDispatchProvider > xHandler; + { + SolarMutexGuard g; + + // create it + bool bInitialize = true; + try + { + // Only create the protocol handler instance once, the creation is expensive. + auto it = m_aProtocolHandlers.find(aHandler.m_sUNOName); + if (it == m_aProtocolHandlers.end()) + { + xHandler.set( + css::uno::Reference(m_xContext->getServiceManager(), css::uno::UNO_QUERY_THROW) + ->createInstance(aHandler.m_sUNOName), + css::uno::UNO_QUERY); + + // Check if the handler explicitly requested to avoid caching. + css::uno::Reference xCacheInfo(xHandler, css::uno::UNO_QUERY); + if (!xCacheInfo.is() || xCacheInfo->isCachingAllowed()) + { + m_aProtocolHandlers.emplace(aHandler.m_sUNOName, xHandler); + } + } + else + { + xHandler = it->second; + bInitialize = false; + } + } + catch(const css::uno::Exception&) {} + + // look if initialization is necessary + css::uno::Reference< css::lang::XInitialization > xInit( xHandler, css::uno::UNO_QUERY ); + if (xInit.is() && bInitialize) + { + css::uno::Reference< css::frame::XFrame > xOwner( m_xFrame.get(), css::uno::UNO_QUERY ); + SAL_WARN_IF(!xOwner.is(), "fwk", "DispatchProvider::implts_searchProtocolHandler(): Couldn't get reference to my owner frame. So I can't set may needed context information for this protocol handler."); + if (xOwner.is()) + { + try + { + // but do it only, if all context information is OK + css::uno::Sequence< css::uno::Any > lContext{ css::uno::Any(xOwner) }; + xInit->initialize(lContext); + } + catch(const css::uno::Exception&) {} + } + } + } + + // ask for his (sub)dispatcher for the given URL + if (xHandler.is()) + xDispatcher = xHandler->queryDispatch(aURL,SPECIALTARGET_SELF,0); + } + + return xDispatcher; +} + +/** + @short get or create new dispatch helper + @descr Sometimes we need some helper implementations to support dispatching of special URLs or commands. + But it's not a good idea to hold these services for the whole life time of this provider instance. + We should create it on demand... + That's why we implement this method. It return an already existing helper or create a new one otherwise. + + @attention The parameter sTarget and nSearchFlags are defaulted to "" and 0! + Mostly it depends from the parameter eHelper is they are required or not. + + @param eHelper + specify the requested dispatch helper + @param xOwner + the target of possible dispatch() call on created dispatch helper + @param sTarget + the target parameter of the original queryDispatch() request + @param nSearchFlags + the flags parameter of the original queryDispatch() request + @return A reference to a dispatch helper. + + @threadsafe yes +*/ +css::uno::Reference< css::frame::XDispatch > DispatchProvider::implts_getOrCreateDispatchHelper( EDispatchHelper eHelper , + const css::uno::Reference< css::frame::XFrame >& xOwner , + const OUString& sTarget , + sal_Int32 nSearchFlags) +{ + css::uno::Reference< css::frame::XDispatch > xDispatchHelper; + + switch (eHelper) + { + case E_CREATEDISPATCHER : + xDispatchHelper = new LoadDispatcher(m_xContext, xOwner, sTarget, nSearchFlags); + break; + + case E_BLANKDISPATCHER : + { + if (xOwner.is()) + xDispatchHelper = new LoadDispatcher(m_xContext, xOwner, SPECIALTARGET_BLANK, 0); + } + break; + + case E_DEFAULTDISPATCHER : + { + if (xOwner.is()) + xDispatchHelper = new LoadDispatcher(m_xContext, xOwner, SPECIALTARGET_DEFAULT, 0); + } + break; + + case E_SELFDISPATCHER : + xDispatchHelper = new LoadDispatcher(m_xContext, xOwner, SPECIALTARGET_SELF, 0); + break; + + case E_CLOSEDISPATCHER : + xDispatchHelper = new CloseDispatcher( m_xContext, xOwner, sTarget ); + break; + + case E_STARTMODULEDISPATCHER : + xDispatchHelper = new StartModuleDispatcher( m_xContext ); + break; + } + + return xDispatchHelper; +} + +/** + @short check URL for support by our used loader or handler + @descr If we must return our own dispatch helper implementations (self, blank, create dispatcher!) + we should be sure, that URL describe any loadable content. Otherwise slot/uno URLs + will be detected... but there exist nothing for real loading into a target frame! + + @param aURL + URL which should be "detected" + @return if somewhere could handle that - otherwise. + + @threadsafe yes +*/ +bool DispatchProvider::implts_isLoadableContent( const css::util::URL& aURL ) +{ + LoadEnv::EContentType eType = LoadEnv::classifyContent(aURL.Complete, css::uno::Sequence< css::beans::PropertyValue >()); + return ( eType == LoadEnv::E_CAN_BE_LOADED ); +} + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/dispatch/interceptionhelper.cxx b/framework/source/dispatch/interceptionhelper.cxx new file mode 100644 index 0000000000..40cfe404ed --- /dev/null +++ b/framework/source/dispatch/interceptionhelper.cxx @@ -0,0 +1,271 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include + +#include +#include +#include +#include +#include +#include + +using namespace com::sun::star; + +namespace framework{ + +InterceptionHelper::InterceptionHelper(const css::uno::Reference< css::frame::XFrame >& xOwner, + rtl::Reference< DispatchProvider > xSlave) + : m_xOwnerWeak (xOwner ) + , m_xSlave (std::move(xSlave )) +{ +} + +InterceptionHelper::~InterceptionHelper() +{ +} + +css::uno::Reference< css::frame::XDispatch > SAL_CALL InterceptionHelper::queryDispatch(const css::util::URL& aURL , + const OUString& sTargetFrameName, + sal_Int32 nSearchFlags ) +{ + css::uno::Reference xInterceptor; + // SAFE { + { + SolarMutexGuard aReadLock; + + // a) first search an interceptor, which match to this URL by its URL pattern registration + // Note: if it return NULL - it does not mean an empty interceptor list automatically! + InterceptorList::const_iterator pIt = m_lInterceptionRegs.findByPattern(aURL.Complete); + if (pIt != m_lInterceptionRegs.end()) + xInterceptor = pIt->xInterceptor; + + // b) No match by registration - but a valid interceptor list. + // Find first interceptor w/o pattern, so we need to query it + if (!xInterceptor.is()) + { + for (auto const& lInterceptionReg : m_lInterceptionRegs) + { + if (!lInterceptionReg.lURLPattern.hasElements()) + { + // no pattern -> need to ask this guy! + xInterceptor = lInterceptionReg.xInterceptor; + break; + } + } + // if we didn't find any non-pattern interceptor, there's no-one + // registered for this command url (we already searched for matching + // patterns above) + } + // c) No registered interceptor => use our direct slave. + // This helper exist by design and must be valid everytimes ... + // But to be more feature proof - we should check that .-) + if (!xInterceptor.is() && m_xSlave.is()) + xInterceptor = m_xSlave; + } + // } SAFE + + css::uno::Reference< css::frame::XDispatch > xReturn; + if (xInterceptor.is()) + xReturn = xInterceptor->queryDispatch(aURL, sTargetFrameName, nSearchFlags); + return xReturn; +} + +css::uno::Sequence< css::uno::Reference< css::frame::XDispatch > > SAL_CALL InterceptionHelper::queryDispatches( const css::uno::Sequence< css::frame::DispatchDescriptor >& lDescriptor ) +{ + sal_Int32 c = lDescriptor.getLength(); + css::uno::Sequence< css::uno::Reference< css::frame::XDispatch > > lDispatches (c); + css::uno::Reference< css::frame::XDispatch >* pDispatches = lDispatches.getArray(); + const css::frame::DispatchDescriptor* pDescriptor = lDescriptor.getConstArray(); + + for (sal_Int32 i=0; i& xInterceptor) +{ + // reject incorrect calls of this interface method + css::uno::Reference< css::frame::XDispatchProvider > xThis(this); + if (!xInterceptor.is()) + throw css::uno::RuntimeException("NULL references not allowed as in parameter", xThis); + + // Fill a new info structure for new interceptor. + // Save his reference and try to get an additional URL/pattern list from him. + // If no list exist register these interceptor for all dispatch events with "*"! + InterceptorInfo aInfo; + + aInfo.xInterceptor = xInterceptor; + css::uno::Reference< css::frame::XInterceptorInfo > xInfo(xInterceptor, css::uno::UNO_QUERY); + if (xInfo.is()) + aInfo.lURLPattern = xInfo->getInterceptedURLs(); + else + aInfo.lURLPattern = { "*" }; + + // SAFE { + SolarMutexClearableGuard aWriteLock; + + // a) no interceptor at all - set this instance as master for given interceptor + // and set our slave as its slave - and put this interceptor to the list. + // Its place there doesn't matter. Because this list is currently empty. + if (m_lInterceptionRegs.empty()) + { + xInterceptor->setMasterDispatchProvider(xThis ); + xInterceptor->setSlaveDispatchProvider (m_xSlave); + m_lInterceptionRegs.push_back(aInfo); + } + + // b) OK - there is at least one interceptor already registered. + // It's slave and it's master must be valid references ... + // because we created it. + + // insert it before any other existing interceptor - means at the beginning of our list. + else + { + css::uno::Reference< css::frame::XDispatchProvider > xSlaveD = m_lInterceptionRegs.begin()->xInterceptor; + css::uno::Reference< css::frame::XDispatchProviderInterceptor > xSlaveI (xSlaveD , css::uno::UNO_QUERY); + + xInterceptor->setMasterDispatchProvider(xThis ); + xInterceptor->setSlaveDispatchProvider (xSlaveD ); + xSlaveI->setMasterDispatchProvider (aInfo.xInterceptor); + + m_lInterceptionRegs.push_front(aInfo); + } + + css::uno::Reference< css::frame::XFrame > xOwner(m_xOwnerWeak.get(), css::uno::UNO_QUERY); + + aWriteLock.clear(); + // } SAFE + + // Don't forget to send a frame action event "context changed". + // Any cached dispatch objects must be validated now! + if (xOwner.is()) + xOwner->contextChanged(); +} + +void SAL_CALL InterceptionHelper::releaseDispatchProviderInterceptor(const css::uno::Reference< css::frame::XDispatchProviderInterceptor >& xInterceptor) +{ + // reject wrong calling of this interface method + css::uno::Reference< css::frame::XDispatchProvider > xThis(this); + if (!xInterceptor.is()) + throw css::uno::RuntimeException("NULL references not allowed as in parameter", xThis); + + // SAFE { + SolarMutexClearableGuard aWriteLock; + + // search this interceptor ... + // If it could be located inside cache - + // use its slave/master relations to update the interception list; + // set empty references for it as new master and slave; + // and release it from out cache. + InterceptorList::iterator pIt = m_lInterceptionRegs.findByReference(xInterceptor); + if (pIt != m_lInterceptionRegs.end()) + { + css::uno::Reference< css::frame::XDispatchProvider > xSlaveD = xInterceptor->getSlaveDispatchProvider(); + css::uno::Reference< css::frame::XDispatchProvider > xMasterD = xInterceptor->getMasterDispatchProvider(); + css::uno::Reference< css::frame::XDispatchProviderInterceptor > xSlaveI (xSlaveD , css::uno::UNO_QUERY); + css::uno::Reference< css::frame::XDispatchProviderInterceptor > xMasterI (xMasterD , css::uno::UNO_QUERY); + + if (xMasterI.is()) + xMasterI->setSlaveDispatchProvider(xSlaveD); + + if (xSlaveI.is()) + { + try + { + xSlaveI->setMasterDispatchProvider(xMasterD); + } + catch (const lang::DisposedException&) + { + TOOLS_WARN_EXCEPTION("fwk.dispatch", + "InterceptionHelper::releaseDispatchProviderInterceptor: " + "xSlaveI is disposed: "); + } + } + + xInterceptor->setSlaveDispatchProvider (css::uno::Reference< css::frame::XDispatchProvider >()); + xInterceptor->setMasterDispatchProvider(css::uno::Reference< css::frame::XDispatchProvider >()); + + m_lInterceptionRegs.erase(pIt); + } + + css::uno::Reference< css::frame::XFrame > xOwner(m_xOwnerWeak.get(), css::uno::UNO_QUERY); + + aWriteLock.clear(); + // } SAFE + + // Don't forget to send a frame action event "context changed". + // Any cached dispatch objects must be validated now! + if (xOwner.is()) + xOwner->contextChanged(); +} + +#define FORCE_DESTRUCTION_OF_INTERCEPTION_CHAIN +void SAL_CALL InterceptionHelper::disposing(const css::lang::EventObject& aEvent) +{ + #ifdef FORCE_DESTRUCTION_OF_INTERCEPTION_CHAIN + // SAFE -> + SolarMutexResettableGuard aReadLock; + + // check call... we accept such disposing calls only from our owner frame. + css::uno::Reference< css::frame::XFrame > xOwner(m_xOwnerWeak.get(), css::uno::UNO_QUERY); + if (aEvent.Source != xOwner) + return; + + // Because every interceptor hold at least one reference to us ... and we destruct this list + // of interception objects ... we should hold ourself alive .-) + css::uno::Reference< css::frame::XDispatchProvider > xThis(static_cast< ::cppu::OWeakObject* >(this), css::uno::UNO_QUERY_THROW); + + // We need a full copy of all currently registered interceptor objects. + // Otherwise we can't iterate over this vector without the risk, that our iterator will be invalid. + // Because this vector will be influenced by every deregistered interceptor. + InterceptionHelper::InterceptorList aCopy = m_lInterceptionRegs; + + aReadLock.clear(); + // <- SAFE + + for (auto & elem : aCopy) + { + if (elem.xInterceptor.is()) + { + css::uno::Reference< css::frame::XDispatchProviderInterceptor > xInterceptor(elem.xInterceptor, css::uno::UNO_QUERY_THROW); + releaseDispatchProviderInterceptor(xInterceptor); + elem.xInterceptor.clear(); + } + } + + aCopy.clear(); + + #if OSL_DEBUG_LEVEL > 0 + // SAFE -> + aReadLock.reset(); + if (!m_lInterceptionRegs.empty() ) + OSL_FAIL("There are some pending interceptor objects, which seems to be registered during (!) the destruction of a frame."); + aReadLock.clear(); + // <- SAFE + #endif // ODL_DEBUG_LEVEL>0 + + #endif // FORCE_DESTRUCTION_OF_INTERCEPTION_CHAIN +} + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/dispatch/isstartmoduledispatch.hxx b/framework/source/dispatch/isstartmoduledispatch.hxx new file mode 100644 index 0000000000..a968a36f68 --- /dev/null +++ b/framework/source/dispatch/isstartmoduledispatch.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 . + */ + +#pragma once + +#include + +namespace framework +{ +inline bool isStartModuleDispatch(css::util::URL const& url) +{ + return url.Complete == ".uno:ShowStartModule"; +} +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/dispatch/loaddispatcher.cxx b/framework/source/dispatch/loaddispatcher.cxx new file mode 100644 index 0000000000..04ea5cf5c0 --- /dev/null +++ b/framework/source/dispatch/loaddispatcher.cxx @@ -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 . + */ + +#include +#include +#include + +#include +#include + +namespace framework{ + +LoadDispatcher::LoadDispatcher(const css::uno::Reference< css::uno::XComponentContext >& xContext , + const css::uno::Reference< css::frame::XFrame >& xOwnerFrame , + OUString sTargetName , + sal_Int32 nSearchFlags) + : m_xOwnerFrame (xOwnerFrame ) + , m_sTarget (std::move(sTargetName )) + , m_nSearchFlags(nSearchFlags) + , m_aLoader (xContext ) +{ +} + +LoadDispatcher::~LoadDispatcher() +{ +} + +void SAL_CALL LoadDispatcher::dispatchWithNotification(const css::util::URL& aURL , + const css::uno::Sequence< css::beans::PropertyValue >& lArguments, + const css::uno::Reference< css::frame::XDispatchResultListener >& xListener ) +{ + impl_dispatch( aURL, lArguments, xListener ); +} + +void SAL_CALL LoadDispatcher::dispatch(const css::util::URL& aURL , + const css::uno::Sequence< css::beans::PropertyValue >& lArguments) +{ + impl_dispatch( aURL, lArguments, css::uno::Reference< css::frame::XDispatchResultListener >() ); +} + +css::uno::Any SAL_CALL LoadDispatcher::dispatchWithReturnValue( const css::util::URL& rURL, + const css::uno::Sequence< css::beans::PropertyValue >& lArguments ) +{ + return impl_dispatch( rURL, lArguments, css::uno::Reference< css::frame::XDispatchResultListener >()); +} + +void SAL_CALL LoadDispatcher::addStatusListener(const css::uno::Reference< css::frame::XStatusListener >& /*xListener*/, + const css::util::URL& /*aURL*/ ) +{ +} + +void SAL_CALL LoadDispatcher::removeStatusListener(const css::uno::Reference< css::frame::XStatusListener >& /*xListener*/, + const css::util::URL& /*aURL*/ ) +{ +} + +css::uno::Any LoadDispatcher::impl_dispatch( const css::util::URL& rURL, + const css::uno::Sequence< css::beans::PropertyValue >& lArguments, + const css::uno::Reference< css::frame::XDispatchResultListener >& xListener ) +{ + // Attention: May be nobody outside hold such temp. dispatch object alive (because + // the container in which we resist isn't implemented threadsafe but updated by a timer + // and clear our reference...) we should hold us self alive! + css::uno::Reference< css::uno::XInterface > xThis(static_cast< css::frame::XNotifyingDispatch* >(this), css::uno::UNO_QUERY); + + osl::MutexGuard g(m_mutex); + + // We are the only client of this load env object... but + // may a dispatch request before is still in progress (?!). + // Then we should wait a little bit and block this new request. + // In case we run into the timeout, we should reject this new request + // and return "FAILED" as result. Otherwise we can start this new operation. + if (!m_aLoader.waitWhileLoading(2000)) // => 2 sec. + { + if (xListener.is()) + xListener->dispatchFinished( + css::frame::DispatchResultEvent(xThis, css::frame::DispatchResultState::DONTKNOW, css::uno::Any())); // DONTKNOW? ... not really started ... not really failed :-) + } + + css::uno::Reference< css::frame::XFrame > xBaseFrame(m_xOwnerFrame.get(), css::uno::UNO_QUERY); + if (!xBaseFrame.is() && xListener.is()) + xListener->dispatchFinished( + css::frame::DispatchResultEvent(xThis, css::frame::DispatchResultState::FAILURE, css::uno::Any())); + + // OK ... now the internal loader seems to be usable for new requests + // and our owner frame seems to be valid for such operations. + // Initialize it with all new but needed properties and start the loading. + css::uno::Reference< css::lang::XComponent > xComponent; + try + { + m_aLoader.startLoading( rURL.Complete, lArguments, xBaseFrame, m_sTarget, m_nSearchFlags, LoadEnvFeatures::AllowContentHandler | LoadEnvFeatures::WorkWithUI); + m_aLoader.waitWhileLoading(); // wait for ever! + xComponent = m_aLoader.getTargetComponent(); + + // TODO thinking about asynchronous operations and listener support + } + catch(const LoadEnvException& e) + { + SAL_WARN( + "fwk.dispatch", + "caught LoadEnvException " << +e.m_nID << " \"" << e.m_sMessage + << "\"" + << (e.m_exOriginal.has() + ? (", " + e.m_exOriginal.getValueTypeName() + " \"" + + e.m_exOriginal.get().Message + + "\"") + : OUString()) + << " while dispatching <" << rURL.Complete << ">"); + xComponent.clear(); + } + + if (xListener.is()) + { + if (xComponent.is()) + xListener->dispatchFinished( + css::frame::DispatchResultEvent(xThis, css::frame::DispatchResultState::SUCCESS, css::uno::Any())); + else + xListener->dispatchFinished( + css::frame::DispatchResultEvent(xThis, css::frame::DispatchResultState::FAILURE, css::uno::Any())); + } + + // return the model - like loadComponentFromURL() + css::uno::Any aRet; + if ( xComponent.is () ) + aRet <<= xComponent; + + return aRet; +} + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/dispatch/mailtodispatcher.cxx b/framework/source/dispatch/mailtodispatcher.cxx new file mode 100644 index 0000000000..9149b9b40d --- /dev/null +++ b/framework/source/dispatch/mailtodispatcher.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 +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace framework{ + +// XInterface, XTypeProvider, XServiceInfo + +OUString SAL_CALL MailToDispatcher::getImplementationName() +{ + return "com.sun.star.comp.framework.MailToDispatcher"; +} + +sal_Bool SAL_CALL MailToDispatcher::supportsService( const OUString& sServiceName ) +{ + return cppu::supportsService(this, sServiceName); +} + +css::uno::Sequence< OUString > SAL_CALL MailToDispatcher::getSupportedServiceNames() +{ + return { SERVICENAME_PROTOCOLHANDLER }; +} + + +/** + @short standard ctor + @descr This initializes a new instance of this class with needed information for work. + + @param rxContext + reference to uno servicemanager for creation of new services +*/ +MailToDispatcher::MailToDispatcher( css::uno::Reference< css::uno::XComponentContext > xContext ) + : m_xContext (std::move( xContext )) +{ +} + +/** + @short standard dtor +*/ +MailToDispatcher::~MailToDispatcher() +{ +} + +/** + @short decide if this dispatch implementation can be used for requested URL or not + @descr A protocol handler is registered for a URL pattern inside configuration and will + be asked by the generic dispatch mechanism inside framework, if he can handle this + special URL which match his registration. He can agree by returning of a valid dispatch + instance or disagree by returning . + We don't create new dispatch instances here really - we return THIS as result to handle it + at the same implementation. +*/ +css::uno::Reference< css::frame::XDispatch > SAL_CALL MailToDispatcher::queryDispatch( const css::util::URL& aURL , + const OUString& /*sTarget*/ , + sal_Int32 /*nFlags*/ ) +{ + css::uno::Reference< css::frame::XDispatch > xDispatcher; + if (aURL.Complete.startsWith("mailto:")) + xDispatcher = this; + return xDispatcher; +} + +/** + @short do the same like dispatch() but for multiple requests at the same time +*/ +css::uno::Sequence< css::uno::Reference< css::frame::XDispatch > > SAL_CALL MailToDispatcher::queryDispatches( const css::uno::Sequence< css::frame::DispatchDescriptor >& lDescriptor ) +{ + sal_Int32 nCount = lDescriptor.getLength(); + css::uno::Sequence< css::uno::Reference< css::frame::XDispatch > > lDispatcher( nCount ); + auto lDispatcherRange = asNonConstRange(lDispatcher); + for( sal_Int32 i=0; i& /*lArguments*/ ) +{ + // dispatch() is an [oneway] call ... and may our user release his reference to us immediately. + // So we should hold us self alive till this call ends. + css::uno::Reference< css::frame::XNotifyingDispatch > xSelfHold(this); + implts_dispatch(aURL); + // No notification for status listener! +} + +/** + @short dispatch with guaranteed notifications about success + @descr We use threadsafe internal method to do so. Return state of this function will be used + for notification if an optional listener is given. + + @param aURL + mail URL which should be executed + @param lArguments + list of optional arguments for this mail request + @param xListener + reference to a valid listener for state events +*/ +void SAL_CALL MailToDispatcher::dispatchWithNotification( const css::util::URL& aURL , + const css::uno::Sequence< css::beans::PropertyValue >& /*lArguments*/, + const css::uno::Reference< css::frame::XDispatchResultListener >& xListener ) +{ + // This class was designed to die by reference. And if user release his reference to us immediately after calling this method + // we can run into some problems. So we hold us self alive till this method ends. + // Another reason: We can use this reference as source of sending event at the end too. + css::uno::Reference< css::frame::XNotifyingDispatch > xThis(this); + + bool bState = implts_dispatch(aURL); + if (xListener.is()) + { + css::frame::DispatchResultEvent aEvent; + if (bState) + aEvent.State = css::frame::DispatchResultState::SUCCESS; + else + aEvent.State = css::frame::DispatchResultState::FAILURE; + aEvent.Source = xThis; + + xListener->dispatchFinished( aEvent ); + } +} + +/** + @short threadsafe helper for dispatch calls + @descr We support two interfaces for the same process - dispatch URLs. That the reason for this internal + function. It implements the real dispatch operation and returns a state value which inform caller + about success. He can notify listener then by using this return value. + + @param aURL + mail URL which should be executed + + @return if dispatch could be started successfully + Note: Our internal used shell executor doesn't return any state value - so we must + believe that call was successful. + if necessary resource couldn't be created or an exception was thrown. +*/ +bool MailToDispatcher::implts_dispatch( const css::util::URL& aURL ) +{ + bool bSuccess = false; + + css::uno::Reference< css::system::XSystemShellExecute > xSystemShellExecute = css::system::SystemShellExecute::create( m_xContext ); + + try + { + // start mail client + // Because there is no notification about success - we use case of + // no detected exception as SUCCESS - FAILED otherwise. + xSystemShellExecute->execute( aURL.Complete, OUString(), css::system::SystemShellExecuteFlags::URIS_ONLY ); + bSuccess = true; + } + catch (const css::lang::IllegalArgumentException&) + { + } + catch (const css::system::SystemShellExecuteException&) + { + } + + return bSuccess; +} + +/** + @short add/remove listener for state events + @descr Because we use an external process to forward such mail URLs, and this process doesn't + return any notifications about success or failed state - we don't support such status + listener. We have no status to send. + + @param xListener + reference to a valid listener for state events + @param aURL + URL about listener will be informed, if something occurred +*/ +void SAL_CALL MailToDispatcher::addStatusListener( const css::uno::Reference< css::frame::XStatusListener >& /*xListener*/ , + const css::util::URL& /*aURL*/ ) +{ + // not supported yet +} + +void SAL_CALL MailToDispatcher::removeStatusListener( const css::uno::Reference< css::frame::XStatusListener >& /*xListener*/ , + const css::util::URL& /*aURL*/ ) +{ + // not supported yet +} + +} // namespace framework + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +framework_MailToDispatcher_get_implementation( + css::uno::XComponentContext* context, css::uno::Sequence const& ) +{ + return cppu::acquire(new framework::MailToDispatcher(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/dispatch/oxt_handler.cxx b/framework/source/dispatch/oxt_handler.cxx new file mode 100644 index 0000000000..df956c7eed --- /dev/null +++ b/framework/source/dispatch/oxt_handler.cxx @@ -0,0 +1,175 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include + +#include +#include +#include +#include +#include + +namespace framework{ + +// XInterface, XTypeProvider, XServiceInfo + +OUString SAL_CALL Oxt_Handler::getImplementationName() +{ + return "com.sun.star.comp.framework.OXTFileHandler"; +} + +sal_Bool SAL_CALL Oxt_Handler::supportsService( const OUString& sServiceName ) +{ + return cppu::supportsService(this, sServiceName); +} + +css::uno::Sequence< OUString > SAL_CALL Oxt_Handler::getSupportedServiceNames() +{ + return { "com.sun.star.frame.ContentHandler" }; +} + + +/*-************************************************************************************************************ + @short standard ctor + @descr These initialize a new instance of this class with needed information for work. + + @seealso using at owner + + @param "xFactory", reference to service manager for creation of new services + @onerror Show an assertion and do nothing else. + @threadsafe yes +*//*-*************************************************************************************************************/ +Oxt_Handler::Oxt_Handler( css::uno::Reference< css::uno::XComponentContext > xContext ) + : m_xContext (std::move( xContext )) +{ +} + +/*-************************************************************************************************************ + @short standard dtor +*//*-*************************************************************************************************************/ +Oxt_Handler::~Oxt_Handler() +{ +} + +/*-************************************************************************************************************ + @interface css::frame::XDispatch + + @short try to load audio file + @descr This method try to load given audio file by URL and play it. We use vcl/Sound class to do that. + Playing of sound is asynchron every time. + + @attention We must hold us alive by ourself ... because we use async. vcl sound player ... but playing is started + in async interface call "dispatch()" too. And caller forget us immediately. But then our uno ref count + will decreased to 0 and will die. The only solution is to use own reference to our implementation. + But we do it for really started jobs only and release it during call back of vcl. + + @seealso class vcl/Sound + @seealso method implts_PlayerNotify() + + @param "aURL" , URL to dispatch. + @param "lArguments", list of optional arguments. + @onerror We do nothing. + @threadsafe yes +*//*-*************************************************************************************************************/ +void SAL_CALL Oxt_Handler::dispatchWithNotification( const css::util::URL& aURL, + const css::uno::Sequence< css::beans::PropertyValue >& /*lArguments*/, + const css::uno::Reference< css::frame::XDispatchResultListener >& xListener ) +{ + std::unique_lock g(m_mutex); + + css::uno::Sequence< css::uno::Any > lParams{ css::uno::Any(aURL.Main) }; + + css::uno::Reference< css::uno::XInterface > xService = m_xContext->getServiceManager()->createInstanceWithArgumentsAndContext( "com.sun.star.deployment.ui.PackageManagerDialog", lParams, m_xContext ); + css::uno::Reference< css::task::XJobExecutor > xExecutable( xService, css::uno::UNO_QUERY ); + if ( xExecutable.is() ) + xExecutable->trigger( OUString() ); + + if ( xListener.is() ) + { + css::frame::DispatchResultEvent aEvent; + aEvent.State = css::frame::DispatchResultState::SUCCESS; + xListener->dispatchFinished( aEvent ); + } +} + +void SAL_CALL Oxt_Handler::dispatch( const css::util::URL& aURL , + const css::uno::Sequence< css::beans::PropertyValue >& lArguments ) +{ + dispatchWithNotification( aURL, lArguments, css::uno::Reference< css::frame::XDispatchResultListener >() ); +} + +/*-************************************************************************************************************ + @interface css::document::XExtendedFilterDetection + + @short try to detect file (given as argument included in "lDescriptor") + @descr We try to detect, if given file could be handled by this class and is a well known one. + If it is - we return right internal type name - otherwise we return nothing! + So call can search for another detect service and ask him too. + + @attention a) We don't need any mutex here ... because we don't use any member! + b) Don't use internal player instance "m_pPlayer" to detect given sound file! + It's not necessary to do that ... and we can use temp. variable to do the same. + This way is easy - we don't must synchronize it with currently played sounds! + Another reason to do so ... We are a listener on our internal ma_Player object. + If you would call "IsSoundFile()" on this instance, he would call us back and + we make some unnecessary things ... + @param "lDescriptor", description of file to detect + @return Internal type name which match this file ... or nothing if it is unknown. + + @onerror We return nothing. + @threadsafe yes +*//*-*************************************************************************************************************/ +OUString SAL_CALL Oxt_Handler::detect( css::uno::Sequence< css::beans::PropertyValue >& lDescriptor ) +{ + // Our default is "nothing". So we can return it, if detection failed or file type is really unknown. + OUString sTypeName; + + // Analyze given descriptor to find filename or input stream or... + utl::MediaDescriptor aDescriptor( lDescriptor ); + OUString sURL = aDescriptor.getUnpackedValueOrDefault( utl::MediaDescriptor::PROP_URL, OUString() ); + + tools::Long nLength = sURL.getLength(); + if ( ( nLength > 4 ) && sURL.matchIgnoreAsciiCase( ".oxt", nLength-4 ) ) + { + // "IsSoundFile" differs between different "wav" and "au" file versions... + // couldn't return this information... because: it use the OS to detect it! + // I think we can than following ones: + // a) look for given extension of url to map our type decision HARD CODED!!! + // b) return preferred type every time... it's easy :-) + sTypeName = "oxt_OpenOffice_Extension"; + aDescriptor[utl::MediaDescriptor::PROP_TYPENAME] <<= sTypeName; + aDescriptor >> lDescriptor; + } + + // Return our decision. + return sTypeName; +} + +} // namespace framework + + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +framework_Oxt_Handler_get_implementation( + css::uno::XComponentContext* context, css::uno::Sequence const& ) +{ + return cppu::acquire(new framework::Oxt_Handler(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/dispatch/popupmenudispatcher.cxx b/framework/source/dispatch/popupmenudispatcher.cxx new file mode 100644 index 0000000000..3220ceece2 --- /dev/null +++ b/framework/source/dispatch/popupmenudispatcher.cxx @@ -0,0 +1,269 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace framework{ + +using namespace ::com::sun::star; +using namespace ::com::sun::star::awt; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::container; +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 ::cppu; +using namespace ::osl; + +PopupMenuDispatcher::PopupMenuDispatcher( + uno::Reference< XComponentContext > xContext ) + : m_xContext (std::move( xContext )) + , m_bAlreadyDisposed ( false ) + , m_bActivateListener ( false ) +{ +} + +PopupMenuDispatcher::~PopupMenuDispatcher() +{ + // Warn programmer if he forgot to dispose this instance. + // We must release all our references ... + // and a dtor isn't the best place to do that! +} + +OUString SAL_CALL PopupMenuDispatcher::getImplementationName() +{ + return "com.sun.star.comp.framework.PopupMenuControllerDispatcher"; +} + +sal_Bool SAL_CALL PopupMenuDispatcher::supportsService( const OUString& sServiceName ) +{ + return cppu::supportsService(this, sServiceName); +} + +css::uno::Sequence< OUString > SAL_CALL PopupMenuDispatcher::getSupportedServiceNames() +{ + return { SERVICENAME_PROTOCOLHANDLER }; +} + +void SAL_CALL PopupMenuDispatcher::initialize( const css::uno::Sequence< css::uno::Any >& lArguments ) +{ + css::uno::Reference< css::frame::XFrame > xFrame; + + SolarMutexGuard g; + for (int a=0; a>= xFrame; + m_xWeakFrame = xFrame; + + m_bActivateListener = true; + uno::Reference< css::frame::XFrameActionListener > xFrameActionListener(this); + xFrame->addFrameActionListener( xFrameActionListener ); + } + } +} + +css::uno::Reference< css::frame::XDispatch > +SAL_CALL PopupMenuDispatcher::queryDispatch( + const css::util::URL& rURL , + const OUString& sTarget , + sal_Int32 nFlags ) +{ + if ( !rURL.Complete.startsWith( "vnd.sun.star.popup:" ) ) + return {}; + + // --- SAFE --- + SolarMutexClearableGuard aGuard; + impl_RetrievePopupControllerQuery(); + if ( !m_xUriRefFactory.is() ) + m_xUriRefFactory = css::uri::UriReferenceFactory::create( m_xContext ); + + css::uno::Reference< css::container::XNameAccess > xPopupCtrlQuery( m_xPopupCtrlQuery ); + aGuard.clear(); + // --- SAFE --- + + if ( !xPopupCtrlQuery.is() ) + return {}; + + css::uno::Reference< css::frame::XDispatch > xDispatch; + + try + { + // Just use the main part of the URL for popup menu controllers + sal_Int32 nSchemePart( 0 ); + OUString aBaseURL( "vnd.sun.star.popup:" ); + OUString aURL( rURL.Complete ); + + nSchemePart = aURL.indexOf( ':' ); + if (( nSchemePart > 0 ) && + ( aURL.getLength() > ( nSchemePart+1 ))) + { + sal_Int32 nQueryPart = aURL.indexOf( '?', nSchemePart ); + if ( nQueryPart > 0 ) + aBaseURL += aURL.subView( nSchemePart+1, nQueryPart-(nSchemePart+1) ); + else if ( nQueryPart == -1 ) + aBaseURL += aURL.subView( nSchemePart+1 ); + } + + css::uno::Reference< css::frame::XDispatchProvider > xDispatchProvider; + + // Find popup menu controller using the base URL + xPopupCtrlQuery->getByName( aBaseURL ) >>= xDispatchProvider; + + // Ask popup menu dispatch provider for dispatch object + if ( xDispatchProvider.is() ) + xDispatch = xDispatchProvider->queryDispatch( rURL, sTarget, nFlags ); + } + catch ( const RuntimeException& ) + { + throw; + } + catch ( const Exception& ) + { + } + return xDispatch; +} + +css::uno::Sequence< css::uno::Reference< css::frame::XDispatch > > SAL_CALL +PopupMenuDispatcher::queryDispatches( + const css::uno::Sequence< css::frame::DispatchDescriptor >& lDescriptor ) +{ + sal_Int32 nCount = lDescriptor.getLength(); + css::uno::Sequence< css::uno::Reference< css::frame::XDispatch > > lDispatcher( nCount ); + auto lDispatcherRange = asNonConstRange(lDispatcher); + for( sal_Int32 i=0; i& /*seqProperties*/ ) +{ +} + +void SAL_CALL PopupMenuDispatcher::addStatusListener( const uno::Reference< XStatusListener >& /*xControl*/, + const URL& /*aURL*/ ) +{ +} + +void SAL_CALL PopupMenuDispatcher::removeStatusListener( const uno::Reference< XStatusListener >& /*xControl*/, + const URL& /*aURL*/ ) +{ +} + +void SAL_CALL PopupMenuDispatcher::frameAction( const FrameActionEvent& aEvent ) +{ + SolarMutexGuard g; + if (( aEvent.Action == css::frame::FrameAction_COMPONENT_DETACHING ) || + ( aEvent.Action == css::frame::FrameAction_COMPONENT_ATTACHED )) + { + // Reset query reference to requery it again next time + m_xPopupCtrlQuery.clear(); + } +} + +void SAL_CALL PopupMenuDispatcher::disposing( const EventObject& ) +{ + SolarMutexGuard g; + // Safe impossible cases + SAL_WARN_IF( m_bAlreadyDisposed, "fwk", "MenuDispatcher::disposing(): Object already disposed .. don't call it again!" ); + + if( m_bAlreadyDisposed ) + return; + + m_bAlreadyDisposed = true; + + if ( m_bActivateListener ) + { + uno::Reference< XFrame > xFrame( m_xWeakFrame.get(), UNO_QUERY ); + if ( xFrame.is() ) + { + xFrame->removeFrameActionListener( uno::Reference< XFrameActionListener >(this) ); + m_bActivateListener = false; + } + } + + // Forget our factory. + m_xContext.clear(); +} + +void PopupMenuDispatcher::impl_RetrievePopupControllerQuery() +{ + if ( m_xPopupCtrlQuery.is() ) + return; + + css::uno::Reference< css::frame::XLayoutManager2 > xLayoutManager; + css::uno::Reference< css::frame::XFrame > xFrame( m_xWeakFrame ); + + if ( !xFrame.is() ) + return; + + css::uno::Reference< css::beans::XPropertySet > xPropSet( xFrame, css::uno::UNO_QUERY ); + if ( !xPropSet.is() ) + return; + + try + { + xPropSet->getPropertyValue( FRAME_PROPNAME_ASCII_LAYOUTMANAGER ) >>= xLayoutManager; + + if ( xLayoutManager.is() ) + { + css::uno::Reference< css::ui::XUIElement > xMenuBar = xLayoutManager->getElement( "private:resource/menubar/menubar" ); + + m_xPopupCtrlQuery.set( xMenuBar, css::uno::UNO_QUERY ); + } + } + catch ( const css::uno::RuntimeException& ) + { + throw; + } + catch ( const css::uno::Exception& ) + { + } +} + +} // namespace framework + + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +framework_PopupMenuDispatcher_get_implementation( + css::uno::XComponentContext* context, css::uno::Sequence const& ) +{ + return cppu::acquire(new framework::PopupMenuDispatcher(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/dispatch/servicehandler.cxx b/framework/source/dispatch/servicehandler.cxx new file mode 100644 index 0000000000..cb29898350 --- /dev/null +++ b/framework/source/dispatch/servicehandler.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 . + */ + +#include +#include + +#include +#include +#include + +#include +#include +#include + +namespace framework{ + +constexpr OUString PROTOCOL_VALUE = u"service:"_ustr; + +// XInterface, XTypeProvider, XServiceInfo + +OUString SAL_CALL ServiceHandler::getImplementationName() +{ + return "com.sun.star.comp.framework.ServiceHandler"; +} + +sal_Bool SAL_CALL ServiceHandler::supportsService( const OUString& sServiceName ) +{ + return cppu::supportsService(this, sServiceName); +} + +css::uno::Sequence< OUString > SAL_CALL ServiceHandler::getSupportedServiceNames() +{ + return { SERVICENAME_PROTOCOLHANDLER }; +} + + +/** + @short standard ctor + @descr This initializes a new instance of this class with needed information for work. + + @param xFactory + reference to uno servicemanager for creation of new services +*/ +ServiceHandler::ServiceHandler( css::uno::Reference< css::uno::XComponentContext > xContext ) + : m_xContext (std::move( xContext )) +{ +} + +/** + @short standard dtor +*/ +ServiceHandler::~ServiceHandler() +{ +} + +/** + @short decide if this dispatch implementation can be used for requested URL or not + @descr A protocol handler is registered for a URL pattern inside configuration and will + be asked by the generic dispatch mechanism inside framework, if he can handle this + special URL which match his registration. He can agree by returning of a valid dispatch + instance or disagree by returning . + We don't create new dispatch instances here really - we return THIS as result to handle it + at the same implementation. +*/ +css::uno::Reference< css::frame::XDispatch > SAL_CALL ServiceHandler::queryDispatch( const css::util::URL& aURL , + const OUString& /*sTarget*/ , + sal_Int32 /*nFlags*/ ) +{ + css::uno::Reference< css::frame::XDispatch > xDispatcher; + if (aURL.Complete.startsWith(PROTOCOL_VALUE)) + xDispatcher = this; + return xDispatcher; +} + +/** + @short do the same like dispatch() but for multiple requests at the same time +*/ +css::uno::Sequence< css::uno::Reference< css::frame::XDispatch > > SAL_CALL ServiceHandler::queryDispatches( const css::uno::Sequence< css::frame::DispatchDescriptor >& lDescriptor ) +{ + sal_Int32 nCount = lDescriptor.getLength(); + css::uno::Sequence< css::uno::Reference< css::frame::XDispatch > > lDispatcher( nCount ); + auto lDispatcherRange = asNonConstRange(lDispatcher); + for( sal_Int32 i=0; i& /*lArguments*/ ) +{ + // dispatch() is an [oneway] call ... and may our user release his reference to us immediately. + // So we should hold us self alive till this call ends. + css::uno::Reference< css::frame::XNotifyingDispatch > xSelfHold(this); + implts_dispatch(aURL); + // No notification for status listener! +} + +/** + @short dispatch with guaranteed notifications about success + @descr We use threadsafe internal method to do so. Return state of this function will be used + for notification if an optional listener is given. + + @param aURL + uno URL which should be executed + @param lArguments + list of optional arguments for this request + @param xListener + optional listener for state events +*/ +void SAL_CALL ServiceHandler::dispatchWithNotification( const css::util::URL& aURL , + const css::uno::Sequence< css::beans::PropertyValue >& /*lArguments*/, + const css::uno::Reference< css::frame::XDispatchResultListener >& xListener ) +{ + // This class was designed to die by reference. And if user release his reference to us immediately after calling this method + // we can run into some problems. So we hold us self alive till this method ends. + // Another reason: We can use this reference as source of sending event at the end too. + css::uno::Reference< css::frame::XNotifyingDispatch > xThis(this); + + css::uno::Reference< css::uno::XInterface > xService = implts_dispatch(aURL); + if (xListener.is()) + { + css::frame::DispatchResultEvent aEvent; + if (xService.is()) + aEvent.State = css::frame::DispatchResultState::SUCCESS; + else + aEvent.State = css::frame::DispatchResultState::FAILURE; + aEvent.Result <<= xService; // may NULL for state=FAILED! + aEvent.Source = xThis; + + xListener->dispatchFinished( aEvent ); + } +} + +/** + @short threadsafe helper for dispatch calls + @descr We support two interfaces for the same process - dispatch URLs. That the reason for this internal + function. It implements the real dispatch operation and returns a state value which inform caller + about success. He can notify listener then by using this return value. + + @param aURL + uno URL which should be executed + + @return if requested service couldn't be created successfully; + a valid reference otherwise. This return value can be used to indicate, + if dispatch was successful. +*/ +css::uno::Reference< css::uno::XInterface > ServiceHandler::implts_dispatch( const css::util::URL& aURL ) +{ + // extract service name and may optional given parameters from given URL + // and use it to create and start the component + OUString sServiceAndArguments = aURL.Complete.copy(PROTOCOL_VALUE.getLength()); + OUString sServiceName; + OUString sArguments; + + sal_Int32 nArgStart = sServiceAndArguments.indexOf('?'); + if (nArgStart!=-1) + { + sServiceName = sServiceAndArguments.copy(0,nArgStart); + ++nArgStart; // ignore '?'! + sArguments = sServiceAndArguments.copy(nArgStart); + } + else + { + sServiceName = sServiceAndArguments; + } + + if (sServiceName.isEmpty()) + return css::uno::Reference< css::uno::XInterface >(); + + // If a service doesn't support an optional job executor interface - he can't get + // any given parameters! + // Because we can't know if we must call createInstanceWithArguments() or XJobExecutor::trigger() ... + + css::uno::Reference< css::uno::XInterface > xService; + try + { + // => a) a service starts running inside his own ctor and we create it only + xService = m_xContext->getServiceManager()->createInstanceWithContext(sServiceName, m_xContext); + // or b) he implements the right interface and starts there (may with optional parameters) + css::uno::Reference< css::task::XJobExecutor > xExecutable(xService, css::uno::UNO_QUERY); + if (xExecutable.is()) + xExecutable->trigger(sArguments); + } + // ignore all errors - inclusive runtime errors! + // E.g. a script based service (written in Python) could not be executed + // because it contains syntax errors, which was detected at runtime... + catch(const css::uno::Exception&) + { + TOOLS_WARN_EXCEPTION("fwk.dispatch", "ignored"); + xService.clear(); + } + + return xService; +} + +/** + @short add/remove listener for state events + @descr We use an internal container to hold such registered listener. This container lives if we live. + And if call pass registration as non breakable transaction - we can accept the request without + any explicit lock. Because we share our mutex with this container. + + @param xListener + reference to a valid listener for state events + @param aURL + URL about listener will be informed, if something occurred +*/ +void SAL_CALL ServiceHandler::addStatusListener( const css::uno::Reference< css::frame::XStatusListener >& /*xListener*/ , + const css::util::URL& /*aURL*/ ) +{ + // not supported yet +} + +void SAL_CALL ServiceHandler::removeStatusListener( const css::uno::Reference< css::frame::XStatusListener >& /*xListener*/ , + const css::util::URL& /*aURL*/ ) +{ + // not supported yet +} + +} // namespace framework + + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +framework_ServiceHandler_get_implementation( + css::uno::XComponentContext* context, css::uno::Sequence const& ) +{ + return cppu::acquire(new framework::ServiceHandler(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/dispatch/startmoduledispatcher.cxx b/framework/source/dispatch/startmoduledispatcher.cxx new file mode 100644 index 0000000000..39ac70ee14 --- /dev/null +++ b/framework/source/dispatch/startmoduledispatcher.cxx @@ -0,0 +1,147 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include "isstartmoduledispatch.hxx" + +#include +#include +#include +#include + +#include +#include + +namespace framework{ + +#ifdef fpf + #error "Who uses \"fpf\" as define. It will overwrite my namespace alias ..." +#endif + +StartModuleDispatcher::StartModuleDispatcher(css::uno::Reference< css::uno::XComponentContext > xContext) + : m_xContext (std::move(xContext )) +{ +} + +StartModuleDispatcher::~StartModuleDispatcher() +{ +} + +void SAL_CALL StartModuleDispatcher::dispatch(const css::util::URL& aURL , + const css::uno::Sequence< css::beans::PropertyValue >& lArguments) +{ + dispatchWithNotification(aURL, lArguments, css::uno::Reference< css::frame::XDispatchResultListener >()); +} + +void SAL_CALL StartModuleDispatcher::dispatchWithNotification(const css::util::URL& aURL , + const css::uno::Sequence< css::beans::PropertyValue >& /*lArguments*/, + const css::uno::Reference< css::frame::XDispatchResultListener >& xListener ) +{ + ::sal_Int16 nResult = css::frame::DispatchResultState::DONTKNOW; + if (isStartModuleDispatch(aURL)) + { + nResult = css::frame::DispatchResultState::FAILURE; + if (implts_isBackingModePossible ()) + { + implts_establishBackingMode (); + nResult = css::frame::DispatchResultState::SUCCESS; + } + } + + implts_notifyResultListener(xListener, nResult, css::uno::Any()); +} + +css::uno::Sequence< ::sal_Int16 > SAL_CALL StartModuleDispatcher::getSupportedCommandGroups() +{ + return css::uno::Sequence< ::sal_Int16 >(); +} + +css::uno::Sequence< css::frame::DispatchInformation > SAL_CALL StartModuleDispatcher::getConfigurableDispatchInformation(::sal_Int16 /*nCommandGroup*/) +{ + return css::uno::Sequence< css::frame::DispatchInformation >(); +} + +void SAL_CALL StartModuleDispatcher::addStatusListener(const css::uno::Reference< css::frame::XStatusListener >& /*xListener*/, + const css::util::URL& /*aURL*/ ) +{ +} + +void SAL_CALL StartModuleDispatcher::removeStatusListener(const css::uno::Reference< css::frame::XStatusListener >& /*xListener*/, + const css::util::URL& /*aURL*/ ) +{ +} + +bool StartModuleDispatcher::implts_isBackingModePossible() +{ + if ( ! SvtModuleOptions().IsModuleInstalled(SvtModuleOptions::EModule::STARTMODULE)) + return false; + + css::uno::Reference< css::frame::XFramesSupplier > xDesktop = + css::frame::Desktop::create( m_xContext ); + + FrameListAnalyzer aCheck( + xDesktop, + css::uno::Reference< css::frame::XFrame >(), + FrameAnalyzerFlags::Help | FrameAnalyzerFlags::BackingComponent); + + bool bIsPossible = false; + + if ( ! aCheck.m_xBackingComponent.is() + && aCheck.m_lOtherVisibleFrames.empty() ) + { + bIsPossible = true; + } + + return bIsPossible; +} + +void StartModuleDispatcher::implts_establishBackingMode() +{ + css::uno::Reference< css::frame::XDesktop2> xDesktop = css::frame::Desktop::create( m_xContext ); + css::uno::Reference< css::frame::XFrame > xFrame = xDesktop->findFrame(SPECIALTARGET_BLANK, 0); + css::uno::Reference< css::awt::XWindow > xContainerWindow = xFrame->getContainerWindow(); + + css::uno::Reference< css::frame::XController > xStartModule = css::frame::StartModule::createWithParentWindow(m_xContext, xContainerWindow); + css::uno::Reference< css::awt::XWindow > xComponentWindow(xStartModule, css::uno::UNO_QUERY); + xFrame->setComponent(xComponentWindow, xStartModule); + xStartModule->attachFrame(xFrame); + xContainerWindow->setVisible(true); +} + +void StartModuleDispatcher::implts_notifyResultListener(const css::uno::Reference< css::frame::XDispatchResultListener >& xListener, + ::sal_Int16 nState , + const css::uno::Any& aResult ) +{ + if ( ! xListener.is()) + return; + + css::frame::DispatchResultEvent aEvent( + css::uno::Reference< css::uno::XInterface >(static_cast< ::cppu::OWeakObject* >(this), css::uno::UNO_QUERY), + nState, + aResult); + + xListener->dispatchFinished(aEvent); +} + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/dispatch/systemexec.cxx b/framework/source/dispatch/systemexec.cxx new file mode 100644 index 0000000000..1891b9b62b --- /dev/null +++ b/framework/source/dispatch/systemexec.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 +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace framework{ + +constexpr OUString PROTOCOL_VALUE = u"systemexecute:"_ustr; + +// XInterface, XTypeProvider, XServiceInfo + +OUString SAL_CALL SystemExec::getImplementationName() +{ + return "com.sun.star.comp.framework.SystemExecute"; +} + +sal_Bool SAL_CALL SystemExec::supportsService( const OUString& sServiceName ) +{ + return cppu::supportsService(this, sServiceName); +} + +css::uno::Sequence< OUString > SAL_CALL SystemExec::getSupportedServiceNames() +{ + return { SERVICENAME_PROTOCOLHANDLER }; +} + +SystemExec::SystemExec( css::uno::Reference< css::uno::XComponentContext > xContext ) + : m_xContext (std::move( xContext )) +{ +} + +SystemExec::~SystemExec() +{ +} + +css::uno::Reference< css::frame::XDispatch > SAL_CALL SystemExec::queryDispatch( const css::util::URL& aURL , + const OUString&, + sal_Int32 ) +{ + css::uno::Reference< css::frame::XDispatch > xDispatcher; + if (aURL.Complete.startsWith(PROTOCOL_VALUE)) + xDispatcher = this; + return xDispatcher; +} + +css::uno::Sequence< css::uno::Reference< css::frame::XDispatch > > SAL_CALL SystemExec::queryDispatches( const css::uno::Sequence< css::frame::DispatchDescriptor >& lDescriptor ) +{ + sal_Int32 nCount = lDescriptor.getLength(); + css::uno::Sequence< css::uno::Reference< css::frame::XDispatch > > lDispatcher( nCount ); + auto lDispatcherRange = asNonConstRange(lDispatcher); + for( sal_Int32 i=0; i& lArguments ) +{ + dispatchWithNotification(aURL, lArguments, css::uno::Reference< css::frame::XDispatchResultListener >()); +} + +void SAL_CALL SystemExec::dispatchWithNotification( const css::util::URL& aURL , + const css::uno::Sequence< css::beans::PropertyValue >&, + const css::uno::Reference< css::frame::XDispatchResultListener >& xListener ) +{ + // convert "systemexec:file:///c:/temp/test.html" => "file:///c:/temp/test.html" + sal_Int32 c = aURL.Complete.getLength()-PROTOCOL_VALUE.getLength(); + if (c<1) // we don't check for valid URLs here! The system will show an error message ... + { + impl_notifyResultListener(xListener, css::frame::DispatchResultState::FAILURE); + return; + } + OUString sSystemURLWithVariables = aURL.Complete.copy(PROTOCOL_VALUE.getLength(), c); + + // TODO check security settings ... + + try + { + css::uno::Reference< css::util::XStringSubstitution > xPathSubst( css::util::PathSubstitution::create(m_xContext) ); + + OUString sSystemURL = xPathSubst->substituteVariables(sSystemURLWithVariables, true); // sal_True force an exception if unknown variables exists ! + + css::uno::Reference< css::system::XSystemShellExecute > xShell = css::system::SystemShellExecute::create( m_xContext ); + + xShell->execute(sSystemURL, OUString(), css::system::SystemShellExecuteFlags::URIS_ONLY); + impl_notifyResultListener(xListener, css::frame::DispatchResultState::SUCCESS); + } + catch(const css::uno::Exception&) + { + impl_notifyResultListener(xListener, css::frame::DispatchResultState::FAILURE); + } +} + +void SAL_CALL SystemExec::addStatusListener( const css::uno::Reference< css::frame::XStatusListener >&, + const css::util::URL& ) +{ + // not supported yet +} + +void SAL_CALL SystemExec::removeStatusListener( const css::uno::Reference< css::frame::XStatusListener >&, + const css::util::URL& ) +{ + // not supported yet +} + +void SystemExec::impl_notifyResultListener(const css::uno::Reference< css::frame::XDispatchResultListener >& xListener, + const sal_Int16 nState ) +{ + if (xListener.is()) + { + css::frame::DispatchResultEvent aEvent; + aEvent.State = nState; + xListener->dispatchFinished(aEvent); + } +} + +} // namespace framework + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +framework_SystemExecute_get_implementation( + css::uno::XComponentContext* context, css::uno::Sequence const& ) +{ + return cppu::acquire(new framework::SystemExec(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/dispatch/windowcommanddispatch.cxx b/framework/source/dispatch/windowcommanddispatch.cxx new file mode 100644 index 0000000000..abad29dc75 --- /dev/null +++ b/framework/source/dispatch/windowcommanddispatch.cxx @@ -0,0 +1,158 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace framework{ + +WindowCommandDispatch::WindowCommandDispatch(css::uno::Reference< css::uno::XComponentContext > xContext , + const css::uno::Reference< css::frame::XFrame >& xFrame) + : m_xContext (std::move(xContext )) + , m_xFrame (xFrame ) + , m_xWindow (xFrame->getContainerWindow()) +{ + impl_startListening(); +} + +WindowCommandDispatch::~WindowCommandDispatch() +{ + impl_stopListening(); + m_xContext.clear(); +} + +void WindowCommandDispatch::impl_startListening() +{ + std::unique_lock aReadLock(m_mutex); + css::uno::Reference< css::awt::XWindow > xWindow( m_xWindow.get(), css::uno::UNO_QUERY ); + aReadLock.unlock(); + + if ( ! xWindow.is()) + return; + + { + SolarMutexGuard aSolarLock; + + VclPtr pWindow = VCLUnoHelper::GetWindow(xWindow); + if ( ! pWindow) + return; + + pWindow->AddEventListener( LINK(this, WindowCommandDispatch, impl_notifyCommand) ); + } +} + +void WindowCommandDispatch::impl_stopListening() +{ + std::unique_lock aReadLock(m_mutex); + css::uno::Reference< css::awt::XWindow > xWindow( m_xWindow.get(), css::uno::UNO_QUERY ); + aReadLock.unlock(); + + if (!xWindow.is()) + return; + + { + SolarMutexGuard aSolarLock; + + VclPtr pWindow = VCLUnoHelper::GetWindow(xWindow); + if (!pWindow) + return; + + pWindow->RemoveEventListener( LINK(this, WindowCommandDispatch, impl_notifyCommand) ); + + m_xWindow.clear(); + } +} + +IMPL_LINK(WindowCommandDispatch, impl_notifyCommand, VclWindowEvent&, rEvent, void) +{ + if (rEvent.GetId() == VclEventId::ObjectDying) + { + impl_stopListening(); + return; + } + if (rEvent.GetId() != VclEventId::WindowCommand) + return; + + const CommandEvent* pCommand = static_cast(rEvent.GetData()); + if (pCommand->GetCommand() != CommandEventId::ShowDialog) + return; + + const CommandDialogData* pData = pCommand->GetDialogData(); + if ( ! pData) + return; + + const ShowDialogId nCommand = pData->GetDialogId(); + OUString sCommand; + + switch (nCommand) + { + case ShowDialogId::Preferences : + sCommand = ".uno:OptionsTreeDialog"; + break; + + case ShowDialogId::About : + sCommand = ".uno:About"; + break; + + default : + return; + } + + // ignore all errors here. It's clicking a menu entry only ... + // The user will try it again, in case nothing happens .-) + try + { + // SYNCHRONIZED -> + std::unique_lock aReadLock(m_mutex); + css::uno::Reference< css::frame::XDispatchProvider > xProvider(m_xFrame.get(), css::uno::UNO_QUERY_THROW); + css::uno::Reference< css::uno::XComponentContext > xContext = m_xContext; + aReadLock.unlock(); + // <- SYNCHRONIZED + + // check provider ... we know it's weak reference only + if ( ! xProvider.is()) + return; + + css::uno::Reference< css::util::XURLTransformer > xParser(css::util::URLTransformer::create(xContext)); + css::util::URL aCommand; + aCommand.Complete = sCommand; + xParser->parseStrict(aCommand); + + css::uno::Reference< css::frame::XDispatch > xDispatch = xProvider->queryDispatch(aCommand, SPECIALTARGET_SELF, 0); + if (xDispatch.is()) + xDispatch->dispatch(aCommand, css::uno::Sequence< css::beans::PropertyValue >()); + } + catch(const css::uno::Exception&) + {} +} + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/fwe/classes/actiontriggercontainer.cxx b/framework/source/fwe/classes/actiontriggercontainer.cxx new file mode 100644 index 0000000000..360223e6eb --- /dev/null +++ b/framework/source/fwe/classes/actiontriggercontainer.cxx @@ -0,0 +1,134 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include + +using namespace cppu; +using namespace com::sun::star::uno; +using namespace com::sun::star::lang; +using namespace com::sun::star::container; + +namespace framework +{ + +ActionTriggerContainer::ActionTriggerContainer() +{ +} + +ActionTriggerContainer::~ActionTriggerContainer() +{ +} + +// XInterface +Any SAL_CALL ActionTriggerContainer::queryInterface( const Type& aType ) +{ + Any a = ::cppu::queryInterface( + aType , + static_cast< XMultiServiceFactory* >(this), + static_cast< XServiceInfo* >(this), + static_cast< XTypeProvider* >(this)); + + if( a.hasValue() ) + { + return a; + } + + return PropertySetContainer::queryInterface( aType ); +} + +void ActionTriggerContainer::acquire() noexcept +{ + PropertySetContainer::acquire(); +} + +void ActionTriggerContainer::release() noexcept +{ + PropertySetContainer::release(); +} + +// XMultiServiceFactory +Reference< XInterface > SAL_CALL ActionTriggerContainer::createInstance( const OUString& aServiceSpecifier ) +{ + if ( aServiceSpecifier == SERVICENAME_ACTIONTRIGGER ) + return static_cast( new ActionTriggerPropertySet()); + else if ( aServiceSpecifier == SERVICENAME_ACTIONTRIGGERCONTAINER ) + return static_cast( new ActionTriggerContainer()); + else if ( aServiceSpecifier == SERVICENAME_ACTIONTRIGGERSEPARATOR ) + return static_cast( new ActionTriggerSeparatorPropertySet()); + else + throw css::uno::RuntimeException("Unknown service specifier!", static_cast(this) ); +} + +Reference< XInterface > SAL_CALL ActionTriggerContainer::createInstanceWithArguments( const OUString& ServiceSpecifier, const Sequence< Any >& /*Arguments*/ ) +{ + return createInstance( ServiceSpecifier ); +} + +Sequence< OUString > SAL_CALL ActionTriggerContainer::getAvailableServiceNames() +{ + Sequence< OUString > aSeq{ SERVICENAME_ACTIONTRIGGER, + SERVICENAME_ACTIONTRIGGERCONTAINER, + SERVICENAME_ACTIONTRIGGERSEPARATOR }; + + return aSeq; +} + +// XServiceInfo +OUString SAL_CALL ActionTriggerContainer::getImplementationName() +{ + return IMPLEMENTATIONNAME_ACTIONTRIGGERCONTAINER; +} + +sal_Bool SAL_CALL ActionTriggerContainer::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService(this, ServiceName); +} + +Sequence< OUString > SAL_CALL ActionTriggerContainer::getSupportedServiceNames() +{ + Sequence< OUString > seqServiceNames { SERVICENAME_ACTIONTRIGGERCONTAINER }; + return seqServiceNames; +} + +// XTypeProvider +Sequence< Type > SAL_CALL ActionTriggerContainer::getTypes() +{ + // Create a static typecollection ... + static ::cppu::OTypeCollection ourTypeCollection( + cppu::UnoType::get(), + cppu::UnoType::get(), + cppu::UnoType::get(), + cppu::UnoType::get()); + + return ourTypeCollection.getTypes(); +} + +Sequence< sal_Int8 > SAL_CALL ActionTriggerContainer::getImplementationId() +{ + return css::uno::Sequence(); +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/fwe/classes/actiontriggerpropertyset.cxx b/framework/source/fwe/classes/actiontriggerpropertyset.cxx new file mode 100644 index 0000000000..4592174df5 --- /dev/null +++ b/framework/source/fwe/classes/actiontriggerpropertyset.cxx @@ -0,0 +1,371 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include + +using namespace cppu; +using namespace com::sun::star::uno; +using namespace com::sun::star::beans; +using namespace com::sun::star::lang; +using namespace com::sun::star::awt; + +//struct SAL_DLLPUBLIC_IMPORT ::cppu::OBroadcastHelperVar< OMultiTypeInterfaceContainerHelper, OMultiTypeInterfaceContainerHelper::keyType >; + +namespace { + +// Handles for properties +// (PLEASE SORT THIS FIELD, IF YOU ADD NEW PROPERTIES!) +// We use an enum to define these handles, to use all numbers from 0 to nn and +// if you add someone, you don't must control this! +// But don't forget to change values of follow defines, if you do something with this enum! +enum EPROPERTIES +{ + HANDLE_COMMANDURL, + HANDLE_HELPURL, + HANDLE_IMAGE, + HANDLE_SUBCONTAINER, + HANDLE_TEXT, + PROPERTYCOUNT +}; + +} + +namespace framework +{ + +ActionTriggerPropertySet::ActionTriggerPropertySet() + : OBroadcastHelper ( m_aMutex ) + , OPropertySetHelper ( *static_cast< OBroadcastHelper * >(this) ) +{ +} + +ActionTriggerPropertySet::~ActionTriggerPropertySet() +{ +} + +// XInterface +Any SAL_CALL ActionTriggerPropertySet::queryInterface( const Type& aType ) +{ + Any a = ::cppu::queryInterface( + aType, + static_cast< XServiceInfo* >(this), + static_cast< XTypeProvider* >(this)); + + if( a.hasValue() ) + return a; + else + { + a = OPropertySetHelper::queryInterface( aType ); + + if( a.hasValue() ) + return a; + } + + return OWeakObject::queryInterface( aType ); +} + +void SAL_CALL ActionTriggerPropertySet::acquire() noexcept +{ + OWeakObject::acquire(); +} + +void SAL_CALL ActionTriggerPropertySet::release() noexcept +{ + OWeakObject::release(); +} + +// XServiceInfo +OUString SAL_CALL ActionTriggerPropertySet::getImplementationName() +{ + return IMPLEMENTATIONNAME_ACTIONTRIGGER; +} + +sal_Bool SAL_CALL ActionTriggerPropertySet::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService(this, ServiceName); +} + +Sequence< OUString > SAL_CALL ActionTriggerPropertySet::getSupportedServiceNames() +{ + Sequence seqServiceNames { SERVICENAME_ACTIONTRIGGER }; + return seqServiceNames; +} + +// XTypeProvider +Sequence< Type > SAL_CALL ActionTriggerPropertySet::getTypes() +{ + // Create a static typecollection ... + static ::cppu::OTypeCollection ourTypeCollection( + cppu::UnoType::get(), + cppu::UnoType::get(), + cppu::UnoType::get(), + cppu::UnoType::get(), + cppu::UnoType::get()); + + + return ourTypeCollection.getTypes(); +} + +Sequence< sal_Int8 > SAL_CALL ActionTriggerPropertySet::getImplementationId() +{ + return css::uno::Sequence(); +} + +sal_Bool SAL_CALL ActionTriggerPropertySet::convertFastPropertyValue( + Any& aConvertedValue, + Any& aOldValue, + sal_Int32 nHandle, + const Any& aValue ) +{ + // Check, if value of property will changed in method "setFastPropertyValue_NoBroadcast()". + // Return sal_True, if changed - else return sal_False. + // Attention: Method "impl_tryToChangeProperty()" can throw the IllegalArgumentException !!! + // Initialize return value with sal_False !!! + // (Handle can be invalid) + bool bReturn = false; + + switch( nHandle ) + { + case HANDLE_COMMANDURL: + bReturn = impl_tryToChangeProperty( m_aCommandURL, aValue, aOldValue, aConvertedValue ); + break; + + case HANDLE_HELPURL: + bReturn = impl_tryToChangeProperty( m_aHelpURL, aValue, aOldValue, aConvertedValue ); + break; + + case HANDLE_IMAGE: + bReturn = impl_tryToChangeProperty( m_xBitmap, aValue, aOldValue, aConvertedValue ); + break; + + case HANDLE_SUBCONTAINER: + bReturn = impl_tryToChangeProperty( m_xActionTriggerContainer, aValue, aOldValue, aConvertedValue ); + break; + + case HANDLE_TEXT: + bReturn = impl_tryToChangeProperty( m_aText, aValue, aOldValue, aConvertedValue ); + break; + } + + // Return state of operation. + return bReturn; +} + +void SAL_CALL ActionTriggerPropertySet::setFastPropertyValue_NoBroadcast( + sal_Int32 nHandle, const Any& aValue ) +{ + SolarMutexGuard aGuard; + + // Search for right handle ... and try to set property value. + switch( nHandle ) + { + case HANDLE_COMMANDURL: + aValue >>= m_aCommandURL; + break; + + case HANDLE_HELPURL: + aValue >>= m_aHelpURL; + break; + + case HANDLE_IMAGE: + aValue >>= m_xBitmap; + break; + + case HANDLE_SUBCONTAINER: + aValue >>= m_xActionTriggerContainer; + break; + + case HANDLE_TEXT: + aValue >>= m_aText; + break; + } +} + +void SAL_CALL ActionTriggerPropertySet::getFastPropertyValue( + Any& aValue, sal_Int32 nHandle ) const +{ + SolarMutexGuard aGuard; + + // Search for right handle ... and try to get property value. + switch( nHandle ) + { + case HANDLE_COMMANDURL: + aValue <<= m_aCommandURL; + break; + + case HANDLE_HELPURL: + aValue <<= m_aHelpURL; + break; + + case HANDLE_IMAGE: + aValue <<= m_xBitmap; + break; + + case HANDLE_SUBCONTAINER: + aValue <<= m_xActionTriggerContainer; + break; + + case HANDLE_TEXT: + aValue <<= m_aText; + break; + } +} + +::cppu::IPropertyArrayHelper& SAL_CALL ActionTriggerPropertySet::getInfoHelper() +{ + // Define static member to give structure of properties to baseclass "OPropertySetHelper". + // "impl_getStaticPropertyDescriptor" is a non exported and static function, who will define a static propertytable. + // "true" say: Table is sorted by name. + static OPropertyArrayHelper ourInfoHelper( impl_getStaticPropertyDescriptor(), true ); + + return ourInfoHelper; +} + +Reference< XPropertySetInfo > SAL_CALL ActionTriggerPropertySet::getPropertySetInfo() +{ + // Create structure of propertysetinfo for baseclass "OPropertySetHelper". + // (Use method "getInfoHelper()".) + static Reference< XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) ); + + return xInfo; +} + +Sequence< Property > ActionTriggerPropertySet::impl_getStaticPropertyDescriptor() +{ + return + { + Property( "CommandURL" , HANDLE_COMMANDURL , cppu::UnoType::get(), PropertyAttribute::TRANSIENT ), + Property( "HelpURL" , HANDLE_HELPURL , cppu::UnoType::get(), PropertyAttribute::TRANSIENT ), + Property( "Image" , HANDLE_IMAGE , cppu::UnoType::get(), PropertyAttribute::TRANSIENT ), + Property( "SubContainer" , HANDLE_SUBCONTAINER , cppu::UnoType::get(), PropertyAttribute::TRANSIENT ), + Property( "Text" , HANDLE_TEXT , cppu::UnoType::get(), PropertyAttribute::TRANSIENT ) + }; +} + +bool ActionTriggerPropertySet::impl_tryToChangeProperty( + const OUString& sCurrentValue , + const Any& aNewValue , + Any& aOldValue , + Any& aConvertedValue ) +{ + // Set default return value if method failed. + bool bReturn = false; + // Get new value from any. + // IllegalArgumentException() can be thrown! + OUString sValue; + convertPropertyValue( sValue, aNewValue ); + + // If value change ... + if( sValue != sCurrentValue ) + { + // ... set information of change. + aOldValue <<= sCurrentValue; + aConvertedValue <<= sValue; + // Return OK - "value will be change ..." + bReturn = true; + } + else + { + // ... clear information of return parameter! + aOldValue.clear (); + aConvertedValue.clear (); + // Return NOTHING - "value will not be change ..." + bReturn = false; + } + + return bReturn; +} + +bool ActionTriggerPropertySet::impl_tryToChangeProperty( + const Reference< XBitmap >& aCurrentValue , + const Any& aNewValue , + Any& aOldValue , + Any& aConvertedValue ) +{ + // Set default return value if method failed. + bool bReturn = false; + // Get new value from any. + // IllegalArgumentException() can be thrown! + Reference< XBitmap > aValue; + convertPropertyValue( aValue, aNewValue ); + + // If value change ... + if( aValue != aCurrentValue ) + { + // ... set information of change. + aOldValue <<= aCurrentValue; + aConvertedValue <<= aValue; + // Return OK - "value will be change ..." + bReturn = true; + } + else + { + // ... clear information of return parameter! + aOldValue.clear (); + aConvertedValue.clear (); + // Return NOTHING - "value will not be change ..." + bReturn = false; + } + + return bReturn; +} + +bool ActionTriggerPropertySet::impl_tryToChangeProperty( + const Reference< XInterface >& aCurrentValue , + const Any& aNewValue , + Any& aOldValue , + Any& aConvertedValue ) +{ + // Set default return value if method failed. + bool bReturn = false; + // Get new value from any. + // IllegalArgumentException() can be thrown! + Reference< XInterface > aValue; + convertPropertyValue( aValue, aNewValue ); + + // If value change ... + if( aValue != aCurrentValue ) + { + // ... set information of change. + aOldValue <<= aCurrentValue; + aConvertedValue <<= aValue; + // Return OK - "value will be change ..." + bReturn = true; + } + else + { + // ... clear information of return parameter! + aOldValue.clear (); + aConvertedValue.clear (); + // Return NOTHING - "value will not be change ..." + bReturn = false; + } + + return bReturn; +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/fwe/classes/actiontriggerseparatorpropertyset.cxx b/framework/source/fwe/classes/actiontriggerseparatorpropertyset.cxx new file mode 100644 index 0000000000..104d765f5c --- /dev/null +++ b/framework/source/fwe/classes/actiontriggerseparatorpropertyset.cxx @@ -0,0 +1,245 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include + +using namespace cppu; +using namespace com::sun::star::uno; +using namespace com::sun::star::beans; +using namespace com::sun::star::lang; +using namespace com::sun::star::awt; + +namespace { + +// Handles for properties +// (PLEASE SORT THIS FIELD, IF YOU ADD NEW PROPERTIES!) +// We use an enum to define these handles, to use all numbers from 0 to nn and +// if you add someone, you don't must control this! +// But don't forget to change values of follow defines, if you do something with this enum! +enum EPROPERTIES +{ + HANDLE_TYPE, + PROPERTYCOUNT +}; + +} + +namespace framework +{ + +ActionTriggerSeparatorPropertySet::ActionTriggerSeparatorPropertySet() + : OBroadcastHelper ( m_aMutex ) + , OPropertySetHelper ( *static_cast< OBroadcastHelper * >(this) ) + , m_nSeparatorType( 0 ) +{ +} + +ActionTriggerSeparatorPropertySet::~ActionTriggerSeparatorPropertySet() +{ +} + +// XInterface +Any SAL_CALL ActionTriggerSeparatorPropertySet::queryInterface( const Type& aType ) +{ + Any a = ::cppu::queryInterface( + aType, + static_cast< XServiceInfo* >(this), + static_cast< XTypeProvider* >(this)); + + if( a.hasValue() ) + return a; + else + { + a = OPropertySetHelper::queryInterface( aType ); + + if( a.hasValue() ) + return a; + } + + return OWeakObject::queryInterface( aType ); +} + +void ActionTriggerSeparatorPropertySet::acquire() noexcept +{ + OWeakObject::acquire(); +} + +void ActionTriggerSeparatorPropertySet::release() noexcept +{ + OWeakObject::release(); +} + +// XServiceInfo +OUString SAL_CALL ActionTriggerSeparatorPropertySet::getImplementationName() +{ + return IMPLEMENTATIONNAME_ACTIONTRIGGERSEPARATOR; +} + +sal_Bool SAL_CALL ActionTriggerSeparatorPropertySet::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService(this, ServiceName); +} + +Sequence< OUString > SAL_CALL ActionTriggerSeparatorPropertySet::getSupportedServiceNames() +{ + Sequence seqServiceNames { SERVICENAME_ACTIONTRIGGERSEPARATOR }; + return seqServiceNames; +} + +// XTypeProvider +Sequence< Type > SAL_CALL ActionTriggerSeparatorPropertySet::getTypes() +{ + // Create a static typecollection ... + static ::cppu::OTypeCollection ourTypeCollection( + cppu::UnoType::get(), + cppu::UnoType::get(), + cppu::UnoType::get(), + cppu::UnoType::get(), + cppu::UnoType::get()); + + return ourTypeCollection.getTypes(); +} + +Sequence< sal_Int8 > SAL_CALL ActionTriggerSeparatorPropertySet::getImplementationId() +{ + return css::uno::Sequence(); +} + +sal_Bool SAL_CALL ActionTriggerSeparatorPropertySet::convertFastPropertyValue( + Any& aConvertedValue, + Any& aOldValue, + sal_Int32 nHandle, + const Any& aValue ) +{ + // Check, if value of property will changed in method "setFastPropertyValue_NoBroadcast()". + // Return sal_True, if changed - else return sal_False. + // Attention: Method "impl_tryToChangeProperty()" can throw the IllegalArgumentException !!! + // Initialize return value with sal_False !!! + // (Handle can be invalid) + bool bReturn = false; + + switch( nHandle ) + { + case HANDLE_TYPE: + bReturn = impl_tryToChangeProperty( m_nSeparatorType, aValue, aOldValue, aConvertedValue ); + break; + } + + // Return state of operation. + return bReturn; +} + +void SAL_CALL ActionTriggerSeparatorPropertySet::setFastPropertyValue_NoBroadcast( + sal_Int32 nHandle, const Any& aValue ) +{ + SolarMutexGuard aGuard; + + // Search for right handle ... and try to set property value. + switch( nHandle ) + { + case HANDLE_TYPE: + aValue >>= m_nSeparatorType; + break; + } +} + +void SAL_CALL ActionTriggerSeparatorPropertySet::getFastPropertyValue( + Any& aValue, sal_Int32 nHandle ) const +{ + SolarMutexGuard aGuard; + + // Search for right handle ... and try to get property value. + switch( nHandle ) + { + case HANDLE_TYPE: + aValue <<= m_nSeparatorType; + break; + } +} + +::cppu::IPropertyArrayHelper& SAL_CALL ActionTriggerSeparatorPropertySet::getInfoHelper() +{ + // Define static member to give structure of properties to baseclass "OPropertySetHelper". + // "impl_getStaticPropertyDescriptor" is a non exported and static function, who will define a static propertytable. + // "true" indicates: Table is sorted by name. + static OPropertyArrayHelper ourInfoHelper( impl_getStaticPropertyDescriptor(), true ); + + return ourInfoHelper; +} + +Reference< XPropertySetInfo > SAL_CALL ActionTriggerSeparatorPropertySet::getPropertySetInfo() +{ + // Create structure of propertysetinfo for baseclass "OPropertySetHelper". + // (Use method "getInfoHelper()".) + static Reference< XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) ); + + return xInfo; +} + +Sequence< Property > ActionTriggerSeparatorPropertySet::impl_getStaticPropertyDescriptor() +{ + return + { + Property( "SeparatorType", HANDLE_TYPE, cppu::UnoType::get(), PropertyAttribute::TRANSIENT ) + }; +} + +bool ActionTriggerSeparatorPropertySet::impl_tryToChangeProperty( + sal_Int16 aCurrentValue , + const Any& aNewValue , + Any& aOldValue , + Any& aConvertedValue ) +{ + // Set default return value if method failed. + bool bReturn = false; + // Get new value from any. + // IllegalArgumentException() can be thrown! + sal_Int16 aValue = 0; + convertPropertyValue( aValue, aNewValue ); + + // If value change ... + if( aValue != aCurrentValue ) + { + // ... set information of change. + aOldValue <<= aCurrentValue; + aConvertedValue <<= aValue; + // Return OK - "value will be change ..." + bReturn = true; + } + else + { + // ... clear information of return parameter! + aOldValue.clear (); + aConvertedValue.clear (); + // Return NOTHING - "value will not be change ..." + bReturn = false; + } + + return bReturn; +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/fwe/classes/addonmenu.cxx b/framework/source/fwe/classes/addonmenu.cxx new file mode 100644 index 0000000000..db6a7435ac --- /dev/null +++ b/framework/source/fwe/classes/addonmenu.cxx @@ -0,0 +1,299 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include + +#include +#include +#include + +#include +#include + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::frame; +using namespace ::com::sun::star::beans; + +namespace framework +{ + +bool AddonMenuManager::HasAddonMenuElements() +{ + return AddonsOptions().HasAddonsMenu(); +} + +// Create the Add-Ons menu +VclPtr AddonMenuManager::CreateAddonMenu( const Reference< XFrame >& rFrame ) +{ + AddonsOptions aOptions; + VclPtr pAddonMenu; + + const Sequence< Sequence< PropertyValue > >& rAddonMenuEntries = aOptions.GetAddonsMenu(); + if ( rAddonMenuEntries.hasElements() ) + { + sal_uInt16 nUniqueMenuId = ADDONMENU_ITEMID_START; + pAddonMenu = VclPtr::Create(); + OUString aModuleIdentifier = vcl::CommandInfoProvider::GetModuleIdentifier( rFrame ); + AddonMenuManager::BuildMenu( pAddonMenu, MENU_APPEND, nUniqueMenuId, rAddonMenuEntries, rFrame, aModuleIdentifier ); + + // Don't return an empty Add-On menu + if ( pAddonMenu->GetItemCount() == 0 ) + { + pAddonMenu.disposeAndClear(); + } + } + + return pAddonMenu; +} + +// Returns the next insert position from nPos. +sal_uInt16 AddonMenuManager::GetNextPos( sal_uInt16 nPos ) +{ + return ( nPos == MENU_APPEND ) ? MENU_APPEND : ( nPos+1 ); +} + +static sal_uInt16 FindMenuId( Menu const * pMenu, std::u16string_view aCommand ) +{ + sal_uInt16 nPos = 0; + OUString aCmd; + for ( nPos = 0; nPos < pMenu->GetItemCount(); nPos++ ) + { + sal_uInt16 nId = pMenu->GetItemId( nPos ); + aCmd = pMenu->GetItemCommand( nId ); + if ( aCmd == aCommand ) + return nId; + } + + return USHRT_MAX; +} + +// Merge the Add-Ons help menu items into the given menu bar at a defined pos +void AddonMenuManager::MergeAddonHelpMenu( const Reference< XFrame >& rFrame, + MenuBar const * pMergeMenuBar ) +{ + if ( !pMergeMenuBar ) + return; + + PopupMenu* pHelpMenu(nullptr); + sal_uInt16 nId = FindMenuId(pMergeMenuBar, u".uno:HelpMenu"); + if ( nId != USHRT_MAX ) + pHelpMenu = pMergeMenuBar->GetPopupMenu( nId ); + + if ( !pHelpMenu ) + return; + + // Add-Ons help menu items should be inserted after the "registration" menu item + sal_uInt16 nItemCount = pHelpMenu->GetItemCount(); + sal_uInt16 nInsSepAfterPos = MENU_APPEND; + sal_uInt16 nUniqueMenuId = ADDONMENU_ITEMID_START; + AddonsOptions aOptions; + + // try to detect the about menu item with the command URL + nId = FindMenuId(pHelpMenu, u".uno:About"); + sal_uInt16 nInsPos = pHelpMenu->GetItemPos( nId ); + + const Sequence< Sequence< PropertyValue > >& rAddonHelpMenuEntries = aOptions.GetAddonsHelpMenu(); + + if ( nInsPos < nItemCount && pHelpMenu->GetItemType( nInsPos ) != MenuItemType::SEPARATOR ) + nInsSepAfterPos = nInsPos; + + OUString aModuleIdentifier = vcl::CommandInfoProvider::GetModuleIdentifier(rFrame); + AddonMenuManager::BuildMenu( pHelpMenu, nInsPos, nUniqueMenuId, rAddonHelpMenuEntries, rFrame, aModuleIdentifier ); + + if ( pHelpMenu->GetItemCount() > nItemCount ) + { + if ( nInsSepAfterPos < MENU_APPEND ) + { + nInsSepAfterPos += ( pHelpMenu->GetItemCount() - nItemCount ); + if ( pHelpMenu->GetItemType( nInsSepAfterPos ) != MenuItemType::SEPARATOR ) + pHelpMenu->InsertSeparator({}, nInsSepAfterPos); + } + pHelpMenu->InsertSeparator({}, nItemCount); + } +} + +// Merge the addon popup menus into the given menu bar at the provided pos. +void AddonMenuManager::MergeAddonPopupMenus( const Reference< XFrame >& rFrame, + sal_uInt16 nMergeAtPos, + MenuBar* pMergeMenuBar ) +{ + if ( !pMergeMenuBar ) + return; + + AddonsOptions aAddonsOptions; + sal_uInt16 nInsertPos = nMergeAtPos; + + OUString aTitle; + OUString aURL; + OUString aTarget; + OUString aContext; + Sequence< Sequence< PropertyValue > > aAddonSubMenu; + sal_uInt16 nUniqueMenuId = ADDONMENU_ITEMID_START; + + OUString aModuleIdentifier = vcl::CommandInfoProvider::GetModuleIdentifier(rFrame); + + const Sequence< Sequence< PropertyValue > >& rAddonMenuEntries = aAddonsOptions.GetAddonsMenuBarPart(); + for ( const Sequence< PropertyValue >& rEntry : rAddonMenuEntries ) + { + AddonMenuManager::GetMenuEntry( rEntry, + aTitle, + aURL, + aTarget, + aContext, + aAddonSubMenu ); + if ( !aTitle.isEmpty() && + !aURL.isEmpty() && + aAddonSubMenu.hasElements() && + AddonMenuManager::IsCorrectContext( aModuleIdentifier, aContext )) + { + sal_uInt16 nId = nUniqueMenuId++; + VclPtrInstance pAddonPopupMenu; + + AddonMenuManager::BuildMenu( pAddonPopupMenu, MENU_APPEND, nUniqueMenuId, aAddonSubMenu, rFrame, aModuleIdentifier ); + + if ( pAddonPopupMenu->GetItemCount() > 0 ) + { + pMergeMenuBar->InsertItem( nId, aTitle, MenuItemBits::NONE, {}, nInsertPos++); + pMergeMenuBar->SetPopupMenu( nId, pAddonPopupMenu ); + + // Store the command URL into the VCL menu bar for later identification + pMergeMenuBar->SetItemCommand( nId, aURL ); + } + else + pAddonPopupMenu.disposeAndClear(); + } + } +} + +// Insert the menu and sub menu entries into pCurrentMenu with the aAddonMenuDefinition provided +void AddonMenuManager::BuildMenu( PopupMenu* pCurrentMenu, + sal_uInt16 nInsPos, + sal_uInt16& nUniqueMenuId, + const Sequence< Sequence< PropertyValue > >& aAddonMenuDefinition, + const Reference< XFrame >& rFrame, + const OUString& rModuleIdentifier ) +{ + Sequence< Sequence< PropertyValue > > aAddonSubMenu; + bool bInsertSeparator = false; + sal_uInt32 i = 0; + sal_uInt32 nElements = 0; + sal_uInt32 nCount = aAddonMenuDefinition.getLength(); + + OUString aTitle; + OUString aURL; + OUString aTarget; + OUString aContext; + + for ( i = 0; i < nCount; ++i ) + { + GetMenuEntry( aAddonMenuDefinition[i], aTitle, aURL, aTarget, aContext, aAddonSubMenu ); + + if ( !IsCorrectContext( rModuleIdentifier, aContext ) || ( aTitle.isEmpty() && aURL.isEmpty() )) + continue; + + if ( aURL == "private:separator" ) + bInsertSeparator = true; + else + { + VclPtr pSubMenu; + if ( aAddonSubMenu.hasElements() ) + { + pSubMenu = VclPtr::Create(); + AddonMenuManager::BuildMenu( pSubMenu, MENU_APPEND, nUniqueMenuId, aAddonSubMenu, rFrame, rModuleIdentifier ); + + // Don't create a menu item for an empty sub menu + if ( pSubMenu->GetItemCount() == 0 ) + { + pSubMenu.disposeAndClear(); + continue; + } + } + + if ( bInsertSeparator && nElements > 0 ) + { + // Insert a separator only when we insert a new element afterwards and we + // have already one before us + nElements = 0; + bInsertSeparator = false; + pCurrentMenu->InsertSeparator({}, nInsPos); + nInsPos = AddonMenuManager::GetNextPos( nInsPos ); + } + + sal_uInt16 nId = nUniqueMenuId++; + pCurrentMenu->InsertItem(nId, aTitle, MenuItemBits::NONE, {}, nInsPos); + nInsPos = AddonMenuManager::GetNextPos( nInsPos ); + + ++nElements; + + void* nAttributePtr = MenuAttributes::CreateAttribute(aTarget, OUString()); + pCurrentMenu->SetUserValue(nId, nAttributePtr, MenuAttributes::ReleaseAttribute); + pCurrentMenu->SetItemCommand( nId, aURL ); + + if ( pSubMenu ) + pCurrentMenu->SetPopupMenu( nId, pSubMenu ); + } + } +} + +// Retrieve the menu entry property values from a sequence +void AddonMenuManager::GetMenuEntry( const Sequence< PropertyValue >& rAddonMenuEntry, + OUString& rTitle, + OUString& rURL, + OUString& rTarget, + OUString& rContext, + Sequence< Sequence< PropertyValue > >& rAddonSubMenu ) +{ + // Reset submenu parameter + rAddonSubMenu = Sequence< Sequence< PropertyValue > >(); + + for ( const PropertyValue& rEntry : rAddonMenuEntry ) + { + OUString aMenuEntryPropName = rEntry.Name; + if ( aMenuEntryPropName == ADDONSMENUITEM_STRING_URL ) + rEntry.Value >>= rURL; + else if ( aMenuEntryPropName == ADDONSMENUITEM_STRING_TITLE ) + rEntry.Value >>= rTitle; + else if ( aMenuEntryPropName == ADDONSMENUITEM_STRING_TARGET ) + rEntry.Value >>= rTarget; + else if ( aMenuEntryPropName == ADDONSMENUITEM_STRING_SUBMENU ) + rEntry.Value >>= rAddonSubMenu; + else if ( aMenuEntryPropName == ADDONSMENUITEM_STRING_CONTEXT ) + rEntry.Value >>= rContext; + } +} + +// Check if the context string matches the provided xModel context +bool AddonMenuManager::IsCorrectContext( std::u16string_view rModuleIdentifier, std::u16string_view rContext ) +{ + if ( rContext.empty() ) + return true; + + if ( !rModuleIdentifier.empty() ) + { + return rContext.find( rModuleIdentifier ) != std::u16string_view::npos; + } + + return false; +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/fwe/classes/addonsoptions.cxx b/framework/source/fwe/classes/addonsoptions.cxx new file mode 100644 index 0000000000..7801fa5e81 --- /dev/null +++ b/framework/source/fwe/classes/addonsoptions.cxx @@ -0,0 +1,1952 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +// namespaces + +using namespace ::utl; +using namespace ::osl; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star; + +constexpr OUStringLiteral ROOTNODE_ADDONMENU = u"Office.Addons"; +constexpr OUStringLiteral PATHDELIMITER = u"/"; +constexpr OUString SEPARATOR_URL = u"private:separator"_ustr; + +#define PROPERTYNAME_URL ADDONSMENUITEM_STRING_URL +#define PROPERTYNAME_TITLE ADDONSMENUITEM_STRING_TITLE +#define PROPERTYNAME_TARGET ADDONSMENUITEM_STRING_TARGET +#define PROPERTYNAME_IMAGEIDENTIFIER ADDONSMENUITEM_STRING_IMAGEIDENTIFIER +#define PROPERTYNAME_CONTEXT ADDONSMENUITEM_STRING_CONTEXT +#define PROPERTYNAME_SUBMENU ADDONSMENUITEM_STRING_SUBMENU + +constexpr OUStringLiteral IMAGES_NODENAME = u"UserDefinedImages"; + +// The following order is mandatory. Please add properties at the end! +#define INDEX_URL 0 +#define INDEX_TITLE 1 +#define INDEX_IMAGEIDENTIFIER 2 +#define INDEX_TARGET 3 +#define INDEX_CONTEXT 4 +#define INDEX_SUBMENU 5 +#define INDEX_CONTROLTYPE 6 +#define INDEX_WIDTH 7 +#define INDEX_ALIGN 8 +#define INDEX_AUTOSIZE 9 +#define INDEX_OWNERDRAW 10 +#define INDEX_MANDATORY 11 +#define INDEX_STYLE 12 +#define PROPERTYCOUNT_INDEX 13 + +// The following order is mandatory. Please add properties at the end! +#define PROPERTYCOUNT_MENUITEM 6 +#define OFFSET_MENUITEM_URL 0 +#define OFFSET_MENUITEM_TITLE 1 +#define OFFSET_MENUITEM_IMAGEIDENTIFIER 2 +#define OFFSET_MENUITEM_TARGET 3 +#define OFFSET_MENUITEM_CONTEXT 4 +#define OFFSET_MENUITEM_SUBMENU 5 + +// The following order is mandatory. Please add properties at the end! +#define PROPERTYCOUNT_POPUPMENU 4 +#define OFFSET_POPUPMENU_TITLE 0 +#define OFFSET_POPUPMENU_CONTEXT 1 +#define OFFSET_POPUPMENU_SUBMENU 2 +#define OFFSET_POPUPMENU_URL 3 // Used for property set + +// The following order is mandatory. Please add properties at the end! +#define PROPERTYCOUNT_TOOLBARITEM 7 +#define OFFSET_TOOLBARITEM_URL 0 +#define OFFSET_TOOLBARITEM_TITLE 1 +#define OFFSET_TOOLBARITEM_IMAGEIDENTIFIER 2 +#define OFFSET_TOOLBARITEM_TARGET 3 +#define OFFSET_TOOLBARITEM_CONTEXT 4 +#define OFFSET_TOOLBARITEM_CONTROLTYPE 5 +#define OFFSET_TOOLBARITEM_WIDTH 6 + +// The following order is mandatory. Please add properties at the end! +#define PROPERTYCOUNT_NOTEBOOKBARITEM 8 +#define OFFSET_NOTEBOOKBARITEM_URL 0 +#define OFFSET_NOTEBOOKBARITEM_TITLE 1 +#define OFFSET_NOTEBOOKBARITEM_IMAGEIDENTIFIER 2 +#define OFFSET_NOTEBOOKBARITEM_TARGET 3 +#define OFFSET_NOTEBOOKBARITEM_CONTEXT 4 +#define OFFSET_NOTEBOOKBARITEM_CONTROLTYPE 5 +#define OFFSET_NOTEBOOKBARITEM_WIDTH 6 +#define OFFSET_NOTEBOOKBARITEM_STYLE 7 + +// The following order is mandatory. Please add properties at the end! +#define PROPERTYCOUNT_STATUSBARITEM 8 +#define OFFSET_STATUSBARITEM_URL 0 +#define OFFSET_STATUSBARITEM_TITLE 1 +#define OFFSET_STATUSBARITEM_CONTEXT 2 +#define OFFSET_STATUSBARITEM_ALIGN 3 +#define OFFSET_STATUSBARITEM_AUTOSIZE 4 +#define OFFSET_STATUSBARITEM_OWNERDRAW 5 +#define OFFSET_STATUSBARITEM_MANDATORY 6 +#define OFFSET_STATUSBARITEM_WIDTH 7 + +// The following order is mandatory. Please add properties at the end! +#define PROPERTYCOUNT_IMAGES 8 +#define PROPERTYCOUNT_EMBEDDED_IMAGES 2 +#define OFFSET_IMAGES_SMALL 0 +#define OFFSET_IMAGES_BIG 1 +#define OFFSET_IMAGES_SMALLHC 2 +#define OFFSET_IMAGES_BIGHC 3 +#define OFFSET_IMAGES_SMALL_URL 4 +#define OFFSET_IMAGES_BIG_URL 5 +#define OFFSET_IMAGES_SMALLHC_URL 6 +#define OFFSET_IMAGES_BIGHC_URL 7 + +#define PROPERTYCOUNT_MERGE_MENUBAR 6 +#define OFFSET_MERGEMENU_MERGEPOINT 0 +#define OFFSET_MERGEMENU_MERGECOMMAND 1 +#define OFFSET_MERGEMENU_MERGECOMMANDPARAMETER 2 +#define OFFSET_MERGEMENU_MERGEFALLBACK 3 +#define OFFSET_MERGEMENU_MERGECONTEXT 4 +#define OFFSET_MERGEMENU_MENUITEMS 5 + +#define PROPERTYCOUNT_MERGE_TOOLBAR 7 +#define OFFSET_MERGETOOLBAR_TOOLBAR 0 +#define OFFSET_MERGETOOLBAR_MERGEPOINT 1 +#define OFFSET_MERGETOOLBAR_MERGECOMMAND 2 +#define OFFSET_MERGETOOLBAR_MERGECOMMANDPARAMETER 3 +#define OFFSET_MERGETOOLBAR_MERGEFALLBACK 4 +#define OFFSET_MERGETOOLBAR_MERGECONTEXT 5 +#define OFFSET_MERGETOOLBAR_TOOLBARITEMS 6 + +#define PROPERTYCOUNT_MERGE_NOTEBOOKBAR 7 +#define OFFSET_MERGENOTEBOOKBAR_NOTEBOOKBAR 0 +#define OFFSET_MERGENOTEBOOKBAR_MERGEPOINT 1 +#define OFFSET_MERGENOTEBOOKBAR_MERGECOMMAND 2 +#define OFFSET_MERGENOTEBOOKBAR_MERGECOMMANDPARAMETER 3 +#define OFFSET_MERGENOTEBOOKBAR_MERGEFALLBACK 4 +#define OFFSET_MERGENOTEBOOKBAR_MERGECONTEXT 5 +#define OFFSET_MERGENOTEBOOKBAR_NOTEBOOKBARITEMS 6 + +#define PROPERTYCOUNT_MERGE_STATUSBAR 6 +#define OFFSET_MERGESTATUSBAR_MERGEPOINT 0 +#define OFFSET_MERGESTATUSBAR_MERGECOMMAND 1 +#define OFFSET_MERGESTATUSBAR_MERGECOMMANDPARAMETER 2 +#define OFFSET_MERGESTATUSBAR_MERGEFALLBACK 3 +#define OFFSET_MERGESTATUSBAR_MERGECONTEXT 4 +#define OFFSET_MERGESTATUSBAR_STATUSBARITEMS 5 + +// private declarations! + +/*-**************************************************************************************************************** + @descr struct to hold information about one menu entry. +****************************************************************************************************************-*/ + +namespace framework +{ + +class AddonsOptions_Impl : public ConfigItem +{ + + // public methods + + public: + + // constructor / destructor + + AddonsOptions_Impl(); + virtual ~AddonsOptions_Impl() override; + + // overridden methods of baseclass + + /*-**************************************************************************************************** + @short called for notify of configmanager + @descr This method is called from the ConfigManager before application ends or from the + PropertyChangeListener if the sub tree broadcasts changes. You must update your + internal values. + + @seealso baseclass ConfigItem + + @param "lPropertyNames" is the list of properties which should be updated. + *//*-*****************************************************************************************************/ + + virtual void Notify( const Sequence< OUString >& lPropertyNames ) override; + + // public interface + + /*-**************************************************************************************************** + @short base implementation of public interface for "SvtDynamicMenuOptions"! + @descr These class is used as static member of "SvtDynamicMenuOptions" ... + => The code exist only for one time and isn't duplicated for every instance! + *//*-*****************************************************************************************************/ + + bool HasAddonsMenu () const; + sal_Int32 GetAddonsToolBarCount() const; + sal_Int32 GetAddonsNotebookBarCount() const; + const Sequence< Sequence< PropertyValue > >& GetAddonsMenu () const { return m_aCachedMenuProperties;} + const Sequence< Sequence< PropertyValue > >& GetAddonsMenuBarPart () const { return m_aCachedMenuBarPartProperties;} + const Sequence< Sequence< PropertyValue > >& GetAddonsToolBarPart ( sal_uInt32 nIndex ) const; + const Sequence< Sequence< PropertyValue > >& GetAddonsNotebookBarPart ( sal_uInt32 nIndex ) const; + OUString GetAddonsToolbarResourceName( sal_uInt32 nIndex ) const; + OUString GetAddonsNotebookBarResourceName( sal_uInt32 nIndex ) const; + const Sequence< Sequence< PropertyValue > >& GetAddonsHelpMenu () const { return m_aCachedHelpMenuProperties;} + BitmapEx GetImageFromURL( const OUString& aURL, bool bBig, bool bNoScale ); + const MergeMenuInstructionContainer& GetMergeMenuInstructions() const { return m_aCachedMergeMenuInsContainer;} + bool GetMergeToolbarInstructions( const OUString& rToolbarName, MergeToolbarInstructionContainer& rToolbarInstructions ) const; + bool GetMergeNotebookBarInstructions( const OUString& rNotebookBarName, MergeNotebookBarInstructionContainer& rNotebookBarInstructions ) const; + const MergeStatusbarInstructionContainer& GetMergeStatusbarInstructions() const { return m_aCachedStatusbarMergingInstructions;} + void ReadConfigurationData(); + + private: + enum ImageSize + { + IMGSIZE_SMALL = 0, + IMGSIZE_BIG + }; + + struct OneImageEntry + { + BitmapEx aScaled; ///< cached scaled image + BitmapEx aImage; ///< original un-scaled image + OUString aURL; ///< URL in case it is not loaded yet + }; + + struct ImageEntry + { + // if the image is set, it was embedded in some way, + // otherwise we use the associated URL to load on demand + + // accessed in this order + OneImageEntry aSizeEntry[2]; + ImageEntry() {} + void addImage(ImageSize eSize, const BitmapEx &rImage); + void addImage(ImageSize eSize, const OUString &rURL); + }; + + typedef std::unordered_map< OUString, ImageEntry > ImageManager; + typedef std::unordered_map< OUString, sal_uInt32 > StringToIndexMap; + typedef std::vector< Sequence< Sequence< PropertyValue > > > AddonToolBars; + typedef std::vector< Sequence< Sequence< PropertyValue > > > AddonNotebookBars; + typedef std::unordered_map< OUString, MergeToolbarInstructionContainer > ToolbarMergingInstructions; + typedef std::unordered_map< OUString, MergeNotebookBarInstructionContainer > NotebookBarMergingInstructions; + + /*-**************************************************************************************************** + @short return list of key names of our configuration management which represent our module tree + @descr These methods return the current list of key names! We need it to get needed values from our + configuration management! + @param "nCount" , returns count of menu entries for "new" + @return A list of configuration key names is returned. + *//*-*****************************************************************************************************/ + + void ReadAddonMenuSet( Sequence< Sequence< PropertyValue > >& aAddonMenuSeq ); + void ReadOfficeMenuBarSet( Sequence< Sequence< PropertyValue > >& aAddonOfficeMenuBarSeq ); + void ReadOfficeToolBarSet( AddonToolBars& rAddonOfficeToolBars, std::vector< OUString >& rAddonOfficeToolBarResNames ); + bool ReadToolBarItemSet( const OUString& rToolBarItemSetNodeName, Sequence< Sequence< PropertyValue > >& aAddonOfficeToolBarSeq ); + void ReadOfficeNotebookBarSet( AddonNotebookBars& rAddonOfficeNotebookBars, std::vector< OUString >& rAddonOfficeNotebookBarResNames ); + bool ReadNotebookBarItemSet( const OUString& rNotebookBarItemSetNodeName, Sequence< Sequence< PropertyValue > >& aAddonOfficeNotebookBarSeq ); + + void ReadOfficeHelpSet( Sequence< Sequence< PropertyValue > >& aAddonOfficeHelpMenuSeq ); + void ReadImages( ImageManager& aImageManager ); + void ReadMenuMergeInstructions( MergeMenuInstructionContainer& rContainer ); + void ReadToolbarMergeInstructions( ToolbarMergingInstructions& rToolbarMergeMap ); + void ReadNotebookBarMergeInstructions( NotebookBarMergingInstructions& rNotebookBarMergeMap ); + void ReadStatusbarMergeInstructions( MergeStatusbarInstructionContainer& rContainer ); + + void ReadMergeMenuData( std::u16string_view aMergeAddonInstructionBase, Sequence< Sequence< PropertyValue > >& rMergeMenu ); + void ReadMergeToolbarData( std::u16string_view aMergeAddonInstructionBase, Sequence< Sequence< PropertyValue > >& rMergeToolbarItems ); + void ReadMergeNotebookBarData( std::u16string_view aMergeAddonInstructionBase, Sequence< Sequence< PropertyValue > >& rMergeNotebookBarItems ); + void ReadMergeStatusbarData( std::u16string_view aMergeAddonInstructionBase, Sequence< Sequence< PropertyValue > >& rMergeStatusbar ); + bool ReadMenuItem( std::u16string_view aMenuItemNodeName, Sequence< PropertyValue >& aMenuItem, bool bIgnoreSubMenu = false ); + bool ReadPopupMenu( std::u16string_view aPopupMenuNodeName, Sequence< PropertyValue >& aPopupMenu ); + void AppendPopupMenu( Sequence< PropertyValue >& aTargetPopupMenu, const Sequence< PropertyValue >& rSourcePopupMenu ); + bool ReadToolBarItem( std::u16string_view aToolBarItemNodeName, Sequence< PropertyValue >& aToolBarItem ); + bool ReadNotebookBarItem( std::u16string_view aNotebookBarItemNodeName, Sequence< PropertyValue >& aNotebookBarItem ); + + bool ReadStatusBarItem( std::u16string_view aStatusbarItemNodeName, Sequence< PropertyValue >& aStatusbarItem ); + std::unique_ptr ReadImageData( std::u16string_view aImagesNodeName ); + void ReadAndAssociateImages( const OUString& aURL, const OUString& aImageId ); + BitmapEx ReadImageFromURL( const OUString& aURL ); + bool HasAssociatedImages( const OUString& aURL ); + void SubstituteVariables( OUString& aURL ); + + void ReadSubMenuEntries( const Sequence< OUString >& aSubMenuNodeNames, Sequence< Sequence< PropertyValue > >& rSubMenu ); + OUString GeneratePrefixURL(); + + Sequence< OUString > GetPropertyNamesMenuItem( std::u16string_view aPropertyRootNode ) + const; + Sequence< OUString > GetPropertyNamesPopupMenu( std::u16string_view aPropertyRootNode ) + const; + Sequence< OUString > GetPropertyNamesToolBarItem( std::u16string_view aPropertyRootNode ) + const; + Sequence< OUString > GetPropertyNamesNotebookBarItem( std::u16string_view aPropertyRootNode ) const; + + Sequence< OUString > GetPropertyNamesStatusbarItem( std::u16string_view aPropertyRootNode ) const; + Sequence< OUString > GetPropertyNamesImages( std::u16string_view aPropertyRootNode ) const; + bool CreateImageFromSequence( BitmapEx& rImage, Sequence< sal_Int8 >& rBitmapDataSeq ) const; + + DECL_LINK(NotifyEvent, void*, void); + + virtual void ImplCommit() override; + + // private member + + private: + sal_Int32 m_nRootAddonPopupMenuId; + OUString m_aPropNames[PROPERTYCOUNT_INDEX]; + OUString m_aPropImagesNames[PROPERTYCOUNT_IMAGES]; + OUString m_aPropMergeMenuNames[PROPERTYCOUNT_MERGE_MENUBAR]; + OUString m_aPropMergeToolbarNames[PROPERTYCOUNT_MERGE_TOOLBAR]; + OUString m_aPropMergeNotebookBarNames[PROPERTYCOUNT_MERGE_NOTEBOOKBAR]; + OUString m_aPropMergeStatusbarNames[PROPERTYCOUNT_MERGE_STATUSBAR]; + OUString m_aPathDelimiter; + OUString m_aRootAddonPopupMenuURLPrexfix; + Sequence< Sequence< PropertyValue > > m_aCachedMenuProperties; + Sequence< Sequence< PropertyValue > > m_aCachedMenuBarPartProperties; + AddonToolBars m_aCachedToolBarPartProperties; + AddonNotebookBars m_aCachedNotebookBarPartProperties; + std::vector< OUString > m_aCachedToolBarPartResourceNames; + std::vector< OUString > m_aCachedNotebookBarPartResourceNames; + Sequence< Sequence< PropertyValue > > m_aCachedHelpMenuProperties; + ImageManager m_aImageManager; + Sequence< Sequence< PropertyValue > > m_aEmptyAddonToolBar; + Sequence< Sequence< PropertyValue > > m_aEmptyAddonNotebookBar; + MergeMenuInstructionContainer m_aCachedMergeMenuInsContainer; + ToolbarMergingInstructions m_aCachedToolbarMergingInstructions; + NotebookBarMergingInstructions m_aCachedNotebookBarMergingInstructions; + MergeStatusbarInstructionContainer m_aCachedStatusbarMergingInstructions; +}; + +void AddonsOptions_Impl::ImageEntry::addImage(ImageSize eSize, const BitmapEx& rImage) +{ + aSizeEntry[static_cast(eSize)].aImage = rImage; +} + +void AddonsOptions_Impl::ImageEntry::addImage(ImageSize eSize, const OUString &rURL) +{ + aSizeEntry[static_cast(eSize)].aURL = rURL; +} + +// constructor + +AddonsOptions_Impl::AddonsOptions_Impl() + // Init baseclasses first + : ConfigItem( ROOTNODE_ADDONMENU ), + m_nRootAddonPopupMenuId( 0 ), + m_aPathDelimiter( PATHDELIMITER ), + m_aRootAddonPopupMenuURLPrexfix( ADDONSPOPUPMENU_URL_PREFIX_STR ) +{ + // initialize array with fixed property names + m_aPropNames[ INDEX_URL ] = PROPERTYNAME_URL; + m_aPropNames[ INDEX_TITLE ] = PROPERTYNAME_TITLE; + m_aPropNames[ INDEX_TARGET ] = PROPERTYNAME_TARGET; + m_aPropNames[ INDEX_IMAGEIDENTIFIER ] = PROPERTYNAME_IMAGEIDENTIFIER; + m_aPropNames[ INDEX_CONTEXT ] = PROPERTYNAME_CONTEXT; + m_aPropNames[ INDEX_SUBMENU ] = PROPERTYNAME_SUBMENU; // Submenu set! + m_aPropNames[ INDEX_CONTROLTYPE ] = "ControlType"; + m_aPropNames[ INDEX_WIDTH ] = "Width"; + m_aPropNames[ INDEX_ALIGN ] = "Alignment"; + m_aPropNames[ INDEX_AUTOSIZE ] = "AutoSize"; + m_aPropNames[ INDEX_OWNERDRAW ] = "OwnerDraw"; + m_aPropNames[ INDEX_MANDATORY ] = "Mandatory"; + m_aPropNames[ INDEX_STYLE ] = "Style"; + + // initialize array with fixed images property names + m_aPropImagesNames[ OFFSET_IMAGES_SMALL ] = "ImageSmall"; + m_aPropImagesNames[ OFFSET_IMAGES_BIG ] = "ImageBig"; + m_aPropImagesNames[ OFFSET_IMAGES_SMALLHC ] = "ImageSmallHC"; + m_aPropImagesNames[ OFFSET_IMAGES_BIGHC ] = "ImageBigHC"; + m_aPropImagesNames[ OFFSET_IMAGES_SMALL_URL ] = "ImageSmallURL"; + m_aPropImagesNames[ OFFSET_IMAGES_BIG_URL ] = "ImageBigURL"; + m_aPropImagesNames[ OFFSET_IMAGES_SMALLHC_URL ] = "ImageSmallHCURL"; + m_aPropImagesNames[ OFFSET_IMAGES_BIGHC_URL ] = "ImageBigHCURL"; + + // initialize array with fixed merge menu property names + m_aPropMergeMenuNames[ OFFSET_MERGEMENU_MERGEPOINT ] = "MergePoint"; + m_aPropMergeMenuNames[ OFFSET_MERGEMENU_MERGECOMMAND ] = "MergeCommand"; + m_aPropMergeMenuNames[ OFFSET_MERGEMENU_MERGECOMMANDPARAMETER ] = "MergeCommandParameter"; + m_aPropMergeMenuNames[ OFFSET_MERGEMENU_MERGEFALLBACK ] = "MergeFallback"; + m_aPropMergeMenuNames[ OFFSET_MERGEMENU_MERGECONTEXT ] = "MergeContext"; + m_aPropMergeMenuNames[ OFFSET_MERGEMENU_MENUITEMS ] = "MenuItems"; + + m_aPropMergeToolbarNames[ OFFSET_MERGETOOLBAR_TOOLBAR ] = "MergeToolBar"; + m_aPropMergeToolbarNames[ OFFSET_MERGETOOLBAR_MERGEPOINT ] = "MergePoint"; + m_aPropMergeToolbarNames[ OFFSET_MERGETOOLBAR_MERGECOMMAND ] = "MergeCommand"; + m_aPropMergeToolbarNames[ OFFSET_MERGETOOLBAR_MERGECOMMANDPARAMETER ] = "MergeCommandParameter"; + m_aPropMergeToolbarNames[ OFFSET_MERGETOOLBAR_MERGEFALLBACK ] = "MergeFallback"; + m_aPropMergeToolbarNames[ OFFSET_MERGETOOLBAR_MERGECONTEXT ] = "MergeContext"; + m_aPropMergeToolbarNames[ OFFSET_MERGETOOLBAR_TOOLBARITEMS ] = "ToolBarItems"; + + m_aPropMergeNotebookBarNames[ OFFSET_MERGENOTEBOOKBAR_NOTEBOOKBAR ] = "MergeNotebookBar"; + m_aPropMergeNotebookBarNames[ OFFSET_MERGENOTEBOOKBAR_MERGEPOINT ] = "MergePoint"; + m_aPropMergeNotebookBarNames[ OFFSET_MERGENOTEBOOKBAR_MERGECOMMAND ] = "MergeCommand"; + m_aPropMergeNotebookBarNames[ OFFSET_MERGENOTEBOOKBAR_MERGECOMMANDPARAMETER ] = "MergeCommandParameter"; + m_aPropMergeNotebookBarNames[ OFFSET_MERGENOTEBOOKBAR_MERGEFALLBACK ] = "MergeFallback"; + m_aPropMergeNotebookBarNames[ OFFSET_MERGENOTEBOOKBAR_MERGECONTEXT ] = "MergeContext"; + m_aPropMergeNotebookBarNames[ OFFSET_MERGENOTEBOOKBAR_NOTEBOOKBARITEMS ] = "NotebookBarItems"; + + m_aPropMergeStatusbarNames[ OFFSET_MERGESTATUSBAR_MERGEPOINT ] = "MergePoint"; + m_aPropMergeStatusbarNames[ OFFSET_MERGESTATUSBAR_MERGECOMMAND ] = "MergeCommand"; + m_aPropMergeStatusbarNames[ OFFSET_MERGESTATUSBAR_MERGECOMMANDPARAMETER ] = "MergeCommandParameter"; + m_aPropMergeStatusbarNames[ OFFSET_MERGESTATUSBAR_MERGEFALLBACK ] = "MergeFallback"; + m_aPropMergeStatusbarNames[ OFFSET_MERGESTATUSBAR_MERGECONTEXT ] = "MergeContext"; + m_aPropMergeStatusbarNames[ OFFSET_MERGESTATUSBAR_STATUSBARITEMS ] = "StatusBarItems"; + + ReadConfigurationData(); + + // Enable notification mechanism of our baseclass. + // We need it to get information about changes outside these class on our used configuration keys! + Sequence aNotifySeq { "AddonUI" }; + EnableNotification( aNotifySeq ); +} + +// destructor + +AddonsOptions_Impl::~AddonsOptions_Impl() +{ + assert(!IsModified()); // should have been committed +} + +void AddonsOptions_Impl::ReadConfigurationData() +{ + // reset members to be read again from configuration + m_aCachedMenuProperties = Sequence< Sequence< PropertyValue > >(); + m_aCachedMenuBarPartProperties = Sequence< Sequence< PropertyValue > >(); + m_aCachedToolBarPartProperties = AddonToolBars(); + m_aCachedNotebookBarPartProperties = AddonNotebookBars(); + m_aCachedHelpMenuProperties = Sequence< Sequence< PropertyValue > >(); + m_aCachedToolBarPartResourceNames.clear(); + m_aCachedNotebookBarPartResourceNames.clear(); + m_aImageManager = ImageManager(); + + ReadAddonMenuSet( m_aCachedMenuProperties ); + ReadOfficeMenuBarSet( m_aCachedMenuBarPartProperties ); + ReadOfficeToolBarSet( m_aCachedToolBarPartProperties, m_aCachedToolBarPartResourceNames ); + ReadOfficeNotebookBarSet( m_aCachedNotebookBarPartProperties, m_aCachedNotebookBarPartResourceNames ); + + ReadOfficeHelpSet( m_aCachedHelpMenuProperties ); + ReadImages( m_aImageManager ); + + m_aCachedMergeMenuInsContainer.clear(); + m_aCachedToolbarMergingInstructions.clear(); + m_aCachedNotebookBarMergingInstructions.clear(); + m_aCachedStatusbarMergingInstructions.clear(); + + ReadMenuMergeInstructions( m_aCachedMergeMenuInsContainer ); + ReadToolbarMergeInstructions( m_aCachedToolbarMergingInstructions ); + ReadNotebookBarMergeInstructions( m_aCachedNotebookBarMergingInstructions ); + ReadStatusbarMergeInstructions( m_aCachedStatusbarMergingInstructions ); +} + +// public method + +void AddonsOptions_Impl::Notify( const Sequence< OUString >& /*lPropertyNames*/ ) +{ + Application::PostUserEvent(LINK(this, AddonsOptions_Impl, NotifyEvent)); +} + +// public method + +void AddonsOptions_Impl::ImplCommit() +{ + SAL_WARN("fwk", "AddonsOptions_Impl::ImplCommit(): Not implemented yet!"); +} + +// public method + +bool AddonsOptions_Impl::HasAddonsMenu() const +{ + return m_aCachedMenuProperties.hasElements(); +} + +// public method + +sal_Int32 AddonsOptions_Impl::GetAddonsToolBarCount() const +{ + return m_aCachedToolBarPartProperties.size(); +} + +// public method + +sal_Int32 AddonsOptions_Impl::GetAddonsNotebookBarCount() const +{ + return m_aCachedNotebookBarPartProperties.size(); +} + +// public method + +const Sequence< Sequence< PropertyValue > >& AddonsOptions_Impl::GetAddonsToolBarPart( sal_uInt32 nIndex ) const +{ + if ( /*nIndex >= 0 &&*/ nIndex < m_aCachedToolBarPartProperties.size() ) + return m_aCachedToolBarPartProperties[nIndex]; + else + return m_aEmptyAddonToolBar; +} + +// public method + +const Sequence< Sequence< PropertyValue > >& AddonsOptions_Impl::GetAddonsNotebookBarPart( sal_uInt32 nIndex ) const +{ + if ( /*nIndex >= 0 &&*/ nIndex < m_aCachedNotebookBarPartProperties.size() ) + return m_aCachedNotebookBarPartProperties[nIndex]; + else + return m_aEmptyAddonNotebookBar; +} + +// public method + +OUString AddonsOptions_Impl::GetAddonsToolbarResourceName( sal_uInt32 nIndex ) const +{ + if ( nIndex < m_aCachedToolBarPartResourceNames.size() ) + return m_aCachedToolBarPartResourceNames[nIndex]; + else + return OUString(); +} + +// public method + +OUString AddonsOptions_Impl::GetAddonsNotebookBarResourceName( sal_uInt32 nIndex ) const +{ + if ( nIndex < m_aCachedNotebookBarPartResourceNames.size() ) + return m_aCachedNotebookBarPartResourceNames[nIndex]; + else + return OUString(); +} + +// public method + +bool AddonsOptions_Impl::GetMergeToolbarInstructions( + const OUString& rToolbarName, + MergeToolbarInstructionContainer& rToolbarInstructions ) const +{ + ToolbarMergingInstructions::const_iterator pIter = m_aCachedToolbarMergingInstructions.find( rToolbarName ); + if ( pIter != m_aCachedToolbarMergingInstructions.end() ) + { + rToolbarInstructions = pIter->second; + return true; + } + else + return false; +} + +// public method + +bool AddonsOptions_Impl::GetMergeNotebookBarInstructions( + const OUString& rNotebookBarName, + MergeNotebookBarInstructionContainer& rNotebookBarInstructions ) const +{ + NotebookBarMergingInstructions::const_iterator pIter = m_aCachedNotebookBarMergingInstructions.find( rNotebookBarName ); + if ( pIter != m_aCachedNotebookBarMergingInstructions.end() ) + { + rNotebookBarInstructions = pIter->second; + return true; + } + else + return false; +} + +// public method + +static BitmapEx ScaleImage( const BitmapEx &rImage, bool bBig ) +{ + Size aSize = ToolBox::GetDefaultImageSize(bBig ? ToolBoxButtonSize::Large : ToolBoxButtonSize::Small); + BitmapEx aScaleBmp(rImage); + SAL_INFO("fwk", "Addons: expensive scale image from " + << aScaleBmp.GetSizePixel() << " to " << aSize); + aScaleBmp.Scale(aSize, BmpScaleFlag::BestQuality); + return aScaleBmp; +} + +BitmapEx AddonsOptions_Impl::GetImageFromURL( const OUString& aURL, bool bBig, bool bNoScale ) +{ + BitmapEx aImage; + + SAL_INFO("fwk", "Expensive: Addons GetImageFromURL " << aURL << + " big " << (bBig?"big":"little") << + " scale " << (bNoScale ? "noscale" : "scale")); + + ImageManager::iterator pIter = m_aImageManager.find(aURL); + if ( pIter != m_aImageManager.end() ) + { + ImageSize eSize = bBig ? IMGSIZE_BIG : IMGSIZE_SMALL; + int nIdx = static_cast(eSize); + int nOtherIdx = nIdx ? 0 : 1; + + OneImageEntry& rSizeEntry = pIter->second.aSizeEntry[nIdx]; + OneImageEntry& rOtherEntry = pIter->second.aSizeEntry[nOtherIdx]; + // actually read the image ... + if (rSizeEntry.aImage.IsEmpty()) + rSizeEntry.aImage = ReadImageFromURL(rSizeEntry.aURL); + + if (rSizeEntry.aImage.IsEmpty()) + { // try the other size and scale it + aImage = ScaleImage(ReadImageFromURL(rOtherEntry.aURL), bBig); + rSizeEntry.aImage = aImage; + if (rSizeEntry.aImage.IsEmpty()) + SAL_WARN("fwk", "failed to load addons image " << aURL); + } + + // FIXME: bNoScale is not terribly meaningful or useful + + if (aImage.IsEmpty() && bNoScale) + aImage = rSizeEntry.aImage; + + if (aImage.IsEmpty() && !rSizeEntry.aScaled.IsEmpty()) + aImage = rSizeEntry.aScaled; + + else // scale to the correct size for the theme / toolbox + { + aImage = rSizeEntry.aImage; + if (aImage.IsEmpty()) // use and scale the other if one size is missing + aImage = rOtherEntry.aImage; + + aImage = ScaleImage(aImage, bBig); + rSizeEntry.aScaled = aImage; // cache for next time + } + } + + return aImage; +} + +void AddonsOptions_Impl::ReadAddonMenuSet( Sequence< Sequence< PropertyValue > >& rAddonMenuSeq ) +{ + // Read the AddonMenu set and fill property sequences + OUString aAddonMenuNodeName( "AddonUI/AddonMenu" ); + Sequence< OUString > aAddonMenuNodeSeq = GetNodeNames( aAddonMenuNodeName ); + OUString aAddonMenuItemNode( aAddonMenuNodeName + m_aPathDelimiter ); + + sal_uInt32 nCount = aAddonMenuNodeSeq.getLength(); + sal_uInt32 nIndex = 0; + Sequence< PropertyValue > aMenuItem( PROPERTYCOUNT_MENUITEM ); + auto pMenuItem = aMenuItem.getArray(); + // Init the property value sequence + pMenuItem[ OFFSET_MENUITEM_URL ].Name = m_aPropNames[ INDEX_URL ]; + pMenuItem[ OFFSET_MENUITEM_TITLE ].Name = m_aPropNames[ INDEX_TITLE ]; + pMenuItem[ OFFSET_MENUITEM_TARGET ].Name = m_aPropNames[ INDEX_TARGET ]; + pMenuItem[ OFFSET_MENUITEM_IMAGEIDENTIFIER ].Name = m_aPropNames[ INDEX_IMAGEIDENTIFIER]; + pMenuItem[ OFFSET_MENUITEM_CONTEXT ].Name = m_aPropNames[ INDEX_CONTEXT ]; + pMenuItem[ OFFSET_MENUITEM_SUBMENU ].Name = m_aPropNames[ INDEX_SUBMENU ]; // Submenu set! + + for ( sal_uInt32 n = 0; n < nCount; n++ ) + { + OUString aRootMenuItemNode( aAddonMenuItemNode + aAddonMenuNodeSeq[n] ); + + // Read the MenuItem + if ( ReadMenuItem( aRootMenuItemNode, aMenuItem ) ) + { + // Successfully read a menu item, append to our list + sal_uInt32 nMenuItemCount = rAddonMenuSeq.getLength() + 1; + rAddonMenuSeq.realloc( nMenuItemCount ); + rAddonMenuSeq.getArray()[nIndex++] = aMenuItem; + } + } +} + +void AddonsOptions_Impl::ReadOfficeHelpSet( Sequence< Sequence< PropertyValue > >& rAddonOfficeHelpMenuSeq ) +{ + // Read the AddonMenu set and fill property sequences + OUString aAddonHelpMenuNodeName( "AddonUI/OfficeHelp" ); + Sequence< OUString > aAddonHelpMenuNodeSeq = GetNodeNames( aAddonHelpMenuNodeName ); + OUString aAddonHelpMenuItemNode( aAddonHelpMenuNodeName + m_aPathDelimiter ); + + sal_uInt32 nCount = aAddonHelpMenuNodeSeq.getLength(); + sal_uInt32 nIndex = 0; + Sequence< PropertyValue > aMenuItem( PROPERTYCOUNT_MENUITEM ); + auto pMenuItem = aMenuItem.getArray(); + // Init the property value sequence + pMenuItem[ OFFSET_MENUITEM_URL ].Name = m_aPropNames[ INDEX_URL ]; + pMenuItem[ OFFSET_MENUITEM_TITLE ].Name = m_aPropNames[ INDEX_TITLE ]; + pMenuItem[ OFFSET_MENUITEM_TARGET ].Name = m_aPropNames[ INDEX_TARGET ]; + pMenuItem[ OFFSET_MENUITEM_IMAGEIDENTIFIER ].Name = m_aPropNames[ INDEX_IMAGEIDENTIFIER]; + pMenuItem[ OFFSET_MENUITEM_CONTEXT ].Name = m_aPropNames[ INDEX_CONTEXT ]; + pMenuItem[ OFFSET_MENUITEM_SUBMENU ].Name = m_aPropNames[ INDEX_SUBMENU ]; // Submenu set! + + for ( sal_uInt32 n = 0; n < nCount; n++ ) + { + OUString aRootMenuItemNode( aAddonHelpMenuItemNode + aAddonHelpMenuNodeSeq[n] ); + + // Read the MenuItem + if ( ReadMenuItem( aRootMenuItemNode, aMenuItem, true ) ) + { + // Successfully read a menu item, append to our list + sal_uInt32 nMenuItemCount = rAddonOfficeHelpMenuSeq.getLength() + 1; + rAddonOfficeHelpMenuSeq.realloc( nMenuItemCount ); + rAddonOfficeHelpMenuSeq.getArray()[nIndex++] = aMenuItem; + } + } +} + +void AddonsOptions_Impl::ReadOfficeMenuBarSet( Sequence< Sequence< PropertyValue > >& rAddonOfficeMenuBarSeq ) +{ + // Read the OfficeMenuBar set and fill property sequences + OUString aAddonMenuBarNodeName( "AddonUI/OfficeMenuBar" ); + Sequence< OUString > aAddonMenuBarNodeSeq = GetNodeNames( aAddonMenuBarNodeName ); + OUString aAddonMenuBarNode( aAddonMenuBarNodeName + m_aPathDelimiter ); + + sal_uInt32 nCount = aAddonMenuBarNodeSeq.getLength(); + sal_uInt32 nIndex = 0; + Sequence< PropertyValue > aPopupMenu( PROPERTYCOUNT_POPUPMENU ); + auto pPopupMenu = aPopupMenu.getArray(); + // Init the property value sequence + pPopupMenu[ OFFSET_POPUPMENU_TITLE ].Name = m_aPropNames[ INDEX_TITLE ]; + pPopupMenu[ OFFSET_POPUPMENU_CONTEXT ].Name = m_aPropNames[ INDEX_CONTEXT]; + pPopupMenu[ OFFSET_POPUPMENU_SUBMENU ].Name = m_aPropNames[ INDEX_SUBMENU]; + pPopupMenu[ OFFSET_POPUPMENU_URL ].Name = m_aPropNames[ INDEX_URL ]; + + StringToIndexMap aTitleToIndexMap; + auto pAddonOfficeMenuBarSeq = rAddonOfficeMenuBarSeq.getArray(); + for ( sal_uInt32 n = 0; n < nCount; n++ ) + { + OUString aPopupMenuNode( aAddonMenuBarNode + aAddonMenuBarNodeSeq[n] ); + + // Read the MenuItem + if ( ReadPopupMenu( aPopupMenuNode, aPopupMenu ) ) + { + // Successfully read a popup menu, append to our list + OUString aPopupTitle; + if ( aPopupMenu[OFFSET_POPUPMENU_TITLE].Value >>= aPopupTitle ) + { + StringToIndexMap::const_iterator pIter = aTitleToIndexMap.find( aPopupTitle ); + if ( pIter != aTitleToIndexMap.end() ) + { + // title already there => concat both popup menus + Sequence< PropertyValue >& rOldPopupMenu = pAddonOfficeMenuBarSeq[pIter->second]; + AppendPopupMenu( rOldPopupMenu, aPopupMenu ); + } + else + { + // not found + sal_uInt32 nMenuItemCount = rAddonOfficeMenuBarSeq.getLength() + 1; + rAddonOfficeMenuBarSeq.realloc( nMenuItemCount ); + pAddonOfficeMenuBarSeq = rAddonOfficeMenuBarSeq.getArray(); + pAddonOfficeMenuBarSeq[nIndex] = aPopupMenu; + aTitleToIndexMap.emplace( aPopupTitle, nIndex ); + ++nIndex; + } + } + } + } +} + +void AddonsOptions_Impl::ReadOfficeToolBarSet( AddonToolBars& rAddonOfficeToolBars, std::vector< OUString >& rAddonOfficeToolBarResNames ) +{ + // Read the OfficeToolBar set and fill property sequences + OUString aAddonToolBarNodeName( "AddonUI/OfficeToolBar" ); + Sequence< OUString > aAddonToolBarNodeSeq = GetNodeNames( aAddonToolBarNodeName ); + OUString aAddonToolBarNode( aAddonToolBarNodeName + m_aPathDelimiter ); + + sal_uInt32 nCount = aAddonToolBarNodeSeq.getLength(); + + for ( sal_uInt32 n = 0; n < nCount; n++ ) + { + OUString aToolBarItemNode( aAddonToolBarNode + aAddonToolBarNodeSeq[n] ); + rAddonOfficeToolBarResNames.push_back( aAddonToolBarNodeSeq[n] ); + rAddonOfficeToolBars.push_back( m_aEmptyAddonToolBar ); + ReadToolBarItemSet( aToolBarItemNode, rAddonOfficeToolBars[n] ); + } +} + +bool AddonsOptions_Impl::ReadToolBarItemSet( const OUString& rToolBarItemSetNodeName, Sequence< Sequence< PropertyValue > >& rAddonOfficeToolBarSeq ) +{ + sal_uInt32 nToolBarItemCount = rAddonOfficeToolBarSeq.getLength(); + OUString aAddonToolBarItemSetNode( rToolBarItemSetNodeName + m_aPathDelimiter ); + Sequence< OUString > aAddonToolBarItemSetNodeSeq = GetNodeNames( rToolBarItemSetNodeName ); + Sequence< PropertyValue > aToolBarItem( PROPERTYCOUNT_TOOLBARITEM ); + auto pToolBarItem = aToolBarItem.getArray(); + // Init the property value sequence + pToolBarItem[ OFFSET_TOOLBARITEM_URL ].Name = m_aPropNames[ INDEX_URL ]; + pToolBarItem[ OFFSET_TOOLBARITEM_TITLE ].Name = m_aPropNames[ INDEX_TITLE ]; + pToolBarItem[ OFFSET_TOOLBARITEM_IMAGEIDENTIFIER ].Name = m_aPropNames[ INDEX_IMAGEIDENTIFIER]; + pToolBarItem[ OFFSET_TOOLBARITEM_TARGET ].Name = m_aPropNames[ INDEX_TARGET ]; + pToolBarItem[ OFFSET_TOOLBARITEM_CONTEXT ].Name = m_aPropNames[ INDEX_CONTEXT ]; + pToolBarItem[ OFFSET_TOOLBARITEM_CONTROLTYPE ].Name = m_aPropNames[ INDEX_CONTROLTYPE ]; + pToolBarItem[ OFFSET_TOOLBARITEM_WIDTH ].Name = m_aPropNames[ INDEX_WIDTH ]; + + sal_uInt32 nCount = aAddonToolBarItemSetNodeSeq.getLength(); + for ( sal_uInt32 n = 0; n < nCount; n++ ) + { + OUString aToolBarItemNode( aAddonToolBarItemSetNode + aAddonToolBarItemSetNodeSeq[n] ); + + // Read the ToolBarItem + if ( ReadToolBarItem( aToolBarItemNode, aToolBarItem ) ) + { + // Successfully read a toolbar item, append to our list + sal_uInt32 nAddonCount = rAddonOfficeToolBarSeq.getLength(); + rAddonOfficeToolBarSeq.realloc( nAddonCount+1 ); + rAddonOfficeToolBarSeq.getArray()[nAddonCount] = aToolBarItem; + } + } + + return ( o3tl::make_unsigned(rAddonOfficeToolBarSeq.getLength()) > nToolBarItemCount ); +} + +void AddonsOptions_Impl::ReadOfficeNotebookBarSet( + AddonNotebookBars& rAddonOfficeNotebookBars, + std::vector& rAddonOfficeNotebookBarResNames) +{ + // Read the OfficeToolBar set and fill property sequences + OUString aAddonNotebookBarNodeName("AddonUI/OfficeNotebookBar"); + Sequence aAddonNotebookBarNodeSeq = GetNodeNames(aAddonNotebookBarNodeName); + OUString aAddonNotebookBarNode(aAddonNotebookBarNodeName + m_aPathDelimiter); + + sal_uInt32 nCount = aAddonNotebookBarNodeSeq.getLength(); + + for (sal_uInt32 n = 0; n < nCount; n++) + { + OUString aNotebookBarItemNode(aAddonNotebookBarNode + aAddonNotebookBarNodeSeq[n]); + rAddonOfficeNotebookBarResNames.push_back(aAddonNotebookBarNodeSeq[n]); + rAddonOfficeNotebookBars.push_back(m_aEmptyAddonNotebookBar); + ReadNotebookBarItemSet(aNotebookBarItemNode, rAddonOfficeNotebookBars[n]); + } +} + +bool AddonsOptions_Impl::ReadNotebookBarItemSet( + const OUString& rNotebookBarItemSetNodeName, + Sequence>& rAddonOfficeNotebookBarSeq) +{ + sal_uInt32 nNotebookBarItemCount = rAddonOfficeNotebookBarSeq.getLength(); + OUString aAddonNotebookBarItemSetNode(rNotebookBarItemSetNodeName + m_aPathDelimiter); + Sequence aAddonNotebookBarItemSetNodeSeq = GetNodeNames(rNotebookBarItemSetNodeName); + Sequence aNotebookBarItem(PROPERTYCOUNT_NOTEBOOKBARITEM); + auto pNotebookBarItem = aNotebookBarItem.getArray(); + // Init the property value sequence + pNotebookBarItem[OFFSET_NOTEBOOKBARITEM_URL].Name = m_aPropNames[INDEX_URL]; + pNotebookBarItem[OFFSET_NOTEBOOKBARITEM_TITLE].Name = m_aPropNames[INDEX_TITLE]; + pNotebookBarItem[OFFSET_NOTEBOOKBARITEM_IMAGEIDENTIFIER].Name + = m_aPropNames[INDEX_IMAGEIDENTIFIER]; + pNotebookBarItem[OFFSET_NOTEBOOKBARITEM_TARGET].Name = m_aPropNames[INDEX_TARGET]; + pNotebookBarItem[OFFSET_NOTEBOOKBARITEM_CONTEXT].Name = m_aPropNames[INDEX_CONTEXT]; + pNotebookBarItem[OFFSET_NOTEBOOKBARITEM_CONTROLTYPE].Name = m_aPropNames[INDEX_CONTROLTYPE]; + pNotebookBarItem[OFFSET_NOTEBOOKBARITEM_WIDTH].Name = m_aPropNames[INDEX_WIDTH]; + pNotebookBarItem[OFFSET_NOTEBOOKBARITEM_STYLE].Name = m_aPropNames[INDEX_STYLE]; + + sal_uInt32 nCount = aAddonNotebookBarItemSetNodeSeq.getLength(); + for (sal_uInt32 n = 0; n < nCount; n++) + { + OUString aNotebookBarItemNode(aAddonNotebookBarItemSetNode + + aAddonNotebookBarItemSetNodeSeq[n]); + // Read the NotebookBarItem + if (ReadNotebookBarItem(aNotebookBarItemNode, aNotebookBarItem)) + { + // Successfully read a toolbar item, append to our list + sal_uInt32 nAddonCount = rAddonOfficeNotebookBarSeq.getLength(); + rAddonOfficeNotebookBarSeq.realloc(nAddonCount + 1); + rAddonOfficeNotebookBarSeq.getArray()[nAddonCount] = aNotebookBarItem; + } + } + + return (o3tl::make_unsigned(rAddonOfficeNotebookBarSeq.getLength()) + > nNotebookBarItemCount); +} + +void AddonsOptions_Impl::ReadImages( ImageManager& aImageManager ) +{ + // Read the user-defined Images set and fill image manager + OUString aAddonImagesNodeName( "AddonUI/Images" ); + Sequence< OUString > aAddonImagesNodeSeq = GetNodeNames( aAddonImagesNodeName ); + OUString aAddonImagesNode( aAddonImagesNodeName + m_aPathDelimiter ); + + sal_uInt32 nCount = aAddonImagesNodeSeq.getLength(); + + // Init the property value sequence + OUString aURL; + + for ( sal_uInt32 n = 0; n < nCount; n++ ) + { + OUString aImagesItemNode( aAddonImagesNode + aAddonImagesNodeSeq[n] ); + + // Create sequence for data access + Sequence< OUString > aAddonImageItemNodePropNames = { aImagesItemNode + + m_aPathDelimiter + + m_aPropNames[ OFFSET_MENUITEM_URL ] }; + + Sequence< Any > aAddonImageItemNodeValues = GetProperties( aAddonImageItemNodePropNames ); + + // An user-defined image entry must have a URL. As "ImageIdentifier" has a higher priority + // we also check if we already have an images association. + if (( aAddonImageItemNodeValues[0] >>= aURL ) && + !aURL.isEmpty() && + !HasAssociatedImages( aURL )) + { + OUString aImagesUserDefinedItemNode = aImagesItemNode + + m_aPathDelimiter + + IMAGES_NODENAME + + m_aPathDelimiter; + + // Read a user-defined images data + std::unique_ptr pImageEntry = ReadImageData( aImagesUserDefinedItemNode ); + if ( pImageEntry ) + { + // Successfully read a user-defined images item, put it into our image manager + aImageManager.emplace( aURL, std::move(*pImageEntry) ); + } + } + } +} + +OUString AddonsOptions_Impl::GeneratePrefixURL() +{ + // Create a unique prefixed Add-On popup menu URL so it can be identified later as a runtime popup menu. + return m_aRootAddonPopupMenuURLPrexfix + OUString::number( ++m_nRootAddonPopupMenuId ); +} + +void AddonsOptions_Impl::ReadMenuMergeInstructions( MergeMenuInstructionContainer& aContainer ) +{ + static constexpr OUString aMenuMergeRootName( u"AddonUI/OfficeMenuBarMerging/"_ustr ); + + Sequence< OUString > aAddonMergeNodesSeq = GetNodeNames( aMenuMergeRootName ); + + sal_uInt32 nCount = aAddonMergeNodesSeq.getLength(); + + // Init the property value sequence + Sequence< OUString > aNodePropNames( 5 ); + auto pNodePropNames = aNodePropNames.getArray(); + + for ( sal_uInt32 i = 0; i < nCount; i++ ) + { + OUString aMergeAddonInstructions( aMenuMergeRootName + aAddonMergeNodesSeq[i] ); + + Sequence< OUString > aAddonInstMergeNodesSeq = GetNodeNames( aMergeAddonInstructions ); + sal_uInt32 nCountAddons = aAddonInstMergeNodesSeq.getLength(); + + for ( sal_uInt32 j = 0; j < nCountAddons; j++ ) + { + OUString aMergeAddonInstructionBase = aMergeAddonInstructions + + m_aPathDelimiter + + aAddonInstMergeNodesSeq[j] + + m_aPathDelimiter; + + // Create sequence for data access + pNodePropNames[0] = aMergeAddonInstructionBase + + m_aPropMergeMenuNames[ OFFSET_MERGEMENU_MERGEPOINT ]; + + pNodePropNames[1] = aMergeAddonInstructionBase + + m_aPropMergeMenuNames[ OFFSET_MERGEMENU_MERGECOMMAND ]; + + pNodePropNames[2] = aMergeAddonInstructionBase + + m_aPropMergeMenuNames[ OFFSET_MERGEMENU_MERGECOMMANDPARAMETER ]; + + pNodePropNames[3] = aMergeAddonInstructionBase + + m_aPropMergeMenuNames[ OFFSET_MERGEMENU_MERGEFALLBACK ]; + + pNodePropNames[4] = aMergeAddonInstructionBase + + m_aPropMergeMenuNames[ OFFSET_MERGEMENU_MERGECONTEXT ]; + + Sequence< Any > aNodePropValues = GetProperties( aNodePropNames ); + + MergeMenuInstruction aMergeMenuInstruction; + aNodePropValues[0] >>= aMergeMenuInstruction.aMergePoint; + aNodePropValues[1] >>= aMergeMenuInstruction.aMergeCommand; + aNodePropValues[2] >>= aMergeMenuInstruction.aMergeCommandParameter; + aNodePropValues[3] >>= aMergeMenuInstruction.aMergeFallback; + aNodePropValues[4] >>= aMergeMenuInstruction.aMergeContext; + + ReadMergeMenuData( aMergeAddonInstructionBase, aMergeMenuInstruction.aMergeMenu ); + + aContainer.push_back( aMergeMenuInstruction ); + } + } +} + +void AddonsOptions_Impl::ReadMergeMenuData( std::u16string_view aMergeAddonInstructionBase, Sequence< Sequence< PropertyValue > >& rMergeMenu ) +{ + OUString aMergeMenuBaseNode( aMergeAddonInstructionBase+m_aPropMergeMenuNames[ OFFSET_MERGEMENU_MENUITEMS ] ); + + Sequence< OUString > aSubMenuNodeNames = GetNodeNames( aMergeMenuBaseNode ); + aMergeMenuBaseNode += m_aPathDelimiter; + + // extend the node names to have full path strings + for ( OUString& rName : asNonConstRange(aSubMenuNodeNames) ) + rName = aMergeMenuBaseNode + rName; + + ReadSubMenuEntries( aSubMenuNodeNames, rMergeMenu ); +} + +void AddonsOptions_Impl::ReadToolbarMergeInstructions( ToolbarMergingInstructions& rCachedToolbarMergingInstructions ) +{ + static constexpr OUString aToolbarMergeRootName( u"AddonUI/OfficeToolbarMerging/"_ustr ); + + Sequence< OUString > aAddonMergeNodesSeq = GetNodeNames( aToolbarMergeRootName ); + sal_uInt32 nCount = aAddonMergeNodesSeq.getLength(); + + // Init the property value sequence + Sequence< OUString > aNodePropNames( 6 ); + auto pNodePropNames = aNodePropNames.getArray(); + + for ( sal_uInt32 i = 0; i < nCount; i++ ) + { + OUString aMergeAddonInstructions( aToolbarMergeRootName + aAddonMergeNodesSeq[i] ); + + Sequence< OUString > aAddonInstMergeNodesSeq = GetNodeNames( aMergeAddonInstructions ); + sal_uInt32 nCountAddons = aAddonInstMergeNodesSeq.getLength(); + + for ( sal_uInt32 j = 0; j < nCountAddons; j++ ) + { + OUString aMergeAddonInstructionBase = aMergeAddonInstructions + + m_aPathDelimiter + + aAddonInstMergeNodesSeq[j] + + m_aPathDelimiter; + + // Create sequence for data access + pNodePropNames[0] = aMergeAddonInstructionBase + + m_aPropMergeToolbarNames[ OFFSET_MERGETOOLBAR_TOOLBAR ]; + + pNodePropNames[1] = aMergeAddonInstructionBase + + m_aPropMergeToolbarNames[ OFFSET_MERGETOOLBAR_MERGEPOINT ]; + + pNodePropNames[2] = aMergeAddonInstructionBase + + m_aPropMergeToolbarNames[ OFFSET_MERGETOOLBAR_MERGECOMMAND ]; + + pNodePropNames[3] = aMergeAddonInstructionBase + + m_aPropMergeToolbarNames[ OFFSET_MERGETOOLBAR_MERGECOMMANDPARAMETER ]; + + pNodePropNames[4] = aMergeAddonInstructionBase + + m_aPropMergeToolbarNames[ OFFSET_MERGETOOLBAR_MERGEFALLBACK ]; + + pNodePropNames[5] = aMergeAddonInstructionBase + + m_aPropMergeToolbarNames[ OFFSET_MERGETOOLBAR_MERGECONTEXT ]; + + Sequence< Any > aNodePropValues = GetProperties( aNodePropNames ); + + MergeToolbarInstruction aMergeToolbarInstruction; + aNodePropValues[0] >>= aMergeToolbarInstruction.aMergeToolbar; + aNodePropValues[1] >>= aMergeToolbarInstruction.aMergePoint; + aNodePropValues[2] >>= aMergeToolbarInstruction.aMergeCommand; + aNodePropValues[3] >>= aMergeToolbarInstruction.aMergeCommandParameter; + aNodePropValues[4] >>= aMergeToolbarInstruction.aMergeFallback; + aNodePropValues[5] >>= aMergeToolbarInstruction.aMergeContext; + + ReadMergeToolbarData( aMergeAddonInstructionBase, + aMergeToolbarInstruction.aMergeToolbarItems ); + + MergeToolbarInstructionContainer& rVector = rCachedToolbarMergingInstructions[ aMergeToolbarInstruction.aMergeToolbar ]; + rVector.push_back( aMergeToolbarInstruction ); + } + } +} + +void AddonsOptions_Impl::ReadMergeToolbarData( std::u16string_view aMergeAddonInstructionBase, Sequence< Sequence< PropertyValue > >& rMergeToolbarItems ) +{ + OUString aMergeToolbarBaseNode = aMergeAddonInstructionBase + + m_aPropMergeToolbarNames[ OFFSET_MERGETOOLBAR_TOOLBARITEMS ]; + + ReadToolBarItemSet( aMergeToolbarBaseNode, rMergeToolbarItems ); +} + +void AddonsOptions_Impl::ReadNotebookBarMergeInstructions( + NotebookBarMergingInstructions& rCachedNotebookBarMergingInstructions) +{ + static constexpr OUString aNotebookBarMergeRootName(u"AddonUI/OfficeNotebookBarMerging/"_ustr); + + Sequence aAddonMergeNodesSeq = GetNodeNames(aNotebookBarMergeRootName); + sal_uInt32 nCount = aAddonMergeNodesSeq.getLength(); + + // Init the property value sequence + Sequence aNodePropNames(6); + auto pNodePropNames = aNodePropNames.getArray(); + + for (sal_uInt32 i = 0; i < nCount; i++) + { + OUString aMergeAddonInstructions(aNotebookBarMergeRootName + aAddonMergeNodesSeq[i]); + + Sequence aAddonInstMergeNodesSeq = GetNodeNames(aMergeAddonInstructions); + sal_uInt32 nCountAddons = aAddonInstMergeNodesSeq.getLength(); + + for (sal_uInt32 j = 0; j < nCountAddons; j++) + { + OUString aMergeAddonInstructionBase = aMergeAddonInstructions + + m_aPathDelimiter + + aAddonInstMergeNodesSeq[j] + + m_aPathDelimiter; + + // Create sequence for data access + pNodePropNames[0] = aMergeAddonInstructionBase + + m_aPropMergeNotebookBarNames[OFFSET_MERGENOTEBOOKBAR_NOTEBOOKBAR]; + + pNodePropNames[1] = aMergeAddonInstructionBase + + m_aPropMergeNotebookBarNames[OFFSET_MERGENOTEBOOKBAR_MERGEPOINT]; + + pNodePropNames[2] = aMergeAddonInstructionBase + + m_aPropMergeNotebookBarNames[OFFSET_MERGENOTEBOOKBAR_MERGECOMMAND]; + + pNodePropNames[3] = aMergeAddonInstructionBase + + m_aPropMergeNotebookBarNames[OFFSET_MERGENOTEBOOKBAR_MERGECOMMANDPARAMETER]; + + pNodePropNames[4] = aMergeAddonInstructionBase + + m_aPropMergeNotebookBarNames[OFFSET_MERGENOTEBOOKBAR_MERGEFALLBACK]; + + pNodePropNames[5] = aMergeAddonInstructionBase + + m_aPropMergeNotebookBarNames[OFFSET_MERGENOTEBOOKBAR_MERGECONTEXT]; + + Sequence aNodePropValues = GetProperties(aNodePropNames); + + MergeNotebookBarInstruction aMergeNotebookBarInstruction; + aNodePropValues[0] >>= aMergeNotebookBarInstruction.aMergeNotebookBar; + aNodePropValues[1] >>= aMergeNotebookBarInstruction.aMergePoint; + aNodePropValues[2] >>= aMergeNotebookBarInstruction.aMergeCommand; + aNodePropValues[3] >>= aMergeNotebookBarInstruction.aMergeCommandParameter; + aNodePropValues[4] >>= aMergeNotebookBarInstruction.aMergeFallback; + aNodePropValues[5] >>= aMergeNotebookBarInstruction.aMergeContext; + + ReadMergeNotebookBarData(aMergeAddonInstructionBase, + aMergeNotebookBarInstruction.aMergeNotebookBarItems); + + MergeNotebookBarInstructionContainer& rVector + = rCachedNotebookBarMergingInstructions[aMergeNotebookBarInstruction + .aMergeNotebookBar]; + rVector.push_back(aMergeNotebookBarInstruction); + } + } +} + +void AddonsOptions_Impl::ReadMergeNotebookBarData( + std::u16string_view aMergeAddonInstructionBase, + Sequence>& rMergeNotebookBarItems) +{ + OUString aMergeNotebookBarBaseNode = aMergeAddonInstructionBase + + m_aPropMergeNotebookBarNames[OFFSET_MERGENOTEBOOKBAR_NOTEBOOKBARITEMS]; + + ReadNotebookBarItemSet(aMergeNotebookBarBaseNode, rMergeNotebookBarItems); +} + +void AddonsOptions_Impl::ReadStatusbarMergeInstructions( MergeStatusbarInstructionContainer& aContainer ) +{ + static constexpr OUString aStatusbarMergeRootName( u"AddonUI/OfficeStatusbarMerging/"_ustr ); + + Sequence< OUString > aAddonMergeNodesSeq = GetNodeNames( aStatusbarMergeRootName ); + sal_uInt32 nCount = aAddonMergeNodesSeq.getLength(); + + Sequence< OUString > aNodePropNames( 5 ); + auto pNodePropNames = aNodePropNames.getArray(); + + for ( sal_uInt32 i = 0; i < nCount; i++ ) + { + OUString aMergeAddonInstructions( aStatusbarMergeRootName + aAddonMergeNodesSeq[i] ); + + Sequence< OUString > aAddonInstMergeNodesSeq = GetNodeNames( aMergeAddonInstructions ); + sal_uInt32 nCountAddons = aAddonInstMergeNodesSeq.getLength(); + + for ( sal_uInt32 j = 0; j < nCountAddons; j++ ) + { + OUString aMergeAddonInstructionBase = aMergeAddonInstructions + + m_aPathDelimiter + + aAddonInstMergeNodesSeq[j] + + m_aPathDelimiter; + + // Create sequence for data access + pNodePropNames[0] = aMergeAddonInstructionBase + + m_aPropMergeMenuNames[ OFFSET_MERGESTATUSBAR_MERGEPOINT ]; + + pNodePropNames[1] = aMergeAddonInstructionBase + + m_aPropMergeMenuNames[ OFFSET_MERGESTATUSBAR_MERGECOMMAND ]; + + pNodePropNames[2] = aMergeAddonInstructionBase + + m_aPropMergeMenuNames[ OFFSET_MERGESTATUSBAR_MERGECOMMANDPARAMETER ]; + + pNodePropNames[3] = aMergeAddonInstructionBase + + m_aPropMergeMenuNames[ OFFSET_MERGESTATUSBAR_MERGEFALLBACK ]; + + pNodePropNames[4] = aMergeAddonInstructionBase + + m_aPropMergeMenuNames[ OFFSET_MERGESTATUSBAR_MERGECONTEXT ]; + + Sequence< Any > aNodePropValues = GetProperties( aNodePropNames ); + + MergeStatusbarInstruction aMergeStatusbarInstruction; + aNodePropValues[0] >>= aMergeStatusbarInstruction.aMergePoint; + aNodePropValues[1] >>= aMergeStatusbarInstruction.aMergeCommand; + aNodePropValues[2] >>= aMergeStatusbarInstruction.aMergeCommandParameter; + // aNodePropValues[3] >>= aMergeStatusbarInstruction.aMergeFallback; + aNodePropValues[4] >>= aMergeStatusbarInstruction.aMergeContext; + + ReadMergeStatusbarData( aMergeAddonInstructionBase, + aMergeStatusbarInstruction.aMergeStatusbarItems ); + + aContainer.push_back( aMergeStatusbarInstruction ); + } + } +} + +void AddonsOptions_Impl::ReadMergeStatusbarData( + std::u16string_view aMergeAddonInstructionBase, + Sequence< Sequence< PropertyValue > >& rMergeStatusbarItems ) +{ + OUString aMergeStatusbarBaseNode = aMergeAddonInstructionBase + + m_aPropMergeStatusbarNames[ OFFSET_MERGESTATUSBAR_STATUSBARITEMS ]; + + OUString aAddonStatusbarItemSetNode( aMergeStatusbarBaseNode + m_aPathDelimiter ); + Sequence< OUString > aAddonStatusbarItemSetNodeSeq = GetNodeNames( aMergeStatusbarBaseNode ); + + Sequence< PropertyValue > aStatusbarItem( PROPERTYCOUNT_STATUSBARITEM ); + auto pStatusbarItem = aStatusbarItem.getArray(); + pStatusbarItem[ OFFSET_STATUSBARITEM_URL ].Name = m_aPropNames[ INDEX_URL ]; + pStatusbarItem[ OFFSET_STATUSBARITEM_TITLE ].Name = m_aPropNames[ INDEX_TITLE ]; + pStatusbarItem[ OFFSET_STATUSBARITEM_CONTEXT ].Name = m_aPropNames[ INDEX_CONTEXT ]; + pStatusbarItem[ OFFSET_STATUSBARITEM_ALIGN ].Name = m_aPropNames[ INDEX_ALIGN ]; + pStatusbarItem[ OFFSET_STATUSBARITEM_AUTOSIZE ].Name = m_aPropNames[ INDEX_AUTOSIZE ]; + pStatusbarItem[ OFFSET_STATUSBARITEM_OWNERDRAW ].Name = m_aPropNames[ INDEX_OWNERDRAW ]; + pStatusbarItem[ OFFSET_STATUSBARITEM_MANDATORY ].Name = m_aPropNames[ INDEX_MANDATORY ]; + pStatusbarItem[ OFFSET_STATUSBARITEM_WIDTH ].Name = m_aPropNames[ INDEX_WIDTH ]; + + sal_uInt32 nCount = aAddonStatusbarItemSetNodeSeq.getLength(); + for ( sal_uInt32 n = 0; n < nCount; n++ ) + { + OUString aStatusbarItemNode( aAddonStatusbarItemSetNode + aAddonStatusbarItemSetNodeSeq[n] ); + + if ( ReadStatusBarItem( aStatusbarItemNode, aStatusbarItem ) ) + { + sal_uInt32 nAddonCount = rMergeStatusbarItems.getLength(); + rMergeStatusbarItems.realloc( nAddonCount+1 ); + rMergeStatusbarItems.getArray()[nAddonCount] = aStatusbarItem; + } + } +} + +bool AddonsOptions_Impl::ReadStatusBarItem( + std::u16string_view aStatusarItemNodeName, + Sequence< PropertyValue >& aStatusbarItem ) +{ + bool bResult( false ); + OUString aURL; + OUString aAddonStatusbarItemTreeNode( aStatusarItemNodeName + m_aPathDelimiter ); + + Sequence< Any > aStatusbarItemNodePropValues = GetProperties( GetPropertyNamesStatusbarItem( aAddonStatusbarItemTreeNode ) ); + + // Command URL is required + if (( aStatusbarItemNodePropValues[ OFFSET_STATUSBARITEM_URL ] >>= aURL ) && aURL.getLength() > 0 ) + { + auto pStatusbarItem = aStatusbarItem.getArray(); + pStatusbarItem[ OFFSET_STATUSBARITEM_URL ].Value <<= aURL; + pStatusbarItem[ OFFSET_STATUSBARITEM_TITLE ].Value = aStatusbarItemNodePropValues[ OFFSET_STATUSBARITEM_TITLE ]; + pStatusbarItem[ OFFSET_STATUSBARITEM_CONTEXT ].Value = aStatusbarItemNodePropValues[ OFFSET_STATUSBARITEM_CONTEXT ]; + pStatusbarItem[ OFFSET_STATUSBARITEM_ALIGN ].Value = aStatusbarItemNodePropValues[ OFFSET_STATUSBARITEM_ALIGN ]; + pStatusbarItem[ OFFSET_STATUSBARITEM_AUTOSIZE ].Value = aStatusbarItemNodePropValues[ OFFSET_STATUSBARITEM_AUTOSIZE ]; + pStatusbarItem[ OFFSET_STATUSBARITEM_OWNERDRAW ].Value = aStatusbarItemNodePropValues[ OFFSET_STATUSBARITEM_OWNERDRAW ]; + pStatusbarItem[ OFFSET_STATUSBARITEM_MANDATORY ].Value = aStatusbarItemNodePropValues[ OFFSET_STATUSBARITEM_MANDATORY ]; + + // Configuration uses hyper for long. Therefore transform into sal_Int32 + sal_Int64 nValue( 0 ); + aStatusbarItemNodePropValues[ OFFSET_STATUSBARITEM_WIDTH ] >>= nValue; + pStatusbarItem[ OFFSET_STATUSBARITEM_WIDTH ].Value <<= sal_Int32( nValue ); + + bResult = true; + } + + return bResult; +} + +bool AddonsOptions_Impl::ReadMenuItem( std::u16string_view aMenuNodeName, Sequence< PropertyValue >& aMenuItem, bool bIgnoreSubMenu ) +{ + bool bResult = false; + OUString aStrValue; + OUString aAddonMenuItemTreeNode( aMenuNodeName + m_aPathDelimiter ); + + Sequence< Any > aMenuItemNodePropValues = GetProperties( GetPropertyNamesMenuItem( aAddonMenuItemTreeNode ) ); + if (( aMenuItemNodePropValues[ OFFSET_MENUITEM_TITLE ] >>= aStrValue ) && !aStrValue.isEmpty() ) + { + auto pMenuItem = aMenuItem.getArray(); + pMenuItem[ OFFSET_MENUITEM_TITLE ].Value <<= aStrValue; + + OUString aRootSubMenuName( aAddonMenuItemTreeNode + m_aPropNames[ INDEX_SUBMENU ] ); + Sequence< OUString > aRootSubMenuNodeNames = GetNodeNames( aRootSubMenuName ); + if ( aRootSubMenuNodeNames.hasElements() && !bIgnoreSubMenu ) + { + // Set a unique prefixed Add-On popup menu URL so it can be identified later + OUString aPopupMenuURL = GeneratePrefixURL(); + OUString aPopupMenuImageId; + + aMenuItemNodePropValues[ OFFSET_MENUITEM_IMAGEIDENTIFIER ] >>= aPopupMenuImageId; + ReadAndAssociateImages( aPopupMenuURL, aPopupMenuImageId ); + + // A popup menu must have a title and can have a URL and ImageIdentifier + // Set the other property values to empty + pMenuItem[ OFFSET_MENUITEM_URL ].Value <<= aPopupMenuURL; + pMenuItem[ OFFSET_MENUITEM_TARGET ].Value <<= OUString(); + pMenuItem[ OFFSET_MENUITEM_IMAGEIDENTIFIER ].Value <<= aPopupMenuImageId; + pMenuItem[ OFFSET_MENUITEM_CONTEXT ].Value = aMenuItemNodePropValues[ OFFSET_MENUITEM_CONTEXT ]; + + // Continue to read the sub menu nodes + Sequence< Sequence< PropertyValue > > aSubMenuSeq; + OUString aSubMenuRootNodeName( aRootSubMenuName + m_aPathDelimiter ); + for ( OUString& rName : asNonConstRange(aRootSubMenuNodeNames) ) + rName = aSubMenuRootNodeName + rName; + ReadSubMenuEntries( aRootSubMenuNodeNames, aSubMenuSeq ); + pMenuItem[ OFFSET_MENUITEM_SUBMENU ].Value <<= aSubMenuSeq; + bResult = true; + } + else if (( aMenuItemNodePropValues[ OFFSET_MENUITEM_URL ] >>= aStrValue ) && !aStrValue.isEmpty() ) + { + // A simple menu item => read the other properties; + OUString aMenuImageId; + + aMenuItemNodePropValues[ OFFSET_MENUITEM_IMAGEIDENTIFIER ] >>= aMenuImageId; + ReadAndAssociateImages( aStrValue, aMenuImageId ); + + pMenuItem[ OFFSET_MENUITEM_URL ].Value <<= aStrValue; + pMenuItem[ OFFSET_MENUITEM_TARGET ].Value = aMenuItemNodePropValues[ OFFSET_MENUITEM_TARGET ]; + pMenuItem[ OFFSET_MENUITEM_IMAGEIDENTIFIER ].Value <<= aMenuImageId; + pMenuItem[ OFFSET_MENUITEM_CONTEXT ].Value = aMenuItemNodePropValues[ OFFSET_MENUITEM_CONTEXT ]; + pMenuItem[ OFFSET_MENUITEM_SUBMENU ].Value <<= Sequence< Sequence< PropertyValue > >(); // Submenu set! + + bResult = true; + } + } + else if (( aMenuItemNodePropValues[ OFFSET_MENUITEM_URL ] >>= aStrValue ) && + aStrValue == SEPARATOR_URL ) + { + auto pMenuItem = aMenuItem.getArray(); + + // Separator + pMenuItem[ OFFSET_MENUITEM_URL ].Value <<= aStrValue; + pMenuItem[ OFFSET_MENUITEM_TARGET ].Value <<= OUString(); + pMenuItem[ OFFSET_MENUITEM_IMAGEIDENTIFIER ].Value <<= OUString(); + pMenuItem[ OFFSET_MENUITEM_CONTEXT ].Value <<= OUString(); + pMenuItem[ OFFSET_MENUITEM_SUBMENU ].Value <<= Sequence< Sequence< PropertyValue > >(); // Submenu set! + bResult = true; + } + + return bResult; +} + +bool AddonsOptions_Impl::ReadPopupMenu( std::u16string_view aPopupMenuNodeName, Sequence< PropertyValue >& aPopupMenu ) +{ + bool bResult = false; + OUString aStrValue; + OUString aAddonPopupMenuTreeNode( aPopupMenuNodeName + m_aPathDelimiter ); + + Sequence< Any > aPopupMenuNodePropValues = GetProperties( GetPropertyNamesPopupMenu( aAddonPopupMenuTreeNode ) ); + if (( aPopupMenuNodePropValues[ OFFSET_POPUPMENU_TITLE ] >>= aStrValue ) && + !aStrValue.isEmpty() ) + { + auto pPopupMenu = aPopupMenu.getArray(); + pPopupMenu[ OFFSET_POPUPMENU_TITLE ].Value <<= aStrValue; + + OUString aRootSubMenuName( aAddonPopupMenuTreeNode + m_aPropNames[ INDEX_SUBMENU ] ); + Sequence< OUString > aRootSubMenuNodeNames = GetNodeNames( aRootSubMenuName ); + if ( aRootSubMenuNodeNames.hasElements() ) + { + // A top-level popup menu needs a title + // Set a unique prefixed Add-On popup menu URL so it can be identified later + OUString aPopupMenuURL = GeneratePrefixURL(); + + pPopupMenu[ OFFSET_POPUPMENU_URL ].Value <<= aPopupMenuURL; + pPopupMenu[ OFFSET_POPUPMENU_CONTEXT ].Value = aPopupMenuNodePropValues[ OFFSET_POPUPMENU_CONTEXT ]; + + // Continue to read the sub menu nodes + Sequence< Sequence< PropertyValue > > aSubMenuSeq; + OUString aSubMenuRootNodeName( aRootSubMenuName + m_aPathDelimiter ); + for ( OUString& rName : asNonConstRange(aRootSubMenuNodeNames) ) + rName = aSubMenuRootNodeName + rName; + ReadSubMenuEntries( aRootSubMenuNodeNames, aSubMenuSeq ); + pPopupMenu[ OFFSET_POPUPMENU_SUBMENU ].Value <<= aSubMenuSeq; + bResult = true; + } + } + + return bResult; +} + +void AddonsOptions_Impl::AppendPopupMenu( Sequence< PropertyValue >& rTargetPopupMenu, const Sequence< PropertyValue >& rSourcePopupMenu ) +{ + Sequence< Sequence< PropertyValue > > aTargetSubMenuSeq; + Sequence< Sequence< PropertyValue > > aSourceSubMenuSeq; + + if (( rTargetPopupMenu[ OFFSET_POPUPMENU_SUBMENU ].Value >>= aTargetSubMenuSeq ) && + ( rSourcePopupMenu[ OFFSET_POPUPMENU_SUBMENU ].Value >>= aSourceSubMenuSeq )) + { + sal_uInt32 nIndex = aTargetSubMenuSeq.getLength(); + aTargetSubMenuSeq.realloc( nIndex + aSourceSubMenuSeq.getLength() ); + auto pTargetSubMenuSeq = aTargetSubMenuSeq.getArray(); + for ( Sequence const & rSeq : std::as_const(aSourceSubMenuSeq) ) + pTargetSubMenuSeq[nIndex++] = rSeq; + rTargetPopupMenu.getArray()[ OFFSET_POPUPMENU_SUBMENU ].Value <<= aTargetSubMenuSeq; + } +} + +bool AddonsOptions_Impl::ReadToolBarItem( std::u16string_view aToolBarItemNodeName, Sequence< PropertyValue >& aToolBarItem ) +{ + bool bResult = false; + OUString aURL; + OUString aAddonToolBarItemTreeNode( aToolBarItemNodeName + m_aPathDelimiter ); + + Sequence< Any > aToolBarItemNodePropValues = GetProperties( GetPropertyNamesToolBarItem( aAddonToolBarItemTreeNode ) ); + + // A toolbar item must have a command URL + if (( aToolBarItemNodePropValues[ OFFSET_TOOLBARITEM_URL ] >>= aURL ) && !aURL.isEmpty() ) + { + OUString aTitle; + if ( aURL == SEPARATOR_URL ) + { + auto pToolBarItem = aToolBarItem.getArray(); + + // A separator toolbar item only needs a URL + pToolBarItem[ OFFSET_TOOLBARITEM_URL ].Value <<= aURL; + pToolBarItem[ OFFSET_TOOLBARITEM_TITLE ].Value <<= OUString(); + pToolBarItem[ OFFSET_TOOLBARITEM_TARGET ].Value <<= OUString(); + pToolBarItem[ OFFSET_TOOLBARITEM_IMAGEIDENTIFIER ].Value <<= OUString(); + pToolBarItem[ OFFSET_TOOLBARITEM_CONTEXT ].Value <<= OUString(); + pToolBarItem[ OFFSET_TOOLBARITEM_CONTROLTYPE ].Value <<= OUString(); + pToolBarItem[ OFFSET_TOOLBARITEM_WIDTH ].Value <<= sal_Int32( 0 ); + + bResult = true; + } + else if (( aToolBarItemNodePropValues[ OFFSET_TOOLBARITEM_TITLE ] >>= aTitle ) && !aTitle.isEmpty() ) + { + auto pToolBarItem = aToolBarItem.getArray(); + + // A normal toolbar item must also have title => read the other properties; + OUString aImageId; + + // Try to map a user-defined image URL to our internal private image URL + aToolBarItemNodePropValues[ OFFSET_TOOLBARITEM_IMAGEIDENTIFIER ] >>= aImageId; + ReadAndAssociateImages( aURL, aImageId ); + + pToolBarItem[ OFFSET_TOOLBARITEM_URL ].Value <<= aURL; + pToolBarItem[ OFFSET_TOOLBARITEM_TITLE ].Value <<= aTitle; + pToolBarItem[ OFFSET_TOOLBARITEM_TARGET ].Value = aToolBarItemNodePropValues[ OFFSET_TOOLBARITEM_TARGET ]; + pToolBarItem[ OFFSET_TOOLBARITEM_IMAGEIDENTIFIER ].Value <<= aImageId; + pToolBarItem[ OFFSET_TOOLBARITEM_CONTEXT ].Value = aToolBarItemNodePropValues[ OFFSET_TOOLBARITEM_CONTEXT ]; + pToolBarItem[ OFFSET_TOOLBARITEM_CONTROLTYPE ].Value = aToolBarItemNodePropValues[ OFFSET_TOOLBARITEM_CONTROLTYPE ]; + + // Configuration uses hyper for long. Therefore transform into sal_Int32 + sal_Int64 nValue( 0 ); + aToolBarItemNodePropValues[ OFFSET_TOOLBARITEM_WIDTH ] >>= nValue; + pToolBarItem[ OFFSET_TOOLBARITEM_WIDTH ].Value <<= sal_Int32( nValue ); + + bResult = true; + } + } + + return bResult; +} + +bool AddonsOptions_Impl::ReadNotebookBarItem( std::u16string_view aNotebookBarItemNodeName, Sequence< PropertyValue >& aNotebookBarItem ) +{ + bool bResult = false; + OUString aURL; + OUString aAddonNotebookBarItemTreeNode( aNotebookBarItemNodeName + m_aPathDelimiter ); + + Sequence< Any > aNotebookBarItemNodePropValues = GetProperties( GetPropertyNamesNotebookBarItem( aAddonNotebookBarItemTreeNode ) ); + + // A toolbar item must have a command URL + if (( aNotebookBarItemNodePropValues[ OFFSET_NOTEBOOKBARITEM_URL ] >>= aURL ) && !aURL.isEmpty() ) + { + OUString aTitle; + if ( aURL == SEPARATOR_URL ) + { + auto pNotebookBarItem = aNotebookBarItem.getArray(); + + // A separator toolbar item only needs a URL + pNotebookBarItem[ OFFSET_NOTEBOOKBARITEM_URL ].Value <<= aURL; + pNotebookBarItem[ OFFSET_NOTEBOOKBARITEM_TITLE ].Value <<= OUString(); + pNotebookBarItem[ OFFSET_NOTEBOOKBARITEM_TARGET ].Value <<= OUString(); + pNotebookBarItem[ OFFSET_NOTEBOOKBARITEM_IMAGEIDENTIFIER ].Value <<= OUString(); + pNotebookBarItem[ OFFSET_NOTEBOOKBARITEM_CONTEXT ].Value <<= OUString(); + pNotebookBarItem[ OFFSET_NOTEBOOKBARITEM_CONTROLTYPE ].Value <<= OUString(); + pNotebookBarItem[ OFFSET_NOTEBOOKBARITEM_WIDTH ].Value <<= sal_Int32( 0 ); + pNotebookBarItem[ OFFSET_NOTEBOOKBARITEM_STYLE ].Value <<= OUString(); + + bResult = true; + } + else if (( aNotebookBarItemNodePropValues[ OFFSET_NOTEBOOKBARITEM_TITLE ] >>= aTitle ) && !aTitle.isEmpty() ) + { + auto pNotebookBarItem = aNotebookBarItem.getArray(); + + // A normal toolbar item must also have title => read the other properties; + OUString aImageId; + + // Try to map a user-defined image URL to our internal private image URL + aNotebookBarItemNodePropValues[ OFFSET_NOTEBOOKBARITEM_IMAGEIDENTIFIER ] >>= aImageId; + ReadAndAssociateImages( aURL, aImageId ); + + pNotebookBarItem[ OFFSET_NOTEBOOKBARITEM_URL ].Value <<= aURL; + pNotebookBarItem[ OFFSET_NOTEBOOKBARITEM_TITLE ].Value <<= aTitle; + pNotebookBarItem[ OFFSET_NOTEBOOKBARITEM_TARGET ].Value = aNotebookBarItemNodePropValues[ OFFSET_NOTEBOOKBARITEM_TARGET ]; + pNotebookBarItem[ OFFSET_NOTEBOOKBARITEM_IMAGEIDENTIFIER ].Value <<= aImageId; + pNotebookBarItem[ OFFSET_NOTEBOOKBARITEM_CONTEXT ].Value = aNotebookBarItemNodePropValues[ OFFSET_NOTEBOOKBARITEM_CONTEXT ]; + pNotebookBarItem[ OFFSET_NOTEBOOKBARITEM_CONTROLTYPE ].Value = aNotebookBarItemNodePropValues[ OFFSET_NOTEBOOKBARITEM_CONTROLTYPE ]; + + // Configuration uses hyper for long. Therefore transform into sal_Int32 + sal_Int64 nValue( 0 ); + aNotebookBarItemNodePropValues[ OFFSET_NOTEBOOKBARITEM_WIDTH ] >>= nValue; + pNotebookBarItem[ OFFSET_NOTEBOOKBARITEM_WIDTH ].Value <<= sal_Int32( nValue ); + pNotebookBarItem[ OFFSET_NOTEBOOKBARITEM_STYLE ].Value = aNotebookBarItemNodePropValues[ OFFSET_NOTEBOOKBARITEM_STYLE ]; + + bResult = true; + } + } + + return bResult; +} + +void AddonsOptions_Impl::ReadSubMenuEntries( const Sequence< OUString >& aSubMenuNodeNames, Sequence< Sequence< PropertyValue > >& rSubMenuSeq ) +{ + Sequence< PropertyValue > aMenuItem( PROPERTYCOUNT_MENUITEM ); + auto pMenuItem = aMenuItem.getArray(); + + // Init the property value sequence + pMenuItem[ OFFSET_MENUITEM_URL ].Name = PROPERTYNAME_URL; + pMenuItem[ OFFSET_MENUITEM_TITLE ].Name = PROPERTYNAME_TITLE; + pMenuItem[ OFFSET_MENUITEM_TARGET ].Name = PROPERTYNAME_TARGET; + pMenuItem[ OFFSET_MENUITEM_IMAGEIDENTIFIER ].Name = PROPERTYNAME_IMAGEIDENTIFIER; + pMenuItem[ OFFSET_MENUITEM_CONTEXT ].Name = PROPERTYNAME_CONTEXT; + pMenuItem[ OFFSET_MENUITEM_SUBMENU ].Name = PROPERTYNAME_SUBMENU; // Submenu set! + + sal_uInt32 nIndex = 0; + sal_uInt32 nCount = aSubMenuNodeNames.getLength(); + for ( sal_uInt32 n = 0; n < nCount; n++ ) + { + if ( ReadMenuItem( aSubMenuNodeNames[n], aMenuItem )) + { + sal_uInt32 nSubMenuCount = rSubMenuSeq.getLength() + 1; + rSubMenuSeq.realloc( nSubMenuCount ); + rSubMenuSeq.getArray()[nIndex++] = aMenuItem; + } + } +} + +bool AddonsOptions_Impl::HasAssociatedImages( const OUString& aURL ) +{ + // FIXME: potentially this is not so useful in a world of delayed image loading + ImageManager::const_iterator pIter = m_aImageManager.find( aURL ); + return ( pIter != m_aImageManager.end() ); +} + +void AddonsOptions_Impl::SubstituteVariables( OUString& aURL ) +{ + aURL = comphelper::getExpandedUri( + comphelper::getProcessComponentContext(), aURL); +} + +BitmapEx AddonsOptions_Impl::ReadImageFromURL(const OUString& aImageURL) +{ + BitmapEx aImage; + + std::unique_ptr pStream = UcbStreamHelper::CreateStream( aImageURL, StreamMode::STD_READ ); + if ( pStream && ( pStream->GetErrorCode() == ERRCODE_NONE )) + { + // Use graphic class to also support more graphic formats (bmp,png,...) + Graphic aGraphic; + + GraphicFilter& rGF = GraphicFilter::GetGraphicFilter(); + rGF.ImportGraphic( aGraphic, u"", *pStream ); + + BitmapEx aBitmapEx = aGraphic.GetBitmapEx(); + + Size aBmpSize = aBitmapEx.GetSizePixel(); + if ( !aBmpSize.IsEmpty() ) + { + // Support non-transparent bitmaps to be downward compatible with OOo 1.1.x addons + if( !aBitmapEx.IsAlpha() ) + aBitmapEx = BitmapEx( aBitmapEx.GetBitmap(), COL_LIGHTMAGENTA ); + + aImage = aBitmapEx; + } + } + + return aImage; +} + +void AddonsOptions_Impl::ReadAndAssociateImages( const OUString& aURL, const OUString& aImageId ) +{ + if ( aImageId.isEmpty() ) + return; + + ImageEntry aImageEntry; + OUString aImageURL( aImageId ); + + SubstituteVariables( aImageURL ); + + // Loop to create the two possible image names and try to read the bitmap files + static const char* aExtArray[] = { "_16", "_26" }; + for ( size_t i = 0; i < std::size(aExtArray); i++ ) + { + OUStringBuffer aFileURL( aImageURL ); + aFileURL.appendAscii( aExtArray[i] ); + aFileURL.append( ".bmp" ); + + aImageEntry.addImage( !i ? IMGSIZE_SMALL : IMGSIZE_BIG, aFileURL.makeStringAndClear() ); + } + + m_aImageManager.emplace( aURL, aImageEntry ); +} + +std::unique_ptr AddonsOptions_Impl::ReadImageData( std::u16string_view aImagesNodeName ) +{ + Sequence< OUString > aImageDataNodeNames = GetPropertyNamesImages( aImagesNodeName ); + Sequence< Any > aPropertyData; + Sequence< sal_Int8 > aImageDataSeq; + OUString aImageURL; + + std::unique_ptr pEntry; + + // It is possible to use both forms (embedded image data and URLs to external bitmap files) at the + // same time. Embedded image data has a higher priority. + aPropertyData = GetProperties( aImageDataNodeNames ); + for ( int i = 0; i < PROPERTYCOUNT_IMAGES; i++ ) + { + if ( i < PROPERTYCOUNT_EMBEDDED_IMAGES ) + { + // Extract image data from the embedded hex binary sequence + BitmapEx aImage; + if (( aPropertyData[i] >>= aImageDataSeq ) && + aImageDataSeq.hasElements() && + ( CreateImageFromSequence( aImage, aImageDataSeq ) ) ) + { + if ( !pEntry ) + pEntry.reset(new ImageEntry); + pEntry->addImage(i == OFFSET_IMAGES_SMALL ? IMGSIZE_SMALL : IMGSIZE_BIG, aImage); + } + } + else if ( i == OFFSET_IMAGES_SMALL_URL || i == OFFSET_IMAGES_BIG_URL ) + { + if(!pEntry) + pEntry.reset(new ImageEntry()); + + // Retrieve image data from an external bitmap file. Make sure that embedded image data + // has a higher priority. + if (aPropertyData[i] >>= aImageURL) + { + SubstituteVariables(aImageURL); + pEntry->addImage(i == OFFSET_IMAGES_SMALL_URL ? IMGSIZE_SMALL : IMGSIZE_BIG, aImageURL); + } + } + } + + return pEntry; +} + +bool AddonsOptions_Impl::CreateImageFromSequence( BitmapEx& rImage, Sequence< sal_Int8 >& rBitmapDataSeq ) const +{ + bool bResult = false; + + if ( rBitmapDataSeq.hasElements() ) + { + SvMemoryStream aMemStream( rBitmapDataSeq.getArray(), rBitmapDataSeq.getLength(), StreamMode::STD_READ ); + + ReadDIBBitmapEx(rImage, aMemStream); + + if( !rImage.IsAlpha() ) + { + // Support non-transparent bitmaps to be downward compatible with OOo 1.1.x addons + rImage = BitmapEx( rImage.GetBitmap(), COL_LIGHTMAGENTA ); + } + + bResult = true; + } + + return bResult; +} + +Sequence< OUString > AddonsOptions_Impl::GetPropertyNamesMenuItem( std::u16string_view aPropertyRootNode ) const +{ + Sequence< OUString > lResult( PROPERTYCOUNT_MENUITEM ); + auto plResult = lResult.getArray(); + + // Create property names dependent from the root node name + plResult[OFFSET_MENUITEM_URL] = aPropertyRootNode + m_aPropNames[ INDEX_URL ]; + plResult[OFFSET_MENUITEM_TITLE] = aPropertyRootNode + m_aPropNames[ INDEX_TITLE ]; + plResult[OFFSET_MENUITEM_IMAGEIDENTIFIER] = aPropertyRootNode + m_aPropNames[ INDEX_IMAGEIDENTIFIER ]; + plResult[OFFSET_MENUITEM_TARGET] = aPropertyRootNode + m_aPropNames[ INDEX_TARGET ]; + plResult[OFFSET_MENUITEM_CONTEXT] = aPropertyRootNode + m_aPropNames[ INDEX_CONTEXT ]; + plResult[OFFSET_MENUITEM_SUBMENU] = aPropertyRootNode + m_aPropNames[ INDEX_SUBMENU ]; + + return lResult; +} + +Sequence< OUString > AddonsOptions_Impl::GetPropertyNamesPopupMenu( std::u16string_view aPropertyRootNode ) const +{ + // The URL is automatically set and not read from the configuration. + Sequence< OUString > lResult( PROPERTYCOUNT_POPUPMENU-1 ); + auto plResult = lResult.getArray(); + + // Create property names dependent from the root node name + plResult[OFFSET_POPUPMENU_TITLE] = aPropertyRootNode + m_aPropNames[ INDEX_TITLE ]; + plResult[OFFSET_POPUPMENU_CONTEXT] = aPropertyRootNode + m_aPropNames[ INDEX_CONTEXT ]; + plResult[OFFSET_POPUPMENU_SUBMENU] = aPropertyRootNode + m_aPropNames[ INDEX_SUBMENU ]; + + return lResult; +} + +Sequence< OUString > AddonsOptions_Impl::GetPropertyNamesToolBarItem( std::u16string_view aPropertyRootNode ) const +{ + Sequence< OUString > lResult( PROPERTYCOUNT_TOOLBARITEM ); + auto plResult = lResult.getArray(); + + // Create property names dependent from the root node name + plResult[0] = aPropertyRootNode + m_aPropNames[ INDEX_URL ]; + plResult[1] = aPropertyRootNode + m_aPropNames[ INDEX_TITLE ]; + plResult[2] = aPropertyRootNode + m_aPropNames[ INDEX_IMAGEIDENTIFIER]; + plResult[3] = aPropertyRootNode + m_aPropNames[ INDEX_TARGET ]; + plResult[4] = aPropertyRootNode + m_aPropNames[ INDEX_CONTEXT ]; + plResult[5] = aPropertyRootNode + m_aPropNames[ INDEX_CONTROLTYPE ]; + plResult[6] = aPropertyRootNode + m_aPropNames[ INDEX_WIDTH ]; + + return lResult; +} + +Sequence< OUString > AddonsOptions_Impl::GetPropertyNamesNotebookBarItem( std::u16string_view aPropertyRootNode ) const +{ + Sequence< OUString > lResult( PROPERTYCOUNT_NOTEBOOKBARITEM ); + auto plResult = lResult.getArray(); + + // Create property names dependent from the root node name + plResult[0] = aPropertyRootNode + m_aPropNames[ INDEX_URL ]; + plResult[1] = aPropertyRootNode + m_aPropNames[ INDEX_TITLE ]; + plResult[2] = aPropertyRootNode + m_aPropNames[ INDEX_IMAGEIDENTIFIER]; + plResult[3] = aPropertyRootNode + m_aPropNames[ INDEX_TARGET ]; + plResult[4] = aPropertyRootNode + m_aPropNames[ INDEX_CONTEXT ]; + plResult[5] = aPropertyRootNode + m_aPropNames[ INDEX_CONTROLTYPE ]; + plResult[6] = aPropertyRootNode + m_aPropNames[ INDEX_WIDTH ]; + plResult[7] = aPropertyRootNode + m_aPropNames[ INDEX_STYLE ]; + + return lResult; +} + +Sequence< OUString > AddonsOptions_Impl::GetPropertyNamesStatusbarItem( + std::u16string_view aPropertyRootNode ) const +{ + Sequence< OUString > lResult( PROPERTYCOUNT_STATUSBARITEM ); + auto plResult = lResult.getArray(); + + plResult[0] = OUString( aPropertyRootNode + m_aPropNames[ INDEX_URL ] ); + plResult[1] = OUString( aPropertyRootNode + m_aPropNames[ INDEX_TITLE ] ); + plResult[2] = OUString( aPropertyRootNode + m_aPropNames[ INDEX_CONTEXT ] ); + plResult[3] = OUString( aPropertyRootNode + m_aPropNames[ INDEX_ALIGN ] ); + plResult[4] = OUString( aPropertyRootNode + m_aPropNames[ INDEX_AUTOSIZE ] ); + plResult[5] = OUString( aPropertyRootNode + m_aPropNames[ INDEX_OWNERDRAW ] ); + plResult[6] = OUString( aPropertyRootNode + m_aPropNames[ INDEX_MANDATORY ] ); + plResult[7] = OUString( aPropertyRootNode + m_aPropNames[ INDEX_WIDTH ] ); + + return lResult; +} + +Sequence< OUString > AddonsOptions_Impl::GetPropertyNamesImages( std::u16string_view aPropertyRootNode ) const +{ + Sequence< OUString > lResult( PROPERTYCOUNT_IMAGES ); + auto plResult = lResult.getArray(); + + // Create property names dependent from the root node name + plResult[0] = aPropertyRootNode + m_aPropImagesNames[ OFFSET_IMAGES_SMALL ]; + plResult[1] = aPropertyRootNode + m_aPropImagesNames[ OFFSET_IMAGES_BIG ]; + plResult[2] = aPropertyRootNode + m_aPropImagesNames[ OFFSET_IMAGES_SMALLHC ]; + plResult[3] = aPropertyRootNode + m_aPropImagesNames[ OFFSET_IMAGES_BIGHC ]; + plResult[4] = aPropertyRootNode + m_aPropImagesNames[ OFFSET_IMAGES_SMALL_URL ]; + plResult[5] = aPropertyRootNode + m_aPropImagesNames[ OFFSET_IMAGES_BIG_URL ]; + plResult[6] = aPropertyRootNode + m_aPropImagesNames[ OFFSET_IMAGES_SMALLHC_URL]; + plResult[7] = aPropertyRootNode + m_aPropImagesNames[ OFFSET_IMAGES_BIGHC_URL ]; + + return lResult; +} + +namespace{ + //global + std::weak_ptr g_pAddonsOptions; +} + +AddonsOptions::AddonsOptions() +{ + // Global access, must be guarded (multithreading!). + MutexGuard aGuard( GetOwnStaticMutex() ); + + m_pImpl = g_pAddonsOptions.lock(); + if( !m_pImpl ) + { + m_pImpl = std::make_shared(); + g_pAddonsOptions = m_pImpl; + } +} + +AddonsOptions::~AddonsOptions() +{ + // Global access, must be guarded (multithreading!) + MutexGuard aGuard( GetOwnStaticMutex() ); + + m_pImpl.reset(); +} + +// public method + +bool AddonsOptions::HasAddonsMenu() const +{ + MutexGuard aGuard( GetOwnStaticMutex() ); + return m_pImpl->HasAddonsMenu(); +} + +// public method + +sal_Int32 AddonsOptions::GetAddonsToolBarCount() const +{ + MutexGuard aGuard( GetOwnStaticMutex() ); + return m_pImpl->GetAddonsToolBarCount(); +} + +// public method + +sal_Int32 AddonsOptions::GetAddonsNotebookBarCount() const +{ + MutexGuard aGuard( GetOwnStaticMutex() ); + return m_pImpl->GetAddonsNotebookBarCount(); +} + +// public method + +const Sequence< Sequence< PropertyValue > >& AddonsOptions::GetAddonsMenu() const +{ + MutexGuard aGuard( GetOwnStaticMutex() ); + return m_pImpl->GetAddonsMenu(); +} + +// public method + +const Sequence< Sequence< PropertyValue > >& AddonsOptions::GetAddonsMenuBarPart() const +{ + MutexGuard aGuard( GetOwnStaticMutex() ); + return m_pImpl->GetAddonsMenuBarPart(); +} + +// public method + +const Sequence< Sequence< PropertyValue > >& AddonsOptions::GetAddonsToolBarPart( sal_uInt32 nIndex ) const +{ + MutexGuard aGuard( GetOwnStaticMutex() ); + return m_pImpl->GetAddonsToolBarPart( nIndex ); +} + +// public method + +const Sequence< Sequence< PropertyValue > >& AddonsOptions::GetAddonsNotebookBarPart( sal_uInt32 nIndex ) const +{ + MutexGuard aGuard( GetOwnStaticMutex() ); + return m_pImpl->GetAddonsNotebookBarPart( nIndex ); +} + +// public method + +OUString AddonsOptions::GetAddonsToolbarResourceName( sal_uInt32 nIndex ) const +{ + MutexGuard aGuard( GetOwnStaticMutex() ); + return m_pImpl->GetAddonsToolbarResourceName( nIndex ); +} + +// public method + +OUString AddonsOptions::GetAddonsNotebookBarResourceName( sal_uInt32 nIndex ) const +{ + MutexGuard aGuard( GetOwnStaticMutex() ); + return m_pImpl->GetAddonsNotebookBarResourceName( nIndex ); +} + +// public method + +const Sequence< Sequence< PropertyValue > >& AddonsOptions::GetAddonsHelpMenu() const +{ + MutexGuard aGuard( GetOwnStaticMutex() ); + return m_pImpl->GetAddonsHelpMenu(); +} + +// public method + +const MergeMenuInstructionContainer& AddonsOptions::GetMergeMenuInstructions() const +{ + MutexGuard aGuard( GetOwnStaticMutex() ); + return m_pImpl->GetMergeMenuInstructions(); +} + +// public method + +bool AddonsOptions::GetMergeToolbarInstructions( + const OUString& rToolbarName, + MergeToolbarInstructionContainer& rToolbarInstructions ) const +{ + MutexGuard aGuard( GetOwnStaticMutex() ); + return m_pImpl->GetMergeToolbarInstructions( + rToolbarName, rToolbarInstructions ); +} + +// public method + +bool AddonsOptions::GetMergeNotebookBarInstructions( + const OUString& rNotebookBarName, + MergeNotebookBarInstructionContainer& rNotebookBarInstructions ) const +{ + MutexGuard aGuard( GetOwnStaticMutex() ); + return m_pImpl->GetMergeNotebookBarInstructions( + rNotebookBarName, rNotebookBarInstructions ); +} + +//public method + +const MergeStatusbarInstructionContainer& AddonsOptions::GetMergeStatusbarInstructions() const +{ + MutexGuard aGuard( GetOwnStaticMutex() ); + return m_pImpl->GetMergeStatusbarInstructions(); +} + +// public method + +BitmapEx AddonsOptions::GetImageFromURL( const OUString& aURL, bool bBig, bool bNoScale ) const +{ + MutexGuard aGuard( GetOwnStaticMutex() ); + return m_pImpl->GetImageFromURL( aURL, bBig, bNoScale ); +} + +// public method + +BitmapEx AddonsOptions::GetImageFromURL( const OUString& aURL, bool bBig ) const +{ + return GetImageFromURL( aURL, bBig, false ); +} + +Mutex& AddonsOptions::GetOwnStaticMutex() +{ + // Create static mutex variable. + static Mutex ourMutex; + + return ourMutex; +} + +IMPL_LINK_NOARG(AddonsOptions_Impl, NotifyEvent, void*, void) +{ + MutexGuard aGuard(AddonsOptions::GetOwnStaticMutex()); + ReadConfigurationData(); +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/fwe/classes/framelistanalyzer.cxx b/framework/source/fwe/classes/framelistanalyzer.cxx new file mode 100644 index 0000000000..d524d23fdf --- /dev/null +++ b/framework/source/fwe/classes/framelistanalyzer.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 . + */ + +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace framework{ + +FrameListAnalyzer::FrameListAnalyzer( const css::uno::Reference< css::frame::XFramesSupplier >& xSupplier , + const css::uno::Reference< css::frame::XFrame >& xReferenceFrame , + FrameAnalyzerFlags eDetectMode ) + : m_xSupplier (xSupplier ) + , m_xReferenceFrame(xReferenceFrame) + , m_eDetectMode (eDetectMode ) +{ + impl_analyze(); +} + +FrameListAnalyzer::~FrameListAnalyzer() +{ +} + +/** returns an analyzed list of all currently opened (top!) frames inside the desktop tree. + + We try to get a snapshot of all opened frames, which are part of the desktop frame container. + Of course we can't access frames, which stands outside of this tree. + But it's necessary to collect top frames here only. Otherwise we interpret closing of last + frame wrong. Further we analyze this list and split into different parts. + E.g. for "CloseDoc" we must know, which frames of the given list refer to the same model. + These frames must be closed then. But all other frames must be untouched. + In case the request was "CloseWin" these split lists can be used too, to decide if the last window + or document was closed. Then we have to initialize the backing window... + Last but not least we must know something about our special help frame. It must be handled + separately. And last but not least - the backing component frame must be detected too. +*/ + +void FrameListAnalyzer::impl_analyze() +{ + // reset all members to get a consistent state + m_bReferenceIsHidden = false; + m_bReferenceIsHelp = false; + m_bReferenceIsBacking = false; + m_xHelp.clear(); + m_xBackingComponent.clear(); + + // try to get the task container by using the given supplier + css::uno::Reference< css::container::XIndexAccess > xFrameContainer = m_xSupplier->getFrames(); + + // All return list get an initial size to include all possible frames. + // They will be packed at the end of this method ... using the actual step positions then. + sal_Int32 nVisibleStep = 0; + sal_Int32 nHiddenStep = 0; + sal_Int32 nModelStep = 0; + sal_Int32 nCount = xFrameContainer->getCount(); + + m_lOtherVisibleFrames.resize(nCount); + m_lOtherHiddenFrames.resize(nCount); + m_lModelFrames.resize(nCount); + + // ask for the model of the given reference frame. + // It must be compared with the model of every frame of the container + // to sort it into the list of frames with the same model. + // Suppress this step, if right detect mode isn't set. + css::uno::Reference< css::frame::XModel > xReferenceModel; + if (m_eDetectMode & FrameAnalyzerFlags::Model) + { + css::uno::Reference< css::frame::XController > xReferenceController; + if (m_xReferenceFrame.is()) + xReferenceController = m_xReferenceFrame->getController(); + if (xReferenceController.is()) + xReferenceModel = xReferenceController->getModel(); + } + + // check, if the reference frame is in hidden mode. + // But look, if this analyze step is really needed. + css::uno::Reference< css::beans::XPropertySet > xSet(m_xReferenceFrame, css::uno::UNO_QUERY); + if ( (m_eDetectMode & FrameAnalyzerFlags::Hidden) && xSet.is() ) + { + xSet->getPropertyValue(FRAME_PROPNAME_ASCII_ISHIDDEN) >>= m_bReferenceIsHidden; + } + + // check, if the reference frame includes the backing component. + // But look, if this analyze step is really needed. + if ((m_eDetectMode & FrameAnalyzerFlags::BackingComponent) && m_xReferenceFrame.is() ) + { + try + { + css::uno::Reference< css::uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext(); + css::uno::Reference< css::frame::XModuleManager2 > xModuleMgr = css::frame::ModuleManager::create(xContext); + OUString sModule = xModuleMgr->identify(m_xReferenceFrame); + m_bReferenceIsBacking = sModule == "com.sun.star.frame.StartModule"; + } + catch(const css::frame::UnknownModuleException&) + { + } + catch(const css::uno::Exception&) + { + DBG_UNHANDLED_EXCEPTION("fwk"); + } + } + + // check, if the reference frame includes the help module. + // But look, if this analyze step is really needed. + if ( + (m_eDetectMode & FrameAnalyzerFlags::Help) && + (m_xReferenceFrame.is() ) && + (m_xReferenceFrame->getName() == SPECIALTARGET_HELPTASK) + ) + { + m_bReferenceIsHelp = true; + } + + try + { + // Step over all frames of the desktop frame container and analyze it. + for (sal_Int32 i=0; i xFrame; + if ( + !(xFrameContainer->getByIndex(i) >>= xFrame) || + !(xFrame.is() ) || + (xFrame==m_xReferenceFrame ) + ) + continue; + + if ( + (m_eDetectMode & FrameAnalyzerFlags::Zombie) && + ( + (!xFrame->getContainerWindow().is()) || + (!xFrame->getComponentWindow().is()) + ) + ) + { + SAL_INFO("fwk", "FrameListAnalyzer::impl_analyze(): ZOMBIE!"); + } + + // a) Is it the special help task? + // Return it separated from any return list. + if ( + (m_eDetectMode & FrameAnalyzerFlags::Help) && + (xFrame->getName()==SPECIALTARGET_HELPTASK) + ) + { + m_xHelp = xFrame; + continue; + } + + // b) Or is includes this task the special backing component? + // Return it separated from any return list. + // But check if the reference task itself is the backing frame. + // Our user must know it to decide right. + if (m_eDetectMode & FrameAnalyzerFlags::BackingComponent) + { + try + { + css::uno::Reference< css::uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext(); + css::uno::Reference< css::frame::XModuleManager2 > xModuleMgr = css::frame::ModuleManager::create(xContext); + OUString sModule = xModuleMgr->identify(xFrame); + if (sModule == "com.sun.star.frame.StartModule") + { + m_xBackingComponent = xFrame; + continue; + } + } + catch (const css::uno::Exception&) + { + } + } + + // c) Or is it the a task, which uses the specified model? + // Add it to the list of "model frames". + if (m_eDetectMode & FrameAnalyzerFlags::Model) + { + css::uno::Reference< css::frame::XController > xController = xFrame->getController(); + css::uno::Reference< css::frame::XModel > xModel; + if (xController.is()) + xModel = xController->getModel(); + if (xModel==xReferenceModel) + { + m_lModelFrames[nModelStep] = xFrame; + ++nModelStep; + continue; + } + } + + // d) Or is it the a task, which use another or no model at all? + // Add it to the list of "other frames". But look for its + // visible state ... if it's allowed to do so. + + bool bHidden = false; + if (m_eDetectMode & FrameAnalyzerFlags::Hidden) + { + xSet.set(xFrame, css::uno::UNO_QUERY); + if (xSet.is()) + { + xSet->getPropertyValue(FRAME_PROPNAME_ASCII_ISHIDDEN) >>= bHidden; + } + } + + if (bHidden) + { + m_lOtherHiddenFrames[nHiddenStep] = xFrame; + ++nHiddenStep; + } + else + { + m_lOtherVisibleFrames[nVisibleStep] = xFrame; + ++nVisibleStep; + } + } + } + catch (const css::lang::IndexOutOfBoundsException&) + { + // stop copying if index seems to be wrong. + // This interface can't really guarantee its count for multithreaded + // environments. So it can occur! + } + + // Pack both lists by using the actual step positions. + // All empty or ignorable items should exist at the end of these lists + // behind the position pointers. So they will be removed by a reallocation. + m_lOtherVisibleFrames.resize(nVisibleStep); + m_lOtherHiddenFrames.resize(nHiddenStep); + m_lModelFrames.resize(nModelStep); +} + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/fwe/classes/fwkresid.cxx b/framework/source/fwe/classes/fwkresid.cxx new file mode 100644 index 0000000000..e9a1d639d3 --- /dev/null +++ b/framework/source/fwe/classes/fwkresid.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 + +OUString FwkResId(TranslateId aId) { return Translate::get(aId, Translate::Create("fwk")); } + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/fwe/classes/rootactiontriggercontainer.cxx b/framework/source/fwe/classes/rootactiontriggercontainer.cxx new file mode 100644 index 0000000000..1493f08bf4 --- /dev/null +++ b/framework/source/fwe/classes/rootactiontriggercontainer.cxx @@ -0,0 +1,237 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace cppu; +using namespace com::sun::star::uno; +using namespace com::sun::star::lang; +using namespace com::sun::star::container; +using namespace com::sun::star::beans; + +namespace framework +{ + +RootActionTriggerContainer::RootActionTriggerContainer(css::uno::Reference xMenu, + const OUString* pMenuIdentifier) + : m_bContainerCreated(false) + , m_xMenu(std::move(xMenu)) + , m_pMenuIdentifier(pMenuIdentifier) +{ +} + +RootActionTriggerContainer::~RootActionTriggerContainer() +{ +} + +// XInterface +Any SAL_CALL RootActionTriggerContainer::queryInterface( const Type& aType ) +{ + Any a = ::cppu::queryInterface( + aType , + static_cast< XMultiServiceFactory* >(this), + static_cast< XServiceInfo* >(this), + static_cast< XTypeProvider* >(this), + static_cast< XNamed* >(this)); + + if( a.hasValue() ) + { + return a; + } + + return PropertySetContainer::queryInterface( aType ); +} + +void SAL_CALL RootActionTriggerContainer::acquire() noexcept +{ + PropertySetContainer::acquire(); +} + +void SAL_CALL RootActionTriggerContainer::release() noexcept +{ + PropertySetContainer::release(); +} + +// XMultiServiceFactory +Reference< XInterface > SAL_CALL RootActionTriggerContainer::createInstance( const OUString& aServiceSpecifier ) +{ + if ( aServiceSpecifier == SERVICENAME_ACTIONTRIGGER ) + return static_cast( new ActionTriggerPropertySet()); + else if ( aServiceSpecifier == SERVICENAME_ACTIONTRIGGERCONTAINER ) + return static_cast( new ActionTriggerContainer()); + else if ( aServiceSpecifier == SERVICENAME_ACTIONTRIGGERSEPARATOR ) + return static_cast( new ActionTriggerSeparatorPropertySet()); + else + throw css::uno::RuntimeException("Unknown service specifier!", static_cast(this) ); +} + +Reference< XInterface > SAL_CALL RootActionTriggerContainer::createInstanceWithArguments( const OUString& ServiceSpecifier, const Sequence< Any >& /*Arguments*/ ) +{ + return createInstance( ServiceSpecifier ); +} + +Sequence< OUString > SAL_CALL RootActionTriggerContainer::getAvailableServiceNames() +{ + Sequence< OUString > aSeq{ SERVICENAME_ACTIONTRIGGER, + SERVICENAME_ACTIONTRIGGERCONTAINER, + SERVICENAME_ACTIONTRIGGERSEPARATOR }; + return aSeq; +} + +// XIndexContainer +void SAL_CALL RootActionTriggerContainer::insertByIndex( sal_Int32 Index, const Any& Element ) +{ + SolarMutexGuard g; + + if ( !m_bContainerCreated ) + FillContainer(); + + PropertySetContainer::insertByIndex( Index, Element ); +} + +void SAL_CALL RootActionTriggerContainer::removeByIndex( sal_Int32 Index ) +{ + SolarMutexGuard g; + + if ( !m_bContainerCreated ) + FillContainer(); + + PropertySetContainer::removeByIndex( Index ); +} + +// XIndexReplace +void SAL_CALL RootActionTriggerContainer::replaceByIndex( sal_Int32 Index, const Any& Element ) +{ + SolarMutexGuard g; + + if ( !m_bContainerCreated ) + FillContainer(); + + PropertySetContainer::replaceByIndex( Index, Element ); +} + +// XIndexAccess +sal_Int32 SAL_CALL RootActionTriggerContainer::getCount() +{ + SolarMutexGuard g; + + if ( !m_bContainerCreated ) + { + if ( m_xMenu ) + return m_xMenu->getItemCount(); + else + return 0; + } + else + { + return PropertySetContainer::getCount(); + } +} + +Any SAL_CALL RootActionTriggerContainer::getByIndex( sal_Int32 Index ) +{ + SolarMutexGuard g; + + if ( !m_bContainerCreated ) + FillContainer(); + + return PropertySetContainer::getByIndex( Index ); +} + +// XElementAccess +Type SAL_CALL RootActionTriggerContainer::getElementType() +{ + return cppu::UnoType::get(); +} + +sal_Bool SAL_CALL RootActionTriggerContainer::hasElements() +{ + if (m_xMenu) + return m_xMenu->getItemCount() > 0; + return false; +} + +// XServiceInfo +OUString SAL_CALL RootActionTriggerContainer::getImplementationName() +{ + return IMPLEMENTATIONNAME_ROOTACTIONTRIGGERCONTAINER; +} + +sal_Bool SAL_CALL RootActionTriggerContainer::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService(this, ServiceName); +} + +Sequence< OUString > SAL_CALL RootActionTriggerContainer::getSupportedServiceNames() +{ + return { SERVICENAME_ACTIONTRIGGERCONTAINER }; +} + +// XTypeProvider +Sequence< Type > SAL_CALL RootActionTriggerContainer::getTypes() +{ + // Create a static typecollection ... + static ::cppu::OTypeCollection ourTypeCollection( + cppu::UnoType::get(), + cppu::UnoType::get(), + cppu::UnoType::get(), + cppu::UnoType::get(), + cppu::UnoType::get(), + cppu::UnoType::get()); + + return ourTypeCollection.getTypes(); +} + +Sequence< sal_Int8 > SAL_CALL RootActionTriggerContainer::getImplementationId() +{ + return css::uno::Sequence(); +} + +// private implementation helper +void RootActionTriggerContainer::FillContainer() +{ + m_bContainerCreated = true; + ActionTriggerHelper::FillActionTriggerContainerFromMenu( + this, m_xMenu); +} +OUString RootActionTriggerContainer::getName() +{ + OUString sRet; + if( m_pMenuIdentifier ) + sRet = *m_pMenuIdentifier; + return sRet; +} + +void RootActionTriggerContainer::setName( const OUString& ) +{ + throw RuntimeException(); +} +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/fwe/classes/sfxhelperfunctions.cxx b/framework/source/fwe/classes/sfxhelperfunctions.cxx new file mode 100644 index 0000000000..5a1cc0d716 --- /dev/null +++ b/framework/source/fwe/classes/sfxhelperfunctions.cxx @@ -0,0 +1,158 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include + +static pfunc_setToolBoxControllerCreator pToolBoxControllerCreator = nullptr; +static pfunc_setStatusBarControllerCreator pStatusBarControllerCreator = nullptr; +static pfunc_getRefreshToolbars pRefreshToolbars = nullptr; +static pfunc_createDockingWindow pCreateDockingWindow = nullptr; +static pfunc_isDockingWindowVisible pIsDockingWindowVisible = nullptr; + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::frame; + +namespace framework +{ + +pfunc_setToolBoxControllerCreator SetToolBoxControllerCreator( pfunc_setToolBoxControllerCreator pSetToolBoxControllerCreator ) +{ + ::osl::MutexGuard aGuard( ::osl::Mutex::getGlobalMutex() ); + pfunc_setToolBoxControllerCreator pOldSetToolBoxControllerCreator = pToolBoxControllerCreator; + pToolBoxControllerCreator = pSetToolBoxControllerCreator; + return pOldSetToolBoxControllerCreator; +} + +rtl::Reference CreateToolBoxController( const Reference< XFrame >& rFrame, ToolBox* pToolbox, ToolBoxItemId nID, const OUString& aCommandURL ) +{ + pfunc_setToolBoxControllerCreator pFactory = nullptr; + { + ::osl::MutexGuard aGuard( ::osl::Mutex::getGlobalMutex() ); + pFactory = pToolBoxControllerCreator; + } + + if ( pFactory ) + return (*pFactory)( rFrame, pToolbox, nID, aCommandURL ); + else + return nullptr; +} + +pfunc_setStatusBarControllerCreator SetStatusBarControllerCreator( pfunc_setStatusBarControllerCreator pSetStatusBarControllerCreator ) +{ + ::osl::MutexGuard aGuard( ::osl::Mutex::getGlobalMutex() ); + pfunc_setStatusBarControllerCreator pOldSetStatusBarControllerCreator = pSetStatusBarControllerCreator; + pStatusBarControllerCreator = pSetStatusBarControllerCreator; + return pOldSetStatusBarControllerCreator; +} + +rtl::Reference CreateStatusBarController( const Reference< XFrame >& rFrame, StatusBar* pStatusBar, unsigned short nID, const OUString& aCommandURL ) +{ + pfunc_setStatusBarControllerCreator pFactory = nullptr; + { + ::osl::MutexGuard aGuard( ::osl::Mutex::getGlobalMutex() ); + pFactory = pStatusBarControllerCreator; + } + + if ( pFactory ) + return (*pFactory)( rFrame, pStatusBar, nID, aCommandURL ); + else + return nullptr; +} + +pfunc_getRefreshToolbars SetRefreshToolbars( pfunc_getRefreshToolbars pNewRefreshToolbarsFunc ) +{ + ::osl::MutexGuard aGuard( ::osl::Mutex::getGlobalMutex() ); + pfunc_getRefreshToolbars pOldFunc = pRefreshToolbars; + pRefreshToolbars = pNewRefreshToolbarsFunc; + + return pOldFunc; +} + +void RefreshToolbars( css::uno::Reference< css::frame::XFrame > const & rFrame ) +{ + pfunc_getRefreshToolbars pCallback = nullptr; + { + ::osl::MutexGuard aGuard( ::osl::Mutex::getGlobalMutex() ); + pCallback = pRefreshToolbars; + } + + if ( pCallback ) + (*pCallback)( rFrame ); +} + +pfunc_createDockingWindow SetDockingWindowCreator( pfunc_createDockingWindow pNewCreateDockingWindow ) +{ + ::osl::MutexGuard aGuard( ::osl::Mutex::getGlobalMutex() ); + pfunc_createDockingWindow pOldFunc = pCreateDockingWindow; + pCreateDockingWindow = pNewCreateDockingWindow; + + return pOldFunc; +} + +void CreateDockingWindow( const css::uno::Reference< css::frame::XFrame >& rFrame, std::u16string_view rResourceURL ) +{ + pfunc_createDockingWindow pFactory = nullptr; + { + ::osl::MutexGuard aGuard( ::osl::Mutex::getGlobalMutex() ); + pFactory = pCreateDockingWindow; + } + + if ( pFactory ) + (*pFactory)( rFrame, rResourceURL ); +} + +pfunc_isDockingWindowVisible SetIsDockingWindowVisible( pfunc_isDockingWindowVisible pNewIsDockingWindowVisible) +{ + ::osl::MutexGuard aGuard( ::osl::Mutex::getGlobalMutex() ); + pfunc_isDockingWindowVisible pOldFunc = pIsDockingWindowVisible; + pIsDockingWindowVisible = pNewIsDockingWindowVisible; + + return pOldFunc; +} + +bool IsDockingWindowVisible( const css::uno::Reference< css::frame::XFrame >& rFrame, std::u16string_view rResourceURL ) +{ + pfunc_isDockingWindowVisible pCall = nullptr; + { + ::osl::MutexGuard aGuard( ::osl::Mutex::getGlobalMutex() ); + pCall = pIsDockingWindowVisible; + } + + if ( pCall ) + return (*pCall)( rFrame, rResourceURL ); + else + return false; +} + +using namespace ::com::sun::star; +uno::Reference GetFirstListenerWith( + css::uno::Reference const & xComponentContext, + uno::Reference const& xEventFocus, + std::function const&)> const& rPredicate) +{ + return GetFirstListenerWith_Impl(xComponentContext, xEventFocus, rPredicate); +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/fwe/dispatch/interaction.cxx b/framework/source/fwe/dispatch/interaction.cxx new file mode 100644 index 0000000000..d0cf88e6f6 --- /dev/null +++ b/framework/source/fwe/dispatch/interaction.cxx @@ -0,0 +1,225 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include + +using namespace ::com::sun::star; + +namespace framework{ + +namespace { + +/*-************************************************************************************************************ + @short declaration of special continuation for filter selection + @descr Sometimes filter detection during loading document failed. Then we need a possibility + to ask user for his decision. These continuation transport selected filter by user to + code user of interaction. + + @attention This implementation could be used one times only. We don't support a resettable continuation yet! + Why? Normally interaction should show a filter selection dialog and ask user for his decision. + He can select any filter - then instances of these class will be called by handler... or user + close dialog without any selection. Then another continuation should be selected by handler to + abort continuations... Retrying isn't very useful here... I think. + + @implements XInteractionFilterSelect + + @base ImplInheritanceHelper + ContinuationBase + + @devstatus ready to use + @threadsafe no (used on once position only!) +*//*-*************************************************************************************************************/ +class ContinuationFilterSelect : public comphelper::OInteraction< css::document::XInteractionFilterSelect > +{ + // c++ interface + public: + ContinuationFilterSelect(); + + // uno interface + public: + virtual void SAL_CALL setFilter( const OUString& sFilter ) override; + virtual OUString SAL_CALL getFilter( ) override; + + // member + private: + OUString m_sFilter; + +}; // class ContinuationFilterSelect + +} + +// initialize continuation with right start values + +ContinuationFilterSelect::ContinuationFilterSelect() +{ +} + +// handler should use it after selection to set user specified filter for transport + +void SAL_CALL ContinuationFilterSelect::setFilter( const OUString& sFilter ) +{ + m_sFilter = sFilter; +} + +// read access to transported filter + +OUString SAL_CALL ContinuationFilterSelect::getFilter() +{ + return m_sFilter; +} + +class RequestFilterSelect_Impl : public ::cppu::WeakImplHelper< css::task::XInteractionRequest > +{ +public: + explicit RequestFilterSelect_Impl(const OUString& rURL); + bool isAbort () const; + OUString getFilter() const; + +public: + virtual css::uno::Any SAL_CALL getRequest() override; + virtual css::uno::Sequence< css::uno::Reference< css::task::XInteractionContinuation > > SAL_CALL getContinuations() override; + +private: + css::uno::Any m_aRequest; + rtl::Reference m_xAbort; + rtl::Reference m_xFilter; +}; + +// initialize instance with all necessary information +// We use it without any further checks on our member then ...! + +RequestFilterSelect_Impl::RequestFilterSelect_Impl( const OUString& sURL ) +{ + css::uno::Reference< css::uno::XInterface > temp2; + css::document::NoSuchFilterRequest aFilterRequest( OUString(), + temp2 , + sURL ); + m_aRequest <<= aFilterRequest; + + m_xAbort = new comphelper::OInteractionAbort; + m_xFilter = new ContinuationFilterSelect; +} + +// return abort state of interaction +// If it is true, return value of method "getFilter()" will be unspecified then! + +bool RequestFilterSelect_Impl::isAbort() const +{ + return m_xAbort->wasSelected(); +} + +// return user selected filter +// Return value valid for non aborted interaction only. Please check "isAbort()" before you call these only! + +OUString RequestFilterSelect_Impl::getFilter() const +{ + return m_xFilter->getFilter(); +} + +// handler call it to get type of request +// Is hard coded to "please select filter" here. see ctor for further information. + +css::uno::Any SAL_CALL RequestFilterSelect_Impl::getRequest() +{ + return m_aRequest; +} + +// handler call it to get possible continuations +// We support "abort/select_filter" only here. +// After interaction we support read access on these continuations on our c++ interface to +// return user decision. + +css::uno::Sequence< css::uno::Reference< css::task::XInteractionContinuation > > SAL_CALL RequestFilterSelect_Impl::getContinuations() +{ + return { m_xAbort, m_xFilter }; +} + +RequestFilterSelect::RequestFilterSelect( const OUString& sURL ) + : mxImpl(new RequestFilterSelect_Impl( sURL )) +{ +} + +RequestFilterSelect::~RequestFilterSelect() +{ +} + +// return abort state of interaction +// If it is true, return value of method "getFilter()" will be unspecified then! + +bool RequestFilterSelect::isAbort() const +{ + return mxImpl->isAbort(); +} + +// return user selected filter +// Return value valid for non aborted interaction only. Please check "isAbort()" before you call these only! + +OUString RequestFilterSelect::getFilter() const +{ + return mxImpl->getFilter(); +} + +uno::Reference < task::XInteractionRequest > RequestFilterSelect::GetRequest() const +{ + return mxImpl; +} + +namespace { + +class InteractionRequest_Impl : public ::cppu::WeakImplHelper< css::task::XInteractionRequest > +{ + uno::Any m_aRequest; + uno::Sequence< css::uno::Reference< css::task::XInteractionContinuation > > m_lContinuations; + +public: + InteractionRequest_Impl( css::uno::Any aRequest, + const css::uno::Sequence< css::uno::Reference< css::task::XInteractionContinuation > >& lContinuations ) + : m_aRequest(std::move(aRequest)), m_lContinuations(lContinuations) + { + } + + virtual uno::Any SAL_CALL getRequest() override; + virtual uno::Sequence< uno::Reference< task::XInteractionContinuation > > SAL_CALL getContinuations() override; +}; + +} + +uno::Any SAL_CALL InteractionRequest_Impl::getRequest() +{ + return m_aRequest; +} + +uno::Sequence< uno::Reference< task::XInteractionContinuation > > SAL_CALL InteractionRequest_Impl::getContinuations() +{ + return m_lContinuations; +} + +uno::Reference < task::XInteractionRequest > InteractionRequest::CreateRequest( + const uno::Any& aRequest, const uno::Sequence< uno::Reference< task::XInteractionContinuation > >& lContinuations ) +{ + return new InteractionRequest_Impl( aRequest, lContinuations ); +} + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/fwe/helper/actiontriggerhelper.cxx b/framework/source/fwe/helper/actiontriggerhelper.cxx new file mode 100644 index 0000000000..88edd70beb --- /dev/null +++ b/framework/source/fwe/helper/actiontriggerhelper.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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +const sal_uInt16 START_ITEMID = 1000; + +using namespace com::sun::star::awt; +using namespace com::sun::star::uno; +using namespace com::sun::star::lang; +using namespace com::sun::star::beans; +using namespace com::sun::star::container; + +namespace framework +{ + +// implementation helper ( menu => ActionTrigger ) + +static bool IsSeparator( const Reference< XPropertySet >& xPropertySet ) +{ + Reference< XServiceInfo > xServiceInfo( xPropertySet, UNO_QUERY ); + try + { + return xServiceInfo->supportsService( SERVICENAME_ACTIONTRIGGERSEPARATOR ); + } + catch (const Exception&) + { + } + + return false; +} + +static void GetMenuItemAttributes( const Reference< XPropertySet >& xActionTriggerPropertySet, + OUString& aMenuLabel, + OUString& aCommandURL, + OUString& aHelpURL, + Reference< XBitmap >& xBitmap, + Reference< XIndexContainer >& xSubContainer ) +{ + Any a; + + try + { + // mandatory properties + a = xActionTriggerPropertySet->getPropertyValue("Text"); + a >>= aMenuLabel; + a = xActionTriggerPropertySet->getPropertyValue("CommandURL"); + a >>= aCommandURL; + a = xActionTriggerPropertySet->getPropertyValue("Image"); + a >>= xBitmap; + a = xActionTriggerPropertySet->getPropertyValue("SubContainer"); + a >>= xSubContainer; + } + catch (const Exception&) + { + } + + // optional properties + try + { + a = xActionTriggerPropertySet->getPropertyValue("HelpURL"); + a >>= aHelpURL; + } + catch (const Exception&) + { + } +} + +static void InsertSubMenuItems(const Reference& rSubMenu, sal_uInt16& nItemId, + const Reference& xActionTriggerContainer) +{ + if ( !xActionTriggerContainer.is() ) + return; + + AddonsOptions aAddonOptions; + OUString aSlotURL( "slot:" ); + + for ( sal_Int32 i = 0; i < xActionTriggerContainer->getCount(); i++ ) + { + try + { + Reference< XPropertySet > xPropSet; + if (( xActionTriggerContainer->getByIndex( i ) >>= xPropSet ) && ( xPropSet.is() )) + { + if ( IsSeparator( xPropSet )) + { + // Separator + SolarMutexGuard aGuard; + rSubMenu->insertSeparator(i); + } + else + { + // Menu item + OUString aLabel; + OUString aCommandURL; + OUString aHelpURL; + Reference< XBitmap > xBitmap; + Reference< XIndexContainer > xSubContainer; + + sal_uInt16 nNewItemId = nItemId++; + GetMenuItemAttributes( xPropSet, aLabel, aCommandURL, aHelpURL, xBitmap, xSubContainer ); + + SolarMutexGuard aGuard; + { + // insert new menu item + sal_Int32 nIndex = aCommandURL.indexOf( aSlotURL ); + if ( nIndex >= 0 ) + { + // Special code for our menu implementation: some menu items don't have a + // command url but uses the item id as a unique identifier. These entries + // got a special url during conversion from menu=>actiontriggercontainer. + // Now we have to extract this special url and set the correct item id!!! + nNewItemId = static_cast(o3tl::toInt32(aCommandURL.subView( nIndex+aSlotURL.getLength() ))); + rSubMenu->insertItem(nNewItemId, aLabel, 0, i); + } + else + { + rSubMenu->insertItem(nNewItemId, aLabel, 0, i); + rSubMenu->setCommand(nNewItemId, aCommandURL); + } + + // handle bitmap + if ( xBitmap.is() ) + { + bool bImageSet = false; + + Reference xGraphic(xBitmap, UNO_QUERY); + if (xGraphic.is()) + { + // we can take the optimized route if XGraphic is supported + rSubMenu->setItemImage(nNewItemId, xGraphic, false); + bImageSet = true; + } + + if ( !bImageSet ) + { + // This is an unknown implementation of a XBitmap interface. We have to + // use a more time consuming way to build an Image! + BitmapEx aBitmap; + + Sequence< sal_Int8 > aDIBSeq; + { + aDIBSeq = xBitmap->getDIB(); + SvMemoryStream aMem( const_cast(aDIBSeq.getConstArray()), aDIBSeq.getLength(), StreamMode::READ ); + ReadDIBBitmapEx(aBitmap, aMem); + } + + aDIBSeq = xBitmap->getMaskDIB(); + if ( aDIBSeq.hasElements() ) + { + Bitmap aMaskBitmap; + SvMemoryStream aMem( const_cast(aDIBSeq.getConstArray()), aDIBSeq.getLength(), StreamMode::READ ); + ReadDIB(aMaskBitmap, aMem, true); + aBitmap = BitmapEx(aBitmap.GetBitmap(), aMaskBitmap); + } + + if (!aBitmap.IsEmpty()) + rSubMenu->setItemImage(nNewItemId, Graphic(aBitmap).GetXGraphic(), false); + } + } + else + { + // Support add-on images for context menu interceptors + BitmapEx aBitmap(aAddonOptions.GetImageFromURL(aCommandURL, false, true)); + if (!aBitmap.IsEmpty()) + rSubMenu->setItemImage(nNewItemId, Graphic(aBitmap).GetXGraphic(), false); + } + + if ( xSubContainer.is() ) + { + rtl::Reference xNewSubMenu(new VCLXPopupMenu); + + // Sub menu (recursive call CreateSubMenu ) + InsertSubMenuItems(xNewSubMenu, nItemId, xSubContainer); + rSubMenu->setPopupMenu(nNewItemId, xNewSubMenu); + } + } + } + } + } + catch (const IndexOutOfBoundsException&) + { + return; + } + catch (const WrappedTargetException&) + { + return; + } + catch (const RuntimeException&) + { + return; + } + } +} + +// implementation helper ( ActionTrigger => menu ) + +/// @throws RuntimeException +static Reference< XPropertySet > CreateActionTrigger(sal_uInt16 nItemId, + const Reference& rMenu, + const Reference& rActionTriggerContainer) +{ + Reference< XPropertySet > xPropSet; + + Reference< XMultiServiceFactory > xMultiServiceFactory( rActionTriggerContainer, UNO_QUERY ); + if ( xMultiServiceFactory.is() ) + { + xPropSet.set( xMultiServiceFactory->createInstance( "com.sun.star.ui.ActionTrigger" ), + UNO_QUERY ); + + Any a; + + try + { + // Retrieve the menu attributes and set them in our PropertySet + OUString aLabel = rMenu->getItemText(nItemId); + a <<= aLabel; + xPropSet->setPropertyValue("Text", a ); + + OUString aCommandURL = rMenu->getCommand(nItemId); + + if ( aCommandURL.isEmpty() ) + { + aCommandURL = "slot:" + OUString::number( nItemId ); + } + + a <<= aCommandURL; + xPropSet->setPropertyValue("CommandURL", a ); + + Reference xBitmap(rMenu->getItemImage(nItemId), UNO_QUERY); + if (xBitmap.is()) + { + a <<= xBitmap; + xPropSet->setPropertyValue("Image", a ); + } + } + catch (const Exception&) + { + } + } + + return xPropSet; +} + +/// @throws RuntimeException +static Reference< XPropertySet > CreateActionTriggerSeparator( const Reference< XIndexContainer >& rActionTriggerContainer ) +{ + Reference< XMultiServiceFactory > xMultiServiceFactory( rActionTriggerContainer, UNO_QUERY ); + if ( xMultiServiceFactory.is() ) + { + return Reference< XPropertySet >( xMultiServiceFactory->createInstance( + "com.sun.star.ui.ActionTriggerSeparator" ), + UNO_QUERY ); + } + + return Reference< XPropertySet >(); +} + +/// @throws RuntimeException +static Reference< XIndexContainer > CreateActionTriggerContainer( const Reference< XIndexContainer >& rActionTriggerContainer ) +{ + Reference< XMultiServiceFactory > xMultiServiceFactory( rActionTriggerContainer, UNO_QUERY ); + if ( xMultiServiceFactory.is() ) + { + return Reference< XIndexContainer >( xMultiServiceFactory->createInstance( + "com.sun.star.ui.ActionTriggerContainer" ), + UNO_QUERY ); + } + + return Reference< XIndexContainer >(); +} + +static void FillActionTriggerContainerWithMenu(const Reference& rMenu, + const Reference& rActionTriggerContainer) +{ + SolarMutexGuard aGuard; + + for (sal_uInt16 nPos = 0, nCount = rMenu->getItemCount(); nPos < nCount; ++nPos) + { + sal_uInt16 nItemId = rMenu->getItemId(nPos); + css::awt::MenuItemType nType = rMenu->getItemType(nPos); + + try + { + Any a; + Reference< XPropertySet > xPropSet; + + if (nType == css::awt::MenuItemType_SEPARATOR) + { + xPropSet = CreateActionTriggerSeparator( rActionTriggerContainer ); + + a <<= xPropSet; + rActionTriggerContainer->insertByIndex( nPos, a ); + } + else + { + xPropSet = CreateActionTrigger(nItemId, rMenu, rActionTriggerContainer); + + a <<= xPropSet; + rActionTriggerContainer->insertByIndex( nPos, a ); + + css::uno::Reference xPopupMenu = rMenu->getPopupMenu(nItemId); + if (xPopupMenu.is()) + { + // recursive call to build next sub menu + Reference< XIndexContainer > xSubContainer = CreateActionTriggerContainer( rActionTriggerContainer ); + + a <<= xSubContainer; + xPropSet->setPropertyValue("SubContainer", a ); + FillActionTriggerContainerWithMenu(xPopupMenu, xSubContainer); + } + } + } + catch (const Exception&) + { + } + } +} + +void ActionTriggerHelper::CreateMenuFromActionTriggerContainer( + const Reference& rNewMenu, + const Reference& rActionTriggerContainer) +{ + sal_uInt16 nItemId = START_ITEMID; + + if ( rActionTriggerContainer.is() ) + InsertSubMenuItems(rNewMenu, nItemId, rActionTriggerContainer); +} + +void ActionTriggerHelper::FillActionTriggerContainerFromMenu( + Reference< XIndexContainer > const & xActionTriggerContainer, + const css::uno::Reference& rMenu) +{ + FillActionTriggerContainerWithMenu(rMenu, xActionTriggerContainer); +} + +Reference< XIndexContainer > ActionTriggerHelper::CreateActionTriggerContainerFromMenu( + const css::uno::Reference& rMenu, + const OUString* pMenuIdentifier ) +{ + return new RootActionTriggerContainer(rMenu, pMenuIdentifier); +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/fwe/helper/configimporter.cxx b/framework/source/fwe/helper/configimporter.cxx new file mode 100644 index 0000000000..bc92d180b9 --- /dev/null +++ b/framework/source/fwe/helper/configimporter.cxx @@ -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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include + +using namespace ::com::sun::star; + +namespace framework +{ + +bool UIConfigurationImporterOOo1x::ImportCustomToolbars( + const uno::Reference< ui::XUIConfigurationManager2 >& rContainerFactory, + std::vector< uno::Reference< container::XIndexContainer > >& rSeqContainer, + const uno::Reference< uno::XComponentContext >& rxContext, + const uno::Reference< embed::XStorage >& rToolbarStorage ) +{ + bool bResult ( false ); + if ( rToolbarStorage.is() && rContainerFactory.is() ) + { + try + { + for ( sal_uInt16 i = 1; i <= 4; i++ ) + { + OUString aTbxStreamName = "userdeftoolbox" + OUString::number(i) + ".xml"; + uno::Reference< io::XStream > xStream = rToolbarStorage->openStreamElement( aTbxStreamName, embed::ElementModes::READ ); + if ( xStream.is() ) + { + uno::Reference< io::XInputStream > xInputStream = xStream->getInputStream(); + if ( xInputStream.is() ) + { + uno::Reference< container::XIndexContainer > xContainer = rContainerFactory->createSettings(); + if ( ToolBoxConfiguration::LoadToolBox( rxContext, xInputStream, xContainer )) + { + rSeqContainer.push_back( xContainer ); + bResult = true; + } + } + } + } + } + catch ( const uno::RuntimeException& ) + { + throw; + } + catch ( const uno::Exception& ) + { + } + } + + return bResult; +} + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/fwe/helper/documentundoguard.cxx b/framework/source/fwe/helper/documentundoguard.cxx new file mode 100644 index 0000000000..f578d86827 --- /dev/null +++ b/framework/source/fwe/helper/documentundoguard.cxx @@ -0,0 +1,195 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include + +#include +#include +#include + +namespace framework +{ + + using ::com::sun::star::uno::Reference; + using ::com::sun::star::uno::XInterface; + using ::com::sun::star::uno::UNO_QUERY; + using ::com::sun::star::uno::Exception; + using ::com::sun::star::document::XUndoManagerSupplier; + using ::com::sun::star::document::XUndoManager; + using ::com::sun::star::document::XUndoManagerListener; + using ::com::sun::star::document::UndoManagerEvent; + using ::com::sun::star::lang::EventObject; + + //= UndoManagerContextListener + + typedef ::cppu::WeakImplHelper < XUndoManagerListener + > UndoManagerContextListener_Base; + + class UndoManagerContextListener : public UndoManagerContextListener_Base + { + public: + explicit UndoManagerContextListener( const Reference< XUndoManager >& i_undoManager ) + :m_xUndoManager( i_undoManager ) + ,m_nRelativeContextDepth( 0 ) + ,m_documentDisposed( false ) + { + osl_atomic_increment( &m_refCount ); + { + m_xUndoManager->addUndoManagerListener( this ); + } + osl_atomic_decrement( &m_refCount ); + } + + void finish() + { + OSL_ENSURE( m_nRelativeContextDepth >= 0, "UndoManagerContextListener: more contexts left than entered?" ); + + if ( m_documentDisposed ) + return; + + // work with a copy of m_nRelativeContextDepth, to be independent from possible bugs in the + // listener notifications (where it would be decremented with every leaveUndoContext) + sal_Int32 nDepth = m_nRelativeContextDepth; + while ( nDepth-- > 0 ) + { + m_xUndoManager->leaveUndoContext(); + } + m_xUndoManager->removeUndoManagerListener( this ); + } + + // XUndoManagerListener + virtual void SAL_CALL undoActionAdded( const UndoManagerEvent& i_event ) override; + virtual void SAL_CALL actionUndone( const UndoManagerEvent& i_event ) override; + virtual void SAL_CALL actionRedone( const UndoManagerEvent& i_event ) override; + virtual void SAL_CALL allActionsCleared( const EventObject& i_event ) override; + virtual void SAL_CALL redoActionsCleared( const EventObject& i_event ) override; + virtual void SAL_CALL resetAll( const EventObject& i_event ) override; + virtual void SAL_CALL enteredContext( const UndoManagerEvent& i_event ) override; + virtual void SAL_CALL enteredHiddenContext( const UndoManagerEvent& i_event ) override; + virtual void SAL_CALL leftContext( const UndoManagerEvent& i_event ) override; + virtual void SAL_CALL leftHiddenContext( const UndoManagerEvent& i_event ) override; + virtual void SAL_CALL cancelledContext( const UndoManagerEvent& i_event ) override; + + // XEventListener + virtual void SAL_CALL disposing( const EventObject& i_event ) override; + + private: + Reference< XUndoManager > const m_xUndoManager; + oslInterlockedCount m_nRelativeContextDepth; + bool m_documentDisposed; + }; + + void SAL_CALL UndoManagerContextListener::undoActionAdded( const UndoManagerEvent& ) + { + // not interested in + } + + void SAL_CALL UndoManagerContextListener::actionUndone( const UndoManagerEvent& ) + { + // not interested in + } + + void SAL_CALL UndoManagerContextListener::actionRedone( const UndoManagerEvent& ) + { + // not interested in + } + + void SAL_CALL UndoManagerContextListener::allActionsCleared( const EventObject& ) + { + // not interested in + } + + void SAL_CALL UndoManagerContextListener::redoActionsCleared( const EventObject& ) + { + // not interested in + } + + void SAL_CALL UndoManagerContextListener::resetAll( const EventObject& ) + { + m_nRelativeContextDepth = 0; + } + + void SAL_CALL UndoManagerContextListener::enteredContext( const UndoManagerEvent& ) + { + osl_atomic_increment( &m_nRelativeContextDepth ); + } + + void SAL_CALL UndoManagerContextListener::enteredHiddenContext( const UndoManagerEvent& ) + { + osl_atomic_increment( &m_nRelativeContextDepth ); + } + + void SAL_CALL UndoManagerContextListener::leftContext( const UndoManagerEvent& ) + { + osl_atomic_decrement( &m_nRelativeContextDepth ); + } + + void SAL_CALL UndoManagerContextListener::leftHiddenContext( const UndoManagerEvent& ) + { + osl_atomic_decrement( &m_nRelativeContextDepth ); + } + + void SAL_CALL UndoManagerContextListener::cancelledContext( const UndoManagerEvent& ) + { + osl_atomic_decrement( &m_nRelativeContextDepth ); + } + + void SAL_CALL UndoManagerContextListener::disposing( const EventObject& ) + { + m_documentDisposed = true; + } + + //= DocumentUndoGuard + + DocumentUndoGuard::DocumentUndoGuard( const Reference< XInterface >& i_undoSupplierComponent ) + { + try + { + Reference< XUndoManagerSupplier > xUndoSupplier( i_undoSupplierComponent, UNO_QUERY ); + if ( xUndoSupplier.is() ) + mxUndoManager.set( xUndoSupplier->getUndoManager(), css::uno::UNO_SET_THROW ); + + if ( mxUndoManager.is() ) + mxContextListener.set( new UndoManagerContextListener( mxUndoManager ) ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("fwk"); + } + } + + DocumentUndoGuard::~DocumentUndoGuard() + { + try + { + if ( mxContextListener.is() ) + mxContextListener->finish(); + mxContextListener.clear(); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("fwk"); + } + } + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/fwe/helper/propertysetcontainer.cxx b/framework/source/fwe/helper/propertysetcontainer.cxx new file mode 100644 index 0000000000..c766b40db5 --- /dev/null +++ b/framework/source/fwe/helper/propertysetcontainer.cxx @@ -0,0 +1,160 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include + +constexpr OUString WRONG_TYPE_EXCEPTION = u"Only XPropertSet allowed!"_ustr; + +using namespace cppu; +using namespace com::sun::star::uno; +using namespace com::sun::star::container; +using namespace com::sun::star::lang; +using namespace com::sun::star::beans; + +namespace framework +{ + +PropertySetContainer::PropertySetContainer() +{ +} + +PropertySetContainer::~PropertySetContainer() +{ +} + +// XInterface +void SAL_CALL PropertySetContainer::acquire() noexcept +{ + OWeakObject::acquire(); +} + +void SAL_CALL PropertySetContainer::release() noexcept +{ + OWeakObject::release(); +} + +Any SAL_CALL PropertySetContainer::queryInterface( const Type& rType ) +{ + Any a = ::cppu::queryInterface( + rType , + static_cast< XIndexContainer* >(this), + static_cast< XIndexReplace* >(this), + static_cast< XIndexAccess* >(this), + static_cast< XElementAccess* >(this) ); + + if( a.hasValue() ) + { + return a; + } + + return OWeakObject::queryInterface( rType ); +} + +// XIndexContainer +void SAL_CALL PropertySetContainer::insertByIndex( sal_Int32 Index, const css::uno::Any& Element ) +{ + std::unique_lock g(m_aMutex); + + sal_Int32 nSize = m_aPropertySetVector.size(); + + if ( nSize < Index ) + throw IndexOutOfBoundsException( OUString(), static_cast(this) ); + + Reference< XPropertySet > aPropertySetElement; + + if ( !(Element >>= aPropertySetElement) ) + { + throw IllegalArgumentException( + WRONG_TYPE_EXCEPTION, + static_cast(this), 2 ); + } + + if ( nSize == Index ) + m_aPropertySetVector.push_back( aPropertySetElement ); + else + { + PropertySetVector::iterator aIter = m_aPropertySetVector.begin(); + aIter += Index; + m_aPropertySetVector.insert( aIter, aPropertySetElement ); + } +} + +void SAL_CALL PropertySetContainer::removeByIndex( sal_Int32 nIndex ) +{ + std::unique_lock g(m_aMutex); + + if ( static_cast(m_aPropertySetVector.size()) <= nIndex ) + throw IndexOutOfBoundsException( OUString(), static_cast(this) ); + + m_aPropertySetVector.erase(m_aPropertySetVector.begin() + nIndex); +} + +// XIndexReplace +void SAL_CALL PropertySetContainer::replaceByIndex( sal_Int32 Index, const css::uno::Any& Element ) +{ + std::unique_lock g(m_aMutex); + + if ( static_cast(m_aPropertySetVector.size()) <= Index ) + throw IndexOutOfBoundsException( OUString(), static_cast(this) ); + + Reference< XPropertySet > aPropertySetElement; + + if ( !(Element >>= aPropertySetElement) ) + { + throw IllegalArgumentException( + WRONG_TYPE_EXCEPTION, + static_cast(this), 2 ); + } + + m_aPropertySetVector[ Index ] = aPropertySetElement; +} + +// XIndexAccess +sal_Int32 SAL_CALL PropertySetContainer::getCount() +{ + std::unique_lock g(m_aMutex); + + return m_aPropertySetVector.size(); +} + +Any SAL_CALL PropertySetContainer::getByIndex( sal_Int32 Index ) +{ + std::unique_lock g(m_aMutex); + + if ( static_cast(m_aPropertySetVector.size()) <= Index ) + throw IndexOutOfBoundsException( OUString(), static_cast(this) ); + + return Any(m_aPropertySetVector[ Index ]); +} + +// XElementAccess +sal_Bool SAL_CALL PropertySetContainer::hasElements() +{ + std::unique_lock g(m_aMutex); + + return !( m_aPropertySetVector.empty() ); +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/fwe/helper/titlehelper.cxx b/framework/source/fwe/helper/titlehelper.cxx new file mode 100644 index 0000000000..5ab03ab10e --- /dev/null +++ b/framework/source/fwe/helper/titlehelper.cxx @@ -0,0 +1,684 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +using namespace css; +using namespace css::uno; +using namespace css::frame; + +namespace framework{ + +TitleHelper::TitleHelper(css::uno::Reference< css::uno::XComponentContext > xContext, + const css::uno::Reference< css::uno::XInterface >& xOwner, + const css::uno::Reference< css::frame::XUntitledNumbers >& xNumbers) + : ::cppu::BaseMutex () + , m_xContext (std::move(xContext)) + , m_xOwner (xOwner) + , m_xUntitledNumbers(xNumbers) + , m_bExternalTitle (false) + , m_nLeasedNumber (css::frame::UntitledNumbersConst::INVALID_NUMBER) + , m_aListener (m_aMutex) +{ + if (css::uno::Reference xModel{ xOwner, css::uno::UNO_QUERY }) + { + impl_startListeningForModel (xModel); + } + else if (css::uno::Reference xController{ xOwner, + css::uno::UNO_QUERY }) + { + impl_startListeningForController (xController); + } + else if (css::uno::Reference xFrame{ xOwner, css::uno::UNO_QUERY }) + { + impl_startListeningForFrame (xFrame); + } +} + +TitleHelper::~TitleHelper() +{ +} + +OUString SAL_CALL TitleHelper::getTitle() +{ + // SYNCHRONIZED -> + osl::MutexGuard aLock(m_aMutex); + + // An external title will win always and disable all internal logic about + // creating/using a title value. + // Even an empty string will be accepted as valid title ! + if (m_bExternalTitle) + return m_sTitle; + + // Title seems to be up-to-date. Return it directly. + if (!m_sTitle.isEmpty()) + return m_sTitle; + + // Title seems to be unused till now ... do bootstrapping + impl_updateTitle (true); + + return m_sTitle; + // <- SYNCHRONIZED +} + +void SAL_CALL TitleHelper::setTitle(const OUString& sTitle) +{ + // SYNCHRONIZED -> + { + osl::MutexGuard aLock(m_aMutex); + + m_bExternalTitle = true; + m_sTitle = sTitle; + } + // <- SYNCHRONIZED + + impl_sendTitleChangedEvent (); +} + +void SAL_CALL TitleHelper::addTitleChangeListener(const css::uno::Reference< css::frame::XTitleChangeListener >& xListener) +{ + // container is threadsafe by himself + m_aListener.addInterface( cppu::UnoType::get(), xListener ); +} + +void SAL_CALL TitleHelper::removeTitleChangeListener(const css::uno::Reference< css::frame::XTitleChangeListener >& xListener) +{ + // container is threadsafe by himself + m_aListener.removeInterface( cppu::UnoType::get(), xListener ); +} + +void SAL_CALL TitleHelper::titleChanged(const css::frame::TitleChangedEvent& aEvent) +{ + css::uno::Reference< css::frame::XTitle > xSubTitle; + // SYNCHRONIZED -> + { + osl::MutexGuard aLock(m_aMutex); + + xSubTitle = m_xSubTitle; + } + // <- SYNCHRONIZED + + if (aEvent.Source != xSubTitle) + return; + + impl_updateTitle (); +} + +void SAL_CALL TitleHelper::documentEventOccured(const css::document::DocumentEvent& aEvent) +{ + if ( ! aEvent.EventName.equalsIgnoreAsciiCase("OnSaveAsDone") + && ! aEvent.EventName.equalsIgnoreAsciiCase("OnModeChanged") + && ! aEvent.EventName.equalsIgnoreAsciiCase("OnTitleChanged")) + return; + + css::uno::Reference< css::frame::XModel > xOwner; + // SYNCHRONIZED -> + { + osl::MutexGuard aLock(m_aMutex); + + xOwner.set(m_xOwner, css::uno::UNO_QUERY); + } + // <- SYNCHRONIZED + + if (aEvent.Source != xOwner + || ((aEvent.EventName.equalsIgnoreAsciiCase("OnModeChanged") + || aEvent.EventName.equalsIgnoreAsciiCase("OnTitleChanged")) + && !xOwner.is())) + { + return; + } + + impl_updateTitle (); +} + +void SAL_CALL TitleHelper::frameAction(const css::frame::FrameActionEvent& aEvent) +{ + css::uno::Reference< css::frame::XFrame > xOwner; + // SYNCHRONIZED -> + { + osl::MutexGuard aLock(m_aMutex); + + xOwner.set(m_xOwner, css::uno::UNO_QUERY); + } + // <- SYNCHRONIZED + + if (aEvent.Source != xOwner) + return; + + // we are interested on events only, which must trigger a title bar update + // because component was changed. + if ( + (aEvent.Action == css::frame::FrameAction_COMPONENT_ATTACHED ) || + (aEvent.Action == css::frame::FrameAction_COMPONENT_REATTACHED) || + (aEvent.Action == css::frame::FrameAction_COMPONENT_DETACHING ) + ) + { + impl_updateListeningForFrame (xOwner); + impl_updateTitle (); + } +} + +void SAL_CALL TitleHelper::disposing(const css::lang::EventObject& aEvent) +{ + css::uno::Reference< css::uno::XInterface > xOwner; + css::uno::Reference< css::frame::XUntitledNumbers > xNumbers; + ::sal_Int32 nLeasedNumber; + // SYNCHRONIZED -> + { + osl::MutexGuard aLock(m_aMutex); + + xOwner = m_xOwner; + xNumbers = m_xUntitledNumbers; + nLeasedNumber = m_nLeasedNumber; + } + // <- SYNCHRONIZED + + if ( ! xOwner.is ()) + return; + + css::uno::Reference< css::frame::XFrame > xFrame(xOwner, css::uno::UNO_QUERY); + if (xFrame.is()) + xFrame->removeFrameActionListener(this); + + if (xOwner != aEvent.Source) + return; + + if ( + (xNumbers.is () ) && + (nLeasedNumber != css::frame::UntitledNumbersConst::INVALID_NUMBER) + ) + xNumbers->releaseNumber (nLeasedNumber); + + // SYNCHRONIZED -> + { + osl::MutexGuard aLock(m_aMutex); + + m_xOwner.clear(); + m_sTitle.clear(); + m_nLeasedNumber = css::frame::UntitledNumbersConst::INVALID_NUMBER; + } + // <- SYNCHRONIZED +} + +void TitleHelper::impl_sendTitleChangedEvent () +{ + css::uno::Reference xOwner; + // SYNCHRONIZED -> + { + osl::MutexGuard aLock(m_aMutex); + + xOwner = m_xOwner; + } + // <- SYNCHRONIZED + + css::frame::TitleChangedEvent aEvent(xOwner, m_sTitle); + + if( ! aEvent.Source.is() ) + return; + + comphelper::OInterfaceContainerHelper2* pContainer = m_aListener.getContainer( cppu::UnoType::get()); + if ( ! pContainer) + return; + + comphelper::OInterfaceIteratorHelper2 pIt( *pContainer ); + while ( pIt.hasMoreElements() ) + { + try + { + static_cast(pIt.next())->titleChanged( aEvent ); + } + catch(const css::uno::Exception&) + { + pIt.remove(); + } + } +} + +void TitleHelper::impl_updateTitle (bool init) +{ + css::uno::Reference xOwner; + + // SYNCHRONIZED -> + { + osl::MutexGuard aLock(m_aMutex); + + xOwner = m_xOwner; + } + // <- SYNCHRONIZED + + if (css::uno::Reference xModel{ xOwner, css::uno::UNO_QUERY }) + { + impl_updateTitleForModel (xModel, init); + } + else if (css::uno::Reference xController{ xOwner, + css::uno::UNO_QUERY }) + { + impl_updateTitleForController (xController, init); + } + else if (css::uno::Reference xFrame{ xOwner, css::uno::UNO_QUERY }) + { + impl_updateTitleForFrame (xFrame, init); + } +} + +static OUString getURLFromModel(const css::uno::Reference< css::frame::XModel3 >& xModel) +{ + if (css::uno::Reference xURLProvider{ xModel, css::uno::UNO_QUERY }) + return xURLProvider->getLocation(); + return {}; +} + +void TitleHelper::impl_updateTitleForModel (const css::uno::Reference< css::frame::XModel3 >& xModel, bool init) +{ + css::uno::Reference< css::uno::XInterface > xOwner; + css::uno::Reference< css::frame::XUntitledNumbers > xNumbers; + ::sal_Int32 nLeasedNumber; + // SYNCHRONIZED -> + { + osl::MutexGuard aLock(m_aMutex); + + // external title won't be updated internally! + // It has to be set from outside new. + if (m_bExternalTitle) + return; + + xOwner = m_xOwner; + xNumbers = m_xUntitledNumbers; + nLeasedNumber = m_nLeasedNumber; + } + // <- SYNCHRONIZED + + if ( + ( ! xOwner.is ()) || + ( ! xNumbers.is ()) || + ( ! xModel.is ()) + ) + return; + + OUString sTitle; + + utl::MediaDescriptor aDescriptor( + xModel->getArgs2({ utl::MediaDescriptor::PROP_DOCUMENTTITLE, + utl::MediaDescriptor::PROP_SUGGESTEDSAVEASNAME })); + + if (const OUString sMediaTitle = aDescriptor.getUnpackedValueOrDefault( + utl::MediaDescriptor::PROP_DOCUMENTTITLE, OUString()); + !sMediaTitle.isEmpty()) + { + sTitle = sMediaTitle; + } + else if (const OUString sURL = getURLFromModel(xModel); !sURL.isEmpty()) + { + sTitle = impl_convertURL2Title(sURL); + if (nLeasedNumber != css::frame::UntitledNumbersConst::INVALID_NUMBER) + xNumbers->releaseNumber (nLeasedNumber); + nLeasedNumber = css::frame::UntitledNumbersConst::INVALID_NUMBER; + } + else if (const OUString sSuggestedSaveAsName = aDescriptor.getUnpackedValueOrDefault( + utl::MediaDescriptor::PROP_SUGGESTEDSAVEASNAME, OUString()); + !sSuggestedSaveAsName.isEmpty()) + { + // tdf#121537 Use suggested save as name for title if file has not yet been saved + sTitle = sSuggestedSaveAsName; + } + else + { + if (nLeasedNumber == css::frame::UntitledNumbersConst::INVALID_NUMBER) + nLeasedNumber = xNumbers->leaseNumber (xOwner); + + if (nLeasedNumber != css::frame::UntitledNumbersConst::INVALID_NUMBER) + sTitle = xNumbers->getUntitledPrefix() + OUString::number(nLeasedNumber); + else + sTitle = xNumbers->getUntitledPrefix() + "?"; + } + + bool bChanged; + // SYNCHRONIZED -> + { + osl::MutexGuard aLock(m_aMutex); + + // WORKAROUND: the notification is currently sent always, + // can be changed after shared mode is supported per UNO API + bChanged = !init; // && m_sTitle != sTitle + + m_sTitle = sTitle; + m_nLeasedNumber = nLeasedNumber; + } + // <- SYNCHRONIZED + + if (bChanged) + impl_sendTitleChangedEvent (); +} + +void TitleHelper::impl_updateTitleForController (const css::uno::Reference< css::frame::XController >& xController, bool init) +{ + css::uno::Reference< css::uno::XInterface > xOwner; + css::uno::Reference< css::frame::XUntitledNumbers > xNumbers; + ::sal_Int32 nLeasedNumber; + // SYNCHRONIZED -> + { + osl::MutexGuard aLock(m_aMutex); + + // external title won't be updated internally! + // It has to be set from outside new. + if (m_bExternalTitle) + return; + + xOwner = m_xOwner; + xNumbers = m_xUntitledNumbers; + nLeasedNumber = m_nLeasedNumber; + } + // <- SYNCHRONIZED + + if ( + ( ! xOwner.is ()) || + ( ! xNumbers.is ()) || + ( ! xController.is ()) + ) + return; + + OUStringBuffer sTitle(256); + + if (nLeasedNumber == css::frame::UntitledNumbersConst::INVALID_NUMBER) + nLeasedNumber = xNumbers->leaseNumber (xOwner); + + css::uno::Reference< css::frame::XTitle > xModelTitle(xController->getModel (), css::uno::UNO_QUERY); + css::uno::Reference< css::frame::XModel > xModel = xController->getModel (); + if (!xModelTitle.is ()) + xModelTitle.set(xController, css::uno::UNO_QUERY); + if (xModelTitle.is ()) + { + sTitle.append (xModelTitle->getTitle ()); + if ( nLeasedNumber > 1 ) + { + sTitle.append(" : " + OUString::number(nLeasedNumber)); + } + if (xModel.is ()) + { + INetURLObject aURL (xModel->getURL ()); + if (aURL.GetProtocol () != INetProtocol::File + && aURL.GetProtocol () != INetProtocol::NotValid) + { + OUString sRemoteText (FwkResId (STR_REMOTE_TITLE)); + sTitle.append (sRemoteText); + } + } + } + else + { + sTitle.append (xNumbers->getUntitledPrefix ()); + if ( nLeasedNumber > 1 ) + { + sTitle.append(nLeasedNumber ); + } + } + + bool bChanged; + // SYNCHRONIZED -> + { + osl::MutexGuard aLock(m_aMutex); + + OUString sNewTitle = sTitle.makeStringAndClear (); + bChanged = !init && m_sTitle != sNewTitle; + m_sTitle = sNewTitle; + m_nLeasedNumber = nLeasedNumber; + } + // <- SYNCHRONIZED + + if (bChanged) + impl_sendTitleChangedEvent (); +} + +void TitleHelper::impl_updateTitleForFrame (const css::uno::Reference< css::frame::XFrame >& xFrame, bool init) +{ + if ( ! xFrame.is ()) + return; + + // SYNCHRONIZED -> + { + osl::MutexGuard aLock(m_aMutex); + + // external title won't be updated internally! + // It has to be set from outside new. + if (m_bExternalTitle) + return; + } + // <- SYNCHRONIZED + + css::uno::Reference< css::uno::XInterface > xComponent = xFrame->getController (); + if ( ! xComponent.is ()) + xComponent = xFrame->getComponentWindow (); + + OUStringBuffer sTitle (256); + + impl_appendComponentTitle (sTitle, xComponent); +#ifndef MACOSX + if (!utl::ConfigManager::IsFuzzing()) + { + // fdo#70376: We want the window title to contain just the + // document name (from the above "component title"). + impl_appendProductName (sTitle); + impl_appendModuleName (sTitle); + impl_appendDebugVersion (sTitle); + } +#endif + impl_appendSafeMode (sTitle); + + bool bChanged; + // SYNCHRONIZED -> + { + osl::MutexGuard aLock(m_aMutex); + + OUString sNewTitle = sTitle.makeStringAndClear (); + bChanged = !init && m_sTitle != sNewTitle; + m_sTitle = sNewTitle; + } + // <- SYNCHRONIZED + + if (bChanged) + impl_sendTitleChangedEvent (); +} + +void TitleHelper::impl_appendComponentTitle ( OUStringBuffer& sTitle , + const css::uno::Reference< css::uno::XInterface >& xComponent) +{ + css::uno::Reference< css::frame::XTitle > xTitle(xComponent, css::uno::UNO_QUERY); + + // Note: Title has to be used (even if it's empty) if the right interface is supported. + if (xTitle.is ()) + sTitle.append (xTitle->getTitle ()); +} + +void TitleHelper::impl_appendProductName (OUStringBuffer& sTitle) +{ + OUString name(utl::ConfigManager::getProductName()); + if (!name.isEmpty()) + { + if (!sTitle.isEmpty()) + { + OUString separator (FwkResId (STR_EMDASH_SEPARATOR)); + sTitle.append(separator); + } + sTitle.append(name); + } +} + +void TitleHelper::impl_appendModuleName (OUStringBuffer& sTitle) +{ + css::uno::Reference< css::uno::XInterface > xOwner; + css::uno::Reference< css::uno::XComponentContext > xContext; + // SYNCHRONIZED -> + { + osl::MutexGuard aLock(m_aMutex); + + xOwner = m_xOwner; + xContext = m_xContext; + } + // <- SYNCHRONIZED + + try + { + css::uno::Reference< css::frame::XModuleManager2 > xModuleManager = + css::frame::ModuleManager::create(xContext); + + const OUString sID = xModuleManager->identify(xOwner); + ::comphelper::SequenceAsHashMap lProps = xModuleManager->getByName (sID); + const OUString sUIName = lProps.getUnpackedValueOrDefault (OFFICEFACTORY_PROPNAME_ASCII_UINAME, OUString()); + + // An UIname property is an optional value ! + // So please add it to the title in case it does really exists only. + if (!sUIName.isEmpty()) + { + sTitle.append (" " ); + sTitle.append (sUIName); + } + } + catch(const css::uno::Exception&) + {} +} + +#ifdef DBG_UTIL +void TitleHelper::impl_appendDebugVersion (OUStringBuffer& sTitle) +{ + OUString version(utl::ConfigManager::getProductVersion()); + sTitle.append(' '); + sTitle.append(version); + OUString sVersion = ::utl::Bootstrap::getBuildIdData("development"); + sTitle.append(" ["); + sTitle.append(sVersion); + sTitle.append("]"); +} +#else +void TitleHelper::impl_appendDebugVersion (OUStringBuffer&) +{ +} +#endif + +void TitleHelper::impl_appendSafeMode (OUStringBuffer& sTitle) +{ + if (Application::IsSafeModeEnabled()) + sTitle.append(FwkResId (STR_SAFEMODE_TITLE)); +} + +void TitleHelper::impl_startListeningForModel (const css::uno::Reference< css::frame::XModel >& xModel) +{ + css::uno::Reference< css::document::XDocumentEventBroadcaster > xBroadcaster(xModel, css::uno::UNO_QUERY); + if ( ! xBroadcaster.is ()) + return; + + xBroadcaster->addDocumentEventListener (static_cast< css::document::XDocumentEventListener* >(this)); +} + +void TitleHelper::impl_startListeningForController (const css::uno::Reference< css::frame::XController >& xController) +{ + xController->addEventListener (static_cast< css::lang::XEventListener* > (static_cast< css::frame::XFrameActionListener* > (this) ) ); + css::uno::Reference< css::frame::XTitle > xSubTitle(xController->getModel (), css::uno::UNO_QUERY); + impl_setSubTitle (xSubTitle); +} + +void TitleHelper::impl_startListeningForFrame (const css::uno::Reference< css::frame::XFrame >& xFrame) +{ + xFrame->addFrameActionListener(this ); + impl_updateListeningForFrame (xFrame); +} + +void TitleHelper::impl_updateListeningForFrame (const css::uno::Reference< css::frame::XFrame >& xFrame) +{ + css::uno::Reference< css::frame::XTitle > xSubTitle(xFrame->getController (), css::uno::UNO_QUERY); + impl_setSubTitle (xSubTitle); +} + +void TitleHelper::impl_setSubTitle (const css::uno::Reference< css::frame::XTitle >& xSubTitle) +{ + css::uno::Reference< css::frame::XTitle > xOldSubTitle; + // SYNCHRONIZED -> + { + osl::MutexGuard aLock(m_aMutex); + + // ignore duplicate calls. Makes outside using of this helper more easy :-) + xOldSubTitle = m_xSubTitle; + if (xOldSubTitle == xSubTitle) + return; + + m_xSubTitle = xSubTitle; + } + // <- SYNCHRONIZED + + css::uno::Reference< css::frame::XTitleChangeBroadcaster > xOldBroadcaster(xOldSubTitle , css::uno::UNO_QUERY ); + css::uno::Reference< css::frame::XTitleChangeBroadcaster > xNewBroadcaster(xSubTitle , css::uno::UNO_QUERY ); + css::uno::Reference< css::frame::XTitleChangeListener > xThis(this); + + if (xOldBroadcaster.is()) + xOldBroadcaster->removeTitleChangeListener (xThis); + + if (xNewBroadcaster.is()) + xNewBroadcaster->addTitleChangeListener (xThis); +} + +OUString TitleHelper::impl_convertURL2Title(std::u16string_view sURL) +{ + INetURLObject aURL (sURL); + OUString sTitle; + + if (aURL.GetProtocol() == INetProtocol::File) + { + if (aURL.HasMark()) + aURL = INetURLObject(aURL.GetURLNoMark()); + + sTitle = aURL.getName(INetURLObject::LAST_SEGMENT, true, INetURLObject::DecodeMechanism::WithCharset); + } + else + { + if (aURL.hasExtension()) + sTitle = aURL.getName(INetURLObject::LAST_SEGMENT, true, INetURLObject::DecodeMechanism::WithCharset); + + if ( sTitle.isEmpty() ) + sTitle = aURL.GetHostPort(INetURLObject::DecodeMechanism::WithCharset); + + if ( sTitle.isEmpty() ) + sTitle = aURL.GetURLNoPass(INetURLObject::DecodeMechanism::WithCharset); + } + + return sTitle; +} + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/fwe/helper/undomanagerhelper.cxx b/framework/source/fwe/helper/undomanagerhelper.cxx new file mode 100644 index 0000000000..3a2fdd6c06 --- /dev/null +++ b/framework/source/fwe/helper/undomanagerhelper.cxx @@ -0,0 +1,1094 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace framework +{ + + using ::com::sun::star::uno::Reference; + using ::com::sun::star::uno::XInterface; + using ::com::sun::star::uno::UNO_QUERY; + using ::com::sun::star::uno::Exception; + using ::com::sun::star::uno::RuntimeException; + using ::com::sun::star::uno::Any; + using ::com::sun::star::uno::Sequence; + using ::com::sun::star::document::XUndoManagerListener; + using ::com::sun::star::document::UndoManagerEvent; + using ::com::sun::star::document::EmptyUndoStackException; + using ::com::sun::star::document::UndoContextNotClosedException; + using ::com::sun::star::document::UndoFailedException; + using ::com::sun::star::util::NotLockedException; + using ::com::sun::star::lang::EventObject; + using ::com::sun::star::document::XUndoAction; + using ::com::sun::star::lang::XComponent; + using ::com::sun::star::document::XUndoManager; + using ::com::sun::star::util::InvalidStateException; + using ::com::sun::star::lang::IllegalArgumentException; + using ::com::sun::star::util::XModifyListener; + + //= UndoActionWrapper + + namespace { + + class UndoActionWrapper : public SfxUndoAction + { + public: + explicit UndoActionWrapper( + Reference< XUndoAction > const& i_undoAction + ); + virtual ~UndoActionWrapper() override; + + virtual OUString GetComment() const override; + virtual void Undo() override; + virtual void Redo() override; + virtual bool CanRepeat(SfxRepeatTarget&) const override; + + private: + const Reference< XUndoAction > m_xUndoAction; + }; + + } + + UndoActionWrapper::UndoActionWrapper( Reference< XUndoAction > const& i_undoAction ) + : m_xUndoAction( i_undoAction ) + { + ENSURE_OR_THROW( m_xUndoAction.is(), "illegal undo action" ); + } + + UndoActionWrapper::~UndoActionWrapper() + { + try + { + Reference< XComponent > xComponent( m_xUndoAction, UNO_QUERY ); + if ( xComponent.is() ) + xComponent->dispose(); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("fwk"); + } + } + + OUString UndoActionWrapper::GetComment() const + { + OUString sComment; + try + { + sComment = m_xUndoAction->getTitle(); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("fwk"); + } + return sComment; + } + + void UndoActionWrapper::Undo() + { + m_xUndoAction->undo(); + } + + void UndoActionWrapper::Redo() + { + m_xUndoAction->redo(); + } + + bool UndoActionWrapper::CanRepeat(SfxRepeatTarget&) const + { + return false; + } + + //= UndoManagerRequest + + namespace { + + class UndoManagerRequest : public ::comphelper::AnyEvent + { + public: + explicit UndoManagerRequest( ::std::function i_request ) + :m_request(std::move( i_request )) + { + m_finishCondition.reset(); + } + + void execute() + { + try + { + m_request(); + } + catch( const Exception& ) + { + m_caughtException = ::cppu::getCaughtException(); + } + m_finishCondition.set(); + } + + void wait() + { + m_finishCondition.wait(); + if ( m_caughtException.hasValue() ) + ::cppu::throwException( m_caughtException ); + } + + void cancel( const Reference< XInterface >& i_context ) + { + m_caughtException <<= RuntimeException( + "Concurrency error: an earlier operation on the stack failed.", + i_context + ); + m_finishCondition.set(); + } + + protected: + virtual ~UndoManagerRequest() override + { + } + + private: + ::std::function m_request; + Any m_caughtException; + ::osl::Condition m_finishCondition; + }; + + } + + //= UndoManagerHelper_Impl + + class UndoManagerHelper_Impl : public SfxUndoListener + { + private: + ::osl::Mutex m_aMutex; + /// Use different mutex for listeners to prevent ABBA deadlocks + std::mutex m_aListenerMutex; + std::mutex m_aQueueMutex; + bool m_bAPIActionRunning; + bool m_bProcessingEvents; + sal_Int32 m_nLockCount; + ::comphelper::OInterfaceContainerHelper4 m_aUndoListeners; + ::comphelper::OInterfaceContainerHelper4 m_aModifyListeners; + IUndoManagerImplementation& m_rUndoManagerImplementation; + ::std::stack< bool > m_aContextVisibilities; +#if OSL_DEBUG_LEVEL > 0 + bool m_bContextAPIFlagsEverPushed = {false}; + ::std::stack< bool > m_aContextAPIFlags; +#endif + ::std::queue< ::rtl::Reference< UndoManagerRequest > > + m_aEventQueue; + + public: + ::osl::Mutex& getMutex() { return m_aMutex; } + + public: + explicit UndoManagerHelper_Impl( IUndoManagerImplementation& i_undoManagerImpl ) + :m_bAPIActionRunning( false ) + ,m_bProcessingEvents( false ) + ,m_nLockCount( 0 ) + ,m_rUndoManagerImplementation( i_undoManagerImpl ) + { + getUndoManager().AddUndoListener( *this ); + } + + virtual ~UndoManagerHelper_Impl() + { + } + + SfxUndoManager& getUndoManager() const + { + return m_rUndoManagerImplementation.getImplUndoManager(); + } + + Reference< XUndoManager > getXUndoManager() const + { + return m_rUndoManagerImplementation.getThis(); + } + + // SfxUndoListener + virtual void actionUndone( const OUString& i_actionComment ) override; + virtual void actionRedone( const OUString& i_actionComment ) override; + virtual void undoActionAdded( const OUString& i_actionComment ) override; + virtual void cleared() override; + virtual void clearedRedo() override; + virtual void resetAll() override; + virtual void listActionEntered( const OUString& i_comment ) override; + virtual void listActionLeft( const OUString& i_comment ) override; + virtual void listActionCancelled() override; + + // public operations + void disposing(); + + void enterUndoContext( const OUString& i_title, const bool i_hidden, IMutexGuard& i_instanceLock ); + void leaveUndoContext( IMutexGuard& i_instanceLock ); + void addUndoAction( const Reference< XUndoAction >& i_action, IMutexGuard& i_instanceLock ); + void undo( IMutexGuard& i_instanceLock ); + void redo( IMutexGuard& i_instanceLock ); + void clear( IMutexGuard& i_instanceLock ); + void clearRedo( IMutexGuard& i_instanceLock ); + void reset( IMutexGuard& i_instanceLock ); + + void lock(); + void unlock(); + + void addUndoManagerListener( const Reference< XUndoManagerListener >& i_listener ) + { + std::unique_lock g(m_aListenerMutex); + m_aUndoListeners.addInterface( g, i_listener ); + } + + void removeUndoManagerListener( const Reference< XUndoManagerListener >& i_listener ) + { + std::unique_lock g(m_aListenerMutex); + m_aUndoListeners.removeInterface( g, i_listener ); + } + + void addModifyListener( const Reference< XModifyListener >& i_listener ) + { + std::unique_lock g(m_aListenerMutex); + m_aModifyListeners.addInterface( g, i_listener ); + } + + void removeModifyListener( const Reference< XModifyListener >& i_listener ) + { + std::unique_lock g(m_aListenerMutex); + m_aModifyListeners.removeInterface( g, i_listener ); + } + + UndoManagerEvent + buildEvent( OUString const& i_title ) const; + + void impl_notifyModified(); + void notify( OUString const& i_title, + void ( SAL_CALL XUndoManagerListener::*i_notificationMethod )( const UndoManagerEvent& ) + ); + void notify( void ( SAL_CALL XUndoManagerListener::*i_notificationMethod )( const EventObject& ) ); + + private: + /// adds a function to be called to the request processor's queue + void impl_processRequest(::std::function const& i_request, IMutexGuard& i_instanceLock); + + /// impl-versions of the XUndoManager API. + void impl_enterUndoContext( const OUString& i_title, const bool i_hidden ); + void impl_leaveUndoContext(); + void impl_addUndoAction( const Reference< XUndoAction >& i_action ); + void impl_doUndoRedo( IMutexGuard& i_externalLock, const bool i_undo ); + void impl_clear(); + void impl_clearRedo(); + void impl_reset(); + }; + + void UndoManagerHelper_Impl::disposing() + { + EventObject aEvent; + aEvent.Source = getXUndoManager(); + { + std::unique_lock g(m_aListenerMutex); + m_aUndoListeners.disposeAndClear( g, aEvent ); + m_aModifyListeners.disposeAndClear( g, aEvent ); + } + ::osl::MutexGuard aGuard( m_aMutex ); + + getUndoManager().RemoveUndoListener( *this ); + } + + UndoManagerEvent UndoManagerHelper_Impl::buildEvent( OUString const& i_title ) const + { + UndoManagerEvent aEvent; + aEvent.Source = getXUndoManager(); + aEvent.UndoActionTitle = i_title; + aEvent.UndoContextDepth = getUndoManager().GetListActionDepth(); + return aEvent; + } + + void UndoManagerHelper_Impl::impl_notifyModified() + { + const EventObject aEvent( getXUndoManager() ); + std::unique_lock g(m_aListenerMutex); + m_aModifyListeners.notifyEach( g, &XModifyListener::modified, aEvent ); + } + + void UndoManagerHelper_Impl::notify( OUString const& i_title, + void ( SAL_CALL XUndoManagerListener::*i_notificationMethod )( const UndoManagerEvent& ) ) + { + const UndoManagerEvent aEvent( buildEvent( i_title ) ); + + // TODO: this notification method here is used by UndoManagerHelper_Impl, to multiplex the notifications we + // receive from the SfxUndoManager. Those notifications are sent with a locked SolarMutex, which means + // we're doing the multiplexing here with a locked SM, too. Which is Bad (TM). + // Fixing this properly would require outsourcing all the notifications into an own thread - which might lead + // to problems of its own, since clients might expect synchronous notifications. + + { + std::unique_lock g(m_aListenerMutex); + m_aUndoListeners.notifyEach( g, i_notificationMethod, aEvent ); + } + impl_notifyModified(); + } + + void UndoManagerHelper_Impl::notify( void ( SAL_CALL XUndoManagerListener::*i_notificationMethod )( const EventObject& ) ) + { + const EventObject aEvent( getXUndoManager() ); + + // TODO: the same comment as in the other notify, regarding SM locking applies here ... + { + std::unique_lock g(m_aListenerMutex); + m_aUndoListeners.notifyEach( g, i_notificationMethod, aEvent ); + } + impl_notifyModified(); + } + + void UndoManagerHelper_Impl::enterUndoContext( const OUString& i_title, const bool i_hidden, IMutexGuard& i_instanceLock ) + { + impl_processRequest( + [this, &i_title, i_hidden] () { return this->impl_enterUndoContext(i_title, i_hidden); }, + i_instanceLock + ); + } + + void UndoManagerHelper_Impl::leaveUndoContext( IMutexGuard& i_instanceLock ) + { + impl_processRequest( + [this] () { return this->impl_leaveUndoContext(); }, + i_instanceLock + ); + } + + void UndoManagerHelper_Impl::addUndoAction( const Reference< XUndoAction >& i_action, IMutexGuard& i_instanceLock ) + { + if ( !i_action.is() ) + throw IllegalArgumentException( + "illegal undo action object", + getXUndoManager(), + 1 + ); + + impl_processRequest( + [this, &i_action] () { return this->impl_addUndoAction(i_action); }, + i_instanceLock + ); + } + + void UndoManagerHelper_Impl::clear( IMutexGuard& i_instanceLock ) + { + impl_processRequest( + [this] () { return this->impl_clear(); }, + i_instanceLock + ); + } + + void UndoManagerHelper_Impl::clearRedo( IMutexGuard& i_instanceLock ) + { + impl_processRequest( + [this] () { return this->impl_clearRedo(); }, + i_instanceLock + ); + } + + void UndoManagerHelper_Impl::reset( IMutexGuard& i_instanceLock ) + { + impl_processRequest( + [this] () { return this->impl_reset(); }, + i_instanceLock + ); + } + + void UndoManagerHelper_Impl::lock() + { + // SYNCHRONIZED ---> + ::osl::MutexGuard aGuard( getMutex() ); + + if ( ++m_nLockCount == 1 ) + { + SfxUndoManager& rUndoManager = getUndoManager(); + rUndoManager.EnableUndo( false ); + } + // <--- SYNCHRONIZED + } + + void UndoManagerHelper_Impl::unlock() + { + // SYNCHRONIZED ---> + ::osl::MutexGuard aGuard( getMutex() ); + + if ( m_nLockCount == 0 ) + throw NotLockedException( "Undo manager is not locked", getXUndoManager() ); + + if ( --m_nLockCount == 0 ) + { + SfxUndoManager& rUndoManager = getUndoManager(); + rUndoManager.EnableUndo( true ); + } + // <--- SYNCHRONIZED + } + + void UndoManagerHelper_Impl::impl_processRequest(::std::function const& i_request, IMutexGuard& i_instanceLock) + { + // create the request, and add it to our queue + ::rtl::Reference< UndoManagerRequest > pRequest( new UndoManagerRequest( i_request ) ); + { + std::unique_lock aQueueGuard( m_aQueueMutex ); + m_aEventQueue.push( pRequest ); + } + + i_instanceLock.clear(); + + if ( m_bProcessingEvents ) + { + // another thread is processing the event queue currently => it will also process the event which we just added + pRequest->wait(); + return; + } + + m_bProcessingEvents = true; + do + { + pRequest.clear(); + { + std::unique_lock aQueueGuard( m_aQueueMutex ); + if ( m_aEventQueue.empty() ) + { + // reset the flag before releasing the queue mutex, otherwise it's possible that another thread + // could add an event after we release the mutex, but before we reset the flag. If then this other + // thread checks the flag before be reset it, this thread's event would starve. + m_bProcessingEvents = false; + return; + } + pRequest = m_aEventQueue.front(); + m_aEventQueue.pop(); + } + try + { + pRequest->execute(); + pRequest->wait(); + } + catch( ... ) + { + { + // no chance to process further requests, if the current one failed + // => discard them + std::unique_lock aQueueGuard( m_aQueueMutex ); + while ( !m_aEventQueue.empty() ) + { + pRequest = m_aEventQueue.front(); + m_aEventQueue.pop(); + pRequest->cancel( getXUndoManager() ); + } + m_bProcessingEvents = false; + } + // re-throw the error + throw; + } + } + while ( true ); + } + + void UndoManagerHelper_Impl::impl_enterUndoContext( const OUString& i_title, const bool i_hidden ) + { + // SYNCHRONIZED ---> + ::osl::ClearableMutexGuard aGuard( m_aMutex ); + + SfxUndoManager& rUndoManager = getUndoManager(); + if ( !rUndoManager.IsUndoEnabled() ) + // ignore this request if the manager is locked + return; + + if ( i_hidden && ( rUndoManager.GetUndoActionCount() == 0 ) ) + throw EmptyUndoStackException( + "can't enter a hidden context without a previous Undo action", + m_rUndoManagerImplementation.getThis() + ); + + { + ::comphelper::FlagGuard aNotificationGuard( m_bAPIActionRunning ); + rUndoManager.EnterListAction( i_title, OUString(), 0, ViewShellId(-1) ); + } + + m_aContextVisibilities.push( i_hidden ); + + const UndoManagerEvent aEvent( buildEvent( i_title ) ); + aGuard.clear(); + // <--- SYNCHRONIZED + + { + std::unique_lock g(m_aListenerMutex); + m_aUndoListeners.notifyEach( g, i_hidden ? &XUndoManagerListener::enteredHiddenContext : &XUndoManagerListener::enteredContext, aEvent ); + } + impl_notifyModified(); + } + + void UndoManagerHelper_Impl::impl_leaveUndoContext() + { + // SYNCHRONIZED ---> + ::osl::ClearableMutexGuard aGuard( m_aMutex ); + + SfxUndoManager& rUndoManager = getUndoManager(); + if ( !rUndoManager.IsUndoEnabled() ) + // ignore this request if the manager is locked + return; + + if ( !rUndoManager.IsInListAction() ) + throw InvalidStateException( + "no active undo context", + getXUndoManager() + ); + + size_t nContextElements = 0; + + const bool isHiddenContext = m_aContextVisibilities.top(); + m_aContextVisibilities.pop(); + + const bool bHadRedoActions = ( rUndoManager.GetRedoActionCount( SfxUndoManager::TopLevel ) > 0 ); + { + ::comphelper::FlagGuard aNotificationGuard( m_bAPIActionRunning ); + if ( isHiddenContext ) + nContextElements = rUndoManager.LeaveAndMergeListAction(); + else + nContextElements = rUndoManager.LeaveListAction(); + } + const bool bHasRedoActions = ( rUndoManager.GetRedoActionCount( SfxUndoManager::TopLevel ) > 0 ); + + // prepare notification + void ( SAL_CALL XUndoManagerListener::*notificationMethod )( const UndoManagerEvent& ) = nullptr; + + UndoManagerEvent aContextEvent( buildEvent( OUString() ) ); + const EventObject aClearedEvent( getXUndoManager() ); + if ( nContextElements == 0 ) + { + notificationMethod = &XUndoManagerListener::cancelledContext; + } + else if ( isHiddenContext ) + { + notificationMethod = &XUndoManagerListener::leftHiddenContext; + } + else + { + aContextEvent.UndoActionTitle = rUndoManager.GetUndoActionComment(); + notificationMethod = &XUndoManagerListener::leftContext; + } + + aGuard.clear(); + // <--- SYNCHRONIZED + + { + std::unique_lock g(m_aListenerMutex); + if ( bHadRedoActions && !bHasRedoActions ) + m_aUndoListeners.notifyEach( g, &XUndoManagerListener::redoActionsCleared, aClearedEvent ); + m_aUndoListeners.notifyEach( g, notificationMethod, aContextEvent ); + } + impl_notifyModified(); + } + + void UndoManagerHelper_Impl::impl_doUndoRedo( IMutexGuard& i_externalLock, const bool i_undo ) + { + ::osl::Guard< ::framework::IMutex > aExternalGuard( i_externalLock.getGuardedMutex() ); + // note that this assumes that the mutex has been released in the thread which added the + // Undo/Redo request, so we can successfully acquire it + + // SYNCHRONIZED ---> + ::osl::ClearableMutexGuard aGuard( m_aMutex ); + + SfxUndoManager& rUndoManager = getUndoManager(); + if ( rUndoManager.IsInListAction() ) + throw UndoContextNotClosedException( OUString(), getXUndoManager() ); + + const size_t nElements = i_undo + ? rUndoManager.GetUndoActionCount( SfxUndoManager::TopLevel ) + : rUndoManager.GetRedoActionCount( SfxUndoManager::TopLevel ); + if ( nElements == 0 ) + throw EmptyUndoStackException("stack is empty", getXUndoManager() ); + + aGuard.clear(); + // <--- SYNCHRONIZED + + try + { + if ( i_undo ) + rUndoManager.Undo(); + else + rUndoManager.Redo(); + } + catch( const RuntimeException& ) { /* allowed to leave here */ throw; } + catch( const UndoFailedException& ) { /* allowed to leave here */ throw; } + catch( const Exception& ) + { + // not allowed to leave + const Any aError( ::cppu::getCaughtException() ); + throw UndoFailedException( OUString(), getXUndoManager(), aError ); + } + + // note that in opposite to all of the other methods, we do *not* have our mutex locked when calling + // into the SfxUndoManager implementation. This ensures that an actual XUndoAction::undo/redo is also + // called without our mutex being locked. + // As a consequence, we do not set m_bAPIActionRunning here. Instead, our actionUndone/actionRedone methods + // *always* multiplex the event to our XUndoManagerListeners, not only when m_bAPIActionRunning is FALSE (This + // again is different from all other SfxUndoListener methods). + // So, we do not need to do this notification here ourself. + } + + void UndoManagerHelper_Impl::impl_addUndoAction( const Reference< XUndoAction >& i_action ) + { + // SYNCHRONIZED ---> + ::osl::ClearableMutexGuard aGuard( m_aMutex ); + + SfxUndoManager& rUndoManager = getUndoManager(); + if ( !rUndoManager.IsUndoEnabled() ) + // ignore the request if the manager is locked + return; + + const UndoManagerEvent aEventAdd( buildEvent( i_action->getTitle() ) ); + const EventObject aEventClear( getXUndoManager() ); + + const bool bHadRedoActions = ( rUndoManager.GetRedoActionCount() > 0 ); + { + ::comphelper::FlagGuard aNotificationGuard( m_bAPIActionRunning ); + rUndoManager.AddUndoAction( std::make_unique( i_action ) ); + } + const bool bHasRedoActions = ( rUndoManager.GetRedoActionCount() > 0 ); + + aGuard.clear(); + // <--- SYNCHRONIZED + + { + std::unique_lock g(m_aListenerMutex); + m_aUndoListeners.notifyEach( g, &XUndoManagerListener::undoActionAdded, aEventAdd ); + if ( bHadRedoActions && !bHasRedoActions ) + m_aUndoListeners.notifyEach( g, &XUndoManagerListener::redoActionsCleared, aEventClear ); + } + impl_notifyModified(); + } + + void UndoManagerHelper_Impl::impl_clear() + { + EventObject aEvent; + { + SolarMutexGuard aGuard; + ::osl::MutexGuard aGuard2( m_aMutex ); + + SfxUndoManager& rUndoManager = getUndoManager(); + if ( rUndoManager.IsInListAction() ) + throw UndoContextNotClosedException( OUString(), getXUndoManager() ); + + { + ::comphelper::FlagGuard aNotificationGuard( m_bAPIActionRunning ); + rUndoManager.Clear(); + } + + aEvent = EventObject( getXUndoManager() ); + } + + { + std::unique_lock g(m_aListenerMutex); + m_aUndoListeners.notifyEach( g, &XUndoManagerListener::allActionsCleared, aEvent ); + } + impl_notifyModified(); + } + + void UndoManagerHelper_Impl::impl_clearRedo() + { + // SYNCHRONIZED ---> + ::osl::ClearableMutexGuard aGuard( m_aMutex ); + + SfxUndoManager& rUndoManager = getUndoManager(); + if ( rUndoManager.IsInListAction() ) + throw UndoContextNotClosedException( OUString(), getXUndoManager() ); + + { + ::comphelper::FlagGuard aNotificationGuard( m_bAPIActionRunning ); + rUndoManager.ClearRedo(); + } + + const EventObject aEvent( getXUndoManager() ); + aGuard.clear(); + // <--- SYNCHRONIZED + + { + std::unique_lock g(m_aListenerMutex); + m_aUndoListeners.notifyEach( g, &XUndoManagerListener::redoActionsCleared, aEvent ); + } + impl_notifyModified(); + } + + void UndoManagerHelper_Impl::impl_reset() + { + // SYNCHRONIZED ---> + ::osl::ClearableMutexGuard aGuard( m_aMutex ); + + SfxUndoManager& rUndoManager = getUndoManager(); + { + ::comphelper::FlagGuard aNotificationGuard( m_bAPIActionRunning ); + rUndoManager.Reset(); + } + + const EventObject aEvent( getXUndoManager() ); + aGuard.clear(); + // <--- SYNCHRONIZED + + { + std::unique_lock g(m_aListenerMutex); + m_aUndoListeners.notifyEach( g, &XUndoManagerListener::resetAll, aEvent ); + } + impl_notifyModified(); + } + + void UndoManagerHelper_Impl::actionUndone( const OUString& i_actionComment ) + { + UndoManagerEvent aEvent; + aEvent.Source = getXUndoManager(); + aEvent.UndoActionTitle = i_actionComment; + aEvent.UndoContextDepth = 0; // Undo can happen on level 0 only + { + std::unique_lock g(m_aListenerMutex); + m_aUndoListeners.notifyEach( g, &XUndoManagerListener::actionUndone, aEvent ); + } + impl_notifyModified(); + } + + void UndoManagerHelper_Impl::actionRedone( const OUString& i_actionComment ) + { + UndoManagerEvent aEvent; + aEvent.Source = getXUndoManager(); + aEvent.UndoActionTitle = i_actionComment; + aEvent.UndoContextDepth = 0; // Redo can happen on level 0 only + { + std::unique_lock g(m_aListenerMutex); + m_aUndoListeners.notifyEach( g, &XUndoManagerListener::actionRedone, aEvent ); + } + impl_notifyModified(); + } + + void UndoManagerHelper_Impl::undoActionAdded( const OUString& i_actionComment ) + { + if ( m_bAPIActionRunning ) + return; + + notify( i_actionComment, &XUndoManagerListener::undoActionAdded ); + } + + void UndoManagerHelper_Impl::cleared() + { + if ( m_bAPIActionRunning ) + return; + + notify( &XUndoManagerListener::allActionsCleared ); + } + + void UndoManagerHelper_Impl::clearedRedo() + { + if ( m_bAPIActionRunning ) + return; + + notify( &XUndoManagerListener::redoActionsCleared ); + } + + void UndoManagerHelper_Impl::resetAll() + { + if ( m_bAPIActionRunning ) + return; + + notify( &XUndoManagerListener::resetAll ); + } + + void UndoManagerHelper_Impl::listActionEntered( const OUString& i_comment ) + { +#if OSL_DEBUG_LEVEL > 0 + m_aContextAPIFlags.push( m_bAPIActionRunning ); + m_bContextAPIFlagsEverPushed = true; +#endif + + if ( m_bAPIActionRunning ) + return; + + notify( i_comment, &XUndoManagerListener::enteredContext ); + } + + void UndoManagerHelper_Impl::listActionLeft( const OUString& i_comment ) + { +#if OSL_DEBUG_LEVEL > 0 + // It may happen that the very first event listener is added during a + // list action after listActionEntered() was already called, e.g. Calc + // formula calculation event listener during the input of the very + // first formula. Instead of checking m_aContextAPIFlags for empty, + // still assert (on calling top()) other stack mismatches but ignore + // this one case. See tdf#142980 + if (m_bContextAPIFlagsEverPushed) + { + const bool bCurrentContextIsAPIContext = m_aContextAPIFlags.top(); + m_aContextAPIFlags.pop(); + OSL_ENSURE( bCurrentContextIsAPIContext == m_bAPIActionRunning, "UndoManagerHelper_Impl::listActionLeft: API and non-API contexts interwoven!" ); + } +#endif + + if ( m_bAPIActionRunning ) + return; + + notify( i_comment, &XUndoManagerListener::leftContext ); + } + + void UndoManagerHelper_Impl::listActionCancelled() + { +#if OSL_DEBUG_LEVEL > 0 + const bool bCurrentContextIsAPIContext = m_aContextAPIFlags.top(); + m_aContextAPIFlags.pop(); + OSL_ENSURE( bCurrentContextIsAPIContext == m_bAPIActionRunning, "UndoManagerHelper_Impl::listActionCancelled: API and non-API contexts interwoven!" ); +#endif + + if ( m_bAPIActionRunning ) + return; + + notify( OUString(), &XUndoManagerListener::cancelledContext ); + } + + //= UndoManagerHelper + + UndoManagerHelper::UndoManagerHelper( IUndoManagerImplementation& i_undoManagerImpl ) + :m_xImpl( new UndoManagerHelper_Impl( i_undoManagerImpl ) ) + { + } + + UndoManagerHelper::~UndoManagerHelper() + { + } + + void UndoManagerHelper::disposing() + { + m_xImpl->disposing(); + } + + void UndoManagerHelper::enterUndoContext( const OUString& i_title, IMutexGuard& i_instanceLock ) + { + m_xImpl->enterUndoContext( i_title, false, i_instanceLock ); + } + + void UndoManagerHelper::enterHiddenUndoContext( IMutexGuard& i_instanceLock ) + { + m_xImpl->enterUndoContext( OUString(), true, i_instanceLock ); + } + + void UndoManagerHelper::leaveUndoContext( IMutexGuard& i_instanceLock ) + { + m_xImpl->leaveUndoContext( i_instanceLock ); + } + + void UndoManagerHelper_Impl::undo( IMutexGuard& i_instanceLock ) + { + impl_processRequest( + [this, &i_instanceLock] () { return this->impl_doUndoRedo(i_instanceLock, true); }, + i_instanceLock + ); + } + + void UndoManagerHelper_Impl::redo( IMutexGuard& i_instanceLock ) + { + impl_processRequest( + [this, &i_instanceLock] () { return this->impl_doUndoRedo(i_instanceLock, false); }, + i_instanceLock + ); + } + + void UndoManagerHelper::addUndoAction( const Reference< XUndoAction >& i_action, IMutexGuard& i_instanceLock ) + { + m_xImpl->addUndoAction( i_action, i_instanceLock ); + } + + void UndoManagerHelper::undo( IMutexGuard& i_instanceLock ) + { + m_xImpl->undo( i_instanceLock ); + } + + void UndoManagerHelper::redo( IMutexGuard& i_instanceLock ) + { + m_xImpl->redo( i_instanceLock ); + } + + bool UndoManagerHelper::isUndoPossible() const + { + // SYNCHRONIZED ---> + ::osl::MutexGuard aGuard( m_xImpl->getMutex() ); + SfxUndoManager& rUndoManager = m_xImpl->getUndoManager(); + if ( rUndoManager.IsInListAction() ) + return false; + return rUndoManager.GetUndoActionCount( SfxUndoManager::TopLevel ) > 0; + // <--- SYNCHRONIZED + } + + bool UndoManagerHelper::isRedoPossible() const + { + // SYNCHRONIZED ---> + ::osl::MutexGuard aGuard( m_xImpl->getMutex() ); + const SfxUndoManager& rUndoManager = m_xImpl->getUndoManager(); + if ( rUndoManager.IsInListAction() ) + return false; + return rUndoManager.GetRedoActionCount( SfxUndoManager::TopLevel ) > 0; + // <--- SYNCHRONIZED + } + + namespace + { + + OUString lcl_getCurrentActionTitle( UndoManagerHelper_Impl& i_impl, const bool i_undo ) + { + // SYNCHRONIZED ---> + ::osl::MutexGuard aGuard( i_impl.getMutex() ); + + const SfxUndoManager& rUndoManager = i_impl.getUndoManager(); + const size_t nActionCount = i_undo + ? rUndoManager.GetUndoActionCount( SfxUndoManager::TopLevel ) + : rUndoManager.GetRedoActionCount( SfxUndoManager::TopLevel ); + if ( nActionCount == 0 ) + throw EmptyUndoStackException( + i_undo ? OUString( "no action on the undo stack" ) + : OUString( "no action on the redo stack" ), + i_impl.getXUndoManager() + ); + return i_undo + ? rUndoManager.GetUndoActionComment( 0, SfxUndoManager::TopLevel ) + : rUndoManager.GetRedoActionComment( 0, SfxUndoManager::TopLevel ); + // <--- SYNCHRONIZED + } + + Sequence< OUString > lcl_getAllActionTitles( UndoManagerHelper_Impl& i_impl, const bool i_undo ) + { + // SYNCHRONIZED ---> + ::osl::MutexGuard aGuard( i_impl.getMutex() ); + + const SfxUndoManager& rUndoManager = i_impl.getUndoManager(); + const size_t nCount = i_undo + ? rUndoManager.GetUndoActionCount( SfxUndoManager::TopLevel ) + : rUndoManager.GetRedoActionCount( SfxUndoManager::TopLevel ); + + Sequence< OUString > aTitles( nCount ); + auto aTitlesRange = asNonConstRange(aTitles); + for ( size_t i=0; i UndoManagerHelper::getAllUndoActionTitles() const + { + return lcl_getAllActionTitles( *m_xImpl, true ); + } + + Sequence< OUString > UndoManagerHelper::getAllRedoActionTitles() const + { + return lcl_getAllActionTitles( *m_xImpl, false ); + } + + void UndoManagerHelper::clear( IMutexGuard& i_instanceLock ) + { + m_xImpl->clear( i_instanceLock ); + } + + void UndoManagerHelper::clearRedo( IMutexGuard& i_instanceLock ) + { + m_xImpl->clearRedo( i_instanceLock ); + } + + void UndoManagerHelper::reset( IMutexGuard& i_instanceLock ) + { + m_xImpl->reset( i_instanceLock ); + } + + void UndoManagerHelper::lock() + { + m_xImpl->lock(); + } + + void UndoManagerHelper::unlock() + { + m_xImpl->unlock(); + } + + bool UndoManagerHelper::isLocked() + { + // SYNCHRONIZED ---> + ::osl::MutexGuard aGuard( m_xImpl->getMutex() ); + + SfxUndoManager& rUndoManager = m_xImpl->getUndoManager(); + return !rUndoManager.IsUndoEnabled(); + // <--- SYNCHRONIZED + } + + void UndoManagerHelper::addUndoManagerListener( const Reference< XUndoManagerListener >& i_listener ) + { + if ( i_listener.is() ) + m_xImpl->addUndoManagerListener( i_listener ); + } + + void UndoManagerHelper::removeUndoManagerListener( const Reference< XUndoManagerListener >& i_listener ) + { + if ( i_listener.is() ) + m_xImpl->removeUndoManagerListener( i_listener ); + } + + void UndoManagerHelper::addModifyListener( const Reference< XModifyListener >& i_listener ) + { + if ( i_listener.is() ) + m_xImpl->addModifyListener( i_listener ); + } + + void UndoManagerHelper::removeModifyListener( const Reference< XModifyListener >& i_listener ) + { + if ( i_listener.is() ) + m_xImpl->removeModifyListener( i_listener ); + } + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/fwe/xml/menuconfiguration.cxx b/framework/source/fwe/xml/menuconfiguration.cxx new file mode 100644 index 0000000000..d2131c8a8d --- /dev/null +++ b/framework/source/fwe/xml/menuconfiguration.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 + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::xml::sax; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::io; + +namespace framework +{ + +MenuConfiguration::MenuConfiguration( css::uno::Reference< css::uno::XComponentContext > xContext ) +: m_xContext(std::move( xContext )) +{ +} + +MenuConfiguration::~MenuConfiguration() +{ +} + +Reference< XIndexAccess > MenuConfiguration::CreateMenuBarConfigurationFromXML( + Reference< XInputStream > const & rInputStream ) +{ + Reference< XParser > xParser = Parser::create( m_xContext ); + + // connect stream to input stream to the parser + InputSource aInputSource; + + aInputSource.aInputStream = rInputStream; + + // create menu bar + Reference< XIndexContainer > xItemContainer( new RootItemContainer() ); + + // create namespace filter and set menudocument handler inside to support xml namespaces + + Reference< XDocumentHandler > xDocHandler( new OReadMenuDocumentHandler( xItemContainer )); + + Reference< XDocumentHandler > xFilter( new SaxNamespaceFilter( xDocHandler )); + + // connect parser and filter + xParser->setDocumentHandler( xFilter ); + + try + { + xParser->parseStream( aInputSource ); + return xItemContainer; + } + catch ( const RuntimeException& e ) + { + css::uno::Any anyEx = cppu::getCaughtException(); + throw WrappedTargetException( e.Message, Reference< XInterface >(), anyEx ); + } + catch( const SAXException& e ) + { + css::uno::Any anyEx = cppu::getCaughtException(); + SAXException aWrappedSAXException; + + if ( !( e.WrappedException >>= aWrappedSAXException )) + throw WrappedTargetException( e.Message, Reference< XInterface >(), anyEx ); + else + throw WrappedTargetException( aWrappedSAXException.Message, Reference< XInterface >(), e.WrappedException ); + } + catch( const css::io::IOException& e ) + { + css::uno::Any anyEx = cppu::getCaughtException(); + throw WrappedTargetException( e.Message, Reference< XInterface >(), anyEx ); + } +} + +void MenuConfiguration::StoreMenuBarConfigurationToXML( + Reference< XIndexAccess > const & rMenuBarConfiguration, + Reference< XOutputStream > const & rOutputStream, bool bIsMenuBar ) +{ + Reference< XWriter > xWriter = Writer::create(m_xContext); + xWriter->setOutputStream( rOutputStream ); + + try + { + OWriteMenuDocumentHandler aWriteMenuDocumentHandler( rMenuBarConfiguration, xWriter, bIsMenuBar ); + aWriteMenuDocumentHandler.WriteMenuDocument(); + } + catch ( const RuntimeException& e ) + { + css::uno::Any anyEx = cppu::getCaughtException(); + throw WrappedTargetException( e.Message, Reference< XInterface >(), anyEx ); + } + catch ( const SAXException& e ) + { + css::uno::Any anyEx = cppu::getCaughtException(); + throw WrappedTargetException( e.Message, Reference< XInterface >(), anyEx ); + } + catch ( const css::io::IOException& e ) + { + css::uno::Any anyEx = cppu::getCaughtException(); + throw WrappedTargetException( e.Message, Reference< XInterface >(), anyEx ); + } +} + +void* MenuAttributes::CreateAttribute(const OUString& rFrame, const OUString& rImageIdStr) +{ + MenuAttributes* pAttributes = new MenuAttributes(rFrame, rImageIdStr); + pAttributes->acquire(); + return pAttributes; +} + +void* MenuAttributes::CreateAttribute(const css::uno::WeakReference& rDispatchProvider) +{ + MenuAttributes* pAttributes = new MenuAttributes(rDispatchProvider); + pAttributes->acquire(); + return pAttributes; +} + +void MenuAttributes::ReleaseAttribute(void* nAttributePtr) +{ + if (!nAttributePtr) + return; + MenuAttributes* pAttributes = static_cast(nAttributePtr); + pAttributes->release(); +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/fwe/xml/menudocumenthandler.cxx b/framework/source/fwe/xml/menudocumenthandler.cxx new file mode 100644 index 0000000000..96e4b2324b --- /dev/null +++ b/framework/source/fwe/xml/menudocumenthandler.cxx @@ -0,0 +1,885 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#ifdef ATTRIBUTE_HELPID +#undef ATTRIBUTE_HELPID +#endif + +constexpr OUStringLiteral XMLNS_MENU = u"http://openoffice.org/2001/menu"; + +constexpr OUString ELEMENT_MENUBAR = u"http://openoffice.org/2001/menu^menubar"_ustr; +constexpr OUString ELEMENT_MENU = u"http://openoffice.org/2001/menu^menu"_ustr; +constexpr OUString ELEMENT_MENUPOPUP = u"http://openoffice.org/2001/menu^menupopup"_ustr; +constexpr OUString ELEMENT_MENUITEM = u"http://openoffice.org/2001/menu^menuitem"_ustr; +constexpr OUString ELEMENT_MENUSEPARATOR = u"http://openoffice.org/2001/menu^menuseparator"_ustr; + +constexpr OUStringLiteral ELEMENT_NS_MENUBAR = u"menu:menubar"; +constexpr OUString ELEMENT_NS_MENU = u"menu:menu"_ustr; +constexpr OUString ELEMENT_NS_MENUPOPUP = u"menu:menupopup"_ustr; +constexpr OUString ELEMENT_NS_MENUITEM = u"menu:menuitem"_ustr; +constexpr OUString ELEMENT_NS_MENUSEPARATOR = u"menu:menuseparator"_ustr; + +constexpr OUString ATTRIBUTE_ID = u"http://openoffice.org/2001/menu^id"_ustr; +constexpr OUString ATTRIBUTE_LABEL = u"http://openoffice.org/2001/menu^label"_ustr; +constexpr OUString ATTRIBUTE_HELPID = u"http://openoffice.org/2001/menu^helpid"_ustr; +constexpr OUString ATTRIBUTE_STYLE = u"http://openoffice.org/2001/menu^style"_ustr; + +constexpr OUString ATTRIBUTE_NS_ID = u"menu:id"_ustr; +constexpr OUString ATTRIBUTE_NS_LABEL = u"menu:label"_ustr; +constexpr OUStringLiteral ATTRIBUTE_NS_HELPID = u"menu:helpid"; +constexpr OUStringLiteral ATTRIBUTE_NS_STYLE = u"menu:style"; + +constexpr OUStringLiteral ATTRIBUTE_XMLNS_MENU = u"xmlns:menu"; + +constexpr OUStringLiteral MENUBAR_DOCTYPE = u""; + +#define ATTRIBUTE_ITEMSTYLE_TEXT "text" +#define ATTRIBUTE_ITEMSTYLE_IMAGE "image" +#define ATTRIBUTE_ITEMSTYLE_RADIO "radio" + +// Property names of a menu/menu item ItemDescriptor +constexpr OUString ITEM_DESCRIPTOR_COMMANDURL = u"CommandURL"_ustr; +constexpr OUString ITEM_DESCRIPTOR_HELPURL = u"HelpURL"_ustr; +constexpr OUString ITEM_DESCRIPTOR_CONTAINER = u"ItemDescriptorContainer"_ustr; +constexpr OUString ITEM_DESCRIPTOR_LABEL = u"Label"_ustr; +constexpr OUString ITEM_DESCRIPTOR_TYPE = u"Type"_ustr; +constexpr OUString ITEM_DESCRIPTOR_STYLE = u"Style"_ustr; + +// using namespaces + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::xml::sax; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::ui; + +namespace framework +{ + +namespace { + +struct MenuStyleItem +{ + sal_Int16 nBit; + const char* attrName; +}; + +} + +const MenuStyleItem MenuItemStyles[ ] = { + { css::ui::ItemStyle::ICON, ATTRIBUTE_ITEMSTYLE_IMAGE }, + { css::ui::ItemStyle::TEXT, ATTRIBUTE_ITEMSTYLE_TEXT }, + { css::ui::ItemStyle::RADIO_CHECK, ATTRIBUTE_ITEMSTYLE_RADIO } +}; + +sal_Int32 const nMenuStyleItemEntries = SAL_N_ELEMENTS(MenuItemStyles); + +static void ExtractMenuParameters( const Sequence< PropertyValue >& rProp, + OUString& rCommandURL, + OUString& rLabel, + OUString& rHelpURL, + Reference< XIndexAccess >& rSubMenu, + sal_Int16& rType, + sal_Int16& rStyle ) +{ + for ( const PropertyValue& p : rProp ) + { + if ( p.Name == ITEM_DESCRIPTOR_COMMANDURL ) + { + p.Value >>= rCommandURL; + } + else if ( p.Name == ITEM_DESCRIPTOR_HELPURL ) + { + p.Value >>= rHelpURL; + } + else if ( p.Name == ITEM_DESCRIPTOR_CONTAINER ) + { + p.Value >>= rSubMenu; + } + else if ( p.Name == ITEM_DESCRIPTOR_LABEL ) + { + p.Value >>= rLabel; + } + else if ( p.Name == ITEM_DESCRIPTOR_TYPE ) + { + p.Value >>= rType; + } + else if ( p.Name == ITEM_DESCRIPTOR_STYLE ) + { + p.Value >>= rStyle; + } + } +} + +// Base class implementation + +ReadMenuDocumentHandlerBase::ReadMenuDocumentHandlerBase() : + m_aType( ITEM_DESCRIPTOR_TYPE ), + m_aLabel( ITEM_DESCRIPTOR_LABEL ), + m_aContainer( ITEM_DESCRIPTOR_CONTAINER ), + m_aHelpURL( ITEM_DESCRIPTOR_HELPURL ), + m_aCommandURL( ITEM_DESCRIPTOR_COMMANDURL ), + m_aStyle( ITEM_DESCRIPTOR_STYLE ) +{ +} + +ReadMenuDocumentHandlerBase::~ReadMenuDocumentHandlerBase() +{ +} + +void SAL_CALL ReadMenuDocumentHandlerBase::ignorableWhitespace( + const OUString& ) +{ +} + +void SAL_CALL ReadMenuDocumentHandlerBase::processingInstruction( + const OUString& /*aTarget*/, const OUString& /*aData*/ ) +{ +} + +void SAL_CALL ReadMenuDocumentHandlerBase::setDocumentLocator( + const Reference< XLocator > &xLocator) +{ + m_xLocator = xLocator; +} + +OUString ReadMenuDocumentHandlerBase::getErrorLineString() +{ + if ( m_xLocator.is() ) + return "Line: " + OUString::number( m_xLocator->getLineNumber() ) + " - "; + else + return OUString(); +} + +void ReadMenuDocumentHandlerBase::initPropertyCommon( + Sequence< PropertyValue > &rProps, const OUString &rCommandURL, + const OUString &rHelpId, const OUString &rLabel, sal_Int16 nItemStyleBits ) +{ + auto pProps = rProps.getArray(); + + pProps[0].Name = m_aCommandURL; + pProps[1].Name = m_aHelpURL; + pProps[2].Name = m_aContainer; + pProps[3].Name = m_aLabel; + pProps[4].Name = m_aStyle; + pProps[5].Name = m_aType; + + // Common values + pProps[0].Value <<= rCommandURL; + pProps[1].Value <<= rHelpId; + pProps[2].Value <<= Reference< XIndexContainer >(); + pProps[3].Value <<= rLabel; + pProps[4].Value <<= nItemStyleBits; + pProps[5].Value <<= css::ui::ItemType::DEFAULT; +} + +OReadMenuDocumentHandler::OReadMenuDocumentHandler( + const Reference< XIndexContainer >& rMenuBarContainer ) +: m_nElementDepth( 0 ), + m_eReaderMode( ReaderMode::None ), + m_xMenuBarContainer( rMenuBarContainer ), + m_xContainerFactory( rMenuBarContainer, UNO_QUERY ) +{ +} + +OReadMenuDocumentHandler::~OReadMenuDocumentHandler() +{ +} + +void SAL_CALL OReadMenuDocumentHandler::startDocument() +{ +} + +void SAL_CALL OReadMenuDocumentHandler::endDocument() +{ + if ( m_nElementDepth > 0 ) + { + OUString aErrorMessage = getErrorLineString() + + "A closing element is missing!"; + throw SAXException( aErrorMessage, Reference< XInterface >(), Any() ); + } +} + +void SAL_CALL OReadMenuDocumentHandler::startElement( + const OUString& aName, const Reference< XAttributeList > &xAttrList ) +{ + if ( m_eReaderMode != ReaderMode::None ) + { + ++m_nElementDepth; + m_xReader->startElement( aName, xAttrList ); + } + else + { + if ( aName == ELEMENT_MENUBAR ) + { + m_eReaderMode = ReaderMode::MenuBar; + m_xReader.set( new OReadMenuBarHandler( m_xMenuBarContainer, m_xContainerFactory )); + } + else if ( aName == ELEMENT_MENUPOPUP ) + { + m_eReaderMode = ReaderMode::MenuPopup; + m_xReader.set( new OReadMenuPopupHandler( m_xMenuBarContainer, m_xContainerFactory )); + } + ++m_nElementDepth; + m_xReader->startDocument(); + } +} + +void SAL_CALL OReadMenuDocumentHandler::characters(const OUString&) +{ +} + +void SAL_CALL OReadMenuDocumentHandler::endElement( const OUString& aName ) +{ + if ( m_eReaderMode == ReaderMode::None ) + return; + + --m_nElementDepth; + m_xReader->endElement( aName ); + if ( 0 != m_nElementDepth ) + return; + + m_xReader->endDocument(); + m_xReader.clear(); + if ( m_eReaderMode == ReaderMode::MenuBar && aName != ELEMENT_MENUBAR ) + { + OUString aErrorMessage = getErrorLineString() + + "closing element menubar expected!"; + throw SAXException( aErrorMessage, Reference< XInterface >(), Any() ); + } + else if ( m_eReaderMode == ReaderMode::MenuPopup && aName != ELEMENT_MENUPOPUP ) + { + OUString aErrorMessage = getErrorLineString() + + "closing element menupopup expected!"; + throw SAXException( aErrorMessage, Reference< XInterface >(), Any() ); + } + m_eReaderMode = ReaderMode::None; +} + +OReadMenuBarHandler::OReadMenuBarHandler( + const Reference< XIndexContainer >& rMenuBarContainer, + const Reference< XSingleComponentFactory >& rFactory ) +: m_nElementDepth( 0 ), + m_bMenuMode( false ), + m_xMenuBarContainer( rMenuBarContainer ), + m_xContainerFactory( rFactory ) +{ +} + +OReadMenuBarHandler::~OReadMenuBarHandler() +{ +} + +void SAL_CALL OReadMenuBarHandler::startDocument() +{ +} + +void SAL_CALL OReadMenuBarHandler::endDocument() +{ +} + +void SAL_CALL OReadMenuBarHandler::startElement( + const OUString& rName, const Reference< XAttributeList > &xAttrList ) +{ + if ( m_bMenuMode ) + { + ++m_nElementDepth; + m_xReader->startElement( rName, xAttrList ); + } + else if ( rName == ELEMENT_MENU ) + { + ++m_nElementDepth; + + OUString aHelpId; + OUString aCommandId; + OUString aLabel; + sal_Int16 nItemBits(0); + + m_bMenuMode = true; + + // Container must be factory to create sub container + Reference< XComponentContext > xComponentContext( + comphelper::getProcessComponentContext() ); + + Reference< XIndexContainer > xSubItemContainer; + if ( m_xContainerFactory.is() ) + xSubItemContainer.set( m_xContainerFactory->createInstanceWithContext( xComponentContext ), UNO_QUERY ); + + if ( xSubItemContainer.is() ) + { + // read attributes for menu + for ( sal_Int16 i=0; i< xAttrList->getLength(); i++ ) + { + OUString aName = xAttrList->getNameByIndex( i ); + const OUString aValue = xAttrList->getValueByIndex( i ); + if ( aName == ATTRIBUTE_ID ) + aCommandId = aValue; + else if ( aName == ATTRIBUTE_LABEL ) + aLabel = aValue; + else if ( aName == ATTRIBUTE_HELPID ) + aHelpId = aValue; + else if ( aName == ATTRIBUTE_STYLE ) + { + sal_Int32 nIndex = 0; + do + { + OUString aToken = aValue.getToken( 0, '+', nIndex ); + if ( !aToken.isEmpty() ) + { + if ( aToken == ATTRIBUTE_ITEMSTYLE_TEXT ) + nItemBits |= css::ui::ItemStyle::TEXT; + else if ( aToken == ATTRIBUTE_ITEMSTYLE_IMAGE ) + nItemBits |= css::ui::ItemStyle::ICON; + else if ( aToken == ATTRIBUTE_ITEMSTYLE_RADIO ) + nItemBits |= css::ui::ItemStyle::RADIO_CHECK; + } + } + while ( nIndex >= 0 ); + } + } + + if ( !aCommandId.isEmpty() ) + { + Sequence< PropertyValue > aSubMenuProp( 6 ); + initPropertyCommon( aSubMenuProp, aCommandId, aHelpId, aLabel, nItemBits ); + aSubMenuProp.getArray()[2].Value <<= xSubItemContainer; + + m_xMenuBarContainer->insertByIndex( m_xMenuBarContainer->getCount(), Any( aSubMenuProp ) ); + } + else + { + OUString aErrorMessage = getErrorLineString() + + "attribute id for element menu required!"; + throw SAXException( aErrorMessage, Reference< XInterface >(), Any() ); + } + + m_xReader.set( new OReadMenuHandler( xSubItemContainer, m_xContainerFactory )); + m_xReader->startDocument(); + } + } + else + { + OUString aErrorMessage = getErrorLineString() + + "element menu expected!"; + throw SAXException( aErrorMessage, Reference< XInterface >(), Any() ); + } +} + +void SAL_CALL OReadMenuBarHandler::characters(const OUString&) +{ +} + +void OReadMenuBarHandler::endElement( const OUString& aName ) +{ + if ( !m_bMenuMode ) + return; + + --m_nElementDepth; + if ( 0 == m_nElementDepth ) + { + m_xReader->endDocument(); + m_xReader.clear(); + m_bMenuMode = false; + if ( aName != ELEMENT_MENU ) + { + OUString aErrorMessage = getErrorLineString() + + "closing element menu expected!"; + throw SAXException( aErrorMessage, Reference< XInterface >(), Any() ); + } + } + else + m_xReader->endElement( aName ); +} + +OReadMenuHandler::OReadMenuHandler( + const Reference< XIndexContainer >& rMenuContainer, + const Reference< XSingleComponentFactory >& rFactory ) : + m_nElementDepth( 0 ), + m_bMenuPopupMode( false ), + m_xMenuContainer( rMenuContainer ), + m_xContainerFactory( rFactory ) +{ +} + +OReadMenuHandler::~OReadMenuHandler() +{ +} + +void SAL_CALL OReadMenuHandler::startDocument() +{ +} + +void SAL_CALL OReadMenuHandler::endDocument() +{ +} + +void SAL_CALL OReadMenuHandler::startElement( + const OUString& aName, const Reference< XAttributeList > &xAttrList ) +{ + if ( m_bMenuPopupMode ) + { + ++m_nElementDepth; + m_xReader->startElement( aName, xAttrList ); + } + else if ( aName == ELEMENT_MENUPOPUP ) + { + ++m_nElementDepth; + m_bMenuPopupMode = true; + m_xReader.set( new OReadMenuPopupHandler( m_xMenuContainer, m_xContainerFactory )); + m_xReader->startDocument(); + } + else + { + OUString aErrorMessage = getErrorLineString() + + "unknown element found!"; + throw SAXException( aErrorMessage, Reference< XInterface >(), Any() ); + } +} + +void SAL_CALL OReadMenuHandler::characters(const OUString&) +{ +} + +void SAL_CALL OReadMenuHandler::endElement( const OUString& aName ) +{ + if ( !m_bMenuPopupMode ) + return; + + --m_nElementDepth; + if ( 0 == m_nElementDepth ) + { + m_xReader->endDocument(); + m_xReader.clear(); + m_bMenuPopupMode = false; + if ( aName != ELEMENT_MENUPOPUP ) + { + OUString aErrorMessage = getErrorLineString() + + "closing element menupopup expected!"; + throw SAXException( aErrorMessage, Reference< XInterface >(), Any() ); + } + } + else + m_xReader->endElement( aName ); +} + +OReadMenuPopupHandler::OReadMenuPopupHandler( + const Reference< XIndexContainer >& rMenuContainer, + const Reference< XSingleComponentFactory >& rFactory ) : + m_nElementDepth( 0 ), + m_bMenuMode( false ), + m_xMenuContainer( rMenuContainer ), + m_xContainerFactory( rFactory ), + m_xComponentContext( comphelper::getProcessComponentContext() ), + m_nNextElementExpected( ELEM_CLOSE_NONE ) +{ +} + +OReadMenuPopupHandler::~OReadMenuPopupHandler() +{ +} + +void SAL_CALL OReadMenuPopupHandler::startDocument() +{ +} + +void SAL_CALL OReadMenuPopupHandler::endDocument() +{ +} + +void SAL_CALL OReadMenuPopupHandler::startElement( + const OUString& rName, const Reference< XAttributeList > &xAttrList ) +{ + ++m_nElementDepth; + + if ( m_bMenuMode ) + m_xReader->startElement( rName, xAttrList ); + else if ( rName == ELEMENT_MENU ) + { + OUString aHelpId; + OUString aCommandId; + OUString aLabel; + sal_Int16 nItemBits(0); + + m_bMenuMode = true; + + // Container must be factory to create sub container + Reference< XIndexContainer > xSubItemContainer; + if ( m_xContainerFactory.is() ) + xSubItemContainer.set( m_xContainerFactory->createInstanceWithContext( m_xComponentContext ), UNO_QUERY ); + + // read attributes for menu + for ( sal_Int16 i=0; i< xAttrList->getLength(); i++ ) + { + OUString aName = xAttrList->getNameByIndex( i ); + const OUString aValue = xAttrList->getValueByIndex( i ); + if ( aName == ATTRIBUTE_ID ) + aCommandId = aValue; + else if ( aName == ATTRIBUTE_LABEL ) + aLabel = aValue; + else if ( aName == ATTRIBUTE_HELPID ) + aHelpId = aValue; + else if ( aName == ATTRIBUTE_STYLE ) + { + sal_Int32 nIndex = 0; + do + { + OUString aToken = aValue.getToken( 0, '+', nIndex ); + if ( !aToken.isEmpty() ) + { + if ( aToken == ATTRIBUTE_ITEMSTYLE_TEXT ) + nItemBits |= css::ui::ItemStyle::TEXT; + else if ( aToken == ATTRIBUTE_ITEMSTYLE_IMAGE ) + nItemBits |= css::ui::ItemStyle::ICON; + else if ( aToken == ATTRIBUTE_ITEMSTYLE_RADIO ) + nItemBits |= css::ui::ItemStyle::RADIO_CHECK; + } + } + while ( nIndex >= 0 ); + } + + } + + if ( !aCommandId.isEmpty() ) + { + Sequence< PropertyValue > aSubMenuProp( 6 ); + initPropertyCommon( aSubMenuProp, aCommandId, aHelpId, aLabel, nItemBits ); + aSubMenuProp.getArray()[2].Value <<= xSubItemContainer; + + m_xMenuContainer->insertByIndex( m_xMenuContainer->getCount(), Any( aSubMenuProp ) ); + } + else + { + OUString aErrorMessage = getErrorLineString() + + "attribute id for element menu required!"; + throw SAXException( aErrorMessage, Reference< XInterface >(), Any() ); + } + + m_xReader.set( new OReadMenuHandler( xSubItemContainer, m_xContainerFactory )); + m_xReader->startDocument(); + } + else if ( rName == ELEMENT_MENUITEM ) + { + OUString aHelpId; + OUString aCommandId; + OUString aLabel; + sal_Int16 nItemBits(0); + // read attributes for menu item + for ( sal_Int16 i=0; i< xAttrList->getLength(); i++ ) + { + OUString aName = xAttrList->getNameByIndex( i ); + const OUString aValue = xAttrList->getValueByIndex( i ); + if ( aName == ATTRIBUTE_ID ) + aCommandId = aValue; + else if ( aName == ATTRIBUTE_LABEL ) + aLabel = aValue; + else if ( aName == ATTRIBUTE_HELPID ) + aHelpId = aValue; + else if ( aName == ATTRIBUTE_STYLE ) + { + sal_Int32 nIndex = 0; + do + { + OUString aToken = aValue.getToken( 0, '+', nIndex ); + if ( !aToken.isEmpty() ) + { + if ( aToken == ATTRIBUTE_ITEMSTYLE_TEXT ) + nItemBits |= css::ui::ItemStyle::TEXT; + else if ( aToken == ATTRIBUTE_ITEMSTYLE_IMAGE ) + nItemBits |= css::ui::ItemStyle::ICON; + else if ( aToken == ATTRIBUTE_ITEMSTYLE_RADIO ) + nItemBits |= css::ui::ItemStyle::RADIO_CHECK; + } + } + while ( nIndex >= 0 ); + } + + } + + if ( !aCommandId.isEmpty() ) + { + Sequence< PropertyValue > aMenuItem( 6 ); + initPropertyCommon( aMenuItem, aCommandId, aHelpId, aLabel, nItemBits ); + aMenuItem.getArray()[2].Value <<= Reference< XIndexContainer >(); + + m_xMenuContainer->insertByIndex( m_xMenuContainer->getCount(), Any( aMenuItem ) ); + } + + m_nNextElementExpected = ELEM_CLOSE_MENUITEM; + } + else if ( rName == ELEMENT_MENUSEPARATOR ) + { + Sequence< PropertyValue > aMenuSeparator{ comphelper::makePropertyValue( + ITEM_DESCRIPTOR_TYPE, css::ui::ItemType::SEPARATOR_LINE) }; + + m_xMenuContainer->insertByIndex( m_xMenuContainer->getCount(), Any( aMenuSeparator ) ); + + m_nNextElementExpected = ELEM_CLOSE_MENUSEPARATOR; + } + else + { + OUString aErrorMessage = getErrorLineString() + + "unknown element found!"; + throw SAXException( aErrorMessage, Reference< XInterface >(), Any() ); + } +} + +void SAL_CALL OReadMenuPopupHandler::characters(const OUString&) +{ +} + +void SAL_CALL OReadMenuPopupHandler::endElement( const OUString& aName ) +{ + --m_nElementDepth; + if ( m_bMenuMode ) + { + if ( 0 == m_nElementDepth ) + { + m_xReader->endDocument(); + m_xReader.clear(); + m_bMenuMode = false; + if ( aName != ELEMENT_MENU ) + { + OUString aErrorMessage = getErrorLineString() + + "closing element menu expected!"; + throw SAXException( aErrorMessage, Reference< XInterface >(), Any() ); + } + } + else + m_xReader->endElement( aName ); + } + else + { + if ( m_nNextElementExpected == ELEM_CLOSE_MENUITEM ) + { + if ( aName != ELEMENT_MENUITEM ) + { + OUString aErrorMessage = getErrorLineString() + + "closing element menuitem expected!"; + throw SAXException( aErrorMessage, Reference< XInterface >(), Any() ); + } + } + else if ( m_nNextElementExpected == ELEM_CLOSE_MENUSEPARATOR ) + { + if ( aName != ELEMENT_MENUSEPARATOR ) + { + OUString aErrorMessage = getErrorLineString() + + "closing element menuseparator expected!"; + throw SAXException( aErrorMessage, Reference< XInterface >(), Any() ); + } + } + + m_nNextElementExpected = ELEM_CLOSE_NONE; + } +} + +// --------------------------------- Write XML --------------------------------- + +OWriteMenuDocumentHandler::OWriteMenuDocumentHandler( + const Reference< XIndexAccess >& rMenuBarContainer, + const Reference< XDocumentHandler >& rDocumentHandler, + bool bIsMenuBar ) : + m_xMenuBarContainer( rMenuBarContainer ), + m_xWriteDocumentHandler( rDocumentHandler ), + m_bIsMenuBar( bIsMenuBar ) +{ + m_xEmptyList = new ::comphelper::AttributeList; +} + +OWriteMenuDocumentHandler::~OWriteMenuDocumentHandler() +{ +} + +void OWriteMenuDocumentHandler::WriteMenuDocument() +{ + rtl::Reference<::comphelper::AttributeList> pList = new ::comphelper::AttributeList; + + m_xWriteDocumentHandler->startDocument(); + + // write DOCTYPE line! + Reference< XExtendedDocumentHandler > xExtendedDocHandler( m_xWriteDocumentHandler, UNO_QUERY ); + if ( m_bIsMenuBar /*FIXME*/ && xExtendedDocHandler.is() ) + { + xExtendedDocHandler->unknown( MENUBAR_DOCTYPE ); + m_xWriteDocumentHandler->ignorableWhitespace( OUString() ); + } + + pList->AddAttribute( ATTRIBUTE_XMLNS_MENU, + XMLNS_MENU ); + + if ( m_bIsMenuBar ) //FIXME + pList->AddAttribute( ATTRIBUTE_NS_ID, + "menubar" ); + + OUString aRootElement; + if ( m_bIsMenuBar ) + aRootElement = ELEMENT_NS_MENUBAR; + else + aRootElement = ELEMENT_NS_MENUPOPUP; + m_xWriteDocumentHandler->startElement( aRootElement, pList ); + m_xWriteDocumentHandler->ignorableWhitespace( OUString() ); + + WriteMenu( m_xMenuBarContainer ); + + m_xWriteDocumentHandler->ignorableWhitespace( OUString() ); + m_xWriteDocumentHandler->endElement( aRootElement ); + m_xWriteDocumentHandler->ignorableWhitespace( OUString() ); + m_xWriteDocumentHandler->endDocument(); +} + +void OWriteMenuDocumentHandler::WriteMenu( const Reference< XIndexAccess >& rMenuContainer ) +{ + sal_Int32 nItemCount = rMenuContainer->getCount(); + bool bSeparator = false; + Any aAny; + + for ( sal_Int32 nItemPos = 0; nItemPos < nItemCount; nItemPos++ ) + { + Sequence< PropertyValue > aProps; + aAny = rMenuContainer->getByIndex( nItemPos ); + if ( aAny >>= aProps ) + { + OUString aCommandURL; + OUString aLabel; + OUString aHelpURL; + sal_Int16 nType( css::ui::ItemType::DEFAULT ); + sal_Int16 nItemBits( 0 ); + Reference< XIndexAccess > xSubMenu; + + ExtractMenuParameters( aProps, aCommandURL, aLabel, aHelpURL, xSubMenu, nType, nItemBits ); + if ( xSubMenu.is() ) + { + if ( !aCommandURL.isEmpty() ) + { + rtl::Reference<::comphelper::AttributeList> pListMenu = new ::comphelper::AttributeList; + + pListMenu->AddAttribute( ATTRIBUTE_NS_ID, + aCommandURL ); + + if ( !aLabel.isEmpty() ) + pListMenu->AddAttribute( ATTRIBUTE_NS_LABEL, + aLabel ); + + m_xWriteDocumentHandler->ignorableWhitespace( OUString() ); + m_xWriteDocumentHandler->startElement( ELEMENT_NS_MENU, pListMenu ); + m_xWriteDocumentHandler->ignorableWhitespace( OUString() ); + m_xWriteDocumentHandler->startElement( ELEMENT_NS_MENUPOPUP, m_xEmptyList ); + m_xWriteDocumentHandler->ignorableWhitespace( OUString() ); + + WriteMenu( xSubMenu ); + + m_xWriteDocumentHandler->ignorableWhitespace( OUString() ); + m_xWriteDocumentHandler->endElement( ELEMENT_NS_MENUPOPUP ); + m_xWriteDocumentHandler->ignorableWhitespace( OUString() ); + m_xWriteDocumentHandler->endElement( ELEMENT_NS_MENU ); + m_xWriteDocumentHandler->ignorableWhitespace( OUString() ); + bSeparator = false; + } + } + else + { + if ( nType == css::ui::ItemType::DEFAULT ) + { + if ( !aCommandURL.isEmpty() ) + { + bSeparator = false; + WriteMenuItem( aCommandURL, aLabel, aHelpURL, nItemBits ); + } + } + else if ( !bSeparator ) + { + // Don't write two separators together + WriteMenuSeparator(); + bSeparator = true; + } + } + } + } +} + +void OWriteMenuDocumentHandler::WriteMenuItem( const OUString& aCommandURL, const OUString& aLabel, const OUString& aHelpURL, sal_Int16 nStyle ) +{ + rtl::Reference<::comphelper::AttributeList> pList = new ::comphelper::AttributeList; + + pList->AddAttribute( ATTRIBUTE_NS_ID, + aCommandURL ); + + if ( !aHelpURL.isEmpty() ) + { + pList->AddAttribute( ATTRIBUTE_NS_HELPID, + aHelpURL ); + } + + if ( !aLabel.isEmpty() ) + { + pList->AddAttribute( ATTRIBUTE_NS_LABEL, + aLabel ); + } + if ( nStyle > 0 ) + { + OUStringBuffer aValue; + const MenuStyleItem* pStyle = MenuItemStyles; + + for ( sal_Int32 nIndex = 0; nIndex < nMenuStyleItemEntries; ++nIndex, ++pStyle ) + { + if ( nStyle & pStyle->nBit ) + { + if ( !aValue.isEmpty() ) + aValue.append("+"); + aValue.appendAscii( pStyle->attrName ); + } + } + pList->AddAttribute( ATTRIBUTE_NS_STYLE, + aValue.makeStringAndClear() ); + } + + m_xWriteDocumentHandler->ignorableWhitespace( OUString() ); + m_xWriteDocumentHandler->startElement( ELEMENT_NS_MENUITEM, pList ); + m_xWriteDocumentHandler->ignorableWhitespace( OUString() ); + m_xWriteDocumentHandler->endElement( ELEMENT_NS_MENUITEM ); +} + +void OWriteMenuDocumentHandler::WriteMenuSeparator() +{ + m_xWriteDocumentHandler->ignorableWhitespace( OUString() ); + m_xWriteDocumentHandler->startElement( ELEMENT_NS_MENUSEPARATOR, m_xEmptyList ); + m_xWriteDocumentHandler->ignorableWhitespace( OUString() ); + m_xWriteDocumentHandler->endElement( ELEMENT_NS_MENUSEPARATOR ); +} + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/fwe/xml/saxnamespacefilter.cxx b/framework/source/fwe/xml/saxnamespacefilter.cxx new file mode 100644 index 0000000000..b5349eb488 --- /dev/null +++ b/framework/source/fwe/xml/saxnamespacefilter.cxx @@ -0,0 +1,163 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include + +#include + +#include + +#include +#include + +using namespace ::com::sun::star::xml::sax; +using namespace ::com::sun::star::uno; + +namespace framework{ + +SaxNamespaceFilter::SaxNamespaceFilter( Reference< XDocumentHandler > const & rSax1DocumentHandler ) : + xDocumentHandler( rSax1DocumentHandler ) +{ +} + +SaxNamespaceFilter::~SaxNamespaceFilter() +{ +} + +// XDocumentHandler +void SAL_CALL SaxNamespaceFilter::startDocument() +{ +} + +void SAL_CALL SaxNamespaceFilter::endDocument() +{ +} + +void SAL_CALL SaxNamespaceFilter::startElement( + const OUString& rName, const Reference< XAttributeList > &xAttribs ) +{ + XMLNamespaces aXMLNamespaces; + if ( !m_aNamespaceStack.empty() ) + aXMLNamespaces = m_aNamespaceStack.top(); + + rtl::Reference<::comphelper::AttributeList> pNewList = new ::comphelper::AttributeList(); + + // examine all namespaces for this level + ::std::vector< sal_Int16 > aAttributeIndexes; + { + for ( sal_Int16 i=0; i< xAttribs->getLength(); i++ ) + { + OUString aName = xAttribs->getNameByIndex( i ); + if ( aName.startsWith( "xmlns" ) ) + aXMLNamespaces.addNamespace( aName, xAttribs->getValueByIndex( i )); + else + aAttributeIndexes.push_back( i ); + } + } + + // current namespaces for this level + m_aNamespaceStack.push( aXMLNamespaces ); + + try + { + // apply namespaces to all remaining attributes + for (auto const& attributeIndex : aAttributeIndexes) + { + OUString aAttributeName = xAttribs->getNameByIndex(attributeIndex); + OUString aValue = xAttribs->getValueByIndex(attributeIndex); + OUString aNamespaceAttributeName = aXMLNamespaces.applyNSToAttributeName( aAttributeName ); + pNewList->AddAttribute(aNamespaceAttributeName, aValue); + } + } + catch ( SAXException& e ) + { + e.Message = getErrorLineString() + e.Message; + throw; + } + + OUString aNamespaceElementName; + + try + { + aNamespaceElementName = aXMLNamespaces.applyNSToElementName( rName ); + } + catch ( SAXException& e ) + { + e.Message = getErrorLineString() + e.Message; + throw; + } + + xDocumentHandler->startElement( aNamespaceElementName, pNewList ); +} + +void SAL_CALL SaxNamespaceFilter::endElement(const OUString& aName) +{ + XMLNamespaces& aXMLNamespaces = m_aNamespaceStack.top(); + OUString aNamespaceElementName; + + try + { + aNamespaceElementName = aXMLNamespaces.applyNSToElementName( aName ); + } + catch ( SAXException& e ) + { + e.Message = getErrorLineString() + e.Message; + throw; + } + + xDocumentHandler->endElement( aNamespaceElementName ); + m_aNamespaceStack.pop(); +} + +void SAL_CALL SaxNamespaceFilter::characters(const OUString& aChars) +{ + xDocumentHandler->characters( aChars ); +} + +void SAL_CALL SaxNamespaceFilter::ignorableWhitespace(const OUString& aWhitespaces) +{ + xDocumentHandler->ignorableWhitespace( aWhitespaces ); +} + +void SAL_CALL SaxNamespaceFilter::processingInstruction( + const OUString& aTarget, const OUString& aData) +{ + xDocumentHandler->processingInstruction( aTarget, aData ); +} + +void SAL_CALL SaxNamespaceFilter::setDocumentLocator( + const Reference< XLocator > &xLocator) +{ + m_xLocator = xLocator; + xDocumentHandler->setDocumentLocator( xLocator ); +} + +OUString SaxNamespaceFilter::getErrorLineString() +{ + if ( m_xLocator.is() ) + return "Line: " + OUString::number( m_xLocator->getLineNumber() ) + " - "; + else + return OUString(); +} + +} // namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/fwe/xml/statusbarconfiguration.cxx b/framework/source/fwe/xml/statusbarconfiguration.cxx new file mode 100644 index 0000000000..ce974237b2 --- /dev/null +++ b/framework/source/fwe/xml/statusbarconfiguration.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 +#include +#include + +#include +#include +#include +#include +#include + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::xml::sax; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::io; +using namespace ::com::sun::star::container; + +namespace framework +{ + +bool StatusBarConfiguration::LoadStatusBar( + const Reference< XComponentContext >& rxContext, + const Reference< XInputStream >& xInputStream, + const Reference< XIndexContainer >& rStatusbarConfiguration ) +{ + Reference< XParser > xParser = Parser::create(rxContext); + + // connect stream to input stream to the parser + InputSource aInputSource; + aInputSource.aInputStream = xInputStream; + + // create namespace filter and set menudocument handler inside to support xml namespaces + Reference< XDocumentHandler > xDocHandler( new OReadStatusBarDocumentHandler( rStatusbarConfiguration )); + Reference< XDocumentHandler > xFilter( new SaxNamespaceFilter( xDocHandler )); + + // connect parser and filter + xParser->setDocumentHandler( xFilter ); + + try + { + xParser->parseStream( aInputSource ); + return true; + } + catch ( const RuntimeException& ) + { + return false; + } + catch( const SAXException& ) + { + return false; + } + catch( const css::io::IOException& ) + { + return false; + } +} + +bool StatusBarConfiguration::StoreStatusBar( + const Reference< XComponentContext >& rxContext, + const Reference< XOutputStream >& xOutputStream, + const Reference< XIndexAccess >& rStatusbarConfiguration ) +{ + Reference< XWriter > xWriter = Writer::create( rxContext ); + xWriter->setOutputStream( xOutputStream ); + + try + { + OWriteStatusBarDocumentHandler aWriteStatusBarDocumentHandler( rStatusbarConfiguration, xWriter ); + aWriteStatusBarDocumentHandler.WriteStatusBarDocument(); + return true; + } + catch ( const RuntimeException& ) + { + return false; + } + catch ( const SAXException& ) + { + return false; + } + catch ( const css::io::IOException& ) + { + return false; + } +} +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/fwe/xml/statusbardocumenthandler.cxx b/framework/source/fwe/xml/statusbardocumenthandler.cxx new file mode 100644 index 0000000000..cafd3258ec --- /dev/null +++ b/framework/source/fwe/xml/statusbardocumenthandler.cxx @@ -0,0 +1,616 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::xml::sax; +using namespace ::com::sun::star::ui; +using namespace ::com::sun::star::container; + +constexpr OUString XMLNS_STATUSBAR = u"http://openoffice.org/2001/statusbar"_ustr; +constexpr OUString XMLNS_XLINK = u"http://www.w3.org/1999/xlink"_ustr; +constexpr OUStringLiteral XMLNS_STATUSBAR_PREFIX = u"statusbar:"; +constexpr OUStringLiteral XMLNS_XLINK_PREFIX = u"xlink:"; + +constexpr OUString XMLNS_FILTER_SEPARATOR = u"^"_ustr; + +#define ELEMENT_STATUSBAR "statusbar" +#define ELEMENT_STATUSBARITEM "statusbaritem" + +#define ATTRIBUTE_ALIGN "align" +#define ATTRIBUTE_STYLE "style" +#define ATTRIBUTE_URL "href" +#define ATTRIBUTE_WIDTH "width" +#define ATTRIBUTE_OFFSET "offset" +#define ATTRIBUTE_AUTOSIZE "autosize" +#define ATTRIBUTE_OWNERDRAW "ownerdraw" +#define ATTRIBUTE_HELPURL "helpid" +#define ATTRIBUTE_MANDATORY "mandatory" + +constexpr OUString ELEMENT_NS_STATUSBAR = u"statusbar:statusbar"_ustr; +constexpr OUString ELEMENT_NS_STATUSBARITEM = u"statusbar:statusbaritem"_ustr; + +constexpr OUStringLiteral ATTRIBUTE_XMLNS_STATUSBAR = u"xmlns:statusbar"; +constexpr OUStringLiteral ATTRIBUTE_XMLNS_XLINK = u"xmlns:xlink"; + +constexpr OUString ATTRIBUTE_BOOLEAN_TRUE = u"true"_ustr; +constexpr OUString ATTRIBUTE_BOOLEAN_FALSE = u"false"_ustr; + +constexpr OUString ATTRIBUTE_ALIGN_LEFT = u"left"_ustr; +constexpr OUString ATTRIBUTE_ALIGN_RIGHT = u"right"_ustr; +constexpr OUString ATTRIBUTE_ALIGN_CENTER = u"center"_ustr; + +constexpr OUStringLiteral ATTRIBUTE_STYLE_IN = u"in"; +constexpr OUString ATTRIBUTE_STYLE_OUT = u"out"_ustr; +constexpr OUString ATTRIBUTE_STYLE_FLAT = u"flat"_ustr; + +constexpr OUStringLiteral STATUSBAR_DOCTYPE = u""; + +namespace framework +{ + +// Property names of a menu/menu item ItemDescriptor +constexpr OUString ITEM_DESCRIPTOR_COMMANDURL = u"CommandURL"_ustr; +constexpr OUString ITEM_DESCRIPTOR_HELPURL = u"HelpURL"_ustr; +constexpr OUString ITEM_DESCRIPTOR_OFFSET = u"Offset"_ustr; +constexpr OUString ITEM_DESCRIPTOR_STYLE = u"Style"_ustr; +constexpr OUString ITEM_DESCRIPTOR_WIDTH = u"Width"_ustr; +constexpr OUString ITEM_DESCRIPTOR_TYPE = u"Type"_ustr; + +static void ExtractStatusbarItemParameters( + const Sequence< PropertyValue >& rProp, + OUString& rCommandURL, + OUString& rHelpURL, + sal_Int16& rOffset, + sal_Int16& rStyle, + sal_Int16& rWidth ) +{ + for ( const PropertyValue& rEntry : rProp ) + { + if ( rEntry.Name == ITEM_DESCRIPTOR_COMMANDURL ) + { + rEntry.Value >>= rCommandURL; + } + else if ( rEntry.Name == ITEM_DESCRIPTOR_HELPURL ) + { + rEntry.Value >>= rHelpURL; + } + else if ( rEntry.Name == ITEM_DESCRIPTOR_OFFSET ) + { + rEntry.Value >>= rOffset; + } + else if ( rEntry.Name == ITEM_DESCRIPTOR_STYLE ) + { + rEntry.Value >>= rStyle; + } + else if ( rEntry.Name == ITEM_DESCRIPTOR_WIDTH ) + { + rEntry.Value >>= rWidth; + } + } +} + +namespace { + +struct StatusBarEntryProperty +{ + OReadStatusBarDocumentHandler::StatusBar_XML_Namespace nNamespace; + char aEntryName[20]; +}; + +} + +StatusBarEntryProperty const StatusBarEntries[OReadStatusBarDocumentHandler::SB_XML_ENTRY_COUNT] = +{ + { OReadStatusBarDocumentHandler::SB_NS_STATUSBAR, ELEMENT_STATUSBAR }, + { OReadStatusBarDocumentHandler::SB_NS_STATUSBAR, ELEMENT_STATUSBARITEM }, + { OReadStatusBarDocumentHandler::SB_NS_XLINK, ATTRIBUTE_URL }, + { OReadStatusBarDocumentHandler::SB_NS_STATUSBAR, ATTRIBUTE_ALIGN }, + { OReadStatusBarDocumentHandler::SB_NS_STATUSBAR, ATTRIBUTE_STYLE }, + { OReadStatusBarDocumentHandler::SB_NS_STATUSBAR, ATTRIBUTE_AUTOSIZE }, + { OReadStatusBarDocumentHandler::SB_NS_STATUSBAR, ATTRIBUTE_OWNERDRAW }, + { OReadStatusBarDocumentHandler::SB_NS_STATUSBAR, ATTRIBUTE_WIDTH }, + { OReadStatusBarDocumentHandler::SB_NS_STATUSBAR, ATTRIBUTE_OFFSET }, + { OReadStatusBarDocumentHandler::SB_NS_STATUSBAR, ATTRIBUTE_HELPURL }, + { OReadStatusBarDocumentHandler::SB_NS_STATUSBAR, ATTRIBUTE_MANDATORY } +}; + +OReadStatusBarDocumentHandler::OReadStatusBarDocumentHandler( + const Reference< XIndexContainer >& rStatusBarItems ) : + m_aStatusBarItems( rStatusBarItems ) +{ + // create hash map + for ( int i = 0; i < SB_XML_ENTRY_COUNT; i++ ) + { + if ( StatusBarEntries[i].nNamespace == SB_NS_STATUSBAR ) + { + OUString temp = XMLNS_STATUSBAR + XMLNS_FILTER_SEPARATOR + + OUString::createFromAscii( StatusBarEntries[i].aEntryName ); + m_aStatusBarMap.emplace( temp, static_cast(i) ); + } + else + { + OUString temp = XMLNS_XLINK + XMLNS_FILTER_SEPARATOR + + OUString::createFromAscii( StatusBarEntries[i].aEntryName ); + m_aStatusBarMap.emplace( temp, static_cast(i) ); + } + } + + m_bStatusBarStartFound = false; + m_bStatusBarItemStartFound = false; +} + +OReadStatusBarDocumentHandler::~OReadStatusBarDocumentHandler() +{ +} + +// XDocumentHandler +void SAL_CALL OReadStatusBarDocumentHandler::startDocument() +{ +} + +void SAL_CALL OReadStatusBarDocumentHandler::endDocument() +{ + if ( m_bStatusBarStartFound ) + { + OUString aErrorMessage = getErrorLineString() + "No matching start or end element 'statusbar' found!"; + throw SAXException( aErrorMessage, Reference< XInterface >(), Any() ); + } +} + +void SAL_CALL OReadStatusBarDocumentHandler::startElement( + const OUString& aName, const Reference< XAttributeList > &xAttribs ) +{ + StatusBarHashMap::const_iterator pStatusBarEntry = m_aStatusBarMap.find( aName ); + if ( pStatusBarEntry == m_aStatusBarMap.end() ) + return; + + switch ( pStatusBarEntry->second ) + { + case SB_ELEMENT_STATUSBAR: + { + if ( m_bStatusBarStartFound ) + { + OUString aErrorMessage = getErrorLineString() + "Element 'statusbar:statusbar' cannot be embedded into 'statusbar:statusbar'!"; + throw SAXException( aErrorMessage, Reference< XInterface >(), Any() ); + } + + m_bStatusBarStartFound = true; + } + break; + + case SB_ELEMENT_STATUSBARITEM: + { + if ( !m_bStatusBarStartFound ) + { + OUString aErrorMessage = getErrorLineString() + "Element 'statusbar:statusbaritem' must be embedded into element 'statusbar:statusbar'!"; + throw SAXException( aErrorMessage, Reference< XInterface >(), Any() ); + } + + if ( m_bStatusBarItemStartFound ) + { + OUString aErrorMessage = getErrorLineString() + "Element statusbar:statusbaritem is not a container!"; + throw SAXException( aErrorMessage, Reference< XInterface >(), Any() ); + } + + OUString aCommandURL; + OUString aHelpURL; + sal_Int16 nItemBits( ItemStyle::ALIGN_CENTER|ItemStyle::DRAW_IN3D|ItemStyle::MANDATORY ); + sal_Int16 nWidth( 0 ); + sal_Int16 nOffset( STATUSBAR_OFFSET ); + bool bCommandURL( false ); + + m_bStatusBarItemStartFound = true; + for ( sal_Int16 n = 0; n < xAttribs->getLength(); n++ ) + { + pStatusBarEntry = m_aStatusBarMap.find( xAttribs->getNameByIndex( n ) ); + if ( pStatusBarEntry != m_aStatusBarMap.end() ) + { + switch ( pStatusBarEntry->second ) + { + case SB_ATTRIBUTE_URL: + { + bCommandURL = true; + aCommandURL = xAttribs->getValueByIndex( n ); + } + break; + + case SB_ATTRIBUTE_ALIGN: + { + if ( xAttribs->getValueByIndex( n ) == ATTRIBUTE_ALIGN_LEFT ) + { + nItemBits |= ItemStyle::ALIGN_LEFT; + nItemBits &= ~ItemStyle::ALIGN_CENTER; + } + else if ( xAttribs->getValueByIndex( n ) == ATTRIBUTE_ALIGN_CENTER ) + { + nItemBits |= ItemStyle::ALIGN_CENTER; + nItemBits &= ~ItemStyle::ALIGN_LEFT; + } + else if ( xAttribs->getValueByIndex( n ) == ATTRIBUTE_ALIGN_RIGHT ) + { + nItemBits |= ItemStyle::ALIGN_RIGHT; + } + else + { + OUString aErrorMessage = getErrorLineString() + "Attribute statusbar:align must have one value of 'left','right' or 'center'!"; + throw SAXException( aErrorMessage, Reference< XInterface >(), Any() ); + } + } + break; + + case SB_ATTRIBUTE_STYLE: + { + if ( xAttribs->getValueByIndex( n ) == ATTRIBUTE_STYLE_IN ) + { + nItemBits |= ItemStyle::DRAW_IN3D; + nItemBits &= ~ItemStyle::DRAW_OUT3D; + } + else if ( xAttribs->getValueByIndex( n ) == ATTRIBUTE_STYLE_OUT ) + { + nItemBits |= ItemStyle::DRAW_OUT3D; + nItemBits &= ~ItemStyle::DRAW_IN3D; + } + else if ( xAttribs->getValueByIndex( n ) == ATTRIBUTE_STYLE_FLAT ) + { + nItemBits |= ItemStyle::DRAW_FLAT; + } + else + { + OUString aErrorMessage = getErrorLineString() + "Attribute statusbar:autosize must have value 'true' or 'false'!"; + throw SAXException( aErrorMessage, Reference< XInterface >(), Any() ); + } + } + break; + + case SB_ATTRIBUTE_AUTOSIZE: + { + if ( xAttribs->getValueByIndex( n ) == ATTRIBUTE_BOOLEAN_TRUE ) + nItemBits |= ItemStyle::AUTO_SIZE; + else if ( xAttribs->getValueByIndex( n ) == ATTRIBUTE_BOOLEAN_FALSE ) + nItemBits &= ~ItemStyle::AUTO_SIZE; + else + { + OUString aErrorMessage = getErrorLineString() + "Attribute statusbar:autosize must have value 'true' or 'false'!"; + throw SAXException( aErrorMessage, Reference< XInterface >(), Any() ); + } + } + break; + + case SB_ATTRIBUTE_OWNERDRAW: + { + if ( xAttribs->getValueByIndex( n ) == ATTRIBUTE_BOOLEAN_TRUE ) + nItemBits |= ItemStyle::OWNER_DRAW; + else if ( xAttribs->getValueByIndex( n ) == ATTRIBUTE_BOOLEAN_FALSE ) + nItemBits &= ~ItemStyle::OWNER_DRAW; + else + { + OUString aErrorMessage = getErrorLineString() + "Attribute statusbar:ownerdraw must have value 'true' or 'false'!"; + throw SAXException( aErrorMessage, Reference< XInterface >(), Any() ); + } + } + break; + + case SB_ATTRIBUTE_WIDTH: + { + nWidth = static_cast(xAttribs->getValueByIndex( n ).toInt32()); + } + break; + + case SB_ATTRIBUTE_OFFSET: + { + nOffset = static_cast(xAttribs->getValueByIndex( n ).toInt32()); + } + break; + + case SB_ATTRIBUTE_HELPURL: + { + aHelpURL = xAttribs->getValueByIndex( n ); + } + break; + + case SB_ATTRIBUTE_MANDATORY: + { + if ( xAttribs->getValueByIndex( n ) == ATTRIBUTE_BOOLEAN_TRUE ) + nItemBits |= ItemStyle::MANDATORY; + else if ( xAttribs->getValueByIndex( n ) == ATTRIBUTE_BOOLEAN_FALSE ) + nItemBits &= ~ItemStyle::MANDATORY; + else + { + OUString aErrorMessage = getErrorLineString() + "Attribute statusbar:mandatory must have value 'true' or 'false'!"; + throw SAXException( aErrorMessage, Reference< XInterface >(), Any() ); + } + } + break; + + default: + break; + } + } + } // for + + if ( !bCommandURL ) + { + OUString aErrorMessage = getErrorLineString() + "Required attribute statusbar:url must have a value!"; + throw SAXException( aErrorMessage, Reference< XInterface >(), Any() ); + } + else + { + Sequence< PropertyValue > aStatusbarItemProp{ + comphelper::makePropertyValue(ITEM_DESCRIPTOR_COMMANDURL, aCommandURL), + comphelper::makePropertyValue(ITEM_DESCRIPTOR_HELPURL, aHelpURL), + comphelper::makePropertyValue(ITEM_DESCRIPTOR_OFFSET, nOffset), + comphelper::makePropertyValue(ITEM_DESCRIPTOR_STYLE, nItemBits), + comphelper::makePropertyValue(ITEM_DESCRIPTOR_WIDTH, nWidth), + comphelper::makePropertyValue(ITEM_DESCRIPTOR_TYPE, css::ui::ItemType::DEFAULT) + }; + + m_aStatusBarItems->insertByIndex( m_aStatusBarItems->getCount(), Any( aStatusbarItemProp ) ); + } + } + break; + + default: + break; + } +} + +void SAL_CALL OReadStatusBarDocumentHandler::endElement(const OUString& aName) +{ + StatusBarHashMap::const_iterator pStatusBarEntry = m_aStatusBarMap.find( aName ); + if ( pStatusBarEntry == m_aStatusBarMap.end() ) + return; + + switch ( pStatusBarEntry->second ) + { + case SB_ELEMENT_STATUSBAR: + { + if ( !m_bStatusBarStartFound ) + { + OUString aErrorMessage = getErrorLineString() + "End element 'statusbar' found, but no start element 'statusbar'"; + throw SAXException( aErrorMessage, Reference< XInterface >(), Any() ); + } + + m_bStatusBarStartFound = false; + } + break; + + case SB_ELEMENT_STATUSBARITEM: + { + if ( !m_bStatusBarItemStartFound ) + { + OUString aErrorMessage = getErrorLineString() + "End element 'statusbar:statusbaritem' found, but no start element 'statusbar:statusbaritem'"; + throw SAXException( aErrorMessage, Reference< XInterface >(), Any() ); + } + + m_bStatusBarItemStartFound = false; + } + break; + + default: break; + } +} + +void SAL_CALL OReadStatusBarDocumentHandler::characters(const OUString&) +{ +} + +void SAL_CALL OReadStatusBarDocumentHandler::ignorableWhitespace(const OUString&) +{ +} + +void SAL_CALL OReadStatusBarDocumentHandler::processingInstruction( + const OUString& /*aTarget*/, const OUString& /*aData*/ ) +{ +} + +void SAL_CALL OReadStatusBarDocumentHandler::setDocumentLocator( + const Reference< XLocator > &xLocator) +{ + m_xLocator = xLocator; +} + +OUString OReadStatusBarDocumentHandler::getErrorLineString() +{ + if ( m_xLocator.is() ) + return "Line: " + OUString::number( m_xLocator->getLineNumber() ) + " - "; + else + return OUString(); +} + +// OWriteStatusBarDocumentHandler + +OWriteStatusBarDocumentHandler::OWriteStatusBarDocumentHandler( + const Reference< XIndexAccess >& aStatusBarItems, + const Reference< XDocumentHandler >& rWriteDocumentHandler ) : + m_aStatusBarItems( aStatusBarItems ), + m_xWriteDocumentHandler( rWriteDocumentHandler ) +{ + m_xEmptyList = new ::comphelper::AttributeList; + m_aXMLXlinkNS = XMLNS_XLINK_PREFIX; + m_aXMLStatusBarNS = XMLNS_STATUSBAR_PREFIX; +} + +OWriteStatusBarDocumentHandler::~OWriteStatusBarDocumentHandler() +{ +} + +void OWriteStatusBarDocumentHandler::WriteStatusBarDocument() +{ + m_xWriteDocumentHandler->startDocument(); + + // write DOCTYPE line! + Reference< XExtendedDocumentHandler > xExtendedDocHandler( m_xWriteDocumentHandler, UNO_QUERY ); + if ( xExtendedDocHandler.is() ) + { + xExtendedDocHandler->unknown( STATUSBAR_DOCTYPE ); + m_xWriteDocumentHandler->ignorableWhitespace( OUString() ); + } + + rtl::Reference<::comphelper::AttributeList> pList = new ::comphelper::AttributeList; + + pList->AddAttribute( ATTRIBUTE_XMLNS_STATUSBAR, + XMLNS_STATUSBAR ); + + pList->AddAttribute( ATTRIBUTE_XMLNS_XLINK, + XMLNS_XLINK ); + + m_xWriteDocumentHandler->startElement( ELEMENT_NS_STATUSBAR, pList ); + m_xWriteDocumentHandler->ignorableWhitespace( OUString() ); + + sal_Int32 nItemCount = m_aStatusBarItems->getCount(); + Any aAny; + + for ( sal_Int32 nItemPos = 0; nItemPos < nItemCount; nItemPos++ ) + { + Sequence< PropertyValue > aProps; + aAny = m_aStatusBarItems->getByIndex( nItemPos ); + if ( aAny >>= aProps ) + { + OUString aCommandURL; + OUString aHelpURL; + sal_Int16 nStyle( ItemStyle::ALIGN_CENTER|ItemStyle::DRAW_IN3D ); + sal_Int16 nWidth( 0 ); + sal_Int16 nOffset( STATUSBAR_OFFSET ); + + ExtractStatusbarItemParameters( + aProps, + aCommandURL, + aHelpURL, + nOffset, + nStyle, + nWidth ); + + if ( !aCommandURL.isEmpty() ) + WriteStatusBarItem( aCommandURL, nOffset, nStyle, nWidth ); + } + } + + m_xWriteDocumentHandler->ignorableWhitespace( OUString() ); + m_xWriteDocumentHandler->endElement( ELEMENT_NS_STATUSBAR ); + m_xWriteDocumentHandler->ignorableWhitespace( OUString() ); + m_xWriteDocumentHandler->endDocument(); +} + +// protected member functions + +void OWriteStatusBarDocumentHandler::WriteStatusBarItem( + const OUString& rCommandURL, + sal_Int16 nOffset, + sal_Int16 nStyle, + sal_Int16 nWidth ) +{ + rtl::Reference<::comphelper::AttributeList> pList = new ::comphelper::AttributeList; + + if (m_aAttributeURL.isEmpty() ) + { + m_aAttributeURL = m_aXMLXlinkNS + ATTRIBUTE_URL; + } + + // save required attribute (URL) + pList->AddAttribute( m_aAttributeURL, rCommandURL ); + + // alignment + if ( nStyle & ItemStyle::ALIGN_RIGHT ) + { + pList->AddAttribute( m_aXMLStatusBarNS + ATTRIBUTE_ALIGN, + ATTRIBUTE_ALIGN_RIGHT ); + } + else if ( nStyle & ItemStyle::ALIGN_CENTER ) + { + pList->AddAttribute( m_aXMLStatusBarNS + ATTRIBUTE_ALIGN, + ATTRIBUTE_ALIGN_CENTER ); + } + else + { + pList->AddAttribute( m_aXMLStatusBarNS + ATTRIBUTE_ALIGN, + ATTRIBUTE_ALIGN_LEFT ); + } + + // style ( StatusBarItemBits::In is default ) + if ( nStyle & ItemStyle::DRAW_FLAT ) + { + pList->AddAttribute( m_aXMLStatusBarNS + ATTRIBUTE_STYLE, + ATTRIBUTE_STYLE_FLAT ); + } + else if ( nStyle & ItemStyle::DRAW_OUT3D ) + { + pList->AddAttribute( m_aXMLStatusBarNS + ATTRIBUTE_STYLE, + ATTRIBUTE_STYLE_OUT ); + } + + // autosize (default sal_False) + if ( nStyle & ItemStyle::AUTO_SIZE ) + { + pList->AddAttribute( m_aXMLStatusBarNS + ATTRIBUTE_AUTOSIZE, + ATTRIBUTE_BOOLEAN_TRUE ); + } + + // ownerdraw (default sal_False) + if ( nStyle & ItemStyle::OWNER_DRAW ) + { + pList->AddAttribute( m_aXMLStatusBarNS + ATTRIBUTE_OWNERDRAW, + ATTRIBUTE_BOOLEAN_TRUE ); + } + + // width (default 0) + if ( nWidth > 0 ) + { + pList->AddAttribute( m_aXMLStatusBarNS + ATTRIBUTE_WIDTH, + OUString::number( nWidth ) ); + } + + // offset (default STATUSBAR_OFFSET) + if ( nOffset != STATUSBAR_OFFSET ) + { + pList->AddAttribute( m_aXMLStatusBarNS + ATTRIBUTE_OFFSET, + OUString::number( nOffset ) ); + } + + // mandatory (default sal_True) + if ( !( nStyle & ItemStyle::MANDATORY ) ) + { + pList->AddAttribute( m_aXMLStatusBarNS + ATTRIBUTE_MANDATORY, + ATTRIBUTE_BOOLEAN_FALSE ); + } + + m_xWriteDocumentHandler->ignorableWhitespace( OUString() ); + m_xWriteDocumentHandler->startElement( ELEMENT_NS_STATUSBARITEM, pList ); + m_xWriteDocumentHandler->ignorableWhitespace( OUString() ); + m_xWriteDocumentHandler->endElement( ELEMENT_NS_STATUSBARITEM ); +} + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/fwe/xml/toolboxconfiguration.cxx b/framework/source/fwe/xml/toolboxconfiguration.cxx new file mode 100644 index 0000000000..d9e34baabc --- /dev/null +++ b/framework/source/fwe/xml/toolboxconfiguration.cxx @@ -0,0 +1,106 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include + +#include +#include +#include +#include +#include + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::xml::sax; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::io; +using namespace ::com::sun::star::container; + +namespace framework +{ +bool ToolBoxConfiguration::LoadToolBox( + const css::uno::Reference& rxContext, + const css::uno::Reference& rInputStream, + const css::uno::Reference& rToolbarConfiguration) +{ + Reference xParser = Parser::create(rxContext); + + // connect stream to input stream to the parser + InputSource aInputSource; + + aInputSource.aInputStream = rInputStream; + + // create namespace filter and set menudocument handler inside to support xml namespaces + Reference xDocHandler(new OReadToolBoxDocumentHandler(rToolbarConfiguration)); + Reference xFilter(new SaxNamespaceFilter(xDocHandler)); + + // connect parser and filter + xParser->setDocumentHandler(xFilter); + + try + { + xParser->parseStream(aInputSource); + return true; + } + catch (const RuntimeException&) + { + return false; + } + catch (const SAXException&) + { + return false; + } + catch (const css::io::IOException&) + { + return false; + } +} + +bool ToolBoxConfiguration::StoreToolBox( + const css::uno::Reference& rxContext, + const css::uno::Reference& rOutputStream, + const css::uno::Reference& rToolbarConfiguration) +{ + Reference xWriter = Writer::create(rxContext); + xWriter->setOutputStream(rOutputStream); + + try + { + Reference xHandler(xWriter, UNO_QUERY_THROW); + OWriteToolBoxDocumentHandler aWriteToolBoxDocumentHandler(rToolbarConfiguration, xHandler); + aWriteToolBoxDocumentHandler.WriteToolBoxDocument(); + return true; + } + catch (const RuntimeException&) + { + return false; + } + catch (const SAXException&) + { + return false; + } + catch (const css::io::IOException&) + { + return false; + } +} +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/fwe/xml/toolboxdocumenthandler.cxx b/framework/source/fwe/xml/toolboxdocumenthandler.cxx new file mode 100644 index 0000000000..7464939e1e --- /dev/null +++ b/framework/source/fwe/xml/toolboxdocumenthandler.cxx @@ -0,0 +1,713 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::xml::sax; + +constexpr OUStringLiteral TOOLBAR_DOCTYPE = u""; + +namespace framework +{ + +// Property names of a menu/menu item ItemDescriptor +constexpr OUString ITEM_DESCRIPTOR_COMMANDURL = u"CommandURL"_ustr; +constexpr OUString ITEM_DESCRIPTOR_LABEL = u"Label"_ustr; +constexpr OUString ITEM_DESCRIPTOR_TYPE = u"Type"_ustr; +constexpr OUString ITEM_DESCRIPTOR_STYLE = u"Style"_ustr; +constexpr OUString ITEM_DESCRIPTOR_VISIBLE = u"IsVisible"_ustr; + +static void ExtractToolbarParameters( const Sequence< PropertyValue >& rProp, + OUString& rCommandURL, + OUString& rLabel, + sal_Int16& rStyle, + bool& rVisible, + sal_Int16& rType ) +{ + for ( const PropertyValue& rEntry : rProp ) + { + if ( rEntry.Name == ITEM_DESCRIPTOR_COMMANDURL ) + rEntry.Value >>= rCommandURL; + else if ( rEntry.Name == ITEM_DESCRIPTOR_LABEL ) + rEntry.Value >>= rLabel; + else if ( rEntry.Name == ITEM_DESCRIPTOR_TYPE ) + rEntry.Value >>= rType; + else if ( rEntry.Name == ITEM_DESCRIPTOR_VISIBLE ) + rEntry.Value >>= rVisible; + else if ( rEntry.Name == ITEM_DESCRIPTOR_STYLE ) + rEntry.Value >>= rStyle; + } +} + +namespace { + +struct ToolboxStyleItem +{ + sal_Int16 nBit; + OUString attrName; +}; + +} + +constexpr ToolboxStyleItem Styles[ ] = { + { css::ui::ItemStyle::RADIO_CHECK, ATTRIBUTE_ITEMSTYLE_RADIO }, + { css::ui::ItemStyle::ALIGN_LEFT, ATTRIBUTE_ITEMSTYLE_LEFT }, + { css::ui::ItemStyle::AUTO_SIZE, ATTRIBUTE_ITEMSTYLE_AUTO }, + { css::ui::ItemStyle::REPEAT, ATTRIBUTE_ITEMSTYLE_REPEAT }, + { css::ui::ItemStyle::DROPDOWN_ONLY, ATTRIBUTE_ITEMSTYLE_DROPDOWNONLY }, + { css::ui::ItemStyle::DROP_DOWN, ATTRIBUTE_ITEMSTYLE_DROPDOWN }, + { css::ui::ItemStyle::ICON, ATTRIBUTE_ITEMSTYLE_IMAGE }, + { css::ui::ItemStyle::TEXT, ATTRIBUTE_ITEMSTYLE_TEXT }, +}; + +sal_Int32 const nStyleItemEntries = SAL_N_ELEMENTS(Styles); + +namespace { + +struct ToolBarEntryProperty +{ + OReadToolBoxDocumentHandler::ToolBox_XML_Namespace nNamespace; + char aEntryName[20]; +}; + +} + +ToolBarEntryProperty const ToolBoxEntries[OReadToolBoxDocumentHandler::TB_XML_ENTRY_COUNT] = +{ + { OReadToolBoxDocumentHandler::TB_NS_TOOLBAR, ELEMENT_TOOLBAR }, + { OReadToolBoxDocumentHandler::TB_NS_TOOLBAR, ELEMENT_TOOLBARITEM }, + { OReadToolBoxDocumentHandler::TB_NS_TOOLBAR, ELEMENT_TOOLBARSPACE }, + { OReadToolBoxDocumentHandler::TB_NS_TOOLBAR, ELEMENT_TOOLBARBREAK }, + { OReadToolBoxDocumentHandler::TB_NS_TOOLBAR, ELEMENT_TOOLBARSEPARATOR }, + { OReadToolBoxDocumentHandler::TB_NS_TOOLBAR, ATTRIBUTE_TEXT }, + { OReadToolBoxDocumentHandler::TB_NS_XLINK, ATTRIBUTE_URL }, + { OReadToolBoxDocumentHandler::TB_NS_TOOLBAR, ATTRIBUTE_VISIBLE }, + { OReadToolBoxDocumentHandler::TB_NS_TOOLBAR, ATTRIBUTE_ITEMSTYLE }, + { OReadToolBoxDocumentHandler::TB_NS_TOOLBAR, ATTRIBUTE_UINAME }, +}; + +OReadToolBoxDocumentHandler::OReadToolBoxDocumentHandler( const Reference< XIndexContainer >& rItemContainer ) : + m_rItemContainer( rItemContainer ), + m_aType( ITEM_DESCRIPTOR_TYPE ), + m_aLabel( ITEM_DESCRIPTOR_LABEL ), + m_aStyle( ITEM_DESCRIPTOR_STYLE ), + m_aIsVisible( ITEM_DESCRIPTOR_VISIBLE ), + m_aCommandURL( ITEM_DESCRIPTOR_COMMANDURL ) + { + // create hash map + for ( int i = 0; i < TB_XML_ENTRY_COUNT; i++ ) + { + if ( ToolBoxEntries[i].nNamespace == TB_NS_TOOLBAR ) + { + OUString temp = XMLNS_TOOLBAR XMLNS_FILTER_SEPARATOR + + OUString::createFromAscii( ToolBoxEntries[i].aEntryName ); + m_aToolBoxMap.emplace( temp, static_cast(i) ); + } + else + { + OUString temp = XMLNS_XLINK XMLNS_FILTER_SEPARATOR + + OUString::createFromAscii( ToolBoxEntries[i].aEntryName ); + m_aToolBoxMap.emplace( temp, static_cast(i) ); + } + } + + m_bToolBarStartFound = false; + m_bToolBarItemStartFound = false; + m_bToolBarSpaceStartFound = false; + m_bToolBarBreakStartFound = false; + m_bToolBarSeparatorStartFound = false; +} + +OReadToolBoxDocumentHandler::~OReadToolBoxDocumentHandler() +{ +} + +// XDocumentHandler +void SAL_CALL OReadToolBoxDocumentHandler::startDocument() +{ +} + +void SAL_CALL OReadToolBoxDocumentHandler::endDocument() +{ + if ( m_bToolBarStartFound ) + { + OUString aErrorMessage = getErrorLineString() + "No matching start or end element 'toolbar' found!"; + throw SAXException( aErrorMessage, Reference< XInterface >(), Any() ); + } +} + +void SAL_CALL OReadToolBoxDocumentHandler::startElement( + const OUString& aName, const Reference< XAttributeList > &xAttribs ) +{ + ToolBoxHashMap::const_iterator pToolBoxEntry = m_aToolBoxMap.find( aName ); + if ( pToolBoxEntry == m_aToolBoxMap.end() ) + return; + + switch ( pToolBoxEntry->second ) + { + case TB_ELEMENT_TOOLBAR: + { + if ( m_bToolBarStartFound ) + { + OUString aErrorMessage = getErrorLineString() + "Element 'toolbar:toolbar' cannot be embedded into 'toolbar:toolbar'!"; + throw SAXException( aErrorMessage, Reference< XInterface >(), Any() ); + } + else + { + // Check if we have a UI name set in our XML file + OUString aUIName; + for ( sal_Int16 n = 0; n < xAttribs->getLength(); n++ ) + { + pToolBoxEntry = m_aToolBoxMap.find( xAttribs->getNameByIndex( n ) ); + if ( pToolBoxEntry != m_aToolBoxMap.end() ) + { + switch ( pToolBoxEntry->second ) + { + case TB_ATTRIBUTE_UINAME: + aUIName = xAttribs->getValueByIndex( n ); + break; + default: + break; + } + } + } + if ( !aUIName.isEmpty() ) + { + // Try to set UI name as a container property + Reference< XPropertySet > xPropSet( m_rItemContainer, UNO_QUERY ); + if ( xPropSet.is() ) + { + try + { + xPropSet->setPropertyValue("UIName", Any( aUIName ) ); + } + catch ( const UnknownPropertyException& ) + { + } + } + + } + } + m_bToolBarStartFound = true; + } + break; + + case TB_ELEMENT_TOOLBARITEM: + { + if ( !m_bToolBarStartFound ) + { + OUString aErrorMessage = getErrorLineString() + "Element 'toolbar:toolbaritem' must be embedded into element 'toolbar:toolbar'!"; + throw SAXException( aErrorMessage, Reference< XInterface >(), Any() ); + } + + if ( m_bToolBarSeparatorStartFound || + m_bToolBarBreakStartFound || + m_bToolBarSpaceStartFound || + m_bToolBarItemStartFound ) + { + OUString aErrorMessage = getErrorLineString() + "Element toolbar:toolbaritem is not a container!"; + throw SAXException( aErrorMessage, Reference< XInterface >(), Any() ); + } + + bool bAttributeURL = false; + + m_bToolBarItemStartFound = true; + OUString aLabel; + OUString aCommandURL; + sal_uInt16 nItemBits( 0 ); + bool bVisible( true ); + + for ( sal_Int16 n = 0; n < xAttribs->getLength(); n++ ) + { + pToolBoxEntry = m_aToolBoxMap.find( xAttribs->getNameByIndex( n ) ); + if ( pToolBoxEntry != m_aToolBoxMap.end() ) + { + switch ( pToolBoxEntry->second ) + { + case TB_ATTRIBUTE_TEXT: + { + aLabel = xAttribs->getValueByIndex( n ); + } + break; + + case TB_ATTRIBUTE_URL: + { + bAttributeURL = true; + aCommandURL = xAttribs->getValueByIndex( n ); + } + break; + + case TB_ATTRIBUTE_VISIBLE: + { + if ( xAttribs->getValueByIndex( n ) == ATTRIBUTE_BOOLEAN_TRUE ) + bVisible = true; + else if ( xAttribs->getValueByIndex( n ) == ATTRIBUTE_BOOLEAN_FALSE ) + bVisible = false; + else + { + OUString aErrorMessage = getErrorLineString() + "Attribute toolbar:visible must have value 'true' or 'false'!"; + throw SAXException( aErrorMessage, Reference< XInterface >(), Any() ); + } + } + break; + + case TB_ATTRIBUTE_STYLE: + { + // read space separated item style list + OUString aTemp = xAttribs->getValueByIndex( n ); + sal_Int32 nIndex = 0; + + do + { + OUString aToken = aTemp.getToken( 0, ' ', nIndex ); + if ( !aToken.isEmpty() ) + { + if ( aToken == ATTRIBUTE_ITEMSTYLE_RADIO ) + nItemBits |= css::ui::ItemStyle::RADIO_CHECK; + else if ( aToken == ATTRIBUTE_ITEMSTYLE_LEFT ) + nItemBits |= css::ui::ItemStyle::ALIGN_LEFT; + else if ( aToken == ATTRIBUTE_ITEMSTYLE_AUTOSIZE ) + nItemBits |= css::ui::ItemStyle::AUTO_SIZE; + else if ( aToken == ATTRIBUTE_ITEMSTYLE_REPEAT ) + nItemBits |= css::ui::ItemStyle::REPEAT; + else if ( aToken == ATTRIBUTE_ITEMSTYLE_DROPDOWNONLY ) + nItemBits |= css::ui::ItemStyle::DROPDOWN_ONLY; + else if ( aToken == ATTRIBUTE_ITEMSTYLE_DROPDOWN ) + nItemBits |= css::ui::ItemStyle::DROP_DOWN; + else if ( aToken == ATTRIBUTE_ITEMSTYLE_TEXT ) + nItemBits |= css::ui::ItemStyle::TEXT; + else if ( aToken == ATTRIBUTE_ITEMSTYLE_IMAGE ) + nItemBits |= css::ui::ItemStyle::ICON; + } + } + while ( nIndex >= 0 ); + } + break; + + default: + break; + } + } + } // for + + if ( !bAttributeURL ) + { + OUString aErrorMessage = getErrorLineString() + "Required attribute toolbar:url must have a value!"; + throw SAXException( aErrorMessage, Reference< XInterface >(), Any() ); + } + + if ( !aCommandURL.isEmpty() ) + { + //fix for fdo#39370 + /// check whether RTL interface or not + if(AllSettings::GetLayoutRTL()){ + if (aCommandURL == ".uno:ParaLeftToRight") + aCommandURL = ".uno:ParaRightToLeft"; + else if (aCommandURL == ".uno:ParaRightToLeft") + aCommandURL = ".uno:ParaLeftToRight"; + else if (aCommandURL == ".uno:LeftPara") + aCommandURL = ".uno:RightPara"; + else if (aCommandURL == ".uno:RightPara") + aCommandURL = ".uno:LeftPara"; + else if (aCommandURL == ".uno:AlignLeft") + aCommandURL = ".uno:AlignRight"; + else if (aCommandURL == ".uno:AlignRight") + aCommandURL = ".uno:AlignLeft"; + else if (aCommandURL == ".uno:WrapLeft") + aCommandURL = ".uno:WrapRight"; + else if (aCommandURL == ".uno:WrapRight") + aCommandURL = ".uno:WrapLeft"; + } + + auto aToolbarItemProp( comphelper::InitPropertySequence( { + { m_aCommandURL, css::uno::Any( aCommandURL ) }, + { m_aLabel, css::uno::Any( aLabel ) }, + { m_aType, css::uno::Any( css::ui::ItemType::DEFAULT ) }, + { m_aStyle, css::uno::Any( nItemBits ) }, + { m_aIsVisible, css::uno::Any( bVisible ) }, + } ) ); + + m_rItemContainer->insertByIndex( m_rItemContainer->getCount(), Any( aToolbarItemProp ) ); + } + } + break; + + case TB_ELEMENT_TOOLBARSPACE: + { + if ( m_bToolBarSeparatorStartFound || + m_bToolBarBreakStartFound || + m_bToolBarSpaceStartFound || + m_bToolBarItemStartFound ) + { + OUString aErrorMessage = getErrorLineString() + "Element toolbar:toolbarspace is not a container!"; + throw SAXException( aErrorMessage, Reference< XInterface >(), Any() ); + } + + m_bToolBarSpaceStartFound = true; + + Sequence< PropertyValue > aToolbarItemProp{ + comphelper::makePropertyValue(m_aCommandURL, OUString()), + comphelper::makePropertyValue(m_aType, css::ui::ItemType::SEPARATOR_SPACE) + }; + + m_rItemContainer->insertByIndex( m_rItemContainer->getCount(), Any( aToolbarItemProp ) ); + } + break; + + case TB_ELEMENT_TOOLBARBREAK: + { + if ( m_bToolBarSeparatorStartFound || + m_bToolBarBreakStartFound || + m_bToolBarSpaceStartFound || + m_bToolBarItemStartFound ) + { + OUString aErrorMessage = getErrorLineString() + "Element toolbar:toolbarbreak is not a container!"; + throw SAXException( aErrorMessage, Reference< XInterface >(), Any() ); + } + + m_bToolBarBreakStartFound = true; + + Sequence< PropertyValue > aToolbarItemProp{ + comphelper::makePropertyValue(m_aCommandURL, OUString()), + comphelper::makePropertyValue(m_aType, css::ui::ItemType::SEPARATOR_LINEBREAK) + }; + + m_rItemContainer->insertByIndex( m_rItemContainer->getCount(), Any( aToolbarItemProp ) ); + } + break; + + case TB_ELEMENT_TOOLBARSEPARATOR: + { + if ( m_bToolBarSeparatorStartFound || + m_bToolBarBreakStartFound || + m_bToolBarSpaceStartFound || + m_bToolBarItemStartFound ) + { + OUString aErrorMessage = getErrorLineString() + "Element toolbar:toolbarseparator is not a container!"; + throw SAXException( aErrorMessage, Reference< XInterface >(), Any() ); + } + + m_bToolBarSeparatorStartFound = true; + + Sequence< PropertyValue > aToolbarItemProp{ + comphelper::makePropertyValue(m_aCommandURL, OUString()), + comphelper::makePropertyValue(m_aType, css::ui::ItemType::SEPARATOR_LINE) + }; + + m_rItemContainer->insertByIndex( m_rItemContainer->getCount(), Any( aToolbarItemProp ) ); + } + break; + + default: + break; + } +} + +void SAL_CALL OReadToolBoxDocumentHandler::endElement(const OUString& aName) +{ + ToolBoxHashMap::const_iterator pToolBoxEntry = m_aToolBoxMap.find( aName ); + if ( pToolBoxEntry == m_aToolBoxMap.end() ) + return; + + switch ( pToolBoxEntry->second ) + { + case TB_ELEMENT_TOOLBAR: + { + if ( !m_bToolBarStartFound ) + { + OUString aErrorMessage = getErrorLineString() + "End element 'toolbar' found, but no start element 'toolbar'"; + throw SAXException( aErrorMessage, Reference< XInterface >(), Any() ); + } + + m_bToolBarStartFound = false; + } + break; + + case TB_ELEMENT_TOOLBARITEM: + { + if ( !m_bToolBarItemStartFound ) + { + OUString aErrorMessage = getErrorLineString() + "End element 'toolbar:toolbaritem' found, but no start element 'toolbar:toolbaritem'"; + throw SAXException( aErrorMessage, Reference< XInterface >(), Any() ); + } + + m_bToolBarItemStartFound = false; + } + break; + + case TB_ELEMENT_TOOLBARBREAK: + { + if ( !m_bToolBarBreakStartFound ) + { + OUString aErrorMessage = getErrorLineString() + "End element 'toolbar:toolbarbreak' found, but no start element 'toolbar:toolbarbreak'"; + throw SAXException( aErrorMessage, Reference< XInterface >(), Any() ); + } + + m_bToolBarBreakStartFound = false; + } + break; + + case TB_ELEMENT_TOOLBARSPACE: + { + if ( !m_bToolBarSpaceStartFound ) + { + OUString aErrorMessage = getErrorLineString() + "End element 'toolbar:toolbarspace' found, but no start element 'toolbar:toolbarspace'"; + throw SAXException( aErrorMessage, Reference< XInterface >(), Any() ); + } + + m_bToolBarSpaceStartFound = false; + } + break; + + case TB_ELEMENT_TOOLBARSEPARATOR: + { + if ( !m_bToolBarSeparatorStartFound ) + { + OUString aErrorMessage = getErrorLineString() + "End element 'toolbar:toolbarseparator' found, but no start element 'toolbar:toolbarseparator'"; + throw SAXException( aErrorMessage, Reference< XInterface >(), Any() ); + } + + m_bToolBarSeparatorStartFound = false; + } + break; + + default: break; + } +} + +void SAL_CALL OReadToolBoxDocumentHandler::characters(const OUString&) +{ +} + +void SAL_CALL OReadToolBoxDocumentHandler::ignorableWhitespace(const OUString&) +{ +} + +void SAL_CALL OReadToolBoxDocumentHandler::processingInstruction( + const OUString& /*aTarget*/, const OUString& /*aData*/ ) +{ +} + +void SAL_CALL OReadToolBoxDocumentHandler::setDocumentLocator( + const Reference< XLocator > &xLocator) +{ + m_xLocator = xLocator; +} + +OUString OReadToolBoxDocumentHandler::getErrorLineString() +{ + if ( m_xLocator.is() ) + return "Line: " + OUString::number( m_xLocator->getLineNumber() ) + " - "; + else + return OUString(); +} + +// OWriteToolBoxDocumentHandler + +OWriteToolBoxDocumentHandler::OWriteToolBoxDocumentHandler( + const Reference< XIndexAccess >& rItemAccess, + Reference< XDocumentHandler > const & rWriteDocumentHandler ) : + m_xWriteDocumentHandler( rWriteDocumentHandler ), + m_rItemAccess( rItemAccess ) +{ + m_xEmptyList = new ::comphelper::AttributeList; + m_aXMLXlinkNS = XMLNS_XLINK_PREFIX; + m_aXMLToolbarNS = XMLNS_TOOLBAR_PREFIX; +} + +OWriteToolBoxDocumentHandler::~OWriteToolBoxDocumentHandler() +{ +} + +void OWriteToolBoxDocumentHandler::WriteToolBoxDocument() +{ + m_xWriteDocumentHandler->startDocument(); + + // write DOCTYPE line! + Reference< XExtendedDocumentHandler > xExtendedDocHandler( m_xWriteDocumentHandler, UNO_QUERY ); + if ( xExtendedDocHandler.is() ) + { + xExtendedDocHandler->unknown( TOOLBAR_DOCTYPE ); + m_xWriteDocumentHandler->ignorableWhitespace( OUString() ); + } + + OUString aUIName; + Reference< XPropertySet > xPropSet( m_rItemAccess, UNO_QUERY ); + if ( xPropSet.is() ) + { + try + { + xPropSet->getPropertyValue("UIName") >>= aUIName; + } + catch ( const UnknownPropertyException& ) + { + } + } + + rtl::Reference<::comphelper::AttributeList> pList = new ::comphelper::AttributeList; + + pList->AddAttribute( ATTRIBUTE_XMLNS_TOOLBAR, + XMLNS_TOOLBAR ); + + pList->AddAttribute( ATTRIBUTE_XMLNS_XLINK, + XMLNS_XLINK ); + + if ( !aUIName.isEmpty() ) + pList->AddAttribute( m_aXMLToolbarNS + ATTRIBUTE_UINAME, + aUIName ); + + m_xWriteDocumentHandler->startElement( ELEMENT_NS_TOOLBAR, pList ); + m_xWriteDocumentHandler->ignorableWhitespace( OUString() ); + + sal_Int32 nItemCount = m_rItemAccess->getCount(); + Any aAny; + + for ( sal_Int32 nItemPos = 0; nItemPos < nItemCount; nItemPos++ ) + { + Sequence< PropertyValue > aProps; + aAny = m_rItemAccess->getByIndex( nItemPos ); + if ( aAny >>= aProps ) + { + OUString aCommandURL; + OUString aLabel; + bool bVisible( true ); + sal_Int16 nType( css::ui::ItemType::DEFAULT ); + sal_Int16 nStyle( 0 ); + + ExtractToolbarParameters( aProps, aCommandURL, aLabel, nStyle, bVisible, nType ); + if ( nType == css::ui::ItemType::DEFAULT ) + WriteToolBoxItem( aCommandURL, aLabel, nStyle, bVisible ); + else if ( nType == css::ui::ItemType::SEPARATOR_SPACE ) + WriteToolBoxSpace(); + else if ( nType == css::ui::ItemType::SEPARATOR_LINE ) + WriteToolBoxSeparator(); + else if ( nType == css::ui::ItemType::SEPARATOR_LINEBREAK ) + WriteToolBoxBreak(); + } + } + + m_xWriteDocumentHandler->ignorableWhitespace( OUString() ); + m_xWriteDocumentHandler->endElement( ELEMENT_NS_TOOLBAR ); + m_xWriteDocumentHandler->ignorableWhitespace( OUString() ); + m_xWriteDocumentHandler->endDocument(); +} + +// protected member functions + +void OWriteToolBoxDocumentHandler::WriteToolBoxItem( + const OUString& rCommandURL, + const OUString& rLabel, + sal_Int16 nStyle, + bool bVisible ) +{ + rtl::Reference<::comphelper::AttributeList> pList = new ::comphelper::AttributeList; + + if ( m_aAttributeURL.isEmpty() ) + { + m_aAttributeURL = m_aXMLXlinkNS + ATTRIBUTE_URL; + } + + // save required attribute (URL) + pList->AddAttribute( m_aAttributeURL, rCommandURL ); + + if ( !rLabel.isEmpty() ) + { + pList->AddAttribute( m_aXMLToolbarNS + ATTRIBUTE_TEXT, + rLabel ); + } + + if ( !bVisible ) + { + pList->AddAttribute( m_aXMLToolbarNS + ATTRIBUTE_VISIBLE, + ATTRIBUTE_BOOLEAN_FALSE ); + } + + if ( nStyle > 0 ) + { + OUStringBuffer aValue; + const ToolboxStyleItem* pStyle = Styles; + + for ( sal_Int32 nIndex = 0; nIndex < nStyleItemEntries; ++nIndex, ++pStyle ) + { + if ( nStyle & pStyle->nBit ) + { + if ( !aValue.isEmpty() ) + aValue.append(" "); + aValue.append( pStyle->attrName ); + } + } + pList->AddAttribute( m_aXMLToolbarNS + ATTRIBUTE_ITEMSTYLE, + aValue.makeStringAndClear() ); + } + + m_xWriteDocumentHandler->ignorableWhitespace( OUString() ); + m_xWriteDocumentHandler->startElement( ELEMENT_NS_TOOLBARITEM, pList ); + m_xWriteDocumentHandler->ignorableWhitespace( OUString() ); + m_xWriteDocumentHandler->endElement( ELEMENT_NS_TOOLBARITEM ); +} + +void OWriteToolBoxDocumentHandler::WriteToolBoxSpace() +{ + m_xWriteDocumentHandler->ignorableWhitespace( OUString() ); + m_xWriteDocumentHandler->startElement( ELEMENT_NS_TOOLBARSPACE, m_xEmptyList ); + m_xWriteDocumentHandler->ignorableWhitespace( OUString() ); + m_xWriteDocumentHandler->endElement( ELEMENT_NS_TOOLBARSPACE ); +} + +void OWriteToolBoxDocumentHandler::WriteToolBoxBreak() +{ + m_xWriteDocumentHandler->ignorableWhitespace( OUString() ); + m_xWriteDocumentHandler->startElement( ELEMENT_NS_TOOLBARBREAK, m_xEmptyList ); + m_xWriteDocumentHandler->ignorableWhitespace( OUString() ); + m_xWriteDocumentHandler->endElement( ELEMENT_NS_TOOLBARBREAK ); +} + +void OWriteToolBoxDocumentHandler::WriteToolBoxSeparator() +{ + m_xWriteDocumentHandler->ignorableWhitespace( OUString() ); + m_xWriteDocumentHandler->startElement( ELEMENT_NS_TOOLBARSEPARATOR, m_xEmptyList ); + m_xWriteDocumentHandler->ignorableWhitespace( OUString() ); + m_xWriteDocumentHandler->endElement( ELEMENT_NS_TOOLBARSEPARATOR ); +} + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/fwe/xml/xmlnamespaces.cxx b/framework/source/fwe/xml/xmlnamespaces.cxx new file mode 100644 index 0000000000..7faa6c48d8 --- /dev/null +++ b/framework/source/fwe/xml/xmlnamespaces.cxx @@ -0,0 +1,152 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include + +using namespace ::com::sun::star::xml::sax; +using namespace ::com::sun::star::uno; + +namespace framework +{ + +void XMLNamespaces::addNamespace( const OUString& aName, const OUString& aValue ) +{ + NamespaceMap::iterator p; + OUString aNamespaceName( aName ); + + // delete preceding "xmlns" + constexpr char aXMLAttributeNamespace[] = "xmlns"; + if ( aNamespaceName.startsWith( aXMLAttributeNamespace ) ) + { + constexpr sal_Int32 nXMLNamespaceLength = RTL_CONSTASCII_LENGTH(aXMLAttributeNamespace); + if ( aNamespaceName.getLength() == nXMLNamespaceLength ) + { + aNamespaceName.clear(); + } + else if ( aNamespaceName.getLength() >= nXMLNamespaceLength+2 ) + { + aNamespaceName = aNamespaceName.copy( nXMLNamespaceLength+1 ); + } + else + { + // a xml namespace without name is not allowed (e.g. "xmlns:" ) + throw SAXException( "A xml namespace without name is not allowed!", Reference< XInterface >(), Any() ); + } + } + + if ( aValue.isEmpty() && !aNamespaceName.isEmpty() ) + { + // namespace should be reset - as xml draft states this is only allowed + // for the default namespace - check and throw exception if check fails + throw SAXException( "Clearing xml namespace only allowed for default namespace!", Reference< XInterface >(), Any() ); + } + + if ( aNamespaceName.isEmpty() ) + m_aDefaultNamespace = aValue; + else + { + p = m_aNamespaceMap.find( aNamespaceName ); + if ( p != m_aNamespaceMap.end() ) + { + // replace current namespace definition + m_aNamespaceMap.erase( p ); + m_aNamespaceMap.emplace( aNamespaceName, aValue ); + } + else + { + m_aNamespaceMap.emplace( aNamespaceName, aValue ); + } + } +} + +OUString XMLNamespaces::applyNSToAttributeName( const OUString& aName ) const +{ + // xml draft: there is no default namespace for attributes! + + int index; + if (( index = aName.indexOf( ':' )) > 0 ) + { + if ( aName.getLength() <= index+1 ) + { + // attribute with namespace but without name "namespace:" is not allowed!! + throw SAXException( "Attribute has no name only preceding namespace!", Reference< XInterface >(), Any() ); + } + OUString aAttributeName = getNamespaceValue( aName.copy( 0, index )) + "^" + aName.subView( index+1); + return aAttributeName; + } + + return aName; +} + +OUString XMLNamespaces::applyNSToElementName( const OUString& aName ) const +{ + // xml draft: element names can have a default namespace + + int index = aName.indexOf( ':' ); + OUString aNamespace; + OUString aElementName = aName; + + if ( index > 0 ) + aNamespace = getNamespaceValue( aName.copy( 0, index ) ); + else + aNamespace = m_aDefaultNamespace; + + if ( !aNamespace.isEmpty() ) + { + aElementName = aNamespace + "^"; + } + else + return aName; + + if ( index > 0 ) + { + if ( aName.getLength() <= index+1 ) + { + // attribute with namespace but without a name is not allowed (e.g. "cfg:" ) + throw SAXException( "Attribute has no name only preceding namespace!", Reference< XInterface >(), Any() ); + } + aElementName += aName.subView( index+1 ); + } + else + aElementName += aName; + + return aElementName; +} + +OUString const & XMLNamespaces::getNamespaceValue( const OUString& aNamespace ) const +{ + if ( aNamespace.isEmpty() ) + return m_aDefaultNamespace; + else + { + NamespaceMap::const_iterator p = m_aNamespaceMap.find( aNamespace ); + if ( p == m_aNamespaceMap.end() ) + { + // namespace not defined => throw exception! + throw SAXException( "XML namespace used but not defined!", Reference< XInterface >(), Any() ); + } + return p->second; + } +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/fwi/classes/converter.cxx b/framework/source/fwi/classes/converter.cxx new file mode 100644 index 0000000000..59ae35d396 --- /dev/null +++ b/framework/source/fwi/classes/converter.cxx @@ -0,0 +1,117 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include + +namespace framework{ + +/** + * converts a sequence of PropertyValue to a sequence of NamedValue. + */ +css::uno::Sequence< css::beans::NamedValue > Converter::convert_seqPropVal2seqNamedVal( const css::uno::Sequence< css::beans::PropertyValue >& lSource ) +{ + sal_Int32 nCount = lSource.getLength(); + css::uno::Sequence< css::beans::NamedValue > lDestination(nCount); + auto lDestinationRange = asNonConstRange(lDestination); + for (sal_Int32 nItem=0; nItem Converter::convert_seqOUString2OUStringList( const css::uno::Sequence< OUString >& lSource ) +{ + std::vector lDestination; + sal_Int32 nCount = lSource.getLength(); + + lDestination.reserve(nCount); + for (sal_Int32 nItem = 0; nItem < nCount; ++nItem) + { + lDestination.push_back(lSource[nItem]); + } + + return lDestination; +} + +OUString Converter::convert_DateTime2ISO8601( const DateTime& aSource ) +{ + OUStringBuffer sBuffer(25); + + sal_Int32 nYear = aSource.GetYear(); + sal_Int32 nMonth = aSource.GetMonth(); + sal_Int32 nDay = aSource.GetDay(); + + sal_Int32 nHour = aSource.GetHour(); + sal_Int32 nMin = aSource.GetMin(); + sal_Int32 nSec = aSource.GetSec(); + + // write year formatted as "YYYY" + if (nYear<10) + sBuffer.append("000"); + else if (nYear<100) + sBuffer.append("00"); + else if (nYear<1000) + sBuffer.append("0"); + sBuffer.append( nYear ); + + // write month formatted as "MM" + sBuffer.append("-"); + if (nMonth<10) + sBuffer.append("0"); + sBuffer.append( nMonth ); + + // write day formatted as "DD" + sBuffer.append("-"); + if (nDay<10) + sBuffer.append("0"); + sBuffer.append( nDay ); + + // write hours formatted as "hh" + sBuffer.append("T"); + if (nHour<10) + sBuffer.append("0"); + sBuffer.append( nHour ); + + // write min formatted as "mm" + sBuffer.append(":"); + if (nMin<10) + sBuffer.append("0"); + sBuffer.append( nMin ); + + // write sec formatted as "ss" + sBuffer.append(":"); + if (nSec<10) + sBuffer.append("0"); + sBuffer.append( nSec ); + + // write time-zone + sBuffer.append("Z"); + + return sBuffer.makeStringAndClear(); +} + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/fwi/classes/protocolhandlercache.cxx b/framework/source/fwi/classes/protocolhandlercache.cxx new file mode 100644 index 0000000000..490e2c9107 --- /dev/null +++ b/framework/source/fwi/classes/protocolhandlercache.cxx @@ -0,0 +1,257 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +/*TODO + - change "singleton" behaviour by using new helper ::comhelper::SingletonRef + - rename method exist() to existHandlerForURL() or similar one + - may it's a good idea to replace struct ProtocolHandler by css::beans::NamedValue type?! +*/ + +#include +#include + +#include +#include +#include +#include + +constexpr OUString SETNAME_HANDLER = u"HandlerSet"_ustr; // name of configuration set inside package + +namespace framework{ + +/** + @short overloaded index operator of hash map to support pattern key search + @descr All keys inside this hash map are URL pattern which points to a uno + implementation name of a protocol handler service which is registered + for this pattern. This operator makes it easy to find such registered + handler by using a full qualified URL and compare it with all pattern + keys. + + @param sURL + the full qualified URL which should match to a registered pattern + + @return An iterator which points to the found item inside the hash or PatternHash::end() + if no pattern match this given sURL. + */ +namespace { + +PatternHash::const_iterator findPatternKey(PatternHash const * hash, const OUString& sURL) +{ + return std::find_if(hash->begin(), hash->end(), + [&sURL](const PatternHash::value_type& rEntry) { + WildCard aPattern(rEntry.first); + return aPattern.Matches(sURL); + }); +} + +} + +/** + @short initialize static member of class HandlerCache + @descr We use a singleton pattern to implement this handler cache. + That means it use two static member list to hold all necessary information + and a ref count mechanism to create/destroy it on demand. + */ +std::optional HandlerCache::s_pHandler; +std::optional HandlerCache::s_pPattern; +sal_Int32 HandlerCache::m_nRefCount = 0; +HandlerCFGAccess* HandlerCache::s_pConfig = nullptr; + +/** + @short ctor of the cache of all registered protocol handler + @descr It tries to open the right configuration package automatically + and fill the internal structures. After that the cache can be + used for read access on this data and perform some search + operations on it. + */ +HandlerCache::HandlerCache() +{ + SolarMutexGuard aGuard; + + if (m_nRefCount==0) + { + s_pHandler.emplace(); + s_pPattern.emplace(); + s_pConfig = new HandlerCFGAccess(PACKAGENAME_PROTOCOLHANDLER); + s_pConfig->read(*s_pHandler, *s_pPattern); + s_pConfig->setCache(this); + } + + ++m_nRefCount; +} + +/** + @short dtor of the cache + @descr It frees all used memory. In further implementations (may if we support write access too) + it's a good place to flush changes back to the configuration - but not needed yet. + */ +HandlerCache::~HandlerCache() +{ + SolarMutexGuard aGuard; + + if( m_nRefCount==1) + { + s_pConfig->setCache(nullptr); + + delete s_pConfig; + s_pConfig = nullptr; + s_pHandler.reset(); + s_pPattern.reset(); + } + + --m_nRefCount; +} + +/** + @short dtor of the cache + @descr It frees all used memory. In further implementations (may if we support write access too) + it's a good place to flush changes back to the configuration - but not needed yet. + */ +bool HandlerCache::search( const OUString& sURL, ProtocolHandler* pReturn ) const +{ + bool bFound = false; + + SolarMutexGuard aGuard; + + PatternHash::const_iterator pItem = findPatternKey(s_pPattern ? &*s_pPattern : nullptr, sURL); + if (pItem != s_pPattern->end()) + { + *pReturn = (*s_pHandler)[pItem->second]; + bFound = true; + } + + return bFound; +} + +/** + @short search for a registered handler by using a URL struct + @descr We combine necessary parts of this struct to a valid URL string + and call our other search method ... + It's a helper for outside code. + */ +bool HandlerCache::search( const css::util::URL& aURL, ProtocolHandler* pReturn ) const +{ + return search( aURL.Complete, pReturn ); +} + +void HandlerCache::takeOver(HandlerHash aHandler, PatternHash aPattern) +{ + SolarMutexGuard aGuard; + + s_pHandler = std::move(aHandler); + s_pPattern = std::move(aPattern); +} + +/** + @short dtor of the config access class + @descr It opens the configuration package automatically by using base class mechanism. + After that "read()" method of this class should be called to use it. + + @param sPackage + specifies the package name of the configuration data which should be used + */ +HandlerCFGAccess::HandlerCFGAccess( const OUString& sPackage ) + : ConfigItem(sPackage) + , m_pCache(nullptr) +{ + css::uno::Sequence< OUString > lListenPaths { SETNAME_HANDLER }; + EnableNotification(lListenPaths); +} + +/** + @short use base class mechanism to fill given structures + @descr User use us as a wrapper between configuration api and his internal structures. + He give us some pointer to his member and we fill it. + + @param rHandlerHash + list of protocol handler infos + + @param rPatternHash + reverse map of handler pattern to her uno names + */ +void HandlerCFGAccess::read( HandlerHash& rHandlerHash, PatternHash& rPatternHash ) +{ + // list of all uno implementation names without encoding + css::uno::Sequence< OUString > lNames = GetNodeNames( SETNAME_HANDLER, ::utl::ConfigNameFormat::LocalPath ); + sal_Int32 nSourceCount = lNames.getLength(); + sal_Int32 nTargetCount = nSourceCount; + // list of all full qualified path names of configuration entries + css::uno::Sequence< OUString > lFullNames ( nTargetCount ); + auto lFullNamesRange = asNonConstRange(lFullNames); + // expand names to full path names + sal_Int32 nSource=0; + sal_Int32 nTarget=0; + for( nSource=0; nSource lValues = GetProperties( lFullNames ); + SAL_WARN_IF( lFullNames.getLength()!=lValues.getLength(), "fwk", "HandlerCFGAccess::read(): Miss some configuration values of handler set!" ); + + // fill structures + nSource = 0; + for( nTarget=0; nTarget lTemp; + lValues[nTarget] >>= lTemp; + aHandler.m_lProtocols = Converter::convert_seqOUString2OUStringList(lTemp); + + // register his pattern into the performance search hash + for (auto const& item : aHandler.m_lProtocols) + { + rPatternHash[item] = lNames[nSource]; + } + + // insert the handler info into the normal handler cache + rHandlerHash[lNames[nSource]] = aHandler; + ++nSource; + } +} + +void HandlerCFGAccess::Notify(const css::uno::Sequence< OUString >& /*lPropertyNames*/) +{ + HandlerHash aHandler; + PatternHash aPattern; + + read(aHandler, aPattern); + if (m_pCache) + m_pCache->takeOver(std::move(aHandler), std::move(aPattern)); +} + +void HandlerCFGAccess::ImplCommit() +{ +} + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/fwi/helper/mischelper.cxx b/framework/source/fwi/helper/mischelper.cxx new file mode 100644 index 0000000000..e9c664d474 --- /dev/null +++ b/framework/source/fwi/helper/mischelper.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 +#include +#include + +#include +#include +#include +#include +#include +#include + +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::container; +using namespace ::com::sun::star::lang; + +namespace framework +{ + +uno::Reference< linguistic2::XLanguageGuessing > const & LanguageGuessingHelper::GetGuesser() const +{ + if (!m_xLanguageGuesser.is()) + { + try + { + m_xLanguageGuesser = linguistic2::LanguageGuessing::create( m_xContext ); + } + catch (const uno::Exception &) + { + SAL_WARN( "fwk", "failed to get language guessing component" ); + } + } + return m_xLanguageGuesser; +} + +void FillLangItems( std::set< OUString > &rLangItems, + const uno::Reference< frame::XFrame > & rxFrame, + const LanguageGuessingHelper & rLangGuessHelper, + SvtScriptType nScriptType, + const OUString & rCurLang, + const OUString & rKeyboardLang, + const OUString & rGuessedTextLang ) +{ + rLangItems.clear(); + + //1--add current language + if( !rCurLang.isEmpty() && + LANGUAGE_DONTKNOW != SvtLanguageTable::GetLanguageType( rCurLang )) + rLangItems.insert( rCurLang ); + + //2--System + const AllSettings& rAllSettings = Application::GetSettings(); + LanguageType rSystemLanguage = rAllSettings.GetLanguageTag().getLanguageType(); + if( rSystemLanguage != LANGUAGE_DONTKNOW ) + { + if ( IsScriptTypeMatchingToLanguage( nScriptType, rSystemLanguage )) + rLangItems.insert( SvtLanguageTable::GetLanguageString( rSystemLanguage ) ); + } + + //3--UI + LanguageType rUILanguage = rAllSettings.GetUILanguageTag().getLanguageType(); + if( rUILanguage != LANGUAGE_DONTKNOW ) + { + if ( IsScriptTypeMatchingToLanguage( nScriptType, rUILanguage )) + rLangItems.insert( SvtLanguageTable::GetLanguageString( rUILanguage ) ); + } + + //4--guessed language + const uno::Reference< linguistic2::XLanguageGuessing >& xLangGuesser( rLangGuessHelper.GetGuesser() ); + if ( xLangGuesser.is() && !rGuessedTextLang.isEmpty()) + { + css::lang::Locale aLocale(xLangGuesser->guessPrimaryLanguage( rGuessedTextLang, 0, rGuessedTextLang.getLength()) ); + LanguageType nLang = LanguageTag( aLocale ).makeFallback().getLanguageType(); + if (nLang != LANGUAGE_DONTKNOW && nLang != LANGUAGE_NONE && nLang != LANGUAGE_SYSTEM + && IsScriptTypeMatchingToLanguage( nScriptType, nLang )) + rLangItems.insert( SvtLanguageTable::GetLanguageString( nLang )); + } + + //5--keyboard language + if( !rKeyboardLang.isEmpty() ) + { + if ( IsScriptTypeMatchingToLanguage( nScriptType, SvtLanguageTable::GetLanguageType( rKeyboardLang ))) + rLangItems.insert( rKeyboardLang ); + } + + //6--all languages used in current document + Reference< css::frame::XModel > xModel; + if ( rxFrame.is() ) + { + Reference< css::frame::XController > xController = rxFrame->getController(); + if ( xController.is() ) + xModel = xController->getModel(); + } + Reference< document::XDocumentLanguages > xDocumentLanguages( xModel, UNO_QUERY ); + /*the description of nScriptType + LATIN : 0x001 + ASIAN : 0x002 + COMPLEX: 0x004 + */ + const sal_Int16 nMaxCount = 7; + if ( !xDocumentLanguages.is() ) + return; + + const Sequence< Locale > rLocales( xDocumentLanguages->getDocumentLanguages( static_cast(nScriptType), nMaxCount )); + for ( const Locale& rLocale : rLocales ) + { + if ( rLangItems.size() == static_cast< size_t >(nMaxCount) ) + break; + if( IsScriptTypeMatchingToLanguage( nScriptType, SvtLanguageTable::GetLanguageType( rLocale.Language ))) + rLangItems.insert( rLocale.Language ); + } +} + +auto (*g_pGetMultiplexerListener)( + css::uno::Reference const & xComponentContext, + uno::Reference const&, + std::function const&)> const&) + -> uno::Reference = nullptr; + +uno::Reference +GetFirstListenerWith_Impl( + css::uno::Reference const & xComponentContext, + uno::Reference const& xEventFocus, + std::function const&)> const& rPredicate) +{ + assert(g_pGetMultiplexerListener != nullptr); // should not be called too early, nor too late + return g_pGetMultiplexerListener(xComponentContext, xEventFocus, rPredicate); +} + + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/fwi/helper/shareablemutex.cxx b/framework/source/fwi/helper/shareablemutex.cxx new file mode 100644 index 0000000000..cd3b6bd18d --- /dev/null +++ b/framework/source/fwi/helper/shareablemutex.cxx @@ -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 . + */ + +#include + +namespace framework +{ +ShareableMutex::ShareableMutex() +{ + m_pMutexRef = new MutexRef; + m_pMutexRef->acquire(); +} + +ShareableMutex::ShareableMutex(const ShareableMutex& rShareableMutex) +{ + m_pMutexRef = rShareableMutex.m_pMutexRef; + m_pMutexRef->acquire(); +} + +ShareableMutex& ShareableMutex::operator=(const ShareableMutex& rShareableMutex) +{ + rShareableMutex.m_pMutexRef->acquire(); + m_pMutexRef->release(); + m_pMutexRef = rShareableMutex.m_pMutexRef; + return *this; +} + +void ShareableMutex::acquire() { m_pMutexRef->m_oslMutex.acquire(); } + +void ShareableMutex::release() { m_pMutexRef->m_oslMutex.release(); } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/fwi/jobs/configaccess.cxx b/framework/source/fwi/jobs/configaccess.cxx new file mode 100644 index 0000000000..045da5ba40 --- /dev/null +++ b/framework/source/fwi/jobs/configaccess.cxx @@ -0,0 +1,184 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include + +#include +#include +#include +#include + +#include + +namespace framework{ + +/** + @short open the configuration of this job + @descr We open the configuration of this job only. Not the whole package or the whole + job set. We are interested on our own properties only. + We set the opened configuration access as our member. So any following method, + which needs cfg access, can use it. That prevent us against multiple open/close requests. + But you can use this method to upgrade an already opened configuration too. + + @param eMode + force opening of the configuration access in readonly or in read/write mode + */ +ConfigAccess::ConfigAccess( /*IN*/ css::uno::Reference< css::uno::XComponentContext > xContext, + /*IN*/ OUString sRoot ) + : m_xContext (std::move( xContext)) + , m_sRoot (std::move( sRoot )) + , m_eMode ( E_CLOSED ) +{ +} + +/** + @short last chance to close an open configuration access point + @descr In case our user forgot to close this configuration point + in the right way, normally he will run into some trouble - + e.g. losing data. + */ +ConfigAccess::~ConfigAccess() +{ + close(); +} + +/** + @short return the internal mode of this instance + @descr May be the outside user need any information about successfully opened + or closed config access point objects. He can control the internal mode to do so. + + @return The internal open state of this object. + */ +ConfigAccess::EOpenMode ConfigAccess::getMode() const +{ + std::unique_lock g(m_mutex); + return m_eMode; +} + +/** + @short open the configuration access in the specified mode + @descr We set the opened configuration access as our member. So any following method, + which needs cfg access, can use it. That prevent us against multiple open/close requests. + But you can use this method to upgrade an already opened configuration too. + It's possible to open a config access in READONLY mode first and "open" it at a second + time within the mode READWRITE. Then we will upgrade it. Downgrade will be possible too. + + But note: closing will be done explicitly by calling method close() ... not by + downgrading with mode CLOSED! + + @param eMode + force (re)opening of the configuration access in readonly or in read/write mode + */ +void ConfigAccess::open( /*IN*/ EOpenMode eMode ) +{ + std::unique_lock g(m_mutex); + + // check if configuration is already open in the right mode. + // By the way: Don't allow closing by using this method! + if ( eMode == E_CLOSED || m_eMode == eMode ) + return; + + // We have to close the old access point without any question here. + // It will be open again using the new mode. + // can be called without checks! It does the checks by itself ... + // e.g. for already closed or not opened configuration. + // Flushing of all made changes will be done here too. + closeImpl(); + + // create the configuration provider, which provides sub access points + css::uno::Reference< css::lang::XMultiServiceFactory > xConfigProvider = css::configuration::theDefaultProvider::get(m_xContext); + css::beans::PropertyValue aParam; + aParam.Name = "nodepath"; + aParam.Value <<= m_sRoot; + + css::uno::Sequence< css::uno::Any > lParams{ css::uno::Any(aParam) }; + + // open it + try + { + if (eMode==E_READONLY) + m_xConfig = xConfigProvider->createInstanceWithArguments(SERVICENAME_CFGREADACCESS , lParams); + else + if (eMode==E_READWRITE) + m_xConfig = xConfigProvider->createInstanceWithArguments(SERVICENAME_CFGUPDATEACCESS, lParams); + } + catch(const css::uno::Exception&) + { + TOOLS_INFO_EXCEPTION("fwk", "open config"); + } + + m_eMode = E_CLOSED; + if (m_xConfig.is()) + m_eMode = eMode; +} + +/** + @short close the internal opened configuration access and flush all changes + @descr It checks, if the given access is valid and react in the right way. + It flushes all changes ... so nobody else must know this state. + */ +void ConfigAccess::close() +{ + std::unique_lock g(m_mutex); + closeImpl(); +} + +void ConfigAccess::closeImpl() +{ + // check already closed configuration + if (m_xConfig.is()) + { + css::uno::Reference< css::util::XChangesBatch > xFlush(m_xConfig, css::uno::UNO_QUERY); + if (xFlush.is()) + xFlush->commitChanges(); + m_xConfig.clear(); + m_eMode = E_CLOSED; + } +} + +/** + @short provides an access to the internal wrapped configuration access + @descr It's not allowed to safe this c++ (!) reference outside. You have + to use it directly. Further you must use our public lock member m_aLock + to synchronize your code with our internal structures and our interface + methods. Acquire it before you call cfg() and release it afterwards immediately. + + E.g.: ConfigAccess aAccess(...); + Guard aReadLock(aAccess.m_aLock); + Reference< XPropertySet > xSet(aAccess.cfg(), UNO_QUERY); + Any aProp = xSet->getPropertyValue("..."); + aReadLock.unlock(); + + @attention During this time it's not allowed to call the methods open() or close()! + Otherwise you will change your own referenced config access. Anything will + be possible then. + + @return A c++(!) reference to the uno instance of the configuration access point. + */ +const css::uno::Reference< css::uno::XInterface >& ConfigAccess::cfg() +{ + // must be synchronized from outside! + // => no lock here ... + return m_xConfig; +} + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/fwi/threadhelp/transactionmanager.cxx b/framework/source/fwi/threadhelp/transactionmanager.cxx new file mode 100644 index 0000000000..86d5b354a0 --- /dev/null +++ b/framework/source/fwi/threadhelp/transactionmanager.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 +#include + +#include + +#include + +namespace framework{ + +/*-************************************************************************************************************ + @short standard ctor + @descr Initialize instance with right start values for correct working. +*//*-*************************************************************************************************************/ +TransactionManager::TransactionManager() + : m_eWorkingMode ( E_INIT ) + , m_nTransactionCount ( 0 ) +{ + m_aBarrier.open(); +} + +/*-************************************************************************************************************ + @short standard dtor +*//*-*************************************************************************************************************/ +TransactionManager::~TransactionManager() +{ +} + +/*-**************************************************************************************************** + @short set new working mode + @descr These implementation knows for states of working: E_INIT, E_WORK, E_BEFORECLOSE, E_CLOSE + You can step during this ones only from the left to the right side and start at left side again! + (This is necessary e.g. for refcounted objects!) + This call will block till all current existing transactions was finished. + Following results can occur: + E_INIT : All requests on this implementation are refused. + It's your decision to react in a right way. + + E_WORK : The object can work now. The full functionality is available. + + E_BEFORECLOSE : The object start the closing mechanism ... but sometimes + e.g. the dispose() method need to call some private methods. + These some special methods should use E_SOFTEXCEPTIONS + to detect this special case! + + E_CLOSE : Object is already dead! All further requests will be refused. + It's your decision to react in a right way. + @param "eMode", is the new mode - but we don't accept setting mode in wrong order! + @onerror We do nothing. +*//*-*****************************************************************************************************/ +void TransactionManager::setWorkingMode( EWorkingMode eMode ) +{ + // Safe member access. + bool bWaitFor = false; + { + std::unique_lock aAccessGuard(m_aAccessLock); + // Change working mode first! + if ( + (m_eWorkingMode == E_INIT && eMode == E_WORK) || + ((m_eWorkingMode == E_WORK || m_eWorkingMode == E_INIT) && eMode == E_BEFORECLOSE) || + (m_eWorkingMode == E_BEFORECLOSE && eMode == E_CLOSE) || + (m_eWorkingMode == E_CLOSE && eMode == E_INIT) + ) + { + m_eWorkingMode = eMode; + if (m_eWorkingMode == E_BEFORECLOSE || m_eWorkingMode == E_CLOSE) + { + bWaitFor = true; + } + } + } + // Wait for current existing transactions then! + // (Only necessary for changing to E_BEFORECLOSE or E_CLOSE!... + // otherwise; if you wait at setting E_WORK another thread could finish an acquire-call during our unlock() and wait() call + // ... and we will wait forever here!!!) + // Don't forget to release access mutex before. + if( bWaitFor ) + { + m_aBarrier.wait(); + } +} + +/*-**************************************************************************************************** + @short get current working mode + @descr If you stand in your close() or init() method ... but don't know + if you called more than ones(!) ... you can use this function to get + right information. + e.g: You have a method init() which is used to change working mode from + E_INIT to E_WORK and should be used to initialize some member too ... + What should you do: + + void init( sal_Int32 nValue ) + { + // Reject this call if our transaction manager say: "Object already initialized!" + // Otherwise initialize your member. + if( m_aTransactionManager.getWorkingMode() == E_INIT ) + { + // Object is uninitialized ... + // Make member access threadsafe! + Guard aGuard( m_aMutex ); + + // Check working mode again .. because another instance could be faster. + // (It's possible to set this guard at first of this method too!) + if( m_aTransactionManager.getWorkingMode() == E_INIT ) + { + m_aMember = nValue; + + // Object is initialized now ... set working mode to E_WORK! + m_aTransactionManager.setWorkingMode( E_WORK ); + } + } + } + + @seealso method setWorkingMode() + @return Current set mode. + + @onerror No error should occur. +*//*-*****************************************************************************************************/ +EWorkingMode TransactionManager::getWorkingMode() const +{ + // Synchronize access to internal member! + std::unique_lock aAccessLock( m_aAccessLock ); + return m_eWorkingMode; +} + +/*-**************************************************************************************************** + @short start new transaction + @descr A guard should use this method to start a new transaction. He should look for rejected + calls to by using parameter eMode and eReason. + If call was not rejected your transaction will be non breakable during releasing your transaction + guard! BUT ... your code isn't threadsafe then! It's a transaction manager only... + + @seealso method unregisterTransaction() + + @param "eMode" ,used to enable/disable throwing exceptions automatically for rejected calls +*//*-*****************************************************************************************************/ +void TransactionManager::registerTransaction( EExceptionMode eMode ) +{ + std::unique_lock aAccessGuard( m_aAccessLock ); + switch( m_eWorkingMode ) + { + case E_INIT: + if( eMode == E_HARDEXCEPTIONS ) + { + // Help programmer to find out, why this exception is thrown! + SAL_WARN( "fwk", "TransactionManager...: Owner instance not correctly initialized yet. Call was rejected! Normally it's an algorithm error ... wrong use of class!" ); + //ATTENTION: temp. disabled - till all bad code positions are detected and changed! */ + // throw css::uno::RuntimeException( "TransactionManager.: Owner instance not right initialized yet. Call was rejected! Normally it's an algorithm error... wrong using of class!\n", css::uno::Reference< css::uno::XInterface >() ); + } + break; + case E_WORK: + break; + case E_BEFORECLOSE: + if( eMode == E_HARDEXCEPTIONS ) + { + // Help programmer to find out, why this exception is thrown! + SAL_WARN( "fwk", "TransactionManager...: Owner instance stand in close method. Call was rejected!" ); + throw css::lang::DisposedException( "TransactionManager: Owner instance stand in close method. Call was rejected!" ); + } + break; + case E_CLOSE: + // Help programmer to find out, why this exception is thrown! + SAL_WARN( "fwk", "TransactionManager...: Owner instance already closed. Call was rejected!" ); + throw css::lang::DisposedException( "TransactionManager: Owner instance already closed. Call was rejected!" ); + } + + // Register this new transaction. + // If it is the first one .. close gate to disable changing of working mode. + ++m_nTransactionCount; + if( m_nTransactionCount == 1 ) + { + m_aBarrier.close(); + } +} + +/*-**************************************************************************************************** + @short finish transaction + @descr A guard should call this method to release current transaction. + + @seealso method registerTransaction() +*//*-*****************************************************************************************************/ +void TransactionManager::unregisterTransaction() +{ + // This call could not rejected! + // Safe access to internal member. + std::unique_lock aAccessGuard( m_aAccessLock ); + + // Deregister this transaction. + // If it was the last one ... open gate to enable changing of working mode! + // (see setWorkingMode()) + + --m_nTransactionCount; + if( m_nTransactionCount == 0 ) + { + m_aBarrier.open(); + } +} + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/fwi/uielement/constitemcontainer.cxx b/framework/source/fwi/uielement/constitemcontainer.cxx new file mode 100644 index 0000000000..40865c8f1b --- /dev/null +++ b/framework/source/fwi/uielement/constitemcontainer.cxx @@ -0,0 +1,276 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include + +#include +#include + +#include +#include +#include + +using namespace cppu; +using namespace com::sun::star::uno; +using namespace com::sun::star::lang; +using namespace com::sun::star::beans; +using namespace com::sun::star::container; + +const int PROPHANDLE_UINAME = 1; +constexpr OUString PROPNAME_UINAME = u"UIName"_ustr; + +namespace framework +{ + +ConstItemContainer::ConstItemContainer() +{ +} + +ConstItemContainer::ConstItemContainer( const ItemContainer& rItemContainer ) +{ + ShareGuard aLock( rItemContainer.m_aShareMutex ); + copyItemContainer( rItemContainer.m_aItemVector ); +} + +ConstItemContainer::ConstItemContainer( const Reference< XIndexAccess >& rSourceContainer, bool bFastCopy ) +{ + // We also have to copy the UIName property + try + { + Reference< XPropertySet > xPropSet( rSourceContainer, UNO_QUERY ); + if ( xPropSet.is() ) + { + xPropSet->getPropertyValue("UIName") >>= m_aUIName; + } + } + catch ( const Exception& ) + { + } + + if ( !rSourceContainer.is() ) + return; + + try + { + sal_Int32 nCount = rSourceContainer->getCount(); + m_aItemVector.reserve(nCount); + if ( bFastCopy ) + { + for ( sal_Int32 i = 0; i < nCount; i++ ) + { + Sequence< PropertyValue > aPropSeq; + if ( rSourceContainer->getByIndex( i ) >>= aPropSeq ) + m_aItemVector.push_back( aPropSeq ); + } + } + else + { + for ( sal_Int32 i = 0; i < nCount; i++ ) + { + Sequence< PropertyValue > aPropSeq; + if ( rSourceContainer->getByIndex( i ) >>= aPropSeq ) + { + sal_Int32 nContainerIndex = -1; + Reference< XIndexAccess > xIndexAccess; + for ( sal_Int32 j = 0; j < aPropSeq.getLength(); j++ ) + { + if ( aPropSeq[j].Name == "ItemDescriptorContainer" ) + { + aPropSeq[j].Value >>= xIndexAccess; + nContainerIndex = j; + break; + } + } + + if ( xIndexAccess.is() && nContainerIndex >= 0 ) + aPropSeq.getArray()[nContainerIndex].Value <<= deepCopyContainer( xIndexAccess ); + + m_aItemVector.push_back( aPropSeq ); + } + } + } + } + catch ( const IndexOutOfBoundsException& ) + { + } +} + +ConstItemContainer::~ConstItemContainer() +{ +} + +// private +void ConstItemContainer::copyItemContainer( const std::vector< Sequence< PropertyValue > >& rSourceVector ) +{ + const sal_uInt32 nCount = rSourceVector.size(); + for ( sal_uInt32 i = 0; i < nCount; i++ ) + { + sal_Int32 nContainerIndex = -1; + Sequence< PropertyValue > aPropSeq( rSourceVector[i] ); + Reference< XIndexAccess > xIndexAccess; + for ( sal_Int32 j = 0; j < aPropSeq.getLength(); j++ ) + { + if ( aPropSeq[j].Name == "ItemDescriptorContainer" ) + { + aPropSeq[j].Value >>= xIndexAccess; + nContainerIndex = j; + break; + } + } + + if ( xIndexAccess.is() && nContainerIndex >= 0 ) + aPropSeq.getArray()[nContainerIndex].Value <<= deepCopyContainer( xIndexAccess ); + + m_aItemVector.push_back( aPropSeq ); + } +} + +Reference< XIndexAccess > ConstItemContainer::deepCopyContainer( const Reference< XIndexAccess >& rSubContainer ) +{ + Reference< XIndexAccess > xReturn; + if ( rSubContainer.is() ) + { + ItemContainer* pSource = dynamic_cast( rSubContainer.get() ); + rtl::Reference pSubContainer; + if ( pSource ) + pSubContainer = new ConstItemContainer( *pSource ); + else + pSubContainer = new ConstItemContainer( rSubContainer ); + xReturn = pSubContainer; + } + + return xReturn; +} + +// XElementAccess +sal_Bool SAL_CALL ConstItemContainer::hasElements() +{ + return ( !m_aItemVector.empty() ); +} + +// XIndexAccess +sal_Int32 SAL_CALL ConstItemContainer::getCount() +{ + return m_aItemVector.size(); +} + +Any SAL_CALL ConstItemContainer::getByIndex( sal_Int32 Index ) +{ + if ( sal_Int32( m_aItemVector.size()) <= Index ) + throw IndexOutOfBoundsException( OUString(), static_cast(this) ); + return Any( m_aItemVector[Index] ); +} + +namespace +{ + std::vector makePropertyMap(const css::uno::Sequence& rProps) + { + std::vector aEntries; + for (auto const& it : rProps) + aEntries.emplace_back(it.Name, it.Handle, it.Type, it.Attributes, 0); + return aEntries; + } +} + +// XPropertySet +Reference< XPropertySetInfo > SAL_CALL ConstItemContainer::getPropertySetInfo() +{ + // Create structure of propertysetinfo for baseclass "OPropertySetHelper". + // (Use method "getInfoHelper()".) + static std::vector aPropertyInfos(makePropertyMap(getInfoHelper().getProperties())); + static Reference< XPropertySetInfo > xInfo(new comphelper::PropertySetInfo(aPropertyInfos)); + + return xInfo; +} + +void SAL_CALL ConstItemContainer::setPropertyValue( const OUString&, const Any& ) +{ +} + +Any SAL_CALL ConstItemContainer::getPropertyValue( const OUString& PropertyName ) +{ + if ( PropertyName == PROPNAME_UINAME ) + return Any( m_aUIName ); + + throw UnknownPropertyException(PropertyName); +} + +void SAL_CALL ConstItemContainer::addPropertyChangeListener( const OUString&, const css::uno::Reference< css::beans::XPropertyChangeListener >& ) +{ +} + +void SAL_CALL ConstItemContainer::removePropertyChangeListener( const OUString&, const css::uno::Reference< css::beans::XPropertyChangeListener >& ) +{ + // Only read-only properties - do nothing +} + +void SAL_CALL ConstItemContainer::addVetoableChangeListener( const OUString&, const css::uno::Reference< css::beans::XVetoableChangeListener >& ) +{ + // Only read-only properties - do nothing +} + +void SAL_CALL ConstItemContainer::removeVetoableChangeListener( const OUString&, const css::uno::Reference< css::beans::XVetoableChangeListener >& ) +{ + // Only read-only properties - do nothing +} + +// XFastPropertySet +void SAL_CALL ConstItemContainer::setFastPropertyValue( sal_Int32, const css::uno::Any& ) +{ +} + +Any SAL_CALL ConstItemContainer::getFastPropertyValue( sal_Int32 nHandle ) +{ + if ( nHandle == PROPHANDLE_UINAME ) + return Any( m_aUIName ); + + throw UnknownPropertyException(OUString::number(nHandle)); +} + +::cppu::IPropertyArrayHelper& ConstItemContainer::getInfoHelper() +{ + // Define static member to give structure of properties to baseclass "OPropertySetHelper". + // "impl_getStaticPropertyDescriptor" is a non exported and static function, who will define a static propertytable. + // "true" say: Table is sorted by name. + static ::cppu::OPropertyArrayHelper ourInfoHelper( impl_getStaticPropertyDescriptor(), true ); + + return ourInfoHelper; +} + +css::uno::Sequence< css::beans::Property > ConstItemContainer::impl_getStaticPropertyDescriptor() +{ + // Create a property array to initialize sequence! + // Table of all predefined properties of this class. It's used from OPropertySetHelper-class! + // Don't forget to change the defines (see begin of this file), if you add, change or delete a property in this list!!! + // It's necessary for methods of OPropertySetHelper. + // ATTENTION: + // YOU MUST SORT FOLLOW TABLE BY NAME ALPHABETICAL !!! + + return + { + css::beans::Property( PROPNAME_UINAME, PROPHANDLE_UINAME , + cppu::UnoType::get(), + css::beans::PropertyAttribute::TRANSIENT | css::beans::PropertyAttribute::READONLY ) + }; +} + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/fwi/uielement/itemcontainer.cxx b/framework/source/fwi/uielement/itemcontainer.cxx new file mode 100644 index 0000000000..22bd077fce --- /dev/null +++ b/framework/source/fwi/uielement/itemcontainer.cxx @@ -0,0 +1,206 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include +#include +#include + +using namespace cppu; +using namespace com::sun::star::uno; +using namespace com::sun::star::lang; +using namespace com::sun::star::beans; +using namespace com::sun::star::container; + +constexpr OUString WRONG_TYPE_EXCEPTION + = u"Type must be css::uno::Sequence< css::beans::PropertyValue >"_ustr; + +namespace framework +{ + +// XInterface, XTypeProvider + +ItemContainer::ItemContainer( const ShareableMutex& rMutex ) : + m_aShareMutex( rMutex ) +{ +} + +ItemContainer::ItemContainer( const ConstItemContainer& rConstItemContainer, const ShareableMutex& rMutex ) : m_aShareMutex( rMutex ) +{ + copyItemContainer( rConstItemContainer.m_aItemVector, rMutex ); +} + +ItemContainer::ItemContainer( const Reference< XIndexAccess >& rSourceContainer, const ShareableMutex& rMutex ) : + m_aShareMutex( rMutex ) +{ + if ( !rSourceContainer.is() ) + return; + + sal_Int32 nCount = rSourceContainer->getCount(); + try + { + for ( sal_Int32 i = 0; i < nCount; i++ ) + { + Sequence< PropertyValue > aPropSeq; + if ( rSourceContainer->getByIndex( i ) >>= aPropSeq ) + { + sal_Int32 nContainerIndex = -1; + Reference< XIndexAccess > xIndexAccess; + for ( sal_Int32 j = 0; j < aPropSeq.getLength(); j++ ) + { + if ( aPropSeq[j].Name == "ItemDescriptorContainer" ) + { + aPropSeq[j].Value >>= xIndexAccess; + nContainerIndex = j; + break; + } + } + + if ( xIndexAccess.is() && nContainerIndex >= 0 ) + aPropSeq.getArray()[nContainerIndex].Value <<= deepCopyContainer( xIndexAccess, rMutex ); + + m_aItemVector.push_back( aPropSeq ); + } + } + } + catch ( const IndexOutOfBoundsException& ) + { + } +} + +ItemContainer::~ItemContainer() +{ +} + +// private +void ItemContainer::copyItemContainer( const std::vector< Sequence< PropertyValue > >& rSourceVector, const ShareableMutex& rMutex ) +{ + const sal_uInt32 nCount = rSourceVector.size(); + for ( sal_uInt32 i = 0; i < nCount; ++i ) + { + sal_Int32 nContainerIndex = -1; + Sequence< PropertyValue > aPropSeq( rSourceVector[i] ); + Reference< XIndexAccess > xIndexAccess; + for ( sal_Int32 j = 0; j < aPropSeq.getLength(); j++ ) + { + if ( aPropSeq[j].Name == "ItemDescriptorContainer" ) + { + aPropSeq[j].Value >>= xIndexAccess; + nContainerIndex = j; + break; + } + } + + if ( xIndexAccess.is() && nContainerIndex >= 0 ) + aPropSeq.getArray()[nContainerIndex].Value <<= deepCopyContainer( xIndexAccess, rMutex ); + + m_aItemVector.push_back( aPropSeq ); + } +} + +Reference< XIndexAccess > ItemContainer::deepCopyContainer( const Reference< XIndexAccess >& rSubContainer, const ShareableMutex& rMutex ) +{ + Reference< XIndexAccess > xReturn; + if ( rSubContainer.is() ) + { + ConstItemContainer* pSource = dynamic_cast( rSubContainer.get() ); + rtl::Reference pSubContainer; + if ( pSource ) + pSubContainer = new ItemContainer( *pSource, rMutex ); + else + pSubContainer = new ItemContainer( rSubContainer, rMutex ); + xReturn = pSubContainer; + } + + return xReturn; +} + +// XElementAccess +sal_Bool SAL_CALL ItemContainer::hasElements() +{ + ShareGuard aLock( m_aShareMutex ); + return ( !m_aItemVector.empty() ); +} + +// XIndexAccess +sal_Int32 SAL_CALL ItemContainer::getCount() +{ + ShareGuard aLock( m_aShareMutex ); + return m_aItemVector.size(); +} + +Any SAL_CALL ItemContainer::getByIndex( sal_Int32 Index ) +{ + ShareGuard aLock( m_aShareMutex ); + if ( sal_Int32( m_aItemVector.size()) <= Index ) + throw IndexOutOfBoundsException( OUString(), static_cast(this) ); + + return Any( m_aItemVector[Index] ); +} + +// XIndexContainer +void SAL_CALL ItemContainer::insertByIndex( sal_Int32 Index, const Any& aItem ) +{ + Sequence< PropertyValue > aSeq; + if ( !(aItem >>= aSeq) ) + throw IllegalArgumentException( WRONG_TYPE_EXCEPTION, + static_cast(this), 2 ); + + ShareGuard aLock( m_aShareMutex ); + if ( sal_Int32( m_aItemVector.size()) == Index ) + m_aItemVector.push_back( aSeq ); + else if ( sal_Int32( m_aItemVector.size()) >Index ) + { + std::vector< Sequence< PropertyValue > >::iterator aIter = m_aItemVector.begin(); + aIter += Index; + m_aItemVector.insert( aIter, aSeq ); + } + else + throw IndexOutOfBoundsException( OUString(), static_cast(this) ); +} + +void SAL_CALL ItemContainer::removeByIndex( sal_Int32 nIndex ) +{ + ShareGuard aLock( m_aShareMutex ); + if ( static_cast(m_aItemVector.size()) <= nIndex ) + throw IndexOutOfBoundsException( OUString(), static_cast(this) ); + + m_aItemVector.erase(m_aItemVector.begin() + nIndex); +} + +void SAL_CALL ItemContainer::replaceByIndex( sal_Int32 Index, const Any& aItem ) +{ + Sequence< PropertyValue > aSeq; + if ( !(aItem >>= aSeq) ) + throw IllegalArgumentException( WRONG_TYPE_EXCEPTION, + static_cast(this), 2 ); + + ShareGuard aLock( m_aShareMutex ); + if ( sal_Int32( m_aItemVector.size()) <= Index ) + throw IndexOutOfBoundsException( OUString(), static_cast(this) ); + + m_aItemVector[Index] = aSeq; +} + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/fwi/uielement/rootitemcontainer.cxx b/framework/source/fwi/uielement/rootitemcontainer.cxx new file mode 100644 index 0000000000..652f0dd802 --- /dev/null +++ b/framework/source/fwi/uielement/rootitemcontainer.cxx @@ -0,0 +1,301 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +using namespace cppu; +using namespace com::sun::star::uno; +using namespace com::sun::star::lang; +using namespace com::sun::star::beans; +using namespace com::sun::star::container; + +constexpr OUString WRONG_TYPE_EXCEPTION + = u"Type must be css::uno::Sequence< css::beans::PropertyValue >"_ustr; + +const int PROPHANDLE_UINAME = 1; +constexpr OUString PROPNAME_UINAME = u"UIName"_ustr; + +namespace framework +{ + +RootItemContainer::RootItemContainer() + : ::cppu::OBroadcastHelperVar< ::cppu::OMultiTypeInterfaceContainerHelper, ::cppu::OMultiTypeInterfaceContainerHelper::keyType >( m_aMutex ) + , ::cppu::OPropertySetHelper ( *static_cast< ::cppu::OBroadcastHelper* >(this) ) +{ +} + +RootItemContainer::RootItemContainer( const Reference< XIndexAccess >& rSourceContainer ) + : ::cppu::OBroadcastHelperVar< ::cppu::OMultiTypeInterfaceContainerHelper, ::cppu::OMultiTypeInterfaceContainerHelper::keyType >( m_aMutex ) + , ::cppu::OPropertySetHelper ( *static_cast< ::cppu::OBroadcastHelper* >(this) ) +{ + // We also have to copy the UIName property + try + { + Reference< XPropertySet > xPropSet( rSourceContainer, UNO_QUERY ); + if ( xPropSet.is() ) + { + xPropSet->getPropertyValue("UIName") >>= m_aUIName; + } + } + catch ( const Exception& ) + { + } + + if ( !rSourceContainer.is() ) + return; + + sal_Int32 nCount = rSourceContainer->getCount(); + try + { + for ( sal_Int32 i = 0; i < nCount; i++ ) + { + Sequence< PropertyValue > aPropSeq; + if ( rSourceContainer->getByIndex( i ) >>= aPropSeq ) + { + sal_Int32 nContainerIndex = -1; + Reference< XIndexAccess > xIndexAccess; + for ( sal_Int32 j = 0; j < aPropSeq.getLength(); j++ ) + { + if ( aPropSeq[j].Name == "ItemDescriptorContainer" ) + { + aPropSeq[j].Value >>= xIndexAccess; + nContainerIndex = j; + break; + } + } + + if ( xIndexAccess.is() && nContainerIndex >= 0 ) + aPropSeq.getArray()[nContainerIndex].Value <<= deepCopyContainer( xIndexAccess ); + + m_aItemVector.push_back( aPropSeq ); + } + } + } + catch ( const IndexOutOfBoundsException& ) + { + } +} + +RootItemContainer::~RootItemContainer() +{ +} + +Any SAL_CALL RootItemContainer::queryInterface( const Type& _rType ) +{ + Any aRet = RootItemContainer_BASE::queryInterface( _rType ); + if ( !aRet.hasValue() ) + aRet = OPropertySetHelper::queryInterface( _rType ); + return aRet; +} + +Sequence< Type > SAL_CALL RootItemContainer::getTypes( ) +{ + return comphelper::concatSequences( + RootItemContainer_BASE::getTypes(), + ::cppu::OPropertySetHelper::getTypes() + ); +} + +Reference< XIndexAccess > RootItemContainer::deepCopyContainer( const Reference< XIndexAccess >& rSubContainer ) +{ + Reference< XIndexAccess > xReturn; + if ( rSubContainer.is() ) + { + ConstItemContainer* pSource = dynamic_cast( rSubContainer.get() ); + rtl::Reference pSubContainer; + if ( pSource ) + pSubContainer = new ItemContainer( *pSource, m_aShareMutex ); + else + pSubContainer = new ItemContainer( rSubContainer, m_aShareMutex ); + xReturn = pSubContainer; + } + + return xReturn; +} + +// XElementAccess +sal_Bool SAL_CALL RootItemContainer::hasElements() +{ + ShareGuard aLock( m_aShareMutex ); + return ( !m_aItemVector.empty() ); +} + +// XIndexAccess +sal_Int32 SAL_CALL RootItemContainer::getCount() +{ + ShareGuard aLock( m_aShareMutex ); + return m_aItemVector.size(); +} + +Any SAL_CALL RootItemContainer::getByIndex( sal_Int32 Index ) +{ + ShareGuard aLock( m_aShareMutex ); + if ( sal_Int32( m_aItemVector.size()) <= Index ) + throw IndexOutOfBoundsException( OUString(), static_cast(this) ); + + return Any( m_aItemVector[Index] ); +} + +// XIndexContainer +void SAL_CALL RootItemContainer::insertByIndex( sal_Int32 Index, const Any& aItem ) +{ + Sequence< PropertyValue > aSeq; + if ( !(aItem >>= aSeq) ) + throw IllegalArgumentException( WRONG_TYPE_EXCEPTION, static_cast(this), 2 ); + + ShareGuard aLock( m_aShareMutex ); + if ( sal_Int32( m_aItemVector.size()) == Index ) + m_aItemVector.push_back( aSeq ); + else if ( sal_Int32( m_aItemVector.size()) >Index ) + { + std::vector< Sequence< PropertyValue > >::iterator aIter = m_aItemVector.begin(); + aIter += Index; + m_aItemVector.insert( aIter, aSeq ); + } + else + throw IndexOutOfBoundsException( OUString(), static_cast(this) ); +} + +void SAL_CALL RootItemContainer::removeByIndex( sal_Int32 nIndex ) +{ + ShareGuard aLock( m_aShareMutex ); + if ( static_cast(m_aItemVector.size()) <= nIndex ) + throw IndexOutOfBoundsException( OUString(), static_cast(this) ); + + m_aItemVector.erase(m_aItemVector.begin() + nIndex); +} + +void SAL_CALL RootItemContainer::replaceByIndex( sal_Int32 Index, const Any& aItem ) +{ + Sequence< PropertyValue > aSeq; + if ( !(aItem >>= aSeq) ) + throw IllegalArgumentException( WRONG_TYPE_EXCEPTION, static_cast(this), 2 ); + + ShareGuard aLock( m_aShareMutex ); + if ( sal_Int32( m_aItemVector.size()) <= Index ) + throw IndexOutOfBoundsException( OUString(), static_cast(this) ); + + m_aItemVector[Index] = aSeq; +} + +Reference< XInterface > SAL_CALL RootItemContainer::createInstanceWithContext( const Reference< XComponentContext >& ) +{ + return static_cast(new ItemContainer( m_aShareMutex )); +} + +Reference< XInterface > SAL_CALL RootItemContainer::createInstanceWithArgumentsAndContext( const Sequence< Any >&, const Reference< XComponentContext >& ) +{ + return static_cast(new ItemContainer( m_aShareMutex )); +} + +// XPropertySet helper +sal_Bool SAL_CALL RootItemContainer::convertFastPropertyValue( Any& aConvertedValue , + Any& aOldValue , + sal_Int32 nHandle , + const Any& aValue ) +{ + // Initialize state with sal_False !!! + // (Handle can be invalid) + bool bReturn = false; + + switch( nHandle ) + { + case PROPHANDLE_UINAME: + bReturn = PropHelper::willPropertyBeChanged( + css::uno::Any(m_aUIName), + aValue, + aOldValue, + aConvertedValue); + break; + } + + // Return state of operation. + return bReturn; +} + +void SAL_CALL RootItemContainer::setFastPropertyValue_NoBroadcast( sal_Int32 nHandle , + const css::uno::Any& aValue ) +{ + switch( nHandle ) + { + case PROPHANDLE_UINAME: + aValue >>= m_aUIName; + break; + } +} + +void SAL_CALL RootItemContainer::getFastPropertyValue( css::uno::Any& aValue , + sal_Int32 nHandle ) const +{ + switch( nHandle ) + { + case PROPHANDLE_UINAME: + aValue <<= m_aUIName; + break; + } +} + +::cppu::IPropertyArrayHelper& SAL_CALL RootItemContainer::getInfoHelper() +{ + // Define static member to give structure of properties to baseclass "OPropertySetHelper". + // "impl_getStaticPropertyDescriptor" is a non exported and static function, who will define a static propertytable. + // "true" say: Table is sorted by name. + static ::cppu::OPropertyArrayHelper ourInfoHelper( impl_getStaticPropertyDescriptor(), true ); + + return ourInfoHelper; +} + +css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL RootItemContainer::getPropertySetInfo() +{ + // Create structure of propertysetinfo for baseclass "OPropertySetHelper". + // (Use method "getInfoHelper()".) + static css::uno::Reference< css::beans::XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) ); + + return xInfo; +} + +css::uno::Sequence< css::beans::Property > RootItemContainer::impl_getStaticPropertyDescriptor() +{ + // Create a property array to initialize sequence! + // Table of all predefined properties of this class. It's used from OPropertySetHelper-class! + // Don't forget to change the defines (see begin of this file), if you add, change or delete a property in this list!!! + // It's necessary for methods of OPropertySetHelper. + // ATTENTION: + // YOU MUST SORT FOLLOW TABLE BY NAME ALPHABETICAL !!! + + return + { + css::beans::Property( PROPNAME_UINAME, PROPHANDLE_UINAME , + cppu::UnoType::get(), + css::beans::PropertyAttribute::TRANSIENT ) + }; +} + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/helper/dockingareadefaultacceptor.cxx b/framework/source/helper/dockingareadefaultacceptor.cxx new file mode 100644 index 0000000000..59a43a1e32 --- /dev/null +++ b/framework/source/helper/dockingareadefaultacceptor.cxx @@ -0,0 +1,129 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include + +#include + +namespace framework{ + +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::frame; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::uno; +using namespace ::cppu; +using namespace ::osl; + +// constructor + +DockingAreaDefaultAcceptor::DockingAreaDefaultAcceptor( const css::uno::Reference< XFrame >& xOwner ) + : m_xOwner ( xOwner ) +{ +} + +// destructor + +DockingAreaDefaultAcceptor::~DockingAreaDefaultAcceptor() +{ +} + +// XDockingAreaAcceptor +css::uno::Reference< css::awt::XWindow > SAL_CALL DockingAreaDefaultAcceptor::getContainerWindow() +{ + SolarMutexGuard g; + + // Try to "lock" the frame for access to taskscontainer. + css::uno::Reference< XFrame > xFrame( m_xOwner ); + //TODO: check xFrame for null? + css::uno::Reference< css::awt::XWindow > xContainerWindow( xFrame->getContainerWindow() ); + + return xContainerWindow; +} + +sal_Bool SAL_CALL DockingAreaDefaultAcceptor::requestDockingAreaSpace( const css::awt::Rectangle& RequestedSpace ) +{ + // Try to "lock" the frame for access to taskscontainer. + css::uno::Reference< XFrame > xFrame( m_xOwner ); + + if ( !xFrame.is() ) + return false; + + css::uno::Reference< css::awt::XWindow > xContainerWindow( xFrame->getContainerWindow() ); + css::uno::Reference< css::awt::XWindow > xComponentWindow( xFrame->getComponentWindow() ); + + if ( !xContainerWindow.is() || !xComponentWindow.is() ) + return false; + + css::uno::Reference< css::awt::XDevice > xDevice( xContainerWindow, css::uno::UNO_QUERY ); + // Convert relative size to output size. + css::awt::Rectangle aRectangle = xContainerWindow->getPosSize(); + css::awt::DeviceInfo aInfo = xDevice->getInfo(); + css::awt::Size aSize ( aRectangle.Width - aInfo.LeftInset - aInfo.RightInset , + aRectangle.Height - aInfo.TopInset - aInfo.BottomInset ); + + css::awt::Size aMinSize( 0, 0 ); // = xLayoutConstraints->getMinimumSize(); + + // Check if request border space would decrease component window size below minimum size + if ((( aSize.Width - RequestedSpace.X - RequestedSpace.Width ) < aMinSize.Width ) || + (( aSize.Height - RequestedSpace.Y - RequestedSpace.Height ) < aMinSize.Height ) ) + return false; + + return true; +} + +void SAL_CALL DockingAreaDefaultAcceptor::setDockingAreaSpace( const css::awt::Rectangle& BorderSpace ) +{ + SolarMutexGuard g; + + // Try to "lock" the frame for access to taskscontainer. + css::uno::Reference< XFrame > xFrame( m_xOwner ); + if ( !xFrame.is() ) + return; + + css::uno::Reference< css::awt::XWindow > xContainerWindow( xFrame->getContainerWindow() ); + css::uno::Reference< css::awt::XWindow > xComponentWindow( xFrame->getComponentWindow() ); + + if ( !(xContainerWindow.is() && xComponentWindow.is()) ) + return; + + css::uno::Reference< css::awt::XDevice > xDevice( xContainerWindow, css::uno::UNO_QUERY ); + // Convert relative size to output size. + css::awt::Rectangle aRectangle = xContainerWindow->getPosSize(); + css::awt::DeviceInfo aInfo = xDevice->getInfo(); + css::awt::Size aSize ( aRectangle.Width - aInfo.LeftInset - aInfo.RightInset , + aRectangle.Height - aInfo.TopInset - aInfo.BottomInset ); + css::awt::Size aMinSize( 0, 0 );// = xLayoutConstraints->getMinimumSize(); + + // Check if request border space would decrease component window size below minimum size + sal_Int32 nWidth = aSize.Width - BorderSpace.X - BorderSpace.Width; + sal_Int32 nHeight = aSize.Height - BorderSpace.Y - BorderSpace.Height; + + if (( nWidth > aMinSize.Width ) && ( nHeight > aMinSize.Height )) + { + // Resize our component window. + xComponentWindow->setPosSize( BorderSpace.X, BorderSpace.Y, nWidth, nHeight, css::awt::PosSize::POSSIZE ); + } +} + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/helper/ocomponentaccess.cxx b/framework/source/helper/ocomponentaccess.cxx new file mode 100644 index 0000000000..ad7409990d --- /dev/null +++ b/framework/source/helper/ocomponentaccess.cxx @@ -0,0 +1,167 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include + +#include + +#include +#include + +namespace framework{ + +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::frame; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::uno; +using namespace ::cppu; +using namespace ::osl; + +// constructor + +OComponentAccess::OComponentAccess( const css::uno::Reference< XDesktop >& xOwner ) + : m_xOwner ( xOwner ) +{ + // Safe impossible cases + SAL_WARN_IF( !xOwner.is(), "fwk", "OComponentAccess::OComponentAccess(): Invalid parameter detected!" ); +} + +// destructor + +OComponentAccess::~OComponentAccess() +{ +} + +// XEnumerationAccess +css::uno::Reference< XEnumeration > SAL_CALL OComponentAccess::createEnumeration() +{ + SolarMutexGuard g; + + // Set default return value, if method failed. + // If no desktop exist and there is no task container - return an empty enumeration! + css::uno::Reference< XEnumeration > xReturn; + + // Try to "lock" the desktop for access to task container. + css::uno::Reference< XInterface > xLock = m_xOwner.get(); + if ( xLock.is() ) + { + // Desktop exist => pointer to task container must be valid. + // Initialize a new enumeration ... if some tasks and his components exist! + // (OTasksEnumeration will make an assert, if we initialize the new instance without valid values!) + + std::vector< css::uno::Reference< XComponent > > seqComponents; + impl_collectAllChildComponents( css::uno::Reference< XFramesSupplier >( xLock, UNO_QUERY ), seqComponents ); + xReturn = new OComponentEnumeration( std::move(seqComponents) ); + } + + // Return result of this operation. + return xReturn; +} + +// XElementAccess +Type SAL_CALL OComponentAccess::getElementType() +{ + // Elements in list an enumeration are components! + // Return the uno-type of XComponent. + return cppu::UnoType::get(); +} + +// XElementAccess +sal_Bool SAL_CALL OComponentAccess::hasElements() +{ + SolarMutexGuard g; + + // Set default return value, if method failed. + bool bReturn = false; + + // Try to "lock" the desktop for access to task container. + css::uno::Reference< XFramesSupplier > xLock( m_xOwner.get(), UNO_QUERY ); + if ( xLock.is() ) + { + // Ask container of owner for existing elements. + bReturn = xLock->getFrames()->hasElements(); + } + + // Return result of this operation. + return bReturn; +} + + +void OComponentAccess::impl_collectAllChildComponents( const css::uno::Reference< XFramesSupplier >& xNode , + std::vector< css::uno::Reference< XComponent > >& seqComponents ) +{ + // If valid node was given ... + if( !xNode.is() ) + return; + + // ... continue collection at these. + + // Get the container of current node, collect the components of existing child frames + // and go down to next level in tree (recursive!). + + const css::uno::Reference< XFrames > xContainer = xNode->getFrames(); + const Sequence< css::uno::Reference< XFrame > > seqFrames = xContainer->queryFrames( FrameSearchFlag::CHILDREN ); + + const sal_Int32 nFrameCount = seqFrames.getLength(); + for( sal_Int32 nFrame=0; nFrame xComponent = impl_getFrameComponent( seqFrames[nFrame] ); + if( xComponent.is() ) + { + seqComponents.push_back( xComponent ); + } + } + // ... otherwise break a recursive path and go back at current stack! +} + +css::uno::Reference< XComponent > OComponentAccess::impl_getFrameComponent( const css::uno::Reference< XFrame >& xFrame ) const +{ + // Set default return value, if method failed. + css::uno::Reference< XComponent > xComponent; + // Does no controller exists? + css::uno::Reference< XController > xController = xFrame->getController(); + if ( !xController.is() ) + { + // Controller not exist - use the VCL-component. + xComponent = xFrame->getComponentWindow(); + } + else + { + // Does no model exists? + css::uno::Reference< XModel > xModel = xController->getModel(); + if ( xModel.is() ) + { + // Model exist - use the model as component. + xComponent = xModel; + } + else + { + // Model not exist - use the controller as component. + xComponent = xController; + } + } + + return xComponent; +} + + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/helper/ocomponentenumeration.cxx b/framework/source/helper/ocomponentenumeration.cxx new file mode 100644 index 0000000000..3d00f75e56 --- /dev/null +++ b/framework/source/helper/ocomponentenumeration.cxx @@ -0,0 +1,117 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include + +namespace framework{ + +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::uno; +using namespace ::cppu; +using namespace ::osl; + +// constructor + +OComponentEnumeration::OComponentEnumeration( std::vector< css::uno::Reference< XComponent > >&& seqComponents ) + : m_nPosition ( 0 ) // 0 is the first position for a valid list and the right value for an invalid list to! + , m_seqComponents ( std::move(seqComponents) ) +{} + +// destructor + +OComponentEnumeration::~OComponentEnumeration() +{ + // Reset instance, free memory... + impl_resetObject(); +} + +// XEventListener +void SAL_CALL OComponentEnumeration::disposing( const EventObject& aEvent ) +{ + SolarMutexGuard g; + + // Safe impossible cases + // This method is not specified for all incoming parameters. + SAL_WARN_IF( !aEvent.Source.is(), "fwk", "OComponentEnumeration::disposing(): Invalid parameter detected!" ); + + // Reset instance to defaults, release references and free memory. + impl_resetObject(); +} + +// XEnumeration +sal_Bool SAL_CALL OComponentEnumeration::hasMoreElements() +{ + SolarMutexGuard g; + + // First position in a valid list is 0. + // => The last one is getLength() - 1! + // m_nPosition's current value is the position for the next element, which will be return, if user call "nextElement()" + // => We have more elements if current position less than the length of the list! + return ( m_nPosition < static_cast(m_seqComponents.size()) ); +} + +// XEnumeration + +Any SAL_CALL OComponentEnumeration::nextElement() +{ + SolarMutexGuard g; + + // If we have no elements or end of enumeration is arrived ... + if ( !hasMoreElements() ) + { + // .. throw an exception! + throw NoSuchElementException(); + } + + // Else; Get next element from list ... + Any aComponent; + aComponent <<= m_seqComponents[m_nPosition]; + // ... and step to next element! + ++m_nPosition; + + // Return listitem. + return aComponent; +} + +// protected method + +void OComponentEnumeration::impl_resetObject() +{ + // Attention: + // Write this for multiple calls - NOT AT THE SAME TIME - but for more than one call again)! + // It exist two ways to call this method. From destructor and from disposing(). + // I can't say, which one is the first. Normally the disposing-call - but other way... + + // Delete list of components. + m_seqComponents.clear(); + // Reset position in list. + // The list has no elements anymore. m_nPosition is normally the current position in list for nextElement! + // But a position of 0 in a list of 0 items is an invalid state. This constellation can't work in future. + // End of enumeration is arrived! + // (see hasMoreElements() for more details...) + m_nPosition = 0; +} + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/helper/oframes.cxx b/framework/source/helper/oframes.cxx new file mode 100644 index 0000000000..2fd43c8a52 --- /dev/null +++ b/framework/source/helper/oframes.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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include +#include + +namespace framework{ + +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::frame; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::uno; +using namespace ::cppu; +using namespace ::osl; + +// constructor + +OFrames::OFrames( const css::uno::Reference< XFrame >& xOwner , + FrameContainer* pFrameContainer ) + : m_xOwner ( xOwner ) + , m_pFrameContainer ( pFrameContainer ) + , m_bRecursiveSearchProtection( false ) +{ + // An instance of this class can only work with valid initialization. + // We share the mutex with our owner class, need a valid factory to instantiate new services and + // use the access to our owner for some operations. + SAL_WARN_IF( !xOwner.is() || !pFrameContainer, "fwk", "OFrames::OFrames(): Invalid parameter detected!" ); +} + +// (protected!) destructor + +OFrames::~OFrames() +{ + // Reset instance, free memory... + impl_resetObject(); +} + +// XFrames +void SAL_CALL OFrames::append( const css::uno::Reference< XFrame >& xFrame ) +{ + SolarMutexGuard g; + + // Safe impossible cases + // Method is not defined for ALL incoming parameters! + SAL_WARN_IF( !xFrame.is(), "fwk", "OFrames::append(): Invalid parameter detected!" ); + + // Do the follow only, if owner instance valid! + // Lock owner for follow operations - make a "hard reference"! + css::uno::Reference< XFramesSupplier > xOwner( m_xOwner.get(), UNO_QUERY ); + if ( xOwner.is() ) + { + // Append frame to the end of the container ... + m_pFrameContainer->append( xFrame ); + // Set owner of this instance as parent of the new frame in container! + xFrame->setCreator( xOwner ); + } + // Else; Do nothing! Our owner is dead. + SAL_WARN_IF( !xOwner.is(), "fwk", "OFrames::append():Our owner is dead - you can't append any frames ...!" ); +} + +// XFrames +void SAL_CALL OFrames::remove( const css::uno::Reference< XFrame >& xFrame ) +{ + SolarMutexGuard g; + + // Safe impossible cases + // Method is not defined for ALL incoming parameters! + SAL_WARN_IF( !xFrame.is(), "fwk", "OFrames::remove(): Invalid parameter detected!" ); + + // Do the follow only, if owner instance valid! + // Lock owner for follow operations - make a "hard reference"! + css::uno::Reference< XFramesSupplier > xOwner( m_xOwner.get(), UNO_QUERY ); + if ( xOwner.is() ) + { + // Search frame and remove it from container ... + m_pFrameContainer->remove( xFrame ); + // Don't reset owner-property of removed frame! + // This must do the caller of this method himself. + // See documentation of interface XFrames for further information. + } + // Else; Do nothing! Our owner is dead. + SAL_WARN_IF( !xOwner.is(), "fwk", "OFrames::remove(): Our owner is dead - you can't remove any frames ...!" ); +} + +// XFrames +Sequence< css::uno::Reference< XFrame > > SAL_CALL OFrames::queryFrames( sal_Int32 nSearchFlags ) +{ + SolarMutexGuard g; + + // Safe impossible cases + // Method is not defined for ALL incoming parameters! + SAL_WARN_IF( !impldbg_checkParameter_queryFrames( nSearchFlags ), "fwk", "OFrames::queryFrames(): Invalid parameter detected!" ); + + // Set default return value. (empty sequence) + Sequence< css::uno::Reference< XFrame > > seqFrames; + + // Do the follow only, if owner instance valid. + // Lock owner for follow operations - make a "hard reference"! + css::uno::Reference< XFrame > xOwner( m_xOwner.get(), UNO_QUERY ); + if ( xOwner.is() ) + { + // Work only, if search was not started here ...! + if( !m_bRecursiveSearchProtection ) + { + // This class is a helper for services, which must implement XFrames. + // His parent and children are MY parent and children to. + // All searchflags are supported by this implementation! + // If some flags should not be supported - don't call me with this flags!!! + + // Search with AUTO-flag is not supported yet! + // We think about right implementation. + SAL_WARN_IF( (nSearchFlags & FrameSearchFlag::AUTO), "fwk", "OFrames::queryFrames(): Search with AUTO-flag is not supported yet!" ); + + // Search for ALL and GLOBAL is superfluous! + // We support all necessary flags, from which these two flags are derived. + // ALL = PARENT + SELF + CHILDREN + SIBLINGS + // GLOBAL = ALL + TASKS + + // Add parent to list ... if any exist! + if( nSearchFlags & FrameSearchFlag::PARENT ) + { + css::uno::Reference< XFrame > xParent = xOwner->getCreator(); + if( xParent.is() ) + { + impl_appendSequence( seqFrames, { xParent } ); + } + } + + // Add owner to list if SELF is searched. + if( nSearchFlags & FrameSearchFlag::SELF ) + { + impl_appendSequence( seqFrames, { xOwner } ); + } + + // Add SIBLINGS to list. + if( nSearchFlags & FrameSearchFlag::SIBLINGS ) + { + // Else; start a new search. + // Protect this instance against recursive calls from parents. + m_bRecursiveSearchProtection = true; + // Ask parent of my owner for frames and append results to return list. + css::uno::Reference< XFramesSupplier > xParent = xOwner->getCreator(); + // If a parent exist ... + if ( xParent.is() ) + { + // ... ask him for right frames. + impl_appendSequence( seqFrames, xParent->getFrames()->queryFrames( nSearchFlags ) ); + } + // We have all searched information. + // Reset protection-mode. + m_bRecursiveSearchProtection = false; + } + + // If searched for children, step over all elements in container and collect the information. + if ( nSearchFlags & FrameSearchFlag::CHILDREN ) + { + // Don't search for parents, siblings and self at children! + // These things are supported by this instance himself. + sal_Int32 const nChildSearchFlags = FrameSearchFlag::SELF | FrameSearchFlag::CHILDREN; + // Step over all items of container and ask children for frames. + sal_uInt32 nCount = m_pFrameContainer->getCount(); + for ( sal_uInt32 nIndex=0; nIndex xItem( (*m_pFrameContainer)[nIndex], UNO_QUERY ); + impl_appendSequence( seqFrames, xItem->getFrames()->queryFrames( nChildSearchFlags ) ); + } + } + } + } + // Else; Do nothing! Our owner is dead. + SAL_WARN_IF( !xOwner.is(), "fwk", "OFrames::queryFrames(): Our owner is dead - you can't query for frames ...!" ); + + // Return result of this operation. + return seqFrames; +} + +// XIndexAccess +sal_Int32 SAL_CALL OFrames::getCount() +{ + SolarMutexGuard g; + + // Set default return value. + sal_Int32 nCount = 0; + + // Do the follow only, if owner instance valid. + // Lock owner for follow operations - make a "hard reference"! + css::uno::Reference< XFrame > xOwner( m_xOwner.get(), UNO_QUERY ); + if ( xOwner.is() ) + { + // Set CURRENT size of container for return. + nCount = m_pFrameContainer->getCount(); + } + + // Return result. + return nCount; +} + +// XIndexAccess + +Any SAL_CALL OFrames::getByIndex( sal_Int32 nIndex ) +{ + SolarMutexGuard g; + + sal_uInt32 nCount = m_pFrameContainer->getCount(); + if ( nIndex < 0 || ( sal::static_int_cast< sal_uInt32 >( nIndex ) >= nCount )) + throw IndexOutOfBoundsException("OFrames::getByIndex - Index out of bounds", + static_cast(this) ); + + // Set default return value. + Any aReturnValue; + + // Do the follow only, if owner instance valid. + // Lock owner for follow operations - make a "hard reference"! + css::uno::Reference< XFrame > xOwner( m_xOwner.get(), UNO_QUERY ); + if ( xOwner.is() ) + { + // Get element form container. + // (If index not valid, FrameContainer return NULL!) + aReturnValue <<= (*m_pFrameContainer)[nIndex]; + } + + // Return result of this operation. + return aReturnValue; +} + +// XElementAccess +Type SAL_CALL OFrames::getElementType() +{ + // This "container" support XFrame-interfaces only! + return cppu::UnoType::get(); +} + +// XElementAccess +sal_Bool SAL_CALL OFrames::hasElements() +{ + SolarMutexGuard g; + + // Set default return value. + bool bHasElements = false; + // Do the follow only, if owner instance valid. + // Lock owner for follow operations - make a "hard reference"! + css::uno::Reference< XFrame > xOwner( m_xOwner.get(), UNO_QUERY ); + if ( xOwner.is() ) + { + // If some elements exist ... + if ( m_pFrameContainer->getCount() > 0 ) + { + // ... change this state value! + bHasElements = true; + } + } + // Return result of this operation. + return bHasElements; +} + +// protected method + +void OFrames::impl_resetObject() +{ + // Attention: + // Write this for multiple calls - NOT AT THE SAME TIME - but for more than one call again)! + // It exist two ways to call this method. From destructor and from disposing(). + // I can't say, which one is the first. Normally the disposing-call - but other way... + + // This instance can't work if the weakreference to owner is invalid! + // Destroy this to reset this object. + m_xOwner.clear(); + // Reset pointer to shared container to! + m_pFrameContainer = nullptr; +} + +void OFrames::impl_appendSequence( Sequence< css::uno::Reference< XFrame > >& seqDestination , + const Sequence< css::uno::Reference< XFrame > >& seqSource ) +{ + // Get some information about the sequences. + sal_Int32 nSourceCount = seqSource.getLength(); + sal_Int32 nDestinationCount = seqDestination.getLength(); + const css::uno::Reference< XFrame >* pSourceAccess = seqSource.getConstArray(); + css::uno::Reference< XFrame >* pDestinationAccess = seqDestination.getArray(); + + // Get memory for result list. + Sequence< css::uno::Reference< XFrame > > seqResult ( nSourceCount + nDestinationCount ); + css::uno::Reference< XFrame >* pResultAccess = seqResult.getArray(); + sal_Int32 nResultPosition = 0; + + // Copy all items from first sequence. + for ( sal_Int32 nSourcePosition=0; nSourcePosition + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace framework{ + +PersistentWindowState::PersistentWindowState(css::uno::Reference< css::uno::XComponentContext > xContext) + : m_xContext (std::move(xContext )) + , m_bWindowStateAlreadySet(false ) +{ +} + +PersistentWindowState::~PersistentWindowState() +{ +} + +void SAL_CALL PersistentWindowState::initialize(const css::uno::Sequence< css::uno::Any >& lArguments) +{ + // check arguments + css::uno::Reference< css::frame::XFrame > xFrame; + if (!lArguments.hasElements()) + throw css::lang::IllegalArgumentException( + "Empty argument list!", + static_cast< ::cppu::OWeakObject* >(this), + 1); + + lArguments[0] >>= xFrame; + if (!xFrame.is()) + throw css::lang::IllegalArgumentException( + "No valid frame specified!", + static_cast< ::cppu::OWeakObject* >(this), + 1); + + { + SolarMutexGuard g; + m_xFrame = xFrame; + } + + // start listening + xFrame->addFrameActionListener(this); +} + +void SAL_CALL PersistentWindowState::frameAction(const css::frame::FrameActionEvent& aEvent) +{ + // We don't want to do this stuff when being used through LibreOfficeKit + if( comphelper::LibreOfficeKit::isActive() ) + return; + + css::uno::Reference< css::uno::XComponentContext > xContext; + css::uno::Reference< css::frame::XFrame > xFrame; + bool bRestoreWindowState; + { + SolarMutexGuard g; + xContext = m_xContext; + xFrame.set(m_xFrame.get(), css::uno::UNO_QUERY); + bRestoreWindowState = !m_bWindowStateAlreadySet; + } + + // frame already gone ? We hold it weak only ... + if (!xFrame.is()) + return; + + // no window -> no position and size available + css::uno::Reference< css::awt::XWindow > xWindow = xFrame->getContainerWindow(); + if (!xWindow.is()) + return; + + // unknown module -> no configuration available! + OUString sModuleName = PersistentWindowState::implst_identifyModule(xContext, xFrame); + if (sModuleName.isEmpty()) + return; + + switch(aEvent.Action) + { + case css::frame::FrameAction_COMPONENT_ATTACHED : + { + if (bRestoreWindowState) + { + OUString sWindowState = PersistentWindowState::implst_getWindowStateFromConfig(xContext, sModuleName); + PersistentWindowState::implst_setWindowStateOnWindow(xWindow,sWindowState); + SolarMutexGuard g; + m_bWindowStateAlreadySet = true; + } + } + break; + + case css::frame::FrameAction_COMPONENT_REATTACHED : + { + // nothing todo here, because it's not allowed to change position and size + // of an already existing frame! + } + break; + + case css::frame::FrameAction_COMPONENT_DETACHING : + { + OUString sWindowState = PersistentWindowState::implst_getWindowStateFromWindow(xWindow); + PersistentWindowState::implst_setWindowStateOnConfig(xContext, sModuleName, sWindowState); + } + break; + default: + break; + } +} + +void SAL_CALL PersistentWindowState::disposing(const css::lang::EventObject&) +{ + css::uno::Reference< css::frame::XFrame > xFrame(m_xFrame.get(), css::uno::UNO_QUERY); + if (xFrame.is()) + xFrame->removeFrameActionListener(this); + + // nothing todo here - because we hold the frame as weak reference only +} + +OUString PersistentWindowState::implst_identifyModule(const css::uno::Reference< css::uno::XComponentContext >& rxContext, + const css::uno::Reference< css::frame::XFrame >& xFrame) +{ + OUString sModuleName; + + css::uno::Reference< css::frame::XModuleManager2 > xModuleManager = + css::frame::ModuleManager::create( rxContext ); + + try + { + sModuleName = xModuleManager->identify(xFrame); + } + catch(const css::uno::RuntimeException&) + { throw; } + catch(const css::uno::Exception&) + { sModuleName.clear(); } + + return sModuleName; +} + +OUString PersistentWindowState::implst_getWindowStateFromConfig( + const css::uno::Reference< css::uno::XComponentContext >& rxContext, + std::u16string_view sModuleName) +{ + OUString sWindowState; + try + { + ::comphelper::ConfigurationHelper::readDirectKey(rxContext, + "org.openoffice.Setup/", + OUString::Concat("Office/Factories/*[\"") + sModuleName + "\"]", + "ooSetupFactoryWindowAttributes", + ::comphelper::EConfigurationModes::ReadOnly) >>= sWindowState; + } + catch(const css::uno::RuntimeException&) + { throw; } + catch(const css::uno::Exception&) + { sWindowState.clear(); } + + return sWindowState; +} + +void PersistentWindowState::implst_setWindowStateOnConfig( + const css::uno::Reference< css::uno::XComponentContext >& rxContext, + std::u16string_view sModuleName, const OUString& sWindowState) +{ + try + { + ::comphelper::ConfigurationHelper::writeDirectKey(rxContext, + "org.openoffice.Setup/", + OUString::Concat("Office/Factories/*[\"") + sModuleName + "\"]", + "ooSetupFactoryWindowAttributes", + css::uno::Any(sWindowState), + ::comphelper::EConfigurationModes::Standard); + } + catch(const css::uno::RuntimeException&) + { throw; } + catch(const css::uno::Exception&) + {} +} + +OUString PersistentWindowState::implst_getWindowStateFromWindow(const css::uno::Reference< css::awt::XWindow >& xWindow) +{ + OUString sWindowState; + + if (xWindow.is()) + { + // SOLAR SAFE -> ------------------------ + SolarMutexGuard aSolarGuard; + + VclPtr pWindow = VCLUnoHelper::GetWindow(xWindow); + // check for system window is necessary to guarantee correct pointer cast! + if ( pWindow && pWindow->IsSystemWindow() ) + { + vcl::WindowDataMask const nMask = vcl::WindowDataMask::All & ~vcl::WindowDataMask::Minimized; + sWindowState = static_cast(pWindow.get())->GetWindowState(nMask); + } + // <- SOLAR SAFE ------------------------ + } + + return sWindowState; +} + +void PersistentWindowState::implst_setWindowStateOnWindow(const css::uno::Reference< css::awt::XWindow >& xWindow , + std::u16string_view sWindowState) +{ + if ( + (!xWindow.is() ) || + ( sWindowState.empty() ) + ) + return; + + // SOLAR SAFE -> ------------------------ + SolarMutexGuard aSolarGuard; + + VclPtr pWindow = VCLUnoHelper::GetWindow(xWindow); + if (!pWindow) + return; + + // check for system and work window - it's necessary to guarantee correct pointer cast! + bool bSystemWindow = pWindow->IsSystemWindow(); + bool bWorkWindow = (pWindow->GetType() == WindowType::WORKWINDOW); + + if (!bSystemWindow && !bWorkWindow) + return; + + SystemWindow* pSystemWindow = static_cast(pWindow.get()); + WorkWindow* pWorkWindow = static_cast(pWindow.get()); + + // don't save this special state! + if (pWorkWindow->IsMinimized()) + return; + + OUString sOldWindowState = pSystemWindow->GetWindowState(); + if ( sOldWindowState != sWindowState ) + pSystemWindow->SetWindowState(sWindowState); + // <- SOLAR SAFE ------------------------ +} + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/helper/statusindicator.cxx b/framework/source/helper/statusindicator.cxx new file mode 100644 index 0000000000..2deb4a0ae5 --- /dev/null +++ b/framework/source/helper/statusindicator.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 +#include + +namespace framework +{ +StatusIndicator::StatusIndicator(StatusIndicatorFactory* pFactory) + : m_xFactory(pFactory) + , m_nRange(100) + , m_nLastCallbackPercent(-1) +{ +} + +StatusIndicator::~StatusIndicator() {} + +void SAL_CALL StatusIndicator::start(const OUString& sText, sal_Int32 nRange) +{ + if (comphelper::LibreOfficeKit::isActive()) + { + m_nRange = nRange; + m_nLastCallbackPercent = -1; + + comphelper::LibreOfficeKit::statusIndicatorStart(sText); + } +#if !defined(IOS) && !defined(ANDROID) + css::uno::Reference xFactory(m_xFactory); + if (xFactory.is()) + { + StatusIndicatorFactory* pFactory = static_cast(xFactory.get()); + pFactory->start(this, sText, nRange); + } +#else + (void)sText; +#endif +} + +void SAL_CALL StatusIndicator::end() +{ + if (comphelper::LibreOfficeKit::isActive()) + { + comphelper::LibreOfficeKit::statusIndicatorFinish(); + } +#if !defined(IOS) && !defined(ANDROID) + css::uno::Reference xFactory(m_xFactory); + if (xFactory.is()) + { + StatusIndicatorFactory* pFactory = static_cast(xFactory.get()); + pFactory->end(this); + } +#endif +} + +void SAL_CALL StatusIndicator::reset() +{ + if (comphelper::LibreOfficeKit::isActive()) + return; +#if !defined(IOS) && !defined(ANDROID) + css::uno::Reference xFactory(m_xFactory); + if (xFactory.is()) + { + StatusIndicatorFactory* pFactory = static_cast(xFactory.get()); + pFactory->reset(this); + } +#endif +} + +void SAL_CALL StatusIndicator::setText(const OUString& sText) +{ + if (comphelper::LibreOfficeKit::isActive()) + return; +#if !defined(IOS) && !defined(ANDROID) + css::uno::Reference xFactory(m_xFactory); + if (xFactory.is()) + { + StatusIndicatorFactory* pFactory = static_cast(xFactory.get()); + pFactory->setText(this, sText); + } +#else + (void)sText; +#endif +} + +void SAL_CALL StatusIndicator::setValue(sal_Int32 nValue) +{ + if (comphelper::LibreOfficeKit::isActive()) + { + if (m_nRange > 0) + { + int nPercent = (100 * nValue) / m_nRange; + if (nPercent >= m_nLastCallbackPercent) + { + comphelper::LibreOfficeKit::statusIndicatorSetValue(nPercent); + m_nLastCallbackPercent = nPercent; + } + } + return; + } +#if !defined(IOS) && !defined(ANDROID) + css::uno::Reference xFactory(m_xFactory); + if (xFactory.is()) + { + StatusIndicatorFactory* pFactory = static_cast(xFactory.get()); + pFactory->setValue(this, nValue); + } +#endif +} + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/helper/statusindicatorfactory.cxx b/framework/source/helper/statusindicatorfactory.cxx new file mode 100644 index 0000000000..64cf3543c2 --- /dev/null +++ b/framework/source/helper/statusindicatorfactory.cxx @@ -0,0 +1,577 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace framework{ + +sal_Int32 StatusIndicatorFactory::m_nInReschedule = 0; ///< static counter for rescheduling + +constexpr OUString PROGRESS_RESOURCE = u"private:resource/progressbar/progressbar"_ustr; + +StatusIndicatorFactory::StatusIndicatorFactory(css::uno::Reference< css::uno::XComponentContext > xContext) + : m_xContext (std::move(xContext )) + , m_bAllowReschedule (false) + , m_bAllowParentShow (false) + , m_bDisableReschedule(false) +{ +} + +StatusIndicatorFactory::~StatusIndicatorFactory() +{ + impl_stopWakeUpThread(); +} + +void SAL_CALL StatusIndicatorFactory::initialize(const css::uno::Sequence< css::uno::Any >& lArguments) +{ + if (lArguments.hasElements()) { + std::scoped_lock g(m_mutex); + + css::uno::Reference< css::frame::XFrame > xTmpFrame; + css::uno::Reference< css::awt::XWindow > xTmpWindow; + bool b1 = lArguments[0] >>= xTmpFrame; + bool b2 = lArguments[0] >>= xTmpWindow; + if (lArguments.getLength() == 3 && b1) { + // it's the first service constructor "createWithFrame" + m_xFrame = xTmpFrame; + lArguments[1] >>= m_bDisableReschedule; + lArguments[2] >>= m_bAllowParentShow; + } else if (lArguments.getLength() == 3 && b2) { + // it's the second service constructor "createWithWindow" + m_xPluggWindow = xTmpWindow; + lArguments[1] >>= m_bDisableReschedule; + lArguments[2] >>= m_bAllowParentShow; + } else { + // it's an old-style initialisation using properties + ::comphelper::SequenceAsHashMap lArgs(lArguments); + + m_xFrame = lArgs.getUnpackedValueOrDefault("Frame" , css::uno::Reference< css::frame::XFrame >()); + m_xPluggWindow = lArgs.getUnpackedValueOrDefault("Window" , css::uno::Reference< css::awt::XWindow >() ); + m_bAllowParentShow = lArgs.getUnpackedValueOrDefault("AllowParentShow" , false ); + m_bDisableReschedule = lArgs.getUnpackedValueOrDefault("DisableReschedule", false ); + } + } + +#ifdef EMSCRIPTEN + m_bDisableReschedule = true; +#endif + impl_createProgress(); +} + +css::uno::Reference< css::task::XStatusIndicator > SAL_CALL StatusIndicatorFactory::createStatusIndicator() +{ + return new StatusIndicator(this); +} + +void SAL_CALL StatusIndicatorFactory::update() +{ + std::scoped_lock g(m_mutex); + m_bAllowReschedule = true; +} + +void StatusIndicatorFactory::start(const css::uno::Reference< css::task::XStatusIndicator >& xChild, + const OUString& sText , + sal_Int32 nRange) +{ + css::uno::Reference< css::task::XStatusIndicator > xProgress; + // SAFE -> ---------------------------------- + { + std::scoped_lock aWriteLock(m_mutex); + + // create new info structure for this child or move it to the front of our stack + IndicatorStack::iterator pItem = ::std::find(m_aStack.begin(), m_aStack.end(), xChild); + if (pItem != m_aStack.end()) + m_aStack.erase(pItem); + IndicatorInfo aInfo(xChild, sText); + m_aStack.push_back (aInfo ); + + m_xActiveChild = xChild; + xProgress = m_xProgress; + } + // <- SAFE ---------------------------------- + + implts_makeParentVisibleIfAllowed(); + + if (xProgress.is()) + xProgress->start(sText, nRange); + + impl_startWakeUpThread(); + impl_reschedule(true); +} + +void StatusIndicatorFactory::reset(const css::uno::Reference< css::task::XStatusIndicator >& xChild) +{ + css::uno::Reference< css::task::XStatusIndicator > xActive; + css::uno::Reference< css::task::XStatusIndicator > xProgress; + // SAFE -> ---------------------------------- + { + std::scoped_lock aReadLock(m_mutex); + + // reset the internal info structure related to this child + IndicatorStack::iterator pItem = ::std::find(m_aStack.begin(), m_aStack.end(), xChild); + if (pItem != m_aStack.end()) + { + pItem->m_nValue = 0; + pItem->m_sText.clear(); + } + + xActive = m_xActiveChild; + xProgress = m_xProgress; + } + // <- SAFE ---------------------------------- + + // not the top most child => don't change UI + // But don't forget Reschedule! + if ( + (xChild == xActive) && + (xProgress.is() ) + ) + xProgress->reset(); + + impl_reschedule(true); +} + +void StatusIndicatorFactory::end(const css::uno::Reference< css::task::XStatusIndicator >& xChild) +{ + css::uno::Reference< css::task::XStatusIndicator > xActive; + css::uno::Reference< css::task::XStatusIndicator > xProgress; + OUString sText; + sal_Int32 nValue = 0; + // SAFE -> ---------------------------------- + { + std::scoped_lock aWriteLock(m_mutex); + + // remove this child from our stack + IndicatorStack::iterator pItem = ::std::find(m_aStack.begin(), m_aStack.end(), xChild); + if (pItem != m_aStack.end()) + m_aStack.erase(pItem); + + // activate next child ... or finish the progress if there is no further one. + m_xActiveChild.clear(); + IndicatorStack::reverse_iterator pNext = m_aStack.rbegin(); + if (pNext != m_aStack.rend()) + { + m_xActiveChild = pNext->m_xIndicator; + sText = pNext->m_sText; + nValue = pNext->m_nValue; + } + + xActive = m_xActiveChild; + xProgress = m_xProgress; + } + // <- SAFE ---------------------------------- + + if (xActive.is()) + { + // There is at least one further child indicator. + // Actualize our progress, so it shows these values from now. + if (xProgress.is()) + { + xProgress->setText (sText ); + xProgress->setValue(nValue); + } + } + else + { + // Our stack is empty. No further child exists. + // Se we must "end" our progress really + if (xProgress.is()) + xProgress->end(); + // Now hide the progress bar again. + impl_hideProgress(); + + impl_stopWakeUpThread(); + } + + impl_reschedule(true); +} + +void StatusIndicatorFactory::setText(const css::uno::Reference< css::task::XStatusIndicator >& xChild, + const OUString& sText ) +{ + css::uno::Reference< css::task::XStatusIndicator > xActive; + css::uno::Reference< css::task::XStatusIndicator > xProgress; + // SAFE -> ---------------------------------- + { + std::scoped_lock aWriteLock(m_mutex); + + IndicatorStack::iterator pItem = ::std::find(m_aStack.begin(), m_aStack.end(), xChild); + if (pItem != m_aStack.end()) + pItem->m_sText = sText; + + xActive = m_xActiveChild; + xProgress = m_xProgress; + } + // SAFE -> ---------------------------------- + + // paint only the top most indicator + // but don't forget to Reschedule! + if ( + (xChild == xActive) && + (xProgress.is() ) + ) + { + xProgress->setText(sText); + } + + impl_reschedule(true); +} + +void StatusIndicatorFactory::setValue( const css::uno::Reference< css::task::XStatusIndicator >& xChild , + sal_Int32 nValue ) +{ + sal_Int32 nOldValue = 0; + css::uno::Reference< css::task::XStatusIndicator > xActive; + css::uno::Reference< css::task::XStatusIndicator > xProgress; + // SAFE -> ---------------------------------- + { + std::scoped_lock aWriteLock(m_mutex); + + IndicatorStack::iterator pItem = ::std::find(m_aStack.begin(), m_aStack.end(), xChild); + if (pItem != m_aStack.end()) + { + nOldValue = pItem->m_nValue; + pItem->m_nValue = nValue; + } + + xActive = m_xActiveChild; + xProgress = m_xProgress; + } + // SAFE -> ---------------------------------- + + if ( + (xChild == xActive) && + (nOldValue != nValue ) && + (xProgress.is() ) + ) + { + xProgress->setValue(nValue); + } + + impl_reschedule(false); +} + +void StatusIndicatorFactory::implts_makeParentVisibleIfAllowed() +{ + css::uno::Reference< css::frame::XFrame > xFrame; + css::uno::Reference< css::awt::XWindow > xPluggWindow; + css::uno::Reference< css::uno::XComponentContext > xContext; + // SAFE -> ---------------------------------- + { + std::scoped_lock aReadLock(m_mutex); + + if (!m_bAllowParentShow) + return; + + xFrame = m_xFrame; + xPluggWindow = m_xPluggWindow; + xContext = m_xContext; + } + // <- SAFE ---------------------------------- + + css::uno::Reference< css::awt::XWindow > xParentWindow; + if (xFrame.is()) + xParentWindow = xFrame->getContainerWindow(); + else + xParentWindow = xPluggWindow; + + // don't disturb user in case he put the loading document into the background! + // Suppress any setVisible() or toFront() call in case the initial show was + // already made. + css::uno::Reference< css::awt::XWindow2 > xVisibleCheck(xParentWindow, css::uno::UNO_QUERY); + bool bIsVisible = false; + if (xVisibleCheck.is()) + bIsVisible = xVisibleCheck->isVisible(); + + if (bIsVisible) + { + impl_showProgress(); + return; + } + + // Check if the layout manager has been set to invisible state. It this case we are also + // not allowed to set the frame visible! + css::uno::Reference< css::beans::XPropertySet > xPropSet(xFrame, css::uno::UNO_QUERY); + if (xPropSet.is()) + { + css::uno::Reference< css::frame::XLayoutManager2 > xLayoutManager; + xPropSet->getPropertyValue(FRAME_PROPNAME_ASCII_LAYOUTMANAGER) >>= xLayoutManager; + if (xLayoutManager.is()) + { + if ( !xLayoutManager->isVisible() ) + return; + } + } + + // Ok the window should be made visible... because it is not currently visible. + // BUT..! + // We need a Hack for our applications: They get her progress from the frame directly + // on saving documents. Because there is no progress set on the MediaDescriptor. + // But that's wrong. In case the document was opened hidden, they should not use any progress .-( + // They only possible workaround: don't show the parent window here, if the document was opened hidden. + bool bHiddenDoc = false; + if (xFrame.is()) + { + css::uno::Reference< css::frame::XController > xController; + css::uno::Reference< css::frame::XModel > xModel; + xController = xFrame->getController(); + if (xController.is()) + xModel = xController->getModel(); + if (xModel.is()) + { + utl::MediaDescriptor lDocArgs(xModel->getArgs()); + bHiddenDoc = lDocArgs.getUnpackedValueOrDefault( + utl::MediaDescriptor::PROP_HIDDEN, + false); + } + } + + if (bHiddenDoc) + return; + + // OK: The document was not opened in hidden mode ... + // and the window isn't already visible. + // Show it and bring it to front. + // But before we have to be sure, that our internal used helper progress + // is visible too. + impl_showProgress(); + + SolarMutexGuard aSolarGuard; + VclPtr pWindow = VCLUnoHelper::GetWindow(xParentWindow); + if ( pWindow ) + { + bool bForceFrontAndFocus(officecfg::Office::Common::View::NewDocumentHandling::ForceFocusAndToFront::get()); + pWindow->Show(true, bForceFrontAndFocus ? ShowFlags::ForegroundTask : ShowFlags::NONE ); + } + +} + +void StatusIndicatorFactory::impl_createProgress() +{ + css::uno::Reference< css::frame::XFrame > xFrame; + css::uno::Reference< css::awt::XWindow > xWindow; + // SAFE -> ---------------------------------- + { + std::scoped_lock aReadLock(m_mutex); + + xFrame = m_xFrame; + xWindow = m_xPluggWindow; + } + // <- SAFE ---------------------------------- + + css::uno::Reference< css::task::XStatusIndicator > xProgress; + + if (xWindow.is()) + { + // use vcl based progress implementation in plugged mode + xProgress = new VCLStatusIndicator(xWindow); + } + else if (xFrame.is()) + { + // use frame layouted progress implementation + css::uno::Reference< css::beans::XPropertySet > xPropSet(xFrame, css::uno::UNO_QUERY); + if (xPropSet.is()) + { + css::uno::Reference< css::frame::XLayoutManager2 > xLayoutManager; + xPropSet->getPropertyValue(FRAME_PROPNAME_ASCII_LAYOUTMANAGER) >>= xLayoutManager; + if (xLayoutManager.is()) + { + xLayoutManager->lock(); + OUString sPROGRESS_RESOURCE(PROGRESS_RESOURCE); + xLayoutManager->createElement( sPROGRESS_RESOURCE ); + xLayoutManager->hideElement( sPROGRESS_RESOURCE ); + + css::uno::Reference< css::ui::XUIElement > xProgressBar = xLayoutManager->getElement(sPROGRESS_RESOURCE); + if (xProgressBar.is()) + xProgress.set(xProgressBar->getRealInterface(), css::uno::UNO_QUERY); + xLayoutManager->unlock(); + } + } + } + + std::scoped_lock g(m_mutex); + m_xProgress = xProgress; +} + +void StatusIndicatorFactory::impl_showProgress() +{ + css::uno::Reference< css::frame::XFrame > xFrame; + // SAFE -> ---------------------------------- + { + std::scoped_lock aReadLock(m_mutex); + + xFrame = m_xFrame; + } + // <- SAFE ---------------------------------- + + css::uno::Reference< css::task::XStatusIndicator > xProgress; + + if (!xFrame.is()) + return; + + // use frame layouted progress implementation + css::uno::Reference< css::beans::XPropertySet > xPropSet(xFrame, css::uno::UNO_QUERY); + if (xPropSet.is()) + { + css::uno::Reference< css::frame::XLayoutManager2 > xLayoutManager; + xPropSet->getPropertyValue(FRAME_PROPNAME_ASCII_LAYOUTMANAGER) >>= xLayoutManager; + if (xLayoutManager.is()) + { + // Be sure that we have always a progress. It can be that our frame + // was recycled and therefore the progress was destroyed! + // CreateElement does nothing if there is already a valid progress. + OUString sPROGRESS_RESOURCE(PROGRESS_RESOURCE); + xLayoutManager->createElement( sPROGRESS_RESOURCE ); + xLayoutManager->showElement( sPROGRESS_RESOURCE ); + + css::uno::Reference< css::ui::XUIElement > xProgressBar = xLayoutManager->getElement(sPROGRESS_RESOURCE); + if (xProgressBar.is()) + xProgress.set(xProgressBar->getRealInterface(), css::uno::UNO_QUERY); + } + } + + std::scoped_lock g(m_mutex); + m_xProgress = xProgress; +} + +void StatusIndicatorFactory::impl_hideProgress() +{ + css::uno::Reference< css::frame::XFrame > xFrame; + // SAFE -> ---------------------------------- + { + std::scoped_lock aReadLock(m_mutex); + + xFrame = m_xFrame; + } + // <- SAFE ---------------------------------- + + if (xFrame.is()) + { + // use frame layouted progress implementation + css::uno::Reference< css::beans::XPropertySet > xPropSet(xFrame, css::uno::UNO_QUERY); + if (xPropSet.is()) + { + css::uno::Reference< css::frame::XLayoutManager2 > xLayoutManager; + xPropSet->getPropertyValue(FRAME_PROPNAME_ASCII_LAYOUTMANAGER) >>= xLayoutManager; + if (xLayoutManager.is()) + xLayoutManager->hideElement( PROGRESS_RESOURCE ); + } + } +} + +void StatusIndicatorFactory::impl_reschedule(bool bForce) +{ + // SAFE -> + { + std::scoped_lock aReadLock(m_mutex); + if (m_bDisableReschedule) + return; + } + // <- SAFE + + bool bReschedule = bForce; + if (!bReschedule) + { + std::scoped_lock g(m_mutex); + bReschedule = m_bAllowReschedule; + m_bAllowReschedule = false; + } + + if (!bReschedule) + return; + + static std::mutex rescheduleLock; + // SAFE -> + std::unique_lock aRescheduleGuard(rescheduleLock); + + if (m_nInReschedule != 0) + return; + + // coverity[missing_lock: FALSE] - coverity fails to see the aRescheduleGuard ctor as taking a lock + ++m_nInReschedule; + aRescheduleGuard.unlock(); + // <- SAFE + + { + SolarMutexGuard g; + Application::Reschedule(true); + } + + // SAFE -> + aRescheduleGuard.lock(); + --m_nInReschedule; +} + +void StatusIndicatorFactory::impl_startWakeUpThread() +{ + std::scoped_lock g(m_mutex); + + if (m_bDisableReschedule) + return; + + if (!m_pWakeUp.is()) + { + m_pWakeUp = new WakeUpThread(this); + m_pWakeUp->launch(); + } +} + +void StatusIndicatorFactory::impl_stopWakeUpThread() +{ + rtl::Reference wakeUp; + { + std::scoped_lock g(m_mutex); + std::swap(wakeUp, m_pWakeUp); + } + if (wakeUp.is()) + { + wakeUp->stop(); + } +} + +} // namespace framework + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_framework_StatusIndicatorFactory_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence const &) +{ + return cppu::acquire(new framework::StatusIndicatorFactory(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/helper/tagwindowasmodified.cxx b/framework/source/helper/tagwindowasmodified.cxx new file mode 100644 index 0000000000..cc27a194e1 --- /dev/null +++ b/framework/source/helper/tagwindowasmodified.cxx @@ -0,0 +1,146 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include + +#include + +#include +#include +#include +#include + +namespace framework{ + +TagWindowAsModified::TagWindowAsModified() +{ +} + +TagWindowAsModified::~TagWindowAsModified() +{ +} + +void SAL_CALL TagWindowAsModified::initialize(const css::uno::Sequence< css::uno::Any >& lArguments) +{ + css::uno::Reference< css::frame::XFrame > xFrame; + + if (lArguments.hasElements()) + lArguments[0] >>= xFrame; + + if (!xFrame) + return; + + m_xFrame = xFrame; + xFrame->addFrameActionListener(this); + impl_update (xFrame); +} + +void SAL_CALL TagWindowAsModified::modified(const css::lang::EventObject& aEvent) +{ + if (!m_xModel || !m_xWindow || aEvent.Source != m_xModel) + return; + + bool bModified = m_xModel->isModified (); + + // SYNCHRONIZED -> + SolarMutexGuard aSolarGuard; + + if (m_xWindow->isDisposed()) + return; + + if (bModified) + m_xWindow->SetExtendedStyle(WindowExtendedStyle::DocModified); + else + m_xWindow->SetExtendedStyle(WindowExtendedStyle::NONE); + // <- SYNCHRONIZED +} + +void SAL_CALL TagWindowAsModified::frameAction(const css::frame::FrameActionEvent& aEvent) +{ + if ( + (aEvent.Action != css::frame::FrameAction_COMPONENT_REATTACHED) && + (aEvent.Action != css::frame::FrameAction_COMPONENT_ATTACHED ) + ) + return; + + if ( aEvent.Source != m_xFrame ) + return; + + impl_update (m_xFrame); +} + +void SAL_CALL TagWindowAsModified::disposing(const css::lang::EventObject& aEvent) +{ + SolarMutexGuard g; + + if (m_xFrame && aEvent.Source == m_xFrame) + { + m_xFrame->removeFrameActionListener(this); + m_xFrame.clear(); + return; + } + + if (m_xModel && aEvent.Source == m_xModel) + { + m_xModel->removeModifyListener(this); + m_xModel.clear(); + return; + } +} + +void TagWindowAsModified::impl_update (const css::uno::Reference< css::frame::XFrame >& xFrame) +{ + if (!xFrame) + return; + + css::uno::Reference< css::awt::XWindow > xWindow = xFrame->getContainerWindow (); + css::uno::Reference< css::frame::XController > xController = xFrame->getController (); + css::uno::Reference< css::util::XModifiable > xModel; + if (xController.is ()) + xModel = css::uno::Reference< css::util::XModifiable >(xController->getModel(), css::uno::UNO_QUERY); + + if (!xWindow || !xModel) + return; + + { + SolarMutexGuard g; + + VclPtr pWindow = VCLUnoHelper::GetWindow(xWindow); + bool bSystemWindow = pWindow->IsSystemWindow(); + bool bWorkWindow = (pWindow->GetType() == WindowType::WORKWINDOW); + if (!bSystemWindow && !bWorkWindow) + return; + + if (m_xModel) + m_xModel->removeModifyListener (this); + + // Note: frame was set as member outside ! we have to refresh connections + // regarding window and model only here. + m_xWindow = pWindow; + m_xModel = xModel; + } + + m_xModel->addModifyListener (this); +} + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/helper/titlebarupdate.cxx b/framework/source/helper/titlebarupdate.cxx new file mode 100644 index 0000000000..add4ea9709 --- /dev/null +++ b/framework/source/helper/titlebarupdate.cxx @@ -0,0 +1,316 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace framework{ + +const ::sal_Int32 INVALID_ICON_ID = -1; +const ::sal_Int32 DEFAULT_ICON_ID = 0; + +TitleBarUpdate::TitleBarUpdate(css::uno::Reference< css::uno::XComponentContext > xContext) + : m_xContext (std::move(xContext )) +{ +} + +TitleBarUpdate::~TitleBarUpdate() +{ +} + +void SAL_CALL TitleBarUpdate::initialize(const css::uno::Sequence< css::uno::Any >& lArguments) +{ + // check arguments + css::uno::Reference< css::frame::XFrame > xFrame; + if (!lArguments.hasElements()) + throw css::lang::IllegalArgumentException( + "Empty argument list!", + static_cast< ::cppu::OWeakObject* >(this), + 1); + + lArguments[0] >>= xFrame; + if (!xFrame.is()) + throw css::lang::IllegalArgumentException( + "No valid frame specified!", + static_cast< ::cppu::OWeakObject* >(this), + 1); + + { + SolarMutexGuard g; + // hold the frame as weak reference(!) so it can die everytimes :-) + m_xFrame = xFrame; + } + + // start listening + xFrame->addFrameActionListener(this); + + css::uno::Reference< css::frame::XTitleChangeBroadcaster > xBroadcaster(xFrame, css::uno::UNO_QUERY); + if (xBroadcaster.is ()) + xBroadcaster->addTitleChangeListener (this); +} + +void SAL_CALL TitleBarUpdate::frameAction(const css::frame::FrameActionEvent& aEvent) +{ + // we are interested on events only, which must trigger a title bar update + // because component was changed. + if ( + (aEvent.Action == css::frame::FrameAction_COMPONENT_ATTACHED ) || + (aEvent.Action == css::frame::FrameAction_COMPONENT_REATTACHED) || + (aEvent.Action == css::frame::FrameAction_COMPONENT_DETACHING ) + ) + { + impl_forceUpdate (); + } +} + +void SAL_CALL TitleBarUpdate::titleChanged(const css::frame::TitleChangedEvent& /* aEvent */) +{ + impl_forceUpdate (); +} + +void SAL_CALL TitleBarUpdate::disposing(const css::lang::EventObject&) +{ + css::uno::Reference< css::frame::XFrame > xFrame(m_xFrame.get(), css::uno::UNO_QUERY); + if (xFrame.is()) + xFrame->removeFrameActionListener(this); + + // nothing todo here - because we hold the frame as weak reference only +} + +//http://live.gnome.org/GnomeShell/ApplicationBased +//http://msdn.microsoft.com/en-us/library/dd378459(v=VS.85).aspx +void TitleBarUpdate::impl_updateApplicationID(const css::uno::Reference< css::frame::XFrame >& xFrame) +{ + css::uno::Reference< css::awt::XWindow > xWindow = xFrame->getContainerWindow (); + if ( ! xWindow.is() ) + return; + +#if !defined(MACOSX) + OUString sApplicationID; + try + { + css::uno::Reference< css::frame::XModuleManager2 > xModuleManager = + css::frame::ModuleManager::create( m_xContext ); + + OUString sDesktopName; + OUString aModuleId = xModuleManager->identify(xFrame); + if ( aModuleId.startsWith("com.sun.star.text.") || aModuleId.startsWith("com.sun.star.xforms.") ) + sDesktopName = "Writer"; + else if ( aModuleId.startsWith("com.sun.star.sheet.") ) + sDesktopName = "Calc"; + else if ( aModuleId.startsWith("com.sun.star.presentation.") ) + sDesktopName = "Impress"; + else if ( aModuleId.startsWith("com.sun.star.drawing." ) ) + sDesktopName = "Draw"; + else if ( aModuleId.startsWith("com.sun.star.formula." ) ) + sDesktopName = "Math"; + else if ( aModuleId.startsWith("com.sun.star.sdb.") ) + sDesktopName = "Base"; + else + sDesktopName = "Startcenter"; +#if defined(_WIN32) + // We use a hardcoded product name matching the registry keys so applications can be associated with file types + sApplicationID = "TheDocumentFoundation.LibreOffice." + sDesktopName; +#else + sApplicationID = utl::ConfigManager::getProductName().toAsciiLowerCase() + "-" + sDesktopName.toAsciiLowerCase(); +#endif + } + catch(const css::uno::Exception&) + { + } +#else + OUString const sApplicationID; +#endif + + // VCL SYNCHRONIZED -> + SolarMutexGuard aSolarGuard; + + VclPtr pWindow = VCLUnoHelper::GetWindow( xWindow ); + if ( pWindow && pWindow->GetType() == WindowType::WORKWINDOW ) + { + WorkWindow* pWorkWindow = static_cast(pWindow.get()); + pWorkWindow->SetApplicationID( sApplicationID ); + } + // <- VCL SYNCHRONIZED +} + +bool TitleBarUpdate::implst_getModuleInfo(const css::uno::Reference< css::frame::XFrame >& xFrame, + TModuleInfo& rInfo ) +{ + if ( ! xFrame.is ()) + return false; + + try + { + css::uno::Reference< css::frame::XModuleManager2 > xModuleManager = + css::frame::ModuleManager::create( m_xContext ); + + rInfo.sID = xModuleManager->identify(xFrame); + ::comphelper::SequenceAsHashMap lProps = xModuleManager->getByName (rInfo.sID); + + rInfo.nIcon = lProps.getUnpackedValueOrDefault (OFFICEFACTORY_PROPNAME_ASCII_ICON , INVALID_ICON_ID ); + + // Note: If we could retrieve a module id ... everything is OK. + // UIName and Icon ID are optional values ! + bool bSuccess = !rInfo.sID.isEmpty(); + return bSuccess; + } + catch(const css::uno::Exception&) + {} + + return false; +} + +void TitleBarUpdate::impl_forceUpdate() +{ + css::uno::Reference< css::frame::XFrame > xFrame; + { + SolarMutexGuard g; + xFrame.set(m_xFrame.get(), css::uno::UNO_QUERY); + } + + // frame already gone ? We hold it weak only ... + if ( ! xFrame.is()) + return; + + // no window -> no chance to set/update title and icon + css::uno::Reference< css::awt::XWindow > xWindow = xFrame->getContainerWindow(); + if ( ! xWindow.is()) + return; + + impl_updateIcon (xFrame); + impl_updateTitle (xFrame); +#if !defined(MACOSX) + impl_updateApplicationID (xFrame); +#endif +} + +void TitleBarUpdate::impl_updateIcon(const css::uno::Reference< css::frame::XFrame >& xFrame) +{ + css::uno::Reference< css::frame::XController > xController = xFrame->getController (); + css::uno::Reference< css::awt::XWindow > xWindow = xFrame->getContainerWindow (); + + if ( + ( ! xController.is() ) || + ( ! xWindow.is() ) + ) + return; + + // a) set default value to an invalid one. So we can start further searches for right icon id, if + // first steps failed! + sal_Int32 nIcon = INVALID_ICON_ID; + + // b) try to find information on controller property set directly + // Don't forget to catch possible exceptions - because these property is an optional one! + css::uno::Reference< css::beans::XPropertySet > xSet( xController, css::uno::UNO_QUERY ); + if ( xSet.is() ) + { + try + { + css::uno::Reference< css::beans::XPropertySetInfo > const xPSI( xSet->getPropertySetInfo(), css::uno::UNO_SET_THROW ); + if ( xPSI->hasPropertyByName( "IconId" ) ) + xSet->getPropertyValue( "IconId" ) >>= nIcon; + } + catch(const css::uno::Exception&) + { + DBG_UNHANDLED_EXCEPTION("fwk"); + } + } + + // c) if b) failed ... identify the used module and retrieve set icon from module config. + // Tirck :-) Module was already specified outside and aInfo contains all needed information. + if ( nIcon == INVALID_ICON_ID ) + { + TModuleInfo aInfo; + if (implst_getModuleInfo(xFrame, aInfo)) + nIcon = aInfo.nIcon; + } + + // d) if all steps failed - use fallback :-) + // ... means using the global staroffice icon + if( nIcon == INVALID_ICON_ID ) + nIcon = DEFAULT_ICON_ID; + + // e) set icon on container window now + // Don't forget SolarMutex! We use vcl directly :-( + // Check window pointer for right WorkWindow class too!!! + + // VCL SYNCHRONIZED -> + SolarMutexGuard aSolarGuard; + + VclPtr pWindow = VCLUnoHelper::GetWindow( xWindow ); + if ( pWindow && ( pWindow->GetType() == WindowType::WORKWINDOW ) ) + { + WorkWindow* pWorkWindow = static_cast(pWindow.get()); + pWorkWindow->SetIcon( static_cast(nIcon) ); + + css::uno::Reference< css::frame::XModel > xModel = xController->getModel(); + OUString aURL; + if( xModel.is() ) + aURL = xModel->getURL(); + pWorkWindow->SetRepresentedURL( aURL ); + } + // <- VCL SYNCHRONIZED +} + +void TitleBarUpdate::impl_updateTitle(const css::uno::Reference< css::frame::XFrame >& xFrame) +{ + // no window ... no chance to set any title -> return + css::uno::Reference< css::awt::XWindow > xWindow = xFrame->getContainerWindow (); + if ( ! xWindow.is() ) + return; + + css::uno::Reference< css::frame::XTitle > xTitle(xFrame, css::uno::UNO_QUERY); + if ( ! xTitle.is() ) + return; + + const OUString sTitle = xTitle->getTitle (); + + // VCL SYNCHRONIZED -> + SolarMutexGuard aSolarGuard; + + VclPtr pWindow = VCLUnoHelper::GetWindow( xWindow ); + if ( pWindow && ( pWindow->GetType() == WindowType::WORKWINDOW ) ) + { + WorkWindow* pWorkWindow = static_cast(pWindow.get()); + pWorkWindow->SetText( sTitle ); + } + // <- VCL SYNCHRONIZED +} + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/helper/uiconfigelementwrapperbase.cxx b/framework/source/helper/uiconfigelementwrapperbase.cxx new file mode 100644 index 0000000000..05e6467a5b --- /dev/null +++ b/framework/source/helper/uiconfigelementwrapperbase.cxx @@ -0,0 +1,481 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +const int UIELEMENT_PROPHANDLE_CONFIGSOURCE = 1; +const int UIELEMENT_PROPHANDLE_FRAME = 2; +const int UIELEMENT_PROPHANDLE_PERSISTENT = 3; +const int UIELEMENT_PROPHANDLE_RESOURCEURL = 4; +const int UIELEMENT_PROPHANDLE_TYPE = 5; +const int UIELEMENT_PROPHANDLE_XMENUBAR = 6; +const int UIELEMENT_PROPHANDLE_CONFIGLISTENER = 7; +const int UIELEMENT_PROPHANDLE_NOCLOSE = 8; +constexpr OUString UIELEMENT_PROPNAME_CONFIGLISTENER = u"ConfigListener"_ustr; +constexpr OUString UIELEMENT_PROPNAME_CONFIGSOURCE = u"ConfigurationSource"_ustr; +constexpr OUString UIELEMENT_PROPNAME_FRAME = u"Frame"_ustr; +constexpr OUString UIELEMENT_PROPNAME_PERSISTENT = u"Persistent"_ustr; +constexpr OUString UIELEMENT_PROPNAME_RESOURCEURL = u"ResourceURL"_ustr; +constexpr OUString UIELEMENT_PROPNAME_TYPE = u"Type"_ustr; +constexpr OUString UIELEMENT_PROPNAME_XMENUBAR = u"XMenuBar"_ustr; +constexpr OUString UIELEMENT_PROPNAME_NOCLOSE = u"NoClose"_ustr; +using namespace com::sun::star::beans; +using namespace com::sun::star::uno; +using namespace com::sun::star::frame; +using namespace com::sun::star::lang; +using namespace com::sun::star::container; +using namespace ::com::sun::star::ui; + +namespace framework +{ + +UIConfigElementWrapperBase::UIConfigElementWrapperBase( sal_Int16 nType ) + : ::cppu::OBroadcastHelperVar< ::cppu::OMultiTypeInterfaceContainerHelper, ::cppu::OMultiTypeInterfaceContainerHelper::keyType >( m_aMutex ) + , ::cppu::OPropertySetHelper ( *static_cast< ::cppu::OBroadcastHelper* >(this) ) + , m_nType ( nType ) + , m_bPersistent ( true ) + , m_bInitialized ( false ) + , m_bConfigListener ( false ) + , m_bConfigListening ( false ) + , m_bDisposed ( false ) + , m_bNoClose ( false ) + , m_aListenerContainer ( m_aMutex ) +{ +} + +UIConfigElementWrapperBase::~UIConfigElementWrapperBase() +{ +} + +Any SAL_CALL UIConfigElementWrapperBase::queryInterface( const Type& _rType ) +{ + Any aRet = UIConfigElementWrapperBase_BASE::queryInterface( _rType ); + if ( !aRet.hasValue() ) + aRet = OPropertySetHelper::queryInterface( _rType ); + return aRet; +} + +Sequence< Type > SAL_CALL UIConfigElementWrapperBase::getTypes( ) +{ + return comphelper::concatSequences( + UIConfigElementWrapperBase_BASE::getTypes(), + ::cppu::OPropertySetHelper::getTypes() + ); +} + +// XComponent +void SAL_CALL UIConfigElementWrapperBase::addEventListener( const css::uno::Reference< css::lang::XEventListener >& xListener ) +{ + m_aListenerContainer.addInterface( cppu::UnoType::get(), xListener ); +} + +void SAL_CALL UIConfigElementWrapperBase::removeEventListener( const css::uno::Reference< css::lang::XEventListener >& aListener ) +{ + m_aListenerContainer.removeInterface( cppu::UnoType::get(), aListener ); +} + +// XEventListener +void SAL_CALL UIConfigElementWrapperBase::disposing( const EventObject& ) +{ + SolarMutexGuard g; + m_xConfigSource.clear(); +} + +void SAL_CALL UIConfigElementWrapperBase::initialize( const Sequence< Any >& aArguments ) +{ + SolarMutexGuard g; + + if ( m_bInitialized ) + return; + + for ( const Any& rArg : aArguments ) + { + PropertyValue aPropValue; + if ( rArg >>= aPropValue ) + { + if ( aPropValue.Name == UIELEMENT_PROPNAME_CONFIGSOURCE ) + setFastPropertyValue_NoBroadcast( UIELEMENT_PROPHANDLE_CONFIGSOURCE, aPropValue.Value ); + else if ( aPropValue.Name == UIELEMENT_PROPNAME_FRAME ) + setFastPropertyValue_NoBroadcast( UIELEMENT_PROPHANDLE_FRAME, aPropValue.Value ); + else if ( aPropValue.Name == UIELEMENT_PROPNAME_PERSISTENT ) + setFastPropertyValue_NoBroadcast( UIELEMENT_PROPHANDLE_PERSISTENT, aPropValue.Value ); + else if ( aPropValue.Name == UIELEMENT_PROPNAME_RESOURCEURL ) + setFastPropertyValue_NoBroadcast( UIELEMENT_PROPHANDLE_RESOURCEURL, aPropValue.Value ); + else if ( aPropValue.Name == UIELEMENT_PROPNAME_TYPE ) + setFastPropertyValue_NoBroadcast( UIELEMENT_PROPHANDLE_TYPE, aPropValue.Value ); + else if ( aPropValue.Name == UIELEMENT_PROPNAME_CONFIGLISTENER ) + setFastPropertyValue_NoBroadcast( UIELEMENT_PROPHANDLE_CONFIGLISTENER, aPropValue.Value ); + else if ( aPropValue.Name == UIELEMENT_PROPNAME_NOCLOSE ) + setFastPropertyValue_NoBroadcast( UIELEMENT_PROPHANDLE_NOCLOSE, aPropValue.Value ); + } + } + + m_bInitialized = true; +} + +// XUpdatable +void SAL_CALL UIConfigElementWrapperBase::update() +{ + // can be implemented by derived class +} + +void SAL_CALL UIConfigElementWrapperBase::elementInserted( const css::ui::ConfigurationEvent& ) +{ + // can be implemented by derived class +} + +void SAL_CALL UIConfigElementWrapperBase::elementRemoved( const css::ui::ConfigurationEvent& ) +{ + // can be implemented by derived class +} + +void SAL_CALL UIConfigElementWrapperBase::elementReplaced( const css::ui::ConfigurationEvent& ) +{ + // can be implemented by derived class +} + +// XPropertySet helper +sal_Bool SAL_CALL UIConfigElementWrapperBase::convertFastPropertyValue( Any& aConvertedValue , + Any& aOldValue , + sal_Int32 nHandle , + const Any& aValue ) +{ + // Initialize state with sal_False !!! + // (Handle can be invalid) + bool bReturn = false; + + switch( nHandle ) + { + case UIELEMENT_PROPHANDLE_CONFIGLISTENER: + bReturn = PropHelper::willPropertyBeChanged( + css::uno::Any(m_bConfigListener), + aValue, + aOldValue, + aConvertedValue); + break; + + case UIELEMENT_PROPHANDLE_CONFIGSOURCE: + bReturn = PropHelper::willPropertyBeChanged( + css::uno::Any(m_xConfigSource), + aValue, + aOldValue, + aConvertedValue); + break; + + case UIELEMENT_PROPHANDLE_FRAME: + { + Reference< XFrame > xFrame( m_xWeakFrame ); + bReturn = PropHelper::willPropertyBeChanged( + css::uno::Any(xFrame), + aValue, + aOldValue, + aConvertedValue); + } + break; + + case UIELEMENT_PROPHANDLE_PERSISTENT: + bReturn = PropHelper::willPropertyBeChanged( + css::uno::Any(m_bPersistent), + aValue, + aOldValue, + aConvertedValue); + break; + + case UIELEMENT_PROPHANDLE_RESOURCEURL: + bReturn = PropHelper::willPropertyBeChanged( + css::uno::Any(m_aResourceURL), + aValue, + aOldValue, + aConvertedValue); + break; + + case UIELEMENT_PROPHANDLE_TYPE : + bReturn = PropHelper::willPropertyBeChanged( + css::uno::Any(m_nType), + aValue, + aOldValue, + aConvertedValue); + break; + + case UIELEMENT_PROPHANDLE_XMENUBAR : + bReturn = PropHelper::willPropertyBeChanged( + css::uno::Any(m_xMenuBar), + aValue, + aOldValue, + aConvertedValue); + break; + + case UIELEMENT_PROPHANDLE_NOCLOSE: + bReturn = PropHelper::willPropertyBeChanged( + css::uno::Any(m_bNoClose), + aValue, + aOldValue, + aConvertedValue); + break; + } + + // Return state of operation. + return bReturn; +} + +void SAL_CALL UIConfigElementWrapperBase::setFastPropertyValue_NoBroadcast( sal_Int32 nHandle , + const css::uno::Any& aValue ) +{ + switch( nHandle ) + { + case UIELEMENT_PROPHANDLE_CONFIGLISTENER: + { + bool bBool( m_bConfigListener ); + aValue >>= bBool; + if ( m_bConfigListener != bBool ) + { + if ( m_bConfigListening ) + { + if ( m_xConfigSource.is() && !bBool ) + { + try + { + Reference< XUIConfiguration > xUIConfig( m_xConfigSource, UNO_QUERY ); + if ( xUIConfig.is() ) + { + xUIConfig->removeConfigurationListener( Reference< XUIConfigurationListener >(this) ); + m_bConfigListening = false; + } + } + catch ( const Exception& ) + { + } + } + } + else + { + if ( m_xConfigSource.is() && bBool ) + { + try + { + Reference< XUIConfiguration > xUIConfig( m_xConfigSource, UNO_QUERY ); + if ( xUIConfig.is() ) + { + xUIConfig->addConfigurationListener( Reference< XUIConfigurationListener >(this) ); + m_bConfigListening = true; + } + } + catch ( const Exception& ) + { + } + } + } + + m_bConfigListener = bBool; + } + } + break; + case UIELEMENT_PROPHANDLE_CONFIGSOURCE: + aValue >>= m_xConfigSource; + break; + case UIELEMENT_PROPHANDLE_FRAME: + { + Reference< XFrame > xFrame; + + aValue >>= xFrame; + m_xWeakFrame = xFrame; + break; + } + case UIELEMENT_PROPHANDLE_PERSISTENT: + { + bool bBool( m_bPersistent ); + aValue >>= bBool; + m_bPersistent = bBool; + break; + } + case UIELEMENT_PROPHANDLE_RESOURCEURL: + aValue >>= m_aResourceURL; + break; + case UIELEMENT_PROPHANDLE_TYPE: + aValue >>= m_nType; + break; + case UIELEMENT_PROPHANDLE_XMENUBAR: + aValue >>= m_xMenuBar; + break; + case UIELEMENT_PROPHANDLE_NOCLOSE: + { + bool bBool( m_bNoClose ); + aValue >>= bBool; + m_bNoClose = bBool; + break; + } + } +} + +void SAL_CALL UIConfigElementWrapperBase::getFastPropertyValue( css::uno::Any& aValue , + sal_Int32 nHandle ) const +{ + switch( nHandle ) + { + case UIELEMENT_PROPHANDLE_CONFIGLISTENER: + aValue <<= m_bConfigListener; + break; + case UIELEMENT_PROPHANDLE_CONFIGSOURCE: + aValue <<= m_xConfigSource; + break; + case UIELEMENT_PROPHANDLE_FRAME: + { + Reference< XFrame > xFrame( m_xWeakFrame ); + aValue <<= xFrame; + break; + } + case UIELEMENT_PROPHANDLE_PERSISTENT: + aValue <<= m_bPersistent; + break; + case UIELEMENT_PROPHANDLE_RESOURCEURL: + aValue <<= m_aResourceURL; + break; + case UIELEMENT_PROPHANDLE_TYPE: + aValue <<= m_nType; + break; + case UIELEMENT_PROPHANDLE_XMENUBAR: + aValue <<= m_xMenuBar; + break; + case UIELEMENT_PROPHANDLE_NOCLOSE: + aValue <<= m_bNoClose; + break; + } +} + +::cppu::IPropertyArrayHelper& SAL_CALL UIConfigElementWrapperBase::getInfoHelper() +{ + // Define static member to give structure of properties to baseclass "OPropertySetHelper". + // "impl_getStaticPropertyDescriptor" is a non exported and static function, who will define a static propertytable. + // "true" say: Table is sorted by name. + static ::cppu::OPropertyArrayHelper ourInfoHelper( impl_getStaticPropertyDescriptor(), true ); + + return ourInfoHelper; +} + +css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL UIConfigElementWrapperBase::getPropertySetInfo() +{ + // Create structure of propertysetinfo for baseclass "OPropertySetHelper". + // (Use method "getInfoHelper()".) + static css::uno::Reference< css::beans::XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) ); + + return xInfo; +} + +css::uno::Sequence< css::beans::Property > UIConfigElementWrapperBase::impl_getStaticPropertyDescriptor() +{ + // Create property array to initialize sequence! + // Table of all predefined properties of this class. It's used from OPropertySetHelper-class! + // Don't forget to change the defines (see begin of this file), if you add, change or delete a property in this list!!! + // It's necessary for methods of OPropertySetHelper. + // ATTENTION: + // YOU MUST SORT FOLLOW TABLE BY NAME ALPHABETICAL !!! + + return + { + css::beans::Property( UIELEMENT_PROPNAME_CONFIGLISTENER, UIELEMENT_PROPHANDLE_CONFIGLISTENER , cppu::UnoType::get(), css::beans::PropertyAttribute::TRANSIENT ), + css::beans::Property( UIELEMENT_PROPNAME_CONFIGSOURCE, UIELEMENT_PROPHANDLE_CONFIGSOURCE , cppu::UnoType::get(), css::beans::PropertyAttribute::TRANSIENT ), + css::beans::Property( UIELEMENT_PROPNAME_FRAME, UIELEMENT_PROPHANDLE_FRAME , cppu::UnoType::get(), css::beans::PropertyAttribute::TRANSIENT | css::beans::PropertyAttribute::READONLY ), + css::beans::Property( UIELEMENT_PROPNAME_NOCLOSE, UIELEMENT_PROPHANDLE_NOCLOSE , cppu::UnoType::get(), css::beans::PropertyAttribute::TRANSIENT ), + css::beans::Property( UIELEMENT_PROPNAME_PERSISTENT, UIELEMENT_PROPHANDLE_PERSISTENT , cppu::UnoType::get(), css::beans::PropertyAttribute::TRANSIENT ), + css::beans::Property( UIELEMENT_PROPNAME_RESOURCEURL, UIELEMENT_PROPHANDLE_RESOURCEURL , cppu::UnoType::get(), css::beans::PropertyAttribute::TRANSIENT | css::beans::PropertyAttribute::READONLY ), + css::beans::Property( UIELEMENT_PROPNAME_TYPE, UIELEMENT_PROPHANDLE_TYPE , cppu::UnoType::get(), css::beans::PropertyAttribute::TRANSIENT | css::beans::PropertyAttribute::READONLY ), + css::beans::Property( UIELEMENT_PROPNAME_XMENUBAR, UIELEMENT_PROPHANDLE_XMENUBAR , cppu::UnoType::get(), css::beans::PropertyAttribute::TRANSIENT | css::beans::PropertyAttribute::READONLY ) + }; +} + +void SAL_CALL UIConfigElementWrapperBase::setSettings( const Reference< XIndexAccess >& xSettings ) +{ + SolarMutexClearableGuard aLock; + + if ( !xSettings.is() ) + return; + + // Create a copy of the data if the container is not const + Reference< XIndexReplace > xReplace( xSettings, UNO_QUERY ); + if ( xReplace.is() ) + m_xConfigData = new ConstItemContainer( xSettings ); + else + m_xConfigData = xSettings; + + if ( m_xConfigSource.is() && m_bPersistent ) + { + OUString aResourceURL( m_aResourceURL ); + Reference< XUIConfigurationManager > xUICfgMgr( m_xConfigSource ); + + aLock.clear(); + + try + { + xUICfgMgr->replaceSettings( aResourceURL, m_xConfigData ); + } + catch( const NoSuchElementException& ) + { + } + } + else if ( !m_bPersistent ) + { + // Transient menubar => Fill menubar with new data + impl_fillNewData(); + } +} +void UIConfigElementWrapperBase::impl_fillNewData() +{ +} +Reference< XIndexAccess > SAL_CALL UIConfigElementWrapperBase::getSettings( sal_Bool bWriteable ) +{ + SolarMutexGuard g; + + if ( bWriteable ) + return Reference< XIndexAccess >( new RootItemContainer( m_xConfigData ) ); + + return m_xConfigData; +} + +Reference< XFrame > SAL_CALL UIConfigElementWrapperBase::getFrame() +{ + SolarMutexGuard g; + Reference< XFrame > xFrame( m_xWeakFrame ); + return xFrame; +} + +OUString SAL_CALL UIConfigElementWrapperBase::getResourceURL() +{ + SolarMutexGuard g; + return m_aResourceURL; +} + +::sal_Int16 SAL_CALL UIConfigElementWrapperBase::getType() +{ + SolarMutexGuard g; + return m_nType; +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/helper/uielementwrapperbase.cxx b/framework/source/helper/uielementwrapperbase.cxx new file mode 100644 index 0000000000..dcf9f89e91 --- /dev/null +++ b/framework/source/helper/uielementwrapperbase.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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include + +#include +#include + +const int UIELEMENT_PROPHANDLE_RESOURCEURL = 1; +const int UIELEMENT_PROPHANDLE_TYPE = 2; +const int UIELEMENT_PROPHANDLE_FRAME = 3; +constexpr OUString UIELEMENT_PROPNAME_RESOURCEURL = u"ResourceURL"_ustr; +constexpr OUString UIELEMENT_PROPNAME_TYPE = u"Type"_ustr; +constexpr OUString UIELEMENT_PROPNAME_FRAME = u"Frame"_ustr; + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::frame; + +namespace framework +{ + +UIElementWrapperBase::UIElementWrapperBase( sal_Int16 nType ) + : ::cppu::OBroadcastHelperVar< ::cppu::OMultiTypeInterfaceContainerHelper, ::cppu::OMultiTypeInterfaceContainerHelper::keyType >( m_aMutex ) + , ::cppu::OPropertySetHelper ( *static_cast< ::cppu::OBroadcastHelper* >(this) ) + , m_aListenerContainer ( m_aMutex ) + , m_nType ( nType ) + , m_bInitialized ( false ) + , m_bDisposed ( false ) +{ +} + +UIElementWrapperBase::~UIElementWrapperBase() +{ +} + +Any SAL_CALL UIElementWrapperBase::queryInterface( const Type& _rType ) +{ + Any aRet = UIElementWrapperBase_BASE::queryInterface( _rType ); + if ( !aRet.hasValue() ) + aRet = OPropertySetHelper::queryInterface( _rType ); + return aRet; +} + +Sequence< Type > SAL_CALL UIElementWrapperBase::getTypes( ) +{ + return comphelper::concatSequences( + UIElementWrapperBase_BASE::getTypes(), + ::cppu::OPropertySetHelper::getTypes() + ); +} + +void SAL_CALL UIElementWrapperBase::addEventListener( const css::uno::Reference< css::lang::XEventListener >& xListener ) +{ + m_aListenerContainer.addInterface( cppu::UnoType::get(), xListener ); +} + +void SAL_CALL UIElementWrapperBase::removeEventListener( const css::uno::Reference< css::lang::XEventListener >& xListener ) +{ + m_aListenerContainer.removeInterface( cppu::UnoType::get(), xListener ); +} + +void SAL_CALL UIElementWrapperBase::initialize( const Sequence< Any >& aArguments ) +{ + SolarMutexGuard g; + + if ( m_bInitialized ) + return; + + for ( const Any& rArg : aArguments ) + { + PropertyValue aPropValue; + if ( rArg >>= aPropValue ) + { + if ( aPropValue.Name == "ResourceURL" ) + aPropValue.Value >>= m_aResourceURL; + else if ( aPropValue.Name == "Frame" ) + { + Reference< XFrame > xFrame; + aPropValue.Value >>= xFrame; + m_xWeakFrame = xFrame; + } + } + } + + m_bInitialized = true; +} + +// XUIElement +css::uno::Reference< css::frame::XFrame > SAL_CALL UIElementWrapperBase::getFrame() +{ + css::uno::Reference< css::frame::XFrame > xFrame( m_xWeakFrame ); + return xFrame; +} + +OUString SAL_CALL UIElementWrapperBase::getResourceURL() +{ + return m_aResourceURL; +} + +::sal_Int16 SAL_CALL UIElementWrapperBase::getType() +{ + return m_nType; +} + +// XUpdatable +void SAL_CALL UIElementWrapperBase::update() +{ + // can be implemented by derived class +} + +// XPropertySet helper +sal_Bool SAL_CALL UIElementWrapperBase::convertFastPropertyValue( Any& /*aConvertedValue*/ , + Any& /*aOldValue*/ , + sal_Int32 /*nHandle*/ , + const Any& /*aValue*/ ) +{ + // Initialize state with sal_False !!! + // (Handle can be invalid) + return false; +} + +void SAL_CALL UIElementWrapperBase::setFastPropertyValue_NoBroadcast( sal_Int32 /*nHandle*/ , + const css::uno::Any& /*aValue*/ ) +{ +} + +void SAL_CALL UIElementWrapperBase::getFastPropertyValue( css::uno::Any& aValue , + sal_Int32 nHandle ) const +{ + switch( nHandle ) + { + case UIELEMENT_PROPHANDLE_RESOURCEURL: + aValue <<= m_aResourceURL; + break; + case UIELEMENT_PROPHANDLE_TYPE: + aValue <<= m_nType; + break; + case UIELEMENT_PROPHANDLE_FRAME: + Reference< XFrame > xFrame( m_xWeakFrame ); + aValue <<= xFrame; + break; + } +} + +::cppu::IPropertyArrayHelper& SAL_CALL UIElementWrapperBase::getInfoHelper() +{ + // Define static member to give structure of properties to baseclass "OPropertySetHelper". + // "impl_getStaticPropertyDescriptor" is a non exported and static function, who will define a static propertytable. + // "true" say: Table is sorted by name. + static ::cppu::OPropertyArrayHelper ourInfoHelper( impl_getStaticPropertyDescriptor(), true ); + + return ourInfoHelper; +} + +css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL UIElementWrapperBase::getPropertySetInfo() +{ + // Create structure of propertysetinfo for baseclass "OPropertySetHelper". + // (Use method "getInfoHelper()".) + static css::uno::Reference< css::beans::XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) ); + + return xInfo; +} + +css::uno::Sequence< css::beans::Property > UIElementWrapperBase::impl_getStaticPropertyDescriptor() +{ + // Create a property array to initialize sequence! + // Table of all predefined properties of this class. It's used from OPropertySetHelper-class! + // Don't forget to change the defines (see begin of this file), if you add, change or delete a property in this list!!! + // It's necessary for methods of OPropertySetHelper. + // ATTENTION: + // YOU MUST SORT FOLLOW TABLE BY NAME ALPHABETICAL !!! + + return + { + css::beans::Property( UIELEMENT_PROPNAME_FRAME, UIELEMENT_PROPHANDLE_FRAME , cppu::UnoType::get(), css::beans::PropertyAttribute::TRANSIENT | css::beans::PropertyAttribute::READONLY ), + css::beans::Property( UIELEMENT_PROPNAME_RESOURCEURL, UIELEMENT_PROPHANDLE_RESOURCEURL , cppu::UnoType::get(), css::beans::PropertyAttribute::TRANSIENT | css::beans::PropertyAttribute::READONLY ), + css::beans::Property( UIELEMENT_PROPNAME_TYPE, UIELEMENT_PROPHANDLE_TYPE , cppu::UnoType::get(), css::beans::PropertyAttribute::TRANSIENT | css::beans::PropertyAttribute::READONLY ) + }; +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/helper/vclstatusindicator.cxx b/framework/source/helper/vclstatusindicator.cxx new file mode 100644 index 0000000000..b493eacc72 --- /dev/null +++ b/framework/source/helper/vclstatusindicator.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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include + +namespace framework { + +VCLStatusIndicator::VCLStatusIndicator(css::uno::Reference< css::awt::XWindow > xParentWindow) + : m_xParentWindow (std::move(xParentWindow )) + , m_pStatusBar (nullptr ) + , m_nRange (0 ) + , m_nValue (0 ) +{ + if (!m_xParentWindow.is()) + throw css::uno::RuntimeException( + "Can't work without a parent window!", + static_cast< css::task::XStatusIndicator* >(this)); +} + +VCLStatusIndicator::~VCLStatusIndicator() +{ +} + +void SAL_CALL VCLStatusIndicator::start(const OUString& sText , + sal_Int32 nRange) +{ + SolarMutexGuard aSolarGuard; + + VclPtr pParentWindow = VCLUnoHelper::GetWindow(m_xParentWindow); + if (!m_pStatusBar) + m_pStatusBar = VclPtr::Create(pParentWindow, WB_3DLOOK|WB_BORDER); + + VCLStatusIndicator::impl_recalcLayout(m_pStatusBar, pParentWindow); + + m_pStatusBar->Show(); + m_pStatusBar->StartProgressMode(sText); + m_pStatusBar->SetProgressValue(0); + + // force repaint! + pParentWindow->Show(); + pParentWindow->Invalidate(InvalidateFlags::Children); + pParentWindow->GetOutDev()->Flush(); + + m_nRange = nRange; + m_nValue = 0; +} + +void SAL_CALL VCLStatusIndicator::reset() +{ + SolarMutexGuard aSolarGuard; + if (m_pStatusBar) + { + m_pStatusBar->SetProgressValue(0); + m_pStatusBar->SetText(OUString()); + } +} + +void SAL_CALL VCLStatusIndicator::end() +{ + SolarMutexGuard aSolarGuard; + + m_nRange = 0; + m_nValue = 0; + + if (m_pStatusBar) + { + m_pStatusBar->EndProgressMode(); + m_pStatusBar->Show(false); + + m_pStatusBar.disposeAndClear(); + } +} + +void SAL_CALL VCLStatusIndicator::setText(const OUString& sText) +{ + SolarMutexGuard aSolarGuard; + if (m_pStatusBar) + m_pStatusBar->SetText(sText); +} + +void SAL_CALL VCLStatusIndicator::setValue(sal_Int32 nValue) +{ + SolarMutexGuard aSolarGuard; + + if (nValue <= m_nRange) + m_nValue = nValue; + else + m_nValue = m_nRange; + + sal_Int32 nRange = m_nRange; + nValue = m_nValue; + + // normalize value to fit the range of 0-100% + sal_uInt16 nPercent = sal::static_int_cast< sal_uInt16 >( + ::std::min( + ((nValue*100) / ::std::max(nRange,sal_Int32(1))), sal_Int32(100))); + + if (m_pStatusBar) + m_pStatusBar->SetProgressValue(nPercent); +} + +void VCLStatusIndicator::impl_recalcLayout(vcl::Window* pStatusBar , + vcl::Window const * pParentWindow) +{ + if ( + (!pStatusBar ) || + (!pParentWindow) + ) + return; + + Size aParentSize = pParentWindow->GetSizePixel(); + pStatusBar->setPosSizePixel(0, + 0, + aParentSize.Width(), + aParentSize.Height()); +} + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/helper/wakeupthread.cxx b/framework/source/helper/wakeupthread.cxx new file mode 100644 index 0000000000..40487c83b8 --- /dev/null +++ b/framework/source/helper/wakeupthread.cxx @@ -0,0 +1,60 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include + +#include +#include + +using namespace std::chrono_literals; + +void framework::WakeUpThread::execute() { + for (;;) { + { + std::unique_lock g(mutex_); + condition_.wait_for(g, 25ms, [this] { return terminate_; }); + if (terminate_) { + break; + } + } + css::uno::Reference up(updatable_); + if (up.is()) { + up->update(); + } + } +} + +framework::WakeUpThread::WakeUpThread( + css::uno::Reference const & updatable): + Thread("WakeUpThread"), updatable_(updatable), terminate_(false) +{} + +void framework::WakeUpThread::stop() { + { + std::unique_lock g(mutex_); + terminate_ = true; + } + condition_.notify_one(); + join(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/inc/accelerators/acceleratorcache.hxx b/framework/source/inc/accelerators/acceleratorcache.hxx new file mode 100644 index 0000000000..adfdf8fe5a --- /dev/null +++ b/framework/source/inc/accelerators/acceleratorcache.hxx @@ -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 . + */ + +#pragma once + +#include + +#include + +#include +#include + +// definition + +namespace framework +{ + +/** + @short implements a cache for any accelerator configuration. + + @descr It's implemented threadsafe, supports copy-on-write pattern + and a flush mechanism to support concurrent access to the same + configuration. + + copy-on-write ... How? Do the following: + */ +class AcceleratorCache +{ + public: + + /** + commands -> keys + */ + typedef ::std::vector< css::awt::KeyEvent > TKeyList; + + private: + + typedef std::unordered_map TCommand2Keys; + + /** + keys -> commands + */ + typedef std::unordered_map< css::awt::KeyEvent , + OUString , + KeyEventHashCode , + KeyEventEqualsFunc > TKey2Commands; + + /** map commands to keys in relation 1:n. + First key is interpreted as preferred one! */ + TCommand2Keys m_lCommand2Keys; + + /** map keys to commands in relation 1:1. */ + TKey2Commands m_lKey2Commands; + + public: + /** @short checks if the specified key exists. + + @param aKey + the key, which should be checked. + + @return [bool] + sal_True if the specified key exists inside this container. + */ + bool hasKey(const css::awt::KeyEvent& aKey) const; + bool hasCommand(const OUString& sCommand) const; + + TKeyList getAllKeys() const; + + /** @short add a new or change an existing key-command pair + of this container. + + @param aKey + describe the key. + + @param sCommand + describe the command. + */ + void setKeyCommandPair(const css::awt::KeyEvent& aKey , + const OUString& sCommand); + + /** @short returns the list of keys, which are registered + for this command. + + @param sCommand + describe the command. + + @return [TKeyList] + the list of registered keys. Can be empty! + */ + TKeyList getKeysByCommand(const OUString& sCommand) const; + + OUString getCommandByKey(const css::awt::KeyEvent& aKey) const; + void removeKey(const css::awt::KeyEvent& aKey); + void removeCommand(const OUString& sCommand); +}; + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/inc/accelerators/acceleratorconfiguration.hxx b/framework/source/inc/accelerators/acceleratorconfiguration.hxx new file mode 100644 index 0000000000..ddd4b3b257 --- /dev/null +++ b/framework/source/inc/accelerators/acceleratorconfiguration.hxx @@ -0,0 +1,312 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 + +#include + +#include +#include + +#include +#include + +#include +#include +#include + +// TODO use XPresetHandler interface instead if available +#include + +#include + +// definition + +namespace framework +{ + +inline constexpr OUString CFG_ENTRY_PRIMARY = u"PrimaryKeys"_ustr; +inline constexpr OUString CFG_ENTRY_GLOBAL = u"Global"_ustr; +inline constexpr OUString CFG_ENTRY_MODULES = u"Modules"_ustr; + +/** + implements a read/write access to the accelerator configuration. + */ +class XMLBasedAcceleratorConfiguration : public ::cppu::WeakImplHelper< + css::form::XReset, // TODO use XPresetHandler instead if available + css::ui::XAcceleratorConfiguration > // => css::ui::XUIConfigurationPersistence + // css::ui::XUIConfigurationStorage + // css::ui::XUIConfiguration +{ + + // member + + protected: + + /** the global uno service manager. + Must be used to create own needed services. */ + css::uno::Reference< css::uno::XComponentContext > m_xContext; + + /** used to: + i ) copy configuration files from the share to the user layer + ii ) provide access to these config files + iii) cache all sub storages on the path from the top to the bottom(!) + iv ) provide commit for changes. */ + PresetHandler m_aPresetHandler; + + /** contains the cached configuration data */ + AcceleratorCache m_aReadCache; + + /** used to implement the copy on write pattern! */ + std::unique_ptr m_pWriteCache; + + // native interface! + + public: + + XMLBasedAcceleratorConfiguration( const css::uno::Reference< css::uno::XComponentContext >& xContext); + virtual ~XMLBasedAcceleratorConfiguration( ) override; + + // uno interface! + + public: + + // XAcceleratorConfiguration + virtual css::uno::Sequence< css::awt::KeyEvent > SAL_CALL getAllKeyEvents() override; + + virtual OUString SAL_CALL getCommandByKeyEvent(const css::awt::KeyEvent& aKeyEvent) override; + + virtual void SAL_CALL setKeyEvent(const css::awt::KeyEvent& aKeyEvent, + const OUString& sCommand ) override; + + virtual void SAL_CALL removeKeyEvent(const css::awt::KeyEvent& aKeyEvent) override; + + virtual css::uno::Sequence< css::awt::KeyEvent > SAL_CALL getKeyEventsByCommand(const OUString& sCommand) override; + + virtual css::uno::Sequence< css::uno::Any > SAL_CALL getPreferredKeyEventsForCommandList(const css::uno::Sequence< OUString >& lCommandList) override; + + virtual void SAL_CALL removeCommandFromAllKeyEvents(const OUString& sCommand) override; + + // XUIConfigurationPersistence + virtual void SAL_CALL reload() override; + + virtual void SAL_CALL store() override; + + virtual void SAL_CALL storeToStorage(const css::uno::Reference< css::embed::XStorage >& xStorage) override; + + virtual sal_Bool SAL_CALL isModified() override; + + virtual sal_Bool SAL_CALL isReadOnly() override; + + // XUIConfigurationStorage + virtual void SAL_CALL setStorage(const css::uno::Reference< css::embed::XStorage >& xStorage) override; + + virtual sal_Bool SAL_CALL hasStorage() override; + + // XUIConfiguration + virtual void SAL_CALL addConfigurationListener(const css::uno::Reference< css::ui::XUIConfigurationListener >& xListener) override; + + virtual void SAL_CALL removeConfigurationListener(const css::uno::Reference< css::ui::XUIConfigurationListener >& xListener) override; + + // XReset + // TODO use XPresetHandler instead if available + virtual void SAL_CALL reset() override; + + virtual void SAL_CALL addResetListener(const css::uno::Reference< css::form::XResetListener >& xListener) override; + + virtual void SAL_CALL removeResetListener(const css::uno::Reference< css::form::XResetListener >& xListener) override; + + // called when changes occurred in the storage + void changesOccurred(); + + // helper for derived classes + + protected: + + /** @short return the current office locale. + + @descr We do not cache this value, because we are not listen + for changes on the configuration layer ... + + @return OUString + The current office locale as BCP47 string. + */ + OUString impl_ts_getLocale() const; + + // helper + + private: + + /** @short load a configuration set, using the given stream. + + @param xStream + provides the XML structure as stream. + */ + void impl_ts_load(const css::uno::Reference< css::io::XInputStream >& xStream); + + /** @short save a configuration set, using the given stream. + + @param xStream + the XML structure can be written there. + */ + void impl_ts_save(const css::uno::Reference< css::io::XOutputStream >& xStream); + + /** @short returns a reference to one of our internal cache members. + + @descr We implement the copy-on-write pattern. Doing so + we know two caches internally. The second one is used + only, if the container was changed. + + This method here returns access to one of these + caches - depending on the change state of this + configuration service. + + @param bWriteAccessRequested + if the outside code wish to change the container + it must call this method with "sal_True". So the internal + cache can be prepared for that (means copy-on-write ...). + + @return [AcceleratorCache] + c++ reference(!) to one of our internal caches. + */ + AcceleratorCache& impl_getCFG(bool bWriteAccessRequested = false); + +}; + +class XCUBasedAcceleratorConfiguration : public ::cppu::WeakImplHelper< + css::util::XChangesListener, + css::form::XReset, // TODO use XPresetHandler instead if available + css::ui::XAcceleratorConfiguration > // => css::ui::XUIConfigurationPersistence + // css::ui::XUIConfigurationStorage + // css::ui::XUIConfiguration +{ + + // member + + protected: + + /** the global uno service manager. + Must be used to create own needed services. */ + css::uno::Reference< css::uno::XComponentContext > m_xContext; + + css::uno::Reference< css::container::XNameAccess > m_xCfg; + AcceleratorCache m_aPrimaryReadCache; + AcceleratorCache m_aSecondaryReadCache; + std::unique_ptr m_pPrimaryWriteCache; + std::unique_ptr m_pSecondaryWriteCache; + + OUString m_sGlobalOrModules; + OUString m_sModuleCFG; + + // native interface! + + public: + + XCUBasedAcceleratorConfiguration( css::uno::Reference< css::uno::XComponentContext > xContext ); + virtual ~XCUBasedAcceleratorConfiguration( ) override; + + // uno interface! + + public: + + // XAcceleratorConfiguration + virtual css::uno::Sequence< css::awt::KeyEvent > SAL_CALL getAllKeyEvents() override; + + virtual OUString SAL_CALL getCommandByKeyEvent(const css::awt::KeyEvent& aKeyEvent) override; + + virtual void SAL_CALL setKeyEvent(const css::awt::KeyEvent& aKeyEvent, + const OUString& sCommand ) override; + + virtual void SAL_CALL removeKeyEvent(const css::awt::KeyEvent& aKeyEvent) override; + + virtual css::uno::Sequence< css::awt::KeyEvent > SAL_CALL getKeyEventsByCommand(const OUString& sCommand) override; + + virtual css::uno::Sequence< css::uno::Any > SAL_CALL getPreferredKeyEventsForCommandList(const css::uno::Sequence< OUString >& lCommandList) override; + + virtual void SAL_CALL removeCommandFromAllKeyEvents(const OUString& sCommand) override; + + // XUIConfigurationPersistence + virtual void SAL_CALL reload() override; + + virtual void SAL_CALL store() override; + + virtual void SAL_CALL storeToStorage(const css::uno::Reference< css::embed::XStorage >& xStorage) override; + + virtual sal_Bool SAL_CALL isModified() override; + + virtual sal_Bool SAL_CALL isReadOnly() override; + + // XUIConfigurationStorage + virtual void SAL_CALL setStorage(const css::uno::Reference< css::embed::XStorage >& xStorage) override; + + virtual sal_Bool SAL_CALL hasStorage() override; + + // XUIConfiguration + virtual void SAL_CALL addConfigurationListener(const css::uno::Reference< css::ui::XUIConfigurationListener >& xListener) override; + + virtual void SAL_CALL removeConfigurationListener(const css::uno::Reference< css::ui::XUIConfigurationListener >& xListener) override; + + // XReset + // TODO use XPresetHandler instead if available + virtual void SAL_CALL reset() override; + + virtual void SAL_CALL addResetListener(const css::uno::Reference< css::form::XResetListener >& xListener) override; + + virtual void SAL_CALL removeResetListener(const css::uno::Reference< css::form::XResetListener >& xListener) override; + + // css.util.XChangesListener + virtual void SAL_CALL changesOccurred(const css::util::ChangesEvent& aEvent) override; + + // css.lang.XEventListener + virtual void SAL_CALL disposing(const css::lang::EventObject& aEvent) override; + + // helper for derived classes + + protected: + + /** @short return the current office locale. + + @descr We do not cache this value, because we are not listen + for changes on the configuration layer ... + + @return OUString + The current office locale as BCP47 string. + */ + OUString impl_ts_getLocale() const; + + // helper + + private: + + void impl_ts_load(bool bPreferred, const css::uno::Reference< css::container::XNameAccess >& xCfg); + void impl_ts_save(bool bPreferred); + + void insertKeyToConfiguration(const css::awt::KeyEvent& aKeyEvent, const OUString& sCommand, const bool bPreferred); + void removeKeyFromConfiguration(const css::awt::KeyEvent& aKeyEvent, const bool bPreferred); + + void reloadChanged(const OUString& sPrimarySecondary, std::u16string_view sGlobalModules, const OUString& sModule, const OUString& sKey); + AcceleratorCache& impl_getCFG(bool bPreferred, bool bWriteAccessRequested = false); + +}; + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/inc/accelerators/keymapping.hxx b/framework/source/inc/accelerators/keymapping.hxx new file mode 100644 index 0000000000..88429a0122 --- /dev/null +++ b/framework/source/inc/accelerators/keymapping.hxx @@ -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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 +#include + +// definition + +namespace framework +{ + +/** + can be used to map key identifier to the + corresponding key codes ... + */ +class KeyMapping +{ + + // const, types + + private: + + /** @short is used to map a key code + to the right key identifier, which is + used to make the xml file "human readable" + */ + struct KeyIdentifierInfo + { + sal_Int16 Code; + const char* Identifier; + }; + + /** @short hash structure to map identifier to key codes. */ + typedef std::unordered_map Identifier2CodeHash; + + /** @short hash structure to map key codes to identifier. */ + typedef std::unordered_map Code2IdentifierHash; + + // member + + private: + + static KeyIdentifierInfo const KeyIdentifierMap[]; + + /** @short hash to map identifier to key codes. */ + Identifier2CodeHash m_lIdentifierHash; + + /** @short hash to map key codes to identifier. */ + Code2IdentifierHash m_lCodeHash; + + // interface + + public: + + KeyMapping(); + + static KeyMapping & get(); + + /** @short return a suitable key code + for the specified key identifier. + + @param sIdentifier + string value, which describe the key. + + @return [css::awt::KeyEvent] + the corresponding key code as + short value. + + @throw [css::lang::IllegalArgumentException] + if the given identifier does not describe + a well known key code. + */ + sal_uInt16 mapIdentifierToCode(const OUString& sIdentifier); + + /** @short return a suitable key identifier + for the specified key code. + + @param nCode + short value, which describe the key. + + @return The corresponding string identifier. + */ + OUString mapCodeToIdentifier(sal_uInt16 nCode); + + // helper + + private: + + /** @short check if the given string describe a numeric + value ... and convert it. + + @param sIdentifier + the string value, which should be converted. + + @param rCode + contains the converted code, but is defined only + if this method returns sal_True! + + @return [boolean] + sal_True if conversion was successful. + */ + bool impl_st_interpretIdentifierAsPureKeyCode(std::u16string_view sIdentifier, + sal_uInt16& rCode ); +}; + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/inc/accelerators/presethandler.hxx b/framework/source/inc/accelerators/presethandler.hxx new file mode 100644 index 0000000000..b0fee38b43 --- /dev/null +++ b/framework/source/inc/accelerators/presethandler.hxx @@ -0,0 +1,378 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 + +#include + +#include + +#include + +#include + +#include + +namespace framework +{ +/** + TODO document me + + /global//.xml + /modules///.xml + + RESOURCETYPE PRESET TARGET + (share) (user) + "accelerator" "default" "current" + "word" + "excel" + + "menubar" "default" "menubar" + + */ +class PresetHandler +{ + public: + + /** @short this handler can provide different + types of configuration. + + @descr Means: a global or a module dependent + or ... configuration. + */ + enum EConfigType + { + E_GLOBAL, + E_MODULES, + E_DOCUMENT + }; + + private: + + /** @short can be used to create on needed uno resources. */ + css::uno::Reference< css::uno::XComponentContext > m_xContext; + + /** @short knows the type of provided configuration. + + @descr e.g. global, modules, ... + */ + EConfigType m_eConfigType; + + /** @short if we run in document mode, we can't use the global root storages! + We have to use a special document storage explicitly. */ + StorageHolder m_lDocumentStorages; + + /** @short holds the folder storage of the share layer alive, + where the current configuration set exists. + + @descr Note: If this preset handler works in document mode + this member is meant relative to the document root... + not to the share layer root! + + Further is defined, that m_xWorkingStorageUser + is equals to m_xWorkingStorageShare then! + */ + css::uno::Reference< css::embed::XStorage > m_xWorkingStorageShare; + + /** @short global language-independent storage + */ + css::uno::Reference< css::embed::XStorage > m_xWorkingStorageNoLang; + + /** @short holds the folder storage of the user layer alive, + where the current configuration set exists. + + @descr Note: If this preset handler works in document mode + this member is meant relative to the document root... + not to the user layer root! + + Further is defined, that m_xWorkingStorageUser + is equals to m_xWorkingStorageShare then! + */ + css::uno::Reference< css::embed::XStorage > m_xWorkingStorageUser; + + /** @short knows the relative path from the root. */ + OUString m_sRelPathShare; + OUString m_sRelPathUser; + + // native interface + + public: + + /** @short does nothing real. + + @param xContext + points to a uno service manager, which is used internally + to create own needed uno resources. + */ + PresetHandler(css::uno::Reference< css::uno::XComponentContext > xContext); + + /** @short copy ctor */ + PresetHandler(const PresetHandler& rCopy); + + /** @short closes all open storages ... if user forgot that .-) */ + ~PresetHandler(); + + /** @short free all currently cache(!) storages. */ + void forgetCachedStorages(); + + /** @short return access to the internally used and cached root storage. + + @descr These root storages are the base of all further opened + presets and targets. They are provided here only, to support + older implementations, which base on them ... + + getOrCreate...() - What does it mean? + Such root storage will be created one times only and + cached then internally till the last instance of such PresetHandler + dies. + + @return css::embed::XStorage + which represent a root storage. + */ + css::uno::Reference< css::embed::XStorage > getOrCreateRootStorageShare(); + css::uno::Reference< css::embed::XStorage > getOrCreateRootStorageUser(); + + /** @short provides access to the current working storages. + + @descr Working storages are the "lowest" storages, where the + preset and target files exists. + + @return css::embed::XStorage + which the current working storage. + */ + css::uno::Reference< css::embed::XStorage > getWorkingStorageUser() const; + + /** @short check if there is a parent storage well known for + the specified child storage and return it. + + @param xChild + the child storage where a paranet storage should be searched for. + + @return css::embed::XStorage + A valid storage if a paranet exists. NULL otherwise. + */ + css::uno::Reference< css::embed::XStorage > getParentStorageShare(); + css::uno::Reference< css::embed::XStorage > getParentStorageUser (); + + /** @short free all internal structures and let this handler + work on a new type of configuration sets. + + @param eConfigType + differ between global or module dependent configuration. + + @param sResourceType + differ between menubar/toolbar/accelerator/... configuration. + + @param sModule + if sResourceType is set to a module dependent configuration, + it address the current application module. + + @param xDocumentRoot + if sResourceType is set to E_DOCUMENT, this value points to the + root storage inside the document, where we can save our + configuration files. Note: that's not the real root of the document... + its only a sub storage. But we interpret it as our root storage. + + @param rLanguageTag + in case this configuration supports localized entries, + the current locale must be set. + + Localization will be represented as directory structure + of provided presets. Means: you call us with a preset name "default"; + and we use e.g. "/en-US/default.xml" internally. + + If no localization exists for this preset set, this class + will work in default mode - means "no locale" - automatically. + e.g. "/default.xml" + + @throw css::uno::RuntimeException(!) + if the specified resource couldn't be located. + */ + void connectToResource( EConfigType eConfigType , + std::u16string_view sResourceType , + std::u16string_view sModule , + const css::uno::Reference< css::embed::XStorage >& xDocumentRoot , + const LanguageTag& rLanguageTag = LanguageTag(LANGUAGE_USER_PRIV_NOTRANSLATE)); + + /** @short try to copy the specified preset from the share + layer to the user layer and establish it as the + specified target. + + @descr Means: copy share/.../.xml user/.../.xml + Note: The target will be overwritten completely or + created as new by this operation! + + @param sPreset + the ALIAS name of an existing preset. + + @param sTarget + the ALIAS name of the target. + + @throw css::container::NoSuchElementException + if the specified preset does not exists. + + @throw css::io::IOException + if copying failed. + */ + void copyPresetToTarget(std::u16string_view sPreset, + std::u16string_view sTarget); + + /** @short open the specified preset as stream object + and return it. + + @descr Note: Because presets resist inside the share + layer, they will be opened readonly every time. + + @param sPreset + the ALIAS name of an existing preset. + + Accesses the global language-independent storage instead of the preset storage + + @return The opened preset stream ... or NULL if the preset does not exists. + */ + css::uno::Reference< css::io::XStream > openPreset(std::u16string_view sPreset); + + /** @short open the specified target as stream object + and return it. + + @descr Note: Targets resist inside the user + layer. Normally they are opened in read/write mode. + But it will be opened readonly automatically if that isn't possible + (may be the file is write protected on the system ...). + + @param sTarget + the ALIAS name of the target. + + @return The opened target stream ... or NULL if the target does not exists + or couldn't be created as new one. + */ + css::uno::Reference< css::io::XStream > openTarget( + std::u16string_view sTarget, sal_Int32 nMode); + + /** @short do anything which is necessary to flush all changes + back to disk. + + @descr We have to call commit on all cached sub storages on the + path from the root storage upside down to the working storage + (which are not really used, but required to be holded alive!). + */ + void commitUserChanges(); + + /** TODO */ + void addStorageListener(XMLBasedAcceleratorConfiguration* pListener); + void removeStorageListener(XMLBasedAcceleratorConfiguration* pListener); + + // helper + + private: + + /** @short open a config path ignoring errors (catching exceptions). + + @descr We catch only normal exceptions here - no runtime exceptions. + + @param sPath + the configuration path, which should be opened. + + @param eMode + the open mode (READ/READWRITE) + + @param bShare + force using of the share layer instead of the user layer. + + @return An opened storage in case method was successful - null otherwise. + */ + css::uno::Reference< css::embed::XStorage > impl_openPathIgnoringErrors(const OUString& sPath , + sal_Int32 eMode , + bool bShare); + + /** @short try to find the specified locale inside list of possible ones. + + @descr The list of possible locale values was e.g. retrieved from the system + (configuration, directory listing etcpp). The locale normally represent + the current office locale. This method search for a suitable item by using + different algorithm. + a) exact search + b) search with using fallbacks + + @param lLocalizedValues + list of BCP47 language tags / locale codes + + @param rLanguageTag + [IN ] the current office locale, which should be searched inside lLocalizedValues. + [OUT] in case fallbacks was allowed, it contains afterwards the fallback locale. + + @param bAllowFallbacks + enable/disable using of fallbacks + + @return An iterator, which points directly into lLocalizedValue list. + As a negative result the special iterator lLocalizedValues.end() will be returned. + */ + ::std::vector< OUString >::const_iterator impl_findMatchingLocalizedValue(const ::std::vector< OUString >& lLocalizedValues, + OUString& rLanguageTag , + bool bAllowFallbacks ); + + /** @short open a config path ignoring errors (catching exceptions). + + @descr We catch only normal exceptions here - no runtime exceptions. + Further the path itself is tries in different versions (using locale + specific attributes). + e.g. "path/e-US" => "path/en" => "path/de" + + @param sPath + the configuration path, which should be opened. + It's further used as out parameter too, so we can return the localized + path! + + @param eMode + the open mode (READ/READWRITE) + + @param bShare + force using of the share layer instead of the user layer. + + @param rLanguageTag + [IN ] contains the start locale for searching localized sub dirs. + [OUT] contains the locale of a found localized sub dir + + @param bAllowFallback + enable/disable fallback handling for locales + + @return An opened storage in case method was successful - null otherwise. + */ + css::uno::Reference< css::embed::XStorage > impl_openLocalizedPathIgnoringErrors(OUString& sPath , + sal_Int32 eMode , + bool bShare , + OUString& rLanguageTag , + bool bAllowFallback); + + /** @short returns the names of all sub storages of specified storage. + + @param xFolder + the base storage for this operation. + + @return [vector< string >] + a list of folder names. + */ + ::std::vector< OUString > impl_getSubFolderNames(const css::uno::Reference< css::embed::XStorage >& xFolder); +}; + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/inc/accelerators/storageholder.hxx b/framework/source/inc/accelerators/storageholder.hxx new file mode 100644 index 0000000000..355bedeaa6 --- /dev/null +++ b/framework/source/inc/accelerators/storageholder.hxx @@ -0,0 +1,182 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 + +#include +#include +#include + +namespace framework +{ + +class XMLBasedAcceleratorConfiguration; +/** + TODO document me + */ +class StorageHolder final +{ + + // types + public: + + /** @short TODO */ + typedef ::std::vector< css::uno::Reference< css::embed::XStorage > > TStorageList; + + typedef ::std::vector< XMLBasedAcceleratorConfiguration* > TStorageListenerList; + + struct TStorageInfo + { + public: + css::uno::Reference< css::embed::XStorage > Storage; + sal_Int32 UseCount; + TStorageListenerList Listener; + + TStorageInfo() + : UseCount(0) + {} + }; + + /** @short TODO */ + typedef std::unordered_map< OUString, + TStorageInfo > TPath2StorageInfo; + + // member + private: + mutable std::mutex m_mutex; + + /** @short TODO */ + css::uno::Reference< css::embed::XStorage > m_xRoot; + + /** @short TODO */ + TPath2StorageInfo m_lStorages; + + // interface + public: + + /** @short TODO + */ + StorageHolder(); + + /** @short TODO + */ + ~StorageHolder(); + + /** @short TODO + */ + void forgetCachedStorages(); + + /** @short TODO + */ + void setRootStorage(const css::uno::Reference< css::embed::XStorage >& xRoot); + + /** @short TODO + */ + css::uno::Reference< css::embed::XStorage > getRootStorage() const; + + /** @short TODO + open or get! + */ + css::uno::Reference< css::embed::XStorage > openPath(const OUString& sPath , + sal_Int32 nOpenMode); + + /** @short TODO + */ + StorageHolder::TStorageList getAllPathStorages(const OUString& sPath); + + /** @short TODO + */ + void commitPath(const OUString& sPath); + + /** @short TODO + */ + void closePath(const OUString& sPath); + + /** @short TODO + */ + void notifyPath(const OUString& sPath); + + /** @short TODO + */ + void addStorageListener( XMLBasedAcceleratorConfiguration* pListener, + const OUString& sPath ); + + /** @short TODO + */ + void removeStorageListener( XMLBasedAcceleratorConfiguration* pListener, + const OUString& sPath ); + + /** @short TODO + */ + OUString getPathOfStorage(const css::uno::Reference< css::embed::XStorage >& xStorage); + + /** @short TODO + */ + css::uno::Reference< css::embed::XStorage > getParentStorage(const css::uno::Reference< css::embed::XStorage >& xChild); + + /** @short TODO + */ + css::uno::Reference< css::embed::XStorage > getParentStorage(const OUString& sChildPath); + + /** @short TODO + */ + StorageHolder& operator=(const StorageHolder& rCopy); + + /** @short opens a sub element of the specified base storage. + If eOpenMode contains an ELEMENT_WRITE flag remove it and try it with the rest of eOpenMode flags + again. + + @descr First this method try to open the requested sub element + using the given open mode. If it failed there is second step, + which tries to do the same again ... but removing a might existing + WRITE flag from the open mode. The user can suppress this fallback + handling by setting the parameter bAllowFallback to sal_False. + + @param xBaseStorage + the storage, where the sub element should be searched. + + @param sSubElement + the full name of the sub element. + e.g. "default.xml" + + @param eOpenMode + a flag field, which set the open mode for this operation. + + */ + static css::uno::Reference< css::embed::XStorage > openSubStorageWithFallback(const css::uno::Reference< css::embed::XStorage >& xBaseStorage , + const OUString& sSubStorage , + sal_Int32 eOpenMode); + + // helper + private: + + /** @short TODO + */ + static OUString impl_st_normPath(const OUString& sPath); + + /** @short TODO + */ + static std::vector impl_st_parsePath(std::u16string_view sPath); +}; + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/inc/dispatch/dispatchdisabler.hxx b/framework/source/inc/dispatch/dispatchdisabler.hxx new file mode 100644 index 0000000000..662eeb5d7c --- /dev/null +++ b/framework/source/inc/dispatch/dispatchdisabler.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/. + */ +#pragma once + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace framework { + +/** + * Implementation of a service to make it easy to disable a whole + * suite of UNO commands in a batch - and have that act in-process. + * + * Often external re-use of LibreOffice wants a very cut-down set + * of functionality included, and disabling elements remotely one + * by one performs poorly. + */ +class DispatchDisabler final : public ::cppu::WeakImplHelper< + css::lang::XInitialization, + css::container::XNameContainer, + css::frame::XDispatchProviderInterceptor, + css::frame::XInterceptorInfo, + css::lang::XServiceInfo > +{ + std::set maDisabledURLs; + css::uno::Reference< css::frame::XDispatchProvider > mxSlave; + css::uno::Reference< css::frame::XDispatchProvider > mxMaster; +public: + DispatchDisabler(const css::uno::Reference< css::uno::XComponentContext >& rxContext); + + // XInitialization + virtual void SAL_CALL initialize( const ::css::uno::Sequence< ::css::uno::Any >& aArguments ) override; + + // XDispatchProvider + virtual ::css::uno::Reference< ::css::frame::XDispatch > SAL_CALL + queryDispatch( const ::css::util::URL& URL, + const OUString& TargetFrameName, + ::sal_Int32 SearchFlags ) override; + virtual ::css::uno::Sequence< ::css::uno::Reference< ::css::frame::XDispatch > > SAL_CALL + queryDispatches( const ::css::uno::Sequence< ::css::frame::DispatchDescriptor >& Requests ) 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 >& NewDispatchProvider ) override; + virtual ::css::uno::Reference< ::css::frame::XDispatchProvider > SAL_CALL + getMasterDispatchProvider() override; + virtual void SAL_CALL + setMasterDispatchProvider( const ::css::uno::Reference< ::css::frame::XDispatchProvider >& NewSupplier ) override; + + // XInterceptorInfo + virtual ::css::uno::Sequence< OUString > SAL_CALL + getInterceptedURLs() override; + + // XElementAccess + virtual ::css::uno::Type SAL_CALL getElementType() override; + virtual ::sal_Bool SAL_CALL hasElements() 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; + + // XNameReplace + virtual void SAL_CALL replaceByName( const OUString& aName, const ::css::uno::Any& aElement ) override; + + // XNameContainer + virtual void SAL_CALL insertByName( const OUString& aName, const ::css::uno::Any& aElement ) override; + virtual void SAL_CALL removeByName( const OUString& Name ) override; + + /* interface 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; +}; + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/inc/dispatch/loaddispatcher.hxx b/framework/source/inc/dispatch/loaddispatcher.hxx new file mode 100644 index 0000000000..195a46af82 --- /dev/null +++ b/framework/source/inc/dispatch/loaddispatcher.hxx @@ -0,0 +1,120 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 + +#include +#include + +#include +#include + +namespace framework{ + +/** @short implements a dispatch object which can be used to load + non-visible components (by using the mechanism of ContentHandler) + or visible-components (by using the mechanism of FrameLoader). + */ +class LoadDispatcher final : public ::cppu::WeakImplHelper< css::frame::XNotifyingDispatch, // => XDispatch => XInterface + css::frame::XSynchronousDispatch > +{ + + // member + + private: + osl::Mutex m_mutex; + + /** @short TODO document me */ + css::uno::WeakReference< css::frame::XFrame > m_xOwnerFrame; + + /** @short TODO document me */ + OUString m_sTarget; + + /** @short TODO document me */ + sal_Int32 m_nSearchFlags; + + /** @short TODO document me */ + LoadEnv m_aLoader; + + // native interface + + public: + + /** @short creates a new instance and initialize it with all necessary parameters. + + @descr Every instance of such LoadDispatcher can be used for the specified context only. + That means: it can be used to load any further requested content into the here(!) + specified target frame. + + @param xContext + will be used to create own needed services on demand. + + @param xOwnerFrame + used as startpoint to locate the right target frame. + + @param sTargetName + the name or the target frame for loading or a special qualifier + which define such target. + + @param nSearchFlags + used in case sTargetFrame isn't a special one. + */ + LoadDispatcher(const css::uno::Reference< css::uno::XComponentContext >& xContext, + const css::uno::Reference< css::frame::XFrame >& xOwnerFrame , + OUString sTargetName , + sal_Int32 nSearchFlags); + + /** @short used to free internal resources. + */ + virtual ~LoadDispatcher() override; + + // uno interface + + public: + + // XNotifyingDispatch + virtual void SAL_CALL dispatchWithNotification(const css::util::URL& aURL , + const css::uno::Sequence< css::beans::PropertyValue >& lArguments, + const css::uno::Reference< css::frame::XDispatchResultListener >& xListener ) 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; + + // XSynchronousDispatch + virtual css::uno::Any SAL_CALL dispatchWithReturnValue( const css::util::URL& aURL , + const css::uno::Sequence< css::beans::PropertyValue >& lArguments ) override; + + private: + css::uno::Any impl_dispatch( const css::util::URL& rURL, + const css::uno::Sequence< css::beans::PropertyValue >& lArguments, + const css::uno::Reference< css::frame::XDispatchResultListener >& xListener ); +}; // class LoadDispatcher + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/inc/dispatch/windowcommanddispatch.hxx b/framework/source/inc/dispatch/windowcommanddispatch.hxx new file mode 100644 index 0000000000..4a8a22d81c --- /dev/null +++ b/framework/source/inc/dispatch/windowcommanddispatch.hxx @@ -0,0 +1,109 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 +#include + +#include +#include +#include + +namespace com::sun::star::uno { + class XComponentContext; +} +class VclWindowEvent; + +namespace framework{ + +/** @short internal helper to bind e.g. MAC-Menu events to our internal dispatch API. + + @descr On e.g. MAC platform system menus are merged together with some fix entries as + e.g. "Pereferences" or "About". These menu entries trigger hard coded commands. + Here we map these commands to the right URLs and dispatch them. + + This helper knows a frame and its container window (where VCL provide the hard coded + commands). We hold those objects weak so there is no need to react for complex UNO dispose/ing() + scenarios. On the other side VCL does not hold us alive (because it doesn't know our UNO reference). + So we register at the VCL level as an event listener and + */ +class WindowCommandDispatch final +{ + private: + std::mutex m_mutex; + + /// can be used to create own needed services on demand. + css::uno::Reference< css::uno::XComponentContext > m_xContext; + + /// knows the frame, where we dispatch our commands as weak reference + css::uno::WeakReference< css::frame::XFrame > m_xFrame; + + /// knows the VCL window (where the hard coded commands occurred) as weak XWindow reference + css::uno::WeakReference< css::awt::XWindow > m_xWindow; + + // native interface + + public: + + /** @short creates a new instance and initialize it with all necessary parameters. + + @descr Every instance of such MACDispatch can be used for the specified context only. + Means: 1 MACDispatch object is bound to 1 Frame/Window pair in which context + the detected commands will be executed. + + @param xContext + will be used to create own needed services on demand. + + @param xFrame + used as for new detected commands. + */ + WindowCommandDispatch(css::uno::Reference< css::uno::XComponentContext > xContext , + const css::uno::Reference< css::frame::XFrame >& xFrame); + + /** @short used to free internal resources. + */ + ~WindowCommandDispatch(); + + // implementation + + private: + + /** @short establish all listener connections we need. + + @descr Those listener connections will be created one times only (see ctor). + Afterwards we listen for incoming events till our referred frame/window pair + will be closed. + */ + void impl_startListening(); + + /** @short drop all listener connections we need. + + */ + void impl_stopListening(); + + /** @short callback from VCL to notify new commands + */ + DECL_LINK( impl_notifyCommand, VclWindowEvent&, void ); + +}; // class WindowCommandDispatch + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/inc/loadenv/actionlockguard.hxx b/framework/source/inc/loadenv/actionlockguard.hxx new file mode 100644 index 0000000000..ee52fcc0dc --- /dev/null +++ b/framework/source/inc/loadenv/actionlockguard.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 . + */ + +#pragma once + +#include +#include + +namespace framework{ + +/** @short implements a guard, which can use the interface + XActionLockable. + + @descr This guard should be used to be sure, that any lock will be + released. Otherwise the locked document can hinder the office on shutdown! +*/ +class ActionLockGuard final +{ + + // member + + private: + std::mutex m_mutex; + + /** @short points to the object, which can be locked from outside. */ + css::uno::Reference< css::document::XActionLockable > m_xActionLock; + + /** @short knows if a lock exists on the internal lock object + forced by this guard instance. */ + bool m_bActionLocked; + + // interface + + public: + + /** @short default ctor to initialize a "non working guard". + + @descr That can be useful in cases, where no resource still exists, + but will be available next time. Then this guard can be used + in a mode "use guard for more than one resources". + */ + ActionLockGuard() + : m_bActionLocked(false) + { + } + + /** @short release this guard instance and make sure, that no lock + will exist afterwards on the internal wrapped resource. + */ + ~ActionLockGuard() + { + unlock(); + } + + /** @short set a new resource for locking at this guard. + + @descr This call will fail, if an internal resource already exists + and is currently locked. + + @param xLock + points to the outside resource, which should be locked. + + @return sal_True, if new resource could be set and locked. + sal_False otherwise. + */ + bool setResource(const css::uno::Reference< css::document::XActionLockable >& xLock) + { + std::unique_lock g(m_mutex); + + if (m_bActionLocked || !xLock.is()) + return false; + + m_xActionLock = xLock; + m_xActionLock->addActionLock(); + m_bActionLocked = m_xActionLock->isActionLocked(); + + return true; + } + + /** @short set a new resource for locking at this guard. + + @descr This call will fail, if an internal resource already exists + and is currently locked. + + @param xLock + points to the outside resource, which should be locked. + + @return sal_True, if new resource could be set and locked. + sal_False otherwise. + */ + void freeResource() + { + // SAFE -> .......................... + std::unique_lock aMutexLock(m_mutex); + + css::uno::Reference< css::document::XActionLockable > xLock = m_xActionLock; + bool bLocked = m_bActionLocked; + + m_xActionLock.clear(); + m_bActionLocked = false; + + aMutexLock.unlock(); + // <- SAFE .......................... + + if (bLocked && xLock.is()) + xLock->removeActionLock(); + } + + /** @short unlock the internal wrapped resource, if it's not already done. */ + void unlock() + { + std::unique_lock g(m_mutex); + if (m_bActionLocked && m_xActionLock.is()) + { + m_xActionLock->removeActionLock(); + // don't check for any locks here ... + // May another guard use the same lock object :-( + m_bActionLocked = false; + } + } +}; + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/inc/loadenv/loadenv.hxx b/framework/source/inc/loadenv/loadenv.hxx new file mode 100644 index 0000000000..baad8fac9b --- /dev/null +++ b/framework/source/inc/loadenv/loadenv.hxx @@ -0,0 +1,551 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 + +#include +#include +#include +#include +#include +#include +#include + + +/** @short enable/disable special features + of a load request. + + @desrc Such features must outcome without + any special parameters. + To make enabling/disabling of + features very easy (e.g. at the ctor of + this class) these values must be combinable + as flags. That means: its values must be in + range of [2^n]! + */ +enum class LoadEnvFeatures +{ + /// we should be informed, if no feature is enabled :-) + NONE = 0, + /// enable using of UI elements during loading (means progress, interaction handler etcpp.) + WorkWithUI = 1, + /// enable loading of resources, which are not related to a target frame! (see concept of ContentHandler) + AllowContentHandler = 2 +}; +namespace o3tl { + template<> struct typed_flags : is_typed_flags {}; +} + + +namespace framework { + +class QuietInteraction; + +/** @short implements general mechanism for loading documents. + + @descr An instance of this class can be used inside the API calls + XComponentLoader::loadComponentFromURL() and + XDispatch::dispatch(). + */ +class LoadEnv +{ +public: + /** @short classify a content. + + @descr The load environment must know, if a content + is related to a target frame or not. Only "visible" + components, which fulfill the requirements of the + model-controller-view paradigm can be loaded into a frame. + Such contents are classified as E_CAN_BE_LOADED. + + But e.g. for the dispatch framework exists special ContentHandler + objects, which can load a content in "non visible" mode ... + and do not need a target frame for its operation. Such + ContentHandler e.g. plays sounds. + Such contents are classified as E_CAN_BE_HANDLED. + + And last but not least a content can be "not valid" in general. + */ + enum EContentType + { + /// identifies a content, which seems to be invalid in general + E_UNSUPPORTED_CONTENT, + /// identifies a content, which can be used with a ContentHandler and is not related to a target frame + E_CAN_BE_HANDLED, + /// identifies a content, which can be loaded into a target frame + E_CAN_BE_LOADED, + /// special mode for non real loading, In such case the model is given directly! + E_CAN_BE_SET + }; + +private: + mutable osl::Mutex m_mutex; + + /** @short reference to a uno service manager, which must be used + to created on needed services on demand. + */ + css::uno::Reference< css::uno::XComponentContext > m_xContext; + + /** @short points to the frame, which uses this LoadEnv object + and must be used to start target search there. + */ + css::uno::Reference< css::frame::XFrame > m_xBaseFrame; + + /** @short points to the frame, into which the new component was loaded. + + @descr Note: This reference will be empty if loading failed + or a non visible content was loaded! + It can be the same frame as m_xBaseFrame it describe, in case + the target "_self", "" or the search flag "SELF" was used. + Otherwise it's the new created or recycled frame, which was + used for loading and contains further the new component. + + Please use method getTarget() or getTargetComponent() + to return the frame/controller or model to any interested + user of the results of this load request. + */ + css::uno::Reference< css::frame::XFrame > m_xTargetFrame; + + /** @short contains the name of the target, in which the specified resource + of this instance must be loaded. + */ + OUString m_sTarget; + + /** @short if m_sTarget is not a special one, this flags regulate searching + of a suitable one. + */ + sal_Int32 m_nSearchFlags; + + /** @short contains all needed information about the resource, + which should be loaded. + + @descr Inside this struct e.g. the URL, its type and filter name, + the stream or a model directly are saved. + */ + utl::MediaDescriptor m_lMediaDescriptor; + + /** @short because the mediadescriptor contains the complete URL ... but + some functionality need the structured version, we hold it twice :-(. + */ + css::util::URL m_aURL; + + /** @short enable/disable special features of a load request. */ + LoadEnvFeatures m_eFeature; + + /** @short classify the content, which should be loaded by this instance. */ + EContentType m_eContentType; + + /** @short it indicates, that the member m_xTargetFrame was new created for this + load request and must be closed in case loading (not handling!) + operation failed. The default value is sal_False! + */ + bool m_bCloseFrameOnError; + + /** @short it indicates, that the old document (which was located inside m_xBaseFrame + in combination with the m_sTarget value "_self") was suspended. + Normally it will be replaced by the new loaded document. But in case + loading (not handling!) failed, it must be reactivated. + The default value is sal_False! + */ + bool m_bReactivateControllerOnError; + + /** @short it holds one (!) asynchronous used contenthandler or frameloader + alive, till the asynchronous operation will be finished. + */ + css::uno::Reference< css::uno::XInterface > m_xAsynchronousJob; + + /** @short holds the information about the finished load process. + + @descr The content of m_xTargetFrame can't be used as valid indicator, + (in case the existing old document was reactivated) + we must hold the result of the load process explicitly. + */ + bool m_bLoaded; + + /** @short If we already brought it to front; do not do that again + (the user could switch elsewhere after the first activation, + and we shouldn't nag them again). + */ + bool m_bFocusedAndToFront = false; + + /** @short holds an XActionLock on the internal used task member. + + @seealso m_xTargetFrame + */ + ActionLockGuard m_aTargetLock; + + rtl::Reference m_pQuietInteraction; + +public: + + /** @short initialize a new instance of this load environment. + + @param xContext + reference to a uno service manager, which can be used internally + to create on needed services on demand. + + @throw Currently there is no reason to throw such exception! + + @throw A RuntimeException in case any internal process indicates, that + the whole runtime can't be used any longer. + */ + LoadEnv(css::uno::Reference< css::uno::XComponentContext > xContext); + + /** @short deinitialize an instance of this class in the right way. + */ + ~LoadEnv(); + + /// @throws css::lang::IllegalArgumentException + /// @throws css::io::IOException + /// @throws css::uno::RuntimeException + static css::uno::Reference< css::lang::XComponent > loadComponentFromURL(const css::uno::Reference< css::frame::XComponentLoader >& xLoader, + const css::uno::Reference< css::uno::XComponentContext >& xContext, + const OUString& sURL , + const OUString& sTarget, + sal_Int32 nFlags , + const css::uno::Sequence< css::beans::PropertyValue >& lArgs ); + + /** @short start loading of a resource + + @descr The parameter for targeting, the content description, and + some environment specifier (UI, dispatch functionality) + can be set here. Of course a still running load request + will be detected here and a suitable exception will be thrown. + Such constellation can be detected outside by using provided + synchronisation methods or callbacks. + + There is no direct return value possible here. Because it depends + from the usage of this instance! E.g. for loading a "visible component" + a frame with a controller/model inside can be possible. For loading + of a "non visible component" only an information about a successfully start + can be provided. + Further it can't be guaranteed, that the internal process runs synchronous. + that's why we prefer using of specialized methods afterwards e.g. to: + - wait till the internal job will be finished + and get the results + - or to let it run without any further control from outside. + + @param sURL + points to the resource, which should be loaded. + + @param lMediaDescriptor + contains additional information for the following load request. + + @param xBaseFrame + points to the frame which must be used as start point for target search. + + @param sTarget + regulate searching/creating of frames, which should contain the + new loaded component afterwards. + + @param nSearchFlags + regulate searching of targets, if sTarget is not a special one. + + @param eFeature + flag field, which enable/disable special features of this + new instance for following load call. + + @throw A LoadEnvException e.g. if another load operation is till in progress + or initialization of a new one fail by other reasons. + The real reason, a suitable message and ID will be given here immediately. + + @throw A RuntimeException in case any internal process indicates, that + the whole runtime can't be used any longer. + */ + void startLoading(const OUString& sURL , + const css::uno::Sequence< css::beans::PropertyValue >& lMediaDescriptor, + const css::uno::Reference< css::frame::XFrame >& xBaseFrame , + const OUString& sTarget , + sal_Int32 nSearchFlags , + LoadEnvFeatures eFeature = LoadEnvFeatures::NONE); + + /** @short wait for an already running load request (started by calling + startLoading() before). + + @descr The timeout parameter can be used to wait some times only + or forever. The return value indicates if the load request + was finished during the specified timeout period. + But it indicates not, if the load request was successful or not! + + @param nTimeout + specify a timeout in [ms]. + A value 0 let it wait forever! + + @return sal_True if the started load process could be finished in time; + sal_False if the specified time was over. + + @throw ... currently not used :-) + + @throw A RuntimeException in case any internal process indicates, that + the whole runtime can't be used any longer. + */ + bool waitWhileLoading(sal_uInt32 nTimeout = 0); + + /** TODO document me ... */ + css::uno::Reference< css::lang::XComponent > getTargetComponent() const; + +public: + + /** @short checks if the specified content can be handled by a + ContentHandler only and is not related to a target frame, + or if it can be loaded by a FrameLoader into a target frame + as "visible" component. + + @descr using: + switch(classifyContent(...)) + { + case E_CAN_BE_HANDLED : + handleIt(...); + break; + + case E_CAN_BE_LOADED : + xFrame = locateTargetFrame(); + loadIt(xFrame); + break; + + case E_NOT_A_CONTENT : + default : throw ...; + } + + @param sURL + describe the content. + + @param lMediaDescriptor + describe the content more detailed! + + @return A suitable enum value, which classify the specified content. + */ + static EContentType classifyContent(const OUString& sURL , + const css::uno::Sequence< css::beans::PropertyValue >& lMediaDescriptor); + + /** TODO document me ... */ + static void initializeUIDefaults( + const css::uno::Reference< css::uno::XComponentContext >& i_rxContext, + utl::MediaDescriptor& io_lMediaDescriptor, + const bool _bUIMode, + rtl::Reference* o_ppQuiteInteraction + ); + + /** TODO document me ... */ + void impl_setResult(bool bResult); + + /** TODO document me ... */ + css::uno::Reference< css::uno::XInterface > impl_searchLoader(); + + /** @short it means; show the frame, bring it to front, + might set the right icon etcpp. in case loading was + successfully or reactivate a might existing old document or + close the frame if it was created before in case loading failed. + + @throw A LoadEnvException only in cases, where an internal error indicates, + that the complete load environment seems to be not usable in general. + In such cases a RuntimeException would be to hard for the outside code :-) + + @throw A RuntimeException in case any internal process indicates, that + the whole runtime can't be used any longer. + */ + void impl_reactForLoadingState(); + +private: + void start(); + + /** @short tries to detect the type and the filter of the specified content. + + @descr This method update the available media descriptor of this instance, + so it contains the right type, a corresponding filter, may a + valid frame loader etc. In case detection failed, this descriptor + is corrected first, before a suitable exception will be thrown. + (Excepting a RuntimeException occurrence!) + + @attention Not all types we know, are supported by filters. So it does not + indicates an error, if no suitable filter(loader etcpp will be found + for a type. But a type must be detected for the specified content. + Otherwise it's an error and loading can't be finished successfully. + + @throw A LoadEnvException if detection failed. + + @throw A RuntimeException in case any internal process indicates, that + the whole runtime can't be used any longer. + */ + void impl_detectTypeAndFilter(); + + /** @short tries to use ContentHandler objects for loading. + + @descr It searches for a suitable content handler object, registered + for the detected content type (must be done before by calling + impl_detectTypeAndFilter()). Because such handler does not depend + from a real target frame, location of such frame will be + suppressed here. + In case handle failed all new created resources will be + removed before a suitable exception is thrown. + (Excepting a RuntimeException occurrence!) + + @return TODO + + @throw A LoadEnvException if handling failed. + + @throw A RuntimeException in case any internal process indicates, that + the whole runtime can't be used any longer. + */ + bool impl_handleContent(); + + /** @short tries to use FrameLoader objects for loading. + + @descr First the target frame will be located. If it could be found + or new created a filter/frame loader will be instantiated and + used to load the content into this frame. + In case loading failed all new created resources will be + removed before a suitable exception is thrown. + (Excepting a RuntimeException occurrence!) + + @return TODO + + @throw A LoadEnvException if loading failed. + + @throw A RuntimeException in case any internal process indicates, that + the whole runtime can't be used any longer. + */ + bool impl_loadContent(); + + /** @short checks if the specified content is already loaded. + + @descr It depends from the set target information, if such + search is allowed or not! So this method checks first, + if the target is the special one "_default". + If not it returns with an empty result immediately! + In case search is allowed, an existing document with the + same URL is searched. If it could be found, the corresponding + view will get the focus and this method return the corresponding frame. + Optional jumpmarks will be accepted here too. So the + view of the document will be updated to show the position + inside the document, which is related to the jumpmark. + + @return A valid reference to the target frame, which contains the already loaded content + and could be activated successfully. An empty reference otherwise. + + @throw A LoadEnvException only in cases, where an internal error indicates, + that the complete load environment seems to be not usable in general. + In such cases a RuntimeException would be to hard for the outside code :-) + + @throw A RuntimeException in case any internal process indicates, that + the whole runtime can't be used any longer. + */ + css::uno::Reference< css::frame::XFrame > impl_searchAlreadyLoaded(); + + /** @short search for any target frame, which seems to be usable + for this load request. + + @descr Because this special feature is bound to the target specifier "_default" + its checked inside first. If it's not set => this method return an empty + reference. Otherwise any currently existing frame will be analyzed, if + it can be used here. The following rules exists: + +

          +
        • The frame must be empty ...
        • +
        • or contains an empty document of the same application module + which the new document will have (Note: the filter of the new content + must be well known here!)
        • +
        • and(!) this target must not be already used by any other load request.
        • +
        + + If a suitable target is located it will be locked. That's why the last rule + exists! If this method returns a valid frame reference, it was locked to be usable + for this load request only. (Don't forget to reset this state later!) + Concurrent LoadEnv instances can synchronize her work be using such locks :-) HOPEFULLY + + @throw A LoadEnvException only in cases, where an internal error indicates, + that the complete load environment seems to be not usable in general. + In such cases a RuntimeException would be to hard for the outside code :-) + + @throw A RuntimeException in case any internal process indicates, that + the whole runtime can't be used any longer. + */ + css::uno::Reference< css::frame::XFrame > impl_searchRecycleTarget(); + + /** @short because showing of a frame is needed more than once... + it's implemented as a separate method .-) + + @descr Note: Showing of a frame is bound to a special feature... + a) If we recycle any existing frame, we must bring it to front. + Showing of such frame is not needed really... because we recycle + visible frames only! + b) If the document was already shown (e.g. by our progress implementation) + we do nothing here. The reason behind: The document was already shown... + and it was already make a top window... + If the user activated another frame inbetween (because loading needed some time) + it's not allowed to disturb the user again. Then the frame must resists in the background. + c) If the frame was not shown before... but loading of a visible document into this frame + was finished... we need both actions: setVisible() and toFront(). + + @param xWindow + points to the container window of a frame. + + @param bForceToFront + if it's set to sal_False... showing of the window is done more intelligent. + setVisible() is called only if the window was not shown before. + This mode is needed by b) and c) + If it's set to sal_True... both actions has to be done: setVisible(), toFront()! + This mode is needed by a) + */ + void impl_makeFrameWindowVisible(const css::uno::Reference< css::awt::XWindow >& xWindow , + bool bForceToFront); + + /** @short checks whether a frame is already used for another load request or not. + + @descr Such frames can't be used for our "recycle feature"! + + @param xFrame + the frame, which should be checked. + + @return [sal_Bool] + sal_True if this frame is already used for loading, + sal_False otherwise. + */ + bool impl_isFrameAlreadyUsedForLoading(const css::uno::Reference< css::frame::XFrame >& xFrame) const; + + /** @short try to determine the used application module + of this load request and apply right position and size + for this document window... hopefully before we show it .-) + */ + void impl_applyPersistentWindowState(const css::uno::Reference< css::awt::XWindow >& xWindow); + + /** @short determine if it's allowed to open new document frames. + */ + bool impl_furtherDocsAllowed(); + + /** @short jumps to the requested bookmark inside a given document. + */ + void impl_jumpToMark(const css::uno::Reference< css::frame::XFrame >& xFrame, + const css::util::URL& aURL ); + + /** @short determine if this loader has an interactive dialog shown before + loading the document. + */ + bool impl_filterHasInteractiveDialog() const; + + /** @short checks if this should bring to front and get focus on load, + according to user settings and to the load flags. + */ + bool shouldFocusAndToFront() const; +}; + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/inc/loadenv/loadenvexception.hxx b/framework/source/inc/loadenv/loadenvexception.hxx new file mode 100644 index 0000000000..62a17d2ac8 --- /dev/null +++ b/framework/source/inc/loadenv/loadenvexception.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 . + */ + +#pragma once + +#include +#include + +namespace framework{ + +/** @short specify an exception, which can be used inside the + load environment only. + + @descr Of course outside code must wrap it, to transport + the occurred information to its caller. + */ +class LoadEnvException +{ + public: + /** @short Can be used as an ID for an instance of a LoadEnvException. + @descr To prevent errors on adding/removing/changing such IDs here, + an enum field is used. Its int values are self organized... + */ + enum EIDs + { + /** @short The specified URL/Stream/etcpp. can not be handled by a LoadEnv instance. */ + ID_UNSUPPORTED_CONTENT, + + /** @short indicates a corrupted media descriptor. + @descr Some parts are required - some other ones are optional. Such exception + should be thrown, if a required item does not exists. */ + ID_INVALID_MEDIADESCRIPTOR, + + /** @short Its similar to a uno::RuntimeException... + @descr But such runtime exception can break the whole office code. + So its capsulated to this specialized load environment only. + Mostly it indicates a missing but needed resource ... e.g the + global desktop reference! */ + ID_INVALID_ENVIRONMENT, + + /** @short indicates a failed search for the right target frame. */ + ID_NO_TARGET_FOUND, + + /** @short TODO */ + ID_COULD_NOT_REACTIVATE_CONTROLLER, + + /** @short indicates an already running load operation. Of course the same + instance can't be used for multiple load requests at the same time. + */ + ID_STILL_RUNNING, + + /** @short sometimes we can't specify the reason for an error, because we + was interrupted by a called code in an unexpected way ... + */ + ID_GENERAL_ERROR + }; + + sal_Int32 m_nID; + OUString m_sMessage; + css::uno::Any m_exOriginal; + + LoadEnvException( + sal_Int32 id, OUString message = OUString(), + css::uno::Any original = css::uno::Any()): + m_nID(id), m_sMessage(std::move(message)), m_exOriginal(std::move(original)) + {} +}; + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/inc/loadenv/targethelper.hxx b/framework/source/inc/loadenv/targethelper.hxx new file mode 100644 index 0000000000..1f0d93d7ed --- /dev/null +++ b/framework/source/inc/loadenv/targethelper.hxx @@ -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 . + */ + +#pragma once + +#include + +namespace framework{ + +/** @short can be used to detect, if a target name (used e.g. for XFrame.findFrame()) + has a special meaning or can be used as normal frame name (e.g. for XFrame.setName()). + */ +class TargetHelper +{ + + public: + + /** @short it's used at the following interfaces to classify + target names. + */ + enum class ESpecialTarget + { + Blank, + Default, + Beamer, + HelpTask + }; + + // interface + + public: + + /** @short it checks the given unknown target name, + if it's the expected special one. + + @note An empty target is similar to "_self"! + + @param sCheckTarget + must be the unknown target name, which should be checked. + + @param eSpecialTarget + represent the expected target. + + @return It returns if sCheckTarget represent + the expected eSpecialTarget value; otherwise. + */ + static bool matchSpecialTarget(std::u16string_view sCheckTarget , + ESpecialTarget eSpecialTarget); + + /** @short it checks, if the given name can be used + to set it at a frame using XFrame.setName() method. + + @descr Because we handle special targets in a hard coded way + (means we do not check the real name of a frame then) + such named frames will never be found! + + And in case such special names can exists one times only + by definition inside the same frame tree (e.g. _beamer and + OFFICE_HELP_TASK) it's not a good idea to allow anything here :-) + + Of course we can't check unknown names, which are not special ones. + But we decide, that it's not allowed to use "_" as first sign + (because we reserve this letter for our own purposes!) + and the value must not a well known special target. + + @param sName + the new frame name, which should be checked. + */ + static bool isValidNameForFrame(std::u16string_view sName); +}; + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/inc/pattern/frame.hxx b/framework/source/inc/pattern/frame.hxx new file mode 100644 index 0000000000..947a036681 --- /dev/null +++ b/framework/source/inc/pattern/frame.hxx @@ -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 . + */ + +#pragma once + +#include +#include +#include +#include + +// namespaces + +namespace framework::pattern::frame{ + +/** @short close (or dispose) the given resource. + + @descr It try to close the given resource first. + Delegating of the ownership can be influenced from + outside. If closing isn't possible (because the + needed interface isn't available) dispose() is tried instead. + All possible exceptions are handled inside. + So the user of this method has to look for the return value only. + + @attention The given resource will not be cleared. + But later using of it can produce an exception! + + @param xResource + the object, which should be closed here. + + @return [bool] + sal_True if closing failed. + */ +inline bool closeIt(const css::uno::Reference< css::uno::XInterface >& xResource) +{ + css::uno::Reference< css::util::XCloseable > xClose (xResource, css::uno::UNO_QUERY); + css::uno::Reference< css::lang::XComponent > xDispose(xResource, css::uno::UNO_QUERY); + + try + { + if (xClose.is()) + xClose->close(false/*bDelegateOwnership*/); + else + if (xDispose.is()) + xDispose->dispose(); + else + return false; + } + catch(const css::util::CloseVetoException&) + { return false; } + catch(const css::lang::DisposedException&) + {} // disposed is closed is ... + catch(const css::uno::RuntimeException&) + { throw; } // should not be suppressed! + catch(const css::uno::Exception&) + { return false; } // ??? We defined to return a boolean value instead of throwing exceptions... + // (OK: RuntimeExceptions should not be caught inside the core..) + + return true; +} + +} // namespace framework::pattern::frame + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/inc/pattern/window.hxx b/framework/source/inc/pattern/window.hxx new file mode 100644 index 0000000000..92134915dc --- /dev/null +++ b/framework/source/inc/pattern/window.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 . + */ + +#pragma once + +#include +#include + +#include +#include +#include + +// namespaces + +namespace framework{ + +class WindowHelper +{ + public: + + +static bool isTopWindow(const css::uno::Reference< css::awt::XWindow >& xWindow) +{ + // even child frame containing top level windows (e.g. query designer of database) will be closed + css::uno::Reference< css::awt::XTopWindow > xTopWindowCheck(xWindow, css::uno::UNO_QUERY); + if (xTopWindowCheck.is()) + { + // Note: Toolkit interface XTopWindow sometimes is used by real VCL-child-windows also .-) + // Be sure that these window is really a "top system window". + // Attention ! Checking Window->GetParent() is not the right approach here. + // Because sometimes VCL create "implicit border windows" as parents even we created + // a simple XWindow using the toolkit only .-( + SolarMutexGuard aSolarGuard; + VclPtr pWindow = VCLUnoHelper::GetWindow( xWindow ); + if ( pWindow && pWindow->IsSystemWindow() ) + return true; + } + + return false; +} + +}; + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/interaction/quietinteraction.cxx b/framework/source/interaction/quietinteraction.cxx new file mode 100644 index 0000000000..11b8bc8b70 --- /dev/null +++ b/framework/source/interaction/quietinteraction.cxx @@ -0,0 +1,129 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +namespace framework{ + +QuietInteraction::QuietInteraction() +{ +} + +void SAL_CALL QuietInteraction::handle( const css::uno::Reference< css::task::XInteractionRequest >& xRequest ) +{ + // safe the request for outside analyzing every time! + css::uno::Any aRequest = xRequest->getRequest(); + { + SolarMutexGuard g; + m_aRequest = aRequest; + } + + // analyze the request + // We need XAbort as possible continuation as minimum! + // An optional filter selection we can handle too. + css::uno::Sequence< css::uno::Reference< css::task::XInteractionContinuation > > lContinuations = xRequest->getContinuations(); + css::uno::Reference< css::task::XInteractionAbort > xAbort; + css::uno::Reference< css::task::XInteractionApprove > xApprove; + css::uno::Reference< css::document::XInteractionFilterSelect > xFilter; + css::uno::Reference< css::document::XInteractionFilterOptions > xFOptions; + + sal_Int32 nCount=lContinuations.getLength(); + for (sal_Int32 i=0; i>= aErrorCodeRequest ) + { + // warnings can be ignored => approve + // errors must break loading => abort + bool bWarning = ErrCode(aErrorCodeRequest.ErrCode).IsWarning(); + if (xApprove.is() && bWarning) + xApprove->select(); + else + if (xAbort.is()) + xAbort->select(); + } + else + if( aRequest >>= aLockedDocumentRequest ) + { + // the locked document should be opened readonly by default + if (xApprove.is()) + xApprove->select(); + else + if (xAbort.is()) + xAbort->select(); + } + else + if (aRequest>>=aFilterOptionsRequest) + { + if (xFOptions.is()) + { + // let the default filter options be used + xFOptions->select(); + } + } + else + if (xAbort.is()) + xAbort->select(); +} + +css::uno::Any QuietInteraction::getRequest() const +{ + SolarMutexGuard g; + return m_aRequest; +} + +bool QuietInteraction::wasUsed() const +{ + SolarMutexGuard g; + return m_aRequest.hasValue(); +} + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/jobs/helponstartup.cxx b/framework/source/jobs/helponstartup.cxx new file mode 100644 index 0000000000..2795a3f450 --- /dev/null +++ b/framework/source/jobs/helponstartup.cxx @@ -0,0 +1,336 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +// include own header +#include +#include +#include + +#include +#include + +// include others +#include +#include +#include +#include + +// include interfaces +#include +#include +#include +#include +#include + +namespace framework{ + +// XInterface, XTypeProvider, XServiceInfo + +OUString SAL_CALL HelpOnStartup::getImplementationName() +{ + return "com.sun.star.comp.framework.HelpOnStartup"; +} + +sal_Bool SAL_CALL HelpOnStartup::supportsService( const OUString& sServiceName ) +{ + return cppu::supportsService(this, sServiceName); +} + +css::uno::Sequence< OUString > SAL_CALL HelpOnStartup::getSupportedServiceNames() +{ + return { SERVICENAME_JOB }; +} + +HelpOnStartup::HelpOnStartup(css::uno::Reference< css::uno::XComponentContext > xContext) + : m_xContext (std::move(xContext)) +{ + // create some needed uno services and cache it + m_xModuleManager = css::frame::ModuleManager::create( m_xContext ); + + m_xDesktop = css::frame::Desktop::create(m_xContext); + + // ask for office locale + m_sLocale = officecfg::Setup::L10N::ooLocale::get(); + + // detect system + m_sSystem = officecfg::Office::Common::Help::System::get(); + + // Start listening for disposing events of these services, + // so we can react e.g. for an office shutdown + css::uno::Reference< css::lang::XComponent > xComponent; + xComponent.set(m_xModuleManager, css::uno::UNO_QUERY); + if (xComponent.is()) + xComponent->addEventListener(static_cast< css::lang::XEventListener* >(this)); + if (m_xDesktop.is()) + m_xDesktop->addEventListener(static_cast< css::lang::XEventListener* >(this)); + xComponent.set(m_xConfig, css::uno::UNO_QUERY); + if (xComponent.is()) + xComponent->addEventListener(static_cast< css::lang::XEventListener* >(this)); +} + +HelpOnStartup::~HelpOnStartup() +{ +} + +// css.task.XJob +css::uno::Any SAL_CALL HelpOnStartup::execute(const css::uno::Sequence< css::beans::NamedValue >& lArguments) +{ + // Analyze the given arguments; try to locate a model there and + // classify it's used application module. + OUString sModule = its_getModuleIdFromEnv(lArguments); + + // Attention: we are bound to events for opening any document inside the office. + // That includes e.g. the help module itself. But we have to do nothing then! + if (sModule.isEmpty()) + return css::uno::Any(); + + // check current state of the help module + // a) help isn't open => show default page for the detected module + // b) help shows any other default page(!) => show default page for the detected module + // c) help shows any other content => do nothing (user travelled to any other content and leaved the set of default pages) + OUString sCurrentHelpURL = its_getCurrentHelpURL(); + bool bCurrentHelpURLIsAnyDefaultURL = its_isHelpUrlADefaultOne(sCurrentHelpURL); + bool bShowIt = false; + + // a) + if (sCurrentHelpURL.isEmpty()) + bShowIt = true; + // b) + else if (bCurrentHelpURLIsAnyDefaultURL) + bShowIt = true; + + if (bShowIt) + { + // retrieve the help URL for the detected application module + OUString sModuleDependentHelpURL = its_checkIfHelpEnabledAndGetURL(sModule); + if (!sModuleDependentHelpURL.isEmpty()) + { + // Show this help page. + // Note: The help window brings itself to front ... + Help* pHelp = Application::GetHelp(); + if (pHelp) + pHelp->Start(sModuleDependentHelpURL); + } + } + + return css::uno::Any(); +} + +void SAL_CALL HelpOnStartup::disposing(const css::lang::EventObject& aEvent) +{ + std::unique_lock g(m_mutex); + if (aEvent.Source == m_xModuleManager) + m_xModuleManager.clear(); + else if (aEvent.Source == m_xDesktop) + m_xDesktop.clear(); + else if (aEvent.Source == m_xConfig) + m_xConfig.clear(); +} + +OUString HelpOnStartup::its_getModuleIdFromEnv(const css::uno::Sequence< css::beans::NamedValue >& lArguments) +{ + ::comphelper::SequenceAsHashMap lArgs (lArguments); + ::comphelper::SequenceAsHashMap lEnvironment = lArgs.getUnpackedValueOrDefault("Environment", css::uno::Sequence< css::beans::NamedValue >()); + + // check for right environment. + // If it's not a DocumentEvent, which triggered this job, + // we can't work correctly! => return immediately and do nothing + OUString sEnvType = lEnvironment.getUnpackedValueOrDefault("EnvType", OUString()); + if (sEnvType != "DOCUMENTEVENT") + return OUString(); + + css::uno::Reference< css::frame::XModel > xDoc = lEnvironment.getUnpackedValueOrDefault("Model", css::uno::Reference< css::frame::XModel >()); + if (!xDoc.is()) + return OUString(); + + // be sure that we work on top level documents only, which are registered + // on the desktop instance. Ignore e.g. life previews, which are top frames too ... + // but not registered at this global desktop instance. + css::uno::Reference< css::frame::XDesktop > xDesktopCheck; + css::uno::Reference< css::frame::XFrame > xFrame; + css::uno::Reference< css::frame::XController > xController = xDoc->getCurrentController(); + if (xController.is()) + xFrame = xController->getFrame(); + if (xFrame.is() && xFrame->isTop()) + xDesktopCheck.set(xFrame->getCreator(), css::uno::UNO_QUERY); + if (!xDesktopCheck.is()) + return OUString(); + + // OK - now we are sure this document is a top level document. + // Classify it. + // SAFE -> + std::unique_lock aLock(m_mutex); + css::uno::Reference< css::frame::XModuleManager2 > xModuleManager = m_xModuleManager; + aLock.unlock(); + // <- SAFE + + OUString sModuleId; + try + { + sModuleId = xModuleManager->identify(xDoc); + } + catch(const css::uno::RuntimeException&) + { throw; } + catch(const css::uno::Exception&) + { sModuleId.clear(); } + + return sModuleId; +} + +OUString HelpOnStartup::its_getCurrentHelpURL() +{ + // SAFE -> + std::unique_lock aLock(m_mutex); + css::uno::Reference< css::frame::XDesktop2 > xDesktop = m_xDesktop; + aLock.unlock(); + // <- SAFE + + if (!xDesktop.is()) + return OUString(); + + css::uno::Reference< css::frame::XFrame > xHelp = xDesktop->findFrame(SPECIALTARGET_HELPTASK, css::frame::FrameSearchFlag::CHILDREN); + if (!xHelp.is()) + return OUString(); + + OUString sCurrentHelpURL; + try + { + css::uno::Reference< css::frame::XFramesSupplier > xHelpRoot (xHelp , css::uno::UNO_QUERY_THROW); + css::uno::Reference< css::container::XIndexAccess > xHelpChildren(xHelpRoot->getFrames(), css::uno::UNO_QUERY_THROW); + + css::uno::Reference< css::frame::XFrame > xHelpChild; + css::uno::Reference< css::frame::XController > xHelpView; + css::uno::Reference< css::frame::XModel > xHelpContent; + + xHelpChildren->getByIndex(0) >>= xHelpChild; + if (xHelpChild.is()) + xHelpView = xHelpChild->getController(); + if (xHelpView.is()) + xHelpContent = xHelpView->getModel(); + if (xHelpContent.is()) + sCurrentHelpURL = xHelpContent->getURL(); + } + catch(const css::uno::RuntimeException&) + { throw; } + catch(const css::uno::Exception&) + { sCurrentHelpURL.clear(); } + + return sCurrentHelpURL; +} + +bool HelpOnStartup::its_isHelpUrlADefaultOne(std::u16string_view sHelpURL) +{ + if (sHelpURL.empty()) + return false; + + // SAFE -> + std::unique_lock aLock(m_mutex); + css::uno::Reference< css::container::XNameAccess > xConfig = m_xConfig; + OUString sLocale = m_sLocale; + OUString sSystem = m_sSystem; + aLock.unlock(); + // <- SAFE + + if (!xConfig.is()) + return false; + + // check given help url against all default ones + const css::uno::Sequence< OUString > lModules = xConfig->getElementNames(); + const OUString* pModules = lModules.getConstArray(); + ::sal_Int32 c = lModules.getLength(); + ::sal_Int32 i = 0; + + for (i=0; i xModuleConfig; + xConfig->getByName(pModules[i]) >>= xModuleConfig; + if (!xModuleConfig.is()) + continue; + + OUString sHelpBaseURL; + xModuleConfig->getByName("ooSetupFactoryHelpBaseURL") >>= sHelpBaseURL; + OUString sHelpURLForModule = HelpOnStartup::ist_createHelpURL(sHelpBaseURL, sLocale, sSystem); + if (sHelpURL == sHelpURLForModule) + return true; + } + catch(const css::uno::RuntimeException&) + { throw; } + catch(const css::uno::Exception&) + {} + } + + return false; +} + +OUString HelpOnStartup::its_checkIfHelpEnabledAndGetURL(const OUString& sModule) +{ + // SAFE -> + std::unique_lock aLock(m_mutex); + css::uno::Reference< css::container::XNameAccess > xConfig = m_xConfig; + OUString sLocale = m_sLocale; + OUString sSystem = m_sSystem; + aLock.unlock(); + // <- SAFE + + OUString sHelpURL; + + try + { + css::uno::Reference< css::container::XNameAccess > xModuleConfig; + if (xConfig.is()) + xConfig->getByName(sModule) >>= xModuleConfig; + + bool bHelpEnabled = false; + if (xModuleConfig.is()) + xModuleConfig->getByName("ooSetupFactoryHelpOnOpen") >>= bHelpEnabled; + + if (bHelpEnabled) + { + OUString sHelpBaseURL; + xModuleConfig->getByName("ooSetupFactoryHelpBaseURL") >>= sHelpBaseURL; + sHelpURL = HelpOnStartup::ist_createHelpURL(sHelpBaseURL, sLocale, sSystem); + } + } + catch(const css::uno::RuntimeException&) + { throw; } + catch(const css::uno::Exception&) + { sHelpURL.clear(); } + + return sHelpURL; +} + +OUString HelpOnStartup::ist_createHelpURL(std::u16string_view sBaseURL, + std::u16string_view sLocale , + std::u16string_view sSystem ) +{ + return OUString::Concat(sBaseURL) + "?Language=" + sLocale + "&System=" + sSystem; +} + +} // namespace framework + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +framework_HelpOnStartup_get_implementation( + css::uno::XComponentContext* context, css::uno::Sequence const& ) +{ + return cppu::acquire(new framework::HelpOnStartup(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/jobs/job.cxx b/framework/source/jobs/job.cxx new file mode 100644 index 0000000000..711bd47b58 --- /dev/null +++ b/framework/source/jobs/job.cxx @@ -0,0 +1,857 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace framework{ + +/** + @short standard ctor + @descr It initialize this new instance. But it set some generic parameters here only. + Specialized information (e.g. the alias or service name ofthis job) will be set + later using the method setJobData(). + + @param xContext + reference to the uno service manager + + @param xFrame + reference to the frame, in which environment we run + (May be null!) +*/ +Job::Job( /*IN*/ const css::uno::Reference< css::uno::XComponentContext >& xContext , + /*IN*/ css::uno::Reference< css::frame::XFrame > xFrame ) + : m_aJobCfg (xContext ) + , m_xContext (xContext ) + , m_xFrame (std::move(xFrame )) + , m_bListenOnDesktop (false ) + , m_bListenOnFrame (false ) + , m_bListenOnModel (false ) + , m_bPendingCloseFrame (false ) + , m_bPendingCloseModel (false ) + , m_eRunState (E_NEW ) +{ +} + +/** + @short standard ctor + @descr It initialize this new instance. But it set some generic parameters here only. + Specialized information (e.g. the alias or service name ofthis job) will be set + later using the method setJobData(). + + @param xContext + reference to the uno service manager + + @param xModel + reference to the model, in which environment we run + (May be null!) +*/ +Job::Job( /*IN*/ const css::uno::Reference< css::uno::XComponentContext >& xContext , + /*IN*/ css::uno::Reference< css::frame::XModel > xModel ) + : m_aJobCfg (xContext ) + , m_xContext (xContext ) + , m_xModel (std::move(xModel )) + , m_bListenOnDesktop (false ) + , m_bListenOnFrame (false ) + , m_bListenOnModel (false ) + , m_bPendingCloseFrame (false ) + , m_bPendingCloseModel (false ) + , m_eRunState (E_NEW ) +{ +} + +/** + @short superfluous! + @descr Releasing of memory and reference must be done inside die() call. + Otherwise it's a bug. +*/ +Job::~Job() +{ +} + +/** + @short set (or delete) a listener for sending dispatch result events + @descr Because this object is used in a wrapped mode ... the original listener + for such events can't be registered here directly. Because the + listener expect to get the original object given as source of the event. + That's why we get this source here too, to fake(!) it at sending time! + + @param xListener + the original listener for dispatch result events + + @param xSourceFake + our user, which got the registration request for this listener +*/ +void Job::setDispatchResultFake( /*IN*/ const css::uno::Reference< css::frame::XDispatchResultListener >& xListener , + /*IN*/ const css::uno::Reference< css::uno::XInterface >& xSourceFake ) +{ + SolarMutexGuard g; + + // reject dangerous calls + if (m_eRunState != E_NEW) + { + SAL_INFO("fwk", "Job::setJobData(): job may still running or already finished"); + return; + } + + m_xResultListener = xListener; + m_xResultSourceFake = xSourceFake; +} + +void Job::setJobData( const JobData& aData ) +{ + SolarMutexGuard g; + + // reject dangerous calls + if (m_eRunState != E_NEW) + { + SAL_INFO("fwk", "Job::setJobData(): job may still running or already finished"); + return; + } + + m_aJobCfg = aData; +} + +/** + @short runs the job + @descr It doesn't matter, if the job is an asynchronous or + synchronous one. This method returns only if it was finished + or cancelled. + + @param lDynamicArgs + optional arguments for job execution + In case the represented job is a configured one (which uses static + arguments too) all information will be merged! +*/ +void Job::execute( /*IN*/ const css::uno::Sequence< css::beans::NamedValue >& lDynamicArgs ) +{ + /* SAFE { */ + class SolarMutexAntiGuard { + SolarMutexResettableGuard & m_rGuard; + public: + SolarMutexAntiGuard(SolarMutexResettableGuard & rGuard) : m_rGuard(rGuard) + { + m_rGuard.clear(); + } + ~SolarMutexAntiGuard() + { + m_rGuard.reset(); + } + }; + SolarMutexResettableGuard aWriteLock; + + // reject dangerous calls + if (m_eRunState != E_NEW) + { + SAL_INFO("fwk", "Job::execute(): job may still running or already finished"); + return; + } + + // create the environment and mark this job as running ... + m_eRunState = E_RUNNING; + impl_startListening(); + + css::uno::Reference< css::task::XAsyncJob > xAJob; + css::uno::Reference< css::task::XJob > xSJob; + css::uno::Sequence< css::beans::NamedValue > lJobArgs = impl_generateJobArgs(lDynamicArgs); + + // It's necessary to hold us self alive! + // Otherwise we might die by ref count ... + css::uno::Reference< css::task::XJobListener > xThis(this); + + try + { + // create the job + // We must check for the supported interface on demand! + // But we prefer the synchronous one ... + m_xJob = m_xContext->getServiceManager()->createInstanceWithContext(m_aJobCfg.getService(), m_xContext); + xSJob.set(m_xJob, css::uno::UNO_QUERY); + if (!xSJob.is()) + xAJob.set(m_xJob, css::uno::UNO_QUERY); + + // execute it asynchronous + if (xAJob.is()) + { + m_aAsyncWait.reset(); + SolarMutexAntiGuard const ag(aWriteLock); + /* } SAFE */ + xAJob->executeAsync(lJobArgs, xThis); + // wait for finishing this job - so this method + // does the same for synchronous and asynchronous jobs! + m_aAsyncWait.wait(); + /* SAFE { */ + // Note: Result handling was already done inside the callback! + } + // execute it synchron + else if (xSJob.is()) + { + css::uno::Any aResult; + { + SolarMutexAntiGuard const ag(aWriteLock); + /* } SAFE */ + aResult = xSJob->execute(lJobArgs); + } + /* SAFE { */ + impl_reactForJobResult(aResult); + } + } + #if OSL_DEBUG_LEVEL > 0 + catch(const css::uno::Exception&) + { + TOOLS_INFO_EXCEPTION("fwk", "Job::execute(): Got exception during job execution"); + } + #else + catch(const css::uno::Exception&) + {} + #endif + + // deinitialize the environment and mark this job as finished... + // but don't overwrite any information about STOPPED or might DISPOSED jobs! + impl_stopListening(); + if (m_eRunState == E_RUNNING) + m_eRunState = E_STOPPED_OR_FINISHED; + + // If we got a close request from our frame or model... + // but we disagreed with that by throwing a veto exception... + // and got the ownership... + // we have to close the resource frame or model now - + // and to disable ourself! + if (m_bPendingCloseFrame) + { + m_bPendingCloseFrame = false; + css::uno::Reference< css::util::XCloseable > xClose(m_xFrame, css::uno::UNO_QUERY); + if (xClose.is()) + { + try + { + xClose->close(true); + } + catch(const css::util::CloseVetoException&) {} + } + } + + if (m_bPendingCloseModel) + { + m_bPendingCloseModel = false; + css::uno::Reference< css::util::XCloseable > xClose(m_xModel, css::uno::UNO_QUERY); + if (xClose.is()) + { + try + { + xClose->close(true); + } + catch(const css::util::CloseVetoException&) {} + } + } + + aWriteLock.clear(); + /* SAFE { */ + + // release this instance ... + die(); +} + +/** + @short kill this job + @descr It doesn't matter if this request is called from inside or + from outside. We release our internal structures and stop + every activity. After doing so - this instance will not be + usable any longer! Of course we try to handle further requests + carefully. Maybe someone else holds a reference to us ... +*/ +void Job::die() +{ + SolarMutexGuard g; + + impl_stopListening(); + + if (m_eRunState != E_DISPOSED) + { + try + { + css::uno::Reference< css::lang::XComponent > xDispose(m_xJob, css::uno::UNO_QUERY); + if (xDispose.is()) + { + xDispose->dispose(); + m_eRunState = E_DISPOSED; + } + } + catch(const css::lang::DisposedException&) + { + m_eRunState = E_DISPOSED; + } + } + + m_xJob.clear(); + m_xFrame.clear(); + m_xModel.clear(); + m_xDesktop.clear(); + m_xResultListener.clear(); + m_xResultSourceFake.clear(); + m_bPendingCloseFrame = false; + m_bPendingCloseModel = false; +} + +/** + @short generates list of arguments for job execute + @descr There exist a set of information, which can be needed by a job. + a) it's static configuration data (Equals for all jobs. ) + b) it's specific configuration data (Different for every job.) + c) some environment values (e.g. the frame, for which this job was started) + d) any other dynamic data (e.g. parameters of a dispatch() request) + We collect all this information and generate one list which include all others. + + @param lDynamicArgs + list of dynamic arguments (given by a corresponding dispatch() call) + Can be empty too. + + @return A list which includes all mentioned sub lists. +*/ +css::uno::Sequence< css::beans::NamedValue > Job::impl_generateJobArgs( /*IN*/ const css::uno::Sequence< css::beans::NamedValue >& lDynamicArgs ) +{ + css::uno::Sequence< css::beans::NamedValue > lAllArgs; + + /* SAFE { */ + SolarMutexClearableGuard aReadLock; + + // the real structure of the returned list depends from the environment of this job! + JobData::EMode eMode = m_aJobCfg.getMode(); + + // Create list of environment variables. This list must be part of the + // returned structure every time... but some of its members are optional! + sal_Int32 nLen = 1; + if (m_xFrame.is()) + ++nLen; + if (m_xModel.is()) + ++nLen; + if (eMode==JobData::E_EVENT) + ++nLen; + css::uno::Sequence< css::beans::NamedValue > lEnvArgs(nLen); + auto plEnvArgs = lEnvArgs.getArray(); + plEnvArgs[0].Name = "EnvType"; + plEnvArgs[0].Value <<= m_aJobCfg.getEnvironmentDescriptor(); + + sal_Int32 i = 0; + if (m_xFrame.is()) + { + ++i; + plEnvArgs[i].Name = "Frame"; + plEnvArgs[i].Value <<= m_xFrame; + } + if (m_xModel.is()) + { + ++i; + plEnvArgs[i].Name = "Model"; + plEnvArgs[i].Value <<= m_xModel; + } + if (eMode==JobData::E_EVENT) + { + ++i; + plEnvArgs[i].Name = "EventName"; + plEnvArgs[i].Value <<= m_aJobCfg.getEvent(); + } + + // get the configuration data from the job data container ... if possible + // Means: if this job has any configuration data. Note: only really + // filled lists will be set to the return structure at the end of this method. + css::uno::Sequence< css::beans::NamedValue > lConfigArgs; + std::vector< css::beans::NamedValue > lJobConfigArgs; + if (eMode==JobData::E_ALIAS || eMode==JobData::E_EVENT) + { + lConfigArgs = m_aJobCfg.getConfig(); + lJobConfigArgs = m_aJobCfg.getJobConfig(); + } + + aReadLock.clear(); + /* } SAFE */ + + // Add all valid (not empty) lists to the return list + if (lConfigArgs.hasElements()) + { + sal_Int32 nLength = lAllArgs.getLength(); + lAllArgs.realloc(nLength+1); + auto plAllArgs = lAllArgs.getArray(); + plAllArgs[nLength].Name = "Config"; + plAllArgs[nLength].Value <<= lConfigArgs; + } + if (!lJobConfigArgs.empty()) + { + sal_Int32 nLength = lAllArgs.getLength(); + lAllArgs.realloc(nLength+1); + auto plAllArgs = lAllArgs.getArray(); + plAllArgs[nLength].Name = "JobConfig"; + plAllArgs[nLength].Value <<= comphelper::containerToSequence(lJobConfigArgs); + } + if (lEnvArgs.hasElements()) + { + sal_Int32 nLength = lAllArgs.getLength(); + lAllArgs.realloc(nLength+1); + auto plAllArgs = lAllArgs.getArray(); + plAllArgs[nLength].Name = "Environment"; + plAllArgs[nLength].Value <<= lEnvArgs; + } + if (lDynamicArgs.hasElements()) + { + sal_Int32 nLength = lAllArgs.getLength(); + lAllArgs.realloc(nLength+1); + auto plAllArgs = lAllArgs.getArray(); + plAllArgs[nLength].Name = "DynamicData"; + plAllArgs[nLength].Value <<= lDynamicArgs; + } + + return lAllArgs; +} + +/** + @short analyze the given job result and change the job configuration + @descr Note: Some results can be handled only, if this job has a valid configuration! + For "not configured jobs" (means pure services) they can be ignored. + But these cases are handled by our JobData member. We can call it every time. + It does the right things automatically. E.g. if the job has no configuration ... + it does nothing during setJobConfig()! + + @param aResult + the job result for analyzing +*/ +void Job::impl_reactForJobResult( /*IN*/ const css::uno::Any& aResult ) +{ + SolarMutexGuard g; + + // analyze the result set ... + JobResult aAnalyzedResult(aResult); + + // some of the following operations will be supported for different environments + // or different type of jobs only. + JobData::EEnvironment eEnvironment = m_aJobCfg.getEnvironment(); + + // write back the job specific configuration data ... + // If the environment allow it and if this job has a configuration! + if ( + (m_aJobCfg.hasConfig() ) && + (aAnalyzedResult.existPart(JobResult::E_ARGUMENTS)) + ) + { + m_aJobCfg.setJobConfig(aAnalyzedResult.getArguments()); + } + + // disable a job for further executions. + // Note: this option is available inside the environment EXECUTOR only + if ( +// (eEnvironment == JobData::E_EXECUTION ) && + (m_aJobCfg.hasConfig() ) && + (aAnalyzedResult.existPart(JobResult::E_DEACTIVATE)) + ) + { + m_aJobCfg.disableJob(); + } + + // notify any interested listener with the may given result state. + // Note: this option is available inside the environment DISPATCH only + if ( + (eEnvironment == JobData::E_DISPATCH ) && + (m_xResultListener.is() ) && + (aAnalyzedResult.existPart(JobResult::E_DISPATCHRESULT)) + ) + { + // Attention: Because the listener expect that the original object send this event ... + // and we nor the job are the right ones ... + // our user has set itself before. So we can fake this source address! + css::frame::DispatchResultEvent aEvent = aAnalyzedResult.getDispatchResult(); + aEvent.Source = m_xResultSourceFake; + m_xResultListener->dispatchFinished(aEvent); + } +} + +/** + @short starts listening for office shutdown and closing of our + given target frame (if it's a valid reference) + @descr We will register ourself as terminate listener + at the global desktop instance. That will hold us + alive and additional we get the information, if the + office wish to shutdown. If then an internal job + is running we will have the chance to suppress that + by throwing a veto exception. If our internal wrapped + job finished his work, we can release this listener + connection. + + Further we are listener for closing of the (possible valid) + given frame. We must be sure, that this resource won't be gone + if our internal job is still running. +*/ +void Job::impl_startListening() +{ + SolarMutexGuard g; + + // listening for office shutdown + if (!m_xDesktop.is() && !m_bListenOnDesktop) + { + try + { + m_xDesktop = css::frame::Desktop::create( m_xContext ); + css::uno::Reference< css::frame::XTerminateListener > xThis(this); + m_xDesktop->addTerminateListener(xThis); + m_bListenOnDesktop = true; + } + catch(const css::uno::Exception&) + { + m_xDesktop.clear(); + } + } + + // listening for frame closing + if (m_xFrame.is() && !m_bListenOnFrame) + { + try + { + css::uno::Reference< css::util::XCloseBroadcaster > xCloseable(m_xFrame , css::uno::UNO_QUERY); + css::uno::Reference< css::util::XCloseListener > xThis(this); + if (xCloseable.is()) + { + xCloseable->addCloseListener(xThis); + m_bListenOnFrame = true; + } + } + catch(const css::uno::Exception&) + { + m_bListenOnFrame = false; + } + } + + // listening for model closing + if (!m_xModel.is() || m_bListenOnModel) + return; + + try + { + css::uno::Reference< css::util::XCloseBroadcaster > xCloseable(m_xModel , css::uno::UNO_QUERY); + css::uno::Reference< css::util::XCloseListener > xThis(this); + if (xCloseable.is()) + { + xCloseable->addCloseListener(xThis); + m_bListenOnModel = true; + } + } + catch(const css::uno::Exception&) + { + m_bListenOnModel = false; + } +} + +/** + @short release listener connection for office shutdown + @descr see description of impl_startListening() +*/ +void Job::impl_stopListening() +{ + SolarMutexGuard g; + + // stop listening for office shutdown + if (m_xDesktop.is() && m_bListenOnDesktop) + { + try + { + css::uno::Reference< css::frame::XTerminateListener > xThis(this); + m_xDesktop->removeTerminateListener(xThis); + m_xDesktop.clear(); + m_bListenOnDesktop = false; + } + catch(const css::uno::Exception&) + { + } + } + + // stop listening for frame closing + if (m_xFrame.is() && m_bListenOnFrame) + { + try + { + css::uno::Reference< css::util::XCloseBroadcaster > xCloseable(m_xFrame , css::uno::UNO_QUERY); + css::uno::Reference< css::util::XCloseListener > xThis(this); + if (xCloseable.is()) + { + xCloseable->removeCloseListener(xThis); + m_bListenOnFrame = false; + } + } + catch(const css::uno::Exception&) + { + } + } + + // stop listening for model closing + if (!(m_xModel.is() && m_bListenOnModel)) + return; + + try + { + css::uno::Reference< css::util::XCloseBroadcaster > xCloseable(m_xModel , css::uno::UNO_QUERY); + css::uno::Reference< css::util::XCloseListener > xThis(this); + if (xCloseable.is()) + { + xCloseable->removeCloseListener(xThis); + m_bListenOnModel = false; + } + } + catch(const css::uno::Exception&) + { + } +} + +/** + @short callback from any asynchronous executed job + + @descr Our execute() method waits for this callback. + We have to react for the possible results here, + to kill the running job and disable the blocked condition + so execute() can be finished too. + + @param xJob + the job, which was running and inform us now + + @param aResult + its results +*/ +void SAL_CALL Job::jobFinished( /*IN*/ const css::uno::Reference< css::task::XAsyncJob >& xJob , + /*IN*/ const css::uno::Any& aResult ) +{ + SolarMutexGuard g; + + // It's necessary to check this. + // May this job was cancelled by any other reason + // some milliseconds before. :-) + if (m_xJob.is() && m_xJob==xJob) + { + // react for his results + // (means enable/disable it for further requests + // or save arguments or notify listener ...) + impl_reactForJobResult(aResult); + + // Let the job die! + m_xJob.clear(); + } + + // And let the start method "execute()" finishing it's job. + // But do it every time. So any outside blocking code can finish + // his work too. + m_aAsyncWait.set(); +} + +/** + @short prevent internal wrapped job against office termination + @descr This event is broadcasted by the desktop instance and ask for an office termination. + If the internal wrapped job is still in progress, we disagree with that by throwing the + right veto exception. If not - we agree. But then we must be aware, that another event + notifyTermination() can follow. Then we have no chance to do the same. Then we have to + accept that and stop our work instandly. + + @param aEvent + describes the broadcaster and must be the desktop instance + + @throw TerminateVetoException + if our internal wrapped job is still running. + */ +void SAL_CALL Job::queryTermination( /*IN*/ const css::lang::EventObject& ) +{ + SolarMutexGuard g; + + // Otherwise try to close() it + css::uno::Reference< css::util::XCloseable > xClose(m_xJob, css::uno::UNO_QUERY); + if (xClose.is()) + { + try + { + xClose->close(false); + m_eRunState = E_STOPPED_OR_FINISHED; + } + catch(const css::util::CloseVetoException&) {} + } + + if (m_eRunState != E_STOPPED_OR_FINISHED) + { + css::uno::Reference< css::uno::XInterface > xThis(static_cast< ::cppu::OWeakObject* >(this), css::uno::UNO_QUERY); + throw css::frame::TerminationVetoException("job still in progress", xThis); + } +} + +/** + @short inform us about office termination + @descr Instead of the method queryTermination(), here is no chance to disagree with that. + We have to accept it and cancel all current processes inside. + It can occur only, if job was not already started if queryTermination() was called here. + Then we had not thrown a veto exception. But now we must agree with this situation and break + all our internal processes. It's not a good idea to mark this instance as non startable any longer + inside queryTermination() if no job was running too. Because that would disable this job and may + the office does not really shutdown, because another listener has thrown the suitable exception. + + @param aEvent + describes the broadcaster and must be the desktop instance + */ +void SAL_CALL Job::notifyTermination( /*IN*/ const css::lang::EventObject& ) +{ + die(); + // Do nothing else here. Our internal resources was released ... +} + +/** + @short prevent internal wrapped job against frame closing + @descr This event is broadcasted by the frame instance and ask for closing. + If the internal wrapped job is still in progress, we disagree with that by throwing the + right veto exception. If not - we agree. But then we must be aware, that another event + notifyClosing() can follow. Then we have no chance to do the same. Then we have to + accept that and stop our work instandly. + + @param aEvent + describes the broadcaster and must be the frame instance + + @param bGetsOwnership + If it's set to and we throw the right veto exception, we have to close this frame later + if our internal processes will be finished. If it's set to we can ignore it. + + @throw CloseVetoException + if our internal wrapped job is still running. + */ +void SAL_CALL Job::queryClosing( const css::lang::EventObject& aEvent , + sal_Bool bGetsOwnership ) +{ + SolarMutexGuard g; + + // do nothing, if no internal job is still running ... + // The frame or model can be closed then successfully. + if (m_eRunState != E_RUNNING) + return; + + // try close() first at the job. + // The job can agree or disagree with this request. + css::uno::Reference< css::util::XCloseable > xClose(m_xJob, css::uno::UNO_QUERY); + if (xClose.is()) + { + xClose->close(bGetsOwnership); + // Here we can say: "this job was stopped successfully". Because + // no veto exception was thrown! + m_eRunState = E_STOPPED_OR_FINISHED; + return; + } + + // try dispose() then + // Here the job has no chance for a veto. + // But we must be aware of an "already disposed exception"... + try + { + css::uno::Reference< css::lang::XComponent > xDispose(m_xJob, css::uno::UNO_QUERY); + if (xDispose.is()) + { + xDispose->dispose(); + m_eRunState = E_DISPOSED; + } + } + catch(const css::lang::DisposedException&) + { + // the job was already disposed by any other mechanism !? + // But it's not interesting for us. For us this job is stopped now. + m_eRunState = E_DISPOSED; + } + + if (m_eRunState != E_DISPOSED) + { + // analyze event source - to find out, which resource called queryClosing() at this + // job wrapper. We must bind a "pending close" request to this resource. + // Closing of the corresponding resource will be done if our internal job finish it's work. + m_bPendingCloseFrame = (m_xFrame.is() && aEvent.Source == m_xFrame); + m_bPendingCloseModel = (m_xModel.is() && aEvent.Source == m_xModel); + + // throw suitable veto exception - because the internal job could not be cancelled. + css::uno::Reference< css::uno::XInterface > xThis(static_cast< ::cppu::OWeakObject* >(this), css::uno::UNO_QUERY); + throw css::util::CloseVetoException("job still in progress", xThis); + } + + // No veto ... + // But don't call die() here or free our internal member. + // This must be done inside notifyClosing() only. Otherwise the + // might stopped job has no chance to return its results or + // call us back. We must give him the chance to finish it's work successfully. +} + +/** + @short inform us about frame closing + @descr Instead of the method queryClosing(), here is no chance to disagree with that. + We have to accept it and cancel all current processes inside. + + @param aEvent + describes the broadcaster and must be the frame or model instance we know + */ +void SAL_CALL Job::notifyClosing( const css::lang::EventObject& ) +{ + die(); + // Do nothing else here. Our internal resources was released ... +} + +/** + @short shouldn't be called normally + @descr But it doesn't matter, who called it. We have to kill our internal + running processes hardly. + + @param aEvent + describe the broadcaster +*/ +void SAL_CALL Job::disposing( const css::lang::EventObject& aEvent ) +{ + /* SAFE { */ + { + SolarMutexGuard aWriteLock; + + if (m_xDesktop.is() && aEvent.Source == m_xDesktop) + { + m_xDesktop.clear(); + m_bListenOnDesktop = false; + } + else if (m_xFrame.is() && aEvent.Source == m_xFrame) + { + m_xFrame.clear(); + m_bListenOnFrame = false; + } + else if (m_xModel.is() && aEvent.Source == m_xModel) + { + m_xModel.clear(); + m_bListenOnModel = false; + } + } + /* } SAFE */ + + die(); + // Do nothing else here. Our internal resources was released ... +} + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/jobs/jobdata.cxx b/framework/source/jobs/jobdata.cxx new file mode 100644 index 0000000000..0ca06fcaca --- /dev/null +++ b/framework/source/jobs/jobdata.cxx @@ -0,0 +1,545 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace framework{ + +/** + @short standard ctor + @descr It initialize this new instance. + But for real working it's necessary to call setAlias() or setService() later. + Because we need the job data ... + + @param rxContext + reference to the uno service manager +*/ +JobData::JobData( css::uno::Reference< css::uno::XComponentContext > xContext ) + : m_xContext (std::move(xContext )) +{ + // share code for member initialization with defaults! + impl_reset(); +} + +/** + @short copy ctor + @descr Sometimes such job data container must be moved from one using place + to another one. Then a copy ctor and copy operator must be available. + + @param rCopy + the original instance, from which we must copy all data +*/ +JobData::JobData( const JobData& rCopy ) +{ + // use the copy operator to share the same code + *this = rCopy; +} + +/** + @short operator for copying JobData instances + @descr Sometimes such job data container must be moved from one using place + to another one. Then a copy ctor and copy operator must be available. + + @param rCopy + the original instance, from which we must copy all data +*/ +JobData& JobData::operator=( const JobData& rCopy ) +{ + // Please don't copy the uno service manager reference. + // That can change the uno context, which isn't a good idea! + m_eMode = rCopy.m_eMode; + m_eEnvironment = rCopy.m_eEnvironment; + m_sAlias = rCopy.m_sAlias; + m_sService = rCopy.m_sService; + m_sContext = rCopy.m_sContext; + m_sEvent = rCopy.m_sEvent; + m_lArguments = rCopy.m_lArguments; + return *this; +} + +/** + @short let this instance die + @descr There is no chance any longer to work. We have to + release all used resources and free used memory. +*/ +JobData::~JobData() +{ + impl_reset(); +} + +/** + @short initialize this instance as a job with configuration + @descr They given alias can be used to address some configuration data. + We read it and fill our internal structures. Of course old information + will be lost doing so. + + @param sAlias + the alias name of this job, used to locate job properties inside cfg +*/ +void JobData::setAlias( const OUString& sAlias ) +{ + // delete all old information! Otherwise we mix it with the new one ... + impl_reset(); + + // take over the new information + m_sAlias = sAlias; + m_eMode = E_ALIAS; + + // try to open the configuration set of this job directly and get a property access to it + // We open it readonly here + ConfigAccess aConfig( + m_xContext, + ("/org.openoffice.Office.Jobs/Jobs/" + + utl::wrapConfigurationElementName(m_sAlias))); + aConfig.open(ConfigAccess::E_READONLY); + if (aConfig.getMode()==ConfigAccess::E_CLOSED) + { + impl_reset(); + return; + } + + css::uno::Reference< css::beans::XPropertySet > xJobProperties(aConfig.cfg(), css::uno::UNO_QUERY); + if (xJobProperties.is()) + { + css::uno::Any aValue; + + // read uno implementation name + aValue = xJobProperties->getPropertyValue("Service"); + aValue >>= m_sService; + + // read module context list + aValue = xJobProperties->getPropertyValue("Context"); + aValue >>= m_sContext; + + // read whole argument list + aValue = xJobProperties->getPropertyValue("Arguments"); + css::uno::Reference< css::container::XNameAccess > xArgumentList; + if ( + (aValue >>= xArgumentList) && + (xArgumentList.is() ) + ) + { + css::uno::Sequence< OUString > lArgumentNames = xArgumentList->getElementNames(); + sal_Int32 nCount = lArgumentNames.getLength(); + m_lArguments.resize(nCount); + for (sal_Int32 i=0; igetByName(m_lArguments[i].Name); + } + } + } + + aConfig.close(); +} + +/** + @short initialize this instance as a job without configuration + @descr This job has no configuration data. We have to forget all old information + and set only some of them new, so this instance can work. + + @param sService + the uno service name of this "non configured" job +*/ +void JobData::setService( const OUString& sService ) +{ + // delete all old information! Otherwise we mix it with the new one ... + impl_reset(); + // take over the new information + m_sService = sService; + m_eMode = E_SERVICE; +} + +/** + @short initialize this instance with new job values. + @descr It reads automatically all properties of the specified + job (using it's alias name) and "register it" for the + given event. This registration will not be validated against + the underlying configuration! (That must be done from outside. + Because the caller must have the configuration already open to + get the values for sEvent and sAlias! And doing so it can perform + only, if the time stamp values are read outside too. + Further it makes no sense to initialize and start a disabled job. + So this initialization method will be called for enabled jobs only.) + + @param sEvent + the triggered event, for which this job should be started + + @param sAlias + mark the required job inside event registration list +*/ +void JobData::setEvent( const OUString& sEvent , + const OUString& sAlias ) +{ + // share code to read all job properties! + setAlias(sAlias); + + // take over the new information - which differ against set one of method setAlias()! + m_sEvent = sEvent; + m_eMode = E_EVENT; +} + +/** + @short set the new job specific arguments + @descr If a job finish his work, it can give us a new list of arguments (which + will not interpreted by us). We write it back to the configuration only + (if this job has its own configuration!). + So a job can have persistent data without implementing anything + or define own config areas for that. + + @param lArguments + list of arguments, which should be set for this job + */ +void JobData::setJobConfig( std::vector< css::beans::NamedValue >&& lArguments ) +{ + // update member + m_lArguments = std::move(lArguments); + + // update the configuration ... if possible! + if (m_eMode!=E_ALIAS) + return; + + // It doesn't matter if this config object was already opened before. + // It doesn nothing here then ... or it change the mode automatically, if + // it was opened using another one before. + ConfigAccess aConfig( + m_xContext, + ("/org.openoffice.Office.Jobs/Jobs/" + + utl::wrapConfigurationElementName(m_sAlias))); + aConfig.open(ConfigAccess::E_READWRITE); + if (aConfig.getMode()==ConfigAccess::E_CLOSED) + return; + + css::uno::Reference< css::beans::XMultiHierarchicalPropertySet > xArgumentList(aConfig.cfg(), css::uno::UNO_QUERY); + if (xArgumentList.is()) + { + sal_Int32 nCount = m_lArguments.size(); + css::uno::Sequence< OUString > lNames (nCount); + auto lNamesRange = asNonConstRange(lNames); + css::uno::Sequence< css::uno::Any > lValues(nCount); + auto lValuesRange = asNonConstRange(lValues); + + for (sal_Int32 i=0; isetHierarchicalPropertyValues(lNames, lValues); + } + aConfig.close(); +} + +/** + @short set a new environment descriptor for this job + @descr It must(!) be done every time this container is initialized + with new job data e.g.: setAlias()/setEvent()/setService() ... + Otherwise the environment will be unknown! + */ +void JobData::setEnvironment( EEnvironment eEnvironment ) +{ + m_eEnvironment = eEnvironment; +} + +/** + @short these functions provides access to our internal members + @descr These member represent any information about the job + and can be used from outside to e.g. start a job. + */ +JobData::EMode JobData::getMode() const +{ + return m_eMode; +} + +JobData::EEnvironment JobData::getEnvironment() const +{ + return m_eEnvironment; +} + +OUString JobData::getEnvironmentDescriptor() const +{ + OUString sDescriptor; + switch(m_eEnvironment) + { + case E_EXECUTION : + sDescriptor = "EXECUTOR"; + break; + + case E_DISPATCH : + sDescriptor = "DISPATCH"; + break; + + case E_DOCUMENTEVENT : + sDescriptor = "DOCUMENTEVENT"; + break; + default: + break; + } + return sDescriptor; +} + +OUString JobData::getService() const +{ + return m_sService; +} + +OUString JobData::getEvent() const +{ + return m_sEvent; +} + +std::vector< css::beans::NamedValue > JobData::getJobConfig() const +{ + return m_lArguments; +} + +css::uno::Sequence< css::beans::NamedValue > JobData::getConfig() const +{ + css::uno::Sequence< css::beans::NamedValue > lConfig; + if (m_eMode==E_ALIAS) + { + lConfig = { { "Alias", css::uno::Any(m_sAlias) }, + { "Service", css::uno::Any(m_sService) }, + { "Context", css::uno::Any(m_sContext) } }; + } + return lConfig; +} + +/** + @short return information, if this job is part of the global configuration package + org.openoffice.Office.Jobs + @descr Because jobs can be executed by the dispatch framework using a uno service name + directly - an executed job must not have any configuration really. Such jobs + must provide the right interfaces only! But after finishing jobs can return + some information (e.g. for updating her configuration ...). We must know + if such request is valid or not then. + + @return sal_True if the represented job is part of the underlying configuration package. + */ +bool JobData::hasConfig() const +{ + return (m_eMode==E_ALIAS || m_eMode==E_EVENT); +} + +/** + @short mark a job as non startable for further requests + @descr We don't remove the configuration entry! We set a timestamp value only. + And there exist two of them: one for an administrator... and one for the + current user. We change it for the user layer only. So this JobDispatch can't be + started any more... till the administrator change his timestamp. + That can be useful for post setup scenarios, which must run one time only. + + Note: This method don't do anything, if this represented job doesn't have a configuration! + */ +void JobData::disableJob() +{ + // No configuration - not used from EXECUTOR and not triggered from an event => no chance! + if (m_eMode!=E_EVENT) + return; + + // update the configuration + // It doesn't matter if this config object was already opened before. + // It doesn nothing here then ... or it change the mode automatically, if + // it was opened using another one before. + ConfigAccess aConfig( + m_xContext, + ("/org.openoffice.Office.Jobs/Events/" + + utl::wrapConfigurationElementName(m_sEvent) + "/JobList/" + + utl::wrapConfigurationElementName(m_sAlias))); + aConfig.open(ConfigAccess::E_READWRITE); + if (aConfig.getMode()==ConfigAccess::E_CLOSED) + return; + + css::uno::Reference< css::beans::XPropertySet > xPropSet(aConfig.cfg(), css::uno::UNO_QUERY); + if (xPropSet.is()) + { + // Convert and write the user timestamp to the configuration. + css::uno::Any aValue; + aValue <<= Converter::convert_DateTime2ISO8601(DateTime( DateTime::SYSTEM)); + xPropSet->setPropertyValue("UserTime", aValue); + } + + aConfig.close(); +} + +static bool isEnabled( std::u16string_view sAdminTime , + std::u16string_view sUserTime ) +{ + /*Attention! + To prevent interpreting of TriGraphs inside next const string value, + we have to encode all '?' signs. Otherwise e.g. "??-" will be translated + to "~" ... + */ + WildCard aISOPattern(u"\?\?\?\?-\?\?-\?\?*"); + + bool bValidAdmin = aISOPattern.Matches(sAdminTime); + bool bValidUser = aISOPattern.Matches(sUserTime ); + + // We check for "isEnabled()" here only. + // Note further: ISO8601 formatted strings can be compared as strings directly! + // FIXME: this is not true! "T1215" is the same time as "T12:15" or "T121500" + return ( + (!bValidAdmin && !bValidUser ) || + ( bValidAdmin && bValidUser && sAdminTime>=sUserTime) + ); +} + +void JobData::appendEnabledJobsForEvent( const css::uno::Reference< css::uno::XComponentContext >& rxContext, + const OUString& sEvent , + ::std::vector< JobData::TJob2DocEventBinding >& lJobs ) +{ + std::vector< OUString > lAdditionalJobs = JobData::getEnabledJobsForEvent(rxContext, sEvent); + sal_Int32 c = lAdditionalJobs.size(); + sal_Int32 i = 0; + + for (i=0; i 0 ) + { + sal_Int32 nIndex = m_sContext.indexOf( rModuleIdent ); + if ( nIndex >= 0 && ( nIndex+nModuleIdLen <= nContextLen )) + { + std::u16string_view sContextModule = m_sContext.subView( nIndex, nModuleIdLen ); + return sContextModule == rModuleIdent; + } + } + + return false; +} + +std::vector< OUString > JobData::getEnabledJobsForEvent( const css::uno::Reference< css::uno::XComponentContext >& rxContext, + std::u16string_view sEvent ) +{ + // create a config access to "/org.openoffice.Office.Jobs/Events" + ConfigAccess aConfig(rxContext, "/org.openoffice.Office.Jobs/Events"); + aConfig.open(ConfigAccess::E_READONLY); + if (aConfig.getMode()==ConfigAccess::E_CLOSED) + return std::vector< OUString >(); + + css::uno::Reference< css::container::XHierarchicalNameAccess > xEventRegistry(aConfig.cfg(), css::uno::UNO_QUERY); + if (!xEventRegistry.is()) + return std::vector< OUString >(); + + // check if the given event exist inside list of registered ones + OUString sPath(OUString::Concat(sEvent) + "/JobList"); + if (!xEventRegistry->hasByHierarchicalName(sPath)) + return std::vector< OUString >(); + + // step to the job list, which is a child of the event node inside cfg + // e.g. "/org.openoffice.Office.Jobs/Events//JobList" + css::uno::Any aJobList = xEventRegistry->getByHierarchicalName(sPath); + css::uno::Reference< css::container::XNameAccess > xJobList; + if (!(aJobList >>= xJobList) || !xJobList.is()) + return std::vector< OUString >(); + + // get all alias names of jobs, which are part of this job list + // But Some of them can be disabled by its timestamp values. + // We create an additional job name list with the same size, then the original list... + // step over all job entries... check her timestamps... and put only job names to the + // destination list, which represent an enabled job. + const css::uno::Sequence< OUString > lAllJobs = xJobList->getElementNames(); + sal_Int32 c = lAllJobs.getLength(); + + std::vector< OUString > lEnabledJobs(c); + sal_Int32 d = 0; + + for (OUString const & jobName : lAllJobs) + { + css::uno::Reference< css::beans::XPropertySet > xJob; + if ( + !(xJobList->getByName(jobName) >>= xJob) || + !(xJob.is() ) + ) + { + continue; + } + + OUString sAdminTime; + xJob->getPropertyValue("AdminTime") >>= sAdminTime; + + OUString sUserTime; + xJob->getPropertyValue("UserTime") >>= sUserTime; + + if (!isEnabled(sAdminTime, sUserTime)) + continue; + + lEnabledJobs[d] = jobName; + ++d; + } + lEnabledJobs.resize(d); + + aConfig.close(); + + return lEnabledJobs; +} + +/** + @short reset all internal structures + @descr If someone recycles this instance, he can switch from one + using mode to another one. But then we have to reset all currently + used information. Otherwise we mix it and they can make trouble. + + But note: that does not set defaults for internal used members, which + does not relate to any job property! e.g. the reference to the global + uno service manager. Such information is used for internal processes only + and are necessary for our work. + */ +void JobData::impl_reset() +{ + m_eMode = E_UNKNOWN_MODE; + m_eEnvironment = E_UNKNOWN_ENVIRONMENT; + m_sAlias.clear(); + m_sService.clear(); + m_sContext.clear(); + m_sEvent.clear(); + m_lArguments.clear(); +} + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/jobs/jobdispatch.cxx b/framework/source/jobs/jobdispatch.cxx new file mode 100644 index 0000000000..2352919dea --- /dev/null +++ b/framework/source/jobs/jobdispatch.cxx @@ -0,0 +1,474 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +using namespace framework; + +namespace { + +/** + @short implements a dispatch object for jobs + @descr Such dispatch object will be used by the generic dispatch mechanism if + a URL "vnd.sun.star.job:alias=" occurs. + Then an instance of this class will be created and used. + This new instance will be called within his method + dispatch() or dispatchWithNotification() for executing the + real job. We do it, control the life cycle of this internal + wrapped job and inform any interested listener if it finish. + */ +class JobDispatch : public ::cppu::WeakImplHelper< + css::lang::XServiceInfo + , css::lang::XInitialization + , css::frame::XDispatchProvider + , css::frame::XNotifyingDispatch > // => XDispatch +{ +private: + + /** reference to the uno service manager */ + css::uno::Reference< css::uno::XComponentContext > m_xContext; + + /** reference to the frame, inside which this dispatch is used */ + css::uno::Reference< css::frame::XFrame > m_xFrame; + + /** name of module (writer, impress etc.) the frame is for */ + OUString m_sModuleIdentifier; + +// native interface methods + +public: + + explicit JobDispatch(css::uno::Reference< css::uno::XComponentContext > xContext); + virtual ~JobDispatch() override; + + void impl_dispatchEvent ( const OUString& sEvent , + const css::uno::Sequence< css::beans::PropertyValue >& lArgs , + const css::uno::Reference< css::frame::XDispatchResultListener >& xListener ); + void impl_dispatchService( const OUString& sService , + const css::uno::Sequence< css::beans::PropertyValue >& lArgs , + const css::uno::Reference< css::frame::XDispatchResultListener >& xListener ); + void impl_dispatchAlias ( const OUString& sAlias , + const css::uno::Sequence< css::beans::PropertyValue >& lArgs , + const css::uno::Reference< css::frame::XDispatchResultListener >& xListener ); + +public: + virtual OUString SAL_CALL getImplementationName() override + { + return "com.sun.star.comp.framework.jobs.JobDispatch"; + } + + virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override + { + return cppu::supportsService(this, ServiceName); + } + + virtual css::uno::Sequence SAL_CALL getSupportedServiceNames() override + { + return {"com.sun.star.frame.ProtocolHandler"}; + } + + // Xinitialization + virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& lArguments ) 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 >& lDescriptor ) override; + + // XNotifyingDispatch + 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 >& xListener ) override; + + // XDispatch + virtual void SAL_CALL dispatch ( 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 >& 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; +}; + +/** + @short standard ctor + @descr It initialize this new instance. + + @param xContext + reference to the uno service manager +*/ +JobDispatch::JobDispatch( /*IN*/ css::uno::Reference< css::uno::XComponentContext > xContext ) + : m_xContext (std::move(xContext )) +{ +} + +/** + @short let this instance die + @descr We have to release all used resources and free used memory. +*/ +JobDispatch::~JobDispatch() +{ + // release all used resources + m_xContext.clear(); + m_xFrame.clear(); +} + +/** + @short implementation of XInitialization + @descr A protocol handler can provide this functionality, if it wish to get additional information + about the context it runs. In this case the frame reference would be given by the outside code. + + @param lArguments + the list of initialization arguments + First parameter should be the frame reference we need. +*/ +void SAL_CALL JobDispatch::initialize( const css::uno::Sequence< css::uno::Any >& lArguments ) +{ + SolarMutexGuard g; + + for (int a=0; a>= m_xFrame; + + css::uno::Reference< css::frame::XModuleManager2 > xModuleManager = + css::frame::ModuleManager::create(m_xContext); + try + { + m_sModuleIdentifier = xModuleManager->identify( m_xFrame ); + } + catch( const css::uno::Exception& ) + {} + } + } +} + +/** + @short implementation of XDispatchProvider::queryDispatches() + @descr Every protocol handler will be asked for his agreement, if a URL was queried + for which this handler is registered. It's the chance for this handler to validate + the given URL and return a dispatch object (may be itself) or not. + + @param aURL + the queried URL, which should be checked + + @param sTargetFrameName + describes the target frame, in which context this handler will be used + Is mostly set to "", "_self", "_blank", "_default" or a non special one + using SELF/CREATE as search flags. + + @param nSearchFlags + Can be SELF or CREATE only and are set only if sTargetFrameName isn't a special target +*/ +css::uno::Reference< css::frame::XDispatch > SAL_CALL JobDispatch::queryDispatch( /*IN*/ const css::util::URL& aURL , + /*IN*/ const OUString& /*sTargetFrameName*/ , + /*IN*/ sal_Int32 /*nSearchFlags*/ ) +{ + css::uno::Reference< css::frame::XDispatch > xDispatch; + + JobURL aAnalyzedURL(aURL.Complete); + if (aAnalyzedURL.isValid()) + xDispatch = this; + + return xDispatch; +} + +/** + @short implementation of XDispatchProvider::queryDispatches() + @descr It's an optimized access for remote, so you can ask for + multiple dispatch objects at the same time. + + @param lDescriptor + a list of queryDispatch() parameter + + @return A list of corresponding dispatch objects. + NULL references are not skipped. Every result + match to one given descriptor item. +*/ +css::uno::Sequence< css::uno::Reference< css::frame::XDispatch > > SAL_CALL JobDispatch::queryDispatches( const css::uno::Sequence< css::frame::DispatchDescriptor >& lDescriptor ) +{ + // don't pack resulting list! + sal_Int32 nCount = lDescriptor.getLength(); + css::uno::Sequence< css::uno::Reference< css::frame::XDispatch > > lDispatches(nCount); + auto lDispatchesRange = asNonConstRange(lDispatches); + for (sal_Int32 i=0; i& lArgs , + /*IN*/ const css::uno::Reference< css::frame::XDispatchResultListener >& xListener ) +{ + JobURL aAnalyzedURL(aURL.Complete); + if (aAnalyzedURL.isValid()) + { + OUString sRequest; + if (aAnalyzedURL.getEvent(sRequest)) + impl_dispatchEvent(sRequest, lArgs, xListener); + else + if (aAnalyzedURL.getService(sRequest)) + impl_dispatchService(sRequest, lArgs, xListener); + else + if (aAnalyzedURL.getAlias(sRequest)) + impl_dispatchAlias(sRequest, lArgs, xListener); + } +} + +/** + @short dispatch an event + @descr We search all registered jobs for this event and execute it. + After doing so, we inform the given listener about the results. + (There will be one notify for every executed job!) + + @param sEvent + the event, for which jobs can be registered + + @param lArgs + optional arguments for this request + Currently not used! + + @param xListener + an interested listener for possible results of this operation +*/ +void JobDispatch::impl_dispatchEvent( /*IN*/ const OUString& sEvent , + /*IN*/ const css::uno::Sequence< css::beans::PropertyValue >& lArgs , + /*IN*/ const css::uno::Reference< css::frame::XDispatchResultListener >& xListener ) +{ + // get list of all enabled jobs + // The called static helper methods read it from the configuration and + // filter disabled jobs using it's time stamp values. + std::vector< OUString > lJobs = JobData::getEnabledJobsForEvent(m_xContext, sEvent); + + css::uno::Reference< css::frame::XDispatchResultListener > xThis( static_cast< ::cppu::OWeakObject* >(this), css::uno::UNO_QUERY ); + + // no jobs... no execution + // But a may given listener will know something... + // I think this operation was finished successfully. + // It's not really an error, if no registered jobs could be located. + // Step over all found jobs and execute it + int nExecutedJobs=0; + for (const OUString & lJob : lJobs) + { + JobData aCfg(m_xContext); + aCfg.setEvent(sEvent, lJob); + aCfg.setEnvironment(JobData::E_DISPATCH); + const bool bIsEnabled=aCfg.hasCorrectContext(m_sModuleIdentifier); + + rtl::Reference pJob = new Job(m_xContext, m_xFrame); + pJob->setJobData(aCfg); + + if (!bIsEnabled) + continue; + + // Special mode for listener. + // We don't notify it directly here. We delegate that + // to the job implementation. But we must set ourself there too. + // Because this job must fake the source address of the event. + // Otherwise the listener may ignore it. + if (xListener.is()) + pJob->setDispatchResultFake(xListener, xThis); + pJob->execute(Converter::convert_seqPropVal2seqNamedVal(lArgs)); + ++nExecutedJobs; + } + + if (nExecutedJobs<1 && xListener.is()) + { + css::frame::DispatchResultEvent aEvent; + aEvent.Source = xThis; + aEvent.State = css::frame::DispatchResultState::SUCCESS; + xListener->dispatchFinished(aEvent); + } +} + +/** + @short dispatch a service + @descr We use the given name only to create and if possible to initialize + it as a uno service. It can be useful for creating (caching?) + of e.g. one instance services. + + @param sService + the uno implementation or service name of the job, which should be instantiated + + @param lArgs + optional arguments for this request + Currently not used! + + @param xListener + an interested listener for possible results of this operation +*/ +void JobDispatch::impl_dispatchService( /*IN*/ const OUString& sService , + /*IN*/ const css::uno::Sequence< css::beans::PropertyValue >& lArgs , + /*IN*/ const css::uno::Reference< css::frame::XDispatchResultListener >& xListener ) +{ + JobData aCfg(m_xContext); + aCfg.setService(sService); + aCfg.setEnvironment(JobData::E_DISPATCH); + + /*Attention! + Jobs implements interfaces and dies by ref count! + And freeing of such uno object is done by uno itself. + So we have to use dynamic memory everytimes. + */ + rtl::Reference pJob = new Job(m_xContext, m_xFrame); + pJob->setJobData(aCfg); + + css::uno::Reference< css::frame::XDispatchResultListener > xThis( static_cast< ::cppu::OWeakObject* >(this), css::uno::UNO_QUERY ); + + // Special mode for listener. + // We don't notify it directly here. We delegate that + // to the job implementation. But we must set ourself there too. + // Because this job must fake the source address of the event. + // Otherwise the listener may ignore it. + if (xListener.is()) + pJob->setDispatchResultFake(xListener, xThis); + pJob->execute(Converter::convert_seqPropVal2seqNamedVal(lArgs)); +} + +/** + @short dispatch an alias + @descr We use this alias to locate a job inside the configuration + and execute it. Further we inform the given listener about the results. + + @param sAlias + the alias name of the configured job + + @param lArgs + optional arguments for this request + Currently not used! + + @param xListener + an interested listener for possible results of this operation +*/ +void JobDispatch::impl_dispatchAlias( /*IN*/ const OUString& sAlias , + /*IN*/ const css::uno::Sequence< css::beans::PropertyValue >& lArgs , + /*IN*/ const css::uno::Reference< css::frame::XDispatchResultListener >& xListener ) +{ + JobData aCfg(m_xContext); + aCfg.setAlias(sAlias); + aCfg.setEnvironment(JobData::E_DISPATCH); + + rtl::Reference pJob = new Job(m_xContext, m_xFrame); + pJob->setJobData(aCfg); + + css::uno::Reference< css::frame::XDispatchResultListener > xThis( static_cast< ::cppu::OWeakObject* >(this), css::uno::UNO_QUERY ); + + // Special mode for listener. + // We don't notify it directly here. We delegate that + // to the job implementation. But we must set ourself there too. + // Because this job must fake the source address of the event. + // Otherwise the listener may ignore it. + if (xListener.is()) + pJob->setDispatchResultFake(xListener, xThis); + pJob->execute(Converter::convert_seqPropVal2seqNamedVal(lArgs)); +} + +/** + @short implementation of XDispatch::dispatch() + @descr Because the methods dispatch() and dispatchWithNotification() are different in her parameters + only, we can forward this request to dispatchWithNotification() by using an empty listener! + + @param aURL + describe the job(s), which should be started + + @param lArgs + optional arguments for this request + + @see dispatchWithNotification() +*/ +void SAL_CALL JobDispatch::dispatch( /*IN*/ const css::util::URL& aURL , + /*IN*/ const css::uno::Sequence< css::beans::PropertyValue >& lArgs ) +{ + dispatchWithNotification(aURL, lArgs, css::uno::Reference< css::frame::XDispatchResultListener >()); +} + +/** + @short not supported +*/ +void SAL_CALL JobDispatch::addStatusListener( /*IN*/ const css::uno::Reference< css::frame::XStatusListener >&, + /*IN*/ const css::util::URL& ) +{ +} + +/** + @short not supported +*/ +void SAL_CALL JobDispatch::removeStatusListener( /*IN*/ const css::uno::Reference< css::frame::XStatusListener >&, + /*IN*/ const css::util::URL& ) +{ +} + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_framework_jobs_JobDispatch_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence const &) +{ + return cppu::acquire(new JobDispatch(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/jobs/jobexecutor.cxx b/framework/source/jobs/jobexecutor.cxx new file mode 100644 index 0000000000..6180653858 --- /dev/null +++ b/framework/source/jobs/jobexecutor.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 +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +using namespace framework; + +namespace { + +typedef comphelper::WeakComponentImplHelper< + css::lang::XServiceInfo + , css::task::XJobExecutor + , css::container::XContainerListener // => lang.XEventListener + , css::document::XEventListener > + Base; + +/** + @short implements a job executor, which can be triggered from any code + @descr It uses the given trigger event to locate any registered job service + inside the configuration and execute it. Of course it controls the + lifetime of such jobs too. + */ +class JobExecutor : public Base +{ +private: + + /** reference to the uno service manager */ + css::uno::Reference< css::uno::XComponentContext > m_xContext; + + /** cached list of all registered event names of cfg for call optimization. */ + std::vector m_lEvents; + + /** we listen at the configuration for changes at the event list. */ + ConfigAccess m_aConfig; + + /** helper to allow us listen to the configuration without a cyclic dependency */ + css::uno::Reference m_xConfigListener; + + virtual void disposing(std::unique_lock& rGuard) final override; + +public: + + explicit JobExecutor(const css::uno::Reference< css::uno::XComponentContext >& xContext); + virtual ~JobExecutor() override; + + virtual OUString SAL_CALL getImplementationName() override + { + return "com.sun.star.comp.framework.JobExecutor"; + } + + virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override + { + return cppu::supportsService(this, ServiceName); + } + + virtual css::uno::Sequence SAL_CALL getSupportedServiceNames() override + { + return {"com.sun.star.task.JobExecutor"}; + } + + // task.XJobExecutor + virtual void SAL_CALL trigger( const OUString& sEvent ) override; + + /// Initialization function after having acquire()'d. + void initListeners(); + + // document.XEventListener + virtual void SAL_CALL notifyEvent( const css::document::EventObject& aEvent ) override; + + // container.XContainerListener + virtual void SAL_CALL elementInserted( const css::container::ContainerEvent& aEvent ) override; + virtual void SAL_CALL elementRemoved ( const css::container::ContainerEvent& aEvent ) override; + virtual void SAL_CALL elementReplaced( const css::container::ContainerEvent& aEvent ) override; + + // lang.XEventListener + virtual void SAL_CALL disposing( const css::lang::EventObject& aEvent ) override; +}; + +/** + @short standard ctor + @descr It initialize this new instance. + + @param xContext + reference to the uno service manager + */ +JobExecutor::JobExecutor( /*IN*/ const css::uno::Reference< css::uno::XComponentContext >& xContext ) + : m_xContext (xContext ) + , m_aConfig (xContext, "/org.openoffice.Office.Jobs/Events") +{ +} + +void JobExecutor::initListeners() +{ + if (utl::ConfigManager::IsFuzzing()) + return; + + // read the list of all currently registered events inside configuration. + // e.g. "/org.openoffice.Office.Jobs/Events/" + // We need it later to check if an incoming event request can be executed successfully + // or must be rejected. It's an optimization! Of course we must implement updating of this + // list too ... Be listener at the configuration. + + m_aConfig.open(ConfigAccess::E_READONLY); + if (m_aConfig.getMode() != ConfigAccess::E_READONLY) + return; + + css::uno::Reference< css::container::XNameAccess > xRegistry( + m_aConfig.cfg(), css::uno::UNO_QUERY); + if (xRegistry.is()) + m_lEvents = Converter::convert_seqOUString2OUStringList( + xRegistry->getElementNames()); + + css::uno::Reference< css::container::XContainer > xNotifier( + m_aConfig.cfg(), css::uno::UNO_QUERY); + if (xNotifier.is()) + { + m_xConfigListener = new WeakContainerListener(this); + xNotifier->addContainerListener(m_xConfigListener); + } + + // don't close cfg here! + // It will be done inside disposing ... +} + +JobExecutor::~JobExecutor() +{ + std::unique_lock g(m_aMutex); + disposing(g); +} + +void JobExecutor::disposing(std::unique_lock& /*rGuard*/) { + css::uno::Reference notifier; + css::uno::Reference listener; + if (m_aConfig.getMode() != ConfigAccess::E_CLOSED) { + notifier.set(m_aConfig.cfg(), css::uno::UNO_QUERY); + listener = m_xConfigListener; + m_aConfig.close(); + } + m_xConfigListener.clear(); + if (notifier.is()) { + notifier->removeContainerListener(listener); + } +} + +/** + @short implementation of XJobExecutor interface + @descr We use the given event to locate any registered job inside our configuration + and execute it. Further we control the lifetime of it and suppress + shutdown of the office till all jobs was finished. + + @param sEvent + is used to locate registered jobs + */ +void SAL_CALL JobExecutor::trigger( const OUString& sEvent ) +{ + SAL_INFO( "fwk", "JobExecutor::trigger()"); + + /* SAFE */ + { + std::unique_lock g(m_aMutex); + + // Optimization! + // Check if the given event name exist inside configuration and reject wrong requests. + // This optimization suppress using of the cfg api for getting event and job descriptions ... + if (std::find(m_lEvents.begin(), m_lEvents.end(), sEvent) == m_lEvents.end()) + return; + + } /* SAFE */ + + // get list of all enabled jobs + // The called static helper methods read it from the configuration and + // filter disabled jobs using it's time stamp values. + std::vector< OUString > lJobs = JobData::getEnabledJobsForEvent(m_xContext, sEvent); + + // step over all enabled jobs and execute it + size_t c = lJobs.size(); + for (size_t j=0; j pJob = new Job(m_xContext, css::uno::Reference< css::frame::XFrame >()); + pJob->setJobData(aCfg); + + pJob->execute(css::uno::Sequence< css::beans::NamedValue >()); + } +} + +void SAL_CALL JobExecutor::notifyEvent( const css::document::EventObject& aEvent ) +{ + static constexpr OUString EVENT_ON_DOCUMENT_OPENED(u"onDocumentOpened"_ustr); // Job UI event : OnNew or OnLoad + static constexpr OUString EVENT_ON_DOCUMENT_ADDED(u"onDocumentAdded"_ustr); // Job API event : OnCreate or OnLoadFinished + + OUString aModuleIdentifier; + ::std::vector< JobData::TJob2DocEventBinding > lJobs; + + // Optimization! + // Check if the given event name exist inside configuration and reject wrong requests. + // This optimization suppress using of the cfg api for getting event and job descriptions. + // see using of m_lEvents.find() below ... + + // retrieve event context from event source + try + { + aModuleIdentifier = css::frame::ModuleManager::create( m_xContext )->identify( aEvent.Source ); + } + catch( const css::uno::Exception& ) + {} + + /* SAFE */ + { + std::unique_lock g(m_aMutex); + + // Special feature: If the events "OnNew" or "OnLoad" occurs - we generate our own event "onDocumentOpened". + if ( + (aEvent.EventName == "OnNew") || + (aEvent.EventName == "OnLoad") + ) + { + if (std::find(m_lEvents.begin(), m_lEvents.end(), EVENT_ON_DOCUMENT_OPENED) != m_lEvents.end()) + JobData::appendEnabledJobsForEvent(m_xContext, EVENT_ON_DOCUMENT_OPENED, lJobs); + } + + // Special feature: If the events "OnCreate" or "OnLoadFinished" occurs - we generate our own event "onDocumentAdded". + if ( + (aEvent.EventName == "OnCreate") || + (aEvent.EventName == "OnLoadFinished") + ) + { + if (std::find(m_lEvents.begin(), m_lEvents.end(), EVENT_ON_DOCUMENT_ADDED) != m_lEvents.end()) + JobData::appendEnabledJobsForEvent(m_xContext, EVENT_ON_DOCUMENT_ADDED, lJobs); + } + + // Add all jobs for "real" notified event too .-) + if (std::find(m_lEvents.begin(), m_lEvents.end(), aEvent.EventName) != m_lEvents.end()) + JobData::appendEnabledJobsForEvent(m_xContext, aEvent.EventName, lJobs); + } /* SAFE */ + + // step over all enabled jobs and execute it + for (auto const& lJob : lJobs) + { + rtl::Reference pJob; + + const JobData::TJob2DocEventBinding& rBinding = lJob; + + JobData aCfg(m_xContext); + aCfg.setEvent(rBinding.m_sDocEvent, rBinding.m_sJobName); + aCfg.setEnvironment(JobData::E_DOCUMENTEVENT); + + if (!aCfg.hasCorrectContext(aModuleIdentifier)) + continue; + + /*Attention! + Jobs implements interfaces and dies by ref count! + And freeing of such uno object is done by uno itself. + So we have to use dynamic memory everytimes. + */ + css::uno::Reference< css::frame::XModel > xModel(aEvent.Source, css::uno::UNO_QUERY); + pJob = new Job(m_xContext, xModel); + pJob->setJobData(aCfg); + + pJob->execute(css::uno::Sequence< css::beans::NamedValue >()); + } +} + +void SAL_CALL JobExecutor::elementInserted( const css::container::ContainerEvent& aEvent ) +{ + OUString sValue; + if (aEvent.Accessor >>= sValue) + { + OUString sEvent = ::utl::extractFirstFromConfigurationPath(sValue); + if (!sEvent.isEmpty()) + { + std::vector::iterator pEvent = std::find(m_lEvents.begin(), m_lEvents.end(), sEvent); + if (pEvent == m_lEvents.end()) + m_lEvents.push_back(sEvent); + } + } +} + +void SAL_CALL JobExecutor::elementRemoved ( const css::container::ContainerEvent& aEvent ) +{ + OUString sValue; + if (aEvent.Accessor >>= sValue) + { + OUString sEvent = ::utl::extractFirstFromConfigurationPath(sValue); + if (!sEvent.isEmpty()) + { + std::vector::iterator pEvent = std::find(m_lEvents.begin(), m_lEvents.end(), sEvent); + if (pEvent != m_lEvents.end()) + m_lEvents.erase(pEvent); + } + } +} + +void SAL_CALL JobExecutor::elementReplaced( const css::container::ContainerEvent& ) +{ + // I'm not interested on changed items :-) +} + +/** @short the used cfg changes notifier wish to be released in its reference. + + @descr We close our internal used configuration instance to + free this reference. + + @attention For the special feature "bind global document event broadcaster to job execution" + this job executor instance was registered from outside code as + css.document.XEventListener. So it can be, that this disposing call comes from + the global event broadcaster service. But we don't hold any reference to this service + which can or must be released. Because this broadcaster itself is a one instance service + too, we can ignore this request. On the other side we must release our internal CFG + reference... SOLUTION => check the given event source and react only, if it's our internal + hold configuration object! + */ +void SAL_CALL JobExecutor::disposing( const css::lang::EventObject& aEvent ) +{ + /* SAFE { */ + std::unique_lock g(m_aMutex); + css::uno::Reference< css::uno::XInterface > xCFG(m_aConfig.cfg(), css::uno::UNO_QUERY); + if ( + (xCFG == aEvent.Source ) && + (m_aConfig.getMode() != ConfigAccess::E_CLOSED) + ) + { + m_aConfig.close(); + } + /* } SAFE */ +} + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_framework_JobExecutor_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence const &) +{ + rtl::Reference xJobExec = new JobExecutor(context); + // 2nd phase initialization needed + xJobExec->initListeners(); + return cppu::acquire(xJobExec.get()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/jobs/jobresult.cxx b/framework/source/jobs/jobresult.cxx new file mode 100644 index 0000000000..183543606b --- /dev/null +++ b/framework/source/jobs/jobresult.cxx @@ -0,0 +1,179 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include + +#include +#include +#include + +namespace framework +{ +/** + @short special ctor + @descr It initialize this new instance with a pure job execution result + and analyze it. Doing so, we update our other members. + +

        + It's a list of named values, packed inside this any. + Following protocol is used: +

        +

          +
        • + "SaveArguments" [sequence< css.beans.NamedValue >] +
          + The returned list of (for this generic implementation unknown!) + properties, will be written directly to the configuration and replace + any old values there. There will no check for changes and we don't + support any merge feature here. They are written only. The job has + to modify this list. +
        • +
        • + "SendDispatchResult" [css.frame.DispatchResultEvent] +
          + The given event is send to all current registered listener. + But it's not guaranteed. In case no listener are available or + this job isn't part of the dispatch environment (because it was used + by the css..task.XJobExecutor->trigger() implementation) this option + will be ignored. +
        • +
        • + "Deactivate" [boolean] +
          + The job wish to be disabled. But note: There is no way, to enable it later + again by using this implementation. It can be done by using the configuration + only. (Means to register this job again.) + If a job knows, that there exist some status or result listener, it must use + the options "SendDispatchStatus" and "SendDispatchResult" (see before) too, to + inform it about the deactivation of this service. +
        • +
        + + @param aResult + the job result +*/ +JobResult::JobResult(/*IN*/ const css::uno::Any& aResult) +{ + // reset the flag mask! + // It will reset the accessible state of this object. + // That can be useful if something will fail here ... + m_eParts = E_NOPART; + + // analyze the result and update our other members + ::comphelper::SequenceAsHashMap aProtocol(aResult); + if (aProtocol.empty()) + return; + + ::comphelper::SequenceAsHashMap::const_iterator pIt + = aProtocol.find(JobConst::ANSWER_DEACTIVATE_JOB); + if (pIt != aProtocol.end()) + { + /** + an executed job can force his deactivation + But we provide this information here only. + Doing so is part of any user of us. + */ + bool bDeactivate(false); + pIt->second >>= bDeactivate; + if (bDeactivate) + m_eParts |= E_DEACTIVATE; + } + + pIt = aProtocol.find(JobConst::ANSWER_SAVE_ARGUMENTS); + if (pIt != aProtocol.end()) + { + css::uno::Sequence aTmp; + pIt->second >>= aTmp; + comphelper::sequenceToContainer(m_lArguments, aTmp); + if (m_lArguments.empty()) + m_eParts |= E_ARGUMENTS; + } + + pIt = aProtocol.find(JobConst::ANSWER_SEND_DISPATCHRESULT); + if (pIt != aProtocol.end()) + { + if (pIt->second >>= m_aDispatchResult) + m_eParts |= E_DISPATCHRESULT; + } +} + +/** + @short copy dtor +*/ +JobResult::JobResult(const JobResult& rCopy) +{ + m_eParts = rCopy.m_eParts; + m_lArguments = rCopy.m_lArguments; + m_aDispatchResult = rCopy.m_aDispatchResult; +} + +/** + @short standard dtor + @descr Free all internally used resources at the end of living. +*/ +JobResult::~JobResult() +{ + // Nothing really to do here. +} + +/** + @short =operator + @descr Must be implemented to overwrite this instance with another one. + + @param rCopy + reference to the other instance, which should be used for copying. +*/ +JobResult& JobResult::operator=(const JobResult& rCopy) +{ + m_eParts = rCopy.m_eParts; + m_lArguments = rCopy.m_lArguments; + m_aDispatchResult = rCopy.m_aDispatchResult; + return *this; +} + +/** + @short checks for existing parts of the analyzed result + @descr The internal flag mask was set after analyzing of the pure result. + An user of us can check here, if the required part was really part + of this result. Otherwise it would use invalid information ... + by using our other members! + + @param eParts + a flag mask too, which will be compared with our internally set one. + + @return We return true only, if any set flag of the given mask match. +*/ +bool JobResult::existPart(sal_uInt32 eParts) const { return ((m_eParts & eParts) == eParts); } + +/** + @short provides access to our internal members + @descr The return value will be valid only in case a call of + existPart(E_...) before returned true! + + @return It returns the state of the internal member + without any checks! +*/ +std::vector JobResult::getArguments() const { return m_lArguments; } + +css::frame::DispatchResultEvent JobResult::getDispatchResult() const { return m_aDispatchResult; } + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/jobs/joburl.cxx b/framework/source/jobs/joburl.cxx new file mode 100644 index 0000000000..5533014edf --- /dev/null +++ b/framework/source/jobs/joburl.cxx @@ -0,0 +1,247 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include + +#include + +#include +#include + +namespace framework{ + +/** + @short special ctor + @descr It initialize this new instance with a (hopefully) valid job URL. + This URL will be parsed. After that we set our members right, + so other interface methods of this class can be used to get + all items of this URL. Of course it will be possible to know, + if this URL was valid too. + + @param sURL + the job URL for parsing +*/ +JobURL::JobURL( /*IN*/ const OUString& sURL ) +{ + m_eRequest = E_UNKNOWN; + + // syntax: vnd.sun.star.job:{[event=],[alias=],[service=]} + + // check for "vnd.sun.star.job:" + if (!sURL.startsWithIgnoreAsciiCase("vnd.sun.star.job:")) + return; + + sal_Int32 t = std::strlen("vnd.sun.star.job:"); + do + { + // separate all token of "{[event=],[alias=],[service=]}" + OUString sToken = sURL.getToken(0, JOBURL_PART_SEPARATOR, t); + OUString sPartValue; + OUString sPartArguments; + + // check for "event=" + if ( + (JobURL::implst_split(sToken,JOBURL_EVENT_STR,JOBURL_EVENT_LEN,sPartValue,sPartArguments)) && + (!sPartValue.isEmpty()) + ) + { + // set the part value + m_sEvent = sPartValue; + m_eRequest |= E_EVENT; + } + else + // check for "alias=" + if ( + (JobURL::implst_split(sToken,JOBURL_ALIAS_STR,JOBURL_ALIAS_LEN,sPartValue,sPartArguments)) && + (!sPartValue.isEmpty()) + ) + { + // set the part value + m_sAlias = sPartValue; + m_eRequest |= E_ALIAS; + } + else + // check for "service=" + if ( + (JobURL::implst_split(sToken,JOBURL_SERVICE_STR,JOBURL_SERVICE_LEN,sPartValue,sPartArguments)) && + (!sPartValue.isEmpty()) + ) + { + // set the part value + m_sService = sPartValue; + m_eRequest |= E_SERVICE; + } + } + while(t!=-1); +} + +/** + @short knows, if this job URL object hold a valid URL inside + + @return if it represent a valid job URL. +*/ +bool JobURL::isValid() const +{ + return (m_eRequest!=E_UNKNOWN); +} + +/** + @short get the event item of this job URL + @descr Because the three possible parts of such URL (event, alias, service) + can't be combined, this method can(!) return a valid value - but it's + not a must. That's why the return value must be used too, to detect a missing + event value. + + @param sEvent + returns the possible existing event value + e.g. "vnd.sun.star.job:event=myEvent" returns "myEvent" + + @return if an event part of the job URL exist and the out parameter + sEvent was filled. + + @attention The out parameter will be reset every time. Don't use it if method returns ! +*/ +bool JobURL::getEvent( /*OUT*/ OUString& sEvent ) const +{ + sEvent.clear(); + bool bSet = ((m_eRequest & E_EVENT) == E_EVENT); + if (bSet) + sEvent = m_sEvent; + + return bSet; +} + +/** + @short get the alias item of this job URL + @descr Because the three possible parts of such URL (event, alias, service) + can't be combined, this method can(!) return a valid value - but it's + not a must. that's why the return value must be used too, to detect a missing + alias value. + + @param sAlias + returns the possible existing alias value + e.g. "vnd.sun.star.job:alias=myAlias" returns "myAlias" + + @return if an alias part of the job URL exist and the out parameter + sAlias was filled. + + @attention The out parameter will be reset every time. Don't use it if method returns ! +*/ +bool JobURL::getAlias( /*OUT*/ OUString& sAlias ) const +{ + sAlias.clear(); + bool bSet = ((m_eRequest & E_ALIAS) == E_ALIAS); + if (bSet) + sAlias = m_sAlias; + + return bSet; +} + +/** + @short get the service item of this job URL + @descr Because the three possible parts of such URL (event, service, service) + can't be combined, this method can(!) return a valid value - but it's + not a must. That's why the return value must be used too, to detect a missing + service value. + + @param sAlias + returns the possible existing service value + e.g. "vnd.sun.star.job:service=com.sun.star.Service" returns "com.sun.star.Service" + + @return if a service part of the job URL exist and the out parameter + sService was filled. + + @attention The out parameter will be reset every time. Don't use it if method returns ! +*/ +bool JobURL::getService( /*OUT*/ OUString& sService ) const +{ + sService.clear(); + bool bSet = ((m_eRequest & E_SERVICE) == E_SERVICE); + if (bSet) + sService = m_sService; + + return bSet; +} + +/** + @short searches for a special identifier in the given string and split it + @descr If the given identifier could be found at the beginning of the given string, + this method split it into different parts and return it. + Following schema is used: =[?] + + @param sPart + the string, which should be analyzed + + @param pPartIdentifier + the part identifier value, which must be found at the beginning of the + parameter sPart + + @param nPartLength + the length of the ascii value pPartIdentifier + + @param rPartValue + returns the part value if sPart was split successfully + + @param rPartArguments + returns the part arguments if sPart was split successfully + + @return if the identifier could be found and the string was split. + otherwise. +*/ +bool JobURL::implst_split( /*IN*/ std::u16string_view sPart , + /*IN*/ const char* pPartIdentifier , + /*IN*/ sal_Int32 nPartLength , + /*OUT*/ OUString& rPartValue , + /*OUT*/ OUString& rPartArguments ) +{ + // first search for the given identifier + bool bPartFound = o3tl::matchIgnoreAsciiCase(sPart, std::string_view(pPartIdentifier,nPartLength)); + + // If it exist - we can split the part and return sal_True. + // Otherwise we do nothing and return sal_False. + if (bPartFound) + { + // But may the part has optional arguments - separated by a "?". + // Do so - we set the return value with the whole part string. + // Arguments will be set to an empty string as default. + // If we detect the right sign - we split the arguments and overwrite the default. + std::u16string_view sValueAndArguments = sPart.substr(nPartLength); + std::u16string_view sValue = sValueAndArguments; + OUString sArguments; + + size_t nArgStart = sValueAndArguments.find('?'); + if (nArgStart != std::u16string_view::npos) + { + sValue = sValueAndArguments.substr(0,nArgStart); + ++nArgStart; // ignore '?'! + sArguments = sValueAndArguments.substr(nArgStart); + } + + rPartValue = sValue; + rPartArguments = sArguments; + } + + return bPartFound; +} + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/jobs/shelljob.cxx b/framework/source/jobs/shelljob.cxx new file mode 100644 index 0000000000..0c895db33f --- /dev/null +++ b/framework/source/jobs/shelljob.cxx @@ -0,0 +1,168 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +// include own header + +#include +#include +#include + +// include others + +#include +#include + +// include interfaces + +#include +#include +#include +#include + +namespace framework{ + + +// XInterface, XTypeProvider, XServiceInfo + +OUString SAL_CALL ShellJob::getImplementationName() +{ + return "com.sun.star.comp.framework.ShellJob"; +} + +sal_Bool SAL_CALL ShellJob::supportsService( const OUString& sServiceName ) +{ + return cppu::supportsService(this, sServiceName); +} + +css::uno::Sequence< OUString > SAL_CALL ShellJob::getSupportedServiceNames() +{ + return { SERVICENAME_JOB }; +} + + +ShellJob::ShellJob(css::uno::Reference< css::uno::XComponentContext > xContext) + : m_xContext (std::move(xContext)) +{ +} + +ShellJob::~ShellJob() +{ +} + +css::uno::Any SAL_CALL ShellJob::execute(const css::uno::Sequence< css::beans::NamedValue >& lJobArguments) +{ + ::comphelper::SequenceAsHashMap lArgs (lJobArguments); + /** address job configuration inside argument set provided on method execute(). */ + ::comphelper::SequenceAsHashMap lOwnCfg(lArgs.getUnpackedValueOrDefault("JobConfig", css::uno::Sequence< css::beans::NamedValue >())); + + const OUString sCommand = lOwnCfg.getUnpackedValueOrDefault("Command" , OUString()); + const css::uno::Sequence< OUString > lCommandArguments = lOwnCfg.getUnpackedValueOrDefault("Arguments" , css::uno::Sequence< OUString >()); + const bool bDeactivateJobIfDone = lOwnCfg.getUnpackedValueOrDefault("DeactivateJobIfDone" , true ); + const bool bCheckExitCode = lOwnCfg.getUnpackedValueOrDefault("CheckExitCode" , true ); + + // replace all might existing place holder. + OUString sRealCommand = impl_substituteCommandVariables(sCommand); + + // Command is required as minimum. + // If it does not exists ... we can't do our job. + // Deactivate such miss configured job silently .-) + if (sRealCommand.isEmpty()) + return ShellJob::impl_generateAnswer4Deactivation(); + + // do it + bool bDone = impl_execute(sRealCommand, lCommandArguments, bCheckExitCode); + if (! bDone) + return css::uno::Any(); + + // Job was done ... user configured deactivation of this job + // in such case. + if (bDeactivateJobIfDone) + return ShellJob::impl_generateAnswer4Deactivation(); + + // There was no decision about deactivation of this job. + // So we have to return nothing here ! + return css::uno::Any(); +} + +css::uno::Any ShellJob::impl_generateAnswer4Deactivation() +{ + css::uno::Sequence< css::beans::NamedValue > aAnswer { { JobConst::ANSWER_DEACTIVATE_JOB, css::uno::Any(true) } }; + return css::uno::Any(aAnswer); +} + +OUString ShellJob::impl_substituteCommandVariables(const OUString& sCommand) +{ + try + { + css::uno::Reference< css::util::XStringSubstitution > xSubst( css::util::PathSubstitution::create(m_xContext) ); + const bool bSubstRequired = true; + const OUString sCompleteCommand = xSubst->substituteVariables(sCommand, bSubstRequired); + + return sCompleteCommand; + } + catch(const css::uno::Exception&) + {} + + return OUString(); +} + +bool ShellJob::impl_execute(const OUString& sCommand , + const css::uno::Sequence< OUString >& lArguments , + bool bCheckExitCode) +{ + ::rtl_uString** pArgs = nullptr; + const ::sal_Int32 nArgs = lArguments.getLength (); + oslProcess hProcess(nullptr); + + if (nArgs > 0) + pArgs = reinterpret_cast< ::rtl_uString** >(const_cast< OUString* >(lArguments.getConstArray())); + + oslProcessError eError = osl_executeProcess(sCommand.pData, pArgs, nArgs, osl_Process_WAIT, nullptr, nullptr, nullptr, 0, &hProcess); + + // executable not found or couldn't be started + if (eError != osl_Process_E_None) + return false; + + bool bRet = true; + if (bCheckExitCode) + { + // check its return codes ... + oslProcessInfo aInfo; + aInfo.Size = sizeof (oslProcessInfo); + eError = osl_getProcessInfo(hProcess, osl_Process_EXITCODE, &aInfo); + + if (eError != osl_Process_E_None) + bRet = false; + else + bRet = (aInfo.Code == 0); + } + osl_freeProcessHandle(hProcess); + return bRet; +} + +} // namespace framework + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +framework_ShellJob_get_implementation( + css::uno::XComponentContext* context, css::uno::Sequence const& ) +{ + return cppu::acquire(new framework::ShellJob(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/layoutmanager/helpers.cxx b/framework/source/layoutmanager/helpers.cxx new file mode 100644 index 0000000000..ccce1b3e86 --- /dev/null +++ b/framework/source/layoutmanager/helpers.cxx @@ -0,0 +1,341 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "helpers.hxx" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +using namespace com::sun::star; + +namespace framework +{ + +bool hasEmptySize( const css::awt::Size& rSize ) +{ + return ( rSize.Width == 0 ) && ( rSize.Height == 0 ); +} + +bool hasDefaultPosValue( const css::awt::Point& rPos ) +{ + return (( rPos.X == SAL_MAX_INT32 ) || ( rPos.Y == SAL_MAX_INT32 )); +} + +bool isDefaultPos( const css::awt::Point& rPos ) +{ + return (( rPos.X == SAL_MAX_INT32 ) && ( rPos.Y == SAL_MAX_INT32 )); +} + +bool isReverseOrderDockingArea( const sal_Int32 nDockArea ) +{ + ui::DockingArea eDockArea = static_cast< ui::DockingArea >( nDockArea ); + return (( eDockArea == ui::DockingArea_DOCKINGAREA_BOTTOM ) || + ( eDockArea == ui::DockingArea_DOCKINGAREA_RIGHT )); +} + +bool isToolboxHorizontalAligned( ToolBox const * pToolBox ) +{ + if ( pToolBox ) + return (( pToolBox->GetAlign() == WindowAlign::Top ) || ( pToolBox->GetAlign() == WindowAlign::Bottom )); + return false; +} + +bool isHorizontalDockingArea( const ui::DockingArea& nDockingArea ) +{ + return (( nDockingArea == ui::DockingArea_DOCKINGAREA_TOP ) || + ( nDockingArea == ui::DockingArea_DOCKINGAREA_BOTTOM )); +} + +bool isHorizontalDockingArea( const sal_Int32 nDockArea ) +{ + return isHorizontalDockingArea(static_cast< ui::DockingArea >( nDockArea )); +} + +OUString retrieveToolbarNameFromHelpURL( vcl::Window* pWindow ) +{ + OUString aToolbarName; + + if ( pWindow->GetType() == WindowType::TOOLBOX ) + { + ToolBox* pToolBox = dynamic_cast( pWindow ); + if ( pToolBox ) + { + aToolbarName = pToolBox->GetHelpId(); + sal_Int32 i = aToolbarName.lastIndexOf( ':' ); + if ( !aToolbarName.isEmpty() && ( i > 0 ) && (( i + 1 ) < aToolbarName.getLength() )) + aToolbarName = aToolbarName.copy( i+1 ); // Remove ".HelpId:" protocol from toolbar name + else + aToolbarName.clear(); + } + } + return aToolbarName; +} + +ToolBox* getToolboxPtr( vcl::Window* pWindow ) +{ + ToolBox* pToolbox(nullptr); + if ( pWindow->GetType() == WindowType::TOOLBOX ) + pToolbox = dynamic_cast( pWindow ); + return pToolbox; +} + +vcl::Window* getWindowFromXUIElement( const uno::Reference< ui::XUIElement >& xUIElement ) +{ + SolarMutexGuard aGuard; + uno::Reference< awt::XWindow > xWindow; + if ( xUIElement.is() ) + xWindow.set( xUIElement->getRealInterface(), uno::UNO_QUERY ); + return VCLUnoHelper::GetWindow( xWindow ); +} + +SystemWindow* getTopSystemWindow( const uno::Reference< awt::XWindow >& xWindow ) +{ + VclPtr pWindow = VCLUnoHelper::GetWindow( xWindow ); + while ( pWindow && !pWindow->IsSystemWindow() ) + pWindow = pWindow->GetParent(); + + if ( pWindow ) + return static_cast(pWindow.get()); + else + return nullptr; +} + +// ATTENTION! +// This value is directly copied from the sfx2 project. +// You have to change BOTH values, see sfx2/inc/sfx2/sfxsids.hrc (SID_DOCKWIN_START) +const sal_Int32 DOCKWIN_ID_BASE = 9800; + +bool lcl_checkUIElement(const uno::Reference< ui::XUIElement >& xUIElement, awt::Rectangle& _rPosSize, uno::Reference< awt::XWindow >& _xWindow) +{ + bool bRet = xUIElement.is(); + if ( bRet ) + { + SolarMutexGuard aGuard; + _xWindow.set( xUIElement->getRealInterface(), uno::UNO_QUERY ); + _rPosSize = _xWindow->getPosSize(); + + VclPtr pWindow = VCLUnoHelper::GetWindow( _xWindow ); + if ( pWindow->GetType() == WindowType::TOOLBOX ) + { + ::Size aSize = static_cast(pWindow.get())->CalcWindowSizePixel( 1 ); + _rPosSize.Width = aSize.Width(); + _rPosSize.Height = aSize.Height(); + } + } // if ( xUIElement.is() ) + return bRet; +} + +uno::Reference< awt::XVclWindowPeer > createToolkitWindow( const uno::Reference< uno::XComponentContext >& rxContext, const uno::Reference< awt::XVclWindowPeer >& rParent, const char* pService ) +{ + uno::Reference< awt::XToolkit2 > xToolkit = awt::Toolkit::create( rxContext ); + + // describe window properties. + css::awt::WindowDescriptor aDescriptor; + aDescriptor.Type = awt::WindowClass_SIMPLE; + aDescriptor.WindowServiceName = OUString::createFromAscii( pService ); + aDescriptor.ParentIndex = -1; + aDescriptor.Parent = rParent; + aDescriptor.Bounds = awt::Rectangle(0,0,0,0); + aDescriptor.WindowAttributes = 0; + + // create an awt window + uno::Reference< awt::XWindowPeer > xPeer = xToolkit->createWindow( aDescriptor ); + uno::Reference< awt::XVclWindowPeer > xVclPeer(xPeer, uno::UNO_QUERY); + assert(xVclPeer || !xPeer); + return xVclPeer; +} + +// convert alignment constant to vcl's WindowAlign type +WindowAlign ImplConvertAlignment( ui::DockingArea aAlignment ) +{ + if ( aAlignment == ui::DockingArea_DOCKINGAREA_LEFT ) + return WindowAlign::Left; + else if ( aAlignment == ui::DockingArea_DOCKINGAREA_RIGHT ) + return WindowAlign::Right; + else if ( aAlignment == ui::DockingArea_DOCKINGAREA_TOP ) + return WindowAlign::Top; + else + return WindowAlign::Bottom; +} + +std::u16string_view getElementTypeFromResourceURL( std::u16string_view aResourceURL ) +{ + if ( o3tl::starts_with(aResourceURL, UIRESOURCE_URL ) ) + { + sal_Int32 nIndex{ UIRESOURCE_URL.getLength() }; + return o3tl::getToken(aResourceURL, 1, '/', nIndex ); + } + + return std::u16string_view(); +} + +void parseResourceURL( std::u16string_view aResourceURL, OUString& aElementType, OUString& aElementName ) +{ + if ( o3tl::starts_with(aResourceURL, UIRESOURCE_URL) ) + { + sal_Int32 nIndex{ UIRESOURCE_URL.getLength() }; + aElementType = o3tl::getToken(aResourceURL, 1, '/', nIndex ); + aElementName = o3tl::getToken(aResourceURL, 0, '/', nIndex ); + } +} + +css::awt::Rectangle putRectangleValueToAWT( const ::tools::Rectangle& rRect ) +{ + css::awt::Rectangle aRect; + aRect.X = rRect.Left(); + aRect.Y = rRect.Top(); + aRect.Width = rRect.Right(); + aRect.Height = rRect.Bottom(); + + return aRect; +} + +::tools::Rectangle putAWTToRectangle( const css::awt::Rectangle& rRect ) +{ + ::tools::Rectangle aRect; + aRect.SetLeft( rRect.X ); + aRect.SetTop( rRect.Y ); + aRect.SetRight( rRect.Width ); + aRect.SetBottom( rRect.Height ); + + return aRect; +} + +bool equalRectangles( const css::awt::Rectangle& rRect1, + const css::awt::Rectangle& rRect2 ) +{ + return (( rRect1.X == rRect2.X ) && + ( rRect1.Y == rRect2.Y ) && + ( rRect1.Width == rRect2.Width ) && + ( rRect1.Height == rRect2.Height )); +} + +uno::Reference< frame::XModel > impl_getModelFromFrame( const uno::Reference< frame::XFrame >& rFrame ) +{ + // Query for the model to get check the context information + uno::Reference< frame::XModel > xModel; + if ( rFrame.is() ) + { + uno::Reference< frame::XController > xController = rFrame->getController(); + if ( xController.is() ) + xModel = xController->getModel(); + } + + return xModel; +} + +bool implts_isPreviewModel( const uno::Reference< frame::XModel >& xModel ) +{ + // the cost in calc of calling getArgs for this property + // includes measuring the entire sheet - which is extremely slow. + if (comphelper::LibreOfficeKit::isActive()) + return false; + + if ( xModel.is() ) + { + utl::MediaDescriptor aDesc( xModel->getArgs() ); + return aDesc.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_PREVIEW, false); + } + else + return false; +} + +bool implts_isFrameOrWindowTop( const uno::Reference< frame::XFrame >& xFrame ) +{ + if (xFrame->isTop()) + return true; + + uno::Reference< awt::XTopWindow > xWindowCheck(xFrame->getContainerWindow(), uno::UNO_QUERY); // don't use _THROW here ... it's a check only + if (xWindowCheck.is()) + { + // #i76867# top and system window is required. + SolarMutexGuard aGuard; + uno::Reference< awt::XWindow > xWindow( xWindowCheck, uno::UNO_QUERY ); + VclPtr pWindow = VCLUnoHelper::GetWindow( xWindow ); + return pWindow && pWindow->IsSystemWindow(); + } + + return false; +} + +void impl_setDockingWindowVisibility( const css::uno::Reference< css::uno::XComponentContext>& rxContext, const css::uno::Reference< css::frame::XFrame >& rFrame, std::u16string_view rDockingWindowName, bool bVisible ) +{ + sal_Int32 nID = o3tl::toInt32(rDockingWindowName); + sal_Int32 nIndex = nID - DOCKWIN_ID_BASE; + + css::uno::Reference< css::frame::XDispatchProvider > xProvider(rFrame, css::uno::UNO_QUERY); + if ( !(nIndex >= 0 && xProvider.is()) ) + return; + + OUString aDockWinArgName = "DockingWindow" + OUString::number( nIndex ); + + css::uno::Sequence< css::beans::PropertyValue > aArgs{ comphelper::makePropertyValue( + aDockWinArgName, bVisible) }; + + css::uno::Reference< css::frame::XDispatchHelper > xDispatcher = css::frame::DispatchHelper::create( rxContext ); + + OUString aDockWinCommand = ".uno:" + aDockWinArgName; + xDispatcher->executeDispatch( + xProvider, + aDockWinCommand, + "_self", + 0, + aArgs); +} + +void impl_addWindowListeners( + const css::uno::Reference< css::uno::XInterface >& xThis, + const css::uno::Reference< css::ui::XUIElement >& xUIElement ) +{ + css::uno::Reference< css::awt::XWindow > xWindow( xUIElement->getRealInterface(), css::uno::UNO_QUERY ); + css::uno::Reference< css::awt::XDockableWindow > xDockWindow( xUIElement->getRealInterface(), css::uno::UNO_QUERY ); + if ( !(xDockWindow.is() && xWindow.is()) ) + return; + + try + { + xDockWindow->addDockableWindowListener( + css::uno::Reference< css::awt::XDockableWindowListener >( + xThis, css::uno::UNO_QUERY )); + xWindow->addWindowListener( + css::uno::Reference< css::awt::XWindowListener >( + xThis, css::uno::UNO_QUERY )); + xDockWindow->enableDocking( true ); + } + catch ( const css::uno::Exception& ) + { + } +} + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/layoutmanager/helpers.hxx b/framework/source/layoutmanager/helpers.hxx new file mode 100644 index 0000000000..c58fa6665b --- /dev/null +++ b/framework/source/layoutmanager/helpers.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 . + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +inline constexpr OUString UIRESOURCE_URL = u"private:resource"_ustr; +inline constexpr OUString UIRESOURCETYPE_TOOLBAR = u"toolbar"_ustr; +#define UIRESOURCETYPE_MENUBAR "menubar" + +namespace framework +{ + +bool hasEmptySize( const css::awt::Size& rSize ); +bool hasDefaultPosValue( const css::awt::Point& rPos ); +bool isDefaultPos( const css::awt::Point& rPos ); +bool isToolboxHorizontalAligned( ToolBox const * pToolBox ); +bool isReverseOrderDockingArea( const sal_Int32 nDockArea ); +bool isHorizontalDockingArea( const sal_Int32 nDockArea ); +bool isHorizontalDockingArea( const css::ui::DockingArea& nDockArea ); +OUString retrieveToolbarNameFromHelpURL( vcl::Window* pWindow ); +ToolBox* getToolboxPtr( vcl::Window* pWindow ); +vcl::Window* getWindowFromXUIElement( const css::uno::Reference< css::ui::XUIElement >& xUIElement ); +SystemWindow* getTopSystemWindow( const css::uno::Reference< css::awt::XWindow >& xWindow ); +bool equalRectangles( const css::awt::Rectangle& rRect1, const css::awt::Rectangle& rRect2 ); +bool lcl_checkUIElement(const css::uno::Reference< css::ui::XUIElement >& xUIElement,css::awt::Rectangle& _rPosSize, css::uno::Reference< css::awt::XWindow >& _xWindow); +css::uno::Reference< css::awt::XVclWindowPeer > createToolkitWindow( const css::uno::Reference< css::uno::XComponentContext >& rxContext, const css::uno::Reference< css::awt::XVclWindowPeer >& rParent, const char* pService ); +WindowAlign ImplConvertAlignment( css::ui::DockingArea aAlignment ); +std::u16string_view getElementTypeFromResourceURL( std::u16string_view aResourceURL ); +void parseResourceURL( std::u16string_view aResourceURL, OUString& aElementType, OUString& aElementName ); +::tools::Rectangle putAWTToRectangle( const css::awt::Rectangle& rRect ); +css::awt::Rectangle putRectangleValueToAWT( const ::tools::Rectangle& rRect ); +css::uno::Reference< css::frame::XModel > impl_getModelFromFrame( const css::uno::Reference< css::frame::XFrame >& rFrame ); +bool implts_isPreviewModel( const css::uno::Reference< css::frame::XModel >& xModel ); +bool implts_isFrameOrWindowTop( const css::uno::Reference< css::frame::XFrame >& xFrame ); +void impl_setDockingWindowVisibility( const css::uno::Reference< css::uno::XComponentContext>& rxContext, const css::uno::Reference< css::frame::XFrame >& rFrame, std::u16string_view rDockingWindowName, bool bVisible ); +void impl_addWindowListeners( const css::uno::Reference< css::uno::XInterface >& xThis, const css::uno::Reference< css::ui::XUIElement >& xUIElement ); + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/layoutmanager/layoutmanager.cxx b/framework/source/layoutmanager/layoutmanager.cxx new file mode 100644 index 0000000000..b915e3f82a --- /dev/null +++ b/framework/source/layoutmanager/layoutmanager.cxx @@ -0,0 +1,3070 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include + +#include +#include +#include "helpers.hxx" + +#include +#include +#include +#include +#include +#include "toolbarlayoutmanager.hxx" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +// using namespace +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::util; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::ui; +using namespace ::com::sun::star::frame; + +constexpr OUString STATUS_BAR_ALIAS = u"private:resource/statusbar/statusbar"_ustr; + +namespace framework +{ + +IMPLEMENT_FORWARD_XTYPEPROVIDER2( LayoutManager, LayoutManager_Base, LayoutManager_PBase ) +IMPLEMENT_FORWARD_XINTERFACE2( LayoutManager, LayoutManager_Base, LayoutManager_PBase ) + +LayoutManager::LayoutManager( const Reference< XComponentContext >& xContext ) : + ::cppu::OBroadcastHelperVar< ::cppu::OMultiTypeInterfaceContainerHelper, ::cppu::OMultiTypeInterfaceContainerHelper::keyType >(m_aMutex) + , LayoutManager_PBase( *static_cast< ::cppu::OBroadcastHelper* >(this) ) + , m_xContext( xContext ) + , m_xURLTransformer( URLTransformer::create(xContext) ) + , m_nLockCount( 0 ) + , m_bInplaceMenuSet( false ) + , m_bMenuVisible( true ) + , m_bVisible( true ) + , m_bParentWindowVisible( false ) + , m_bMustDoLayout( true ) +#if HAVE_FEATURE_DESKTOP + , m_bAutomaticToolbars( true ) +#else + , m_bAutomaticToolbars( false ) +#endif + , m_bHideCurrentUI( false ) + , m_bGlobalSettings( false ) + , m_bPreserveContentSize( false ) + , m_bMenuBarCloseButton( false ) + , m_xModuleManager( ModuleManager::create( xContext )) + , m_xUIElementFactoryManager( ui::theUIElementFactoryManager::get(xContext) ) + , m_xPersistentWindowStateSupplier( ui::theWindowStateConfiguration::get( xContext ) ) + , m_aAsyncLayoutTimer( "framework::LayoutManager m_aAsyncLayoutTimer" ) + , m_aListenerContainer( m_aMutex ) +{ + // Initialize statusbar member + m_aStatusBarElement.m_aType = "statusbar"; + m_aStatusBarElement.m_aName = STATUS_BAR_ALIAS; + + if (!comphelper::LibreOfficeKit::isActive()) + { + m_xToolbarManager = new ToolbarLayoutManager( xContext, Reference(m_xUIElementFactoryManager, UNO_QUERY_THROW), this ); + } + + m_aAsyncLayoutTimer.SetPriority( TaskPriority::HIGH_IDLE ); + m_aAsyncLayoutTimer.SetTimeout( 50 ); + m_aAsyncLayoutTimer.SetInvokeHandler( LINK( this, LayoutManager, AsyncLayoutHdl ) ); + + registerProperty( LAYOUTMANAGER_PROPNAME_ASCII_AUTOMATICTOOLBARS, LAYOUTMANAGER_PROPHANDLE_AUTOMATICTOOLBARS, css::beans::PropertyAttribute::TRANSIENT, &m_bAutomaticToolbars, cppu::UnoType::get() ); + registerProperty( LAYOUTMANAGER_PROPNAME_ASCII_HIDECURRENTUI, LAYOUTMANAGER_PROPHANDLE_HIDECURRENTUI, beans::PropertyAttribute::TRANSIENT, &m_bHideCurrentUI, cppu::UnoType::get() ); + registerProperty( LAYOUTMANAGER_PROPNAME_ASCII_LOCKCOUNT, LAYOUTMANAGER_PROPHANDLE_LOCKCOUNT, beans::PropertyAttribute::TRANSIENT | beans::PropertyAttribute::READONLY, &m_nLockCount, cppu::UnoType::get() ); + registerProperty( LAYOUTMANAGER_PROPNAME_MENUBARCLOSER, LAYOUTMANAGER_PROPHANDLE_MENUBARCLOSER, beans::PropertyAttribute::TRANSIENT, &m_bMenuBarCloseButton, cppu::UnoType::get() ); + registerPropertyNoMember( LAYOUTMANAGER_PROPNAME_ASCII_REFRESHVISIBILITY, LAYOUTMANAGER_PROPHANDLE_REFRESHVISIBILITY, beans::PropertyAttribute::TRANSIENT, cppu::UnoType::get(), css::uno::Any(false) ); + registerProperty( LAYOUTMANAGER_PROPNAME_ASCII_PRESERVE_CONTENT_SIZE, LAYOUTMANAGER_PROPHANDLE_PRESERVE_CONTENT_SIZE, beans::PropertyAttribute::TRANSIENT, &m_bPreserveContentSize, cppu::UnoType::get() ); + registerPropertyNoMember( LAYOUTMANAGER_PROPNAME_ASCII_REFRESHTOOLTIP, LAYOUTMANAGER_PROPHANDLE_REFRESHTOOLTIP, beans::PropertyAttribute::TRANSIENT, cppu::UnoType::get(), css::uno::Any(false) ); +} + +LayoutManager::~LayoutManager() +{ + m_aAsyncLayoutTimer.Stop(); + setDockingAreaAcceptor(nullptr); + m_pGlobalSettings.reset(); +} + +void LayoutManager::implts_createMenuBar(const OUString& rMenuBarName) +{ + SolarMutexGuard aWriteLock; + + // Create a customized menu if compatibility mode is on + if (m_aModuleIdentifier == "com.sun.star.text.TextDocument" && officecfg::Office::Compatibility::View::MSCompatibleFormsMenu::get()) + { + implts_createMSCompatibleMenuBar(rMenuBarName); + } + + // Create the default menubar otherwise + if (m_bInplaceMenuSet || m_xMenuBar.is()) + return; + + m_xMenuBar.set( static_cast< MenuBarWrapper* >(implts_createElement( rMenuBarName ).get()) ); + if ( !m_xMenuBar.is() ) + return; + + SystemWindow* pSysWindow = getTopSystemWindow( m_xContainerWindow ); + if ( !pSysWindow ) + return; + + Reference< awt::XMenuBar > xMenuBar; + + try + { + m_xMenuBar->getPropertyValue("XMenuBar") >>= xMenuBar; + } + catch (const beans::UnknownPropertyException&) + { + } + catch (const lang::WrappedTargetException&) + { + } + + if ( !xMenuBar.is() ) + return; + + VCLXMenu* pAwtMenuBar = dynamic_cast( xMenuBar.get() ); + if ( pAwtMenuBar ) + { + MenuBar* pMenuBar = static_cast(pAwtMenuBar->GetMenu()); + if ( pMenuBar ) + { + pSysWindow->SetMenuBar(pMenuBar); + pMenuBar->SetDisplayable( m_bMenuVisible ); + implts_updateMenuBarClose(); + } + } +} + +// Internal helper function +void LayoutManager::impl_clearUpMenuBar() +{ + implts_lock(); + + // Clear up VCL menu bar to prepare shutdown + if ( m_xContainerWindow.is() ) + { + SolarMutexGuard aGuard; + + SystemWindow* pSysWindow = getTopSystemWindow( m_xContainerWindow ); + if ( pSysWindow ) + { + MenuBar* pSetMenuBar = nullptr; + if ( m_xInplaceMenuBar.is() ) + pSetMenuBar = static_cast(m_xInplaceMenuBar->GetMenuBar()); + else + { + Reference< awt::XMenuBar > xMenuBar; + + if ( m_xMenuBar.is() ) + { + try + { + m_xMenuBar->getPropertyValue("XMenuBar") >>= xMenuBar; + } + catch (const beans::UnknownPropertyException&) + { + } + catch (const lang::WrappedTargetException&) + { + } + } + + VCLXMenu* pAwtMenuBar = dynamic_cast( xMenuBar.get() ); + if ( pAwtMenuBar ) + pSetMenuBar = static_cast(pAwtMenuBar->GetMenu()); + } + + MenuBar* pTopMenuBar = pSysWindow->GetMenuBar(); + if ( pSetMenuBar == pTopMenuBar ) + pSysWindow->SetMenuBar( nullptr ); + } + } + + // reset inplace menubar manager + VclPtr pMenuBar; + if (m_xInplaceMenuBar.is()) + { + pMenuBar = m_xInplaceMenuBar->GetMenuBar(); + m_xInplaceMenuBar->dispose(); + m_xInplaceMenuBar.clear(); + } + pMenuBar.disposeAndClear(); + m_bInplaceMenuSet = false; + + if ( m_xMenuBar.is() ) + { + m_xMenuBar->dispose(); + m_xMenuBar.clear(); + } + implts_unlock(); +} + +void LayoutManager::implts_lock() +{ + SolarMutexGuard g; + ++m_nLockCount; +} + +bool LayoutManager::implts_unlock() +{ + SolarMutexGuard g; + m_nLockCount = std::max( m_nLockCount-1, static_cast(0) ); + return ( m_nLockCount == 0 ); +} + +void LayoutManager::implts_reset( bool bAttached ) +{ + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + SolarMutexClearableGuard aReadLock; + Reference< XFrame > xFrame = m_xFrame; + Reference< awt::XWindow > xContainerWindow( m_xContainerWindow ); + Reference< XUIConfiguration > xModuleCfgMgr( m_xModuleCfgMgr, UNO_QUERY ); + Reference< XUIConfiguration > xDocCfgMgr( m_xDocCfgMgr, UNO_QUERY ); + Reference< XNameAccess > xPersistentWindowState( m_xPersistentWindowState ); + Reference< XComponentContext > xContext( m_xContext ); + Reference< XNameAccess > xPersistentWindowStateSupplier( m_xPersistentWindowStateSupplier ); + rtl::Reference xToolbarManager( m_xToolbarManager ); + OUString aModuleIdentifier( m_aModuleIdentifier ); + bool bAutomaticToolbars( m_bAutomaticToolbars ); + aReadLock.clear(); + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + + implts_lock(); + + Reference< XModel > xModel; + if ( xFrame.is() ) + { + if ( bAttached ) + { + OUString aOldModuleIdentifier( aModuleIdentifier ); + try + { + aModuleIdentifier = m_xModuleManager->identify( xFrame ); + } + catch( const Exception& ) {} + + if ( !aModuleIdentifier.isEmpty() && aOldModuleIdentifier != aModuleIdentifier ) + { + Reference< XModuleUIConfigurationManagerSupplier > xModuleCfgSupplier; + if ( xContext.is() ) + xModuleCfgSupplier = theModuleUIConfigurationManagerSupplier::get( xContext ); + + if ( xModuleCfgMgr.is() ) + { + try + { + // Remove listener to old module ui configuration manager + xModuleCfgMgr->removeConfigurationListener( Reference< XUIConfigurationListener >(this) ); + } + catch (const Exception&) + { + } + } + + try + { + // Add listener to new module ui configuration manager + xModuleCfgMgr.set( xModuleCfgSupplier->getUIConfigurationManager( aModuleIdentifier ), UNO_QUERY ); + if ( xModuleCfgMgr.is() ) + xModuleCfgMgr->addConfigurationListener( Reference< XUIConfigurationListener >(this) ); + } + catch (const Exception&) + { + } + + try + { + // Retrieve persistent window state reference for our new module + if ( xPersistentWindowStateSupplier.is() ) + xPersistentWindowStateSupplier->getByName( aModuleIdentifier ) >>= xPersistentWindowState; + } + catch (const NoSuchElementException&) + { + } + catch (const WrappedTargetException&) + { + } + } + + xModel = impl_getModelFromFrame( xFrame ); + if ( xModel.is() ) + { + Reference< XUIConfigurationManagerSupplier > xUIConfigurationManagerSupplier( xModel, UNO_QUERY ); + if ( xUIConfigurationManagerSupplier.is() ) + { + if ( xDocCfgMgr.is() ) + { + try + { + // Remove listener to old ui configuration manager + xDocCfgMgr->removeConfigurationListener( Reference< XUIConfigurationListener >(this) ); + } + catch (const Exception&) + { + } + } + + try + { + xDocCfgMgr.set( xUIConfigurationManagerSupplier->getUIConfigurationManager(), UNO_QUERY ); + if ( xDocCfgMgr.is() ) + xDocCfgMgr->addConfigurationListener( Reference< XUIConfigurationListener >(this) ); + } + catch (const Exception&) + { + } + } + } + } + else + { + // Remove configuration listeners before we can release our references + if ( xModuleCfgMgr.is() ) + { + try + { + xModuleCfgMgr->removeConfigurationListener( + Reference< XUIConfigurationListener >(this) ); + } + catch (const Exception&) + { + } + } + + if ( xDocCfgMgr.is() ) + { + try + { + xDocCfgMgr->removeConfigurationListener( + Reference< XUIConfigurationListener >(this) ); + } + catch (const Exception&) + { + } + } + + // Release references to our configuration managers as we currently don't have + // an attached module. + xModuleCfgMgr.clear(); + xDocCfgMgr.clear(); + xPersistentWindowState.clear(); + aModuleIdentifier.clear(); + } + + Reference< XUIConfigurationManager > xModCfgMgr( xModuleCfgMgr, UNO_QUERY ); + Reference< XUIConfigurationManager > xDokCfgMgr( xDocCfgMgr, UNO_QUERY ); + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + SolarMutexClearableGuard aWriteLock; + m_aDockingArea = awt::Rectangle(); + m_aModuleIdentifier = aModuleIdentifier; + m_xModuleCfgMgr = xModCfgMgr; + m_xDocCfgMgr = xDokCfgMgr; + m_xPersistentWindowState = xPersistentWindowState; + m_aStatusBarElement.m_bStateRead = false; // reset state to read data again! + aWriteLock.clear(); + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + + // reset/notify toolbar layout manager + if ( xToolbarManager.is() ) + { + if ( bAttached ) + { + xToolbarManager->attach( xFrame, xModCfgMgr, xDokCfgMgr, xPersistentWindowState ); + uno::Reference< awt::XVclWindowPeer > xParent( xContainerWindow, UNO_QUERY ); + xToolbarManager->setParentWindow( xParent ); + if ( bAutomaticToolbars ) + xToolbarManager->createStaticToolbars(); + } + else + { + xToolbarManager->reset(); + implts_destroyElements(); + } + } + } + + implts_unlock(); +} + +bool LayoutManager::implts_isEmbeddedLayoutManager() const +{ + SolarMutexClearableGuard aReadLock; + Reference< XFrame > xFrame = m_xFrame; + Reference< awt::XWindow > xContainerWindow( m_xContainerWindow ); + aReadLock.clear(); + + Reference< awt::XWindow > xFrameContainerWindow = xFrame->getContainerWindow(); + return xFrameContainerWindow != xContainerWindow; +} + +void LayoutManager::implts_destroyElements() +{ + SolarMutexResettableGuard aWriteLock; + ToolbarLayoutManager* pToolbarManager = m_xToolbarManager.get(); + aWriteLock.clear(); + + if ( pToolbarManager ) + pToolbarManager->destroyToolbars(); + + implts_destroyStatusBar(); + + aWriteLock.reset(); + impl_clearUpMenuBar(); + aWriteLock.clear(); +} + +void LayoutManager::implts_toggleFloatingUIElementsVisibility( bool bActive ) +{ + SolarMutexClearableGuard aReadLock; + ToolbarLayoutManager* pToolbarManager = m_xToolbarManager.get(); + aReadLock.clear(); + + if ( pToolbarManager ) + pToolbarManager->setFloatingToolbarsVisibility( bActive ); +} + +uno::Reference< ui::XUIElement > LayoutManager::implts_findElement( std::u16string_view aName ) +{ + OUString aElementType; + OUString aElementName; + + parseResourceURL( aName, aElementType, aElementName ); + if ( aElementType.equalsIgnoreAsciiCase("menubar") && + aElementName.equalsIgnoreAsciiCase("menubar") ) + return m_xMenuBar; + else if (( aElementType.equalsIgnoreAsciiCase("statusbar") && + aElementName.equalsIgnoreAsciiCase("statusbar") ) || + ( m_aStatusBarElement.m_aName == aName )) + return m_aStatusBarElement.m_xUIElement; + else if ( aElementType.equalsIgnoreAsciiCase("progressbar") && + aElementName.equalsIgnoreAsciiCase("progressbar") ) + return m_aProgressBarElement.m_xUIElement; + + return uno::Reference< ui::XUIElement >(); +} + +bool LayoutManager::implts_readWindowStateData( const OUString& aName, UIElement& rElementData ) +{ + return readWindowStateData( aName, rElementData, m_xPersistentWindowState, + m_pGlobalSettings, m_bGlobalSettings, m_xContext ); +} + +bool LayoutManager::readWindowStateData( const OUString& aName, UIElement& rElementData, + const Reference< XNameAccess > &rPersistentWindowState, + std::unique_ptr &rGlobalSettings, bool &bInGlobalSettings, + const Reference< XComponentContext > &rComponentContext ) +{ + if ( !rPersistentWindowState.is() ) + return false; + + bool bGetSettingsState( false ); + + SolarMutexClearableGuard aWriteLock; + bool bGlobalSettings( bInGlobalSettings ); + if ( rGlobalSettings == nullptr ) + { + rGlobalSettings.reset( new GlobalSettings( rComponentContext ) ); + bGetSettingsState = true; + } + GlobalSettings* pGlobalSettings = rGlobalSettings.get(); + aWriteLock.clear(); + + try + { + Sequence< PropertyValue > aWindowState; + if ( rPersistentWindowState->hasByName( aName ) && (rPersistentWindowState->getByName( aName ) >>= aWindowState) ) + { + bool bValue( false ); + for ( PropertyValue const & rProp : std::as_const(aWindowState) ) + { + if ( rProp.Name == WINDOWSTATE_PROPERTY_DOCKED ) + { + if ( rProp.Value >>= bValue ) + rElementData.m_bFloating = !bValue; + } + else if ( rProp.Name == WINDOWSTATE_PROPERTY_VISIBLE ) + { + if ( rProp.Value >>= bValue ) + rElementData.m_bVisible = bValue; + } + else if ( rProp.Name == WINDOWSTATE_PROPERTY_DOCKINGAREA ) + { + ui::DockingArea eDockingArea; + if ( rProp.Value >>= eDockingArea ) + rElementData.m_aDockedData.m_nDockedArea = eDockingArea; + } + else if ( rProp.Name == WINDOWSTATE_PROPERTY_DOCKPOS ) + { + awt::Point aPoint; + if (rProp.Value >>= aPoint) + { + //tdf#90256 repair these broken Docking positions + if (aPoint.X < 0) + aPoint.X = SAL_MAX_INT32; + if (aPoint.Y < 0) + aPoint.Y = SAL_MAX_INT32; + rElementData.m_aDockedData.m_aPos = aPoint; + } + } + else if ( rProp.Name == WINDOWSTATE_PROPERTY_POS ) + { + awt::Point aPoint; + if ( rProp.Value >>= aPoint ) + rElementData.m_aFloatingData.m_aPos = aPoint; + } + else if ( rProp.Name == WINDOWSTATE_PROPERTY_SIZE ) + { + awt::Size aSize; + if ( rProp.Value >>= aSize ) + rElementData.m_aFloatingData.m_aSize = aSize; + } + else if ( rProp.Name == WINDOWSTATE_PROPERTY_UINAME ) + rProp.Value >>= rElementData.m_aUIName; + else if ( rProp.Name == WINDOWSTATE_PROPERTY_STYLE ) + { + sal_Int32 nStyle = 0; + if ( rProp.Value >>= nStyle ) + rElementData.m_nStyle = static_cast( nStyle ); + } + else if ( rProp.Name == WINDOWSTATE_PROPERTY_LOCKED ) + { + if ( rProp.Value >>= bValue ) + rElementData.m_aDockedData.m_bLocked = bValue; + } + else if ( rProp.Name == WINDOWSTATE_PROPERTY_CONTEXT ) + { + if ( rProp.Value >>= bValue ) + rElementData.m_bContextSensitive = bValue; + } + else if ( rProp.Name == WINDOWSTATE_PROPERTY_NOCLOSE ) + { + if ( rProp.Value >>= bValue ) + rElementData.m_bNoClose = bValue; + } + } + } + + // oversteer values with global settings + if (bGetSettingsState || bGlobalSettings) + { + if ( pGlobalSettings->HasToolbarStatesInfo()) + { + { + SolarMutexGuard aWriteLock2; + bInGlobalSettings = true; + } + + uno::Any aValue; + if ( pGlobalSettings->GetToolbarStateInfo( + GlobalSettings::STATEINFO_LOCKED, + aValue )) + aValue >>= rElementData.m_aDockedData.m_bLocked; + if ( pGlobalSettings->GetToolbarStateInfo( + GlobalSettings::STATEINFO_DOCKED, + aValue )) + { + bool bValue; + if ( aValue >>= bValue ) + rElementData.m_bFloating = !bValue; + } + } + } + + const bool bDockingSupportCrippled = !StyleSettings::GetDockingFloatsSupported(); + if (bDockingSupportCrippled) + rElementData.m_bFloating = false; + + return true; + } + catch (const NoSuchElementException&) + { + } + + return false; +} + +void LayoutManager::implts_writeWindowStateData( const OUString& aName, const UIElement& rElementData ) +{ + SolarMutexClearableGuard aWriteLock; + Reference< XNameAccess > xPersistentWindowState( m_xPersistentWindowState ); + + aWriteLock.clear(); + + bool bPersistent( false ); + Reference< XPropertySet > xPropSet( rElementData.m_xUIElement, UNO_QUERY ); + if ( xPropSet.is() ) + { + try + { + // Check persistent flag of the user interface element + xPropSet->getPropertyValue("Persistent") >>= bPersistent; + } + catch (const beans::UnknownPropertyException&) + { + // Non-configurable elements should at least store their dimension/position + bPersistent = true; + } + catch (const lang::WrappedTargetException&) + { + } + } + + if ( !(bPersistent && xPersistentWindowState.is()) ) + return; + + try + { + Sequence< PropertyValue > aWindowState{ + comphelper::makePropertyValue(WINDOWSTATE_PROPERTY_DOCKED, !rElementData.m_bFloating), + comphelper::makePropertyValue(WINDOWSTATE_PROPERTY_VISIBLE, rElementData.m_bVisible), + comphelper::makePropertyValue(WINDOWSTATE_PROPERTY_DOCKINGAREA, + rElementData.m_aDockedData.m_nDockedArea), + comphelper::makePropertyValue(WINDOWSTATE_PROPERTY_DOCKPOS, + rElementData.m_aDockedData.m_aPos), + comphelper::makePropertyValue(WINDOWSTATE_PROPERTY_POS, + rElementData.m_aFloatingData.m_aPos), + comphelper::makePropertyValue(WINDOWSTATE_PROPERTY_SIZE, + rElementData.m_aFloatingData.m_aSize), + comphelper::makePropertyValue(WINDOWSTATE_PROPERTY_UINAME, rElementData.m_aUIName), + comphelper::makePropertyValue(WINDOWSTATE_PROPERTY_LOCKED, + rElementData.m_aDockedData.m_bLocked) + }; + + if ( xPersistentWindowState->hasByName( aName )) + { + Reference< XNameReplace > xReplace( xPersistentWindowState, uno::UNO_QUERY ); + xReplace->replaceByName( aName, Any( aWindowState )); + } + else + { + Reference< XNameContainer > xInsert( xPersistentWindowState, uno::UNO_QUERY ); + xInsert->insertByName( aName, Any( aWindowState )); + } + } + catch (const Exception&) + { + } +} + +::Size LayoutManager::implts_getContainerWindowOutputSize() +{ + ::Size aContainerWinSize; + vcl::Window* pContainerWindow( nullptr ); + + // Retrieve output size from container Window + SolarMutexGuard aGuard; + pContainerWindow = VCLUnoHelper::GetWindow( m_xContainerWindow ); + if ( pContainerWindow ) + aContainerWinSize = pContainerWindow->GetOutputSizePixel(); + + return aContainerWinSize; +} + +Reference< XUIElement > LayoutManager::implts_createElement( const OUString& aName ) +{ + Reference< ui::XUIElement > xUIElement; + + SolarMutexGuard g; + Sequence< PropertyValue > aPropSeq{ comphelper::makePropertyValue("Frame", m_xFrame), + comphelper::makePropertyValue("Persistent", true) }; + + try + { + xUIElement = m_xUIElementFactoryManager->createUIElement( aName, aPropSeq ); + } + catch (const NoSuchElementException&) + { + } + catch (const IllegalArgumentException&) + { + } + + return xUIElement; +} + +void LayoutManager::implts_setVisibleState( bool bShow ) +{ + { + SolarMutexGuard aWriteLock; + m_aStatusBarElement.m_bMasterHide = !bShow; + } + + implts_updateUIElementsVisibleState( bShow ); +} + +void LayoutManager::implts_updateUIElementsVisibleState( bool bSetVisible ) +{ + // notify listeners + uno::Any a; + if ( bSetVisible ) + implts_notifyListeners( frame::LayoutManagerEvents::VISIBLE, a ); + else + implts_notifyListeners( frame::LayoutManagerEvents::INVISIBLE, a ); + + SolarMutexResettableGuard aWriteLock; + rtl::Reference< MenuBarWrapper > xMenuBar = m_xMenuBar; + Reference< awt::XWindow > xContainerWindow( m_xContainerWindow ); + rtl::Reference< MenuBarManager > xInplaceMenuBar( m_xInplaceMenuBar ); + aWriteLock.clear(); + + if (( xMenuBar.is() || xInplaceMenuBar.is() ) && xContainerWindow.is() ) + { + SolarMutexGuard aGuard; + + MenuBar* pMenuBar( nullptr ); + if ( xInplaceMenuBar.is() ) + pMenuBar = static_cast(xInplaceMenuBar->GetMenuBar()); + else + { + pMenuBar = static_cast(xMenuBar->GetMenuBarManager()->GetMenuBar()); + } + + SystemWindow* pSysWindow = getTopSystemWindow( xContainerWindow ); + if ( pSysWindow ) + { + if ( bSetVisible ) + { + pSysWindow->SetMenuBar(pMenuBar); + } + else + pSysWindow->SetMenuBar( nullptr ); + } + } + + bool bMustDoLayout; + // Hide/show the statusbar according to bSetVisible + if ( bSetVisible ) + bMustDoLayout = !implts_showStatusBar(); + else + bMustDoLayout = !implts_hideStatusBar(); + + aWriteLock.reset(); + ToolbarLayoutManager* pToolbarManager( m_xToolbarManager.get() ); + aWriteLock.clear(); + + if ( pToolbarManager ) + { + pToolbarManager->setVisible( bSetVisible ); + bMustDoLayout = pToolbarManager->isLayoutDirty(); + } + + if ( bMustDoLayout ) + implts_doLayout_notify( false ); +} + +void LayoutManager::implts_setCurrentUIVisibility( bool bShow ) +{ + { + SolarMutexGuard aWriteLock; + if (!bShow && m_aStatusBarElement.m_bVisible && m_aStatusBarElement.m_xUIElement.is()) + m_aStatusBarElement.m_bMasterHide = true; + else if (bShow && m_aStatusBarElement.m_bVisible) + m_aStatusBarElement.m_bMasterHide = false; + } + + implts_updateUIElementsVisibleState( bShow ); +} + +void LayoutManager::implts_destroyStatusBar() +{ + Reference< XComponent > xCompStatusBar; + + SolarMutexClearableGuard aWriteLock; + m_aStatusBarElement.m_aName.clear(); + xCompStatusBar.set( m_aStatusBarElement.m_xUIElement, UNO_QUERY ); + m_aStatusBarElement.m_xUIElement.clear(); + aWriteLock.clear(); + + if ( xCompStatusBar.is() ) + xCompStatusBar->dispose(); + + implts_destroyProgressBar(); +} + +void LayoutManager::implts_createStatusBar( const OUString& aStatusBarName ) +{ + { + SolarMutexGuard aWriteLock; + if (!m_aStatusBarElement.m_xUIElement.is()) + { + implts_readStatusBarState(aStatusBarName); + m_aStatusBarElement.m_aName = aStatusBarName; + m_aStatusBarElement.m_xUIElement = implts_createElement(aStatusBarName); + } + } + + implts_createProgressBar(); +} + +void LayoutManager::implts_readStatusBarState( const OUString& rStatusBarName ) +{ + SolarMutexGuard g; + if ( !m_aStatusBarElement.m_bStateRead ) + { + // Read persistent data for status bar if not yet read! + if ( implts_readWindowStateData( rStatusBarName, m_aStatusBarElement )) + m_aStatusBarElement.m_bStateRead = true; + } +} + +void LayoutManager::implts_createProgressBar() +{ + Reference< XUIElement > xStatusBar; + Reference< XUIElement > xProgressBar; + rtl::Reference< ProgressBarWrapper > xProgressBarBackup; + Reference< awt::XWindow > xContainerWindow; + + SolarMutexResettableGuard aWriteLock; + xStatusBar = m_aStatusBarElement.m_xUIElement; + xProgressBar = m_aProgressBarElement.m_xUIElement; + xProgressBarBackup = m_xProgressBarBackup; + m_xProgressBarBackup.clear(); + xContainerWindow = m_xContainerWindow; + aWriteLock.clear(); + + bool bRecycled = xProgressBarBackup.is(); + rtl::Reference pWrapper; + if ( bRecycled ) + pWrapper = xProgressBarBackup.get(); + else if ( xProgressBar.is() ) + pWrapper = static_cast(xProgressBar.get()); + else + pWrapper = new ProgressBarWrapper(); + + if ( xStatusBar.is() ) + { + Reference< awt::XWindow > xWindow( xStatusBar->getRealInterface(), UNO_QUERY ); + pWrapper->setStatusBar( xWindow ); + } + else + { + Reference< awt::XWindow > xStatusBarWindow = pWrapper->getStatusBar(); + + SolarMutexGuard aGuard; + VclPtr pStatusBarWnd = VCLUnoHelper::GetWindow( xStatusBarWindow ); + if ( !pStatusBarWnd ) + { + VclPtr pWindow = VCLUnoHelper::GetWindow( xContainerWindow ); + if ( pWindow ) + { + VclPtrInstance pStatusBar( pWindow, WinBits( WB_LEFT | WB_3DLOOK ) ); + Reference< awt::XWindow > xStatusBarWindow2( VCLUnoHelper::GetInterface( pStatusBar )); + pWrapper->setStatusBar( xStatusBarWindow2, true ); + } + } + } + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + aWriteLock.reset(); + m_aProgressBarElement.m_xUIElement = pWrapper; + aWriteLock.clear(); + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + + if ( bRecycled ) + implts_showProgressBar(); +} + +void LayoutManager::implts_backupProgressBarWrapper() +{ + SolarMutexGuard g; + + if (m_xProgressBarBackup.is()) + return; + + // safe a backup copy of the current progress! + // This copy will be used automatically inside createProgressBar() which is called + // implicitly from implts_doLayout() .-) + m_xProgressBarBackup = static_cast(m_aProgressBarElement.m_xUIElement.get()); + + // remove the relation between this old progress bar and our old status bar. + // Otherwise we work on disposed items ... + // The internal used ProgressBarWrapper can handle a NULL reference. + if ( m_xProgressBarBackup.is() ) + m_xProgressBarBackup->setStatusBar( Reference< awt::XWindow >() ); + + // prevent us from dispose() the m_aProgressBarElement.m_xUIElement inside implts_reset() + m_aProgressBarElement.m_xUIElement.clear(); +} + +void LayoutManager::implts_destroyProgressBar() +{ + // don't remove the progressbar in general + // We must reuse it if a new status bar is created later. + // Of course there exists one backup only. + // And further this backup will be released inside our dtor. + implts_backupProgressBarWrapper(); +} + +void LayoutManager::implts_setStatusBarPosSize( const ::Point& rPos, const ::Size& rSize ) +{ + Reference< XUIElement > xStatusBar; + Reference< XUIElement > xProgressBar; + Reference< awt::XWindow > xContainerWindow; + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + SolarMutexClearableGuard aReadLock; + xStatusBar = m_aStatusBarElement.m_xUIElement; + xProgressBar = m_aProgressBarElement.m_xUIElement; + xContainerWindow = m_xContainerWindow; + + Reference< awt::XWindow > xWindow; + if ( xStatusBar.is() ) + xWindow.set( xStatusBar->getRealInterface(), UNO_QUERY ); + else if ( xProgressBar.is() ) + { + ProgressBarWrapper* pWrapper = static_cast(xProgressBar.get()); + if ( pWrapper ) + xWindow = pWrapper->getStatusBar(); + } + aReadLock.clear(); + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + + if ( !xWindow.is() ) + return; + + SolarMutexGuard aGuard; + VclPtr pParentWindow = VCLUnoHelper::GetWindow( xContainerWindow ); + VclPtr pWindow = VCLUnoHelper::GetWindow( xWindow ); + if ( pParentWindow && ( pWindow && pWindow->GetType() == WindowType::STATUSBAR )) + { + vcl::Window* pOldParentWindow = pWindow->GetParent(); + if ( pParentWindow != pOldParentWindow ) + pWindow->SetParent( pParentWindow ); + static_cast(pWindow.get())->SetPosSizePixel( rPos, rSize ); + } +} + +bool LayoutManager::implts_showProgressBar() +{ + Reference< XUIElement > xStatusBar; + Reference< XUIElement > xProgressBar; + Reference< awt::XWindow > xWindow; + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + SolarMutexGuard aWriteLock; + xStatusBar = m_aStatusBarElement.m_xUIElement; + xProgressBar = m_aProgressBarElement.m_xUIElement; + bool bVisible( m_bVisible ); + + m_aProgressBarElement.m_bVisible = true; + if ( bVisible ) + { + if ( xStatusBar.is() && !m_aStatusBarElement.m_bMasterHide ) + { + xWindow.set( xStatusBar->getRealInterface(), UNO_QUERY ); + } + else if ( xProgressBar.is() ) + { + ProgressBarWrapper* pWrapper = static_cast(xProgressBar.get()); + if ( pWrapper ) + xWindow = pWrapper->getStatusBar(); + } + } + + VclPtr pWindow = VCLUnoHelper::GetWindow( xWindow ); + if ( pWindow ) + { + if ( !pWindow->IsVisible() ) + { + implts_setOffset( pWindow->GetSizePixel().Height() ); + pWindow->Show(); + implts_doLayout_notify( false ); + } + return true; + } + + return false; +} + +bool LayoutManager::implts_hideProgressBar() +{ + Reference< XUIElement > xProgressBar; + Reference< awt::XWindow > xWindow; + bool bHideStatusBar( false ); + + SolarMutexGuard g; + xProgressBar = m_aProgressBarElement.m_xUIElement; + + bool bInternalStatusBar( false ); + if ( xProgressBar.is() ) + { + Reference< awt::XWindow > xStatusBar; + ProgressBarWrapper* pWrapper = static_cast(xProgressBar.get()); + if ( pWrapper ) + xWindow = pWrapper->getStatusBar(); + Reference< ui::XUIElement > xStatusBarElement = m_aStatusBarElement.m_xUIElement; + if ( xStatusBarElement.is() ) + xStatusBar.set( xStatusBarElement->getRealInterface(), UNO_QUERY ); + bInternalStatusBar = xStatusBar != xWindow; + } + m_aProgressBarElement.m_bVisible = false; + implts_readStatusBarState( STATUS_BAR_ALIAS ); + bHideStatusBar = !m_aStatusBarElement.m_bVisible; + + VclPtr pWindow = VCLUnoHelper::GetWindow( xWindow ); + if ( pWindow && pWindow->IsVisible() && ( bHideStatusBar || bInternalStatusBar )) + { + implts_setOffset( 0 ); + pWindow->Hide(); + implts_doLayout_notify( false ); + return true; + } + + return false; +} + +bool LayoutManager::implts_showStatusBar( bool bStoreState ) +{ + SolarMutexClearableGuard aWriteLock; + Reference< ui::XUIElement > xStatusBar = m_aStatusBarElement.m_xUIElement; + if ( bStoreState ) + m_aStatusBarElement.m_bVisible = true; + aWriteLock.clear(); + + if ( xStatusBar.is() ) + { + Reference< awt::XWindow > xWindow( xStatusBar->getRealInterface(), UNO_QUERY ); + + SolarMutexGuard aGuard; + VclPtr pWindow = VCLUnoHelper::GetWindow( xWindow ); + if ( pWindow && !pWindow->IsVisible() ) + { + implts_setOffset( pWindow->GetSizePixel().Height() ); + pWindow->Show(); + implts_doLayout_notify( false ); + return true; + } + } + + return false; +} + +bool LayoutManager::implts_hideStatusBar( bool bStoreState ) +{ + SolarMutexClearableGuard aWriteLock; + Reference< ui::XUIElement > xStatusBar = m_aStatusBarElement.m_xUIElement; + if ( bStoreState ) + m_aStatusBarElement.m_bVisible = false; + aWriteLock.clear(); + + if ( xStatusBar.is() ) + { + Reference< awt::XWindow > xWindow( xStatusBar->getRealInterface(), UNO_QUERY ); + + SolarMutexGuard aGuard; + VclPtr pWindow = VCLUnoHelper::GetWindow( xWindow ); + if ( pWindow && pWindow->IsVisible() ) + { + implts_setOffset( 0 ); + pWindow->Hide(); + implts_doLayout_notify( false ); + return true; + } + } + + return false; +} + +void LayoutManager::implts_setOffset( const sal_Int32 nBottomOffset ) +{ + if ( m_xToolbarManager.is() ) + m_xToolbarManager->setDockingAreaOffsets({ 0, 0, 0, nBottomOffset }); +} + +void LayoutManager::implts_setInplaceMenuBar( const Reference< XIndexAccess >& xMergedMenuBar ) +{ + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + SolarMutexClearableGuard aWriteLock; + + if ( m_bInplaceMenuSet ) + return; + + SolarMutexGuard aGuard; + + // Reset old inplace menubar! + VclPtr pOldMenuBar; + if (m_xInplaceMenuBar.is()) + { + pOldMenuBar = m_xInplaceMenuBar->GetMenuBar(); + m_xInplaceMenuBar->dispose(); + m_xInplaceMenuBar.clear(); + } + pOldMenuBar.disposeAndClear(); + + m_bInplaceMenuSet = false; + + if ( m_xFrame.is() && m_xContainerWindow.is() ) + { + Reference< XDispatchProvider > xDispatchProvider; + + VclPtr pMenuBar = VclPtr::Create(); + m_xInplaceMenuBar = new MenuBarManager( m_xContext, m_xFrame, m_xURLTransformer, xDispatchProvider, OUString(), pMenuBar, true ); + m_xInplaceMenuBar->SetItemContainer( xMergedMenuBar ); + + SystemWindow* pSysWindow = getTopSystemWindow( m_xContainerWindow ); + if ( pSysWindow ) + pSysWindow->SetMenuBar(pMenuBar); + + m_bInplaceMenuSet = true; + } + + aWriteLock.clear(); + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + + implts_updateMenuBarClose(); +} + +void LayoutManager::implts_resetInplaceMenuBar() +{ + SolarMutexGuard g; + m_bInplaceMenuSet = false; + + if ( m_xContainerWindow.is() ) + { + SolarMutexGuard aGuard; + SystemWindow* pSysWindow = getTopSystemWindow( m_xContainerWindow ); + if ( pSysWindow ) + { + if ( m_xMenuBar ) + pSysWindow->SetMenuBar(static_cast(m_xMenuBar->GetMenuBarManager()->GetMenuBar())); + else + pSysWindow->SetMenuBar(nullptr); + } + } + + // Remove inplace menu bar + VclPtr pMenuBar; + if (m_xInplaceMenuBar.is()) + { + pMenuBar = m_xInplaceMenuBar->GetMenuBar(); + m_xInplaceMenuBar->dispose(); + m_xInplaceMenuBar.clear(); + } + pMenuBar.disposeAndClear(); +} + +void SAL_CALL LayoutManager::attachFrame( const Reference< XFrame >& xFrame ) +{ + SolarMutexGuard g; + m_xFrame = xFrame; +} + +void SAL_CALL LayoutManager::reset() +{ + implts_reset( true ); +} + +// XMenuBarMergingAcceptor + +sal_Bool SAL_CALL LayoutManager::setMergedMenuBar( + const Reference< XIndexAccess >& xMergedMenuBar ) +{ + implts_setInplaceMenuBar( xMergedMenuBar ); + + uno::Any a; + implts_notifyListeners( frame::LayoutManagerEvents::MERGEDMENUBAR, a ); + return true; +} + +void SAL_CALL LayoutManager::removeMergedMenuBar() +{ + implts_resetInplaceMenuBar(); +} + +awt::Rectangle SAL_CALL LayoutManager::getCurrentDockingArea() +{ + SolarMutexGuard g; + return m_aDockingArea; +} + +Reference< XDockingAreaAcceptor > SAL_CALL LayoutManager::getDockingAreaAcceptor() +{ + SolarMutexGuard g; + return m_xDockingAreaAcceptor; +} + +void SAL_CALL LayoutManager::setDockingAreaAcceptor( const Reference< ui::XDockingAreaAcceptor >& xDockingAreaAcceptor ) +{ + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + SolarMutexClearableGuard aWriteLock; + + if (( m_xDockingAreaAcceptor == xDockingAreaAcceptor ) || !m_xFrame.is() ) + return; + + // IMPORTANT: Be sure to stop layout timer if don't have a docking area acceptor! + if ( !xDockingAreaAcceptor.is() ) + m_aAsyncLayoutTimer.Stop(); + + bool bAutomaticToolbars( m_bAutomaticToolbars ); + + ToolbarLayoutManager* pToolbarManager = m_xToolbarManager.get(); + + if ( !xDockingAreaAcceptor.is() ) + m_aAsyncLayoutTimer.Stop(); + + // Remove listener from old docking area acceptor + if ( m_xDockingAreaAcceptor.is() ) + { + Reference< awt::XWindow > xWindow( m_xDockingAreaAcceptor->getContainerWindow() ); + if ( xWindow.is() && ( m_xFrame->getContainerWindow() != m_xContainerWindow || !xDockingAreaAcceptor.is() ) ) + xWindow->removeWindowListener( Reference< awt::XWindowListener >(this) ); + + m_aDockingArea = awt::Rectangle(); + if ( pToolbarManager ) + pToolbarManager->resetDockingArea(); + + VclPtr pContainerWindow = VCLUnoHelper::GetWindow( xWindow ); + if ( pContainerWindow ) + pContainerWindow->RemoveChildEventListener( LINK( this, LayoutManager, WindowEventListener ) ); + } + + m_xDockingAreaAcceptor = xDockingAreaAcceptor; + if ( m_xDockingAreaAcceptor.is() ) + { + m_aDockingArea = awt::Rectangle(); + m_xContainerWindow = m_xDockingAreaAcceptor->getContainerWindow(); + m_xContainerTopWindow.set( m_xContainerWindow, UNO_QUERY ); + m_xContainerWindow->addWindowListener( Reference< awt::XWindowListener >(this) ); + + // we always must keep a connection to the window of our frame for resize events + if ( m_xContainerWindow != m_xFrame->getContainerWindow() ) + m_xFrame->getContainerWindow()->addWindowListener( Reference< awt::XWindowListener >(this) ); + + // #i37884# set initial visibility state - in the plugin case the container window is already shown + // and we get no notification anymore + { + VclPtr pContainerWindow = VCLUnoHelper::GetWindow( m_xContainerWindow ); + if( pContainerWindow ) + m_bParentWindowVisible = pContainerWindow->IsVisible(); + } + } + + aWriteLock.clear(); + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + + if ( xDockingAreaAcceptor.is() ) + { + SolarMutexGuard aGuard; + + // Add layout manager as listener to get notifications about toolbar button activities + VclPtr pContainerWindow = VCLUnoHelper::GetWindow( m_xContainerWindow ); + if ( pContainerWindow ) + pContainerWindow->AddChildEventListener( LINK( this, LayoutManager, WindowEventListener ) ); + + // We have now a new container window, reparent all child windows! + implts_reparentChildWindows(); + } + else + implts_destroyElements(); // remove all elements + + if ( pToolbarManager && xDockingAreaAcceptor.is() ) + { + if ( bAutomaticToolbars ) + { + lock(); + pToolbarManager->createStaticToolbars(); + unlock(); + } + implts_doLayout( true, false ); + } +} + +void LayoutManager::implts_reparentChildWindows() +{ + SolarMutexResettableGuard aWriteLock; + UIElement aStatusBarElement = m_aStatusBarElement; + uno::Reference< awt::XWindow > xContainerWindow = m_xContainerWindow; + aWriteLock.clear(); + + uno::Reference< awt::XWindow > xStatusBarWindow; + if ( aStatusBarElement.m_xUIElement.is() ) + { + try + { + xStatusBarWindow.set( aStatusBarElement.m_xUIElement->getRealInterface(), UNO_QUERY ); + } + catch (const RuntimeException&) + { + throw; + } + catch (const Exception&) + { + } + } + + if ( xStatusBarWindow.is() ) + { + SolarMutexGuard aGuard; + VclPtr pContainerWindow = VCLUnoHelper::GetWindow( xContainerWindow ); + VclPtr pWindow = VCLUnoHelper::GetWindow( xStatusBarWindow ); + if ( pWindow && pContainerWindow ) + pWindow->SetParent( pContainerWindow ); + } + + implts_resetMenuBar(); + + aWriteLock.reset(); + ToolbarLayoutManager* pToolbarManager = m_xToolbarManager.get(); + if ( pToolbarManager ) + pToolbarManager->setParentWindow( uno::Reference< awt::XVclWindowPeer >( xContainerWindow, uno::UNO_QUERY )); + aWriteLock.clear(); +} + +uno::Reference< ui::XUIElement > LayoutManager::implts_createDockingWindow( const OUString& aElementName ) +{ + Reference< XUIElement > xUIElement = implts_createElement( aElementName ); + return xUIElement; +} + +IMPL_LINK( LayoutManager, WindowEventListener, VclWindowEvent&, rEvent, void ) +{ + vcl::Window* pWindow = rEvent.GetWindow(); + if ( pWindow && pWindow->GetType() == WindowType::TOOLBOX ) + { + SolarMutexClearableGuard aReadLock; + ToolbarLayoutManager* pToolbarManager( m_xToolbarManager.get() ); + aReadLock.clear(); + + if ( pToolbarManager ) + pToolbarManager->childWindowEvent( &rEvent ); + } +} + +void SAL_CALL LayoutManager::createElement( const OUString& aName ) +{ + SAL_INFO( "fwk", "LayoutManager::createElement " << aName ); + + SolarMutexClearableGuard aReadLock; + Reference< XFrame > xFrame = m_xFrame; + aReadLock.clear(); + + if ( !xFrame.is() ) + return; + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + SolarMutexClearableGuard aWriteLock; + + bool bMustBeLayouted( false ); + bool bNotify( false ); + + bool bPreviewFrame; + if (m_xToolbarManager.is()) + // Assumes that we created the ToolbarLayoutManager with our frame, if + // not then we're somewhat fouled up ... + bPreviewFrame = m_xToolbarManager->isPreviewFrame(); + else + { + Reference< XModel > xModel( impl_getModelFromFrame( xFrame ) ); + bPreviewFrame = implts_isPreviewModel( xModel ); + } + + if ( m_xContainerWindow.is() && !bPreviewFrame ) // no UI elements on preview frames + { + OUString aElementType; + OUString aElementName; + + parseResourceURL( aName, aElementType, aElementName ); + + if ( aElementType.equalsIgnoreAsciiCase( UIRESOURCETYPE_TOOLBAR ) && m_xToolbarManager.is() ) + { + bNotify = m_xToolbarManager->createToolbar( aName ); + bMustBeLayouted = m_xToolbarManager->isLayoutDirty(); + } + else if ( aElementType.equalsIgnoreAsciiCase("menubar") && + aElementName.equalsIgnoreAsciiCase("menubar") && + implts_isFrameOrWindowTop(xFrame) ) + { + implts_createMenuBar( aName ); + if (m_bMenuVisible) + bNotify = true; + + aWriteLock.clear(); + } + else if ( aElementType.equalsIgnoreAsciiCase("statusbar") && + ( implts_isFrameOrWindowTop(xFrame) || implts_isEmbeddedLayoutManager() )) + { + implts_createStatusBar( aName ); + bNotify = true; + } + else if ( aElementType.equalsIgnoreAsciiCase("progressbar") && + aElementName.equalsIgnoreAsciiCase("progressbar") && + implts_isFrameOrWindowTop(xFrame) ) + { + implts_createProgressBar(); + bNotify = true; + } + else if ( aElementType.equalsIgnoreAsciiCase("dockingwindow")) + { + // Add layout manager as listener for docking and other window events + uno::Reference< uno::XInterface > xThis( static_cast< OWeakObject* >(this), uno::UNO_QUERY ); + uno::Reference< ui::XUIElement > xUIElement( implts_createDockingWindow( aName )); + + if ( xUIElement.is() ) + { + impl_addWindowListeners( xThis, xUIElement ); + } + + // The docking window is created by a factory method located in the sfx2 library. +// CreateDockingWindow( xFrame, aElementName ); + } + } + + if ( bMustBeLayouted ) + implts_doLayout_notify( true ); + + if ( bNotify ) + { + // UI element is invisible - provide information to listeners + implts_notifyListeners( frame::LayoutManagerEvents::UIELEMENT_VISIBLE, uno::Any( aName ) ); + } +} + +void SAL_CALL LayoutManager::destroyElement( const OUString& aName ) +{ + SAL_INFO( "fwk", "LayoutManager::destroyElement " << aName ); + + bool bMustBeLayouted(false); + bool bNotify(false); + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + { + SolarMutexClearableGuard aWriteLock; + + OUString aElementType; + OUString aElementName; + + parseResourceURL(aName, aElementType, aElementName); + + if (aElementType.equalsIgnoreAsciiCase("menubar") + && aElementName.equalsIgnoreAsciiCase("menubar")) + { + if (!m_bInplaceMenuSet) + { + impl_clearUpMenuBar(); + m_xMenuBar.clear(); + bNotify = true; + } + } + else if ((aElementType.equalsIgnoreAsciiCase("statusbar") + && aElementName.equalsIgnoreAsciiCase("statusbar")) + || (m_aStatusBarElement.m_aName == aName)) + { + aWriteLock.clear(); + implts_destroyStatusBar(); + bMustBeLayouted = true; + bNotify = true; + } + else if (aElementType.equalsIgnoreAsciiCase("progressbar") + && aElementName.equalsIgnoreAsciiCase("progressbar")) + { + aWriteLock.clear(); + implts_createProgressBar(); + bMustBeLayouted = true; + bNotify = true; + } + else if (aElementType.equalsIgnoreAsciiCase(UIRESOURCETYPE_TOOLBAR) + && m_xToolbarManager.is()) + { + aWriteLock.clear(); + bNotify = m_xToolbarManager->destroyToolbar(aName); + bMustBeLayouted = m_xToolbarManager->isLayoutDirty(); + } + else if (aElementType.equalsIgnoreAsciiCase("dockingwindow")) + { + uno::Reference xFrame(m_xFrame); + uno::Reference xContext(m_xContext); + aWriteLock.clear(); + + impl_setDockingWindowVisibility(xContext, xFrame, aElementName, false); + bMustBeLayouted = false; + bNotify = false; + } + } + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + + if ( bMustBeLayouted ) + doLayout(); + + if ( bNotify ) + implts_notifyListeners( frame::LayoutManagerEvents::UIELEMENT_INVISIBLE, uno::Any( aName ) ); +} + +sal_Bool SAL_CALL LayoutManager::requestElement( const OUString& rResourceURL ) +{ + bool bResult( false ); + bool bNotify( false ); + OUString aElementType; + OUString aElementName; + + parseResourceURL( rResourceURL, aElementType, aElementName ); + + SolarMutexClearableGuard aWriteLock; + + OString aResName = OUStringToOString( aElementName, RTL_TEXTENCODING_ASCII_US ); + SAL_INFO( "fwk", "LayoutManager::requestElement " << aResName ); + + if (( aElementType.equalsIgnoreAsciiCase("statusbar") && + aElementName.equalsIgnoreAsciiCase("statusbar") ) || + ( m_aStatusBarElement.m_aName == rResourceURL )) + { + implts_readStatusBarState( rResourceURL ); + if ( m_aStatusBarElement.m_bVisible && !m_aStatusBarElement.m_bMasterHide ) + { + aWriteLock.clear(); + createElement( rResourceURL ); + + // There are some situation where we are not able to create an element. + // Therefore we have to check the reference before further action. + // See #i70019# + uno::Reference< ui::XUIElement > xUIElement( m_aStatusBarElement.m_xUIElement ); + if ( xUIElement.is() ) + { + // we need VCL here to pass special flags to Show() + SolarMutexGuard aGuard; + Reference< awt::XWindow > xWindow( xUIElement->getRealInterface(), UNO_QUERY ); + VclPtr pWindow = VCLUnoHelper::GetWindow( xWindow ); + if ( pWindow ) + { + pWindow->Show( true, ShowFlags::NoFocusChange | ShowFlags::NoActivate ); + bResult = true; + bNotify = true; + } + } + } + } + else if ( aElementType.equalsIgnoreAsciiCase("progressbar") && + aElementName.equalsIgnoreAsciiCase("progressbar") ) + { + aWriteLock.clear(); + implts_showProgressBar(); + bResult = true; + bNotify = true; + } + else if ( aElementType.equalsIgnoreAsciiCase( UIRESOURCETYPE_TOOLBAR ) && m_bVisible ) + { + bool bComponentAttached( !m_aModuleIdentifier.isEmpty() ); + ToolbarLayoutManager* pToolbarManager = m_xToolbarManager.get(); + aWriteLock.clear(); + + if ( pToolbarManager && bComponentAttached ) + { + bNotify = pToolbarManager->requestToolbar( rResourceURL ); + } + } + else if ( aElementType.equalsIgnoreAsciiCase("dockingwindow")) + { + uno::Reference< frame::XFrame > xFrame( m_xFrame ); + aWriteLock.clear(); + + CreateDockingWindow( xFrame, aElementName ); + } + + if ( bNotify ) + implts_notifyListeners( frame::LayoutManagerEvents::UIELEMENT_VISIBLE, uno::Any( rResourceURL ) ); + + return bResult; +} + +Reference< XUIElement > SAL_CALL LayoutManager::getElement( const OUString& aName ) +{ + Reference< XUIElement > xUIElement = implts_findElement( aName ); + if ( !xUIElement.is() ) + { + SolarMutexClearableGuard aReadLock; + ToolbarLayoutManager* pToolbarManager( m_xToolbarManager.get() ); + aReadLock.clear(); + + if ( pToolbarManager ) + xUIElement = pToolbarManager->getToolbar( aName ); + } + + return xUIElement; +} + +Sequence< Reference< ui::XUIElement > > SAL_CALL LayoutManager::getElements() +{ + SolarMutexClearableGuard aReadLock; + rtl::Reference< MenuBarWrapper > xMenuBar( m_xMenuBar ); + uno::Reference< ui::XUIElement > xStatusBar( m_aStatusBarElement.m_xUIElement ); + ToolbarLayoutManager* pToolbarManager( m_xToolbarManager.get() ); + aReadLock.clear(); + + Sequence< Reference< ui::XUIElement > > aSeq; + if ( pToolbarManager ) + aSeq = pToolbarManager->getToolbars(); + + sal_Int32 nSize = aSeq.getLength(); + sal_Int32 nMenuBarIndex(-1); + sal_Int32 nStatusBarIndex(-1); + if ( xMenuBar.is() ) + { + nMenuBarIndex = nSize; + ++nSize; + } + if ( xStatusBar.is() ) + { + nStatusBarIndex = nSize; + ++nSize; + } + + aSeq.realloc(nSize); + auto pSeq = aSeq.getArray(); + if ( nMenuBarIndex >= 0 ) + pSeq[nMenuBarIndex] = xMenuBar; + if ( nStatusBarIndex >= 0 ) + pSeq[nStatusBarIndex] = xStatusBar; + + return aSeq; +} + +sal_Bool SAL_CALL LayoutManager::showElement( const OUString& aName ) +{ + bool bResult( false ); + bool bNotify( false ); + bool bMustLayout( false ); + OUString aElementType; + OUString aElementName; + + parseResourceURL( aName, aElementType, aElementName ); + + OString aResName = OUStringToOString( aElementName, RTL_TEXTENCODING_ASCII_US ); + SAL_INFO( "fwk", "LayoutManager::showElement " << aResName ); + + if ( aElementType.equalsIgnoreAsciiCase("menubar") && + aElementName.equalsIgnoreAsciiCase("menubar") ) + { + { + SolarMutexGuard aWriteLock; + m_bMenuVisible = true; + } + + bResult = implts_resetMenuBar(); + bNotify = bResult; + } + else if (( aElementType.equalsIgnoreAsciiCase("statusbar") && + aElementName.equalsIgnoreAsciiCase("statusbar") ) || + ( m_aStatusBarElement.m_aName == aName )) + { + SolarMutexClearableGuard aWriteLock; + if ( m_aStatusBarElement.m_xUIElement.is() && !m_aStatusBarElement.m_bMasterHide && + implts_showStatusBar( true )) + { + aWriteLock.clear(); + + implts_writeWindowStateData( STATUS_BAR_ALIAS, m_aStatusBarElement ); + bMustLayout = true; + bResult = true; + bNotify = true; + } + } + else if ( aElementType.equalsIgnoreAsciiCase("progressbar") && + aElementName.equalsIgnoreAsciiCase("progressbar") ) + { + bNotify = bResult = implts_showProgressBar(); + } + else if ( aElementType.equalsIgnoreAsciiCase( UIRESOURCETYPE_TOOLBAR )) + { + SolarMutexClearableGuard aReadLock; + ToolbarLayoutManager* pToolbarManager = m_xToolbarManager.get(); + aReadLock.clear(); + + if ( pToolbarManager ) + { + bNotify = pToolbarManager->showToolbar( aName ); + bMustLayout = pToolbarManager->isLayoutDirty(); + } + } + else if ( aElementType.equalsIgnoreAsciiCase("dockingwindow")) + { + SolarMutexClearableGuard aReadGuard; + uno::Reference< frame::XFrame > xFrame( m_xFrame ); + uno::Reference< XComponentContext > xContext( m_xContext ); + aReadGuard.clear(); + + impl_setDockingWindowVisibility( xContext, xFrame, aElementName, true ); + } + + if ( bMustLayout ) + doLayout(); + + if ( bNotify ) + implts_notifyListeners( frame::LayoutManagerEvents::UIELEMENT_VISIBLE, uno::Any( aName ) ); + + return bResult; +} + +sal_Bool SAL_CALL LayoutManager::hideElement( const OUString& aName ) +{ + bool bNotify( false ); + bool bMustLayout( false ); + OUString aElementType; + OUString aElementName; + + parseResourceURL( aName, aElementType, aElementName ); + OString aResName = OUStringToOString( aElementName, RTL_TEXTENCODING_ASCII_US ); + SAL_INFO( "fwk", "LayoutManager::hideElement " << aResName ); + + if ( aElementType.equalsIgnoreAsciiCase("menubar") && + aElementName.equalsIgnoreAsciiCase("menubar") ) + { + SolarMutexGuard g; + + if ( m_xContainerWindow.is() ) + { + m_bMenuVisible = false; + + SolarMutexGuard aGuard; + SystemWindow* pSysWindow = getTopSystemWindow( m_xContainerWindow ); + if ( pSysWindow ) + { + MenuBar* pMenuBar = pSysWindow->GetMenuBar(); + if ( pMenuBar ) + { + pMenuBar->SetDisplayable( false ); + bNotify = true; + } + } + } + } + else if (( aElementType.equalsIgnoreAsciiCase("statusbar") && + aElementName.equalsIgnoreAsciiCase("statusbar") ) || + ( m_aStatusBarElement.m_aName == aName )) + { + SolarMutexGuard g; + if ( m_aStatusBarElement.m_xUIElement.is() && !m_aStatusBarElement.m_bMasterHide && + implts_hideStatusBar( true )) + { + implts_writeWindowStateData( STATUS_BAR_ALIAS, m_aStatusBarElement ); + bMustLayout = true; + bNotify = true; + } + } + else if ( aElementType.equalsIgnoreAsciiCase("progressbar") && + aElementName.equalsIgnoreAsciiCase("progressbar") ) + { + bNotify = implts_hideProgressBar(); + } + else if ( aElementType.equalsIgnoreAsciiCase( UIRESOURCETYPE_TOOLBAR )) + { + SolarMutexClearableGuard aReadLock; + ToolbarLayoutManager* pToolbarManager = m_xToolbarManager.get(); + aReadLock.clear(); + + if ( pToolbarManager ) + { + bNotify = pToolbarManager->hideToolbar( aName ); + bMustLayout = pToolbarManager->isLayoutDirty(); + } + } + else if ( aElementType.equalsIgnoreAsciiCase("dockingwindow")) + { + SolarMutexClearableGuard aReadGuard; + uno::Reference< frame::XFrame > xFrame( m_xFrame ); + uno::Reference< XComponentContext > xContext( m_xContext ); + aReadGuard.clear(); + + impl_setDockingWindowVisibility( xContext, xFrame, aElementName, false ); + } + + if ( bMustLayout ) + doLayout(); + + if ( bNotify ) + implts_notifyListeners( frame::LayoutManagerEvents::UIELEMENT_INVISIBLE, uno::Any( aName ) ); + + return false; +} + +sal_Bool SAL_CALL LayoutManager::dockWindow( const OUString& aName, DockingArea DockingArea, const awt::Point& Pos ) +{ + OUString aElementType; + OUString aElementName; + + parseResourceURL( aName, aElementType, aElementName ); + if ( aElementType.equalsIgnoreAsciiCase( UIRESOURCETYPE_TOOLBAR )) + { + SolarMutexClearableGuard aReadLock; + ToolbarLayoutManager* pToolbarManager = m_xToolbarManager.get(); + aReadLock.clear(); + + if ( pToolbarManager ) + { + pToolbarManager->dockToolbar( aName, DockingArea, Pos ); + if ( pToolbarManager->isLayoutDirty() ) + doLayout(); + } + } + return false; +} + +sal_Bool SAL_CALL LayoutManager::dockAllWindows( ::sal_Int16 /*nElementType*/ ) +{ + SolarMutexClearableGuard aReadLock; + bool bResult( false ); + ToolbarLayoutManager* pToolbarManager = m_xToolbarManager.get(); + aReadLock.clear(); + + if ( pToolbarManager ) + { + bResult = pToolbarManager->dockAllToolbars(); + if ( pToolbarManager->isLayoutDirty() ) + doLayout(); + } + return bResult; +} + +sal_Bool SAL_CALL LayoutManager::floatWindow( const OUString& aName ) +{ + bool bResult( false ); + if ( o3tl::equalsIgnoreAsciiCase(getElementTypeFromResourceURL( aName ), UIRESOURCETYPE_TOOLBAR )) + { + SolarMutexClearableGuard aReadLock; + ToolbarLayoutManager* pToolbarManager = m_xToolbarManager.get(); + aReadLock.clear(); + + if ( pToolbarManager ) + { + bResult = pToolbarManager->floatToolbar( aName ); + if ( pToolbarManager->isLayoutDirty() ) + doLayout(); + } + } + return bResult; +} + +sal_Bool SAL_CALL LayoutManager::lockWindow( const OUString& aName ) +{ + bool bResult( false ); + if ( o3tl::equalsIgnoreAsciiCase(getElementTypeFromResourceURL( aName ), UIRESOURCETYPE_TOOLBAR )) + { + SolarMutexClearableGuard aReadLock; + ToolbarLayoutManager* pToolbarManager = m_xToolbarManager.get(); + aReadLock.clear(); + + if ( pToolbarManager ) + { + bResult = pToolbarManager->lockToolbar( aName ); + if ( pToolbarManager->isLayoutDirty() ) + doLayout(); + } + } + return bResult; +} + +sal_Bool SAL_CALL LayoutManager::unlockWindow( const OUString& aName ) +{ + bool bResult( false ); + if ( o3tl::equalsIgnoreAsciiCase(getElementTypeFromResourceURL( aName ), UIRESOURCETYPE_TOOLBAR )) + { + SolarMutexClearableGuard aReadLock; + ToolbarLayoutManager* pToolbarManager = m_xToolbarManager.get(); + aReadLock.clear(); + + if ( pToolbarManager ) + { + bResult = pToolbarManager->unlockToolbar( aName ); + if ( pToolbarManager->isLayoutDirty() ) + doLayout(); + } + } + return bResult; +} + +void SAL_CALL LayoutManager::setElementSize( const OUString& aName, const awt::Size& aSize ) +{ + if ( !o3tl::equalsIgnoreAsciiCase(getElementTypeFromResourceURL( aName ), UIRESOURCETYPE_TOOLBAR )) + return; + + SolarMutexClearableGuard aReadLock; + ToolbarLayoutManager* pToolbarManager = m_xToolbarManager.get(); + aReadLock.clear(); + + if ( pToolbarManager ) + { + pToolbarManager->setToolbarSize( aName, aSize ); + if ( pToolbarManager->isLayoutDirty() ) + doLayout(); + } +} + +void SAL_CALL LayoutManager::setElementPos( const OUString& aName, const awt::Point& aPos ) +{ + if ( !o3tl::equalsIgnoreAsciiCase(getElementTypeFromResourceURL( aName ), UIRESOURCETYPE_TOOLBAR )) + return; + + SolarMutexClearableGuard aReadLock; + ToolbarLayoutManager* pToolbarManager( m_xToolbarManager.get() ); + aReadLock.clear(); + + if ( pToolbarManager ) + { + pToolbarManager->setToolbarPos( aName, aPos ); + if ( pToolbarManager->isLayoutDirty() ) + doLayout(); + } +} + +void SAL_CALL LayoutManager::setElementPosSize( const OUString& aName, const awt::Point& aPos, const awt::Size& aSize ) +{ + if ( !o3tl::equalsIgnoreAsciiCase(getElementTypeFromResourceURL( aName ), UIRESOURCETYPE_TOOLBAR )) + return; + + SolarMutexClearableGuard aReadLock; + ToolbarLayoutManager* pToolbarManager( m_xToolbarManager.get() ); + aReadLock.clear(); + + if ( pToolbarManager ) + { + pToolbarManager->setToolbarPosSize( aName, aPos, aSize ); + if ( pToolbarManager->isLayoutDirty() ) + doLayout(); + } +} + +sal_Bool SAL_CALL LayoutManager::isElementVisible( const OUString& aName ) +{ + OUString aElementType; + OUString aElementName; + + parseResourceURL( aName, aElementType, aElementName ); + if ( aElementType.equalsIgnoreAsciiCase("menubar") && + aElementName.equalsIgnoreAsciiCase("menubar") ) + { + SolarMutexResettableGuard aReadLock; + if ( m_xContainerWindow.is() ) + { + aReadLock.clear(); + + SolarMutexGuard aGuard; + SystemWindow* pSysWindow = getTopSystemWindow( m_xContainerWindow ); + if ( pSysWindow ) + { + MenuBar* pMenuBar = pSysWindow->GetMenuBar(); + if ( pMenuBar && pMenuBar->IsDisplayable() ) + return true; + } + else + { + aReadLock.reset(); + return m_bMenuVisible; + } + } + } + else if (( aElementType.equalsIgnoreAsciiCase("statusbar") && + aElementName.equalsIgnoreAsciiCase("statusbar") ) || + ( m_aStatusBarElement.m_aName == aName )) + { + if ( m_aStatusBarElement.m_xUIElement.is() ) + { + Reference< awt::XWindow > xWindow( m_aStatusBarElement.m_xUIElement->getRealInterface(), UNO_QUERY ); + if ( xWindow.is() ) + { + SolarMutexGuard g; + VclPtr pWindow = VCLUnoHelper::GetWindow( xWindow ); + if ( pWindow && pWindow->IsVisible() ) + return true; + else + return false; + } + } + } + else if ( aElementType.equalsIgnoreAsciiCase("progressbar") && + aElementName.equalsIgnoreAsciiCase("progressbar") ) + { + if ( m_aProgressBarElement.m_xUIElement.is() ) + return m_aProgressBarElement.m_bVisible; + } + else if ( aElementType.equalsIgnoreAsciiCase( UIRESOURCETYPE_TOOLBAR )) + { + SolarMutexClearableGuard aReadLock; + ToolbarLayoutManager* pToolbarManager = m_xToolbarManager.get(); + aReadLock.clear(); + + if ( pToolbarManager ) + return pToolbarManager->isToolbarVisible( aName ); + } + else if ( aElementType.equalsIgnoreAsciiCase("dockingwindow")) + { + SolarMutexClearableGuard aReadGuard; + uno::Reference< frame::XFrame > xFrame( m_xFrame ); + aReadGuard.clear(); + + return IsDockingWindowVisible( xFrame, aElementName ); + } + + return false; +} + +sal_Bool SAL_CALL LayoutManager::isElementFloating( const OUString& aName ) +{ + if ( o3tl::equalsIgnoreAsciiCase(getElementTypeFromResourceURL( aName ), UIRESOURCETYPE_TOOLBAR )) + { + SolarMutexClearableGuard aReadLock; + ToolbarLayoutManager* pToolbarManager = m_xToolbarManager.get(); + aReadLock.clear(); + + if ( pToolbarManager ) + return pToolbarManager->isToolbarFloating( aName ); + } + + return false; +} + +sal_Bool SAL_CALL LayoutManager::isElementDocked( const OUString& aName ) +{ + if ( o3tl::equalsIgnoreAsciiCase(getElementTypeFromResourceURL( aName ), UIRESOURCETYPE_TOOLBAR )) + { + SolarMutexClearableGuard aReadLock; + ToolbarLayoutManager* pToolbarManager = m_xToolbarManager.get(); + aReadLock.clear(); + + if ( pToolbarManager ) + return pToolbarManager->isToolbarDocked( aName ); + } + + return false; +} + +sal_Bool SAL_CALL LayoutManager::isElementLocked( const OUString& aName ) +{ + if ( o3tl::equalsIgnoreAsciiCase(getElementTypeFromResourceURL( aName ), UIRESOURCETYPE_TOOLBAR )) + { + SolarMutexClearableGuard aReadLock; + ToolbarLayoutManager* pToolbarManager = m_xToolbarManager.get(); + aReadLock.clear(); + + if ( pToolbarManager ) + return pToolbarManager->isToolbarLocked( aName ); + } + + return false; +} + +awt::Size SAL_CALL LayoutManager::getElementSize( const OUString& aName ) +{ + if ( o3tl::equalsIgnoreAsciiCase(getElementTypeFromResourceURL( aName ), UIRESOURCETYPE_TOOLBAR )) + { + SolarMutexClearableGuard aReadLock; + ToolbarLayoutManager* pToolbarManager = m_xToolbarManager.get(); + aReadLock.clear(); + + if ( pToolbarManager ) + return pToolbarManager->getToolbarSize( aName ); + } + + return awt::Size(); +} + +awt::Point SAL_CALL LayoutManager::getElementPos( const OUString& aName ) +{ + if ( o3tl::equalsIgnoreAsciiCase(getElementTypeFromResourceURL( aName ), UIRESOURCETYPE_TOOLBAR )) + { + SolarMutexClearableGuard aReadLock; + ToolbarLayoutManager* pToolbarManager = m_xToolbarManager.get(); + aReadLock.clear(); + + if ( pToolbarManager ) + return pToolbarManager->getToolbarPos( aName ); + } + + return awt::Point(); +} + +void SAL_CALL LayoutManager::lock() +{ + implts_lock(); + + SolarMutexClearableGuard aReadLock; + sal_Int32 nLockCount( m_nLockCount ); + aReadLock.clear(); + + SAL_INFO( "fwk", "LayoutManager::lock " << reinterpret_cast(this) << " - " << nLockCount ); + + Any a( nLockCount ); + implts_notifyListeners( frame::LayoutManagerEvents::LOCK, a ); +} + +void SAL_CALL LayoutManager::unlock() +{ + bool bDoLayout( implts_unlock() ); + + SolarMutexClearableGuard aReadLock; + sal_Int32 nLockCount( m_nLockCount ); + aReadLock.clear(); + + SAL_INFO( "fwk", "LayoutManager::unlock " << reinterpret_cast(this) << " - " << nLockCount); + + // conform to documentation: unlock with lock count == 0 means force a layout + + { + SolarMutexGuard aWriteLock; + if (bDoLayout) + m_aAsyncLayoutTimer.Stop(); + } + + Any a( nLockCount ); + implts_notifyListeners( frame::LayoutManagerEvents::UNLOCK, a ); + + if ( bDoLayout ) + implts_doLayout_notify( true ); +} + +void SAL_CALL LayoutManager::doLayout() +{ + implts_doLayout_notify( true ); +} + +// ILayoutNotifications + +void LayoutManager::requestLayout() +{ + doLayout(); +} + +void LayoutManager::implts_doLayout_notify( bool bOuterResize ) +{ + bool bLayouted = implts_doLayout( false, bOuterResize ); + if ( bLayouted ) + implts_notifyListeners( frame::LayoutManagerEvents::LAYOUT, Any() ); +} + +bool LayoutManager::implts_doLayout( bool bForceRequestBorderSpace, bool bOuterResize ) +{ + SAL_INFO( "fwk", "LayoutManager::implts_doLayout" ); + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + SolarMutexClearableGuard aReadLock; + + if ( !m_xFrame.is() || !m_bParentWindowVisible ) + return false; + + bool bPreserveContentSize( m_bPreserveContentSize ); + bool bMustDoLayout( m_bMustDoLayout ); + bool bNoLock = ( m_nLockCount == 0 ); + awt::Rectangle aCurrBorderSpace( m_aDockingArea ); + Reference< awt::XWindow > xContainerWindow( m_xContainerWindow ); + Reference< awt::XTopWindow2 > xContainerTopWindow( m_xContainerTopWindow ); + Reference< awt::XWindow > xComponentWindow; + try { + xComponentWindow = m_xFrame->getComponentWindow(); + } catch (css::lang::DisposedException &) { + // There can be a race between one thread calling Frame::dispose + // (framework/source/services/frame.cxx) -> Frame::disableLayoutManager + // -> LayoutManager::attachFrame(null) setting m_xFrame to null, and + // the main thread firing the timer-triggered + // LayoutManager::AsyncLayoutHdl -> LayoutManager::implts_doLayout and + // calling into the in-dispose m_xFrame here, so silently ignore a + // DisposedException here: + return false; + } + Reference< XDockingAreaAcceptor > xDockingAreaAcceptor( m_xDockingAreaAcceptor ); + aReadLock.clear(); + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + + bool bLayouted( false ); + + if ( bNoLock && xDockingAreaAcceptor.is() && xContainerWindow.is() && xComponentWindow.is() ) + { + bLayouted = true; + + awt::Rectangle aDockSpace( implts_calcDockingAreaSizes() ); + awt::Rectangle aBorderSpace( aDockSpace ); + bool bGotRequestedBorderSpace( true ); + + // We have to add the height of a possible status bar + aBorderSpace.Height += implts_getStatusBarSize().Height(); + + if ( !equalRectangles( aBorderSpace, aCurrBorderSpace ) || bForceRequestBorderSpace || bMustDoLayout ) + { + // we always resize the content window (instead of the complete container window) if we're not set up + // to (attempt to) preserve the content window's size + if ( bOuterResize && !bPreserveContentSize ) + bOuterResize = false; + + // maximized windows can resized their content window only, not their container window + if ( bOuterResize && xContainerTopWindow.is() && xContainerTopWindow->getIsMaximized() ) + bOuterResize = false; + + // if the component window does not have a size (yet), then we can't use it to calc the container + // window size + awt::Rectangle aComponentRect = xComponentWindow->getPosSize(); + if ( bOuterResize && ( aComponentRect.Width == 0 ) && ( aComponentRect.Height == 0 ) ) + bOuterResize = false; + + bGotRequestedBorderSpace = false; + if ( bOuterResize ) + { + Reference< awt::XDevice > xDevice( xContainerWindow, uno::UNO_QUERY ); + awt::DeviceInfo aContainerInfo = xDevice->getInfo(); + + awt::Size aRequestedSize( aComponentRect.Width + aContainerInfo.LeftInset + aContainerInfo.RightInset + aBorderSpace.X + aBorderSpace.Width, + aComponentRect.Height + aContainerInfo.TopInset + aContainerInfo.BottomInset + aBorderSpace.Y + aBorderSpace.Height ); + awt::Point aComponentPos( aBorderSpace.X, aBorderSpace.Y ); + + bGotRequestedBorderSpace = implts_resizeContainerWindow( aRequestedSize, aComponentPos ); + } + + // if we did not do a container window resize, or it failed, then use the DockingAcceptor as usual + if ( !bGotRequestedBorderSpace ) + bGotRequestedBorderSpace = xDockingAreaAcceptor->requestDockingAreaSpace( aBorderSpace ); + + if ( bGotRequestedBorderSpace ) + { + SolarMutexGuard aWriteGuard; + m_aDockingArea = aBorderSpace; + m_bMustDoLayout = false; + } + } + + if ( bGotRequestedBorderSpace ) + { + ::Size aContainerSize; + ::Size aStatusBarSize; + + // Interim solution to let the layout method within the + // toolbar layout manager. + implts_setOffset( implts_getStatusBarSize().Height() ); + if ( m_xToolbarManager.is() ) + m_xToolbarManager->setDockingArea( aDockSpace ); + + // Subtract status bar size from our container output size. Docking area windows + // don't contain the status bar! + aStatusBarSize = implts_getStatusBarSize(); + aContainerSize = implts_getContainerWindowOutputSize(); + aContainerSize.AdjustHeight( -(aStatusBarSize.Height()) ); + + if ( m_xToolbarManager.is() ) + m_xToolbarManager->doLayout(aContainerSize); + + // Position the status bar + if ( aStatusBarSize.Height() > 0 ) + { + implts_setStatusBarPosSize( ::Point( 0, std::max(( aContainerSize.Height() ), tools::Long( 0 ))), + ::Size( aContainerSize.Width(),aStatusBarSize.Height() )); + } + + xDockingAreaAcceptor->setDockingAreaSpace( aBorderSpace ); + } + } + + return bLayouted; +} + +bool LayoutManager::implts_resizeContainerWindow( const awt::Size& rContainerSize, + const awt::Point& rComponentPos ) +{ + SolarMutexClearableGuard aReadLock; + Reference< awt::XWindow > xContainerWindow = m_xContainerWindow; + Reference< awt::XTopWindow2 > xContainerTopWindow = m_xContainerTopWindow; + Reference< awt::XWindow > xComponentWindow = m_xFrame->getComponentWindow(); + aReadLock.clear(); + + // calculate the maximum size we have for the container window + sal_Int32 nDisplay = xContainerTopWindow->getDisplay(); + AbsoluteScreenPixelRectangle aWorkArea = Application::GetScreenPosSizePixel( nDisplay ); + + if (!aWorkArea.IsEmpty()) + { + if (( rContainerSize.Width > aWorkArea.GetWidth() ) || ( rContainerSize.Height > aWorkArea.GetHeight() )) + return false; + // Strictly, this is not correct. If we have a multi-screen display (css.awt.DisplayAccess.MultiDisplay == true), + // the "effective work area" would be much larger than the work area of a single display, since we could in theory + // position the container window across multiple screens. + // However, this should suffice as a heuristics here ... (nobody really wants to check whether the different screens are + // stacked horizontally or vertically, whether their work areas can really be combined, or are separated by non-work-areas, + // and the like ... right?) + } + + // resize our container window + xContainerWindow->setPosSize( 0, 0, rContainerSize.Width, rContainerSize.Height, awt::PosSize::SIZE ); + // position the component window + xComponentWindow->setPosSize( rComponentPos.X, rComponentPos.Y, 0, 0, awt::PosSize::POS ); + return true; +} + +void SAL_CALL LayoutManager::setVisible( sal_Bool bVisible ) +{ + SolarMutexClearableGuard aWriteLock; + bool bWasVisible( m_bVisible ); + m_bVisible = bVisible; + aWriteLock.clear(); + + if ( bWasVisible != bool(bVisible) ) + implts_setVisibleState( bVisible ); +} + +sal_Bool SAL_CALL LayoutManager::isVisible() +{ + SolarMutexGuard g; + return m_bVisible; +} + +::Size LayoutManager::implts_getStatusBarSize() +{ + SolarMutexClearableGuard aReadLock; + bool bStatusBarVisible( isElementVisible( STATUS_BAR_ALIAS )); + bool bProgressBarVisible( isElementVisible( "private:resource/progressbar/progressbar" )); + bool bVisible( m_bVisible ); + Reference< XUIElement > xStatusBar( m_aStatusBarElement.m_xUIElement ); + Reference< XUIElement > xProgressBar( m_aProgressBarElement.m_xUIElement ); + + Reference< awt::XWindow > xWindow; + if ( bStatusBarVisible && bVisible && xStatusBar.is() ) + xWindow.set( xStatusBar->getRealInterface(), UNO_QUERY ); + else if ( xProgressBar.is() && !xStatusBar.is() && bProgressBarVisible ) + { + ProgressBarWrapper* pWrapper = static_cast(xProgressBar.get()); + if ( pWrapper ) + xWindow = pWrapper->getStatusBar(); + } + aReadLock.clear(); + + if ( xWindow.is() ) + { + awt::Rectangle aPosSize = xWindow->getPosSize(); + return ::Size( aPosSize.Width, aPosSize.Height ); + } + else + return ::Size(); +} + +awt::Rectangle LayoutManager::implts_calcDockingAreaSizes() +{ + SolarMutexClearableGuard aReadLock; + Reference< awt::XWindow > xContainerWindow( m_xContainerWindow ); + Reference< XDockingAreaAcceptor > xDockingAreaAcceptor( m_xDockingAreaAcceptor ); + aReadLock.clear(); + + awt::Rectangle aBorderSpace; + if ( m_xToolbarManager.is() && xDockingAreaAcceptor.is() && xContainerWindow.is() ) + aBorderSpace = m_xToolbarManager->getDockingArea(); + + return aBorderSpace; +} + +void LayoutManager::implts_setDockingAreaWindowSizes() +{ + SolarMutexClearableGuard aReadLock; + Reference< awt::XWindow > xContainerWindow( m_xContainerWindow ); + aReadLock.clear(); + + uno::Reference< awt::XDevice > xDevice( xContainerWindow, uno::UNO_QUERY ); + // Convert relative size to output size. + awt::Rectangle aRectangle = xContainerWindow->getPosSize(); + awt::DeviceInfo aInfo = xDevice->getInfo(); + awt::Size aContainerClientSize( aRectangle.Width - aInfo.LeftInset - aInfo.RightInset, + aRectangle.Height - aInfo.TopInset - aInfo.BottomInset ); + ::Size aStatusBarSize = implts_getStatusBarSize(); + + // Position the status bar + if ( aStatusBarSize.Height() > 0 ) + { + implts_setStatusBarPosSize( ::Point( 0, std::max(( aContainerClientSize.Height - aStatusBarSize.Height() ), tools::Long( 0 ))), + ::Size( aContainerClientSize.Width, aStatusBarSize.Height() )); + } +} + +void LayoutManager::implts_updateMenuBarClose() +{ + SolarMutexClearableGuard aWriteLock; + bool bShowCloseButton( m_bMenuBarCloseButton ); + Reference< awt::XWindow > xContainerWindow( m_xContainerWindow ); + aWriteLock.clear(); + + if ( !xContainerWindow.is() ) + return; + + SolarMutexGuard aGuard; + + SystemWindow* pSysWindow = getTopSystemWindow( xContainerWindow ); + if ( pSysWindow ) + { + MenuBar* pMenuBar = pSysWindow->GetMenuBar(); + if ( pMenuBar ) + { + // TODO remove link on sal_False ?! + pMenuBar->ShowCloseButton(bShowCloseButton); + pMenuBar->SetCloseButtonClickHdl(LINK(this, LayoutManager, MenuBarClose)); + } + } +} + +bool LayoutManager::implts_resetMenuBar() +{ + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + SolarMutexGuard aWriteLock; + bool bMenuVisible( m_bMenuVisible ); + Reference< awt::XWindow > xContainerWindow( m_xContainerWindow ); + + MenuBar* pSetMenuBar = nullptr; + if ( m_xInplaceMenuBar.is() ) + pSetMenuBar = static_cast(m_xInplaceMenuBar->GetMenuBar()); + else if ( m_xMenuBar ) + pSetMenuBar = static_cast(m_xMenuBar->GetMenuBarManager()->GetMenuBar()); + + SystemWindow* pSysWindow = getTopSystemWindow( xContainerWindow ); + if ( pSysWindow && bMenuVisible && pSetMenuBar ) + { + pSysWindow->SetMenuBar(pSetMenuBar); + pSetMenuBar->SetDisplayable( true ); + return true; + } + + return false; +} + +void LayoutManager::implts_createMSCompatibleMenuBar( const OUString& aName ) +{ + SolarMutexGuard aWriteLock; + + // Find Form menu in the original menubar + m_xMenuBar.set( static_cast< MenuBarWrapper* >(implts_createElement( aName ).get()) ); + uno::Reference< container::XIndexReplace > xMenuIndex(m_xMenuBar->getSettings(true), UNO_QUERY); + + sal_Int32 nFormsMenu = -1; + for (sal_Int32 nIndex = 0; nIndex < xMenuIndex->getCount(); ++nIndex) + { + uno::Sequence< beans::PropertyValue > aProps; + xMenuIndex->getByIndex( nIndex ) >>= aProps; + OUString aCommand; + for ( beans::PropertyValue const & rProp : std::as_const(aProps) ) + { + if (rProp.Name == "CommandURL") + { + rProp.Value >>= aCommand; + break; + } + } + + if (aCommand == ".uno:FormatFormMenu") + nFormsMenu = nIndex; + } + assert(nFormsMenu != -1); + + // Create the MS compatible Form menu + css::uno::Reference< css::ui::XUIElement > xFormsMenu = implts_createElement( "private:resource/menubar/mscompatibleformsmenu" ); + if(!xFormsMenu.is()) + return; + + // Merge the MS compatible Form menu into the menubar + uno::Reference< XUIElementSettings > xFormsMenuSettings(xFormsMenu, UNO_QUERY); + uno::Reference< container::XIndexAccess > xFormsMenuIndex(xFormsMenuSettings->getSettings(true)); + + assert(xFormsMenuIndex->getCount() >= 1); + uno::Sequence< beans::PropertyValue > aNewFormsMenu; + xFormsMenuIndex->getByIndex( 0 ) >>= aNewFormsMenu; + xMenuIndex->replaceByIndex(nFormsMenu, uno::Any(aNewFormsMenu)); + + setMergedMenuBar( xMenuIndex ); + + // Clear up the temporal forms menubar + Reference< XComponent > xFormsMenuComp( xFormsMenu, UNO_QUERY ); + if ( xFormsMenuComp.is() ) + xFormsMenuComp->dispose(); + xFormsMenu.clear(); +} + +IMPL_LINK_NOARG(LayoutManager, MenuBarClose, void*, void) +{ + SolarMutexClearableGuard aReadLock; + uno::Reference< frame::XDispatchProvider > xProvider(m_xFrame, uno::UNO_QUERY); + uno::Reference< XComponentContext > xContext( m_xContext ); + aReadLock.clear(); + + if ( !xProvider.is()) + return; + + uno::Reference< frame::XDispatchHelper > xDispatcher = frame::DispatchHelper::create( xContext ); + + xDispatcher->executeDispatch( + xProvider, + ".uno:CloseWin", + "_self", + 0, + uno::Sequence< beans::PropertyValue >()); +} + +// XLayoutManagerEventBroadcaster + +void SAL_CALL LayoutManager::addLayoutManagerEventListener( const uno::Reference< frame::XLayoutManagerListener >& xListener ) +{ + m_aListenerContainer.addInterface( cppu::UnoType::get(), xListener ); +} + +void SAL_CALL LayoutManager::removeLayoutManagerEventListener( const uno::Reference< frame::XLayoutManagerListener >& xListener ) +{ + m_aListenerContainer.removeInterface( cppu::UnoType::get(), xListener ); +} + +void LayoutManager::implts_notifyListeners(short nEvent, const uno::Any& rInfoParam) +{ + comphelper::OInterfaceContainerHelper2* pContainer = m_aListenerContainer.getContainer( cppu::UnoType::get()); + if (pContainer==nullptr) + return; + + lang::EventObject aSource( static_cast< ::cppu::OWeakObject*>(this) ); + comphelper::OInterfaceIteratorHelper2 pIterator(*pContainer); + while (pIterator.hasMoreElements()) + { + try + { + static_cast(pIterator.next())->layoutEvent(aSource, nEvent, rInfoParam); + } + catch( const uno::RuntimeException& ) + { + pIterator.remove(); + } + } +} + +// XWindowListener + +void SAL_CALL LayoutManager::windowResized( const awt::WindowEvent& aEvent ) +{ + SolarMutexGuard g; + Reference< awt::XWindow > xContainerWindow( m_xContainerWindow ); + + Reference< XInterface > xIfac( xContainerWindow, UNO_QUERY ); + if ( xIfac == aEvent.Source && m_bVisible ) + { + // We have to call our resize handler at least once synchronously, as some + // application modules need this. So we have to check if this is the first + // call after the async layout time expired. + m_bMustDoLayout = true; + if ( !m_aAsyncLayoutTimer.IsActive() ) + { + m_aAsyncLayoutTimer.Invoke(); + if ( m_nLockCount == 0 ) + m_aAsyncLayoutTimer.Start(); + } + } + else if ( m_xFrame.is() && aEvent.Source == m_xFrame->getContainerWindow() ) + { + // the container window of my DockingAreaAcceptor is not the same as of my frame + // I still have to resize my frames' window as nobody else will do it + Reference< awt::XWindow > xComponentWindow( m_xFrame->getComponentWindow() ); + if( xComponentWindow.is() ) + { + uno::Reference< awt::XDevice > xDevice( m_xFrame->getContainerWindow(), uno::UNO_QUERY ); + + // Convert relative size to output size. + awt::Rectangle aRectangle = m_xFrame->getContainerWindow()->getPosSize(); + awt::DeviceInfo aInfo = xDevice->getInfo(); + awt::Size aSize( aRectangle.Width - aInfo.LeftInset - aInfo.RightInset , + aRectangle.Height - aInfo.TopInset - aInfo.BottomInset ); + + // Resize our component window. + xComponentWindow->setPosSize( 0, 0, aSize.Width, aSize.Height, awt::PosSize::POSSIZE ); + } + } +} + +void SAL_CALL LayoutManager::windowMoved( const awt::WindowEvent& ) +{ +} + +void SAL_CALL LayoutManager::windowShown( const lang::EventObject& aEvent ) +{ + SolarMutexClearableGuard aReadLock; + Reference< awt::XWindow > xContainerWindow( m_xContainerWindow ); + bool bParentWindowVisible( m_bParentWindowVisible ); + aReadLock.clear(); + + Reference< XInterface > xIfac( xContainerWindow, UNO_QUERY ); + if ( xIfac == aEvent.Source ) + { + SolarMutexClearableGuard aWriteLock; + m_bParentWindowVisible = true; + bool bSetVisible = ( m_bParentWindowVisible != bParentWindowVisible ); + aWriteLock.clear(); + + if ( bSetVisible ) + implts_updateUIElementsVisibleState( true ); + } +} + +void SAL_CALL LayoutManager::windowHidden( const lang::EventObject& aEvent ) +{ + SolarMutexClearableGuard aReadLock; + Reference< awt::XWindow > xContainerWindow( m_xContainerWindow ); + bool bParentWindowVisible( m_bParentWindowVisible ); + aReadLock.clear(); + + Reference< XInterface > xIfac( xContainerWindow, UNO_QUERY ); + if ( xIfac == aEvent.Source ) + { + SolarMutexClearableGuard aWriteLock; + m_bParentWindowVisible = false; + bool bSetInvisible = ( m_bParentWindowVisible != bParentWindowVisible ); + aWriteLock.clear(); + + if ( bSetInvisible ) + implts_updateUIElementsVisibleState( false ); + } +} + +IMPL_LINK_NOARG(LayoutManager, AsyncLayoutHdl, Timer *, void) +{ + { + SolarMutexGuard aReadLock; + + if (!m_xContainerWindow.is()) + return; + } + + implts_setDockingAreaWindowSizes(); + implts_doLayout( true, false ); +} + +// XFrameActionListener + +void SAL_CALL LayoutManager::frameAction( const FrameActionEvent& aEvent ) +{ + if (( aEvent.Action == FrameAction_COMPONENT_ATTACHED ) || ( aEvent.Action == FrameAction_COMPONENT_REATTACHED )) + { + SAL_INFO( "fwk", "LayoutManager::frameAction (COMPONENT_ATTACHED|REATTACHED)" ); + + { + SolarMutexGuard aWriteLock; + m_bMustDoLayout = true; + } + + implts_reset( true ); + implts_doLayout( true, false ); + implts_doLayout( true, true ); + } + else if (( aEvent.Action == FrameAction_FRAME_UI_ACTIVATED ) || ( aEvent.Action == FrameAction_FRAME_UI_DEACTIVATING )) + { + SAL_INFO( "fwk", "LayoutManager::frameAction (FRAME_UI_ACTIVATED|DEACTIVATING)" ); + + implts_toggleFloatingUIElementsVisibility( aEvent.Action == FrameAction_FRAME_UI_ACTIVATED ); + } + else if ( aEvent.Action == FrameAction_COMPONENT_DETACHING ) + { + SAL_INFO( "fwk", "LayoutManager::frameAction (COMPONENT_DETACHING)" ); + + implts_reset( false ); + } +} + +void SAL_CALL LayoutManager::disposing( const lang::EventObject& rEvent ) +{ + bool bDisposeAndClear( false ); + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + { + SolarMutexGuard aWriteLock; + + if (rEvent.Source == Reference(m_xFrame, UNO_QUERY)) + { + // Our frame gets disposed, release all our references that depends on a working frame reference. + + setDockingAreaAcceptor(Reference()); + + // destroy all elements, it's possible that detaching is NOT called! + implts_destroyElements(); + impl_clearUpMenuBar(); + m_xMenuBar.clear(); + VclPtr pMenuBar; + if (m_xInplaceMenuBar.is()) + { + pMenuBar = m_xInplaceMenuBar->GetMenuBar(); + m_xInplaceMenuBar->dispose(); + m_xInplaceMenuBar.clear(); + } + pMenuBar.disposeAndClear(); + m_xContainerWindow.clear(); + m_xContainerTopWindow.clear(); + + // forward disposing call to toolbar manager + if (m_xToolbarManager.is()) + m_xToolbarManager->disposing(rEvent); + + if (m_xModuleCfgMgr.is()) + { + try + { + Reference xModuleCfgMgr(m_xModuleCfgMgr, UNO_QUERY); + xModuleCfgMgr->removeConfigurationListener(Reference(this)); + } + catch (const Exception&) + { + } + } + + if (m_xDocCfgMgr.is()) + { + try + { + Reference xDocCfgMgr(m_xDocCfgMgr, UNO_QUERY); + xDocCfgMgr->removeConfigurationListener(Reference(this)); + } + catch (const Exception&) + { + } + } + + m_xDocCfgMgr.clear(); + m_xModuleCfgMgr.clear(); + m_xFrame.clear(); + m_pGlobalSettings.reset(); + + bDisposeAndClear = true; + } + else if (rEvent.Source == Reference(m_xContainerWindow, UNO_QUERY)) + { + // Our container window gets disposed. Remove all user interface elements. + ToolbarLayoutManager* pToolbarManager = m_xToolbarManager.get(); + if (pToolbarManager) + { + uno::Reference aEmptyWindowPeer; + pToolbarManager->setParentWindow(aEmptyWindowPeer); + } + impl_clearUpMenuBar(); + m_xMenuBar.clear(); + VclPtr pMenuBar; + if (m_xInplaceMenuBar.is()) + { + pMenuBar = m_xInplaceMenuBar->GetMenuBar(); + m_xInplaceMenuBar->dispose(); + m_xInplaceMenuBar.clear(); + } + pMenuBar.disposeAndClear(); + m_xContainerWindow.clear(); + m_xContainerTopWindow.clear(); + } + else if (rEvent.Source == Reference(m_xDocCfgMgr, UNO_QUERY)) + m_xDocCfgMgr.clear(); + else if (rEvent.Source == Reference(m_xModuleCfgMgr, UNO_QUERY)) + m_xModuleCfgMgr.clear(); + } + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + + // Send disposing to our listener when we have lost our frame. + if ( bDisposeAndClear ) + { + // Send message to all listener and forget her references. + uno::Reference< frame::XLayoutManager > xThis(this); + lang::EventObject aEvent( xThis ); + m_aListenerContainer.disposeAndClear( aEvent ); + } +} + +void SAL_CALL LayoutManager::elementInserted( const ui::ConfigurationEvent& Event ) +{ + SolarMutexClearableGuard aReadLock; + Reference< XFrame > xFrame( m_xFrame ); + rtl::Reference< ToolbarLayoutManager > xToolbarManager( m_xToolbarManager ); + aReadLock.clear(); + + if ( !xFrame.is() ) + return; + + OUString aElementType; + OUString aElementName; + bool bRefreshLayout(false); + + parseResourceURL( Event.ResourceURL, aElementType, aElementName ); + if ( aElementType.equalsIgnoreAsciiCase( UIRESOURCETYPE_TOOLBAR )) + { + if ( xToolbarManager.is() ) + { + xToolbarManager->elementInserted( Event ); + bRefreshLayout = xToolbarManager->isLayoutDirty(); + } + } + else if ( aElementType.equalsIgnoreAsciiCase( UIRESOURCETYPE_MENUBAR )) + { + Reference< XUIElement > xUIElement = implts_findElement( Event.ResourceURL ); + Reference< XUIElementSettings > xElementSettings( xUIElement, UNO_QUERY ); + if ( xElementSettings.is() ) + { + uno::Reference< XPropertySet > xPropSet( xElementSettings, uno::UNO_QUERY ); + if ( xPropSet.is() ) + { + if ( Event.Source == uno::Reference< uno::XInterface >( m_xDocCfgMgr, uno::UNO_QUERY )) + xPropSet->setPropertyValue( "ConfigurationSource", Any( m_xDocCfgMgr )); + } + xElementSettings->updateSettings(); + } + } + + if ( bRefreshLayout ) + doLayout(); +} + +void SAL_CALL LayoutManager::elementRemoved( const ui::ConfigurationEvent& Event ) +{ + SolarMutexClearableGuard aReadLock; + Reference< frame::XFrame > xFrame( m_xFrame ); + rtl::Reference< ToolbarLayoutManager > xToolbarManager( m_xToolbarManager ); + Reference< awt::XWindow > xContainerWindow( m_xContainerWindow ); + rtl::Reference< MenuBarWrapper > xMenuBar( m_xMenuBar ); + Reference< ui::XUIConfigurationManager > xModuleCfgMgr( m_xModuleCfgMgr ); + Reference< ui::XUIConfigurationManager > xDocCfgMgr( m_xDocCfgMgr ); + aReadLock.clear(); + + if ( !xFrame.is() ) + return; + + OUString aElementType; + OUString aElementName; + bool bRefreshLayout(false); + + parseResourceURL( Event.ResourceURL, aElementType, aElementName ); + if ( aElementType.equalsIgnoreAsciiCase( UIRESOURCETYPE_TOOLBAR )) + { + if ( xToolbarManager.is() ) + { + xToolbarManager->elementRemoved( Event ); + bRefreshLayout = xToolbarManager->isLayoutDirty(); + } + } + else + { + Reference< XUIElement > xUIElement = implts_findElement( Event.ResourceURL ); + Reference< XUIElementSettings > xElementSettings( xUIElement, UNO_QUERY ); + if ( xElementSettings.is() ) + { + bool bNoSettings( false ); + OUString aConfigSourcePropName( "ConfigurationSource" ); + Reference< XInterface > xElementCfgMgr; + Reference< XPropertySet > xPropSet( xElementSettings, UNO_QUERY ); + + if ( xPropSet.is() ) + xPropSet->getPropertyValue( aConfigSourcePropName ) >>= xElementCfgMgr; + + if ( !xElementCfgMgr.is() ) + return; + + // Check if the same UI configuration manager has changed => check further + if ( Event.Source == xElementCfgMgr ) + { + // Same UI configuration manager where our element has its settings + if ( Event.Source == Reference< XInterface >( xDocCfgMgr, UNO_QUERY )) + { + // document settings removed + if ( xModuleCfgMgr->hasSettings( Event.ResourceURL )) + { + xPropSet->setPropertyValue( aConfigSourcePropName, Any( m_xModuleCfgMgr )); + xElementSettings->updateSettings(); + return; + } + } + + bNoSettings = true; + } + + // No settings anymore, element must be destroyed + if ( xContainerWindow.is() && bNoSettings ) + { + if ( aElementType.equalsIgnoreAsciiCase("menubar") && + aElementName.equalsIgnoreAsciiCase("menubar") ) + { + SystemWindow* pSysWindow = getTopSystemWindow( xContainerWindow ); + if ( pSysWindow && !m_bInplaceMenuSet ) + pSysWindow->SetMenuBar( nullptr ); + + if ( xMenuBar.is() ) + xMenuBar->dispose(); + + SolarMutexGuard g; + m_xMenuBar.clear(); + } + } + } + } + + if ( bRefreshLayout ) + doLayout(); +} + +void SAL_CALL LayoutManager::elementReplaced( const ui::ConfigurationEvent& Event ) +{ + SolarMutexClearableGuard aReadLock; + Reference< XFrame > xFrame( m_xFrame ); + rtl::Reference< ToolbarLayoutManager > xToolbarManager( m_xToolbarManager ); + aReadLock.clear(); + + if ( !xFrame.is() ) + return; + + OUString aElementType; + OUString aElementName; + bool bRefreshLayout(false); + + parseResourceURL( Event.ResourceURL, aElementType, aElementName ); + if ( aElementType.equalsIgnoreAsciiCase( UIRESOURCETYPE_TOOLBAR )) + { + if ( xToolbarManager.is() ) + { + xToolbarManager->elementReplaced( Event ); + bRefreshLayout = xToolbarManager->isLayoutDirty(); + } + } + else + { + Reference< XUIElement > xUIElement = implts_findElement( Event.ResourceURL ); + Reference< XUIElementSettings > xElementSettings( xUIElement, UNO_QUERY ); + if ( xElementSettings.is() ) + { + Reference< XInterface > xElementCfgMgr; + Reference< XPropertySet > xPropSet( xElementSettings, UNO_QUERY ); + + if ( xPropSet.is() ) + xPropSet->getPropertyValue( "ConfigurationSource" ) >>= xElementCfgMgr; + + if ( !xElementCfgMgr.is() ) + return; + + // Check if the same UI configuration manager has changed => update settings + if ( Event.Source == xElementCfgMgr ) + xElementSettings->updateSettings(); + } + } + + if ( bRefreshLayout ) + doLayout(); +} + +void SAL_CALL LayoutManager::setFastPropertyValue_NoBroadcast( sal_Int32 nHandle, + const uno::Any& aValue ) +{ + if ( (nHandle != LAYOUTMANAGER_PROPHANDLE_REFRESHVISIBILITY) && (nHandle != LAYOUTMANAGER_PROPHANDLE_REFRESHTOOLTIP) ) + LayoutManager_PBase::setFastPropertyValue_NoBroadcast( nHandle, aValue ); + + switch( nHandle ) + { + case LAYOUTMANAGER_PROPHANDLE_MENUBARCLOSER: + implts_updateMenuBarClose(); + break; + + case LAYOUTMANAGER_PROPHANDLE_REFRESHVISIBILITY: + { + bool bValue(false); + if (( aValue >>= bValue ) && bValue ) + { + SolarMutexClearableGuard aReadLock; + ToolbarLayoutManager* pToolbarManager = m_xToolbarManager.get(); + bool bAutomaticToolbars( m_bAutomaticToolbars ); + aReadLock.clear(); + + if ( pToolbarManager ) + pToolbarManager->refreshToolbarsVisibility( bAutomaticToolbars ); + } + break; + } + + case LAYOUTMANAGER_PROPHANDLE_HIDECURRENTUI: + implts_setCurrentUIVisibility( !m_bHideCurrentUI ); + break; + + case LAYOUTMANAGER_PROPHANDLE_REFRESHTOOLTIP: + if (m_xToolbarManager.is()) + m_xToolbarManager->updateToolbarsTips(); + break; + + default: break; + } +} + +namespace detail +{ + class InfoHelperBuilder + { + private: + std::unique_ptr<::cppu::OPropertyArrayHelper> m_pInfoHelper; + public: + explicit InfoHelperBuilder(const LayoutManager &rManager) + { + uno::Sequence< beans::Property > aProperties; + rManager.describeProperties(aProperties); + m_pInfoHelper.reset( new ::cppu::OPropertyArrayHelper(aProperties, true) ); + } + InfoHelperBuilder(const InfoHelperBuilder&) = delete; + InfoHelperBuilder& operator=(const InfoHelperBuilder&) = delete; + + ::cppu::OPropertyArrayHelper& getHelper() { return *m_pInfoHelper; } + }; +} + +::cppu::IPropertyArrayHelper& SAL_CALL LayoutManager::getInfoHelper() +{ + static detail::InfoHelperBuilder INFO(*this); + return INFO.getHelper(); +} + +uno::Reference< beans::XPropertySetInfo > SAL_CALL LayoutManager::getPropertySetInfo() +{ + static uno::Reference< beans::XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) ); + + return xInfo; +} + +} // namespace framework + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_framework_LayoutManager_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence const &) +{ + return cppu::acquire(new framework::LayoutManager(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/layoutmanager/toolbarlayoutmanager.cxx b/framework/source/layoutmanager/toolbarlayoutmanager.cxx new file mode 100644 index 0000000000..846b111d58 --- /dev/null +++ b/framework/source/layoutmanager/toolbarlayoutmanager.cxx @@ -0,0 +1,4130 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "toolbarlayoutmanager.hxx" +#include +#include +#include "helpers.hxx" +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +using namespace ::com::sun::star; + +namespace framework +{ + +ToolbarLayoutManager::ToolbarLayoutManager( + uno::Reference< uno::XComponentContext > xContext, + uno::Reference< ui::XUIElementFactory > xUIElementFactory, + LayoutManager* pParentLayouter ): + m_xContext(std::move( xContext)), + m_xUIElementFactoryManager(std::move( xUIElementFactory )), + m_pParentLayouter( pParentLayouter ), + m_aDockingArea(0, 0, 0, 0), + m_aDockingAreaOffsets(0, 0, 0, 0), + m_eDockOperation( DOCKOP_ON_COLROW ), + m_ePreviewDetection( PREVIEWFRAME_UNKNOWN ), + m_bComponentAttached( false ), + m_bLayoutDirty( false ), + m_bGlobalSettings( false ), + m_bDockingInProgress( false ), + m_bLayoutInProgress( false ), + m_bToolbarCreation( false ) +{ +} + +ToolbarLayoutManager::~ToolbarLayoutManager() +{ + m_pGlobalSettings.reset(); + m_pAddonOptions.reset(); +} + +// XInterface + +void SAL_CALL ToolbarLayoutManager::acquire() noexcept +{ + OWeakObject::acquire(); +} + +void SAL_CALL ToolbarLayoutManager::release() noexcept +{ + OWeakObject::release(); +} + +uno::Any SAL_CALL ToolbarLayoutManager::queryInterface( const uno::Type & rType ) +{ + uno::Any a = ::cppu::queryInterface( rType, + static_cast< awt::XDockableWindowListener* >(this), + static_cast< ui::XUIConfigurationListener* >(this), + static_cast< awt::XWindowListener* >(this)); + + if ( a.hasValue() ) + return a; + + return OWeakObject::queryInterface( rType ); +} + +void SAL_CALL ToolbarLayoutManager::disposing( const lang::EventObject& aEvent ) +{ + if ( aEvent.Source == m_xFrame ) + { + // Reset all internal references + reset(); + implts_destroyDockingAreaWindows(); + } +} + +awt::Rectangle ToolbarLayoutManager::getDockingArea() +{ + SolarMutexResettableGuard aWriteLock; + tools::Rectangle aNewDockingArea( m_aDockingArea ); + aWriteLock.clear(); + + if ( isLayoutDirty() ) + aNewDockingArea = implts_calcDockingArea(); + + aWriteLock.reset(); + m_aDockingArea = aNewDockingArea; + aWriteLock.clear(); + + return putRectangleValueToAWT(aNewDockingArea); +} + +void ToolbarLayoutManager::setDockingArea( const awt::Rectangle& rDockingArea ) +{ + SolarMutexGuard g; + m_aDockingArea = putAWTToRectangle( rDockingArea ); + m_bLayoutDirty = true; +} + +void ToolbarLayoutManager::implts_setDockingAreaWindowSizes( const awt::Rectangle& rBorderSpace ) +{ + SolarMutexClearableGuard aReadLock; + tools::Rectangle aDockOffsets = m_aDockingAreaOffsets; + uno::Reference< awt::XWindow2 > xContainerWindow( m_xContainerWindow ); + uno::Reference< awt::XWindow > xTopDockAreaWindow( m_xDockAreaWindows[int(ui::DockingArea_DOCKINGAREA_TOP)] ); + uno::Reference< awt::XWindow > xBottomDockAreaWindow( m_xDockAreaWindows[int(ui::DockingArea_DOCKINGAREA_BOTTOM)] ); + uno::Reference< awt::XWindow > xLeftDockAreaWindow( m_xDockAreaWindows[int(ui::DockingArea_DOCKINGAREA_LEFT)] ); + uno::Reference< awt::XWindow > xRightDockAreaWindow( m_xDockAreaWindows[int(ui::DockingArea_DOCKINGAREA_RIGHT)] ); + aReadLock.clear(); + + uno::Reference< awt::XDevice > xDevice( xContainerWindow, uno::UNO_QUERY ); + + // Convert relative size to output size. + awt::Rectangle aRectangle = xContainerWindow->getPosSize(); + awt::DeviceInfo aInfo = xDevice->getInfo(); + awt::Size aContainerClientSize( aRectangle.Width - aInfo.LeftInset - aInfo.RightInset , + aRectangle.Height - aInfo.TopInset - aInfo.BottomInset ); + tools::Long aStatusBarHeight = aDockOffsets.GetHeight(); + + sal_Int32 nLeftRightDockingAreaHeight( aContainerClientSize.Height ); + if ( rBorderSpace.Y >= 0 ) + { + // Top docking area window + xTopDockAreaWindow->setPosSize( 0, 0, aContainerClientSize.Width, rBorderSpace.Y, awt::PosSize::POSSIZE ); + xTopDockAreaWindow->setVisible( true ); + nLeftRightDockingAreaHeight -= rBorderSpace.Y; + } + + if ( rBorderSpace.Height >= 0 ) + { + // Bottom docking area window + sal_Int32 nBottomPos = std::max( sal_Int32( aContainerClientSize.Height - rBorderSpace.Height - aStatusBarHeight + 1 ), sal_Int32( 0 )); + sal_Int32 nHeight = ( nBottomPos == 0 ) ? 0 : rBorderSpace.Height; + + xBottomDockAreaWindow->setPosSize( 0, nBottomPos, aContainerClientSize.Width, nHeight, awt::PosSize::POSSIZE ); + xBottomDockAreaWindow->setVisible( true ); + nLeftRightDockingAreaHeight -= nHeight - 1; + } + + nLeftRightDockingAreaHeight -= aStatusBarHeight; + if ( rBorderSpace.X >= 0 || nLeftRightDockingAreaHeight > 0 ) + { + // Left docking area window + // We also have to change our right docking area window if the top or bottom area has changed. They have a higher priority! + sal_Int32 nHeight = std::max( 0, nLeftRightDockingAreaHeight ); + + xLeftDockAreaWindow->setPosSize( 0, rBorderSpace.Y, rBorderSpace.X, nHeight, awt::PosSize::POSSIZE ); + xLeftDockAreaWindow->setVisible( true ); + } + if ( rBorderSpace.Width >= 0 || nLeftRightDockingAreaHeight > 0 ) + { + // Right docking area window + // We also have to change our right docking area window if the top or bottom area has changed. They have a higher priority! + sal_Int32 nLeftPos = std::max( 0, aContainerClientSize.Width - rBorderSpace.Width ); + sal_Int32 nHeight = std::max( 0, nLeftRightDockingAreaHeight ); + sal_Int32 nWidth = ( nLeftPos == 0 ) ? 0 : rBorderSpace.Width; + + xRightDockAreaWindow->setPosSize( nLeftPos, rBorderSpace.Y, nWidth, nHeight, awt::PosSize::POSSIZE ); + xRightDockAreaWindow->setVisible( true ); + } +} + + +void ToolbarLayoutManager::doLayout(const ::Size& aContainerSize) +{ + SolarMutexResettableGuard aWriteLock; + bool bLayoutInProgress( m_bLayoutInProgress ); + m_bLayoutInProgress = true; + awt::Rectangle aDockingArea = putRectangleValueToAWT( m_aDockingArea ); + aWriteLock.clear(); + + if ( bLayoutInProgress ) + return; + + // Retrieve row/column dependent data from all docked user-interface elements + for ( sal_Int32 i = 0; i < DOCKINGAREAS_COUNT; i++ ) + { + bool bReverse( isReverseOrderDockingArea( i )); + std::vector< SingleRowColumnWindowData > aRowColumnsWindowData; + + implts_getDockingAreaElementInfos( static_cast(i), aRowColumnsWindowData ); + + sal_Int32 nOffset( 0 ); + const sal_uInt32 nCount = aRowColumnsWindowData.size(); + for ( sal_uInt32 j = 0; j < nCount; ++j ) + { + sal_uInt32 nIndex = bReverse ? nCount-j-1 : j; + implts_calcWindowPosSizeOnSingleRowColumn( i, nOffset, aRowColumnsWindowData[nIndex], aContainerSize ); + nOffset += aRowColumnsWindowData[j].nStaticSize; + } + } + + implts_setDockingAreaWindowSizes( aDockingArea ); + + aWriteLock.reset(); + m_bLayoutDirty = false; + m_bLayoutInProgress = false; + aWriteLock.clear(); +} + +bool ToolbarLayoutManager::implts_isParentWindowVisible() const +{ + SolarMutexGuard g; + bool bVisible( false ); + if ( m_xContainerWindow.is() ) + bVisible = m_xContainerWindow->isVisible(); + + return bVisible; +} + +tools::Rectangle ToolbarLayoutManager::implts_calcDockingArea() +{ + SolarMutexClearableGuard aReadLock; + UIElementVector aWindowVector( m_aUIElements ); + aReadLock.clear(); + + tools::Rectangle aBorderSpace; + sal_Int32 nCurrRowColumn( 0 ); + sal_Int32 nCurrPos( 0 ); + ui::DockingArea nCurrDockingArea( ui::DockingArea_DOCKINGAREA_TOP ); + std::vector< sal_Int32 > aRowColumnSizes[DOCKINGAREAS_COUNT]; + + // initialize rectangle with zero values! + aBorderSpace.setWidth(0); + aBorderSpace.setHeight(0); + + aRowColumnSizes[static_cast(nCurrDockingArea)].clear(); + aRowColumnSizes[static_cast(nCurrDockingArea)].push_back( 0 ); + + for (auto const& window : aWindowVector) + { + if ( window.m_xUIElement.is() ) + { + uno::Reference< awt::XWindow > xWindow( window.m_xUIElement->getRealInterface(), uno::UNO_QUERY ); + uno::Reference< awt::XDockableWindow > xDockWindow( xWindow, uno::UNO_QUERY ); + if ( xWindow.is() && xDockWindow.is() ) + { + SolarMutexGuard aGuard; + + VclPtr pWindow = VCLUnoHelper::GetWindow( xWindow ); + if ( pWindow && !xDockWindow->isFloating() && window.m_bVisible && !window.m_bMasterHide ) + { + awt::Rectangle aPosSize = xWindow->getPosSize(); + if ( window.m_aDockedData.m_nDockedArea != nCurrDockingArea ) + { + nCurrDockingArea = window.m_aDockedData.m_nDockedArea; + nCurrRowColumn = 0; + nCurrPos = 0; + aRowColumnSizes[static_cast(nCurrDockingArea)].clear(); + aRowColumnSizes[static_cast(nCurrDockingArea)].push_back( 0 ); + } + + if ( window.m_aDockedData.m_nDockedArea == nCurrDockingArea ) + { + if ( isHorizontalDockingArea( window.m_aDockedData.m_nDockedArea )) + { + if ( window.m_aDockedData.m_aPos.Y > nCurrPos ) + { + ++nCurrRowColumn; + nCurrPos = window.m_aDockedData.m_aPos.Y; + aRowColumnSizes[static_cast(nCurrDockingArea)].push_back( 0 ); + } + + if ( aPosSize.Height > aRowColumnSizes[static_cast(nCurrDockingArea)][nCurrRowColumn] ) + aRowColumnSizes[static_cast(nCurrDockingArea)][nCurrRowColumn] = aPosSize.Height; + } + else + { + if ( window.m_aDockedData.m_aPos.X > nCurrPos ) + { + ++nCurrRowColumn; + nCurrPos = window.m_aDockedData.m_aPos.X; + aRowColumnSizes[static_cast(nCurrDockingArea)].push_back( 0 ); + } + + if ( aPosSize.Width > aRowColumnSizes[static_cast(nCurrDockingArea)][nCurrRowColumn] ) + aRowColumnSizes[static_cast(nCurrDockingArea)][nCurrRowColumn] = aPosSize.Width; + } + } + } + } + } + } + + // Sum up max heights from every row/column + if ( !aWindowVector.empty() ) + { + for ( sal_Int32 i = 0; i <= sal_Int32(ui::DockingArea_DOCKINGAREA_RIGHT); i++ ) + { + sal_Int32 nSize( 0 ); + const sal_uInt32 nCount = aRowColumnSizes[i].size(); + for ( sal_uInt32 j = 0; j < nCount; j++ ) + nSize += aRowColumnSizes[i][j]; + + if ( i == sal_Int32(ui::DockingArea_DOCKINGAREA_TOP) ) + aBorderSpace.SetTop( nSize ); + else if ( i == sal_Int32(ui::DockingArea_DOCKINGAREA_BOTTOM) ) + aBorderSpace.SetBottom( nSize ); + else if ( i == sal_Int32(ui::DockingArea_DOCKINGAREA_LEFT) ) + aBorderSpace.SetLeft( nSize ); + else + aBorderSpace.SetRight( nSize ); + } + } + + return aBorderSpace; +} + +void ToolbarLayoutManager::reset() +{ + { + SolarMutexGuard aWriteLock; + m_xModuleCfgMgr.clear(); + m_xDocCfgMgr.clear(); + m_ePreviewDetection = PREVIEWFRAME_UNKNOWN; + m_bComponentAttached = false; + } + + destroyToolbars(); + resetDockingArea(); +} + +void ToolbarLayoutManager::attach( + const uno::Reference< frame::XFrame >& xFrame, + const uno::Reference< ui::XUIConfigurationManager >& xModuleCfgMgr, + const uno::Reference< ui::XUIConfigurationManager >& xDocCfgMgr, + const uno::Reference< container::XNameAccess >& xPersistentWindowState ) +{ + // reset toolbar manager if we lose our current frame + if ( m_xFrame.is() && m_xFrame != xFrame ) + reset(); + + SolarMutexGuard g; + m_xFrame = xFrame; + m_xModuleCfgMgr = xModuleCfgMgr; + m_xDocCfgMgr = xDocCfgMgr; + m_xPersistentWindowState = xPersistentWindowState; + m_bComponentAttached = true; +} + +bool ToolbarLayoutManager::isPreviewFrame() +{ + SolarMutexGuard g; + if (m_ePreviewDetection == PREVIEWFRAME_UNKNOWN) + { + uno::Reference< frame::XFrame > xFrame( m_xFrame ); + + uno::Reference< frame::XModel > xModel( impl_getModelFromFrame( xFrame )); + + m_ePreviewDetection = (implts_isPreviewModel( xModel ) ? PREVIEWFRAME_YES : PREVIEWFRAME_NO); + } + return m_ePreviewDetection == PREVIEWFRAME_YES; +} + +void ToolbarLayoutManager::createStaticToolbars() +{ + resetDockingArea(); + implts_createCustomToolBars(); + implts_createAddonsToolBars(); + implts_createNonContextSensitiveToolBars(); + implts_sortUIElements(); +} + +bool ToolbarLayoutManager::requestToolbar( const OUString& rResourceURL ) +{ + if (isPreviewFrame()) + return false; // no toolbars for preview frame! + + bool bNotify( false ); + bool bMustCallCreate( false ); + uno::Reference< ui::XUIElement > xUIElement; + + UIElement aRequestedToolbar = impl_findToolbar( rResourceURL ); + if ( aRequestedToolbar.m_aName != rResourceURL ) + { + bMustCallCreate = true; + aRequestedToolbar.m_aName = rResourceURL; + aRequestedToolbar.m_aType = UIRESOURCETYPE_TOOLBAR; + aRequestedToolbar.m_xUIElement = xUIElement; + implts_readWindowStateData( rResourceURL, aRequestedToolbar ); + } + + xUIElement = aRequestedToolbar.m_xUIElement; + if ( !xUIElement.is() ) + bMustCallCreate = true; + + bool bCreateOrShowToolbar( aRequestedToolbar.m_bVisible && !aRequestedToolbar.m_bMasterHide ); + + if ( bCreateOrShowToolbar ) + bNotify = bMustCallCreate ? createToolbar( rResourceURL ) : showToolbar( rResourceURL ); + + return bNotify; +} + +bool ToolbarLayoutManager::createToolbar( const OUString& rResourceURL ) +{ + bool bNotify( false ); + + uno::Reference xFrame; + uno::Reference xContainerWindow; + { + SolarMutexGuard aReadLock; + xFrame.set(m_xFrame); + xContainerWindow.set(m_xContainerWindow); + } + + if ( !xFrame.is() || !xContainerWindow.is() ) + return false; + + UIElement aToolbarElement = implts_findToolbar( rResourceURL ); + if ( !aToolbarElement.m_xUIElement.is() ) + { + uno::Reference< ui::XUIElement > xUIElement; + + uno::Sequence< beans::PropertyValue > aPropSeq{ + comphelper::makePropertyValue("Frame", xFrame), + comphelper::makePropertyValue("Persistent", true) + }; + uno::Reference xUIElementFactory; + { + SolarMutexGuard aReadLock; + xUIElementFactory.set(m_xUIElementFactoryManager); + } + + implts_setToolbarCreation(); + try + { + if ( xUIElementFactory.is() ) + xUIElement = xUIElementFactory->createUIElement( rResourceURL, aPropSeq ); + } + catch (const container::NoSuchElementException&) + { + } + catch (const lang::IllegalArgumentException&) + { + } + implts_setToolbarCreation( false ); + + if ( xUIElement.is() ) + { + uno::Reference< awt::XWindow > xWindow( xUIElement->getRealInterface(), uno::UNO_QUERY ); + uno::Reference< awt::XDockableWindow > xDockWindow( xWindow, uno::UNO_QUERY ); + if ( xDockWindow.is() && xWindow.is() ) + { + try + { + xDockWindow->addDockableWindowListener( uno::Reference< awt::XDockableWindowListener >(this) ); + xWindow->addWindowListener( uno::Reference< awt::XWindowListener >(this) ); + xDockWindow->enableDocking( true ); + } + catch (const uno::Exception&) + { + } + } + + bool bVisible = false; + bool bFloating = false; + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + { + SolarMutexClearableGuard aWriteLock; + + UIElement& rElement = impl_findToolbar(rResourceURL); + if (rElement.m_xUIElement.is()) + { + // somebody else must have created it while we released + // the SolarMutex - just dispose our new instance and + // do nothing. (We have to dispose either the new or the + // existing m_xUIElement.) + aWriteLock.clear(); + uno::Reference const xC(xUIElement, uno::UNO_QUERY); + xC->dispose(); + return false; + } + if (!rElement.m_aName.isEmpty()) + { + // Reuse a local entry so we are able to use the latest + // UI changes for this document. + implts_setElementData(rElement, xDockWindow); + rElement.m_xUIElement = xUIElement; + bVisible = rElement.m_bVisible; + bFloating = rElement.m_bFloating; + } + else + { + // Create new UI element and try to read its state data + UIElement aNewToolbar(rResourceURL, UIRESOURCETYPE_TOOLBAR, xUIElement); + implts_readWindowStateData(rResourceURL, aNewToolbar); + implts_setElementData(aNewToolbar, xDockWindow); + implts_insertToolbar(aNewToolbar); + bVisible = aNewToolbar.m_bVisible; + bFloating = rElement.m_bFloating; + } + } + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + + // set toolbar menu style according to customize command state + SvtCommandOptions aCmdOptions; + + SolarMutexGuard aGuard; + VclPtr pWindow = VCLUnoHelper::GetWindow( xWindow ); + if ( pWindow && pWindow->GetType() == WindowType::TOOLBOX ) + { + ToolBox* pToolbar = static_cast(pWindow.get()); + ToolBoxMenuType nMenuType = pToolbar->GetMenuType(); + if ( aCmdOptions.LookupDisabled( "ConfigureDialog" )) + pToolbar->SetMenuType( nMenuType & ~ToolBoxMenuType::Customize ); + else + pToolbar->SetMenuType( nMenuType | ToolBoxMenuType::Customize ); + } + bNotify = true; + + implts_sortUIElements(); + + if ( bVisible && !bFloating ) + implts_setLayoutDirty(); + } + } + + return bNotify; +} + +bool ToolbarLayoutManager::destroyToolbar( std::u16string_view rResourceURL ) +{ + uno::Reference< lang::XComponent > xComponent; + + bool bNotify( false ); + bool bMustBeSorted( false ); + bool bMustLayouted( false ); + bool bMustBeDestroyed( !o3tl::starts_with(rResourceURL, u"private:resource/toolbar/addon_") ); + + { + SolarMutexGuard aWriteLock; + for (auto & elem : m_aUIElements) + { + if (elem.m_aName == rResourceURL) + { + xComponent.set(elem.m_xUIElement, uno::UNO_QUERY); + if (bMustBeDestroyed) + elem.m_xUIElement.clear(); + else + elem.m_bVisible = false; + break; + } + } + } + + uno::Reference< ui::XUIElement > xUIElement( xComponent, uno::UNO_QUERY ); + if ( xUIElement.is() ) + { + uno::Reference< awt::XWindow > xWindow( xUIElement->getRealInterface(), uno::UNO_QUERY ); + uno::Reference< awt::XDockableWindow > xDockWindow( xWindow, uno::UNO_QUERY ); + + if ( bMustBeDestroyed ) + { + try + { + if ( xWindow.is() ) + xWindow->removeWindowListener( uno::Reference< awt::XWindowListener >(this) ); + } + catch (const uno::Exception&) + { + } + + try + { + if ( xDockWindow.is() ) + xDockWindow->removeDockableWindowListener( uno::Reference< awt::XDockableWindowListener >(this) ); + } + catch (const uno::Exception&) + { + } + } + else + { + if ( xWindow.is() ) + xWindow->setVisible( false ); + bNotify = true; + } + + if ( !xDockWindow->isFloating() ) + bMustLayouted = true; + bMustBeSorted = true; + } + + if ( bMustBeDestroyed ) + { + if ( xComponent.is() ) + xComponent->dispose(); + bNotify = true; + } + + if ( bMustLayouted ) + implts_setLayoutDirty(); + + if ( bMustBeSorted ) + implts_sortUIElements(); + + return bNotify; +} + +void ToolbarLayoutManager::destroyToolbars() +{ + UIElementVector aUIElementVector; + implts_getUIElementVectorCopy( aUIElementVector ); + + { + SolarMutexGuard aWriteLock; + m_aUIElements.clear(); + m_bLayoutDirty = true; + } + + for (auto const& elem : aUIElementVector) + { + uno::Reference< lang::XComponent > xComponent( elem.m_xUIElement, uno::UNO_QUERY ); + if ( xComponent.is() ) + xComponent->dispose(); + } +} + +bool ToolbarLayoutManager::showToolbar( std::u16string_view rResourceURL ) +{ + UIElement aUIElement = implts_findToolbar( rResourceURL ); + + SolarMutexGuard aGuard; + vcl::Window* pWindow = getWindowFromXUIElement( aUIElement.m_xUIElement ); + + // Addons appear to need to be populated at start, but we don't + // want to populate them with (scaled) images until later. + AddonsToolBarWrapper *pAddOns; + pAddOns = dynamic_cast( aUIElement.m_xUIElement.get()); + if (pAddOns) + pAddOns->populateImages(); + + if ( pWindow ) + { + if ( !aUIElement.m_bFloating ) + implts_setLayoutDirty(); + else + pWindow->Show( true, ShowFlags::NoFocusChange | ShowFlags::NoActivate ); + + aUIElement.m_bVisible = true; + implts_writeWindowStateData( aUIElement ); + implts_setToolbar( aUIElement ); + implts_sortUIElements(); + return true; + } + + return false; +} + +bool ToolbarLayoutManager::hideToolbar( std::u16string_view rResourceURL ) +{ + UIElement aUIElement = implts_findToolbar( rResourceURL ); + + SolarMutexGuard aGuard; + vcl::Window* pWindow = getWindowFromXUIElement( aUIElement.m_xUIElement ); + if ( pWindow ) + { + pWindow->Show( false ); + if ( !aUIElement.m_bFloating ) + implts_setLayoutDirty(); + + aUIElement.m_bVisible = false; + implts_writeWindowStateData( aUIElement ); + implts_setToolbar( aUIElement ); + return true; + } + + return false; +} + +void ToolbarLayoutManager::refreshToolbarsVisibility( bool bAutomaticToolbars ) +{ + UIElementVector aUIElementVector; + + if ( !bAutomaticToolbars ) + return; + + implts_getUIElementVectorCopy( aUIElementVector ); + + UIElement aUIElement; + SolarMutexGuard aGuard; + for (auto const& elem : aUIElementVector) + { + if ( implts_readWindowStateData( elem.m_aName, aUIElement ) && + ( elem.m_bVisible != aUIElement.m_bVisible ) && !elem.m_bMasterHide ) + { + SolarMutexGuard g; + UIElement& rUIElement = impl_findToolbar( elem.m_aName ); + if ( rUIElement.m_aName == elem.m_aName ) + { + rUIElement.m_bVisible = aUIElement.m_bVisible; + implts_setLayoutDirty(); + } + } + } +} + +void ToolbarLayoutManager::setFloatingToolbarsVisibility( bool bVisible ) +{ + UIElementVector aUIElementVector; + implts_getUIElementVectorCopy( aUIElementVector ); + + SolarMutexGuard aGuard; + for (auto const& elem : aUIElementVector) + { + vcl::Window* pWindow = getWindowFromXUIElement( elem.m_xUIElement ); + if ( pWindow && elem.m_bFloating ) + { + if ( bVisible ) + { + if ( elem.m_bVisible && !elem.m_bMasterHide ) + pWindow->Show( true, ShowFlags::NoFocusChange | ShowFlags::NoActivate ); + } + else + pWindow->Show( false ); + } + } +} + +void ToolbarLayoutManager::setVisible( bool bVisible ) +{ + UIElementVector aUIElementVector; + implts_getUIElementVectorCopy( aUIElementVector ); + + SolarMutexGuard aGuard; + for (auto & elem : aUIElementVector) + { + if (!elem.m_bFloating) + { + elem.m_bMasterHide = !bVisible; + implts_setToolbar(elem); + implts_setLayoutDirty(); + } + + vcl::Window* pWindow = getWindowFromXUIElement( elem.m_xUIElement ); + if ( pWindow ) + { + bool bSetVisible( elem.m_bVisible && bVisible ); + if ( !bSetVisible ) + pWindow->Hide(); + else + { + if ( elem.m_bFloating ) + pWindow->Show(true, ShowFlags::NoFocusChange | ShowFlags::NoActivate ); + } + } + } + + if ( !bVisible ) + resetDockingArea(); +} + +bool ToolbarLayoutManager::dockToolbar( std::u16string_view rResourceURL, ui::DockingArea eDockingArea, const awt::Point& aPos ) +{ + UIElement aUIElement = implts_findToolbar( rResourceURL ); + + if ( !aUIElement.m_xUIElement.is() ) + return false; + + try + { + uno::Reference< awt::XWindow > xWindow( aUIElement.m_xUIElement->getRealInterface(), uno::UNO_QUERY ); + uno::Reference< awt::XDockableWindow > xDockWindow( xWindow, uno::UNO_QUERY ); + if ( xDockWindow.is() ) + { + if ( eDockingArea != ui::DockingArea_DOCKINGAREA_DEFAULT ) + aUIElement.m_aDockedData.m_nDockedArea = eDockingArea; + + if ( !isDefaultPos( aPos )) + aUIElement.m_aDockedData.m_aPos = aPos; + + if ( !xDockWindow->isFloating() ) + { + vcl::Window* pWindow( nullptr ); + ToolBox* pToolBox( nullptr ); + + { + SolarMutexGuard aGuard; + pWindow = VCLUnoHelper::GetWindow( xWindow ); + if ( pWindow && pWindow->GetType() == WindowType::TOOLBOX ) + { + pToolBox = static_cast(pWindow); + + // We have to set the alignment of the toolbox. It's possible that the toolbox is moved from a + // horizontal to a vertical docking area! + pToolBox->SetAlign( ImplConvertAlignment( aUIElement.m_aDockedData.m_nDockedArea )); + } + } + + if ( hasDefaultPosValue( aUIElement.m_aDockedData.m_aPos )) + { + // Docking on its default position without a preset position - + // we have to find a good place for it. + ::Size aSize; + + SolarMutexGuard aGuard; + { + if (pToolBox) + aSize = pToolBox->CalcWindowSizePixel( 1, ImplConvertAlignment( aUIElement.m_aDockedData.m_nDockedArea ) ); + else if (pWindow) + aSize = pWindow->GetSizePixel(); + } + + ::Point aPixelPos; + awt::Point aDockPos; + implts_findNextDockingPos(aUIElement.m_aDockedData.m_nDockedArea, aSize, aDockPos, aPixelPos ); + aUIElement.m_aDockedData.m_aPos = aDockPos; + } + } + + implts_setToolbar( aUIElement ); + + if ( xDockWindow->isFloating() ) + { + // ATTENTION: This will call toggleFloatingMode() via notifications which + // sets the floating member of the UIElement correctly! + xDockWindow->setFloatingMode( false ); + } + else + { + implts_writeWindowStateData( aUIElement ); + implts_sortUIElements(); + + if ( aUIElement.m_bVisible ) + implts_setLayoutDirty(); + } + return true; + } + } + catch (const lang::DisposedException&) + { + } + + return false; +} + +bool ToolbarLayoutManager::dockAllToolbars() +{ + std::vector< OUString > aToolBarNameVector; + + { + SolarMutexGuard aReadLock; + for (auto const& elem : m_aUIElements) + { + if (elem.m_aType == "toolbar" && elem.m_xUIElement.is() && elem.m_bFloating + && elem.m_bVisible) + aToolBarNameVector.push_back(elem.m_aName); + } + } + + bool bResult(true); + const sal_uInt32 nCount = aToolBarNameVector.size(); + for ( sal_uInt32 i = 0; i < nCount; ++i ) + { + awt::Point aPoint; + aPoint.X = aPoint.Y = SAL_MAX_INT32; + bResult &= dockToolbar( aToolBarNameVector[i], ui::DockingArea_DOCKINGAREA_DEFAULT, aPoint ); + } + + return bResult; +} + +void ToolbarLayoutManager::childWindowEvent( VclSimpleEvent const * pEvent ) +{ + // To enable toolbar controllers to change their image when a sub-toolbar function + // is activated, we need this mechanism. We have NO connection between these toolbars + // anymore! + auto pWindowEvent = dynamic_cast< const VclWindowEvent* >(pEvent); + if (!pWindowEvent) + return; + + if ( pEvent->GetId() == VclEventId::ToolboxSelect ) + { + OUString aToolbarName; + OUString aCommand; + ToolBox* pToolBox = getToolboxPtr( pWindowEvent->GetWindow() ); + + if ( pToolBox ) + { + aToolbarName = retrieveToolbarNameFromHelpURL( pToolBox ); + ToolBoxItemId nId = pToolBox->GetCurItemId(); + if ( nId > ToolBoxItemId(0) ) + aCommand = pToolBox->GetItemCommand( nId ); + } + + if ( !aToolbarName.isEmpty() && !aCommand.isEmpty() ) + { + SolarMutexClearableGuard aReadLock; + ::std::vector< uno::Reference< ui::XUIFunctionListener > > aListenerArray; + + for (auto const& elem : m_aUIElements) + { + if ( elem.m_xUIElement.is() ) + { + uno::Reference< ui::XUIFunctionListener > xListener( elem.m_xUIElement, uno::UNO_QUERY ); + if ( xListener.is() ) + aListenerArray.push_back( xListener ); + } + } + aReadLock.clear(); + + const sal_uInt32 nCount = aListenerArray.size(); + for ( sal_uInt32 i = 0; i < nCount; ++i ) + { + try + { + aListenerArray[i]->functionExecute( aToolbarName, aCommand ); + } + catch (const uno::RuntimeException&) + { + throw; + } + catch (const uno::Exception&) + { + } + } + } + } + else if ( pEvent->GetId() == VclEventId::ToolboxFormatChanged ) + { + if ( !implts_isToolbarCreationActive() ) + { + ToolBox* pToolBox = getToolboxPtr( pWindowEvent->GetWindow() ); + if ( pToolBox ) + { + OUString aToolbarName = retrieveToolbarNameFromHelpURL( pToolBox ); + if ( !aToolbarName.isEmpty() ) + { + OUString aToolbarUrl = "private:resource/toolbar/" + aToolbarName; + + UIElement aToolbar = implts_findToolbar( aToolbarUrl ); + if ( aToolbar.m_xUIElement.is() && !aToolbar.m_bFloating ) + { + implts_setLayoutDirty(); + m_pParentLayouter->requestLayout(); + } + } + } + } + } +} + +void ToolbarLayoutManager::resetDockingArea() +{ + SolarMutexClearableGuard aReadLock; + uno::Reference< awt::XWindow > xTopDockingWindow( m_xDockAreaWindows[int(ui::DockingArea_DOCKINGAREA_TOP)] ); + uno::Reference< awt::XWindow > xLeftDockingWindow( m_xDockAreaWindows[int(ui::DockingArea_DOCKINGAREA_LEFT)] ); + uno::Reference< awt::XWindow > xRightDockingWindow( m_xDockAreaWindows[int(ui::DockingArea_DOCKINGAREA_RIGHT)] ); + uno::Reference< awt::XWindow > xBottomDockingWindow( m_xDockAreaWindows[int(ui::DockingArea_DOCKINGAREA_BOTTOM)] ); + aReadLock.clear(); + + if ( xTopDockingWindow.is() ) + xTopDockingWindow->setPosSize( 0, 0, 0, 0, awt::PosSize::POSSIZE ); + if ( xLeftDockingWindow.is() ) + xLeftDockingWindow->setPosSize( 0, 0, 0, 0, awt::PosSize::POSSIZE ); + if ( xRightDockingWindow.is() ) + xRightDockingWindow->setPosSize( 0, 0, 0, 0, awt::PosSize::POSSIZE ); + if ( xBottomDockingWindow.is() ) + xBottomDockingWindow->setPosSize( 0, 0, 0, 0, awt::PosSize::POSSIZE ); +} + +void ToolbarLayoutManager::setParentWindow( + const uno::Reference< awt::XVclWindowPeer >& xParentWindow ) +{ + static const char DOCKINGAREASTRING[] = "dockingarea"; + + uno::Reference< awt::XWindow > xTopDockWindow( createToolkitWindow( m_xContext, xParentWindow, DOCKINGAREASTRING ), uno::UNO_QUERY ); + uno::Reference< awt::XWindow > xLeftDockWindow( createToolkitWindow( m_xContext, xParentWindow, DOCKINGAREASTRING ), uno::UNO_QUERY ); + uno::Reference< awt::XWindow > xRightDockWindow( createToolkitWindow( m_xContext, xParentWindow, DOCKINGAREASTRING ), uno::UNO_QUERY ); + uno::Reference< awt::XWindow > xBottomDockWindow( createToolkitWindow( m_xContext, xParentWindow, DOCKINGAREASTRING ), uno::UNO_QUERY ); + + { + SolarMutexGuard aWriteLock; + m_xContainerWindow.set(xParentWindow, uno::UNO_QUERY); + m_xDockAreaWindows[int(ui::DockingArea_DOCKINGAREA_TOP)] = xTopDockWindow; + m_xDockAreaWindows[int(ui::DockingArea_DOCKINGAREA_LEFT)] = xLeftDockWindow; + m_xDockAreaWindows[int(ui::DockingArea_DOCKINGAREA_RIGHT)] = xRightDockWindow; + m_xDockAreaWindows[int(ui::DockingArea_DOCKINGAREA_BOTTOM)] = xBottomDockWindow; + } + + if ( xParentWindow.is() ) + { + SolarMutexGuard aGuard; + VclPtr< ::DockingAreaWindow > pWindow = dynamic_cast< ::DockingAreaWindow* >(VCLUnoHelper::GetWindow( xTopDockWindow ) ); + if( pWindow ) + pWindow->SetAlign( WindowAlign::Top ); + pWindow = dynamic_cast< ::DockingAreaWindow* >(VCLUnoHelper::GetWindow( xBottomDockWindow ) ); + if( pWindow ) + pWindow->SetAlign( WindowAlign::Bottom ); + pWindow = dynamic_cast< ::DockingAreaWindow* >(VCLUnoHelper::GetWindow( xLeftDockWindow ) ); + if( pWindow ) + pWindow->SetAlign( WindowAlign::Left ); + pWindow = dynamic_cast< ::DockingAreaWindow* >(VCLUnoHelper::GetWindow( xRightDockWindow ) ); + if( pWindow ) + pWindow->SetAlign( WindowAlign::Right ); + implts_reparentToolbars(); + } + else + { + destroyToolbars(); + resetDockingArea(); + } +} + +void ToolbarLayoutManager::setDockingAreaOffsets( const ::tools::Rectangle& rOffsets ) +{ + SolarMutexGuard g; + m_aDockingAreaOffsets = rOffsets; + m_bLayoutDirty = true; +} + +OUString ToolbarLayoutManager::implts_generateGenericAddonToolbarTitle( sal_Int32 nNumber ) const +{ + OUString aAddonGenericTitle(FwkResId(STR_TOOLBAR_TITLE_ADDON)); + const vcl::I18nHelper& rI18nHelper = Application::GetSettings().GetUILocaleI18nHelper(); + + OUString aNumStr = rI18nHelper.GetNum( nNumber, 0, false, false ); + aAddonGenericTitle = aAddonGenericTitle.replaceFirst( "%num%", aNumStr ); + + return aAddonGenericTitle; +} + +void ToolbarLayoutManager::implts_createAddonsToolBars() +{ + SolarMutexClearableGuard aWriteLock; + if ( !m_pAddonOptions ) + m_pAddonOptions.reset( new AddonsOptions ); + + uno::Reference< ui::XUIElementFactory > xUIElementFactory( m_xUIElementFactoryManager ); + uno::Reference< frame::XFrame > xFrame( m_xFrame ); + aWriteLock.clear(); + + if (isPreviewFrame()) + return; // no addon toolbars for preview frame! + + uno::Sequence< uno::Sequence< beans::PropertyValue > > aAddonToolBarData; + uno::Reference< ui::XUIElement > xUIElement; + + sal_uInt32 nCount = m_pAddonOptions->GetAddonsToolBarCount(); + + uno::Sequence< beans::PropertyValue > aPropSeq( 2 ); + auto pPropSeq = aPropSeq.getArray(); + pPropSeq[0].Name = "Frame"; + pPropSeq[0].Value <<= xFrame; + pPropSeq[1].Name = "ConfigurationData"; + for ( sal_uInt32 i = 0; i < nCount; i++ ) + { + OUString aAddonToolBarName( "private:resource/toolbar/addon_" + + m_pAddonOptions->GetAddonsToolbarResourceName(i) ); + aAddonToolBarData = m_pAddonOptions->GetAddonsToolBarPart( i ); + pPropSeq[1].Value <<= aAddonToolBarData; + + UIElement aElement = implts_findToolbar( aAddonToolBarName ); + + // #i79828 + // It's now possible that we are called more than once. Be sure to not create + // add-on toolbars more than once! + if ( aElement.m_xUIElement.is() ) + continue; + + try + { + xUIElement = xUIElementFactory->createUIElement( aAddonToolBarName, aPropSeq ); + if ( xUIElement.is() ) + { + uno::Reference< awt::XDockableWindow > xDockWindow( xUIElement->getRealInterface(), uno::UNO_QUERY ); + if ( xDockWindow.is() ) + { + try + { + xDockWindow->addDockableWindowListener( uno::Reference< awt::XDockableWindowListener >(this) ); + xDockWindow->enableDocking( true ); + uno::Reference< awt::XWindow > xWindow( xDockWindow, uno::UNO_QUERY ); + if ( xWindow.is() ) + xWindow->addWindowListener( uno::Reference< awt::XWindowListener >(this) ); + } + catch (const uno::Exception&) + { + } + } + + OUString aGenericAddonTitle = implts_generateGenericAddonToolbarTitle( i+1 ); + + if ( !aElement.m_aName.isEmpty() ) + { + // Reuse a local entry so we are able to use the latest + // UI changes for this document. + implts_setElementData( aElement, xDockWindow ); + aElement.m_xUIElement = xUIElement; + if ( aElement.m_aUIName.isEmpty() ) + { + aElement.m_aUIName = aGenericAddonTitle; + implts_writeWindowStateData( aElement ); + } + } + else + { + // Create new UI element and try to read its state data + UIElement aNewToolbar( aAddonToolBarName, "toolbar", xUIElement ); + aNewToolbar.m_bFloating = true; + implts_readWindowStateData( aAddonToolBarName, aNewToolbar ); + implts_setElementData( aNewToolbar, xDockWindow ); + if ( aNewToolbar.m_aUIName.isEmpty() ) + { + aNewToolbar.m_aUIName = aGenericAddonTitle; + implts_writeWindowStateData( aNewToolbar ); + } + implts_insertToolbar( aNewToolbar ); + } + + uno::Reference< awt::XWindow > xWindow( xDockWindow, uno::UNO_QUERY ); + if ( xWindow.is() ) + { + // Set generic title for add-on toolbar + SolarMutexGuard aGuard; + VclPtr pWindow = VCLUnoHelper::GetWindow( xWindow ); + if ( pWindow->GetText().isEmpty() ) + pWindow->SetText( aGenericAddonTitle ); + if ( pWindow->GetType() == WindowType::TOOLBOX ) + { + ToolBox* pToolbar = static_cast(pWindow.get()); + pToolbar->SetMenuType(); + } + } + } + } + catch (const container::NoSuchElementException&) + { + } + catch (const lang::IllegalArgumentException&) + { + } + } +} + +void ToolbarLayoutManager::implts_createCustomToolBars() +{ + SolarMutexClearableGuard aReadLock; + if ( !m_bComponentAttached ) + return; + + uno::Reference< frame::XFrame > xFrame( m_xFrame ); + uno::Reference< ui::XUIConfigurationManager > xModuleCfgMgr = m_xModuleCfgMgr; + uno::Reference< ui::XUIConfigurationManager > xDocCfgMgr = m_xDocCfgMgr; + aReadLock.clear(); + + if ( !xFrame.is() ) + return; + + if (isPreviewFrame()) + return; // no custom toolbars for preview frame! + + uno::Sequence< uno::Sequence< beans::PropertyValue > > aTbxSeq; + if ( xDocCfgMgr.is() ) + { + aTbxSeq = xDocCfgMgr->getUIElementsInfo( ui::UIElementType::TOOLBAR ); + implts_createCustomToolBars( aTbxSeq ); // first create all document based toolbars + } + if ( xModuleCfgMgr.is() ) + { + aTbxSeq = xModuleCfgMgr->getUIElementsInfo( ui::UIElementType::TOOLBAR ); + implts_createCustomToolBars( aTbxSeq ); // second create module based toolbars + } +} + +void ToolbarLayoutManager::implts_createNonContextSensitiveToolBars() +{ + SolarMutexClearableGuard aReadLock; + + if ( !m_xPersistentWindowState.is() || !m_xFrame.is() || !m_bComponentAttached ) + return; + + uno::Reference< container::XNameAccess > xPersistentWindowState( m_xPersistentWindowState ); + aReadLock.clear(); + + if (isPreviewFrame()) + return; + + std::vector< OUString > aMakeVisibleToolbars; + + try + { + const uno::Sequence< OUString > aToolbarNames = xPersistentWindowState->getElementNames(); + + if ( aToolbarNames.hasElements() ) + { + OUString aElementType; + OUString aElementName; + + aMakeVisibleToolbars.reserve(aToolbarNames.getLength()); + + SolarMutexGuard g; + + for ( OUString const & aName : aToolbarNames ) + { + parseResourceURL( aName, aElementType, aElementName ); + + // Check that we only create: + // - Toolbars (the statusbar is also member of the persistent window state) + // - Not custom toolbars, there are created with their own method (implts_createCustomToolbars) + if ( aElementType.equalsIgnoreAsciiCase("toolbar") && + aElementName.indexOf( "custom_" ) == -1 ) + { + UIElement aNewToolbar = implts_findToolbar( aName ); + bool bFound = ( aNewToolbar.m_aName == aName ); + if ( !bFound ) + implts_readWindowStateData( aName, aNewToolbar ); + + if ( aNewToolbar.m_bVisible && !aNewToolbar.m_bContextSensitive ) + { + if ( !bFound ) + implts_insertToolbar( aNewToolbar ); + aMakeVisibleToolbars.push_back( aName ); + } + } + } + } + } + catch (const uno::RuntimeException&) + { + throw; + } + catch (const uno::Exception&) + { + } + + for (auto const& rURL : aMakeVisibleToolbars) + { + requestToolbar(rURL); + } +} + +void ToolbarLayoutManager::implts_createCustomToolBars( const uno::Sequence< uno::Sequence< beans::PropertyValue > >& aTbxSeqSeq ) +{ + for ( const uno::Sequence< beans::PropertyValue >& rTbxSeq : aTbxSeqSeq ) + { + OUString aTbxResName; + OUString aTbxTitle; + for ( const beans::PropertyValue& rProp : rTbxSeq ) + { + if ( rProp.Name == "ResourceURL" ) + rProp.Value >>= aTbxResName; + else if ( rProp.Name == "UIName" ) + rProp.Value >>= aTbxTitle; + } + + // Only create custom toolbars. Their name have to start with "custom_"! + if ( !aTbxResName.isEmpty() && ( aTbxResName.indexOf( "custom_" ) != -1 ) ) + implts_createCustomToolBar( aTbxResName, aTbxTitle ); + } +} + +void ToolbarLayoutManager::implts_createCustomToolBar( const OUString& aTbxResName, const OUString& aTitle ) +{ + if ( aTbxResName.isEmpty() ) + return; + + if ( !createToolbar( aTbxResName ) ) + SAL_WARN("fwk.uielement", "ToolbarLayoutManager cannot create custom toolbar"); + + uno::Reference< ui::XUIElement > xUIElement = getToolbar( aTbxResName ); + + if ( !aTitle.isEmpty() && xUIElement.is() ) + { + SolarMutexGuard aGuard; + + vcl::Window* pWindow = getWindowFromXUIElement( xUIElement ); + if ( pWindow ) + pWindow->SetText( aTitle ); + } +} + +void ToolbarLayoutManager::implts_reparentToolbars() +{ + SolarMutexClearableGuard aWriteLock; + UIElementVector aUIElementVector = m_aUIElements; + VclPtr pContainerWindow = VCLUnoHelper::GetWindow( m_xContainerWindow ); + VclPtr pTopDockWindow = VCLUnoHelper::GetWindow( m_xDockAreaWindows[int(ui::DockingArea_DOCKINGAREA_TOP)] ); + VclPtr pBottomDockWindow = VCLUnoHelper::GetWindow( m_xDockAreaWindows[int(ui::DockingArea_DOCKINGAREA_BOTTOM)] ); + VclPtr pLeftDockWindow = VCLUnoHelper::GetWindow( m_xDockAreaWindows[int(ui::DockingArea_DOCKINGAREA_LEFT)] ); + VclPtr pRightDockWindow = VCLUnoHelper::GetWindow( m_xDockAreaWindows[int(ui::DockingArea_DOCKINGAREA_RIGHT)] ); + aWriteLock.clear(); + + SolarMutexGuard aGuard; + if ( !pContainerWindow ) + return; + + for (auto const& elem : aUIElementVector) + { + uno::Reference< ui::XUIElement > xUIElement( elem.m_xUIElement ); + if ( xUIElement.is() ) + { + uno::Reference< awt::XWindow > xWindow; + try + { + // We have to retrieve the window reference with try/catch as it is + // possible that all elements have been disposed! + xWindow.set( xUIElement->getRealInterface(), uno::UNO_QUERY ); + } + catch (const uno::RuntimeException&) + { + throw; + } + catch (const uno::Exception&) + { + } + + VclPtr pWindow = VCLUnoHelper::GetWindow( xWindow ); + if ( pWindow ) + { + // Reparent our child windows according to their current state. + if ( elem.m_bFloating ) + pWindow->SetParent( pContainerWindow ); + else + { + if ( elem.m_aDockedData.m_nDockedArea == ui::DockingArea_DOCKINGAREA_TOP ) + pWindow->SetParent( pTopDockWindow ); + else if ( elem.m_aDockedData.m_nDockedArea == ui::DockingArea_DOCKINGAREA_BOTTOM ) + pWindow->SetParent( pBottomDockWindow ); + else if ( elem.m_aDockedData.m_nDockedArea == ui::DockingArea_DOCKINGAREA_LEFT ) + pWindow->SetParent( pLeftDockWindow ); + else + pWindow->SetParent( pRightDockWindow ); + } + } + } + } +} + +void ToolbarLayoutManager::implts_setToolbarCreation( bool bStart ) +{ + SolarMutexGuard g; + m_bToolbarCreation = bStart; +} + +bool ToolbarLayoutManager::implts_isToolbarCreationActive() +{ + SolarMutexGuard g; + return m_bToolbarCreation; +} + +void ToolbarLayoutManager::implts_setElementData( UIElement& rElement, const uno::Reference< awt::XDockableWindow >& rDockWindow ) +{ + SolarMutexClearableGuard aReadLock; + bool bShowElement( rElement.m_bVisible && !rElement.m_bMasterHide && implts_isParentWindowVisible() ); + aReadLock.clear(); + + uno::Reference< awt::XWindow2 > xWindow( rDockWindow, uno::UNO_QUERY ); + + vcl::Window* pWindow( nullptr ); + ToolBox* pToolBox( nullptr ); + + if ( !(rDockWindow.is() && xWindow.is()) ) + return; + + { + SolarMutexGuard aGuard; + pWindow = VCLUnoHelper::GetWindow( xWindow ); + if ( pWindow ) + { + OUString aText = pWindow->GetText(); + if ( aText.isEmpty() ) + pWindow->SetText( rElement.m_aUIName ); + if ( rElement.m_bNoClose ) + pWindow->SetStyle( pWindow->GetStyle() & ~WB_CLOSEABLE ); + if ( pWindow->GetType() == WindowType::TOOLBOX ) + pToolBox = static_cast(pWindow); + } + if ( pToolBox ) + { + pToolBox->SetButtonType( rElement.m_nStyle ); + if ( rElement.m_bNoClose ) + pToolBox->SetFloatStyle( pToolBox->GetFloatStyle() & ~WB_CLOSEABLE ); + } + } + + if ( rElement.m_bFloating ) + { + if ( pWindow ) + { + SolarMutexGuard aGuard; + OUString aText = pWindow->GetText(); + if ( aText.isEmpty() ) + pWindow->SetText( rElement.m_aUIName ); + } + + awt::Point aPos(rElement.m_aFloatingData.m_aPos); + bool bWriteData( false ); + bool bUndefPos = hasDefaultPosValue( rElement.m_aFloatingData.m_aPos ); + bool bSetSize = ( rElement.m_aFloatingData.m_aSize.Width != 0 && + rElement.m_aFloatingData.m_aSize.Height != 0 ); + rDockWindow->setFloatingMode( true ); + if ( bUndefPos ) + { + aPos = implts_findNextCascadeFloatingPos(); + rElement.m_aFloatingData.m_aPos = aPos; // set new cascaded position + bWriteData = true; + } + + if( bSetSize ) + xWindow->setOutputSize(rElement.m_aFloatingData.m_aSize); + else + { + if( pToolBox ) + { + // set an optimal initial floating size + SolarMutexGuard aGuard; + ::Size aSize( pToolBox->CalcFloatingWindowSizePixel() ); + pToolBox->SetOutputSizePixel( aSize ); + } + } + + // #i60882# IMPORTANT: Set position after size as it is + // possible that we position some part of the toolbar + // outside of the desktop. A default constructed toolbar + // always has one line. Now VCL automatically + // position the toolbar back into the desktop. Therefore + // we resize the toolbar with the new (wrong) position. + // To fix this problem we have to set the size BEFORE the + // position. + xWindow->setPosSize( aPos.X, aPos.Y, 0, 0, awt::PosSize::POS ); + + if ( bWriteData ) + implts_writeWindowStateData( rElement ); + if ( bShowElement && pWindow ) + { + SolarMutexGuard aGuard; + pWindow->Show( true, ShowFlags::NoFocusChange | ShowFlags::NoActivate ); + } + } + else + { + bool bSetSize( false ); + ::Point aPixelPos; + ::Size aSize; + + if ( pToolBox ) + { + SolarMutexGuard aGuard; + pToolBox->SetAlign( ImplConvertAlignment(rElement.m_aDockedData.m_nDockedArea ) ); + pToolBox->SetLineCount( 1 ); + rDockWindow->setFloatingMode( false ); + if ( rElement.m_aDockedData.m_bLocked ) + rDockWindow->lock(); + aSize = pToolBox->CalcWindowSizePixel(); + bSetSize = true; + + if ( isDefaultPos( rElement.m_aDockedData.m_aPos )) + { + awt::Point aDockPos; + implts_findNextDockingPos( rElement.m_aDockedData.m_nDockedArea, aSize, aDockPos, aPixelPos ); + rElement.m_aDockedData.m_aPos = aDockPos; + } + } + + xWindow->setPosSize( aPixelPos.X(), aPixelPos.Y(), 0, 0, awt::PosSize::POS ); + if( bSetSize ) + xWindow->setOutputSize( AWTSize( aSize) ); + + if ( pWindow ) + { + SolarMutexGuard aGuard; + if ( !bShowElement ) + pWindow->Hide(); + } + } +} + +void ToolbarLayoutManager::implts_destroyDockingAreaWindows() +{ + SolarMutexClearableGuard aWriteLock; + uno::Reference< awt::XWindow > xTopDockingWindow( m_xDockAreaWindows[int(ui::DockingArea_DOCKINGAREA_TOP)] ); + uno::Reference< awt::XWindow > xLeftDockingWindow( m_xDockAreaWindows[int(ui::DockingArea_DOCKINGAREA_LEFT)] ); + uno::Reference< awt::XWindow > xRightDockingWindow( m_xDockAreaWindows[int(ui::DockingArea_DOCKINGAREA_RIGHT)] ); + uno::Reference< awt::XWindow > xBottomDockingWindow( m_xDockAreaWindows[int(ui::DockingArea_DOCKINGAREA_BOTTOM)] ); + m_xDockAreaWindows[int(ui::DockingArea_DOCKINGAREA_TOP)].clear(); + m_xDockAreaWindows[int(ui::DockingArea_DOCKINGAREA_LEFT)].clear(); + m_xDockAreaWindows[int(ui::DockingArea_DOCKINGAREA_RIGHT)].clear(); + m_xDockAreaWindows[int(ui::DockingArea_DOCKINGAREA_BOTTOM)].clear(); + aWriteLock.clear(); + + // destroy windows + xTopDockingWindow->dispose(); + xLeftDockingWindow->dispose(); + xRightDockingWindow->dispose(); + xBottomDockingWindow->dispose(); +} + +// persistence methods + +bool ToolbarLayoutManager::implts_readWindowStateData( const OUString& aName, UIElement& rElementData ) +{ + return LayoutManager::readWindowStateData( aName, rElementData, m_xPersistentWindowState, + m_pGlobalSettings, m_bGlobalSettings, m_xContext ); +} + +void ToolbarLayoutManager::implts_writeWindowStateData( const UIElement& rElementData ) +{ + SolarMutexClearableGuard aWriteLock; + uno::Reference< container::XNameAccess > xPersistentWindowState( m_xPersistentWindowState ); + aWriteLock.clear(); + + bool bPersistent( false ); + uno::Reference< beans::XPropertySet > xPropSet( rElementData.m_xUIElement, uno::UNO_QUERY ); + if ( xPropSet.is() ) + { + try + { + // Check persistent flag of the user interface element + xPropSet->getPropertyValue("Persistent") >>= bPersistent; + } + catch (const beans::UnknownPropertyException&) + { + bPersistent = true; // Non-configurable elements should at least store their dimension/position + } + catch (const lang::WrappedTargetException&) + { + } + } + + if ( !(bPersistent && xPersistentWindowState.is()) ) + return; + + try + { + uno::Sequence aWindowState{ + comphelper::makePropertyValue(WINDOWSTATE_PROPERTY_DOCKED, !rElementData.m_bFloating), + comphelper::makePropertyValue(WINDOWSTATE_PROPERTY_VISIBLE, rElementData.m_bVisible), + comphelper::makePropertyValue(WINDOWSTATE_PROPERTY_DOCKINGAREA, + rElementData.m_aDockedData.m_nDockedArea), + comphelper::makePropertyValue(WINDOWSTATE_PROPERTY_DOCKPOS, + rElementData.m_aDockedData.m_aPos), + comphelper::makePropertyValue(WINDOWSTATE_PROPERTY_POS, + rElementData.m_aFloatingData.m_aPos), + comphelper::makePropertyValue(WINDOWSTATE_PROPERTY_SIZE, + rElementData.m_aFloatingData.m_aSize), + comphelper::makePropertyValue(WINDOWSTATE_PROPERTY_UINAME, rElementData.m_aUIName), + comphelper::makePropertyValue(WINDOWSTATE_PROPERTY_LOCKED, + rElementData.m_aDockedData.m_bLocked), + comphelper::makePropertyValue(WINDOWSTATE_PROPERTY_STYLE, + static_cast(rElementData.m_nStyle)) + }; + + OUString aName = rElementData.m_aName; + if ( xPersistentWindowState->hasByName( aName )) + { + uno::Reference< container::XNameReplace > xReplace( xPersistentWindowState, uno::UNO_QUERY ); + xReplace->replaceByName( aName, uno::Any( aWindowState )); + } + else + { + uno::Reference< container::XNameContainer > xInsert( xPersistentWindowState, uno::UNO_QUERY ); + xInsert->insertByName( aName, uno::Any( aWindowState )); + } + } + catch (const uno::Exception&) + { + } +} + +/****************************************************************************** + LOOKUP PART FOR TOOLBARS +******************************************************************************/ + +UIElement& ToolbarLayoutManager::impl_findToolbar( std::u16string_view aName ) +{ + static UIElement aEmptyElement; + + SolarMutexGuard g; + for (auto & elem : m_aUIElements) + { + if ( elem.m_aName == aName ) + return elem; + } + + return aEmptyElement; +} + +UIElement ToolbarLayoutManager::implts_findToolbar( std::u16string_view aName ) +{ + SolarMutexGuard g; + return impl_findToolbar( aName ); +} + +UIElement ToolbarLayoutManager::implts_findToolbar( const uno::Reference< uno::XInterface >& xToolbar ) +{ + UIElement aToolbar; + + SolarMutexGuard g; + for (auto const& elem : m_aUIElements) + { + if ( elem.m_xUIElement.is() ) + { + uno::Reference< uno::XInterface > xIfac( elem.m_xUIElement->getRealInterface(), uno::UNO_QUERY ); + if ( xIfac == xToolbar ) + { + aToolbar = elem; + break; + } + } + } + + return aToolbar; +} + +uno::Reference< awt::XWindow > ToolbarLayoutManager::implts_getXWindow( std::u16string_view aName ) +{ + uno::Reference< awt::XWindow > xWindow; + + SolarMutexGuard g; + for (auto const& elem : m_aUIElements) + { + if ( elem.m_aName == aName && elem.m_xUIElement.is() ) + { + xWindow.set( elem.m_xUIElement->getRealInterface(), uno::UNO_QUERY ); + break; + } + } + + return xWindow; +} + +vcl::Window* ToolbarLayoutManager::implts_getWindow( std::u16string_view aName ) +{ + uno::Reference< awt::XWindow > xWindow = implts_getXWindow( aName ); + VclPtr pWindow = VCLUnoHelper::GetWindow( xWindow ); + + return pWindow; +} + +bool ToolbarLayoutManager::implts_insertToolbar( const UIElement& rUIElement ) +{ + UIElement aTempData; + bool bFound( false ); + bool bResult( false ); + + aTempData = implts_findToolbar( rUIElement.m_aName ); + if ( aTempData.m_aName == rUIElement.m_aName ) + bFound = true; + + if ( !bFound ) + { + SolarMutexGuard g; + m_aUIElements.push_back( rUIElement ); + bResult = true; + } + + return bResult; +} + +void ToolbarLayoutManager::implts_setToolbar( const UIElement& rUIElement ) +{ + SolarMutexGuard g; + UIElement& rData = impl_findToolbar( rUIElement.m_aName ); + if ( rData.m_aName == rUIElement.m_aName ) + rData = rUIElement; + else + m_aUIElements.push_back( rUIElement ); +} + +/****************************************************************************** + LAYOUT CODE PART FOR TOOLBARS +******************************************************************************/ + +awt::Point ToolbarLayoutManager::implts_findNextCascadeFloatingPos() +{ + const sal_Int32 nHotZoneX = 50; + const sal_Int32 nHotZoneY = 50; + const sal_Int32 nCascadeIndentX = 15; + const sal_Int32 nCascadeIndentY = 15; + + SolarMutexClearableGuard aReadLock; + uno::Reference< awt::XWindow2 > xContainerWindow( m_xContainerWindow ); + uno::Reference< awt::XWindow > xTopDockingWindow( m_xDockAreaWindows[int(ui::DockingArea_DOCKINGAREA_TOP)] ); + uno::Reference< awt::XWindow > xLeftDockingWindow( m_xDockAreaWindows[int(ui::DockingArea_DOCKINGAREA_LEFT)] ); + aReadLock.clear(); + + awt::Point aStartPos( nCascadeIndentX, nCascadeIndentY ); + awt::Point aCurrPos( aStartPos ); + + if ( xContainerWindow.is() ) + { + SolarMutexGuard aGuard; + VclPtr pContainerWindow = VCLUnoHelper::GetWindow( xContainerWindow ); + if ( pContainerWindow ) + aStartPos = AWTPoint(pContainerWindow->OutputToScreenPixel(VCLPoint(aStartPos))); + } + + // Determine size of top and left docking area + awt::Rectangle aTopRect( xTopDockingWindow->getPosSize() ); + awt::Rectangle aLeftRect( xLeftDockingWindow->getPosSize() ); + + aStartPos.X += aLeftRect.Width + nCascadeIndentX; + aStartPos.Y += aTopRect.Height + nCascadeIndentY; + aCurrPos = aStartPos; + + // Try to find a cascaded position for the new floating window + for (auto const& elem : m_aUIElements) + { + if ( elem.m_xUIElement.is() ) + { + uno::Reference< awt::XDockableWindow > xDockWindow( elem.m_xUIElement->getRealInterface(), uno::UNO_QUERY ); + uno::Reference< awt::XWindow > xWindow( xDockWindow, uno::UNO_QUERY ); + if ( xDockWindow.is() && xDockWindow->isFloating() ) + { + SolarMutexGuard aGuard; + VclPtr pWindow = VCLUnoHelper::GetWindow( xWindow ); + if ( pWindow && pWindow->IsVisible() ) + { + awt::Rectangle aFloatRect = xWindow->getPosSize(); + if ((( aFloatRect.X - nHotZoneX ) <= aCurrPos.X ) && + ( aFloatRect.X >= aCurrPos.X ) && + (( aFloatRect.Y - nHotZoneY ) <= aCurrPos.Y ) && + ( aFloatRect.Y >= aCurrPos.Y )) + { + aCurrPos.X = aFloatRect.X + nCascadeIndentX; + aCurrPos.Y = aFloatRect.Y + nCascadeIndentY; + } + } + } + } + } + + return aCurrPos; +} + +void ToolbarLayoutManager::implts_sortUIElements() +{ + SolarMutexGuard g; + + std::stable_sort( m_aUIElements.begin(), m_aUIElements.end()); // first created element should first + + // We have to reset our temporary flags. + for (auto & elem : m_aUIElements) + elem.m_bUserActive = false; +} + +void ToolbarLayoutManager::implts_getUIElementVectorCopy( UIElementVector& rCopy ) +{ + SolarMutexGuard g; + rCopy = m_aUIElements; +} + +::Size ToolbarLayoutManager::implts_getTopBottomDockingAreaSizes() +{ + ::Size aSize; + uno::Reference< awt::XWindow > xTopDockingAreaWindow; + uno::Reference< awt::XWindow > xBottomDockingAreaWindow; + + { + SolarMutexGuard aReadLock; + xTopDockingAreaWindow = m_xDockAreaWindows[int(ui::DockingArea_DOCKINGAREA_TOP)]; + xBottomDockingAreaWindow = m_xDockAreaWindows[int(ui::DockingArea_DOCKINGAREA_BOTTOM)]; + } + + if ( xTopDockingAreaWindow.is() ) + aSize.setWidth( xTopDockingAreaWindow->getPosSize().Height ); + if ( xBottomDockingAreaWindow.is() ) + aSize.setHeight( xBottomDockingAreaWindow->getPosSize().Height ); + + return aSize; +} + +void ToolbarLayoutManager::implts_getDockingAreaElementInfos( ui::DockingArea eDockingArea, std::vector< SingleRowColumnWindowData >& rRowColumnsWindowData ) +{ + std::vector< UIElement > aWindowVector; + + if (( eDockingArea < ui::DockingArea_DOCKINGAREA_TOP ) || ( eDockingArea > ui::DockingArea_DOCKINGAREA_RIGHT )) + eDockingArea = ui::DockingArea_DOCKINGAREA_TOP; + + uno::Reference< awt::XWindow > xDockAreaWindow; + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + { + SolarMutexGuard aReadLock; + aWindowVector.reserve(m_aUIElements.size()); + xDockAreaWindow = m_xDockAreaWindows[static_cast(eDockingArea)]; + for (auto const& elem : m_aUIElements) + { + if (elem.m_aDockedData.m_nDockedArea == eDockingArea && elem.m_bVisible) + { + uno::Reference xUIElement(elem.m_xUIElement); + if (xUIElement.is()) + { + uno::Reference xWindow(xUIElement->getRealInterface(), + uno::UNO_QUERY); + uno::Reference xDockWindow(xWindow, uno::UNO_QUERY); + if (xDockWindow.is()) + { + if (!elem.m_bFloating) + { + // docked windows + aWindowVector.push_back(elem); + } + else + { + // floating windows + VclPtr pWindow = VCLUnoHelper::GetWindow(xWindow); + DockingManager* pDockMgr = vcl::Window::GetDockingManager(); + if (pDockMgr != nullptr) + { + SystemWindow* pFloatingWindow = pDockMgr->GetFloatingWindow(pWindow); + if (pFloatingWindow) + { + // update the position data of the floating window + if (pFloatingWindow->UpdatePositionData()) + { + awt::Rectangle aTmpRect = xWindow->getPosSize(); + UIElement uiElem = elem; + uiElem.m_aFloatingData.m_aPos + = awt::Point(aTmpRect.X, aTmpRect.Y); + implts_setToolbar(uiElem); + implts_writeWindowStateData(uiElem); + } + } + } + } + } + } + } + } + } + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + + rRowColumnsWindowData.clear(); + + // Collect data from windows that are on the same row/column + sal_Int32 j; + sal_Int32 nIndex( 0 ); + sal_Int32 nLastPos( 0 ); + sal_Int32 nCurrPos( -1 ); + sal_Int32 nLastRowColPixelPos( 0 ); + awt::Rectangle aDockAreaRect; + + if ( xDockAreaWindow.is() ) + aDockAreaRect = xDockAreaWindow->getPosSize(); + + if ( eDockingArea == ui::DockingArea_DOCKINGAREA_TOP ) + nLastRowColPixelPos = 0; + else if ( eDockingArea == ui::DockingArea_DOCKINGAREA_BOTTOM ) + nLastRowColPixelPos = aDockAreaRect.Height; + else if ( eDockingArea == ui::DockingArea_DOCKINGAREA_LEFT ) + nLastRowColPixelPos = 0; + else + nLastRowColPixelPos = aDockAreaRect.Width; + + const sal_uInt32 nCount = aWindowVector.size(); + for ( j = 0; j < sal_Int32( nCount); j++ ) + { + const UIElement& rElement = aWindowVector[j]; + uno::Reference< awt::XWindow > xWindow; + uno::Reference< ui::XUIElement > xUIElement( rElement.m_xUIElement ); + awt::Rectangle aPosSize; + + if ( !lcl_checkUIElement(xUIElement,aPosSize,xWindow) ) + continue; + if ( isHorizontalDockingArea( eDockingArea )) + { + if ( nCurrPos == -1 ) + { + nCurrPos = rElement.m_aDockedData.m_aPos.Y; + nLastPos = 0; + + SingleRowColumnWindowData aRowColumnWindowData; + aRowColumnWindowData.nRowColumn = nCurrPos; + rRowColumnsWindowData.push_back( aRowColumnWindowData ); + } + + sal_Int32 nSpace( 0 ); + if ( rElement.m_aDockedData.m_aPos.Y != nCurrPos ) + { + if ( eDockingArea == ui::DockingArea_DOCKINGAREA_TOP ) + nLastRowColPixelPos += rRowColumnsWindowData[nIndex].nStaticSize; + else + nLastRowColPixelPos -= rRowColumnsWindowData[nIndex].nStaticSize; + ++nIndex; + nLastPos = 0; + nCurrPos = rElement.m_aDockedData.m_aPos.Y; + SingleRowColumnWindowData aRowColumnWindowData; + aRowColumnWindowData.nRowColumn = nCurrPos; + rRowColumnsWindowData.push_back( aRowColumnWindowData ); + } + + // Calc space before an element and store it + nSpace = ( rElement.m_aDockedData.m_aPos.X - nLastPos ); + if ( rElement.m_aDockedData.m_aPos.X >= nLastPos ) + { + rRowColumnsWindowData[nIndex].nSpace += nSpace; + nLastPos = rElement.m_aDockedData.m_aPos.X + aPosSize.Width; + } + else + { + nSpace = 0; + nLastPos += aPosSize.Width; + } + rRowColumnsWindowData[nIndex].aRowColumnSpace.push_back( nSpace ); + + rRowColumnsWindowData[nIndex].aRowColumnWindows.push_back( xWindow ); + rRowColumnsWindowData[nIndex].aUIElementNames.push_back( rElement.m_aName ); + rRowColumnsWindowData[nIndex].aRowColumnWindowSizes.emplace_back( + rElement.m_aDockedData.m_aPos.X, + rElement.m_aDockedData.m_aPos.Y, + aPosSize.Width, + aPosSize.Height ); + if ( rRowColumnsWindowData[nIndex].nStaticSize < aPosSize.Height ) + rRowColumnsWindowData[nIndex].nStaticSize = aPosSize.Height; + if ( eDockingArea == ui::DockingArea_DOCKINGAREA_TOP ) + rRowColumnsWindowData[nIndex].aRowColumnRect = awt::Rectangle( 0, nLastRowColPixelPos, + aDockAreaRect.Width, aPosSize.Height ); + else + rRowColumnsWindowData[nIndex].aRowColumnRect = awt::Rectangle( 0, ( nLastRowColPixelPos - aPosSize.Height ), + aDockAreaRect.Width, aPosSize.Height ); + rRowColumnsWindowData[nIndex].nVarSize += aPosSize.Width + nSpace; + } + else + { + if ( nCurrPos == -1 ) + { + nCurrPos = rElement.m_aDockedData.m_aPos.X; + nLastPos = 0; + + SingleRowColumnWindowData aRowColumnWindowData; + aRowColumnWindowData.nRowColumn = nCurrPos; + rRowColumnsWindowData.push_back( aRowColumnWindowData ); + } + + sal_Int32 nSpace( 0 ); + if ( rElement.m_aDockedData.m_aPos.X != nCurrPos ) + { + if ( eDockingArea == ui::DockingArea_DOCKINGAREA_LEFT ) + nLastRowColPixelPos += rRowColumnsWindowData[nIndex].nStaticSize; + else + nLastRowColPixelPos -= rRowColumnsWindowData[nIndex].nStaticSize; + ++nIndex; + nLastPos = 0; + nCurrPos = rElement.m_aDockedData.m_aPos.X; + SingleRowColumnWindowData aRowColumnWindowData; + aRowColumnWindowData.nRowColumn = nCurrPos; + rRowColumnsWindowData.push_back( aRowColumnWindowData ); + } + + // Calc space before an element and store it + nSpace = ( rElement.m_aDockedData.m_aPos.Y - nLastPos ); + if ( rElement.m_aDockedData.m_aPos.Y > nLastPos ) + { + rRowColumnsWindowData[nIndex].nSpace += nSpace; + nLastPos = rElement.m_aDockedData.m_aPos.Y + aPosSize.Height; + } + else + { + nSpace = 0; + nLastPos += aPosSize.Height; + } + rRowColumnsWindowData[nIndex].aRowColumnSpace.push_back( nSpace ); + + rRowColumnsWindowData[nIndex].aRowColumnWindows.push_back( xWindow ); + rRowColumnsWindowData[nIndex].aUIElementNames.push_back( rElement.m_aName ); + rRowColumnsWindowData[nIndex].aRowColumnWindowSizes.emplace_back( + rElement.m_aDockedData.m_aPos.X, + rElement.m_aDockedData.m_aPos.Y, + aPosSize.Width, + aPosSize.Height ); + if ( rRowColumnsWindowData[nIndex].nStaticSize < aPosSize.Width ) + rRowColumnsWindowData[nIndex].nStaticSize = aPosSize.Width; + if ( eDockingArea == ui::DockingArea_DOCKINGAREA_LEFT ) + rRowColumnsWindowData[nIndex].aRowColumnRect = awt::Rectangle( nLastRowColPixelPos, 0, + aPosSize.Width, aDockAreaRect.Height ); + else + rRowColumnsWindowData[nIndex].aRowColumnRect = awt::Rectangle( ( nLastRowColPixelPos - aPosSize.Width ), 0, + aPosSize.Width, aDockAreaRect.Height ); + rRowColumnsWindowData[nIndex].nVarSize += aPosSize.Height + nSpace; + } + } +} + +void ToolbarLayoutManager::implts_getDockingAreaElementInfoOnSingleRowCol( ui::DockingArea eDockingArea, sal_Int32 nRowCol, SingleRowColumnWindowData& rRowColumnWindowData ) +{ + std::vector< UIElement > aWindowVector; + + if (( eDockingArea < ui::DockingArea_DOCKINGAREA_TOP ) || ( eDockingArea > ui::DockingArea_DOCKINGAREA_RIGHT )) + eDockingArea = ui::DockingArea_DOCKINGAREA_TOP; + + bool bHorzDockArea = isHorizontalDockingArea( eDockingArea ); + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + { + SolarMutexGuard aReadLock; + for (auto const& elem : m_aUIElements) + { + if (elem.m_aDockedData.m_nDockedArea == eDockingArea) + { + bool bSameRowCol = bHorzDockArea ? (elem.m_aDockedData.m_aPos.Y == nRowCol) + : (elem.m_aDockedData.m_aPos.X == nRowCol); + uno::Reference xUIElement(elem.m_xUIElement); + + if (bSameRowCol && xUIElement.is()) + { + uno::Reference xWindow(xUIElement->getRealInterface(), + uno::UNO_QUERY); + if (xWindow.is()) + { + SolarMutexGuard aGuard; + VclPtr pWindow = VCLUnoHelper::GetWindow(xWindow); + uno::Reference xDockWindow(xWindow, uno::UNO_QUERY); + if (pWindow && elem.m_bVisible && xDockWindow.is() && !elem.m_bFloating) + aWindowVector.push_back(elem); // docked windows + } + } + } + } + } + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + + // Initialize structure + rRowColumnWindowData.aUIElementNames.clear(); + rRowColumnWindowData.aRowColumnWindows.clear(); + rRowColumnWindowData.aRowColumnWindowSizes.clear(); + rRowColumnWindowData.aRowColumnSpace.clear(); + rRowColumnWindowData.nVarSize = 0; + rRowColumnWindowData.nStaticSize = 0; + rRowColumnWindowData.nSpace = 0; + rRowColumnWindowData.nRowColumn = nRowCol; + + // Collect data from windows that are on the same row/column + sal_Int32 j; + sal_Int32 nLastPos( 0 ); + + const sal_uInt32 nCount = aWindowVector.size(); + for ( j = 0; j < sal_Int32( nCount); j++ ) + { + const UIElement& rElement = aWindowVector[j]; + uno::Reference< awt::XWindow > xWindow; + uno::Reference< ui::XUIElement > xUIElement( rElement.m_xUIElement ); + awt::Rectangle aPosSize; + if ( !lcl_checkUIElement(xUIElement,aPosSize,xWindow) ) + continue; + + sal_Int32 nSpace; + if ( isHorizontalDockingArea( eDockingArea )) + { + nSpace = ( rElement.m_aDockedData.m_aPos.X - nLastPos ); + + // Calc space before an element and store it + if ( rElement.m_aDockedData.m_aPos.X > nLastPos ) + rRowColumnWindowData.nSpace += nSpace; + else + nSpace = 0; + + nLastPos = rElement.m_aDockedData.m_aPos.X + aPosSize.Width; + + rRowColumnWindowData.aRowColumnWindowSizes.emplace_back( + rElement.m_aDockedData.m_aPos.X, rElement.m_aDockedData.m_aPos.Y, + aPosSize.Width, aPosSize.Height ); + if ( rRowColumnWindowData.nStaticSize < aPosSize.Height ) + rRowColumnWindowData.nStaticSize = aPosSize.Height; + rRowColumnWindowData.nVarSize += aPosSize.Width; + } + else + { + // Calc space before an element and store it + nSpace = ( rElement.m_aDockedData.m_aPos.Y - nLastPos ); + if ( rElement.m_aDockedData.m_aPos.Y > nLastPos ) + rRowColumnWindowData.nSpace += nSpace; + else + nSpace = 0; + + nLastPos = rElement.m_aDockedData.m_aPos.Y + aPosSize.Height; + + rRowColumnWindowData.aRowColumnWindowSizes.emplace_back( + rElement.m_aDockedData.m_aPos.X, rElement.m_aDockedData.m_aPos.Y, + aPosSize.Width, aPosSize.Height ); + if ( rRowColumnWindowData.nStaticSize < aPosSize.Width ) + rRowColumnWindowData.nStaticSize = aPosSize.Width; + rRowColumnWindowData.nVarSize += aPosSize.Height; + } + + rRowColumnWindowData.aUIElementNames.push_back( rElement.m_aName ); + rRowColumnWindowData.aRowColumnWindows.push_back( xWindow ); + rRowColumnWindowData.aRowColumnSpace.push_back( nSpace ); + rRowColumnWindowData.nVarSize += nSpace; + } +} + +::tools::Rectangle ToolbarLayoutManager::implts_getWindowRectFromRowColumn( + ui::DockingArea DockingArea, + const SingleRowColumnWindowData& rRowColumnWindowData, + const ::Point& rMousePos, + std::u16string_view rExcludeElementName ) +{ + ::tools::Rectangle aWinRect; + + if (( DockingArea < ui::DockingArea_DOCKINGAREA_TOP ) || ( DockingArea > ui::DockingArea_DOCKINGAREA_RIGHT )) + DockingArea = ui::DockingArea_DOCKINGAREA_TOP; + + if ( rRowColumnWindowData.aRowColumnWindows.empty() ) + return aWinRect; + else + { + SolarMutexClearableGuard aReadLock; + VclPtr pContainerWindow( VCLUnoHelper::GetWindow( m_xContainerWindow )); + VclPtr pDockingAreaWindow( VCLUnoHelper::GetWindow( m_xDockAreaWindows[static_cast(DockingArea)] )); + aReadLock.clear(); + + // Calc correct position of the column/row rectangle to be able to compare it with mouse pos/tracking rect + SolarMutexGuard aGuard; + + // Retrieve output size from container Window + if ( pDockingAreaWindow && pContainerWindow ) + { + const sal_uInt32 nCount = rRowColumnWindowData.aRowColumnWindows.size(); + for ( sal_uInt32 i = 0; i < nCount; i++ ) + { + awt::Rectangle aWindowRect = rRowColumnWindowData.aRowColumnWindows[i]->getPosSize(); + ::tools::Rectangle aRect( aWindowRect.X, aWindowRect.Y, aWindowRect.X+aWindowRect.Width, aWindowRect.Y+aWindowRect.Height ); + aRect.SetPos( pContainerWindow->ScreenToOutputPixel( pDockingAreaWindow->OutputToScreenPixel( aRect.TopLeft() ))); + if ( aRect.Contains( rMousePos )) + { + // Check if we have found the excluded element. If yes, we have to provide an empty rectangle. + // We prevent that a toolbar cannot be moved when the mouse pointer is inside its own rectangle! + if ( rExcludeElementName != rRowColumnWindowData.aUIElementNames[i] ) + return aRect; + else + break; + } + } + } + } + + return aWinRect; +} + +::tools::Rectangle ToolbarLayoutManager::implts_determineFrontDockingRect( + ui::DockingArea eDockingArea, + sal_Int32 nRowCol, + const ::tools::Rectangle& rDockedElementRect, + std::u16string_view rMovedElementName, + const ::tools::Rectangle& rMovedElementRect ) +{ + SingleRowColumnWindowData aRowColumnWindowData; + + bool bHorzDockArea( isHorizontalDockingArea( eDockingArea )); + implts_getDockingAreaElementInfoOnSingleRowCol( eDockingArea, nRowCol, aRowColumnWindowData ); + if ( aRowColumnWindowData.aRowColumnWindows.empty() ) + return rMovedElementRect; + else + { + sal_Int32 nSpace( 0 ); + ::tools::Rectangle aFrontDockingRect( rMovedElementRect ); + const sal_uInt32 nCount = aRowColumnWindowData.aRowColumnWindows.size(); + for ( sal_uInt32 i = 0; i < nCount; i++ ) + { + if ( bHorzDockArea ) + { + if ( aRowColumnWindowData.aRowColumnWindowSizes[i].X >= rDockedElementRect.Left() ) + { + nSpace += aRowColumnWindowData.aRowColumnSpace[i]; + break; + } + else if ( aRowColumnWindowData.aUIElementNames[i] == rMovedElementName ) + nSpace += aRowColumnWindowData.aRowColumnWindowSizes[i].Width + + aRowColumnWindowData.aRowColumnSpace[i]; + else + nSpace = 0; + } + else + { + if ( aRowColumnWindowData.aRowColumnWindowSizes[i].Y >= rDockedElementRect.Top() ) + { + nSpace += aRowColumnWindowData.aRowColumnSpace[i]; + break; + } + else if ( aRowColumnWindowData.aUIElementNames[i] == rMovedElementName ) + nSpace += aRowColumnWindowData.aRowColumnWindowSizes[i].Height + + aRowColumnWindowData.aRowColumnSpace[i]; + else + nSpace = 0; + } + } + + if ( nSpace > 0 ) + { + sal_Int32 nMove = std::min( nSpace, static_cast(aFrontDockingRect.getOpenWidth()) ); + if ( bHorzDockArea ) + aFrontDockingRect.Move( -nMove, 0 ); + else + aFrontDockingRect.Move( 0, -nMove ); + } + + return aFrontDockingRect; + } +} + +void ToolbarLayoutManager::implts_findNextDockingPos( ui::DockingArea DockingArea, const ::Size& aUIElementSize, awt::Point& rVirtualPos, ::Point& rPixelPos ) +{ + SolarMutexClearableGuard aReadLock; + if (( DockingArea < ui::DockingArea_DOCKINGAREA_TOP ) || ( DockingArea > ui::DockingArea_DOCKINGAREA_RIGHT )) + DockingArea = ui::DockingArea_DOCKINGAREA_TOP; + uno::Reference< awt::XWindow > xDockingWindow( m_xDockAreaWindows[static_cast(DockingArea)] ); + ::Size aDockingWinSize; + + // Retrieve output size from container Window + vcl::Window* pDockingWindow = VCLUnoHelper::GetWindow( xDockingWindow ); + if ( pDockingWindow ) + aDockingWinSize = pDockingWindow->GetOutputSizePixel(); + aReadLock.clear(); + + sal_Int32 nFreeRowColPixelPos( 0 ); + sal_Int32 nMaxSpace( 0 ); + sal_Int32 nNeededSpace( 0 ); + sal_Int32 nTopDockingAreaSize( 0 ); + + if ( isHorizontalDockingArea( DockingArea )) + { + nMaxSpace = aDockingWinSize.Width(); + nNeededSpace = aUIElementSize.Width(); + } + else + { + nMaxSpace = aDockingWinSize.Height(); + nNeededSpace = aUIElementSize.Height(); + nTopDockingAreaSize = implts_getTopBottomDockingAreaSizes().Width(); + } + + std::vector< SingleRowColumnWindowData > aRowColumnsWindowData; + + implts_getDockingAreaElementInfos( DockingArea, aRowColumnsWindowData ); + sal_Int32 nPixelPos( 0 ); + const sal_uInt32 nCount = aRowColumnsWindowData.size(); + for ( sal_uInt32 i = 0; i < nCount; i++ ) + { + SingleRowColumnWindowData& rRowColumnWindowData = aRowColumnsWindowData[i]; + + if (( DockingArea == ui::DockingArea_DOCKINGAREA_BOTTOM ) || + ( DockingArea == ui::DockingArea_DOCKINGAREA_RIGHT )) + nPixelPos += rRowColumnWindowData.nStaticSize; + + if ((( nMaxSpace - rRowColumnWindowData.nVarSize ) >= nNeededSpace ) || + ( rRowColumnWindowData.nSpace >= nNeededSpace )) + { + // Check current row where we can find the needed space + sal_Int32 nCurrPos( 0 ); + const sal_uInt32 nWindowSizesCount = rRowColumnWindowData.aRowColumnWindowSizes.size(); + for ( sal_uInt32 j = 0; j < nWindowSizesCount; j++ ) + { + awt::Rectangle rRect = rRowColumnWindowData.aRowColumnWindowSizes[j]; + sal_Int32& rSpace = rRowColumnWindowData.aRowColumnSpace[j]; + if ( isHorizontalDockingArea( DockingArea )) + { + if ( rSpace >= nNeededSpace ) + { + rVirtualPos = awt::Point( nCurrPos, rRowColumnWindowData.nRowColumn ); + if ( DockingArea == ui::DockingArea_DOCKINGAREA_TOP ) + rPixelPos = ::Point( nCurrPos, nPixelPos ); + else + rPixelPos = ::Point( nCurrPos, aDockingWinSize.Height() - nPixelPos ); + return; + } + nCurrPos = rRect.X + rRect.Width; + } + else + { + if ( rSpace >= nNeededSpace ) + { + rVirtualPos = awt::Point( rRowColumnWindowData.nRowColumn, nCurrPos ); + if ( DockingArea == ui::DockingArea_DOCKINGAREA_LEFT ) + rPixelPos = ::Point( nPixelPos, nTopDockingAreaSize + nCurrPos ); + else + rPixelPos = ::Point( aDockingWinSize.Width() - nPixelPos , nTopDockingAreaSize + nCurrPos ); + return; + } + nCurrPos = rRect.Y + rRect.Height; + } + } + + if (( nCurrPos + nNeededSpace ) <= nMaxSpace ) + { + if ( isHorizontalDockingArea( DockingArea )) + { + rVirtualPos = awt::Point( nCurrPos, rRowColumnWindowData.nRowColumn ); + if ( DockingArea == ui::DockingArea_DOCKINGAREA_TOP ) + rPixelPos = ::Point( nCurrPos, nPixelPos ); + else + rPixelPos = ::Point( nCurrPos, aDockingWinSize.Height() - nPixelPos ); + return; + } + else + { + rVirtualPos = awt::Point( rRowColumnWindowData.nRowColumn, nCurrPos ); + if ( DockingArea == ui::DockingArea_DOCKINGAREA_LEFT ) + rPixelPos = ::Point( nPixelPos, nTopDockingAreaSize + nCurrPos ); + else + rPixelPos = ::Point( aDockingWinSize.Width() - nPixelPos , nTopDockingAreaSize + nCurrPos ); + return; + } + } + } + + if (( DockingArea == ui::DockingArea_DOCKINGAREA_TOP ) || ( DockingArea == ui::DockingArea_DOCKINGAREA_LEFT )) + nPixelPos += rRowColumnWindowData.nStaticSize; + } + + sal_Int32 nNextFreeRowCol( 0 ); + sal_Int32 nRowColumnsCount = aRowColumnsWindowData.size(); + if ( nRowColumnsCount > 0 ) + nNextFreeRowCol = aRowColumnsWindowData[nRowColumnsCount-1].nRowColumn+1; + else + nNextFreeRowCol = 0; + + if ( nNextFreeRowCol == 0 ) + { + if ( DockingArea == ui::DockingArea_DOCKINGAREA_BOTTOM ) + nFreeRowColPixelPos = aDockingWinSize.Height() - aUIElementSize.Height(); + else if ( DockingArea == ui::DockingArea_DOCKINGAREA_RIGHT ) + nFreeRowColPixelPos = aDockingWinSize.Width() - aUIElementSize.Width(); + } + + if ( isHorizontalDockingArea( DockingArea )) + { + rVirtualPos = awt::Point( 0, nNextFreeRowCol ); + if ( DockingArea == ui::DockingArea_DOCKINGAREA_TOP ) + rPixelPos = ::Point( 0, nFreeRowColPixelPos ); + else + rPixelPos = ::Point( 0, aDockingWinSize.Height() - nFreeRowColPixelPos ); + } + else + { + rVirtualPos = awt::Point( nNextFreeRowCol, 0 ); + rPixelPos = ::Point( aDockingWinSize.Width() - nFreeRowColPixelPos, 0 ); + } +} + +void ToolbarLayoutManager::implts_calcWindowPosSizeOnSingleRowColumn( + sal_Int32 nDockingArea, + sal_Int32 nOffset, + SingleRowColumnWindowData& rRowColumnWindowData, + const ::Size& rContainerSize ) +{ + sal_Int32 nDiff(0); + sal_Int32 nRCSpace( rRowColumnWindowData.nSpace ); + sal_Int32 nContainerClientSize(0); + + if ( rRowColumnWindowData.aRowColumnWindows.empty() ) + return; + + if ( isHorizontalDockingArea( nDockingArea )) + { + nContainerClientSize = rContainerSize.Width(); + nDiff = nContainerClientSize - rRowColumnWindowData.nVarSize; + } + else + { + sal_Int32 nTopDockingAreaSize = implts_getTopBottomDockingAreaSizes().Width(); + sal_Int32 nBottomDockingAreaSize = implts_getTopBottomDockingAreaSizes().Height(); + nContainerClientSize = ( rContainerSize.Height() - nTopDockingAreaSize - nBottomDockingAreaSize ); + nDiff = nContainerClientSize - rRowColumnWindowData.nVarSize; + } + + const sal_uInt32 nCount = rRowColumnWindowData.aRowColumnWindowSizes.size(); + if (( nDiff < 0 ) && ( nRCSpace > 0 )) + { + // First we try to reduce the size of blank space before/behind docked windows + sal_Int32 i = nCount - 1; + while ( i >= 0 ) + { + sal_Int32 nSpace = rRowColumnWindowData.aRowColumnSpace[i]; + if ( nSpace >= -nDiff ) + { + if ( isHorizontalDockingArea( nDockingArea )) + { + // Try to move this and all user elements behind with the calculated difference + for ( sal_uInt32 j = i; j < nCount; j++ ) + rRowColumnWindowData.aRowColumnWindowSizes[j].X += nDiff; + } + else + { + // Try to move this and all user elements behind with the calculated difference + for ( sal_uInt32 j = i; j < nCount; j++ ) + rRowColumnWindowData.aRowColumnWindowSizes[j].Y += nDiff; + } + nDiff = 0; + + break; + } + else if ( nSpace > 0 ) + { + if ( isHorizontalDockingArea( nDockingArea )) + { + // Try to move this and all user elements behind with the calculated difference + for ( sal_uInt32 j = i; j < nCount; j++ ) + rRowColumnWindowData.aRowColumnWindowSizes[j].X -= nSpace; + } + else + { + // Try to move this and all user elements behind with the calculated difference + for ( sal_uInt32 j = i; j < nCount; j++ ) + rRowColumnWindowData.aRowColumnWindowSizes[j].Y -= nSpace; + } + nDiff += nSpace; + } + --i; + } + } + + // Check if we have to reduce further + if ( nDiff < 0 ) + { + // Now we have to reduce the size of certain docked windows + sal_Int32 i = sal_Int32( nCount - 1 ); + while ( i >= 0 ) + { + awt::Rectangle& rWinRect = rRowColumnWindowData.aRowColumnWindowSizes[i]; + ::Size aMinSize; + + SolarMutexGuard aGuard; + { + uno::Reference< awt::XWindow > xWindow = rRowColumnWindowData.aRowColumnWindows[i]; + VclPtr pWindow = VCLUnoHelper::GetWindow( xWindow ); + if ( pWindow && pWindow->GetType() == WindowType::TOOLBOX ) + aMinSize = static_cast(pWindow.get())->CalcMinimumWindowSizePixel(); + } + + if ( !aMinSize.IsEmpty() ) + { + if ( isHorizontalDockingArea( nDockingArea )) + { + sal_Int32 nMaxReducation = rWinRect.Width - aMinSize.Width(); + if ( nMaxReducation >= -nDiff ) + { + rWinRect.Width = rWinRect.Width + nDiff; + nDiff = 0; + } + else + { + rWinRect.Width = aMinSize.Width(); + nDiff += nMaxReducation; + } + + // Try to move this and all user elements behind with the calculated difference + for ( sal_uInt32 j = i; j < nCount; j++ ) + rRowColumnWindowData.aRowColumnWindowSizes[j].X += nDiff; + } + else + { + sal_Int32 nMaxReducation = rWinRect.Height - aMinSize.Height(); + if ( nMaxReducation >= -nDiff ) + { + rWinRect.Height = rWinRect.Height + nDiff; + nDiff = 0; + } + else + { + rWinRect.Height = aMinSize.Height(); + nDiff += nMaxReducation; + } + + // Try to move this and all user elements behind with the calculated difference + for ( sal_uInt32 j = i; j < nCount; j++ ) + rRowColumnWindowData.aRowColumnWindowSizes[j].Y += nDiff; + } + } + + if ( nDiff >= 0 ) + break; + + --i; + } + } + + SolarMutexClearableGuard aReadLock; + VclPtr pDockAreaWindow = VCLUnoHelper::GetWindow( m_xDockAreaWindows[nDockingArea] ); + aReadLock.clear(); + + sal_Int32 nCurrPos( 0 ); + + SolarMutexGuard aGuard; + for ( sal_uInt32 i = 0; i < nCount; i++ ) + { + uno::Reference< awt::XWindow > xWindow = rRowColumnWindowData.aRowColumnWindows[i]; + VclPtr pWindow = VCLUnoHelper::GetWindow( xWindow ); + vcl::Window* pOldParentWindow = pWindow->GetParent(); + + if ( pDockAreaWindow != pOldParentWindow ) + pWindow->SetParent( pDockAreaWindow ); + + awt::Rectangle aWinRect = rRowColumnWindowData.aRowColumnWindowSizes[i]; + if ( isHorizontalDockingArea( nDockingArea )) + { + if ( aWinRect.X < nCurrPos ) + aWinRect.X = nCurrPos; + pWindow->SetPosSizePixel( ::Point( aWinRect.X, nOffset ), ::Size( aWinRect.Width, rRowColumnWindowData.nStaticSize )); + pWindow->Show( true, ShowFlags::NoFocusChange | ShowFlags::NoActivate ); + nCurrPos += ( aWinRect.X - nCurrPos ) + aWinRect.Width; + } + else + { + if ( aWinRect.Y < nCurrPos ) + aWinRect.Y = nCurrPos; + pWindow->SetPosSizePixel( ::Point( nOffset, aWinRect.Y ), ::Size( rRowColumnWindowData.nStaticSize, aWinRect.Height )); + pWindow->Show( true, ShowFlags::NoFocusChange | ShowFlags::NoActivate ); + nCurrPos += ( aWinRect.Y - nCurrPos ) + aWinRect.Height; + } + } +} + +void ToolbarLayoutManager::implts_setLayoutDirty() +{ + SolarMutexGuard g; + m_bLayoutDirty = true; +} + +void ToolbarLayoutManager::implts_setLayoutInProgress( bool bInProgress ) +{ + SolarMutexGuard g; + m_bLayoutInProgress = bInProgress; +} + +::tools::Rectangle ToolbarLayoutManager::implts_calcHotZoneRect( const ::tools::Rectangle& rRect, sal_Int32 nHotZoneOffset ) +{ + ::tools::Rectangle aRect( rRect ); + + aRect.AdjustLeft( -nHotZoneOffset ); + aRect.AdjustTop( -nHotZoneOffset ); + aRect.AdjustRight(nHotZoneOffset ); + aRect.AdjustBottom(nHotZoneOffset ); + + return aRect; +} + +void ToolbarLayoutManager::implts_calcDockingPosSize( + UIElement& rUIElement, + DockingOperation& rDockingOperation, + ::tools::Rectangle& rTrackingRect, + const Point& rMousePos ) +{ + SolarMutexResettableGuard aReadLock; + uno::Reference< awt::XWindow2 > xContainerWindow( m_xContainerWindow ); + ::Size aContainerWinSize; + vcl::Window* pContainerWindow( nullptr ); + ::tools::Rectangle aDockingAreaOffsets( m_aDockingAreaOffsets ); + aReadLock.clear(); + + if ( !rUIElement.m_xUIElement.is() ) + { + rTrackingRect = ::tools::Rectangle(); + return; + } + + { + // Retrieve output size from container Window + SolarMutexGuard aGuard; + pContainerWindow = VCLUnoHelper::GetWindow( xContainerWindow ); + aContainerWinSize = pContainerWindow->GetOutputSizePixel(); + } + + vcl::Window* pDockingAreaWindow( nullptr ); + uno::Reference< awt::XWindow > xWindow( rUIElement.m_xUIElement->getRealInterface(), uno::UNO_QUERY ); + uno::Reference< awt::XWindow > xDockingAreaWindow; + ::tools::Rectangle aTrackingRect( rTrackingRect ); + ui::DockingArea eDockedArea( rUIElement.m_aDockedData.m_nDockedArea ); + sal_Int32 nTopDockingAreaSize( implts_getTopBottomDockingAreaSizes().Width() ); + sal_Int32 nBottomDockingAreaSize( implts_getTopBottomDockingAreaSizes().Height() ); + bool bHorizontalDockArea(( eDockedArea == ui::DockingArea_DOCKINGAREA_TOP ) || + ( eDockedArea == ui::DockingArea_DOCKINGAREA_BOTTOM )); + sal_Int32 nMaxLeftRightDockAreaSize = aContainerWinSize.Height() - + nTopDockingAreaSize - + nBottomDockingAreaSize - + aDockingAreaOffsets.Top() - + aDockingAreaOffsets.Bottom(); + ::tools::Rectangle aDockingAreaRect; + + aReadLock.reset(); + xDockingAreaWindow = m_xDockAreaWindows[static_cast(eDockedArea)]; + aReadLock.clear(); + + { + SolarMutexGuard aGuard; + pDockingAreaWindow = VCLUnoHelper::GetWindow( xDockingAreaWindow ); + VclPtr pDockWindow = VCLUnoHelper::GetWindow( xWindow ); + ToolBox* pToolBox( nullptr ); + if ( pDockWindow && pDockWindow->GetType() == WindowType::TOOLBOX ) + pToolBox = static_cast(pDockWindow.get()); + + aDockingAreaRect = ::tools::Rectangle( pDockingAreaWindow->GetPosPixel(), pDockingAreaWindow->GetSizePixel() ); + if ( pToolBox ) + { + // docked toolbars always have one line + ::Size aSize = pToolBox->CalcWindowSizePixel( 1, ImplConvertAlignment( eDockedArea ) ); + aTrackingRect.SetSize( ::Size( aSize.Width(), aSize.Height() )); + } + } + + // default docking operation, dock on the given row/column + bool bOpOutsideOfDockingArea( !aDockingAreaRect.Contains( rMousePos )); + + std::vector< SingleRowColumnWindowData > aRowColumnsWindowData; + + rDockingOperation = DOCKOP_ON_COLROW; + implts_getDockingAreaElementInfos( eDockedArea, aRowColumnsWindowData ); + + // determine current first row/column and last row/column + sal_Int32 nMaxRowCol( -1 ); + sal_Int32 nMinRowCol( SAL_MAX_INT32 ); + const sal_uInt32 nCount = aRowColumnsWindowData.size(); + for ( sal_uInt32 i = 0; i < nCount; i++ ) + { + if ( aRowColumnsWindowData[i].nRowColumn > nMaxRowCol ) + nMaxRowCol = aRowColumnsWindowData[i].nRowColumn; + if ( aRowColumnsWindowData[i].nRowColumn < nMinRowCol ) + nMinRowCol = aRowColumnsWindowData[i].nRowColumn; + } + + if ( !bOpOutsideOfDockingArea ) + { + // docking inside our docking area + sal_Int32 nIndex( -1 ); + sal_Int32 nRowCol( -1 ); + ::tools::Rectangle aWindowRect; + ::tools::Rectangle aRowColumnRect; + + const sal_uInt32 nWindowDataCount = aRowColumnsWindowData.size(); + for ( sal_uInt32 i = 0; i < nWindowDataCount; i++ ) + { + ::tools::Rectangle aRect( aRowColumnsWindowData[i].aRowColumnRect.X, + aRowColumnsWindowData[i].aRowColumnRect.Y, + aRowColumnsWindowData[i].aRowColumnRect.X + aRowColumnsWindowData[i].aRowColumnRect.Width, + aRowColumnsWindowData[i].aRowColumnRect.Y + aRowColumnsWindowData[i].aRowColumnRect.Height ); + + { + // Calc correct position of the column/row rectangle to be able to compare it with mouse pos/tracking rect + SolarMutexGuard aGuard; + aRect.SetPos( pContainerWindow->ScreenToOutputPixel( pDockingAreaWindow->OutputToScreenPixel( aRect.TopLeft() ))); + } + + bool bIsInsideRowCol( aRect.Contains( rMousePos ) ); + if ( bIsInsideRowCol ) + { + nIndex = i; + nRowCol = aRowColumnsWindowData[i].nRowColumn; + rDockingOperation = implts_determineDockingOperation( eDockedArea, aRect, rMousePos ); + aWindowRect = implts_getWindowRectFromRowColumn( eDockedArea, aRowColumnsWindowData[i], rMousePos, rUIElement.m_aName ); + aRowColumnRect = aRect; + break; + } + } + + OSL_ENSURE( ( nIndex >= 0 ) && ( nRowCol >= 0 ), "Impossible case - no row/column found but mouse pointer is inside our docking area" ); + if (( nIndex >= 0 ) && ( nRowCol >= 0 )) + { + if ( rDockingOperation == DOCKOP_ON_COLROW ) + { + if ( !aWindowRect.IsEmpty()) + { + // Tracking rect is on a row/column and mouse is over a docked toolbar. + // Determine if the tracking rect must be located before/after the docked toolbar. + + ::tools::Rectangle aUIElementRect( aWindowRect ); + sal_Int32 nMiddle( bHorizontalDockArea ? ( aWindowRect.Left() + aWindowRect.getOpenWidth() / 2 ) : + ( aWindowRect.Top() + aWindowRect.getOpenHeight() / 2 )); + bool bInsertBefore( bHorizontalDockArea ? ( rMousePos.X() < nMiddle ) : ( rMousePos.Y() < nMiddle )); + if ( bInsertBefore ) + { + if ( bHorizontalDockArea ) + { + sal_Int32 nSize = std::clamp( sal_Int32(aContainerWinSize.Width() - aWindowRect.Left()), + sal_Int32(0), sal_Int32(aTrackingRect.getOpenWidth()) ); + if ( nSize == 0 ) + nSize = aWindowRect.getOpenWidth(); + + aUIElementRect.SetSize( ::Size( nSize, aWindowRect.getOpenHeight() )); + aWindowRect = implts_determineFrontDockingRect( eDockedArea, nRowCol, aWindowRect,rUIElement.m_aName, aUIElementRect ); + + // Set virtual position + rUIElement.m_aDockedData.m_aPos.X = aWindowRect.Left(); + rUIElement.m_aDockedData.m_aPos.Y = nRowCol; + } + else + { + sal_Int32 nSize = std::clamp( sal_Int32(nTopDockingAreaSize + nMaxLeftRightDockAreaSize - aWindowRect.Top()), + sal_Int32(0), sal_Int32(aTrackingRect.getOpenHeight()) ); + if ( nSize == 0 ) + nSize = aWindowRect.getOpenHeight(); + + aUIElementRect.SetSize( ::Size( aWindowRect.getOpenWidth(), nSize )); + aWindowRect = implts_determineFrontDockingRect( eDockedArea, nRowCol, aWindowRect, rUIElement.m_aName, aUIElementRect ); + + // Set virtual position + sal_Int32 nPosY = pDockingAreaWindow->ScreenToOutputPixel( + pContainerWindow->OutputToScreenPixel( aWindowRect.TopLeft() )).Y(); + rUIElement.m_aDockedData.m_aPos.X = nRowCol; + rUIElement.m_aDockedData.m_aPos.Y = nPosY; + } + + rTrackingRect = aWindowRect; + return; + } + else + { + if ( bHorizontalDockArea ) + { + sal_Int32 nSize = ::std::clamp( sal_Int32(aContainerWinSize.Width() - aWindowRect.Right()), + sal_Int32(0), sal_Int32(aTrackingRect.getOpenWidth()) ); + if ( nSize == 0 ) + { + aUIElementRect.SetPos( ::Point( aContainerWinSize.Width() - aTrackingRect.getOpenWidth(), aWindowRect.Top() )); + aUIElementRect.SetSize( ::Size( aTrackingRect.getOpenWidth(), aWindowRect.getOpenHeight() )); + rUIElement.m_aDockedData.m_aPos.X = aUIElementRect.Left(); + + } + else + { + aUIElementRect.SetPos( ::Point( aWindowRect.Right(), aWindowRect.Top() )); + aUIElementRect.SetSize( ::Size( nSize, aWindowRect.getOpenHeight() )); + rUIElement.m_aDockedData.m_aPos.X = aWindowRect.Right(); + } + + // Set virtual position + rUIElement.m_aDockedData.m_aPos.Y = nRowCol; + } + else + { + sal_Int32 nSize = std::clamp( sal_Int32(nTopDockingAreaSize + nMaxLeftRightDockAreaSize - aWindowRect.Bottom()), + sal_Int32(0), sal_Int32(aTrackingRect.getOpenHeight()) ); + aUIElementRect.SetPos( ::Point( aWindowRect.Left(), aWindowRect.Bottom() )); + aUIElementRect.SetSize( ::Size( aWindowRect.getOpenWidth(), nSize )); + + // Set virtual position + sal_Int32 nPosY( 0 ); + { + SolarMutexGuard aGuard; + nPosY = pDockingAreaWindow->ScreenToOutputPixel( + pContainerWindow->OutputToScreenPixel( aWindowRect.BottomRight() )).Y(); + } + rUIElement.m_aDockedData.m_aPos.X = nRowCol; + rUIElement.m_aDockedData.m_aPos.Y = nPosY; + } + + rTrackingRect = aUIElementRect; + return; + } + } + else + { + implts_setTrackingRect( eDockedArea, rMousePos, aTrackingRect ); + rTrackingRect = implts_calcTrackingAndElementRect( + eDockedArea, nRowCol, rUIElement, + aTrackingRect, aRowColumnRect, aContainerWinSize ); + return; + } + } + else + { + if ((( nRowCol == nMinRowCol ) && ( rDockingOperation == DOCKOP_BEFORE_COLROW )) || + (( nRowCol == nMaxRowCol ) && ( rDockingOperation == DOCKOP_AFTER_COLROW ))) + bOpOutsideOfDockingArea = true; + else + { + // handle docking before/after a row + implts_setTrackingRect( eDockedArea, rMousePos, aTrackingRect ); + rTrackingRect = implts_calcTrackingAndElementRect( + eDockedArea, nRowCol, rUIElement, + aTrackingRect, aRowColumnRect, aContainerWinSize ); + + sal_Int32 nOffsetX( 0 ); + sal_Int32 nOffsetY( 0 ); + if ( bHorizontalDockArea ) + nOffsetY = sal_Int32( floor( aRowColumnRect.getOpenHeight() / 2.0 + 0.5 )); + else + nOffsetX = sal_Int32( floor( aRowColumnRect.getOpenWidth() / 2.0 + 0.5 )); + + if ( rDockingOperation == DOCKOP_BEFORE_COLROW ) + { + if (( eDockedArea == ui::DockingArea_DOCKINGAREA_TOP ) || ( eDockedArea == ui::DockingArea_DOCKINGAREA_LEFT )) + { + // Docking before/after means move track rectangle half column/row. + // As left and top are ordered 0...n instead of right and bottom + // which uses n...0, we have to use negative values for top/left. + nOffsetX *= -1; + nOffsetY *= -1; + } + } + else + { + if (( eDockedArea == ui::DockingArea_DOCKINGAREA_BOTTOM ) || ( eDockedArea == ui::DockingArea_DOCKINGAREA_RIGHT )) + { + // Docking before/after means move track rectangle half column/row. + // As left and top are ordered 0...n instead of right and bottom + // which uses n...0, we have to use negative values for top/left. + nOffsetX *= -1; + nOffsetY *= -1; + } + nRowCol++; + } + + if ( bHorizontalDockArea ) + rUIElement.m_aDockedData.m_aPos.Y = nRowCol; + else + rUIElement.m_aDockedData.m_aPos.X = nRowCol; + + rTrackingRect.Move( nOffsetX, nOffsetY ); + rTrackingRect.SetSize( aTrackingRect.GetSize() ); + } + } + } + } + + // Docking outside of our docking window area => + // Users want to dock before/after first/last docked element or to an empty docking area + if ( !bOpOutsideOfDockingArea ) + return; + + // set correct size for docking + implts_setTrackingRect( eDockedArea, rMousePos, aTrackingRect ); + rTrackingRect = aTrackingRect; + + if ( bHorizontalDockArea ) + { + sal_Int32 nPosX( std::max( sal_Int32( rTrackingRect.Left()), sal_Int32( 0 ))); + if (( nPosX + rTrackingRect.getOpenWidth()) > aContainerWinSize.Width() ) + nPosX = std::min( nPosX, + std::max( sal_Int32( aContainerWinSize.Width() - rTrackingRect.getOpenWidth() ), + sal_Int32( 0 ))); + + sal_Int32 nSize = std::min( aContainerWinSize.Width(), rTrackingRect.getOpenWidth() ); + sal_Int32 nDockHeight = std::max( static_cast(aDockingAreaRect.getOpenHeight()), sal_Int32( 0 )); + if ( nDockHeight == 0 ) + { + sal_Int32 nPosY( std::max( aDockingAreaRect.Top(), aDockingAreaRect.Bottom() )); + if ( eDockedArea == ui::DockingArea_DOCKINGAREA_BOTTOM ) + nPosY -= rTrackingRect.getOpenHeight(); + rTrackingRect.SetPos( Point( nPosX, nPosY )); + rUIElement.m_aDockedData.m_aPos.Y = 0; + } + else if ( rMousePos.Y() < ( aDockingAreaRect.Top() + ( nDockHeight / 2 ))) + { + rTrackingRect.SetPos( Point( nPosX, aDockingAreaRect.Top() - rTrackingRect.getOpenHeight() )); + if ( eDockedArea == ui::DockingArea_DOCKINGAREA_TOP ) + rUIElement.m_aDockedData.m_aPos.Y = 0; + else + rUIElement.m_aDockedData.m_aPos.Y = ( nMaxRowCol >= 0 ) ? nMaxRowCol+1 : 0; + rDockingOperation = DOCKOP_BEFORE_COLROW; + } + else + { + rTrackingRect.SetPos( Point( nPosX, aDockingAreaRect.Bottom() )); + if ( eDockedArea == ui::DockingArea_DOCKINGAREA_TOP ) + rUIElement.m_aDockedData.m_aPos.Y = ( nMaxRowCol >= 0 ) ? nMaxRowCol+1 : 0; + else + rUIElement.m_aDockedData.m_aPos.Y = 0; + rDockingOperation = DOCKOP_AFTER_COLROW; + } + rTrackingRect.setWidth( nSize ); + + { + SolarMutexGuard aGuard; + nPosX = pDockingAreaWindow->ScreenToOutputPixel( + pContainerWindow->OutputToScreenPixel( rTrackingRect.TopLeft() )).X(); + } + rUIElement.m_aDockedData.m_aPos.X = nPosX; + } + else + { + sal_Int32 nMaxDockingAreaHeight = std::max( 0, nMaxLeftRightDockAreaSize ); + sal_Int32 nPosY( std::max( aTrackingRect.Top(), nTopDockingAreaSize )); + if (( nPosY + aTrackingRect.getOpenHeight()) > ( nTopDockingAreaSize + nMaxDockingAreaHeight )) + nPosY = std::min( nPosY, + std::max( nTopDockingAreaSize + ( nMaxDockingAreaHeight - aTrackingRect.getOpenHeight() ), + nTopDockingAreaSize )); + + sal_Int32 nSize = std::min( nMaxDockingAreaHeight, static_cast(aTrackingRect.getOpenHeight()) ); + sal_Int32 nDockWidth = std::max( static_cast(aDockingAreaRect.getOpenWidth()), sal_Int32( 0 )); + if ( nDockWidth == 0 ) + { + sal_Int32 nPosX( std::max( aDockingAreaRect.Left(), aDockingAreaRect.Right() )); + if ( eDockedArea == ui::DockingArea_DOCKINGAREA_RIGHT ) + nPosX -= rTrackingRect.getOpenWidth(); + rTrackingRect.SetPos( Point( nPosX, nPosY )); + rUIElement.m_aDockedData.m_aPos.X = 0; + } + else if ( rMousePos.X() < ( aDockingAreaRect.Left() + ( nDockWidth / 2 ))) + { + rTrackingRect.SetPos( Point( aDockingAreaRect.Left() - rTrackingRect.getOpenWidth(), nPosY )); + if ( eDockedArea == ui::DockingArea_DOCKINGAREA_LEFT ) + rUIElement.m_aDockedData.m_aPos.X = 0; + else + rUIElement.m_aDockedData.m_aPos.X = ( nMaxRowCol >= 0 ) ? nMaxRowCol+1 : 0; + rDockingOperation = DOCKOP_BEFORE_COLROW; + } + else + { + rTrackingRect.SetPos( Point( aDockingAreaRect.Right(), nPosY )); + if ( eDockedArea == ui::DockingArea_DOCKINGAREA_LEFT ) + rUIElement.m_aDockedData.m_aPos.X = ( nMaxRowCol >= 0 ) ? nMaxRowCol+1 : 0; + else + rUIElement.m_aDockedData.m_aPos.X = 0; + rDockingOperation = DOCKOP_AFTER_COLROW; + } + rTrackingRect.setHeight( nSize ); + + { + SolarMutexGuard aGuard; + nPosY = pDockingAreaWindow->ScreenToOutputPixel( + pContainerWindow->OutputToScreenPixel( rTrackingRect.TopLeft() )).Y(); + } + rUIElement.m_aDockedData.m_aPos.Y = nPosY; + } +} + +framework::ToolbarLayoutManager::DockingOperation ToolbarLayoutManager::implts_determineDockingOperation( + ui::DockingArea DockingArea, + const ::tools::Rectangle& rRowColRect, + const Point& rMousePos ) +{ + constexpr sal_Int32 nHorzVerticalRegionSize = 6; + constexpr sal_Int32 nHorzVerticalMoveRegion = 4; + + if ( rRowColRect.Contains( rMousePos )) + { + if ( isHorizontalDockingArea( DockingArea )) + { + sal_Int32 nRegion = rRowColRect.getOpenHeight() / nHorzVerticalRegionSize; + sal_Int32 nPosY = rRowColRect.Top() + nRegion; + + if ( rMousePos.Y() < nPosY ) + return ( DockingArea == ui::DockingArea_DOCKINGAREA_TOP ) ? DOCKOP_BEFORE_COLROW : DOCKOP_AFTER_COLROW; + else if ( rMousePos.Y() < ( nPosY + nRegion*nHorzVerticalMoveRegion )) + return DOCKOP_ON_COLROW; + else + return ( DockingArea == ui::DockingArea_DOCKINGAREA_TOP ) ? DOCKOP_AFTER_COLROW : DOCKOP_BEFORE_COLROW; + } + else + { + sal_Int32 nRegion = rRowColRect.getOpenWidth() / nHorzVerticalRegionSize; + sal_Int32 nPosX = rRowColRect.Left() + nRegion; + + if ( rMousePos.X() < nPosX ) + return ( DockingArea == ui::DockingArea_DOCKINGAREA_LEFT ) ? DOCKOP_BEFORE_COLROW : DOCKOP_AFTER_COLROW; + else if ( rMousePos.X() < ( nPosX + nRegion*nHorzVerticalMoveRegion )) + return DOCKOP_ON_COLROW; + else + return ( DockingArea == ui::DockingArea_DOCKINGAREA_LEFT ) ? DOCKOP_AFTER_COLROW : DOCKOP_BEFORE_COLROW; + } + } + else + return DOCKOP_ON_COLROW; +} + +::tools::Rectangle ToolbarLayoutManager::implts_calcTrackingAndElementRect( + ui::DockingArea eDockingArea, + sal_Int32 nRowCol, + UIElement& rUIElement, + const ::tools::Rectangle& rTrackingRect, + const ::tools::Rectangle& rRowColumnRect, + const ::Size& rContainerWinSize ) +{ + SolarMutexResettableGuard aReadGuard; + ::tools::Rectangle aDockingAreaOffsets( m_aDockingAreaOffsets ); + aReadGuard.clear(); + + bool bHorizontalDockArea( isHorizontalDockingArea( eDockingArea )); + + sal_Int32 nTopDockingAreaSize( implts_getTopBottomDockingAreaSizes().Width() ); + sal_Int32 nBottomDockingAreaSize( implts_getTopBottomDockingAreaSizes().Height() ); + + sal_Int32 nMaxLeftRightDockAreaSize = rContainerWinSize.Height() - + nTopDockingAreaSize - + nBottomDockingAreaSize - + aDockingAreaOffsets.Top() - + aDockingAreaOffsets.Bottom(); + + ::tools::Rectangle aTrackingRect( rTrackingRect ); + if ( bHorizontalDockArea ) + { + sal_Int32 nPosX( std::max( sal_Int32( rTrackingRect.Left()), sal_Int32( 0 ))); + if (( nPosX + rTrackingRect.getOpenWidth()) > rContainerWinSize.Width() ) + nPosX = std::min( nPosX, + std::max( sal_Int32( rContainerWinSize.Width() - rTrackingRect.getOpenWidth() ), + sal_Int32( 0 ))); + + sal_Int32 nSize = std::min( rContainerWinSize.Width(), rTrackingRect.getOpenWidth() ); + + aTrackingRect.SetPos( ::Point( nPosX, rRowColumnRect.Top() )); + aTrackingRect.setWidth( nSize ); + aTrackingRect.setHeight( rRowColumnRect.getOpenHeight() ); + + // Set virtual position + rUIElement.m_aDockedData.m_aPos.X = nPosX; + rUIElement.m_aDockedData.m_aPos.Y = nRowCol; + } + else + { + sal_Int32 nMaxDockingAreaHeight = std::max( 0, nMaxLeftRightDockAreaSize ); + + sal_Int32 nPosY( std::max( aTrackingRect.Top(), nTopDockingAreaSize )); + if (( nPosY + aTrackingRect.getOpenHeight()) > ( nTopDockingAreaSize + nMaxDockingAreaHeight )) + nPosY = std::min( nPosY, + std::max( nTopDockingAreaSize + ( nMaxDockingAreaHeight - aTrackingRect.getOpenHeight() ), + nTopDockingAreaSize )); + + sal_Int32 nSize = std::min( nMaxDockingAreaHeight, static_cast(aTrackingRect.getOpenHeight()) ); + + aTrackingRect.SetPos( ::Point( rRowColumnRect.Left(), nPosY )); + aTrackingRect.setWidth( rRowColumnRect.getOpenWidth() ); + aTrackingRect.setHeight( nSize ); + + aReadGuard.reset(); + uno::Reference< awt::XWindow > xDockingAreaWindow( m_xDockAreaWindows[static_cast(eDockingArea)] ); + uno::Reference< awt::XWindow2 > xContainerWindow( m_xContainerWindow ); + aReadGuard.clear(); + + sal_Int32 nDockPosY( 0 ); + { + SolarMutexGuard aGuard; + vcl::Window* pDockingAreaWindow = VCLUnoHelper::GetWindow( xDockingAreaWindow ); + VclPtr pContainerWindow = VCLUnoHelper::GetWindow( xContainerWindow ); + nDockPosY = pDockingAreaWindow->ScreenToOutputPixel( pContainerWindow->OutputToScreenPixel( ::Point( 0, nPosY ))).Y(); + } + + // Set virtual position + rUIElement.m_aDockedData.m_aPos.X = nRowCol; + rUIElement.m_aDockedData.m_aPos.Y = nDockPosY; + } + + return aTrackingRect; +} + +void ToolbarLayoutManager::implts_setTrackingRect( ui::DockingArea eDockingArea, const ::Point& rMousePos, ::tools::Rectangle& rTrackingRect ) +{ + ::Point aPoint( rTrackingRect.TopLeft()); + if ( isHorizontalDockingArea( eDockingArea )) + aPoint.setX( rMousePos.X() ); + else + aPoint.setY( rMousePos.Y() ); + rTrackingRect.SetPos( aPoint ); +} + +void ToolbarLayoutManager::implts_renumberRowColumnData( + ui::DockingArea eDockingArea, + const UIElement& rUIElement ) +{ + SolarMutexClearableGuard aReadLock; + uno::Reference< container::XNameAccess > xPersistentWindowState( m_xPersistentWindowState ); + aReadLock.clear(); + + bool bHorzDockingArea( isHorizontalDockingArea( eDockingArea )); + sal_Int32 nRowCol( bHorzDockingArea ? rUIElement.m_aDockedData.m_aPos.Y : rUIElement.m_aDockedData.m_aPos.X ); + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + { + SolarMutexGuard aWriteLock; + for (auto& elem : m_aUIElements) + { + if ((elem.m_aDockedData.m_nDockedArea == eDockingArea) + && (elem.m_aName != rUIElement.m_aName)) + { + // Don't change toolbars without a valid docking position! + if (isDefaultPos(elem.m_aDockedData.m_aPos)) + continue; + + sal_Int32 nWindowRowCol + = bHorzDockingArea ? elem.m_aDockedData.m_aPos.Y : elem.m_aDockedData.m_aPos.X; + if (nWindowRowCol >= nRowCol) + { + if (bHorzDockingArea) + elem.m_aDockedData.m_aPos.Y += 1; + else + elem.m_aDockedData.m_aPos.X += 1; + } + } + } + } + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + + // We have to change the persistent window state part + if ( !xPersistentWindowState.is() ) + return; + + try + { + const uno::Sequence< OUString > aWindowElements = xPersistentWindowState->getElementNames(); + for ( OUString const & rWindowElementName : aWindowElements ) + { + if ( rUIElement.m_aName != rWindowElementName ) + { + try + { + uno::Sequence< beans::PropertyValue > aPropValueSeq; + awt::Point aDockedPos; + ui::DockingArea nDockedArea( ui::DockingArea_DOCKINGAREA_DEFAULT ); + + xPersistentWindowState->getByName( rWindowElementName ) >>= aPropValueSeq; + for ( beans::PropertyValue const & rProp : std::as_const(aPropValueSeq) ) + { + if ( rProp.Name == WINDOWSTATE_PROPERTY_DOCKINGAREA ) + rProp.Value >>= nDockedArea; + else if ( rProp.Name == WINDOWSTATE_PROPERTY_DOCKPOS ) + rProp.Value >>= aDockedPos; + } + + // Don't change toolbars without a valid docking position! + if ( isDefaultPos( aDockedPos )) + continue; + + sal_Int32 nWindowRowCol = bHorzDockingArea ? aDockedPos.Y : aDockedPos.X; + if (( nDockedArea == eDockingArea ) && ( nWindowRowCol >= nRowCol )) + { + if ( bHorzDockingArea ) + aDockedPos.Y += 1; + else + aDockedPos.X += 1; + + uno::Reference< container::XNameReplace > xReplace( xPersistentWindowState, uno::UNO_QUERY ); + xReplace->replaceByName( rWindowElementName, css::uno::Any( aPropValueSeq )); + } + } + catch (const uno::Exception&) + { + } + } + } + } + catch (const uno::Exception&) + { + } +} + +// XWindowListener + +void SAL_CALL ToolbarLayoutManager::windowResized( const awt::WindowEvent& aEvent ) +{ + SolarMutexClearableGuard aWriteLock; + bool bLocked( m_bDockingInProgress ); + bool bLayoutInProgress( m_bLayoutInProgress ); + aWriteLock.clear(); + + // Do not do anything if we are in the middle of a docking process. This would interfere all other + // operations. We will store the new position and size in the docking handlers. + // Do not do anything if we are in the middle of our layouting process. We will adapt the position + // and size of the user interface elements. + if ( bLocked || bLayoutInProgress ) + return; + + bool bNotify( false ); + uno::Reference< awt::XWindow > xWindow( aEvent.Source, uno::UNO_QUERY ); + + UIElement aUIElement = implts_findToolbar( aEvent.Source ); + if ( aUIElement.m_xUIElement.is() ) + { + if ( aUIElement.m_bFloating ) + { + uno::Reference< awt::XWindow2 > xWindow2( xWindow, uno::UNO_QUERY ); + + if( xWindow2.is() ) + { + awt::Rectangle aPos = xWindow2->getPosSize(); + awt::Size aSize = xWindow2->getOutputSize(); // always use output size for consistency + bool bVisible = xWindow2->isVisible(); + + // update element data + aUIElement.m_aFloatingData.m_aPos = awt::Point(aPos.X, aPos.Y); + aUIElement.m_aFloatingData.m_aSize = aSize; + aUIElement.m_bVisible = bVisible; + } + + implts_writeWindowStateData( aUIElement ); + } + else + { + implts_setLayoutDirty(); + bNotify = true; + } + } + + if ( bNotify ) + m_pParentLayouter->requestLayout(); +} + +void SAL_CALL ToolbarLayoutManager::windowMoved( const awt::WindowEvent& /*aEvent*/ ) +{ +} + +void SAL_CALL ToolbarLayoutManager::windowShown( const lang::EventObject& /*aEvent*/ ) +{ +} + +void SAL_CALL ToolbarLayoutManager::windowHidden( const lang::EventObject& /*aEvent*/ ) +{ +} + +// XDockableWindowListener + +void SAL_CALL ToolbarLayoutManager::startDocking( const awt::DockingEvent& e ) +{ + bool bWinFound( false ); + + SolarMutexClearableGuard aReadGuard; + uno::Reference< awt::XWindow2 > xWindow( e.Source, uno::UNO_QUERY ); + aReadGuard.clear(); + + UIElement aUIElement = implts_findToolbar( e.Source ); + + if ( aUIElement.m_xUIElement.is() && xWindow.is() ) + { + bWinFound = true; + uno::Reference< awt::XDockableWindow > xDockWindow( xWindow, uno::UNO_QUERY ); + if ( xDockWindow->isFloating() ) + { + awt::Rectangle aPos = xWindow->getPosSize(); + awt::Size aSize = xWindow->getOutputSize(); + + aUIElement.m_aFloatingData.m_aPos = awt::Point(aPos.X, aPos.Y); + aUIElement.m_aFloatingData.m_aSize = aSize; + + SolarMutexGuard aGuard; + + VclPtr pWindow = VCLUnoHelper::GetWindow( xWindow ); + if ( pWindow && pWindow->GetType() == WindowType::TOOLBOX ) + { + ToolBox* pToolBox = static_cast(pWindow.get()); + aUIElement.m_aFloatingData.m_nLines = pToolBox->GetFloatingLines(); + aUIElement.m_aFloatingData.m_bIsHorizontal = isToolboxHorizontalAligned( pToolBox ); + } + } + } + + SolarMutexGuard g; + m_bDockingInProgress = bWinFound; + m_aDockUIElement = aUIElement; + m_aDockUIElement.m_bUserActive = true; +} + +awt::DockingData SAL_CALL ToolbarLayoutManager::docking( const awt::DockingEvent& e ) +{ + constexpr sal_Int32 MAGNETIC_DISTANCE_UNDOCK = 25; + constexpr sal_Int32 MAGNETIC_DISTANCE_DOCK = 20; + + SolarMutexClearableGuard aReadLock; + awt::DockingData aDockingData; + uno::Reference< awt::XDockableWindow > xDockWindow( e.Source, uno::UNO_QUERY ); + uno::Reference< awt::XWindow > xWindow( e.Source, uno::UNO_QUERY ); + uno::Reference< awt::XWindow > xTopDockingWindow( m_xDockAreaWindows[int(ui::DockingArea_DOCKINGAREA_TOP)] ); + uno::Reference< awt::XWindow > xLeftDockingWindow( m_xDockAreaWindows[int(ui::DockingArea_DOCKINGAREA_LEFT)] ); + uno::Reference< awt::XWindow > xRightDockingWindow( m_xDockAreaWindows[int(ui::DockingArea_DOCKINGAREA_RIGHT)] ); + uno::Reference< awt::XWindow > xBottomDockingWindow( m_xDockAreaWindows[int(ui::DockingArea_DOCKINGAREA_BOTTOM)] ); + uno::Reference< awt::XWindow2 > xContainerWindow( m_xContainerWindow ); + UIElement aUIDockingElement( m_aDockUIElement ); + + bool bDockingInProgress( m_bDockingInProgress ); + aReadLock.clear(); + + if ( bDockingInProgress ) + aDockingData.TrackingRectangle = e.TrackingRectangle; + + if ( bDockingInProgress && xDockWindow.is() && xWindow.is() ) + { + try + { + SolarMutexGuard aGuard; + + DockingOperation eDockingOperation( DOCKOP_ON_COLROW ); + ui::DockingArea eDockingArea( ui::DockingArea(-1) ); // none + sal_Int32 nMagneticZone( aUIDockingElement.m_bFloating ? MAGNETIC_DISTANCE_DOCK : MAGNETIC_DISTANCE_UNDOCK ); + ::tools::Rectangle aTrackingRect( e.TrackingRectangle.X, e.TrackingRectangle.Y, + ( e.TrackingRectangle.X + e.TrackingRectangle.Width ), + ( e.TrackingRectangle.Y + e.TrackingRectangle.Height )); + + awt::Rectangle aTmpRect = xTopDockingWindow->getPosSize(); + ::tools::Rectangle aTopDockRect( aTmpRect.X, aTmpRect.Y, aTmpRect.Width, aTmpRect.Height ); + ::tools::Rectangle aHotZoneTopDockRect( implts_calcHotZoneRect( aTopDockRect, nMagneticZone )); + + aTmpRect = xBottomDockingWindow->getPosSize(); + ::tools::Rectangle aBottomDockRect( aTmpRect.X, aTmpRect.Y, ( aTmpRect.X + aTmpRect.Width), ( aTmpRect.Y + aTmpRect.Height )); + ::tools::Rectangle aHotZoneBottomDockRect( implts_calcHotZoneRect( aBottomDockRect, nMagneticZone )); + + aTmpRect = xLeftDockingWindow->getPosSize(); + ::tools::Rectangle aLeftDockRect( aTmpRect.X, aTmpRect.Y, ( aTmpRect.X + aTmpRect.Width ), ( aTmpRect.Y + aTmpRect.Height )); + ::tools::Rectangle aHotZoneLeftDockRect( implts_calcHotZoneRect( aLeftDockRect, nMagneticZone )); + + aTmpRect = xRightDockingWindow->getPosSize(); + ::tools::Rectangle aRightDockRect( aTmpRect.X, aTmpRect.Y, ( aTmpRect.X + aTmpRect.Width ), ( aTmpRect.Y + aTmpRect.Height )); + ::tools::Rectangle aHotZoneRightDockRect( implts_calcHotZoneRect( aRightDockRect, nMagneticZone )); + + VclPtr pContainerWindow( VCLUnoHelper::GetWindow( xContainerWindow ) ); + ::Point aMousePos( pContainerWindow->ScreenToOutputPixel( ::Point( e.MousePos.X, e.MousePos.Y ))); + + if ( aHotZoneTopDockRect.Contains( aMousePos )) + eDockingArea = ui::DockingArea_DOCKINGAREA_TOP; + else if ( aHotZoneBottomDockRect.Contains( aMousePos )) + eDockingArea = ui::DockingArea_DOCKINGAREA_BOTTOM; + else if ( aHotZoneLeftDockRect.Contains( aMousePos )) + eDockingArea = ui::DockingArea_DOCKINGAREA_LEFT; + else if ( aHotZoneRightDockRect.Contains( aMousePos )) + eDockingArea = ui::DockingArea_DOCKINGAREA_RIGHT; + + // Higher priority for movements inside the real docking area + if ( aTopDockRect.Contains( aMousePos )) + eDockingArea = ui::DockingArea_DOCKINGAREA_TOP; + else if ( aBottomDockRect.Contains( aMousePos )) + eDockingArea = ui::DockingArea_DOCKINGAREA_BOTTOM; + else if ( aLeftDockRect.Contains( aMousePos )) + eDockingArea = ui::DockingArea_DOCKINGAREA_LEFT; + else if ( aRightDockRect.Contains( aMousePos )) + eDockingArea = ui::DockingArea_DOCKINGAREA_RIGHT; + + // Determine if we have a toolbar and set alignment according to the docking area! + VclPtr pWindow = VCLUnoHelper::GetWindow( xWindow ); + ToolBox* pToolBox = nullptr; + if ( pWindow && pWindow->GetType() == WindowType::TOOLBOX ) + pToolBox = static_cast(pWindow.get()); + + if ( eDockingArea != ui::DockingArea(-1) ) + { + if ( eDockingArea == ui::DockingArea_DOCKINGAREA_TOP ) + { + aUIDockingElement.m_aDockedData.m_nDockedArea = ui::DockingArea_DOCKINGAREA_TOP; + aUIDockingElement.m_bFloating = false; + } + else if ( eDockingArea == ui::DockingArea_DOCKINGAREA_BOTTOM ) + { + aUIDockingElement.m_aDockedData.m_nDockedArea = ui::DockingArea_DOCKINGAREA_BOTTOM; + aUIDockingElement.m_bFloating = false; + } + else if ( eDockingArea == ui::DockingArea_DOCKINGAREA_LEFT ) + { + aUIDockingElement.m_aDockedData.m_nDockedArea = ui::DockingArea_DOCKINGAREA_LEFT; + aUIDockingElement.m_bFloating = false; + } + else if ( eDockingArea == ui::DockingArea_DOCKINGAREA_RIGHT ) + { + aUIDockingElement.m_aDockedData.m_nDockedArea = ui::DockingArea_DOCKINGAREA_RIGHT; + aUIDockingElement.m_bFloating = false; + } + + ::Point aOutputPos = pContainerWindow->ScreenToOutputPixel( aTrackingRect.TopLeft() ); + aTrackingRect.SetPos( aOutputPos ); + + ::tools::Rectangle aNewDockingRect( aTrackingRect ); + + implts_calcDockingPosSize( aUIDockingElement, eDockingOperation, aNewDockingRect, aMousePos ); + + ::Point aScreenPos = pContainerWindow->OutputToScreenPixel( aNewDockingRect.TopLeft() ); + aDockingData.TrackingRectangle = awt::Rectangle( aScreenPos.X(), aScreenPos.Y(), + aNewDockingRect.getOpenWidth(), aNewDockingRect.getOpenHeight() ); + } + else if (pToolBox) + { + bool bIsHorizontal = isToolboxHorizontalAligned( pToolBox ); + awt::Size aFloatSize = aUIDockingElement.m_aFloatingData.m_aSize; + if ( aFloatSize.Width > 0 && aFloatSize.Height > 0 ) + { + aUIDockingElement.m_aFloatingData.m_aPos = AWTPoint(pContainerWindow->ScreenToOutputPixel(VCLPoint(e.MousePos))); + aDockingData.TrackingRectangle.Height = aFloatSize.Height; + aDockingData.TrackingRectangle.Width = aFloatSize.Width; + } + else + { + aFloatSize = AWTSize(pToolBox->CalcWindowSizePixel()); + if ( !bIsHorizontal ) + { + // Floating toolbars are always horizontal aligned! We have to swap + // width/height if we have a vertical aligned toolbar. + sal_Int32 nTemp = aFloatSize.Height; + aFloatSize.Height = aFloatSize.Width; + aFloatSize.Width = nTemp; + } + + aDockingData.TrackingRectangle.Height = aFloatSize.Height; + aDockingData.TrackingRectangle.Width = aFloatSize.Width; + + // For the first time we don't have any data about the floating size of a toolbar. + // We calculate it and store it for later use. + aUIDockingElement.m_aFloatingData.m_aPos = AWTPoint(pContainerWindow->ScreenToOutputPixel(VCLPoint(e.MousePos))); + aUIDockingElement.m_aFloatingData.m_aSize = aFloatSize; + aUIDockingElement.m_aFloatingData.m_nLines = pToolBox->GetFloatingLines(); + aUIDockingElement.m_aFloatingData.m_bIsHorizontal = isToolboxHorizontalAligned( pToolBox ); + } + aDockingData.TrackingRectangle.X = e.MousePos.X; + aDockingData.TrackingRectangle.Y = e.MousePos.Y; + } + + aDockingData.bFloating = ( eDockingArea == ui::DockingArea(-1) ); + + // Write current data to the member docking progress data + SolarMutexGuard g; + m_aDockUIElement.m_bFloating = aDockingData.bFloating; + if ( !aDockingData.bFloating ) + { + m_aDockUIElement.m_aDockedData = aUIDockingElement.m_aDockedData; + + m_eDockOperation = eDockingOperation; + } + else + m_aDockUIElement.m_aFloatingData = aUIDockingElement.m_aFloatingData; + } + catch (const uno::Exception&) + { + } + } + + return aDockingData; +} + +void SAL_CALL ToolbarLayoutManager::endDocking( const awt::EndDockingEvent& e ) +{ + if (e.bCancelled) + return; + + bool bDockingInProgress( false ); + bool bStartDockFloated( false ); + bool bFloating( false ); + UIElement aUIDockingElement; + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + SolarMutexResettableGuard aWriteLock; + bDockingInProgress = m_bDockingInProgress; + aUIDockingElement = m_aDockUIElement; + bFloating = aUIDockingElement.m_bFloating; + + UIElement& rUIElement = impl_findToolbar( aUIDockingElement.m_aName ); + if ( rUIElement.m_aName == aUIDockingElement.m_aName ) + { + if ( aUIDockingElement.m_bFloating ) + { + // Write last position into position data + uno::Reference< awt::XWindow > xWindow( aUIDockingElement.m_xUIElement->getRealInterface(), uno::UNO_QUERY ); + rUIElement.m_aFloatingData = aUIDockingElement.m_aFloatingData; + awt::Rectangle aTmpRect = xWindow->getPosSize(); + rUIElement.m_aFloatingData.m_aPos = awt::Point(aTmpRect.X, aTmpRect.Y); + // make changes also for our local data as we use it to make data persistent + aUIDockingElement.m_aFloatingData = rUIElement.m_aFloatingData; + } + else + { + rUIElement.m_aDockedData = aUIDockingElement.m_aDockedData; + rUIElement.m_aFloatingData.m_aSize = aUIDockingElement.m_aFloatingData.m_aSize; + + if ( m_eDockOperation != DOCKOP_ON_COLROW ) + { + // we have to renumber our row/column data to insert a new row/column + implts_renumberRowColumnData(aUIDockingElement.m_aDockedData.m_nDockedArea, aUIDockingElement ); + } + } + + bStartDockFloated = rUIElement.m_bFloating; + rUIElement.m_bFloating = m_aDockUIElement.m_bFloating; + rUIElement.m_bUserActive = true; + } + + // reset member for next docking operation + m_aDockUIElement.m_xUIElement.clear(); + m_eDockOperation = DOCKOP_ON_COLROW; + aWriteLock.clear(); + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + + implts_writeWindowStateData( aUIDockingElement ); + + if ( bDockingInProgress ) + { + SolarMutexGuard aGuard; + VclPtr pWindow = VCLUnoHelper::GetWindow( uno::Reference< awt::XWindow >( e.Source, uno::UNO_QUERY )); + ToolBox* pToolBox = nullptr; + if ( pWindow && pWindow->GetType() == WindowType::TOOLBOX ) + pToolBox = static_cast(pWindow.get()); + + if ( pToolBox ) + { + if( e.bFloating ) + { + if ( aUIDockingElement.m_aFloatingData.m_bIsHorizontal ) + pToolBox->SetAlign(); + else + pToolBox->SetAlign( WindowAlign::Left ); + } + else + { + ::Size aSize; + + pToolBox->SetAlign( ImplConvertAlignment( aUIDockingElement.m_aDockedData.m_nDockedArea) ); + + // Docked toolbars have always one line + aSize = pToolBox->CalcWindowSizePixel( 1 ); + + // Lock layouting updates as our listener would be called due to SetSizePixel + pToolBox->SetOutputSizePixel( aSize ); + } + } + } + + implts_sortUIElements(); + + aWriteLock.reset(); + m_bDockingInProgress = false; + m_bLayoutDirty = !bStartDockFloated || !bFloating; + bool bNotify = m_bLayoutDirty; + aWriteLock.clear(); + + if ( bNotify ) + m_pParentLayouter->requestLayout(); +} + +sal_Bool SAL_CALL ToolbarLayoutManager::prepareToggleFloatingMode( const lang::EventObject& e ) +{ + SolarMutexClearableGuard aReadLock; + bool bDockingInProgress = m_bDockingInProgress; + aReadLock.clear(); + + UIElement aUIDockingElement = implts_findToolbar( e.Source ); + bool bWinFound( !aUIDockingElement.m_aName.isEmpty() ); + uno::Reference< awt::XWindow > xWindow( e.Source, uno::UNO_QUERY ); + + if ( !bWinFound || !xWindow.is() ) + return true; + + if ( bDockingInProgress ) + return true; + + uno::Reference< awt::XDockableWindow > xDockWindow( xWindow, uno::UNO_QUERY ); + if ( !xDockWindow->isFloating() ) + return true; + + { + SolarMutexGuard aGuard; + VclPtr pWindow = VCLUnoHelper::GetWindow( xWindow ); + if ( pWindow && pWindow->GetType() == WindowType::TOOLBOX ) + { + ToolBox* pToolBox = static_cast< ToolBox *>( pWindow.get() ); + aUIDockingElement.m_aFloatingData.m_aPos = AWTPoint(pToolBox->GetPosPixel()); + aUIDockingElement.m_aFloatingData.m_aSize = AWTSize(pToolBox->GetOutputSizePixel()); + aUIDockingElement.m_aFloatingData.m_nLines = pToolBox->GetFloatingLines(); + aUIDockingElement.m_aFloatingData.m_bIsHorizontal = isToolboxHorizontalAligned( pToolBox ); + } + } + + UIElement aUIElement = implts_findToolbar( aUIDockingElement.m_aName ); + if ( aUIElement.m_aName == aUIDockingElement.m_aName ) + implts_setToolbar( aUIDockingElement ); + + return true; +} + +void SAL_CALL ToolbarLayoutManager::toggleFloatingMode( const lang::EventObject& e ) +{ + UIElement aUIDockingElement; + + SolarMutexResettableGuard aReadLock; + bool bDockingInProgress( m_bDockingInProgress ); + if ( bDockingInProgress ) + aUIDockingElement = m_aDockUIElement; + aReadLock.clear(); + + vcl::Window* pWindow( nullptr ); + ToolBox* pToolBox( nullptr ); + uno::Reference< awt::XWindow2 > xWindow; + + { + SolarMutexGuard aGuard; + xWindow.set( e.Source, uno::UNO_QUERY ); + pWindow = VCLUnoHelper::GetWindow( xWindow ); + + if ( pWindow && pWindow->GetType() == WindowType::TOOLBOX ) + pToolBox = static_cast(pWindow); + } + + if ( !bDockingInProgress ) + { + aUIDockingElement = implts_findToolbar( e.Source ); + bool bWinFound = !aUIDockingElement.m_aName.isEmpty(); + + if ( bWinFound && xWindow.is() ) + { + aUIDockingElement.m_bFloating = !aUIDockingElement.m_bFloating; + aUIDockingElement.m_bUserActive = true; + + implts_setLayoutInProgress(); + if ( aUIDockingElement.m_bFloating ) + { + SolarMutexGuard aGuard; + if ( pToolBox ) + { + pToolBox->SetLineCount( aUIDockingElement.m_aFloatingData.m_nLines ); + if ( aUIDockingElement.m_aFloatingData.m_bIsHorizontal ) + pToolBox->SetAlign(); + else + pToolBox->SetAlign( WindowAlign::Left ); + } + + bool bUndefPos = hasDefaultPosValue( aUIDockingElement.m_aFloatingData.m_aPos ); + bool bSetSize = !hasEmptySize( aUIDockingElement.m_aFloatingData.m_aSize ); + + if ( bUndefPos ) + aUIDockingElement.m_aFloatingData.m_aPos = implts_findNextCascadeFloatingPos(); + + if ( !bSetSize ) + { + if ( pToolBox ) + aUIDockingElement.m_aFloatingData.m_aSize = AWTSize(pToolBox->CalcFloatingWindowSizePixel()); + else if ( pWindow ) + aUIDockingElement.m_aFloatingData.m_aSize = AWTSize(pWindow->GetOutputSizePixel()); + } + + xWindow->setPosSize( aUIDockingElement.m_aFloatingData.m_aPos.X, + aUIDockingElement.m_aFloatingData.m_aPos.Y, + 0, 0, awt::PosSize::POS ); + xWindow->setOutputSize(aUIDockingElement.m_aFloatingData.m_aSize); + } + else + { + if ( isDefaultPos( aUIDockingElement.m_aDockedData.m_aPos )) + { + // Docking on its default position without a preset position - + // we have to find a good place for it. + ::Point aPixelPos; + awt::Point aDockPos; + ::Size aSize; + + { + SolarMutexGuard aGuard; + if ( pToolBox ) + aSize = pToolBox->CalcWindowSizePixel( 1, ImplConvertAlignment( aUIDockingElement.m_aDockedData.m_nDockedArea ) ); + else if ( pWindow ) + aSize = pWindow->GetSizePixel(); + } + + implts_findNextDockingPos(aUIDockingElement.m_aDockedData.m_nDockedArea, aSize, aDockPos, aPixelPos ); + aUIDockingElement.m_aDockedData.m_aPos = aDockPos; + } + + SolarMutexGuard aGuard; + if ( pToolBox ) + { + pToolBox->SetAlign( ImplConvertAlignment( aUIDockingElement.m_aDockedData.m_nDockedArea) ); + ::Size aSize = pToolBox->CalcWindowSizePixel( 1 ); + awt::Rectangle aRect = xWindow->getPosSize(); + xWindow->setPosSize( aRect.X, aRect.Y, 0, 0, awt::PosSize::POS ); + xWindow->setOutputSize( AWTSize( aSize ) ); + } + } + + implts_setLayoutInProgress( false ); + implts_setToolbar( aUIDockingElement ); + implts_writeWindowStateData( aUIDockingElement ); + implts_sortUIElements(); + implts_setLayoutDirty(); + + aReadLock.reset(); + LayoutManager* pParentLayouter( m_pParentLayouter ); + aReadLock.clear(); + + if ( pParentLayouter ) + pParentLayouter->requestLayout(); + } + } + else + { + SolarMutexGuard aGuard; + if ( pToolBox ) + { + if ( aUIDockingElement.m_bFloating ) + { + if ( aUIDockingElement.m_aFloatingData.m_bIsHorizontal ) + pToolBox->SetAlign(); + else + pToolBox->SetAlign( WindowAlign::Left ); + } + else + pToolBox->SetAlign( ImplConvertAlignment( aUIDockingElement.m_aDockedData.m_nDockedArea) ); + } + } +} + +void SAL_CALL ToolbarLayoutManager::closed( const lang::EventObject& e ) +{ + OUString aName; + UIElement aUIElement; + + { + SolarMutexGuard aWriteLock; + for (auto& elem : m_aUIElements) + { + uno::Reference xUIElement(elem.m_xUIElement); + if (xUIElement.is()) + { + uno::Reference xIfac(xUIElement->getRealInterface(), + uno::UNO_QUERY); + if (xIfac == e.Source) + { + aName = elem.m_aName; + + // user closes a toolbar => + // context sensitive toolbar: only destroy toolbar and store state. + // non context sensitive toolbar: make it invisible, store state and destroy it. + if (!elem.m_bContextSensitive) + elem.m_bVisible = false; + + aUIElement = elem; + break; + } + } + } + } + + // destroy element + if ( aName.isEmpty() ) + return; + + implts_writeWindowStateData( aUIElement ); + destroyToolbar( aName ); + + SolarMutexClearableGuard aReadLock; + bool bLayoutDirty = m_bLayoutDirty; + LayoutManager* pParentLayouter( m_pParentLayouter ); + aReadLock.clear(); + + if ( bLayoutDirty && pParentLayouter ) + pParentLayouter->requestLayout(); +} + +void SAL_CALL ToolbarLayoutManager::endPopupMode( const awt::EndPopupModeEvent& /*e*/ ) +{ +} + +// XUIConfigurationListener + +void SAL_CALL ToolbarLayoutManager::elementInserted( const ui::ConfigurationEvent& rEvent ) +{ + UIElement aUIElement = implts_findToolbar( rEvent.ResourceURL ); + + uno::Reference< ui::XUIElementSettings > xElementSettings( aUIElement.m_xUIElement, uno::UNO_QUERY ); + if ( xElementSettings.is() ) + { + uno::Reference< beans::XPropertySet > xPropSet( xElementSettings, uno::UNO_QUERY ); + if ( xPropSet.is() ) + { + if ( rEvent.Source == uno::Reference< uno::XInterface >( m_xDocCfgMgr, uno::UNO_QUERY )) + xPropSet->setPropertyValue( "ConfigurationSource", css::uno::Any( m_xDocCfgMgr )); + } + xElementSettings->updateSettings(); + } + else + { + OUString aElementType; + OUString aElementName; + parseResourceURL( rEvent.ResourceURL, aElementType, aElementName ); + if ( aElementName.indexOf( "custom_" ) != -1 ) + { + // custom toolbar must be directly created, shown and layouted! + createToolbar( rEvent.ResourceURL ); + uno::Reference< ui::XUIElement > xUIElement = getToolbar( rEvent.ResourceURL ); + if ( xUIElement.is() ) + { + OUString aUIName; + uno::Reference< ui::XUIConfigurationManager > xCfgMgr; + uno::Reference< beans::XPropertySet > xPropSet; + + try + { + xCfgMgr.set( rEvent.Source, uno::UNO_QUERY ); + xPropSet.set( xCfgMgr->getSettings( rEvent.ResourceURL, false ), uno::UNO_QUERY ); + + if ( xPropSet.is() ) + xPropSet->getPropertyValue("UIName") >>= aUIName; + } + catch (const container::NoSuchElementException&) + { + } + catch (const beans::UnknownPropertyException&) + { + } + catch (const lang::WrappedTargetException&) + { + } + + { + SolarMutexGuard aGuard; + vcl::Window* pWindow = getWindowFromXUIElement( xUIElement ); + if ( pWindow ) + pWindow->SetText( aUIName ); + } + + showToolbar( rEvent.ResourceURL ); + } + } + } +} + +void SAL_CALL ToolbarLayoutManager::elementRemoved( const ui::ConfigurationEvent& rEvent ) +{ + SolarMutexClearableGuard aReadLock; + uno::Reference< awt::XWindow > xContainerWindow = m_xContainerWindow; + uno::Reference< ui::XUIConfigurationManager > xModuleCfgMgr( m_xModuleCfgMgr ); + uno::Reference< ui::XUIConfigurationManager > xDocCfgMgr( m_xDocCfgMgr ); + aReadLock.clear(); + + UIElement aUIElement = implts_findToolbar( rEvent.ResourceURL ); + uno::Reference< ui::XUIElementSettings > xElementSettings( aUIElement.m_xUIElement, uno::UNO_QUERY ); + if ( !xElementSettings.is() ) + return; + + bool bNoSettings( false ); + OUString aConfigSourcePropName( "ConfigurationSource" ); + uno::Reference< uno::XInterface > xElementCfgMgr; + uno::Reference< beans::XPropertySet > xPropSet( xElementSettings, uno::UNO_QUERY ); + + if ( xPropSet.is() ) + xPropSet->getPropertyValue( aConfigSourcePropName ) >>= xElementCfgMgr; + + if ( !xElementCfgMgr.is() ) + return; + + // Check if the same UI configuration manager has changed => check further + if ( rEvent.Source == xElementCfgMgr ) + { + // Same UI configuration manager where our element has its settings + if ( rEvent.Source == uno::Reference< uno::XInterface >( xDocCfgMgr, uno::UNO_QUERY )) + { + // document settings removed + if ( xModuleCfgMgr->hasSettings( rEvent.ResourceURL )) + { + xPropSet->setPropertyValue( aConfigSourcePropName, css::uno::Any( xModuleCfgMgr )); + xElementSettings->updateSettings(); + return; + } + } + + bNoSettings = true; + } + + // No settings anymore, element must be destroyed + if ( xContainerWindow.is() && bNoSettings ) + destroyToolbar( rEvent.ResourceURL ); +} + +void SAL_CALL ToolbarLayoutManager::elementReplaced( const ui::ConfigurationEvent& rEvent ) +{ + UIElement aUIElement = implts_findToolbar( rEvent.ResourceURL ); + + uno::Reference< ui::XUIElementSettings > xElementSettings( aUIElement.m_xUIElement, uno::UNO_QUERY ); + if ( !xElementSettings.is() ) + return; + + uno::Reference< uno::XInterface > xElementCfgMgr; + uno::Reference< beans::XPropertySet > xPropSet( xElementSettings, uno::UNO_QUERY ); + + if ( xPropSet.is() ) + xPropSet->getPropertyValue( "ConfigurationSource" ) >>= xElementCfgMgr; + + if ( !xElementCfgMgr.is() ) + return; + + // Check if the same UI configuration manager has changed => update settings + if ( rEvent.Source != xElementCfgMgr ) + return; + + xElementSettings->updateSettings(); + + SolarMutexClearableGuard aWriteLock; + bool bNotify = !aUIElement.m_bFloating; + m_bLayoutDirty = bNotify; + LayoutManager* pParentLayouter( m_pParentLayouter ); + aWriteLock.clear(); + + if ( bNotify && pParentLayouter ) + pParentLayouter->requestLayout(); +} + +void ToolbarLayoutManager::updateToolbarsTips() +{ + SolarMutexGuard g; + + for (auto& elem : m_aUIElements) + { + uno::Reference< ui::XUIElementSettings > xElementSettings(elem.m_xUIElement, uno::UNO_QUERY); + if (!xElementSettings.is()) + continue; + xElementSettings->updateSettings(); + } +} + + +uno::Reference< ui::XUIElement > ToolbarLayoutManager::getToolbar( std::u16string_view aName ) +{ + return implts_findToolbar( aName ).m_xUIElement; +} + +uno::Sequence< uno::Reference< ui::XUIElement > > ToolbarLayoutManager::getToolbars() +{ + uno::Sequence< uno::Reference< ui::XUIElement > > aSeq; + + SolarMutexGuard g; + if ( !m_aUIElements.empty() ) + { + sal_uInt32 nCount(0); + for (auto const& elem : m_aUIElements) + { + if ( elem.m_xUIElement.is() ) + { + ++nCount; + aSeq.realloc( nCount ); + aSeq.getArray()[nCount-1] = elem.m_xUIElement; + } + } + } + + return aSeq; +} + +bool ToolbarLayoutManager::floatToolbar( std::u16string_view rResourceURL ) +{ + UIElement aUIElement = implts_findToolbar( rResourceURL ); + if ( !aUIElement.m_xUIElement.is() ) + return false; + + try + { + uno::Reference< awt::XDockableWindow > xDockWindow( aUIElement.m_xUIElement->getRealInterface(), uno::UNO_QUERY ); + if ( xDockWindow.is() && !xDockWindow->isFloating() ) + { + aUIElement.m_bFloating = true; + implts_writeWindowStateData( aUIElement ); + xDockWindow->setFloatingMode( true ); + + implts_setLayoutDirty(); + implts_setToolbar( aUIElement ); + return true; + } + } + catch (const lang::DisposedException&) + { + } + + return false; +} + +bool ToolbarLayoutManager::lockToolbar( std::u16string_view rResourceURL ) +{ + UIElement aUIElement = implts_findToolbar( rResourceURL ); + if ( !aUIElement.m_xUIElement.is() ) + return false; + + try + { + uno::Reference< awt::XDockableWindow > xDockWindow( aUIElement.m_xUIElement->getRealInterface(), uno::UNO_QUERY ); + if ( xDockWindow.is() && !xDockWindow->isFloating() && !xDockWindow->isLocked() ) + { + aUIElement.m_aDockedData.m_bLocked = true; + implts_writeWindowStateData( aUIElement ); + xDockWindow->lock(); + + implts_setLayoutDirty(); + implts_setToolbar( aUIElement ); + return true; + } + } + catch (const lang::DisposedException&) + { + } + + return false; +} + +bool ToolbarLayoutManager::unlockToolbar( std::u16string_view rResourceURL ) +{ + UIElement aUIElement = implts_findToolbar( rResourceURL ); + if ( !aUIElement.m_xUIElement.is() ) + return false; + + try + { + uno::Reference< awt::XDockableWindow > xDockWindow( aUIElement.m_xUIElement->getRealInterface(), uno::UNO_QUERY ); + if ( xDockWindow.is() && !xDockWindow->isFloating() && xDockWindow->isLocked() ) + { + aUIElement.m_aDockedData.m_bLocked = false; + implts_writeWindowStateData( aUIElement ); + xDockWindow->unlock(); + + implts_setLayoutDirty(); + implts_setToolbar( aUIElement ); + return true; + } + } + catch (const lang::DisposedException&) + { + } + + return false; +} + +bool ToolbarLayoutManager::isToolbarVisible( std::u16string_view rResourceURL ) +{ + uno::Reference< awt::XWindow2 > xWindow2( implts_getXWindow( rResourceURL ), uno::UNO_QUERY ); + return ( xWindow2.is() && xWindow2->isVisible() ); +} + +bool ToolbarLayoutManager::isToolbarFloating( std::u16string_view rResourceURL ) +{ + uno::Reference< awt::XDockableWindow > xDockWindow( implts_getXWindow( rResourceURL ), uno::UNO_QUERY ); + return ( xDockWindow.is() && xDockWindow->isFloating() ); +} + +bool ToolbarLayoutManager::isToolbarDocked( std::u16string_view rResourceURL ) +{ + return !isToolbarFloating( rResourceURL ); +} + +bool ToolbarLayoutManager::isToolbarLocked( std::u16string_view rResourceURL ) +{ + uno::Reference< awt::XDockableWindow > xDockWindow( implts_getXWindow( rResourceURL ), uno::UNO_QUERY ); + return ( xDockWindow.is() && xDockWindow->isLocked() ); +} + +awt::Size ToolbarLayoutManager::getToolbarSize( std::u16string_view rResourceURL ) +{ + vcl::Window* pWindow = implts_getWindow( rResourceURL ); + + SolarMutexGuard aGuard; + if ( pWindow ) + { + ::Size aSize = pWindow->GetSizePixel(); + awt::Size aWinSize; + aWinSize.Width = aSize.Width(); + aWinSize.Height = aSize.Height(); + return aWinSize; + } + + return awt::Size(); +} + +awt::Point ToolbarLayoutManager::getToolbarPos( std::u16string_view rResourceURL ) +{ + awt::Point aPos; + UIElement aUIElement = implts_findToolbar( rResourceURL ); + + uno::Reference< awt::XWindow > xWindow( implts_getXWindow( rResourceURL )); + if ( xWindow.is() ) + { + if ( aUIElement.m_bFloating ) + { + awt::Rectangle aRect = xWindow->getPosSize(); + aPos.X = aRect.X; + aPos.Y = aRect.Y; + } + else + aPos = aUIElement.m_aDockedData.m_aPos; + } + + return aPos; +} + +void ToolbarLayoutManager::setToolbarSize( std::u16string_view rResourceURL, const awt::Size& aSize ) +{ + uno::Reference< awt::XWindow2 > xWindow( implts_getXWindow( rResourceURL ), uno::UNO_QUERY ); + uno::Reference< awt::XDockableWindow > xDockWindow( xWindow, uno::UNO_QUERY ); + UIElement aUIElement = implts_findToolbar( rResourceURL ); + + if ( xWindow.is() && xDockWindow.is() && xDockWindow->isFloating() ) + { + xWindow->setOutputSize( aSize ); + aUIElement.m_aFloatingData.m_aSize = aSize; + implts_setToolbar( aUIElement ); + implts_writeWindowStateData( aUIElement ); + implts_sortUIElements(); + } +} + +void ToolbarLayoutManager::setToolbarPos( std::u16string_view rResourceURL, const awt::Point& aPos ) +{ + uno::Reference< awt::XWindow > xWindow( implts_getXWindow( rResourceURL )); + uno::Reference< awt::XDockableWindow > xDockWindow( xWindow, uno::UNO_QUERY ); + UIElement aUIElement = implts_findToolbar( rResourceURL ); + + if ( xWindow.is() && xDockWindow.is() && xDockWindow->isFloating() ) + { + xWindow->setPosSize( aPos.X, aPos.Y, 0, 0, awt::PosSize::POS ); + aUIElement.m_aFloatingData.m_aPos = aPos; + implts_setToolbar( aUIElement ); + implts_writeWindowStateData( aUIElement ); + implts_sortUIElements(); + } +} + +void ToolbarLayoutManager::setToolbarPosSize( std::u16string_view rResourceURL, const awt::Point& aPos, const awt::Size& aSize ) +{ + setToolbarPos( rResourceURL, aPos ); + setToolbarSize( rResourceURL, aSize ); +} + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/layoutmanager/toolbarlayoutmanager.hxx b/framework/source/layoutmanager/toolbarlayoutmanager.hxx new file mode 100644 index 0000000000..5e21438c8c --- /dev/null +++ b/framework/source/layoutmanager/toolbarlayoutmanager.hxx @@ -0,0 +1,285 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace framework +{ + +class ToolbarLayoutManager : public ::cppu::WeakImplHelper< css::awt::XDockableWindowListener, + css::ui::XUIConfigurationListener, + css::awt::XWindowListener > +{ + public: + enum { DOCKINGAREAS_COUNT = 4 }; + + enum PreviewFrameDetection + { + PREVIEWFRAME_UNKNOWN, + PREVIEWFRAME_NO, + PREVIEWFRAME_YES + }; + + ToolbarLayoutManager( css::uno::Reference< css::uno::XComponentContext > xContext, + css::uno::Reference< css::ui::XUIElementFactory > xUIElementFactory, + LayoutManager* pParentLayouter ); + virtual ~ToolbarLayoutManager() override; + + void reset(); + void attach( const css::uno::Reference< css::frame::XFrame >& xFrame, + const css::uno::Reference< css::ui::XUIConfigurationManager >& xModuleCfgMgr, + const css::uno::Reference< css::ui::XUIConfigurationManager >& xDocCfgMgr, + const css::uno::Reference< css::container::XNameAccess >& xPersistentWindowState ); + + void setParentWindow( const css::uno::Reference< css::awt::XVclWindowPeer >& xParentWindow ); + void setDockingAreaOffsets(const ::tools::Rectangle& rOffsets); + + void resetDockingArea(); + + css::awt::Rectangle getDockingArea(); + void setDockingArea( const css::awt::Rectangle& rDockingArea ); + + bool isPreviewFrame(); + + // layouting + bool isLayoutDirty() const { return m_bLayoutDirty;} + void doLayout(const ::Size& aContainerSize); + + // creation/destruction + void createStaticToolbars(); + void destroyToolbars(); + + bool requestToolbar( const OUString& rResourceURL ); + bool createToolbar( const OUString& rResourceURL ); + bool destroyToolbar( std::u16string_view rResourceURL ); + + // visibility + bool showToolbar( std::u16string_view rResourceURL ); + bool hideToolbar( std::u16string_view rResourceURL ); + + void refreshToolbarsVisibility( bool bAutomaticToolbars ); + void setFloatingToolbarsVisibility( bool bVisible ); + void setVisible(bool bVisible); + + // docking and further functions + bool dockToolbar( std::u16string_view rResourceURL, css::ui::DockingArea eDockingArea, const css::awt::Point& aPos ); + bool dockAllToolbars(); + bool floatToolbar( std::u16string_view rResourceURL ); + bool lockToolbar( std::u16string_view rResourceURL ); + bool unlockToolbar( std::u16string_view rResourceURL ); + void setToolbarPos( std::u16string_view rResourceURL, const css::awt::Point& aPos ); + void setToolbarSize( std::u16string_view rResourceURL, const css::awt::Size& aSize ); + void setToolbarPosSize( std::u16string_view rResourceURL, const css::awt::Point& aPos, const css::awt::Size& aSize ); + bool isToolbarVisible( std::u16string_view rResourceURL ); + bool isToolbarFloating( std::u16string_view rResourceURL ); + bool isToolbarDocked( std::u16string_view rResourceURL ); + bool isToolbarLocked( std::u16string_view rResourceURL ); + css::awt::Point getToolbarPos( std::u16string_view rResourceURL ); + css::awt::Size getToolbarSize( std::u16string_view rResourceURL ); + css::uno::Reference< css::ui::XUIElement > getToolbar( std::u16string_view aName ); + css::uno::Sequence< css::uno::Reference< css::ui::XUIElement > > getToolbars(); + + void updateToolbarsTips(); + + // child window notifications + void childWindowEvent( VclSimpleEvent const * pEvent ); + + // XInterface + + virtual void SAL_CALL acquire() noexcept override; + virtual void SAL_CALL release() noexcept override; + virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type & rType ) override; + + // XEventListener + virtual void SAL_CALL disposing( const css::lang::EventObject& aEvent ) override; + + // XWindowListener + virtual void SAL_CALL windowResized( const css::awt::WindowEvent& aEvent ) override; + virtual void SAL_CALL windowMoved( const css::awt::WindowEvent& aEvent ) override; + virtual void SAL_CALL windowShown( const css::lang::EventObject& aEvent ) override; + virtual void SAL_CALL windowHidden( const css::lang::EventObject& aEvent ) override; + + // XDockableWindowListener + virtual void SAL_CALL startDocking( const css::awt::DockingEvent& e ) override; + virtual css::awt::DockingData SAL_CALL docking( const css::awt::DockingEvent& e ) override; + virtual void SAL_CALL endDocking( const css::awt::EndDockingEvent& e ) override; + virtual sal_Bool SAL_CALL prepareToggleFloatingMode( const css::lang::EventObject& e ) override; + virtual void SAL_CALL toggleFloatingMode( const css::lang::EventObject& e ) override; + virtual void SAL_CALL closed( const css::lang::EventObject& e ) override; + virtual void SAL_CALL endPopupMode( const css::awt::EndPopupModeEvent& e ) override; + + // XUIConfigurationListener + virtual void SAL_CALL elementInserted( const css::ui::ConfigurationEvent& Event ) override; + virtual void SAL_CALL elementRemoved( const css::ui::ConfigurationEvent& Event ) override; + virtual void SAL_CALL elementReplaced( const css::ui::ConfigurationEvent& Event ) override; + + private: + enum DockingOperation + { + DOCKOP_BEFORE_COLROW, + DOCKOP_ON_COLROW, + DOCKOP_AFTER_COLROW + }; + + typedef std::vector< UIElement > UIElementVector; + struct SingleRowColumnWindowData + { + SingleRowColumnWindowData() + : nVarSize(0) + , nStaticSize(0) + , nSpace(0) + , nRowColumn(0) + {} + + std::vector< OUString > aUIElementNames; + std::vector< css::uno::Reference< css::awt::XWindow > > aRowColumnWindows; + std::vector< css::awt::Rectangle > aRowColumnWindowSizes; + std::vector< sal_Int32 > aRowColumnSpace; + css::awt::Rectangle aRowColumnRect; + sal_Int32 nVarSize; + sal_Int32 nStaticSize; + sal_Int32 nSpace; + sal_Int32 nRowColumn; + }; + + // internal helper methods + + bool implts_isParentWindowVisible() const; + ::tools::Rectangle implts_calcDockingArea(); + void implts_sortUIElements(); + void implts_reparentToolbars(); + OUString implts_generateGenericAddonToolbarTitle( sal_Int32 nNumber ) const; + void implts_setElementData( UIElement& rUIElement, const css::uno::Reference< css::awt::XDockableWindow >& rDockWindow ); + void implts_destroyDockingAreaWindows(); + + // layout methods + + void implts_setDockingAreaWindowSizes( const css::awt::Rectangle& rBorderSpace ); + css::awt::Point implts_findNextCascadeFloatingPos(); + void implts_renumberRowColumnData( css::ui::DockingArea eDockingArea, const UIElement& rUIElement ); + void implts_calcWindowPosSizeOnSingleRowColumn( sal_Int32 nDockingArea, + sal_Int32 nOffset, + SingleRowColumnWindowData& rRowColumnWindowData, + const ::Size& rContainerSize ); + void implts_setLayoutDirty(); + void implts_setLayoutInProgress( bool bInProgress = true ); + + // lookup/container methods + + UIElement implts_findToolbar( std::u16string_view aName ); + UIElement implts_findToolbar( const css::uno::Reference< css::uno::XInterface >& xToolbar ); + UIElement& impl_findToolbar( std::u16string_view aName ); + css::uno::Reference< css::awt::XWindow > implts_getXWindow( std::u16string_view aName ); + vcl::Window* implts_getWindow( std::u16string_view aName ); + bool implts_insertToolbar( const UIElement& rUIElement ); + void implts_setToolbar( const UIElement& rUIElement ); + ::Size implts_getTopBottomDockingAreaSizes(); + void implts_getUIElementVectorCopy( UIElementVector& rCopy ); + + // internal docking methods + + ::tools::Rectangle implts_calcHotZoneRect( const ::tools::Rectangle& rRect, sal_Int32 nHotZoneOffset ); + void implts_calcDockingPosSize( UIElement& aUIElement, DockingOperation& eDockOperation, ::tools::Rectangle& rTrackingRect, const Point& rMousePos ); + DockingOperation implts_determineDockingOperation( css::ui::DockingArea DockingArea, const ::tools::Rectangle& rRowColRect, const Point& rMousePos ); + ::tools::Rectangle implts_getWindowRectFromRowColumn( css::ui::DockingArea DockingArea, const SingleRowColumnWindowData& rRowColumnWindowData, const ::Point& rMousePos, std::u16string_view rExcludeElementName ); + ::tools::Rectangle implts_determineFrontDockingRect( css::ui::DockingArea eDockingArea, + sal_Int32 nRowCol, + const ::tools::Rectangle& rDockedElementRect, + std::u16string_view rMovedElementName, + const ::tools::Rectangle& rMovedElementRect ); + ::tools::Rectangle implts_calcTrackingAndElementRect( css::ui::DockingArea eDockingArea, + sal_Int32 nRowCol, + UIElement& rUIElement, + const ::tools::Rectangle& rTrackingRect, + const ::tools::Rectangle& rRowColumnRect, + const ::Size& rContainerWinSize ); + + void implts_getDockingAreaElementInfos( css::ui::DockingArea DockingArea, std::vector< SingleRowColumnWindowData >& rRowColumnsWindowData ); + void implts_getDockingAreaElementInfoOnSingleRowCol( css::ui::DockingArea, sal_Int32 nRowCol, SingleRowColumnWindowData& rRowColumnWindowData ); + void implts_findNextDockingPos( css::ui::DockingArea DockingArea, const ::Size& aUIElementSize, css::awt::Point& rVirtualPos, ::Point& rPixelPos ); + void implts_setTrackingRect( css::ui::DockingArea eDockingArea, const ::Point& rMousePos, ::tools::Rectangle& rTrackingRect ); + + // creation methods + + void implts_createAddonsToolBars(); + void implts_createCustomToolBars(); + void implts_createNonContextSensitiveToolBars(); + void implts_createCustomToolBars( const css::uno::Sequence< css::uno::Sequence< css::beans::PropertyValue > >& aCustomTbxSeq ); + void implts_createCustomToolBar( const OUString& aTbxResName, const OUString& aTitle ); + void implts_setToolbarCreation( bool bStart = true ); + bool implts_isToolbarCreationActive(); + + // persistence methods + + bool implts_readWindowStateData( const OUString& aName, UIElement& rElementData ); + void implts_writeWindowStateData( const UIElement& rElementData ); + + // members + + css::uno::Reference< css::uno::XComponentContext > m_xContext; + css::uno::Reference< css::frame::XFrame > m_xFrame; + css::uno::Reference< css::awt::XWindow2 > m_xContainerWindow; + css::uno::Reference< css::awt::XWindow > m_xDockAreaWindows[DOCKINGAREAS_COUNT]; + css::uno::Reference< css::ui::XUIElementFactory > m_xUIElementFactoryManager; + css::uno::Reference< css::ui::XUIConfigurationManager > m_xModuleCfgMgr; + css::uno::Reference< css::ui::XUIConfigurationManager > m_xDocCfgMgr; + css::uno::Reference< css::container::XNameAccess > m_xPersistentWindowState; + LayoutManager* m_pParentLayouter; + + UIElementVector m_aUIElements; + UIElement m_aDockUIElement; + tools::Rectangle m_aDockingArea; + tools::Rectangle m_aDockingAreaOffsets; + DockingOperation m_eDockOperation; + PreviewFrameDetection m_ePreviewDetection; + + std::unique_ptr m_pAddonOptions; + std::unique_ptr m_pGlobalSettings; + + bool m_bComponentAttached; + bool m_bLayoutDirty; + bool m_bGlobalSettings; + bool m_bDockingInProgress; + bool m_bLayoutInProgress; + bool m_bToolbarCreation; +}; + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/layoutmanager/uielement.cxx b/framework/source/layoutmanager/uielement.cxx new file mode 100644 index 0000000000..5821875152 --- /dev/null +++ b/framework/source/layoutmanager/uielement.cxx @@ -0,0 +1,99 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include + +using namespace ::com::sun::star; + +namespace framework +{ + + bool UIElement::operator< ( const ::framework::UIElement& aUIElement ) const +{ + if ( !m_xUIElement.is() && aUIElement.m_xUIElement.is() ) + return false; + else if ( m_xUIElement.is() && !aUIElement.m_xUIElement.is() ) + return true; + else if ( !m_bVisible && aUIElement.m_bVisible ) + return false; + else if ( m_bVisible && !aUIElement.m_bVisible ) + return true; + else if ( !m_bFloating && aUIElement.m_bFloating ) + return true; + else if ( m_bFloating && !aUIElement.m_bFloating ) + return false; + else + { + if ( m_bFloating ) + { + bool bEqual = ( m_aFloatingData.m_aPos.Y == aUIElement.m_aFloatingData.m_aPos.Y ); + if ( bEqual ) + return ( m_aFloatingData.m_aPos.X < aUIElement.m_aFloatingData.m_aPos.X ); + else + return ( m_aFloatingData.m_aPos.Y < aUIElement.m_aFloatingData.m_aPos.Y ); + } + else + { + if ( m_aDockedData.m_nDockedArea < aUIElement.m_aDockedData.m_nDockedArea ) + return true; + else if ( m_aDockedData.m_nDockedArea > aUIElement.m_aDockedData.m_nDockedArea ) + return false; + else + { + if ( m_aDockedData.m_nDockedArea == ui::DockingArea_DOCKINGAREA_TOP || + m_aDockedData.m_nDockedArea == ui::DockingArea_DOCKINGAREA_BOTTOM ) + { + if ( m_aDockedData.m_aPos.Y != aUIElement.m_aDockedData.m_aPos.Y ) + return ( m_aDockedData.m_aPos.Y < aUIElement.m_aDockedData.m_aPos.Y ); + else + { + bool bEqual = ( m_aDockedData.m_aPos.X == aUIElement.m_aDockedData.m_aPos.X ); + if ( bEqual ) + { + return m_bUserActive && !aUIElement.m_bUserActive; + } + else + return ( m_aDockedData.m_aPos.X <= aUIElement.m_aDockedData.m_aPos.X ); + } + } + else + { + if ( m_aDockedData.m_aPos.X != aUIElement.m_aDockedData.m_aPos.X ) + return ( m_aDockedData.m_aPos.X < aUIElement.m_aDockedData.m_aPos.X ); + else + { + bool bEqual = ( m_aDockedData.m_aPos.Y == aUIElement.m_aDockedData.m_aPos.Y ); + if ( bEqual ) + { + return m_bUserActive && !aUIElement.m_bUserActive; + } + else + return ( m_aDockedData.m_aPos.Y <= aUIElement.m_aDockedData.m_aPos.Y ); + } + } + } + } + } +} + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/loadenv/loadenv.cxx b/framework/source/loadenv/loadenv.cxx new file mode 100644 index 0000000000..277e69fae8 --- /dev/null +++ b/framework/source/loadenv/loadenv.cxx @@ -0,0 +1,1819 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +constexpr OUString PROP_TYPES = u"Types"_ustr; +constexpr OUString PROP_NAME = u"Name"_ustr; + +namespace framework { + +using namespace com::sun::star; + +namespace { + +class LoadEnvListener : public ::cppu::WeakImplHelper< css::frame::XLoadEventListener , + css::frame::XDispatchResultListener > +{ + private: + std::mutex m_mutex; + bool m_bWaitingResult; + LoadEnv* m_pLoadEnv; + + public: + + explicit LoadEnvListener(LoadEnv* pLoadEnv) + : m_bWaitingResult(true) + , m_pLoadEnv(pLoadEnv) + { + } + + // frame.XLoadEventListener + virtual void SAL_CALL loadFinished(const css::uno::Reference< css::frame::XFrameLoader >& xLoader) override; + + virtual void SAL_CALL loadCancelled(const css::uno::Reference< css::frame::XFrameLoader >& xLoader) override; + + // frame.XDispatchResultListener + virtual void SAL_CALL dispatchFinished(const css::frame::DispatchResultEvent& aEvent) override; + + // lang.XEventListener + virtual void SAL_CALL disposing(const css::lang::EventObject& aEvent) override; +}; + +} + +LoadEnv::LoadEnv(css::uno::Reference< css::uno::XComponentContext > xContext) + : m_xContext(std::move(xContext)) + , m_nSearchFlags(0) + , m_eFeature(LoadEnvFeatures::NONE) + , m_eContentType(E_UNSUPPORTED_CONTENT) + , m_bCloseFrameOnError(false) + , m_bReactivateControllerOnError(false) + , m_bLoaded( false ) +{ +} + +LoadEnv::~LoadEnv() +{ +} + +css::uno::Reference< css::lang::XComponent > LoadEnv::loadComponentFromURL(const css::uno::Reference< css::frame::XComponentLoader >& xLoader, + const css::uno::Reference< css::uno::XComponentContext >& xContext , + const OUString& sURL , + const OUString& sTarget, + sal_Int32 nSearchFlags , + const css::uno::Sequence< css::beans::PropertyValue >& lArgs ) +{ + css::uno::Reference< css::lang::XComponent > xComponent; + comphelper::ProfileZone aZone("loadComponentFromURL"); + + try + { + LoadEnv aEnv(xContext); + + LoadEnvFeatures loadEnvFeatures = LoadEnvFeatures::WorkWithUI; + // tdf#118238 Only disable UI interaction when loading as hidden + if (comphelper::NamedValueCollection::get(lArgs, u"Hidden") == uno::Any(true) || Application::IsHeadlessModeEnabled()) + loadEnvFeatures = LoadEnvFeatures::NONE; + + aEnv.startLoading(sURL, + lArgs, + css::uno::Reference< css::frame::XFrame >(xLoader, css::uno::UNO_QUERY), + sTarget, + nSearchFlags, + loadEnvFeatures); + aEnv.waitWhileLoading(); // wait for ever! + + xComponent = aEnv.getTargetComponent(); + } + catch(const LoadEnvException& ex) + { + switch(ex.m_nID) + { + case LoadEnvException::ID_INVALID_MEDIADESCRIPTOR: + throw css::lang::IllegalArgumentException( + "Optional list of arguments seem to be corrupted.", xLoader, 4); + + case LoadEnvException::ID_UNSUPPORTED_CONTENT: + throw css::lang::IllegalArgumentException( + "Unsupported URL <" + sURL + ">: \"" + ex.m_sMessage + "\"", + xLoader, 1); + + default: + SAL_WARN( + "fwk.loadenv", + "caught LoadEnvException " << +ex.m_nID << " \"" + << ex.m_sMessage << "\"" + << (ex.m_exOriginal.has() + ? (", " + ex.m_exOriginal.getValueTypeName() + " \"" + + (ex.m_exOriginal.get(). + Message) + + "\"") + : OUString()) + << " while loading <" << sURL << ">"); + xComponent.clear(); + break; + } + } + + return xComponent; +} + +namespace { + +utl::MediaDescriptor addModelArgs(const uno::Sequence& rDescriptor) +{ + utl::MediaDescriptor rResult(rDescriptor); + uno::Reference xModel(rResult.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_MODEL, uno::Reference())); + + if (xModel.is()) + { + utl::MediaDescriptor aModelArgs(xModel->getArgs()); + utl::MediaDescriptor::iterator pIt = aModelArgs.find( utl::MediaDescriptor::PROP_MACROEXECUTIONMODE); + if (pIt != aModelArgs.end()) + rResult[utl::MediaDescriptor::PROP_MACROEXECUTIONMODE] = pIt->second; + } + + return rResult; +} + +} + +void LoadEnv::startLoading(const OUString& sURL, const uno::Sequence& lMediaDescriptor, + const uno::Reference& xBaseFrame, const OUString& sTarget, + sal_Int32 nSearchFlags, LoadEnvFeatures eFeature) +{ + osl::MutexGuard g(m_mutex); + + // Handle still running processes! + if (m_xAsynchronousJob.is()) + throw LoadEnvException(LoadEnvException::ID_STILL_RUNNING); + + // take over all new parameters. + m_xTargetFrame.clear(); + m_xBaseFrame = xBaseFrame; + m_lMediaDescriptor = addModelArgs(lMediaDescriptor); + m_sTarget = sTarget; + m_nSearchFlags = nSearchFlags; + m_eFeature = eFeature; + m_eContentType = E_UNSUPPORTED_CONTENT; + m_bCloseFrameOnError = false; + m_bReactivateControllerOnError = false; + m_bLoaded = false; + + OUString aRealURL; + if (!officecfg::Office::Common::Load::DetectWebDAVRedirection::get() + || !tools::IsMappedWebDAVPath(sURL, &aRealURL)) + aRealURL = sURL; + + // try to find out, if it's really a content, which can be loaded or must be "handled" + // We use a default value for this in-parameter. Then we have to start a complex check method + // internally. But if this check was already done outside it can be suppressed to perform + // the load request. We take over the result then! + m_eContentType = LoadEnv::classifyContent(aRealURL, lMediaDescriptor); + if (m_eContentType == E_UNSUPPORTED_CONTENT) + throw LoadEnvException(LoadEnvException::ID_UNSUPPORTED_CONTENT, "from LoadEnv::startLoading"); + + // make URL part of the MediaDescriptor + // It doesn't matter if it is already an item of it. + // It must be the same value... so we can overwrite it :-) + m_lMediaDescriptor[utl::MediaDescriptor::PROP_URL] <<= aRealURL; + + // parse it - because some following code require that + m_aURL.Complete = aRealURL; + uno::Reference xParser(util::URLTransformer::create(m_xContext)); + xParser->parseStrict(m_aURL); + + // BTW: Split URL and JumpMark ... + // Because such mark is an explicit value of the media descriptor! + if (!m_aURL.Mark.isEmpty()) + m_lMediaDescriptor[utl::MediaDescriptor::PROP_JUMPMARK] <<= m_aURL.Mark; + + // By the way: remove the old and deprecated value "FileName" from the descriptor! + utl::MediaDescriptor::iterator pIt = m_lMediaDescriptor.find(utl::MediaDescriptor::PROP_FILENAME); + if (pIt != m_lMediaDescriptor.end()) + m_lMediaDescriptor.erase(pIt); + + // patch the MediaDescriptor, so it fulfil the outside requirements + // Means especially items like e.g. UI InteractionHandler, Status Indicator, + // MacroExecutionMode, etc. + + /*TODO progress is bound to a frame ... How can we set it here? */ + + // UI mode + const bool bUIMode = + (m_eFeature & LoadEnvFeatures::WorkWithUI) && + !m_lMediaDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_HIDDEN, false) && + !m_lMediaDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_PREVIEW, false); + + if( comphelper::LibreOfficeKit::isActive() && + m_lMediaDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_SILENT, false)) + { + rtl::Reference pQuietInteraction = new QuietInteraction(); + uno::Reference xInteractionHandler(pQuietInteraction); + m_lMediaDescriptor[utl::MediaDescriptor::PROP_INTERACTIONHANDLER] <<= xInteractionHandler; + } + + initializeUIDefaults(m_xContext, m_lMediaDescriptor, bUIMode, &m_pQuietInteraction); + + start(); +} + +void LoadEnv::initializeUIDefaults( const css::uno::Reference< css::uno::XComponentContext >& i_rxContext, + utl::MediaDescriptor& io_lMediaDescriptor, const bool i_bUIMode, + rtl::Reference* o_ppQuietInteraction ) +{ + css::uno::Reference< css::task::XInteractionHandler > xInteractionHandler; + sal_Int16 nMacroMode; + sal_Int16 nUpdateMode; + + if ( i_bUIMode ) + { + nMacroMode = css::document::MacroExecMode::USE_CONFIG; + nUpdateMode = css::document::UpdateDocMode::ACCORDING_TO_CONFIG; + try + { + // tdf#154308 At least for the case the document is launched from the StartCenter, put that StartCenter as the + // parent for any dialogs that may appear during typedetection (once load starts a permanent frame will be set + // anyway and used as dialog parent, which will be this one if the startcenter was running) + css::uno::Reference xSupplier = css::frame::Desktop::create(i_rxContext); + FrameListAnalyzer aTasksAnalyzer(xSupplier, css::uno::Reference(), FrameAnalyzerFlags::BackingComponent); + css::uno::Reference xDialogParent(aTasksAnalyzer.m_xBackingComponent ? + aTasksAnalyzer.m_xBackingComponent->getContainerWindow() : + nullptr); + + xInteractionHandler.set( css::task::InteractionHandler::createWithParent(i_rxContext, xDialogParent), css::uno::UNO_QUERY_THROW ); + } + catch(const css::uno::RuntimeException&) {throw;} + catch(const css::uno::Exception& ) { } + } + // hidden mode + else + { + nMacroMode = css::document::MacroExecMode::NEVER_EXECUTE; + nUpdateMode = css::document::UpdateDocMode::NO_UPDATE; + rtl::Reference pQuietInteraction = new QuietInteraction(); + xInteractionHandler = pQuietInteraction.get(); + if ( o_ppQuietInteraction != nullptr ) + { + *o_ppQuietInteraction = pQuietInteraction; + } + } + + if ( xInteractionHandler.is() ) + { + if( io_lMediaDescriptor.find(utl::MediaDescriptor::PROP_INTERACTIONHANDLER) == io_lMediaDescriptor.end() ) + { + io_lMediaDescriptor[utl::MediaDescriptor::PROP_INTERACTIONHANDLER] <<= xInteractionHandler; + } + if( io_lMediaDescriptor.find(utl::MediaDescriptor::PROP_AUTHENTICATIONHANDLER) == io_lMediaDescriptor.end() ) + { + io_lMediaDescriptor[utl::MediaDescriptor::PROP_AUTHENTICATIONHANDLER] <<= xInteractionHandler; + } + } + + if (io_lMediaDescriptor.find(utl::MediaDescriptor::PROP_MACROEXECUTIONMODE) == io_lMediaDescriptor.end()) + io_lMediaDescriptor[utl::MediaDescriptor::PROP_MACROEXECUTIONMODE] <<= nMacroMode; + + if (io_lMediaDescriptor.find(utl::MediaDescriptor::PROP_UPDATEDOCMODE) == io_lMediaDescriptor.end()) + io_lMediaDescriptor[utl::MediaDescriptor::PROP_UPDATEDOCMODE] <<= nUpdateMode; +} + +void LoadEnv::start() +{ + // SAFE -> + { + osl::MutexGuard aReadLock(m_mutex); + + // Handle still running processes! + if (m_xAsynchronousJob.is()) + throw LoadEnvException(LoadEnvException::ID_STILL_RUNNING); + + // content can not be loaded or handled + // check "classifyContent()" failed before ... + if (m_eContentType == E_UNSUPPORTED_CONTENT) + throw LoadEnvException(LoadEnvException::ID_UNSUPPORTED_CONTENT, + "from LoadEnv::start"); + } + // <- SAFE + + // detect its type/filter etc. + // This information will be available by the + // used descriptor member afterwards and is needed + // for all following operations! + // Note: An exception will be thrown, in case operation was not successfully ... + if (m_eContentType != E_CAN_BE_SET)/* Attention: special feature to set existing component on a frame must ignore type detection! */ + impl_detectTypeAndFilter(); + + // start loading the content... + // Attention: Don't check m_eContentType deeper then UNSUPPORTED/SUPPORTED! + // Because it was made in the easiest way... may a flat detection was made only. + // And such simple detection can fail sometimes .-) + // Use another strategy here. Try it and let it run into the case "loading not possible". + bool bStarted = false; + if ( + (m_eFeature & LoadEnvFeatures::AllowContentHandler) && + (m_eContentType != E_CAN_BE_SET ) /* Attention: special feature to set existing component on a frame must ignore type detection! */ + ) + { + bStarted = impl_handleContent(); + } + + if (!bStarted) + bStarted = impl_loadContent(); + + // not started => general error + // We can't say - what was the reason for. + if (!bStarted) + throw LoadEnvException( + LoadEnvException::ID_GENERAL_ERROR, "not started"); +} + +/*----------------------------------------------- + TODO + First draft does not implement timeout using [ms]. + Current implementation counts yield calls only ... +-----------------------------------------------*/ +bool LoadEnv::waitWhileLoading(sal_uInt32 nTimeout) +{ + // Because it's not a good idea to block the main thread + // (and we can't be sure that we are currently not used inside the + // main thread!), we can't use conditions here really. We must yield + // in an intelligent manner :-) + + sal_Int32 nTime = nTimeout; + while(!Application::IsQuit()) + { + // SAFE -> ------------------------------ + { + osl::MutexGuard aReadLock1(m_mutex); + if (!m_xAsynchronousJob.is()) + break; + } + // <- SAFE ------------------------------ + + Application::Yield(); + + // forever! + if (nTimeout==0) + continue; + + // timed out? + --nTime; + if (nTime<1) + break; + } + + osl::MutexGuard g(m_mutex); + return !m_xAsynchronousJob.is(); +} + +css::uno::Reference< css::lang::XComponent > LoadEnv::getTargetComponent() const +{ + osl::MutexGuard g(m_mutex); + + if (!m_xTargetFrame.is()) + return css::uno::Reference< css::lang::XComponent >(); + + css::uno::Reference< css::frame::XController > xController = m_xTargetFrame->getController(); + if (!xController.is()) + return m_xTargetFrame->getComponentWindow(); + + css::uno::Reference< css::frame::XModel > xModel = xController->getModel(); + if (!xModel.is()) + return xController; + + return xModel; +} + +void SAL_CALL LoadEnvListener::loadFinished(const css::uno::Reference< css::frame::XFrameLoader >&) +{ + std::unique_lock g(m_mutex); + if (m_bWaitingResult) + m_pLoadEnv->impl_setResult(true); + m_bWaitingResult = false; +} + +void SAL_CALL LoadEnvListener::loadCancelled(const css::uno::Reference< css::frame::XFrameLoader >&) +{ + std::unique_lock g(m_mutex); + if (m_bWaitingResult) + m_pLoadEnv->impl_setResult(false); + m_bWaitingResult = false; +} + +void SAL_CALL LoadEnvListener::dispatchFinished(const css::frame::DispatchResultEvent& aEvent) +{ + std::unique_lock g(m_mutex); + + if (!m_bWaitingResult) + return; + + switch(aEvent.State) + { + case css::frame::DispatchResultState::FAILURE : + m_pLoadEnv->impl_setResult(false); + break; + + case css::frame::DispatchResultState::SUCCESS : + m_pLoadEnv->impl_setResult(false); + break; + + case css::frame::DispatchResultState::DONTKNOW : + m_pLoadEnv->impl_setResult(false); + break; + } + m_bWaitingResult = false; +} + +void SAL_CALL LoadEnvListener::disposing(const css::lang::EventObject&) +{ + std::unique_lock g(m_mutex); + if (m_bWaitingResult) + m_pLoadEnv->impl_setResult(false); + m_bWaitingResult = false; +} + +void LoadEnv::impl_setResult(bool bResult) +{ + osl::MutexGuard g(m_mutex); + + m_bLoaded = bResult; + + impl_reactForLoadingState(); + + // clearing of this reference will unblock waitWhileLoading()! + // So we must be sure, that loading process was really finished. + // => do it as last operation of this method ... + m_xAsynchronousJob.clear(); +} + +/*----------------------------------------------- + TODO: Is it a good idea to change Sequence<> + parameter to stl-adapter? +-----------------------------------------------*/ +LoadEnv::EContentType LoadEnv::classifyContent(const OUString& sURL , + const css::uno::Sequence< css::beans::PropertyValue >& lMediaDescriptor) +{ + + // (i) Filter some special well known URL protocols, + // which can not be handled or loaded in general. + // Of course an empty URL must be ignored here too. + // Note: These URL schemata are fix and well known ... + // But there can be some additional ones, which was not + // defined at implementation time of this class :-( + // So we have to make sure, that the following code + // can detect such protocol schemata too :-) + + if( + (sURL.isEmpty() ) || + (ProtocolCheck::isProtocol(sURL,EProtocol::Uno )) || + (ProtocolCheck::isProtocol(sURL,EProtocol::Slot )) || + (ProtocolCheck::isProtocol(sURL,EProtocol::Macro )) || + (ProtocolCheck::isProtocol(sURL,EProtocol::Service)) || + (ProtocolCheck::isProtocol(sURL,EProtocol::MailTo )) || + (ProtocolCheck::isProtocol(sURL,EProtocol::News )) + ) + { + return E_UNSUPPORTED_CONTENT; + } + + // (ii) Some special URLs indicates a given input stream, + // a full featured document model directly or + // specify a request for opening an empty document. + // Such contents are loadable in general. + // But we have to check, if the media descriptor contains + // all needed resources. If they are missing - the following + // load request will fail. + + /* Attention: The following code can't work on such special URLs! + It should not break the office... but it makes no sense + to start expensive object creations and complex search + algorithm if it's clear, that such URLs must be handled + in a special way .-) + */ + + // creation of new documents + if (ProtocolCheck::isProtocol(sURL,EProtocol::PrivateFactory)) + return E_CAN_BE_LOADED; + + // using of an existing input stream + utl::MediaDescriptor stlMediaDescriptor(lMediaDescriptor); + utl::MediaDescriptor::const_iterator pIt; + if (ProtocolCheck::isProtocol(sURL,EProtocol::PrivateStream)) + { + pIt = stlMediaDescriptor.find(utl::MediaDescriptor::PROP_INPUTSTREAM); + css::uno::Reference< css::io::XInputStream > xStream; + if (pIt != stlMediaDescriptor.end()) + pIt->second >>= xStream; + if (xStream.is()) + return E_CAN_BE_LOADED; + SAL_INFO("fwk.loadenv", "LoadEnv::classifyContent(): loading from stream with right URL but invalid stream detected"); + return E_UNSUPPORTED_CONTENT; + } + + // using of a full featured document + if (ProtocolCheck::isProtocol(sURL,EProtocol::PrivateObject)) + { + pIt = stlMediaDescriptor.find(utl::MediaDescriptor::PROP_MODEL); + css::uno::Reference< css::frame::XModel > xModel; + if (pIt != stlMediaDescriptor.end()) + pIt->second >>= xModel; + if (xModel.is()) + return E_CAN_BE_SET; + SAL_INFO("fwk.loadenv", "LoadEnv::classifyContent(): loading with object with right URL but invalid object detected"); + return E_UNSUPPORTED_CONTENT; + } + + // following operations can work on an internal type name only :-( + css::uno::Reference< css::uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext(); + css::uno::Reference< css::document::XTypeDetection > xDetect( + xContext->getServiceManager()->createInstanceWithContext( + "com.sun.star.document.TypeDetection", xContext), + css::uno::UNO_QUERY_THROW); + + OUString sType = xDetect->queryTypeByURL(sURL); + + css::uno::Reference< css::frame::XLoaderFactory > xLoaderFactory; + css::uno::Reference< css::container::XEnumeration > xSet; + + // (iii) If a FrameLoader service (or at least + // a Filter) can be found, which supports + // this URL - it must be a loadable content. + // Because both items are registered for types + // it's enough to check for frame loaders only. + // Most of our filters are handled by our global + // default loader. But there exist some specialized + // loader, which does not work on top of filters! + // So it's not enough to search on the filter configuration. + // Further it's not enough to search for types! + // Because there exist some types, which are referenced by + // other objects... but neither by filters nor frame loaders! + css::uno::Sequence< OUString > lTypesReg { sType }; + css::uno::Sequence< css::beans::NamedValue > lQuery + { + css::beans::NamedValue(PROP_TYPES, css::uno::Any(lTypesReg)) + }; + + xLoaderFactory = css::frame::FrameLoaderFactory::create(xContext); + xSet = xLoaderFactory->createSubSetEnumerationByProperties(lQuery); + // at least one registered frame loader is enough! + if (xSet->hasMoreElements()) + return E_CAN_BE_LOADED; + + // (iv) Some URL protocols are supported by special services. + // E.g. ContentHandler. + // Such contents can be handled ... but not loaded. + + xLoaderFactory = css::frame::ContentHandlerFactory::create(xContext); + xSet = xLoaderFactory->createSubSetEnumerationByProperties(lQuery); + // at least one registered content handler is enough! + if (xSet->hasMoreElements()) + return E_CAN_BE_HANDLED; + + // (v) Last but not least the UCB is used inside office to + // load contents. He has a special configuration to know + // which URL schemata can be used inside office. + css::uno::Reference< css::ucb::XUniversalContentBroker > xUCB(css::ucb::UniversalContentBroker::create(xContext)); + if (xUCB->queryContentProvider(sURL).is()) + return E_CAN_BE_LOADED; + + // (TODO) At this point, we have no idea .-) + // But it seems to be better, to break all + // further requests for this URL. Otherwise + // we can run into some trouble. + return E_UNSUPPORTED_CONTENT; +} + +namespace { + +bool queryOrcusTypeAndFilter(const uno::Sequence& rDescriptor, OUString& rType, OUString& rFilter) +{ + OUString aURL; + sal_Int32 nSize = rDescriptor.getLength(); + for (sal_Int32 i = 0; i < nSize; ++i) + { + const beans::PropertyValue& rProp = rDescriptor[i]; + if (rProp.Name == "URL") + { + rProp.Value >>= aURL; + break; + } + } + + if (aURL.isEmpty() || o3tl::equalsIgnoreAsciiCase(aURL.subView(0,8), u"private:")) + return false; + + // TODO : Type must be set to be generic_Text (or any other type that + // exists) in order to find a usable loader. Exploit it as a temporary + // hack. + + // depending on the experimental mode + if (!officecfg::Office::Common::Misc::ExperimentalMode::get()) + { + return false; + } + + OUString aUseOrcus; + rtl::Bootstrap::get("LIBO_USE_ORCUS", aUseOrcus); + bool bUseOrcus = (aUseOrcus == "YES"); + + if (!bUseOrcus) + return false; + + if (aURL.endsWith(".xlsx")) + { + rType = "generic_Text"; + rFilter = "xlsx"; + return true; + } + else if (aURL.endsWith(".ods")) + { + rType = "generic_Text"; + rFilter = "ods"; + return true; + } + else if (aURL.endsWith(".csv")) + { + rType = "generic_Text"; + rFilter = "csv"; + return true; + } + + return false; +} + +} + +void LoadEnv::impl_detectTypeAndFilter() +{ + static const sal_Int32 FILTERFLAG_TEMPLATEPATH = 16; + + // SAFE -> + osl::ClearableMutexGuard aReadLock(m_mutex); + + // Attention: Because our stl media descriptor is a copy of a uno sequence + // we can't use as an in/out parameter here. Copy it before and don't forget to + // update structure afterwards again! + css::uno::Sequence< css::beans::PropertyValue > lDescriptor = m_lMediaDescriptor.getAsConstPropertyValueList(); + css::uno::Reference< css::uno::XComponentContext > xContext = m_xContext; + + aReadLock.clear(); + // <- SAFE + + OUString sType, sFilter; + + if (queryOrcusTypeAndFilter(lDescriptor, sType, sFilter) && !sType.isEmpty() && !sFilter.isEmpty()) + { + // SAFE -> + osl::MutexGuard aWriteLock(m_mutex); + + // Orcus type detected. Skip the normal type detection process. + m_lMediaDescriptor << lDescriptor; + m_lMediaDescriptor[utl::MediaDescriptor::PROP_TYPENAME] <<= sType; + m_lMediaDescriptor[utl::MediaDescriptor::PROP_FILTERNAME] <<= sFilter; + m_lMediaDescriptor[utl::MediaDescriptor::PROP_FILTERPROVIDER] <<= OUString("orcus"); + m_lMediaDescriptor[utl::MediaDescriptor::PROP_DOCUMENTSERVICE] <<= OUString("com.sun.star.sheet.SpreadsheetDocument"); + return; + // <- SAFE + } + + css::uno::Reference< css::document::XTypeDetection > xDetect( + xContext->getServiceManager()->createInstanceWithContext( + "com.sun.star.document.TypeDetection", xContext), + css::uno::UNO_QUERY_THROW); + sType = xDetect->queryTypeByDescriptor(lDescriptor, true); /*TODO should deep detection be able for enable/disable it from outside? */ + + // no valid content -> loading not possible + if (sType.isEmpty()) + throw LoadEnvException( + LoadEnvException::ID_UNSUPPORTED_CONTENT, "type detection failed"); + + // SAFE -> + osl::ResettableMutexGuard aWriteLock(m_mutex); + + // detection was successful => update the descriptor member of this class + m_lMediaDescriptor << lDescriptor; + m_lMediaDescriptor[utl::MediaDescriptor::PROP_TYPENAME] <<= sType; + // Is there an already detected (may be preselected) filter? + // see below ... + sFilter = m_lMediaDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_FILTERNAME, OUString()); + + aWriteLock.clear(); + // <- SAFE + + // We do have potentially correct type, but the detection process was aborted. + if (m_lMediaDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_ABORTED, false)) + throw LoadEnvException( + LoadEnvException::ID_UNSUPPORTED_CONTENT, "type detection aborted"); + + // But the type isn't enough. For loading sometimes we need more information. + // E.g. for our "_default" feature, where we recycle any frame which contains + // and "Untitled" document, we must know if the new document is based on a template! + // But this information is available as a filter property only. + // => We must try(!) to detect the right filter for this load request. + // On the other side ... if no filter is available .. ignore it. + // Then the type information must be enough. + if (sFilter.isEmpty()) + { + // no -> try to find a preferred filter for the detected type. + // Don't forget to update the media descriptor. + css::uno::Reference< css::container::XNameAccess > xTypeCont(xDetect, css::uno::UNO_QUERY_THROW); + try + { + ::comphelper::SequenceAsHashMap lTypeProps(xTypeCont->getByName(sType)); + sFilter = lTypeProps.getUnpackedValueOrDefault("PreferredFilter", OUString()); + if (!sFilter.isEmpty()) + { + // SAFE -> + aWriteLock.reset(); + m_lMediaDescriptor[utl::MediaDescriptor::PROP_FILTERNAME] <<= sFilter; + aWriteLock.clear(); + // <- SAFE + } + } + catch(const css::container::NoSuchElementException&) + {} + } + + // check if the filter (if one exists) points to a template format filter. + // Then we have to add the property "AsTemplate". + // We need this information to decide afterwards if we can use a "recycle frame" + // for target "_default" or has to create a new one every time. + // On the other side we have to suppress that, if this property already exists + // and should trigger a special handling. Then the outside call of this method here, + // has to know, what he is doing .-) + + bool bIsOwnTemplate = false; + if (!sFilter.isEmpty()) + { + css::uno::Reference< css::container::XNameAccess > xFilterCont(xContext->getServiceManager()->createInstanceWithContext(SERVICENAME_FILTERFACTORY, xContext), css::uno::UNO_QUERY_THROW); + try + { + ::comphelper::SequenceAsHashMap lFilterProps(xFilterCont->getByName(sFilter)); + sal_Int32 nFlags = lFilterProps.getUnpackedValueOrDefault("Flags", sal_Int32(0)); + bIsOwnTemplate = ((nFlags & FILTERFLAG_TEMPLATEPATH) == FILTERFLAG_TEMPLATEPATH); + } + catch(const css::container::NoSuchElementException&) + {} + } + if (bIsOwnTemplate) + { + // SAFE -> + aWriteLock.reset(); + // Don't overwrite external decisions! See comments before ... + utl::MediaDescriptor::const_iterator pAsTemplateItem = m_lMediaDescriptor.find(utl::MediaDescriptor::PROP_ASTEMPLATE); + if (pAsTemplateItem == m_lMediaDescriptor.end()) + m_lMediaDescriptor[utl::MediaDescriptor::PROP_ASTEMPLATE] <<= true; + aWriteLock.clear(); + // <- SAFE + } +} + +bool LoadEnv::impl_handleContent() +{ + // SAFE -> ----------------------------------- + osl::ClearableMutexGuard aReadLock(m_mutex); + + // the type must exist inside the descriptor ... otherwise this class is implemented wrong :-) + OUString sType = m_lMediaDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_TYPENAME, OUString()); + if (sType.isEmpty()) + throw LoadEnvException(LoadEnvException::ID_INVALID_MEDIADESCRIPTOR); + + // convert media descriptor and URL to right format for later interface call! + css::uno::Sequence< css::beans::PropertyValue > lDescriptor; + m_lMediaDescriptor >> lDescriptor; + css::util::URL aURL = m_aURL; + + // get necessary container to query for a handler object + css::uno::Reference< css::frame::XLoaderFactory > xLoaderFactory = css::frame::ContentHandlerFactory::create(m_xContext); + + aReadLock.clear(); + // <- SAFE ----------------------------------- + + // query + css::uno::Sequence< OUString > lTypeReg { sType }; + + css::uno::Sequence< css::beans::NamedValue > lQuery { { PROP_TYPES, css::uno::Any(lTypeReg) } }; + + css::uno::Reference< css::container::XEnumeration > xSet = xLoaderFactory->createSubSetEnumerationByProperties(lQuery); + while(xSet->hasMoreElements()) + { + ::comphelper::SequenceAsHashMap lProps (xSet->nextElement()); + OUString sHandler = lProps.getUnpackedValueOrDefault(PROP_NAME, OUString()); + + css::uno::Reference< css::frame::XNotifyingDispatch > xHandler; + try + { + xHandler.set(xLoaderFactory->createInstance(sHandler), css::uno::UNO_QUERY); + if (!xHandler.is()) + continue; + } + catch(const css::uno::RuntimeException&) + { throw; } + catch(const css::uno::Exception&) + { continue; } + + // SAFE -> ----------------------------------- + osl::ClearableMutexGuard aWriteLock(m_mutex); + m_xAsynchronousJob = xHandler; + rtl::Reference xListener = new LoadEnvListener(this); + aWriteLock.clear(); + // <- SAFE ----------------------------------- + + xHandler->dispatchWithNotification(aURL, lDescriptor, xListener); + + return true; + } + + return false; +} + +bool LoadEnv::impl_furtherDocsAllowed() +{ + // SAFE -> + osl::ResettableMutexGuard aReadLock(m_mutex); + css::uno::Reference< css::uno::XComponentContext > xContext = m_xContext; + aReadLock.clear(); + // <- SAFE + + bool bAllowed = true; + + try + { + std::optional x(officecfg::Office::Common::Misc::MaxOpenDocuments::get()); + + // NIL means: count of allowed documents = infinite ! + // => return true + if ( !x) + bAllowed = true; + else + { + sal_Int32 nMaxOpenDocuments(*x); + + css::uno::Reference< css::frame::XFramesSupplier > xDesktop( + css::frame::Desktop::create(xContext), + css::uno::UNO_QUERY_THROW); + + FrameListAnalyzer aAnalyzer(xDesktop, + css::uno::Reference< css::frame::XFrame >(), + FrameAnalyzerFlags::Help | + FrameAnalyzerFlags::BackingComponent | + FrameAnalyzerFlags::Hidden); + + sal_Int32 nOpenDocuments = aAnalyzer.m_lOtherVisibleFrames.size(); + bAllowed = (nOpenDocuments < nMaxOpenDocuments); + } + } + catch(const css::uno::Exception&) + { bAllowed = true; } // !! internal errors are no reason to disturb the office from opening documents .-) + + if ( ! bAllowed ) + { + // SAFE -> + aReadLock.reset(); + css::uno::Reference< css::task::XInteractionHandler > xInteraction = m_lMediaDescriptor.getUnpackedValueOrDefault( + utl::MediaDescriptor::PROP_INTERACTIONHANDLER, + css::uno::Reference< css::task::XInteractionHandler >()); + aReadLock.clear(); + // <- SAFE + + if (xInteraction.is()) + { + css::uno::Any aInteraction; + + rtl::Reference pAbort = new comphelper::OInteractionAbort(); + rtl::Reference pApprove = new comphelper::OInteractionApprove(); + + css::uno::Sequence< css::uno::Reference< css::task::XInteractionContinuation > > lContinuations{ + pAbort, pApprove + }; + + css::task::ErrorCodeRequest aErrorCode; + aErrorCode.ErrCode = sal_uInt32(ERRCODE_SFX_NOMOREDOCUMENTSALLOWED); + aInteraction <<= aErrorCode; + xInteraction->handle( InteractionRequest::CreateRequest(aInteraction, lContinuations) ); + } + } + + return bAllowed; +} + +bool LoadEnv::impl_filterHasInteractiveDialog() const +{ + //show the frame now so it can be the parent for any message dialogs shown during import + + //unless (tdf#114648) an Interactive case such as the new database wizard + if (m_aURL.Arguments == "Interactive") + return true; + + // unless (tdf#116277) it's the labels/business cards slave frame + if (m_aURL.Arguments.indexOf("slot=") != -1) + return true; + + OUString sFilter = m_lMediaDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_FILTERNAME, OUString()); + if (sFilter.isEmpty()) + return false; + + // unless (tdf#115683) the filter has a UIComponent + OUString sUIComponent; + css::uno::Reference xFilterCont(m_xContext->getServiceManager()->createInstanceWithContext(SERVICENAME_FILTERFACTORY, m_xContext), + css::uno::UNO_QUERY_THROW); + try + { + ::comphelper::SequenceAsHashMap lFilterProps(xFilterCont->getByName(sFilter)); + sUIComponent = lFilterProps.getUnpackedValueOrDefault("UIComponent", OUString()); + } + catch(const css::container::NoSuchElementException&) + { + } + + return !sUIComponent.isEmpty(); +} + +bool LoadEnv::impl_loadContent() +{ + // SAFE -> ----------------------------------- + osl::ClearableMutexGuard aWriteLock(m_mutex); + + // search or create right target frame + OUString sTarget = m_sTarget; + if (TargetHelper::matchSpecialTarget(sTarget, TargetHelper::ESpecialTarget::Default)) + { + m_xTargetFrame = impl_searchAlreadyLoaded(); + if (m_xTargetFrame.is()) + { + impl_setResult(true); + return true; + } + m_xTargetFrame = impl_searchRecycleTarget(); + } + + if (! m_xTargetFrame.is()) + { + if ( + (TargetHelper::matchSpecialTarget(sTarget, TargetHelper::ESpecialTarget::Blank )) || + (TargetHelper::matchSpecialTarget(sTarget, TargetHelper::ESpecialTarget::Default)) + ) + { + if (! impl_furtherDocsAllowed()) + return false; + TaskCreator aCreator(m_xContext); + m_xTargetFrame = aCreator.createTask(SPECIALTARGET_BLANK, m_lMediaDescriptor); + m_bCloseFrameOnError = m_xTargetFrame.is(); + } + else + { + sal_Int32 nSearchFlags = m_nSearchFlags & ~css::frame::FrameSearchFlag::CREATE; + m_xTargetFrame = m_xBaseFrame->findFrame(sTarget, nSearchFlags); + if (! m_xTargetFrame.is()) + { + if (! impl_furtherDocsAllowed()) + return false; + m_xTargetFrame = m_xBaseFrame->findFrame(SPECIALTARGET_BLANK, 0); + m_bCloseFrameOnError = m_xTargetFrame.is(); + } + } + } + + // If we couldn't find a valid frame or the frame has no container window + // we have to throw an exception. + if ( + ( ! m_xTargetFrame.is() ) || + ( ! m_xTargetFrame->getContainerWindow().is() ) + ) + throw LoadEnvException(LoadEnvException::ID_NO_TARGET_FOUND); + + css::uno::Reference< css::frame::XFrame > xTargetFrame = m_xTargetFrame; + + // Now we have a valid frame ... and type detection was already done. + // We should apply the module dependent window position and size to the + // frame window. + impl_applyPersistentWindowState(xTargetFrame->getContainerWindow()); + + // Don't forget to lock task for following load process. Otherwise it could die + // during this operation runs by terminating the office or closing this task via api. + // If we set this lock "close()" will return false and closing will be broken. + // Attention: Don't forget to reset this lock again after finishing operation. + // Otherwise task AND office couldn't die!!! + // This includes gracefully handling of Exceptions (Runtime!) too ... + // That's why we use a specialized guard, which will reset the lock + // if it will be run out of scope. + + // Note further: ignore if this internal guard already contains a resource. + // Might impl_searchRecycleTarget() set it before. But in case this impl-method wasn't used + // and the target frame was new created ... this lock here must be set! + css::uno::Reference< css::document::XActionLockable > xTargetLock(xTargetFrame, css::uno::UNO_QUERY); + m_aTargetLock.setResource(xTargetLock); + + // Add status indicator to descriptor. Loader can show a progress then. + // But don't do it, if loading should be hidden or preview is used...! + // So we prevent our code against wrong using. Why? + // It could be, that using of this progress could make trouble. e.g. He makes window visible... + // but shouldn't do that. But if no indicator is available... nobody has a chance to do that! + bool bHidden = m_lMediaDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_HIDDEN, false); + bool bMinimized = m_lMediaDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_MINIMIZED, false); + bool bPreview = m_lMediaDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_PREVIEW, false); + + if (!bHidden && !bMinimized && !bPreview) + { + css::uno::Reference xProgress = m_lMediaDescriptor.getUnpackedValueOrDefault( + utl::MediaDescriptor::PROP_STATUSINDICATOR, css::uno::Reference()); + if (!xProgress.is()) + { + // Note: it's an optional interface! + css::uno::Reference< css::task::XStatusIndicatorFactory > xProgressFactory(xTargetFrame, css::uno::UNO_QUERY); + if (xProgressFactory.is()) + { + xProgress = xProgressFactory->createStatusIndicator(); + if (xProgress.is()) + m_lMediaDescriptor[utl::MediaDescriptor::PROP_STATUSINDICATOR] <<= xProgress; + } + } + + // Now that we have a target window into which we can load, reinit the interaction handler to have this + // window as its parent for modal dialogs and ensure the window is visible + css::uno::Reference< css::task::XInteractionHandler > xInteraction = m_lMediaDescriptor.getUnpackedValueOrDefault( + utl::MediaDescriptor::PROP_INTERACTIONHANDLER, + css::uno::Reference< css::task::XInteractionHandler >()); + css::uno::Reference xHandler(xInteraction, css::uno::UNO_QUERY); + if (xHandler.is()) + { + css::uno::Reference xWindow = xTargetFrame->getContainerWindow(); + uno::Sequence aArguments(comphelper::InitAnyPropertySequence( + { + {"Parent", uno::Any(xWindow)} + })); + xHandler->initialize(aArguments); + //show the frame as early as possible to make it the parent of any message dialogs + if (!impl_filterHasInteractiveDialog()) + { + impl_makeFrameWindowVisible(xWindow, shouldFocusAndToFront()); + m_bFocusedAndToFront = true; // no need to ask shouldFocusAndToFront second time + } + } + } + + // convert media descriptor and URL to right format for later interface call! + css::uno::Sequence< css::beans::PropertyValue > lDescriptor; + m_lMediaDescriptor >> lDescriptor; + OUString sURL = m_aURL.Complete; + + // try to locate any interested frame loader + css::uno::Reference< css::uno::XInterface > xLoader = impl_searchLoader(); + css::uno::Reference< css::frame::XFrameLoader > xAsyncLoader(xLoader, css::uno::UNO_QUERY); + css::uno::Reference< css::frame::XSynchronousFrameLoader > xSyncLoader (xLoader, css::uno::UNO_QUERY); + + if (xAsyncLoader.is()) + { + m_xAsynchronousJob = xAsyncLoader; + rtl::Reference xListener = new LoadEnvListener(this); + aWriteLock.clear(); + // <- SAFE ----------------------------------- + + xAsyncLoader->load(xTargetFrame, sURL, lDescriptor, xListener); + + return true; + } + else if (xSyncLoader.is()) + { + uno::Reference xTargetFrameProps(xTargetFrame, uno::UNO_QUERY); + if (xTargetFrameProps.is()) + { + // Set the URL on the frame itself, for the duration of the load, when it has no + // controller. + xTargetFrameProps->setPropertyValue("URL", uno::Any(sURL)); + } + bool bResult = xSyncLoader->load(lDescriptor, xTargetFrame); + // react for the result here, so the outside waiting + // code can ask for it later. + impl_setResult(bResult); + // But the return value indicates a valid started(!) operation. + // And that's true every time we reach this line :-) + return true; + } + + aWriteLock.clear(); + // <- SAFE + + return false; +} + +css::uno::Reference< css::uno::XInterface > LoadEnv::impl_searchLoader() +{ + // SAFE -> ----------------------------------- + osl::ClearableMutexGuard aReadLock(m_mutex); + + // special mode to set an existing component on this frame + // In such case the loader is fix. It must be the SFX based implementation, + // which can create a view on top of such xModel components :-) + if (m_eContentType == E_CAN_BE_SET) + { + try + { + return css::frame::OfficeFrameLoader::create(m_xContext); + } + catch(const css::uno::RuntimeException&) + { throw; } + catch(const css::uno::Exception&) + {} + throw LoadEnvException(LoadEnvException::ID_INVALID_ENVIRONMENT); + } + + // Otherwise... + // We need this type information to locate a registered frame loader + // Without such information we can't work! + OUString sType = m_lMediaDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_TYPENAME, OUString()); + if (sType.isEmpty()) + throw LoadEnvException(LoadEnvException::ID_INVALID_MEDIADESCRIPTOR); + + // try to locate any interested frame loader + css::uno::Reference< css::frame::XLoaderFactory > xLoaderFactory = css::frame::FrameLoaderFactory::create(m_xContext); + + aReadLock.clear(); + // <- SAFE ----------------------------------- + + css::uno::Sequence< OUString > lTypesReg { sType }; + + css::uno::Sequence< css::beans::NamedValue > lQuery { { PROP_TYPES, css::uno::Any(lTypesReg) } }; + + css::uno::Reference< css::container::XEnumeration > xSet = xLoaderFactory->createSubSetEnumerationByProperties(lQuery); + while(xSet->hasMoreElements()) + { + try + { + // try everyone ... + // Ignore any loader, which makes trouble :-) + ::comphelper::SequenceAsHashMap lLoaderProps(xSet->nextElement()); + OUString sLoader = lLoaderProps.getUnpackedValueOrDefault(PROP_NAME, OUString()); + css::uno::Reference< css::uno::XInterface > xLoader = xLoaderFactory->createInstance(sLoader); + if (xLoader.is()) + return xLoader; + } + catch(const css::uno::RuntimeException&) + { throw; } + catch(const css::uno::Exception&) + { continue; } + } + + return css::uno::Reference< css::uno::XInterface >(); +} + +void LoadEnv::impl_jumpToMark(const css::uno::Reference< css::frame::XFrame >& xFrame, + const css::util::URL& aURL ) +{ + if (aURL.Mark.isEmpty()) + return; + + css::uno::Reference< css::frame::XDispatchProvider > xProvider(xFrame, css::uno::UNO_QUERY); + if (! xProvider.is()) + return; + + // SAFE -> + osl::ClearableMutexGuard aReadLock(m_mutex); + css::uno::Reference< css::uno::XComponentContext > xContext = m_xContext; + aReadLock.clear(); + // <- SAFE + + css::util::URL aCmd; + aCmd.Complete = ".uno:JumpToMark"; + + css::uno::Reference< css::util::XURLTransformer > xParser(css::util::URLTransformer::create(xContext)); + xParser->parseStrict(aCmd); + + css::uno::Reference< css::frame::XDispatch > xDispatcher = xProvider->queryDispatch(aCmd, SPECIALTARGET_SELF, 0); + if (! xDispatcher.is()) + return; + + ::comphelper::SequenceAsHashMap lArgs; + lArgs[OUString("Bookmark")] <<= aURL.Mark; + xDispatcher->dispatch(aCmd, lArgs.getAsConstPropertyValueList()); +} + +css::uno::Reference< css::frame::XFrame > LoadEnv::impl_searchAlreadyLoaded() +{ + osl::MutexGuard g(m_mutex); + + // such search is allowed for special requests only ... + // or better it's not allowed for some requests in general :-) + if ( + ( ! TargetHelper::matchSpecialTarget(m_sTarget, TargetHelper::ESpecialTarget::Default) ) || + m_lMediaDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_ASTEMPLATE , false) || +// (m_lMediaDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_HIDDEN() , false) == sal_True) || + m_lMediaDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_OPENNEWVIEW, false) + ) + { + return css::uno::Reference< css::frame::XFrame >(); + } + + // check URL + // May it's not useful to start expensive document search, if it + // can fail only .. because we load from a stream or model directly! + if ( + (ProtocolCheck::isProtocol(m_aURL.Complete, EProtocol::PrivateStream )) || + (ProtocolCheck::isProtocol(m_aURL.Complete, EProtocol::PrivateObject )) + /*TODO should be private:factory here tested too? */ + ) + { + return css::uno::Reference< css::frame::XFrame >(); + } + + // otherwise - iterate through the tasks of the desktop container + // to find out, which of them might contains the requested document + css::uno::Reference< css::frame::XDesktop2 > xSupplier = css::frame::Desktop::create( m_xContext ); + css::uno::Reference< css::container::XIndexAccess > xTaskList = xSupplier->getFrames(); + + if (!xTaskList.is()) + return css::uno::Reference< css::frame::XFrame >(); // task list can be empty! + + // Note: To detect if a document was already loaded before + // we check URLs here only. But might the existing and the required + // document has different versions! Then its URLs are the same... + sal_Int16 nNewVersion = m_lMediaDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_VERSION, sal_Int16(-1)); + + // will be used to save the first hidden frame referring the searched model + // Normally we are interested on visible frames... but if there is no such visible + // frame we refer to any hidden frame also (but as fallback only). + css::uno::Reference< css::frame::XFrame > xHiddenTask; + css::uno::Reference< css::frame::XFrame > xTask; + + sal_Int32 count = xTaskList->getCount(); + for (sal_Int32 i=0; igetByIndex(i) >>= xTask; + if (!xTask.is()) + continue; + + OUString sURL; + css::uno::Reference< css::frame::XController > xController = xTask->getController(); + if (!xController.is()) + { + // If we have no controller, then perhaps there is a load in progress. The frame + // itself has the URL in this case. + uno::Reference xTaskProps(xTask, uno::UNO_QUERY); + if (xTaskProps.is()) + { + xTaskProps->getPropertyValue("URL") >>= sURL; + } + if (sURL.isEmpty()) + { + xTask.clear(); + continue; + } + } + + uno::Reference xModel; + if (sURL.isEmpty()) + { + xModel = xController->getModel(); + if (!xModel.is()) + { + xTask.clear(); + continue; + } + + // don't check the complete URL here. + // use its main part - ignore optional jumpmarks! + sURL = xModel->getURL(); + } + if (!::utl::UCBContentHelper::EqualURLs( m_aURL.Main, sURL )) + { + xTask.clear (); + continue; + } + + // get the original load arguments from the current document + // and decide if it's really the same then the one will be. + // It must be visible and must use the same file revision ... + // or must not have any file revision set (-1 == -1!) + utl::MediaDescriptor lOldDocDescriptor; + if (xModel.is()) + { + lOldDocDescriptor = xModel->getArgs(); + + if (lOldDocDescriptor.getUnpackedValueOrDefault( + utl::MediaDescriptor::PROP_VERSION, sal_Int32(-1)) + != nNewVersion) + { + xTask.clear(); + continue; + } + } + + // Hidden frames are special. + // They will be used as "last chance" if there is no visible frame pointing to the same model. + // Safe the result but continue with current loop might be looking for other visible frames. + bool bIsHidden = lOldDocDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_HIDDEN, false); + if ( bIsHidden && ! xHiddenTask.is() ) + { + xHiddenTask = xTask; + xTask.clear (); + continue; + } + + // We found a visible task pointing to the right model ... + // Break search. + break; + } + catch(const css::uno::RuntimeException&) + { throw; } + catch(const css::uno::Exception&) + { continue; } + } + + css::uno::Reference< css::frame::XFrame > xResult; + if (xTask.is()) + xResult = xTask; + else if (xHiddenTask.is()) + xResult = xHiddenTask; + + if (xResult.is()) + { + // Now we are sure, that this task includes the searched document. + // It's time to activate it. As special feature we try to jump internally + // if an optional jumpmark is given too. + if (!m_aURL.Mark.isEmpty()) + impl_jumpToMark(xResult, m_aURL); + } + + return xResult; +} + +bool LoadEnv::impl_isFrameAlreadyUsedForLoading(const css::uno::Reference< css::frame::XFrame >& xFrame) const +{ + css::uno::Reference< css::document::XActionLockable > xLock(xFrame, css::uno::UNO_QUERY); + + // ? no lock interface ? + // Maybe it's an external written frame implementation :-( + // Allowing using of it... but it can fail if it's not synchronized with our processes! + if (!xLock.is()) + return false; + + // Otherwise we have to look for any other existing lock. + return xLock->isActionLocked(); +} + +css::uno::Reference< css::frame::XFrame > LoadEnv::impl_searchRecycleTarget() +{ + // SAFE -> .................................. + osl::ClearableMutexGuard aReadLock(m_mutex); + + // The special backing mode frame will be recycled by definition! + // It doesn't matter if somewhere wants to create a new view + // or open a new untitled document... + // The only exception from that - hidden frames! + if (m_lMediaDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_HIDDEN, false)) + return css::uno::Reference< css::frame::XFrame >(); + + css::uno::Reference< css::frame::XFramesSupplier > xSupplier = css::frame::Desktop::create( m_xContext ); + FrameListAnalyzer aTasksAnalyzer(xSupplier, css::uno::Reference< css::frame::XFrame >(), FrameAnalyzerFlags::BackingComponent); + if (aTasksAnalyzer.m_xBackingComponent.is()) + { + if (!impl_isFrameAlreadyUsedForLoading(aTasksAnalyzer.m_xBackingComponent)) + { + m_bReactivateControllerOnError = true; + return aTasksAnalyzer.m_xBackingComponent; + } + } + + // These states indicates a wish for creation of a new view in general. + if ( + m_lMediaDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_ASTEMPLATE , false) || + m_lMediaDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_OPENNEWVIEW, false) + ) + { + return css::uno::Reference< css::frame::XFrame >(); + } + + // On the other side some special URLs will open a new frame every time (expecting + // they can use the backing-mode frame!) + if ( + (ProtocolCheck::isProtocol(m_aURL.Complete, EProtocol::PrivateFactory )) || + (ProtocolCheck::isProtocol(m_aURL.Complete, EProtocol::PrivateStream )) || + (ProtocolCheck::isProtocol(m_aURL.Complete, EProtocol::PrivateObject )) + ) + { + return css::uno::Reference< css::frame::XFrame >(); + } + + // No backing frame! No special URL => recycle active task - if possible. + // Means - if it does not already contains a modified document, or + // use another office module. + css::uno::Reference< css::frame::XFrame > xTask = xSupplier->getActiveFrame(); + + // not a real error - but might a focus problem! + if (!xTask.is()) + return css::uno::Reference< css::frame::XFrame >(); + + // not a real error - may it's a view only + css::uno::Reference< css::frame::XController > xController = xTask->getController(); + if (!xController.is()) + return css::uno::Reference< css::frame::XFrame >(); + + // not a real error - may it's a db component instead of a full featured office document + css::uno::Reference< css::frame::XModel > xModel = xController->getModel(); + if (!xModel.is()) + return css::uno::Reference< css::frame::XFrame >(); + + // get some more information ... + + // A valid set URL means: there is already a location for this document. + // => it was saved there or opened from there. Such Documents can not be used here. + // We search for empty document ... created by a private:factory/ URL! + if (xModel->getURL().getLength()>0) + return css::uno::Reference< css::frame::XFrame >(); + + // The old document must be unmodified ... + css::uno::Reference< css::util::XModifiable > xModified(xModel, css::uno::UNO_QUERY); + if (xModified->isModified()) + return css::uno::Reference< css::frame::XFrame >(); + + VclPtr pWindow = VCLUnoHelper::GetWindow(xTask->getContainerWindow()); + if (pWindow && pWindow->IsInModalMode()) + return css::uno::Reference< css::frame::XFrame >(); + + // find out the application type of this document + // We can recycle only documents, which uses the same application + // then the new one. + SvtModuleOptions::EFactory eOldApp = SvtModuleOptions::ClassifyFactoryByModel(xModel); + SvtModuleOptions::EFactory eNewApp = SvtModuleOptions::ClassifyFactoryByURL (m_aURL.Complete, m_lMediaDescriptor.getAsConstPropertyValueList()); + + aReadLock.clear(); + // <- SAFE .................................. + + if (eOldApp != eNewApp) + return css::uno::Reference< css::frame::XFrame >(); + + // OK this task seems to be usable for recycling + // But we should mark it as such - means set an action lock. + // Otherwise it would be used more than ones or will be destroyed + // by a close() or terminate() request. + // But if such lock already exist ... it means this task is used for + // any other operation already. Don't use it then. + if (impl_isFrameAlreadyUsedForLoading(xTask)) + return css::uno::Reference< css::frame::XFrame >(); + + // OK - there is a valid target frame. + // But may be it contains already a document. + // Then we have to ask it, if it allows recycling of this frame .-) + bool bReactivateOldControllerOnError = false; + css::uno::Reference< css::frame::XController > xOldDoc = xTask->getController(); + if (xOldDoc.is()) + { + utl::MediaDescriptor lOldDocDescriptor(xModel->getArgs()); + + // replaceable document + if (!lOldDocDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_REPLACEABLE, false)) + return css::uno::Reference< css::frame::XFrame >(); + + bReactivateOldControllerOnError = xOldDoc->suspend(true); + if (! bReactivateOldControllerOnError) + return css::uno::Reference< css::frame::XFrame >(); + } + + // SAFE -> .................................. + { + osl::MutexGuard aWriteLock(m_mutex); + + css::uno::Reference< css::document::XActionLockable > xLock(xTask, css::uno::UNO_QUERY); + if (!m_aTargetLock.setResource(xLock)) + return css::uno::Reference< css::frame::XFrame >(); + + m_bReactivateControllerOnError = bReactivateOldControllerOnError; + } + // <- SAFE .................................. + + return xTask; +} + +void LoadEnv::impl_reactForLoadingState() +{ + /*TODO reset action locks */ + + // SAFE -> ---------------------------------- + osl::ClearableMutexGuard aReadLock(m_mutex); + + if (m_bLoaded) + { + // Bring the new loaded document to front (if allowed!). + // Note: We show new created frames here only. + // We don't hide already visible frames here ... + css::uno::Reference< css::awt::XWindow > xWindow = m_xTargetFrame->getContainerWindow(); + bool bHidden = m_lMediaDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_HIDDEN, false); + bool bMinimized = m_lMediaDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_MINIMIZED, false); + + if (bMinimized) + { + SolarMutexGuard aSolarGuard; + VclPtr pWindow = VCLUnoHelper::GetWindow(xWindow); + // check for system window is necessary to guarantee correct pointer cast! + if (pWindow && pWindow->IsSystemWindow()) + static_cast(pWindow.get())->Minimize(); + } + else if (!bHidden) + { + // show frame ... if it's not still visible ... + // But do nothing if it's already visible! + impl_makeFrameWindowVisible(xWindow, !m_bFocusedAndToFront && shouldFocusAndToFront()); + } + + // Note: Only if an existing property "FrameName" is given by this media descriptor, + // it should be used. Otherwise we should do nothing. May be the outside code has already + // set a frame name on the target! + utl::MediaDescriptor::const_iterator pFrameName = m_lMediaDescriptor.find(utl::MediaDescriptor::PROP_FRAMENAME); + if (pFrameName != m_lMediaDescriptor.end()) + { + OUString sFrameName; + pFrameName->second >>= sFrameName; + // Check the name again. e.g. "_default" isn't allowed. + // On the other side "_beamer" is a valid name :-) + if (TargetHelper::isValidNameForFrame(sFrameName)) + m_xTargetFrame->setName(sFrameName); + } + } + else if (m_bReactivateControllerOnError) + { + // Try to reactivate the old document (if any exists!) + css::uno::Reference< css::frame::XController > xOldDoc = m_xTargetFrame->getController(); + // clear does not depend from reactivation state of a might existing old document! + // We must make sure, that a might following getTargetComponent() call does not return + // the old document! + m_xTargetFrame.clear(); + if (xOldDoc.is()) + { + bool bReactivated = xOldDoc->suspend(false); + if (!bReactivated) + throw LoadEnvException(LoadEnvException::ID_COULD_NOT_REACTIVATE_CONTROLLER); + m_bReactivateControllerOnError = false; + } + } + else if (m_bCloseFrameOnError) + { + // close empty frames + css::uno::Reference< css::util::XCloseable > xCloseable (m_xTargetFrame, css::uno::UNO_QUERY); + + try + { + if (xCloseable.is()) + xCloseable->close(true); + else if (m_xTargetFrame.is()) + m_xTargetFrame->dispose(); + } + catch(const css::util::CloseVetoException&) + {} + catch(const css::lang::DisposedException&) + {} + m_xTargetFrame.clear(); + } + + // This max force an implicit closing of our target frame ... + // e.g. in case close(sal_True) was called before and the frame + // kill itself if our external use-lock is released here! + // That's why we release this lock AFTER ALL OPERATIONS on this frame + // are finished. The frame itself must handle then + // this situation gracefully. + m_aTargetLock.freeResource(); + + // Last but not least :-) + // We have to clear the current media descriptor. + // Otherwise it hold a might existing stream open! + m_lMediaDescriptor.clear(); + + css::uno::Any aRequest; + bool bThrow = false; + if ( !m_bLoaded && m_pQuietInteraction.is() && m_pQuietInteraction->wasUsed() ) + { + aRequest = m_pQuietInteraction->getRequest(); + m_pQuietInteraction.clear(); + bThrow = true; + } + + aReadLock.clear(); + + if (bThrow) + { + if ( aRequest.isExtractableTo( ::cppu::UnoType< css::uno::Exception >::get() ) ) + throw LoadEnvException( + LoadEnvException::ID_GENERAL_ERROR, "interaction request", + aRequest); + } + + // <- SAFE ---------------------------------- +} + +bool LoadEnv::shouldFocusAndToFront() const +{ + bool const preview( + m_lMediaDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_PREVIEW, false)); + return !preview + && officecfg::Office::Common::View::NewDocumentHandling::ForceFocusAndToFront::get(); +} + +void LoadEnv::impl_makeFrameWindowVisible(const css::uno::Reference< css::awt::XWindow >& xWindow , + bool bForceToFront) +{ + SolarMutexGuard aSolarGuard; + VclPtr pWindow = VCLUnoHelper::GetWindow(xWindow); + if ( !pWindow ) + return; + + if (pWindow->IsVisible() && bForceToFront) + pWindow->ToTop( ToTopFlags::RestoreWhenMin | ToTopFlags::ForegroundTask ); + else + pWindow->Show(true, bForceToFront ? ShowFlags::ForegroundTask : ShowFlags::NONE); +} + +void LoadEnv::impl_applyPersistentWindowState(const css::uno::Reference< css::awt::XWindow >& xWindow) +{ + // no window -> action not possible + if (!xWindow.is()) + return; + + // window already visible -> do nothing! If we use a "recycle frame" for loading ... + // the current position and size must be used. + css::uno::Reference< css::awt::XWindow2 > xVisibleCheck(xWindow, css::uno::UNO_QUERY); + if ( + (xVisibleCheck.is() ) && + (xVisibleCheck->isVisible()) + ) + return; + + // SOLAR SAFE -> + { + SolarMutexGuard aSolarGuard1; + + VclPtr pWindow = VCLUnoHelper::GetWindow(xWindow); + if (!pWindow) + return; + + bool bSystemWindow = pWindow->IsSystemWindow(); + bool bWorkWindow = (pWindow->GetType() == WindowType::WORKWINDOW); + + if (!bSystemWindow && !bWorkWindow) + return; + + // don't overwrite this special state! + WorkWindow* pWorkWindow = static_cast(pWindow.get()); + if (pWorkWindow->IsMinimized()) + return; + } + // <- SOLAR SAFE + + // SAFE -> + osl::ClearableMutexGuard aReadLock(m_mutex); + + // no filter -> no module -> no persistent window state + OUString sFilter = m_lMediaDescriptor.getUnpackedValueOrDefault( + utl::MediaDescriptor::PROP_FILTERNAME, + OUString()); + if (sFilter.isEmpty()) + return; + + css::uno::Reference< css::uno::XComponentContext > xContext = m_xContext; + + aReadLock.clear(); + // <- SAFE + + try + { + // retrieve the module name from the filter configuration + css::uno::Reference< css::container::XNameAccess > xFilterCfg( + xContext->getServiceManager()->createInstanceWithContext(SERVICENAME_FILTERFACTORY, xContext), + css::uno::UNO_QUERY_THROW); + ::comphelper::SequenceAsHashMap lProps (xFilterCfg->getByName(sFilter)); + OUString sModule = lProps.getUnpackedValueOrDefault(FILTER_PROPNAME_ASCII_DOCUMENTSERVICE, OUString()); + + // get access to the configuration of this office module + css::uno::Reference< css::container::XNameAccess > xModuleCfg(officecfg::Setup::Office::Factories::get()); + + // read window state from the configuration + // and apply it on the window. + // Do nothing, if no configuration entry exists! + OUString sWindowState; + + // Don't look for persistent window attributes when used through LibreOfficeKit + if( !comphelper::LibreOfficeKit::isActive() ) + comphelper::ConfigurationHelper::readRelativeKey(xModuleCfg, sModule, "ooSetupFactoryWindowAttributes") >>= sWindowState; + + if (!sWindowState.isEmpty()) + { + // SOLAR SAFE -> + SolarMutexGuard aSolarGuard; + + // We have to retrieve the window pointer again. Because nobody can guarantee + // that the XWindow was not disposed in between .-) + // But if we get a valid pointer we can be sure, that it's the system window pointer + // we already checked and used before. Because nobody recycle the same uno reference for + // a new internal c++ implementation ... hopefully .-)) + VclPtr pWindowCheck = VCLUnoHelper::GetWindow(xWindow); + if (! pWindowCheck) + return; + + SystemWindow* pSystemWindow = static_cast(pWindowCheck.get()); + pSystemWindow->SetWindowState(sWindowState); + // <- SOLAR SAFE + } + } + catch(const css::uno::RuntimeException&) + { throw; } + catch(const css::uno::Exception&) + {} +} + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/loadenv/targethelper.cxx b/framework/source/loadenv/targethelper.cxx new file mode 100644 index 0000000000..7c06521da6 --- /dev/null +++ b/framework/source/loadenv/targethelper.cxx @@ -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 . + */ + +#include +#include + +namespace framework{ + +bool TargetHelper::matchSpecialTarget(std::u16string_view sCheckTarget , + ESpecialTarget eSpecialTarget) +{ + switch(eSpecialTarget) + { + case ESpecialTarget::Blank : + return sCheckTarget == SPECIALTARGET_BLANK; + + case ESpecialTarget::Default : + return sCheckTarget == SPECIALTARGET_DEFAULT; + + case ESpecialTarget::Beamer : + return sCheckTarget == SPECIALTARGET_BEAMER; + + case ESpecialTarget::HelpTask : + return sCheckTarget == SPECIALTARGET_HELPTASK; + default: + return false; + } +} + +bool TargetHelper::isValidNameForFrame(std::u16string_view sName) +{ + // some special targets are really special ones :-) + // E.g. the are really used to locate one frame inside the frame tree. + if ( + (sName.empty() ) || + (TargetHelper::matchSpecialTarget(sName, ESpecialTarget::HelpTask)) || + (TargetHelper::matchSpecialTarget(sName, ESpecialTarget::Beamer) ) + ) + return true; + + // all other names must be checked more general + // special targets starts with a "_". + return (sName.find('_') != 0); +} + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/recording/dispatchrecorder.cxx b/framework/source/recording/dispatchrecorder.cxx new file mode 100644 index 0000000000..965042290f --- /dev/null +++ b/framework/source/recording/dispatchrecorder.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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace ::com::sun::star::uno; + +namespace framework{ + +// used to mark a dispatch as comment (mostly it indicates an error) Changing of this define will impact all using of such comments... +constexpr OUString REM_AS_COMMENT = u"rem "_ustr; + +// XInterface, XTypeProvider, XServiceInfo + +OUString SAL_CALL DispatchRecorder::getImplementationName() +{ + return "com.sun.star.comp.framework.DispatchRecorder"; +} + +sal_Bool SAL_CALL DispatchRecorder::supportsService( const OUString& sServiceName ) +{ + return cppu::supportsService(this, sServiceName); +} + +css::uno::Sequence< OUString > SAL_CALL DispatchRecorder::getSupportedServiceNames() +{ + return { "com.sun.star.frame.DispatchRecorder" }; +} + + + +static void flatten_struct_members( + ::std::vector< Any > * vec, void const * data, + typelib_CompoundTypeDescription * pTD ) +{ + if (pTD->pBaseTypeDescription) + { + flatten_struct_members( vec, data, pTD->pBaseTypeDescription ); + } + for ( sal_Int32 nPos = 0; nPos < pTD->nMembers; ++nPos ) + { + vec->push_back( + Any( static_cast(data) + pTD->pMemberOffsets[ nPos ], pTD->ppTypeRefs[ nPos ] ) ); + } +} + +static Sequence< Any > make_seq_out_of_struct( + Any const & val ) +{ + Type const & type = val.getValueType(); + TypeClass eTypeClass = type.getTypeClass(); + if (TypeClass_STRUCT != eTypeClass && TypeClass_EXCEPTION != eTypeClass) + { + throw RuntimeException( + type.getTypeName() + "is no struct or exception!" ); + } + typelib_TypeDescription * pTD = nullptr; + TYPELIB_DANGER_GET( &pTD, type.getTypeLibType() ); + OSL_ASSERT( pTD ); + if (! pTD) + { + throw RuntimeException( + "cannot get type descr of type " + type.getTypeName() ); + } + + ::std::vector< Any > vec; + vec.reserve( reinterpret_cast(pTD)->nMembers ); // good guess + flatten_struct_members( &vec, val.getValue(), reinterpret_cast(pTD) ); + TYPELIB_DANGER_RELEASE( pTD ); + return Sequence< Any >( vec.data(), vec.size() ); +} + +DispatchRecorder::DispatchRecorder( const css::uno::Reference< css::uno::XComponentContext >& xContext ) + : m_nRecordingID(0) + , m_xConverter(css::script::Converter::create(xContext)) +{ +} + +DispatchRecorder::~DispatchRecorder() +{ +} + +// generate header +void SAL_CALL DispatchRecorder::startRecording( const css::uno::Reference< css::frame::XFrame >& ) +{ + /* SAFE{ */ + /* } */ +} + +void SAL_CALL DispatchRecorder::recordDispatch( const css::util::URL& aURL, + const css::uno::Sequence< css::beans::PropertyValue >& lArguments ) +{ + css::frame::DispatchStatement aStatement( aURL.Complete, OUString(), lArguments, 0, false ); + m_aStatements.push_back( aStatement ); +} + +void SAL_CALL DispatchRecorder::recordDispatchAsComment( const css::util::URL& aURL, + const css::uno::Sequence< css::beans::PropertyValue >& lArguments ) +{ + // last parameter must be set to true -> it's a comment + css::frame::DispatchStatement aStatement( aURL.Complete, OUString(), lArguments, 0, true ); + m_aStatements.push_back( aStatement ); +} + +void SAL_CALL DispatchRecorder::endRecording() +{ + SolarMutexGuard g; + m_aStatements.clear(); +} + +OUString SAL_CALL DispatchRecorder::getRecordedMacro() +{ + SolarMutexGuard g; + + if ( m_aStatements.empty() ) + return OUString(); + + OUStringBuffer aScriptBuffer; + aScriptBuffer.ensureCapacity(10000); + m_nRecordingID = 1; + + aScriptBuffer.append( + "rem ----------------------------------------------------------------------\n" + "rem define variables\n" + "dim document as object\n" + "dim dispatcher as object\n" + "rem ----------------------------------------------------------------------\n" + "rem get access to the document\n" + "document = ThisComponent.CurrentController.Frame\n" + "dispatcher = createUnoService(\"com.sun.star.frame.DispatchHelper\")\n\n"); + + for (auto const& statement : m_aStatements) + implts_recordMacro( statement.aCommand, statement.aArgs, statement.bIsComment, aScriptBuffer ); + OUString sScript = aScriptBuffer.makeStringAndClear(); + return sScript; +} + +void DispatchRecorder::AppendToBuffer( const css::uno::Any& aValue, OUStringBuffer& aArgumentBuffer ) +{ + // if value == bool + if (aValue.getValueTypeClass() == css::uno::TypeClass_STRUCT ) + { + // structs are recorded as arrays, convert to "Sequence of any" + Sequence< Any > aSeq = make_seq_out_of_struct( aValue ); + aArgumentBuffer.append("Array("); + for ( sal_Int32 nAny=0; nAny aSeq; + css::uno::Any aNew; + try { aNew = m_xConverter->convertTo( aValue, cppu::UnoType>::get() ); } + catch (const css::uno::Exception&) {} + + aNew >>= aSeq; + aArgumentBuffer.append("Array("); + for ( sal_Int32 nAny=0; nAny>= sVal; + + // encode non printable characters or '"' by using the CHR$ function + if ( !sVal.isEmpty() ) + { + const sal_Unicode* pChars = sVal.getStr(); + bool bInString = false; + for ( sal_Int32 nChar=0; nChar0 ) + // if this is not the first character, parts of the string have already been added + aArgumentBuffer.append("+"); + + // add the character constant + aArgumentBuffer.append("CHR$("); + aArgumentBuffer.append( static_cast(pChars[nChar]) ); + aArgumentBuffer.append(")"); + } + else + { + if ( !bInString ) + { + if ( nChar>0 ) + // if this is not the first character, parts of the string have already been added + aArgumentBuffer.append("+"); + + // start a new string + aArgumentBuffer.append("\""); + bInString = true; + } + + aArgumentBuffer.append( pChars[nChar] ); + } + } + + // close string + if ( bInString ) + aArgumentBuffer.append("\""); + } + else + aArgumentBuffer.append("\"\""); + } + else if (auto nVal = o3tl::tryAccess(aValue)) + { + // character variables are recorded as strings, back conversion must be handled in client code + aArgumentBuffer.append("\""); + if ( *nVal == '\"' ) + // encode \" to \"\" + aArgumentBuffer.append(*nVal); + aArgumentBuffer.append(*nVal); + aArgumentBuffer.append("\""); + } + else + { + css::uno::Any aNew; + try + { + aNew = m_xConverter->convertToSimpleType( aValue, css::uno::TypeClass_STRING ); + } + catch (const css::script::CannotConvertException&) {} + catch (const css::uno::Exception&) {} + OUString sVal; + aNew >>= sVal; + + if (aValue.getValueTypeClass() == css::uno::TypeClass_ENUM ) + { + OUString aName = aValue.getValueType().getTypeName(); + aArgumentBuffer.append( aName ); + aArgumentBuffer.append("."); + } + + aArgumentBuffer.append(sVal); + } +} + +void DispatchRecorder::implts_recordMacro( std::u16string_view aURL, + const css::uno::Sequence< css::beans::PropertyValue >& lArguments, + bool bAsComment, OUStringBuffer& aScriptBuffer ) +{ + OUStringBuffer aArgumentBuffer(1000); + // this value is used to name the arrays of aArgumentBuffer + OUString sArrayName = "args" + OUString::number(m_nRecordingID); + + aScriptBuffer.append("rem ----------------------------------------------------------------------\n"); + + sal_Int32 nLength = lArguments.getLength(); + sal_Int32 nValidArgs = 0; + for( sal_Int32 i=0; i0) + { + if(bAsComment) + aScriptBuffer.append(REM_AS_COMMENT); + aScriptBuffer.append("dim "); + aScriptBuffer.append (sArrayName); + aScriptBuffer.append("("); + aScriptBuffer.append (static_cast(nValidArgs-1)); // 0 based! + aScriptBuffer.append(") as new com.sun.star.beans.PropertyValue\n"); + aScriptBuffer.append (aArgumentBuffer); + aScriptBuffer.append("\n"); + } + + // add code for dispatches + if(bAsComment) + aScriptBuffer.append(REM_AS_COMMENT); + aScriptBuffer.append("dispatcher.executeDispatch(document, \""); + aScriptBuffer.append(aURL); + aScriptBuffer.append("\", \"\", 0, "); + if(nValidArgs<1) + aScriptBuffer.append("Array()"); + else + { + aScriptBuffer.append( sArrayName ); + aScriptBuffer.append("()"); + } + aScriptBuffer.append(")\n\n"); + + /* SAFE { */ + m_nRecordingID++; + /* } */ +} + +css::uno::Type SAL_CALL DispatchRecorder::getElementType() +{ + return cppu::UnoType::get(); +} + +sal_Bool SAL_CALL DispatchRecorder::hasElements() +{ + return (! m_aStatements.empty()); +} + +sal_Int32 SAL_CALL DispatchRecorder::getCount() +{ + return m_aStatements.size(); +} + +css::uno::Any SAL_CALL DispatchRecorder::getByIndex(sal_Int32 idx) +{ + if (idx >= static_cast(m_aStatements.size())) + throw css::lang::IndexOutOfBoundsException( "Dispatch recorder out of bounds" ); + + Any element(&m_aStatements[idx], + cppu::UnoType::get()); + + return element; +} + +void SAL_CALL DispatchRecorder::replaceByIndex(sal_Int32 idx, const css::uno::Any& element) +{ + if (element.getValueType() != + cppu::UnoType::get()) { + throw css::lang::IllegalArgumentException( + "Illegal argument in dispatch recorder", + Reference< XInterface >(), 2 ); + } + + if (idx >= static_cast(m_aStatements.size())) + throw css::lang::IndexOutOfBoundsException( + "Dispatch recorder out of bounds" ); + + auto pStatement = o3tl::doAccess(element); + + css::frame::DispatchStatement aStatement( + pStatement->aCommand, + pStatement->aTarget, + pStatement->aArgs, + pStatement->nFlags, + pStatement->bIsComment); + + m_aStatements[idx] = aStatement; +} + +} // namespace framework + + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +framework_DispatchRecorder_get_implementation( + css::uno::XComponentContext* context, css::uno::Sequence const& ) +{ + return cppu::acquire(new framework::DispatchRecorder(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/recording/dispatchrecordersupplier.cxx b/framework/source/recording/dispatchrecordersupplier.cxx new file mode 100644 index 0000000000..39707aca6e --- /dev/null +++ b/framework/source/recording/dispatchrecordersupplier.cxx @@ -0,0 +1,161 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include + +#include +#include + +namespace framework{ + +// XInterface, XTypeProvider, XServiceInfo + +OUString SAL_CALL DispatchRecorderSupplier::getImplementationName() +{ + return "com.sun.star.comp.framework.DispatchRecorderSupplier"; +} + +sal_Bool SAL_CALL DispatchRecorderSupplier::supportsService( const OUString& sServiceName ) +{ + return cppu::supportsService(this, sServiceName); +} + +css::uno::Sequence< OUString > SAL_CALL DispatchRecorderSupplier::getSupportedServiceNames() +{ + return { "com.sun.star.frame.DispatchRecorderSupplier" }; +} + + +DispatchRecorderSupplier::DispatchRecorderSupplier() +{ +} + +/** + @short standard destructor + @descr We are a helper and not a real service. So we don't provide + dispose() functionality. This supplier dies by ref count mechanism + and should release all internal used ones too. + */ +DispatchRecorderSupplier::~DispatchRecorderSupplier() +{ + m_xDispatchRecorder = nullptr; +} + +/** + @short set a new dispatch recorder on this supplier + @descr Because there can exist more than one recorder implementations + (to generate java/basic/... scripts from recorded data) it must + be possible to set it on a supplier. + + @see getDispatchRecorder() + + @param xRecorder + the new recorder to set it +
        isn't recommended, because recording without a + valid recorder can't work. But it's not checked here. So user + of this supplier can decide that without changing this + implementation. + + @change 09.04.2002 by Andreas Schluens + */ +void SAL_CALL DispatchRecorderSupplier::setDispatchRecorder( const css::uno::Reference< css::frame::XDispatchRecorder >& xRecorder ) +{ + SolarMutexGuard g; + m_xDispatchRecorder=xRecorder; +} + +/** + @short provides access to the dispatch recorder of this supplier + @descr Such recorder can be used outside to record dispatches. + But normally he is used internally only. Of course he must used + from outside to get the recorded data e.g. for saving it as a + script. + + @see setDispatchRecorder() + + @return the internal used dispatch recorder +
        May it can be if no one was set before. + + @change 09.04.2002 by Andreas Schluens + */ +css::uno::Reference< css::frame::XDispatchRecorder > SAL_CALL DispatchRecorderSupplier::getDispatchRecorder() +{ + SolarMutexGuard g; + return m_xDispatchRecorder; +} + +/** + @short execute a dispatch request and record it + @descr If given dispatch object provides right recording interface it + will be used. If it's not supported it record the pure dispatch + parameters only. There is no code neither the possibility to + check if recording is enabled or not. + + @param aURL the command URL + @param lArguments optional arguments (see com.sun.star.document.MediaDescriptor for further information) + @param xDispatcher the original dispatch object which should be recorded + + @change 09.04.2002 by Andreas Schluens + */ +void SAL_CALL DispatchRecorderSupplier::dispatchAndRecord( const css::util::URL& aURL , + const css::uno::Sequence< css::beans::PropertyValue >& lArguments , + const css::uno::Reference< css::frame::XDispatch >& xDispatcher ) +{ + SolarMutexClearableGuard aReadLock; + css::uno::Reference< css::frame::XDispatchRecorder > xRecorder = m_xDispatchRecorder; + aReadLock.clear(); + + // clear unspecific situations + if (!xDispatcher.is()) + throw css::uno::RuntimeException("specification violation: dispatcher is NULL", static_cast< ::cppu::OWeakObject* >(this)); + + if (!xRecorder.is()) + throw css::uno::RuntimeException("specification violation: no valid dispatch recorder available", static_cast< ::cppu::OWeakObject* >(this)); + + // check, if given dispatch supports record functionality by itself ... + // or must be wrapped. + css::uno::Reference< css::frame::XRecordableDispatch > xRecordable( + xDispatcher, + css::uno::UNO_QUERY); + + if (xRecordable.is()) + xRecordable->dispatchAndRecord(aURL,lArguments,xRecorder); + else + { + // There is no reason to wait for information about success + // of this request. Because status information of a dispatch + // are not guaranteed. So we execute it and record used + // parameters only. + xDispatcher->dispatch(aURL,lArguments); + xRecorder->recordDispatch(aURL,lArguments); + } +} + +} // namespace framework + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +framework_DispatchRecorderSupplier_get_implementation( + css::uno::XComponentContext* , css::uno::Sequence const& ) +{ + return cppu::acquire(new framework::DispatchRecorderSupplier()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/services/ContextChangeEventMultiplexer.cxx b/framework/source/services/ContextChangeEventMultiplexer.cxx new file mode 100644 index 0000000000..1cf4a670cf --- /dev/null +++ b/framework/source/services/ContextChangeEventMultiplexer.cxx @@ -0,0 +1,367 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +using namespace css; +using namespace css::uno; + +namespace { + +typedef comphelper::WeakComponentImplHelper < + css::ui::XContextChangeEventMultiplexer, + css::lang::XServiceInfo, + css::lang::XEventListener + > ContextChangeEventMultiplexerInterfaceBase; + +class ContextChangeEventMultiplexer + : public ContextChangeEventMultiplexerInterfaceBase +{ +public: + ContextChangeEventMultiplexer(); + ContextChangeEventMultiplexer(const ContextChangeEventMultiplexer&) = delete; + ContextChangeEventMultiplexer& operator=(const ContextChangeEventMultiplexer&) = delete; + + virtual void disposing(std::unique_lock&) override; + + // XContextChangeEventMultiplexer + virtual void SAL_CALL addContextChangeEventListener ( + const css::uno::Reference& rxListener, + const css::uno::Reference& rxEventFocus) override; + virtual void SAL_CALL removeContextChangeEventListener ( + const css::uno::Reference& rxListener, + const css::uno::Reference& rxEventFocus) override; + virtual void SAL_CALL removeAllContextChangeEventListeners ( + const css::uno::Reference& rxListener) override; + virtual void SAL_CALL broadcastContextChangeEvent ( + const css::ui::ContextChangeEventObject& rContextChangeEventObject, + const css::uno::Reference& rxEventFocus) override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual sal_Bool SAL_CALL supportsService ( + const OUString& rsServiceName) override; + virtual css::uno::Sequence< OUString> SAL_CALL getSupportedServiceNames() override; + + // XEventListener + virtual void SAL_CALL disposing ( + const css::lang::EventObject& rEvent) override; + + typedef ::std::vector > ListenerContainer; + class FocusDescriptor + { + public: + ListenerContainer maListeners; + OUString msCurrentApplicationName; + OUString msCurrentContextName; + }; + typedef ::std::map, FocusDescriptor> ListenerMap; + ListenerMap maListeners; + + /** Notify all listeners in the container that is associated with + the given event focus. + + Typically called twice from broadcastEvent(), once for the + given event focus and once for NULL. + */ + void BroadcastEventToSingleContainer ( + const css::ui::ContextChangeEventObject& rEventObject, + const css::uno::Reference& rxEventFocus); + FocusDescriptor* GetFocusDescriptor ( + const css::uno::Reference& rxEventFocus, + const bool bCreateWhenMissing); +}; + +ContextChangeEventMultiplexer::ContextChangeEventMultiplexer() +{ +} + +void ContextChangeEventMultiplexer::disposing(std::unique_lock& rGuard) +{ + ListenerMap aListeners; + aListeners.swap(maListeners); + + rGuard.unlock(); + + css::uno::Reference xThis (static_cast(this)); + css::lang::EventObject aEvent (xThis); + for (auto const& container : aListeners) + { + // Unregister from the focus object. + Reference xComponent (container.first, UNO_QUERY); + if (xComponent.is()) + xComponent->removeEventListener(this); + + // Tell all listeners that we are being disposed. + const FocusDescriptor& rFocusDescriptor (container.second); + for (auto const& listener : rFocusDescriptor.maListeners) + { + listener->disposing(aEvent); + } + } +} + +// XContextChangeEventMultiplexer +void SAL_CALL ContextChangeEventMultiplexer::addContextChangeEventListener ( + const css::uno::Reference& rxListener, + const css::uno::Reference& rxEventFocus) +{ + if ( ! rxListener.is()) + throw css::lang::IllegalArgumentException( + "can not add an empty reference", + static_cast(this), + 0); + + FocusDescriptor* pFocusDescriptor = GetFocusDescriptor(rxEventFocus, true); + if (pFocusDescriptor != nullptr) + { + ListenerContainer& rContainer (pFocusDescriptor->maListeners); + if (::std::find(rContainer.begin(), rContainer.end(), rxListener) != rContainer.end()) + { + // The listener was added for the same event focus + // previously. That is an error. + throw css::lang::IllegalArgumentException("listener added twice", static_cast(this), 0); + } + rContainer.push_back(rxListener); + } + + // Send out an initial event that informs the new listener about + // the current context. + if (!(rxEventFocus.is() && pFocusDescriptor!=nullptr)) + return; + + css::ui::ContextChangeEventObject aEvent ( + nullptr, + pFocusDescriptor->msCurrentApplicationName, + pFocusDescriptor->msCurrentContextName); + rxListener->notifyContextChangeEvent(aEvent); +} + +void SAL_CALL ContextChangeEventMultiplexer::removeContextChangeEventListener ( + const css::uno::Reference& rxListener, + const css::uno::Reference& rxEventFocus) +{ + if ( ! rxListener.is()) + throw css::lang::IllegalArgumentException( + "can not remove an empty reference", + static_cast(this), 0); + + FocusDescriptor* pFocusDescriptor = GetFocusDescriptor(rxEventFocus, false); + if (pFocusDescriptor == nullptr) + return; + + ListenerContainer& rContainer (pFocusDescriptor->maListeners); + const ListenerContainer::iterator iListener ( + ::std::find(rContainer.begin(), rContainer.end(), rxListener)); + if (iListener != rContainer.end()) + { + rContainer.erase(iListener); + + // We hold on to the focus descriptor even when the last listener has been removed. + // This allows us to keep track of the current context and send it to new listeners. + } + +} + +void SAL_CALL ContextChangeEventMultiplexer::removeAllContextChangeEventListeners ( + const css::uno::Reference& rxListener) +{ + if ( ! rxListener.is()) + throw css::lang::IllegalArgumentException( + "can not remove an empty reference", + static_cast(this), 0); + + for (auto& rContainer : maListeners) + { + const ListenerContainer::iterator iListener ( + ::std::find(rContainer.second.maListeners.begin(), rContainer.second.maListeners.end(), rxListener)); + if (iListener != rContainer.second.maListeners.end()) + { + rContainer.second.maListeners.erase(iListener); + + // We hold on to the focus descriptor even when the last listener has been removed. + // This allows us to keep track of the current context and send it to new listeners. + } + } +} + +void SAL_CALL ContextChangeEventMultiplexer::broadcastContextChangeEvent ( + const css::ui::ContextChangeEventObject& rEventObject, + const css::uno::Reference& rxEventFocus) +{ + // Remember the current context. + if (rxEventFocus.is()) + { + FocusDescriptor* pFocusDescriptor = GetFocusDescriptor(rxEventFocus, true); + if (pFocusDescriptor != nullptr) + { + pFocusDescriptor->msCurrentApplicationName = rEventObject.ApplicationName; + pFocusDescriptor->msCurrentContextName = rEventObject.ContextName; + } + } + + BroadcastEventToSingleContainer(rEventObject, rxEventFocus); + if (rxEventFocus.is()) + BroadcastEventToSingleContainer(rEventObject, nullptr); +} + +void ContextChangeEventMultiplexer::BroadcastEventToSingleContainer ( + const css::ui::ContextChangeEventObject& rEventObject, + const css::uno::Reference& rxEventFocus) +{ + FocusDescriptor* pFocusDescriptor = GetFocusDescriptor(rxEventFocus, false); + if (pFocusDescriptor != nullptr) + { + // Create a copy of the listener container to avoid problems + // when one of the called listeners calls add... or remove... + ListenerContainer aContainer (pFocusDescriptor->maListeners); + for (auto const& listener : aContainer) + { + listener->notifyContextChangeEvent(rEventObject); + } + } +} + +ContextChangeEventMultiplexer::FocusDescriptor* ContextChangeEventMultiplexer::GetFocusDescriptor ( + const css::uno::Reference& rxEventFocus, + const bool bCreateWhenMissing) +{ + ListenerMap::iterator iDescriptor (maListeners.find(rxEventFocus)); + if (iDescriptor == maListeners.end() && bCreateWhenMissing) + { + // Listen for the focus being disposed. + Reference xComponent (rxEventFocus, UNO_QUERY); + if (xComponent.is()) + xComponent->addEventListener(this); + + // Create a new listener container for the event focus. + iDescriptor = maListeners.emplace( + rxEventFocus, + FocusDescriptor()).first; + } + if (iDescriptor != maListeners.end()) + return &iDescriptor->second; + else + return nullptr; +} + +OUString SAL_CALL ContextChangeEventMultiplexer::getImplementationName() +{ + return "org.apache.openoffice.comp.framework.ContextChangeEventMultiplexer"; +} + +sal_Bool SAL_CALL ContextChangeEventMultiplexer::supportsService ( const OUString& rsServiceName) +{ + return cppu::supportsService(this, rsServiceName); +} + +css::uno::Sequence SAL_CALL ContextChangeEventMultiplexer::getSupportedServiceNames() +{ + // it's a singleton, not a service + return css::uno::Sequence(); +} + +void SAL_CALL ContextChangeEventMultiplexer::disposing ( const css::lang::EventObject& rEvent) +{ + ListenerMap::iterator iDescriptor (maListeners.find(rEvent.Source)); + + if (iDescriptor == maListeners.end()) + { + OSL_ASSERT(iDescriptor != maListeners.end()); + return; + } + + // Should we notify the remaining listeners? + + maListeners.erase(iDescriptor); +} + +} + +namespace framework { + +// right now we assume there's one matching listener +static uno::Reference GetFirstListenerWith_ImplImpl( + css::uno::Reference const & xComponentContext, + uno::Reference const& xEventFocus, + std::function const&)> const& rPredicate) +{ + assert(xEventFocus.is()); // in current usage it's a bug if the XController is null here + uno::Reference xRet; + + rtl::Reference pMultiplexer = + // [-loplugin:unocast] + dynamic_cast(ui::ContextChangeEventMultiplexer::get(xComponentContext).get()); + assert(pMultiplexer); + + ContextChangeEventMultiplexer::FocusDescriptor const*const pFocusDescriptor( + pMultiplexer->GetFocusDescriptor(xEventFocus, false)); + if (!pFocusDescriptor) + return xRet; + + for (auto & xListener : pFocusDescriptor->maListeners) + { + if (rPredicate(xListener)) + { + assert(!xRet.is()); // generalize this if it is used for more than 1:1 mapping? + xRet = xListener; + } + } + return xRet; +} + +namespace { + +struct Hook +{ + Hook() { g_pGetMultiplexerListener = &GetFirstListenerWith_ImplImpl; } + ~Hook() { g_pGetMultiplexerListener = nullptr; } +}; + +Hook g_hook; + +} + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +org_apache_openoffice_comp_framework_ContextChangeEventMultiplexer_get_implementation( + css::uno::XComponentContext *, + css::uno::Sequence const &) +{ + return cppu::acquire(new ContextChangeEventMultiplexer()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/services/autorecovery.cxx b/framework/source/services/autorecovery.cxx new file mode 100644 index 0000000000..03936b54ae --- /dev/null +++ b/framework/source/services/autorecovery.cxx @@ -0,0 +1,4342 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace css::uno; +using namespace css::document; +using namespace css::frame; +using namespace css::lang; +using namespace framework; + +/** After the fact documentation - hopefully it is correct. + * + * AutoRecovery handles 3 types of recovery, as well as periodic document saving + * 1) timed, ODF, temporary, recovery files created in the backup folder + * -can instead be used to actually save the documents periodically if settings request that. + * -temporary: deleted when the document itself is saved + * -handles the situation where LO immediately exits (power outage, program crash, pkill -9 soffice) + * -not restored immediately + * -no guarantee of availability of recovery file (since deleted on document save) + * or original document (perhaps /tmp, removeable, disconnected server). + * -therefore does not include unmodified files in RecoveryList (@since LO 24.2). + * -TODO: perhaps can be enhanced for users who always want sessions restored? + * 2) emergency save-and-restart immediately triggers creation of temporary, ODF, recovery files + * -handles the situation where LO is partially functioning (pkill -6 soffice) + * -restore attempted immediately, so try to restore entire session - all open files + * -always create recovery file for every open document in emergency situation + * -works without requiring AutoRecovery to be enabled + * 3) session save on exit desired by OS or user creates recovery files for every open document + * -triggered by some OS's shutdown/logout (no known way for user to initiate within LO) + * -same as emergency save, except maybe more time critical - OS kill timeout + * -not restored until much later - the user has stopped doing computer work + * -always create recovery file for every open document: needed for /tmp, disconnected docs + * + * All of these use the same recovery dialog - re-opening all the files listed in the RecoveryList + * of the user's officecfg settings. + * + * Since these 3 have very different expectations, and yet share the same code, keep all of them + * in mind when making code changes. + * + * Note: often, entries in m_lDocCache are copied. So realize that changes to aInfo/rInfo might not + * apply to async events like mark-document-as-saved-and-delete-TMP-URLs or set-modified-status, + * or ignoreClosing, or ListenForModify. For example, DocState::Modified should be considered only + * a good hint, and not as definitively accurate. + */ + +namespace { + +/** @short hold all needed information for an asynchronous dispatch alive. + + @descr Because some operations are forced to be executed asynchronously + (e.g. requested by our CrashSave/Recovery dialog) ... we must make sure + that this information won't be set as "normal" members of our AutoRecovery + instance. Otherwise they can disturb our normal AutoSave-timer handling. + e.g. it can be unclear then, which progress has to be used for storing documents... + */ +struct DispatchParams +{ +public: + DispatchParams(); + DispatchParams(const ::comphelper::SequenceAsHashMap& lArgs , + const css::uno::Reference< css::uno::XInterface >& xOwner); + + void forget(); + +public: + + /** @short can be set from outside and is provided to + our internal started operations. + + @descr Normally we use the normal status indicator + of the document windows to show a progress. + But in case we are used by any special UI, + it can provide its own status indicator object + to us - so we use it instead of the normal one. + */ + css::uno::Reference< css::task::XStatusIndicator > m_xProgress; + + /** TODO document me */ + OUString m_sSavePath; + + /** @short define the current cache entry, which should be used for current + backup or cleanUp operation ... which is may be done asynchronous */ + sal_Int32 m_nWorkingEntryID; + + /** @short used for async operations, to prevent us from dying. + + @descr If our dispatch() method was forced to start the + internal operation asynchronous... we send an event + to start and return immediately. But we must be sure that + our instance live if the event callback reach us. + So we hold a uno reference to ourself. + */ + css::uno::Reference< css::uno::XInterface > m_xHoldRefForAsyncOpAlive; +}; + +/** These values are used as flags and represent the current state of a document. + Every state of the life time of a document has to be recognized here. + + @attention Do not change (means reorganize) already used numbers. + There exists some code inside SVX, which uses the same numbers, + to analyze such document states. + Not the best design ... but may be it will be changed later .-) +*/ +enum class DocState: sal_Int32 +{ + /* TEMP STATES */ + + /// default state, if a document was new created or loaded + Unknown = 0, + /// modified against the original file + Modified = 1, + /// an active document can be postponed to be saved later. + Postponed = 2, + /// was already handled during one AutoSave/Recovery session. + Handled = 4, + /** an action was started (saving/loading) ... Can be interesting later if the process may be was interrupted by an exception. */ + TrySave = 8, + TryLoadBackup = 16, + TryLoadOriginal = 32, + + /* FINAL STATES */ + + /// the Auto/Emergency saved document is not usable any longer + Damaged = 64, + /// the Auto/Emergency saved document is not really up-to-date (some changes can be missing) + Incomplete = 128, + /// the Auto/Emergency saved document was processed successfully + Succeeded = 512 +}; + +} + +template<> struct o3tl::typed_flags: o3tl::is_typed_flags {}; + +namespace { + +// TODO document me ... flag field +// Emergency_Save and Recovery overwrites Auto_Save! +enum class Job +{ + NoJob = 0, + AutoSave = 1, + EmergencySave = 2, + Recovery = 4, + EntryBackup = 8, + EntryCleanup = 16, + PrepareEmergencySave = 32, + SessionSave = 64, + SessionRestore = 128, + DisableAutorecovery = 256, + SetAutosaveState = 512, + SessionQuietQuit = 1024, + UserAutoSave = 2048 +}; + +} + +template<> struct o3tl::typed_flags: o3tl::is_typed_flags {}; + +namespace { + +/** + implements the functionality of AutoSave and AutoRecovery + of documents - including features of an EmergencySave in + case a GPF occurs. + */ +typedef ::cppu::WeakComponentImplHelper< + css::lang::XServiceInfo, + css::frame::XDispatch, + css::document::XDocumentEventListener, // => css.lang.XEventListener + css::util::XChangesListener, // => css.lang.XEventListener + css::util::XModifyListener > // => css.lang.XEventListener + AutoRecovery_BASE; + +class AutoRecovery : private cppu::BaseMutex + , public AutoRecovery_BASE + , public ::cppu::OPropertySetHelper // => XPropertySet, XFastPropertySet, XMultiPropertySet +{ +public: + /** @short indicates the results of a FAILURE_SAFE operation + + @descr We must know, which reason was the real one in case + we couldn't copy a "failure document" to a user specified path. + We must know, if we can forget our cache entry or not. + */ + enum EFailureSafeResult + { + E_COPIED, + E_ORIGINAL_FILE_MISSING, + E_WRONG_TARGET_PATH + }; + + // TODO document me + enum ETimerType + { + /** the timer should not be used next time */ + E_DONT_START_TIMER, + /** timer (was/must be) started with normal AutoSaveTimeIntervall */ + E_NORMAL_AUTOSAVE_INTERVALL, + /** timer must be started with special short time interval, + to poll for an user idle period */ + E_POLL_FOR_USER_IDLE, + /** timer must be started with a very(!) short time interval, + to poll for the end of an user action, which does not allow saving documents in general */ + E_POLL_TILL_AUTOSAVE_IS_ALLOWED, + /** don't start the timer - but calls the same action then before immediately again! */ + E_CALL_ME_BACK + }; + + /** @short combine different information about one office document. */ + struct TDocumentInfo + { + public: + + TDocumentInfo() + : DocumentState (DocState::Unknown) + , UsedForSaving (false) + , ListenForModify (false) + , IgnoreClosing (false) + , ID (-1 ) + {} + + /** @short points to the document. */ + css::uno::Reference< css::frame::XModel > Document; + + /** @short knows, if the document is really modified since the last autosave, + or was postponed, because it was an active one etcpp... + + @descr Because we have no CHANGE TRACKING mechanism, based on office document, + we implements it by ourself. We listen for MODIFIED events + of each document and update this state flag here. + + Further we postpone saving of active documents, e.g. if the user + works currently on it. We wait for an idle period then... + */ + DocState DocumentState; + + /** Because our applications not ready for concurrent save requests at the same time, + we have suppress our own AutoSave for the moment, a document will be already saved + by others. + */ + bool UsedForSaving; + + /** For every user action, which modifies a document (e.g. key input) we get + a notification as XModifyListener. That seems to be a "performance issue" .-) + So we decided to listen for such modify events only for the time in which the document + was stored as temp. file and was not modified again by the user. + */ + bool ListenForModify; + + /** For SessionSave we must close all open documents by ourself. + But because we are listen for documents events, we get some ... + and deregister these documents from our configuration. + That's why we mark these documents as "Closed by ourself" so we can + ignore these "OnUnload" or disposing() events .-) + */ + bool IgnoreClosing; + + /** TODO: document me */ + OUString OrgURL; + OUString FactoryURL; + OUString TemplateURL; + + OUString OldTempURL; // previous recovery file (filename_0.odf) which will be removed + OUString NewTempURL; // new recovery file (filename_1.odf) that is being created + + OUString AppModule; // e.g. com.sun.star.text.TextDocument - used to identify app module + OUString FactoryService; // the service to create a document of the module + OUString RealFilter; // real filter, which was used at loading time + OUString DefaultFilter; // supports saving of the default format without losing data + OUString Extension; // file extension of the default filter + OUString Title; // can be used as "DisplayName" on every recovery UI! + css::uno::Sequence< OUString > + ViewNames; // names of the view which were active at emergency-save time + + sal_Int32 ID; + }; + + /** @short used to know every currently open document. */ + typedef ::std::vector< TDocumentInfo > TDocumentList; + +// member + +private: + + /** @short the global uno service manager. + @descr Must be used to create own needed services. + */ + css::uno::Reference< css::uno::XComponentContext > m_xContext; + + /** @short points to the underlying recovery configuration. + @descr This instance does not cache - it calls directly the + configuration API! + */ + css::uno::Reference< css::container::XNameAccess > m_xRecoveryCFG; + + /** @short proxy weak binding to forward Events to ourself without + an ownership cycle + */ + css::uno::Reference< css::util::XChangesListener > m_xRecoveryCFGListener; + + /** @short points to the used configuration package or.openoffice.Setup + @descr This instance does not cache - it calls directly the + configuration API! + */ + css::uno::Reference< css::container::XNameAccess > m_xModuleCFG; + + /** @short holds the global event broadcaster alive, + where we listen for new created documents. + */ + css::uno::Reference< css::frame::XGlobalEventBroadcaster > m_xNewDocBroadcaster; + + /** @short proxy weak binding to forward Events to ourself without + an ownership cycle + */ + css::uno::Reference< css::document::XDocumentEventListener > m_xNewDocBroadcasterListener; + + /** @short because we stop/restart listening sometimes, it's a good idea to know + if we already registered as listener .-) + */ + bool m_bListenForDocEvents; + bool m_bListenForConfigChanges; + + /** @short for an asynchronous operation we must know, if there is + at least one running job (may be asynchronous!). + */ + Job m_eJob; + + /** @short the timer, which is used to be informed about the next + saving time ... + @remark must lock SolarMutex to use + */ + Timer m_aTimer; + + /** @short make our dispatch asynchronous ... if required to do so! */ + std::unique_ptr m_xAsyncDispatcher; + + /** @see DispatchParams + */ + DispatchParams m_aDispatchParams; + + /** @short indicates, which time period is currently used by the + internal timer. + */ + ETimerType m_eTimerType; + + /** @short this cache is used to hold all information about + recovery/emergency save documents alive. + */ + TDocumentList m_lDocCache; + + // TODO document me + sal_Int32 m_nIdPool; + + /** @short contains all status listener registered at this instance. + */ + comphelper::OMultiTypeInterfaceContainerHelperVar3 m_lListener; + + /** @descr This member is used to prevent us against re-entrance problems. + A mutex can't help to prevent us from concurrent using of members + inside the same thread. But e.g. our internally used stl structures + are not threadsafe ... and furthermore they can't be used at the same time + for iteration and add/remove requests! + So we have to detect such states and ... show a warning. + May be there will be a better solution next time ... (copying the cache temp. + bevor using). + + And further it's not possible to use a simple boolean value here. + Because if more than one operation iterates over the same stl container ... + (only to modify it's elements but don't add new or removing existing ones!) + it should be possible doing so. But we must guarantee that the last operation reset + this lock ... not the first one ! So we use a "ref count" mechanism for that." + */ + sal_Int32 m_nDocCacheLock; + + /** @descr These members are used to check the minimum disc space, which must exists + to start the corresponding operation. + */ + sal_Int32 m_nMinSpaceDocSave; + sal_Int32 m_nMinSpaceConfigSave; + +// interface + +public: + + explicit AutoRecovery(css::uno::Reference< css::uno::XComponentContext > xContext); + virtual ~AutoRecovery( ) override; + + virtual OUString SAL_CALL getImplementationName() override + { + return "com.sun.star.comp.framework.AutoRecovery"; + } + + virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override + { + return cppu::supportsService(this, ServiceName); + } + + virtual css::uno::Sequence SAL_CALL getSupportedServiceNames() override + { + return {"com.sun.star.frame.AutoRecovery"}; + } + + // XInterface + virtual void SAL_CALL acquire() noexcept override + { OWeakObject::acquire(); } + virtual void SAL_CALL release() noexcept override + { OWeakObject::release(); } + virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type& type) override; + + /// Initialization function after having acquire()'d. + void initListeners(); + + // XTypeProvider + virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes( ) override; + + // css.frame.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; + + // css.document.XDocumentEventListener + /** @short informs about created/opened documents. + + @descr Every new opened/created document will be saved internally + so it can be checked if it's modified. This modified state + is used later to decide, if it must be saved or not. + + @param aEvent + points to the new created/opened document. + */ + virtual void SAL_CALL documentEventOccured(const css::document::DocumentEvent& aEvent) override; + + // css.util.XChangesListener + virtual void SAL_CALL changesOccurred(const css::util::ChangesEvent& aEvent) override; + + // css.util.XModifyListener + virtual void SAL_CALL modified(const css::lang::EventObject& aEvent) override; + + // css.lang.XEventListener + virtual void SAL_CALL disposing(const css::lang::EventObject& aEvent) override; + +protected: + + // OPropertySetHelper + + virtual sal_Bool SAL_CALL convertFastPropertyValue( css::uno::Any& aConvertedValue, + css::uno::Any& aOldValue , + sal_Int32 nHandle , + const css::uno::Any& aValue ) override; + + virtual void SAL_CALL setFastPropertyValue_NoBroadcast( sal_Int32 nHandle, + const css::uno::Any& aValue ) override; + using cppu::OPropertySetHelper::getFastPropertyValue; + virtual void SAL_CALL getFastPropertyValue(css::uno::Any& aValue , + sal_Int32 nHandle) const override; + + virtual ::cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper() override; + + virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo() override; + +private: + virtual void SAL_CALL disposing() final override; + + /** @short open the underlying configuration. + + @descr This method must be called every time + a configuration call is needed. Because + method works together with the member + m_xCFG, open it on demand and cache it + afterwards. + + @throw [com.sun.star.uno.RuntimeException] + if config could not be opened successfully! + + @threadsafe + */ + void implts_openConfig(); + + /** @short read the underlying configuration. + + @descr After that we know the initial state - means: + - if AutoSave was enabled by the user + - which time interval has to be used + - which recovery entries may already exists + + @throw [com.sun.star.uno.RuntimeException] + if config could not be opened or read successfully! + + @threadsafe + */ + void implts_readConfig(); + + /** @short read the underlying configuration... + + @descr ... but only keys related to the AutoSave mechanism. + Means: State and Timer interval. + E.g. the recovery list is not addressed here. + + @throw [com.sun.star.uno.RuntimeException] + if config could not be opened or read successfully! + + @threadsafe + */ + void implts_readAutoSaveConfig(); + + /** After the fact documentation + * @short adds/updates/removes entries in the RecoveryList - files to be recovered at startup + * + * @descr Deciding whether to add or remove an entry is very dependent on the context! + * EmergencySave and SessionSave are interested in all open documents (which may not + * even be available at next start - i.e. /tmp files might be lost after a reboot, + * or removable media / server access might not be connected). + * On the other hand, timer-based autorecovery should not be interested in recovering + * the session, but only modified documents that are recoverable + * (TODO: unless the user always wants to recover a session). + */ + void implts_flushConfigItem(AutoRecovery::TDocumentInfo& rInfo, bool bRemoveIt = false, + bool bAllowAdd = true); + + // TODO document me + void implts_startListening(); + void implts_startModifyListeningOnDoc(AutoRecovery::TDocumentInfo& rInfo); + + // TODO document me + void implts_stopListening(); + void implts_stopModifyListeningOnDoc(AutoRecovery::TDocumentInfo& rInfo); + + /** @short stops and may be(!) restarts the timer. + + @descr A running timer is stopped every time here. + But starting depends from the different internal + timer variables (e.g. AutoSaveEnabled, AutoSaveTimeIntervall, + TimerType etcpp.) + + @throw [com.sun.star.uno.RuntimeException] + if timer could not be stopped or started! + + @threadsafe + */ + void implts_updateTimer(); + + /** @short stop the timer. + + @descr Double calls will be ignored - means we do + nothing here, if the timer is already disabled. + + @throw [com.sun.star.uno.RuntimeException] + if timer could not be stopped! + + @threadsafe + */ + void implts_stopTimer(); + + /** @short callback of our internal timer. + */ + DECL_LINK(implts_timerExpired, Timer*, void); + + /** @short makes our dispatch() method asynchronous! + */ + DECL_LINK(implts_asyncDispatch, LinkParamNone*, void); + + /** @short implements the dispatch real. */ + void implts_dispatch(const DispatchParams& aParams); + + /** @short validate new detected document and add it into the internal + document list. + + @descr This method should be called only if it's clear that a new + document was opened/created during office runtime. + This method checks if it's a top level document (means not an embedded one). + Only such top level documents can be recognized by this auto save mechanism. + + @param xDocument + the new document, which should be checked and registered. + + @threadsafe + */ + void implts_registerDocument(const css::uno::Reference< css::frame::XModel3 >& xDocument); + + /** @short remove the specified document from our internal document list. + + @param xDocument + the closing document, which should be deregistered. + + @param bStopListening + sal_False: must be used in case this method is called within disposing() of the document, + where it makes no sense to deregister our listener. The container dies... + sal_True : must be used in case this method is used on "deregistration" of this document, where + we must deregister our listener .-) + + @threadsafe + */ + void implts_deregisterDocument(const css::uno::Reference< css::frame::XModel >& xDocument , + bool bStopListening = true); + + // TODO document me + void implts_markDocumentModifiedAgainstLastBackup(const css::uno::Reference< css::frame::XModel >& xDocument); + + // TODO document me + void implts_updateModifiedState(const css::uno::Reference< css::frame::XModel >& xDocument); + + // TODO document me + void implts_updateDocumentUsedForSavingState(const css::uno::Reference< css::frame::XModel >& xDocument , + bool bSaveInProgress); + + // TODO document me + void implts_markDocumentAsSaved(const css::uno::Reference< css::frame::XModel >& xDocument); + + /** @short search a document inside given list. + + @param rList + reference to a vector, which can contain such + document. + + @param xDocument + the document, which should be located inside the + given list. + + @return [TDocumentList::iterator] + which points to the located document. + If document does not exists - it's set to + rList.end()! + */ + static TDocumentList::iterator impl_searchDocument( AutoRecovery::TDocumentList& rList , + const css::uno::Reference< css::frame::XModel >& xDocument); + + /** TODO document me */ + void implts_changeAllDocVisibility(bool bVisible); + void implts_prepareSessionShutdown(); + + /** @short save all current opened documents to a specific + backup directory. + + @descr Only really changed documents will be saved here. + + Further this method returns a suggestion, if and how it should + be called again. May be some documents was not saved yet + and must wait for an user idle period ... + + @param bAllowUserIdleLoop + Because this method is used for different uses cases, it must + know, which actions are allowed or not. + Job::AutoSave => + If a document is the most active one, saving it + will be postponed if there exists other unsaved + documents. This feature was implemented, because + we don't wish to disturb the user on it's work. + ... bAllowUserIdleLoop should be set to sal_True + Job::EmergencySave / Job::SessionSave => + Here we must finish our work ASAP! It's not allowed + to postpone any document. + ... bAllowUserIdleLoop must(!) be set to sal_False + + @param pParams + sometimes this method is required inside an external dispatch request. + The it contains some special environment variables, which overwrites + our normal environment. + AutoSave => pParams == 0 + SessionSave/CrashSave => pParams != 0 + + @return A suggestion, how the timer (if it's not already disabled!) + should be restarted to fulfill the requirements. + + @threadsafe + */ + AutoRecovery::ETimerType implts_saveDocs( bool bAllowUserIdleLoop, + bool bRemoveLockFiles, + const DispatchParams* pParams = nullptr); + + /** @short save one of the current documents to a specific + backup directory. + + @descr It: + - defines a new(!) unique temp file name + - save the new temp file + - remove the old temp file + - patch the given info struct + - and return errors. + + It does not: + - patch the configuration. + + Note further: it patches the info struct + more than ones. E.g. the new temp URL is set + before the file is saved. And the old URL is removed + only if removing of the old file was successful. + If this method returns without an exception - everything + was OK. Otherwise the info struct can be analyzed to + get more information, e.g. when the problem occurs. + + @param sBackupPath + the base path for saving such temp files. + + @param rInfo + points to an information structure, where + e.g. the document, its modified state, the count + of autosave-retries etcpp. exists. + It's used also to return the new temp file name + and some other state values! + + @threadsafe + */ + void implts_saveOneDoc(const OUString& sBackupPath , + AutoRecovery::TDocumentInfo& rInfo , + const css::uno::Reference< css::task::XStatusIndicator >& xExternalProgress); + + /** @short recovery all documents, which was saved during + a crash before. + + @return A suggestion, how this method must be called back! + + @threadsafe + */ + AutoRecovery::ETimerType implts_openDocs(const DispatchParams& aParams); + + // TODO document me + void implts_openOneDoc(const OUString& sURL , + utl::MediaDescriptor& lDescriptor, + AutoRecovery::TDocumentInfo& rInfo ); + + // TODO document me + void implts_generateNewTempURL(const OUString& sBackupPath , + utl::MediaDescriptor& rMediaDescriptor, + AutoRecovery::TDocumentInfo& rInfo ); + + /** @short notifies all interested listener about the current state + of the currently running operation. + + @descr We support different set's of functions. Job::AutoSave, Job::EmergencySave, + Job::Recovery ... etcpp. + Listener can register itself for any type of supported + functionality ... but not for document URL's in special. + + @param eJob + is used to know, which set of listener we must notify. + + @param aEvent + describe the event more in detail. + + @threadsafe + */ + void implts_informListener( Job eJob , + const css::frame::FeatureStateEvent& aEvent); + + /** short create a feature event struct, which can be send + to any interested listener. + + @param eJob + describe the current running operation + Job::AutoSave, Job::EmergencySave, Job::Recovery + + @param sEventType + describe the type of this event + START, STOP, UPDATE + + @param pInfo + if sOperation is an update, this parameter must be different from NULL + and is used to send information regarding the current handled document. + + @return [css::frame::FeatureStateEvent] + the event structure for sending. + */ + static css::frame::FeatureStateEvent implst_createFeatureStateEvent( Job eJob , + const OUString& sEventType, + AutoRecovery::TDocumentInfo const * pInfo ); + + class ListenerInformer + { + private: + AutoRecovery &m_rRecovery; + Job m_eJob; + bool m_bStopped; + public: + ListenerInformer(AutoRecovery &rRecovery, Job eJob) + : m_rRecovery(rRecovery), m_eJob(eJob), m_bStopped(false) + { + } + void start(); + void stop(); + ~ListenerInformer() + { + stop(); + } + }; + + // TODO document me + void implts_resetHandleStates(); + + // TODO document me + void implts_specifyDefaultFilterAndExtension(AutoRecovery::TDocumentInfo& rInfo); + + // TODO document me + void implts_specifyAppModuleAndFactory(AutoRecovery::TDocumentInfo& rInfo); + + /** retrieves the names of all active views of the given document + @param rInfo + the document info, whose Document member must not be . + */ + void implts_collectActiveViewNames( AutoRecovery::TDocumentInfo& rInfo ); + + /** updates the configuration so that for all documents, their current view/names are stored + */ + void implts_persistAllActiveViewNames(); + + // TODO document me + void implts_prepareEmergencySave(); + + // TODO document me + void implts_doEmergencySave(const DispatchParams& aParams); + + // TODO document me + void implts_doRecovery(const DispatchParams& aParams); + + // TODO document me + void implts_doSessionSave(const DispatchParams& aParams); + + // TODO document me + void implts_doSessionQuietQuit(); + + // TODO document me + void implts_doSessionRestore(const DispatchParams& aParams); + + // TODO document me + void implts_backupWorkingEntry(const DispatchParams& aParams); + + // TODO document me + void implts_cleanUpWorkingEntry(const DispatchParams& aParams); + + /** try to make sure that all changed config items (not our used + config access only) will be flushed back to disc. + + E.g. our svtools::ConfigItems() has to be flushed explicitly .-( + + Note: This method can't fail. Flushing of config entries is an + optional feature. Errors can be ignored. + */ + void impl_flushALLConfigChanges(); + + // TODO document me + AutoRecovery::EFailureSafeResult implts_copyFile(const OUString& sSource , + const OUString& sTargetPath, + const OUString& sTargetName); + + /** @short converts m_eJob into a job description, which + can be used to inform an outside listener + about the current running operation + + @param eJob + describe the current running operation + Job::AutoSave, Job::EmergencySave, Job::Recovery + + @return [string] + a suitable job description of form: + vnd.sun.star.autorecovery:/do... + */ + static OUString implst_getJobDescription(Job eJob); + + /** @short map the given URL to an internal int representation. + + @param aURL + the url, which describe the next starting or may be already running + operation. + + @return [long] + the internal int representation + see enum class Job + */ + static Job implst_classifyJob(const css::util::URL& aURL); + + /// TODO + void implts_verifyCacheAgainstDesktopDocumentList(); + + /// TODO document me + bool impl_enoughDiscSpace(sal_Int32 nRequiredSpace); + + /// TODO document me + static void impl_showFullDiscError(); + + /** @short try to create/use a progress and set it inside the + environment. + + @descr The problem behind: There exists different use case of this method. + a) An external progress is provided by our CrashSave or Recovery dialog. + b) We must create our own progress e.g. for an AutoSave + c) Sometimes our application filters don't use the progress + provided by the MediaDescriptor. They use the Frame every time to create + its own progress. So we implemented a HACK for these and now we set + an InterceptedProgress there for the time WE use this frame for loading/storing documents .-) + + @param xNewFrame + must be set only in case WE create a new frame (e.g. for loading documents + on session restore or recovery). Then search for a frame using rInfo.Document must + be suppressed and xFrame must be preferred instead .-) + + @param rInfo + used e.g. to find the frame corresponding to a document. + This frame must be used to create a new progress e.g. for an AutoSave. + + @param rArgs + is used to set the new created progress as parameter on these set. + */ + void impl_establishProgress(const AutoRecovery::TDocumentInfo& rInfo , + utl::MediaDescriptor& rArgs , + const css::uno::Reference< css::frame::XFrame >& xNewFrame); + + void impl_forgetProgress(const AutoRecovery::TDocumentInfo& rInfo , + utl::MediaDescriptor& rArgs , + const css::uno::Reference< css::frame::XFrame >& xNewFrame); + + /** try to remove the specified file from disc. + + Every URL supported by our UCB component can be used here. + Further it doesn't matter if the file really exists or not. + Because removing a non existent file will have the same + result at the end... a non existing file .-) + + On the other side removing of files from disc is an optional + feature. If we are not able doing so... it's not a real problem. + Ok - users disc place will be smaller then... but we should produce + a crash during crash save because we can't delete a temporary file only! + + @param sURL + the url of the file, which should be removed. + */ + void st_impl_removeFile(const OUString& sURL); + + /** try to remove ".lock" file from disc if office will be terminated + not using the official way .-) + + This method has to be handled "optional". So every error inside + has to be ignored ! This method CAN NOT FAIL ... it can forget something only .-) + */ + void st_impl_removeLockFile(); +}; + +// recovery.xcu +constexpr OUStringLiteral CFG_PACKAGE_RECOVERY = u"/org.openoffice.Office.Recovery"; + +const char CFG_ENTRY_AUTOSAVE_ENABLED[] = "AutoSave/Enabled"; +const char CFG_ENTRY_AUTOSAVE_USERAUTOSAVE_ENABLED[] = "AutoSave/UserAutoSaveEnabled"; + +constexpr OUStringLiteral CFG_ENTRY_REALDEFAULTFILTER = u"ooSetupFactoryActualFilter"; + +constexpr OUString CFG_ENTRY_PROP_TEMPURL = u"TempURL"_ustr; +constexpr OUString CFG_ENTRY_PROP_ORIGINALURL = u"OriginalURL"_ustr; +constexpr OUString CFG_ENTRY_PROP_TEMPLATEURL = u"TemplateURL"_ustr; +constexpr OUStringLiteral CFG_ENTRY_PROP_FACTORYURL = u"FactoryURL"; +constexpr OUString CFG_ENTRY_PROP_MODULE = u"Module"_ustr; +constexpr OUString CFG_ENTRY_PROP_DOCUMENTSTATE = u"DocumentState"_ustr; +constexpr OUString CFG_ENTRY_PROP_FILTER = u"Filter"_ustr; +constexpr OUString CFG_ENTRY_PROP_TITLE = u"Title"_ustr; +constexpr OUStringLiteral CFG_ENTRY_PROP_ID = u"ID"; +constexpr OUString CFG_ENTRY_PROP_VIEWNAMES = u"ViewNames"_ustr; + +constexpr OUStringLiteral FILTER_PROP_TYPE = u"Type"; +constexpr OUStringLiteral TYPE_PROP_EXTENSIONS = u"Extensions"; + +// setup.xcu +constexpr OUStringLiteral CFG_ENTRY_PROP_EMPTYDOCUMENTURL = u"ooSetupFactoryEmptyDocumentURL"; +constexpr OUStringLiteral CFG_ENTRY_PROP_FACTORYSERVICE = u"ooSetupFactoryDocumentService"; + +const char EVENT_ON_NEW[] = "OnNew"; +const char EVENT_ON_LOAD[] = "OnLoad"; +const char EVENT_ON_UNLOAD[] = "OnUnload"; +const char EVENT_ON_MODIFYCHANGED[] = "OnModifyChanged"; +const char EVENT_ON_SAVE[] = "OnSave"; +const char EVENT_ON_SAVEAS[] = "OnSaveAs"; +const char EVENT_ON_SAVETO[] = "OnCopyTo"; +const char EVENT_ON_SAVEDONE[] = "OnSaveDone"; +const char EVENT_ON_SAVEASDONE[] = "OnSaveAsDone"; +const char EVENT_ON_SAVETODONE[] = "OnCopyToDone"; +const char EVENT_ON_SAVEFAILED[] = "OnSaveFailed"; +const char EVENT_ON_SAVEASFAILED[] = "OnSaveAsFailed"; +const char EVENT_ON_SAVETOFAILED[] = "OnCopyToFailed"; + +constexpr OUString RECOVERY_ITEM_BASE_IDENTIFIER = u"recovery_item_"_ustr; + +const char CMD_PROTOCOL[] = "vnd.sun.star.autorecovery:"; + +const char CMD_DO_AUTO_SAVE[] = "/doAutoSave"; // force AutoSave ignoring the AutoSave timer +const char CMD_DO_PREPARE_EMERGENCY_SAVE[] = "/doPrepareEmergencySave"; // prepare the office for the following EmergencySave step (hide windows etcpp.) +const char CMD_DO_EMERGENCY_SAVE[] = "/doEmergencySave"; // do EmergencySave on crash +const char CMD_DO_RECOVERY[] = "/doAutoRecovery"; // recover all crashed documents +const char CMD_DO_ENTRY_BACKUP[] = "/doEntryBackup"; // try to store a temp or original file to a user defined location +const char CMD_DO_ENTRY_CLEANUP[] = "/doEntryCleanUp"; // remove the specified entry from the recovery cache +const char CMD_DO_SESSION_SAVE[] = "/doSessionSave"; // save all open documents if e.g. a window manager closes an user session +const char CMD_DO_SESSION_QUIET_QUIT[] = "/doSessionQuietQuit"; // let the current session be quietly closed ( the saving should be done using doSessionSave previously ) if e.g. a window manager closes an user session +const char CMD_DO_SESSION_RESTORE[] = "/doSessionRestore"; // restore a saved user session from disc +const char CMD_DO_DISABLE_RECOVERY[] = "/disableRecovery"; // disable recovery and auto save (!) temp. for this office session +const char CMD_DO_SET_AUTOSAVE_STATE[] = "/setAutoSaveState"; // disable/enable auto save (not crash save) for this office session + +constexpr OUStringLiteral REFERRER_USER = u"private:user"; + +constexpr OUStringLiteral PROP_DISPATCH_ASYNCHRON = u"DispatchAsynchron"; +constexpr OUStringLiteral PROP_PROGRESS = u"StatusIndicator"; +constexpr OUStringLiteral PROP_SAVEPATH = u"SavePath"; +constexpr OUStringLiteral PROP_ENTRY_ID = u"EntryID"; +constexpr OUStringLiteral PROP_AUTOSAVE_STATE = u"AutoSaveState"; + +constexpr OUString OPERATION_START = u"start"_ustr; +constexpr OUString OPERATION_STOP = u"stop"_ustr; +constexpr OUString OPERATION_UPDATE = u"update"_ustr; + +const sal_Int32 MIN_DISCSPACE_DOCSAVE = 5; // [MB] +const sal_Int32 MIN_DISCSPACE_CONFIGSAVE = 1; // [MB] +const sal_Int32 RETRY_STORE_ON_FULL_DISC_FOREVER = 300; // not forever ... but often enough .-) +const sal_Int32 RETRY_STORE_ON_MIGHT_FULL_DISC_USEFULL = 3; // in case FULL DISC does not seem the real problem +const sal_Int32 GIVE_UP_RETRY = 1; // in case FULL DISC does not seem the real problem + +#define SAVE_IN_PROGRESS true +#define SAVE_FINISHED false + +#define LOCK_FOR_CACHE_ADD_REMOVE true +#define LOCK_FOR_CACHE_USE false + +#define MIN_TIME_FOR_USER_IDLE 10000 // 10s user idle + +// enable the following defines in case you wish to simulate a full disc for debug purposes .-) + +// this define throws every time a document is stored or a configuration change +// should be flushed an exception ... so the special error handler for this scenario is triggered +// #define TRIGGER_FULL_DISC_CHECK + +// force "return sal_False" for the method impl_enoughDiscSpace(). +// #define SIMULATE_FULL_DISC + +class CacheLockGuard +{ + private: + + // holds the outside caller alive, so it's shared resources + // are valid every time + css::uno::Reference< css::uno::XInterface > m_xOwner; + + // mutex shared with outside caller! + osl::Mutex& m_rSharedMutex; + + // this variable knows the state of the "cache lock" + sal_Int32& m_rCacheLock; + + // to prevent increasing/decreasing of m_rCacheLock more than once + // we must know if THIS guard has an actual lock set there! + bool m_bLockedByThisGuard; + + public: + + CacheLockGuard(AutoRecovery* pOwner , + osl::Mutex& rMutex , + sal_Int32& rCacheLock , + bool bLockForAddRemoveVectorItems); + ~CacheLockGuard(); + + void lock(bool bLockForAddRemoveVectorItems); + void unlock(); +}; + +CacheLockGuard::CacheLockGuard(AutoRecovery* pOwner , + osl::Mutex& rMutex , + sal_Int32& rCacheLock , + bool bLockForAddRemoveVectorItems) + : m_xOwner (static_cast< css::frame::XDispatch* >(pOwner)) + , m_rSharedMutex (rMutex ) + , m_rCacheLock (rCacheLock ) + , m_bLockedByThisGuard(false ) +{ + lock(bLockForAddRemoveVectorItems); +} + +CacheLockGuard::~CacheLockGuard() +{ + unlock(); + m_xOwner.clear(); +} + +void CacheLockGuard::lock(bool bLockForAddRemoveVectorItems) +{ + /* SAFE */ + osl::MutexGuard g(m_rSharedMutex); + + if (m_bLockedByThisGuard) + return; + + // This cache lock is needed only to prevent us from removing/adding + // items from/into the recovery cache ... during it's used at another code place + // for iterating .-) + + // Modifying of item properties is allowed and sometimes needed! + // So we should detect only the dangerous state of concurrent add/remove + // requests and throw an exception then ... which can of course break the whole + // operation. On the other side a crash reasoned by an invalid stl iterator + // will have the same effect .-) + + if ( (m_rCacheLock > 0) && bLockForAddRemoveVectorItems ) + { + OSL_FAIL("Re-entrance problem detected. Using of an stl structure in combination with iteration, adding, removing of elements etcpp."); + throw css::uno::RuntimeException( + "Re-entrance problem detected. Using of an stl structure in combination with iteration, adding, removing of elements etcpp.", + m_xOwner); + } + + ++m_rCacheLock; + m_bLockedByThisGuard = true; + /* SAFE */ +} + +void CacheLockGuard::unlock() +{ + /* SAFE */ + osl::MutexGuard g(m_rSharedMutex); + + if ( ! m_bLockedByThisGuard) + return; + + --m_rCacheLock; + m_bLockedByThisGuard = false; + + if (m_rCacheLock < 0) + { + OSL_FAIL("Wrong using of member m_nDocCacheLock detected. A ref counted value shouldn't reach values <0 .-)"); + throw css::uno::RuntimeException( + "Wrong using of member m_nDocCacheLock detected. A ref counted value shouldn't reach values <0 .-)", + m_xOwner); + } + /* SAFE */ +} + +DispatchParams::DispatchParams() + : m_nWorkingEntryID(-1) +{ +}; + +DispatchParams::DispatchParams(const ::comphelper::SequenceAsHashMap& lArgs , + const css::uno::Reference< css::uno::XInterface >& xOwner) +{ + m_nWorkingEntryID = lArgs.getUnpackedValueOrDefault(PROP_ENTRY_ID, sal_Int32(-1) ); + m_xProgress = lArgs.getUnpackedValueOrDefault(PROP_PROGRESS, css::uno::Reference< css::task::XStatusIndicator >()); + m_sSavePath = lArgs.getUnpackedValueOrDefault(PROP_SAVEPATH, OUString() ); + m_xHoldRefForAsyncOpAlive = xOwner; +}; + +void DispatchParams::forget() +{ + m_sSavePath.clear(); + m_nWorkingEntryID = -1; + m_xProgress.clear(); + m_xHoldRefForAsyncOpAlive.clear(); +}; + +AutoRecovery::AutoRecovery(css::uno::Reference< css::uno::XComponentContext > xContext) + : AutoRecovery_BASE (m_aMutex) + , ::cppu::OPropertySetHelper(cppu::WeakComponentImplHelperBase::rBHelper) + , m_xContext (std::move(xContext )) + , m_bListenForDocEvents (false ) + , m_bListenForConfigChanges (false ) + , m_eJob (Job::NoJob) + , m_aTimer( "framework::AutoRecovery m_aTimer" ) + , m_xAsyncDispatcher (new vcl::EventPoster( LINK( this, AutoRecovery, implts_asyncDispatch ) )) + , m_eTimerType (E_DONT_START_TIMER ) + , m_nIdPool (0 ) + , m_lListener (cppu::WeakComponentImplHelperBase::rBHelper.rMutex) + , m_nDocCacheLock (0 ) + , m_nMinSpaceDocSave (MIN_DISCSPACE_DOCSAVE ) + , m_nMinSpaceConfigSave (MIN_DISCSPACE_CONFIGSAVE ) +{ +} + +void AutoRecovery::initListeners() +{ + // read configuration to know if autosave/recovery is on/off etcpp... + implts_readConfig(); + + implts_startListening(); + + // establish callback for our internal used timer. + // Note: Its only active, if the timer will be started ... + SolarMutexGuard g; + m_aTimer.SetInvokeHandler(LINK(this, AutoRecovery, implts_timerExpired)); +} + +AutoRecovery::~AutoRecovery() +{ + assert(!m_aTimer.IsActive()); +} + +void AutoRecovery::disposing() +{ + implts_stopTimer(); + SolarMutexGuard g; + m_xAsyncDispatcher.reset(); +} + +Any SAL_CALL AutoRecovery::queryInterface( const css::uno::Type& _rType ) +{ + Any aRet = AutoRecovery_BASE::queryInterface( _rType ); + if ( !aRet.hasValue() ) + aRet = OPropertySetHelper::queryInterface( _rType ); + return aRet; +} + +Sequence< css::uno::Type > SAL_CALL AutoRecovery::getTypes( ) +{ + return comphelper::concatSequences( + AutoRecovery_BASE::getTypes(), + ::cppu::OPropertySetHelper::getTypes() + ); +} + +void SAL_CALL AutoRecovery::dispatch(const css::util::URL& aURL , + const css::uno::Sequence< css::beans::PropertyValue >& lArguments) +{ + SAL_INFO("fwk.autorecovery", "AutoRecovery::dispatch() starts ..." << aURL.Complete); + + // valid request ? + Job eNewJob = AutoRecovery::implst_classifyJob(aURL); + if (eNewJob == Job::NoJob) + return; + + bool bAsync; + DispatchParams aParams; + /* SAFE */ { + osl::ClearableMutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex); + + // still running operation ... ignoring Job::AutoSave. + // All other requests has higher prio! + if ( + ( m_eJob != Job::NoJob ) && + ((m_eJob & Job::AutoSave ) != Job::AutoSave) + ) + { + SAL_INFO("fwk.autorecovery", "AutoRecovery::dispatch(): There is already an asynchronous dispatch() running. New request will be ignored!"); + return; + } + + ::comphelper::SequenceAsHashMap lArgs(lArguments); + + // check if somewhere wish to disable recovery temp. for this office session + // This can be done immediately... must not been done asynchronous. + if ((eNewJob & Job::DisableAutorecovery) == Job::DisableAutorecovery) + { + // it's important to set a flag internally, so AutoRecovery will be suppressed - even if it's requested. + m_eJob |= eNewJob; + implts_stopTimer(); + implts_stopListening(); + return; + } + + // disable/enable AutoSave for this office session only + // independent from the configuration entry. + if ((eNewJob & Job::SetAutosaveState) == Job::SetAutosaveState) + { + bool bOn = lArgs.getUnpackedValueOrDefault(PROP_AUTOSAVE_STATE, true); + if (bOn) + { + // don't enable AutoSave hardly ! + // reload configuration to know the current state. + implts_readAutoSaveConfig(); + g.clear(); + implts_updateTimer(); + // can it happen that might be the listener was stopped? .-) + // make sure it runs always... even if AutoSave itself was disabled temporarily. + implts_startListening(); + } + else + { + implts_stopTimer(); + m_eJob &= ~Job::AutoSave; + m_eTimerType = AutoRecovery::E_DONT_START_TIMER; + } + return; + } + + m_eJob |= eNewJob; + + bAsync = lArgs.getUnpackedValueOrDefault(PROP_DISPATCH_ASYNCHRON, false); + aParams = DispatchParams(lArgs, static_cast< css::frame::XDispatch* >(this)); + + // Hold this instance alive till the asynchronous operation will be finished. + if (bAsync) + m_aDispatchParams = aParams; + + } /* SAFE */ + + if (bAsync) + m_xAsyncDispatcher->Post(); + else + implts_dispatch(aParams); +} + +void AutoRecovery::ListenerInformer::start() +{ + m_rRecovery.implts_informListener(m_eJob, + AutoRecovery::implst_createFeatureStateEvent(m_eJob, OPERATION_START, nullptr)); +} + +void AutoRecovery::ListenerInformer::stop() +{ + if (m_bStopped) + return; + m_rRecovery.implts_informListener(m_eJob, + AutoRecovery::implst_createFeatureStateEvent(m_eJob, OPERATION_STOP, nullptr)); + m_bStopped = true; +} + +void AutoRecovery::implts_dispatch(const DispatchParams& aParams) +{ + Job eJob; + /* SAFE */ { + osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex); + eJob = m_eJob; + } /* SAFE */ + + // in case a new dispatch overwrites a may ba active AutoSave session + // we must restore this session later. see below ... + bool bWasAutoSaveActive = ((eJob & Job::AutoSave) == Job::AutoSave); + bool bWasUserAutoSaveActive = + ((eJob & Job::UserAutoSave) == Job::UserAutoSave); + + // On the other side it makes no sense to reactivate the AutoSave operation + // if the new dispatch indicates a final decision... + // E.g. an EmergencySave/SessionSave indicates the end of life of the current office session. + // It makes no sense to reactivate an AutoSave then. + // But a Recovery or SessionRestore should reactivate a may be already active AutoSave. + bool bAllowAutoSaveReactivation = true; + + implts_stopTimer(); + implts_stopListening(); + + ListenerInformer aListenerInformer(*this, eJob); + aListenerInformer.start(); + + try + { + // Auto save is called from our internal timer ... not via dispatch() API ! + // else + if ( + ((eJob & Job::PrepareEmergencySave) == Job::PrepareEmergencySave) && + ((eJob & Job::DisableAutorecovery ) != Job::DisableAutorecovery ) + ) + { + SAL_INFO("fwk.autorecovery", "... prepare emergency save ..."); + bAllowAutoSaveReactivation = false; + implts_prepareEmergencySave(); + } + else + if ( + ((eJob & Job::EmergencySave ) == Job::EmergencySave ) && + ((eJob & Job::DisableAutorecovery) != Job::DisableAutorecovery) + ) + { + SAL_INFO("fwk.autorecovery", "... do emergency save ..."); + bAllowAutoSaveReactivation = false; + implts_doEmergencySave(aParams); + } + else + if ( + ((eJob & Job::Recovery ) == Job::Recovery ) && + ((eJob & Job::DisableAutorecovery) != Job::DisableAutorecovery) + ) + { + SAL_INFO("fwk.autorecovery", "... do recovery ..."); + implts_doRecovery(aParams); + } + else + if ( + ((eJob & Job::SessionSave ) == Job::SessionSave ) && + ((eJob & Job::DisableAutorecovery) != Job::DisableAutorecovery) + ) + { + SAL_INFO("fwk.autorecovery", "... do session save ..."); + bAllowAutoSaveReactivation = false; + implts_doSessionSave(aParams); + } + else + if ( + ((eJob & Job::SessionQuietQuit ) == Job::SessionQuietQuit ) && + ((eJob & Job::DisableAutorecovery) != Job::DisableAutorecovery) + ) + { + SAL_INFO("fwk.autorecovery", "... do session quiet quit ..."); + bAllowAutoSaveReactivation = false; + implts_doSessionQuietQuit(); + } + else + if ( + ((eJob & Job::SessionRestore ) == Job::SessionRestore ) && + ((eJob & Job::DisableAutorecovery) != Job::DisableAutorecovery) + ) + { + SAL_INFO("fwk.autorecovery", "... do session restore ..."); + implts_doSessionRestore(aParams); + } + else + if ( + ((eJob & Job::EntryBackup ) == Job::EntryBackup ) && + ((eJob & Job::DisableAutorecovery) != Job::DisableAutorecovery) + ) + implts_backupWorkingEntry(aParams); + else + if ( + ((eJob & Job::EntryCleanup ) == Job::EntryCleanup ) && + ((eJob & Job::DisableAutorecovery) != Job::DisableAutorecovery) + ) + implts_cleanUpWorkingEntry(aParams); + } + catch(const css::uno::RuntimeException&) + { + throw; + } + catch(const css::uno::Exception&) + { + // TODO better error handling + } + + aListenerInformer.stop(); + + /* SAFE */ { + osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex); + m_eJob = Job::NoJob; + if ( bAllowAutoSaveReactivation && bWasAutoSaveActive ) + { + m_eJob |= Job::AutoSave; + + if (bWasUserAutoSaveActive) + { + m_eJob |= Job::UserAutoSave; + } + } + + } /* SAFE */ + + // depends on bAllowAutoSaveReactivation implicitly by looking on m_eJob=Job::AutoSave! see before ... + implts_updateTimer(); + + if (bAllowAutoSaveReactivation) + implts_startListening(); +} + +void SAL_CALL AutoRecovery::addStatusListener(const css::uno::Reference< css::frame::XStatusListener >& xListener, + const css::util::URL& aURL ) +{ + if (!xListener.is()) + throw css::uno::RuntimeException("Invalid listener reference.", static_cast< css::frame::XDispatch* >(this)); + // container is threadsafe by using a shared mutex! + m_lListener.addInterface(aURL.Complete, xListener); + + // REENTRANT !? -> -------------------------------- + CacheLockGuard aCacheLock(this, cppu::WeakComponentImplHelperBase::rBHelper.rMutex, m_nDocCacheLock, LOCK_FOR_CACHE_USE); + + /* SAFE */ { + osl::ResettableMutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex); + + for (auto const& elem : m_lDocCache) + { + css::frame::FeatureStateEvent aEvent = AutoRecovery::implst_createFeatureStateEvent(m_eJob, OPERATION_UPDATE, &elem); + + // } /* SAFE */ + g.clear(); + xListener->statusChanged(aEvent); + g.reset(); + // /* SAFE */ { + } + + } /* SAFE */ +} + +void SAL_CALL AutoRecovery::removeStatusListener(const css::uno::Reference< css::frame::XStatusListener >& xListener, + const css::util::URL& aURL ) +{ + if (!xListener.is()) + throw css::uno::RuntimeException("Invalid listener reference.", static_cast< css::frame::XDispatch* >(this)); + // container is threadsafe by using a shared mutex! + m_lListener.removeInterface(aURL.Complete, xListener); +} + +void SAL_CALL AutoRecovery::documentEventOccured(const css::document::DocumentEvent& aEvent) +{ + css::uno::Reference< css::frame::XModel3 > xDocument(aEvent.Source, css::uno::UNO_QUERY); + + // new document => put it into the internal list + if ( + (aEvent.EventName == EVENT_ON_NEW) || + (aEvent.EventName == EVENT_ON_LOAD) + ) + { + implts_registerDocument(xDocument); + } + // document modified => set its modify state new (means modified against the original file!) + else if ( aEvent.EventName == EVENT_ON_MODIFYCHANGED ) + { + implts_updateModifiedState(xDocument); + } + /* at least one document starts saving process => + Our application code is not ready for multiple save requests + at the same time. So we have to suppress our AutoSave feature + for the moment, till this other save requests will be finished. + */ + else if ( + (aEvent.EventName == EVENT_ON_SAVE) || + (aEvent.EventName == EVENT_ON_SAVEAS) || + (aEvent.EventName == EVENT_ON_SAVETO) + ) + { + implts_updateDocumentUsedForSavingState(xDocument, SAVE_IN_PROGRESS); + } + // document saved => remove tmp. files - but hold config entries alive! + else if ( + (aEvent.EventName == EVENT_ON_SAVEDONE) || + (aEvent.EventName == EVENT_ON_SAVEASDONE) + ) + { + SolarMutexGuard g; + implts_markDocumentAsSaved(xDocument); + implts_updateDocumentUsedForSavingState(xDocument, SAVE_FINISHED); + } + /* document saved as copy => mark it as "non used by concurrent save operation". + so we can try to create a backup copy if next time AutoSave is started too. + Don't remove temp. files or change the modified state of the document! + It was not really saved to the original file... + */ + else if ( aEvent.EventName == EVENT_ON_SAVETODONE ) + { + implts_updateDocumentUsedForSavingState(xDocument, SAVE_FINISHED); + } + // If saving of a document failed by an error ... we have to save this document + // by ourself next time AutoSave or EmergencySave is triggered. + // But we can reset the state "used for other save requests". Otherwise + // these documents will never be saved! + else if ( + (aEvent.EventName == EVENT_ON_SAVEFAILED) || + (aEvent.EventName == EVENT_ON_SAVEASFAILED) || + (aEvent.EventName == EVENT_ON_SAVETOFAILED) + ) + { + implts_updateDocumentUsedForSavingState(xDocument, SAVE_FINISHED); + } + // document closed => remove temp. files and configuration entries + else if ( aEvent.EventName == EVENT_ON_UNLOAD ) + { + implts_deregisterDocument(xDocument); // sal_True => stop listening for disposing() ! + } +} + +void SAL_CALL AutoRecovery::changesOccurred(const css::util::ChangesEvent& aEvent) +{ + const css::uno::Sequence< css::util::ElementChange > lChanges (aEvent.Changes); + const css::util::ElementChange* pChanges = lChanges.getConstArray(); + + sal_Int32 c = lChanges.getLength(); + sal_Int32 i = 0; + + /* SAFE */ { + osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex); + + // Changes of the configuration must be ignored if AutoSave/Recovery was disabled for this + // office session. That can happen if e.g. the command line arguments "--norestore" or "--headless" + // was set. + if ((m_eJob & Job::DisableAutorecovery) == Job::DisableAutorecovery) + return; + + for (i=0; i>= sPath; + + if ( sPath == CFG_ENTRY_AUTOSAVE_ENABLED ) + { + bool bEnabled = false; + if (pChanges[i].Element >>= bEnabled) + { + if (bEnabled) + { + m_eJob |= Job::AutoSave; + m_eTimerType = AutoRecovery::E_NORMAL_AUTOSAVE_INTERVALL; + } + else + { + m_eJob &= ~Job::AutoSave; + m_eTimerType = AutoRecovery::E_DONT_START_TIMER; + } + } + } + else if (sPath == CFG_ENTRY_AUTOSAVE_USERAUTOSAVE_ENABLED) + { + bool bEnabled = false; + if (pChanges[i].Element >>= bEnabled) + { + if (bEnabled) + m_eJob |= Job::UserAutoSave; + else + m_eJob &= ~Job::UserAutoSave; + } + } + } + + } /* SAFE */ + + // Note: This call stops the timer and starts it again. + // But it checks the different timer states internally and + // may be suppress the restart! + implts_updateTimer(); +} + +void SAL_CALL AutoRecovery::modified(const css::lang::EventObject& aEvent) +{ + css::uno::Reference< css::frame::XModel > xDocument(aEvent.Source, css::uno::UNO_QUERY); + if (! xDocument.is()) + return; + + implts_markDocumentModifiedAgainstLastBackup(xDocument); +} + +void SAL_CALL AutoRecovery::disposing(const css::lang::EventObject& aEvent) +{ + /* SAFE */ + osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex); + + if (aEvent.Source == m_xNewDocBroadcaster) + { + m_xNewDocBroadcaster.clear(); + return; + } + + if (aEvent.Source == m_xRecoveryCFG) + { + m_xRecoveryCFG.clear(); + return; + } + + // dispose from one of our cached documents ? + // Normally they should send a OnUnload message ... + // But some stacktraces shows another possible use case .-) + css::uno::Reference< css::frame::XModel > xDocument(aEvent.Source, css::uno::UNO_QUERY); + if (xDocument.is()) + { + implts_deregisterDocument(xDocument, false); // sal_False => don't call removeEventListener() .. because it's not needed here + return; + } + + /* SAFE */ +} + +void AutoRecovery::implts_openConfig() +{ + /* SAFE */ { + osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex); + + if (m_xRecoveryCFG.is()) + return; + } /* SAFE */ + + css::uno::Reference xConfigProvider( + css::configuration::theDefaultProvider::get(m_xContext)); + + std::vector lParams; + css::beans::PropertyValue aParam; + + // set root path + aParam.Name = "nodepath"; + aParam.Value <<= OUString(CFG_PACKAGE_RECOVERY); + lParams.push_back(css::uno::Any(aParam)); + + // throws a RuntimeException if an error occurs! + css::uno::Reference xCFG( + xConfigProvider->createInstanceWithArguments( + "com.sun.star.configuration.ConfigurationAccess", + comphelper::containerToSequence(lParams)), + css::uno::UNO_QUERY); + + sal_Int32 nMinSpaceDocSave = MIN_DISCSPACE_DOCSAVE; + sal_Int32 nMinSpaceConfigSave = MIN_DISCSPACE_CONFIGSAVE; + + try + { + nMinSpaceDocSave = officecfg::Office::Recovery::AutoSave::MinSpaceDocSave::get(); + nMinSpaceConfigSave = officecfg::Office::Recovery::AutoSave::MinSpaceConfigSave::get(); + } + catch(const css::uno::Exception&) + { + // These config keys are not sooooo important, that + // we are interested on errors here really .-) + nMinSpaceDocSave = MIN_DISCSPACE_DOCSAVE; + nMinSpaceConfigSave = MIN_DISCSPACE_CONFIGSAVE; + } + + /* SAFE */ { + osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex); + m_xRecoveryCFG = xCFG; + m_nMinSpaceDocSave = nMinSpaceDocSave; + m_nMinSpaceConfigSave = nMinSpaceConfigSave; + } /* SAFE */ +} + +void AutoRecovery::implts_readAutoSaveConfig() +{ + implts_openConfig(); + + // AutoSave [bool] + bool bEnabled(officecfg::Office::Recovery::AutoSave::Enabled::get()); + + /* SAFE */ { + osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex); + if (bEnabled) + { + bool bUserEnabled(officecfg::Office::Recovery::AutoSave::UserAutoSaveEnabled::get()); + + m_eJob |= Job::AutoSave; + m_eTimerType = AutoRecovery::E_NORMAL_AUTOSAVE_INTERVALL; + + if (bUserEnabled) + { + m_eJob |= Job::UserAutoSave; + } + else + { + m_eJob &= ~Job::UserAutoSave; + } + } + else + { + m_eJob &= ~Job::AutoSave; + m_eTimerType = AutoRecovery::E_DONT_START_TIMER; + } + } /* SAFE */ +} + +void AutoRecovery::implts_readConfig() +{ + implts_readAutoSaveConfig(); + + // REENTRANT -> -------------------------------- + CacheLockGuard aCacheLock(this, cppu::WeakComponentImplHelperBase::rBHelper.rMutex, m_nDocCacheLock, LOCK_FOR_CACHE_ADD_REMOVE); + + /* SAFE */ { + osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex); + // reset current cache load cache + m_lDocCache.clear(); + m_nIdPool = 0; + } /* SAFE */ + + aCacheLock.unlock(); + // <- REENTRANT -------------------------------- + + css::uno::Reference xRecoveryList( + officecfg::Office::Recovery::RecoveryList::get()); + const css::uno::Sequence< OUString > lItems = xRecoveryList->getElementNames(); + const OUString* pItems = lItems.getConstArray(); + sal_Int32 c = lItems.getLength(); + sal_Int32 i = 0; + + // REENTRANT -> -------------------------- + aCacheLock.lock(LOCK_FOR_CACHE_ADD_REMOVE); + + for (i=0; i xItem; + xRecoveryList->getByName(pItems[i]) >>= xItem; + if (!xItem.is()) + continue; + + AutoRecovery::TDocumentInfo aInfo; + aInfo.NewTempURL.clear(); + aInfo.Document.clear(); + xItem->getPropertyValue(CFG_ENTRY_PROP_ORIGINALURL) >>= aInfo.OrgURL; + xItem->getPropertyValue(CFG_ENTRY_PROP_TEMPURL) >>= aInfo.OldTempURL; + xItem->getPropertyValue(CFG_ENTRY_PROP_TEMPLATEURL) >>= aInfo.TemplateURL; + xItem->getPropertyValue(CFG_ENTRY_PROP_FILTER) >>= aInfo.RealFilter; + sal_Int32 tmp = 0; + xItem->getPropertyValue(CFG_ENTRY_PROP_DOCUMENTSTATE) >>= tmp; + aInfo.DocumentState = DocState(tmp); + xItem->getPropertyValue(CFG_ENTRY_PROP_MODULE) >>= aInfo.AppModule; + xItem->getPropertyValue(CFG_ENTRY_PROP_TITLE) >>= aInfo.Title; + xItem->getPropertyValue(CFG_ENTRY_PROP_VIEWNAMES) >>= aInfo.ViewNames; + implts_specifyAppModuleAndFactory(aInfo); + implts_specifyDefaultFilterAndExtension(aInfo); + + if (pItems[i].startsWith(RECOVERY_ITEM_BASE_IDENTIFIER)) + { + std::u16string_view sID = pItems[i].subView(RECOVERY_ITEM_BASE_IDENTIFIER.getLength()); + aInfo.ID = o3tl::toInt32(sID); + /* SAFE */ { + osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex); + if (aInfo.ID > m_nIdPool) + { + m_nIdPool = aInfo.ID+1; + SAL_WARN_IF(m_nIdPool<0, "fwk.autorecovery", "AutoRecovery::implts_readConfig(): Overflow of IDPool detected!"); + } + } /* SAFE */ + } + else + SAL_INFO("fwk.autorecovery", "AutoRecovery::implts_readConfig(): Who changed numbering of recovery items? Cache will be inconsistent then! I do not know, what will happen next time .-)"); + + /* SAFE */ { + osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex); + m_lDocCache.push_back(aInfo); + } /* SAFE */ + } + + aCacheLock.unlock(); + // <- REENTRANT -------------------------- + + implts_updateTimer(); +} + +void AutoRecovery::implts_specifyDefaultFilterAndExtension(AutoRecovery::TDocumentInfo& rInfo) +{ + if (rInfo.AppModule.isEmpty()) + { + throw css::uno::RuntimeException( + "Can not find out the default filter and its extension, if no application module is known!", + static_cast< css::frame::XDispatch* >(this)); + } + + css::uno::Reference< css::container::XNameAccess> xCFG; + /* SAFE */ { + osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex); + xCFG = m_xModuleCFG; + } /* SAFE */ + + try + { + if (! xCFG.is()) + { + implts_openConfig(); + // open module config on demand and cache the update access + xCFG.set(officecfg::Setup::Office::Factories::get(), + css::uno::UNO_SET_THROW); + + /* SAFE */ { + osl::MutexGuard g2(cppu::WeakComponentImplHelperBase::rBHelper.rMutex); + m_xModuleCFG = xCFG; + } /* SAFE */ + } + + css::uno::Reference< css::container::XNameAccess > xModuleProps( + xCFG->getByName(rInfo.AppModule), + css::uno::UNO_QUERY_THROW); + + xModuleProps->getByName(CFG_ENTRY_REALDEFAULTFILTER) >>= rInfo.DefaultFilter; + + css::uno::Reference< css::container::XNameAccess > xFilterCFG( + m_xContext->getServiceManager()->createInstanceWithContext( + "com.sun.star.document.FilterFactory", m_xContext), css::uno::UNO_QUERY_THROW); + css::uno::Reference< css::container::XNameAccess > xTypeCFG( + m_xContext->getServiceManager()->createInstanceWithContext( + "com.sun.star.document.TypeDetection", m_xContext), css::uno::UNO_QUERY_THROW); + + ::comphelper::SequenceAsHashMap lFilterProps (xFilterCFG->getByName(rInfo.DefaultFilter)); + OUString sTypeRegistration = lFilterProps.getUnpackedValueOrDefault(FILTER_PROP_TYPE, OUString()); + ::comphelper::SequenceAsHashMap lTypeProps (xTypeCFG->getByName(sTypeRegistration)); + css::uno::Sequence< OUString > lExtensions = lTypeProps.getUnpackedValueOrDefault(TYPE_PROP_EXTENSIONS, css::uno::Sequence< OUString >()); + if (lExtensions.hasElements()) + { + rInfo.Extension = "." + lExtensions[0]; + } + else + rInfo.Extension = ".unknown"; + } + catch(const css::uno::Exception&) + { + rInfo.DefaultFilter.clear(); + rInfo.Extension.clear(); + } +} + +void AutoRecovery::implts_specifyAppModuleAndFactory(AutoRecovery::TDocumentInfo& rInfo) +{ + ENSURE_OR_THROW2( + !rInfo.AppModule.isEmpty() || rInfo.Document.is(), + "Can not find out the application module nor its factory URL, if no application module (or a suitable) document is known!", + *this ); + + css::uno::Reference< css::frame::XModuleManager2 > xManager = ModuleManager::create(m_xContext); + + if (rInfo.AppModule.isEmpty()) + rInfo.AppModule = xManager->identify(rInfo.Document); + + ::comphelper::SequenceAsHashMap lModuleDescription(xManager->getByName(rInfo.AppModule)); + lModuleDescription[CFG_ENTRY_PROP_EMPTYDOCUMENTURL] >>= rInfo.FactoryURL; + lModuleDescription[CFG_ENTRY_PROP_FACTORYSERVICE] >>= rInfo.FactoryService; +} + +void AutoRecovery::implts_collectActiveViewNames( AutoRecovery::TDocumentInfo& i_rInfo ) +{ + ENSURE_OR_THROW2( i_rInfo.Document.is(), "need at document, at the very least", *this ); + + i_rInfo.ViewNames.realloc(0); + + // obtain list of controllers of this document + ::std::vector< OUString > aViewNames; + const Reference< XModel2 > xModel( i_rInfo.Document, UNO_QUERY ); + if ( xModel.is() ) + { + const Reference< css::container::XEnumeration > xEnumControllers( xModel->getControllers() ); + while ( xEnumControllers->hasMoreElements() ) + { + const Reference< XController2 > xController( xEnumControllers->nextElement(), UNO_QUERY ); + OUString sViewName; + if ( xController.is() ) + sViewName = xController->getViewControllerName(); + OSL_ENSURE( !sViewName.isEmpty(), "AutoRecovery::implts_collectActiveViewNames: (no XController2 ->) no view name -> no recovery of this view!" ); + + if ( !sViewName.isEmpty() ) + aViewNames.push_back( sViewName ); + } + } + + i_rInfo.ViewNames.realloc( aViewNames.size() ); + ::std::copy( aViewNames.begin(), aViewNames.end(), i_rInfo.ViewNames.getArray() ); +} + +void AutoRecovery::implts_persistAllActiveViewNames() +{ + osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex); + + // This list will be filled with every document + for (auto & elem : m_lDocCache) + { + implts_collectActiveViewNames(elem); + implts_flushConfigItem(elem); + } +} + +void AutoRecovery::implts_flushConfigItem(AutoRecovery::TDocumentInfo& rInfo, bool bRemoveIt, + bool bAllowAdd) +{ + std::shared_ptr batch( + comphelper::ConfigurationChanges::create()); + + try + { + implts_openConfig(); + + css::uno::Reference xCheck( + officecfg::Office::Recovery::RecoveryList::get(batch)); + + css::uno::Reference< css::container::XNameContainer > xModify(xCheck, css::uno::UNO_QUERY_THROW); + css::uno::Reference< css::lang::XSingleServiceFactory > xCreate(xCheck, css::uno::UNO_QUERY_THROW); + + OUString sID = RECOVERY_ITEM_BASE_IDENTIFIER + OUString::number(rInfo.ID); + + // remove + if (bRemoveIt) + { + // Catch NoSuchElementException. + // It's not a good idea inside multithreaded environments to call hasElement - removeElement. + // DO IT! + try + { + osl::File::remove(rInfo.OldTempURL); + osl::File::remove(rInfo.NewTempURL); + rInfo.OldTempURL.clear(); + rInfo.NewTempURL.clear(); + + xModify->removeByName(sID); + } + catch(const css::container::NoSuchElementException&) + { + return; + } + } + else + { + // new/modify + css::uno::Reference< css::beans::XPropertySet > xSet; + bool bNew = !xCheck->hasByName(sID); + if (bNew) + { + if (!bAllowAdd) + return; // no change made, just exit + + xSet.set(xCreate->createInstance(), css::uno::UNO_QUERY_THROW); + } + else + xCheck->getByName(sID) >>= xSet; + + xSet->setPropertyValue(CFG_ENTRY_PROP_ORIGINALURL, css::uno::Any(rInfo.OrgURL )); + xSet->setPropertyValue(CFG_ENTRY_PROP_TEMPURL, css::uno::Any(rInfo.OldTempURL )); + xSet->setPropertyValue(CFG_ENTRY_PROP_TEMPLATEURL, css::uno::Any(rInfo.TemplateURL )); + xSet->setPropertyValue(CFG_ENTRY_PROP_FILTER, css::uno::Any(rInfo.RealFilter)); + xSet->setPropertyValue(CFG_ENTRY_PROP_DOCUMENTSTATE, css::uno::Any(sal_Int32(rInfo.DocumentState))); + xSet->setPropertyValue(CFG_ENTRY_PROP_MODULE, css::uno::Any(rInfo.AppModule)); + xSet->setPropertyValue(CFG_ENTRY_PROP_TITLE, css::uno::Any(rInfo.Title)); + xSet->setPropertyValue(CFG_ENTRY_PROP_VIEWNAMES, css::uno::Any(rInfo.ViewNames)); + + if (bNew) + xModify->insertByName(sID, css::uno::Any(xSet)); + } + } + catch(const css::uno::RuntimeException&) + { + throw; + } + catch(const css::uno::Exception&) + { + // ??? can it happen that a full disc let these set of operations fail too ??? + } + + sal_Int32 nRetry = RETRY_STORE_ON_FULL_DISC_FOREVER; + do + { + try + { + batch->commit(); + +#ifdef TRIGGER_FULL_DISC_CHECK + throw css::uno::Exception("trigger full disk check"); +#else // TRIGGER_FULL_DISC_CHECK + nRetry = 0; +#endif // TRIGGER_FULL_DISC_CHECK + } + catch(const css::uno::Exception&) + { + // a) FULL DISC seems to be the problem behind => show error and retry it forever (e.g. retry=300) + // b) unknown problem (may be locking problem) => reset RETRY value to more useful value(!) (e.g. retry=3) + // c) unknown problem (may be locking problem) + 1..2 repeating operations => throw the original exception to force generation of a stacktrace ! + + sal_Int32 nMinSpaceConfigSave; + /* SAFE */ { + osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex); + nMinSpaceConfigSave = m_nMinSpaceConfigSave; + } /* SAFE */ + + if (! impl_enoughDiscSpace(nMinSpaceConfigSave)) + AutoRecovery::impl_showFullDiscError(); + else if (nRetry > RETRY_STORE_ON_MIGHT_FULL_DISC_USEFULL) + nRetry = RETRY_STORE_ON_MIGHT_FULL_DISC_USEFULL; + else if (nRetry <= GIVE_UP_RETRY) + throw; // force stacktrace to know if there exist might other reasons, why an AutoSave can fail !!! + + --nRetry; + } + } + while(nRetry>0); +} + +void AutoRecovery::implts_startListening() +{ + css::uno::Reference< css::util::XChangesNotifier > xCFG; + css::uno::Reference< css::frame::XGlobalEventBroadcaster > xBroadcaster; + bool bListenForDocEvents; + bool bListenForConfigChanges; + /* SAFE */ { + osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex); + xCFG.set (m_xRecoveryCFG, css::uno::UNO_QUERY); + xBroadcaster = m_xNewDocBroadcaster; + bListenForDocEvents = m_bListenForDocEvents; + bListenForConfigChanges = m_bListenForConfigChanges; + } /* SAFE */ + + if ( + ( xCFG.is() ) && + (! bListenForConfigChanges) + ) + { + css::uno::Reference const xListener( + new WeakChangesListener(this)); + xCFG->addChangesListener(xListener); + /* SAFE */ { + osl::MutexGuard g2(cppu::WeakComponentImplHelperBase::rBHelper.rMutex); + m_xRecoveryCFGListener = xListener; + m_bListenForConfigChanges = true; + } /* SAFE */ + } + + if (!xBroadcaster.is()) + { + xBroadcaster = css::frame::theGlobalEventBroadcaster::get(m_xContext); + /* SAFE */ { + osl::MutexGuard g2(cppu::WeakComponentImplHelperBase::rBHelper.rMutex); + m_xNewDocBroadcaster = xBroadcaster; + } /* SAFE */ + } + + if ( + ( xBroadcaster.is() ) && + (! bListenForDocEvents) + ) + { + css::uno::Reference const + xListener(new WeakDocumentEventListener(this)); + xBroadcaster->addDocumentEventListener(xListener); + /* SAFE */ { + osl::MutexGuard g2(cppu::WeakComponentImplHelperBase::rBHelper.rMutex); + m_xNewDocBroadcasterListener = xListener; + m_bListenForDocEvents = true; + } /* SAFE */ + } +} + +void AutoRecovery::implts_stopListening() +{ + css::uno::Reference< css::util::XChangesNotifier > xCFG; + css::uno::Reference< css::document::XDocumentEventBroadcaster > xGlobalEventBroadcaster; + /* SAFE */ { + osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex); + // Attention: Don't reset our internal members here too. + // May be we must work with our configuration, but don't wish to be informed + // about changes any longer. Needed e.g. during Job::EmergencySave! + xCFG.set (m_xRecoveryCFG , css::uno::UNO_QUERY); + xGlobalEventBroadcaster = m_xNewDocBroadcaster; + } /* SAFE */ + + if (xGlobalEventBroadcaster.is() && m_bListenForDocEvents) + { + xGlobalEventBroadcaster->removeDocumentEventListener(m_xNewDocBroadcasterListener); + m_bListenForDocEvents = false; + } + + if (xCFG.is() && m_bListenForConfigChanges) + { + xCFG->removeChangesListener(m_xRecoveryCFGListener); + m_bListenForConfigChanges = false; + } +} + +void AutoRecovery::implts_startModifyListeningOnDoc(AutoRecovery::TDocumentInfo& rInfo) +{ + if (rInfo.ListenForModify) + return; + + css::uno::Reference< css::util::XModifyBroadcaster > xBroadcaster(rInfo.Document, css::uno::UNO_QUERY); + if (xBroadcaster.is()) + { + css::uno::Reference< css::util::XModifyListener > xThis(this); + xBroadcaster->addModifyListener(xThis); + rInfo.ListenForModify = true; + } +} + +void AutoRecovery::implts_stopModifyListeningOnDoc(AutoRecovery::TDocumentInfo& rInfo) +{ + if (! rInfo.ListenForModify) + return; + + css::uno::Reference< css::util::XModifyBroadcaster > xBroadcaster(rInfo.Document, css::uno::UNO_QUERY); + if (xBroadcaster.is()) + { + css::uno::Reference< css::util::XModifyListener > xThis(this); + xBroadcaster->removeModifyListener(xThis); + rInfo.ListenForModify = false; + } +} + +void AutoRecovery::implts_updateTimer() +{ + implts_stopTimer(); + + sal_Int64 nMilliSeconds = 0; + + /* SAFE */ { + osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex); + + if ( + (m_eJob == Job::NoJob ) || // TODO may be superfluous - E_DONT_START_TIMER should be used only + (m_eTimerType == AutoRecovery::E_DONT_START_TIMER) + ) + return; + + if (m_eTimerType == AutoRecovery::E_NORMAL_AUTOSAVE_INTERVALL) + { + const sal_Int64 nConfiguredAutoSaveInterval + = officecfg::Office::Recovery::AutoSave::TimeIntervall::get() + * sal_Int64(60000); // [min] => 60.000 ms + nMilliSeconds = nConfiguredAutoSaveInterval; + + // Calculate how soon the nearest dirty document's autosave time is; + // store the shortest document autosave timeout as the next timer timeout. + for (const auto& docInfo : m_lDocCache) + { + if (auto xDocRecovery2 = docInfo.Document.query()) + { + sal_Int64 nDirtyDuration = xDocRecovery2->getModifiedStateDuration(); + if (nDirtyDuration < 0) + continue; + if (nDirtyDuration > nConfiguredAutoSaveInterval) + nDirtyDuration = nConfiguredAutoSaveInterval; // nMilliSeconds will be 0 + + nMilliSeconds + = std::min(nMilliSeconds, nConfiguredAutoSaveInterval - nDirtyDuration); + } + } + } + else if (m_eTimerType == AutoRecovery::E_POLL_FOR_USER_IDLE) + { + nMilliSeconds = MIN_TIME_FOR_USER_IDLE; + } + else if (m_eTimerType == AutoRecovery::E_POLL_TILL_AUTOSAVE_IS_ALLOWED) + nMilliSeconds = 300; // there is a minimum time frame, where the user can lose some key input data! + + + } /* SAFE */ + + SolarMutexGuard g; + m_aTimer.SetTimeout(nMilliSeconds); + m_aTimer.Start(); +} + +void AutoRecovery::implts_stopTimer() +{ + SolarMutexGuard g; + + if (!m_aTimer.IsActive()) + return; + m_aTimer.Stop(); +} + +IMPL_LINK_NOARG(AutoRecovery, implts_timerExpired, Timer *, void) +{ + try + { + // This method is called by using a pointer to us. + // But we must be aware that we can be destroyed hardly + // if our uno reference will be gone! + // => Hold this object alive till this method finish its work. + css::uno::Reference< css::uno::XInterface > xSelfHold(static_cast< css::lang::XTypeProvider* >(this)); + + // Needed! Otherwise every reschedule request allow a new triggered timer event :-( + implts_stopTimer(); + + // The timer must be ignored if AutoSave/Recovery was disabled for this + // office session. That can happen if e.g. the command line arguments "--norestore" or "--headless" + // was set. But normally the timer was disabled if recovery was disabled ... + // But so we are more "safe" .-) + /* SAFE */ { + osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex); + if ((m_eJob & Job::DisableAutorecovery) == Job::DisableAutorecovery) + return; + } /* SAFE */ + + // check some "states", where it's not allowed (better: not a good idea) to + // start an AutoSave. (e.g. if the user makes drag & drop ...) + // Then we poll till this "disallowed" state is gone. + bool bAutoSaveNotAllowed = Application::IsUICaptured(); + if (bAutoSaveNotAllowed) + { + /* SAFE */ { + osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex); + m_eTimerType = AutoRecovery::E_POLL_TILL_AUTOSAVE_IS_ALLOWED; + } /* SAFE */ + implts_updateTimer(); + return; + } + + // analyze timer type. + // If we poll for an user idle period, may be we must + // do nothing here and start the timer again. + /* SAFE */ { + osl::ClearableMutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex); + + if (m_eTimerType == AutoRecovery::E_POLL_FOR_USER_IDLE) + { + bool bUserIdle = Application::GetLastInputInterval() > MIN_TIME_FOR_USER_IDLE; + if (!bUserIdle) + { + g.clear(); + implts_updateTimer(); + return; + } + } + + } /* SAFE */ + + implts_informListener(Job::AutoSave, + AutoRecovery::implst_createFeatureStateEvent(Job::AutoSave, OPERATION_START, nullptr)); + + // force save of all currently open documents + // The called method returns an info, if and how this + // timer must be restarted. + const bool bIsAlreadyIdle(m_eTimerType == AutoRecovery::E_POLL_FOR_USER_IDLE); + AutoRecovery::ETimerType eSuggestedTimer + = implts_saveDocs(/*AllowUserIdleLoop=*/!bIsAlreadyIdle, /*RemoveLockFiles=*/false); + + // If timer is not used for "short callbacks" (means polling + // for special states) ... reset the handle state of all + // cache items. Such handle state indicates, that a document + // was already saved during the THIS(!) AutoSave session. + // Of course NEXT AutoSave session must be started without + // any "handle" state ... + if ( + (eSuggestedTimer == AutoRecovery::E_DONT_START_TIMER ) || + (eSuggestedTimer == AutoRecovery::E_NORMAL_AUTOSAVE_INTERVALL) + ) + { + implts_resetHandleStates(); + } + + implts_informListener(Job::AutoSave, + AutoRecovery::implst_createFeatureStateEvent(Job::AutoSave, OPERATION_STOP, nullptr)); + + // restart timer - because it was disabled before ... + /* SAFE */ { + osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex); + m_eTimerType = eSuggestedTimer; + } /* SAFE */ + + implts_updateTimer(); + } + catch(const css::uno::Exception&) + { + } +} + +IMPL_LINK_NOARG(AutoRecovery, implts_asyncDispatch, LinkParamNone*, void) +{ + DispatchParams aParams; + /* SAFE */ { + osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex); + aParams = m_aDispatchParams; + css::uno::Reference< css::uno::XInterface > xHoldRefForMethodAlive = aParams.m_xHoldRefForAsyncOpAlive; + m_aDispatchParams.forget(); // clears all members ... including the ref-hold object .-) + } /* SAFE */ + + try + { + implts_dispatch(aParams); + } + catch (...) + { + } +} + +void AutoRecovery::implts_registerDocument(const css::uno::Reference< css::frame::XModel3 > & xDocument) +{ + // ignore corrupted events, where no document is given ... Runtime Error ?! + if (!xDocument.is()) + return; + + CacheLockGuard aCacheLock(this, cppu::WeakComponentImplHelperBase::rBHelper.rMutex, m_nDocCacheLock, LOCK_FOR_CACHE_USE); + + // notification for already existing document ! + // Can happen if events came in asynchronous on recovery time. + // Then our cache was filled from the configuration ... but now we get some + // asynchronous events from the global event broadcaster. We must be sure that + // we don't add the same document more than once. + AutoRecovery::TDocumentList::iterator pIt = AutoRecovery::impl_searchDocument(m_lDocCache, xDocument); + if (pIt != m_lDocCache.end()) + { + // Normally nothing must be done for this "late" notification. + // But may be the modified state was changed inbetween. + // Check it... + implts_updateModifiedState(xDocument); + return; + } + + aCacheLock.unlock(); + + utl::MediaDescriptor lDescriptor(xDocument->getArgs2( { utl::MediaDescriptor::PROP_FILTERNAME, utl::MediaDescriptor::PROP_NOAUTOSAVE } )); + + // check if this document must be ignored for recovery ! + // Some use cases don't wish support for AutoSave/Recovery ... as e.g. OLE-Server / ActiveX Control etcpp. + bool bNoAutoSave = lDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_NOAUTOSAVE, false); + if (bNoAutoSave) + return; + + // Check if doc is well known on the desktop. Otherwise ignore it! + // Other frames mostly are used from external programs - e.g. the bean ... + css::uno::Reference< css::frame::XController > xController = xDocument->getCurrentController(); + if (!xController.is()) + return; + + css::uno::Reference< css::frame::XFrame > xFrame = xController->getFrame(); + if (!xFrame.is()) + return; + css::uno::Reference< css::frame::XDesktop > xDesktop (xFrame->getCreator(), css::uno::UNO_QUERY); + if (!xDesktop.is()) + return; + + // if the document doesn't support the XDocumentRecovery interface, we're not interested in it. + Reference< XDocumentRecovery > xDocRecovery( xDocument, UNO_QUERY ); + if ( !xDocRecovery.is() ) + return; + + // get all needed information of this document + // We need it to update our cache or to locate already existing elements there! + AutoRecovery::TDocumentInfo aNew; + aNew.Document = xDocument; + + // TODO replace getLocation() with getURL() ... it's a workaround currently only! + css::uno::Reference< css::frame::XStorable > xDoc(aNew.Document, css::uno::UNO_QUERY_THROW); + aNew.OrgURL = xDoc->getLocation(); + + css::uno::Reference< css::frame::XTitle > xTitle(aNew.Document, css::uno::UNO_QUERY_THROW); + aNew.Title = xTitle->getTitle (); + + // classify the used application module, which is used by this document. + implts_specifyAppModuleAndFactory(aNew); + + // Hack! Check for "illegal office documents"... as e.g. the Basic IDE + // It's not really a full featured office document. It doesn't provide a URL, any filter, a factory URL etcpp. + // TODO file bug to Basic IDE developers. They must remove the office document API from its service. + if ( + (aNew.OrgURL.isEmpty()) && + (aNew.FactoryURL.isEmpty()) + ) + { + OSL_FAIL( "AutoRecovery::implts_registerDocument: this should not happen anymore!" ); + // nowadays, the Basic IDE should already die on the "supports XDocumentRecovery" check. And no other known + // document type fits in here ... + return; + } + + // By the way - get some information about the default format for saving! + // and save an information about the real used filter by this document. + // We save this document with DefaultFilter ... and load it with the RealFilter. + implts_specifyDefaultFilterAndExtension(aNew); + aNew.RealFilter = lDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_FILTERNAME, OUString()); + + // Further we must know, if this document base on a template. + // Then we must load it in a different way. + css::uno::Reference< css::document::XDocumentPropertiesSupplier > xSupplier(aNew.Document, css::uno::UNO_QUERY); + if (xSupplier.is()) // optional interface! + { + css::uno::Reference< css::document::XDocumentProperties > xDocProps(xSupplier->getDocumentProperties(), css::uno::UNO_SET_THROW); + aNew.TemplateURL = xDocProps->getTemplateURL(); + } + + css::uno::Reference< css::util::XModifiable > xModifyCheck(xDocument, css::uno::UNO_QUERY_THROW); + if (xModifyCheck->isModified()) + { + aNew.DocumentState |= DocState::Modified; + } + + aCacheLock.lock(LOCK_FOR_CACHE_ADD_REMOVE); + + AutoRecovery::TDocumentInfo aInfo; + /* SAFE */ { + osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex); + + // create a new cache entry ... this document is not known. + ++m_nIdPool; + aNew.ID = m_nIdPool; + SAL_WARN_IF(m_nIdPool<0, "fwk.autorecovery", "AutoRecovery::implts_registerDocument(): Overflow of ID pool detected."); + m_lDocCache.push_back(aNew); + + AutoRecovery::TDocumentList::iterator pIt1 = AutoRecovery::impl_searchDocument(m_lDocCache, xDocument); + aInfo = *pIt1; + + } /* SAFE */ + + // Even if the document is modified, we don't know if we have anything to recover, so don't add. + implts_flushConfigItem(aInfo, /*bRemoveIt=*/false, /*bAllowAdd=*/false); + implts_startModifyListeningOnDoc(aInfo); + + aCacheLock.unlock(); +} + +void AutoRecovery::implts_deregisterDocument(const css::uno::Reference< css::frame::XModel >& xDocument , + bool bStopListening) +{ + AutoRecovery::TDocumentInfo aInfo; + /* SAFE */ { + osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex); + + // Attention: Don't leave SAFE section, if you work with pIt! + // Because it points directly into the m_lDocCache list ... + CacheLockGuard aCacheLock(this, cppu::WeakComponentImplHelperBase::rBHelper.rMutex, m_nDocCacheLock, LOCK_FOR_CACHE_USE); + + AutoRecovery::TDocumentList::iterator pIt = AutoRecovery::impl_searchDocument(m_lDocCache, xDocument); + if (pIt == m_lDocCache.end()) + return; // unknown document => not a runtime error! Because we register only a few documents. see registration ... + + aInfo = *pIt; + + aCacheLock.unlock(); + + // Sometimes we close documents by ourself. + // And these documents can't be deregistered. + // Otherwise we lose our configuration data... but need it ! + // see SessionSave ! + if (aInfo.IgnoreClosing) + return; + + CacheLockGuard aCacheLock2(this, cppu::WeakComponentImplHelperBase::rBHelper.rMutex, m_nDocCacheLock, LOCK_FOR_CACHE_ADD_REMOVE); + pIt = AutoRecovery::impl_searchDocument(m_lDocCache, xDocument); + if (pIt != m_lDocCache.end()) + m_lDocCache.erase(pIt); + pIt = m_lDocCache.end(); // otherwise it's not specified what pIt means! + aCacheLock2.unlock(); + + } /* SAFE */ + + /* This method is called within disposing() of the document too. But there it's not a good idea to + deregister us as listener. Further it makes no sense - because the broadcaster dies. + So we suppress deregistration in such case... + */ + if (bStopListening) + implts_stopModifyListeningOnDoc(aInfo); + + implts_flushConfigItem(aInfo, true); // sal_True => remove it from config +} + +void AutoRecovery::implts_markDocumentModifiedAgainstLastBackup(const css::uno::Reference< css::frame::XModel >& xDocument) +{ + CacheLockGuard aCacheLock(this, cppu::WeakComponentImplHelperBase::rBHelper.rMutex, m_nDocCacheLock, LOCK_FOR_CACHE_USE); + + /* SAFE */ { + osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex); + + AutoRecovery::TDocumentList::iterator pIt = AutoRecovery::impl_searchDocument(m_lDocCache, xDocument); + if (pIt != m_lDocCache.end()) + { + /* Now we know, that this document was modified again and must be saved next time. + But we don't need this information for every e.g. key input of the user. + So we stop listening here. + But if the document was saved as temp. file we start listening for this event again. + */ + implts_stopModifyListeningOnDoc(*pIt); + } + + } /* SAFE */ +} + +void AutoRecovery::implts_updateModifiedState(const css::uno::Reference< css::frame::XModel >& xDocument) +{ + // use true as fallback to get every document on EmergencySave/AutoRecovery! + bool bModified = true; + css::uno::Reference< css::util::XModifiable > xModify(xDocument, css::uno::UNO_QUERY); + if (xModify.is()) + bModified = xModify->isModified(); + + CacheLockGuard aCacheLock(this, cppu::WeakComponentImplHelperBase::rBHelper.rMutex, m_nDocCacheLock, LOCK_FOR_CACHE_USE); + + /* SAFE */ { + osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex); + + AutoRecovery::TDocumentList::iterator pIt = AutoRecovery::impl_searchDocument(m_lDocCache, xDocument); + if (pIt != m_lDocCache.end()) + { + AutoRecovery::TDocumentInfo& rInfo = *pIt; + + if (bModified) + { + rInfo.DocumentState |= DocState::Modified; + } + else + { + rInfo.DocumentState &= ~DocState::Modified; + } + } + + } /* SAFE */ +} + +void AutoRecovery::implts_updateDocumentUsedForSavingState(const css::uno::Reference< css::frame::XModel >& xDocument , + bool bSaveInProgress) +{ + CacheLockGuard aCacheLock(this, cppu::WeakComponentImplHelperBase::rBHelper.rMutex, m_nDocCacheLock, LOCK_FOR_CACHE_USE); + + /* SAFE */ { + osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex); + + AutoRecovery::TDocumentList::iterator pIt = AutoRecovery::impl_searchDocument(m_lDocCache, xDocument); + if (pIt == m_lDocCache.end()) + return; + AutoRecovery::TDocumentInfo& rInfo = *pIt; + rInfo.UsedForSaving = bSaveInProgress; + + } /* SAFE */ +} + +void AutoRecovery::implts_markDocumentAsSaved(const css::uno::Reference< css::frame::XModel >& xDocument) +{ + CacheLockGuard aCacheLock(this, cppu::WeakComponentImplHelperBase::rBHelper.rMutex, m_nDocCacheLock, LOCK_FOR_CACHE_USE); + + AutoRecovery::TDocumentInfo aInfo; + OUString sRemoveURL1; + OUString sRemoveURL2; + /* SAFE */ { + osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex); + + AutoRecovery::TDocumentList::iterator pIt = AutoRecovery::impl_searchDocument(m_lDocCache, xDocument); + if (pIt == m_lDocCache.end()) + return; + aInfo = *pIt; + + /* Since the document has been saved, update its entry in the document + * cache. We essentially reset the state of the document from an + * autorecovery perspective, updating things like the filename (which + * would change in the case of a 'Save as' operation) and the associated + * backup file URL. */ + + aInfo.DocumentState = DocState::Unknown; + // TODO replace getLocation() with getURL() ... it's a workaround currently only! + css::uno::Reference< css::frame::XStorable > xDoc(aInfo.Document, css::uno::UNO_QUERY); + aInfo.OrgURL = xDoc->getLocation(); + + /* Save off the backup file URLs and then clear them. NOTE - it is + * important that we clear them - otherwise, we could enter a state + * where pIt->OldTempURL == pIt->NewTempURL and our backup algorithm + * in implts_saveOneDoc will write to that URL and then delete the file + * at that URL (bug #96607) */ + sRemoveURL1 = aInfo.OldTempURL; + sRemoveURL2 = aInfo.NewTempURL; + aInfo.OldTempURL.clear(); + aInfo.NewTempURL.clear(); + + utl::MediaDescriptor lDescriptor(aInfo.Document->getArgs()); + aInfo.RealFilter = lDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_FILTERNAME, OUString()); + + css::uno::Reference< css::frame::XTitle > xDocTitle(xDocument, css::uno::UNO_QUERY); + if (xDocTitle.is ()) + aInfo.Title = xDocTitle->getTitle (); + else + { + aInfo.Title = lDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_TITLE, OUString()); + if (aInfo.Title.isEmpty()) + aInfo.Title = lDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_DOCUMENTTITLE, OUString()); + } + + aInfo.UsedForSaving = false; + + *pIt = aInfo; + + } /* SAFE */ + + // no need to recover a saved document until modified and new recovery file is created + implts_flushConfigItem(aInfo, /*bRemoveIt=*/true); + + aCacheLock.unlock(); + + AutoRecovery::st_impl_removeFile(sRemoveURL1); + AutoRecovery::st_impl_removeFile(sRemoveURL2); +} + +AutoRecovery::TDocumentList::iterator AutoRecovery::impl_searchDocument( AutoRecovery::TDocumentList& rList , + const css::uno::Reference< css::frame::XModel >& xDocument) +{ + return std::find_if(rList.begin(), rList.end(), + [&xDocument](const AutoRecovery::TDocumentInfo& rInfo) { return rInfo.Document == xDocument; }); +} + +void lcl_changeVisibility( const css::uno::Reference< css::frame::XFramesSupplier >& i_rFrames, bool i_bVisible ) +{ + css::uno::Reference< css::container::XIndexAccess > xFramesContainer = i_rFrames->getFrames(); + const sal_Int32 count = xFramesContainer->getCount(); + + Any aElement; + for ( sal_Int32 i=0; i < count; ++i ) + { + aElement = xFramesContainer->getByIndex(i); + // check for sub frames + css::uno::Reference< css::frame::XFramesSupplier > xFramesSupp( aElement, css::uno::UNO_QUERY ); + if ( xFramesSupp.is() ) + lcl_changeVisibility( xFramesSupp, i_bVisible ); + + css::uno::Reference< css::frame::XFrame > xFrame( aElement, css::uno::UNO_QUERY ); + if ( !xFrame.is() ) + continue; + + css::uno::Reference< css::awt::XWindow > xWindow( xFrame->getContainerWindow(), UNO_SET_THROW ); + xWindow->setVisible( i_bVisible ); + } +} + +void AutoRecovery::implts_changeAllDocVisibility(bool bVisible) +{ + css::uno::Reference< css::frame::XFramesSupplier > xDesktop = css::frame::Desktop::create(m_xContext); + lcl_changeVisibility( xDesktop, bVisible ); +} + +/* Currently the document is not closed in case of crash, + so the lock file must be removed explicitly +*/ +void lc_removeLockFile(AutoRecovery::TDocumentInfo const & rInfo) +{ +#if !HAVE_FEATURE_MULTIUSER_ENVIRONMENT || HAVE_FEATURE_MACOSX_SANDBOX + (void) rInfo; +#else + if ( !rInfo.Document.is() ) + return; + + try + { + css::uno::Reference< css::frame::XStorable > xStore(rInfo.Document, css::uno::UNO_QUERY_THROW); + OUString aURL = xStore->getLocation(); + if ( !aURL.isEmpty() ) + { + ::svt::DocumentLockFile aLockFile( aURL ); + aLockFile.RemoveFile(); + } + } + catch( const css::uno::Exception& ) + { + } +#endif +} + +void AutoRecovery::implts_prepareSessionShutdown() +{ + SAL_INFO("fwk.autorecovery", "AutoRecovery::implts_prepareSessionShutdown() starts ..."); + + // a) reset modified documents (of course the must be saved before this method is called!) + // b) close it without showing any UI! + + /* SAFE */ { + CacheLockGuard aCacheLock(this, cppu::WeakComponentImplHelperBase::rBHelper.rMutex, m_nDocCacheLock, LOCK_FOR_CACHE_USE); + + for (auto & info : m_lDocCache) + { + // WORKAROUND... Since the documents are not closed the lock file must be removed explicitly + // it is not done on documents saving since shutdown can be cancelled + lc_removeLockFile( info ); + + // Prevent us from deregistration of these documents. + // Because we close these documents by ourself (see XClosable below) ... + // it's fact, that we reach our deregistration method. There we + // must not(!) update our configuration ... Otherwise all + // session data are lost !!! + info.IgnoreClosing = true; + + // reset modified flag of these documents (ignoring the notification about it!) + // Otherwise a message box is shown on closing these models. + implts_stopModifyListeningOnDoc(info); + + // if the session save is still running the documents should not be thrown away, + // actually that would be a bad sign, that means that the SessionManager tries + // to kill the session before the saving is ready + if ((m_eJob & Job::SessionSave) != Job::SessionSave) + { + css::uno::Reference< css::util::XModifiable > xModify(info.Document, css::uno::UNO_QUERY); + if (xModify.is()) + xModify->setModified(false); + + // close the model. + css::uno::Reference< css::util::XCloseable > xClose(info.Document, css::uno::UNO_QUERY); + if (xClose.is()) + { + try + { + xClose->close(false); + } + catch(const css::uno::Exception&) + { + // At least it's only a try to close these documents before anybody else it does. + // So it seems to be possible to ignore any error here .-) + } + + info.Document.clear(); + } + } + } + + aCacheLock.unlock(); + } /* SAFE */ +} + +/* TODO WORKAROUND: + + #i64599# + + Normally the MediaDescriptor argument NoAutoSave indicates, + that a document must be ignored for AutoSave and Recovery. + But sometimes XModel->getArgs() does not contained this information + if implts_registerDocument() was called. + So we have to check a second time, if this property is set... + Best place doing so is to check it immediately before saving + and suppressing saving the document then. + Of course removing the corresponding cache entry is not an option. + Because it would disturb iteration over the cache! + So we ignore such documents only... + Hopefully next time they are not inserted in our cache. +*/ +bool lc_checkIfSaveForbiddenByArguments(AutoRecovery::TDocumentInfo const & rInfo) +{ + if (! rInfo.Document.is()) + return true; + + utl::MediaDescriptor lDescriptor(rInfo.Document->getArgs()); + bool bNoAutoSave = lDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_NOAUTOSAVE, false); + + return bNoAutoSave; +} + +AutoRecovery::ETimerType AutoRecovery::implts_saveDocs( bool bAllowUserIdleLoop, + bool bRemoveLockFiles, + const DispatchParams* pParams ) +{ + css::uno::Reference< css::task::XStatusIndicator > xExternalProgress; + if (pParams) + xExternalProgress = pParams->m_xProgress; + + OUString sBackupPath(SvtPathOptions().GetBackupPath()); + + // Set the default timer action for our call. + // Default = NORMAL_AUTOSAVE + // We return a suggestion for an active timer only. + // It will be ignored if the timer was disabled by the user ... + // Further this state can be set to USER_IDLE only later in this method. + // It's not allowed to reset such state then. Because we must know, if + // there exists POSTPONED documents. see below ... + AutoRecovery::ETimerType eTimer = AutoRecovery::E_NORMAL_AUTOSAVE_INTERVALL; + + Job eJob = m_eJob; + + CacheLockGuard aCacheLock(this, cppu::WeakComponentImplHelperBase::rBHelper.rMutex, m_nDocCacheLock, LOCK_FOR_CACHE_USE); + + const sal_Int64 nConfiguredAutoSaveInterval + = officecfg::Office::Recovery::AutoSave::TimeIntervall::get() + * sal_Int64(60000); // min -> ms + + /* SAFE */ { + osl::ResettableMutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex); + + // This list will be filled with every document + // which should be saved as last one. E.g. if it was used + // already for a UI save operation => crashed ... and + // now we try to save it again ... which can fail again ( of course .-) ). + ::std::vector< AutoRecovery::TDocumentList::iterator > lDangerousDocs; + + AutoRecovery::TDocumentList::iterator pIt; + for ( pIt = m_lDocCache.begin(); + pIt != m_lDocCache.end(); + ++pIt ) + { + AutoRecovery::TDocumentInfo aInfo = *pIt; + + // WORKAROUND... Since the documents are not closed the lock file must be removed explicitly + if ( bRemoveLockFiles ) + lc_removeLockFile( aInfo ); + + // WORKAROUND ... see comment of this method + if (lc_checkIfSaveForbiddenByArguments(aInfo)) + continue; + + // already auto saved during this session :-) + // This state must be reset for all documents + // if timer is started with normal AutoSaveTimerIntervall! + if ((aInfo.DocumentState & DocState::Handled) == DocState::Handled) + continue; + + // don't allow implts_deregisterDocument to remove from RecoveryList during shutdown jobs + if (m_eJob & (Job::EmergencySave | Job::SessionSave)) + aInfo.IgnoreClosing = true; + + // Not modified documents are not saved. + // We save information about the URL only! + Reference< XDocumentRecovery > xDocRecover( aInfo.Document, UNO_QUERY_THROW ); + if ( !xDocRecover->wasModifiedSinceLastSave() ) + { + aInfo.DocumentState |= DocState::Handled; + *pIt = aInfo; + continue; + } + + // If the document became modified not too long ago, don't autosave it yet. + if (bAllowUserIdleLoop) + { + if (auto xDocRecovery2 = xDocRecover.query()) + { + const sal_Int64 nDirtyDuration = xDocRecovery2->getModifiedStateDuration(); + // Round up to second - if this document is almost ready for autosave, do it now. + if (nDirtyDuration + 999 < nConfiguredAutoSaveInterval) + { + aInfo.DocumentState |= DocState::Handled; + continue; + } + } + } + + // check if this document is still used by a concurrent save operation + // e.g. if the user tried to save via UI. + // Handle it in the following way: + // i) For an AutoSave ... ignore this document! It will be saved and next time we will (hopefully) + // get a notification about the state of this operation. + // And if a document was saved by the user we can remove our temp. file. But that will be done inside + // our callback for SaveDone notification. + // ii) For a CrashSave ... add it to the list of dangerous documents and + // save it after all other documents was saved successfully. That decrease + // the chance for a crash inside a crash. + // On the other side it's not necessary for documents, which are not modified. + // They can be handled normally - means we patch the corresponding configuration entry only. + // iii) For a SessionSave ... ignore it! There is no time to wait for this save operation. + // Because the WindowManager will kill the process if it doesn't react immediately. + // On the other side we can't risk a concurrent save request ... because we know + // that it will produce a crash. + + // Attention: Because eJob is used as a flag field, you have to check for the worst case first. + // E.g. a CrashSave can overwrite an AutoSave. So you have to check for a CrashSave before an AutoSave! + if (aInfo.UsedForSaving) + { + if ((eJob & Job::EmergencySave) == Job::EmergencySave) + { + lDangerousDocs.push_back(pIt); + continue; + } + else + if ((eJob & Job::SessionSave) == Job::SessionSave) + { + continue; + } + else + if ((eJob & Job::AutoSave) == Job::AutoSave) + { + eTimer = AutoRecovery::E_POLL_TILL_AUTOSAVE_IS_ALLOWED; + aInfo.DocumentState |= DocState::Postponed; + continue; + } + } + + // a) Document was not postponed => wait for user idle if not urgent + // b) Document was postponed => save it (because user idle/call_back was checked already) + if (!(aInfo.DocumentState & DocState::Postponed)) + { + aInfo.DocumentState |= DocState::Postponed; + *pIt = aInfo; + // postponed documents will be saved if this method is called again! + // That can be done by an outside started timer => E_POLL_FOR_USER_IDLE (if normal AutoSave is active) + // or it must be done directly without starting any timer => E_CALL_ME_BACK (if Emergency- or SessionSave is active and must be finished ASAP!) + if (!bAllowUserIdleLoop) + eTimer = AutoRecovery::E_CALL_ME_BACK; + else + eTimer = AutoRecovery::E_POLL_FOR_USER_IDLE; + continue; + } + + // } /* SAFE */ + g.clear(); + // changing of aInfo and flushing it is done inside implts_saveOneDoc! + implts_saveOneDoc(sBackupPath, aInfo, xExternalProgress); + implts_informListener(eJob, AutoRecovery::implst_createFeatureStateEvent(eJob, OPERATION_UPDATE, &aInfo)); + g.reset(); + // /* SAFE */ { + + *pIt = aInfo; + } + + // Did we have some "dangerous candidates" ? + // Try to save it ... but may be it will fail ! + for (auto const& dangerousDoc : lDangerousDocs) + { + pIt = dangerousDoc; + AutoRecovery::TDocumentInfo aInfo = *pIt; + + // } /* SAFE */ + g.clear(); + // changing of aInfo and flushing it is done inside implts_saveOneDoc! + implts_saveOneDoc(sBackupPath, aInfo, xExternalProgress); + implts_informListener(eJob, AutoRecovery::implst_createFeatureStateEvent(eJob, OPERATION_UPDATE, &aInfo)); + g.reset(); + // /* SAFE */ { + + *pIt = aInfo; + } + + } /* SAFE */ + + return eTimer; +} + +void AutoRecovery::implts_saveOneDoc(const OUString& sBackupPath , + AutoRecovery::TDocumentInfo& rInfo , + const css::uno::Reference< css::task::XStatusIndicator >& xExternalProgress) +{ + // no document? => can occur if we loaded our configuration with files, + // which couldn't be recovered successfully. In such case we have all needed information + // excepting the real document instance! + + // TODO: search right place, where such "dead files" can be removed from the configuration! + if (!rInfo.Document.is()) + return; + + utl::MediaDescriptor lOldArgs(rInfo.Document->getArgs()); + implts_generateNewTempURL(sBackupPath, lOldArgs, rInfo); + + // if the document was loaded with a password, it should be + // stored with password + utl::MediaDescriptor lNewArgs; + css::uno::Sequence< css::beans::NamedValue > aEncryptionData = + lOldArgs.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_ENCRYPTIONDATA, + css::uno::Sequence< css::beans::NamedValue >()); + if (aEncryptionData.hasElements()) + lNewArgs[utl::MediaDescriptor::PROP_ENCRYPTIONDATA] <<= aEncryptionData; + + // Further it must be saved using the default file format of that application. + // Otherwise we will some data lost. + if (!rInfo.DefaultFilter.isEmpty()) + lNewArgs[utl::MediaDescriptor::PROP_FILTERNAME] <<= rInfo.DefaultFilter; + + // prepare frame/document/mediadescriptor in a way, that it uses OUR progress .-) + if (xExternalProgress.is()) + lNewArgs[utl::MediaDescriptor::PROP_STATUSINDICATOR] <<= xExternalProgress; + impl_establishProgress(rInfo, lNewArgs, css::uno::Reference< css::frame::XFrame >()); + + // #i66598# use special handling of property "DocumentBaseURL" (it must be an empty string!) + // for make hyperlinks working + lNewArgs[utl::MediaDescriptor::PROP_DOCUMENTBASEURL] <<= OUString(); + + lNewArgs[utl::MediaDescriptor::PROP_AUTOSAVEEVENT] <<= true; + + // try to save this document as a new temp file every time. + // Mark AutoSave state as "INCOMPLETE" if it failed. + // Because the last temp file is too old and does not include all changes. + Reference< XDocumentRecovery > xDocRecover(rInfo.Document, css::uno::UNO_QUERY_THROW); + + // save the state about "trying to save" + // ... we need it for recovery if e.g. a crash occurs inside next line! + rInfo.DocumentState |= DocState::TrySave; + // just update existing info: don't add any recovery record until recovery file created. + implts_flushConfigItem(rInfo, /*bRemoveIt=*/false, /*bAllowAdd=*/false); + + // If userautosave is enabled, first try to save the original file. + // Note that we must do it *before* calling storeToRecoveryFile, so in case of failure here + // we won't remain with the modified flag set to true, even though the autorecovery save succeeded. + const bool bEmergencySave(m_eJob & Job::EmergencySave); + bool bUserAutoSaved = false; + try + { + // We must check here for an empty URL to avoid a "This operation is not supported on this operating system." + // message during autosave. + if (!bEmergencySave && m_eJob & Job::UserAutoSave && !rInfo.OrgURL.isEmpty()) + { + Reference< XStorable > xDocSave(rInfo.Document, css::uno::UNO_QUERY_THROW); + xDocSave->store(); + bUserAutoSaved = true; + } + } + catch(const css::uno::Exception&) + { + } + + // DocState::Modified status cannot be trusted to be accurate, but at least attempt to be so, + // since this rInfo will eventually get assigned to m_lDocCache as the authoritative status. + const Reference xModify(rInfo.Document, UNO_QUERY); + const bool bModified = xModify.is() && xModify->isModified(); + if (bModified) + rInfo.DocumentState |= DocState::Modified; + else if (xModify.is()) + rInfo.DocumentState &= ~DocState::Modified; + + // If it is no longer modified, it is the same as on disk, and can be removed from RecoveryList. + const bool bRemoveIt + = xModify.is() && !xModify->isModified() && bUserAutoSaved && !(m_eJob & Job::SessionSave); + + sal_Int32 nRetry = RETRY_STORE_ON_FULL_DISC_FOREVER; + bool bError = false; + do + { + try + { + // skip recovery if it will be removed anyway. + if (!bRemoveIt) + xDocRecover->storeToRecoveryFile(rInfo.NewTempURL, + lNewArgs.getAsConstPropertyValueList()); + +#ifdef TRIGGER_FULL_DISC_CHECK + throw css::uno::Exception("trigger full disk check"); +#else // TRIGGER_FULL_DISC_CHECK + + bError = false; + nRetry = 0; +#endif // TRIGGER_FULL_DISC_CHECK + } + catch(const css::uno::Exception& rException) + { + bError = true; + + // skip saving XLSX with protected sheets, if their passwords haven't supported yet + if ( rException.Message.startsWith("SfxBaseModel::impl_store") ) + { + const css::task::ErrorCodeIOException& pErrorCodeIOException = + static_cast(rException); + if ( static_cast(pErrorCodeIOException.ErrCode) == ERRCODE_SFX_WRONGPASSWORD ) + { + // stop and remove the bad temporary file, instead of filling the disk with them + bError = false; + break; + } + } + + // a) FULL DISC seems to be the problem behind => show error and retry it forever (e.g. retry=300) + // b) unknown problem (may be locking problem) => reset RETRY value to more useful value(!) (e.g. retry=3) + // c) unknown problem (may be locking problem) + 1..2 repeating operations => throw the original exception to force generation of a stacktrace ! + + sal_Int32 nMinSpaceDocSave; + /* SAFE */ { + osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex); + nMinSpaceDocSave = m_nMinSpaceDocSave; + } /* SAFE */ + + if (! impl_enoughDiscSpace(nMinSpaceDocSave)) + AutoRecovery::impl_showFullDiscError(); + else if (nRetry > RETRY_STORE_ON_MIGHT_FULL_DISC_USEFULL) + nRetry = RETRY_STORE_ON_MIGHT_FULL_DISC_USEFULL; + else if (nRetry <= GIVE_UP_RETRY) + { + // delete the empty file created by implts_generateNewTempURL + if (tools::isEmptyFileUrl(rInfo.NewTempURL)) + osl::File::remove(rInfo.NewTempURL); + + throw; // force stacktrace to know if there exist might other reasons, why an AutoSave can fail !!! + } + + --nRetry; + } + } + while(nRetry>0); + + if (! bError) + { + // safe the state about success + // ... you know the reason: to know it on recovery time if next line crash .-) + rInfo.DocumentState &= ~DocState::TrySave; + rInfo.DocumentState |= DocState::Handled; + rInfo.DocumentState |= DocState::Succeeded; + } + else + { + // safe the state about error ... + rInfo.NewTempURL.clear(); + rInfo.DocumentState &= ~DocState::TrySave; + rInfo.DocumentState |= DocState::Handled; + rInfo.DocumentState |= DocState::Incomplete; + } + + // make sure the progress is not referred any longer + impl_forgetProgress(rInfo, lNewArgs, css::uno::Reference< css::frame::XFrame >()); + + // try to remove the old temp file. + // Ignore any error here. We have a new temp file, which is up to date. + // The only thing is: we fill the disk with temp files, if we can't remove old ones :-) + OUString sRemoveFile = rInfo.OldTempURL; + rInfo.OldTempURL = rInfo.NewTempURL; + rInfo.NewTempURL.clear(); + + // If it is modified, a recovery file has just been created, so add to RecoveryList. + implts_flushConfigItem(rInfo, bRemoveIt, /*bAllowAdd=*/bModified); + + // We must know if the user modifies the document again ... + implts_startModifyListeningOnDoc(rInfo); + + AutoRecovery::st_impl_removeFile(sRemoveFile); +} + +AutoRecovery::ETimerType AutoRecovery::implts_openDocs(const DispatchParams& aParams) +{ + AutoRecovery::ETimerType eTimer = AutoRecovery::E_DONT_START_TIMER; + + CacheLockGuard aCacheLock(this, cppu::WeakComponentImplHelperBase::rBHelper.rMutex, m_nDocCacheLock, LOCK_FOR_CACHE_USE); + + /* SAFE */ { + osl::ResettableMutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex); + + Job eJob = m_eJob; + for (auto & info : m_lDocCache) + { + // Such documents are already loaded by the last loop. + // Don't check DocState::Succeeded here! It may be the final state of an AutoSave + // operation before!!! + if ((info.DocumentState & DocState::Handled) == DocState::Handled) + continue; + + // a1,b1,c1,d2,e2,f2) + if ((info.DocumentState & DocState::Damaged) == DocState::Damaged) + { + // don't forget to inform listener! May be this document was + // damaged on last saving time ... + // Then our listener need this notification. + // If it was damaged during last "try to open" ... + // it will be notified more than once. SH.. HAPPENS ... + // } /* SAFE */ + g.clear(); + implts_informListener(eJob, + AutoRecovery::implst_createFeatureStateEvent(eJob, OPERATION_UPDATE, &info)); + g.reset(); + // /* SAFE */ { + continue; + } + + utl::MediaDescriptor lDescriptor; + + // it's a UI feature - so the "USER" itself must be set as referrer + lDescriptor[utl::MediaDescriptor::PROP_REFERRER] <<= OUString(REFERRER_USER); + lDescriptor[utl::MediaDescriptor::PROP_SALVAGEDFILE] <<= OUString(); + + // recovered documents are loaded hidden, and shown all at once, later + lDescriptor[utl::MediaDescriptor::PROP_HIDDEN] <<= true; + + if (aParams.m_xProgress.is()) + lDescriptor[utl::MediaDescriptor::PROP_STATUSINDICATOR] <<= aParams.m_xProgress; + + bool bBackupWasTried = ( + ((info.DocumentState & DocState::TryLoadBackup ) == DocState::TryLoadBackup) || // temp. state! + ((info.DocumentState & DocState::Incomplete ) == DocState::Incomplete ) // transport DocState::TryLoadBackup from last loop to this new one! + ); + bool bOriginalWasTried = ((info.DocumentState & DocState::TryLoadOriginal) == DocState::TryLoadOriginal); + + if (bBackupWasTried) + { + if (!bOriginalWasTried) + { + info.DocumentState |= DocState::Incomplete; + // try original URL ... ! don't continue with next item here ... + } + else + { + info.DocumentState |= DocState::Damaged; + continue; + } + } + + OUString sLoadOriginalURL; + OUString sLoadBackupURL; + + if (!bBackupWasTried) + sLoadBackupURL = info.OldTempURL; + + if (!info.OrgURL.isEmpty()) + { + sLoadOriginalURL = info.OrgURL; + } + else if (!info.TemplateURL.isEmpty()) + { + sLoadOriginalURL = info.TemplateURL; + lDescriptor[utl::MediaDescriptor::PROP_ASTEMPLATE] <<= true; + lDescriptor[utl::MediaDescriptor::PROP_TEMPLATENAME] <<= info.TemplateURL; + } + else if (!info.FactoryURL.isEmpty()) + { + sLoadOriginalURL = info.FactoryURL; + lDescriptor[utl::MediaDescriptor::PROP_ASTEMPLATE] <<= true; + } + + // A "Salvaged" item must exists every time. The core can make something special then for recovery. + // Of course it should be the real file name of the original file, in case we load the temp. backup here. + OUString sURL; + if (!sLoadBackupURL.isEmpty()) + { + sURL = sLoadBackupURL; + info.DocumentState |= DocState::TryLoadBackup; + lDescriptor[utl::MediaDescriptor::PROP_SALVAGEDFILE] <<= sLoadOriginalURL; + } + else if (!sLoadOriginalURL.isEmpty()) + { + sURL = sLoadOriginalURL; + info.DocumentState |= DocState::TryLoadOriginal; + } + else + continue; // TODO ERROR! + + LoadEnv::initializeUIDefaults( m_xContext, lDescriptor, true, nullptr ); + + // } /* SAFE */ + g.clear(); + + implts_flushConfigItem(info); + implts_informListener(eJob, + AutoRecovery::implst_createFeatureStateEvent(eJob, OPERATION_UPDATE, &info)); + + try + { + implts_openOneDoc(sURL, lDescriptor, info); + } + catch(const css::uno::Exception&) + { + info.DocumentState &= ~DocState::TryLoadBackup; + info.DocumentState &= ~DocState::TryLoadOriginal; + if (!sLoadBackupURL.isEmpty()) + { + info.DocumentState |= DocState::Incomplete; + eTimer = AutoRecovery::E_CALL_ME_BACK; + } + else + { + info.DocumentState |= DocState::Handled; + info.DocumentState |= DocState::Damaged; + } + + implts_flushConfigItem(info, /*bRemoveIt=*/true); + + implts_informListener(eJob, + AutoRecovery::implst_createFeatureStateEvent(eJob, OPERATION_UPDATE, &info)); + + // /* SAFE */ { + // Needed for next loop! + g.reset(); + continue; + } + + if (!info.RealFilter.isEmpty()) + { + utl::MediaDescriptor lPatchDescriptor(info.Document->getArgs()); + lPatchDescriptor[utl::MediaDescriptor::PROP_FILTERNAME] <<= info.RealFilter; + info.Document->attachResource(info.Document->getURL(), lPatchDescriptor.getAsConstPropertyValueList()); + // do *not* use sURL here. In case this points to the recovery file, it has already been passed + // to recoverFromFile. Also, passing it here is logically wrong, as attachResource is intended + // to take the logical file URL. + } + + css::uno::Reference< css::util::XModifiable > xModify(info.Document, css::uno::UNO_QUERY); + if ( xModify.is() ) + { + bool bModified = ((info.DocumentState & DocState::Modified) == DocState::Modified); + xModify->setModified(bModified); + } + + info.DocumentState &= ~DocState::TryLoadBackup; + info.DocumentState &= ~DocState::TryLoadOriginal; + info.DocumentState |= DocState::Handled; + info.DocumentState |= DocState::Succeeded; + + implts_flushConfigItem(info); + implts_informListener(eJob, + AutoRecovery::implst_createFeatureStateEvent(eJob, OPERATION_UPDATE, &info)); + + /* Normally we listen as XModifyListener on a document to know if a document was changed + since our last AutoSave. And we deregister us in case we know this state. + But directly after one document as recovered ... we must start listening. + Otherwise the first "modify" doesn't reach us. Because we ourself called setModified() + on the document via API. And currently we don't listen for any events (not at theGlobalEventBroadcaster + nor at any document!). + */ + implts_startModifyListeningOnDoc(info); + + // /* SAFE */ { + // Needed for next loop. Don't unlock it again! + g.reset(); + } + + } /* SAFE */ + + return eTimer; +} + +void AutoRecovery::implts_openOneDoc(const OUString& sURL , + utl::MediaDescriptor& lDescriptor, + AutoRecovery::TDocumentInfo& rInfo ) +{ + css::uno::Reference< css::frame::XDesktop2 > xDesktop = css::frame::Desktop::create(m_xContext); + + ::std::vector< Reference< XComponent > > aCleanup; + try + { + // create a new document of the desired type + Reference< XModel2 > xModel(m_xContext->getServiceManager()->createInstanceWithContext( + rInfo.FactoryService, m_xContext), UNO_QUERY_THROW); + aCleanup.emplace_back(xModel.get() ); + + // put the filter name into the descriptor - we're not going to involve any type detection, so + // the document might be lost without the FilterName property + if ( (rInfo.DocumentState & DocState::TryLoadOriginal) == DocState::TryLoadOriginal) + lDescriptor[ utl::MediaDescriptor::PROP_FILTERNAME ] <<= rInfo.RealFilter; + else + lDescriptor[ utl::MediaDescriptor::PROP_FILTERNAME ] <<= rInfo.DefaultFilter; + + if ( sURL == rInfo.FactoryURL ) + { + // if the document was a new, unmodified document, then there's nothing to recover, just to init + ENSURE_OR_THROW( ( rInfo.DocumentState & DocState::Modified ) == DocState(0), + "unexpected document state" ); + Reference< XLoadable > xModelLoad( xModel, UNO_QUERY_THROW ); + xModelLoad->initNew(); + + // TODO: remove load-process specific arguments from the descriptor, e.g. the status indicator + xModel->attachResource( sURL, lDescriptor.getAsConstPropertyValueList() ); + } + else if (!utl::UCBContentHelper::Exists(sURL)) + throw css::uno::Exception(); + else + { + OUString sFilterName; + lDescriptor[utl::MediaDescriptor::PROP_FILTERNAME] >>= sFilterName; + if (!sFilterName.isEmpty() + && ( sFilterName == "Calc MS Excel 2007 XML" + || sFilterName == "Impress MS PowerPoint 2007 XML" + || sFilterName == "MS Word 2007 XML")) + // TODO: Probably need to check other affected formats + templates? + { + // tdf#129096: in case of recovery of password protected OOXML document it is done not + // the same way as ordinal loading. Inside XDocumentRecovery::recoverFromFile + // there is a call to XFilter::filter which has constant media descriptor and thus + // all encryption data used in document is lost. To avoid this try to walkaround + // with explicit call to FormatDetector. It will try to load document, prompt for password + // and store this info in media descriptor we will use for recoverFromFile call. + Reference< css::document::XExtendedFilterDetection > xDetection( + m_xContext->getServiceManager()->createInstanceWithContext( + "com.sun.star.comp.oox.FormatDetector", m_xContext), + UNO_QUERY_THROW); + lDescriptor[utl::MediaDescriptor::PROP_URL] <<= sURL; + Sequence< css::beans::PropertyValue > aDescriptorSeq = lDescriptor.getAsConstPropertyValueList(); + OUString sType = xDetection->detect(aDescriptorSeq); + + OUString sNewFilterName; + lDescriptor[utl::MediaDescriptor::PROP_FILTERNAME] >>= sNewFilterName; + if (!sType.isEmpty() && sNewFilterName == sFilterName) + { + // Filter detection was okay, update media descriptor with one received from FilterDetect + lDescriptor = aDescriptorSeq; + } + } + + // let it recover itself + Reference< XDocumentRecovery > xDocRecover( xModel, UNO_QUERY_THROW ); + xDocRecover->recoverFromFile( + sURL, + lDescriptor.getUnpackedValueOrDefault( utl::MediaDescriptor::PROP_SALVAGEDFILE, OUString() ), + lDescriptor.getAsConstPropertyValueList() + ); + + // No attachResource needed here. By definition (of XDocumentRecovery), the implementation is responsible + // for completely initializing the model, which includes attachResource (or equivalent), if required. + } + + // re-create all the views + ::std::vector< OUString > aViewsToRestore( std::cbegin(rInfo.ViewNames), std::cend(rInfo.ViewNames) ); + // if we don't have views for whatever reason, then create a default-view, at least + if ( aViewsToRestore.empty() ) + aViewsToRestore.emplace_back( ); + + for (auto const& viewToRestore : aViewsToRestore) + { + // create a frame + Reference< XFrame > xTargetFrame = xDesktop->findFrame( SPECIALTARGET_BLANK, 0 ); + aCleanup.emplace_back(xTargetFrame.get() ); + + // create a view to the document + Reference< XController2 > xController; + if ( viewToRestore.getLength() ) + { + xController.set( xModel->createViewController( viewToRestore, Sequence< css::beans::PropertyValue >(), xTargetFrame ), UNO_SET_THROW ); + } + else + { + xController.set( xModel->createDefaultViewController( xTargetFrame ), UNO_SET_THROW ); + } + + // introduce model/view/controller to each other + utl::ConnectFrameControllerModel(xTargetFrame, xController, xModel); + } + + rInfo.Document = xModel.get(); + } + catch(const css::uno::RuntimeException&) + { + throw; + } + catch(const css::uno::Exception&) + { + Any aCaughtException( ::cppu::getCaughtException() ); + + // clean up + for (auto const& component : aCleanup) + { + css::uno::Reference< css::util::XCloseable > xClose(component, css::uno::UNO_QUERY); + if ( xClose.is() ) + xClose->close( true ); + else + component->dispose(); + } + + // re-throw + throw css::lang::WrappedTargetException( + "Recovery of \"" + sURL + "\" failed.", + static_cast< css::frame::XDispatch* >(this), + aCaughtException + ); + } +} + +void AutoRecovery::implts_generateNewTempURL(const OUString& sBackupPath , + utl::MediaDescriptor& /*rMediaDescriptor*/, + AutoRecovery::TDocumentInfo& rInfo ) +{ + // specify URL for saving (which points to a temp file inside backup directory) + // and define a unique name, so we can locate it later. + // This unique name must solve an optimization problem too! + // In case we are asked to save unmodified documents too - and one of them + // is an empty one (because it was new created using e.g. a URL private:factory/...) + // we should not save it really. Then we put the information about such "empty document" + // into the configuration and don't create any recovery file on disk. + // We use the title of the document to make it unique. + OUStringBuffer sUniqueName; + if (!rInfo.OrgURL.isEmpty()) + { + css::uno::Reference< css::util::XURLTransformer > xParser(css::util::URLTransformer::create(m_xContext)); + css::util::URL aURL; + aURL.Complete = rInfo.OrgURL; + xParser->parseStrict(aURL); + sUniqueName.append(aURL.Name); + } + else if (!rInfo.FactoryURL.isEmpty()) + sUniqueName.append("untitled"); + sUniqueName.append("_"); + + // TODO: Must we strip some illegal signs - if we use the title? + + rInfo.NewTempURL = ::utl::CreateTempURL(sUniqueName, true, rInfo.Extension, &sBackupPath, true); +} + +void AutoRecovery::implts_informListener( Job eJob , + const css::frame::FeatureStateEvent& aEvent) +{ + // Helper shares mutex with us -> threadsafe! + ::comphelper::OInterfaceContainerHelper3* pListenerForURL = nullptr; + OUString sJob = AutoRecovery::implst_getJobDescription(eJob); + + // inform listener, which are registered for any URLs(!) + pListenerForURL = m_lListener.getContainer(sJob); + if(pListenerForURL == nullptr) + return; + + ::comphelper::OInterfaceIteratorHelper3 pIt(*pListenerForURL); + while(pIt.hasMoreElements()) + { + try + { + pIt.next()->statusChanged(aEvent); + } + catch(const css::uno::RuntimeException&) + { + pIt.remove(); + } + } +} + +OUString AutoRecovery::implst_getJobDescription(Job eJob) +{ + // describe the current running operation + OUStringBuffer sFeature(256); + sFeature.append(CMD_PROTOCOL); + + // Attention: Because "eJob" is used as a flag field the order of checking these + // flags is important. We must prefer job with higher priorities! + // E.g. EmergencySave has an higher prio then AutoSave ... + // On the other side there exist a well defined order between two different jobs. + // e.g. PrepareEmergencySave must be done before EmergencySave is started of course. + + if ((eJob & Job::PrepareEmergencySave) == Job::PrepareEmergencySave) + sFeature.append(CMD_DO_PREPARE_EMERGENCY_SAVE); + else if ((eJob & Job::EmergencySave) == Job::EmergencySave) + sFeature.append(CMD_DO_EMERGENCY_SAVE); + else if ((eJob & Job::Recovery) == Job::Recovery) + sFeature.append(CMD_DO_RECOVERY); + else if ((eJob & Job::SessionSave) == Job::SessionSave) + sFeature.append(CMD_DO_SESSION_SAVE); + else if ((eJob & Job::SessionQuietQuit) == Job::SessionQuietQuit) + sFeature.append(CMD_DO_SESSION_QUIET_QUIT); + else if ((eJob & Job::SessionRestore) == Job::SessionRestore) + sFeature.append(CMD_DO_SESSION_RESTORE); + else if ((eJob & Job::EntryBackup) == Job::EntryBackup) + sFeature.append(CMD_DO_ENTRY_BACKUP); + else if ((eJob & Job::EntryCleanup) == Job::EntryCleanup) + sFeature.append(CMD_DO_ENTRY_CLEANUP); + else if ((eJob & Job::AutoSave) == Job::AutoSave) + sFeature.append(CMD_DO_AUTO_SAVE); + else if ( eJob != Job::NoJob ) + SAL_INFO("fwk.autorecovery", "AutoRecovery::implst_getJobDescription(): Invalid job identifier detected."); + + return sFeature.makeStringAndClear(); +} + +Job AutoRecovery::implst_classifyJob(const css::util::URL& aURL) +{ + if ( aURL.Protocol == CMD_PROTOCOL ) + { + if ( aURL.Path == CMD_DO_PREPARE_EMERGENCY_SAVE ) + return Job::PrepareEmergencySave; + else if ( aURL.Path == CMD_DO_EMERGENCY_SAVE ) + return Job::EmergencySave; + else if ( aURL.Path == CMD_DO_RECOVERY ) + return Job::Recovery; + else if ( aURL.Path == CMD_DO_ENTRY_BACKUP ) + return Job::EntryBackup; + else if ( aURL.Path == CMD_DO_ENTRY_CLEANUP ) + return Job::EntryCleanup; + else if ( aURL.Path == CMD_DO_SESSION_SAVE ) + return Job::SessionSave; + else if ( aURL.Path == CMD_DO_SESSION_QUIET_QUIT ) + return Job::SessionQuietQuit; + else if ( aURL.Path == CMD_DO_SESSION_RESTORE ) + return Job::SessionRestore; + else if ( aURL.Path == CMD_DO_DISABLE_RECOVERY ) + return Job::DisableAutorecovery; + else if ( aURL.Path == CMD_DO_SET_AUTOSAVE_STATE ) + return Job::SetAutosaveState; + } + + SAL_INFO("fwk.autorecovery", "AutoRecovery::implts_classifyJob(): Invalid URL (protocol)."); + return Job::NoJob; +} + +css::frame::FeatureStateEvent AutoRecovery::implst_createFeatureStateEvent( Job eJob , + const OUString& sEventType, + AutoRecovery::TDocumentInfo const * pInfo ) +{ + css::frame::FeatureStateEvent aEvent; + aEvent.FeatureURL.Complete = AutoRecovery::implst_getJobDescription(eJob); + aEvent.FeatureDescriptor = sEventType; + + if (pInfo && sEventType == OPERATION_UPDATE) + { + // pack rInfo for transport via UNO + ::comphelper::NamedValueCollection aInfo; + aInfo.put( CFG_ENTRY_PROP_ID, pInfo->ID ); + aInfo.put( CFG_ENTRY_PROP_ORIGINALURL, pInfo->OrgURL ); + aInfo.put( CFG_ENTRY_PROP_FACTORYURL, pInfo->FactoryURL ); + aInfo.put( CFG_ENTRY_PROP_TEMPLATEURL, pInfo->TemplateURL ); + aInfo.put( CFG_ENTRY_PROP_TEMPURL, pInfo->OldTempURL.isEmpty() ? pInfo->NewTempURL : pInfo->OldTempURL ); + aInfo.put( CFG_ENTRY_PROP_MODULE, pInfo->AppModule); + aInfo.put( CFG_ENTRY_PROP_TITLE, pInfo->Title); + aInfo.put( CFG_ENTRY_PROP_VIEWNAMES, pInfo->ViewNames); + aInfo.put( CFG_ENTRY_PROP_DOCUMENTSTATE, sal_Int32(pInfo->DocumentState)); + + aEvent.State <<= aInfo.getPropertyValues(); + } + + return aEvent; +} + +void AutoRecovery::implts_resetHandleStates() +{ + CacheLockGuard aCacheLock(this, cppu::WeakComponentImplHelperBase::rBHelper.rMutex, m_nDocCacheLock, LOCK_FOR_CACHE_USE); + + /* SAFE */ { + osl::ResettableMutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex); + + for (auto & info : m_lDocCache) + { + info.DocumentState &= ~DocState::Handled; + info.DocumentState &= ~DocState::Postponed; + + // } /* SAFE */ + g.clear(); + // just update existing records. + implts_flushConfigItem(info, /*bRemoveIt=*/false, /*bAllowAdd=*/false); + g.reset(); + // /* SAFE */ { + } + } /* SAFE */ +} + +void AutoRecovery::implts_prepareEmergencySave() +{ + // Be sure to know all open documents really .-) + implts_verifyCacheAgainstDesktopDocumentList(); + + // hide all docs, so the user can't disturb our emergency save .-) + implts_changeAllDocVisibility(false); +} + +void AutoRecovery::implts_doEmergencySave(const DispatchParams& aParams) +{ + // Write a hint "we crashed" into the configuration, so + // the error report tool is started too in case no recovery + // documents exists and was saved. + + std::shared_ptr batch( + comphelper::ConfigurationChanges::create()); + officecfg::Office::Recovery::RecoveryInfo::Crashed::set(true, batch); + batch->commit(); + + // for all docs, store their current view/names in the configuration + implts_persistAllActiveViewNames(); + + // The called method for saving documents runs + // during normal AutoSave more than once. Because + // it postpone active documents and save it later. + // That is normally done by recalling it from a timer. + // Here we must do it immediately! + // Of course this method returns the right state - + // because it knows, that we are running in EMERGENCY SAVE mode .-) + + bool const bAllowUserIdleLoop = false; // not allowed to change that .-) + AutoRecovery::ETimerType eSuggestedTimer = AutoRecovery::E_DONT_START_TIMER; + do + { + eSuggestedTimer = implts_saveDocs(bAllowUserIdleLoop, true, &aParams); + } + while(eSuggestedTimer == AutoRecovery::E_CALL_ME_BACK); + + // reset the handle state of all + // cache items. Such handle state indicates, that a document + // was already saved during the THIS(!) EmergencySave session. + // Of course following recovery session must be started without + // any "handle" state ... + implts_resetHandleStates(); + + // flush config cached back to disc. + impl_flushALLConfigChanges(); + + // try to make sure next time office will be started user won't be + // notified about any other might be running office instance + // remove ".lock" file from disc ! + AutoRecovery::st_impl_removeLockFile(); +} + +void AutoRecovery::implts_doRecovery(const DispatchParams& aParams) +{ + AutoRecovery::ETimerType eSuggestedTimer = AutoRecovery::E_DONT_START_TIMER; + do + { + eSuggestedTimer = implts_openDocs(aParams); + } + while(eSuggestedTimer == AutoRecovery::E_CALL_ME_BACK); + + // reset the handle state of all + // cache items. Such handle state indicates, that a document + // was already saved during the THIS(!) Recovery session. + // Of course a may be following EmergencySave session must be started without + // any "handle" state... + implts_resetHandleStates(); + + // Reset the configuration hint "we were crashed"! + std::shared_ptr batch( + comphelper::ConfigurationChanges::create()); + officecfg::Office::Recovery::RecoveryInfo::Crashed::set(false, batch); + batch->commit(); +} + +void AutoRecovery::implts_doSessionSave(const DispatchParams& aParams) +{ + SAL_INFO("fwk.autorecovery", "AutoRecovery::implts_doSessionSave()"); + + // Be sure to know all open documents really .-) + implts_verifyCacheAgainstDesktopDocumentList(); + + // for all docs, store their current view/names in the configuration + implts_persistAllActiveViewNames(); + + // The called method for saving documents runs + // during normal AutoSave more than once. Because + // it postpone active documents and save it later. + // That is normally done by recalling it from a timer. + // Here we must do it immediately! + // Of course this method returns the right state - + // because it knows, that we are running in SESSION SAVE mode .-) + + bool const bAllowUserIdleLoop = false; // not allowed to change that .-) + AutoRecovery::ETimerType eSuggestedTimer = AutoRecovery::E_DONT_START_TIMER; + do + { + // do not remove lock files of the documents, it will be done on session quit + eSuggestedTimer = implts_saveDocs(bAllowUserIdleLoop, false, &aParams); + } + while(eSuggestedTimer == AutoRecovery::E_CALL_ME_BACK); + + // reset the handle state of all + // cache items. Such handle state indicates, that a document + // was already saved during the THIS(!) save session. + // Of course following restore session must be started without + // any "handle" state ... + implts_resetHandleStates(); + + // flush config cached back to disc. + impl_flushALLConfigChanges(); +} + +void AutoRecovery::implts_doSessionQuietQuit() +{ + SAL_INFO("fwk.autorecovery", "AutoRecovery::implts_doSessionQuietQuit()"); + + // try to make sure next time office will be started user won't be + // notified about any other might be running office instance + // remove ".lock" file from disc! + // it is done as a first action for session save since Gnome sessions + // do not provide enough time for shutdown, and the dialog looks to be + // confusing for the user + AutoRecovery::st_impl_removeLockFile(); + + // reset all modified documents, so the don't show any UI on closing ... + // and close all documents, so we can shutdown the OS! + implts_prepareSessionShutdown(); + + // Write a hint for "stored session data" into the configuration, so + // the on next startup we know what's happen last time + std::shared_ptr batch( + comphelper::ConfigurationChanges::create()); + officecfg::Office::Recovery::RecoveryInfo::SessionData::set(true, batch); + batch->commit(); + + // flush config cached back to disc. + impl_flushALLConfigChanges(); +} + +void AutoRecovery::implts_doSessionRestore(const DispatchParams& aParams) +{ + SAL_INFO("fwk.autorecovery", "AutoRecovery::implts_doSessionRestore() ..."); + + AutoRecovery::ETimerType eSuggestedTimer = AutoRecovery::E_DONT_START_TIMER; + do + { + eSuggestedTimer = implts_openDocs(aParams); + } + while(eSuggestedTimer == AutoRecovery::E_CALL_ME_BACK); + + // reset the handle state of all + // cache items. Such handle state indicates, that a document + // was already saved during the THIS(!) Restore session. + // Of course a may be following save session must be started without + // any "handle" state ... + implts_resetHandleStates(); + + // make all opened documents visible + implts_changeAllDocVisibility(true); + + // Reset the configuration hint for "session save"! + SAL_INFO("fwk.autorecovery", "... reset config key 'SessionData'"); + std::shared_ptr batch( + comphelper::ConfigurationChanges::create()); + officecfg::Office::Recovery::RecoveryInfo::SessionData::set(false, batch); + batch->commit(); + + SAL_INFO("fwk.autorecovery", "... AutoRecovery::implts_doSessionRestore()"); +} + +void AutoRecovery::implts_backupWorkingEntry(const DispatchParams& aParams) +{ + CacheLockGuard aCacheLock(this, cppu::WeakComponentImplHelperBase::rBHelper.rMutex, m_nDocCacheLock, LOCK_FOR_CACHE_USE); + + for (auto const& info : m_lDocCache) + { + if (info.ID != aParams.m_nWorkingEntryID) + continue; + + OUString sSourceURL; + // Prefer temp file. It contains the changes against the original document! + if (!info.OldTempURL.isEmpty()) + sSourceURL = info.OldTempURL; + else if (!info.NewTempURL.isEmpty()) + sSourceURL = info.NewTempURL; + else if (!info.OrgURL.isEmpty()) + sSourceURL = info.OrgURL; + else + continue; // nothing real to save! An unmodified but new created document. + + INetURLObject aParser(sSourceURL); + // AutoRecovery::EFailureSafeResult eResult = + implts_copyFile(sSourceURL, aParams.m_sSavePath, aParser.getName()); + + // TODO: Check eResult and react for errors (InteractionHandler!?) + // Currently we ignore it ... + // DON'T UPDATE THE CACHE OR REMOVE ANY TEMP. FILES FROM DISK. + // That has to be forced from outside explicitly. + // See implts_cleanUpWorkingEntry() for further details. + } +} + +void AutoRecovery::implts_cleanUpWorkingEntry(const DispatchParams& aParams) +{ + CacheLockGuard aCacheLock(this, cppu::WeakComponentImplHelperBase::rBHelper.rMutex, m_nDocCacheLock, LOCK_FOR_CACHE_ADD_REMOVE); + + AutoRecovery::TDocumentList::iterator pIt = std::find_if(m_lDocCache.begin(), m_lDocCache.end(), + [&aParams](const AutoRecovery::TDocumentInfo& rInfo) { return rInfo.ID == aParams.m_nWorkingEntryID; }); + if (pIt != m_lDocCache.end()) + { + AutoRecovery::TDocumentInfo& rInfo = *pIt; + implts_flushConfigItem(rInfo, true); // sal_True => remove it from xml config! + + m_lDocCache.erase(pIt); + } +} + +AutoRecovery::EFailureSafeResult AutoRecovery::implts_copyFile(const OUString& sSource , + const OUString& sTargetPath, + const OUString& sTargetName) +{ + // create content for the parent folder and call transfer on that content with the source content + // and the destination file name as parameters + + css::uno::Reference< css::ucb::XCommandEnvironment > xEnvironment; + + ::ucbhelper::Content aSourceContent; + ::ucbhelper::Content aTargetContent; + + try + { + aTargetContent = ::ucbhelper::Content(sTargetPath, xEnvironment, m_xContext); + } + catch(const css::uno::Exception&) + { + return AutoRecovery::E_WRONG_TARGET_PATH; + } + + sal_Int32 nNameClash; + nNameClash = css::ucb::NameClash::RENAME; + + try + { + bool bSuccess = ::ucbhelper::Content::create(sSource, xEnvironment, m_xContext, aSourceContent); + if (!bSuccess) + return AutoRecovery::E_ORIGINAL_FILE_MISSING; + aTargetContent.transferContent(aSourceContent, ::ucbhelper::InsertOperation::Copy, sTargetName, nNameClash); + } + catch(const css::uno::Exception&) + { + return AutoRecovery::E_ORIGINAL_FILE_MISSING; + } + + return AutoRecovery::E_COPIED; +} + +sal_Bool SAL_CALL AutoRecovery::convertFastPropertyValue( css::uno::Any& /*aConvertedValue*/, + css::uno::Any& /*aOldValue*/ , + sal_Int32 /*nHandle*/ , + const css::uno::Any& /*aValue*/ ) +{ + // not needed currently + return false; +} + +void SAL_CALL AutoRecovery::setFastPropertyValue_NoBroadcast( sal_Int32 /*nHandle*/, + const css::uno::Any& /*aValue*/ ) +{ + // not needed currently +} + +void SAL_CALL AutoRecovery::getFastPropertyValue(css::uno::Any& aValue , + sal_Int32 nHandle) const +{ + switch(nHandle) + { + case AUTORECOVERY_PROPHANDLE_EXISTS_RECOVERYDATA : + { + bool bSessionData = officecfg::Office::Recovery::RecoveryInfo::SessionData::get(); + bool bRecoveryData = !m_lDocCache.empty(); + + // exists session data ... => then we can't say, that these + // data are valid for recovery. So we have to return sal_False then! + if (bSessionData) + bRecoveryData = false; + + aValue <<= bRecoveryData; + } + break; + + case AUTORECOVERY_PROPHANDLE_CRASHED : + aValue <<= officecfg::Office::Recovery::RecoveryInfo::Crashed::get(); + break; + + case AUTORECOVERY_PROPHANDLE_EXISTS_SESSIONDATA : + aValue <<= officecfg::Office::Recovery::RecoveryInfo::SessionData::get(); + break; + } +} + +css::uno::Sequence< css::beans::Property > impl_getStaticPropertyDescriptor() +{ + return + { + css::beans::Property( AUTORECOVERY_PROPNAME_CRASHED , AUTORECOVERY_PROPHANDLE_CRASHED , cppu::UnoType::get() , css::beans::PropertyAttribute::TRANSIENT | css::beans::PropertyAttribute::READONLY ), + css::beans::Property( AUTORECOVERY_PROPNAME_EXISTS_RECOVERYDATA, AUTORECOVERY_PROPHANDLE_EXISTS_RECOVERYDATA, cppu::UnoType::get() , css::beans::PropertyAttribute::TRANSIENT | css::beans::PropertyAttribute::READONLY ), + css::beans::Property( AUTORECOVERY_PROPNAME_EXISTS_SESSIONDATA , AUTORECOVERY_PROPHANDLE_EXISTS_SESSIONDATA , cppu::UnoType::get() , css::beans::PropertyAttribute::TRANSIENT | css::beans::PropertyAttribute::READONLY ), + }; +} + +::cppu::IPropertyArrayHelper& SAL_CALL AutoRecovery::getInfoHelper() +{ + static ::cppu::OPropertyArrayHelper ourInfoHelper(impl_getStaticPropertyDescriptor(), true); + + return ourInfoHelper; +} + +css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL AutoRecovery::getPropertySetInfo() +{ + static css::uno::Reference< css::beans::XPropertySetInfo > xInfo( + ::cppu::OPropertySetHelper::createPropertySetInfo(getInfoHelper())); + + return xInfo; +} + +void AutoRecovery::implts_verifyCacheAgainstDesktopDocumentList() +{ + SAL_INFO("fwk.autorecovery", "AutoRecovery::implts_verifyCacheAgainstDesktopDocumentList() ..."); + try + { + css::uno::Reference< css::frame::XDesktop2 > xDesktop = css::frame::Desktop::create(m_xContext); + + css::uno::Reference< css::container::XIndexAccess > xContainer( + xDesktop->getFrames(), + css::uno::UNO_QUERY_THROW); + + sal_Int32 i = 0; + sal_Int32 c = xContainer->getCount(); + + for (i=0; i xFrame; + try + { + xContainer->getByIndex(i) >>= xFrame; + if (!xFrame.is()) + continue; + } + // can happen in multithreaded environments, that frames was removed from the container during this loop runs! + // Ignore it. + catch(const css::lang::IndexOutOfBoundsException&) + { + continue; + } + + // We are interested on visible documents only. + // Note: It's n optional interface .-( + css::uno::Reference< css::awt::XWindow2 > xVisibleCheck( + xFrame->getContainerWindow(), + css::uno::UNO_QUERY); + if ( + (!xVisibleCheck.is() ) || + (!xVisibleCheck->isVisible()) + ) + { + continue; + } + + // extract the model from the frame. + // Ignore "view only" frames, which does not have a model. + css::uno::Reference< css::frame::XController > xController; + css::uno::Reference< css::frame::XModel3 > xModel; + + xController = xFrame->getController(); + if (xController.is()) + xModel.set( xController->getModel(), UNO_QUERY_THROW ); + if (!xModel.is()) + continue; + + // insert model into cache ... + // If the model is already well known inside cache + // it's information set will be updated by asking the + // model again for its new states. + implts_registerDocument(xModel); + } + } + catch(const css::uno::RuntimeException&) + { + throw; + } + catch(const css::uno::Exception&) + { + } + + SAL_INFO("fwk.autorecovery", "... AutoRecovery::implts_verifyCacheAgainstDesktopDocumentList()"); +} + +bool AutoRecovery::impl_enoughDiscSpace(sal_Int32 nRequiredSpace) +{ +#ifdef SIMULATE_FULL_DISC + return sal_False; +#else // SIMULATE_FULL_DISC + // In case an error occurs and we are not able to retrieve the needed information + // it's better to "disable" the feature ShowErrorOnFullDisc ! + // Otherwise we start a confusing process of error handling ... + + sal_uInt64 nFreeSpace = SAL_MAX_UINT64; + + OUString sBackupPath(SvtPathOptions().GetBackupPath()); + ::osl::VolumeInfo aInfo (osl_VolumeInfo_Mask_FreeSpace); + ::osl::FileBase::RC aRC = ::osl::Directory::getVolumeInfo(sBackupPath, aInfo); + + if ( + (aInfo.isValid(osl_VolumeInfo_Mask_FreeSpace)) && + (aRC == ::osl::FileBase::E_None ) + ) + { + nFreeSpace = aInfo.getFreeSpace(); + } + + sal_uInt64 nFreeMB = nFreeSpace/1048576; + return (nFreeMB >= o3tl::make_unsigned(nRequiredSpace)); +#endif // SIMULATE_FULL_DISC +} + +void AutoRecovery::impl_showFullDiscError() +{ + OUString sBtn(FwkResId(STR_FULL_DISC_RETRY_BUTTON)); + OUString sMsg(FwkResId(STR_FULL_DISC_MSG)); + + OUString sBackupURL(SvtPathOptions().GetBackupPath()); + INetURLObject aConverter(sBackupURL); + sal_Unicode aDelimiter; + OUString sBackupPath = aConverter.getFSysPath(FSysStyle::Detect, &aDelimiter); + if (sBackupPath.getLength() < 1) + sBackupPath = sBackupURL; + + std::unique_ptr xBox(Application::CreateMessageDialog(nullptr, + VclMessageType::Error, VclButtonsType::NONE, + sMsg.replaceAll("%PATH", sBackupPath))); + xBox->add_button(sBtn, RET_OK); + xBox->run(); +} + +void AutoRecovery::impl_establishProgress(const AutoRecovery::TDocumentInfo& rInfo , + utl::MediaDescriptor& rArgs , + const css::uno::Reference< css::frame::XFrame >& xNewFrame) +{ + // external well known frame must be preferred (because it was created by ourself + // for loading documents into this frame)! + // But if no frame exists... we can try to locate it using any frame bound to the provided + // document. Of course we must live without any frame in case the document does not exists at this + // point. But this state should not occur. In such case xNewFrame should be valid ... hopefully .-) + css::uno::Reference< css::frame::XFrame > xFrame = xNewFrame; + if ( + (!xFrame.is() ) && + (rInfo.Document.is()) + ) + { + css::uno::Reference< css::frame::XController > xController = rInfo.Document->getCurrentController(); + if (xController.is()) + xFrame = xController->getFrame(); + } + + // Any outside progress must be used ... + // Only if there is no progress, we can create our own one. + css::uno::Reference< css::task::XStatusIndicator > xInternalProgress; + css::uno::Reference< css::task::XStatusIndicator > xExternalProgress = rArgs.getUnpackedValueOrDefault( + utl::MediaDescriptor::PROP_STATUSINDICATOR, + css::uno::Reference< css::task::XStatusIndicator >() ); + + // Normally a progress is set from outside (e.g. by the CrashSave/Recovery dialog, which uses our dispatch API). + // But for a normal auto save we don't have such "external progress"... because this function is triggered by our own timer then. + // In such case we must create our own progress ! + if ( + (! xExternalProgress.is()) && + (xFrame.is() ) + ) + { + css::uno::Reference< css::task::XStatusIndicatorFactory > xProgressFactory(xFrame, css::uno::UNO_QUERY); + if (xProgressFactory.is()) + xInternalProgress = xProgressFactory->createStatusIndicator(); + } + + // HACK + // An external provided progress (most given by the CrashSave/Recovery dialog) + // must be preferred. But we know that some application filters query its own progress instance + // at the frame method Frame::createStatusIndicator(). + // So we use a two step mechanism: + // 1) we set the progress inside the MediaDescriptor, which will be provided to the filter + // 2) and we set a special Frame property, which overwrites the normal behaviour of Frame::createStatusIndicator .-) + // But we suppress 2) in case we uses an internal progress. Because then it doesn't matter + // if our applications make it wrong. In such case the internal progress resists at the same frame + // and there is no need to forward progress activities to e.g. an outside dialog .-) + if ( + (xExternalProgress.is()) && + (xFrame.is() ) + ) + { + css::uno::Reference< css::beans::XPropertySet > xFrameProps(xFrame, css::uno::UNO_QUERY); + if (xFrameProps.is()) + xFrameProps->setPropertyValue(FRAME_PROPNAME_ASCII_INDICATORINTERCEPTION, css::uno::Any(xExternalProgress)); + } + + // But inside the MediaDescriptor we must set our own create progress ... + // in case there is not already another progress set. + rArgs.createItemIfMissing(utl::MediaDescriptor::PROP_STATUSINDICATOR, xInternalProgress); +} + +void AutoRecovery::impl_forgetProgress(const AutoRecovery::TDocumentInfo& rInfo , + utl::MediaDescriptor& rArgs , + const css::uno::Reference< css::frame::XFrame >& xNewFrame) +{ + // external well known frame must be preferred (because it was created by ourself + // for loading documents into this frame)! + // But if no frame exists... we can try to locate it using any frame bound to the provided + // document. Of course we must live without any frame in case the document does not exists at this + // point. But this state should not occur. In such case xNewFrame should be valid ... hopefully .-) + css::uno::Reference< css::frame::XFrame > xFrame = xNewFrame; + if ( + (!xFrame.is() ) && + (rInfo.Document.is()) + ) + { + css::uno::Reference< css::frame::XController > xController = rInfo.Document->getCurrentController(); + if (xController.is()) + xFrame = xController->getFrame(); + } + + // stop progress interception on corresponding frame. + css::uno::Reference< css::beans::XPropertySet > xFrameProps(xFrame, css::uno::UNO_QUERY); + if (xFrameProps.is()) + xFrameProps->setPropertyValue(FRAME_PROPNAME_ASCII_INDICATORINTERCEPTION, css::uno::Any(css::uno::Reference< css::task::XStatusIndicator >())); + + // forget progress inside list of arguments. + utl::MediaDescriptor::iterator pArg = rArgs.find(utl::MediaDescriptor::PROP_STATUSINDICATOR); + if (pArg != rArgs.end()) + { + rArgs.erase(pArg); + pArg = rArgs.end(); + } +} + +void AutoRecovery::impl_flushALLConfigChanges() +{ + try + { + // SOLAR SAFE -> + SolarMutexGuard aGuard; + ::utl::ConfigManager::storeConfigItems(); + } + catch(const css::uno::Exception&) + { + } +} + +void AutoRecovery::st_impl_removeFile(const OUString& sURL) +{ + if ( sURL.isEmpty()) + return; + + try + { + ::ucbhelper::Content aContent(sURL, css::uno::Reference< css::ucb::XCommandEnvironment >(), m_xContext); + aContent.executeCommand("delete", css::uno::Any(true)); + } + catch(const css::uno::Exception&) + { + } +} + +void AutoRecovery::st_impl_removeLockFile() +{ + try + { + OUString sUserURL; + ::utl::Bootstrap::locateUserInstallation( sUserURL ); + + OUString sLockURL = sUserURL + "/.lock"; + AutoRecovery::st_impl_removeFile(sLockURL); + } + catch(const css::uno::Exception&) + { + } +} + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_framework_AutoRecovery_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence const &) +{ + rtl::Reference xAutoRecovery = new AutoRecovery(context); + // 2nd phase initialization needed + xAutoRecovery->initListeners(); + + return cppu::acquire(xAutoRecovery.get()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/services/desktop.cxx b/framework/source/services/desktop.cxx new file mode 100644 index 0000000000..237d35afc6 --- /dev/null +++ b/framework/source/services/desktop.cxx @@ -0,0 +1,1772 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace framework{ + +namespace { + +enum PropHandle { + ActiveFrame, DispatchRecorderSupplier, IsPlugged, SuspendQuickstartVeto, + Title }; + +} + +OUString SAL_CALL Desktop::getImplementationName() +{ + return "com.sun.star.comp.framework.Desktop"; +} + +sal_Bool SAL_CALL Desktop::supportsService(OUString const & ServiceName) +{ + return cppu::supportsService(this, ServiceName); +} + +css::uno::Sequence SAL_CALL Desktop::getSupportedServiceNames() +{ + return { "com.sun.star.frame.Desktop" }; +} + +void Desktop::constructorInit() +{ + // Initialize a new XFrames-helper-object to handle XIndexAccess and XElementAccess. + // We hold member as reference ... not as pointer too! + // Attention: We share our frame container with this helper. Container is threadsafe himself ... So I think we can do that. + // But look on dispose() for right order of deinitialization. + m_xFramesHelper = new OFrames( this, &m_aChildTaskContainer ); + + // Initialize a new dispatchhelper-object to handle dispatches. + // We use these helper as slave for our interceptor helper ... not directly! + // But he is event listener on THIS instance! + rtl::Reference xDispatchProvider = new DispatchProvider( m_xContext, this ); + + // Initialize a new interception helper object to handle dispatches and implement an interceptor mechanism. + // Set created dispatch provider as slowest slave of it. + // Hold interception helper by reference only - not by pointer! + // So it's easier to destroy it. + m_xDispatchHelper = new InterceptionHelper( this, xDispatchProvider ); + + OUString sUntitledPrefix = FwkResId(STR_UNTITLED_DOCUMENT) + " "; + + rtl::Reference<::comphelper::NumberedCollection> pNumbers = new ::comphelper::NumberedCollection (); + m_xTitleNumberGenerator = pNumbers; + pNumbers->setOwner ( static_cast< ::cppu::OWeakObject* >(this) ); + pNumbers->setUntitledPrefix ( sUntitledPrefix ); + + // Safe impossible cases + // We can't work without this helper! + SAL_WARN_IF( !m_xFramesHelper.is(), "fwk.desktop", "Desktop::Desktop(): Frames helper is not valid. XFrames, XIndexAccess and XElementAccess are not supported!"); + SAL_WARN_IF( !m_xDispatchHelper.is(), "fwk.desktop", "Desktop::Desktop(): Dispatch helper is not valid. XDispatch will not work correctly!" ); + + // Enable object for real working! + // Otherwise all calls will be rejected ... + m_aTransactionManager.setWorkingMode( E_WORK ); +} + +/*-************************************************************************************************************ + @short standard constructor to create instance by factory + @descr This constructor initialize a new instance of this class by valid factory, + and will be set valid values on his member and baseclasses. + + @attention a) Don't use your own reference during a UNO-Service-ctor! There is no guarantee, that you + will get over this. (e.g. using of your reference as parameter to initialize some member) + Do such things in DEFINE_INIT_SERVICE() method, which is called automatically after your ctor!!! + b) Baseclass OBroadcastHelper is a typedef in namespace cppu! + The microsoft compiler has some problems to handle it right BY using namespace explicitly ::cppu::OBroadcastHelper. + If we write it without a namespace or expand the typedef to OBroadcastHelperVar<...> -> it will be OK!? + I don't know why! (other compiler not tested .. but it works!) + + @seealso method DEFINE_INIT_SERVICE() + + @param "xFactory" is the multi service manager, which create this instance. + The value must be different from NULL! + @onerror We throw an ASSERT in debug version or do nothing in release version. +*//*-*************************************************************************************************************/ +Desktop::Desktop( css::uno::Reference< css::uno::XComponentContext > xContext ) + : Desktop_BASE ( m_aMutex ) + , cppu::OPropertySetHelper( cppu::WeakComponentImplHelperBase::rBHelper ) + // Init member + , m_bIsTerminated(false) + , m_bIsShutdown(false) // see dispose() for further information! + , m_bSession ( false ) + , m_xContext (std::move( xContext )) + , m_aListenerContainer ( m_aMutex ) + , m_eLoadState ( E_NOTSET ) + , m_bSuspendQuickstartVeto( false ) +{ +} + +/*-************************************************************************************************************ + @short standard destructor + @descr This one do NOTHING! Use dispose() instead of this. + + @seealso method dispose() +*//*-*************************************************************************************************************/ +Desktop::~Desktop() +{ + SAL_WARN_IF(!m_bIsShutdown, "fwk.desktop", "Desktop not terminated before being destructed"); + SAL_WARN_IF( m_aTransactionManager.getWorkingMode()!=E_CLOSE, "fwk.desktop", "Desktop::~Desktop(): Who forgot to dispose this service?" ); +} + +css::uno::Any SAL_CALL Desktop::queryInterface( const css::uno::Type& _rType ) +{ + css::uno::Any aRet = Desktop_BASE::queryInterface( _rType ); + if ( !aRet.hasValue() ) + aRet = OPropertySetHelper::queryInterface( _rType ); + return aRet; +} + +css::uno::Sequence< css::uno::Type > SAL_CALL Desktop::getTypes( ) +{ + return comphelper::concatSequences( + Desktop_BASE::getTypes(), + ::cppu::OPropertySetHelper::getTypes() + ); +} + +sal_Bool SAL_CALL Desktop::terminate() +{ + TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS ); + SolarMutexResettableGuard aGuard; + + if (m_bIsTerminated) + return true; + + css::uno::Reference< css::frame::XTerminateListener > xPipeTerminator = m_xPipeTerminator; + css::uno::Reference< css::frame::XTerminateListener > xQuickLauncher = m_xQuickLauncher; + css::uno::Reference< css::frame::XTerminateListener > xSWThreadManager = m_xSWThreadManager; + css::uno::Reference< css::frame::XTerminateListener > xSfxTerminator = m_xSfxTerminator; + + css::lang::EventObject aEvent ( static_cast< ::cppu::OWeakObject* >(this) ); + bool bAskQuickStart = !m_bSuspendQuickstartVeto; + const bool bRestartableMainLoop = comphelper::LibreOfficeKit::isActive(); + aGuard.clear(); + + // Allow using of any UI ... because Desktop.terminate() was designed as UI functionality in the past. + + // Ask normal terminate listener. They could veto terminating the process. + Desktop::TTerminateListenerList lCalledTerminationListener; + if (!impl_sendQueryTerminationEvent(lCalledTerminationListener)) + { + impl_sendCancelTerminationEvent(lCalledTerminationListener); + return false; + } + + // try to close all open frames + if (!impl_closeFrames(!bRestartableMainLoop)) + { + impl_sendCancelTerminationEvent(lCalledTerminationListener); + return false; + } + + // Normal listener had no problem ... + // all frames was closed ... + // now it's time to ask our specialized listener. + // They are handled these way because they wish to hinder the office on termination + // but they wish also closing of all frames. + + // Note further: + // We shouldn't ask quicklauncher in case it was allowed from outside only. + // This is special trick to "ignore existing quick starter" for debug purposes. + + // Attention: + // Order of called listener is important! + // Some of them are harmless,-) + // but some can be dangerous. E.g. it would be dangerous if we close our pipe + // and don't terminate in real because another listener throws a veto exception .-) + + try + { + if( bAskQuickStart && xQuickLauncher.is() ) + { + xQuickLauncher->queryTermination( aEvent ); + lCalledTerminationListener.push_back( xQuickLauncher ); + } + + if ( xSWThreadManager.is() ) + { + xSWThreadManager->queryTermination( aEvent ); + lCalledTerminationListener.push_back( xSWThreadManager ); + } + + if ( xPipeTerminator.is() ) + { + xPipeTerminator->queryTermination( aEvent ); + lCalledTerminationListener.push_back( xPipeTerminator ); + } + + if ( xSfxTerminator.is() ) + { + xSfxTerminator->queryTermination( aEvent ); + lCalledTerminationListener.push_back( xSfxTerminator ); + } + } + catch(const css::frame::TerminationVetoException&) + { + impl_sendCancelTerminationEvent(lCalledTerminationListener); + return false; + } + + aGuard.reset(); + if (m_bIsTerminated) + return true; + m_bIsTerminated = true; + + if (!bRestartableMainLoop) + { + CrashReporter::addKeyValue("ShutDown", OUString::boolean(true), CrashReporter::Write); + + // The clipboard listener needs to be the first. It can create copies of the + // existing document which needs basically all the available infrastructure. + impl_sendTerminateToClipboard(); + { + SolarMutexReleaser aReleaser; + impl_sendNotifyTerminationEvent(); + } + Scheduler::ProcessEventsToIdle(); + + if( bAskQuickStart && xQuickLauncher.is() ) + xQuickLauncher->notifyTermination( aEvent ); + + if ( xSWThreadManager.is() ) + xSWThreadManager->notifyTermination( aEvent ); + + if ( xPipeTerminator.is() ) + xPipeTerminator->notifyTermination( aEvent ); + + // further termination is postponed to shutdown, if LO already runs the main loop + if (!Application::IsInExecute()) + shutdown(); + } + else + m_bIsShutdown = true; + +#ifndef IOS // or ANDROID? + aGuard.clear(); + // In the iOS app, posting the ImplQuitMsg user event will be too late, it will not be handled during the + // lifetime of the current document, but handled for the next document opened, which thus will break horribly. + Application::Quit(); +#endif + + return true; +} + +void Desktop::shutdown() +{ + TransactionGuard aTransaction(m_aTransactionManager, E_HARDEXCEPTIONS); + SolarMutexGuard aGuard; + + if (m_bIsShutdown) + return; + m_bIsShutdown = true; + + css::uno::Reference xSfxTerminator = m_xSfxTerminator; + css::lang::EventObject aEvent(static_cast<::cppu::OWeakObject* >(this)); + + // we need a copy here as the notifyTermination call might cause a removeTerminateListener call + std::vector< css::uno::Reference > xComponentDllListeners; + xComponentDllListeners.swap(m_xComponentDllListeners); + for (auto& xListener : xComponentDllListeners) + xListener->notifyTermination(aEvent); + xComponentDllListeners.clear(); + + // Must be really the last listener to be called. + // Because it shuts down the whole process asynchronous! + if (xSfxTerminator.is()) + xSfxTerminator->notifyTermination(aEvent); +} + +namespace +{ + class QuickstartSuppressor + { + Desktop* const m_pDesktop; + css::uno::Reference< css::frame::XTerminateListener > m_xQuickLauncher; + public: + QuickstartSuppressor(Desktop* const pDesktop, css::uno::Reference< css::frame::XTerminateListener > xQuickLauncher) + : m_pDesktop(pDesktop) + , m_xQuickLauncher(std::move(xQuickLauncher)) + { + SAL_INFO("fwk.desktop", "temporary removing Quickstarter"); + if(m_xQuickLauncher.is()) + m_pDesktop->removeTerminateListener(m_xQuickLauncher); + } + ~QuickstartSuppressor() + { + SAL_INFO("fwk.desktop", "readding Quickstarter"); + if(m_xQuickLauncher.is()) + m_pDesktop->addTerminateListener(m_xQuickLauncher); + } + }; +} + +bool Desktop::terminateQuickstarterToo() +{ + QuickstartSuppressor aQuickstartSuppressor(this, m_xQuickLauncher); + m_bSession = true; + return terminate(); +} + +void SAL_CALL Desktop::addTerminateListener( const css::uno::Reference< css::frame::XTerminateListener >& xListener ) +{ + TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS ); + + css::uno::Reference< css::lang::XServiceInfo > xInfo( xListener, css::uno::UNO_QUERY ); + if ( xInfo.is() ) + { + OUString sImplementationName = xInfo->getImplementationName(); + + SolarMutexGuard g; + + if( sImplementationName == "com.sun.star.comp.sfx2.SfxTerminateListener" ) + { + m_xSfxTerminator = xListener; + return; + } + if( sImplementationName == "com.sun.star.comp.RequestHandlerController" ) + { + m_xPipeTerminator = xListener; + return; + } + if( sImplementationName == "com.sun.star.comp.desktop.QuickstartWrapper" ) + { + m_xQuickLauncher = xListener; + return; + } + if( sImplementationName == "com.sun.star.util.comp.FinalThreadManager" ) + { + m_xSWThreadManager = xListener; + return; + } + else if ( sImplementationName == "com.sun.star.comp.ComponentDLLListener" ) + { + m_xComponentDllListeners.push_back(xListener); + return; + } + } + + // No lock required... container is threadsafe by itself. + m_aListenerContainer.addInterface( cppu::UnoType::get(), xListener ); +} + +void SAL_CALL Desktop::removeTerminateListener( const css::uno::Reference< css::frame::XTerminateListener >& xListener ) +{ + TransactionGuard aTransaction( m_aTransactionManager, E_SOFTEXCEPTIONS ); + + css::uno::Reference< css::lang::XServiceInfo > xInfo( xListener, css::uno::UNO_QUERY ); + if ( xInfo.is() ) + { + OUString sImplementationName = xInfo->getImplementationName(); + + SolarMutexGuard g; + + if( sImplementationName == "com.sun.star.comp.sfx2.SfxTerminateListener" ) + { + m_xSfxTerminator.clear(); + return; + } + + if( sImplementationName == "com.sun.star.comp.RequestHandlerController" ) + { + m_xPipeTerminator.clear(); + return; + } + + if( sImplementationName == "com.sun.star.comp.desktop.QuickstartWrapper" ) + { + m_xQuickLauncher.clear(); + return; + } + + if( sImplementationName == "com.sun.star.util.comp.FinalThreadManager" ) + { + m_xSWThreadManager.clear(); + return; + } + else if (sImplementationName == "com.sun.star.comp.ComponentDLLListener") + { + std::erase(m_xComponentDllListeners, xListener); + return; + } + } + + // No lock required ... container is threadsafe by itself. + m_aListenerContainer.removeInterface( cppu::UnoType::get(), xListener ); +} + +/*-************************************************************************************************************ + @interface XDesktop + @short get access to create enumerations of all current components + @descr You will be the owner of the returned object and must delete it if you don't use it again. + + @seealso class TasksAccess + @seealso class TasksEnumeration + @return A reference to an XEnumerationAccess-object. + + @onerror We return a null-reference. + @threadsafe yes +*//*-*************************************************************************************************************/ +css::uno::Reference< css::container::XEnumerationAccess > SAL_CALL Desktop::getComponents() +{ + /* UNSAFE AREA --------------------------------------------------------------------------------------------- */ + // Register transaction and reject wrong calls. + TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS ); + + // We use a helper class OComponentAccess to have access on all child components. + // Create it on demand and return it as a reference. + return new OComponentAccess( this ); +} + +/*-************************************************************************************************************ + @interface XDesktop + @short return the current active component + @descr The most current component is the window, model or the controller of the current active frame. + + @seealso method getCurrentFrame() + @seealso method impl_getFrameComponent() + @return A reference to the component. + + @onerror We return a null-reference. + @threadsafe yes +*//*-*************************************************************************************************************/ +css::uno::Reference< css::lang::XComponent > SAL_CALL Desktop::getCurrentComponent() +{ + /* UNSAFE AREA --------------------------------------------------------------------------------------------- */ + // Register transaction and reject wrong calls. + TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS ); + + // Set return value if method failed. + css::uno::Reference< css::lang::XComponent > xComponent; + + // Get reference to current frame ... + // ... get component of this frame ... (It can be the window, the model or the controller.) + // ... and return the result. + css::uno::Reference< css::frame::XFrame > xCurrentFrame = getCurrentFrame(); + if( xCurrentFrame.is() ) + { + xComponent = impl_getFrameComponent( xCurrentFrame ); + } + return xComponent; +} + +/*-************************************************************************************************************ + @interface XDesktop + @short return the current active frame in hierarchy + @descr There can be more than one different active paths in our frame hierarchy. But only one of them + could be the most active frame (normal he has the focus). + Don't mix it with getActiveFrame()! That will return our current active frame, which must be + a direct child of us and should be a part(!) of an active path. + + @seealso method getActiveFrame() + @return A valid reference, if there is an active frame. + A null reference , otherwise. + + @onerror We return a null reference. + @threadsafe yes +*//*-*************************************************************************************************************/ +css::uno::Reference< css::frame::XFrame > SAL_CALL Desktop::getCurrentFrame() +{ + /* UNSAFE AREA --------------------------------------------------------------------------------------------- */ + // Register transaction and reject wrong calls. + TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS ); + + // Start search with our direct active frame (if it exist!). + // Search on his children for other active frames too. + // Stop if no one could be found and return last of found ones. + css::uno::Reference< css::frame::XFramesSupplier > xLast( getActiveFrame(), css::uno::UNO_QUERY ); + if( xLast.is() ) + { + css::uno::Reference< css::frame::XFramesSupplier > xNext( xLast->getActiveFrame(), css::uno::UNO_QUERY ); + while( xNext.is() ) + { + xLast = xNext; + xNext.set( xNext->getActiveFrame(), css::uno::UNO_QUERY ); + } + } + return xLast; +} + +/*-************************************************************************************************************ + @interface XComponentLoader + @short try to load given URL into a task + @descr You can give us some information about the content, which you will load into a frame. + We search or create this target for you, make a type detection of given URL and try to load it. + As result of this operation we return the new created component or nothing, if loading failed. + @param "sURL" , URL, which represent the content + @param "sTargetFrameName" , name of target frame or special value like "_self", "_blank" ... + @param "nSearchFlags" , optional arguments for frame search, if target isn't a special one + @param "lArguments" , optional arguments for loading + @return A valid component reference, if loading was successful. + A null reference otherwise. + + @onerror We return a null reference. + @threadsafe yes +*//*-*************************************************************************************************************/ +css::uno::Reference< css::lang::XComponent > SAL_CALL Desktop::loadComponentFromURL( const OUString& sURL , + const OUString& sTargetFrameName, + sal_Int32 nSearchFlags , + const css::uno::Sequence< css::beans::PropertyValue >& lArguments ) +{ + /* UNSAFE AREA --------------------------------------------------------------------------------------------- */ + // Register transaction and reject wrong calls. + TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS ); + SAL_INFO( "fwk.desktop", "loadComponentFromURL" ); + + css::uno::Reference< css::frame::XComponentLoader > xThis(this); + + utl::MediaDescriptor aDescriptor(lArguments); + 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 + // another thread, leading to an std::abort() at the end. + SolarMutexGuard g; + + return vcl::solarthread::syncExecute(std::bind(&LoadEnv::loadComponentFromURL, xThis, + m_xContext, sURL, sTargetFrameName, + nSearchFlags, lArguments)); + } + else + { + return LoadEnv::loadComponentFromURL(xThis, m_xContext, sURL, sTargetFrameName, + nSearchFlags, lArguments); + } +} + +/*-************************************************************************************************************ + @interface XTasksSupplier + @short get access to create enumerations of our taskchildren + @descr Direct children of desktop are tasks every time. + Call these method to could create enumerations of it. + +But; Don't forget - you will be the owner of returned object and must release it! + We use a helper class to implement the access interface. They hold a weakreference to us. + It can be, that the desktop is dead - but not your tasksaccess-object! Then they will do nothing! + You can't create enumerations then. + + @attention Normally we don't need any lock here. We don't work on internal member! + + @seealso class TasksAccess + @return A reference to an accessobject, which can create enumerations of our childtasks. + + @onerror A null reference is returned. + @threadsafe yes +*//*-*************************************************************************************************************/ +css::uno::Reference< css::container::XEnumerationAccess > SAL_CALL Desktop::getTasks() +{ + SAL_INFO("fwk.desktop", "Desktop::getTasks(): Use of obsolete interface XTaskSupplier"); + return nullptr; +} + +/*-************************************************************************************************************ + @interface XTasksSupplier + @short return current active task of our direct children + @descr Desktop children are tasks only ! If we have an active path from desktop + as top to any frame on bottom, we must have an active direct child. His reference is returned here. + + @attention a) Do not confuse it with getCurrentFrame()! The current frame don't must one of our direct children. + It can be every frame in subtree and must have the focus (Is the last one of an active path!). + b) We don't need any lock here. Our container is threadsafe himself and live, if we live! + + @seealso method getCurrentFrame() + @return A reference to our current active taskchild. + + @onerror A null reference is returned. + @threadsafe yes +*//*-*************************************************************************************************************/ +css::uno::Reference< css::frame::XTask > SAL_CALL Desktop::getActiveTask() +{ + SAL_INFO("fwk.desktop", "Desktop::getActiveTask(): Use of obsolete interface XTaskSupplier"); + return nullptr; +} + +/*-************************************************************************************************************ + @interface XDispatchProvider + @short search a dispatcher for given URL + @descr We use a helper implementation (class DispatchProvider) to do so. + So we don't must implement this algorithm twice! + + @attention We don't need any lock here. Our helper is threadsafe himself and live, if we live! + + @seealso class DispatchProvider + + @param "aURL" , URL to dispatch + @param "sTargetFrameName" , name of target frame, who should dispatch these URL + @param "nSearchFlags" , flags to regulate the search + @param "lQueries" , list of queryDispatch() calls! + @return A reference or list of founded dispatch objects for these URL. + + @onerror A null reference is returned. + @threadsafe yes +*//*-*************************************************************************************************************/ +css::uno::Reference< css::frame::XDispatch > SAL_CALL Desktop::queryDispatch( const css::util::URL& aURL , + const OUString& sTargetFrameName , + sal_Int32 nSearchFlags ) +{ + /* UNSAFE AREA --------------------------------------------------------------------------------------------- */ + // Register transaction and reject wrong calls. + TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS ); + + // Remove uno and cmd protocol part as we want to support both of them. We store only the command part + // in our hash map. All other protocols are stored with the protocol part. + OUString aCommand( aURL.Main ); + if ( aURL.Protocol.equalsIgnoreAsciiCase(".uno:") ) + aCommand = aURL.Path; + + if (!m_xCommandOptions && !utl::ConfigManager::IsFuzzing()) + m_xCommandOptions.reset(new SvtCommandOptions); + + // Make std::unordered_map lookup if the current URL is in the disabled list + if (m_xCommandOptions && m_xCommandOptions->LookupDisabled(aCommand)) + return css::uno::Reference< css::frame::XDispatch >(); + else + { + // We use a helper to support these interface and an interceptor mechanism. + // Our helper is threadsafe by himself! + return m_xDispatchHelper->queryDispatch( aURL, sTargetFrameName, nSearchFlags ); + } +} + +css::uno::Sequence< css::uno::Reference< css::frame::XDispatch > > SAL_CALL Desktop::queryDispatches( const css::uno::Sequence< css::frame::DispatchDescriptor >& lQueries ) +{ + /* UNSAFE AREA --------------------------------------------------------------------------------------------- */ + // Register transaction and reject wrong calls. + TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS ); + + return m_xDispatchHelper->queryDispatches( lQueries ); +} + +/*-************************************************************************************************************ + @interface XDispatchProviderInterception + @short supports registration/deregistration of interception objects, which + are interested on special dispatches. + + @descr It's really provided by an internal helper, which is used inside the dispatch API too. + @param xInterceptor + the interceptor object, which wishes to be (de)registered. + + @threadsafe yes +*//*-*************************************************************************************************************/ +void SAL_CALL Desktop::registerDispatchProviderInterceptor( const css::uno::Reference< css::frame::XDispatchProviderInterceptor >& xInterceptor) +{ + TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS ); + + css::uno::Reference< css::frame::XDispatchProviderInterception > xInterceptionHelper( m_xDispatchHelper, css::uno::UNO_QUERY ); + xInterceptionHelper->registerDispatchProviderInterceptor( xInterceptor ); +} + +void SAL_CALL Desktop::releaseDispatchProviderInterceptor ( const css::uno::Reference< css::frame::XDispatchProviderInterceptor >& xInterceptor) +{ + TransactionGuard aTransaction( m_aTransactionManager, E_SOFTEXCEPTIONS ); + + css::uno::Reference< css::frame::XDispatchProviderInterception > xInterceptionHelper( m_xDispatchHelper, css::uno::UNO_QUERY ); + xInterceptionHelper->releaseDispatchProviderInterceptor( xInterceptor ); +} + +/*-************************************************************************************************************ + @interface XFramesSupplier + @short return access to append or remove children on desktop + @descr We don't implement these interface directly. We use a helper class to do this. + If you wish to add or delete children to/from the container, call these method to get + a reference to the helper. + + @attention Helper is threadsafe himself. So we don't need any lock here. + + @seealso class OFrames + @return A reference to the helper. + + @onerror A null reference is returned. + @threadsafe yes +*//*-*************************************************************************************************************/ +css::uno::Reference< css::frame::XFrames > SAL_CALL Desktop::getFrames() +{ + /* UNSAFE AREA --------------------------------------------------------------------------------------------- */ + // Register transaction and reject wrong calls. + TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS ); + + return m_xFramesHelper; +} + +/*-************************************************************************************************************ + @interface XFramesSupplier + @short set/get the current active child frame + @descr It must be a task. Direct children of desktop are tasks only! No frames are accepted. + We don't save this information directly in this class. We use our container-helper + to do that. + + @attention Helper is threadsafe himself. So we don't need any lock here. + + @seealso class OFrameContainer + + @param "xFrame", new active frame (must be valid!) + @return A reference to our current active childtask, if anyone exist. + + @onerror A null reference is returned. + @threadsafe yes +*//*-*************************************************************************************************************/ +void SAL_CALL Desktop::setActiveFrame( const css::uno::Reference< css::frame::XFrame >& xFrame ) +{ + /* UNSAFE AREA --------------------------------------------------------------------------------------------- */ + // Register transaction and reject wrong calls. + TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS ); + + // Get old active frame first. + // If nothing will change - do nothing! + // Otherwise set new active frame ... + // and deactivate last frame. + // It's necessary for our FrameActionEvent listener on a frame! + css::uno::Reference< css::frame::XFrame > xLastActiveChild = m_aChildTaskContainer.getActive(); + if( xLastActiveChild != xFrame ) + { + m_aChildTaskContainer.setActive( xFrame ); + if( xLastActiveChild.is() ) + { + xLastActiveChild->deactivate(); + } + } +} + +css::uno::Reference< css::frame::XFrame > SAL_CALL Desktop::getActiveFrame() +{ + /* UNSAFE AREA --------------------------------------------------------------------------------------------- */ + // Register transaction and reject wrong calls. + TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS ); + + return m_aChildTaskContainer.getActive(); +} + +/* + @interface XFrame + @short non implemented methods! + @descr Some method make no sense for our desktop! He has no window or parent or ... + So we should implement it empty and warn programmer, if he use it! +*/ +void SAL_CALL Desktop::initialize( const css::uno::Reference< css::awt::XWindow >& ) +{ +} + +css::uno::Reference< css::awt::XWindow > SAL_CALL Desktop::getContainerWindow() +{ + return css::uno::Reference< css::awt::XWindow >(); +} + +void SAL_CALL Desktop::setCreator( const css::uno::Reference< css::frame::XFramesSupplier >& /*xCreator*/ ) +{ +} + +css::uno::Reference< css::frame::XFramesSupplier > SAL_CALL Desktop::getCreator() +{ + return css::uno::Reference< css::frame::XFramesSupplier >(); +} + +OUString SAL_CALL Desktop::getName() +{ + SolarMutexGuard g; + return m_sName; +} + +void SAL_CALL Desktop::setName( const OUString& sName ) +{ + SolarMutexGuard g; + m_sName = sName; +} + +sal_Bool SAL_CALL Desktop::isTop() +{ + return true; +} + +void SAL_CALL Desktop::activate() +{ + // Desktop is active always... but sometimes our frames try to activate + // the complete path from bottom to top... And our desktop is the topest frame :-( + // So - please don't show any assertions here. Do nothing! +} + +void SAL_CALL Desktop::deactivate() +{ + // Desktop is active always... but sometimes our frames try to deactivate + // the complete path from bottom to top... And our desktop is the topest frame :-( + // So - please don't show any assertions here. Do nothing! +} + +sal_Bool SAL_CALL Desktop::isActive() +{ + return true; +} + +sal_Bool SAL_CALL Desktop::setComponent( const css::uno::Reference< css::awt::XWindow >& /*xComponentWindow*/ , + const css::uno::Reference< css::frame::XController >& /*xController*/ ) +{ + return false; +} + +css::uno::Reference< css::awt::XWindow > SAL_CALL Desktop::getComponentWindow() +{ + return css::uno::Reference< css::awt::XWindow >(); +} + +css::uno::Reference< css::frame::XController > SAL_CALL Desktop::getController() +{ + return css::uno::Reference< css::frame::XController >(); +} + +void SAL_CALL Desktop::contextChanged() +{ +} + +void SAL_CALL Desktop::addFrameActionListener( const css::uno::Reference< css::frame::XFrameActionListener >& ) +{ +} + +// css::frame::XFrame +void SAL_CALL Desktop::removeFrameActionListener( const css::uno::Reference< css::frame::XFrameActionListener >& ) +{ +} + +/*-************************************************************************************************************ + @interface XFrame + @short try to find a frame with special parameters + @descr This method searches for a frame with the specified name. + Frames may contain other frames (e.g. a frameset) and may + be contained in other frames. This hierarchy is searched by + this method. + First some special names are taken into account, i.e. "", + "_self", "_top", "_parent" etc. The FrameSearchFlags are ignored + when comparing these names with aTargetFrameName, further steps are + controlled by the FrameSearchFlags. If allowed, the name of the frame + itself is compared with the desired one, then ( again if allowed ) + the method findFrame is called for all children of the frame. + If no Frame with the given name is found until the top frames container, + a new top Frame is created, if this is allowed by a special + FrameSearchFlag. The new Frame also gets the desired name. + We use a helper to get right search direction and react in a right manner. + + @seealso class TargetFinder + + @param "sTargetFrameName" , name of searched frame + @param "nSearchFlags" , flags to regulate search + @return A reference to an existing frame in hierarchy, if it exist. + + @onerror A null reference is returned. + @threadsafe yes +*//*-*************************************************************************************************************/ +css::uno::Reference< css::frame::XFrame > SAL_CALL Desktop::findFrame( const OUString& sTargetFrameName , + sal_Int32 nSearchFlags ) +{ + css::uno::Reference< css::frame::XFrame > xTarget; + + // 0) Ignore wrong parameter! + // We don't support search for following special targets. + // If we reject these requests, we must not check for such names + // in following code again and again. If we do not, so wrong + // search results can occur! + + if ( + (sTargetFrameName==SPECIALTARGET_DEFAULT ) || // valid for dispatches - not for findFrame()! + (sTargetFrameName==SPECIALTARGET_PARENT ) || // we have no parent by definition + (sTargetFrameName==SPECIALTARGET_BEAMER ) // beamer frames are allowed as child of tasks only - + // and they exist more than ones. We have no idea which our sub tasks is the right one + ) + { + return nullptr; + } + + // I) check for special defined targets first which must be handled exclusive. + // force using of "if() else if() ..." + + // I.I) "_blank" + // create a new task as child of this desktop instance + // Note: Used helper TaskCreator use us automatically ... + + if ( sTargetFrameName==SPECIALTARGET_BLANK ) + { + TaskCreator aCreator( m_xContext ); + xTarget = aCreator.createTask(sTargetFrameName, utl::MediaDescriptor()); + } + + // I.II) "_top" + // We are top by definition + + else if ( sTargetFrameName==SPECIALTARGET_TOP ) + { + xTarget = this; + } + + // I.III) "_self", "" + // This mean this "frame" in every case. + + else if ( + ( sTargetFrameName==SPECIALTARGET_SELF ) || + ( sTargetFrameName.isEmpty() ) + ) + { + xTarget = this; + } + + else + { + + // II) otherwise use optional given search flags + // force using of combinations of such flags. means no "else" part of use if() statements. + // But we ust break further searches if target was already found. + // Order of using flags is fix: SELF - CHILDREN - SIBLINGS - PARENT + // TASK and CREATE are handled special. + // But note: Such flags are not valid for the desktop - especially SIBLINGS or PARENT. + + // II.I) SELF + // Check for right name. If it's the searched one return ourself - otherwise + // ignore this flag. + + if ( + (nSearchFlags & css::frame::FrameSearchFlag::SELF) && + (m_sName == sTargetFrameName) + ) + { + xTarget = this; + } + + // II.II) TASKS + // This is a special flag. Normally it regulate search inside tasks and forbid access to parent trees. + // But the desktop exists outside such task trees. They are our sub trees. So the desktop implement + // a special feature: We use it to start search on our direct children only. That means we suppress + // search on ALL child frames. May that can be useful to get access on opened document tasks + // only without filter out all non really required sub frames ... + // Used helper method on our container doesn't create any frame - it's a search only. + + if ( + ( ! xTarget.is() ) && + (nSearchFlags & css::frame::FrameSearchFlag::TASKS) + ) + { + xTarget = m_aChildTaskContainer.searchOnDirectChildrens(sTargetFrameName); + } + + // II.III) CHILDREN + // Search on all children for the given target name. + // An empty name value can't occur here - because it must be already handled as "_self" + // before. Used helper function of container doesn't create any frame. + // It makes a deep search only. + + if ( + ( ! xTarget.is() ) && + (nSearchFlags & css::frame::FrameSearchFlag::CHILDREN) + ) + { + xTarget = m_aChildTaskContainer.searchOnAllChildrens(sTargetFrameName); + } + + // II.IV) CREATE + // If we haven't found any valid target frame by using normal flags - but user allowed us to create + // a new one ... we should do that. Used TaskCreator use us automatically as parent! + + if ( + ( ! xTarget.is() ) && + (nSearchFlags & css::frame::FrameSearchFlag::CREATE) + ) + { + TaskCreator aCreator( m_xContext ); + xTarget = aCreator.createTask(sTargetFrameName, utl::MediaDescriptor()); + } + } + + return xTarget; +} + +void SAL_CALL Desktop::disposing() +{ + // Safe impossible cases + // It's a programming error if dispose is called before terminate! + + // But if you just ignore the assertion (which happens in unit + // tests for instance in sc/qa/unit) nothing bad happens. + assert(m_bIsShutdown && "Desktop disposed before terminating it"); + + { + SolarMutexGuard aWriteLock; + + { + TransactionGuard aTransaction(m_aTransactionManager, E_HARDEXCEPTIONS); + } + + // Disable this instance for further work. + // This will wait for all current running transactions ... + // and reject all new incoming requests! + m_aTransactionManager.setWorkingMode(E_BEFORECLOSE); + } + + // Following lines of code can be called outside a synchronized block ... + // Because our transaction manager will block all new requests to this object. + // So nobody can use us any longer. + // Exception: Only removing of listener will work ... and this code can't be dangerous. + + // First we have to kill all listener connections. + // They might rely on our member and can hinder us on releasing them. + css::uno::Reference< css::uno::XInterface > xThis ( static_cast< ::cppu::OWeakObject* >(this), css::uno::UNO_QUERY ); + css::lang::EventObject aEvent( xThis ); + m_aListenerContainer.disposeAndClear( aEvent ); + + // Clear our child task container and forget all task references hardly. + // Normally all open document was already closed by our terminate() function before ... + // New opened frames will have a problem now .-) + m_aChildTaskContainer.clear(); + + // Dispose our helper too. + css::uno::Reference< css::lang::XEventListener > xFramesHelper( m_xFramesHelper, css::uno::UNO_QUERY ); + if( xFramesHelper.is() ) + xFramesHelper->disposing( aEvent ); + + // At least clean up other member references. + m_xDispatchHelper.clear(); + m_xFramesHelper.clear(); + m_xContext.clear(); + + m_xPipeTerminator.clear(); + m_xQuickLauncher.clear(); + m_xSWThreadManager.clear(); + + // we need a copy because the disposing might call the removeEventListener method + std::vector< css::uno::Reference > xComponentDllListeners; + xComponentDllListeners.swap(m_xComponentDllListeners); + for (auto& xListener: xComponentDllListeners) + { + xListener->disposing(aEvent); + } + xComponentDllListeners.clear(); + m_xSfxTerminator.clear(); + m_xCommandOptions.reset(); + + // From this point nothing will work further on this object ... + // excepting our dtor() .-) + m_aTransactionManager.setWorkingMode( E_CLOSE ); +} + +/* + @interface XComponent + @short add/remove listener for dispose events + @descr Add an event listener to this object, if you wish to get information + about our dying! + You must release this listener reference during your own disposing() method. + + @attention Our container is threadsafe himself. So we don't need any lock here. + @param "xListener", reference to valid listener. We don't accept invalid values! + @threadsafe yes +*/ +void SAL_CALL Desktop::addEventListener( const css::uno::Reference< css::lang::XEventListener >& xListener ) +{ + /* UNSAFE AREA --------------------------------------------------------------------------------------------- */ + // Safe impossible cases + // Method not defined for all incoming parameter. + SAL_WARN_IF( !xListener.is(), "fwk.desktop", "Desktop::addEventListener(): Invalid parameter detected!" ); + // Register transaction and reject wrong calls. + TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS ); + + m_aListenerContainer.addInterface( cppu::UnoType::get(), xListener ); +} + +void SAL_CALL Desktop::removeEventListener( const css::uno::Reference< css::lang::XEventListener >& xListener ) +{ + /* UNSAFE AREA --------------------------------------------------------------------------------------------- */ + // Safe impossible cases + // Method not defined for all incoming parameter. + SAL_WARN_IF( !xListener.is(), "fwk.desktop", "Desktop::removeEventListener(): Invalid parameter detected!" ); + // Register transaction and reject wrong calls. + TransactionGuard aTransaction( m_aTransactionManager, E_SOFTEXCEPTIONS ); + + m_aListenerContainer.removeInterface( cppu::UnoType::get(), xListener ); +} + +/*-************************************************************************************************************ + @interface XDispatchResultListener + @short callback for dispatches + @descr To support our method "loadComponentFromURL()" we are listener on temp. created dispatcher. + They call us back in this method "statusChanged()". As source of given state event, they give us a + reference to the target frame, in which dispatch was loaded! So we can use it to return his component + to caller! If no target exist ... ??!! + + @seealso method loadComponentFromURL() + + @param "aEvent", state event which (hopefully) valid information + @threadsafe yes +*//*-*************************************************************************************************************/ +void SAL_CALL Desktop::dispatchFinished( const css::frame::DispatchResultEvent& aEvent ) +{ + /* UNSAFE AREA --------------------------------------------------------------------------------------------- */ + // Register transaction and reject wrong calls. + TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS ); + + SolarMutexGuard g; + if( m_eLoadState != E_INTERACTION ) + { + m_eLoadState = E_FAILED; + if( aEvent.State == css::frame::DispatchResultState::SUCCESS ) + { + css::uno::Reference< css::frame::XFrame > xLastFrame; /// last target of "loadComponentFromURL()"! + if ( aEvent.Result >>= xLastFrame ) + m_eLoadState = E_SUCCESSFUL; + } + } +} + +/*-************************************************************************************************************ + @interface XEventListener + @short not implemented! + @descr We are a status listener ... and so we must be an event listener too ... But we don't need it really! + We are a temp. listener only and our lifetime isn't smaller then of our temp. used dispatcher. + + @seealso method loadComponentFromURL() +*//*-*************************************************************************************************************/ +void SAL_CALL Desktop::disposing( const css::lang::EventObject& ) +{ + SAL_WARN( "fwk.desktop", "Desktop::disposing(): Algorithm error! Normally desktop is temp. listener ... not all the time. So this method shouldn't be called." ); +} + +/*-************************************************************************************************************ + @interface XInteractionHandler + @short callback for loadComponentFromURL for detected exceptions during load process + @descr In this case we must cancel loading and throw these detected exception again as result + of our own called method. + + @attention a) + Normal loop in loadComponentFromURL() breaks on set member m_eLoadState during callback statusChanged(). + But these interaction feature implements second way to do so! So we must look on different callbacks + for same operation ... and live with it. + b) + Search for given continuations too. If any XInteractionAbort exist ... use it to abort further operations + for currently running operation! + + @seealso method loadComponentFromURL() + @seealso member m_eLoadState + + @param "xRequest", request for interaction - normal a wrapped target exception from bottom services + @threadsafe yes +*//*-*************************************************************************************************************/ +void SAL_CALL Desktop::handle( const css::uno::Reference< css::task::XInteractionRequest >& xRequest ) +{ + /* UNSAFE AREA --------------------------------------------------------------------------------------------- */ + // Register transaction and reject wrong calls. + TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS ); + + // Don't check incoming request! + // If somewhere starts interaction without right parameter - he made something wrong. + // loadComponentFromURL() waits for these event - otherwise it yield for ever! + + // get packed request and work on it first + // Attention: Don't set it on internal member BEFORE interaction is finished - because + // "loadComponentFromURL()" yield tills this member is changed. If we do it before + // interaction finish we can't guarantee right functionality. May be we cancel load process to earlier... + css::uno::Any aRequest = xRequest->getRequest(); + + // extract continuations from request + css::uno::Sequence< css::uno::Reference< css::task::XInteractionContinuation > > lContinuations = xRequest->getContinuations(); + css::uno::Reference< css::task::XInteractionAbort > xAbort; + css::uno::Reference< css::task::XInteractionApprove > xApprove; + css::uno::Reference< css::document::XInteractionFilterSelect > xFilterSelect; + bool bAbort = false; + + sal_Int32 nCount=lContinuations.getLength(); + for( sal_Int32 nStep=0; nStep>= aErrorCodeRequest ) + { + bool bWarning = ErrCode(aErrorCodeRequest.ErrCode).IsWarning(); + if (xApprove.is() && bWarning) + xApprove->select(); + else + if (xAbort.is()) + { + xAbort->select(); + bAbort = true; + } + } + else if( xAbort.is() ) + { + xAbort->select(); + bAbort = true; + } + + // Ok now it's time to break yield loop of loadComponentFromURL(). + // But only for really aborted requests! + // For example warnings will be approved and we wait for any success story ... + if (bAbort) + { + SolarMutexGuard g; + m_eLoadState = E_INTERACTION; + } +} + +::sal_Int32 SAL_CALL Desktop::leaseNumber( const css::uno::Reference< css::uno::XInterface >& xComponent ) +{ + TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS ); + return m_xTitleNumberGenerator->leaseNumber (xComponent); +} + +void SAL_CALL Desktop::releaseNumber( ::sal_Int32 nNumber ) +{ + TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS ); + m_xTitleNumberGenerator->releaseNumber (nNumber); +} + +void SAL_CALL Desktop::releaseNumberForComponent( const css::uno::Reference< css::uno::XInterface >& xComponent ) +{ + TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS ); + m_xTitleNumberGenerator->releaseNumberForComponent (xComponent); +} + +OUString SAL_CALL Desktop::getUntitledPrefix() +{ + TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS ); + return m_xTitleNumberGenerator->getUntitledPrefix (); +} + +/*-************************************************************************************************************ + @short try to convert a property value + @descr This method is called from helperclass "OPropertySetHelper". + Don't use this directly! + You must try to convert the value of given PropHandle and + return results of this operation. This will be used to ask vetoable + listener. If no listener has a veto, we will change value really! + ( in method setFastPropertyValue_NoBroadcast(...) ) + + @attention Methods of OPropertySethelper are safed by using our shared osl mutex! (see ctor!) + So we must use different locks to make our implementation threadsafe. + + @seealso class OPropertySetHelper + @seealso method setFastPropertyValue_NoBroadcast() + + @param "aConvertedValue" new converted value of property + @param "aOldValue" old value of property + @param "nHandle" handle of property + @param "aValue" new value of property + @return sal_True if value will be changed, sal_FALSE otherway + + @onerror IllegalArgumentException, if you call this with an invalid argument + @threadsafe yes +*//*-*************************************************************************************************************/ +sal_Bool SAL_CALL Desktop::convertFastPropertyValue( css::uno::Any& aConvertedValue , + css::uno::Any& aOldValue , + sal_Int32 nHandle , + const css::uno::Any& aValue ) +{ + /* UNSAFE AREA --------------------------------------------------------------------------------------------- */ + // Register transaction and reject wrong calls. + TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS ); + + // Initialize state with sal_False !!! + // (Handle can be invalid) + bool bReturn = false; + + switch( nHandle ) + { + case PropHandle::SuspendQuickstartVeto: + bReturn = PropHelper::willPropertyBeChanged( + css::uno::Any(m_bSuspendQuickstartVeto), + aValue, + aOldValue, + aConvertedValue); + break; + case PropHandle::DispatchRecorderSupplier : + bReturn = PropHelper::willPropertyBeChanged( + css::uno::Any(m_xDispatchRecorderSupplier), + aValue, + aOldValue, + aConvertedValue); + break; + case PropHandle::Title : + bReturn = PropHelper::willPropertyBeChanged( + css::uno::Any(m_sTitle), + aValue, + aOldValue, + aConvertedValue); + break; + } + + // Return state of operation. + return bReturn; +} + +/*-************************************************************************************************************ + @short set value of a transient property + @descr This method is calling from helperclass "OPropertySetHelper". + Don't use this directly! + Handle and value are valid everyway! You must set the new value only. + After this, baseclass send messages to all listener automatically. + + @seealso class OPropertySetHelper + + @param "nHandle" handle of property to change + @param "aValue" new value of property + @onerror An exception is thrown. + @threadsafe yes +*//*-*************************************************************************************************************/ +void SAL_CALL Desktop::setFastPropertyValue_NoBroadcast( sal_Int32 nHandle , + const css::uno::Any& aValue ) +{ + /* UNSAFE AREA --------------------------------------------------------------------------------------------- */ + // Register transaction and reject wrong calls. + TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS ); + + switch( nHandle ) + { + case PropHandle::SuspendQuickstartVeto: aValue >>= m_bSuspendQuickstartVeto; + break; + case PropHandle::DispatchRecorderSupplier: aValue >>= m_xDispatchRecorderSupplier; + break; + case PropHandle::Title: aValue >>= m_sTitle; + break; + } +} + +/*-************************************************************************************************************ + @short get value of a transient property + @descr This method is calling from helperclass "OPropertySetHelper". + Don't use this directly! + + @attention We don't need any mutex or lock here ... We use threadsafe container or methods here only! + + @seealso class OPropertySetHelper + + @param "nHandle" handle of property to change + @param "aValue" current value of property + @threadsafe yes +*//*-*************************************************************************************************************/ +void SAL_CALL Desktop::getFastPropertyValue( css::uno::Any& aValue , + sal_Int32 nHandle ) const +{ + /* UNSAFE AREA --------------------------------------------------------------------------------------------- */ + // Register transaction and reject wrong calls. + TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS ); + + switch( nHandle ) + { + case PropHandle::ActiveFrame : aValue <<= m_aChildTaskContainer.getActive(); + break; + case PropHandle::IsPlugged : aValue <<= false; + break; + case PropHandle::SuspendQuickstartVeto: aValue <<= m_bSuspendQuickstartVeto; + break; + case PropHandle::DispatchRecorderSupplier: aValue <<= m_xDispatchRecorderSupplier; + break; + case PropHandle::Title: aValue <<= m_sTitle; + break; + } +} + +::cppu::IPropertyArrayHelper& SAL_CALL Desktop::getInfoHelper() +{ + static cppu::OPropertyArrayHelper HELPER = + [] () { + return cppu::OPropertyArrayHelper { + {{"ActiveFrame", PropHandle::ActiveFrame, + cppu::UnoType::get(), + (css::beans::PropertyAttribute::TRANSIENT + | css::beans::PropertyAttribute::READONLY)}, + {"DispatchRecorderSupplier", + PropHandle::DispatchRecorderSupplier, + cppu::UnoType::get(), + css::beans::PropertyAttribute::TRANSIENT}, + {"IsPlugged", + PropHandle::IsPlugged, cppu::UnoType::get(), + (css::beans::PropertyAttribute::TRANSIENT + | css::beans::PropertyAttribute::READONLY)}, + {"SuspendQuickstartVeto", PropHandle::SuspendQuickstartVeto, + cppu::UnoType::get(), + css::beans::PropertyAttribute::TRANSIENT}, + {"Title", PropHandle::Title, cppu::UnoType::get(), + css::beans::PropertyAttribute::TRANSIENT}}, + true}; + }(); + return HELPER; +} + +/*-************************************************************************************************************ + @short return propertysetinfo + @descr You can call this method to get information about transient properties + of this object. + + @attention You must use global lock (method use static variable) ... and it must be the shareable osl mutex of it. + Because; our baseclass use this mutex to make his code threadsafe. We use our lock! + So we could have two different mutex/lock mechanism at the same object. + + @seealso class OPropertySetHelper + @seealso interface XPropertySet + @seealso interface XMultiPropertySet + @return reference to object with information [XPropertySetInfo] + @threadsafe yes +*//*-*************************************************************************************************************/ +css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL Desktop::getPropertySetInfo() +{ + /* UNSAFE AREA --------------------------------------------------------------------------------------------- */ + // Register transaction and reject wrong calls. + TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS ); + + // Create structure of propertysetinfo for baseclass "OPropertySetHelper". + // (Use method "getInfoHelper()".) + static css::uno::Reference< css::beans::XPropertySetInfo > xInfo( + cppu::OPropertySetHelper::createPropertySetInfo( getInfoHelper() ) ); + + return xInfo; +} + +/*-************************************************************************************************************ + @short return current component of current frame + @descr The desktop himself has no component. But every frame in subtree. + If somewhere call getCurrentComponent() at this class, we try to find the right frame and + then we try to become his component. It can be a VCL-component, the model or the controller + of founded frame. + + @attention We don't work on internal member ... so we don't need any lock here. + + @seealso method getCurrentComponent(); + + @param "xFrame", reference to valid frame in hierarchy. Method is not defined for invalid values. + But we don't check these. It's an IMPL-method and caller must use it right! + @return A reference to found component. + + @onerror A null reference is returned. + @threadsafe yes +*//*-*************************************************************************************************************/ +css::uno::Reference< css::lang::XComponent > Desktop::impl_getFrameComponent( const css::uno::Reference< css::frame::XFrame >& xFrame ) const +{ + /* UNSAFE AREA --------------------------------------------------------------------------------------------- */ + // Register transaction and reject wrong calls. + TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS ); + + // Set default return value, if method failed. + css::uno::Reference< css::lang::XComponent > xComponent; + // Does no controller exists? + css::uno::Reference< css::frame::XController > xController = xFrame->getController(); + if( !xController.is() ) + { + // Controller not exist - use the VCL-component. + xComponent = xFrame->getComponentWindow(); + } + else + { + // Does no model exists? + css::uno::Reference< css::frame::XModel > xModel = xController->getModel(); + if( xModel.is() ) + { + // Model exist - use the model as component. + xComponent = xModel; + } + else + { + // Model not exist - use the controller as component. + xComponent = xController; + } + } + + return xComponent; +} + +bool Desktop::impl_sendQueryTerminationEvent(Desktop::TTerminateListenerList& lCalledListener) +{ + TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS ); + + comphelper::OInterfaceContainerHelper2* pContainer = m_aListenerContainer.getContainer( cppu::UnoType::get()); + if ( ! pContainer ) + return true; + + css::lang::EventObject aEvent( static_cast< ::cppu::OWeakObject* >(this) ); + + comphelper::OInterfaceIteratorHelper2 aIterator( *pContainer ); + while ( aIterator.hasMoreElements() ) + { + try + { + css::uno::Reference< css::frame::XTerminateListener > xListener(aIterator.next(), css::uno::UNO_QUERY); + if ( ! xListener.is() ) + continue; + xListener->queryTermination( aEvent ); + lCalledListener.push_back(xListener); + } + catch( const css::frame::TerminationVetoException& ) + { + // first veto will stop the query loop. + return false; + } + catch( const css::uno::Exception& ) + { + // clean up container. + // E.g. dead remote listener objects can make trouble otherwise. + // Iterator implementation allows removing objects during it's used ! + aIterator.remove(); + } + } + + return true; +} + +void Desktop::impl_sendCancelTerminationEvent(const Desktop::TTerminateListenerList& lCalledListener) +{ + TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS ); + + css::lang::EventObject aEvent( static_cast< ::cppu::OWeakObject* >(this) ); + for (const css::uno::Reference& xListener : lCalledListener) + { + try + { + // Note: cancelTermination() is a new and optional interface method ! + css::uno::Reference< css::frame::XTerminateListener2 > xListenerGeneration2(xListener, css::uno::UNO_QUERY); + if ( ! xListenerGeneration2.is() ) + continue; + xListenerGeneration2->cancelTermination( aEvent ); + } + catch( const css::uno::Exception& ) + {} + } +} + +void Desktop::impl_sendTerminateToClipboard() +{ + TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS ); + + comphelper::OInterfaceContainerHelper2* pContainer = m_aListenerContainer.getContainer( cppu::UnoType::get()); + if ( ! pContainer ) + return; + + comphelper::OInterfaceIteratorHelper2 aIterator( *pContainer ); + while ( aIterator.hasMoreElements() ) + { + try + { + css::frame::XTerminateListener* pTerminateListener = + static_cast< css::frame::XTerminateListener* >(aIterator.next()); + css::uno::Reference< css::lang::XServiceInfo > xInfo( pTerminateListener, css::uno::UNO_QUERY ); + if ( !xInfo.is() ) + continue; + + if ( xInfo->getImplementationName() != "com.sun.star.comp.svt.TransferableHelperTerminateListener" ) + continue; + + css::lang::EventObject aEvent( static_cast< ::cppu::OWeakObject* >(this) ); + pTerminateListener->notifyTermination( aEvent ); + + // don't notify twice + aIterator.remove(); + } + catch( const css::uno::Exception& ) + { + // clean up container. + // E.g. dead remote listener objects can make trouble otherwise. + // Iterator implementation allows removing objects during it's used ! + aIterator.remove(); + } + } +} + +void Desktop::impl_sendNotifyTerminationEvent() +{ + TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS ); + + comphelper::OInterfaceContainerHelper2* pContainer = m_aListenerContainer.getContainer( cppu::UnoType::get()); + if ( ! pContainer ) + return; + + css::lang::EventObject aEvent( static_cast< ::cppu::OWeakObject* >(this) ); + + comphelper::OInterfaceIteratorHelper2 aIterator( *pContainer ); + while ( aIterator.hasMoreElements() ) + { + try + { + static_cast< css::frame::XTerminateListener* >(aIterator.next())->notifyTermination( aEvent ); + } + catch( const css::uno::Exception& ) + { + // clean up container. + // E.g. dead remote listener objects can make trouble otherwise. + // Iterator implementation allows removing objects during it's used ! + aIterator.remove(); + } + } +} + +bool Desktop::impl_closeFrames(bool bAllowUI) +{ + SolarMutexClearableGuard aReadLock; + css::uno::Sequence< css::uno::Reference< css::frame::XFrame > > lFrames = m_aChildTaskContainer.getAllElements(); + aReadLock.clear(); + + ::sal_Int32 c = lFrames.getLength(); + ::sal_Int32 i = 0; + ::sal_Int32 nNonClosedFrames = 0; + + for( i=0; i xFrame = lFrames[i]; + + // XController.suspend() will show a UI ... + // Use it in case it was allowed from outside only. + bool bSuspended = false; + css::uno::Reference< css::frame::XController > xController = xFrame->getController(); + if ( bAllowUI && xController.is() ) + { + bSuspended = xController->suspend( true ); + if ( ! bSuspended ) + { + ++nNonClosedFrames; + if(m_bSession) + break; + else + continue; + } + } + + // Try to close frame (in case no UI was allowed without calling XController->suspend() before!) + // But don't deliver ownership to any other one! + // This method can be called again. + css::uno::Reference< css::util::XCloseable > xClose( xFrame, css::uno::UNO_QUERY ); + if ( xClose.is() ) + { + try + { + xClose->close(false); + } + catch(const css::util::CloseVetoException&) + { + // Any internal process of this frame disagree with our request. + // Safe this state but don't break these loop. Other frames has to be closed! + ++nNonClosedFrames; + + // Reactivate controller. + // It can happen that XController.suspend() returned true... but a registered close listener + // threw these veto exception. Then the controller has to be reactivated. Otherwise + // these document doesn't work any more. + if ( bSuspended && xController.is()) + xController->suspend(false); + } + + // If interface XClosable interface exists and was used... + // it's not allowed to use XComponent->dispose() also! + continue; + } + + // XClosable not supported ? + // Then we have to dispose these frame hardly. + if ( xFrame.is() ) + xFrame->dispose(); + + // Don't remove these frame from our child container! + // A frame do it by itself inside close()/dispose() method. + } + catch(const css::lang::DisposedException&) + { + // Dispose frames are closed frames. + // So we can count it here .-) + } + } + + // reset the session + m_bSession = false; + + return (nNonClosedFrames < 1); +} + +} // namespace framework + +namespace { + +rtl::Reference createDesktop( + css::uno::Reference const & context) +{ + SolarMutexGuard g; // tdf#114025 init with SolarMutex to avoid deadlock + rtl::Reference desktop(new framework::Desktop(context)); + desktop->constructorInit(); + return desktop; +} + +} + +const rtl::Reference & framework::getDesktop( + css::uno::Reference const & context) +{ + static auto const instance = createDesktop(context); + return instance; +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_framework_Desktop_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence const &) +{ + return cppu::acquire(framework::getDesktop(context).get()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/services/dispatchhelper.cxx b/framework/source/services/dispatchhelper.cxx new file mode 100644 index 0000000000..8f3d77d322 --- /dev/null +++ b/framework/source/services/dispatchhelper.cxx @@ -0,0 +1,218 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace framework +{ +// XInterface, XTypeProvider, XServiceInfo + +OUString SAL_CALL DispatchHelper::getImplementationName() +{ + return "com.sun.star.comp.framework.services.DispatchHelper"; +} + +sal_Bool SAL_CALL DispatchHelper::supportsService(const OUString& sServiceName) +{ + return cppu::supportsService(this, sServiceName); +} + +css::uno::Sequence SAL_CALL DispatchHelper::getSupportedServiceNames() +{ + return { "com.sun.star.frame.DispatchHelper" }; +} + +/** ctor. + + @param xSMGR the global uno service manager, which can be used to create own needed services. +*/ +DispatchHelper::DispatchHelper(css::uno::Reference xContext) + : m_xContext(std::move(xContext)) + , m_aBlockFlag(false) +{ +} + +/** dtor. +*/ +DispatchHelper::~DispatchHelper() {} + +/** capsulate all steps of a dispatch request and provide so an easy way for dispatches. + + @param xDispatchProvider + identifies the object, which provides may be valid dispatch objects for this execute. + + @param sURL + describes the requested feature. + + @param sTargetFrameName + points to the frame, which must be used (or may be created) for this dispatch. + + @param nSearchFlags + in case the sTargetFrameName isn't unique, these flags regulate further searches. + + @param lArguments + optional arguments for this request. + + @return An Any which capsulate a possible result of the internal wrapped dispatch. + */ +css::uno::Any SAL_CALL DispatchHelper::executeDispatch( + const css::uno::Reference& xDispatchProvider, + const OUString& sURL, const OUString& sTargetFrameName, sal_Int32 nSearchFlags, + const css::uno::Sequence& lArguments) +{ + // check for valid parameters + if ((!xDispatchProvider.is()) || (!m_xContext.is()) || (sURL.isEmpty())) + { + return css::uno::Any(); + } + + // parse given URL + css::uno::Reference xParser; + /* SAFE { */ + { + std::scoped_lock aReadLock(m_mutex); + xParser = css::util::URLTransformer::create(m_xContext); + } + /* } SAFE */ + + css::util::URL aURL; + aURL.Complete = sURL; + xParser->parseStrict(aURL); + + // search dispatcher + css::uno::Reference xDispatch + = xDispatchProvider->queryDispatch(aURL, sTargetFrameName, nSearchFlags); + + utl::MediaDescriptor aDescriptor(lArguments); + bool bOnMainThread = aDescriptor.getUnpackedValueOrDefault("OnMainThread", false); + + if (bOnMainThread) + return vcl::solarthread::syncExecute([this, &xDispatch, &aURL, &lArguments]() { + return executeDispatch(xDispatch, aURL, true, lArguments); + }); + else + return executeDispatch(xDispatch, aURL, true, lArguments); +} + +const css::uno::Any& +DispatchHelper::executeDispatch(const css::uno::Reference& xDispatch, + const css::util::URL& aURL, bool SyncronFlag, + const css::uno::Sequence& lArguments) +{ + comphelper::ProfileZone aZone("executeDispatch"); + css::uno::Reference xTHIS(static_cast<::cppu::OWeakObject*>(this), + css::uno::UNO_QUERY); + m_aResult.clear(); + + // check for valid parameters + if (xDispatch.is()) + { + css::uno::Reference xNotifyDispatch(xDispatch, + css::uno::UNO_QUERY); + + // make sure that synchronous execution is used (if possible) + css::uno::Sequence aArguments(lArguments); + sal_Int32 nLength = lArguments.getLength(); + aArguments.realloc(nLength + 1); + auto pArguments = aArguments.getArray(); + pArguments[nLength].Name = "SynchronMode"; + pArguments[nLength].Value <<= SyncronFlag; + + if (xNotifyDispatch.is()) + { + // dispatch it with guaranteed notification + // Here we can hope for a result ... instead of the normal dispatch. + css::uno::Reference xListener(xTHIS, + css::uno::UNO_QUERY); + /* SAFE { */ + { + std::scoped_lock aWriteLock(m_mutex); + m_xBroadcaster = xNotifyDispatch; + m_aBlockFlag = false; + } + /* } SAFE */ + + // dispatch it and wait for a notification + // TODO/MBA: waiting in main thread?! + xNotifyDispatch->dispatchWithNotification(aURL, aArguments, xListener); + + std::unique_lock aWriteLock(m_mutex); + m_aBlock.wait(aWriteLock, [this] { return m_aBlockFlag; }); // wait for result + } + else + { + // dispatch it without any chance to get a result + xDispatch->dispatch(aURL, aArguments); + } + } + + return m_aResult; +} + +/** callback for started dispatch with guaranteed notifications. + + We must save the result, so the method executeDispatch() can return it. + Further we must release the broadcaster (otherwise it can't die) + and unblock the waiting executeDispatch() request. + + @param aResult + describes the result of the dispatch operation + */ +void SAL_CALL DispatchHelper::dispatchFinished(const css::frame::DispatchResultEvent& aResult) +{ + std::scoped_lock g(m_mutex); + m_aResult <<= aResult; + m_aBlockFlag = true; + m_aBlock.notify_one(); + m_xBroadcaster.clear(); +} + +/** we have to release our broadcaster reference. + + @param aEvent + describe the source of this event and MUST be our save broadcaster! + */ +void SAL_CALL DispatchHelper::disposing(const css::lang::EventObject&) +{ + std::scoped_lock g(m_mutex); + m_aResult.clear(); + m_aBlockFlag = true; + m_aBlock.notify_one(); + m_xBroadcaster.clear(); +} +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +framework_DispatchHelper_get_implementation(css::uno::XComponentContext* context, + css::uno::Sequence const&) +{ + return cppu::acquire(new framework::DispatchHelper(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/services/frame.cxx b/framework/source/services/frame.cxx new file mode 100644 index 0000000000..659578975a --- /dev/null +++ b/framework/source/services/frame.cxx @@ -0,0 +1,3337 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +using namespace framework; + +namespace { + +// This enum can be used to set different active states of frames +enum EActiveState +{ + E_INACTIVE, // I am not a member of active path in tree and i don't have the focus. + E_ACTIVE, // I am in the middle of an active path in tree and i don't have the focus. + E_FOCUS // I have the focus now. I must a member of an active path! +}; + +/*-************************************************************************************************************ + @short implements a normal frame of hierarchy + @descr An instance of these class can be a normal node in frame tree. A frame support influencing of his + subtree, find of subframes, activate- and deactivate-mechanism as well as + set/get of a frame window, component or controller. +*//*-*************************************************************************************************************/ +class XFrameImpl: + private cppu::BaseMutex, + public cppu::PartialWeakComponentImplHelper< + css::lang::XServiceInfo, css::frame::XFrame2, css::awt::XWindowListener, + css::awt::XTopWindowListener, css::awt::XFocusListener, + css::document::XActionLockable, css::util::XCloseable, + css::frame::XComponentLoader, css::frame::XTitle, + css::frame::XTitleChangeBroadcaster, css::beans::XPropertySet, + css::beans::XPropertySetInfo> +{ +public: + + explicit XFrameImpl(css::uno::Reference< css::uno::XComponentContext > xContext); + + /// Initialization function after having acquire()'d. + void initListeners(); + + virtual OUString SAL_CALL getImplementationName() override + { + return "com.sun.star.comp.framework.Frame"; + } + + virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override + { + return cppu::supportsService(this, ServiceName); + } + + virtual css::uno::Sequence SAL_CALL getSupportedServiceNames() override + { + return {"com.sun.star.frame.Frame"}; + } + + // XComponentLoader + + virtual css::uno::Reference< css::lang::XComponent > SAL_CALL loadComponentFromURL( + const OUString& sURL, + const OUString& sTargetFrameName, + sal_Int32 nSearchFlags, + const css::uno::Sequence< css::beans::PropertyValue >& lArguments ) override; + + // XFramesSupplier + + virtual css::uno::Reference < css::frame::XFrames > SAL_CALL getFrames() override; + virtual css::uno::Reference < css::frame::XFrame > SAL_CALL getActiveFrame() override; + virtual void SAL_CALL setActiveFrame(const css::uno::Reference < css::frame::XFrame > & xFrame) override; + + // XFrame + + virtual void SAL_CALL initialize(const css::uno::Reference < css::awt::XWindow > & xWindow) override; + virtual css::uno::Reference < css::awt::XWindow > SAL_CALL getContainerWindow() override; + virtual void SAL_CALL setCreator(const css::uno::Reference < css::frame::XFramesSupplier > & xCreator) override; + virtual css::uno::Reference < css::frame::XFramesSupplier > SAL_CALL getCreator() override; + virtual OUString SAL_CALL getName() override; + virtual void SAL_CALL setName(const OUString & sName) override; + virtual css::uno::Reference < css::frame::XFrame > SAL_CALL findFrame( + const OUString & sTargetFrameName, + sal_Int32 nSearchFlags) override; + virtual sal_Bool SAL_CALL isTop() override; + virtual void SAL_CALL activate() override; + virtual void SAL_CALL deactivate() override; + virtual sal_Bool SAL_CALL isActive() override; + virtual void SAL_CALL contextChanged() override; + virtual sal_Bool SAL_CALL setComponent( + const css::uno::Reference < css::awt::XWindow > & xComponentWindow, + const css::uno::Reference < css::frame::XController > & xController) override; + virtual css::uno::Reference < css::awt::XWindow > SAL_CALL getComponentWindow() override; + virtual css::uno::Reference < css::frame::XController > SAL_CALL getController() override; + virtual void SAL_CALL addFrameActionListener(const css::uno::Reference < css::frame::XFrameActionListener > & xListener) override; + virtual void SAL_CALL removeFrameActionListener(const css::uno::Reference < css::frame::XFrameActionListener > & xListener) override; + + // XComponent + + virtual void SAL_CALL disposing() 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; + + // XStatusIndicatorFactory + + virtual css::uno::Reference < css::task::XStatusIndicator > SAL_CALL createStatusIndicator() 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 > & lDescriptor) override; + + // XDispatchProviderInterception + + virtual void SAL_CALL registerDispatchProviderInterceptor( + const css::uno::Reference < css::frame::XDispatchProviderInterceptor > & xInterceptor) override; + virtual void SAL_CALL releaseDispatchProviderInterceptor( + const css::uno::Reference < css::frame::XDispatchProviderInterceptor > & xInterceptor) override; + + // XDispatchInformationProvider + + virtual css::uno::Sequence < sal_Int16 > SAL_CALL getSupportedCommandGroups() override; + virtual css::uno::Sequence < css::frame::DispatchInformation > SAL_CALL getConfigurableDispatchInformation(sal_Int16 nCommandGroup) override; + + // XWindowListener + // Attention: windowResized() and windowShown() are implement only! All other are empty! + + virtual void SAL_CALL windowResized(const css::awt::WindowEvent & aEvent) override; + virtual void SAL_CALL windowMoved(const css::awt::WindowEvent & /*aEvent*/ ) override {}; + virtual void SAL_CALL windowShown(const css::lang::EventObject & aEvent) override; + virtual void SAL_CALL windowHidden(const css::lang::EventObject & aEvent) override; + + // XFocusListener + // Attention: focusLost() not implemented yet! + + virtual void SAL_CALL focusGained(const css::awt::FocusEvent & aEvent) override; + virtual void SAL_CALL focusLost(const css::awt::FocusEvent & /*aEvent*/ ) override {}; + + // XTopWindowListener + // Attention: windowActivated(), windowDeactivated() and windowClosing() are implement only! All other are empty! + + virtual void SAL_CALL windowActivated(const css::lang::EventObject & aEvent) override; + virtual void SAL_CALL windowDeactivated(const css::lang::EventObject & aEvent) override; + virtual void SAL_CALL windowOpened(const css::lang::EventObject & /*aEvent*/ ) override {}; + virtual void SAL_CALL windowClosing(const css::lang::EventObject & aEvent) override; + virtual void SAL_CALL windowClosed(const css::lang::EventObject & /*aEvent*/ ) override {}; + virtual void SAL_CALL windowMinimized(const css::lang::EventObject & /*aEvent*/ ) override {}; + virtual void SAL_CALL windowNormalized(const css::lang::EventObject & /*aEvent*/ ) override {}; + + // XEventListener + + virtual void SAL_CALL disposing(const css::lang::EventObject & aEvent) override; + + // XActionLockable + + virtual sal_Bool SAL_CALL isActionLocked() override; + virtual void SAL_CALL addActionLock() override; + virtual void SAL_CALL removeActionLock() override; + virtual void SAL_CALL setActionLocks(sal_Int16 nLock) override; + virtual sal_Int16 SAL_CALL resetActionLocks() override; + + // XCloseable + + virtual void SAL_CALL close(sal_Bool bDeliverOwnership) override; + + // XCloseBroadcaster + + 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; + + // XTitle + + virtual OUString SAL_CALL getTitle() override; + virtual void SAL_CALL setTitle(const OUString & sTitle) override; + + // XTitleChangeBroadcaster + + virtual void SAL_CALL addTitleChangeListener(const css::uno::Reference < css::frame::XTitleChangeListener > & xListener) override; + virtual void SAL_CALL removeTitleChangeListener(const css::uno::Reference < css::frame::XTitleChangeListener > & xListenr) override; + + // XFrame2 attributes + + virtual css::uno::Reference < css::container::XNameContainer > SAL_CALL getUserDefinedAttributes() override; + + virtual css::uno::Reference < css::frame::XDispatchRecorderSupplier > SAL_CALL getDispatchRecorderSupplier() override; + virtual void SAL_CALL setDispatchRecorderSupplier(const css::uno::Reference < css::frame::XDispatchRecorderSupplier > & ) override; + + virtual css::uno::Reference < css::uno::XInterface > SAL_CALL getLayoutManager() override; + virtual void SAL_CALL setLayoutManager(const css::uno::Reference < css::uno::XInterface > & ) override; + + // XPropertySet + virtual css::uno::Reference < css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo() override; + + virtual void SAL_CALL setPropertyValue(const OUString & sProperty, const css::uno::Any & aValue) override; + + virtual css::uno::Any SAL_CALL getPropertyValue(const OUString & sProperty) override; + + virtual void SAL_CALL addPropertyChangeListener( + const OUString & sProperty, + const css::uno::Reference < css::beans::XPropertyChangeListener > & xListener) override; + + virtual void SAL_CALL removePropertyChangeListener( + const OUString & sProperty, + const css::uno::Reference < css::beans::XPropertyChangeListener > & xListener) override; + + virtual void SAL_CALL addVetoableChangeListener( + const OUString & sProperty, + const css::uno::Reference < css::beans::XVetoableChangeListener > & xListener) override; + + virtual void SAL_CALL removeVetoableChangeListener( + const OUString & sProperty, + const css::uno::Reference < css::beans::XVetoableChangeListener > & xListener) override; + + // XPropertySetInfo + virtual css::uno::Sequence < css::beans::Property > SAL_CALL getProperties() override; + + virtual css::beans::Property SAL_CALL getPropertyByName(const OUString & sName) override; + + virtual sal_Bool SAL_CALL hasPropertyByName(const OUString & sName) override; + + +private: + + void impl_setPropertyValue(sal_Int32 nHandle, + const css::uno::Any& aValue); + + css::uno::Any impl_getPropertyValue(sal_Int32 nHandle); + + /** set a new owner for this helper. + * + * This owner is used as source for all broadcasted events. + * Further we hold it weak, because we don't wish to be disposed() .-) + */ + void impl_setPropertyChangeBroadcaster(const css::uno::Reference< css::uno::XInterface >& xBroadcaster); + + /** add a new property info to the set of supported ones. + * + * @param aProperty + * describes the new property. + * + * @throw [css::beans::PropertyExistException] + * if a property with the same name already exists. + * + * Note: The consistence of the whole set of properties is not checked here. + * Means e.g. ... a handle which exists more than once is not detected. + * The owner of this class has to be sure, that every new property does + * not clash with any existing one. + */ + void impl_addPropertyInfo(const css::beans::Property& aProperty); + + /** mark the object as "dead". + */ + void impl_disablePropertySet(); + + bool impl_existsVeto(const css::beans::PropertyChangeEvent& aEvent); + + void impl_notifyChangeListener(const css::beans::PropertyChangeEvent& aEvent); + + /*-**************************************************************************************************** + @short helper methods + @descr Follow methods are needed at different points of our code (more than ones!). + + @attention Threadsafe methods are signed by "implts_..."! + *//*-*****************************************************************************************************/ + + // threadsafe + void implts_sendFrameActionEvent ( const css::frame::FrameAction& aAction ); + void implts_resizeComponentWindow ( ); + void implts_setIconOnWindow ( ); + void implts_startWindowListening ( ); + void implts_stopWindowListening ( ); + void implts_checkSuicide ( ); + void implts_forgetSubFrames ( ); + + // non threadsafe + void impl_checkMenuCloser ( ); + void impl_setCloser ( const css::uno::Reference< css::frame::XFrame2 >& xFrame , bool bState ); + + void disableLayoutManager(const css::uno::Reference< css::frame::XLayoutManager2 >& xLayoutManager); + + void checkDisposed() { + osl::MutexGuard g(rBHelper.rMutex); + if (rBHelper.bInDispose || rBHelper.bDisposed) { + throw css::lang::DisposedException("Frame disposed"); + } + } + +// variables +// -threadsafe by SolarMutex + + /// reference to factory, which has create this instance + css::uno::Reference< css::uno::XComponentContext > m_xContext; + /// reference to factory helper to create status indicator objects + css::uno::Reference< css::task::XStatusIndicatorFactory > m_xIndicatorFactoryHelper; + /// points to an external set progress, which should be used instead of the internal one. + css::uno::WeakReference< css::task::XStatusIndicator > m_xIndicatorInterception; + /// helper for XDispatch/Provider and interception interfaces + rtl::Reference< InterceptionHelper > m_xDispatchHelper; + /// helper for XFrames, XIndexAccess and XElementAccess interfaces + css::uno::Reference< css::frame::XFrames > m_xFramesHelper; + /// container for ALL Listener + comphelper::OMultiTypeInterfaceContainerHelper2 m_aListenerContainer; + /// parent of this frame + css::uno::Reference< css::frame::XFramesSupplier > m_xParent; + /// containerwindow of this frame for embedded components + css::uno::Reference< css::awt::XWindow > m_xContainerWindow; + /// window of the actual component + css::uno::Reference< css::awt::XWindow > m_xComponentWindow; + /// controller of the actual frame + css::uno::Reference< css::frame::XController > m_xController; + /// listen to drag & drop + css::uno::Reference< css::datatransfer::dnd::XDropTargetListener > m_xDropTargetListener; + /// state, if I am a member of active path in tree or I have the focus or... + EActiveState m_eActiveState; + /// name of this frame + OUString m_sName; + /// frame has no parent or the parent is a task or the desktop + bool m_bIsFrameTop; + /// due to FrameActionEvent + bool m_bConnected; + sal_Int16 m_nExternalLockCount; + /// is used for dispatch recording and will be set/get from outside. Frame provide it only! + css::uno::Reference< css::frame::XDispatchRecorderSupplier > m_xDispatchRecorderSupplier; + /// ref counted class to support disabling commands defined by configuration file + SvtCommandOptions m_aCommandOptions; + /// in case of CloseVetoException on method close() was thrown by ourself - we must close ourself later if no internal processes are running + bool m_bSelfClose; + /// indicates, if this frame is used in hidden mode or not + bool m_bIsHidden; + /// The container window has WindowExtendedStyle::DocHidden set. + bool m_bDocHidden = false; + /// is used to layout the child windows of the frame. + css::uno::Reference< css::frame::XLayoutManager2 > m_xLayoutManager; + css::uno::Reference< css::frame::XDispatchInformationProvider > m_xDispatchInfoHelper; + css::uno::Reference< css::frame::XTitle > m_xTitleHelper; + + std::unique_ptr m_pWindowCommandDispatch; + + typedef std::unordered_map TPropInfoHash; + TPropInfoHash m_lProps; + + comphelper::OMultiTypeInterfaceContainerHelperVar3 m_lSimpleChangeListener; + comphelper::OMultiTypeInterfaceContainerHelperVar3 m_lVetoChangeListener; + + // hold it weak ... otherwise this helper has to be "killed" explicitly .-) + css::uno::WeakReference< css::uno::XInterface > m_xBroadcaster; + + FrameContainer m_aChildFrameContainer; /// array of child frames + /** + * URL of the file that is being loaded, when the load already started by we have no controller + * yet. + */ + OUString m_aURL; +}; + + +/*-**************************************************************************************************** + @short standard constructor to create instance by factory + @descr This constructor initialize a new instance of this class by valid factory, + and will be set valid values on his member and baseclasses. + + @attention a) Don't use your own reference during a UNO-Service-ctor! There is no guarantee, that you + will get over this. (e.g. using of your reference as parameter to initialize some member) + Do such things in DEFINE_INIT_SERVICE() method, which is called automatically after your ctor!!! + b) Baseclass OBroadcastHelper is a typedef in namespace cppu! + The microsoft compiler has some problems to handle it right BY using namespace explicitly ::cppu::OBroadcastHelper. + If we write it without a namespace or expand the typedef to OBroadcastHelperVar<...> -> it will be OK!? + I don't know why! (other compiler not tested .. but it works!) + + @seealso method DEFINE_INIT_SERVICE() + + @param xContext is the multi service manager, which creates this instance. + The value must be different from NULL! + @onerror ASSERT in debug version or nothing in release version. +*//*-*****************************************************************************************************/ +XFrameImpl::XFrameImpl( css::uno::Reference< css::uno::XComponentContext > xContext ) + : PartialWeakComponentImplHelper(m_aMutex) + // init member + , m_xContext (std::move( xContext )) + , m_aListenerContainer ( m_aMutex ) + , m_eActiveState ( E_INACTIVE ) + , m_bIsFrameTop ( true ) // I think we are top without a parent ... and there is no parent yet! + , m_bConnected ( false ) // There exist no component inside of use => sal_False, we are not connected! + , m_nExternalLockCount ( 0 ) + , m_bSelfClose ( false ) // Important! + , m_bIsHidden ( true ) + , m_lSimpleChangeListener ( m_aMutex ) + , m_lVetoChangeListener ( m_aMutex ) +{ +} + +void XFrameImpl::initListeners() +{ + css::uno::Reference< css::uno::XInterface > xThis(static_cast< ::cppu::OWeakObject* >(this), css::uno::UNO_QUERY_THROW); + + // Initialize a new dispatchhelper-object to handle dispatches. + // We use these helper as slave for our interceptor helper ... not directly! + // But he is event listener on THIS instance! + rtl::Reference xDispatchProvider = new DispatchProvider( m_xContext, this ); + + m_xDispatchInfoHelper = new DispatchInformationProvider(m_xContext, this); + + // Initialize a new interception helper object to handle dispatches and implement an interceptor mechanism. + // Set created dispatch provider as slowest slave of it. + // Hold interception helper by reference only - not by pointer! + // So it's easier to destroy it. + m_xDispatchHelper = new InterceptionHelper( this, xDispatchProvider ); + + // Initialize a new XFrames-helper-object to handle XIndexAccess and XElementAccess. + // We hold member as reference ... not as pointer too! + // Attention: We share our frame container with this helper. Container is threadsafe himself ... So I think we can do that. + // But look on dispose() for right order of deinitialization. + m_xFramesHelper = new OFrames( this, &m_aChildFrameContainer ); + + // Initialize the drop target listener. + // We hold member as reference ... not as pointer too! + m_xDropTargetListener = new OpenFileDropTargetListener( m_xContext, this ); + + // Safe impossible cases + // We can't work without these helpers! + SAL_WARN_IF( !xDispatchProvider.is(), "fwk.frame", "XFrameImpl::XFrameImpl(): Slowest slave for dispatch- and interception helper " + "is not valid. XDispatchProvider, XDispatch, XDispatchProviderInterception are not full supported!" ); + SAL_WARN_IF( !m_xDispatchHelper.is(), "fwk.frame", "XFrameImpl::XFrameImpl(): Interception helper is not valid. XDispatchProvider, " + "XDispatch, XDispatchProviderInterception are not full supported!" ); + SAL_WARN_IF( !m_xFramesHelper.is(), "fwk.frame", "XFrameImpl::XFrameImpl(): Frames helper is not valid. XFrames, " + "XIndexAccess and XElementAccess are not supported!" ); + SAL_WARN_IF( !m_xDropTargetListener.is(), "fwk.frame", "XFrameImpl::XFrameImpl(): DropTarget helper is not valid. " + "Drag and drop without functionality!" ); + + // establish notifies for changing of "disabled commands" configuration during runtime + m_aCommandOptions.EstablishFrameCallback(this); + + // Create an initial layout manager + // Create layout manager and connect it to the newly created frame + m_xLayoutManager = css::frame::LayoutManager::create(m_xContext); + + // set information about all supported properties + impl_setPropertyChangeBroadcaster(static_cast< css::frame::XFrame* >(this)); + impl_addPropertyInfo( + css::beans::Property( + FRAME_PROPNAME_ASCII_DISPATCHRECORDERSUPPLIER, + FRAME_PROPHANDLE_DISPATCHRECORDERSUPPLIER, + cppu::UnoType::get(), + css::beans::PropertyAttribute::TRANSIENT)); + impl_addPropertyInfo( + css::beans::Property( + FRAME_PROPNAME_ASCII_INDICATORINTERCEPTION, + FRAME_PROPHANDLE_INDICATORINTERCEPTION, + cppu::UnoType::get(), + css::beans::PropertyAttribute::TRANSIENT)); + impl_addPropertyInfo( + css::beans::Property( + FRAME_PROPNAME_ASCII_ISHIDDEN, + FRAME_PROPHANDLE_ISHIDDEN, + cppu::UnoType::get(), + css::beans::PropertyAttribute::TRANSIENT | css::beans::PropertyAttribute::READONLY)); + impl_addPropertyInfo( + css::beans::Property( + FRAME_PROPNAME_ASCII_LAYOUTMANAGER, + FRAME_PROPHANDLE_LAYOUTMANAGER, + cppu::UnoType::get(), + css::beans::PropertyAttribute::TRANSIENT)); + impl_addPropertyInfo( + css::beans::Property( + FRAME_PROPNAME_ASCII_TITLE, + FRAME_PROPHANDLE_TITLE, + cppu::UnoType::get(), + css::beans::PropertyAttribute::TRANSIENT)); + impl_addPropertyInfo(css::beans::Property(FRAME_PROPNAME_ASCII_URL, FRAME_PROPHANDLE_URL, + cppu::UnoType::get(), + css::beans::PropertyAttribute::TRANSIENT)); +} + +/*-************************************************************************************************************ + @interface XComponentLoader + @short try to load given URL into a task + @descr You can give us some information about the content, which you will load into a frame. + We search or create this target for you, make a type detection of given URL and try to load it. + As result of this operation we return the new created component or nothing, if loading failed. + @param "sURL" , URL, which represent the content + @param "sTargetFrameName" , name of target frame or special value like "_self", "_blank" ... + @param "nSearchFlags" , optional arguments for frame search, if target is not a special one + @param "lArguments" , optional arguments for loading + @return A valid component reference, if loading was successful. + A null reference otherwise. + + @onerror We return a null reference. + @threadsafe yes +*//*-*************************************************************************************************************/ +css::uno::Reference< css::lang::XComponent > SAL_CALL XFrameImpl::loadComponentFromURL( + const OUString& sURL, + const OUString& sTargetFrameName, + sal_Int32 nSearchFlags, + const css::uno::Sequence< css::beans::PropertyValue >& lArguments ) +{ + checkDisposed(); + + css::uno::Reference< css::frame::XComponentLoader > xThis(this); + + utl::MediaDescriptor aDescriptor(lArguments); + 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 + // another thread, leading to an std::abort() at the end. + SolarMutexGuard g; + + return vcl::solarthread::syncExecute(std::bind(&LoadEnv::loadComponentFromURL, xThis, + m_xContext, sURL, sTargetFrameName, + nSearchFlags, lArguments)); + } + else + return LoadEnv::loadComponentFromURL(xThis, m_xContext, sURL, sTargetFrameName, + nSearchFlags, lArguments); +} + +/*-**************************************************************************************************** + @short return access to append or remove children on desktop + @descr We don't implement these interface directly. We use a helper class to do this. + If you wish to add or delete children to/from the container, call these method to get + a reference to the helper. + + @seealso class OFrames + @return A reference to the helper which answer your queries. + + @onerror A null reference is returned. +*//*-*****************************************************************************************************/ +css::uno::Reference< css::frame::XFrames > SAL_CALL XFrameImpl::getFrames() +{ + checkDisposed(); + + SolarMutexGuard g; + // Return access to all child frames to caller. + // Our childframe container is implemented in helper class OFrames and used as a reference m_xFramesHelper! + return m_xFramesHelper; +} + +/*-**************************************************************************************************** + @short get the current active child frame + @descr It must be a frameto. Direct children of a frame are frames only! No task or desktop is accepted. + We don't save this information directly in this class. We use our container-helper + to do that. + + @seealso class OFrameContainer + @seealso method setActiveFrame() + @return A reference to our current active childframe, if anyone exist. + @return A null reference, if nobody is active. + + @onerror A null reference is returned. +*//*-*****************************************************************************************************/ +css::uno::Reference< css::frame::XFrame > SAL_CALL XFrameImpl::getActiveFrame() +{ + checkDisposed(); + + SolarMutexGuard g; + // Return current active frame. + // This information is available on the container. + return m_aChildFrameContainer.getActive(); +} + +/*-**************************************************************************************************** + @short set the new active direct child frame + @descr It must be a frame to. Direct children of frame are frames only! No task or desktop is accepted. + We don't save this information directly in this class. We use our container-helper + to do that. + + @seealso class OFrameContainer + @seealso method getActiveFrame() + + @param "xFrame", reference to new active child. It must be an already existing child! + @onerror An assertion is thrown and element is ignored, if given frame isn't already a child of us. +*//*-*****************************************************************************************************/ +void SAL_CALL XFrameImpl::setActiveFrame( const css::uno::Reference< css::frame::XFrame >& xFrame ) +{ + checkDisposed(); + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + SolarMutexResettableGuard aWriteLock; + + // Copy necessary member for threadsafe access! + // m_aChildFrameContainer is threadsafe himself and he live if we live!!! + css::uno::Reference< css::frame::XFrame > xActiveChild = m_aChildFrameContainer.getActive(); + EActiveState eActiveState = m_eActiveState; + + aWriteLock.clear(); + /* UNSAFE AREA --------------------------------------------------------------------------------------------- */ + + // Don't work, if "new" active frame isn't different from current one! + // (xFrame==NULL is allowed to UNSET it!) + if( xActiveChild != xFrame ) + { + // ... otherwise set new and deactivate old one. + m_aChildFrameContainer.setActive( xFrame ); + if ( + ( eActiveState != E_INACTIVE ) && + xActiveChild.is() + ) + { + xActiveChild->deactivate(); + } + } + + if( xFrame.is() ) + { + // If last active frame had focus ... + // ... reset state to ACTIVE and send right FrameActionEvent for focus lost. + if( eActiveState == E_FOCUS ) + { + aWriteLock.reset(); + eActiveState = E_ACTIVE; + m_eActiveState = eActiveState; + aWriteLock.clear(); + implts_sendFrameActionEvent( css::frame::FrameAction_FRAME_UI_DEACTIVATING ); + } + + // If last active frame was active ... + // but new one is not it ... + // ... set it as active one. + if ( eActiveState == E_ACTIVE && !xFrame->isActive() ) + { + xFrame->activate(); + } + } + else + // If this frame is active and has no active subframe anymore it is UI active too + if( eActiveState == E_ACTIVE ) + { + aWriteLock.reset(); + eActiveState = E_FOCUS; + m_eActiveState = eActiveState; + aWriteLock.clear(); + implts_sendFrameActionEvent( css::frame::FrameAction_FRAME_UI_ACTIVATED ); + } +} + +/*-**************************************************************************************************** + initialize new created layout manager +**/ +void lcl_enableLayoutManager(const css::uno::Reference< css::frame::XLayoutManager2 >& xLayoutManager, + const css::uno::Reference< css::frame::XFrame >& xFrame ) +{ + // Provide container window to our layout manager implementation + xLayoutManager->attachFrame(xFrame); + + xFrame->addFrameActionListener(xLayoutManager); + + rtl::Reference xDockingAreaAcceptor = new DockingAreaDefaultAcceptor(xFrame); + xLayoutManager->setDockingAreaAcceptor(xDockingAreaAcceptor); +} + +/*-**************************************************************************************************** + deinitialize layout manager +**/ +void XFrameImpl::disableLayoutManager(const css::uno::Reference< css::frame::XLayoutManager2 >& xLayoutManager) +{ + removeFrameActionListener(xLayoutManager); + xLayoutManager->setDockingAreaAcceptor(css::uno::Reference< css::ui::XDockingAreaAcceptor >()); + xLayoutManager->attachFrame(css::uno::Reference< css::frame::XFrame >()); +} + +/*-**************************************************************************************************** + @short initialize frame instance + @descr A frame needs a window. This method set a new one ... but should called one times only! + We use this window to listen for window events and forward it to our set component. + It's used as parent of component window too. + + @seealso method getContainerWindow() + @seealso method setComponent() + @seealso member m_xContainerWindow + + @param "xWindow", reference to new container window - must be valid! + @onerror We do nothing. +*//*-*****************************************************************************************************/ +void SAL_CALL XFrameImpl::initialize( const css::uno::Reference< css::awt::XWindow >& xWindow ) +{ + /* UNSAFE AREA --------------------------------------------------------------------------------------------- */ + if (!xWindow.is()) + throw css::uno::RuntimeException( + "XFrameImpl::initialize() called without a valid container window reference.", + static_cast< css::frame::XFrame* >(this)); + + checkDisposed(); + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + SolarMutexResettableGuard aWriteLock; + + // This must be the first call of this method! + // We should initialize our object and open it for working. + if ( m_xContainerWindow.is() ) + throw css::uno::RuntimeException( + "XFrameImpl::initialized() is called more than once, which is not useful nor allowed.", + static_cast< css::frame::XFrame* >(this)); + + // Set the new window. + m_xContainerWindow = xWindow; + + // if window is initially visible, we will never get a windowShowing event + VclPtr pWindow = VCLUnoHelper::GetWindow(xWindow); + if (pWindow) + { + if (pWindow->IsVisible()) + m_bIsHidden = false; + m_bDocHidden + = static_cast(pWindow->GetExtendedStyle() & WindowExtendedStyle::DocHidden); + } + + css::uno::Reference< css::frame::XLayoutManager2 > xLayoutManager = m_xLayoutManager; + + // Release lock ... because we call some impl methods, which are threadsafe by himself. + // If we hold this lock - we will produce our own deadlock! + aWriteLock.clear(); + /* UNSAFE AREA --------------------------------------------------------------------------------------------- */ + + // Avoid enabling the layout manager for hidden frames: it's expensive and + // provides little value. + if (xLayoutManager.is() && !m_bDocHidden) + lcl_enableLayoutManager(xLayoutManager, this); + + // create progress helper + css::uno::Reference< css::frame::XFrame > xThis (this); + css::uno::Reference< css::task::XStatusIndicatorFactory > xIndicatorFactory = + css::task::StatusIndicatorFactory::createWithFrame(m_xContext, xThis, + false/*DisableReschedule*/, true/*AllowParentShow*/ ); + + // SAFE -> ---------------------------------- + aWriteLock.reset(); + m_xIndicatorFactoryHelper = xIndicatorFactory; + aWriteLock.clear(); + // <- SAFE ---------------------------------- + + // Start listening for events after setting it on helper class ... + // So superfluous messages are filtered to NULL :-) + implts_startWindowListening(); + + m_pWindowCommandDispatch.reset(new WindowCommandDispatch(m_xContext, this)); + + // Initialize title functionality + m_xTitleHelper = new TitleHelper( m_xContext, xThis, nullptr ); +} + +/*-**************************************************************************************************** + @short returns current set container window + @descr The ContainerWindow property is used as a container for the component + in this frame. So this object implements a container interface too. + The instantiation of the container window is done by the user of this class. + The frame is the owner of its container window. + + @seealso method initialize() + @return A reference to current set containerwindow. + + @onerror A null reference is returned. +*//*-*****************************************************************************************************/ +css::uno::Reference< css::awt::XWindow > SAL_CALL XFrameImpl::getContainerWindow() +{ + SolarMutexGuard g; + return m_xContainerWindow; +} + +/*-**************************************************************************************************** + @short set parent frame + @descr We need a parent to support some functionality! e.g. findFrame() + By the way we use the chance to set an internal information about our top state. + So we must not check this information during every isTop() call. + We are top, if our parent is the desktop instance or we haven't any parent. + + @seealso getCreator() + @seealso findFrame() + @seealso isTop() + @seealso m_bIsFrameTop + + @param xCreator + valid reference to our new owner frame, which should implement a supplier interface + + @threadsafe yes +*//*-*****************************************************************************************************/ +void SAL_CALL XFrameImpl::setCreator( const css::uno::Reference< css::frame::XFramesSupplier >& xCreator ) +{ + checkDisposed(); + + /* SAFE { */ + { + SolarMutexGuard aWriteLock; + m_xParent = xCreator; + } + /* } SAFE */ + + css::uno::Reference< css::frame::XDesktop > xIsDesktop( xCreator, css::uno::UNO_QUERY ); + m_bIsFrameTop = ( xIsDesktop.is() || ! xCreator.is() ); +} + +/*-**************************************************************************************************** + @short returns current parent frame + @descr The Creator is the parent frame container. If it is NULL, the frame is the uppermost one. + + @seealso method setCreator() + @return A reference to current set parent frame container. + + @onerror A null reference is returned. +*//*-*****************************************************************************************************/ +css::uno::Reference< css::frame::XFramesSupplier > SAL_CALL XFrameImpl::getCreator() +{ + checkDisposed(); + SolarMutexGuard g; + return m_xParent; +} + +/*-**************************************************************************************************** + @short returns current set name of frame + @descr This name is used to find target of findFrame() or queryDispatch() calls. + + @seealso method setName() + @return Current set name of frame. + + @onerror An empty string is returned. +*//*-*****************************************************************************************************/ +OUString SAL_CALL XFrameImpl::getName() +{ + SolarMutexGuard g; + return m_sName; +} + +/*-**************************************************************************************************** + @short set new name for frame + @descr This name is used to find target of findFrame() or queryDispatch() calls. + + @attention Special names like "_blank", "_self" aren't allowed... + "_beamer" excepts this rule! + + @seealso method getName() + + @param "sName", new frame name. + @onerror We do nothing. +*//*-*****************************************************************************************************/ +void SAL_CALL XFrameImpl::setName( const OUString& sName ) +{ + SolarMutexGuard g; + // Set new name... but look for invalid special target names! + // They are not allowed to set. + if (TargetHelper::isValidNameForFrame(sName)) + m_sName = sName; +} + +/*-**************************************************************************************************** + @short search for frames + @descr This method searches for a frame with the specified name. + Frames may contain other frames (e.g. a frameset) and may + be contained in other frames. This hierarchy is searched by + this method. + First some special names are taken into account, i.e. "", + "_self", "_top", "_blank" etc. The nSearchFlags are ignored + when comparing these names with sTargetFrameName, further steps are + controlled by the search flags. If allowed, the name of the frame + itself is compared with the desired one, then ( again if allowed ) + the method findFrame() is called for all children, for siblings + and as last for the parent frame. + If no frame with the given name is found until the top frames container, + a new top one is created, if this is allowed by a special + flag. The new frame also gets the desired name. + + @param sTargetFrameName + special names (_blank, _self) or real name of target frame + @param nSearchFlags + optional flags which regulate search for non special target frames + + @return A reference to found or may be new created frame. + @threadsafe yes +*//*-*****************************************************************************************************/ +css::uno::Reference< css::frame::XFrame > SAL_CALL XFrameImpl::findFrame( const OUString& sTargetFrameName, + sal_Int32 nSearchFlags ) +{ + css::uno::Reference< css::frame::XFrame > xTarget; + + // 0) Ignore wrong parameter! + // We don't support search for following special targets. + // If we reject this requests - we must not check for such names + // in following code again and again. If we do not so -wrong + // search results can occur! + + if ( sTargetFrameName == SPECIALTARGET_DEFAULT ) // valid for dispatches - not for findFrame()! + { + return nullptr; + } + + // I) check for special defined targets first which must be handled exclusive. + // force using of "if() else if() ..." + + // get threadsafe some necessary member which are necessary for following functionality + /* SAFE { */ + SolarMutexResettableGuard aReadLock; + css::uno::Reference< css::frame::XFrame > xParent = m_xParent; + bool bIsTopFrame = m_bIsFrameTop; + bool bIsTopWindow = WindowHelper::isTopWindow(m_xContainerWindow); + aReadLock.clear(); + /* } SAFE */ + + // I.I) "_blank" + // Not allowed for a normal frame - but for the desktop. + // Use helper class to do so. It use the desktop automatically. + + if ( sTargetFrameName==SPECIALTARGET_BLANK ) + { + TaskCreator aCreator(m_xContext); + xTarget = aCreator.createTask(sTargetFrameName, utl::MediaDescriptor()); + } + + // I.II) "_parent" + // It doesn't matter if we have a valid parent or not. User ask for him and get it. + // An empty result is a valid result too. + + else if ( sTargetFrameName==SPECIALTARGET_PARENT ) + { + xTarget = xParent; + } + + // I.III) "_top" + // If we are not the top frame in this hierarchy, we must forward request to our parent. + // Otherwise we must return ourself. + + else if ( sTargetFrameName==SPECIALTARGET_TOP ) + { + if (bIsTopFrame) + xTarget = this; + else if (xParent.is()) // If we are not top - the parent MUST exist. But may it's better to check it again .-) + xTarget = xParent->findFrame(SPECIALTARGET_TOP,0); + } + + // I.IV) "_self", "" + // This mean this frame in every case. + + else if ( + ( sTargetFrameName==SPECIALTARGET_SELF ) || + ( sTargetFrameName.isEmpty() ) + ) + { + xTarget = this; + } + + // I.V) "_beamer" + // This is a special sub frame of any task. We must return it if we found it on our direct children + // or create it there if it not already exists. + // Note: Such beamer exists for task(top) frames only! + + else if ( sTargetFrameName==SPECIALTARGET_BEAMER ) + { + // We are a task => search or create the beamer + if (bIsTopWindow) + { + xTarget = m_aChildFrameContainer.searchOnDirectChildrens(SPECIALTARGET_BEAMER); + if ( ! xTarget.is() ) + { + /* TODO + Creation not supported yet! + Wait for new layout manager service because we can't plug it + inside already opened document of this frame... + */ + } + } + // We aren't a task => forward request to our parent or ignore it. + else if (xParent.is()) + xTarget = xParent->findFrame(SPECIALTARGET_BEAMER,0); + } + + else + { + + // II) otherwise use optional given search flags + // force using of combinations of such flags. means no "else" part of use if() statements. + // But we ust break further searches if target was already found. + // Order of using flags is fix: SELF - CHILDREN - SIBLINGS - PARENT + // TASK and CREATE are handled special. + + // get threadsafe some necessary member which are necessary for following functionality + /* SAFE { */ + aReadLock.reset(); + OUString sOwnName = m_sName; + aReadLock.clear(); + /* } SAFE */ + + // II.I) SELF + // Check for right name. If it's the searched one return ourself - otherwise + // ignore this flag. + + if ( + (nSearchFlags & css::frame::FrameSearchFlag::SELF) && + (sOwnName == sTargetFrameName ) + ) + { + xTarget = this; + } + + // II.II) CHILDREN + // Search on all children for the given target name. + // An empty name value can't occur here - because it must be already handled as "_self" + // before. Used helper function of container doesn't create any frame. + // It makes a deep search only. + + if ( + ( ! xTarget.is() ) && + (nSearchFlags & css::frame::FrameSearchFlag::CHILDREN) + ) + { + xTarget = m_aChildFrameContainer.searchOnAllChildrens(sTargetFrameName); + } + + // II.III) TASKS + // This is a special flag. It regulate search on this task tree only or allow search on + // all other ones (which are sibling trees of us) too. + // Upper search must stop at this frame if we are the topest one and the TASK flag is not set + // or we can ignore it if we have no valid parent. + + if ( + ( bIsTopFrame && (nSearchFlags & css::frame::FrameSearchFlag::TASKS) ) || + ( ! bIsTopFrame ) + ) + { + + // II.III.I) SIBLINGS + // Search on all our direct siblings - means all children of our parent. + // Use this flag in combination with TASK. We must suppress such upper search if + // user has not set it and if we are a top frame. + // Attention: Don't forward this request to our parent as a findFrame() call. + // In such case we must protect us against recursive calls. + // Use snapshot of our parent. But don't use queryFrames() of XFrames interface. + // Because it's return all siblings and all her children including our children too + // if we call it with the CHILDREN flag. We don't need that - we need the direct container + // items of our parent only to start searches there. So we must use the container interface + // XIndexAccess instead of XFrames. + + if ( + ( ! xTarget.is() ) && + (nSearchFlags & css::frame::FrameSearchFlag::SIBLINGS) && + ( xParent.is() ) // search on siblings is impossible without a parent + ) + { + css::uno::Reference< css::frame::XFramesSupplier > xSupplier( xParent, css::uno::UNO_QUERY ); + if (xSupplier.is()) + { + css::uno::Reference< css::container::XIndexAccess > xContainer = xSupplier->getFrames(); + if (xContainer.is()) + { + sal_Int32 nCount = xContainer->getCount(); + for( sal_Int32 i=0; i xSibling; + if ( + // control unpacking + ( !(xContainer->getByIndex(i)>>=xSibling) ) || + // check for valid items + ( ! xSibling.is() ) || + // ignore ourself! (We are a part of this container too - but search on our children was already done.) + ( xSibling==static_cast< ::cppu::OWeakObject* >(this) ) + ) + { + continue; + } + + // Don't allow upper search here! Use right flags to regulate it. + // And allow deep search on children only - if it was allowed for us too. + sal_Int32 nRightFlags = css::frame::FrameSearchFlag::SELF; + if (nSearchFlags & css::frame::FrameSearchFlag::CHILDREN) + nRightFlags |= css::frame::FrameSearchFlag::CHILDREN; + xTarget = xSibling->findFrame(sTargetFrameName, nRightFlags ); + // perform search be breaking further search if a result exist. + if (xTarget.is()) + break; + } + } + } + } + + // II.III.II) PARENT + // Forward search to our parent (if he exists.) + // To prevent us against recursive and superfluous calls (which can occur if we allow him + // to search on his children too) we must change used search flags. + + if ( + ( ! xTarget.is() ) && + (nSearchFlags & css::frame::FrameSearchFlag::PARENT) && + ( xParent.is() ) + ) + { + if (xParent->getName() == sTargetFrameName) + xTarget = xParent; + else + { + sal_Int32 nRightFlags = nSearchFlags & ~css::frame::FrameSearchFlag::CHILDREN; + xTarget = xParent->findFrame(sTargetFrameName, nRightFlags); + } + } + } + + // II.IV) CREATE + // If we haven't found any valid target frame by using normal flags - but user allowed us to create + // a new one ... we should do that. Used TaskCreator use Desktop instance automatically as parent! + + if ( + ( ! xTarget.is() ) && + (nSearchFlags & css::frame::FrameSearchFlag::CREATE) + ) + { + TaskCreator aCreator(m_xContext); + xTarget = aCreator.createTask(sTargetFrameName, utl::MediaDescriptor()); + } + } + + return xTarget; +} + +/*-**************************************************************************************************** + @descr Returns sal_True, if this frame is a "top frame", otherwise sal_False. + The "m_bIsFrameTop" member must be set in the ctor or setCreator() method. + A top frame is a member of the top frame container or a member of the + task frame container. Both containers can create new frames if the findFrame() + method of their css::frame::XFrame interface is called with a frame name not yet known. + + @seealso ctor + @seealso method setCreator() + @seealso method findFrame() + @return true, if is it a top frame ... false otherwise. + + @onerror No error should occur! +*//*-*****************************************************************************************************/ +sal_Bool SAL_CALL XFrameImpl::isTop() +{ + checkDisposed(); + SolarMutexGuard g; + // This information is set in setCreator(). + // We are top, if our parent is a task or the desktop or if no parent exist! + return m_bIsFrameTop; +} + +/*-**************************************************************************************************** + @short activate frame in hierarchy + @descr This feature is used to mark active paths in our frame hierarchy. + You can be a listener for this event to react for it ... change some internal states or something else. + + @seealso method deactivate() + @seealso method isActivate() + @seealso enum EActiveState + @seealso listener mechanism +*//*-*****************************************************************************************************/ +void SAL_CALL XFrameImpl::activate() +{ + checkDisposed(); + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + SolarMutexResettableGuard aWriteLock; + + // Copy necessary member and free the lock. + // It's not necessary for m_aChildFrameContainer ... because + // he is threadsafe himself and live if we live. + css::uno::Reference< css::frame::XFrame > xActiveChild = m_aChildFrameContainer.getActive(); + css::uno::Reference< css::frame::XFramesSupplier > xParent = m_xParent; + css::uno::Reference< css::frame::XFrame > xThis(this); + EActiveState eState = m_eActiveState; + + aWriteLock.clear(); + /* UNSAFE AREA --------------------------------------------------------------------------------------------- */ + + // 1) If I am not active before... + if( eState == E_INACTIVE ) + { + // ... do it then. + aWriteLock.reset(); + eState = E_ACTIVE; + m_eActiveState = eState; + aWriteLock.clear(); + // Deactivate sibling path and forward activation to parent ... if any parent exist! + if( xParent.is() ) + { + // Every time set THIS frame as active child of parent and activate it. + // We MUST have a valid path from bottom to top as active path! + // But we must deactivate the old active sibling path first. + + // Attention: Deactivation of an active path, deactivate the whole path ... from bottom to top! + // But we wish to deactivate founded sibling-tree only. + // [ see deactivate() / step 4) for further information! ] + + xParent->setActiveFrame( xThis ); + + // Then we can activate from here to top. + // Attention: We are ACTIVE now. And the parent will call activate() at us! + // But we do nothing then! We are already activated. + xParent->activate(); + } + // It's necessary to send event NOW - not before. + // Activation goes from bottom to top! + // That's the reason to activate parent first and send event now. + implts_sendFrameActionEvent( css::frame::FrameAction_FRAME_ACTIVATED ); + } + + // 2) I was active before or current activated and there is a path from here to bottom, who CAN be active. + // But our direct child of path is not active yet. + // (It can be, if activation occur in the middle of a current path!) + // In these case we activate path to bottom to set focus on right frame! + if ( eState == E_ACTIVE && xActiveChild.is() && !xActiveChild->isActive() ) + { + xActiveChild->activate(); + } + + // 3) I was active before or current activated. But if I have no active child => I will get the focus! + if ( eState == E_ACTIVE && !xActiveChild.is() ) + { + aWriteLock.reset(); + eState = E_FOCUS; + m_eActiveState = eState; + aWriteLock.clear(); + implts_sendFrameActionEvent( css::frame::FrameAction_FRAME_UI_ACTIVATED ); + } +} + +/*-**************************************************************************************************** + @short deactivate frame in hierarchy + @descr This feature is used to deactivate paths in our frame hierarchy. + You can be a listener for this event to react for it... change some internal states or something else. + + @seealso method activate() + @seealso method isActivate() + @seealso enum EActiveState + @seealso listener mechanism +*//*-*****************************************************************************************************/ +void SAL_CALL XFrameImpl::deactivate() +{ + checkDisposed(); + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + SolarMutexResettableGuard aWriteLock; + + // Copy necessary member and free the lock. + css::uno::Reference< css::frame::XFrame > xActiveChild = m_aChildFrameContainer.getActive(); + css::uno::Reference< css::frame::XFramesSupplier > xParent = m_xParent; + css::uno::Reference< css::frame::XFrame > xThis(this); + EActiveState eState = m_eActiveState; + + aWriteLock.clear(); + /* UNSAFE AREA --------------------------------------------------------------------------------------------- */ + + // Work only, if there something to do! + if( eState == E_INACTIVE ) + return; + + + // 1) Deactivate all active children. + if ( xActiveChild.is() && xActiveChild->isActive() ) + { + xActiveChild->deactivate(); + } + + // 2) If I have the focus - I will lost it now. + if( eState == E_FOCUS ) + { + // Set new state INACTIVE(!) and send message to all listener. + // Don't set ACTIVE as new state. This frame is deactivated for next time - due to activate(). + aWriteLock.reset(); + eState = E_ACTIVE; + m_eActiveState = eState; + aWriteLock.clear(); + implts_sendFrameActionEvent( css::frame::FrameAction_FRAME_UI_DEACTIVATING ); + } + + // 3) If I am active - I will be deactivated now. + if( eState == E_ACTIVE ) + { + // Set new state and send message to all listener. + aWriteLock.reset(); + eState = E_INACTIVE; + m_eActiveState = eState; + aWriteLock.clear(); + implts_sendFrameActionEvent( css::frame::FrameAction_FRAME_DEACTIVATING ); + } + + // 4) If there is a path from here to my parent... + // ... I am on the top or in the middle of deactivated subtree and action was started here. + // I must deactivate all frames from here to top, which are members of current path. + // Stop, if THESE frame not the active frame of our parent! + if ( xParent.is() && xParent->getActiveFrame() == xThis ) + { + // We MUST break the path - otherwise we will get the focus - not our parent! ... + // Attention: Our parent don't call us again - WE ARE NOT ACTIVE YET! + // [ see step 3 and condition "if ( m_eActiveState!=INACTIVE ) ..." in this method! ] + xParent->deactivate(); + } +} + +/*-**************************************************************************************************** + @short returns active state + @descr Call it to get information about current active state of this frame. + + @seealso method activate() + @seealso method deactivate() + @seealso enum EActiveState + @return true if active, false otherwise. + + @onerror No error should occur. +*//*-*****************************************************************************************************/ +sal_Bool SAL_CALL XFrameImpl::isActive() +{ + checkDisposed(); + SolarMutexGuard g; + return m_eActiveState == E_ACTIVE || m_eActiveState == E_FOCUS; +} + +/*-**************************************************************************************************** + @short ??? +*//*-*****************************************************************************************************/ +void SAL_CALL XFrameImpl::contextChanged() +{ + // Sometimes called during closing object... + // Impl-method is threadsafe himself! + // Send event to all listener for frame actions. + implts_sendFrameActionEvent( css::frame::FrameAction_CONTEXT_CHANGED ); +} + +/*-**************************************************************************************************** + @short set new component inside the frame + @descr A frame is a container for a component. Use this method to set, change or release it! + We accept null references! The xComponentWindow will be a child of our container window + and get all window events from us. + + @attention (a) A current set component can disagree with the suspend() request! + We don't set the new one and return with false then. + (b) It's possible to set: + (b1) a simple component here which supports the window only - no controller; + (b2) a full featured component which supports window and controller; + (b3) or both to NULL if outside code which to forget this component. + + @seealso method getComponentWindow() + @seealso method getController() + + @param xComponentWindow + valid reference to new component window which will be a child of internal container window + May for releasing. + @param xController + reference to new component controller + (may for releasing or setting of a simple component) + + @return if operation was successful, otherwise. + + @onerror We return . + @threadsafe yes +*//*-*****************************************************************************************************/ +sal_Bool SAL_CALL XFrameImpl::setComponent(const css::uno::Reference< css::awt::XWindow >& xComponentWindow, + const css::uno::Reference< css::frame::XController >& xController ) +{ + + // Ignore this HACK of sfx2! + // He call us with a valid controller without a valid window... that's not allowed! + if ( xController.is() && ! xComponentWindow.is() ) + return true; + + checkDisposed(); + + // Get threadsafe some copies of used members. + /* SAFE { */ + SolarMutexClearableGuard aReadLock; + css::uno::Reference< css::awt::XWindow > xContainerWindow = m_xContainerWindow; + css::uno::Reference< css::awt::XWindow > xOldComponentWindow = m_xComponentWindow; + css::uno::Reference< css::frame::XController > xOldController = m_xController; + VclPtr pOwnWindow = VCLUnoHelper::GetWindow( xContainerWindow ); + bool bHadFocus = pOwnWindow != nullptr && pOwnWindow->HasChildPathFocus(); + bool bWasConnected = m_bConnected; + aReadLock.clear(); + /* } SAFE */ + + // stop listening on old window + // May it produce some trouble. + // But don't forget to listen on new window again ... or reactivate listening + // if we reject this setComponent() request and leave this method without changing the old window. + implts_stopWindowListening(); + + // Notify all listener, that this component (if current one exist) will be unloaded. + if (bWasConnected) + implts_sendFrameActionEvent( css::frame::FrameAction_COMPONENT_DETACHING ); + + // otherwise release old component first + // Always release controller before releasing window, + // because controller may want to access its window! + // But check for real changes - may the new controller is the old one. + if ( + (xOldController.is() ) && + (xOldController != xController) + ) + { + /* ATTENTION + Don't suspend the old controller here. Because the outside caller must do that + by definition. We have to dispose it here only. + */ + + // Before we dispose this controller we should hide it inside this frame instance. + // We hold it alive for next calls by using xOldController! + /* SAFE {*/ + { + SolarMutexGuard aWriteLock; + m_xController = nullptr; + + if (m_xDispatchHelper) + { + rtl::Reference pDispatchProvider = m_xDispatchHelper->GetSlave(); + if (pDispatchProvider) + { + pDispatchProvider->ClearProtocolHandlers(); + } + } + } + /* } SAFE */ + + if (xOldController.is()) + { + try + { + xOldController->dispose(); + } + catch(const css::lang::DisposedException&) + {} + } + xOldController = nullptr; + } + + // Now it's time to release the component window. + // If controller wasn't released successfully - this code line shouldn't be reached. + // Because in case of "suspend()==false" we return immediately with false ... + // see before + // Check for real changes too. + if ( + (xOldComponentWindow.is() ) && + (xOldComponentWindow != xComponentWindow) + ) + { + /* SAFE { */ + { + SolarMutexGuard aWriteLock; + m_xComponentWindow = nullptr; + } + /* } SAFE */ + + if (xOldComponentWindow.is()) + { + try + { + xOldComponentWindow->dispose(); + } + catch(const css::lang::DisposedException&) + { + } + } + xOldComponentWindow = nullptr; + } + + // Now it's time to set the new component ... + // By the way - find out our new "load state" - means if we have a valid component inside. + /* SAFE { */ + SolarMutexResettableGuard aWriteLock; + m_xComponentWindow = xComponentWindow; + m_xController = xController; + + // Clear the URL on the frame itself, now that the controller has it. + m_aURL.clear(); + + m_bConnected = (m_xComponentWindow.is() || m_xController.is()); + bool bIsConnected = m_bConnected; + aWriteLock.clear(); + /* } SAFE */ + + // notifies all interest listener, that current component was changed or a new one was loaded + if (bIsConnected && bWasConnected) + implts_sendFrameActionEvent( css::frame::FrameAction_COMPONENT_REATTACHED ); + else if (bIsConnected && !bWasConnected) + implts_sendFrameActionEvent( css::frame::FrameAction_COMPONENT_ATTACHED ); + + // A new component window doesn't know anything about current active/focus states. + // Set this information on it! + if ( bHadFocus && xComponentWindow.is() ) + { + xComponentWindow->setFocus(); + } + + // If it was a new component window - we must resize it to fill out + // our container window. + implts_resizeComponentWindow(); + // New component should change our current icon ... + implts_setIconOnWindow(); + // OK - start listening on new window again - or do nothing if it is an empty one. + implts_startWindowListening(); + + /* SAFE { */ + aWriteLock.reset(); + impl_checkMenuCloser(); + aWriteLock.clear(); + /* } SAFE */ + + return true; +} + +/*-**************************************************************************************************** + @short returns current set component window + @descr Frames are used to display components. The actual displayed component is + held by the m_xComponentWindow property. If the component implements only a + XComponent interface, the communication between the frame and the + component is very restricted. Better integration is achievable through a + XController interface. + If the component wants other objects to be able to get information about its + ResourceDescriptor it has to implement a XModel interface. + This frame is the owner of the component window. + + @seealso method setComponent() + @return css::uno::Reference to current set component window. + + @onerror A null reference is returned. +*//*-*****************************************************************************************************/ +css::uno::Reference< css::awt::XWindow > SAL_CALL XFrameImpl::getComponentWindow() +{ + checkDisposed(); + SolarMutexGuard g; + return m_xComponentWindow; +} + +/*-**************************************************************************************************** + @short returns current set controller + @descr Frames are used to display components. The actual displayed component is + held by the m_xComponentWindow property. If the component implements only a + XComponent interface, the communication between the frame and the + component is very restricted. Better integration is achievable through a + XController interface. + If the component wants other objects to be able to get information about its + ResourceDescriptor it has to implement a XModel interface. + This frame is the owner of the component window. + + @seealso method setComponent() + @return css::uno::Reference to current set controller. + + @onerror A null reference is returned. +*//*-*****************************************************************************************************/ +css::uno::Reference< css::frame::XController > SAL_CALL XFrameImpl::getController() +{ + SolarMutexGuard g; + return m_xController; +} + +/*-**************************************************************************************************** + @short add/remove listener for activate/deactivate/contextChanged events + @seealso method activate() + @seealso method deactivate() + @seealso method contextChanged() + + @param "xListener" reference to your listener object + @onerror Listener is ignored. +*//*-*****************************************************************************************************/ +void SAL_CALL XFrameImpl::addFrameActionListener( const css::uno::Reference< css::frame::XFrameActionListener >& xListener ) +{ + checkDisposed(); + m_aListenerContainer.addInterface( cppu::UnoType::get(), xListener ); +} + +void SAL_CALL XFrameImpl::removeFrameActionListener( const css::uno::Reference< css::frame::XFrameActionListener >& xListener ) +{ + m_aListenerContainer.removeInterface( cppu::UnoType::get(), xListener ); +} + +/*-**************************************************************************************************** + @short support two way mechanism to release a frame + @descr This method ask internal component (controller) if he accept this close request. + In case of nothing will be happen (from point of caller of this close method). + In case of a CloseVetoException is thrown. After such exception given parameter + bDeliverOwnership regulate which will be the new owner of this instance. + + @attention It's the replacement for XTask::close() which is marked as obsolete method. + + @param bDeliverOwnership + If parameter is set to the original caller will be the owner after thrown + veto exception and must try to close this frame at later time again. Otherwise the + source of thrown exception is the right one. May it will be the frame himself. + + @throws CloseVetoException + if any internal things will not be closed + + @threadsafe yes +*//*-*****************************************************************************************************/ +void SAL_CALL XFrameImpl::close( sal_Bool bDeliverOwnership ) +{ + checkDisposed(); + + // At the end of this method may we must dispose ourself... + // and may nobody from outside hold a reference to us... + // then it's a good idea to do that by ourself. + css::uno::Reference< css::uno::XInterface > xSelfHold( static_cast< ::cppu::OWeakObject* >(this) ); + + // Check any close listener before we look for currently running internal processes. + // Because if a listener disagree with this close() request - we have time to finish this + // internal operations too... + // Note: container is threadsafe himself. + css::lang::EventObject aSource (static_cast< ::cppu::OWeakObject*>(this)); + comphelper::OInterfaceContainerHelper2* pContainer = m_aListenerContainer.getContainer( cppu::UnoType::get()); + if (pContainer!=nullptr) + { + comphelper::OInterfaceIteratorHelper2 pIterator(*pContainer); + while (pIterator.hasMoreElements()) + { + try + { + static_cast(pIterator.next())->queryClosing( aSource, bDeliverOwnership ); + } + catch( const css::uno::RuntimeException& ) + { + pIterator.remove(); + } + } + } + + // Ok - no listener disagreed with this close() request + // check if this frame is used for any load process currently + if (isActionLocked()) + { + if (bDeliverOwnership) + { + SolarMutexGuard g; + m_bSelfClose = true; + } + + throw css::util::CloseVetoException("Frame in use for loading document...",static_cast< ::cppu::OWeakObject*>(this)); + } + + if ( ! setComponent(nullptr,nullptr) ) + throw css::util::CloseVetoException("Component couldn't be detached...",static_cast< ::cppu::OWeakObject*>(this)); + + // If closing is allowed... inform all listeners and dispose this frame! + pContainer = m_aListenerContainer.getContainer( cppu::UnoType::get()); + if (pContainer!=nullptr) + { + comphelper::OInterfaceIteratorHelper2 pIterator(*pContainer); + while (pIterator.hasMoreElements()) + { + try + { + static_cast(pIterator.next())->notifyClosing( aSource ); + } + catch( const css::uno::RuntimeException& ) + { + pIterator.remove(); + } + } + } + + /* SAFE { */ + { + SolarMutexGuard aWriteLock; + m_bIsHidden = true; + } + /* } SAFE */ + impl_checkMenuCloser(); + + dispose(); +} + +/*-**************************************************************************************************** + @short be a listener for close events! + @descr Adds/remove a CloseListener at this frame instance. If the close() method is called on + this object, the such listener are informed and can disagree with that by throwing + a CloseVetoException. + + @seealso XFrameImpl::close() + + @param xListener + reference to your listener object + + @onerror Listener is ignored. + + @threadsafe yes +*//*-*****************************************************************************************************/ +void SAL_CALL XFrameImpl::addCloseListener( const css::uno::Reference< css::util::XCloseListener >& xListener ) +{ + checkDisposed(); + m_aListenerContainer.addInterface( cppu::UnoType::get(), xListener ); +} + +void SAL_CALL XFrameImpl::removeCloseListener( const css::uno::Reference< css::util::XCloseListener >& xListener ) +{ + m_aListenerContainer.removeInterface( cppu::UnoType::get(), xListener ); +} + +OUString SAL_CALL XFrameImpl::getTitle() +{ + checkDisposed(); + + // SAFE -> + SolarMutexClearableGuard aReadLock; + css::uno::Reference< css::frame::XTitle > xTitle(m_xTitleHelper, css::uno::UNO_SET_THROW); + aReadLock.clear(); + // <- SAFE + + return xTitle->getTitle(); +} + +void SAL_CALL XFrameImpl::setTitle( const OUString& sTitle ) +{ + checkDisposed(); + + // SAFE -> + SolarMutexClearableGuard aReadLock; + css::uno::Reference< css::frame::XTitle > xTitle(m_xTitleHelper, css::uno::UNO_SET_THROW); + aReadLock.clear(); + // <- SAFE + + xTitle->setTitle(sTitle); +} + +void SAL_CALL XFrameImpl::addTitleChangeListener( const css::uno::Reference< css::frame::XTitleChangeListener >& xListener) +{ + checkDisposed(); + + // SAFE -> + SolarMutexClearableGuard aReadLock; + css::uno::Reference< css::frame::XTitleChangeBroadcaster > xTitle(m_xTitleHelper, css::uno::UNO_QUERY_THROW); + aReadLock.clear(); + // <- SAFE + + xTitle->addTitleChangeListener(xListener); +} + +void SAL_CALL XFrameImpl::removeTitleChangeListener( const css::uno::Reference< css::frame::XTitleChangeListener >& xListener ) +{ + checkDisposed(); + + // SAFE -> + SolarMutexClearableGuard aReadLock; + css::uno::Reference< css::frame::XTitleChangeBroadcaster > xTitle(m_xTitleHelper, css::uno::UNO_QUERY_THROW); + aReadLock.clear(); + // <- SAFE + + xTitle->removeTitleChangeListener(xListener); +} + +css::uno::Reference SAL_CALL XFrameImpl::getUserDefinedAttributes() +{ + // optional attribute + return nullptr; +} + +css::uno::Reference SAL_CALL XFrameImpl::getDispatchRecorderSupplier() +{ + SolarMutexGuard g; + return m_xDispatchRecorderSupplier; +} + +void SAL_CALL XFrameImpl::setDispatchRecorderSupplier(const css::uno::Reference& p) +{ + checkDisposed(); + SolarMutexGuard g; + m_xDispatchRecorderSupplier.set(p); +} + +css::uno::Reference SAL_CALL XFrameImpl::getLayoutManager() +{ + SolarMutexGuard g; + return m_xLayoutManager; +} + +void SAL_CALL XFrameImpl::setLayoutManager(const css::uno::Reference& p1) +{ + checkDisposed(); + SolarMutexGuard g; + + css::uno::Reference xOldLayoutManager = m_xLayoutManager; + css::uno::Reference xNewLayoutManager(p1, css::uno::UNO_QUERY); + + if (xOldLayoutManager != xNewLayoutManager) + { + m_xLayoutManager = xNewLayoutManager; + if (xOldLayoutManager.is()) + disableLayoutManager(xOldLayoutManager); + if (xNewLayoutManager.is() && !m_bDocHidden) + lcl_enableLayoutManager(xNewLayoutManager, this); + } +} + +css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL XFrameImpl::getPropertySetInfo() +{ + checkDisposed(); + return css::uno::Reference< css::beans::XPropertySetInfo >(this); +} + +void SAL_CALL XFrameImpl::setPropertyValue(const OUString& sProperty, + const css::uno::Any& aValue ) +{ + // TODO look for e.g. readonly props and reject setProp() call! + + checkDisposed(); + + // SAFE -> + SolarMutexGuard g; + + TPropInfoHash::const_iterator pIt = m_lProps.find(sProperty); + if (pIt == m_lProps.end()) + throw css::beans::UnknownPropertyException(sProperty); + + css::beans::Property aPropInfo = pIt->second; + + css::uno::Any aCurrentValue = impl_getPropertyValue(aPropInfo.Handle); + + bool bWillBeChanged = (aCurrentValue != aValue); + if (! bWillBeChanged) + return; + + css::beans::PropertyChangeEvent aEvent; + aEvent.PropertyName = aPropInfo.Name; + aEvent.Further = false; + aEvent.PropertyHandle = aPropInfo.Handle; + aEvent.OldValue = aCurrentValue; + aEvent.NewValue = aValue; + aEvent.Source.set(m_xBroadcaster.get(), css::uno::UNO_QUERY); + + if (impl_existsVeto(aEvent)) + throw css::beans::PropertyVetoException(); + + impl_setPropertyValue(aPropInfo.Handle, aValue); + + impl_notifyChangeListener(aEvent); +} + +css::uno::Any SAL_CALL XFrameImpl::getPropertyValue(const OUString& sProperty) +{ + checkDisposed(); + + // SAFE -> + SolarMutexGuard aReadLock; + + TPropInfoHash::const_iterator pIt = m_lProps.find(sProperty); + if (pIt == m_lProps.end()) + throw css::beans::UnknownPropertyException(sProperty); + + css::beans::Property aPropInfo = pIt->second; + + return impl_getPropertyValue(aPropInfo.Handle); +} + +void SAL_CALL XFrameImpl::addPropertyChangeListener( + const OUString& sProperty, + const css::uno::Reference< css::beans::XPropertyChangeListener >& xListener) +{ + checkDisposed(); + + // SAFE -> + { + SolarMutexGuard aReadLock; + + TPropInfoHash::const_iterator pIt = m_lProps.find(sProperty); + if (pIt == m_lProps.end()) + throw css::beans::UnknownPropertyException(sProperty); + } + // <- SAFE + + m_lSimpleChangeListener.addInterface(sProperty, xListener); +} + +void SAL_CALL XFrameImpl::removePropertyChangeListener( + const OUString& sProperty, + const css::uno::Reference< css::beans::XPropertyChangeListener >& xListener) +{ + // SAFE -> + { + SolarMutexGuard aReadLock; + + TPropInfoHash::const_iterator pIt = m_lProps.find(sProperty); + if (pIt == m_lProps.end()) + throw css::beans::UnknownPropertyException(sProperty); + } + // <- SAFE + + m_lSimpleChangeListener.removeInterface(sProperty, xListener); +} + +void SAL_CALL XFrameImpl::addVetoableChangeListener( + const OUString& sProperty, + const css::uno::Reference< css::beans::XVetoableChangeListener >& xListener) +{ + checkDisposed(); + + // SAFE -> + { + SolarMutexGuard aReadLock; + + TPropInfoHash::const_iterator pIt = m_lProps.find(sProperty); + if (pIt == m_lProps.end()) + throw css::beans::UnknownPropertyException(sProperty); + } + // <- SAFE + + m_lVetoChangeListener.addInterface(sProperty, xListener); +} + +void SAL_CALL XFrameImpl::removeVetoableChangeListener( + const OUString& sProperty, + const css::uno::Reference< css::beans::XVetoableChangeListener >& xListener) +{ + // SAFE -> + { + SolarMutexGuard aReadLock; + + TPropInfoHash::const_iterator pIt = m_lProps.find(sProperty); + if (pIt == m_lProps.end()) + throw css::beans::UnknownPropertyException(sProperty); + } + // <- SAFE + + m_lVetoChangeListener.removeInterface(sProperty, xListener); +} + +css::uno::Sequence< css::beans::Property > SAL_CALL XFrameImpl::getProperties() +{ + checkDisposed(); + + SolarMutexGuard g; + + sal_Int32 c = static_cast(m_lProps.size()); + css::uno::Sequence< css::beans::Property > lProps(c); + auto lPropsRange = asNonConstRange(lProps); + for (auto const& elem : m_lProps) + { + lPropsRange[--c] = elem.second; + } + + return lProps; +} + +css::beans::Property SAL_CALL XFrameImpl::getPropertyByName(const OUString& sName) +{ + checkDisposed(); + + SolarMutexGuard g; + + TPropInfoHash::const_iterator pIt = m_lProps.find(sName); + if (pIt == m_lProps.end()) + throw css::beans::UnknownPropertyException(sName); + + return pIt->second; +} + +sal_Bool SAL_CALL XFrameImpl::hasPropertyByName(const OUString& sName) +{ + checkDisposed(); + + SolarMutexGuard g; + + TPropInfoHash::iterator pIt = m_lProps.find(sName); + bool bExist = (pIt != m_lProps.end()); + + return bExist; +} + +/*-****************************************************************************************************/ +void XFrameImpl::implts_forgetSubFrames() +{ + // SAFE -> + SolarMutexClearableGuard aReadLock; + css::uno::Reference< css::container::XIndexAccess > xContainer(m_xFramesHelper, css::uno::UNO_QUERY_THROW); + aReadLock.clear(); + // <- SAFE + + sal_Int32 c = xContainer->getCount(); + sal_Int32 i = 0; + + for (i=0; i xFrame; + xContainer->getByIndex(i) >>= xFrame; + if (xFrame.is()) + xFrame->setCreator(css::uno::Reference< css::frame::XFramesSupplier >()); + } + catch(const css::uno::Exception&) + { + // Ignore errors here. + // Nobody can guarantee a stable index in multi threaded environments .-) + } + } + + SolarMutexGuard g; + m_xFramesHelper.clear(); // clear uno reference + m_aChildFrameContainer.clear(); // clear container content +} + +/*-**************************************************************************************************** + @short destroy instance + @descr The owner of this object calls the dispose method if the object + should be destroyed. All other objects and components, that are registered + as an EventListener are forced to release their references to this object. + Furthermore this frame is removed from its parent frame container to release + this reference. The reference attributes are disposed and released also. + + @attention Look for globale description at beginning of file too! + (DisposedException, FairRWLock ..., initialize, dispose) + + @seealso method initialize() + @seealso baseclass FairRWLockBase! +*//*-*****************************************************************************************************/ +void SAL_CALL XFrameImpl::disposing() +{ + // We should hold a reference to ourself ... + // because our owner dispose us and release our reference ... + // May be we will die before we could finish this method ... + css::uno::Reference< css::frame::XFrame > xThis(this); + + SAL_INFO("fwk.frame", "[Frame] " << m_sName << " send dispose event to listener"); + + // First operation should be... "stop all listening for window events on our container window". + // These events are superfluous but can make trouble! + // We will die, die and die... + implts_stopWindowListening(); + + css::uno::Reference layoutMgr; + { + SolarMutexGuard g; + layoutMgr = m_xLayoutManager; + } + if (layoutMgr.is()) { + disableLayoutManager(layoutMgr); + } + + std::unique_ptr disp; + { + SolarMutexGuard g; + std::swap(disp, m_pWindowCommandDispatch); + } + disp.reset(); + + // Send message to all listener and forget her references. + css::lang::EventObject aEvent( xThis ); + m_aListenerContainer.disposeAndClear( aEvent ); + + // set "end of live" for our property set helper + impl_disablePropertySet(); + + // interception/dispatch chain must be destructed explicitly + // Otherwise some dispatches and/or interception objects won't die. + css::uno::Reference< css::lang::XEventListener > xDispatchHelper; + { + SolarMutexGuard g; + xDispatchHelper = m_xDispatchHelper; + } + xDispatchHelper->disposing(aEvent); + xDispatchHelper.clear(); + + // Don't show any dialogs, errors or something else any more! + // If somewhere called dispose() without close() before - normally no dialogs + // should exist. Otherwise it's the problem of the outside caller. + // Note: + // (a) Do it after stopWindowListening(). May that force some active/deactivate + // notifications which we don't need here really. + // (b) Don't forget to save the old value of IsDialogCancelEnabled() to + // restore it afterwards (to not kill headless mode). + DialogCancelMode old = Application::GetDialogCancelMode(); + Application::SetDialogCancelMode( DialogCancelMode::Silent ); + + // We should be alone for ever and further dispose calls are rejected by lines before ... + // I hope it :-) + + // Free references of our frame tree. + // Force parent container to forget this frame too ... + // ( It's contained in m_xParent and so no css::lang::XEventListener for m_xParent! ) + // It's important to do that before we free some other internal structures. + // Because if our parent gets an activate and found us as last possible active frame + // he try to deactivate us ... and we run into some trouble (DisposedExceptions!). + css::uno::Reference parent; + { + SolarMutexGuard g; + std::swap(parent, m_xParent); + } + if( parent.is() ) + { + parent->getFrames()->remove( xThis ); + } + + /* } SAFE */ + // Forget our internal component and her window first. + // So we can release our container window later without problems. + // Because this container window is the parent of the component window ... + // Note: Dispose it hard - because suspending must be done inside close() call! + // But try to dispose the controller first before you destroy the window. + // Because the window is used by the controller too ... + css::uno::Reference< css::lang::XComponent > xDisposableCtrl; + css::uno::Reference< css::lang::XComponent > xDisposableComp; + { + SolarMutexGuard g; + xDisposableCtrl = m_xController; + xDisposableComp = m_xComponentWindow; + } + if (xDisposableCtrl.is()) + xDisposableCtrl->dispose(); + if (xDisposableComp.is()) + xDisposableComp->dispose(); + + impl_checkMenuCloser(); + + css::uno::Reference contWin; + { + SolarMutexGuard g; + std::swap(contWin, m_xContainerWindow); + } + if( contWin.is() ) + { + contWin->setVisible( false ); + // All VclComponents are XComponents; so call dispose before discarding + // a css::uno::Reference< XVclComponent >, because this frame is the owner of the window + contWin->dispose(); + } + + /*ATTENTION + Clear container after successful removing from parent container ... + because our parent could be the desktop which stand in dispose too! + If we have already cleared our own container we lost our child before this could be + remove himself at this instance ... + Release m_xFramesHelper after that ... it's the same problem between parent and child! + "m_xParent->getFrames()->remove( xThis );" needs this helper ... + Otherwise we get a null reference and could finish removing successfully. + => You see: Order of calling operations is important!!! + */ + implts_forgetSubFrames(); + + { + SolarMutexGuard g; + + // Release some other references. + // This calls should be easy ... I hope it :-) + m_xDispatchHelper.clear(); + m_xDropTargetListener.clear(); + m_xDispatchRecorderSupplier.clear(); + m_xLayoutManager.clear(); + m_xIndicatorFactoryHelper.clear(); + + // It's important to set default values here! + // If may be later somewhere change the disposed-behaviour of this implementation + // and doesn't throw any DisposedExceptions we must guarantee best matching default values ... + m_eActiveState = E_INACTIVE; + m_sName.clear(); + m_bIsFrameTop = false; + m_bConnected = false; + m_nExternalLockCount = 0; + m_bSelfClose = false; + m_bIsHidden = true; + } + + // Don't forget it restore old value - + // otherwise no dialogs can be shown anymore in other frames. + Application::SetDialogCancelMode( old ); +} + +/*-**************************************************************************************************** + @short Be a listener for dispose events! + @descr Adds/remove an EventListener to this object. If the dispose method is called on + this object, the disposing method of the listener is called. + @param "xListener" reference to your listener object. + @onerror Listener is ignored. +*//*-*****************************************************************************************************/ +void SAL_CALL XFrameImpl::addEventListener( const css::uno::Reference< css::lang::XEventListener >& xListener ) +{ + checkDisposed(); + m_aListenerContainer.addInterface( cppu::UnoType::get(), xListener ); +} + +void SAL_CALL XFrameImpl::removeEventListener( const css::uno::Reference< css::lang::XEventListener >& xListener ) +{ + m_aListenerContainer.removeInterface( cppu::UnoType::get(), xListener ); +} + +/*-**************************************************************************************************** + @short create new status indicator + @descr Use returned status indicator to show progresses and some text information. + All created objects share the same dialog! Only the last one can show his information. + + @seealso class StatusIndicatorFactory + @seealso class StatusIndicator + @return A reference to created object. + + @onerror We return a null reference. +*//*-*****************************************************************************************************/ +css::uno::Reference< css::task::XStatusIndicator > SAL_CALL XFrameImpl::createStatusIndicator() +{ + checkDisposed(); + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + SolarMutexClearableGuard aReadLock; + + // Make snapshot of necessary member and define default return value. + css::uno::Reference< css::task::XStatusIndicator > xExternal(m_xIndicatorInterception.get(), css::uno::UNO_QUERY); + css::uno::Reference< css::task::XStatusIndicatorFactory > xFactory = m_xIndicatorFactoryHelper; + + aReadLock.clear(); + /* UNSAFE AREA ----------------------------------------------------------------------------------------- */ + + // Was set from outside to intercept any progress activities! + if (xExternal.is()) + return xExternal; + + // Or use our own factory as fallback, to create such progress. + if (xFactory.is()) + return xFactory->createStatusIndicator(); + + return css::uno::Reference< css::task::XStatusIndicator >(); +} + +/*-**************************************************************************************************** + @short search for target to load URL + @descr This method searches for a dispatch for the specified DispatchDescriptor. + The FrameSearchFlags and the FrameName of the DispatchDescriptor are + treated as described for findFrame. + + @seealso method findFrame() + @seealso method queryDispatches() + @seealso method set/getName() + @seealso class TargetFinder + + @param "aURL" , URL for loading + @param "sTargetFrameName" , name of target frame + @param "nSearchFlags" , additional flags to regulate search if sTargetFrameName is not clear + @return css::uno::Reference to dispatch handler. + + @onerror A null reference is returned. +*//*-*****************************************************************************************************/ +css::uno::Reference< css::frame::XDispatch > SAL_CALL XFrameImpl::queryDispatch( const css::util::URL& aURL, + const OUString& sTargetFrameName, + sal_Int32 nSearchFlags) +{ + // Don't check incoming parameter here! Our helper do it for us and it is not a good idea to do it more than once! + + checkDisposed(); + + // Remove uno and cmd protocol part as we want to support both of them. We store only the command part + // in our hash map. All other protocols are stored with the protocol part. + OUString aCommand( aURL.Main ); + if ( aURL.Protocol.equalsIgnoreAsciiCase(".uno:") ) + aCommand = aURL.Path; + + // Make std::unordered_map lookup if the current URL is in the disabled list + if ( m_aCommandOptions.LookupDisabled( aCommand ) ) + return css::uno::Reference< css::frame::XDispatch >(); + else + { + // We use a helper to support these interface and an interceptor mechanism. + css::uno::Reference disp; + { + SolarMutexGuard g; + disp = m_xDispatchHelper; + } + if (!disp.is()) { + throw css::lang::DisposedException("Frame disposed"); + } + return disp->queryDispatch( aURL, sTargetFrameName, nSearchFlags ); + } +} + +/*-**************************************************************************************************** + @short handle more than ones dispatches at same call + @descr Returns a sequence of dispatches. For details see the queryDispatch method. + For failed dispatches we return empty items in list! + + @seealso method queryDispatch() + + @param "lDescriptor" list of dispatch arguments for queryDispatch()! + @return List of dispatch references. Some elements can be NULL! + + @onerror An empty list is returned. +*//*-*****************************************************************************************************/ +css::uno::Sequence< css::uno::Reference< css::frame::XDispatch > > SAL_CALL XFrameImpl::queryDispatches( + const css::uno::Sequence< css::frame::DispatchDescriptor >& lDescriptor ) +{ + // Don't check incoming parameter here! Our helper do it for us and it is not a good idea to do it more than ones! + + checkDisposed(); + + // We use a helper to support these interface and an interceptor mechanism. + css::uno::Reference disp; + { + SolarMutexGuard g; + disp = m_xDispatchHelper; + } + if (!disp.is()) { + throw css::lang::DisposedException("Frame disposed"); + } + return disp->queryDispatches( lDescriptor ); +} + +/*-**************************************************************************************************** + @short register/unregister interceptor for dispatch calls + @descr If you wish to handle some dispatches by himself ... you should be + an interceptor for it. Please see class OInterceptionHelper for further information. + + @seealso class OInterceptionHelper + + @param "xInterceptor", reference to your interceptor implementation. + @onerror Interceptor is ignored. +*//*-*****************************************************************************************************/ +void SAL_CALL XFrameImpl::registerDispatchProviderInterceptor( + const css::uno::Reference< css::frame::XDispatchProviderInterceptor >& xInterceptor ) +{ + // We use a helper to support these interface and an interceptor mechanism. + // This helper is threadsafe himself and check incoming parameter too. + // I think we don't need any lock here! + + checkDisposed(); + + css::uno::Reference< css::frame::XDispatchProviderInterception > xInterceptionHelper; + { + SolarMutexGuard g; + xInterceptionHelper = m_xDispatchHelper; + } + if (xInterceptionHelper.is()) { + xInterceptionHelper->registerDispatchProviderInterceptor( xInterceptor ); + } +} + +void SAL_CALL XFrameImpl::releaseDispatchProviderInterceptor( + const css::uno::Reference< css::frame::XDispatchProviderInterceptor >& xInterceptor ) +{ + // We use a helper to support these interface and an interceptor mechanism. + // This helper is threadsafe himself and check incoming parameter too. + // I think we don't need any lock here! + + // Sometimes we are called during our dispose() method + + css::uno::Reference< css::frame::XDispatchProviderInterception > xInterceptionHelper; + { + SolarMutexGuard g; + xInterceptionHelper = m_xDispatchHelper; + } + if (xInterceptionHelper.is()) { + xInterceptionHelper->releaseDispatchProviderInterceptor( xInterceptor ); + } +} + +/*-**************************************************************************************************** + @short provides information about all possible dispatch functions + inside the current frame environment +*//*-*****************************************************************************************************/ +css::uno::Sequence< sal_Int16 > SAL_CALL XFrameImpl::getSupportedCommandGroups() +{ + return m_xDispatchInfoHelper->getSupportedCommandGroups(); +} + +css::uno::Sequence< css::frame::DispatchInformation > SAL_CALL XFrameImpl::getConfigurableDispatchInformation( + sal_Int16 nCommandGroup) +{ + return m_xDispatchInfoHelper->getConfigurableDispatchInformation(nCommandGroup); +} + +/*-**************************************************************************************************** + @short notifications for window events + @descr We are a listener on our container window to forward it to our component window. + + @seealso method setComponent() + @seealso member m_xContainerWindow + @seealso member m_xComponentWindow + + @param "aEvent" describe source of detected event +*//*-*****************************************************************************************************/ +void SAL_CALL XFrameImpl::windowResized( const css::awt::WindowEvent& ) +{ + // Part of dispose-mechanism + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + // Impl-method is threadsafe! + // If we have a current component window - we must resize it! + implts_resizeComponentWindow(); +} + +void SAL_CALL XFrameImpl::focusGained( const css::awt::FocusEvent& ) +{ + // Part of dispose() mechanism + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + SolarMutexClearableGuard aReadLock; + // Make snapshot of member! + css::uno::Reference< css::awt::XWindow > xComponentWindow = m_xComponentWindow; + aReadLock.clear(); + /* UNSAFE AREA --------------------------------------------------------------------------------------------- */ + + if( xComponentWindow.is() ) + { + xComponentWindow->setFocus(); + } +} + +/*-**************************************************************************************************** + @short notifications for window events + @descr We are a listener on our container window to forward it to our component window ... + but a XTopWindowListener we are only if we are a top frame! + + @seealso method setComponent() + @seealso member m_xContainerWindow + @seealso member m_xComponentWindow + + @param "aEvent" describe source of detected event +*//*-*****************************************************************************************************/ +void SAL_CALL XFrameImpl::windowActivated( const css::lang::EventObject& ) +{ + checkDisposed(); + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + SolarMutexClearableGuard aReadLock; + // Make snapshot of member! + EActiveState eState = m_eActiveState; + aReadLock.clear(); + /* UNSAFE AREA --------------------------------------------------------------------------------------------- */ + // Activate the new active path from here to top. + if( eState == E_INACTIVE ) + { + setActiveFrame( css::uno::Reference< css::frame::XFrame >() ); + activate(); + } +} + +void SAL_CALL XFrameImpl::windowDeactivated( const css::lang::EventObject& ) +{ + // Sometimes called during dispose() + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + SolarMutexClearableGuard aReadLock; + + css::uno::Reference< css::frame::XFrame > xParent = m_xParent; + css::uno::Reference< css::awt::XWindow > xContainerWindow = m_xContainerWindow; + EActiveState eActiveState = m_eActiveState; + + aReadLock.clear(); + + if( eActiveState == E_INACTIVE ) + return; + + // Deactivation is always done implicitly by activation of another frame. + // Only if no activation is done, deactivations have to be processed if the activated window + // is a parent window of the last active Window! + SolarMutexClearableGuard aSolarGuard; + vcl::Window* pFocusWindow = Application::GetFocusWindow(); + if ( !xContainerWindow.is() || !xParent.is() || + css::uno::Reference< css::frame::XDesktop >( xParent, css::uno::UNO_QUERY ).is() + ) + return; + + css::uno::Reference< css::awt::XWindow > xParentWindow = xParent->getContainerWindow(); + VclPtr pParentWindow = VCLUnoHelper::GetWindow( xParentWindow ); + //#i70261#: dialogs opened from an OLE object will cause a deactivate on the frame of the OLE object + // on Solaris/Linux at that time pFocusWindow is still NULL because the focus handling is different; right after + // the deactivation the focus will be set into the dialog! + // currently I see no case where a sub frame could get a deactivate with pFocusWindow being NULL permanently + // so for now this case is omitted from handled deactivations + if( pFocusWindow && pParentWindow->IsChild( pFocusWindow ) ) + { + css::uno::Reference< css::frame::XFramesSupplier > xSupplier( xParent, css::uno::UNO_QUERY ); + if( xSupplier.is() ) + { + aSolarGuard.clear(); + xSupplier->setActiveFrame( css::uno::Reference< css::frame::XFrame >() ); + } + } +} + +void SAL_CALL XFrameImpl::windowClosing( const css::lang::EventObject& ) +{ + checkDisposed(); + + // deactivate this frame ... + deactivate(); + + // ... and try to close it + // But do it asynchronous inside the main thread. + // VCL has no fun to do such things outside his main thread :-( + // Note: The used dispatch make it asynchronous for us .-) + + /*ATTENTION! + Don't try to suspend the controller here! Because it's done inside used dispatch(). + Otherwise the dialog "would you save your changes?" will be shown more than once ... + */ + + css::util::URL aURL; + aURL.Complete = ".uno:CloseFrame"; + css::uno::Reference< css::util::XURLTransformer > xParser(css::util::URLTransformer::create(m_xContext)); + xParser->parseStrict(aURL); + + css::uno::Reference< css::frame::XDispatch > xCloser = queryDispatch(aURL, SPECIALTARGET_SELF, 0); + if (xCloser.is()) + xCloser->dispatch(aURL, css::uno::Sequence< css::beans::PropertyValue >()); + + // Attention: If this dispatch works synchronous ... and fulfill its job ... + // this line of code will never be reached ... + // Or if it will be reached it will be for sure that all your member are gone .-) +} + +/*-**************************************************************************************************** + @short react for a show event for the internal container window + @descr Normally we don't need this information really. But we can use it to + implement the special feature "trigger first visible task". + + Algorithm: - first we have to check if we are a top (task) frame + It's not enough to be a top frame! Because we MUST have the desktop as parent. + But frames without a parent are top too. So it's not possible to check isTop() here! + We have to look for the type of our parent. + - if we are a task frame, then we have to check if we are the first one. + We use a static variable to do so. They will be reset to afterwards be sure + that further calls of this method doesn't do anything then. + - Then we have to trigger the right event string on the global job executor. + + @seealso css::task::JobExecutor + + @param aEvent + describes the source of this event + We are not interested on this information. We are interested on the visible state only. + + @threadsafe yes +*//*-*****************************************************************************************************/ +void SAL_CALL XFrameImpl::windowShown( const css::lang::EventObject& ) +{ + static std::mutex aFirstVisibleLock; + + /* SAFE { */ + SolarMutexClearableGuard aReadLock; + css::uno::Reference< css::frame::XDesktop > xDesktopCheck( m_xParent, css::uno::UNO_QUERY ); + m_bIsHidden = false; + aReadLock.clear(); + /* } SAFE */ + + impl_checkMenuCloser(); + + if (!xDesktopCheck.is()) + return; + + static bool bFirstVisibleTask = true; + std::unique_lock aGuard(aFirstVisibleLock); + bool bMustBeTriggered = bFirstVisibleTask; + bFirstVisibleTask = false; + aGuard.unlock(); + + if (bMustBeTriggered) + { + css::uno::Reference< css::task::XJobExecutor > xExecutor + = css::task::theJobExecutor::get( m_xContext ); + xExecutor->trigger( "onFirstVisibleTask" ); + } +} + +void SAL_CALL XFrameImpl::windowHidden( const css::lang::EventObject& ) +{ + /* SAFE { */ + { + SolarMutexGuard aReadLock; + m_bIsHidden = true; + } + /* } SAFE */ + + impl_checkMenuCloser(); +} + +/*-**************************************************************************************************** + @short called by dispose of our windows! + @descr This object is forced to release all references to the interfaces given + by the parameter source. We are a listener at our container window and + should listen for his disposing. + + @seealso XWindowListener + @seealso XTopWindowListener + @seealso XFocusListener +*//*-*****************************************************************************************************/ +void SAL_CALL XFrameImpl::disposing( const css::lang::EventObject& aEvent ) +{ + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + SolarMutexResettableGuard aWriteLock; + + if( aEvent.Source == m_xContainerWindow ) + { + // NECESSARY: Impl-method is threadsafe by himself! + aWriteLock.clear(); + implts_stopWindowListening(); + aWriteLock.reset(); + m_xContainerWindow.clear(); + } +} + +/*-************************************************************************************************************ + @interface com.sun.star.document.XActionLockable + @short implement locking of frame/task from outside + @descr Sometimes we have problems to decide if closing of task is allowed. Because; frame/task + could be used for pending loading jobs. So you can lock this object from outside and + prevent instance against closing during using! But - don't do it in a wrong or expensive manner. + Otherwise task couldn't die anymore!!! + + @seealso interface XActionLockable + @seealso method BaseDispatcher::implts_loadIt() + @seealso method Desktop::loadComponentFromURL() + @return true if frame/task is locked + false otherwise + @threadsafe yes +*//*-*************************************************************************************************************/ +sal_Bool SAL_CALL XFrameImpl::isActionLocked() +{ + SolarMutexGuard g; + return( m_nExternalLockCount!=0); +} + +void SAL_CALL XFrameImpl::addActionLock() +{ + SolarMutexGuard g; + ++m_nExternalLockCount; +} + +void SAL_CALL XFrameImpl::removeActionLock() +{ + { + SolarMutexGuard g; + SAL_WARN_IF( m_nExternalLockCount<=0, "fwk.frame", "XFrameImpl::removeActionLock(): Frame is not locked! " + "Possible multithreading problem detected." ); + --m_nExternalLockCount; + } + + implts_checkSuicide(); +} + +void SAL_CALL XFrameImpl::setActionLocks( sal_Int16 nLock ) +{ + SolarMutexGuard g; + // Attention: If somewhere called resetActionLocks() before and get e.g. 5 locks ... + // and tried to set these 5 ones here after his operations ... + // we can't ignore set requests during these two calls! + // So we must add(!) these 5 locks here. + m_nExternalLockCount = m_nExternalLockCount + nLock; +} + +sal_Int16 SAL_CALL XFrameImpl::resetActionLocks() +{ + sal_Int16 nCurrentLocks = 0; + { + SolarMutexGuard g; + nCurrentLocks = m_nExternalLockCount; + m_nExternalLockCount = 0; + } + + // Attention: + // external lock count is 0 here every time... but if + // member m_bSelfClose is set to true too... we call our own close()/dispose(). + // See close() for further information + implts_checkSuicide(); + + return nCurrentLocks; +} + +void XFrameImpl::impl_setPropertyValue(sal_Int32 nHandle, + const css::uno::Any& aValue) + +{ + /* There is no need to lock any mutex here. Because we share the + solar mutex with our base class. And we said to our base class: "don't release it on calling us" .-) + */ + + /* Attention: You can use nHandle only, if you are sure that all supported + properties has a unique handle. That must be guaranteed + inside method initListeners()! + */ + switch (nHandle) + { + case FRAME_PROPHANDLE_TITLE : + { + OUString sExternalTitle; + aValue >>= sExternalTitle; + setTitle (sExternalTitle); + } + break; + + case FRAME_PROPHANDLE_DISPATCHRECORDERSUPPLIER : + aValue >>= m_xDispatchRecorderSupplier; + break; + + case FRAME_PROPHANDLE_LAYOUTMANAGER : + { + css::uno::Reference< css::frame::XLayoutManager2 > xOldLayoutManager = m_xLayoutManager; + css::uno::Reference< css::frame::XLayoutManager2 > xNewLayoutManager; + aValue >>= xNewLayoutManager; + + if (xOldLayoutManager != xNewLayoutManager) + { + m_xLayoutManager = xNewLayoutManager; + if (xOldLayoutManager.is()) + disableLayoutManager(xOldLayoutManager); + if (xNewLayoutManager.is() && !m_bDocHidden) + lcl_enableLayoutManager(xNewLayoutManager, this); + } + } + break; + + case FRAME_PROPHANDLE_INDICATORINTERCEPTION : + { + css::uno::Reference< css::task::XStatusIndicator > xProgress; + aValue >>= xProgress; + m_xIndicatorInterception = xProgress; + } + break; + + case FRAME_PROPHANDLE_URL: + { + aValue >>= m_aURL; + } + break; + default : + SAL_INFO("fwk.frame", "XFrameImpl::setFastPropertyValue_NoBroadcast(): Invalid handle detected!" ); + break; + } +} + +css::uno::Any XFrameImpl::impl_getPropertyValue(sal_Int32 nHandle) +{ + /* There is no need to lock any mutex here. Because we share the + solar mutex with our base class. And we said to our base class: "don't release it on calling us" .-) + */ + + /* Attention: You can use nHandle only, if you are sure that all supported + properties has a unique handle. That must be guaranteed + inside method initListeners()! + */ + css::uno::Any aValue; + switch (nHandle) + { + case FRAME_PROPHANDLE_TITLE : + aValue <<= getTitle (); + break; + + case FRAME_PROPHANDLE_DISPATCHRECORDERSUPPLIER : + aValue <<= m_xDispatchRecorderSupplier; + break; + + case FRAME_PROPHANDLE_ISHIDDEN : + aValue <<= m_bIsHidden; + break; + + case FRAME_PROPHANDLE_LAYOUTMANAGER : + aValue <<= m_xLayoutManager; + break; + + case FRAME_PROPHANDLE_INDICATORINTERCEPTION : + { + css::uno::Reference< css::task::XStatusIndicator > xProgress(m_xIndicatorInterception.get(), + css::uno::UNO_QUERY); + aValue <<= xProgress; + } + break; + + case FRAME_PROPHANDLE_URL: + { + aValue <<= m_aURL; + } + break; + default : + SAL_INFO("fwk.frame", "XFrameImpl::getFastPropertyValue(): Invalid handle detected!" ); + break; + } + + return aValue; +} + +void XFrameImpl::impl_setPropertyChangeBroadcaster(const css::uno::Reference< css::uno::XInterface >& xBroadcaster) +{ + SolarMutexGuard g; + m_xBroadcaster = xBroadcaster; +} + +void XFrameImpl::impl_addPropertyInfo(const css::beans::Property& aProperty) +{ + SolarMutexGuard g; + + TPropInfoHash::const_iterator pIt = m_lProps.find(aProperty.Name); + if (pIt != m_lProps.end()) + throw css::beans::PropertyExistException(); + + m_lProps[aProperty.Name] = aProperty; +} + +void XFrameImpl::impl_disablePropertySet() +{ + SolarMutexGuard g; + + css::uno::Reference< css::uno::XInterface > xThis(static_cast< css::beans::XPropertySet* >(this), css::uno::UNO_QUERY); + css::lang::EventObject aEvent(xThis); + + m_lSimpleChangeListener.disposeAndClear(aEvent); + m_lVetoChangeListener.disposeAndClear(aEvent); + m_lProps.clear(); +} + +bool XFrameImpl::impl_existsVeto(const css::beans::PropertyChangeEvent& aEvent) +{ + /* Don't use the lock here! + The used helper is threadsafe and it lives for the whole lifetime of + our own object. + */ + ::comphelper::OInterfaceContainerHelper3* pVetoListener = m_lVetoChangeListener.getContainer(aEvent.PropertyName); + if (! pVetoListener) + return false; + + ::comphelper::OInterfaceIteratorHelper3 pListener(*pVetoListener); + while (pListener.hasMoreElements()) + { + try + { + pListener.next()->vetoableChange(aEvent); + } + catch(const css::uno::RuntimeException&) + { pListener.remove(); } + catch(const css::beans::PropertyVetoException&) + { return true; } + } + + return false; +} + +void XFrameImpl::impl_notifyChangeListener(const css::beans::PropertyChangeEvent& aEvent) +{ + /* Don't use the lock here! + The used helper is threadsafe and it lives for the whole lifetime of + our own object. + */ + ::comphelper::OInterfaceContainerHelper3* pSimpleListener = m_lSimpleChangeListener.getContainer(aEvent.PropertyName); + if (! pSimpleListener) + return; + + ::comphelper::OInterfaceIteratorHelper3 pListener(*pSimpleListener); + while (pListener.hasMoreElements()) + { + try + { + pListener.next()->propertyChange(aEvent); + } + catch(const css::uno::RuntimeException&) + { pListener.remove(); } + } +} + +/*-**************************************************************************************************** + @short send frame action event to our listener + @descr This method is threadsafe AND can be called by our dispose method too! + @param "aAction", describe the event for sending +*//*-*****************************************************************************************************/ +void XFrameImpl::implts_sendFrameActionEvent( const css::frame::FrameAction& aAction ) +{ + // Sometimes used by dispose() + + // Log information about order of events to file! + // (only activated in debug version!) + SAL_INFO( "fwk.frame", + "[Frame] " << m_sName << " send event " << + (aAction == css::frame::FrameAction_COMPONENT_ATTACHED ? OUString("COMPONENT ATTACHED") : + (aAction == css::frame::FrameAction_COMPONENT_DETACHING ? OUString("COMPONENT DETACHING") : + (aAction == css::frame::FrameAction_COMPONENT_REATTACHED ? OUString("COMPONENT REATTACHED") : + (aAction == css::frame::FrameAction_FRAME_ACTIVATED ? OUString("FRAME ACTIVATED") : + (aAction == css::frame::FrameAction_FRAME_DEACTIVATING ? OUString("FRAME DEACTIVATING") : + (aAction == css::frame::FrameAction_CONTEXT_CHANGED ? OUString("CONTEXT CHANGED") : + (aAction == css::frame::FrameAction_FRAME_UI_ACTIVATED ? OUString("FRAME UI ACTIVATED") : + (aAction == css::frame::FrameAction_FRAME_UI_DEACTIVATING ? OUString("FRAME UI DEACTIVATING") : + (aAction == css::frame::FrameAction::FrameAction_MAKE_FIXED_SIZE ? OUString("MAKE_FIXED_SIZE") : + OUString("*invalid*"))))))))))); + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + // Send css::frame::FrameAction event to all listener. + // Get container for right listener. + // FOLLOW LINES ARE THREADSAFE!!! + // ( OInterfaceContainerHelper2 is synchronized with m_aListenerContainer! ) + comphelper::OInterfaceContainerHelper2* pContainer = m_aListenerContainer.getContainer( + cppu::UnoType::get()); + + if( pContainer == nullptr ) + return; + + // Build action event. + css::frame::FrameActionEvent aFrameActionEvent( static_cast< ::cppu::OWeakObject* >(this), this, aAction ); + + // Get iterator for access to listener. + comphelper::OInterfaceIteratorHelper2 aIterator( *pContainer ); + // Send message to all listener. + while( aIterator.hasMoreElements() ) + { + try + { + static_cast(aIterator.next())->frameAction( aFrameActionEvent ); + } + catch( const css::uno::RuntimeException& ) + { + aIterator.remove(); + } + } +} + +/*-**************************************************************************************************** + @short helper to resize our component window + @descr A frame contains 2 windows - a container ~ and a component window. + This method resize inner component window to full size of outer container window. + This method is threadsafe AND can be called by our dispose method too! +*//*-*****************************************************************************************************/ +void XFrameImpl::implts_resizeComponentWindow() +{ + // usually the LayoutManager does the resizing + // in case there is no LayoutManager resizing has to be done here + if ( m_xLayoutManager.is() ) + return; + + css::uno::Reference< css::awt::XWindow > xComponentWindow( getComponentWindow() ); + if( !xComponentWindow.is() ) + return; + + css::uno::Reference< css::awt::XDevice > xDevice( getContainerWindow(), css::uno::UNO_QUERY ); + + // Convert relative size to output size. + css::awt::Rectangle aRectangle = getContainerWindow()->getPosSize(); + css::awt::DeviceInfo aInfo = xDevice->getInfo(); + css::awt::Size aSize( aRectangle.Width - aInfo.LeftInset - aInfo.RightInset, + aRectangle.Height - aInfo.TopInset - aInfo.BottomInset ); + + // Resize our component window. + xComponentWindow->setPosSize( 0, 0, aSize.Width, aSize.Height, css::awt::PosSize::POSSIZE ); +} + +/*-**************************************************************************************************** + @short helper to set icon on our container window (if it is a system window!) + @descr We use our internal set controller (if it exist) to specify which factory he represented. + This information can be used to find right icon. But our controller can say it us directly + too ... we should ask his optional property set first ... + + @seealso method Window::SetIcon() + @onerror We do nothing. +*//*-*****************************************************************************************************/ +void XFrameImpl::implts_setIconOnWindow() +{ + checkDisposed(); + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + // Make snapshot of necessary members and release lock. + SolarMutexClearableGuard aReadLock; + css::uno::Reference< css::awt::XWindow > xContainerWindow = m_xContainerWindow; + css::uno::Reference< css::frame::XController > xController = m_xController; + aReadLock.clear(); + /* UNSAFE AREA --------------------------------------------------------------------------------------------- */ + + if( !(xContainerWindow.is() && xController.is()) ) + return; + + + // a) set default value to an invalid one. So we can start further searches for right icon id, if + // first steps failed! + // We must reset it to any fallback value - if no search step returns a valid result. + sal_Int32 nIcon = -1; + + // b) try to find information on controller propertyset directly + // Don't forget to catch possible exceptions - because these property is an optional one! + css::uno::Reference< css::beans::XPropertySet > xSet( xController, css::uno::UNO_QUERY ); + if( xSet.is() ) + { + try + { + css::uno::Reference< css::beans::XPropertySetInfo > const xPSI( xSet->getPropertySetInfo(), + css::uno::UNO_SET_THROW ); + if ( xPSI->hasPropertyByName( "IconId" ) ) + xSet->getPropertyValue( "IconId" ) >>= nIcon; + } + catch( css::uno::Exception& ) + { + DBG_UNHANDLED_EXCEPTION("fwk"); + } + } + + // c) if b) failed... analyze argument list of currently loaded document inside the frame to find the filter. + // He can be used to detect right factory - and these can be used to match factory to icon... + if( nIcon == -1 ) + { + css::uno::Reference< css::frame::XModel > xModel = xController->getModel(); + if( xModel.is() ) + { + SvtModuleOptions::EFactory eFactory = SvtModuleOptions::ClassifyFactoryByModel(xModel); + if (eFactory != SvtModuleOptions::EFactory::UNKNOWN_FACTORY) + nIcon = SvtModuleOptions().GetFactoryIcon( eFactory ); + } + } + + // d) if all steps failed - use fallback! + if( nIcon == -1 ) + { + nIcon = 0; + } + + // e) set icon on container window now + // Don't forget SolarMutex! We use vcl directly :-( + // Check window pointer for right WorkWindow class too!!! + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + { + SolarMutexGuard aSolarGuard; + VclPtr pWindow = VCLUnoHelper::GetWindow( xContainerWindow ); + if( + ( pWindow != nullptr ) && + ( pWindow->GetType() == WindowType::WORKWINDOW ) + ) + { + WorkWindow* pWorkWindow = static_cast(pWindow.get()); + pWorkWindow->SetIcon( static_cast(nIcon) ); + } + } + /* UNSAFE AREA --------------------------------------------------------------------------------------------- */ +} + +/*-************************************************************************************************************ + @short helper to start/stop listening for window events on container window + @descr If we get a new container window, we must set it on internal member ... + and stop listening at old one ... and start listening on new one! + But sometimes (in dispose() call!) it's necessary to stop listening without starting + on new connections. So we split this functionality to make it easier at use. + + @seealso method initialize() + @seealso method dispose() + @onerror We do nothing! + @threadsafe yes +*//*-*************************************************************************************************************/ +void XFrameImpl::implts_startWindowListening() +{ + checkDisposed(); + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + // Make snapshot of necessary member! + SolarMutexClearableGuard aReadLock; + css::uno::Reference< css::awt::XWindow > xContainerWindow = m_xContainerWindow; + css::uno::Reference< css::datatransfer::dnd::XDropTargetListener > xDragDropListener = m_xDropTargetListener; + css::uno::Reference< css::awt::XWindowListener > xWindowListener(this); + css::uno::Reference< css::awt::XFocusListener > xFocusListener(this); + css::uno::Reference< css::awt::XTopWindowListener > xTopWindowListener(this); + aReadLock.clear(); + /* UNSAFE AREA --------------------------------------------------------------------------------------------- */ + + if( !xContainerWindow.is() ) + return; + + xContainerWindow->addWindowListener( xWindowListener); + xContainerWindow->addFocusListener ( xFocusListener ); + + css::uno::Reference< css::awt::XTopWindow > xTopWindow( xContainerWindow, css::uno::UNO_QUERY ); + if( xTopWindow.is() ) + { + xTopWindow->addTopWindowListener( xTopWindowListener ); + + css::uno::Reference< css::awt::XToolkit2 > xToolkit = css::awt::Toolkit::create( m_xContext ); + css::uno::Reference< css::datatransfer::dnd::XDropTarget > xDropTarget = xToolkit->getDropTarget( xContainerWindow ); + if( xDropTarget.is() ) + { + xDropTarget->addDropTargetListener( xDragDropListener ); + xDropTarget->setActive( true ); + } + } +} + +void XFrameImpl::implts_stopWindowListening() +{ + // Sometimes used by dispose() + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + // Make snapshot of necessary member! + SolarMutexClearableGuard aReadLock; + css::uno::Reference< css::awt::XWindow > xContainerWindow = m_xContainerWindow; + css::uno::Reference< css::datatransfer::dnd::XDropTargetListener > xDragDropListener = m_xDropTargetListener; + css::uno::Reference< css::awt::XWindowListener > xWindowListener(this); + css::uno::Reference< css::awt::XFocusListener > xFocusListener(this); + css::uno::Reference< css::awt::XTopWindowListener > xTopWindowListener(this); + aReadLock.clear(); + /* UNSAFE AREA --------------------------------------------------------------------------------------------- */ + + if( !xContainerWindow.is() ) + return; + + xContainerWindow->removeWindowListener( xWindowListener); + xContainerWindow->removeFocusListener ( xFocusListener ); + + css::uno::Reference< css::awt::XTopWindow > xTopWindow( xContainerWindow, css::uno::UNO_QUERY ); + if( !xTopWindow.is() ) + return; + + xTopWindow->removeTopWindowListener( xTopWindowListener ); + + css::uno::Reference< css::awt::XToolkit2 > xToolkit = css::awt::Toolkit::create( m_xContext ); + css::uno::Reference< css::datatransfer::dnd::XDropTarget > xDropTarget = + xToolkit->getDropTarget( xContainerWindow ); + if( xDropTarget.is() ) + { + xDropTarget->removeDropTargetListener( xDragDropListener ); + xDropTarget->setActive( false ); + } +} + +/*-**************************************************************************************************** + @short helper to force broken close() request again + @descr If we self disagree with a close() request, and detect that all external locks are gone ... + then we must try to close this frame again. + + @seealso XCloseable::close() + @seealso XFrameImpl::close() + @seealso XFrameImpl::removeActionLock() + @seealso XFrameImpl::resetActionLock() + @seealso m_bSelfClose + @seealso m_nExternalLockCount + + @threadsafe yes +*//*-*****************************************************************************************************/ +void XFrameImpl::implts_checkSuicide() +{ + /* SAFE */ + SolarMutexClearableGuard aReadLock; + // in case of lock==0 and safed state of previous close() request m_bSelfClose + // we must force close() again. Because we had disagreed with that before. + bool bSuicide = (m_nExternalLockCount==0 && m_bSelfClose); + m_bSelfClose = false; + aReadLock.clear(); + /* } SAFE */ + // force close and deliver ownership to source of possible thrown veto exception + // Attention: Because this method is not designed to throw such exception we must suppress + // it for outside code! + try + { + if (bSuicide) + close(true); + } + catch(const css::util::CloseVetoException&) + {} + catch(const css::lang::DisposedException&) + {} +} + +/** little helper to enable/disable the menu closer at the menubar of the given frame. + + @param xFrame + we use its layout manager to set/reset a special callback. + Its existence regulate visibility of this closer item. + + @param bState + enable; disable this state + */ + +void XFrameImpl::impl_setCloser( /*IN*/ const css::uno::Reference< css::frame::XFrame2 >& xFrame , + /*IN*/ bool bState ) +{ + // Note: If start module is not installed - no closer has to be shown! + if (!SvtModuleOptions().IsModuleInstalled(SvtModuleOptions::EModule::STARTMODULE)) + return; + + try + { + css::uno::Reference< css::beans::XPropertySet > xFrameProps(xFrame, css::uno::UNO_QUERY_THROW); + css::uno::Reference< css::frame::XLayoutManager > xLayoutManager; + xFrameProps->getPropertyValue(FRAME_PROPNAME_ASCII_LAYOUTMANAGER) >>= xLayoutManager; + css::uno::Reference< css::beans::XPropertySet > xLayoutProps(xLayoutManager, css::uno::UNO_QUERY_THROW); + xLayoutProps->setPropertyValue(LAYOUTMANAGER_PROPNAME_MENUBARCLOSER, css::uno::Any(bState)); + } + catch(const css::uno::RuntimeException&) + { throw; } + catch(const css::uno::Exception&) + {} +} + +/** it checks, which of the top level task frames must have the special menu closer for + switching to the backing window mode. + + It analyze the current list of visible top level frames. Only the last real document + frame can have this symbol. Not the help frame nor the backing task itself. + Here we do anything related to this closer. We remove it from the old frame and set it + for the new one. + */ + +void XFrameImpl::impl_checkMenuCloser() +{ + /* SAFE { */ + SolarMutexClearableGuard aReadLock; + + // only top frames, which are part of our desktop hierarchy, can + // do so! By the way - we need the desktop instance to have access + // to all other top level frames too. + css::uno::Reference< css::frame::XDesktop > xDesktop (m_xParent, css::uno::UNO_QUERY); + css::uno::Reference< css::frame::XFramesSupplier > xTaskSupplier(xDesktop , css::uno::UNO_QUERY); + if ( !xDesktop.is() || !xTaskSupplier.is() ) + return; + + aReadLock.clear(); + /* } SAFE */ + + // analyze the list of current open tasks + // Suppress search for other views to the same model ... + // It's not needed here and can be very expensive. + FrameListAnalyzer aAnalyzer( + xTaskSupplier, + this, + FrameAnalyzerFlags::Hidden | FrameAnalyzerFlags::Help | FrameAnalyzerFlags::BackingComponent); + + // specify the new frame, which must have this special state... + css::uno::Reference< css::frame::XFrame2 > xNewCloserFrame; + + // a) + // If there exist at least one other frame - there are two frames currently open. + // But we can enable this closer only, if one of these two tasks includes the help module. + // The "other frame" couldn't be the help. Because then it wouldn't be part of this "other list". + // In such case it will be separated to the reference aAnalyzer.m_xHelp! + // But we must check, if we include ourself the help... + // Check aAnalyzer.m_bReferenceIsHelp! + if ( + (aAnalyzer.m_lOtherVisibleFrames.size()==1) && + ( + (aAnalyzer.m_bReferenceIsHelp ) || + (aAnalyzer.m_bReferenceIsHidden) + ) + ) + { + // others[0] can't be the backing component! + // Because it's set at the special member aAnalyzer.m_xBackingComponent ... :-) + xNewCloserFrame.set( aAnalyzer.m_lOtherVisibleFrames[0], css::uno::UNO_QUERY_THROW ); + } + + // b) + // There is no other frame... means no other document frame. The help module + // will be handled separately and must(!) be ignored here... excepting if we include ourself the help. + else if ( + (aAnalyzer.m_lOtherVisibleFrames.empty()) && + (!aAnalyzer.m_bReferenceIsHelp) && + (!aAnalyzer.m_bReferenceIsHidden) && + (!aAnalyzer.m_bReferenceIsBacking) + ) + { + xNewCloserFrame = this; + } + + // Look for necessary actions ... + // Only if the closer state must be moved from one frame to another one + // or must be enabled/disabled at all. + SolarMutexGuard aGuard; + // Holds the only frame, which must show the special closer menu item (can be NULL!) + static css::uno::WeakReference< css::frame::XFrame2 > s_xCloserFrame; + css::uno::Reference< css::frame::XFrame2 > xCloserFrame (s_xCloserFrame.get(), css::uno::UNO_QUERY); + if (xCloserFrame!=xNewCloserFrame) + { + if (xCloserFrame.is()) + impl_setCloser(xCloserFrame, false); + if (xNewCloserFrame.is()) + impl_setCloser(xNewCloserFrame, true); + s_xCloserFrame = xNewCloserFrame; + } +} + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_framework_Frame_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence const &) +{ + rtl::Reference inst = new XFrameImpl(context); + css::uno::XInterface *acquired_inst = cppu::acquire(inst.get()); + + inst->initListeners(); + + return acquired_inst; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/services/mediatypedetectionhelper.cxx b/framework/source/services/mediatypedetectionhelper.cxx new file mode 100644 index 0000000000..894f95740a --- /dev/null +++ b/framework/source/services/mediatypedetectionhelper.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 +#include +#include +#include + +namespace framework +{ + +using namespace ::com::sun::star; + +// constructor + +MediaTypeDetectionHelper::MediaTypeDetectionHelper() +{ +} + +// destructor + +MediaTypeDetectionHelper::~MediaTypeDetectionHelper() +{ +} + +// XInterface, XTypeProvider, XServiceInfo + +OUString SAL_CALL MediaTypeDetectionHelper::getImplementationName() +{ + return "com.sun.star.comp.framework.MediaTypeDetectionHelper"; +} + +sal_Bool SAL_CALL MediaTypeDetectionHelper::supportsService( const OUString& sServiceName ) +{ + return cppu::supportsService(this, sServiceName); +} + +css::uno::Sequence< OUString > SAL_CALL MediaTypeDetectionHelper::getSupportedServiceNames() +{ + return { "com.sun.star.frame.MediaTypeDetectionHelper" }; +} + + +// XStringMapping + +sal_Bool SAL_CALL MediaTypeDetectionHelper::mapStrings(uno::Sequence< OUString >& rSeq) +{ + bool bModified = false; + auto rSeqRange = asNonConstRange(rSeq); + for( sal_Int32 i = rSeq.getLength(); i--; ) + { + + OUString& rUrl = rSeqRange[i]; + INetContentType eType = INetContentTypes::GetContentTypeFromURL( rUrl ); + + OUString aType( INetContentTypes::GetContentType( eType ) ); + if (!aType.isEmpty()) + { + rUrl = aType; + bModified = true; + } + } + return bModified; +} + +} // namespace framework + + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +framework_MediaTypeDetectionHelper_get_implementation( + css::uno::XComponentContext* , css::uno::Sequence const& ) +{ + return cppu::acquire(new framework::MediaTypeDetectionHelper()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/services/modulemanager.cxx b/framework/source/services/modulemanager.cxx new file mode 100644 index 0000000000..ce48cfd441 --- /dev/null +++ b/framework/source/services/modulemanager.cxx @@ -0,0 +1,355 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace { + +class ModuleManager: + public cppu::WeakImplHelper< + css::lang::XServiceInfo, + css::frame::XModuleManager2, + css::container::XContainerQuery > +{ +private: + + /** the global uno service manager. + Must be used to create own needed services. + */ + css::uno::Reference< css::uno::XComponentContext > m_xContext; + + /** points to the underlying configuration. + This ModuleManager does not cache - it calls directly the + configuration API! + */ + css::uno::Reference< css::container::XNameAccess > m_xCFG; + +public: + + explicit ModuleManager(css::uno::Reference< css::uno::XComponentContext > xContext); + + ModuleManager(const ModuleManager&) = delete; + ModuleManager& operator=(const ModuleManager&) = delete; + + // XServiceInfo + 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; + + // XModuleManager + virtual OUString SAL_CALL identify(const css::uno::Reference< css::uno::XInterface >& xModule) override; + + // XNameReplace + virtual void SAL_CALL replaceByName(const OUString& sName , + const css::uno::Any& aValue) override; + + // XNameAccess + virtual css::uno::Any SAL_CALL getByName(const OUString& sName) override; + + virtual css::uno::Sequence< OUString > SAL_CALL getElementNames() override; + + virtual sal_Bool SAL_CALL hasByName(const OUString& sName) override; + + // XElementAccess + virtual css::uno::Type SAL_CALL getElementType() override; + + virtual sal_Bool SAL_CALL hasElements() override; + + // XContainerQuery + virtual css::uno::Reference< css::container::XEnumeration > SAL_CALL createSubSetEnumerationByQuery(const OUString& sQuery) override; + + virtual css::uno::Reference< css::container::XEnumeration > SAL_CALL createSubSetEnumerationByProperties(const css::uno::Sequence< css::beans::NamedValue >& lProperties) override; + +private: + + /** @short makes the real identification of the module. + + @descr It checks for the optional but preferred interface + XModule first. If this module does not exists at the + given component it tries to use XServiceInfo instead. + + Note: This method try to locate a suitable module name. + Nothing else. Selecting the right component and throwing suitable + exceptions must be done outside. + + @see identify() + + @param xComponent + the module for identification. + + @return The identifier of the given module. + Can be empty if given component is not a real module ! + + @threadsafe + */ + OUString implts_identify(const css::uno::Reference< css::uno::XInterface >& xComponent); +}; + +ModuleManager::ModuleManager(css::uno::Reference< css::uno::XComponentContext > xContext) + : m_xContext(std::move(xContext)) +{ + if (!utl::ConfigManager::IsFuzzing()) + { + m_xCFG.set( comphelper::ConfigurationHelper::openConfig( + m_xContext, "/org.openoffice.Setup/Office/Factories", + comphelper::EConfigurationModes::ReadOnly ), + css::uno::UNO_QUERY_THROW ); + } +} + +OUString ModuleManager::getImplementationName() +{ + return "com.sun.star.comp.framework.ModuleManager"; +} + +sal_Bool ModuleManager::supportsService(OUString const & ServiceName) +{ + return cppu::supportsService(this, ServiceName); +} + +css::uno::Sequence< OUString > ModuleManager::getSupportedServiceNames() +{ + return { "com.sun.star.frame.ModuleManager" }; +} + +OUString SAL_CALL ModuleManager::identify(const css::uno::Reference< css::uno::XInterface >& xModule) +{ + // valid parameter? + css::uno::Reference< css::frame::XFrame > xFrame (xModule, css::uno::UNO_QUERY); + css::uno::Reference< css::awt::XWindow > xWindow (xModule, css::uno::UNO_QUERY); + css::uno::Reference< css::frame::XController > xController(xModule, css::uno::UNO_QUERY); + css::uno::Reference< css::frame::XModel > xModel (xModule, css::uno::UNO_QUERY); + + if ( + (!xFrame.is() ) && + (!xWindow.is() ) && + (!xController.is()) && + (!xModel.is() ) + ) + { + throw css::lang::IllegalArgumentException( + "Given module is not a frame nor a window, controller or model.", + static_cast< ::cppu::OWeakObject* >(this), + 1); + } + + if (xFrame.is()) + { + xController = xFrame->getController(); + xWindow = xFrame->getComponentWindow(); + } + if (xController.is()) + xModel = xController->getModel(); + + // modules are implemented by the deepest component in hierarchy ... + // Means: model -> controller -> window + // No fallbacks to higher components are allowed ! + // Note : A frame provides access to module components only ... but it's not a module by himself. + + OUString sModule; + if (xModel.is()) + sModule = implts_identify(xModel); + else if (xController.is()) + sModule = implts_identify(xController); + else if (xWindow.is()) + sModule = implts_identify(xWindow); + + if (sModule.isEmpty()) + throw css::frame::UnknownModuleException( + "Can not find suitable module for the given component.", + static_cast< ::cppu::OWeakObject* >(this)); + + return sModule; +} + +void SAL_CALL ModuleManager::replaceByName(const OUString& sName , + const css::uno::Any& aValue) +{ + ::comphelper::SequenceAsHashMap lProps(aValue); + if (lProps.empty() ) + { + throw css::lang::IllegalArgumentException( + "No properties given to replace part of module.", + static_cast< cppu::OWeakObject * >(this), + 2); + } + + // get access to the element + // Note: Don't use impl_getConfig() method here. Because it creates a readonly access only, further + // it cache it as a member of this module manager instance. If we change some props there ... but don't + // flush changes (because an error occurred) we will read them later. If we use a different config access + // we can close it without a flush... and our read data won't be affected .-) + css::uno::Reference< css::uno::XInterface > xCfg = ::comphelper::ConfigurationHelper::openConfig( + m_xContext, + "/org.openoffice.Setup/Office/Factories", + ::comphelper::EConfigurationModes::Standard); + css::uno::Reference< css::container::XNameAccess > xModules (xCfg, css::uno::UNO_QUERY_THROW); + css::uno::Reference< css::container::XNameReplace > xModule ; + + xModules->getByName(sName) >>= xModule; + if (!xModule.is()) + { + throw css::uno::RuntimeException( + "Was not able to get write access to the requested module entry inside configuration.", + static_cast< cppu::OWeakObject * >(this)); + } + + for (auto const& prop : lProps) + { + // let "NoSuchElementException" out ! We support the same API ... + // and without a flush() at the end all changed data before will be ignored ! + xModule->replaceByName(prop.first.maString, prop.second); + } + + ::comphelper::ConfigurationHelper::flush(xCfg); +} + +css::uno::Any SAL_CALL ModuleManager::getByName(const OUString& sName) +{ + // get access to the element + css::uno::Reference< css::container::XNameAccess > xModule; + if (m_xCFG) + m_xCFG->getByName(sName) >>= xModule; + if (!xModule.is()) + { + throw css::uno::RuntimeException( + "Was not able to get write access to the requested module entry inside configuration.", + static_cast< cppu::OWeakObject * >(this)); + } + + // convert it to seq< PropertyValue > + const css::uno::Sequence< OUString > lPropNames = xModule->getElementNames(); + comphelper::SequenceAsHashMap lProps; + + lProps[OUString("ooSetupFactoryModuleIdentifier")] <<= sName; + for (const OUString& sPropName : lPropNames) + { + lProps[sPropName] = xModule->getByName(sPropName); + } + + return css::uno::Any(lProps.getAsConstPropertyValueList()); +} + +css::uno::Sequence< OUString > SAL_CALL ModuleManager::getElementNames() +{ + return m_xCFG ? m_xCFG->getElementNames() : css::uno::Sequence(); +} + +sal_Bool SAL_CALL ModuleManager::hasByName(const OUString& sName) +{ + return m_xCFG && m_xCFG->hasByName(sName); +} + +css::uno::Type SAL_CALL ModuleManager::getElementType() +{ + return cppu::UnoType>::get(); +} + +sal_Bool SAL_CALL ModuleManager::hasElements() +{ + return m_xCFG && m_xCFG->hasElements(); +} + +css::uno::Reference< css::container::XEnumeration > SAL_CALL ModuleManager::createSubSetEnumerationByQuery(const OUString&) +{ + return css::uno::Reference< css::container::XEnumeration >(); +} + +css::uno::Reference< css::container::XEnumeration > SAL_CALL ModuleManager::createSubSetEnumerationByProperties(const css::uno::Sequence< css::beans::NamedValue >& lProperties) +{ + ::comphelper::SequenceAsHashMap lSearchProps(lProperties); + const css::uno::Sequence< OUString > lModules = getElementNames(); + ::std::vector< css::uno::Any > lResult; + + for (const OUString& rModuleName : lModules) + { + try + { + ::comphelper::SequenceAsHashMap lModuleProps = getByName(rModuleName); + if (lModuleProps.match(lSearchProps)) + lResult.push_back(css::uno::Any(lModuleProps.getAsConstPropertyValueList())); + } + catch(const css::uno::Exception&) + { + } + } + + return new ::comphelper::OAnyEnumeration(comphelper::containerToSequence(lResult)); +} + +OUString ModuleManager::implts_identify(const css::uno::Reference< css::uno::XInterface >& xComponent) +{ + // Search for an optional (!) interface XModule first. + // It's used to overrule an existing service name. Used e.g. by our database form designer + // which uses a writer module internally. + css::uno::Reference< css::frame::XModule > xModule(xComponent, css::uno::UNO_QUERY); + if (xModule.is()) + return xModule->getIdentifier(); + + // detect modules in a generic way... + // comparing service names with configured entries... + css::uno::Reference< css::lang::XServiceInfo > xInfo(xComponent, css::uno::UNO_QUERY); + if (!xInfo.is()) + return OUString(); + + const css::uno::Sequence< OUString > lKnownModules = getElementNames(); + for (const OUString& rName : lKnownModules) + { + if (xInfo->supportsService(rName)) + return rName; + } + + return OUString(); +} + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_framework_ModuleManager_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence const &) +{ + return cppu::acquire(new ModuleManager(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/services/pathsettings.cxx b/framework/source/services/pathsettings.cxx new file mode 100644 index 0000000000..36237e824e --- /dev/null +++ b/framework/source/services/pathsettings.cxx @@ -0,0 +1,1421 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace framework; + +constexpr OUString CFGPROP_USERPATHS = u"UserPaths"_ustr; +constexpr OUString CFGPROP_WRITEPATH = u"WritePath"_ustr; + +/* + 0 : old style "Template" string using ";" as separator + 1 : internal paths "Template_internal" string list + 2 : user paths "Template_user" string list + 3 : write path "Template_write" string + */ + +constexpr OUString POSTFIX_INTERNAL_PATHS = u"_internal"_ustr; +constexpr OUString POSTFIX_USER_PATHS = u"_user"_ustr; +constexpr OUString POSTFIX_WRITE_PATH = u"_writable"_ustr; + +namespace { + +const sal_Int32 IDGROUP_OLDSTYLE = 0; +const sal_Int32 IDGROUP_INTERNAL_PATHS = 1; +const sal_Int32 IDGROUP_USER_PATHS = 2; +const sal_Int32 IDGROUP_WRITE_PATH = 3; + +const sal_Int32 IDGROUP_COUNT = 4; + +sal_Int32 impl_getPropGroup(sal_Int32 nID) +{ + return (nID % IDGROUP_COUNT); +} + +/* enable it if you wish to migrate old user settings (using the old cfg schema) on demand... + disable it in case only the new schema must be used. + */ + +typedef ::cppu::WeakComponentImplHelper< + css::lang::XServiceInfo, + css::util::XChangesListener, // => XEventListener + css::util::XPathSettings> // => XPropertySet + PathSettings_BASE; + +class PathSettings : private cppu::BaseMutex + , public PathSettings_BASE + , public ::cppu::OPropertySetHelper +{ + struct PathInfo + { + public: + + PathInfo() + : bIsSinglePath (false) + , bIsReadonly (false) + {} + + /// an internal name describing this path + OUString sPathName; + + /// contains all paths, which are used internally - but are not visible for the user. + std::vector lInternalPaths; + + /// contains all paths configured by the user + std::vector lUserPaths; + + /// this special path is used to generate feature depending content there + OUString sWritePath; + + /// indicates real single paths, which uses WritePath property only + bool bIsSinglePath; + + /// simple handling of finalized/mandatory states ... => we know one state READONLY only .-) + bool bIsReadonly; + }; + + typedef std::unordered_map PathHash; + + enum EChangeOp + { + E_UNDEFINED, + E_ADDED, + E_CHANGED, + E_REMOVED + }; + +private: + + /** reference to factory, which has create this instance. */ + css::uno::Reference< css::uno::XComponentContext > m_xContext; + + /** list of all path variables and her corresponding values. */ + PathSettings::PathHash m_lPaths; + + /** describes all properties available on our interface. + Will be generated on demand based on our path list m_lPaths. */ + css::uno::Sequence< css::beans::Property > m_lPropDesc; + + /** helper needed to (re-)substitute all internal save path values. */ + css::uno::Reference< css::util::XStringSubstitution > m_xSubstitution; + + /** provides access to the old configuration schema (which will be migrated on demand). */ + css::uno::Reference< css::container::XNameAccess > m_xCfgOld; + + /** provides access to the new configuration schema. */ + css::uno::Reference< css::container::XNameAccess > m_xCfgNew; + + /** helper to listen for configuration changes without ownership cycle problems */ + css::uno::Reference< css::util::XChangesListener > m_xCfgNewListener; + + std::unique_ptr<::cppu::OPropertyArrayHelper> m_pPropHelp; + +public: + + /** initialize a new instance of this class. + Attention: It's necessary for right function of this class, that the order of base + classes is the right one. Because we transfer information from one base to another + during this ctor runs! */ + explicit PathSettings(css::uno::Reference< css::uno::XComponentContext > xContext); + + /** free all used resources ... if it was not already done. */ + virtual ~PathSettings() override; + + virtual OUString SAL_CALL getImplementationName() override + { + return "com.sun.star.comp.framework.PathSettings"; + } + + virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override + { + return cppu::supportsService(this, ServiceName); + } + + virtual css::uno::Sequence SAL_CALL getSupportedServiceNames() override + { + return {"com.sun.star.util.PathSettings"}; + } + + // XInterface + virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type& type) override; + virtual void SAL_CALL acquire() noexcept override + { OWeakObject::acquire(); } + virtual void SAL_CALL release() noexcept override + { OWeakObject::release(); } + + // XTypeProvider + virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes( ) override; + + // css::util::XChangesListener + virtual void SAL_CALL changesOccurred(const css::util::ChangesEvent& aEvent) override; + + // css::lang::XEventListener + virtual void SAL_CALL disposing(const css::lang::EventObject& aSource) override; + + /** + * XPathSettings attribute methods + */ + virtual OUString SAL_CALL getAddin() override + { return getStringProperty("Addin"); } + virtual void SAL_CALL setAddin(const OUString& p1) override + { setStringProperty("Addin", p1); } + virtual OUString SAL_CALL getAutoCorrect() override + { return getStringProperty("AutoCorrect"); } + virtual void SAL_CALL setAutoCorrect(const OUString& p1) override + { setStringProperty("AutoCorrect", p1); } + virtual OUString SAL_CALL getAutoText() override + { return getStringProperty("AutoText"); } + virtual void SAL_CALL setAutoText(const OUString& p1) override + { setStringProperty("AutoText", p1); } + virtual OUString SAL_CALL getBackup() override + { return getStringProperty("Backup"); } + virtual void SAL_CALL setBackup(const OUString& p1) override + { setStringProperty("Backup", p1); } + virtual OUString SAL_CALL getBasic() override + { return getStringProperty("Basic"); } + virtual void SAL_CALL setBasic(const OUString& p1) override + { setStringProperty("Basic", p1); } + virtual OUString SAL_CALL getBitmap() override + { return getStringProperty("Bitmap"); } + virtual void SAL_CALL setBitmap(const OUString& p1) override + { setStringProperty("Bitmap", p1); } + virtual OUString SAL_CALL getConfig() override + { return getStringProperty("Config"); } + virtual void SAL_CALL setConfig(const OUString& p1) override + { setStringProperty("Config", p1); } + virtual OUString SAL_CALL getDictionary() override + { return getStringProperty("Dictionary"); } + virtual void SAL_CALL setDictionary(const OUString& p1) override + { setStringProperty("Dictionary", p1); } + virtual OUString SAL_CALL getFavorite() override + { return getStringProperty("Favorite"); } + virtual void SAL_CALL setFavorite(const OUString& p1) override + { setStringProperty("Favorite", p1); } + virtual OUString SAL_CALL getFilter() override + { return getStringProperty("Filter"); } + virtual void SAL_CALL setFilter(const OUString& p1) override + { setStringProperty("Filter", p1); } + virtual OUString SAL_CALL getGallery() override + { return getStringProperty("Gallery"); } + virtual void SAL_CALL setGallery(const OUString& p1) override + { setStringProperty("Gallery", p1); } + virtual OUString SAL_CALL getGraphic() override + { return getStringProperty("Graphic"); } + virtual void SAL_CALL setGraphic(const OUString& p1) override + { setStringProperty("Graphic", p1); } + virtual OUString SAL_CALL getHelp() override + { return getStringProperty("Help"); } + virtual void SAL_CALL setHelp(const OUString& p1) override + { setStringProperty("Help", p1); } + virtual OUString SAL_CALL getLinguistic() override + { return getStringProperty("Linguistic"); } + virtual void SAL_CALL setLinguistic(const OUString& p1) override + { setStringProperty("Linguistic", p1); } + virtual OUString SAL_CALL getModule() override + { return getStringProperty("Module"); } + virtual void SAL_CALL setModule(const OUString& p1) override + { setStringProperty("Module", p1); } + virtual OUString SAL_CALL getPalette() override + { return getStringProperty("Palette"); } + virtual void SAL_CALL setPalette(const OUString& p1) override + { setStringProperty("Palette", p1); } + virtual OUString SAL_CALL getPlugin() override + { return getStringProperty("Plugin"); } + virtual void SAL_CALL setPlugin(const OUString& p1) override + { setStringProperty("Plugin", p1); } + virtual OUString SAL_CALL getStorage() override + { return getStringProperty("Storage"); } + virtual void SAL_CALL setStorage(const OUString& p1) override + { setStringProperty("Storage", p1); } + virtual OUString SAL_CALL getTemp() override + { return getStringProperty("Temp"); } + virtual void SAL_CALL setTemp(const OUString& p1) override + { setStringProperty("Temp", p1); } + virtual OUString SAL_CALL getTemplate() override + { return getStringProperty("Template"); } + virtual void SAL_CALL setTemplate(const OUString& p1) override + { setStringProperty("Template", p1); } + virtual OUString SAL_CALL getUIConfig() override + { return getStringProperty("UIConfig"); } + virtual void SAL_CALL setUIConfig(const OUString& p1) override + { setStringProperty("UIConfig", p1); } + virtual OUString SAL_CALL getUserConfig() override + { return getStringProperty("UserConfig"); } + virtual void SAL_CALL setUserConfig(const OUString& p1) override + { setStringProperty("UserConfig", p1); } + virtual OUString SAL_CALL getUserDictionary() override + { return getStringProperty("UserDictionary"); } + virtual void SAL_CALL setUserDictionary(const OUString& p1) override + { setStringProperty("UserDictionary", p1); } + virtual OUString SAL_CALL getWork() override + { return getStringProperty("Work"); } + virtual void SAL_CALL setWork(const OUString& p1) override + { setStringProperty("Work", p1); } + virtual OUString SAL_CALL getBasePathShareLayer() override + { return getStringProperty("UIConfig"); } + virtual void SAL_CALL setBasePathShareLayer(const OUString& p1) override + { setStringProperty("UIConfig", p1); } + virtual OUString SAL_CALL getBasePathUserLayer() override + { return getStringProperty("UserConfig"); } + virtual void SAL_CALL setBasePathUserLayer(const OUString& p1) override + { setStringProperty("UserConfig", p1); } + + /** + * overrides to resolve inheritance ambiguity + */ + virtual void SAL_CALL setPropertyValue(const OUString& p1, const css::uno::Any& p2) override + { ::cppu::OPropertySetHelper::setPropertyValue(p1, p2); } + virtual css::uno::Any SAL_CALL getPropertyValue(const OUString& p1) override + { return ::cppu::OPropertySetHelper::getPropertyValue(p1); } + virtual void SAL_CALL addPropertyChangeListener(const OUString& p1, const css::uno::Reference& p2) override + { ::cppu::OPropertySetHelper::addPropertyChangeListener(p1, p2); } + virtual void SAL_CALL removePropertyChangeListener(const OUString& p1, const css::uno::Reference& p2) override + { ::cppu::OPropertySetHelper::removePropertyChangeListener(p1, p2); } + virtual void SAL_CALL addVetoableChangeListener(const OUString& p1, const css::uno::Reference& p2) override + { ::cppu::OPropertySetHelper::addVetoableChangeListener(p1, p2); } + virtual void SAL_CALL removeVetoableChangeListener(const OUString& p1, const css::uno::Reference& p2) override + { ::cppu::OPropertySetHelper::removeVetoableChangeListener(p1, p2); } + /** read all configured paths and create all needed internal structures. */ + void impl_readAll(); + +private: + virtual void SAL_CALL disposing() final override; + + /// @throws css::uno::RuntimeException + OUString getStringProperty(const OUString& p1); + + /// @throws css::uno::RuntimeException + void setStringProperty(const OUString& p1, const OUString& p2); + + /** read a path info using the old cfg schema. + This is needed for "migration on demand" reasons only. + Can be removed for next major release .-) */ + std::vector impl_readOldFormat(const OUString& sPath); + + /** read a path info using the new cfg schema. */ + PathSettings::PathInfo impl_readNewFormat(const OUString& sPath); + + /** filter "real user defined paths" from the old configuration schema + and set it as UserPaths on the new schema. + Can be removed with new major release ... */ + + void impl_mergeOldUserPaths( PathSettings::PathInfo& rPath, + const std::vector& lOld ); + + /** reload one path directly from the new configuration schema (because + it was updated by any external code) */ + PathSettings::EChangeOp impl_updatePath(const OUString& sPath , + bool bNotifyListener); + + /** replace all might existing placeholder variables inside the given path ... + or check if the given path value uses paths, which can be replaced with predefined + placeholder variables ... + */ + void impl_subst(std::vector& lVals , + const css::uno::Reference< css::util::XStringSubstitution >& xSubst , + bool bReSubst); + + void impl_subst(PathSettings::PathInfo& aPath , + bool bReSubst); + + /** converts our new string list schema to the old ";" separated schema ... */ + OUString impl_convertPath2OldStyle(const PathSettings::PathInfo& rPath ) const; + std::vector impl_convertOldStyle2Path(std::u16string_view sOldStylePath) const; + + /** remove still known paths from the given lList argument. + So real user defined paths can be extracted from the list of + fix internal paths ! + */ + void impl_purgeKnownPaths(PathSettings::PathInfo& rPath, + std::vector& lList); + + /** rebuild the member m_lPropDesc using the path list m_lPaths. */ + void impl_rebuildPropertyDescriptor(); + + /** provides direct access to the list of path values + using its internal property id. + */ + css::uno::Any impl_getPathValue( sal_Int32 nID ) const; + void impl_setPathValue( sal_Int32 nID , + const css::uno::Any& aVal); + + /** check the given handle and return the corresponding PathInfo reference. + These reference can be used then directly to manipulate these path. */ + PathSettings::PathInfo* impl_getPathAccess (sal_Int32 nHandle); + const PathSettings::PathInfo* impl_getPathAccessConst(sal_Int32 nHandle) const; + + /** it checks, if the given path value seems to be a valid URL or system path. */ + bool impl_isValidPath(std::u16string_view sPath) const; + bool impl_isValidPath(const std::vector& lPath) const; + + void impl_storePath(const PathSettings::PathInfo& aPath); + + css::uno::Sequence< sal_Int32 > impl_mapPathName2IDList(std::u16string_view sPath); + + void impl_notifyPropListener( std::u16string_view sPath , + const PathSettings::PathInfo* pPathOld, + const PathSettings::PathInfo* pPathNew); + + // OPropertySetHelper + virtual sal_Bool SAL_CALL convertFastPropertyValue( css::uno::Any& aConvertedValue, + css::uno::Any& aOldValue, + sal_Int32 nHandle, + const css::uno::Any& aValue ) override; + virtual void SAL_CALL setFastPropertyValue_NoBroadcast( sal_Int32 nHandle, + const css::uno::Any& aValue ) override; + virtual void SAL_CALL getFastPropertyValue( css::uno::Any& aValue, + sal_Int32 nHandle ) const override; + // Avoid: + // warning: 'virtual css::uno::Any cppu::OPropertySetHelper::getFastPropertyValue(sal_Int32)' was hidden [-Woverloaded-virtual] + // warning: by ‘virtual void {anonymous}::PathSettings::getFastPropertyValue(css::uno::Any&, sal_Int32) const’ [-Woverloaded-virtual] + using cppu::OPropertySetHelper::getFastPropertyValue; + virtual ::cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper() override; + virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo() override; + + /** factory methods to guarantee right (but on demand) initialized members ... */ + css::uno::Reference< css::util::XStringSubstitution > fa_getSubstitution(); + css::uno::Reference< css::container::XNameAccess > fa_getCfgOld(); + css::uno::Reference< css::container::XNameAccess > fa_getCfgNew(); +}; + +PathSettings::PathSettings( css::uno::Reference< css::uno::XComponentContext > xContext ) + : PathSettings_BASE(m_aMutex) + , ::cppu::OPropertySetHelper(cppu::WeakComponentImplHelperBase::rBHelper) + , m_xContext (std::move(xContext)) +{ +} + +PathSettings::~PathSettings() +{ + disposing(); +} + +void SAL_CALL PathSettings::disposing() +{ + osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex); + + css::uno::Reference< css::util::XChangesNotifier > + xBroadcaster(m_xCfgNew, css::uno::UNO_QUERY); + if (xBroadcaster.is()) + xBroadcaster->removeChangesListener(m_xCfgNewListener); + + m_xSubstitution.clear(); + m_xCfgOld.clear(); + m_xCfgNew.clear(); + m_xCfgNewListener.clear(); + + m_pPropHelp.reset(); +} + +css::uno::Any SAL_CALL PathSettings::queryInterface( const css::uno::Type& _rType ) +{ + css::uno::Any aRet = PathSettings_BASE::queryInterface( _rType ); + if ( !aRet.hasValue() ) + aRet = ::cppu::OPropertySetHelper::queryInterface( _rType ); + return aRet; +} + +css::uno::Sequence< css::uno::Type > SAL_CALL PathSettings::getTypes( ) +{ + return comphelper::concatSequences( + PathSettings_BASE::getTypes(), + ::cppu::OPropertySetHelper::getTypes() + ); +} + +void SAL_CALL PathSettings::changesOccurred(const css::util::ChangesEvent& aEvent) +{ + sal_Int32 c = aEvent.Changes.getLength(); + sal_Int32 i = 0; + bool bUpdateDescriptor = false; + + for (i=0; i>= sChanged; + + OUString sPath = ::utl::extractFirstFromConfigurationPath(sChanged); + if (!sPath.isEmpty()) + { + PathSettings::EChangeOp eOp = impl_updatePath(sPath, true); + if ( + (eOp == PathSettings::E_ADDED ) || + (eOp == PathSettings::E_REMOVED) + ) + bUpdateDescriptor = true; + } + } + + if (bUpdateDescriptor) + impl_rebuildPropertyDescriptor(); +} + +void SAL_CALL PathSettings::disposing(const css::lang::EventObject& aSource) +{ + osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex); + + if (aSource.Source == m_xCfgNew) + m_xCfgNew.clear(); +} + +OUString PathSettings::getStringProperty(const OUString& p1) +{ + css::uno::Any a = ::cppu::OPropertySetHelper::getPropertyValue(p1); + OUString s; + a >>= s; + return s; +} + +void PathSettings::setStringProperty(const OUString& p1, const OUString& p2) +{ + ::cppu::OPropertySetHelper::setPropertyValue(p1, css::uno::Any(p2)); +} + +void PathSettings::impl_readAll() +{ + try + { + // TODO think about me + css::uno::Reference< css::container::XNameAccess > xCfg = fa_getCfgNew(); + css::uno::Sequence< OUString > lPaths = xCfg->getElementNames(); + + sal_Int32 c = lPaths.getLength(); + for (sal_Int32 i = 0; i < c; ++i) + { + const OUString& sPath = lPaths[i]; + impl_updatePath(sPath, false); + } + } + catch(const css::uno::RuntimeException& ) + { + } + + impl_rebuildPropertyDescriptor(); +} + +// NO substitution here ! It's done outside ... +std::vector PathSettings::impl_readOldFormat(const OUString& sPath) +{ + css::uno::Reference< css::container::XNameAccess > xCfg( fa_getCfgOld() ); + std::vector aPathVal; + + if( xCfg->hasByName(sPath) ) + { + css::uno::Any aVal( xCfg->getByName(sPath) ); + + OUString sStringVal; + css::uno::Sequence< OUString > lStringListVal; + + if (aVal >>= sStringVal) + { + aPathVal.push_back(sStringVal); + } + else if (aVal >>= lStringListVal) + { + aPathVal = comphelper::sequenceToContainer>(lStringListVal); + } + } + + return aPathVal; +} + +// NO substitution here ! It's done outside ... +PathSettings::PathInfo PathSettings::impl_readNewFormat(const OUString& sPath) +{ + css::uno::Reference< css::container::XNameAccess > xCfg = fa_getCfgNew(); + + // get access to the "queried" path + css::uno::Reference< css::container::XNameAccess > xPath; + xCfg->getByName(sPath) >>= xPath; + + PathSettings::PathInfo aPathVal; + + // read internal path list + css::uno::Reference< css::container::XNameAccess > xIPath; + xPath->getByName("InternalPaths") >>= xIPath; + aPathVal.lInternalPaths = comphelper::sequenceToContainer>(xIPath->getElementNames()); + + // read user defined path list + css::uno::Sequence vTmpUserPathsSeq; + xPath->getByName(CFGPROP_USERPATHS) >>= vTmpUserPathsSeq; + aPathVal.lUserPaths = comphelper::sequenceToContainer>(vTmpUserPathsSeq); + + // read the writeable path + xPath->getByName(CFGPROP_WRITEPATH) >>= aPathVal.sWritePath; + + // avoid duplicates, by removing the writeable path from + // the user defined path list if it happens to be there too + std::vector::iterator aI = std::find(aPathVal.lUserPaths.begin(), aPathVal.lUserPaths.end(), aPathVal.sWritePath); + if (aI != aPathVal.lUserPaths.end()) + aPathVal.lUserPaths.erase(aI); + + // read state props + xPath->getByName("IsSinglePath") >>= aPathVal.bIsSinglePath; + + // analyze finalized/mandatory states + aPathVal.bIsReadonly = false; + css::uno::Reference< css::beans::XProperty > xInfo(xPath, css::uno::UNO_QUERY); + if (xInfo.is()) + { + css::beans::Property aInfo = xInfo->getAsProperty(); + bool bFinalized = ((aInfo.Attributes & css::beans::PropertyAttribute::READONLY ) == css::beans::PropertyAttribute::READONLY ); + + // Note: 'till we support finalized/mandatory on our API more in detail we handle + // all states simple as READONLY! But because all really needed paths are "mandatory" by default + // we have to handle "finalized" as the real "readonly" indicator. + aPathVal.bIsReadonly = bFinalized; + } + + return aPathVal; +} + +void PathSettings::impl_storePath(const PathSettings::PathInfo& aPath) +{ + css::uno::Reference< css::container::XNameAccess > xCfgNew = fa_getCfgNew(); + css::uno::Reference< css::container::XNameAccess > xCfgOld = fa_getCfgOld(); + + // try to replace path-parts with well known and supported variables. + // So an office can be moved easily to another location without losing + // its related paths. + PathInfo aResubstPath(aPath); + impl_subst(aResubstPath, true); + + // update new configuration + if (! aResubstPath.bIsSinglePath) + { + ::comphelper::ConfigurationHelper::writeRelativeKey(xCfgNew, + aResubstPath.sPathName, + CFGPROP_USERPATHS, + css::uno::Any(comphelper::containerToSequence(aResubstPath.lUserPaths))); + } + + ::comphelper::ConfigurationHelper::writeRelativeKey(xCfgNew, + aResubstPath.sPathName, + CFGPROP_WRITEPATH, + css::uno::Any(aResubstPath.sWritePath)); + + ::comphelper::ConfigurationHelper::flush(xCfgNew); + + // remove the whole path from the old configuration! + // Otherwise we can't make sure that the diff between new and old configuration + // on loading time really represents a user setting!!! + + // Check if the given path exists inside the old configuration. + // Because our new configuration knows more than the list of old paths ... ! + if (xCfgOld->hasByName(aResubstPath.sPathName)) + { + css::uno::Reference< css::beans::XPropertySet > xProps(xCfgOld, css::uno::UNO_QUERY_THROW); + xProps->setPropertyValue(aResubstPath.sPathName, css::uno::Any()); + ::comphelper::ConfigurationHelper::flush(xCfgOld); + } +} + +void PathSettings::impl_mergeOldUserPaths( PathSettings::PathInfo& rPath, + const std::vector& lOld ) +{ + for (auto const& old : lOld) + { + if (rPath.bIsSinglePath) + { + SAL_WARN_IF(lOld.size()>1, "fwk", "PathSettings::impl_mergeOldUserPaths(): Single path has more than one path value inside old configuration (Common.xcu)!"); + if ( rPath.sWritePath != old ) + rPath.sWritePath = old; + } + else + { + if ( + ( std::find(rPath.lInternalPaths.begin(), rPath.lInternalPaths.end(), old) == rPath.lInternalPaths.end()) && + ( std::find(rPath.lUserPaths.begin(), rPath.lUserPaths.end(), old) == rPath.lUserPaths.end() ) && + ( rPath.sWritePath != old ) + ) + rPath.lUserPaths.push_back(old); + } + } +} + +PathSettings::EChangeOp PathSettings::impl_updatePath(const OUString& sPath , + bool bNotifyListener) +{ + // SAFE -> + osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex); + + PathSettings::PathInfo* pPathOld = nullptr; + PathSettings::PathInfo* pPathNew = nullptr; + PathSettings::EChangeOp eOp = PathSettings::E_UNDEFINED; + PathSettings::PathInfo aPath; + + try + { + aPath = impl_readNewFormat(sPath); + aPath.sPathName = sPath; + // replace all might existing variables with real values + // Do it before these old paths will be compared against the + // new path configuration. Otherwise some strings uses different variables ... but substitution + // will produce strings with same content (because some variables are redundant!) + impl_subst(aPath, false); + } + catch(const css::uno::RuntimeException&) + { throw; } + catch(const css::container::NoSuchElementException&) + { eOp = PathSettings::E_REMOVED; } + catch(const css::uno::Exception&) + { throw; } + + try + { + // migration of old user defined values on demand + // can be disabled for a new major + std::vector lOldVals = impl_readOldFormat(sPath); + // replace all might existing variables with real values + // Do it before these old paths will be compared against the + // new path configuration. Otherwise some strings uses different variables ... but substitution + // will produce strings with same content (because some variables are redundant!) + impl_subst(lOldVals, fa_getSubstitution(), false); + impl_mergeOldUserPaths(aPath, lOldVals); + } + catch(const css::uno::RuntimeException&) + { throw; } + // Normal(!) exceptions can be ignored! + // E.g. in case an addon installs a new path, which was not well known for an OOo 1.x installation + // we can't find a value for it inside the "old" configuration. So a NoSuchElementException + // will be normal .-) + catch(const css::uno::Exception&) + {} + + PathSettings::PathHash::iterator pPath = m_lPaths.find(sPath); + if (eOp == PathSettings::E_UNDEFINED) + { + if (pPath != m_lPaths.end()) + eOp = PathSettings::E_CHANGED; + else + eOp = PathSettings::E_ADDED; + } + + switch(eOp) + { + case PathSettings::E_ADDED : + { + if (bNotifyListener) + { + pPathOld = nullptr; + pPathNew = &aPath; + impl_notifyPropListener(sPath, pPathOld, pPathNew); + } + m_lPaths[sPath] = aPath; + } + break; + + case PathSettings::E_CHANGED : + { + if (bNotifyListener) + { + pPathOld = &(pPath->second); + pPathNew = &aPath; + impl_notifyPropListener(sPath, pPathOld, pPathNew); + } + m_lPaths[sPath] = aPath; + } + break; + + case PathSettings::E_REMOVED : + { + if (pPath != m_lPaths.end()) + { + if (bNotifyListener) + { + pPathOld = &(pPath->second); + pPathNew = nullptr; + impl_notifyPropListener(sPath, pPathOld, pPathNew); + } + m_lPaths.erase(pPath); + } + } + break; + + default: // to let compiler be happy + break; + } + + return eOp; +} + +css::uno::Sequence< sal_Int32 > PathSettings::impl_mapPathName2IDList(std::u16string_view sPath) +{ + OUString sInternalProp = OUString::Concat(sPath)+POSTFIX_INTERNAL_PATHS; + OUString sUserProp = OUString::Concat(sPath)+POSTFIX_USER_PATHS; + OUString sWriteProp = OUString::Concat(sPath)+POSTFIX_WRITE_PATH; + + // Attention: The default set of IDs is fix and must follow these schema. + // Otherwise the outside code ant work for new added properties. + // Why? + // The outside code must fire N events for every changed property. + // And the knowing about packaging of variables of the structure PathInfo + // follow these group IDs! But if such ID is not in the range of [0..IDGROUP_COUNT] + // the outside can't determine the right group ... and can not fire the right events .-) + + css::uno::Sequence lIDs{ IDGROUP_OLDSTYLE, IDGROUP_INTERNAL_PATHS, + IDGROUP_USER_PATHS, IDGROUP_WRITE_PATH }; + assert(lIDs.getLength() == IDGROUP_COUNT); + auto plIDs = lIDs.getArray(); + + sal_Int32 c = m_lPropDesc.getLength(); + sal_Int32 i = 0; + for (i=0; i lHandles(1); + auto plHandles = lHandles.getArray(); + css::uno::Sequence< css::uno::Any > lOldVals(1); + auto plOldVals = lOldVals.getArray(); + css::uno::Sequence< css::uno::Any > lNewVals(1); + auto plNewVals = lNewVals.getArray(); + + css::uno::Sequence< sal_Int32 > lIDs = impl_mapPathName2IDList(sPath); + sal_Int32 c = lIDs.getLength(); + sal_Int32 i = 0; + sal_Int32 nMaxID = m_lPropDesc.getLength()-1; + for (i=0; i nMaxID) + ) + continue; + + plHandles[0] = nID; + switch(impl_getPropGroup(nID)) + { + case IDGROUP_OLDSTYLE : + { + if (pPathOld) + { + OUString sVal = impl_convertPath2OldStyle(*pPathOld); + plOldVals[0] <<= sVal; + } + if (pPathNew) + { + OUString sVal = impl_convertPath2OldStyle(*pPathNew); + plNewVals[0] <<= sVal; + } + } + break; + + case IDGROUP_INTERNAL_PATHS : + { + if (pPathOld) + plOldVals[0] <<= comphelper::containerToSequence(pPathOld->lInternalPaths); + if (pPathNew) + plNewVals[0] <<= comphelper::containerToSequence(pPathNew->lInternalPaths); + } + break; + + case IDGROUP_USER_PATHS : + { + if (pPathOld) + plOldVals[0] <<= comphelper::containerToSequence(pPathOld->lUserPaths); + if (pPathNew) + plNewVals[0] <<= comphelper::containerToSequence(pPathNew->lUserPaths); + } + break; + + case IDGROUP_WRITE_PATH : + { + if (pPathOld) + plOldVals[0] <<= pPathOld->sWritePath; + if (pPathNew) + plNewVals[0] <<= pPathNew->sWritePath; + } + break; + } + + fire(plHandles, + plNewVals, + plOldVals, + 1, + false); + } +} + +void PathSettings::impl_subst(std::vector& lVals , + const css::uno::Reference< css::util::XStringSubstitution >& xSubst , + bool bReSubst) +{ + for (auto & old : lVals) + { + OUString sNew; + if (bReSubst) + sNew = xSubst->reSubstituteVariables(old); + else + sNew = xSubst->substituteVariables(old, false); + + old = sNew; + } +} + +void PathSettings::impl_subst(PathSettings::PathInfo& aPath , + bool bReSubst) +{ + css::uno::Reference< css::util::XStringSubstitution > xSubst = fa_getSubstitution(); + + impl_subst(aPath.lInternalPaths, xSubst, bReSubst); + impl_subst(aPath.lUserPaths , xSubst, bReSubst); + if (bReSubst) + aPath.sWritePath = xSubst->reSubstituteVariables(aPath.sWritePath); + else + aPath.sWritePath = xSubst->substituteVariables(aPath.sWritePath, false); +} + +OUString PathSettings::impl_convertPath2OldStyle(const PathSettings::PathInfo& rPath) const +{ + OUStringBuffer sPathVal(256); + + for (auto const& internalPath : rPath.lInternalPaths) + { + if (sPathVal.getLength()) + sPathVal.append(";"); + sPathVal.append(internalPath); + } + for (auto const& userPath : rPath.lUserPaths) + { + if (sPathVal.getLength()) + sPathVal.append(";"); + sPathVal.append(userPath); + } + if (!rPath.sWritePath.isEmpty()) + { + if (sPathVal.getLength()) + sPathVal.append(";"); + sPathVal.append(rPath.sWritePath); + } + + return sPathVal.makeStringAndClear(); +} + +std::vector PathSettings::impl_convertOldStyle2Path(std::u16string_view sOldStylePath) const +{ + std::vector lList; + sal_Int32 nToken = 0; + do + { + OUString sToken( o3tl::getToken(sOldStylePath, 0, ';', nToken) ); + if (!sToken.isEmpty()) + lList.push_back(sToken); + } + while(nToken >= 0); + + return lList; +} + +void PathSettings::impl_purgeKnownPaths(PathSettings::PathInfo& rPath, + std::vector& lList) +{ + // Erase items in the internal path list from lList. + // Also erase items in the internal path list from the user path list. + for (auto const& internalPath : rPath.lInternalPaths) + { + std::vector::iterator pItem = std::find(lList.begin(), lList.end(), internalPath); + if (pItem != lList.end()) + lList.erase(pItem); + pItem = std::find(rPath.lUserPaths.begin(), rPath.lUserPaths.end(), internalPath); + if (pItem != rPath.lUserPaths.end()) + rPath.lUserPaths.erase(pItem); + } + + // Erase items not in lList from the user path list. + std::erase_if(rPath.lUserPaths, + [&lList](const OUString& rItem) { + return std::find(lList.begin(), lList.end(), rItem) == lList.end(); + }); + + // Erase items in the user path list from lList. + for (auto const& userPath : rPath.lUserPaths) + { + std::vector::iterator pItem = std::find(lList.begin(), lList.end(), userPath); + if (pItem != lList.end()) + lList.erase(pItem); + } + + // Erase the write path from lList + std::vector::iterator pItem = std::find(lList.begin(), lList.end(), rPath.sWritePath); + if (pItem != lList.end()) + lList.erase(pItem); +} + +void PathSettings::impl_rebuildPropertyDescriptor() +{ + // SAFE -> + osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex); + + sal_Int32 c = static_cast(m_lPaths.size()); + sal_Int32 i = 0; + m_lPropDesc.realloc(c*IDGROUP_COUNT); + auto plPropDesc = m_lPropDesc.getArray(); + + for (auto const& path : m_lPaths) + { + const PathSettings::PathInfo& rPath = path.second; + css::beans::Property* pProp = nullptr; + + pProp = &(plPropDesc[i]); + pProp->Name = rPath.sPathName; + pProp->Handle = i; + pProp->Type = cppu::UnoType::get(); + pProp->Attributes = css::beans::PropertyAttribute::BOUND; + if (rPath.bIsReadonly) + pProp->Attributes |= css::beans::PropertyAttribute::READONLY; + ++i; + + pProp = &(plPropDesc[i]); + pProp->Name = rPath.sPathName+POSTFIX_INTERNAL_PATHS; + pProp->Handle = i; + pProp->Type = cppu::UnoType>::get(); + pProp->Attributes = css::beans::PropertyAttribute::BOUND | + css::beans::PropertyAttribute::READONLY; + ++i; + + pProp = &(plPropDesc[i]); + pProp->Name = rPath.sPathName+POSTFIX_USER_PATHS; + pProp->Handle = i; + pProp->Type = cppu::UnoType>::get(); + pProp->Attributes = css::beans::PropertyAttribute::BOUND; + if (rPath.bIsReadonly) + pProp->Attributes |= css::beans::PropertyAttribute::READONLY; + ++i; + + pProp = &(plPropDesc[i]); + pProp->Name = rPath.sPathName+POSTFIX_WRITE_PATH; + pProp->Handle = i; + pProp->Type = cppu::UnoType::get(); + pProp->Attributes = css::beans::PropertyAttribute::BOUND; + if (rPath.bIsReadonly) + pProp->Attributes |= css::beans::PropertyAttribute::READONLY; + ++i; + } + + m_pPropHelp.reset(new ::cppu::OPropertyArrayHelper(m_lPropDesc, false)); // false => not sorted ... must be done inside helper + + // <- SAFE +} + +css::uno::Any PathSettings::impl_getPathValue(sal_Int32 nID) const +{ + const PathSettings::PathInfo* pPath = impl_getPathAccessConst(nID); + if (! pPath) + throw css::lang::IllegalArgumentException(); + + css::uno::Any aVal; + switch(impl_getPropGroup(nID)) + { + case IDGROUP_OLDSTYLE : + { + OUString sVal = impl_convertPath2OldStyle(*pPath); + aVal <<= sVal; + } + break; + + case IDGROUP_INTERNAL_PATHS : + { + aVal <<= comphelper::containerToSequence(pPath->lInternalPaths); + } + break; + + case IDGROUP_USER_PATHS : + { + aVal <<= comphelper::containerToSequence(pPath->lUserPaths); + } + break; + + case IDGROUP_WRITE_PATH : + { + aVal <<= pPath->sWritePath; + } + break; + } + + return aVal; +} + +void PathSettings::impl_setPathValue( sal_Int32 nID , + const css::uno::Any& aVal) +{ + PathSettings::PathInfo* pOrgPath = impl_getPathAccess(nID); + if (! pOrgPath) + throw css::container::NoSuchElementException(); + + // We work on a copied path ... so we can be sure that errors during this operation + // does not make our internal cache invalid .-) + PathSettings::PathInfo aChangePath(*pOrgPath); + + switch(impl_getPropGroup(nID)) + { + case IDGROUP_OLDSTYLE : + { + OUString sVal; + aVal >>= sVal; + std::vector lList = impl_convertOldStyle2Path(sVal); + impl_subst(lList, fa_getSubstitution(), false); + impl_purgeKnownPaths(aChangePath, lList); + if (! impl_isValidPath(lList)) + throw css::lang::IllegalArgumentException(); + + if (aChangePath.bIsSinglePath) + { + SAL_WARN_IF(lList.size()>1, "fwk", "PathSettings::impl_setPathValue(): You try to set more than path value for a defined SINGLE_PATH!"); + if ( !lList.empty() ) + aChangePath.sWritePath = *(lList.begin()); + else + aChangePath.sWritePath.clear(); + } + else + { + for (auto const& elem : lList) + { + aChangePath.lUserPaths.push_back(elem); + } + } + } + break; + + case IDGROUP_INTERNAL_PATHS : + { + if (aChangePath.bIsSinglePath) + { + throw css::uno::Exception( + "The path '" + aChangePath.sPathName + + "' is defined as SINGLE_PATH. It's sub set of internal paths can't be set.", + static_cast< ::cppu::OWeakObject* >(this)); + } + + css::uno::Sequence lTmpList; + aVal >>= lTmpList; + std::vector lList = comphelper::sequenceToContainer>(lTmpList); + if (! impl_isValidPath(lList)) + throw css::lang::IllegalArgumentException(); + aChangePath.lInternalPaths = lList; + } + break; + + case IDGROUP_USER_PATHS : + { + if (aChangePath.bIsSinglePath) + { + throw css::uno::Exception( + "The path '" + aChangePath.sPathName + + "' is defined as SINGLE_PATH. It's sub set of internal paths can't be set.", + static_cast< ::cppu::OWeakObject* >(this)); + } + + css::uno::Sequence lTmpList; + aVal >>= lTmpList; + std::vector lList = comphelper::sequenceToContainer>(lTmpList); + if (! impl_isValidPath(lList)) + throw css::lang::IllegalArgumentException(); + aChangePath.lUserPaths = lList; + } + break; + + case IDGROUP_WRITE_PATH : + { + OUString sVal; + aVal >>= sVal; + if (! impl_isValidPath(sVal)) + throw css::lang::IllegalArgumentException(); + aChangePath.sWritePath = sVal; + } + break; + } + + // TODO check if path has at least one path value set + // At least it depends from the feature using this path, if an empty path list is allowed. + + // first we should try to store the changed (copied!) path ... + // In case an error occurs on saving time an exception is thrown ... + // If no exception occurs we can update our internal cache (means + // we can overwrite pOrgPath ! + impl_storePath(aChangePath); + *pOrgPath = std::move(aChangePath); +} + +bool PathSettings::impl_isValidPath(const std::vector& lPath) const +{ + for (auto const& path : lPath) + { + if (! impl_isValidPath(path)) + return false; + } + + return true; +} + +bool PathSettings::impl_isValidPath(std::u16string_view sPath) const +{ + // allow empty path to reset a path. +// idea by LLA to support empty paths +// if (sPath.getLength() == 0) +// { +// return sal_True; +// } + + return (! INetURLObject(sPath).HasError()); +} + +OUString impl_extractBaseFromPropName(const OUString& sPropName) +{ + sal_Int32 i = sPropName.indexOf(POSTFIX_INTERNAL_PATHS); + if (i > -1) + return sPropName.copy(0, i); + i = sPropName.indexOf(POSTFIX_USER_PATHS); + if (i > -1) + return sPropName.copy(0, i); + i = sPropName.indexOf(POSTFIX_WRITE_PATH); + if (i > -1) + return sPropName.copy(0, i); + + return sPropName; +} + +PathSettings::PathInfo* PathSettings::impl_getPathAccess(sal_Int32 nHandle) +{ + // SAFE -> + osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex); + + if (nHandle > (m_lPropDesc.getLength()-1)) + return nullptr; + + const css::beans::Property& rProp = m_lPropDesc[nHandle]; + OUString sProp = impl_extractBaseFromPropName(rProp.Name); + PathSettings::PathHash::iterator rPath = m_lPaths.find(sProp); + + if (rPath != m_lPaths.end()) + return &(rPath->second); + + return nullptr; + // <- SAFE +} + +const PathSettings::PathInfo* PathSettings::impl_getPathAccessConst(sal_Int32 nHandle) const +{ + // SAFE -> + osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex); + + if (nHandle > (m_lPropDesc.getLength()-1)) + return nullptr; + + const css::beans::Property& rProp = m_lPropDesc[nHandle]; + OUString sProp = impl_extractBaseFromPropName(rProp.Name); + PathSettings::PathHash::const_iterator rPath = m_lPaths.find(sProp); + + if (rPath != m_lPaths.end()) + return &(rPath->second); + + return nullptr; + // <- SAFE +} + +sal_Bool SAL_CALL PathSettings::convertFastPropertyValue( css::uno::Any& aConvertedValue, + css::uno::Any& aOldValue , + sal_Int32 nHandle , + const css::uno::Any& aValue ) +{ + // throws NoSuchElementException ! + css::uno::Any aCurrentVal = impl_getPathValue(nHandle); + + return PropHelper::willPropertyBeChanged( + aCurrentVal, + aValue, + aOldValue, + aConvertedValue); +} + +void SAL_CALL PathSettings::setFastPropertyValue_NoBroadcast( sal_Int32 nHandle, + const css::uno::Any& aValue ) +{ + // throws NoSuchElement- and IllegalArgumentException ! + impl_setPathValue(nHandle, aValue); +} + +void SAL_CALL PathSettings::getFastPropertyValue(css::uno::Any& aValue , + sal_Int32 nHandle) const +{ + aValue = impl_getPathValue(nHandle); +} + +::cppu::IPropertyArrayHelper& SAL_CALL PathSettings::getInfoHelper() +{ + return *m_pPropHelp; +} + +css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL PathSettings::getPropertySetInfo() +{ + return ::cppu::OPropertySetHelper::createPropertySetInfo(getInfoHelper()); +} + +css::uno::Reference< css::util::XStringSubstitution > PathSettings::fa_getSubstitution() +{ + css::uno::Reference< css::util::XStringSubstitution > xSubst; + { // SAFE -> + osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex); + xSubst = m_xSubstitution; + } + + if (! xSubst.is()) + { + // create the needed substitution service. + // We must replace all used variables inside read path values. + // In case we can't do so... the whole office can't work really. + // That's why it seems to be OK to throw a RuntimeException then. + xSubst = css::util::PathSubstitution::create(m_xContext); + + { // SAFE -> + osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex); + m_xSubstitution = xSubst; + } + } + + return xSubst; +} + +css::uno::Reference< css::container::XNameAccess > PathSettings::fa_getCfgOld() +{ + css::uno::Reference< css::container::XNameAccess > xCfg; + { // SAFE -> + osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex); + xCfg = m_xCfgOld; + } // <- SAFE + + if (! xCfg.is()) + { + xCfg.set( ::comphelper::ConfigurationHelper::openConfig( + m_xContext, + "org.openoffice.Office.Common/Path/Current", + ::comphelper::EConfigurationModes::Standard), // not readonly! Sometimes we need write access there !!! + css::uno::UNO_QUERY_THROW); + + { // SAFE -> + osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex); + m_xCfgOld = xCfg; + } + } + + return xCfg; +} + +css::uno::Reference< css::container::XNameAccess > PathSettings::fa_getCfgNew() +{ + css::uno::Reference< css::container::XNameAccess > xCfg; + { // SAFE -> + osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex); + xCfg = m_xCfgNew; + } // <- SAFE + + if (! xCfg.is()) + { + xCfg.set( ::comphelper::ConfigurationHelper::openConfig( + m_xContext, + "org.openoffice.Office.Paths/Paths", + ::comphelper::EConfigurationModes::Standard), + css::uno::UNO_QUERY_THROW); + + { // SAFE -> + osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex); + m_xCfgNew = xCfg; + m_xCfgNewListener = new WeakChangesListener(this); + } + + css::uno::Reference< css::util::XChangesNotifier > xBroadcaster(xCfg, css::uno::UNO_QUERY_THROW); + xBroadcaster->addChangesListener(m_xCfgNewListener); + } + + return xCfg; +} + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_framework_PathSettings_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence const &) +{ + rtl::Reference xPathSettings = new PathSettings(context); + // fill cache + xPathSettings->impl_readAll(); + + return cppu::acquire(xPathSettings.get()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/services/sessionlistener.cxx b/framework/source/services/sessionlistener.cxx new file mode 100644 index 0000000000..a77e7f961e --- /dev/null +++ b/framework/source/services/sessionlistener.cxx @@ -0,0 +1,414 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +using namespace css; +using namespace com::sun::star::uno; +using namespace com::sun::star::util; +using namespace com::sun::star::beans; +using namespace framework; + +namespace { + +/// @HTML +/** @short implements flat/deep detection of file/stream formats and provides + further read/write access to the global office type configuration. + + @descr Using of this class makes it possible to get information about the + format type of a given URL or stream. The returned internal type name + can be used to get more information about this format. Further this + class provides full access to the configuration data and following + implementations will support some special query modes. + + @docdate 10.03.2003 by as96863 + + @todo
          +
        • implementation of query mode
        • +
        • simple restore mechanism of last consistent cache state, + if flush failed
        • +
        + */ +typedef cppu::WeakImplHelper< + css::lang::XInitialization, + css::frame::XSessionManagerListener2, + css::frame::XStatusListener, + css::lang::XServiceInfo> SessionListener_BASE; + +class SessionListener : public SessionListener_BASE +{ +private: + osl::Mutex m_aMutex; + + /** reference to the uno service manager, which created this service. + It can be used to create own needed helper services. */ + css::uno::Reference< css::uno::XComponentContext > m_xContext; + + css::uno::Reference< css::frame::XSessionManagerClient > m_rSessionManager; + + // restore handling + bool m_bRestored; + + bool m_bSessionStoreRequested; + + bool m_bAllowUserInteractionOnQuit; + bool m_bTerminated; + + // in case of synchronous call the caller should do saveDone() call himself! + void StoreSession( bool bAsync ); + + // let session quietly close the documents, remove lock files, store configuration and etc. + void QuitSessionQuietly(); + +public: + explicit SessionListener(css::uno::Reference< css::uno::XComponentContext > xContext); + + virtual ~SessionListener() override; + + virtual OUString SAL_CALL getImplementationName() override + { + return "com.sun.star.comp.frame.SessionListener"; + } + + virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override + { + return cppu::supportsService(this, ServiceName); + } + + virtual css::uno::Sequence SAL_CALL getSupportedServiceNames() override + { + return {"com.sun.star.frame.SessionListener"}; + } + + virtual void SAL_CALL disposing(const css::lang::EventObject&) override; + + // XInitialization + virtual void SAL_CALL initialize(const css::uno::Sequence< css::uno::Any >& args) override; + + // XSessionManagerListener + virtual void SAL_CALL doSave( sal_Bool bShutdown, sal_Bool bCancelable ) override; + virtual void SAL_CALL approveInteraction( sal_Bool bInteractionGranted ) override; + virtual void SAL_CALL shutdownCanceled() override; + virtual sal_Bool SAL_CALL doRestore() override; + + // XSessionManagerListener2 + virtual void SAL_CALL doQuit() override; + + // XStatusListener + virtual void SAL_CALL statusChanged(const css::frame::FeatureStateEvent& event) override; +}; + +SessionListener::SessionListener(css::uno::Reference< css::uno::XComponentContext > rxContext ) + : m_xContext(std::move( rxContext )) + , m_bRestored( false ) + , m_bSessionStoreRequested( false ) + , m_bAllowUserInteractionOnQuit( false ) + , m_bTerminated( false ) +{ + SAL_INFO("fwk.session", "SessionListener::SessionListener"); +} + +SessionListener::~SessionListener() +{ + SAL_INFO("fwk.session", "SessionListener::~SessionListener"); + if (m_rSessionManager.is()) + { + css::uno::Reference< XSessionManagerListener> me(this); + m_rSessionManager->removeSessionManagerListener(me); + } +} + +void SessionListener::StoreSession( bool bAsync ) +{ + SAL_INFO("fwk.session", "SessionListener::StoreSession"); + osl::MutexGuard g(m_aMutex); + try + { + // xd create SERVICENAME_AUTORECOVERY -> frame::XDispatch + // xd->dispatch("vnd.sun.star.autorecovery:/doSessionSave, async=bAsync + // on stop event m_rSessionManager->saveDone(this); in case of asynchronous call + // in case of synchronous call the caller should do saveDone() call himself! + + css::uno::Reference< frame::XDispatch > xDispatch = css::frame::theAutoRecovery::get( m_xContext ); + css::uno::Reference< XURLTransformer > xURLTransformer = URLTransformer::create( m_xContext ); + URL aURL; + aURL.Complete = "vnd.sun.star.autorecovery:/doSessionSave"; + xURLTransformer->parseStrict(aURL); + + // in case of asynchronous call the notification will trigger saveDone() + if ( bAsync ) + xDispatch->addStatusListener(this, aURL); + + Sequence< PropertyValue > args{ PropertyValue("DispatchAsynchron",-1,Any(bAsync), + PropertyState_DIRECT_VALUE) }; + xDispatch->dispatch(aURL, args); + } catch (const css::uno::Exception&) { + TOOLS_WARN_EXCEPTION("fwk.session", ""); + // save failed, but tell manager to go on if we haven't yet dispatched the request + // in case of synchronous saving the notification is done by the caller + if ( bAsync && m_rSessionManager.is() ) + m_rSessionManager->saveDone(this); + } +} + +void SessionListener::QuitSessionQuietly() +{ + SAL_INFO("fwk.session", "SessionListener::QuitSessionQuietly"); + osl::MutexGuard g(m_aMutex); + try + { + // xd create SERVICENAME_AUTORECOVERY -> frame::XDispatch + // xd->dispatch("vnd.sun.star.autorecovery:/doSessionQuietQuit, async=false + // it is done synchronously to avoid conflict with normal quit process + + css::uno::Reference< frame::XDispatch > xDispatch = css::frame::theAutoRecovery::get( m_xContext ); + css::uno::Reference< XURLTransformer > xURLTransformer = URLTransformer::create( m_xContext ); + URL aURL; + aURL.Complete = "vnd.sun.star.autorecovery:/doSessionQuietQuit"; + xURLTransformer->parseStrict(aURL); + + Sequence< PropertyValue > args{ PropertyValue("DispatchAsynchron",-1,Any(false), + PropertyState_DIRECT_VALUE) }; + xDispatch->dispatch(aURL, args); + } catch (const css::uno::Exception&) { + TOOLS_WARN_EXCEPTION("fwk.session", ""); + } +} + +void SAL_CALL SessionListener::disposing(const css::lang::EventObject& Source) +{ + SAL_INFO("fwk.session", "SessionListener::disposing"); + if (Source.Source == m_rSessionManager) { + m_rSessionManager.clear(); + } +} + +void SAL_CALL SessionListener::initialize(const Sequence< Any >& args) +{ + SAL_INFO("fwk.session", "SessionListener::initialize"); + + OUString aSMgr("com.sun.star.frame.SessionManagerClient"); + if ( (args.getLength() == 1) && (args[0] >>= m_bAllowUserInteractionOnQuit) ) + ;// do nothing + else if (args.hasElements()) + { + NamedValue v; + for (const Any& rArg : args) + { + if (rArg >>= v) + { + if ( v.Name == "SessionManagerName" ) + v.Value >>= aSMgr; + else if ( v.Name == "SessionManager" ) + v.Value >>= m_rSessionManager; + else if ( v.Name == "AllowUserInteractionOnQuit" ) + v.Value >>= m_bAllowUserInteractionOnQuit; + } + } + } + + SAL_INFO("fwk.session.debug", " m_bAllowUserInteractionOnQuit = " << (m_bAllowUserInteractionOnQuit ? "true" : "false")); + if (!m_rSessionManager.is()) + m_rSessionManager = css::uno::Reference< frame::XSessionManagerClient > + (m_xContext->getServiceManager()->createInstanceWithContext(aSMgr, m_xContext), UNO_QUERY); + + if (m_rSessionManager.is()) + { + m_rSessionManager->addSessionManagerListener(this); + } +} + +void SAL_CALL SessionListener::statusChanged(const frame::FeatureStateEvent& event) +{ + SAL_INFO("fwk.session", "SessionListener::statusChanged"); + + SAL_INFO("fwk.session.debug", " ev.Feature = " << event.FeatureURL.Complete << + ", ev.Descript = " << event.FeatureDescriptor); + if ( event.FeatureURL.Complete == "vnd.sun.star.autorecovery:/doSessionRestore" ) + { + if (event.FeatureDescriptor == "update") + m_bRestored = true; // a document was restored + + } + else if ( event.FeatureURL.Complete == "vnd.sun.star.autorecovery:/doAutoSave" ) + { // the "doSessionSave" was never set, look to framework/source/services/autorecovery.cxx + // it always testing but never setting (enum AutoRecovery::E_SESSION_SAVE) + if (event.FeatureDescriptor == "update") + { + if (m_rSessionManager.is()) + m_rSessionManager->saveDone(this); // done with save + } + } +} + +sal_Bool SAL_CALL SessionListener::doRestore() +{ + SAL_INFO("fwk.session", "SessionListener::doRestore"); + osl::MutexGuard g(m_aMutex); + m_bRestored = false; + try { + css::uno::Reference< frame::XDispatch > xDispatch = css::frame::theAutoRecovery::get( m_xContext ); + + URL aURL; + aURL.Complete = "vnd.sun.star.autorecovery:/doSessionRestore"; + css::uno::Reference< XURLTransformer > xURLTransformer(URLTransformer::create(m_xContext)); + xURLTransformer->parseStrict(aURL); + Sequence< PropertyValue > args; + xDispatch->addStatusListener(this, aURL); + xDispatch->dispatch(aURL, args); + m_bRestored = true; + + } catch (const css::uno::Exception&) { + TOOLS_WARN_EXCEPTION("fwk.session", ""); + } + + return m_bRestored; +} + +void SAL_CALL SessionListener::doSave( sal_Bool bShutdown, sal_Bool /*bCancelable*/ ) +{ + SAL_INFO("fwk.session", "SessionListener::doSave"); + + SAL_INFO("fwk.session.debug", " m_bAllowUserInteractionOnQuit = " << (m_bAllowUserInteractionOnQuit ? "true" : "false") << + ", bShutdown = " << (bShutdown ? "true" : "false")); + if (bShutdown) + { + m_bSessionStoreRequested = true; // there is no need to protect it with mutex + if ( m_bAllowUserInteractionOnQuit && m_rSessionManager.is() ) + m_rSessionManager->queryInteraction( static_cast< css::frame::XSessionManagerListener* >( this ) ); + else + StoreSession( true ); + } + // we don't have anything to do so tell the session manager we're done + else if( m_rSessionManager.is() ) + m_rSessionManager->saveDone( this ); +} + +void SAL_CALL SessionListener::approveInteraction( sal_Bool bInteractionGranted ) +{ + SAL_INFO("fwk.session", "SessionListener::approveInteraction"); + // do AutoSave as the first step + osl::MutexGuard g(m_aMutex); + + if ( bInteractionGranted ) + { + // close the office documents in normal way + try + { + // first of all let the session be stored to be sure that we lose no information + StoreSession( false ); + + css::uno::Reference< css::frame::XDesktop2 > xDesktop = css::frame::Desktop::create( m_xContext ); + // honestly: how many implementations of XDesktop will we ever have? + // so casting this directly to the implementation + Desktop* pDesktop(dynamic_cast(xDesktop.get())); + if(pDesktop) + { + SAL_INFO("fwk.session", " XDesktop is a framework::Desktop -- good."); + m_bTerminated = pDesktop->terminateQuickstarterToo(); + } + else + { + SAL_WARN("fwk.session", " XDesktop is not a framework::Desktop -- this should never happen."); + m_bTerminated = xDesktop->terminate(); + } + + if ( m_rSessionManager.is() ) + { + // false means that the application closing has been cancelled + if ( !m_bTerminated ) + m_rSessionManager->cancelShutdown(); + else + m_rSessionManager->interactionDone( this ); + } + } + catch( const css::uno::Exception& ) + { + StoreSession( true ); + if (m_rSessionManager.is()) + m_rSessionManager->interactionDone(this); + } + + if ( m_rSessionManager.is() && m_bTerminated ) + m_rSessionManager->saveDone(this); + } + else + { + StoreSession( true ); + } +} + +void SessionListener::shutdownCanceled() +{ + SAL_INFO("fwk.session", "SessionListener::shutdownCanceled"); + // set the state back + m_bSessionStoreRequested = false; // there is no need to protect it with mutex + + if ( m_rSessionManager.is() ) + m_rSessionManager->saveDone(this); +} + +void SessionListener::doQuit() +{ + SAL_INFO("fwk.session", "SessionListener::doQuit"); + if ( m_bSessionStoreRequested && !m_bTerminated ) + { + // let the session be closed quietly in this case + QuitSessionQuietly(); + } +} + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_frame_SessionListener_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence const &) +{ + SAL_INFO("fwk.session", "com_sun_star_comp_frame_SessionListener_get_implementation"); + + return cppu::acquire(new SessionListener(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/services/substitutepathvars.cxx b/framework/source/services/substitutepathvars.cxx new file mode 100644 index 0000000000..b575233065 --- /dev/null +++ b/framework/source/services/substitutepathvars.cxx @@ -0,0 +1,695 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include + +using namespace com::sun::star::uno; +using namespace com::sun::star::container; + +namespace { + +enum PreDefVariable +{ + PREDEFVAR_INST, + PREDEFVAR_PROG, + PREDEFVAR_USER, + PREDEFVAR_WORK, + PREDEFVAR_HOME, + PREDEFVAR_TEMP, + PREDEFVAR_PATH, + PREDEFVAR_USERNAME, + PREDEFVAR_LANGID, + PREDEFVAR_VLANG, + PREDEFVAR_INSTPATH, + PREDEFVAR_PROGPATH, + PREDEFVAR_USERPATH, + PREDEFVAR_INSTURL, + PREDEFVAR_PROGURL, + PREDEFVAR_USERURL, + PREDEFVAR_WORKDIRURL, + // New variable of hierarchy service (#i32656#) + PREDEFVAR_BASEINSTURL, + PREDEFVAR_USERDATAURL, + PREDEFVAR_BRANDBASEURL, + PREDEFVAR_COUNT +}; + +struct FixedVariable +{ + const char* pVarName; + bool bAbsPath; +}; + +// Table with all fixed/predefined variables supported. +const FixedVariable aFixedVarTable[PREDEFVAR_COUNT] = +{ + { "$(inst)", true }, // PREDEFVAR_INST + { "$(prog)", true }, // PREDEFVAR_PROG + { "$(user)", true }, // PREDEFVAR_USER + { "$(work)", true }, // PREDEFVAR_WORK, special variable + // (transient) + { "$(home)", true }, // PREDEFVAR_HOME + { "$(temp)", true }, // PREDEFVAR_TEMP + { "$(path)", true }, // PREDEFVAR_PATH + { "$(username)", false }, // PREDEFVAR_USERNAME + { "$(langid)", false }, // PREDEFVAR_LANGID + { "$(vlang)", false }, // PREDEFVAR_VLANG + { "$(instpath)", true }, // PREDEFVAR_INSTPATH + { "$(progpath)", true }, // PREDEFVAR_PROGPATH + { "$(userpath)", true }, // PREDEFVAR_USERPATH + { "$(insturl)", true }, // PREDEFVAR_INSTURL + { "$(progurl)", true }, // PREDEFVAR_PROGURL + { "$(userurl)", true }, // PREDEFVAR_USERURL + { "$(workdirurl)", true }, // PREDEFVAR_WORKDIRURL, special variable + // (transient) and don't use for + // resubstitution + { "$(baseinsturl)", true }, // PREDEFVAR_BASEINSTURL + { "$(userdataurl)", true }, // PREDEFVAR_USERDATAURL + { "$(brandbaseurl)", true } // PREDEFVAR_BRANDBASEURL +}; + +struct PredefinedPathVariables +{ + // Predefined variables supported by substitute variables + LanguageType m_eLanguageType; // Language type of Office + OUString m_FixedVar[ PREDEFVAR_COUNT ]; // Variable value access by PreDefVariable + OUString m_FixedVarNames[ PREDEFVAR_COUNT ]; // Variable name access by PreDefVariable +}; + +struct ReSubstFixedVarOrder +{ + sal_Int32 nVarValueLength; + PreDefVariable eVariable; + + bool operator< ( const ReSubstFixedVarOrder& aFixedVarOrder ) const + { + // Reverse operator< to have high to low ordering + return ( nVarValueLength > aFixedVarOrder.nVarValueLength ); + } +}; + +typedef comphelper::WeakComponentImplHelper< + css::util::XStringSubstitution, + css::lang::XServiceInfo > SubstitutePathVariables_BASE; + +class SubstitutePathVariables : public SubstitutePathVariables_BASE +{ +public: + explicit SubstitutePathVariables(); + + virtual OUString SAL_CALL getImplementationName() override + { + return "com.sun.star.comp.framework.PathSubstitution"; + } + + virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override + { + return cppu::supportsService(this, ServiceName); + } + + virtual css::uno::Sequence SAL_CALL getSupportedServiceNames() override + { + return {"com.sun.star.util.PathSubstitution"}; + } + + // XStringSubstitution + virtual OUString SAL_CALL substituteVariables( const OUString& aText, sal_Bool bSubstRequired ) override; + virtual OUString SAL_CALL reSubstituteVariables( const OUString& aText ) override; + virtual OUString SAL_CALL getSubstituteVariableValue( const OUString& variable ) override; + +protected: + void SetPredefinedPathVariables(); + + // Special case (transient) values can change during runtime! + // Don't store them in the pre defined struct + OUString GetWorkPath() const; + OUString GetWorkVariableValue() const; + OUString GetPathVariableValue() const; + + OUString GetHomeVariableValue() const; + + // XStringSubstitution implementation methods + /// @throws css::container::NoSuchElementException + /// @throws css::uno::RuntimeException + OUString impl_substituteVariable( const OUString& aText, bool bSustRequired ); + /// @throws css::uno::RuntimeException + OUString impl_reSubstituteVariables( const OUString& aText ); + /// @throws css::container::NoSuchElementException + /// @throws css::uno::RuntimeException + OUString const & impl_getSubstituteVariableValue( const OUString& variable ); + +private: + typedef std::unordered_map + VarNameToIndexMap; + + VarNameToIndexMap m_aPreDefVarMap; // Mapping from pre-def variable names to enum for array access + PredefinedPathVariables m_aPreDefVars; // All predefined variables + std::vector m_aReSubstFixedVarOrder; // To speed up resubstitution fixed variables (order for lookup) +}; + +SubstitutePathVariables::SubstitutePathVariables() +{ + SetPredefinedPathVariables(); + + // Init the predefined/fixed variable to index hash map + for ( int i = 0; i < PREDEFVAR_COUNT; i++ ) + { + // Store variable name into struct of predefined/fixed variables + m_aPreDefVars.m_FixedVarNames[i] = OUString::createFromAscii( aFixedVarTable[i].pVarName ); + + // Create hash map entry + m_aPreDefVarMap.emplace( m_aPreDefVars.m_FixedVarNames[i], PreDefVariable(i) ); + } + + // Sort predefined/fixed variable to path length + for ( int i = 0; i < PREDEFVAR_COUNT; i++ ) + { + if (( i != PREDEFVAR_WORKDIRURL ) && ( i != PREDEFVAR_PATH )) + { + // Special path variables, don't include into automatic resubstitution search! + // $(workdirurl) is not allowed to resubstitute! This variable is the value of path settings entry + // and it could be possible that it will be resubstituted by itself!! + // Example: WORK_PATH=c:\test, $(workdirurl)=WORK_PATH => WORK_PATH=$(workdirurl) and this cannot be substituted! + ReSubstFixedVarOrder aFixedVar; + aFixedVar.eVariable = PreDefVariable(i); + aFixedVar.nVarValueLength = m_aPreDefVars.m_FixedVar[static_cast(aFixedVar.eVariable)].getLength(); + m_aReSubstFixedVarOrder.push_back( aFixedVar ); + } + } + sort(m_aReSubstFixedVarOrder.begin(),m_aReSubstFixedVarOrder.end()); +} + +// XStringSubstitution +OUString SAL_CALL SubstitutePathVariables::substituteVariables( const OUString& aText, sal_Bool bSubstRequired ) +{ + std::unique_lock g(m_aMutex); + return impl_substituteVariable( aText, bSubstRequired ); +} + +OUString SAL_CALL SubstitutePathVariables::reSubstituteVariables( const OUString& aText ) +{ + std::unique_lock g(m_aMutex); + return impl_reSubstituteVariables( aText ); +} + +OUString SAL_CALL SubstitutePathVariables::getSubstituteVariableValue( const OUString& aVariable ) +{ + std::unique_lock g(m_aMutex); + return impl_getSubstituteVariableValue( aVariable ); +} + +OUString SubstitutePathVariables::GetWorkPath() const +{ + OUString aWorkPath; + css::uno::Reference< css::container::XHierarchicalNameAccess > xPaths(officecfg::Office::Paths::Paths::get(), css::uno::UNO_QUERY_THROW); + if (!(xPaths->getByHierarchicalName("['Work']/WritePath") >>= aWorkPath)) + // fallback in case config layer does not return a usable work dir value. + aWorkPath = GetWorkVariableValue(); + + return aWorkPath; +} + +OUString SubstitutePathVariables::GetWorkVariableValue() const +{ + OUString aWorkPath; + std::optional x(officecfg::Office::Paths::Variables::Work::get()); + if (!x) + { + // fallback to $HOME in case platform dependent config layer does not return + // a usable work dir value. + osl::Security aSecurity; + aSecurity.getHomeDir( aWorkPath ); + } + else + aWorkPath = *x; + return aWorkPath; +} + +OUString SubstitutePathVariables::GetHomeVariableValue() const +{ + osl::Security aSecurity; + OUString aHomePath; + + aSecurity.getHomeDir( aHomePath ); + return aHomePath; +} + +OUString SubstitutePathVariables::GetPathVariableValue() const +{ + OUString aRetStr; + const char* pEnv = getenv( "PATH" ); + + if ( pEnv ) + { + const int PATH_EXTEND_FACTOR = 200; + OUString aTmp; + OUString aPathList( pEnv, strlen( pEnv ), osl_getThreadTextEncoding() ); + OUStringBuffer aPathStrBuffer( aPathList.getLength() * PATH_EXTEND_FACTOR / 100 ); + + bool bAppendSep = false; + sal_Int32 nToken = 0; + do + { + OUString sToken = aPathList.getToken(0, SAL_PATHSEPARATOR, nToken); + if (!sToken.isEmpty() && + osl::FileBase::getFileURLFromSystemPath( sToken, aTmp ) == + osl::FileBase::RC::E_None ) + { + if ( bAppendSep ) + aPathStrBuffer.append( ";" ); // Office uses ';' as path separator + aPathStrBuffer.append( aTmp ); + bAppendSep = true; + } + } + while(nToken>=0); + + aRetStr = aPathStrBuffer.makeStringAndClear(); + } + + return aRetStr; +} + +OUString SubstitutePathVariables::impl_substituteVariable( const OUString& rText, bool bSubstRequired ) +{ + // This is maximal recursive depth supported! + const sal_Int32 nMaxRecursiveDepth = 8; + + OUString aWorkText = rText; + OUString aResult; + + // Use vector with strings to detect endless recursions! + std::vector< OUString > aEndlessRecursiveDetector; + + // Search for first occurrence of "$(...". + sal_Int32 nDepth = 0; + bool bSubstitutionCompleted = false; + sal_Int32 nPosition = aWorkText.indexOf( "$(" ); + sal_Int32 nLength = 0; // = count of letters from "$(" to ")" in string + bool bVarNotSubstituted = false; + + // Have we found any variable like "$(...)"? + if ( nPosition != -1 ) + { + // Yes; Get length of found variable. + // If no ")" was found - nLength is set to 0 by default! see before. + sal_Int32 nEndPosition = aWorkText.indexOf( ')', nPosition ); + if ( nEndPosition != -1 ) + nLength = nEndPosition - nPosition + 1; + } + + // Is there something to replace ? + bool bWorkRetrieved = false; + bool bWorkDirURLRetrieved = false; + while (nDepth < nMaxRecursiveDepth) + { + while ( ( nPosition != -1 ) && ( nLength > 3 ) ) // "$(" ")" + { + // YES; Get the next variable for replace. + sal_Int32 nReplaceLength = 0; + OUString aReplacement; + OUString aSubString = aWorkText.copy( nPosition, nLength ); + + // Path variables are not case sensitive! + OUString aSubVarString = aSubString.toAsciiLowerCase(); + VarNameToIndexMap::const_iterator pNTOIIter = m_aPreDefVarMap.find( aSubVarString ); + if ( pNTOIIter != m_aPreDefVarMap.end() ) + { + // Fixed/Predefined variable found + PreDefVariable nIndex = pNTOIIter->second; + + // Determine variable value and length from array/table + if ( nIndex == PREDEFVAR_WORK && !bWorkRetrieved ) + { + // Transient value, retrieve it again + m_aPreDefVars.m_FixedVar[ nIndex ] = GetWorkVariableValue(); + bWorkRetrieved = true; + } + else if ( nIndex == PREDEFVAR_WORKDIRURL && !bWorkDirURLRetrieved ) + { + // Transient value, retrieve it again + m_aPreDefVars.m_FixedVar[ nIndex ] = GetWorkPath(); + bWorkDirURLRetrieved = true; + } + + // Check preconditions to substitute path variables. + // 1. A path variable can only be substituted if it follows a ';'! + // 2. It's located exactly at the start of the string being substituted! + if (( aFixedVarTable[ int( nIndex ) ].bAbsPath && (( nPosition == 0 ) || (( nPosition > 0 ) && ( aWorkText[nPosition-1] == ';')))) || + ( !aFixedVarTable[ int( nIndex ) ].bAbsPath )) + { + aReplacement = m_aPreDefVars.m_FixedVar[ nIndex ]; + nReplaceLength = nLength; + } + } + + // Have we found something to replace? + if ( nReplaceLength > 0 ) + { + // Yes ... then do it. + aWorkText = aWorkText.replaceAt( nPosition, nReplaceLength, aReplacement ); + } + else + { + // Variable not known + bVarNotSubstituted = true; + nPosition += nLength; + } + + // Step after replaced text! If no text was replaced (unknown variable!), + // length of aReplacement is 0 ... and we don't step then. + nPosition += aReplacement.getLength(); + + // We must control index in string before call something at OUString! + // The OUString-implementation don't do it for us :-( but the result is not defined otherwise. + if ( nPosition + 1 > aWorkText.getLength() ) + { + // Position is out of range. Break loop! + nPosition = -1; + nLength = 0; + } + else + { + // Else; Position is valid. Search for next variable to replace. + nPosition = aWorkText.indexOf( "$(", nPosition ); + // Have we found any variable like "$(...)"? + if ( nPosition != -1 ) + { + // Yes; Get length of found variable. If no ")" was found - nLength must set to 0! + nLength = 0; + sal_Int32 nEndPosition = aWorkText.indexOf( ')', nPosition ); + if ( nEndPosition != -1 ) + nLength = nEndPosition - nPosition + 1; + } + } + } + + nPosition = aWorkText.indexOf( "$(" ); + if ( nPosition == -1 ) + { + bSubstitutionCompleted = true; + break; // All variables are substituted + } + else + { + // Check for recursion + const sal_uInt32 nCount = aEndlessRecursiveDetector.size(); + for ( sal_uInt32 i=0; i < nCount; i++ ) + { + if ( aEndlessRecursiveDetector[i] == aWorkText ) + { + if ( bVarNotSubstituted ) + break; // Not all variables could be substituted! + else + { + nDepth = nMaxRecursiveDepth; + break; // Recursion detected! + } + } + } + + aEndlessRecursiveDetector.push_back( aWorkText ); + + // Initialize values for next + sal_Int32 nEndPosition = aWorkText.indexOf( ')', nPosition ); + if ( nEndPosition != -1 ) + nLength = nEndPosition - nPosition + 1; + bVarNotSubstituted = false; + ++nDepth; + } + } + + // Fill return value with result + if ( bSubstitutionCompleted ) + { + // Substitution successful! + aResult = aWorkText; + } + else + { + // Substitution not successful! + if ( nDepth == nMaxRecursiveDepth ) + { + // recursion depth reached! + if ( bSubstRequired ) + { + throw NoSuchElementException( "Endless recursion detected. Cannot substitute variables!", static_cast(this) ); + } + aResult = rText; + } + else + { + // variable in text but unknown! + if ( bSubstRequired ) + { + throw NoSuchElementException( "Unknown variable found!", static_cast(this) ); + } + aResult = aWorkText; + } + } + + return aResult; +} + +OUString SubstitutePathVariables::impl_reSubstituteVariables( const OUString& rURL ) +{ + OUString aURL; + + INetURLObject aUrl( rURL ); + if ( !aUrl.HasError() ) + aURL = aUrl.GetMainURL( INetURLObject::DecodeMechanism::NONE ); + else + { + // Convert a system path to a UCB compliant URL before resubstitution + OUString aTemp; + if ( osl::FileBase::getFileURLFromSystemPath( rURL, aTemp ) == osl::FileBase::E_None ) + { + aURL = INetURLObject( aTemp ).GetMainURL( INetURLObject::DecodeMechanism::NONE ); + if( aURL.isEmpty() ) + return rURL; + } + else + { + // rURL is not a valid URL nor a osl system path. Give up and return error! + return rURL; + } + } + + // Get transient predefined path variable $(work) value before starting resubstitution + m_aPreDefVars.m_FixedVar[ PREDEFVAR_WORK ] = GetWorkVariableValue(); + + for (;;) + { + bool bVariableFound = false; + + for (auto const & i: m_aReSubstFixedVarOrder) + { + OUString aValue = m_aPreDefVars.m_FixedVar[i.eVariable]; + sal_Int32 nPos = aURL.indexOf( aValue ); + if ( nPos >= 0 ) + { + bool bMatch = true; + if ( !aFixedVarTable[i.eVariable].bAbsPath ) + { + // Special path variables as they can occur in the middle of a path. Only match if they + // describe a whole directory and not only a substring of a directory! + // (Ideally, all substitutions should stick to syntactical + // boundaries within the given URL, like not covering only + // part of a URL path segment; however, at least when saving + // an Impress document, one URL that is passed in is of the + // form , re-substituted to + // <$(inst)/share/palette%3B$(user)/config/standard.sob>.) + const sal_Unicode* pStr = aURL.getStr(); + + if ( nPos > 0 ) + bMatch = ( aURL[ nPos-1 ] == '/' ); + + if ( bMatch ) + { + if ( nPos + aValue.getLength() < aURL.getLength() ) + bMatch = ( pStr[ nPos + aValue.getLength() ] == '/' ); + } + } + + if ( bMatch ) + { + aURL = aURL.replaceAt( + nPos, aValue.getLength(), + m_aPreDefVars.m_FixedVarNames[i.eVariable]); + bVariableFound = true; // Resubstitution not finished yet! + break; + } + } + } + + if ( !bVariableFound ) + { + return aURL; + } + } +} + +// This method support both request schemes "$("")" or "". +OUString const & SubstitutePathVariables::impl_getSubstituteVariableValue( const OUString& rVariable ) +{ + OUString aVariable; + + sal_Int32 nPos = rVariable.indexOf( "$(" ); + if ( nPos == -1 ) + { + // Prepare variable name before hash map access + aVariable = "$(" + rVariable + ")"; + } + + VarNameToIndexMap::const_iterator pNTOIIter = m_aPreDefVarMap.find( ( nPos == -1 ) ? aVariable : rVariable ); + + // Fixed/Predefined variable + if ( pNTOIIter == m_aPreDefVarMap.end() ) + { + throw NoSuchElementException("Unknown variable!", static_cast(this)); + } + PreDefVariable nIndex = pNTOIIter->second; + return m_aPreDefVars.m_FixedVar[static_cast(nIndex)]; +} + +void SubstitutePathVariables::SetPredefinedPathVariables() +{ + + m_aPreDefVars.m_FixedVar[PREDEFVAR_BRANDBASEURL] = "$BRAND_BASE_DIR"; + rtl::Bootstrap::expandMacros( + m_aPreDefVars.m_FixedVar[PREDEFVAR_BRANDBASEURL]); + + // Get inspath and userpath from bootstrap mechanism in every case as file URL + ::utl::Bootstrap::PathStatus aState; + OUString sVal; + + aState = utl::Bootstrap::locateUserData( sVal ); + //There can be the valid case that there is no user installation. + //TODO: Is that still the case? (With OOo 3.4, "unopkg sync" was run as part + // of the setup. Then no user installation was required.) + //Therefore we do not assert here. + // It's not possible to detect when an empty value would actually be used. + // (note: getenv is a hack to detect if we're running in a unit test) + // Also, it's okay to have an empty user installation path in case of LOK + if (aState == ::utl::Bootstrap::PATH_EXISTS || getenv("SRC_ROOT") || + (comphelper::LibreOfficeKit::isActive() && aState == ::utl::Bootstrap::PATH_VALID)) + { + m_aPreDefVars.m_FixedVar[ PREDEFVAR_USERPATH ] = sVal; + } + + // Set $(inst), $(instpath), $(insturl) + m_aPreDefVars.m_FixedVar[ PREDEFVAR_INSTPATH ] = m_aPreDefVars.m_FixedVar[PREDEFVAR_BRANDBASEURL]; + m_aPreDefVars.m_FixedVar[ PREDEFVAR_INSTURL ] = m_aPreDefVars.m_FixedVar[ PREDEFVAR_INSTPATH ]; + m_aPreDefVars.m_FixedVar[ PREDEFVAR_INST ] = m_aPreDefVars.m_FixedVar[ PREDEFVAR_INSTPATH ]; + // New variable of hierarchy service (#i32656#) + m_aPreDefVars.m_FixedVar[ PREDEFVAR_BASEINSTURL ]= m_aPreDefVars.m_FixedVar[ PREDEFVAR_INSTPATH ]; + + // Set $(user), $(userpath), $(userurl) + m_aPreDefVars.m_FixedVar[ PREDEFVAR_USERURL ] = m_aPreDefVars.m_FixedVar[ PREDEFVAR_USERPATH ]; + m_aPreDefVars.m_FixedVar[ PREDEFVAR_USER ] = m_aPreDefVars.m_FixedVar[ PREDEFVAR_USERPATH ]; + // New variable of hierarchy service (#i32656#) + m_aPreDefVars.m_FixedVar[ PREDEFVAR_USERDATAURL ]= m_aPreDefVars.m_FixedVar[ PREDEFVAR_USERPATH ]; + + // Detect the program directory + // Set $(prog), $(progpath), $(progurl) + INetURLObject aProgObj( + m_aPreDefVars.m_FixedVar[PREDEFVAR_BRANDBASEURL] ); + if ( !aProgObj.HasError() && aProgObj.insertName( u"" LIBO_BIN_FOLDER ) ) + { + m_aPreDefVars.m_FixedVar[ PREDEFVAR_PROGPATH ] = aProgObj.GetMainURL(INetURLObject::DecodeMechanism::NONE); + m_aPreDefVars.m_FixedVar[ PREDEFVAR_PROGURL ] = m_aPreDefVars.m_FixedVar[ PREDEFVAR_PROGPATH ]; + m_aPreDefVars.m_FixedVar[ PREDEFVAR_PROG ] = m_aPreDefVars.m_FixedVar[ PREDEFVAR_PROGPATH ]; + } + + // Set $(username) + OUString aSystemUser; + ::osl::Security aSecurity; + aSecurity.getUserName( aSystemUser, false ); + m_aPreDefVars.m_FixedVar[ PREDEFVAR_USERNAME ] = aSystemUser; + + // Detect the language type of the current office + m_aPreDefVars.m_eLanguageType = LANGUAGE_ENGLISH_US; + OUString aLocaleStr( utl::ConfigManager::getUILocale() ); + m_aPreDefVars.m_eLanguageType = LanguageTag::convertToLanguageTypeWithFallback( aLocaleStr ); + // We used to have an else branch here with a SAL_WARN, but that + // always fired in some unit tests when this code was built with + // debug=t, so it seems fairly pointless, especially as + // m_aPreDefVars.m_eLanguageType has been initialized to a + // default value above anyway. + + // Set $(vlang) + m_aPreDefVars.m_FixedVar[ PREDEFVAR_VLANG ] = aLocaleStr; + + // Set $(langid) + m_aPreDefVars.m_FixedVar[ PREDEFVAR_LANGID ] = OUString::number( static_cast(m_aPreDefVars.m_eLanguageType) ); + + // Set the other pre defined path variables + // Set $(work) + m_aPreDefVars.m_FixedVar[ PREDEFVAR_WORK ] = GetWorkVariableValue(); + m_aPreDefVars.m_FixedVar[ PREDEFVAR_HOME ] = GetHomeVariableValue(); + + // Set $(workdirurl) this is the value of the path PATH_WORK which doesn't make sense + // anymore because the path settings service has this value! It can deliver this value more + // quickly than the substitution service! + m_aPreDefVars.m_FixedVar[ PREDEFVAR_WORKDIRURL ] = GetWorkPath(); + + // Set $(path) variable + m_aPreDefVars.m_FixedVar[ PREDEFVAR_PATH ] = GetPathVariableValue(); + + // Set $(temp) + OUString aTmp; + osl::FileBase::getTempDirURL( aTmp ); + m_aPreDefVars.m_FixedVar[ PREDEFVAR_TEMP ] = aTmp; +} + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_framework_PathSubstitution_get_implementation( + css::uno::XComponentContext *, + css::uno::Sequence const &) +{ + return cppu::acquire(new SubstitutePathVariables()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/services/taskcreatorsrv.cxx b/framework/source/services/taskcreatorsrv.cxx new file mode 100644 index 0000000000..a4db7856d3 --- /dev/null +++ b/framework/source/services/taskcreatorsrv.cxx @@ -0,0 +1,357 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace framework; + +namespace { + +typedef comphelper::WeakComponentImplHelper< + css::lang::XServiceInfo, + css::lang::XSingleServiceFactory> TaskCreatorService_BASE; + +class TaskCreatorService : public TaskCreatorService_BASE +{ +private: + + /** @short the global uno service manager. + @descr Must be used to create own needed services. + */ + css::uno::Reference< css::uno::XComponentContext > m_xContext; + +public: + + explicit TaskCreatorService(css::uno::Reference< css::uno::XComponentContext > xContext); + + virtual OUString SAL_CALL getImplementationName() override + { + return "com.sun.star.comp.framework.TaskCreator"; + } + + virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override + { + return cppu::supportsService(this, ServiceName); + } + + virtual css::uno::Sequence SAL_CALL getSupportedServiceNames() override + { + return {"com.sun.star.frame.TaskCreator"}; + } + + // XSingleServiceFactory + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL createInstance() override; + + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL createInstanceWithArguments(const css::uno::Sequence< css::uno::Any >& lArguments) override; + +private: + + css::uno::Reference< css::awt::XWindow > implts_createContainerWindow( const css::uno::Reference< css::awt::XWindow >& xParentWindow , + const css::awt::Rectangle& aPosSize , + bool bTopWindow ); + + void implts_applyDocStyleToWindow(const css::uno::Reference< css::awt::XWindow >& xWindow) const; + + css::uno::Reference< css::frame::XFrame2 > implts_createFrame( const css::uno::Reference< css::frame::XFrame >& xParentFrame , + const css::uno::Reference< css::awt::XWindow >& xContainerWindow , + const OUString& sName ); + + void implts_establishWindowStateListener( const css::uno::Reference< css::frame::XFrame2 >& xFrame ); + void implts_establishTitleBarUpdate( const css::uno::Reference< css::frame::XFrame2 >& xFrame ); + + void implts_establishDocModifyListener( const css::uno::Reference< css::frame::XFrame2 >& xFrame ); + + OUString impl_filterNames( const OUString& sName ); +}; + +TaskCreatorService::TaskCreatorService(css::uno::Reference< css::uno::XComponentContext > xContext) + : m_xContext (std::move(xContext )) +{ +} + +css::uno::Reference< css::uno::XInterface > SAL_CALL TaskCreatorService::createInstance() +{ + return createInstanceWithArguments(css::uno::Sequence< css::uno::Any >()); +} + +css::uno::Reference< css::uno::XInterface > SAL_CALL TaskCreatorService::createInstanceWithArguments(const css::uno::Sequence< css::uno::Any >& lArguments) +{ + ::comphelper::SequenceAsHashMap lArgs(lArguments); + + css::uno::Reference< css::frame::XFrame > xParentFrame = lArgs.getUnpackedValueOrDefault(ARGUMENT_PARENTFRAME , css::uno::Reference< css::frame::XFrame >()); + OUString sFrameName = lArgs.getUnpackedValueOrDefault(ARGUMENT_FRAMENAME , OUString() ); + bool bVisible = lArgs.getUnpackedValueOrDefault(ARGUMENT_MAKEVISIBLE , false ); + bool bCreateTopWindow = lArgs.getUnpackedValueOrDefault(ARGUMENT_CREATETOPWINDOW , true ); + // only possize=[0,0,0,0] triggers default handling of vcl ! + css::awt::Rectangle aPosSize = lArgs.getUnpackedValueOrDefault(ARGUMENT_POSSIZE , css::awt::Rectangle(0, 0, 0, 0) ); + css::uno::Reference< css::awt::XWindow > xContainerWindow = lArgs.getUnpackedValueOrDefault(ARGUMENT_CONTAINERWINDOW , css::uno::Reference< css::awt::XWindow >() ); + bool bSupportPersistentWindowState = lArgs.getUnpackedValueOrDefault(ARGUMENT_SUPPORTPERSISTENTWINDOWSTATE , false ); + bool bEnableTitleBarUpdate = lArgs.getUnpackedValueOrDefault(ARGUMENT_ENABLE_TITLEBARUPDATE , true ); + // If the frame is explicitly requested to be hidden. + bool bHidden = lArgs.getUnpackedValueOrDefault(ARGUMENT_HIDDENFORCONVERSION, false); + + // We use FrameName property to set it as API name of the new created frame later. + // But those frame names must be different from the set of special target names as e.g. _blank, _self etcpp ! + OUString sRightName = impl_filterNames(sFrameName); + + // if no external frame window was given ... create a new one. + if ( ! xContainerWindow.is()) + { + css::uno::Reference< css::awt::XWindow > xParentWindow; + if (xParentFrame.is()) + xParentWindow = xParentFrame->getContainerWindow(); + + // Parent has no own window ... + // So we have to create a top level window always ! + if ( ! xParentWindow.is()) + bCreateTopWindow = true; + + xContainerWindow = implts_createContainerWindow(xParentWindow, aPosSize, bCreateTopWindow); + } + + // #i53630# + // Mark all document windows as "special ones", so VCL can bind + // special features to it. Because VCL doesn't know anything about documents ... + // Note: Doing so it's no longer supported, that e.g. our wizards can use findFrame(_blank) + // to create it's previous frames. They must do it manually by using WindowDescriptor+Toolkit! + css::uno::Reference< css::frame::XDesktop > xDesktop(xParentFrame, css::uno::UNO_QUERY); + bool bTopLevelDocumentWindow = ( + sRightName.isEmpty() && + ( + (! xParentFrame.is() ) || + ( xDesktop.is() ) + ) + ); + if (bTopLevelDocumentWindow) + implts_applyDocStyleToWindow(xContainerWindow); + //-------------------> + + // create the new frame + VclPtr pContainerWindow = VCLUnoHelper::GetWindow(xContainerWindow); + if (pContainerWindow && bHidden) + { + WindowExtendedStyle eStyle = pContainerWindow->GetExtendedStyle(); + eStyle |= WindowExtendedStyle::DocHidden; + pContainerWindow->SetExtendedStyle(eStyle); + } + css::uno::Reference< css::frame::XFrame2 > xFrame = implts_createFrame(xParentFrame, xContainerWindow, sRightName); + + // special feature: + // A special listener will restore pos/size states in case + // a component was loaded into the frame first time. + if (bSupportPersistentWindowState) + implts_establishWindowStateListener(xFrame); + + // special feature: On Mac we need tagging the window in case + // the underlying model was modified. + // VCL will ignore our calls in case different platform then Mac + // is used ... + if (bTopLevelDocumentWindow) + implts_establishDocModifyListener (xFrame); + + // special feature: + // A special listener will update title bar (text and icon) + // if component of frame will be changed. + if (bEnableTitleBarUpdate) + implts_establishTitleBarUpdate(xFrame); + + // Make it visible directly here... + // if it's required from outside. + if (bVisible) + xContainerWindow->setVisible(bVisible); + + return css::uno::Reference< css::uno::XInterface >(xFrame, css::uno::UNO_QUERY_THROW); +} + +void TaskCreatorService::implts_applyDocStyleToWindow(const css::uno::Reference< css::awt::XWindow >& xWindow) const +{ + // SYNCHRONIZED -> + SolarMutexGuard aSolarGuard; + VclPtr pVCLWindow = VCLUnoHelper::GetWindow(xWindow); + if (pVCLWindow) + pVCLWindow->SetExtendedStyle(WindowExtendedStyle::Document); + // <- SYNCHRONIZED +} + +css::uno::Reference< css::awt::XWindow > TaskCreatorService::implts_createContainerWindow( const css::uno::Reference< css::awt::XWindow >& xParentWindow , + const css::awt::Rectangle& aPosSize , + bool bTopWindow ) +{ + // get toolkit to create task container window + css::uno::Reference< css::awt::XToolkit2 > xToolkit = css::awt::Toolkit::create( m_xContext ); + + // Check if child frames can be created really. We need at least a valid window at the parent frame ... + css::uno::Reference< css::awt::XWindowPeer > xParentWindowPeer; + if ( ! bTopWindow) + { + if ( ! xParentWindow.is()) + bTopWindow = false; + else + xParentWindowPeer.set(xParentWindow, css::uno::UNO_QUERY_THROW); + } + + // describe window properties. + css::awt::WindowDescriptor aDescriptor; + if (bTopWindow) + { + aDescriptor.Type = css::awt::WindowClass_TOP; + aDescriptor.WindowServiceName = "window"; + aDescriptor.ParentIndex = -1; + aDescriptor.Parent.clear(); + aDescriptor.Bounds = aPosSize; + aDescriptor.WindowAttributes = css::awt::WindowAttribute::BORDER | + css::awt::WindowAttribute::MOVEABLE | + css::awt::WindowAttribute::SIZEABLE | + css::awt::WindowAttribute::CLOSEABLE | + css::awt::VclWindowPeerAttribute::CLIPCHILDREN; + } + else + { + aDescriptor.Type = css::awt::WindowClass_TOP; + aDescriptor.WindowServiceName = "dockingwindow"; + aDescriptor.ParentIndex = 1; + aDescriptor.Parent = xParentWindowPeer; + aDescriptor.Bounds = aPosSize; + aDescriptor.WindowAttributes = css::awt::VclWindowPeerAttribute::CLIPCHILDREN; + } + + // create a new blank container window and get access to parent container to append new created task. + css::uno::Reference< css::awt::XWindowPeer > xPeer = xToolkit->createWindow( aDescriptor ); + css::uno::Reference< css::awt::XWindow > xWindow ( xPeer, css::uno::UNO_QUERY_THROW ); + + sal_Int32 nBackground = 0xffffffff; + + if (bTopWindow) + { + try + { + nBackground = sal_Int32(::svtools::ColorConfig().GetColorValue(::svtools::APPBACKGROUND).nColor); + } + catch (const css::uno::Exception &) + { + // Ignore + } + } + xPeer->setBackground(nBackground); + + return xWindow; +} + +css::uno::Reference< css::frame::XFrame2 > TaskCreatorService::implts_createFrame( const css::uno::Reference< css::frame::XFrame >& xParentFrame , + const css::uno::Reference< css::awt::XWindow >& xContainerWindow, + const OUString& sName ) +{ + // create new frame. + css::uno::Reference< css::frame::XFrame2 > xNewFrame = css::frame::Frame::create( m_xContext ); + + // Set window on frame. + // Do it before calling any other interface methods ... + // The new created frame must be initialized before you can do anything else there. + xNewFrame->initialize( xContainerWindow ); + + // Put frame to the frame tree. + // Note: The property creator/parent will be set on the new putted frame automatically ... by the parent container. + if (xParentFrame.is()) + { + css::uno::Reference< css::frame::XFramesSupplier > xSupplier (xParentFrame, css::uno::UNO_QUERY_THROW); + css::uno::Reference< css::frame::XFrames > xContainer = xSupplier->getFrames(); + xContainer->append( css::uno::Reference(xNewFrame, css::uno::UNO_QUERY_THROW) ); + } + + // Set it's API name (if there is one from outside) + if (!sName.isEmpty()) + xNewFrame->setName( sName ); + + return xNewFrame; +} + +void TaskCreatorService::implts_establishWindowStateListener( const css::uno::Reference< css::frame::XFrame2 >& xFrame ) +{ + // Special feature: It's allowed for frames using a top level window only! + // We must create a special listener service and couple it with the new created task frame. + // He will restore or save the window state of it ... + // See used classes for further information too. + rtl::Reference pPersistentStateHandler = new PersistentWindowState( m_xContext ); + + css::uno::Sequence< css::uno::Any > lInitData{ css::uno::Any(xFrame) }; + pPersistentStateHandler->initialize(lInitData); +} + +void TaskCreatorService::implts_establishDocModifyListener( const css::uno::Reference< css::frame::XFrame2 >& xFrame ) +{ + // Special feature: It's allowed for frames using a top level window only! + // We must create a special listener service and couple it with the new created task frame. + // It will tag the window as modified if the underlying model was modified ... + rtl::Reference pTag = new TagWindowAsModified(); + + css::uno::Sequence< css::uno::Any > lInitData{ css::uno::Any(xFrame) }; + pTag->initialize(lInitData); +} + +void TaskCreatorService::implts_establishTitleBarUpdate( const css::uno::Reference< css::frame::XFrame2 >& xFrame ) +{ + rtl::Reference pHelper = new TitleBarUpdate (m_xContext); + + css::uno::Sequence< css::uno::Any > lInitData{ css::uno::Any(xFrame) }; + pHelper->initialize(lInitData); +} + +OUString TaskCreatorService::impl_filterNames( const OUString& sName ) +{ + OUString sFiltered; + if (TargetHelper::isValidNameForFrame(sName)) + sFiltered = sName; + return sFiltered; +} + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_framework_TaskCreator_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence const &) +{ + return cppu::acquire(new TaskCreatorService(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/services/uriabbreviation.cxx b/framework/source/services/uriabbreviation.cxx new file mode 100644 index 0000000000..6faa0c8e3d --- /dev/null +++ b/framework/source/services/uriabbreviation.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 + +#include + +#include +#include + +// framework namespace +namespace framework +{ + +// XInterface, XTypeProvider, XServiceInfo + +OUString SAL_CALL UriAbbreviation::getImplementationName() +{ + return "com.sun.star.comp.framework.UriAbbreviation"; +} + +sal_Bool SAL_CALL UriAbbreviation::supportsService( const OUString& sServiceName ) +{ + return cppu::supportsService(this, sServiceName); +} + +css::uno::Sequence< OUString > SAL_CALL UriAbbreviation::getSupportedServiceNames() +{ + return { "com.sun.star.util.UriAbbreviation" }; +} + +UriAbbreviation::UriAbbreviation(css::uno::Reference< css::uno::XComponentContext > const & ) +{ +} + +// css::util::XStringAbbreviation: +OUString SAL_CALL UriAbbreviation::abbreviateString(const css::uno::Reference< css::util::XStringWidth > & xStringWidth, ::sal_Int32 nWidth, const OUString & aString) +{ + OUString aResult( aString ); + if ( xStringWidth.is() ) + { + // Use INetURLObject to abbreviate URLs + INetURLObject aURL( aString ); + aResult = aURL.getAbbreviated( xStringWidth, nWidth, INetURLObject::DecodeMechanism::Unambiguous ); + } + + return aResult; +} + +} // namespace framework + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +framework_UriAbbreviation_get_implementation( + css::uno::XComponentContext* context, css::uno::Sequence const& ) +{ + return cppu::acquire(new framework::UriAbbreviation(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/services/urltransformer.cxx b/framework/source/services/urltransformer.cxx new file mode 100644 index 0000000000..84e44e422d --- /dev/null +++ b/framework/source/services/urltransformer.cxx @@ -0,0 +1,307 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace { + +class URLTransformer : public ::cppu::WeakImplHelper< css::util::XURLTransformer, css::lang::XServiceInfo> +{ +public: + URLTransformer() {} + + virtual OUString SAL_CALL getImplementationName() override + { + return "com.sun.star.comp.framework.URLTransformer"; + } + + virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override + { + return cppu::supportsService(this, ServiceName); + } + + virtual css::uno::Sequence SAL_CALL getSupportedServiceNames() override + { + return {"com.sun.star.util.URLTransformer"}; + } + + virtual sal_Bool SAL_CALL parseStrict( css::util::URL& aURL ) override; + + virtual sal_Bool SAL_CALL parseSmart( css::util::URL& aURL, const OUString& sSmartProtocol ) override; + + virtual sal_Bool SAL_CALL assemble( css::util::URL& aURL ) override; + + virtual OUString SAL_CALL getPresentation( const css::util::URL& aURL, sal_Bool bWithPassword ) override; +}; + +void lcl_ParserHelper(INetURLObject& _rParser, css::util::URL& _rURL) +{ + // Get all information about this URL. + _rURL.Protocol = INetURLObject::GetScheme( _rParser.GetProtocol() ); + _rURL.User = _rParser.GetUser ( INetURLObject::DecodeMechanism::WithCharset ); + _rURL.Password = _rParser.GetPass ( INetURLObject::DecodeMechanism::WithCharset ); + _rURL.Server = _rParser.GetHost ( INetURLObject::DecodeMechanism::WithCharset ); + _rURL.Port = static_cast(_rParser.GetPort()); + + sal_Int32 nCount = _rParser.getSegmentCount( false ); + if ( nCount > 0 ) + { + // Don't add last segment as it is the name! + --nCount; + + OUStringBuffer aPath(128); + for ( sal_Int32 nIndex = 0; nIndex < nCount; nIndex++ ) + { + aPath.append( "/" + + _rParser.getName( nIndex, false, INetURLObject::DecodeMechanism::NONE )); + } + + if ( nCount > 0 ) + aPath.append( '/' ); // final slash! + + _rURL.Path = aPath.makeStringAndClear(); + _rURL.Name = _rParser.getName( INetURLObject::LAST_SEGMENT, false, INetURLObject::DecodeMechanism::NONE ); + } + else + { + _rURL.Path = _rParser.GetURLPath( INetURLObject::DecodeMechanism::NONE ); + _rURL.Name = _rParser.GetLastName(); + } + + _rURL.Arguments = _rParser.GetParam(); + _rURL.Mark = _rParser.GetMark( INetURLObject::DecodeMechanism::WithCharset ); + + // INetURLObject supports only an intelligent method of parsing URL's. So write + // back Complete to have a valid encoded URL in all cases! + _rURL.Complete = _rParser.GetMainURL( INetURLObject::DecodeMechanism::NONE ); + + _rParser.SetMark( u"" ); + _rParser.SetParam( u"" ); + + _rURL.Main = _rParser.GetMainURL( INetURLObject::DecodeMechanism::NONE ); +} + +// XURLTransformer +sal_Bool SAL_CALL URLTransformer::parseStrict( css::util::URL& aURL ) +{ + // Safe impossible cases. + if ( aURL.Complete.isEmpty() ) + { + return false; + } + // Try to extract the protocol + sal_Int32 nURLIndex = aURL.Complete.indexOf( ':' ); + if ( nURLIndex <= 1 ) + return false; + + std::u16string_view aProtocol = aURL.Complete.subView( 0, nURLIndex+1 ); + + // If INetURLObject knows this protocol let it parse + if ( INetURLObject::CompareProtocolScheme( aProtocol ) != INetProtocol::NotValid ) + { + // Initialize parser with given URL. + INetURLObject aParser( aURL.Complete ); + + // Get all information about this URL. + INetProtocol eINetProt = aParser.GetProtocol(); + if ( eINetProt == INetProtocol::NotValid ) + { + return false; + } + else if ( !aParser.HasError() ) + { + lcl_ParserHelper(aParser,aURL); + // Return "URL is parsed". + return true; + } + } + else + { + // Minimal support for unknown protocols. This is mandatory to support the "Protocol Handlers" implemented + // in framework! + aURL.Protocol = aProtocol; + aURL.Main = aURL.Complete; + aURL.Path = aURL.Complete.copy( nURLIndex+1 ); + + // Return "URL is parsed". + return true; + } + + return false; +} + +// XURLTransformer + +sal_Bool SAL_CALL URLTransformer::parseSmart( css::util::URL& aURL, + const OUString& sSmartProtocol ) +{ + // Safe impossible cases. + if ( aURL.Complete.isEmpty() ) + { + return false; + } + + // Initialize parser with given URL. + INetURLObject aParser; + + aParser.SetSmartProtocol( INetURLObject::CompareProtocolScheme( sSmartProtocol )); + bool bOk = aParser.SetSmartURL( aURL.Complete ); + if ( bOk ) + { + lcl_ParserHelper(aParser,aURL); + // Return "URL is parsed". + return true; + } + else + { + // Minimal support for unknown protocols. This is mandatory to support the "Protocol Handlers" implemented + // in framework! + if ( INetURLObject::CompareProtocolScheme( sSmartProtocol ) == INetProtocol::NotValid ) + { + // Try to extract the protocol + sal_Int32 nIndex = aURL.Complete.indexOf( ':' ); + if ( nIndex > 1 ) + { + OUString aProtocol = aURL.Complete.copy( 0, nIndex+1 ); + + // If INetURLObject knows this protocol something is wrong as detected before => + // give up and return false! + if ( INetURLObject::CompareProtocolScheme( aProtocol ) != INetProtocol::NotValid ) + return false; + else + aURL.Protocol = aProtocol; + } + else + return false; + + aURL.Main = aURL.Complete; + aURL.Path = aURL.Complete.copy( nIndex+1 ); + return true; + } + else + return false; + } +} + +// XURLTransformer +sal_Bool SAL_CALL URLTransformer::assemble( css::util::URL& aURL ) +{ + // Initialize parser. + INetURLObject aParser; + + if ( INetURLObject::CompareProtocolScheme( aURL.Protocol ) != INetProtocol::NotValid ) + { + OUStringBuffer aCompletePath( aURL.Path ); + + // Concat the name if it is provided, just support a final slash + if ( !aURL.Name.isEmpty() ) + { + sal_Int32 nIndex = aURL.Path.lastIndexOf( '/' ); + if ( nIndex == ( aURL.Path.getLength() -1 )) + aCompletePath.append( aURL.Name ); + else + { + aCompletePath.append( "/" + aURL.Name ); + } + } + + bool bResult = aParser.ConcatData( + INetURLObject::CompareProtocolScheme( aURL.Protocol ) , + aURL.User , + aURL.Password , + aURL.Server , + aURL.Port , + aCompletePath); + + if ( !bResult ) + return false; + + // First parse URL WITHOUT ... + aURL.Main = aParser.GetMainURL( INetURLObject::DecodeMechanism::NONE ); + // ...and then WITH parameter and mark. + aParser.SetParam( aURL.Arguments); + aParser.SetMark ( aURL.Mark, INetURLObject::EncodeMechanism::All ); + aURL.Complete = aParser.GetMainURL( INetURLObject::DecodeMechanism::NONE ); + + // Return "URL is assembled". + return true; + } + else if ( !aURL.Protocol.isEmpty() ) + { + // Minimal support for unknown protocols + aURL.Complete = aURL.Protocol + aURL.Path; + aURL.Main = aURL.Complete; + return true; + } + + return false; +} + +// XURLTransformer + +OUString SAL_CALL URLTransformer::getPresentation( const css::util::URL& aURL, + sal_Bool bWithPassword ) +{ + // Safe impossible cases. + if ( aURL.Complete.isEmpty() ) + { + return OUString(); + } + + // Check given URL + css::util::URL aTestURL = aURL; + bool bParseResult = parseSmart( aTestURL, aTestURL.Protocol ); + if ( bParseResult ) + { + if ( !bWithPassword && !aTestURL.Password.isEmpty() ) + { + // Exchange password text with other placeholder string + aTestURL.Password = "<******>"; + assemble( aTestURL ); + } + + // Convert internal URLs to "praesentation"-URLs! + OUString sPraesentationURL; + INetURLObject::translateToExternal( aTestURL.Complete, sPraesentationURL, INetURLObject::DecodeMechanism::Unambiguous ); + + return sPraesentationURL; + } + else + return OUString(); +} + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_framework_URLTransformer_get_implementation( + css::uno::XComponentContext *, + css::uno::Sequence const &) +{ + return cppu::acquire(new URLTransformer()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uiconfiguration/CommandImageResolver.cxx b/framework/source/uiconfiguration/CommandImageResolver.cxx new file mode 100644 index 0000000000..a431ae320b --- /dev/null +++ b/framework/source/uiconfiguration/CommandImageResolver.cxx @@ -0,0 +1,152 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.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 "CommandImageResolver.hxx" +#include +#include +#include +#include + +using css::uno::Sequence; + +namespace vcl +{ + +namespace +{ + +const o3tl::enumarray ImageType_Prefixes = +{ + "cmd/sc_", + "cmd/lc_", + "cmd/32/" +}; + +OUString lclConvertToCanonicalName(const OUString& rFileName) +{ + bool bRemoveSlash(true); + sal_Int32 nLength = rFileName.getLength(); + const sal_Unicode* pString = rFileName.getStr(); + + OUStringBuffer aBuffer(nLength*2); + + for (sal_Int32 i = 0; i < nLength; i++) + { + const sal_Unicode cCurrentChar = pString[i]; + switch (cCurrentChar) + { + // map forbidden characters to escape + case '/': + if (!bRemoveSlash) + aBuffer.append("%2f"); + break; + case '\\': aBuffer.append("%5c"); bRemoveSlash = false; break; + case ':': aBuffer.append("%3a"); bRemoveSlash = false; break; + case '*': aBuffer.append("%2a"); bRemoveSlash = false; break; + case '?': aBuffer.append("%3f"); bRemoveSlash = false; break; + case '<': aBuffer.append("%3c"); bRemoveSlash = false; break; + case '>': aBuffer.append("%3e"); bRemoveSlash = false; break; + case '|': aBuffer.append("%7c"); bRemoveSlash = false; break; + default: + aBuffer.append(cCurrentChar); bRemoveSlash = false; break; + } + } + return aBuffer.makeStringAndClear(); +} + +} // end anonymous namespace + +CommandImageResolver::CommandImageResolver() +{ +} + +CommandImageResolver::~CommandImageResolver() +{ +} + +void CommandImageResolver::registerCommands(const Sequence& aCommandSequence) +{ + sal_Int32 nSequenceSize = aCommandSequence.getLength(); + + m_aImageCommandNameVector.resize(nSequenceSize); + m_aImageNameVector.resize(nSequenceSize); + + for (sal_Int32 i = 0; i < nSequenceSize; ++i) + { + OUString aCommandName(aCommandSequence[i]); + OUString aImageName; + + m_aImageCommandNameVector[i] = aCommandName; + + if (aCommandName.indexOf(".uno:") != 0) + { + INetURLObject aUrlObject(aCommandName, INetURLObject::EncodeMechanism::All); + aImageName = aUrlObject.GetURLPath(); + aImageName = lclConvertToCanonicalName(aImageName); + } + else + { + // just remove the schema + if (aCommandName.getLength() > 5) + aImageName = aCommandName.copy(5); + + // Search for query part. + if (aImageName.indexOf('?') != -1) + aImageName = lclConvertToCanonicalName(aImageName); + } + + // Image names are not case-dependent. Always use lower case characters to + // reflect this. + aImageName = aImageName.toAsciiLowerCase() + ".png"; + + m_aImageNameVector[i] = aImageName; + m_aCommandToImageNameMap[aCommandName] = aImageName; + } +} + +bool CommandImageResolver::hasImage(const OUString& rCommandURL) +{ + CommandToImageNameMap::const_iterator pIterator = m_aCommandToImageNameMap.find(rCommandURL); + return pIterator != m_aCommandToImageNameMap.end(); +} + +ImageList* CommandImageResolver::getImageList(ImageType nImageType) +{ + const OUString sIconTheme = Application::GetSettings().GetStyleSettings().DetermineIconTheme(); + + if (sIconTheme != m_sIconTheme) + { + m_sIconTheme = sIconTheme; + for (auto& rp : m_pImageList) + rp.reset(); + } + + if (!m_pImageList[nImageType]) + { + OUString sIconPath = OUString::createFromAscii(ImageType_Prefixes[nImageType]); + m_pImageList[nImageType].reset( new ImageList(m_aImageNameVector, sIconPath) ); + } + + return m_pImageList[nImageType].get(); +} + +Image CommandImageResolver::getImageFromCommandURL(ImageType nImageType, const OUString& rCommandURL) +{ + CommandToImageNameMap::const_iterator pIterator = m_aCommandToImageNameMap.find(rCommandURL); + if (pIterator != m_aCommandToImageNameMap.end()) + { + ImageList* pImageList = getImageList(nImageType); + return pImageList->GetImage(pIterator->second); + } + return Image(); +} + +} // end namespace vcl + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uiconfiguration/CommandImageResolver.hxx b/framework/source/uiconfiguration/CommandImageResolver.hxx new file mode 100644 index 0000000000..0622caf332 --- /dev/null +++ b/framework/source/uiconfiguration/CommandImageResolver.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/. + */ + +#pragma once + +#include +#include + +#include + +#include "ImageList.hxx" + +#include +#include +#include + +namespace vcl +{ +class CommandImageResolver final +{ +private: + typedef std::unordered_map CommandToImageNameMap; + + CommandToImageNameMap m_aCommandToImageNameMap; + std::vector m_aImageCommandNameVector; + std::vector m_aImageNameVector; + + o3tl::enumarray> m_pImageList; + OUString m_sIconTheme; + + ImageList* getImageList(ImageType nImageType); + +public: + CommandImageResolver(); + ~CommandImageResolver(); + + void registerCommands(const css::uno::Sequence& aCommandSequence); + Image getImageFromCommandURL(ImageType nImageType, const OUString& rCommandURL); + + std::vector& getCommandNames() { return m_aImageCommandNameVector; } + + bool hasImage(const OUString& rCommandURL); +}; + +} // end namespace vcl + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uiconfiguration/ImageList.cxx b/framework/source/uiconfiguration/ImageList.cxx new file mode 100644 index 0000000000..5fb0f44f65 --- /dev/null +++ b/framework/source/uiconfiguration/ImageList.cxx @@ -0,0 +1,201 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include "ImageList.hxx" + +ImageList::ImageList() +{ +} + +ImageList::ImageList(const std::vector< OUString >& rNameVector, + const OUString& rPrefix) +{ + SAL_INFO( "vcl", "vcl: ImageList::ImageList(const vector< OUString >& ..." ); + + maImages.reserve( rNameVector.size() ); + + maPrefix = rPrefix; + for( size_t i = 0; i < rNameVector.size(); ++i ) + ImplAddImage( rPrefix, rNameVector[ i ], static_cast< sal_uInt16 >( i ) + 1, Image() ); +} + +// FIXME: Rather a performance hazard +BitmapEx ImageList::GetAsHorizontalStrip() const +{ + sal_uInt16 nCount = maImages.size(); + if( !nCount ) + return BitmapEx(); + + BitmapEx aTempl = maImages[ 0 ]->maImage.GetBitmapEx(); + Size aImageSize(aTempl.GetSizePixel()); + Size aSize(aImageSize.Width() * nCount, aImageSize.Height()); + BitmapEx aResult( aTempl, Point(), aSize ); + + tools::Rectangle aSrcRect( Point( 0, 0 ), aImageSize ); + for (sal_uInt16 nIdx = 0; nIdx < nCount; nIdx++) + { + tools::Rectangle aDestRect( Point( nIdx * aImageSize.Width(), 0 ), aImageSize ); + ImageAryData *pData = maImages[ nIdx ].get(); + BitmapEx aTmp = pData->maImage.GetBitmapEx(); + aResult.CopyPixel( aDestRect, aSrcRect, aTmp); + } + + return aResult; +} + +void ImageList::InsertFromHorizontalStrip( const BitmapEx &rBitmapEx, + const std::vector< OUString > &rNameVector ) +{ + sal_uInt16 nItems = sal::static_int_cast< sal_uInt16 >( rNameVector.size() ); + + if (!nItems) + return; + + Size aSize( rBitmapEx.GetSizePixel() ); + DBG_ASSERT (rBitmapEx.GetSizePixel().Width() % nItems == 0, + "ImageList::InsertFromHorizontalStrip - very odd size"); + aSize.setWidth( aSize.Width() / nItems ); + maImages.clear(); + maNameHash.clear(); + maImages.reserve( nItems ); + maPrefix.clear(); + + for (sal_uInt16 nIdx = 0; nIdx < nItems; nIdx++) + { + BitmapEx aBitmap( rBitmapEx, Point( nIdx * aSize.Width(), 0 ), aSize ); + ImplAddImage( maPrefix, rNameVector[ nIdx ], nIdx + 1, Image( aBitmap ) ); + } +} + +sal_uInt16 ImageList::ImplGetImageId( const OUString& rImageName ) const +{ + auto it = maNameHash.find( rImageName ); + if (it == maNameHash.end()) + return 0; + return it->second->mnId; +} + +void ImageList::AddImage( const OUString& rImageName, const Image& rImage ) +{ + SAL_WARN_IF( GetImagePos( rImageName ) != IMAGELIST_IMAGE_NOTFOUND, "vcl", "ImageList::AddImage() - ImageName already exists" ); + + ImplAddImage( maPrefix, rImageName, GetImageCount() + 1, rImage ); +} + +void ImageList::ReplaceImage( const OUString& rImageName, const Image& rImage ) +{ + const sal_uInt16 nId = ImplGetImageId( rImageName ); + + if( nId ) + { + // Just replace the bitmap rather than doing RemoveImage / AddImage + // which breaks index-based iteration. + ImageAryData *pImg = maNameHash[ rImageName ]; + pImg->maImage = rImage; + } +} + +void ImageList::RemoveImage( sal_uInt16 nId ) +{ + for( size_t i = 0; i < maImages.size(); ++i ) + { + if( maImages[ i ]->mnId == nId ) + { + ImplRemoveImage( static_cast< sal_uInt16 >( i ) ); + break; + } + } +} + +Image ImageList::GetImage( const OUString& rImageName ) const +{ + auto it = maNameHash.find( rImageName ); + if (it == maNameHash.end()) + return Image(); + return it->second->maImage; +} + +sal_uInt16 ImageList::GetImageCount() const +{ + return static_cast< sal_uInt16 >( maImages.size() ); +} + +sal_uInt16 ImageList::GetImagePos( std::u16string_view rImageName ) const +{ + if( !rImageName.empty() ) + { + for( size_t i = 0; i < maImages.size(); i++ ) + { + if (maImages[i]->maName == rImageName) + return static_cast< sal_uInt16 >( i ); + } + } + + return IMAGELIST_IMAGE_NOTFOUND; +} + +sal_uInt16 ImageList::GetImageId( sal_uInt16 nPos ) const +{ + return maImages[ nPos ]->mnId; +} + +const OUString & ImageList::GetImageName( sal_uInt16 nPos ) const +{ + return maImages[ nPos ]->maName; +} + +void ImageList::GetImageNames( std::vector< OUString >& rNames ) const +{ + SAL_INFO( "vcl", "vcl: ImageList::GetImageNames" ); + + rNames = std::vector< OUString >(); + + for(auto const & pImage : maImages) + { + const OUString& rName( pImage->maName ); + if( !rName.isEmpty()) + rNames.push_back( rName ); + } +} + +void ImageList::ImplAddImage( std::u16string_view aPrefix, const OUString &aName, + sal_uInt16 nId, const Image &aImage ) +{ + Image aInsert = aImage; + if (!aInsert) + aInsert = Image( OUString::Concat("private:graphicrepository/") + aPrefix + aName ); + + ImageAryData *pImg = new ImageAryData{ aName, nId, aInsert }; + maImages.emplace_back( pImg ); + if( !aName.isEmpty() ) + maNameHash [ aName ] = pImg; +} + +void ImageList::ImplRemoveImage( sal_uInt16 nPos ) +{ + ImageAryData *pImg = maImages[ nPos ].get(); + if( !pImg->maName.isEmpty() ) + maNameHash.erase( pImg->maName ); + maImages.erase( maImages.begin() + nPos ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uiconfiguration/ImageList.hxx b/framework/source/uiconfiguration/ImageList.hxx new file mode 100644 index 0000000000..edd0789ed1 --- /dev/null +++ b/framework/source/uiconfiguration/ImageList.hxx @@ -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 . + */ + +#pragma once + +#include + +#include +#include +#include + +// Images identified by either name, or by id +struct ImageAryData +{ + OUString maName; + sal_uInt16 mnId; + Image maImage; +}; + + +class ImageList +{ +public: + explicit ImageList(); + ImageList( const std::vector& rNameVector, + const OUString& rPrefix); + + void InsertFromHorizontalStrip( const BitmapEx &rBitmapEx, + const std::vector< OUString > &rNameVector ); + BitmapEx GetAsHorizontalStrip() const; + sal_uInt16 GetImageCount() const; + + void AddImage( const OUString& rImageName, const Image& rImage ); + + void ReplaceImage( const OUString& rImageName, const Image& rImage ); + + void RemoveImage( sal_uInt16 nId ); + + Image GetImage( const OUString& rImageName ) const; + + sal_uInt16 GetImagePos( std::u16string_view rImageName ) const; + + sal_uInt16 GetImageId( sal_uInt16 nPos ) const; + + const OUString & GetImageName( sal_uInt16 nPos ) const; + void GetImageNames( ::std::vector< OUString >& rNames ) const; + +private: + + std::vector< std::unique_ptr > maImages; + std::unordered_map< OUString, ImageAryData * > maNameHash; + OUString maPrefix; + + sal_uInt16 ImplGetImageId( const OUString& rImageName ) const; + void ImplAddImage( std::u16string_view aPrefix, const OUString &aName, sal_uInt16 nId, const Image &aImage ); + void ImplRemoveImage( sal_uInt16 nPos ); +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uiconfiguration/globalsettings.cxx b/framework/source/uiconfiguration/globalsettings.cxx new file mode 100644 index 0000000000..0883cc8af1 --- /dev/null +++ b/framework/source/uiconfiguration/globalsettings.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 +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +// Defines + +using namespace ::com::sun::star; + +// Namespace + +namespace framework +{ + +// Configuration access class for WindowState supplier implementation + +namespace { + +class GlobalSettings_Access : public ::cppu::WeakImplHelper< + css::lang::XComponent, + css::lang::XEventListener> +{ + public: + explicit GlobalSettings_Access( css::uno::Reference< css::uno::XComponentContext > xContext ); + + // 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 >& aListener ) override; + + // XEventListener + virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override; + + // settings access + bool HasToolbarStatesInfo(); + bool GetToolbarStateInfo( GlobalSettings::StateInfo eStateInfo, css::uno::Any& aValue ); + + private: + void impl_initConfigAccess(); + + std::mutex m_mutex; + bool m_bDisposed : 1, + m_bConfigRead : 1; + OUString m_aNodeRefStates; + OUString m_aPropStatesEnabled; + OUString m_aPropLocked; + OUString m_aPropDocked; + css::uno::Reference< css::container::XNameAccess > m_xConfigAccess; + css::uno::Reference< css::uno::XComponentContext> m_xContext; +}; + +} + +GlobalSettings_Access::GlobalSettings_Access( css::uno::Reference< css::uno::XComponentContext > xContext ) : + m_bDisposed( false ), + m_bConfigRead( false ), + m_aNodeRefStates( "States" ), + m_aPropStatesEnabled( "StatesEnabled" ), + m_aPropLocked( "Locked" ), + m_aPropDocked( "Docked" ), + m_xContext(std::move( xContext )) +{ +} + +// XComponent +void SAL_CALL GlobalSettings_Access::dispose() +{ + std::unique_lock g(m_mutex); + m_xConfigAccess.clear(); + m_bDisposed = true; +} + +void SAL_CALL GlobalSettings_Access::addEventListener( const css::uno::Reference< css::lang::XEventListener >& ) +{ +} + +void SAL_CALL GlobalSettings_Access::removeEventListener( const css::uno::Reference< css::lang::XEventListener >& ) +{ +} + +// XEventListener +void SAL_CALL GlobalSettings_Access::disposing( const css::lang::EventObject& ) +{ + std::unique_lock g(m_mutex); + m_xConfigAccess.clear(); +} + +// settings access +bool GlobalSettings_Access::HasToolbarStatesInfo() +{ + std::unique_lock g(m_mutex); + + if ( m_bDisposed ) + return false; + + if ( !m_bConfigRead ) + { + m_bConfigRead = true; + impl_initConfigAccess(); + } + + if ( m_xConfigAccess.is() ) + { + try + { + css::uno::Any a; + bool bValue; + a = m_xConfigAccess->getByName( m_aPropStatesEnabled ); + if ( a >>= bValue ) + return bValue; + } + catch ( const css::container::NoSuchElementException& ) + { + } + catch ( const css::uno::Exception& ) + { + } + } + + return false; +} + +bool GlobalSettings_Access::GetToolbarStateInfo( GlobalSettings::StateInfo eStateInfo, css::uno::Any& aValue ) +{ + std::unique_lock g(m_mutex); + + if ( m_bDisposed ) + return false; + + if ( !m_bConfigRead ) + { + m_bConfigRead = true; + impl_initConfigAccess(); + } + + if ( !m_xConfigAccess.is() ) + return false; + + try + { + css::uno::Any a = m_xConfigAccess->getByName( m_aNodeRefStates ); + css::uno::Reference< css::container::XNameAccess > xNameAccess; + if ( a >>= xNameAccess ) + { + if ( eStateInfo == GlobalSettings::STATEINFO_LOCKED ) + a = xNameAccess->getByName( m_aPropLocked ); + else if ( eStateInfo == GlobalSettings::STATEINFO_DOCKED ) + a = xNameAccess->getByName( m_aPropDocked ); + + aValue = a; + return true; + } + } + catch ( const css::container::NoSuchElementException& ) + { + } + catch ( const css::uno::Exception& ) + { + } + + return false; +} + +void GlobalSettings_Access::impl_initConfigAccess() +{ + try + { + if ( m_xContext.is() ) + { + css::uno::Reference< css::lang::XMultiServiceFactory > xConfigProvider = + css::configuration::theDefaultProvider::get( m_xContext ); + + uno::Sequence aArgs(comphelper::InitAnyPropertySequence( + { + {"nodepath", uno::Any(OUString("/org.openoffice.Office.UI.GlobalSettings/Toolbars"))} + })); + m_xConfigAccess.set(xConfigProvider->createInstanceWithArguments( + SERVICENAME_CFGREADACCESS, aArgs ), + css::uno::UNO_QUERY ); + + css::uno::Reference< css::lang::XComponent >( + xConfigProvider, css::uno::UNO_QUERY_THROW )->addEventListener( + css::uno::Reference< css::lang::XEventListener >(this)); + } + } + catch ( const css::lang::WrappedTargetException& ) + { + } + catch ( const css::uno::Exception& ) + { + } +} + +// global class + +static GlobalSettings_Access* GetGlobalSettings( const css::uno::Reference< css::uno::XComponentContext >& rxContext ) +{ + static rtl::Reference pStaticSettings = new GlobalSettings_Access( rxContext ); + return pStaticSettings.get(); +} + +GlobalSettings::GlobalSettings( css::uno::Reference< css::uno::XComponentContext > xContext ) : + m_xContext(std::move( xContext )) +{ +} + +GlobalSettings::~GlobalSettings() +{ +} + +// settings access +bool GlobalSettings::HasToolbarStatesInfo() const +{ + GlobalSettings_Access* pSettings( GetGlobalSettings( m_xContext )); + + if ( pSettings ) + return pSettings->HasToolbarStatesInfo(); + else + return false; +} + +bool GlobalSettings::GetToolbarStateInfo( StateInfo eStateInfo, css::uno::Any& aValue ) +{ + GlobalSettings_Access* pSettings( GetGlobalSettings( m_xContext )); + + if ( pSettings ) + return pSettings->GetToolbarStateInfo( eStateInfo, aValue ); + else + return false; +} + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uiconfiguration/graphicnameaccess.cxx b/framework/source/uiconfiguration/graphicnameaccess.cxx new file mode 100644 index 0000000000..6812f5604c --- /dev/null +++ b/framework/source/uiconfiguration/graphicnameaccess.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 + +#include + +using namespace ::com::sun::star; + +namespace framework +{ + +GraphicNameAccess::GraphicNameAccess() +{ +} + +GraphicNameAccess::~GraphicNameAccess() +{ +} + +void GraphicNameAccess::addElement( const OUString& rName, const uno::Reference< graphic::XGraphic >& rElement ) +{ + m_aNameToElementMap.emplace( rName, rElement ); +} + +// XNameAccess +uno::Any SAL_CALL GraphicNameAccess::getByName( const OUString& aName ) +{ + NameGraphicHashMap::const_iterator pIter = m_aNameToElementMap.find( aName ); + if ( pIter == m_aNameToElementMap.end() ) + throw container::NoSuchElementException(); + return uno::Any( pIter->second ); +} + +uno::Sequence< OUString > SAL_CALL GraphicNameAccess::getElementNames() +{ + if ( !m_aSeq.hasElements() ) + { + m_aSeq = comphelper::mapKeysToSequence(m_aNameToElementMap); + } + + return m_aSeq; +} + +sal_Bool SAL_CALL GraphicNameAccess::hasByName( const OUString& aName ) +{ + NameGraphicHashMap::const_iterator pIter = m_aNameToElementMap.find( aName ); + return ( pIter != m_aNameToElementMap.end() ); +} + +// XElementAccess +sal_Bool SAL_CALL GraphicNameAccess::hasElements() +{ + return ( !m_aNameToElementMap.empty() ); +} + +uno::Type SAL_CALL GraphicNameAccess::getElementType() +{ + return cppu::UnoType::get(); +} + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uiconfiguration/imagemanager.cxx b/framework/source/uiconfiguration/imagemanager.cxx new file mode 100644 index 0000000000..1e104b6df6 --- /dev/null +++ b/framework/source/uiconfiguration/imagemanager.cxx @@ -0,0 +1,173 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include "imagemanagerimpl.hxx" + +#include + +#include + +using ::com::sun::star::uno::Sequence; +using ::com::sun::star::uno::XInterface; +using ::com::sun::star::uno::Any; +using ::com::sun::star::graphic::XGraphic; +using namespace ::com::sun::star; +using namespace ::com::sun::star::io; +using namespace ::com::sun::star::embed; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::beans; + +namespace framework +{ + +ImageManager::ImageManager( const uno::Reference< uno::XComponentContext >& rxContext, bool bForModule ) : + m_pImpl( new ImageManagerImpl(rxContext, this, bForModule) ) +{ +} + +ImageManager::~ImageManager() +{ + m_pImpl->clear(); +} + +// XComponent +void SAL_CALL ImageManager::dispose() +{ + m_pImpl->dispose(); +} + +void SAL_CALL ImageManager::addEventListener( const uno::Reference< XEventListener >& xListener ) +{ + m_pImpl->addEventListener(xListener); +} + +void SAL_CALL ImageManager::removeEventListener( const uno::Reference< XEventListener >& xListener ) +{ + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + m_pImpl->removeEventListener(xListener); +} + +// Non-UNO methods +void ImageManager::setStorage( const uno::Reference< XStorage >& Storage ) +{ + SolarMutexGuard g; + + m_pImpl->m_xUserConfigStorage = Storage; + m_pImpl->implts_initialize(); +} + +// XInitialization +void SAL_CALL ImageManager::initialize( const Sequence< Any >& aArguments ) +{ + m_pImpl->initialize(aArguments); +} + +// XImageManager +void SAL_CALL ImageManager::reset() +{ + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + m_pImpl->reset(); +} + +Sequence< OUString > SAL_CALL ImageManager::getAllImageNames( ::sal_Int16 nImageType ) +{ + return m_pImpl->getAllImageNames( nImageType ); +} + +sal_Bool SAL_CALL ImageManager::hasImage( ::sal_Int16 nImageType, const OUString& aCommandURL ) +{ + return m_pImpl->hasImage(nImageType,aCommandURL); +} + +Sequence< uno::Reference< XGraphic > > SAL_CALL ImageManager::getImages( + ::sal_Int16 nImageType, + const Sequence< OUString >& aCommandURLSequence ) +{ + return m_pImpl->getImages(nImageType,aCommandURLSequence); +} + +void SAL_CALL ImageManager::replaceImages( + ::sal_Int16 nImageType, + const Sequence< OUString >& aCommandURLSequence, + const Sequence< uno::Reference< XGraphic > >& aGraphicsSequence ) +{ + m_pImpl->replaceImages(nImageType,aCommandURLSequence,aGraphicsSequence); +} + +void SAL_CALL ImageManager::removeImages( ::sal_Int16 nImageType, const Sequence< OUString >& aCommandURLSequence ) +{ + m_pImpl->removeImages(nImageType,aCommandURLSequence); +} + +void SAL_CALL ImageManager::insertImages( ::sal_Int16 nImageType, const Sequence< OUString >& aCommandURLSequence, const Sequence< uno::Reference< XGraphic > >& aGraphicSequence ) +{ + m_pImpl->insertImages(nImageType,aCommandURLSequence,aGraphicSequence); +} + +// XUIConfiguration +void SAL_CALL ImageManager::addConfigurationListener( const uno::Reference< css::ui::XUIConfigurationListener >& xListener ) +{ + m_pImpl->addConfigurationListener(xListener); +} + +void SAL_CALL ImageManager::removeConfigurationListener( const uno::Reference< css::ui::XUIConfigurationListener >& xListener ) +{ + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + m_pImpl->removeConfigurationListener(xListener); +} + +// XUIConfigurationPersistence +void SAL_CALL ImageManager::reload() +{ + m_pImpl->reload(); +} + +void SAL_CALL ImageManager::store() +{ + m_pImpl->store(); +} + +void SAL_CALL ImageManager::storeToStorage( const uno::Reference< XStorage >& Storage ) +{ + m_pImpl->storeToStorage(Storage); +} + +sal_Bool SAL_CALL ImageManager::isModified() +{ + return m_pImpl->isModified(); +} + +sal_Bool SAL_CALL ImageManager::isReadOnly() +{ + return m_pImpl->isReadOnly(); +} + +} // namespace framework + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_framework_ImageManager_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence const &) +{ + return cppu::acquire(new framework::ImageManager(context, /*bForModule*/false)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uiconfiguration/imagemanagerimpl.cxx b/framework/source/uiconfiguration/imagemanagerimpl.cxx new file mode 100644 index 0000000000..a387fa0115 --- /dev/null +++ b/framework/source/uiconfiguration/imagemanagerimpl.cxx @@ -0,0 +1,1210 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "imagemanagerimpl.hxx" +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using ::com::sun::star::uno::Sequence; +using ::com::sun::star::uno::XInterface; +using ::com::sun::star::uno::RuntimeException; +using ::com::sun::star::uno::UNO_QUERY; +using ::com::sun::star::uno::Any; +using ::com::sun::star::graphic::XGraphic; +using namespace ::com::sun::star; +using namespace ::com::sun::star::io; +using namespace ::com::sun::star::embed; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::ui; +using namespace ::cppu; + +const sal_Int16 MAX_IMAGETYPE_VALUE = css::ui::ImageType::SIZE_32; + +constexpr OUString IMAGE_FOLDER = u"images"_ustr; +constexpr OUString BITMAPS_FOLDER = u"Bitmaps"_ustr; + +const o3tl::enumarray IMAGELIST_XML_FILE = +{ + "sc_imagelist.xml", + "lc_imagelist.xml", + "xc_imagelist.xml" +}; + +const o3tl::enumarray BITMAP_FILE_NAMES = +{ + "sc_userimages.png", + "lc_userimages.png", + "xc_userimages.png" +}; + +namespace framework +{ + +static GlobalImageList* pGlobalImageList = nullptr; + +static std::mutex& getGlobalImageListMutex() +{ + static std::mutex mutex; + return mutex; +} + +static GlobalImageList* getGlobalImageList( const uno::Reference< uno::XComponentContext >& rxContext ) +{ + std::unique_lock guard( getGlobalImageListMutex() ); + + if ( pGlobalImageList == nullptr ) + pGlobalImageList = new GlobalImageList( rxContext ); + + return pGlobalImageList; +} + +CmdImageList::CmdImageList( uno::Reference< uno::XComponentContext > rxContext, OUString aModuleIdentifier ) : + m_bInitialized(false), + m_aModuleIdentifier(std::move( aModuleIdentifier )), + m_xContext(std::move( rxContext )) +{ +} + +CmdImageList::~CmdImageList() +{ +} + +void CmdImageList::initialize() +{ + if (m_bInitialized) + return; + + const OUString aCommandImageList(UICOMMANDDESCRIPTION_NAMEACCESS_COMMANDIMAGELIST); + + Sequence aCommandImageSeq; + uno::Reference xCommandDesc = frame::theUICommandDescription::get(m_xContext); + + if (!m_aModuleIdentifier.isEmpty()) + { + // If we have a module identifier - use to retrieve the command image name list from it. + // Otherwise we will use the global command image list + try + { + xCommandDesc->getByName(m_aModuleIdentifier) >>= xCommandDesc; + if (xCommandDesc.is()) + xCommandDesc->getByName(aCommandImageList) >>= aCommandImageSeq; + } + catch (const NoSuchElementException&) + { + // Module unknown we will work with an empty command image list! + return; + } + } + + if (xCommandDesc.is()) + { + try + { + xCommandDesc->getByName(aCommandImageList) >>= aCommandImageSeq; + } + catch (const NoSuchElementException&) + { + } + catch (const WrappedTargetException&) + { + } + } + + m_aResolver.registerCommands(aCommandImageSeq); + + m_bInitialized = true; +} + + +Image CmdImageList::getImageFromCommandURL(vcl::ImageType nImageType, const OUString& rCommandURL) +{ + initialize(); + return m_aResolver.getImageFromCommandURL(nImageType, rCommandURL); +} + +bool CmdImageList::hasImage(vcl::ImageType /*nImageType*/, const OUString& rCommandURL) +{ + initialize(); + return m_aResolver.hasImage(rCommandURL); +} + +std::vector& CmdImageList::getImageCommandNames() +{ + return m_aResolver.getCommandNames(); +} + +GlobalImageList::GlobalImageList( const uno::Reference< uno::XComponentContext >& rxContext ) : + CmdImageList( rxContext, OUString() ) +{ +} + +GlobalImageList::~GlobalImageList() +{ + std::unique_lock guard( getGlobalImageListMutex() ); + // remove global pointer as we destroy the object now + pGlobalImageList = nullptr; +} + +Image GlobalImageList::getImageFromCommandURL( vcl::ImageType nImageType, const OUString& rCommandURL ) +{ + std::unique_lock guard( getGlobalImageListMutex() ); + return CmdImageList::getImageFromCommandURL( nImageType, rCommandURL ); +} + +bool GlobalImageList::hasImage( vcl::ImageType nImageType, const OUString& rCommandURL ) +{ + std::unique_lock guard( getGlobalImageListMutex() ); + return CmdImageList::hasImage( nImageType, rCommandURL ); +} + +::std::vector< OUString >& GlobalImageList::getImageCommandNames() +{ + std::unique_lock guard( getGlobalImageListMutex() ); + return CmdImageList::getImageCommandNames(); +} + +static bool implts_checkAndScaleGraphic( uno::Reference< XGraphic >& rOutGraphic, const uno::Reference< XGraphic >& rInGraphic, vcl::ImageType nImageType ) +{ + if ( !rInGraphic.is() ) + { + rOutGraphic = uno::Reference(); + return false; + } + + static const o3tl::enumarray BITMAP_SIZE = + { + Size(16, 16), Size(24, 24), Size(32, 32) + }; + + // Check size and scale it + Graphic aImage(rInGraphic); + if (BITMAP_SIZE[nImageType] != aImage.GetSizePixel()) + { + BitmapEx aBitmap = aImage.GetBitmapEx(); + aBitmap.Scale(BITMAP_SIZE[nImageType]); + aImage = Graphic(aBitmap); + rOutGraphic = aImage.GetXGraphic(); + } + else + rOutGraphic = rInGraphic; + + return true; +} + +static vcl::ImageType implts_convertImageTypeToIndex( sal_Int16 nImageType ) +{ + if (nImageType & css::ui::ImageType::SIZE_LARGE) + return vcl::ImageType::Size26; + else if (nImageType & css::ui::ImageType::SIZE_32) + return vcl::ImageType::Size32; + else + return vcl::ImageType::Size16; +} + +ImageList* ImageManagerImpl::implts_getUserImageList( vcl::ImageType nImageType ) +{ + SolarMutexGuard g; + if ( !m_pUserImageList[nImageType] ) + implts_loadUserImages( nImageType, m_xUserImageStorage, m_xUserBitmapsStorage ); + + return m_pUserImageList[nImageType].get(); +} + +void ImageManagerImpl::implts_initialize() +{ + // Initialize the top-level structures with the storage data + if ( !m_xUserConfigStorage.is() ) + return; + + tools::Long nModes = m_bReadOnly ? ElementModes::READ : ElementModes::READWRITE; + + try + { + m_xUserImageStorage = m_xUserConfigStorage->openStorageElement( IMAGE_FOLDER, + nModes ); + if ( m_xUserImageStorage.is() ) + { + m_xUserBitmapsStorage = m_xUserImageStorage->openStorageElement( BITMAPS_FOLDER, + nModes ); + } + } + catch ( const css::container::NoSuchElementException& ) + { + } + catch ( const css::embed::InvalidStorageException& ) + { + } + catch ( const css::lang::IllegalArgumentException& ) + { + } + catch ( const css::io::IOException& ) + { + } + catch ( const css::embed::StorageWrappedTargetException& ) + { + } +} + +void ImageManagerImpl::implts_loadUserImages( + vcl::ImageType nImageType, + const uno::Reference< XStorage >& xUserImageStorage, + const uno::Reference< XStorage >& xUserBitmapsStorage ) +{ + SolarMutexGuard g; + + if ( xUserImageStorage.is() && xUserBitmapsStorage.is() ) + { + try + { + uno::Reference< XStream > xStream = xUserImageStorage->openStreamElement( OUString::createFromAscii( IMAGELIST_XML_FILE[nImageType] ), + ElementModes::READ ); + uno::Reference< XInputStream > xInputStream = xStream->getInputStream(); + + ImageItemDescriptorList aUserImageListInfo; + ImagesConfiguration::LoadImages( m_xContext, + xInputStream, + aUserImageListInfo ); + if ( !aUserImageListInfo.empty() ) + { + sal_Int32 nCount = aUserImageListInfo.size(); + std::vector< OUString > aUserImagesVector; + aUserImagesVector.reserve(nCount); + for ( sal_Int32 i=0; i < nCount; i++ ) + { + const ImageItemDescriptor& rItem = aUserImageListInfo[i]; + aUserImagesVector.push_back( rItem.aCommandURL ); + } + + uno::Reference< XStream > xBitmapStream = xUserBitmapsStorage->openStreamElement( + OUString::createFromAscii( BITMAP_FILE_NAMES[nImageType] ), + ElementModes::READ ); + + if ( xBitmapStream.is() ) + { + BitmapEx aUserBitmap; + { + std::unique_ptr pSvStream(utl::UcbStreamHelper::CreateStream( xBitmapStream )); + vcl::PngImageReader aPngReader( *pSvStream ); + aUserBitmap = aPngReader.read(); + } + + // Delete old image list and create a new one from the read bitmap + m_pUserImageList[nImageType].reset(new ImageList()); + m_pUserImageList[nImageType]->InsertFromHorizontalStrip + ( aUserBitmap, aUserImagesVector ); + return; + } + } + } + catch ( const css::container::NoSuchElementException& ) + { + } + catch ( const css::embed::InvalidStorageException& ) + { + } + catch ( const css::lang::IllegalArgumentException& ) + { + } + catch ( const css::io::IOException& ) + { + } + catch ( const css::embed::StorageWrappedTargetException& ) + { + } + } + + // Destroy old image list - create a new empty one + m_pUserImageList[nImageType].reset(new ImageList); +} + +bool ImageManagerImpl::implts_storeUserImages( + vcl::ImageType nImageType, + const uno::Reference< XStorage >& xUserImageStorage, + const uno::Reference< XStorage >& xUserBitmapsStorage ) +{ + SolarMutexGuard g; + + if ( !m_bModified ) + return false; + + ImageList* pImageList = implts_getUserImageList( nImageType ); + if ( pImageList->GetImageCount() > 0 ) + { + ImageItemDescriptorList aUserImageListInfo; + + for ( sal_uInt16 i=0; i < pImageList->GetImageCount(); i++ ) + { + ImageItemDescriptor aItem; + aItem.aCommandURL = pImageList->GetImageName( i ); + aUserImageListInfo.push_back( aItem ); + } + + uno::Reference< XTransactedObject > xTransaction; + uno::Reference< XOutputStream > xOutputStream; + uno::Reference< XStream > xStream = xUserImageStorage->openStreamElement( OUString::createFromAscii( IMAGELIST_XML_FILE[nImageType] ), + ElementModes::WRITE|ElementModes::TRUNCATE ); + if ( xStream.is() ) + { + uno::Reference< XStream > xBitmapStream = + xUserBitmapsStorage->openStreamElement( OUString::createFromAscii( BITMAP_FILE_NAMES[nImageType] ), + ElementModes::WRITE|ElementModes::TRUNCATE ); + if ( xBitmapStream.is() ) + { + { + std::unique_ptr pSvStream(utl::UcbStreamHelper::CreateStream( xBitmapStream )); + vcl::PngImageWriter aPngWriter( *pSvStream ); + auto rBitmap = pImageList->GetAsHorizontalStrip(); + aPngWriter.write( rBitmap ); + } + + // Commit user bitmaps storage + xTransaction.set( xUserBitmapsStorage, UNO_QUERY ); + if ( xTransaction.is() ) + xTransaction->commit(); + } + + xOutputStream = xStream->getOutputStream(); + if ( xOutputStream.is() ) + ImagesConfiguration::StoreImages( m_xContext, xOutputStream, aUserImageListInfo ); + + // Commit user image storage + xTransaction.set( xUserImageStorage, UNO_QUERY ); + if ( xTransaction.is() ) + xTransaction->commit(); + } + + return true; + } + else + { + // Remove the streams from the storage, if we have no data. We have to catch + // the NoSuchElementException as it can be possible that there is no stream at all! + try + { + xUserImageStorage->removeElement( OUString::createFromAscii( IMAGELIST_XML_FILE[nImageType] )); + } + catch ( const css::container::NoSuchElementException& ) + { + } + + try + { + xUserBitmapsStorage->removeElement( OUString::createFromAscii( BITMAP_FILE_NAMES[nImageType] )); + } + catch ( const css::container::NoSuchElementException& ) + { + } + + uno::Reference< XTransactedObject > xTransaction; + + // Commit user image storage + xTransaction.set( xUserImageStorage, UNO_QUERY ); + if ( xTransaction.is() ) + xTransaction->commit(); + + // Commit user bitmaps storage + xTransaction.set( xUserBitmapsStorage, UNO_QUERY ); + if ( xTransaction.is() ) + xTransaction->commit(); + + return true; + } + + return false; +} + +const rtl::Reference< GlobalImageList >& ImageManagerImpl::implts_getGlobalImageList() +{ + SolarMutexGuard g; + + if ( !m_pGlobalImageList.is() ) + m_pGlobalImageList = getGlobalImageList( m_xContext ); + return m_pGlobalImageList; +} + +CmdImageList* ImageManagerImpl::implts_getDefaultImageList() +{ + SolarMutexGuard g; + + if ( !m_pDefaultImageList ) + m_pDefaultImageList.reset(new CmdImageList( m_xContext, m_aModuleIdentifier )); + + return m_pDefaultImageList.get(); +} + +ImageManagerImpl::ImageManagerImpl( uno::Reference< uno::XComponentContext > xContext, ::cppu::OWeakObject* pOwner, bool _bUseGlobal ) : + m_xContext(std::move( xContext )) + , m_pOwner(pOwner) + , m_aResourceString( "private:resource/images/moduleimages" ) + , m_bUseGlobal(_bUseGlobal) + , m_bReadOnly( true ) + , m_bInitialized( false ) + , m_bModified( false ) + , m_bDisposed( false ) +{ + for ( vcl::ImageType n : o3tl::enumrange() ) + { + m_pUserImageList[n] = nullptr; + m_bUserImageListModified[n] = false; + } +} + +ImageManagerImpl::~ImageManagerImpl() +{ + clear(); +} + +void ImageManagerImpl::dispose() +{ + uno::Reference< uno::XInterface > xOwner(m_pOwner); + css::lang::EventObject aEvent( xOwner ); + { + std::unique_lock aGuard(m_mutex); + m_aEventListeners.disposeAndClear( aGuard, aEvent ); + } + { + std::unique_lock aGuard(m_mutex); + m_aConfigListeners.disposeAndClear( aGuard, aEvent ); + } + + { + SolarMutexGuard g; + m_xUserConfigStorage.clear(); + m_xUserImageStorage.clear(); + m_xUserRootCommit.clear(); + m_bModified = false; + m_bDisposed = true; + + // delete user and default image list on dispose + for (auto& n : m_pUserImageList) + { + n.reset(); + } + m_pDefaultImageList.reset(); + } + +} +void ImageManagerImpl::addEventListener( const uno::Reference< XEventListener >& xListener ) +{ + { + SolarMutexGuard g; + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + if ( m_bDisposed ) + throw DisposedException(); + } + + std::unique_lock aGuard(m_mutex); + m_aEventListeners.addInterface( aGuard, xListener ); +} + +void ImageManagerImpl::removeEventListener( const uno::Reference< XEventListener >& xListener ) +{ + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + std::unique_lock aGuard(m_mutex); + m_aEventListeners.removeInterface( aGuard, xListener ); +} + +// XInitialization +void ImageManagerImpl::initialize( const Sequence< Any >& aArguments ) +{ + SolarMutexGuard g; + + if ( m_bInitialized ) + return; + + for ( const Any& rArg : aArguments ) + { + PropertyValue aPropValue; + if ( rArg >>= aPropValue ) + { + if ( aPropValue.Name == "UserConfigStorage" ) + { + aPropValue.Value >>= m_xUserConfigStorage; + } + else if ( aPropValue.Name == "ModuleIdentifier" ) + { + aPropValue.Value >>= m_aModuleIdentifier; + } + else if ( aPropValue.Name == "UserRootCommit" ) + { + aPropValue.Value >>= m_xUserRootCommit; + } + } + } + + if ( m_xUserConfigStorage.is() ) + { + uno::Reference< XPropertySet > xPropSet( m_xUserConfigStorage, UNO_QUERY ); + if ( xPropSet.is() ) + { + tools::Long nOpenMode = 0; + if ( xPropSet->getPropertyValue("OpenMode") >>= nOpenMode ) + m_bReadOnly = !( nOpenMode & ElementModes::WRITE ); + } + } + + implts_initialize(); + + m_bInitialized = true; +} + +// XImageManagerImpl +void ImageManagerImpl::reset() +{ + SolarMutexGuard g; + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + if ( m_bDisposed ) + throw DisposedException(); + + std::vector< OUString > aUserImageNames; + + for ( vcl::ImageType i : o3tl::enumrange() ) + { + aUserImageNames.clear(); + ImageList* pImageList = implts_getUserImageList(i); + pImageList->GetImageNames( aUserImageNames ); + + Sequence< OUString > aRemoveList( comphelper::containerToSequence(aUserImageNames) ); + + // Remove images + removeImages( sal_Int16( i ), aRemoveList ); + m_bUserImageListModified[i] = true; + } + + m_bModified = true; +} + +Sequence< OUString > ImageManagerImpl::getAllImageNames( ::sal_Int16 nImageType ) +{ + SolarMutexGuard g; + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + if ( m_bDisposed ) + throw DisposedException(); + + std::unordered_set< OUString > aImageCmdNames; + + vcl::ImageType nIndex = implts_convertImageTypeToIndex( nImageType ); + + sal_uInt32 i( 0 ); + if ( m_bUseGlobal ) + { + rtl::Reference< GlobalImageList > rGlobalImageList = implts_getGlobalImageList(); + + const std::vector< OUString >& rGlobalImageNameVector = rGlobalImageList->getImageCommandNames(); + const sal_uInt32 nGlobalCount = rGlobalImageNameVector.size(); + for ( i = 0; i < nGlobalCount; i++ ) + aImageCmdNames.insert( rGlobalImageNameVector[i] ); + + const std::vector< OUString >& rModuleImageNameVector = implts_getDefaultImageList()->getImageCommandNames(); + const sal_uInt32 nModuleCount = rModuleImageNameVector.size(); + for ( i = 0; i < nModuleCount; i++ ) + aImageCmdNames.insert( rModuleImageNameVector[i] ); + } + + ImageList* pImageList = implts_getUserImageList(nIndex); + std::vector< OUString > rUserImageNames; + pImageList->GetImageNames( rUserImageNames ); + const sal_uInt32 nUserCount = rUserImageNames.size(); + for ( i = 0; i < nUserCount; i++ ) + aImageCmdNames.insert( rUserImageNames[i] ); + + return comphelper::containerToSequence( aImageCmdNames ); +} + +bool ImageManagerImpl::hasImage( ::sal_Int16 nImageType, const OUString& aCommandURL ) +{ + SolarMutexGuard g; + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + if ( m_bDisposed ) + throw DisposedException(); + + if (( nImageType < 0 ) || ( nImageType > MAX_IMAGETYPE_VALUE )) + throw IllegalArgumentException(); + + vcl::ImageType nIndex = implts_convertImageTypeToIndex( nImageType ); + if ( m_bUseGlobal && implts_getGlobalImageList()->hasImage( nIndex, aCommandURL )) + return true; + else + { + if ( m_bUseGlobal && implts_getDefaultImageList()->hasImage( nIndex, aCommandURL )) + return true; + else + { + // User layer + ImageList* pImageList = implts_getUserImageList(nIndex); + if ( pImageList ) + return ( pImageList->GetImagePos( aCommandURL ) != IMAGELIST_IMAGE_NOTFOUND ); + } + } + + return false; +} + +namespace +{ + css::uno::Reference< css::graphic::XGraphic > GetXGraphic(const Image &rImage) + { + return Graphic(rImage).GetXGraphic(); + } +} + +Sequence< uno::Reference< XGraphic > > ImageManagerImpl::getImages( + ::sal_Int16 nImageType, + const Sequence< OUString >& aCommandURLSequence ) +{ + SolarMutexGuard g; + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + if ( m_bDisposed ) + throw DisposedException(); + + if (( nImageType < 0 ) || ( nImageType > MAX_IMAGETYPE_VALUE )) + throw IllegalArgumentException(); + + Sequence< uno::Reference< XGraphic > > aGraphSeq( aCommandURLSequence.getLength() ); + + vcl::ImageType nIndex = implts_convertImageTypeToIndex( nImageType ); + rtl::Reference< GlobalImageList > rGlobalImageList; + CmdImageList* pDefaultImageList = nullptr; + if ( m_bUseGlobal ) + { + rGlobalImageList = implts_getGlobalImageList(); + pDefaultImageList = implts_getDefaultImageList(); + } + ImageList* pUserImageList = implts_getUserImageList(nIndex); + + // We have to search our image list in the following order: + // 1. user image list (read/write) + // 2. module image list (read) + // 3. global image list (read) + auto aGraphSeqRange = asNonConstRange(aGraphSeq); + sal_Int32 n = 0; + for ( const OUString& rURL : aCommandURLSequence ) + { + Image aImage = pUserImageList->GetImage( rURL ); + if ( !aImage && m_bUseGlobal ) + { + aImage = pDefaultImageList->getImageFromCommandURL( nIndex, rURL ); + if ( !aImage ) + aImage = rGlobalImageList->getImageFromCommandURL( nIndex, rURL ); + } + + aGraphSeqRange[n++] = GetXGraphic(aImage); + } + + return aGraphSeq; +} + +void ImageManagerImpl::replaceImages( + ::sal_Int16 nImageType, + const Sequence< OUString >& aCommandURLSequence, + const Sequence< uno::Reference< XGraphic > >& aGraphicsSequence ) +{ + rtl::Reference pInsertedImages; + rtl::Reference pReplacedImages; + + { + SolarMutexGuard g; + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + if ( m_bDisposed ) + throw DisposedException(); + + if (( aCommandURLSequence.getLength() != aGraphicsSequence.getLength() ) || + (( nImageType < 0 ) || ( nImageType > MAX_IMAGETYPE_VALUE ))) + throw IllegalArgumentException(); + + if ( m_bReadOnly ) + throw IllegalAccessException(); + + vcl::ImageType nIndex = implts_convertImageTypeToIndex( nImageType ); + ImageList* pImageList = implts_getUserImageList(nIndex); + + uno::Reference< XGraphic > xGraphic; + for ( sal_Int32 i = 0; i < aCommandURLSequence.getLength(); i++ ) + { + // Check size and scale. If we don't have any graphics ignore it + if ( !implts_checkAndScaleGraphic( xGraphic, aGraphicsSequence[i], nIndex )) + continue; + + sal_uInt16 nPos = pImageList->GetImagePos( aCommandURLSequence[i] ); + if ( nPos == IMAGELIST_IMAGE_NOTFOUND ) + { + pImageList->AddImage(aCommandURLSequence[i], Image(xGraphic)); + if ( !pInsertedImages ) + pInsertedImages = new GraphicNameAccess(); + pInsertedImages->addElement( aCommandURLSequence[i], xGraphic ); + } + else + { + pImageList->ReplaceImage(aCommandURLSequence[i], Image(xGraphic)); + if ( !pReplacedImages ) + pReplacedImages = new GraphicNameAccess(); + pReplacedImages->addElement( aCommandURLSequence[i], xGraphic ); + } + } + + if (( pInsertedImages != nullptr ) || ( pReplacedImages != nullptr )) + { + m_bModified = true; + m_bUserImageListModified[nIndex] = true; + } + } + + uno::Reference< uno::XInterface > xOwner(m_pOwner); + // Notify listeners + if ( pInsertedImages != nullptr ) + { + ConfigurationEvent aInsertEvent; + aInsertEvent.aInfo <<= nImageType; + aInsertEvent.Accessor <<= xOwner; + aInsertEvent.Source = xOwner; + aInsertEvent.ResourceURL = m_aResourceString; + aInsertEvent.Element <<= uno::Reference< XNameAccess >(pInsertedImages); + implts_notifyContainerListener( aInsertEvent, NotifyOp_Insert ); + } + if ( pReplacedImages != nullptr ) + { + ConfigurationEvent aReplaceEvent; + aReplaceEvent.aInfo <<= nImageType; + aReplaceEvent.Accessor <<= xOwner; + aReplaceEvent.Source = xOwner; + aReplaceEvent.ResourceURL = m_aResourceString; + aReplaceEvent.ReplacedElement = Any(); + aReplaceEvent.Element <<= uno::Reference< XNameAccess >(pReplacedImages); + implts_notifyContainerListener( aReplaceEvent, NotifyOp_Replace ); + } +} + +void ImageManagerImpl::removeImages( ::sal_Int16 nImageType, const Sequence< OUString >& aCommandURLSequence ) +{ + rtl::Reference pRemovedImages; + rtl::Reference pReplacedImages; + + { + SolarMutexGuard g; + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + if ( m_bDisposed ) + throw DisposedException(); + + if (( nImageType < 0 ) || ( nImageType > MAX_IMAGETYPE_VALUE )) + throw IllegalArgumentException(); + + if ( m_bReadOnly ) + throw IllegalAccessException(); + + vcl::ImageType nIndex = implts_convertImageTypeToIndex( nImageType ); + rtl::Reference< GlobalImageList > rGlobalImageList; + CmdImageList* pDefaultImageList = nullptr; + if ( m_bUseGlobal ) + { + rGlobalImageList = implts_getGlobalImageList(); + pDefaultImageList = implts_getDefaultImageList(); + } + ImageList* pImageList = implts_getUserImageList(nIndex); + uno::Reference xEmptyGraphic; + + for ( const OUString& rURL : aCommandURLSequence ) + { + sal_uInt16 nPos = pImageList->GetImagePos( rURL ); + if ( nPos != IMAGELIST_IMAGE_NOTFOUND ) + { + sal_uInt16 nId = pImageList->GetImageId( nPos ); + pImageList->RemoveImage( nId ); + + if ( m_bUseGlobal ) + { + // Check, if we have an image in our module/global image list. If we find one => + // this is a replace instead of a remove operation! + Image aNewImage = pDefaultImageList->getImageFromCommandURL( nIndex, rURL ); + if ( !aNewImage ) + aNewImage = rGlobalImageList->getImageFromCommandURL( nIndex, rURL ); + if ( !aNewImage ) + { + if ( !pRemovedImages ) + pRemovedImages = new GraphicNameAccess(); + pRemovedImages->addElement( rURL, xEmptyGraphic ); + } + else + { + if ( !pReplacedImages ) + pReplacedImages = new GraphicNameAccess(); + pReplacedImages->addElement(rURL, GetXGraphic(aNewImage)); + } + } // if ( m_bUseGlobal ) + else + { + if ( !pRemovedImages ) + pRemovedImages = new GraphicNameAccess(); + pRemovedImages->addElement( rURL, xEmptyGraphic ); + } + } + } + + if (( pReplacedImages != nullptr ) || ( pRemovedImages != nullptr )) + { + m_bModified = true; + m_bUserImageListModified[nIndex] = true; + } + } + + // Notify listeners + uno::Reference< uno::XInterface > xOwner(m_pOwner); + if ( pRemovedImages != nullptr ) + { + ConfigurationEvent aRemoveEvent; + aRemoveEvent.aInfo <<= nImageType; + aRemoveEvent.Accessor <<= xOwner; + aRemoveEvent.Source = xOwner; + aRemoveEvent.ResourceURL = m_aResourceString; + aRemoveEvent.Element <<= uno::Reference< XNameAccess >(pRemovedImages); + implts_notifyContainerListener( aRemoveEvent, NotifyOp_Remove ); + } + if ( pReplacedImages != nullptr ) + { + ConfigurationEvent aReplaceEvent; + aReplaceEvent.aInfo <<= nImageType; + aReplaceEvent.Accessor <<= xOwner; + aReplaceEvent.Source = xOwner; + aReplaceEvent.ResourceURL = m_aResourceString; + aReplaceEvent.ReplacedElement = Any(); + aReplaceEvent.Element <<= uno::Reference< XNameAccess >(pReplacedImages); + implts_notifyContainerListener( aReplaceEvent, NotifyOp_Replace ); + } +} + +void ImageManagerImpl::insertImages( ::sal_Int16 nImageType, const Sequence< OUString >& aCommandURLSequence, const Sequence< uno::Reference< XGraphic > >& aGraphicSequence ) +{ + replaceImages(nImageType,aCommandURLSequence,aGraphicSequence); +} + +// XUIConfigurationPersistence +void ImageManagerImpl::reload() +{ + SolarMutexResettableGuard aGuard; + + if ( m_bDisposed ) + throw DisposedException(); + + CommandMap aOldUserCmdImageSet; + std::vector< OUString > aNewUserCmdImageSet; + + if ( !m_bModified ) + return; + + for ( vcl::ImageType i : o3tl::enumrange() ) + { + if ( !m_bDisposed && m_bUserImageListModified[i] ) + { + std::vector< OUString > aOldUserCmdImageVector; + ImageList* pImageList = implts_getUserImageList(i); + pImageList->GetImageNames( aOldUserCmdImageVector ); + + // Fill hash map to speed up search afterwards + sal_uInt32 j( 0 ); + const sal_uInt32 nOldCount = aOldUserCmdImageVector.size(); + for ( j = 0; j < nOldCount; j++ ) + aOldUserCmdImageSet.emplace( aOldUserCmdImageVector[j], false ); + + // Attention: This can make the old image list pointer invalid! + implts_loadUserImages( i, m_xUserImageStorage, m_xUserBitmapsStorage ); + pImageList = implts_getUserImageList(i); + pImageList->GetImageNames( aNewUserCmdImageSet ); + + rtl::Reference pInsertedImages; + rtl::Reference pReplacedImages; + rtl::Reference pRemovedImages; + + for (auto const& newUserCmdImage : aNewUserCmdImageSet) + { + CommandMap::iterator pIter = aOldUserCmdImageSet.find(newUserCmdImage); + if ( pIter != aOldUserCmdImageSet.end() ) + { + pIter->second = true; // mark entry as replaced + if ( !pReplacedImages ) + pReplacedImages = new GraphicNameAccess(); + pReplacedImages->addElement( newUserCmdImage, + GetXGraphic(pImageList->GetImage(newUserCmdImage)) ); + } + else + { + if ( !pInsertedImages ) + pInsertedImages = new GraphicNameAccess(); + pInsertedImages->addElement( newUserCmdImage, + GetXGraphic(pImageList->GetImage(newUserCmdImage)) ); + } + } + + // Search map for unmarked entries => they have been removed from the user list + // through this reload operation. + // We have to search the module and global image list! + rtl::Reference< GlobalImageList > rGlobalImageList; + CmdImageList* pDefaultImageList = nullptr; + if ( m_bUseGlobal ) + { + rGlobalImageList = implts_getGlobalImageList(); + pDefaultImageList = implts_getDefaultImageList(); + } + uno::Reference xEmptyGraphic; + for (auto const& oldUserCmdImage : aOldUserCmdImageSet) + { + if ( !oldUserCmdImage.second ) + { + if ( m_bUseGlobal ) + { + Image aImage = pDefaultImageList->getImageFromCommandURL( i, oldUserCmdImage.first ); + if ( !aImage ) + aImage = rGlobalImageList->getImageFromCommandURL( i, oldUserCmdImage.first ); + + if ( !aImage ) + { + // No image in the module/global image list => remove user image + if ( !pRemovedImages ) + pRemovedImages = new GraphicNameAccess(); + pRemovedImages->addElement( oldUserCmdImage.first, xEmptyGraphic ); + } + else + { + // Image has been found in the module/global image list => replace user image + if ( !pReplacedImages ) + pReplacedImages = new GraphicNameAccess(); + pReplacedImages->addElement(oldUserCmdImage.first, GetXGraphic(aImage)); + } + } // if ( m_bUseGlobal ) + else + { + // No image in the user image list => remove user image + if ( !pRemovedImages ) + pRemovedImages = new GraphicNameAccess(); + pRemovedImages->addElement( oldUserCmdImage.first, xEmptyGraphic ); + } + } + } + + aGuard.clear(); + + // Now notify our listeners. Unlock mutex to prevent deadlocks + uno::Reference< uno::XInterface > xOwner(m_pOwner); + if ( pInsertedImages != nullptr ) + { + ConfigurationEvent aInsertEvent; + aInsertEvent.aInfo <<=static_cast(i); + aInsertEvent.Accessor <<= xOwner; + aInsertEvent.Source = xOwner; + aInsertEvent.ResourceURL = m_aResourceString; + aInsertEvent.Element <<= uno::Reference< XNameAccess >( pInsertedImages ); + implts_notifyContainerListener( aInsertEvent, NotifyOp_Insert ); + } + if ( pReplacedImages != nullptr ) + { + ConfigurationEvent aReplaceEvent; + aReplaceEvent.aInfo <<= static_cast(i); + aReplaceEvent.Accessor <<= xOwner; + aReplaceEvent.Source = xOwner; + aReplaceEvent.ResourceURL = m_aResourceString; + aReplaceEvent.ReplacedElement = Any(); + aReplaceEvent.Element <<= uno::Reference< XNameAccess >( pReplacedImages ); + implts_notifyContainerListener( aReplaceEvent, NotifyOp_Replace ); + } + if ( pRemovedImages != nullptr ) + { + ConfigurationEvent aRemoveEvent; + aRemoveEvent.aInfo <<= static_cast(i); + aRemoveEvent.Accessor <<= xOwner; + aRemoveEvent.Source = xOwner; + aRemoveEvent.ResourceURL = m_aResourceString; + aRemoveEvent.Element <<= uno::Reference< XNameAccess >( pRemovedImages ); + implts_notifyContainerListener( aRemoveEvent, NotifyOp_Remove ); + } + + aGuard.reset(); + } + } +} + +void ImageManagerImpl::store() +{ + SolarMutexGuard g; + + if ( m_bDisposed ) + throw DisposedException(); + + if ( !m_bModified ) + return; + + bool bWritten( false ); + for ( vcl::ImageType i : o3tl::enumrange() ) + { + bool bSuccess = implts_storeUserImages(i, m_xUserImageStorage, m_xUserBitmapsStorage ); + if ( bSuccess ) + bWritten = true; + m_bUserImageListModified[i] = false; + } + + if ( bWritten && + m_xUserConfigStorage.is() ) + { + uno::Reference< XTransactedObject > xUserConfigStorageCommit( m_xUserConfigStorage, UNO_QUERY ); + if ( xUserConfigStorageCommit.is() ) + xUserConfigStorageCommit->commit(); + if ( m_xUserRootCommit.is() ) + m_xUserRootCommit->commit(); + } + + m_bModified = false; +} + +void ImageManagerImpl::storeToStorage( const uno::Reference< XStorage >& Storage ) +{ + SolarMutexGuard g; + + if ( m_bDisposed ) + throw DisposedException(); + + if ( !(m_bModified && Storage.is()) ) + return; + + tools::Long nModes = ElementModes::READWRITE; + + uno::Reference< XStorage > xUserImageStorage = Storage->openStorageElement( IMAGE_FOLDER, + nModes ); + if ( !xUserImageStorage.is() ) + return; + + uno::Reference< XStorage > xUserBitmapsStorage = xUserImageStorage->openStorageElement( BITMAPS_FOLDER, + nModes ); + for ( vcl::ImageType i : o3tl::enumrange() ) + { + implts_getUserImageList(i); + implts_storeUserImages( i, xUserImageStorage, xUserBitmapsStorage ); + } + + uno::Reference< XTransactedObject > xTransaction( Storage, UNO_QUERY ); + if ( xTransaction.is() ) + xTransaction->commit(); +} + +bool ImageManagerImpl::isModified() const +{ + SolarMutexGuard g; + return m_bModified; +} + +bool ImageManagerImpl::isReadOnly() const +{ + SolarMutexGuard g; + return m_bReadOnly; +} +// XUIConfiguration +void ImageManagerImpl::addConfigurationListener( const uno::Reference< css::ui::XUIConfigurationListener >& xListener ) +{ + { + SolarMutexGuard g; + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + if ( m_bDisposed ) + throw DisposedException(); + } + + std::unique_lock aGuard(m_mutex); + m_aConfigListeners.addInterface( aGuard, xListener ); +} + +void ImageManagerImpl::removeConfigurationListener( const uno::Reference< css::ui::XUIConfigurationListener >& xListener ) +{ + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + std::unique_lock aGuard(m_mutex); + m_aConfigListeners.removeInterface( aGuard, xListener ); +} + +void ImageManagerImpl::implts_notifyContainerListener( const ConfigurationEvent& aEvent, NotifyOp eOp ) +{ + std::unique_lock aGuard(m_mutex); + comphelper::OInterfaceIteratorHelper4 pIterator( aGuard, m_aConfigListeners ); + aGuard.unlock(); + while ( pIterator.hasMoreElements() ) + { + try + { + switch ( eOp ) + { + case NotifyOp_Replace: + pIterator.next()->elementReplaced( aEvent ); + break; + case NotifyOp_Insert: + pIterator.next()->elementInserted( aEvent ); + break; + case NotifyOp_Remove: + pIterator.next()->elementRemoved( aEvent ); + break; + } + } + catch( const css::uno::RuntimeException& ) + { + aGuard.lock(); + pIterator.remove(aGuard); + aGuard.unlock(); + } + } +} +void ImageManagerImpl::clear() +{ + SolarMutexGuard g; + + for (auto & n : m_pUserImageList) + { + n.reset(); + } +} +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uiconfiguration/imagemanagerimpl.hxx b/framework/source/uiconfiguration/imagemanagerimpl.hxx new file mode 100644 index 0000000000..4d48da1c23 --- /dev/null +++ b/framework/source/uiconfiguration/imagemanagerimpl.hxx @@ -0,0 +1,182 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include "CommandImageResolver.hxx" + +namespace framework +{ + class CmdImageList + { + public: + CmdImageList(css::uno::Reference< css::uno::XComponentContext > xContext, OUString aModuleIdentifier); + virtual ~CmdImageList(); + + virtual Image getImageFromCommandURL(vcl::ImageType nImageType, const OUString& rCommandURL); + virtual bool hasImage(vcl::ImageType nImageType, const OUString& rCommandURL); + virtual std::vector& getImageCommandNames(); + + protected: + void initialize(); + + private: + bool m_bInitialized; + vcl::CommandImageResolver m_aResolver; + + OUString m_aModuleIdentifier; + css::uno::Reference m_xContext; + }; + + class GlobalImageList : public CmdImageList, public salhelper::SimpleReferenceObject + { + public: + explicit GlobalImageList(const css::uno::Reference< css::uno::XComponentContext >& rxContext); + virtual ~GlobalImageList() override; + + virtual Image getImageFromCommandURL( vcl::ImageType nImageType, const OUString& rCommandURL ) override; + virtual bool hasImage( vcl::ImageType nImageType, const OUString& rCommandURL ) override; + virtual ::std::vector< OUString >& getImageCommandNames() override; + }; + + class ImageManagerImpl + { + public: + ImageManagerImpl(css::uno::Reference< css::uno::XComponentContext > xContext + ,::cppu::OWeakObject *pOwner + ,bool _bUseGlobal); + ~ImageManagerImpl(); + + void dispose(); + void initialize( const css::uno::Sequence< css::uno::Any >& aArguments ); + /// @throws css::uno::RuntimeException + void addEventListener( const css::uno::Reference< css::lang::XEventListener >& xListener ); + /// @throws css::uno::RuntimeException + void removeEventListener( const css::uno::Reference< css::lang::XEventListener >& aListener ); + + // XImageManager + /// @throws css::uno::RuntimeException + /// @throws css::lang::IllegalAccessException + void reset(); + /// @throws css::uno::RuntimeException + css::uno::Sequence< OUString > getAllImageNames( ::sal_Int16 nImageType ); + /// @throws css::lang::IllegalArgumentException + /// @throws css::uno::RuntimeException + bool hasImage( ::sal_Int16 nImageType, const OUString& aCommandURL ); + /// @throws css::lang::IllegalArgumentException + /// @throws css::uno::RuntimeException + css::uno::Sequence< css::uno::Reference< css::graphic::XGraphic > > getImages( ::sal_Int16 nImageType, const css::uno::Sequence< OUString >& aCommandURLSequence ); + /// @throws css::lang::IllegalArgumentException + /// @throws css::lang::IllegalAccessException + /// @throws css::uno::RuntimeException + void replaceImages( ::sal_Int16 nImageType, const css::uno::Sequence< OUString >& aCommandURLSequence, const css::uno::Sequence< css::uno::Reference< css::graphic::XGraphic > >& aGraphicsSequence ); + /// @throws css::lang::IllegalArgumentException + /// @throws css::lang::IllegalAccessException + /// @throws css::uno::RuntimeException + void removeImages( ::sal_Int16 nImageType, const css::uno::Sequence< OUString >& aResourceURLSequence ); + /// @throws css::container::ElementExistException + /// @throws css::lang::IllegalArgumentException + /// @throws css::lang::IllegalAccessException + /// @throws css::uno::RuntimeException + void insertImages( ::sal_Int16 nImageType, const css::uno::Sequence< OUString >& aCommandURLSequence, const css::uno::Sequence< css::uno::Reference< css::graphic::XGraphic > >& aGraphicSequence ); + + // XUIConfiguration + /// @throws css::uno::RuntimeException + void addConfigurationListener( const css::uno::Reference< css::ui::XUIConfigurationListener >& Listener ); + /// @throws css::uno::RuntimeException + void removeConfigurationListener( const css::uno::Reference< css::ui::XUIConfigurationListener >& Listener ); + + // XUIConfigurationPersistence + /// @throws css::uno::Exception + /// @throws css::uno::RuntimeException + void reload(); + /// @throws css::uno::Exception + /// @throws css::uno::RuntimeException + void store(); + /// @throws css::uno::Exception + /// @throws css::uno::RuntimeException + void storeToStorage( const css::uno::Reference< css::embed::XStorage >& Storage ); + /// @throws css::uno::RuntimeException + bool isModified() const; + /// @throws css::uno::RuntimeException + bool isReadOnly() const; + + void clear(); + + enum NotifyOp + { + NotifyOp_Remove, + NotifyOp_Insert, + NotifyOp_Replace + }; + + void implts_initialize(); + void implts_notifyContainerListener( const css::ui::ConfigurationEvent& aEvent, NotifyOp eOp ); + ImageList* implts_getUserImageList( vcl::ImageType nImageType ); + void implts_loadUserImages( vcl::ImageType nImageType, + const css::uno::Reference< css::embed::XStorage >& xUserImageStorage, + const css::uno::Reference< css::embed::XStorage >& xUserBitmapsStorage ); + bool implts_storeUserImages( vcl::ImageType nImageType, + const css::uno::Reference< css::embed::XStorage >& xUserImageStorage, + const css::uno::Reference< css::embed::XStorage >& xUserBitmapsStorage ); + const rtl::Reference< GlobalImageList >& implts_getGlobalImageList(); + CmdImageList* implts_getDefaultImageList(); + + css::uno::Reference< css::embed::XStorage > m_xUserConfigStorage; + css::uno::Reference< css::embed::XStorage > m_xUserImageStorage; + css::uno::Reference< css::embed::XStorage > m_xUserBitmapsStorage; + css::uno::Reference< css::embed::XTransactedObject > m_xUserRootCommit; + css::uno::Reference< css::uno::XComponentContext > m_xContext; + ::cppu::OWeakObject* m_pOwner; + rtl::Reference< GlobalImageList > m_pGlobalImageList; + std::unique_ptr m_pDefaultImageList; + OUString m_aModuleIdentifier; + OUString m_aResourceString; + std::mutex m_mutex; + comphelper::OInterfaceContainerHelper4 m_aEventListeners; + comphelper::OInterfaceContainerHelper4 m_aConfigListeners; + o3tl::enumarray> m_pUserImageList; + o3tl::enumarray m_bUserImageListModified; + bool m_bUseGlobal; + bool m_bReadOnly; + bool m_bInitialized; + bool m_bModified; + bool m_bDisposed; + }; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uiconfiguration/moduleuicfgsupplier.cxx b/framework/source/uiconfiguration/moduleuicfgsupplier.cxx new file mode 100644 index 0000000000..cdbd647c31 --- /dev/null +++ b/framework/source/uiconfiguration/moduleuicfgsupplier.cxx @@ -0,0 +1,180 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +using namespace com::sun::star::uno; +using namespace com::sun::star::io; +using namespace com::sun::star::lang; +using namespace com::sun::star::container; +using namespace com::sun::star::beans; +using namespace com::sun::star::embed; +using namespace ::com::sun::star::ui; +using namespace ::com::sun::star::frame; +using namespace framework; + +namespace { + +typedef comphelper::WeakComponentImplHelper< + css::lang::XServiceInfo, + css::ui::XModuleUIConfigurationManagerSupplier > + ModuleUIConfigurationManagerSupplier_BASE; + +class ModuleUIConfigurationManagerSupplier : public ModuleUIConfigurationManagerSupplier_BASE +{ +public: + explicit ModuleUIConfigurationManagerSupplier( const css::uno::Reference< css::uno::XComponentContext >& rxContext ); + virtual ~ModuleUIConfigurationManagerSupplier() override; + + virtual OUString SAL_CALL getImplementationName() override + { + return "com.sun.star.comp.framework.ModuleUIConfigurationManagerSupplier"; + } + + virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override + { + return cppu::supportsService(this, ServiceName); + } + + virtual css::uno::Sequence SAL_CALL getSupportedServiceNames() override + { + return {"com.sun.star.ui.ModuleUIConfigurationManagerSupplier"}; + } + + // XModuleUIConfigurationManagerSupplier + virtual css::uno::Reference< css::ui::XUIConfigurationManager > SAL_CALL getUIConfigurationManager( const OUString& ModuleIdentifier ) override; + +private: + virtual void disposing(std::unique_lock&) final override; + + typedef std::unordered_map< OUString, css::uno::Reference< css::ui::XModuleUIConfigurationManager2 > > ModuleToModuleCfgMgr; + +//TODO_AS void impl_initStorages(); + + ModuleToModuleCfgMgr m_aModuleToModuleUICfgMgrMap; + css::uno::Reference< css::frame::XModuleManager2 > m_xModuleMgr; + css::uno::Reference< css::uno::XComponentContext > m_xContext; +}; + +ModuleUIConfigurationManagerSupplier::ModuleUIConfigurationManagerSupplier( const Reference< XComponentContext >& xContext ) : + m_xModuleMgr( ModuleManager::create( xContext ) ) + , m_xContext( xContext ) +{ + try + { + // Retrieve known modules and insert them into our unordered_map to speed-up access time. + Reference< XNameAccess > xNameAccess( m_xModuleMgr, UNO_QUERY_THROW ); + const Sequence< OUString > aNameSeq = xNameAccess->getElementNames(); + for ( const OUString& rName : aNameSeq ) + m_aModuleToModuleUICfgMgrMap.emplace( rName, Reference< XModuleUIConfigurationManager2 >() ); + } + catch(...) + { + } +} + +ModuleUIConfigurationManagerSupplier::~ModuleUIConfigurationManagerSupplier() +{ + std::unique_lock g(m_aMutex); + disposing(g); +} + +void ModuleUIConfigurationManagerSupplier::disposing(std::unique_lock&) +{ + // dispose all our module user interface configuration managers + for (auto const& elem : m_aModuleToModuleUICfgMgrMap) + { + Reference< XComponent > xComponent( elem.second, UNO_QUERY ); + if ( xComponent.is() ) + xComponent->dispose(); + } + m_aModuleToModuleUICfgMgrMap.clear(); + m_xModuleMgr.clear(); +} + +// XModuleUIConfigurationManagerSupplier +Reference< XUIConfigurationManager > SAL_CALL ModuleUIConfigurationManagerSupplier::getUIConfigurationManager( const OUString& sModuleIdentifier ) +{ + std::unique_lock g(m_aMutex); + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + + ModuleToModuleCfgMgr::iterator pIter = m_aModuleToModuleUICfgMgrMap.find( sModuleIdentifier ); + if ( pIter == m_aModuleToModuleUICfgMgrMap.end() ) + throw NoSuchElementException(); +//TODO_AS impl_initStorages(); + + // Create instance on demand + if ( !pIter->second.is() ) + { + OUString sShort; + try + { + Sequence< PropertyValue > lProps; + m_xModuleMgr->getByName(sModuleIdentifier) >>= lProps; + for (PropertyValue const & rProp : std::as_const(lProps)) + { + if ( rProp.Name == "ooSetupFactoryShortName" ) + { + rProp.Value >>= sShort; + break; + } + } + } + catch( const Exception& ) + { + sShort.clear(); + } + + if (sShort.isEmpty()) + throw NoSuchElementException(); + + pIter->second = css::ui::ModuleUIConfigurationManager::createDefault(m_xContext, sShort, sModuleIdentifier); + } + + return pIter->second; +} + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_framework_ModuleUIConfigurationManagerSupplier_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence const &) +{ + return cppu::acquire(new ModuleUIConfigurationManagerSupplier(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uiconfiguration/moduleuiconfigurationmanager.cxx b/framework/source/uiconfiguration/moduleuiconfigurationmanager.cxx new file mode 100644 index 0000000000..65e12c8f8d --- /dev/null +++ b/framework/source/uiconfiguration/moduleuiconfigurationmanager.cxx @@ -0,0 +1,1660 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace css; +using namespace com::sun::star::uno; +using namespace com::sun::star::io; +using namespace com::sun::star::embed; +using namespace com::sun::star::lang; +using namespace com::sun::star::container; +using namespace com::sun::star::beans; +using namespace framework; + +constexpr OUStringLiteral RESOURCETYPE_MENUBAR = u"menubar"; +constexpr OUStringLiteral RESOURCETYPE_TOOLBAR = u"toolbar"; +constexpr OUStringLiteral RESOURCETYPE_STATUSBAR = u"statusbar"; +constexpr OUStringLiteral RESOURCETYPE_POPUPMENU = u"popupmenu"; + +namespace { + +class ModuleUIConfigurationManager : public cppu::WeakImplHelper< + css::lang::XServiceInfo, + css::lang::XComponent, + css::ui::XModuleUIConfigurationManager2 > +{ +public: + ModuleUIConfigurationManager( + const css::uno::Reference< css::uno::XComponentContext >& xServiceManager, + const css::uno::Sequence< css::uno::Any >& aArguments); + + virtual OUString SAL_CALL getImplementationName() override + { + return "com.sun.star.comp.framework.ModuleUIConfigurationManager"; + } + + virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override + { + return cppu::supportsService(this, ServiceName); + } + + virtual css::uno::Sequence SAL_CALL getSupportedServiceNames() override + { + return {"com.sun.star.ui.ModuleUIConfigurationManager"}; + } + + // 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 >& aListener ) override; + + // XUIConfiguration + virtual void SAL_CALL addConfigurationListener( const css::uno::Reference< css::ui::XUIConfigurationListener >& Listener ) override; + virtual void SAL_CALL removeConfigurationListener( const css::uno::Reference< css::ui::XUIConfigurationListener >& Listener ) override; + + // XUIConfigurationManager + virtual void SAL_CALL reset() override; + virtual css::uno::Sequence< css::uno::Sequence< css::beans::PropertyValue > > SAL_CALL getUIElementsInfo( sal_Int16 ElementType ) override; + virtual css::uno::Reference< css::container::XIndexContainer > SAL_CALL createSettings( ) override; + virtual sal_Bool SAL_CALL hasSettings( const OUString& ResourceURL ) override; + virtual css::uno::Reference< css::container::XIndexAccess > SAL_CALL getSettings( const OUString& ResourceURL, sal_Bool bWriteable ) override; + virtual void SAL_CALL replaceSettings( const OUString& ResourceURL, const css::uno::Reference< css::container::XIndexAccess >& aNewData ) override; + virtual void SAL_CALL removeSettings( const OUString& ResourceURL ) override; + virtual void SAL_CALL insertSettings( const OUString& NewResourceURL, const css::uno::Reference< css::container::XIndexAccess >& aNewData ) override; + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL getImageManager() override; + virtual css::uno::Reference< css::ui::XAcceleratorConfiguration > SAL_CALL getShortCutManager() override; + virtual css::uno::Reference< css::ui::XAcceleratorConfiguration > SAL_CALL createShortCutManager() override; + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL getEventsManager() override; + + // XModuleUIConfigurationManager + virtual sal_Bool SAL_CALL isDefaultSettings( const OUString& ResourceURL ) override; + virtual css::uno::Reference< css::container::XIndexAccess > SAL_CALL getDefaultSettings( const OUString& ResourceURL ) override; + + // XUIConfigurationPersistence + virtual void SAL_CALL reload() override; + virtual void SAL_CALL store() override; + virtual void SAL_CALL storeToStorage( const css::uno::Reference< css::embed::XStorage >& Storage ) override; + virtual sal_Bool SAL_CALL isModified() override; + virtual sal_Bool SAL_CALL isReadOnly() override; + +private: + // private data types + enum Layer + { + LAYER_DEFAULT, + LAYER_USERDEFINED, + LAYER_COUNT + }; + + enum NotifyOp + { + NotifyOp_Remove, + NotifyOp_Insert, + NotifyOp_Replace + }; + + struct UIElementInfo + { + UIElementInfo( OUString _aResourceURL, OUString _aUIName ) : + aResourceURL(std::move( _aResourceURL)), aUIName(std::move( _aUIName )) {} + OUString aResourceURL; + OUString aUIName; + }; + + struct UIElementData + { + UIElementData() : bModified( false ), bDefault( true ), bDefaultNode( true ) {}; + + OUString aResourceURL; + OUString aName; + bool bModified; // has been changed since last storing + bool bDefault; // default settings + bool bDefaultNode; // this is a default layer element data + css::uno::Reference< css::container::XIndexAccess > xSettings; + }; + + typedef std::unordered_map< OUString, UIElementData > UIElementDataHashMap; + + struct UIElementType + { + UIElementType() : bModified( false ), + bLoaded( false ), + nElementType( css::ui::UIElementType::UNKNOWN ) {} + + bool bModified; + bool bLoaded; + sal_Int16 nElementType; + UIElementDataHashMap aElementsHashMap; + css::uno::Reference< css::embed::XStorage > xStorage; + }; + + typedef std::vector< UIElementType > UIElementTypesVector; + typedef std::vector< css::ui::ConfigurationEvent > ConfigEventNotifyContainer; + typedef std::unordered_map< OUString, UIElementInfo > UIElementInfoHashMap; + + void impl_Initialize(); + void implts_notifyContainerListener( const css::ui::ConfigurationEvent& aEvent, NotifyOp eOp ); + void impl_fillSequenceWithElementTypeInfo( UIElementInfoHashMap& aUIElementInfoCollection, sal_Int16 nElementType ); + void impl_preloadUIElementTypeList( Layer eLayer, sal_Int16 nElementType ); + UIElementData* impl_findUIElementData( const OUString& aResourceURL, sal_Int16 nElementType, bool bLoad = true ); + void impl_requestUIElementData( sal_Int16 nElementType, Layer eLayer, UIElementData& aUIElementData ); + void impl_storeElementTypeData( const css::uno::Reference< css::embed::XStorage >& xStorage, UIElementType& rElementType, bool bResetModifyState = true ); + void impl_resetElementTypeData( UIElementType& rUserElementType, UIElementType const & rDefaultElementType, ConfigEventNotifyContainer& rRemoveNotifyContainer, ConfigEventNotifyContainer& rReplaceNotifyContainer ); + void impl_reloadElementTypeData( UIElementType& rUserElementType, UIElementType const & rDefaultElementType, ConfigEventNotifyContainer& rRemoveNotifyContainer, ConfigEventNotifyContainer& rReplaceNotifyContainer ); + + UIElementTypesVector m_aUIElements[LAYER_COUNT]; + std::unique_ptr m_pStorageHandler[css::ui::UIElementType::COUNT]; + css::uno::Reference< css::embed::XStorage > m_xDefaultConfigStorage; + css::uno::Reference< css::embed::XStorage > m_xUserConfigStorage; + bool m_bReadOnly; + bool m_bModified; + bool m_bDisposed; + OUString m_aXMLPostfix; + OUString m_aPropUIName; + OUString m_aModuleIdentifier; + css::uno::Reference< css::embed::XTransactedObject > m_xUserRootCommit; + css::uno::Reference< css::uno::XComponentContext > m_xContext; + std::mutex m_mutex; + comphelper::OInterfaceContainerHelper4 m_aEventListeners; + comphelper::OInterfaceContainerHelper4 m_aConfigListeners; + rtl::Reference< ImageManager > m_xModuleImageManager; + css::uno::Reference< css::ui::XAcceleratorConfiguration > m_xModuleAcceleratorManager; +}; + +// important: The order and position of the elements must match the constant +// definition of "css::ui::UIElementType" +std::u16string_view UIELEMENTTYPENAMES[] = +{ + u"", // Dummy value for unknown! + u"" UIELEMENTTYPE_MENUBAR_NAME, + u"" UIELEMENTTYPE_POPUPMENU_NAME, + u"" UIELEMENTTYPE_TOOLBAR_NAME, + u"" UIELEMENTTYPE_STATUSBAR_NAME, + u"" UIELEMENTTYPE_FLOATINGWINDOW_NAME, + u"" UIELEMENTTYPE_PROGRESSBAR_NAME, + u"" UIELEMENTTYPE_TOOLPANEL_NAME +}; + +constexpr std::u16string_view RESOURCEURL_PREFIX = u"private:resource/"; + +sal_Int16 RetrieveTypeFromResourceURL( std::u16string_view aResourceURL ) +{ + + if (( o3tl::starts_with(aResourceURL, RESOURCEURL_PREFIX ) ) && + ( aResourceURL.size() > RESOURCEURL_PREFIX.size() )) + { + std::u16string_view aTmpStr = aResourceURL.substr( RESOURCEURL_PREFIX.size() ); + size_t nIndex = aTmpStr.find( '/' ); + if (( nIndex > 0 ) && ( aTmpStr.size() > nIndex )) + { + std::u16string_view aTypeStr( aTmpStr.substr( 0, nIndex )); + for ( int i = 0; i < ui::UIElementType::COUNT; i++ ) + { + if ( aTypeStr == UIELEMENTTYPENAMES[i] ) + return sal_Int16( i ); + } + } + } + + return ui::UIElementType::UNKNOWN; +} + +OUString RetrieveNameFromResourceURL( std::u16string_view aResourceURL ) +{ + if (( o3tl::starts_with(aResourceURL, RESOURCEURL_PREFIX ) ) && + ( aResourceURL.size() > RESOURCEURL_PREFIX.size() )) + { + size_t nIndex = aResourceURL.rfind( '/' ); + + if ( nIndex > 0 && nIndex != std::u16string_view::npos && (( nIndex+1 ) < aResourceURL.size()) ) + return OUString(aResourceURL.substr( nIndex+1 )); + } + + return OUString(); +} + +void ModuleUIConfigurationManager::impl_fillSequenceWithElementTypeInfo( UIElementInfoHashMap& aUIElementInfoCollection, sal_Int16 nElementType ) +{ + // preload list of element types on demand + impl_preloadUIElementTypeList( LAYER_USERDEFINED, nElementType ); + impl_preloadUIElementTypeList( LAYER_DEFAULT, nElementType ); + + UIElementDataHashMap& rUserElements = m_aUIElements[LAYER_USERDEFINED][nElementType].aElementsHashMap; + + OUString aCustomUrlPrefix( "custom_" ); + for (auto const& userElement : rUserElements) + { + sal_Int32 nIndex = userElement.second.aResourceURL.indexOf( aCustomUrlPrefix, RESOURCEURL_PREFIX.size() ); + if ( nIndex > static_cast(RESOURCEURL_PREFIX.size()) ) + { + // Performance: Retrieve user interface name only for custom user interface elements. + // It's only used by them! + UIElementData* pDataSettings = impl_findUIElementData( userElement.second.aResourceURL, nElementType ); + if ( pDataSettings ) + { + // Retrieve user interface name from XPropertySet interface + OUString aUIName; + Reference< XPropertySet > xPropSet( pDataSettings->xSettings, UNO_QUERY ); + if ( xPropSet.is() ) + { + Any a = xPropSet->getPropertyValue( m_aPropUIName ); + a >>= aUIName; + } + + UIElementInfo aInfo( userElement.second.aResourceURL, aUIName ); + aUIElementInfoCollection.emplace( userElement.second.aResourceURL, aInfo ); + } + } + else + { + // The user interface name for standard user interface elements is stored in the WindowState.xcu file + UIElementInfo aInfo( userElement.second.aResourceURL, OUString() ); + aUIElementInfoCollection.emplace( userElement.second.aResourceURL, aInfo ); + } + } + + UIElementDataHashMap& rDefaultElements = m_aUIElements[LAYER_DEFAULT][nElementType].aElementsHashMap; + + for (auto const& defaultElement : rDefaultElements) + { + UIElementInfoHashMap::const_iterator pIterInfo = aUIElementInfoCollection.find( defaultElement.second.aResourceURL ); + if ( pIterInfo == aUIElementInfoCollection.end() ) + { + sal_Int32 nIndex = defaultElement.second.aResourceURL.indexOf( aCustomUrlPrefix, RESOURCEURL_PREFIX.size() ); + if ( nIndex > static_cast(RESOURCEURL_PREFIX.size()) ) + { + // Performance: Retrieve user interface name only for custom user interface elements. + // It's only used by them! + UIElementData* pDataSettings = impl_findUIElementData( defaultElement.second.aResourceURL, nElementType ); + if ( pDataSettings ) + { + // Retrieve user interface name from XPropertySet interface + OUString aUIName; + Reference< XPropertySet > xPropSet( pDataSettings->xSettings, UNO_QUERY ); + if ( xPropSet.is() ) + { + Any a = xPropSet->getPropertyValue( m_aPropUIName ); + a >>= aUIName; + } + UIElementInfo aInfo( defaultElement.second.aResourceURL, aUIName ); + aUIElementInfoCollection.emplace( defaultElement.second.aResourceURL, aInfo ); + } + } + else + { + // The user interface name for standard user interface elements is stored in the WindowState.xcu file + UIElementInfo aInfo( defaultElement.second.aResourceURL, OUString() ); + aUIElementInfoCollection.emplace( defaultElement.second.aResourceURL, aInfo ); + } + } + } +} + +void ModuleUIConfigurationManager::impl_preloadUIElementTypeList( Layer eLayer, sal_Int16 nElementType ) +{ + UIElementType& rElementTypeData = m_aUIElements[eLayer][nElementType]; + + if ( rElementTypeData.bLoaded ) + return; + + Reference< XStorage > xElementTypeStorage = rElementTypeData.xStorage; + if ( !xElementTypeStorage.is() ) + return; + + OUString aResURLPrefix = + OUString::Concat(RESOURCEURL_PREFIX) + + UIELEMENTTYPENAMES[ nElementType ] + + "/"; + + UIElementDataHashMap& rHashMap = rElementTypeData.aElementsHashMap; + const Sequence< OUString > aUIElementNames = xElementTypeStorage->getElementNames(); + for ( OUString const & rElementName : aUIElementNames ) + { + UIElementData aUIElementData; + + // Resource name must be without ".xml" + sal_Int32 nIndex = rElementName.lastIndexOf( '.' ); + if (( nIndex > 0 ) && ( nIndex < rElementName.getLength() )) + { + std::u16string_view aExtension( rElementName.subView( nIndex+1 )); + std::u16string_view aUIElementName( rElementName.subView( 0, nIndex )); + + if (!aUIElementName.empty() && + ( o3tl::equalsIgnoreAsciiCase(aExtension, u"xml"))) + { + aUIElementData.aResourceURL = aResURLPrefix + aUIElementName; + aUIElementData.aName = rElementName; + + if ( eLayer == LAYER_USERDEFINED ) + { + aUIElementData.bModified = false; + aUIElementData.bDefault = false; + aUIElementData.bDefaultNode = false; + } + + // Create std::unordered_map entries for all user interface elements inside the storage. We don't load the + // settings to speed up the process. + rHashMap.emplace( aUIElementData.aResourceURL, aUIElementData ); + } + } + rElementTypeData.bLoaded = true; + } + +} + +void ModuleUIConfigurationManager::impl_requestUIElementData( sal_Int16 nElementType, Layer eLayer, UIElementData& aUIElementData ) +{ + UIElementType& rElementTypeData = m_aUIElements[eLayer][nElementType]; + + Reference< XStorage > xElementTypeStorage = rElementTypeData.xStorage; + if ( xElementTypeStorage.is() && !aUIElementData.aName.isEmpty() ) + { + try + { + Reference< XStream > xStream = xElementTypeStorage->openStreamElement( aUIElementData.aName, ElementModes::READ ); + Reference< XInputStream > xInputStream = xStream->getInputStream(); + + if ( xInputStream.is() ) + { + switch ( nElementType ) + { + case css::ui::UIElementType::UNKNOWN: + break; + + case css::ui::UIElementType::MENUBAR: + case css::ui::UIElementType::POPUPMENU: + { + try + { + MenuConfiguration aMenuCfg( m_xContext ); + Reference< XIndexAccess > xContainer( aMenuCfg.CreateMenuBarConfigurationFromXML( xInputStream )); + auto pRootItemContainer = dynamic_cast( xContainer.get() ); + if ( pRootItemContainer ) + aUIElementData.xSettings = new ConstItemContainer( pRootItemContainer, true ); + else + aUIElementData.xSettings = new ConstItemContainer( xContainer, true ); + return; + } + catch ( const css::lang::WrappedTargetException& ) + { + } + } + break; + + case css::ui::UIElementType::TOOLBAR: + { + try + { + Reference< XIndexContainer > xIndexContainer( new RootItemContainer() ); + ToolBoxConfiguration::LoadToolBox( m_xContext, xInputStream, xIndexContainer ); + auto pRootItemContainer = dynamic_cast( xIndexContainer.get() ); + aUIElementData.xSettings = new ConstItemContainer( pRootItemContainer, true ); + return; + } + catch ( const css::lang::WrappedTargetException& ) + { + } + + break; + } + + case css::ui::UIElementType::STATUSBAR: + { + try + { + Reference< XIndexContainer > xIndexContainer( new RootItemContainer() ); + StatusBarConfiguration::LoadStatusBar( m_xContext, xInputStream, xIndexContainer ); + auto pRootItemContainer = dynamic_cast( xIndexContainer.get() ); + aUIElementData.xSettings = new ConstItemContainer( pRootItemContainer, true ); + return; + } + catch ( const css::lang::WrappedTargetException& ) + { + } + + break; + } + + case css::ui::UIElementType::FLOATINGWINDOW: + { + break; + } + } + } + } + catch ( const css::embed::InvalidStorageException& ) + { + } + catch ( const css::lang::IllegalArgumentException& ) + { + } + catch ( const css::io::IOException& ) + { + } + catch ( const css::embed::StorageWrappedTargetException& ) + { + } + } + + // At least we provide an empty settings container! + aUIElementData.xSettings = new ConstItemContainer(); +} + +ModuleUIConfigurationManager::UIElementData* ModuleUIConfigurationManager::impl_findUIElementData( const OUString& aResourceURL, sal_Int16 nElementType, bool bLoad ) +{ + // preload list of element types on demand + impl_preloadUIElementTypeList( LAYER_USERDEFINED, nElementType ); + impl_preloadUIElementTypeList( LAYER_DEFAULT, nElementType ); + + // first try to look into our user-defined vector/unordered_map combination + UIElementDataHashMap& rUserHashMap = m_aUIElements[LAYER_USERDEFINED][nElementType].aElementsHashMap; + UIElementDataHashMap::iterator pIter = rUserHashMap.find( aResourceURL ); + if ( pIter != rUserHashMap.end() ) + { + // Default data settings data must be retrieved from the default layer! + if ( !pIter->second.bDefault ) + { + if ( !pIter->second.xSettings.is() && bLoad ) + impl_requestUIElementData( nElementType, LAYER_USERDEFINED, pIter->second ); + return &(pIter->second); + } + } + + // Not successful, we have to look into our default vector/unordered_map combination + UIElementDataHashMap& rDefaultHashMap = m_aUIElements[LAYER_DEFAULT][nElementType].aElementsHashMap; + pIter = rDefaultHashMap.find( aResourceURL ); + if ( pIter != rDefaultHashMap.end() ) + { + if ( !pIter->second.xSettings.is() && bLoad ) + impl_requestUIElementData( nElementType, LAYER_DEFAULT, pIter->second ); + return &(pIter->second); + } + + // Nothing has been found! + return nullptr; +} + +void ModuleUIConfigurationManager::impl_storeElementTypeData( const Reference< XStorage >& xStorage, UIElementType& rElementType, bool bResetModifyState ) +{ + UIElementDataHashMap& rHashMap = rElementType.aElementsHashMap; + + for (auto & elem : rHashMap) + { + UIElementData& rElement = elem.second; + if ( rElement.bModified ) + { + if ( rElement.bDefault ) + { + xStorage->removeElement( rElement.aName ); + rElement.bModified = false; // mark as not modified + } + else + { + Reference< XStream > xStream = xStorage->openStreamElement( rElement.aName, ElementModes::WRITE|ElementModes::TRUNCATE ); + Reference< XOutputStream > xOutputStream( xStream->getOutputStream() ); + + if ( xOutputStream.is() ) + { + switch( rElementType.nElementType ) + { + case css::ui::UIElementType::MENUBAR: + case css::ui::UIElementType::POPUPMENU: + { + try + { + MenuConfiguration aMenuCfg( m_xContext ); + aMenuCfg.StoreMenuBarConfigurationToXML( + rElement.xSettings, xOutputStream, rElementType.nElementType == css::ui::UIElementType::MENUBAR ); + } + catch ( const css::lang::WrappedTargetException& ) + { + } + } + break; + + case css::ui::UIElementType::TOOLBAR: + { + try + { + ToolBoxConfiguration::StoreToolBox( m_xContext, xOutputStream, rElement.xSettings ); + } + catch ( const css::lang::WrappedTargetException& ) + { + } + } + break; + + case css::ui::UIElementType::STATUSBAR: + { + try + { + StatusBarConfiguration::StoreStatusBar( m_xContext, xOutputStream, rElement.xSettings ); + } + catch ( const css::lang::WrappedTargetException& ) + { + } + } + break; + + default: + break; + } + } + + // mark as not modified if we store to our own storage + if ( bResetModifyState ) + rElement.bModified = false; + } + } + } + + // commit element type storage + Reference< XTransactedObject > xTransactedObject( xStorage, UNO_QUERY ); + if ( xTransactedObject.is() ) + xTransactedObject->commit(); + + // mark UIElementType as not modified if we store to our own storage + if ( bResetModifyState ) + rElementType.bModified = false; +} + +// This is only allowed to be called on the LAYER_USER_DEFINED! +void ModuleUIConfigurationManager::impl_resetElementTypeData( + UIElementType& rUserElementType, + UIElementType const & rDefaultElementType, + ConfigEventNotifyContainer& rRemoveNotifyContainer, + ConfigEventNotifyContainer& rReplaceNotifyContainer ) +{ + UIElementDataHashMap& rHashMap = rUserElementType.aElementsHashMap; + + Reference< XUIConfigurationManager > xThis(this); + Reference< XInterface > xIfac( xThis, UNO_QUERY ); + sal_Int16 nType = rUserElementType.nElementType; + + // Make copies of the event structures to be thread-safe. We have to unlock our mutex before calling + // our listeners! + for (auto & elem : rHashMap) + { + UIElementData& rElement = elem.second; + if ( !rElement.bDefault ) + { + if ( rDefaultElementType.xStorage->hasByName( rElement.aName )) + { + // Replace settings with data from default layer + Reference< XIndexAccess > xOldSettings( rElement.xSettings ); + impl_requestUIElementData( nType, LAYER_DEFAULT, rElement ); + + ui::ConfigurationEvent aReplaceEvent; + aReplaceEvent.ResourceURL = rElement.aResourceURL; + aReplaceEvent.Accessor <<= xThis; + aReplaceEvent.Source = xIfac; + aReplaceEvent.ReplacedElement <<= xOldSettings; + aReplaceEvent.Element <<= rElement.xSettings; + + rReplaceNotifyContainer.push_back( aReplaceEvent ); + + // Mark element as default and not modified. That means "not active" + // in the user layer anymore. + rElement.bModified = false; + rElement.bDefault = true; + } + else + { + // Remove user-defined settings from user layer + ui::ConfigurationEvent aEvent; + aEvent.ResourceURL = rElement.aResourceURL; + aEvent.Accessor <<= xThis; + aEvent.Source = xIfac; + aEvent.Element <<= rElement.xSettings; + + rRemoveNotifyContainer.push_back( aEvent ); + + // Mark element as default and not modified. That means "not active" + // in the user layer anymore. + rElement.bModified = false; + rElement.bDefault = true; + } + } + } + + // Remove all settings from our user interface elements + rHashMap.clear(); +} + +void ModuleUIConfigurationManager::impl_reloadElementTypeData( + UIElementType& rUserElementType, + UIElementType const & rDefaultElementType, + ConfigEventNotifyContainer& rRemoveNotifyContainer, + ConfigEventNotifyContainer& rReplaceNotifyContainer ) +{ + UIElementDataHashMap& rHashMap = rUserElementType.aElementsHashMap; + + Reference< XUIConfigurationManager > xThis(this); + Reference< XInterface > xIfac( xThis, UNO_QUERY ); + sal_Int16 nType = rUserElementType.nElementType; + + for (auto & elem : rHashMap) + { + UIElementData& rElement = elem.second; + if ( rElement.bModified ) + { + if ( rUserElementType.xStorage->hasByName( rElement.aName )) + { + // Replace settings with data from user layer + Reference< XIndexAccess > xOldSettings( rElement.xSettings ); + + impl_requestUIElementData( nType, LAYER_USERDEFINED, rElement ); + + ui::ConfigurationEvent aReplaceEvent; + + aReplaceEvent.ResourceURL = rElement.aResourceURL; + aReplaceEvent.Accessor <<= xThis; + aReplaceEvent.Source = xIfac; + aReplaceEvent.ReplacedElement <<= xOldSettings; + aReplaceEvent.Element <<= rElement.xSettings; + rReplaceNotifyContainer.push_back( aReplaceEvent ); + + rElement.bModified = false; + } + else if ( rDefaultElementType.xStorage->hasByName( rElement.aName )) + { + // Replace settings with data from default layer + Reference< XIndexAccess > xOldSettings( rElement.xSettings ); + + impl_requestUIElementData( nType, LAYER_DEFAULT, rElement ); + + ui::ConfigurationEvent aReplaceEvent; + + aReplaceEvent.ResourceURL = rElement.aResourceURL; + aReplaceEvent.Accessor <<= xThis; + aReplaceEvent.Source = xIfac; + aReplaceEvent.ReplacedElement <<= xOldSettings; + aReplaceEvent.Element <<= rElement.xSettings; + rReplaceNotifyContainer.push_back( aReplaceEvent ); + + // Mark element as default and not modified. That means "not active" + // in the user layer anymore. + rElement.bModified = false; + rElement.bDefault = true; + } + else + { + // Element settings are not in any storage => remove + ui::ConfigurationEvent aRemoveEvent; + + aRemoveEvent.ResourceURL = rElement.aResourceURL; + aRemoveEvent.Accessor <<= xThis; + aRemoveEvent.Source = xIfac; + aRemoveEvent.Element <<= rElement.xSettings; + + rRemoveNotifyContainer.push_back( aRemoveEvent ); + + // Mark element as default and not modified. That means "not active" + // in the user layer anymore. + rElement.bModified = false; + rElement.bDefault = true; + } + } + } + + rUserElementType.bModified = false; +} + +void ModuleUIConfigurationManager::impl_Initialize() +{ + // Initialize the top-level structures with the storage data + if ( m_xUserConfigStorage.is() ) + { + // Try to access our module sub folder + for ( sal_Int16 i = 1; i < css::ui::UIElementType::COUNT; + i++ ) + { + Reference< XStorage > xElementTypeStorage; + try + { + if ( m_pStorageHandler[i] ) + xElementTypeStorage = m_pStorageHandler[i]->getWorkingStorageUser(); + } + catch ( const css::container::NoSuchElementException& ) + { + } + catch ( const css::embed::InvalidStorageException& ) + { + } + catch ( const css::lang::IllegalArgumentException& ) + { + } + catch ( const css::io::IOException& ) + { + } + catch ( const css::embed::StorageWrappedTargetException& ) + { + } + + m_aUIElements[LAYER_USERDEFINED][i].nElementType = i; + m_aUIElements[LAYER_USERDEFINED][i].bModified = false; + m_aUIElements[LAYER_USERDEFINED][i].xStorage = xElementTypeStorage; + } + } + + if ( !m_xDefaultConfigStorage.is() ) + return; + + Reference< XNameAccess > xNameAccess( m_xDefaultConfigStorage, UNO_QUERY_THROW ); + + // Try to access our module sub folder + for ( sal_Int16 i = 1; i < css::ui::UIElementType::COUNT; + i++ ) + { + Reference< XStorage > xElementTypeStorage; + try + { + const OUString sName( UIELEMENTTYPENAMES[i] ); + if( xNameAccess->hasByName( sName ) ) + xNameAccess->getByName( sName ) >>= xElementTypeStorage; + } + catch ( const css::container::NoSuchElementException& ) + { + } + + m_aUIElements[LAYER_DEFAULT][i].nElementType = i; + m_aUIElements[LAYER_DEFAULT][i].bModified = false; + m_aUIElements[LAYER_DEFAULT][i].xStorage = xElementTypeStorage; + } +} + +ModuleUIConfigurationManager::ModuleUIConfigurationManager( + const Reference< XComponentContext >& xContext, + const css::uno::Sequence< css::uno::Any >& aArguments) + : m_bReadOnly( true ) + , m_bModified( false ) + , m_bDisposed( false ) + , m_aXMLPostfix( ".xml" ) + , m_aPropUIName( "UIName" ) + , m_xContext( xContext ) +{ + // Make sure we have a default initialized entry for every layer and user interface element type! + // The following code depends on this! + m_aUIElements[LAYER_DEFAULT].resize( css::ui::UIElementType::COUNT ); + m_aUIElements[LAYER_USERDEFINED].resize( css::ui::UIElementType::COUNT ); + + SolarMutexGuard g; + + OUString aModuleShortName; + if( aArguments.getLength() == 2 && (aArguments[0] >>= aModuleShortName) && (aArguments[1] >>= m_aModuleIdentifier)) + { + } + else + { + ::comphelper::SequenceAsHashMap lArgs(aArguments); + aModuleShortName = lArgs.getUnpackedValueOrDefault("ModuleShortName", OUString()); + m_aModuleIdentifier = lArgs.getUnpackedValueOrDefault("ModuleIdentifier", OUString()); + } + + for ( int i = 1; i < css::ui::UIElementType::COUNT; i++ ) + { + OUString aResourceType; + if ( i == css::ui::UIElementType::MENUBAR ) + aResourceType = RESOURCETYPE_MENUBAR; + else if ( i == css::ui::UIElementType::TOOLBAR ) + aResourceType = RESOURCETYPE_TOOLBAR; + else if ( i == css::ui::UIElementType::STATUSBAR ) + aResourceType = RESOURCETYPE_STATUSBAR; + else if ( i == css::ui::UIElementType::POPUPMENU ) + aResourceType = RESOURCETYPE_POPUPMENU; + + if ( !aResourceType.isEmpty() ) + { + m_pStorageHandler[i].reset( new PresetHandler( m_xContext ) ); + m_pStorageHandler[i]->connectToResource( PresetHandler::E_MODULES, + aResourceType, // this path won't be used later... see next lines! + aModuleShortName, + css::uno::Reference< css::embed::XStorage >()); // no document root used here! + } + } + + // initialize root storages for all resource types + m_xUserRootCommit.set( m_pStorageHandler[css::ui::UIElementType::MENUBAR]->getOrCreateRootStorageUser(), css::uno::UNO_QUERY); // can be empty + m_xDefaultConfigStorage = m_pStorageHandler[css::ui::UIElementType::MENUBAR]->getParentStorageShare(); + m_xUserConfigStorage = m_pStorageHandler[css::ui::UIElementType::MENUBAR]->getParentStorageUser(); + + if ( m_xUserConfigStorage.is() ) + { + Reference< XPropertySet > xPropSet( m_xUserConfigStorage, UNO_QUERY ); + if ( xPropSet.is() ) + { + tools::Long nOpenMode = 0; + Any a = xPropSet->getPropertyValue("OpenMode"); + if ( a >>= nOpenMode ) + m_bReadOnly = !( nOpenMode & ElementModes::WRITE ); + } + } + + impl_Initialize(); +} + +// XComponent +void SAL_CALL ModuleUIConfigurationManager::dispose() +{ + Reference< XComponent > xThis(this); + + css::lang::EventObject aEvent( xThis ); + { + std::unique_lock aGuard(m_mutex); + m_aEventListeners.disposeAndClear( aGuard, aEvent ); + } + { + std::unique_lock aGuard(m_mutex); + m_aConfigListeners.disposeAndClear( aGuard, aEvent ); + } + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + SolarMutexClearableGuard aGuard; + Reference< XComponent > xModuleImageManager( m_xModuleImageManager ); + m_xModuleImageManager.clear(); + m_xModuleAcceleratorManager.clear(); + m_aUIElements[LAYER_USERDEFINED].clear(); + m_aUIElements[LAYER_DEFAULT].clear(); + m_xDefaultConfigStorage.clear(); + m_xUserConfigStorage.clear(); + m_xUserRootCommit.clear(); + m_bModified = false; + m_bDisposed = true; + aGuard.clear(); + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + + try + { + if ( xModuleImageManager.is() ) + xModuleImageManager->dispose(); + } + catch ( const Exception& ) + { + } +} + +void SAL_CALL ModuleUIConfigurationManager::addEventListener( const Reference< XEventListener >& xListener ) +{ + { + SolarMutexGuard g; + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + if ( m_bDisposed ) + throw DisposedException(); + } + + std::unique_lock aGuard(m_mutex); + m_aEventListeners.addInterface( aGuard, xListener ); +} + +void SAL_CALL ModuleUIConfigurationManager::removeEventListener( const Reference< XEventListener >& xListener ) +{ + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + std::unique_lock aGuard(m_mutex); + m_aEventListeners.removeInterface( aGuard, xListener ); +} + +// XUIConfiguration +void SAL_CALL ModuleUIConfigurationManager::addConfigurationListener( const Reference< css::ui::XUIConfigurationListener >& xListener ) +{ + { + SolarMutexGuard g; + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + if ( m_bDisposed ) + throw DisposedException(); + } + + std::unique_lock aGuard(m_mutex); + m_aConfigListeners.addInterface( aGuard, xListener ); +} + +void SAL_CALL ModuleUIConfigurationManager::removeConfigurationListener( const Reference< css::ui::XUIConfigurationListener >& xListener ) +{ + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + std::unique_lock aGuard(m_mutex); + m_aConfigListeners.removeInterface( aGuard, xListener ); +} + +// XUIConfigurationManager +void SAL_CALL ModuleUIConfigurationManager::reset() +{ + SolarMutexClearableGuard aGuard; + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + if ( m_bDisposed ) + throw DisposedException(); + + if ( isReadOnly() ) + return; + + // Remove all elements from our user-defined storage! + try + { + for ( int i = 1; i < css::ui::UIElementType::COUNT; i++ ) + { + UIElementType& rElementType = m_aUIElements[LAYER_USERDEFINED][i]; + + if ( rElementType.xStorage.is() ) + { + bool bCommitSubStorage( false ); + const Sequence< OUString > aUIElementStreamNames = rElementType.xStorage->getElementNames(); + for ( OUString const & rName : aUIElementStreamNames ) + { + rElementType.xStorage->removeElement( rName ); + bCommitSubStorage = true; + } + + if ( bCommitSubStorage ) + { + Reference< XTransactedObject > xTransactedObject( rElementType.xStorage, UNO_QUERY ); + if ( xTransactedObject.is() ) + xTransactedObject->commit(); + m_pStorageHandler[i]->commitUserChanges(); + } + } + } + + // remove settings from user defined layer and notify listener about removed settings data! + ConfigEventNotifyContainer aRemoveEventNotifyContainer; + ConfigEventNotifyContainer aReplaceEventNotifyContainer; + for ( sal_Int16 j = 1; j < css::ui::UIElementType::COUNT; j++ ) + { + try + { + UIElementType& rUserElementType = m_aUIElements[LAYER_USERDEFINED][j]; + UIElementType& rDefaultElementType = m_aUIElements[LAYER_DEFAULT][j]; + + impl_resetElementTypeData( rUserElementType, rDefaultElementType, aRemoveEventNotifyContainer, aReplaceEventNotifyContainer ); + rUserElementType.bModified = false; + } + catch (const Exception&) + { + css::uno::Any anyEx = cppu::getCaughtException(); + throw css::lang::WrappedTargetRuntimeException( + "ModuleUIConfigurationManager::reset exception", + css::uno::Reference(*this), anyEx); + } + } + + m_bModified = false; + + // Unlock mutex before notify our listeners + aGuard.clear(); + + // Notify our listeners + for ( auto const & k: aRemoveEventNotifyContainer ) + implts_notifyContainerListener( k, NotifyOp_Remove ); + for ( auto const & k: aReplaceEventNotifyContainer ) + implts_notifyContainerListener( k, NotifyOp_Replace ); + } + catch ( const css::lang::IllegalArgumentException& ) + { + } + catch ( const css::container::NoSuchElementException& ) + { + } + catch ( const css::embed::InvalidStorageException& ) + { + } + catch ( const css::embed::StorageWrappedTargetException& ) + { + } +} + +Sequence< Sequence< PropertyValue > > SAL_CALL ModuleUIConfigurationManager::getUIElementsInfo( sal_Int16 ElementType ) +{ + if (( ElementType < 0 ) || ( ElementType >= css::ui::UIElementType::COUNT )) + throw IllegalArgumentException(); + + SolarMutexGuard g; + if ( m_bDisposed ) + throw DisposedException(); + + std::vector< Sequence< PropertyValue > > aElementInfoSeq; + UIElementInfoHashMap aUIElementInfoCollection; + + if ( ElementType == css::ui::UIElementType::UNKNOWN ) + { + for ( sal_Int16 i = 0; i < css::ui::UIElementType::COUNT; i++ ) + impl_fillSequenceWithElementTypeInfo( aUIElementInfoCollection, i ); + } + else + impl_fillSequenceWithElementTypeInfo( aUIElementInfoCollection, ElementType ); + + aElementInfoSeq.resize( aUIElementInfoCollection.size() ); + + sal_Int32 n = 0; + for (auto const& elem : aUIElementInfoCollection) + { + Sequence< PropertyValue > aUIElementInfo{ + comphelper::makePropertyValue("ResourceURL", elem.second.aResourceURL), + comphelper::makePropertyValue(m_aPropUIName, elem.second.aUIName) + }; + aElementInfoSeq[n++] = aUIElementInfo; + } + + return comphelper::containerToSequence(aElementInfoSeq); +} + +Reference< XIndexContainer > SAL_CALL ModuleUIConfigurationManager::createSettings() +{ + SolarMutexGuard g; + + if ( m_bDisposed ) + throw DisposedException(); + + // Creates an empty item container which can be filled from outside + return Reference< XIndexContainer >( new RootItemContainer() ); +} + +sal_Bool SAL_CALL ModuleUIConfigurationManager::hasSettings( const OUString& ResourceURL ) +{ + sal_Int16 nElementType = RetrieveTypeFromResourceURL( ResourceURL ); + + if (( nElementType == css::ui::UIElementType::UNKNOWN ) || + ( nElementType >= css::ui::UIElementType::COUNT )) + throw IllegalArgumentException(); + + SolarMutexGuard g; + + if ( m_bDisposed ) + throw DisposedException(); + + UIElementData* pDataSettings = impl_findUIElementData( ResourceURL, nElementType, false ); + if ( pDataSettings ) + return true; + + return false; +} + +Reference< XIndexAccess > SAL_CALL ModuleUIConfigurationManager::getSettings( const OUString& ResourceURL, sal_Bool bWriteable ) +{ + sal_Int16 nElementType = RetrieveTypeFromResourceURL( ResourceURL ); + + if (( nElementType == css::ui::UIElementType::UNKNOWN ) || + ( nElementType >= css::ui::UIElementType::COUNT )) + throw IllegalArgumentException(); + + SolarMutexGuard g; + + if ( m_bDisposed ) + throw DisposedException(); + + UIElementData* pDataSettings = impl_findUIElementData( ResourceURL, nElementType ); + if ( pDataSettings ) + { + // Create a copy of our data if someone wants to change the data. + if ( bWriteable ) + return Reference< XIndexAccess >( new RootItemContainer( pDataSettings->xSettings ) ); + else + return pDataSettings->xSettings; + } + + throw NoSuchElementException(); +} + +void SAL_CALL ModuleUIConfigurationManager::replaceSettings( const OUString& ResourceURL, const Reference< css::container::XIndexAccess >& aNewData ) +{ + sal_Int16 nElementType = RetrieveTypeFromResourceURL( ResourceURL ); + + if (( nElementType == css::ui::UIElementType::UNKNOWN ) || + ( nElementType >= css::ui::UIElementType::COUNT )) + throw IllegalArgumentException(); + else if ( m_bReadOnly ) + throw IllegalAccessException(); + else + { + SolarMutexClearableGuard aGuard; + + if ( m_bDisposed ) + throw DisposedException(); + + UIElementData* pDataSettings = impl_findUIElementData( ResourceURL, nElementType ); + if ( !pDataSettings ) + throw NoSuchElementException(); + if ( !pDataSettings->bDefaultNode ) + { + // we have a settings entry in our user-defined layer - replace + Reference< XIndexAccess > xOldSettings = pDataSettings->xSettings; + + // Create a copy of the data if the container is not const + Reference< XIndexReplace > xReplace( aNewData, UNO_QUERY ); + if ( xReplace.is() ) + pDataSettings->xSettings = new ConstItemContainer( aNewData ); + else + pDataSettings->xSettings = aNewData; + pDataSettings->bDefault = false; + pDataSettings->bModified = true; + m_bModified = true; + + // Modify type container + UIElementType& rElementType = m_aUIElements[LAYER_USERDEFINED][nElementType]; + rElementType.bModified = true; + + Reference< XUIConfigurationManager > xThis(this); + Reference< XInterface > xIfac( xThis, UNO_QUERY ); + + // Create event to notify listener about replaced element settings + ui::ConfigurationEvent aEvent; + aEvent.ResourceURL = ResourceURL; + aEvent.Accessor <<= xThis; + aEvent.Source = xIfac; + aEvent.ReplacedElement <<= xOldSettings; + aEvent.Element <<= pDataSettings->xSettings; + + aGuard.clear(); + + implts_notifyContainerListener( aEvent, NotifyOp_Replace ); + } + else + { + // we have no settings in our user-defined layer - insert + UIElementData aUIElementData; + + aUIElementData.bDefault = false; + aUIElementData.bDefaultNode = false; + aUIElementData.bModified = true; + + // Create a copy of the data if the container is not const + Reference< XIndexReplace > xReplace( aNewData, UNO_QUERY ); + if ( xReplace.is() ) + aUIElementData.xSettings = new ConstItemContainer( aNewData ); + else + aUIElementData.xSettings = aNewData; + aUIElementData.aName = RetrieveNameFromResourceURL( ResourceURL ) + m_aXMLPostfix; + aUIElementData.aResourceURL = ResourceURL; + m_bModified = true; + + // Modify type container + UIElementType& rElementType = m_aUIElements[LAYER_USERDEFINED][nElementType]; + rElementType.bModified = true; + + UIElementDataHashMap& rElements = rElementType.aElementsHashMap; + + // Check our user element settings hash map as it can already contain settings that have been set to default! + // If no node can be found, we have to insert it. + UIElementDataHashMap::iterator pIter = rElements.find( ResourceURL ); + if ( pIter != rElements.end() ) + pIter->second = aUIElementData; + else + rElements.emplace( ResourceURL, aUIElementData ); + + Reference< XUIConfigurationManager > xThis(this); + + // Create event to notify listener about replaced element settings + ui::ConfigurationEvent aEvent; + + aEvent.ResourceURL = ResourceURL; + aEvent.Accessor <<= xThis; + aEvent.Source.set(xThis, UNO_QUERY); + aEvent.ReplacedElement <<= pDataSettings->xSettings; + aEvent.Element <<= aUIElementData.xSettings; + + aGuard.clear(); + + implts_notifyContainerListener( aEvent, NotifyOp_Replace ); + } + } +} + +void SAL_CALL ModuleUIConfigurationManager::removeSettings( const OUString& ResourceURL ) +{ + sal_Int16 nElementType = RetrieveTypeFromResourceURL( ResourceURL ); + + if (( nElementType == css::ui::UIElementType::UNKNOWN ) || + ( nElementType >= css::ui::UIElementType::COUNT )) + throw IllegalArgumentException( "The ResourceURL is not valid or " + "describes an unknown type. " + "ResourceURL: " + ResourceURL, nullptr, 0 ); + else if ( m_bReadOnly ) + throw IllegalAccessException( "The configuration manager is read-only. " + "ResourceURL: " + ResourceURL, nullptr ); + else + { + SolarMutexClearableGuard aGuard; + + if ( m_bDisposed ) + throw DisposedException( "The configuration manager has been disposed, " + "and can't uphold its method specification anymore. " + "ResourceURL: " + ResourceURL, nullptr ); + + UIElementData* pDataSettings = impl_findUIElementData( ResourceURL, nElementType ); + if ( !pDataSettings ) + throw NoSuchElementException( "The settings data cannot be found. " + "ResourceURL: " + ResourceURL, nullptr ); + // If element settings are default, we don't need to change anything! + if ( pDataSettings->bDefault ) + return; + else + { + Reference< XIndexAccess > xRemovedSettings = pDataSettings->xSettings; + pDataSettings->bDefault = true; + + // check if this is a default layer node + if ( !pDataSettings->bDefaultNode ) + pDataSettings->bModified = true; // we have to remove this node from the user layer! + pDataSettings->xSettings.clear(); + m_bModified = true; // user layer must be written + + // Modify type container + UIElementType& rElementType = m_aUIElements[LAYER_USERDEFINED][nElementType]; + rElementType.bModified = true; + + Reference< XUIConfigurationManager > xThis(this); + Reference< XInterface > xIfac( xThis, UNO_QUERY ); + + // Check if we have settings in the default layer which replaces the user-defined one! + UIElementData* pDefaultDataSettings = impl_findUIElementData( ResourceURL, nElementType ); + if ( pDefaultDataSettings ) + { + // Create event to notify listener about replaced element settings + ui::ConfigurationEvent aEvent; + + aEvent.ResourceURL = ResourceURL; + aEvent.Accessor <<= xThis; + aEvent.Source = xIfac; + aEvent.Element <<= xRemovedSettings; + aEvent.ReplacedElement <<= pDefaultDataSettings->xSettings; + + aGuard.clear(); + + implts_notifyContainerListener( aEvent, NotifyOp_Replace ); + } + else + { + // Create event to notify listener about removed element settings + ui::ConfigurationEvent aEvent; + + aEvent.ResourceURL = ResourceURL; + aEvent.Accessor <<= xThis; + aEvent.Source = xIfac; + aEvent.Element <<= xRemovedSettings; + + aGuard.clear(); + + implts_notifyContainerListener( aEvent, NotifyOp_Remove ); + } + } + } +} + +void SAL_CALL ModuleUIConfigurationManager::insertSettings( const OUString& NewResourceURL, const Reference< XIndexAccess >& aNewData ) +{ + sal_Int16 nElementType = RetrieveTypeFromResourceURL( NewResourceURL ); + + if (( nElementType == css::ui::UIElementType::UNKNOWN ) || + ( nElementType >= css::ui::UIElementType::COUNT )) + throw IllegalArgumentException(); + else if ( m_bReadOnly ) + throw IllegalAccessException(); + else + { + SolarMutexClearableGuard aGuard; + + if ( m_bDisposed ) + throw DisposedException(); + + UIElementData* pDataSettings = impl_findUIElementData( NewResourceURL, nElementType ); + if ( !(!pDataSettings) ) + throw ElementExistException(); + UIElementData aUIElementData; + + aUIElementData.bDefault = false; + aUIElementData.bDefaultNode = false; + aUIElementData.bModified = true; + + // Create a copy of the data if the container is not const + Reference< XIndexReplace > xReplace( aNewData, UNO_QUERY ); + if ( xReplace.is() ) + aUIElementData.xSettings = new ConstItemContainer( aNewData ); + else + aUIElementData.xSettings = aNewData; + aUIElementData.aName = RetrieveNameFromResourceURL( NewResourceURL ) + m_aXMLPostfix; + aUIElementData.aResourceURL = NewResourceURL; + m_bModified = true; + + UIElementType& rElementType = m_aUIElements[LAYER_USERDEFINED][nElementType]; + rElementType.bModified = true; + + UIElementDataHashMap& rElements = rElementType.aElementsHashMap; + rElements.emplace( NewResourceURL, aUIElementData ); + + Reference< XIndexAccess > xInsertSettings( aUIElementData.xSettings ); + Reference< XUIConfigurationManager > xThis(this); + + // Create event to notify listener about removed element settings + ui::ConfigurationEvent aEvent; + + aEvent.ResourceURL = NewResourceURL; + aEvent.Accessor <<= xThis; + aEvent.Source = xThis; + aEvent.Element <<= xInsertSettings; + + aGuard.clear(); + + implts_notifyContainerListener( aEvent, NotifyOp_Insert ); + } +} + +Reference< XInterface > SAL_CALL ModuleUIConfigurationManager::getImageManager() +{ + SolarMutexGuard g; + + if ( m_bDisposed ) + throw DisposedException(); + + if ( !m_xModuleImageManager.is() ) + { + m_xModuleImageManager = new ImageManager( m_xContext, /*bForModule*/true ); + + uno::Sequence aPropSeq(comphelper::InitAnyPropertySequence( + { + {"UserConfigStorage", uno::Any(m_xUserConfigStorage)}, + {"ModuleIdentifier", uno::Any(m_aModuleIdentifier)}, + {"UserRootCommit", uno::Any(m_xUserRootCommit)}, + })); + m_xModuleImageManager->initialize( aPropSeq ); + } + + return Reference< XInterface >( static_cast(m_xModuleImageManager.get()), UNO_QUERY ); +} + +Reference< ui::XAcceleratorConfiguration > SAL_CALL ModuleUIConfigurationManager::createShortCutManager() +{ + return ui::ModuleAcceleratorConfiguration::createWithModuleIdentifier(m_xContext, m_aModuleIdentifier); +} + +Reference< ui::XAcceleratorConfiguration > SAL_CALL ModuleUIConfigurationManager::getShortCutManager() +{ + SolarMutexGuard g; + + if ( m_bDisposed ) + throw DisposedException(); + + if ( !m_xModuleAcceleratorManager.is() ) try + { + m_xModuleAcceleratorManager = ui::ModuleAcceleratorConfiguration:: + createWithModuleIdentifier(m_xContext, m_aModuleIdentifier); + } + catch ( const css::uno::DeploymentException& ) + { + SAL_WARN("fwk.uiconfiguration", "ModuleAcceleratorConfiguration" + " not available. This should happen only on mobile platforms."); + } + + return m_xModuleAcceleratorManager; +} + +Reference< XInterface > SAL_CALL ModuleUIConfigurationManager::getEventsManager() +{ + return Reference< XInterface >(); +} + +// XModuleUIConfigurationManager +sal_Bool SAL_CALL ModuleUIConfigurationManager::isDefaultSettings( const OUString& ResourceURL ) +{ + sal_Int16 nElementType = RetrieveTypeFromResourceURL( ResourceURL ); + + if (( nElementType == css::ui::UIElementType::UNKNOWN ) || + ( nElementType >= css::ui::UIElementType::COUNT )) + throw IllegalArgumentException(); + + SolarMutexGuard g; + + if ( m_bDisposed ) + throw DisposedException(); + + UIElementData* pDataSettings = impl_findUIElementData( ResourceURL, nElementType, false ); + if ( pDataSettings && pDataSettings->bDefaultNode ) + return true; + + return false; +} + +Reference< XIndexAccess > SAL_CALL ModuleUIConfigurationManager::getDefaultSettings( const OUString& ResourceURL ) +{ + sal_Int16 nElementType = RetrieveTypeFromResourceURL( ResourceURL ); + + if (( nElementType == css::ui::UIElementType::UNKNOWN ) || + ( nElementType >= css::ui::UIElementType::COUNT )) + throw IllegalArgumentException(); + + SolarMutexGuard g; + + if ( m_bDisposed ) + throw DisposedException(); + + // preload list of element types on demand + impl_preloadUIElementTypeList( LAYER_DEFAULT, nElementType ); + + // Look into our default vector/unordered_map combination + UIElementDataHashMap& rDefaultHashMap = m_aUIElements[LAYER_DEFAULT][nElementType].aElementsHashMap; + UIElementDataHashMap::iterator pIter = rDefaultHashMap.find( ResourceURL ); + if ( pIter != rDefaultHashMap.end() ) + { + if ( !pIter->second.xSettings.is() ) + impl_requestUIElementData( nElementType, LAYER_DEFAULT, pIter->second ); + return pIter->second.xSettings; + } + + // Nothing has been found! + throw NoSuchElementException(); +} + +// XUIConfigurationPersistence +void SAL_CALL ModuleUIConfigurationManager::reload() +{ + SolarMutexClearableGuard aGuard; + + if ( m_bDisposed ) + throw DisposedException(); + + if ( !m_xUserConfigStorage.is() || !m_bModified || m_bReadOnly ) + return; + + // Try to access our module sub folder + ConfigEventNotifyContainer aRemoveNotifyContainer; + ConfigEventNotifyContainer aReplaceNotifyContainer; + for ( sal_Int16 i = 1; i < css::ui::UIElementType::COUNT; i++ ) + { + try + { + UIElementType& rUserElementType = m_aUIElements[LAYER_USERDEFINED][i]; + + if ( rUserElementType.bModified ) + { + UIElementType& rDefaultElementType = m_aUIElements[LAYER_DEFAULT][i]; + impl_reloadElementTypeData( rUserElementType, rDefaultElementType, aRemoveNotifyContainer, aReplaceNotifyContainer ); + } + } + catch ( const Exception& ) + { + throw IOException(); + } + } + + m_bModified = false; + + // Unlock mutex before notify our listeners + aGuard.clear(); + + // Notify our listeners + for (const ui::ConfigurationEvent & j : aRemoveNotifyContainer) + implts_notifyContainerListener( j, NotifyOp_Remove ); + for (const ui::ConfigurationEvent & k : aReplaceNotifyContainer) + implts_notifyContainerListener( k, NotifyOp_Replace ); +} + +void SAL_CALL ModuleUIConfigurationManager::store() +{ + SolarMutexGuard g; + + if ( m_bDisposed ) + throw DisposedException(); + + if ( !m_xUserConfigStorage.is() || !m_bModified || m_bReadOnly ) + return; + + // Try to access our module sub folder + for ( int i = 1; i < css::ui::UIElementType::COUNT; i++ ) + { + try + { + UIElementType& rElementType = m_aUIElements[LAYER_USERDEFINED][i]; + + if ( rElementType.bModified && rElementType.xStorage.is() ) + { + impl_storeElementTypeData( rElementType.xStorage, rElementType ); + m_pStorageHandler[i]->commitUserChanges(); + } + } + catch ( const Exception& ) + { + throw IOException(); + } + } + + m_bModified = false; +} + +void SAL_CALL ModuleUIConfigurationManager::storeToStorage( const Reference< XStorage >& Storage ) +{ + SolarMutexGuard g; + + if ( m_bDisposed ) + throw DisposedException(); + + if ( !m_xUserConfigStorage.is() || !m_bModified || m_bReadOnly ) + return; + + // Try to access our module sub folder + for ( int i = 1; i < css::ui::UIElementType::COUNT; i++ ) + { + try + { + Reference< XStorage > xElementTypeStorage( Storage->openStorageElement( + OUString(UIELEMENTTYPENAMES[i]), ElementModes::READWRITE )); + UIElementType& rElementType = m_aUIElements[LAYER_USERDEFINED][i]; + + if ( rElementType.bModified && xElementTypeStorage.is() ) + impl_storeElementTypeData( xElementTypeStorage, rElementType, false ); // store data to storage, but don't reset modify flag! + } + catch ( const Exception& ) + { + throw IOException(); + } + } + + Reference< XTransactedObject > xTransactedObject( Storage, UNO_QUERY ); + if ( xTransactedObject.is() ) + xTransactedObject->commit(); +} + +sal_Bool SAL_CALL ModuleUIConfigurationManager::isModified() +{ + SolarMutexGuard g; + + return m_bModified; +} + +sal_Bool SAL_CALL ModuleUIConfigurationManager::isReadOnly() +{ + SolarMutexGuard g; + + return m_bReadOnly; +} + +void ModuleUIConfigurationManager::implts_notifyContainerListener( const ui::ConfigurationEvent& aEvent, NotifyOp eOp ) +{ + std::unique_lock aGuard(m_mutex); + using ListenerMethodType = void (SAL_CALL css::ui::XUIConfigurationListener::*)(const ui::ConfigurationEvent&); + ListenerMethodType aListenerMethod {}; + switch ( eOp ) + { + case NotifyOp_Replace: + aListenerMethod = &css::ui::XUIConfigurationListener::elementReplaced; + break; + case NotifyOp_Insert: + aListenerMethod = &css::ui::XUIConfigurationListener::elementInserted; + break; + case NotifyOp_Remove: + aListenerMethod = &css::ui::XUIConfigurationListener::elementRemoved; + break; + } + m_aConfigListeners.notifyEach(aGuard, aListenerMethod, aEvent); +} + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_framework_ModuleUIConfigurationManager_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence const &arguments) +{ + return cppu::acquire(new ModuleUIConfigurationManager(context, arguments)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uiconfiguration/uicategorydescription.cxx b/framework/source/uiconfiguration/uicategorydescription.cxx new file mode 100644 index 0000000000..8820a871fd --- /dev/null +++ b/framework/source/uiconfiguration/uicategorydescription.cxx @@ -0,0 +1,395 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include +#include + +using namespace com::sun::star::uno; +using namespace com::sun::star::lang; +using namespace com::sun::star::beans; +using namespace com::sun::star::configuration; +using namespace com::sun::star::container; +using namespace framework; + +namespace { + +class ConfigurationAccess_UICategory : public ::cppu::WeakImplHelper +{ + std::mutex aMutex; + public: + ConfigurationAccess_UICategory( std::u16string_view aModuleName, const Reference< XNameAccess >& xGenericUICommands, const Reference< XComponentContext >& rxContext ); + virtual ~ConfigurationAccess_UICategory() 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; + + // XElementAccess + virtual css::uno::Type SAL_CALL getElementType() override; + + virtual sal_Bool SAL_CALL hasElements() override; + + // container.XContainerListener + virtual void SAL_CALL elementInserted( const ContainerEvent& aEvent ) override; + virtual void SAL_CALL elementRemoved ( const ContainerEvent& aEvent ) override; + virtual void SAL_CALL elementReplaced( const ContainerEvent& aEvent ) override; + + // lang.XEventListener + virtual void SAL_CALL disposing( const EventObject& aEvent ) override; + + protected: + Any getUINameFromID( const OUString& rId ); + Any getUINameFromCache( const OUString& rId ); + Sequence< OUString > getAllIds(); + void fillCache(); + + private: + typedef std::unordered_map< OUString, + OUString > IdToInfoCache; + + void initializeConfigAccess(); + + OUString m_aConfigCategoryAccess; + OUString m_aPropUIName; + Reference< XNameAccess > m_xGenericUICategories; + Reference< XMultiServiceFactory > m_xConfigProvider; + Reference< XNameAccess > m_xConfigAccess; + Reference< XContainerListener > m_xConfigListener; + bool m_bConfigAccessInitialized; + bool m_bCacheFilled; + IdToInfoCache m_aIdCache; +}; + +// XInterface, XTypeProvider + +ConfigurationAccess_UICategory::ConfigurationAccess_UICategory( std::u16string_view aModuleName, const Reference< XNameAccess >& rGenericUICategories, const Reference< XComponentContext >& rxContext ) : + // Create configuration hierarchical access name + m_aConfigCategoryAccess( + OUString::Concat("/org.openoffice.Office.UI.") + aModuleName + "/Commands/Categories"), + m_aPropUIName( "Name" ), + m_xGenericUICategories( rGenericUICategories ), + m_xConfigProvider(theDefaultProvider::get( rxContext )), + m_bConfigAccessInitialized( false ), + m_bCacheFilled( false ) +{ +} + +ConfigurationAccess_UICategory::~ConfigurationAccess_UICategory() +{ + // SAFE + std::unique_lock g(aMutex); + Reference< XContainer > xContainer( m_xConfigAccess, UNO_QUERY ); + if ( xContainer.is() ) + xContainer->removeContainerListener(m_xConfigListener); +} + +// XNameAccess +Any SAL_CALL ConfigurationAccess_UICategory::getByName( const OUString& rId ) +{ + std::unique_lock g(aMutex); + if ( !m_bConfigAccessInitialized ) + { + initializeConfigAccess(); + m_bConfigAccessInitialized = true; + fillCache(); + } + + // SAFE + Any a = getUINameFromID( rId ); + + if ( !a.hasValue() ) + throw NoSuchElementException(); + + return a; +} + +Sequence< OUString > SAL_CALL ConfigurationAccess_UICategory::getElementNames() +{ + return getAllIds(); +} + +sal_Bool SAL_CALL ConfigurationAccess_UICategory::hasByName( const OUString& rId ) +{ + return getByName( rId ).hasValue(); +} + +// XElementAccess +Type SAL_CALL ConfigurationAccess_UICategory::getElementType() +{ + return cppu::UnoType::get(); +} + +sal_Bool SAL_CALL ConfigurationAccess_UICategory::hasElements() +{ + // There must be global categories! + return true; +} + +void ConfigurationAccess_UICategory::fillCache() +{ + SAL_INFO( "fwk", "framework (cd100003) ::ConfigurationAccess_UICategory::fillCache" ); + + if ( m_bCacheFilled ) + return; + + OUString aUIName; + const Sequence< OUString > aNameSeq = m_xConfigAccess->getElementNames(); + + for ( OUString const & rName : aNameSeq ) + { + try + { + Reference< XNameAccess > xNameAccess(m_xConfigAccess->getByName( rName ),UNO_QUERY); + if ( xNameAccess.is() ) + { + xNameAccess->getByName( m_aPropUIName ) >>= aUIName; + + m_aIdCache.emplace( rName, aUIName ); + } + } + catch ( const css::lang::WrappedTargetException& ) + { + } + catch ( const css::container::NoSuchElementException& ) + { + } + } + + m_bCacheFilled = true; +} + +Any ConfigurationAccess_UICategory::getUINameFromID( const OUString& rId ) +{ + Any a; + + try + { + a = getUINameFromCache( rId ); + if ( !a.hasValue() ) + { + // Try to ask our global commands configuration access + if ( m_xGenericUICategories.is() ) + { + try + { + return m_xGenericUICategories->getByName( rId ); + } + catch ( const css::lang::WrappedTargetException& ) + { + } + catch ( const css::container::NoSuchElementException& ) + { + } + } + } + } + catch( const css::container::NoSuchElementException& ) + { + } + catch ( const css::lang::WrappedTargetException& ) + { + } + + return a; +} + +Any ConfigurationAccess_UICategory::getUINameFromCache( const OUString& rId ) +{ + Any a; + + IdToInfoCache::const_iterator pIter = m_aIdCache.find( rId ); + if ( pIter != m_aIdCache.end() ) + a <<= pIter->second; + + return a; +} + +Sequence< OUString > ConfigurationAccess_UICategory::getAllIds() +{ + // SAFE + std::unique_lock g(aMutex); + + if ( !m_bConfigAccessInitialized ) + { + initializeConfigAccess(); + m_bConfigAccessInitialized = true; + fillCache(); + } + + if ( m_xConfigAccess.is() ) + { + try + { + Sequence< OUString > aNameSeq = m_xConfigAccess->getElementNames(); + + if ( m_xGenericUICategories.is() ) + { + // Create concat list of supported user interface commands of the module + Sequence< OUString > aGenericNameSeq = m_xGenericUICategories->getElementNames(); + sal_uInt32 nCount1 = aNameSeq.getLength(); + sal_uInt32 nCount2 = aGenericNameSeq.getLength(); + + aNameSeq.realloc( nCount1 + nCount2 ); + OUString* pNameSeq = aNameSeq.getArray(); + const OUString* pGenericSeq = aGenericNameSeq.getConstArray(); + for ( sal_uInt32 i = 0; i < nCount2; i++ ) + pNameSeq[nCount1+i] = pGenericSeq[i]; + } + + return aNameSeq; + } + catch( const css::container::NoSuchElementException& ) + { + } + catch ( const css::lang::WrappedTargetException& ) + { + } + } + + return Sequence< OUString >(); +} + +void ConfigurationAccess_UICategory::initializeConfigAccess() +{ + try + { + Sequence aArgs(comphelper::InitAnyPropertySequence( + { + {"nodepath", Any(m_aConfigCategoryAccess)} + })); + + m_xConfigAccess.set( m_xConfigProvider->createInstanceWithArguments( + "com.sun.star.configuration.ConfigurationAccess", aArgs ),UNO_QUERY ); + if ( m_xConfigAccess.is() ) + { + // Add as container listener + Reference< XContainer > xContainer( m_xConfigAccess, UNO_QUERY ); + if ( xContainer.is() ) + { + m_xConfigListener = new WeakContainerListener(this); + xContainer->addContainerListener(m_xConfigListener); + } + } + } + catch ( const WrappedTargetException& ) + { + } + catch ( const Exception& ) + { + } +} + +// container.XContainerListener +void SAL_CALL ConfigurationAccess_UICategory::elementInserted( const ContainerEvent& ) +{ +} + +void SAL_CALL ConfigurationAccess_UICategory::elementRemoved ( const ContainerEvent& ) +{ +} + +void SAL_CALL ConfigurationAccess_UICategory::elementReplaced( const ContainerEvent& ) +{ +} + +// lang.XEventListener +void SAL_CALL ConfigurationAccess_UICategory::disposing( const EventObject& aEvent ) +{ + // SAFE + // remove our reference to the config access + std::unique_lock g(aMutex); + + Reference< XInterface > xIfac1( aEvent.Source, UNO_QUERY ); + Reference< XInterface > xIfac2( m_xConfigAccess, UNO_QUERY ); + if ( xIfac1 == xIfac2 ) + m_xConfigAccess.clear(); +} + +class UICategoryDescription : public UICommandDescription +{ +public: + explicit UICategoryDescription( const css::uno::Reference< css::uno::XComponentContext >& rxContext ); + + virtual OUString SAL_CALL getImplementationName() override + { + return "com.sun.star.comp.framework.UICategoryDescription"; + } + + virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override + { + return cppu::supportsService(this, ServiceName); + } + + virtual css::uno::Sequence SAL_CALL getSupportedServiceNames() override + { + return {"com.sun.star.ui.UICategoryDescription"}; + } + +}; + +UICategoryDescription::UICategoryDescription( const Reference< XComponentContext >& rxContext ) : + UICommandDescription(rxContext,true) +{ + SvtSysLocale aSysLocale; + const LanguageTag& rCurrentLanguage = aSysLocale.GetUILanguageTag(); + Reference< XNameAccess > xEmpty; + OUString aGenericCategories( "GenericCategories" ); + m_xGenericUICommands[rCurrentLanguage] = new ConfigurationAccess_UICategory( aGenericCategories, xEmpty, rxContext ); + + // insert generic categories mappings + m_aModuleToCommandFileMap.emplace( OUString("generic"), aGenericCategories ); + + auto& rMap = m_aUICommandsHashMap[rCurrentLanguage]; + UICommandsHashMap::iterator pCatIter = rMap.find( aGenericCategories ); + if ( pCatIter != rMap.end() ) + pCatIter->second = m_xGenericUICommands[rCurrentLanguage]; + + impl_fillElements("ooSetupFactoryCmdCategoryConfigRef"); +} + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_framework_UICategoryDescription_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence const &) +{ + return cppu::acquire(new UICategoryDescription(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uiconfiguration/uiconfigurationmanager.cxx b/framework/source/uiconfiguration/uiconfigurationmanager.cxx new file mode 100644 index 0000000000..b678f7b063 --- /dev/null +++ b/framework/source/uiconfiguration/uiconfigurationmanager.cxx @@ -0,0 +1,1382 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +using namespace com::sun::star::uno; +using namespace com::sun::star::io; +using namespace com::sun::star::embed; +using namespace com::sun::star::lang; +using namespace com::sun::star::container; +using namespace com::sun::star::beans; +using namespace com::sun::star::ui; +using namespace framework; + +namespace { + +class UIConfigurationManager : public ::cppu::WeakImplHelper< + css::lang::XServiceInfo , + css::ui::XUIConfigurationManager2 > +{ +public: + virtual OUString SAL_CALL getImplementationName() override + { + return "com.sun.star.comp.framework.UIConfigurationManager"; + } + + virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override + { + return cppu::supportsService(this, ServiceName); + } + + virtual css::uno::Sequence SAL_CALL getSupportedServiceNames() override + { + return {"com.sun.star.ui.UIConfigurationManager"}; + } + + explicit UIConfigurationManager( css::uno::Reference< css::uno::XComponentContext > xContext ); + + // 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 >& aListener ) override; + + // XUIConfiguration + virtual void SAL_CALL addConfigurationListener( const css::uno::Reference< css::ui::XUIConfigurationListener >& Listener ) override; + virtual void SAL_CALL removeConfigurationListener( const css::uno::Reference< css::ui::XUIConfigurationListener >& Listener ) override; + + // XUIConfigurationManager + virtual void SAL_CALL reset() override; + virtual css::uno::Sequence< css::uno::Sequence< css::beans::PropertyValue > > SAL_CALL getUIElementsInfo( sal_Int16 ElementType ) override; + virtual css::uno::Reference< css::container::XIndexContainer > SAL_CALL createSettings( ) override; + virtual sal_Bool SAL_CALL hasSettings( const OUString& ResourceURL ) override; + virtual css::uno::Reference< css::container::XIndexAccess > SAL_CALL getSettings( const OUString& ResourceURL, sal_Bool bWriteable ) override; + virtual void SAL_CALL replaceSettings( const OUString& ResourceURL, const css::uno::Reference< css::container::XIndexAccess >& aNewData ) override; + virtual void SAL_CALL removeSettings( const OUString& ResourceURL ) override; + virtual void SAL_CALL insertSettings( const OUString& NewResourceURL, const css::uno::Reference< css::container::XIndexAccess >& aNewData ) override; + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL getImageManager() override; + virtual css::uno::Reference< css::ui::XAcceleratorConfiguration > SAL_CALL getShortCutManager() override; + virtual css::uno::Reference< css::ui::XAcceleratorConfiguration > SAL_CALL createShortCutManager() override; + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL getEventsManager() override; + + // XUIConfigurationPersistence + virtual void SAL_CALL reload() override; + virtual void SAL_CALL store() override; + virtual void SAL_CALL storeToStorage( const css::uno::Reference< css::embed::XStorage >& Storage ) override; + virtual sal_Bool SAL_CALL isModified() override; + virtual sal_Bool SAL_CALL isReadOnly() override; + + // XUIConfigurationStorage + virtual void SAL_CALL setStorage( const css::uno::Reference< css::embed::XStorage >& Storage ) override; + virtual sal_Bool SAL_CALL hasStorage() override; + +private: + // private data types + enum NotifyOp + { + NotifyOp_Remove, + NotifyOp_Insert, + NotifyOp_Replace + }; + + struct UIElementInfo + { + UIElementInfo( OUString _aResourceURL, OUString _aUIName ) : + aResourceURL(std::move( _aResourceURL)), aUIName(std::move( _aUIName )) {} + OUString aResourceURL; + OUString aUIName; + }; + + struct UIElementData + { + UIElementData() : bModified( false ), bDefault( true ) {}; + + OUString aResourceURL; + OUString aName; + bool bModified; // has been changed since last storing + bool bDefault; // default settings + css::uno::Reference< css::container::XIndexAccess > xSettings; + }; + + struct UIElementType; + friend struct UIElementType; + typedef std::unordered_map< OUString, UIElementData > UIElementDataHashMap; + + struct UIElementType + { + UIElementType() : bModified( false ), + bLoaded( false ), + nElementType( css::ui::UIElementType::UNKNOWN ) {} + + bool bModified; + bool bLoaded; + sal_Int16 nElementType; + UIElementDataHashMap aElementsHashMap; + css::uno::Reference< css::embed::XStorage > xStorage; + }; + + typedef std::vector< UIElementType > UIElementTypesVector; + typedef std::vector< css::ui::ConfigurationEvent > ConfigEventNotifyContainer; + typedef std::unordered_map< OUString, UIElementInfo > UIElementInfoHashMap; + + void impl_Initialize(); + void implts_notifyContainerListener( const css::ui::ConfigurationEvent& aEvent, NotifyOp eOp ); + void impl_fillSequenceWithElementTypeInfo( UIElementInfoHashMap& aUIElementInfoCollection, sal_Int16 nElementType ); + void impl_preloadUIElementTypeList( sal_Int16 nElementType ); + UIElementData* impl_findUIElementData( const OUString& aResourceURL, sal_Int16 nElementType, bool bLoad = true ); + void impl_requestUIElementData( sal_Int16 nElementType, UIElementData& aUIElementData ); + void impl_storeElementTypeData( css::uno::Reference< css::embed::XStorage > const & xStorage, UIElementType& rElementType, bool bResetModifyState = true ); + void impl_resetElementTypeData( UIElementType& rDocElementType, ConfigEventNotifyContainer& rRemoveNotifyContainer ); + void impl_reloadElementTypeData( UIElementType& rDocElementType, ConfigEventNotifyContainer& rRemoveNotifyContainer, ConfigEventNotifyContainer& rReplaceNotifyContainer ); + + UIElementTypesVector m_aUIElements; + css::uno::Reference< css::embed::XStorage > m_xDocConfigStorage; + bool m_bReadOnly; + bool m_bModified; + bool m_bDisposed; + OUString m_aPropUIName; + css::uno::Reference< css::uno::XComponentContext > m_xContext; + std::mutex m_mutex; + comphelper::OInterfaceContainerHelper4 m_aEventListeners; + comphelper::OInterfaceContainerHelper4 m_aConfigListeners; + rtl::Reference< ImageManager > m_xImageManager; + css::uno::Reference< css::ui::XAcceleratorConfiguration > m_xAccConfig; +}; + +// important: The order and position of the elements must match the constant +// definition of "css::ui::UIElementType" +std::u16string_view UIELEMENTTYPENAMES[] = +{ + u"", // Dummy value for unknown! + u"" UIELEMENTTYPE_MENUBAR_NAME, + u"" UIELEMENTTYPE_POPUPMENU_NAME, + u"" UIELEMENTTYPE_TOOLBAR_NAME, + u"" UIELEMENTTYPE_STATUSBAR_NAME, + u"" UIELEMENTTYPE_FLOATINGWINDOW_NAME, + u"" UIELEMENTTYPE_PROGRESSBAR_NAME, + u"" UIELEMENTTYPE_TOOLPANEL_NAME +}; + +constexpr std::u16string_view RESOURCEURL_PREFIX = u"private:resource/"; + +sal_Int16 RetrieveTypeFromResourceURL( std::u16string_view aResourceURL ) +{ + + if (( o3tl::starts_with(aResourceURL, RESOURCEURL_PREFIX ) ) && + ( aResourceURL.size() > RESOURCEURL_PREFIX.size() )) + { + std::u16string_view aTmpStr = aResourceURL.substr( RESOURCEURL_PREFIX.size() ); + size_t nIndex = aTmpStr.find( '/' ); + if (( nIndex > 0 ) && ( aTmpStr.size() > nIndex )) + { + std::u16string_view aTypeStr( aTmpStr.substr( 0, nIndex )); + for ( int i = 0; i < UIElementType::COUNT; i++ ) + { + if ( aTypeStr == UIELEMENTTYPENAMES[i] ) + return sal_Int16( i ); + } + } + } + + return UIElementType::UNKNOWN; +} + +OUString RetrieveNameFromResourceURL( std::u16string_view aResourceURL ) +{ + if (( o3tl::starts_with(aResourceURL, RESOURCEURL_PREFIX ) ) && + ( aResourceURL.size() > RESOURCEURL_PREFIX.size() )) + { + size_t nIndex = aResourceURL.rfind( '/' ); + if ( (nIndex > 0) && (nIndex != std::u16string_view::npos) && (( nIndex+1 ) < aResourceURL.size()) ) + return OUString(aResourceURL.substr( nIndex+1 )); + } + + return OUString(); +} + +void UIConfigurationManager::impl_fillSequenceWithElementTypeInfo( UIElementInfoHashMap& aUIElementInfoCollection, sal_Int16 nElementType ) +{ + // preload list of element types on demand + impl_preloadUIElementTypeList( nElementType ); + + UIElementDataHashMap& rUserElements = m_aUIElements[nElementType].aElementsHashMap; + + for (auto const& elem : rUserElements) + { + UIElementData* pDataSettings = impl_findUIElementData( elem.second.aResourceURL, nElementType ); + if ( pDataSettings && !pDataSettings->bDefault ) + { + // Retrieve user interface name from XPropertySet interface + OUString aUIName; + Reference< XPropertySet > xPropSet( pDataSettings->xSettings, UNO_QUERY ); + if ( xPropSet.is() ) + { + Any a = xPropSet->getPropertyValue( m_aPropUIName ); + a >>= aUIName; + } + + UIElementInfo aInfo( elem.second.aResourceURL, aUIName ); + aUIElementInfoCollection.emplace( elem.second.aResourceURL, aInfo ); + } + } +} + +void UIConfigurationManager::impl_preloadUIElementTypeList( sal_Int16 nElementType ) +{ + UIElementType& rElementTypeData = m_aUIElements[nElementType]; + + if ( !rElementTypeData.bLoaded ) + { + Reference< XStorage > xElementTypeStorage = rElementTypeData.xStorage; + if ( xElementTypeStorage.is() ) + { + OUString aResURLPrefix = + OUString::Concat(RESOURCEURL_PREFIX) + + UIELEMENTTYPENAMES[ nElementType ] + + "/"; + + UIElementDataHashMap& rHashMap = rElementTypeData.aElementsHashMap; + const Sequence< OUString > aUIElementNames = xElementTypeStorage->getElementNames(); + for ( OUString const & rElementName : aUIElementNames ) + { + UIElementData aUIElementData; + + // Resource name must be without ".xml" + sal_Int32 nIndex = rElementName.lastIndexOf( '.' ); + if (( nIndex > 0 ) && ( nIndex < rElementName.getLength() )) + { + std::u16string_view aExtension( rElementName.subView( nIndex+1 )); + std::u16string_view aUIElementName( rElementName.subView( 0, nIndex )); + + if (!aUIElementName.empty() && + ( o3tl::equalsIgnoreAsciiCase(aExtension, u"xml"))) + { + aUIElementData.aResourceURL = aResURLPrefix + aUIElementName; + aUIElementData.aName = rElementName; + aUIElementData.bModified = false; + aUIElementData.bDefault = false; + + // Create unordered_map entries for all user interface elements inside the storage. We don't load the + // settings to speed up the process. + rHashMap.emplace( aUIElementData.aResourceURL, aUIElementData ); + } + } + } + } + } + + rElementTypeData.bLoaded = true; +} + +void UIConfigurationManager::impl_requestUIElementData( sal_Int16 nElementType, UIElementData& aUIElementData ) +{ + UIElementType& rElementTypeData = m_aUIElements[nElementType]; + + Reference< XStorage > xElementTypeStorage = rElementTypeData.xStorage; + if ( xElementTypeStorage.is() && !aUIElementData.aName.isEmpty() ) + { + try + { + Reference< XStream > xStream = xElementTypeStorage->openStreamElement( aUIElementData.aName, ElementModes::READ ); + Reference< XInputStream > xInputStream = xStream->getInputStream(); + + if ( xInputStream.is() ) + { + switch ( nElementType ) + { + case css::ui::UIElementType::UNKNOWN: + break; + + case css::ui::UIElementType::MENUBAR: + case css::ui::UIElementType::POPUPMENU: + { + try + { + MenuConfiguration aMenuCfg( m_xContext ); + Reference< XIndexAccess > xContainer( aMenuCfg.CreateMenuBarConfigurationFromXML( xInputStream )); + auto pRootItemContainer = dynamic_cast( xContainer.get() ); + if ( pRootItemContainer ) + aUIElementData.xSettings = new ConstItemContainer( pRootItemContainer, true ); + else + aUIElementData.xSettings = new ConstItemContainer( xContainer, true ); + return; + } + catch ( const css::lang::WrappedTargetException& ) + { + } + } + break; + + case css::ui::UIElementType::TOOLBAR: + { + try + { + Reference< XIndexContainer > xIndexContainer( new RootItemContainer() ); + ToolBoxConfiguration::LoadToolBox( m_xContext, xInputStream, xIndexContainer ); + auto pRootItemContainer = dynamic_cast( xIndexContainer.get() ); + aUIElementData.xSettings = new ConstItemContainer( pRootItemContainer, true ); + return; + } + catch ( const css::lang::WrappedTargetException& ) + { + } + + break; + } + + case css::ui::UIElementType::STATUSBAR: + { + try + { + Reference< XIndexContainer > xIndexContainer( new RootItemContainer() ); + StatusBarConfiguration::LoadStatusBar( m_xContext, xInputStream, xIndexContainer ); + auto pRootItemContainer = dynamic_cast( xIndexContainer.get() ); + aUIElementData.xSettings = new ConstItemContainer( pRootItemContainer, true ); + return; + } + catch ( const css::lang::WrappedTargetException& ) + { + } + + break; + } + + case css::ui::UIElementType::FLOATINGWINDOW: + { + break; + } + } + } + } + catch ( const css::embed::InvalidStorageException& ) + { + } + catch ( const css::lang::IllegalArgumentException& ) + { + } + catch ( const css::io::IOException& ) + { + } + catch ( const css::embed::StorageWrappedTargetException& ) + { + } + } + + // At least we provide an empty settings container! + aUIElementData.xSettings = new ConstItemContainer(); +} + +UIConfigurationManager::UIElementData* UIConfigurationManager::impl_findUIElementData( const OUString& aResourceURL, sal_Int16 nElementType, bool bLoad ) +{ + // preload list of element types on demand + impl_preloadUIElementTypeList( nElementType ); + + // try to look into our document vector/unordered_map combination + UIElementDataHashMap& rUserHashMap = m_aUIElements[nElementType].aElementsHashMap; + UIElementDataHashMap::iterator pIter = rUserHashMap.find( aResourceURL ); + if ( pIter != rUserHashMap.end() ) + { + // Default data settings data means removed! + if ( pIter->second.bDefault ) + return &(pIter->second); + else + { + if ( !pIter->second.xSettings.is() && bLoad ) + impl_requestUIElementData( nElementType, pIter->second ); + return &(pIter->second); + } + } + + // Nothing has been found! + return nullptr; +} + +void UIConfigurationManager::impl_storeElementTypeData( Reference< XStorage > const & xStorage, UIElementType& rElementType, bool bResetModifyState ) +{ + UIElementDataHashMap& rHashMap = rElementType.aElementsHashMap; + + for (auto & elem : rHashMap) + { + UIElementData& rElement = elem.second; + if ( rElement.bModified ) + { + if ( rElement.bDefault ) + { + xStorage->removeElement( rElement.aName ); + rElement.bModified = false; // mark as not modified + } + else + { + Reference< XStream > xStream = xStorage->openStreamElement( rElement.aName, ElementModes::WRITE|ElementModes::TRUNCATE ); + Reference< XOutputStream > xOutputStream( xStream->getOutputStream() ); + + if ( xOutputStream.is() ) + { + switch( rElementType.nElementType ) + { + case css::ui::UIElementType::MENUBAR: + case css::ui::UIElementType::POPUPMENU: + { + try + { + MenuConfiguration aMenuCfg( m_xContext ); + aMenuCfg.StoreMenuBarConfigurationToXML( + rElement.xSettings, xOutputStream, rElementType.nElementType == css::ui::UIElementType::MENUBAR ); + } + catch ( const css::lang::WrappedTargetException& ) + { + } + } + break; + + case css::ui::UIElementType::TOOLBAR: + { + try + { + ToolBoxConfiguration::StoreToolBox( m_xContext, xOutputStream, rElement.xSettings ); + } + catch ( const css::lang::WrappedTargetException& ) + { + } + } + break; + + case css::ui::UIElementType::STATUSBAR: + { + try + { + StatusBarConfiguration::StoreStatusBar( m_xContext, xOutputStream, rElement.xSettings ); + } + catch ( const css::lang::WrappedTargetException& ) + { + } + } + break; + + default: + break; + } + } + + // mark as not modified if we store to our own storage + if ( bResetModifyState ) + rElement.bModified = false; + } + } + } + + // commit element type storage + Reference< XTransactedObject > xTransactedObject( xStorage, UNO_QUERY ); + if ( xTransactedObject.is() ) + xTransactedObject->commit(); + + // mark UIElementType as not modified if we store to our own storage + if ( bResetModifyState ) + rElementType.bModified = false; +} + +void UIConfigurationManager::impl_resetElementTypeData( + UIElementType& rDocElementType, + ConfigEventNotifyContainer& rRemoveNotifyContainer ) +{ + UIElementDataHashMap& rHashMap = rDocElementType.aElementsHashMap; + + Reference< XUIConfigurationManager > xThis(this); + Reference< XInterface > xIfac( xThis, UNO_QUERY ); + + // Make copies of the event structures to be thread-safe. We have to unlock our mutex before calling + // our listeners! + for (auto & elem : rHashMap) + { + UIElementData& rElement = elem.second; + if ( !rElement.bDefault ) + { + // Remove user-defined settings from document + ConfigurationEvent aEvent; + aEvent.ResourceURL = rElement.aResourceURL; + aEvent.Accessor <<= xThis; + aEvent.Source = xIfac; + aEvent.Element <<= rElement.xSettings; + + rRemoveNotifyContainer.push_back( aEvent ); + + // Mark element as default. + rElement.bModified = false; + rElement.bDefault = true; + } + else + rElement.bModified = false; + } + + // Remove all settings from our user interface elements + rHashMap.clear(); +} + +void UIConfigurationManager::impl_reloadElementTypeData( + UIElementType& rDocElementType, + ConfigEventNotifyContainer& rRemoveNotifyContainer, + ConfigEventNotifyContainer& rReplaceNotifyContainer ) +{ + UIElementDataHashMap& rHashMap = rDocElementType.aElementsHashMap; + Reference< XStorage > xElementStorage( rDocElementType.xStorage ); + + Reference< XUIConfigurationManager > xThis(this); + Reference< XInterface > xIfac( xThis, UNO_QUERY ); + sal_Int16 nType = rDocElementType.nElementType; + + for (auto & elem : rHashMap) + { + UIElementData& rElement = elem.second; + if ( rElement.bModified ) + { + if ( xElementStorage->hasByName( rElement.aName )) + { + // Replace settings with data from user layer + Reference< XIndexAccess > xOldSettings( rElement.xSettings ); + + impl_requestUIElementData( nType, rElement ); + + ConfigurationEvent aReplaceEvent; + + aReplaceEvent.ResourceURL = rElement.aResourceURL; + aReplaceEvent.Accessor <<= xThis; + aReplaceEvent.Source = xIfac; + aReplaceEvent.ReplacedElement <<= xOldSettings; + aReplaceEvent.Element <<= rElement.xSettings; + rReplaceNotifyContainer.push_back( aReplaceEvent ); + + rElement.bModified = false; + } + else + { + // Element settings are not in any storage => remove + ConfigurationEvent aRemoveEvent; + + aRemoveEvent.ResourceURL = rElement.aResourceURL; + aRemoveEvent.Accessor <<= xThis; + aRemoveEvent.Source = xIfac; + aRemoveEvent.Element <<= rElement.xSettings; + + rRemoveNotifyContainer.push_back( aRemoveEvent ); + + // Mark element as default and not modified. That means "not active" in the document anymore + rElement.bModified = false; + rElement.bDefault = true; + } + } + } + + rDocElementType.bModified = false; +} + +void UIConfigurationManager::impl_Initialize() +{ + // Initialize the top-level structures with the storage data + if ( m_xDocConfigStorage.is() ) + { + tools::Long nModes = m_bReadOnly ? ElementModes::READ : ElementModes::READWRITE; + + // Try to access our module sub folder + for ( sal_Int16 i = 1; i < css::ui::UIElementType::COUNT; + i++ ) + { + Reference< XStorage > xElementTypeStorage; + try + { + xElementTypeStorage = m_xDocConfigStorage->openStorageElement( OUString(UIELEMENTTYPENAMES[i]), nModes ); + } + catch ( const css::container::NoSuchElementException& ) + { + } + catch ( const css::embed::InvalidStorageException& ) + { + } + catch ( const css::lang::IllegalArgumentException& ) + { + } + catch ( const css::io::IOException& ) + { + } + catch ( const css::embed::StorageWrappedTargetException& ) + { + } + + m_aUIElements[i].nElementType = i; + m_aUIElements[i].bModified = false; + m_aUIElements[i].xStorage = xElementTypeStorage; + } + } + else + { + // We have no storage, just initialize ui element types with empty storage! + for ( int i = 1; i < css::ui::UIElementType::COUNT; i++ ) + m_aUIElements[i].xStorage = m_xDocConfigStorage; + } +} + +UIConfigurationManager::UIConfigurationManager( css::uno::Reference< css::uno::XComponentContext > xContext ) : + m_bReadOnly( true ) + , m_bModified( false ) + , m_bDisposed( false ) + , m_aPropUIName( "UIName" ) + , m_xContext(std::move( xContext )) +{ + // Make sure we have a default initialized entry for every layer and user interface element type! + // The following code depends on this! + m_aUIElements.resize( css::ui::UIElementType::COUNT ); +} + +// XComponent +void SAL_CALL UIConfigurationManager::dispose() +{ + Reference< XComponent > xThis(this); + + css::lang::EventObject aEvent( xThis ); + { + std::unique_lock aGuard(m_mutex); + m_aEventListeners.disposeAndClear( aGuard, aEvent ); + } + { + std::unique_lock aGuard(m_mutex); + m_aConfigListeners.disposeAndClear( aGuard, aEvent ); + } + + { + SolarMutexGuard g; + try + { + if ( m_xImageManager.is() ) + m_xImageManager->dispose(); + } + catch ( const Exception& ) + { + } + + m_xImageManager.clear(); + m_aUIElements.clear(); + m_xDocConfigStorage.clear(); + m_bModified = false; + m_bDisposed = true; + } +} + +void SAL_CALL UIConfigurationManager::addEventListener( const Reference< XEventListener >& xListener ) +{ + { + SolarMutexGuard g; + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + if ( m_bDisposed ) + throw DisposedException(); + } + + std::unique_lock aGuard(m_mutex); + m_aEventListeners.addInterface( aGuard, xListener ); +} + +void SAL_CALL UIConfigurationManager::removeEventListener( const Reference< XEventListener >& xListener ) +{ + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + std::unique_lock aGuard(m_mutex); + m_aEventListeners.removeInterface( aGuard, xListener ); +} + +// XUIConfigurationManager +void SAL_CALL UIConfigurationManager::addConfigurationListener( const Reference< css::ui::XUIConfigurationListener >& xListener ) +{ + { + SolarMutexGuard g; + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + if ( m_bDisposed ) + throw DisposedException(); + } + + std::unique_lock aGuard(m_mutex); + m_aConfigListeners.addInterface( aGuard, xListener ); +} + +void SAL_CALL UIConfigurationManager::removeConfigurationListener( const Reference< css::ui::XUIConfigurationListener >& xListener ) +{ + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + std::unique_lock aGuard(m_mutex); + m_aConfigListeners.removeInterface( aGuard, xListener ); +} + +void SAL_CALL UIConfigurationManager::reset() +{ + SolarMutexClearableGuard aGuard; + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + if ( m_bDisposed ) + throw DisposedException(); + + if ( isReadOnly() ) + return; + + if ( !m_xDocConfigStorage.is() ) + return; + + try + { + // Remove all elements from our user-defined storage! + bool bCommit( false ); + for ( int i = 1; i < css::ui::UIElementType::COUNT; i++ ) + { + UIElementType& rElementType = m_aUIElements[i]; + + if ( rElementType.xStorage.is() ) + { + bool bCommitSubStorage( false ); + const Sequence< OUString > aUIElementStreamNames = rElementType.xStorage->getElementNames(); + for ( OUString const & rStreamName : aUIElementStreamNames ) + { + rElementType.xStorage->removeElement( rStreamName ); + bCommitSubStorage = true; + bCommit = true; + } + + if ( bCommitSubStorage ) + { + Reference< XTransactedObject > xTransactedObject( rElementType.xStorage, UNO_QUERY ); + if ( xTransactedObject.is() ) + xTransactedObject->commit(); + } + } + } + + // Commit changes + if ( bCommit ) + { + Reference< XTransactedObject > xTransactedObject( m_xDocConfigStorage, UNO_QUERY ); + if ( xTransactedObject.is() ) + xTransactedObject->commit(); + } + + // remove settings from user defined layer and notify listener about removed settings data! + // Try to access our module sub folder + ConfigEventNotifyContainer aRemoveEventNotifyContainer; + for ( sal_Int16 j = 1; j < css::ui::UIElementType::COUNT; j++ ) + { + UIElementType& rDocElementType = m_aUIElements[j]; + + impl_resetElementTypeData( rDocElementType, aRemoveEventNotifyContainer ); + rDocElementType.bModified = false; + } + + m_bModified = false; + + // Unlock mutex before notify our listeners + aGuard.clear(); + + // Notify our listeners + for (const ConfigurationEvent & k : aRemoveEventNotifyContainer) + implts_notifyContainerListener( k, NotifyOp_Remove ); + } + catch ( const css::lang::IllegalArgumentException& ) + { + } + catch ( const css::container::NoSuchElementException& ) + { + } + catch ( const css::embed::InvalidStorageException& ) + { + } + catch ( const css::embed::StorageWrappedTargetException& ) + { + } +} + +Sequence< Sequence< PropertyValue > > SAL_CALL UIConfigurationManager::getUIElementsInfo( sal_Int16 ElementType ) +{ + if (( ElementType < 0 ) || ( ElementType >= css::ui::UIElementType::COUNT )) + throw IllegalArgumentException(); + + SolarMutexGuard g; + if ( m_bDisposed ) + throw DisposedException(); + + std::vector< Sequence< PropertyValue > > aElementInfoSeq; + UIElementInfoHashMap aUIElementInfoCollection; + + if ( ElementType == css::ui::UIElementType::UNKNOWN ) + { + for ( sal_Int16 i = 0; i < css::ui::UIElementType::COUNT; i++ ) + impl_fillSequenceWithElementTypeInfo( aUIElementInfoCollection, i ); + } + else + impl_fillSequenceWithElementTypeInfo( aUIElementInfoCollection, ElementType ); + + aElementInfoSeq.resize( aUIElementInfoCollection.size() ); + sal_Int32 n = 0; + for (auto const& elem : aUIElementInfoCollection) + { + Sequence< PropertyValue > aUIElementInfo{ + comphelper::makePropertyValue("ResourceURL", elem.second.aResourceURL), + comphelper::makePropertyValue(m_aPropUIName, elem.second.aUIName) + }; + aElementInfoSeq[n++] = aUIElementInfo; + } + + return comphelper::containerToSequence(aElementInfoSeq); +} + +Reference< XIndexContainer > SAL_CALL UIConfigurationManager::createSettings() +{ + SolarMutexGuard g; + + if ( m_bDisposed ) + throw DisposedException(); + + // Creates an empty item container which can be filled from outside + return Reference< XIndexContainer >( new RootItemContainer() ); +} + +sal_Bool SAL_CALL UIConfigurationManager::hasSettings( const OUString& ResourceURL ) +{ + sal_Int16 nElementType = RetrieveTypeFromResourceURL( ResourceURL ); + + if (( nElementType == css::ui::UIElementType::UNKNOWN ) || + ( nElementType >= css::ui::UIElementType::COUNT )) + throw IllegalArgumentException(); + UIElementData* pDataSettings = impl_findUIElementData( ResourceURL, nElementType, false ); + if ( pDataSettings && !pDataSettings->bDefault ) + return true; + + return false; +} + +Reference< XIndexAccess > SAL_CALL UIConfigurationManager::getSettings( const OUString& ResourceURL, sal_Bool bWriteable ) +{ + sal_Int16 nElementType = RetrieveTypeFromResourceURL( ResourceURL ); + + if (( nElementType == css::ui::UIElementType::UNKNOWN ) || + ( nElementType >= css::ui::UIElementType::COUNT )) + throw IllegalArgumentException(); + + SolarMutexGuard g; + + if ( m_bDisposed ) + throw DisposedException(); + + UIElementData* pDataSettings = impl_findUIElementData( ResourceURL, nElementType ); + if ( pDataSettings && !pDataSettings->bDefault ) + { + // Create a copy of our data if someone wants to change the data. + if ( bWriteable ) + return Reference< XIndexAccess >( new RootItemContainer( pDataSettings->xSettings ) ); + else + return pDataSettings->xSettings; + } + + throw NoSuchElementException(); +} + +void SAL_CALL UIConfigurationManager::replaceSettings( const OUString& ResourceURL, const Reference< css::container::XIndexAccess >& aNewData ) +{ + sal_Int16 nElementType = RetrieveTypeFromResourceURL( ResourceURL ); + + if (( nElementType == css::ui::UIElementType::UNKNOWN ) || + ( nElementType >= css::ui::UIElementType::COUNT )) + throw IllegalArgumentException(); + else if ( m_bReadOnly ) + throw IllegalAccessException(); + else + { + SolarMutexClearableGuard aGuard; + + if ( m_bDisposed ) + throw DisposedException(); + + UIElementData* pDataSettings = impl_findUIElementData( ResourceURL, nElementType ); + if ( !pDataSettings || pDataSettings->bDefault ) + throw NoSuchElementException(); + // we have a settings entry in our user-defined layer - replace + Reference< XIndexAccess > xOldSettings = pDataSettings->xSettings; + + // Create a copy of the data if the container is not const + Reference< XIndexReplace > xReplace( aNewData, UNO_QUERY ); + if ( xReplace.is() ) + pDataSettings->xSettings = new ConstItemContainer( aNewData ); + else + pDataSettings->xSettings = aNewData; + + pDataSettings->bDefault = false; + pDataSettings->bModified = true; + m_bModified = true; + + // Modify type container + UIElementType& rElementType = m_aUIElements[nElementType]; + rElementType.bModified = true; + + Reference< XUIConfigurationManager > xThis(this); + Reference< XInterface > xIfac( xThis, UNO_QUERY ); + + // Create event to notify listener about replaced element settings + ConfigurationEvent aEvent; + + aEvent.ResourceURL = ResourceURL; + aEvent.Accessor <<= xThis; + aEvent.Source = xIfac; + aEvent.ReplacedElement <<= xOldSettings; + aEvent.Element <<= pDataSettings->xSettings; + + aGuard.clear(); + + implts_notifyContainerListener( aEvent, NotifyOp_Replace ); + } +} + +void SAL_CALL UIConfigurationManager::removeSettings( const OUString& ResourceURL ) +{ + sal_Int16 nElementType = RetrieveTypeFromResourceURL( ResourceURL ); + + if (( nElementType == css::ui::UIElementType::UNKNOWN ) || + ( nElementType >= css::ui::UIElementType::COUNT )) + throw IllegalArgumentException( "The ResourceURL is not valid or " + "describes an unknown type. " + "ResourceURL: " + ResourceURL, nullptr, 0 ); + else if ( m_bReadOnly ) + throw IllegalAccessException( "The configuration manager is read-only. " + "ResourceURL: " + ResourceURL, nullptr ); + else + { + SolarMutexClearableGuard aGuard; + + if ( m_bDisposed ) + throw DisposedException( "The configuration manager has been disposed, " + "and can't uphold its method specification anymore. " + "ResourceURL: " + ResourceURL, nullptr ); + + UIElementData* pDataSettings = impl_findUIElementData( ResourceURL, nElementType ); + if ( !pDataSettings ) + throw NoSuchElementException( "The settings data cannot be found. " + "ResourceURL: " + ResourceURL, nullptr); + // If element settings are default, we don't need to change anything! + if ( pDataSettings->bDefault ) + return; + else + { + Reference< XIndexAccess > xRemovedSettings = pDataSettings->xSettings; + pDataSettings->bDefault = true; + + // check if this is a default layer node + pDataSettings->bModified = true; // we have to remove this node from the user layer! + pDataSettings->xSettings.clear(); + m_bModified = true; // user layer must be written + + // Modify type container + UIElementType& rElementType = m_aUIElements[nElementType]; + rElementType.bModified = true; + + Reference< XUIConfigurationManager > xThis(this); + + // Create event to notify listener about removed element settings + ConfigurationEvent aEvent; + + aEvent.ResourceURL = ResourceURL; + aEvent.Accessor <<= xThis; + aEvent.Source.set(xThis, UNO_QUERY); + + aEvent.Element <<= xRemovedSettings; + + aGuard.clear(); + + implts_notifyContainerListener( aEvent, NotifyOp_Remove ); + } + } +} + +void SAL_CALL UIConfigurationManager::insertSettings( const OUString& NewResourceURL, const Reference< XIndexAccess >& aNewData ) +{ + sal_Int16 nElementType = RetrieveTypeFromResourceURL( NewResourceURL ); + + if (( nElementType == css::ui::UIElementType::UNKNOWN ) || + ( nElementType >= css::ui::UIElementType::COUNT )) + throw IllegalArgumentException(); + else if ( m_bReadOnly ) + throw IllegalAccessException(); + else + { + SolarMutexClearableGuard aGuard; + + if ( m_bDisposed ) + throw DisposedException(); + + bool bInsertData( false ); + UIElementData aUIElementData; + UIElementData* pDataSettings = impl_findUIElementData( NewResourceURL, nElementType ); + + if ( pDataSettings && !pDataSettings->bDefault ) + throw ElementExistException(); + + if ( !pDataSettings ) + { + pDataSettings = &aUIElementData; + bInsertData = true; + } + + { + pDataSettings->bDefault = false; + pDataSettings->bModified = true; + + // Create a copy of the data if the container is not const + Reference< XIndexReplace > xReplace( aNewData, UNO_QUERY ); + if ( xReplace.is() ) + pDataSettings->xSettings = new ConstItemContainer( aNewData ); + else + pDataSettings->xSettings = aNewData; + + m_bModified = true; + + UIElementType& rElementType = m_aUIElements[nElementType]; + rElementType.bModified = true; + + if ( bInsertData ) + { + pDataSettings->aName = RetrieveNameFromResourceURL( NewResourceURL ) + ".xml"; + pDataSettings->aResourceURL = NewResourceURL; + + UIElementDataHashMap& rElements = rElementType.aElementsHashMap; + rElements.emplace( NewResourceURL, *pDataSettings ); + } + + Reference< XIndexAccess > xInsertSettings( aUIElementData.xSettings ); + Reference< XUIConfigurationManager > xThis(this); + Reference< XInterface > xIfac( xThis, UNO_QUERY ); + + // Create event to notify listener about removed element settings + ConfigurationEvent aEvent; + + aEvent.ResourceURL = NewResourceURL; + aEvent.Accessor <<= xThis; + aEvent.Source = xIfac; + aEvent.Element <<= xInsertSettings; + + aGuard.clear(); + + implts_notifyContainerListener( aEvent, NotifyOp_Insert ); + } + } +} + +Reference< XInterface > SAL_CALL UIConfigurationManager::getImageManager() +{ + if ( m_bDisposed ) + throw DisposedException(); + + if ( !m_xImageManager.is() ) + { + m_xImageManager = new ImageManager( m_xContext, /*bForModule*/false ); + + Sequence aPropSeq(comphelper::InitAnyPropertySequence( + { + {"UserConfigStorage", Any(m_xDocConfigStorage)}, + {"ModuleIdentifier", Any(OUString())}, + })); + + m_xImageManager->initialize( aPropSeq ); + } + + return Reference< XInterface >( static_cast(m_xImageManager.get()), UNO_QUERY ); +} + +Reference< XAcceleratorConfiguration > SAL_CALL UIConfigurationManager::createShortCutManager() +{ + return DocumentAcceleratorConfiguration::createWithDocumentRoot(m_xContext, m_xDocConfigStorage); +} + +Reference< XAcceleratorConfiguration > SAL_CALL UIConfigurationManager::getShortCutManager() +{ + // SAFE -> + SolarMutexGuard g; + + if (!m_xAccConfig.is()) try + { + m_xAccConfig = DocumentAcceleratorConfiguration:: + createWithDocumentRoot(m_xContext, m_xDocConfigStorage); + } + catch ( const css::uno::DeploymentException& ) + { + SAL_WARN("fwk.uiconfiguration", "DocumentAcceleratorConfiguration" + " not available. This should happen only on mobile platforms."); + } + + return m_xAccConfig; +} + +Reference< XInterface > SAL_CALL UIConfigurationManager::getEventsManager() +{ + return Reference< XInterface >(); +} + +// XUIConfigurationStorage +void SAL_CALL UIConfigurationManager::setStorage( const Reference< XStorage >& Storage ) +{ + SolarMutexGuard g; + + if ( m_bDisposed ) + throw DisposedException(); + + if ( m_xDocConfigStorage.is() ) + { + try + { + // Dispose old storage to be sure that it will be closed + m_xDocConfigStorage->dispose(); + } + catch ( const Exception& ) + { + } + } + + // We store the new storage. Be careful it could be an empty reference! + m_xDocConfigStorage = Storage; + m_bReadOnly = true; + + if ( m_xAccConfig.is() ) + m_xAccConfig->setStorage( m_xDocConfigStorage ); + + if ( m_xImageManager ) + m_xImageManager->setStorage( m_xDocConfigStorage ); + + if ( m_xDocConfigStorage.is() ) + { + Reference< XPropertySet > xPropSet( m_xDocConfigStorage, UNO_QUERY ); + if ( xPropSet.is() ) + { + try + { + tools::Long nOpenMode = 0; + Any a = xPropSet->getPropertyValue("OpenMode"); + if ( a >>= nOpenMode ) + m_bReadOnly = !( nOpenMode & ElementModes::WRITE ); + } + catch ( const css::beans::UnknownPropertyException& ) + { + } + catch ( const css::lang::WrappedTargetException& ) + { + } + } + } + + impl_Initialize(); +} + +sal_Bool SAL_CALL UIConfigurationManager::hasStorage() +{ + SolarMutexGuard g; + + if ( m_bDisposed ) + throw DisposedException(); + + return m_xDocConfigStorage.is(); +} + +// XUIConfigurationPersistence +void SAL_CALL UIConfigurationManager::reload() +{ + SolarMutexClearableGuard aGuard; + + if ( m_bDisposed ) + throw DisposedException(); + + if ( !m_xDocConfigStorage.is() || !m_bModified || m_bReadOnly ) + return; + + // Try to access our module sub folder + ConfigEventNotifyContainer aRemoveNotifyContainer; + ConfigEventNotifyContainer aReplaceNotifyContainer; + for ( sal_Int16 i = 1; i < css::ui::UIElementType::COUNT; i++ ) + { + try + { + UIElementType& rDocElementType = m_aUIElements[i]; + if ( rDocElementType.bModified ) + impl_reloadElementTypeData( rDocElementType, aRemoveNotifyContainer, aReplaceNotifyContainer ); + } + catch ( const Exception& ) + { + throw IOException(); + } + } + + m_bModified = false; + + // Unlock mutex before notify our listeners + aGuard.clear(); + + // Notify our listeners + for (const ConfigurationEvent & j : aRemoveNotifyContainer) + implts_notifyContainerListener( j, NotifyOp_Remove ); + for (const ConfigurationEvent & k : aReplaceNotifyContainer) + implts_notifyContainerListener( k, NotifyOp_Replace ); +} + +void SAL_CALL UIConfigurationManager::store() +{ + SolarMutexGuard g; + + if ( m_bDisposed ) + throw DisposedException(); + + if ( !m_xDocConfigStorage.is() || !m_bModified || m_bReadOnly ) + return; + + // Try to access our module sub folder + for ( int i = 1; i < css::ui::UIElementType::COUNT; i++ ) + { + try + { + UIElementType& rElementType = m_aUIElements[i]; + + if ( rElementType.bModified && rElementType.xStorage.is() ) + impl_storeElementTypeData( rElementType.xStorage, rElementType ); + } + catch ( const Exception& ) + { + throw IOException(); + } + } + + m_bModified = false; + Reference< XTransactedObject > xTransactedObject( m_xDocConfigStorage, UNO_QUERY ); + if ( xTransactedObject.is() ) + xTransactedObject->commit(); +} + +void SAL_CALL UIConfigurationManager::storeToStorage( const Reference< XStorage >& Storage ) +{ + SolarMutexGuard g; + + if ( m_bDisposed ) + throw DisposedException(); + + if ( !m_xDocConfigStorage.is() || !m_bModified || m_bReadOnly ) + return; + + // Try to access our module sub folder + for ( int i = 1; i < css::ui::UIElementType::COUNT; i++ ) + { + try + { + Reference< XStorage > xElementTypeStorage( Storage->openStorageElement( + OUString(UIELEMENTTYPENAMES[i]), ElementModes::READWRITE )); + UIElementType& rElementType = m_aUIElements[i]; + + if ( rElementType.bModified && xElementTypeStorage.is() ) + impl_storeElementTypeData( xElementTypeStorage, rElementType, false ); // store data to storage, but don't reset modify flag! + } + catch ( const Exception& ) + { + throw IOException(); + } + } + + Reference< XTransactedObject > xTransactedObject( Storage, UNO_QUERY ); + if ( xTransactedObject.is() ) + xTransactedObject->commit(); +} + +sal_Bool SAL_CALL UIConfigurationManager::isModified() +{ + SolarMutexGuard g; + + return m_bModified; +} + +sal_Bool SAL_CALL UIConfigurationManager::isReadOnly() +{ + SolarMutexGuard g; + + return m_bReadOnly; +} + +void UIConfigurationManager::implts_notifyContainerListener( const ConfigurationEvent& aEvent, NotifyOp eOp ) +{ + std::unique_lock aGuard(m_mutex); + m_aConfigListeners.forEach(aGuard, [&eOp, &aEvent](const css::uno::Reference& l) { + switch ( eOp ) + { + case NotifyOp_Replace: + l->elementReplaced( aEvent ); + break; + case NotifyOp_Insert: + l->elementInserted( aEvent ); + break; + case NotifyOp_Remove: + l->elementRemoved( aEvent ); + break; + } + }); +} + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_framework_UIConfigurationManager_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence const &) +{ + return cppu::acquire(new UIConfigurationManager(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uiconfiguration/windowstateconfiguration.cxx b/framework/source/uiconfiguration/windowstateconfiguration.cxx new file mode 100644 index 0000000000..be600fae3c --- /dev/null +++ b/framework/source/uiconfiguration/windowstateconfiguration.cxx @@ -0,0 +1,1391 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +using namespace com::sun::star::uno; +using namespace com::sun::star::lang; +using namespace com::sun::star::beans; +using namespace com::sun::star::util; +using namespace com::sun::star::configuration; +using namespace com::sun::star::container; +using namespace ::com::sun::star::frame; +using namespace ::com::sun::star::ui; +using namespace framework; + +namespace { + +// Zero based indexes, order must be the same as WindowStateMask && CONFIGURATION_PROPERTIES! +const sal_Int16 PROPERTY_LOCKED = 0; +const sal_Int16 PROPERTY_DOCKED = 1; +const sal_Int16 PROPERTY_VISIBLE = 2; +const sal_Int16 PROPERTY_CONTEXT = 3; +const sal_Int16 PROPERTY_HIDEFROMMENU = 4; +const sal_Int16 PROPERTY_NOCLOSE = 5; +const sal_Int16 PROPERTY_SOFTCLOSE = 6; +const sal_Int16 PROPERTY_CONTEXTACTIVE = 7; +const sal_Int16 PROPERTY_DOCKINGAREA = 8; +const sal_Int16 PROPERTY_POS = 9; +const sal_Int16 PROPERTY_SIZE = 10; +const sal_Int16 PROPERTY_UINAME = 11; +const sal_Int16 PROPERTY_INTERNALSTATE = 12; +const sal_Int16 PROPERTY_STYLE = 13; +const sal_Int16 PROPERTY_DOCKPOS = 14; +const sal_Int16 PROPERTY_DOCKSIZE = 15; + +// Order must be the same as WindowStateMask!! +constexpr OUString CONFIGURATION_PROPERTIES[] +{ + WINDOWSTATE_PROPERTY_LOCKED, + WINDOWSTATE_PROPERTY_DOCKED, + WINDOWSTATE_PROPERTY_VISIBLE, + WINDOWSTATE_PROPERTY_CONTEXT, + WINDOWSTATE_PROPERTY_HIDEFROMENU, + WINDOWSTATE_PROPERTY_NOCLOSE, + WINDOWSTATE_PROPERTY_SOFTCLOSE, + WINDOWSTATE_PROPERTY_CONTEXTACTIVE, + WINDOWSTATE_PROPERTY_DOCKINGAREA, + WINDOWSTATE_PROPERTY_POS, + WINDOWSTATE_PROPERTY_SIZE, + WINDOWSTATE_PROPERTY_UINAME, + WINDOWSTATE_PROPERTY_INTERNALSTATE, + WINDOWSTATE_PROPERTY_STYLE, + WINDOWSTATE_PROPERTY_DOCKPOS, + WINDOWSTATE_PROPERTY_DOCKSIZE +}; + +// Configuration access class for WindowState supplier implementation + +class ConfigurationAccess_WindowState : public ::cppu::WeakImplHelper< XNameContainer, XContainerListener > +{ + public: + ConfigurationAccess_WindowState( std::u16string_view aWindowStateConfigFile, const Reference< XComponentContext >& rxContext ); + virtual ~ConfigurationAccess_WindowState() 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; + + // XNameContainer + virtual void SAL_CALL removeByName( const OUString& sName ) override; + + virtual void SAL_CALL insertByName( const OUString& sName, const css::uno::Any& aPropertySet ) override; + + // XNameReplace + virtual void SAL_CALL replaceByName( const OUString& sName, const css::uno::Any& aPropertySet ) override; + + // XElementAccess + virtual css::uno::Type SAL_CALL getElementType() override; + + virtual sal_Bool SAL_CALL hasElements() override; + + // container.XContainerListener + virtual void SAL_CALL elementInserted( const ContainerEvent& aEvent ) override; + virtual void SAL_CALL elementRemoved ( const ContainerEvent& aEvent ) override; + virtual void SAL_CALL elementReplaced( const ContainerEvent& aEvent ) override; + + // lang.XEventListener + virtual void SAL_CALL disposing( const EventObject& aEvent ) override; + + protected: + enum // WindowStateMask + { + WINDOWSTATE_MASK_DOCKINGAREA = 256, + WINDOWSTATE_MASK_POS = 512, + WINDOWSTATE_MASK_SIZE = 1024, + WINDOWSTATE_MASK_UINAME = 2048, + WINDOWSTATE_MASK_INTERNALSTATE = 4096, + WINDOWSTATE_MASK_STYLE = 8192, + WINDOWSTATE_MASK_DOCKPOS = 16384, + WINDOWSTATE_MASK_DOCKSIZE = 32768 + }; + + // Cache structure. Valid values are described by the eMask member. All other values should not be + // provided to outside code! + struct WindowStateInfo + { + WindowStateInfo() + : bLocked(false) + , bDocked(false) + , bVisible(false) + , bContext(false) + , bHideFromMenu(false) + , bNoClose(false) + , bSoftClose(false) + , bContextActive(false) + , aDockingArea(css::ui::DockingArea_DOCKINGAREA_TOP) + , aDockPos(0, 0) + , aPos(0, 0) + , aSize(0, 0) + , nInternalState(0) + , nStyle(0) + , nMask(0) + { + } + + bool bLocked : 1, + bDocked : 1, + bVisible : 1, + bContext : 1, + bHideFromMenu : 1, + bNoClose : 1, + bSoftClose : 1, + bContextActive : 1; + css::ui::DockingArea aDockingArea; + css::awt::Point aDockPos; + css::awt::Size aDockSize; + css::awt::Point aPos; + css::awt::Size aSize; + OUString aUIName; + sal_uInt32 nInternalState; + sal_uInt16 nStyle; + sal_uInt32 nMask; // see WindowStateMask + }; + + void impl_putPropertiesFromStruct( const WindowStateInfo& rWinStateInfo, Reference< XPropertySet > const & xPropSet ); + Any impl_insertCacheAndReturnSequence( const OUString& rResourceURL, Reference< XNameAccess > const & rNameAccess ); + WindowStateInfo& impl_insertCacheAndReturnWinState( const OUString& rResourceURL, Reference< XNameAccess > const & rNameAccess ); + Any impl_getSequenceFromStruct( const WindowStateInfo& rWinStateInfo ); + void impl_fillStructFromSequence( WindowStateInfo& rWinStateInfo, const Sequence< PropertyValue >& rSeq ); + Any impl_getWindowStateFromResourceURL( const OUString& rResourceURL ); + void impl_initializeConfigAccess(); + + private: + typedef std::unordered_map< OUString, + WindowStateInfo > ResourceURLToInfoCache; + + std::mutex m_aMutex; + OUString m_aConfigWindowAccess; + Reference< XMultiServiceFactory > m_xConfigProvider; + Reference< XNameAccess > m_xConfigAccess; + Reference< XContainerListener > m_xConfigListener; + ResourceURLToInfoCache m_aResourceURLToInfoCache; + bool m_bConfigAccessInitialized : 1, + m_bModified : 1; + std::vector< OUString > m_aPropArray; +}; + +ConfigurationAccess_WindowState::ConfigurationAccess_WindowState( std::u16string_view aModuleName, const Reference< XComponentContext >& rxContext ) : + // Create configuration hierarchical access name + m_aConfigWindowAccess( + OUString::Concat("/org.openoffice.Office.UI.") + aModuleName + "/UIElements/States"), + m_xConfigProvider(theDefaultProvider::get( rxContext )), + m_bConfigAccessInitialized( false ), + m_bModified( false ) +{ + // Initialize access array with property names. + for (const OUString & s : CONFIGURATION_PROPERTIES ) + m_aPropArray.push_back(s); +} + +ConfigurationAccess_WindowState::~ConfigurationAccess_WindowState() +{ + // SAFE + std::unique_lock g(m_aMutex); + Reference< XContainer > xContainer( m_xConfigAccess, UNO_QUERY ); + if ( xContainer.is() ) + xContainer->removeContainerListener(m_xConfigListener); +} + +// XNameAccess +Any SAL_CALL ConfigurationAccess_WindowState::getByName( const OUString& rResourceURL ) +{ + // SAFE + std::unique_lock g(m_aMutex); + + ResourceURLToInfoCache::const_iterator pIter = m_aResourceURLToInfoCache.find( rResourceURL ); + if ( pIter != m_aResourceURLToInfoCache.end() ) + return impl_getSequenceFromStruct( pIter->second ); + else + { + Any a( impl_getWindowStateFromResourceURL( rResourceURL ) ); + if ( a == Any() ) + throw NoSuchElementException(); + return a; + } +} + +Sequence< OUString > SAL_CALL ConfigurationAccess_WindowState::getElementNames() +{ + // SAFE + std::unique_lock g(m_aMutex); + + if ( !m_bConfigAccessInitialized ) + { + impl_initializeConfigAccess(); + m_bConfigAccessInitialized = true; + } + + if ( m_xConfigAccess.is() ) + return m_xConfigAccess->getElementNames(); + else + return Sequence< OUString > (); +} + +sal_Bool SAL_CALL ConfigurationAccess_WindowState::hasByName( const OUString& rResourceURL ) +{ + // SAFE + std::unique_lock g(m_aMutex); + + ResourceURLToInfoCache::const_iterator pIter = m_aResourceURLToInfoCache.find( rResourceURL ); + if ( pIter != m_aResourceURLToInfoCache.end() ) + return true; + else + { + Any a( impl_getWindowStateFromResourceURL( rResourceURL ) ); + if ( a == Any() ) + return false; + else + return true; + } +} + +// XElementAccess +Type SAL_CALL ConfigurationAccess_WindowState::getElementType() +{ + return cppu::UnoType>::get(); +} + +sal_Bool SAL_CALL ConfigurationAccess_WindowState::hasElements() +{ + // SAFE + std::unique_lock g(m_aMutex); + + if ( !m_bConfigAccessInitialized ) + { + impl_initializeConfigAccess(); + m_bConfigAccessInitialized = true; + } + + if ( m_xConfigAccess.is() ) + return m_xConfigAccess->hasElements(); + else + return false; +} + +// XNameContainer +void SAL_CALL ConfigurationAccess_WindowState::removeByName( const OUString& rResourceURL ) +{ + // SAFE + std::unique_lock g(m_aMutex); + + ResourceURLToInfoCache::iterator pIter = m_aResourceURLToInfoCache.find( rResourceURL ); + if ( pIter != m_aResourceURLToInfoCache.end() ) + m_aResourceURLToInfoCache.erase( pIter ); + + if ( !m_bConfigAccessInitialized ) + { + impl_initializeConfigAccess(); + m_bConfigAccessInitialized = true; + } + + try + { + // Remove must be write-through => remove element from configuration + Reference< XNameContainer > xNameContainer( m_xConfigAccess, UNO_QUERY ); + if ( xNameContainer.is() ) + { + g.unlock(); + + xNameContainer->removeByName( rResourceURL ); + Reference< XChangesBatch > xFlush( m_xConfigAccess, UNO_QUERY ); + if ( xFlush.is() ) + xFlush->commitChanges(); + } + } + catch ( const css::lang::WrappedTargetException& ) + { + } +} + +void SAL_CALL ConfigurationAccess_WindowState::insertByName( const OUString& rResourceURL, const css::uno::Any& aPropertySet ) +{ + // SAFE + std::unique_lock g(m_aMutex); + + Sequence< PropertyValue > aPropSet; + if ( !(aPropertySet >>= aPropSet) ) + throw IllegalArgumentException(); + + ResourceURLToInfoCache::const_iterator pIter = m_aResourceURLToInfoCache.find( rResourceURL ); + if ( pIter != m_aResourceURLToInfoCache.end() ) + throw ElementExistException(); + + if ( !m_bConfigAccessInitialized ) + { + impl_initializeConfigAccess(); + m_bConfigAccessInitialized = true; + } + + // Try to ask our configuration access + if ( !m_xConfigAccess.is() ) + return; + + if ( m_xConfigAccess->hasByName( rResourceURL ) ) + throw ElementExistException(); + + WindowStateInfo aWinStateInfo; + impl_fillStructFromSequence( aWinStateInfo, aPropSet ); + m_aResourceURLToInfoCache.emplace( rResourceURL, aWinStateInfo ); + + // insert must be write-through => insert element into configuration + Reference< XNameContainer > xNameContainer( m_xConfigAccess, UNO_QUERY ); + if ( !xNameContainer.is() ) + return; + + Reference< XSingleServiceFactory > xFactory( m_xConfigAccess, UNO_QUERY ); + g.unlock(); + + try + { + Reference< XPropertySet > xPropSet( xFactory->createInstance(), UNO_QUERY ); + if ( xPropSet.is() ) + { + Any a; + impl_putPropertiesFromStruct( aWinStateInfo, xPropSet ); + a <<= xPropSet; + xNameContainer->insertByName( rResourceURL, a ); + Reference< XChangesBatch > xFlush( xFactory, UNO_QUERY ); + if ( xFlush.is() ) + xFlush->commitChanges(); + } + } + catch ( const Exception& ) + { + } +} + +// XNameReplace +void SAL_CALL ConfigurationAccess_WindowState::replaceByName( const OUString& rResourceURL, const css::uno::Any& aPropertySet ) +{ + // SAFE + std::unique_lock g(m_aMutex); + + Sequence< PropertyValue > aPropSet; + if ( !(aPropertySet >>= aPropSet) ) + throw IllegalArgumentException(); + + ResourceURLToInfoCache::iterator pIter = m_aResourceURLToInfoCache.find( rResourceURL ); + if ( pIter != m_aResourceURLToInfoCache.end() ) + { + WindowStateInfo& rWinStateInfo = pIter->second; + impl_fillStructFromSequence( rWinStateInfo, aPropSet ); + m_bModified = true; + } + else + { + if ( !m_bConfigAccessInitialized ) + { + impl_initializeConfigAccess(); + m_bConfigAccessInitialized = true; + } + + // Try to ask our configuration access + Reference< XNameAccess > xNameAccess; + Any a( m_xConfigAccess->getByName( rResourceURL )); + + if ( !(a >>= xNameAccess) ) + throw NoSuchElementException(); + + WindowStateInfo& rWinStateInfo( impl_insertCacheAndReturnWinState( rResourceURL, xNameAccess )); + impl_fillStructFromSequence( rWinStateInfo, aPropSet ); + m_bModified = true; + pIter = m_aResourceURLToInfoCache.find( rResourceURL ); + + } + + if ( !(m_bModified && pIter != m_aResourceURLToInfoCache.end()) ) + return; + + Reference< XNameContainer > xNameContainer( m_xConfigAccess, UNO_QUERY ); + if ( !xNameContainer.is() ) + return; + + WindowStateInfo aWinStateInfo( pIter->second ); + OUString aResourceURL( pIter->first ); + m_bModified = false; + g.unlock(); + + try + { + Reference< XPropertySet > xPropSet; + if ( xNameContainer->getByName( aResourceURL ) >>= xPropSet ) + { + impl_putPropertiesFromStruct( aWinStateInfo, xPropSet ); + + Reference< XChangesBatch > xFlush( m_xConfigAccess, UNO_QUERY ); + if ( xFlush.is() ) + xFlush->commitChanges(); + } + } + catch ( const Exception& ) + { + } + +} + +// container.XContainerListener +void SAL_CALL ConfigurationAccess_WindowState::elementInserted( const ContainerEvent& ) +{ + // do nothing - next time someone wants to retrieve this node we will find it in the configuration +} + +void SAL_CALL ConfigurationAccess_WindowState::elementRemoved ( const ContainerEvent& ) +{ +} + +void SAL_CALL ConfigurationAccess_WindowState::elementReplaced( const ContainerEvent& ) +{ +} + +// lang.XEventListener +void SAL_CALL ConfigurationAccess_WindowState::disposing( const EventObject& aEvent ) +{ + // SAFE + // remove our reference to the config access + std::unique_lock g(m_aMutex); + + Reference< XInterface > xIfac1( aEvent.Source, UNO_QUERY ); + Reference< XInterface > xIfac2( m_xConfigAccess, UNO_QUERY ); + if ( xIfac1 == xIfac2 ) + m_xConfigAccess.clear(); +} + +// private helper methods +Any ConfigurationAccess_WindowState::impl_getSequenceFromStruct( const WindowStateInfo& rWinStateInfo ) +{ + sal_Int32 i( 0 ); + sal_Int32 nCount( m_aPropArray.size() ); + std::vector< PropertyValue > aPropVec; + + for ( i = 0; i < nCount; i++ ) + { + if ( rWinStateInfo.nMask & ( 1 << i )) + { + // put value into the return sequence + PropertyValue pv; + pv.Name = m_aPropArray[i]; + + switch ( i ) + { + case PROPERTY_LOCKED: + pv.Value <<= rWinStateInfo.bLocked; break; + case PROPERTY_DOCKED: + pv.Value <<= rWinStateInfo.bDocked; break; + case PROPERTY_VISIBLE: + pv.Value <<= rWinStateInfo.bVisible; break; + case PROPERTY_CONTEXT: + pv.Value <<= rWinStateInfo.bContext; break; + case PROPERTY_HIDEFROMMENU: + pv.Value <<= rWinStateInfo.bHideFromMenu; break; + case PROPERTY_NOCLOSE: + pv.Value <<= rWinStateInfo.bNoClose; break; + case PROPERTY_SOFTCLOSE: + pv.Value <<= rWinStateInfo.bSoftClose; break; + case PROPERTY_CONTEXTACTIVE: + pv.Value <<= rWinStateInfo.bContextActive; break; + case PROPERTY_DOCKINGAREA: + pv.Value <<= rWinStateInfo.aDockingArea; break; + case PROPERTY_POS: + pv.Value <<= rWinStateInfo.aPos; break; + case PROPERTY_SIZE: + pv.Value <<= rWinStateInfo.aSize; break; + case PROPERTY_UINAME: + pv.Value <<= rWinStateInfo.aUIName; break; + case PROPERTY_INTERNALSTATE: + pv.Value <<= sal_Int32( rWinStateInfo.nInternalState ); break; + case PROPERTY_STYLE: + pv.Value <<= sal_Int16( rWinStateInfo.nStyle ); break; + case PROPERTY_DOCKPOS: + pv.Value <<= rWinStateInfo.aDockPos; break; + case PROPERTY_DOCKSIZE: + pv.Value <<= rWinStateInfo.aDockSize; break; + default: + assert( false && "Wrong value for ConfigurationAccess_WindowState. Who has forgotten to add this new property!" ); + } + aPropVec.push_back(pv); + } + } + + return Any( comphelper::containerToSequence(aPropVec) ); +} + +Any ConfigurationAccess_WindowState::impl_insertCacheAndReturnSequence( const OUString& rResourceURL, Reference< XNameAccess > const & xNameAccess ) +{ + sal_Int32 nMask( 0 ); + sal_Int32 nCount( m_aPropArray.size() ); + sal_Int32 i( 0 ); + std::vector< PropertyValue > aPropVec; + WindowStateInfo aWindowStateInfo; + + for ( i = 0; i < nCount; i++ ) + { + try + { + bool bAddToSeq( false ); + Any a( xNameAccess->getByName( m_aPropArray[i] ) ); + switch ( i ) + { + case PROPERTY_LOCKED: + case PROPERTY_DOCKED: + case PROPERTY_VISIBLE: + case PROPERTY_CONTEXT: + case PROPERTY_HIDEFROMMENU: + case PROPERTY_NOCLOSE: + case PROPERTY_SOFTCLOSE: + case PROPERTY_CONTEXTACTIVE: + { + bool bValue; + if ( a >>= bValue ) + { + sal_Int32 nValue( 1 << i ); + nMask |= nValue; + bAddToSeq = true; + switch ( i ) + { + case PROPERTY_LOCKED: + aWindowStateInfo.bLocked = bValue; break; + case PROPERTY_DOCKED: + aWindowStateInfo.bDocked = bValue; break; + case PROPERTY_VISIBLE: + aWindowStateInfo.bVisible = bValue; break; + case PROPERTY_CONTEXT: + aWindowStateInfo.bContext = bValue; break; + case PROPERTY_HIDEFROMMENU: + aWindowStateInfo.bHideFromMenu = bValue; break; + case PROPERTY_NOCLOSE: + aWindowStateInfo.bNoClose = bValue; break; + case PROPERTY_SOFTCLOSE: + aWindowStateInfo.bSoftClose = bValue; break; + case PROPERTY_CONTEXTACTIVE: + aWindowStateInfo.bContextActive = bValue; break; + } + } + } + break; + + case PROPERTY_DOCKINGAREA: + { + sal_Int32 nDockingArea = 0; + if ( a >>= nDockingArea ) + { + if (( nDockingArea >= 0 ) && + ( nDockingArea <= sal_Int32( DockingArea_DOCKINGAREA_RIGHT ))) + { + aWindowStateInfo.aDockingArea = static_cast(nDockingArea); + nMask |= WINDOWSTATE_MASK_DOCKINGAREA; + a <<= aWindowStateInfo.aDockingArea; + bAddToSeq = true; + } + } + } + break; + + case PROPERTY_POS: + case PROPERTY_DOCKPOS: + { + OUString aString; + if ( a >>= aString ) + { + sal_Int32 nToken( 0 ); + std::u16string_view aXStr = o3tl::getToken(aString, 0, ',', nToken ); + if ( nToken > 0 ) + { + css::awt::Point aPos; + aPos.X = o3tl::toInt32(aXStr); + aPos.Y = o3tl::toInt32(o3tl::getToken(aString, 0, ',', nToken )); + + if ( i == PROPERTY_POS ) + { + aWindowStateInfo.aPos = aPos; + nMask |= WINDOWSTATE_MASK_POS; + } + else + { + aWindowStateInfo.aDockPos = aPos; + nMask |= WINDOWSTATE_MASK_DOCKPOS; + } + + a <<= aPos; + bAddToSeq = true; + } + } + } + break; + + case PROPERTY_SIZE: + case PROPERTY_DOCKSIZE: + { + OUString aString; + if ( a >>= aString ) + { + sal_Int32 nToken( 0 ); + std::u16string_view aStr = o3tl::getToken(aString, 0, ',', nToken ); + if ( nToken > 0 ) + { + css::awt::Size aSize; + aSize.Width = o3tl::toInt32(aStr); + aSize.Height = o3tl::toInt32(o3tl::getToken(aString, 0, ',', nToken )); + if ( i == PROPERTY_SIZE ) + { + aWindowStateInfo.aSize = aSize; + nMask |= WINDOWSTATE_MASK_SIZE; + } + else + { + aWindowStateInfo.aDockSize = aSize; + nMask |= WINDOWSTATE_MASK_DOCKSIZE; + } + + a <<= aSize; + bAddToSeq = true; + } + } + } + break; + + case PROPERTY_UINAME: + { + OUString aValue; + if ( a >>= aValue ) + { + nMask |= WINDOWSTATE_MASK_UINAME; + aWindowStateInfo.aUIName = aValue; + bAddToSeq = true; + } + } + break; + + case PROPERTY_INTERNALSTATE: + { + sal_uInt32 nValue = 0; + if ( a >>= nValue ) + { + nMask |= WINDOWSTATE_MASK_INTERNALSTATE; + aWindowStateInfo.nInternalState = nValue; + bAddToSeq = true; + } + } + break; + + case PROPERTY_STYLE: + { + sal_Int32 nValue = 0; + if ( a >>= nValue ) + { + nMask |= WINDOWSTATE_MASK_STYLE; + aWindowStateInfo.nStyle = sal_uInt16( nValue ); + bAddToSeq = true; + } + } + break; + + default: + assert( false && "Wrong value for ConfigurationAccess_WindowState. Who has forgotten to add this new property!" ); + } + + if ( bAddToSeq ) + { + // put value into the return sequence + PropertyValue pv; + pv.Name = m_aPropArray[i]; + pv.Value = a; + aPropVec.push_back(pv); + } + } + catch( const css::container::NoSuchElementException& ) + { + } + catch ( const css::lang::WrappedTargetException& ) + { + } + } + + aWindowStateInfo.nMask = nMask; + m_aResourceURLToInfoCache.emplace( rResourceURL, aWindowStateInfo ); + return Any( comphelper::containerToSequence(aPropVec) ); +} + +ConfigurationAccess_WindowState::WindowStateInfo& ConfigurationAccess_WindowState::impl_insertCacheAndReturnWinState( const OUString& rResourceURL, Reference< XNameAccess > const & rNameAccess ) +{ + sal_Int32 nMask( 0 ); + sal_Int32 nCount( m_aPropArray.size() ); + sal_Int32 i( 0 ); + WindowStateInfo aWindowStateInfo; + + for ( i = 0; i < nCount; i++ ) + { + try + { + Any a( rNameAccess->getByName( m_aPropArray[i] ) ); + switch ( i ) + { + case PROPERTY_LOCKED: + case PROPERTY_DOCKED: + case PROPERTY_VISIBLE: + case PROPERTY_CONTEXT: + case PROPERTY_HIDEFROMMENU: + case PROPERTY_NOCLOSE: + case PROPERTY_SOFTCLOSE: + case PROPERTY_CONTEXTACTIVE: + { + bool bValue; + if ( a >>= bValue ) + { + sal_Int32 nValue( 1 << i ); + nMask |= nValue; + switch ( i ) + { + case PROPERTY_LOCKED: + aWindowStateInfo.bLocked = bValue; break; + case PROPERTY_DOCKED: + aWindowStateInfo.bDocked = bValue; break; + case PROPERTY_VISIBLE: + aWindowStateInfo.bVisible = bValue; break; + case PROPERTY_CONTEXT: + aWindowStateInfo.bContext = bValue; break; + case PROPERTY_HIDEFROMMENU: + aWindowStateInfo.bHideFromMenu = bValue; break; + case PROPERTY_NOCLOSE: + aWindowStateInfo.bNoClose = bValue; break; + case PROPERTY_SOFTCLOSE: + aWindowStateInfo.bNoClose = bValue; break; + case PROPERTY_CONTEXTACTIVE: + aWindowStateInfo.bContextActive = bValue; break; + default: + SAL_WARN( "fwk.uiconfiguration", "Unknown boolean property in WindowState found!" ); + } + } + } + break; + + case PROPERTY_DOCKINGAREA: + { + sal_Int32 nDockingArea = 0; + if ( a >>= nDockingArea ) + { + if (( nDockingArea >= 0 ) && + ( nDockingArea <= sal_Int32( DockingArea_DOCKINGAREA_RIGHT ))) + { + aWindowStateInfo.aDockingArea = static_cast(nDockingArea); + nMask |= WINDOWSTATE_MASK_DOCKINGAREA; + } + } + } + break; + + case PROPERTY_POS: + case PROPERTY_DOCKPOS: + { + OUString aString; + if ( a >>= aString ) + { + sal_Int32 nToken( 0 ); + std::u16string_view aXStr = o3tl::getToken(aString, 0, ',', nToken ); + if ( nToken > 0 ) + { + css::awt::Point aPos; + aPos.X = o3tl::toInt32(aXStr); + aPos.Y = o3tl::toInt32(o3tl::getToken(aString, 0, ',', nToken )); + + if ( i == PROPERTY_POS ) + { + aWindowStateInfo.aPos = aPos; + nMask |= WINDOWSTATE_MASK_POS; + } + else + { + aWindowStateInfo.aDockPos = aPos; + nMask |= WINDOWSTATE_MASK_DOCKPOS; + } + } + } + } + break; + + case PROPERTY_SIZE: + case PROPERTY_DOCKSIZE: + { + OUString aString; + if ( a >>= aString ) + { + sal_Int32 nToken( 0 ); + std::u16string_view aStr = o3tl::getToken(aString, 0, ',', nToken ); + if ( nToken > 0 ) + { + css::awt::Size aSize; + aSize.Width = o3tl::toInt32(aStr); + aSize.Height = o3tl::toInt32(o3tl::getToken(aString, 0, ',', nToken )); + if ( i == PROPERTY_SIZE ) + { + aWindowStateInfo.aSize = aSize; + nMask |= WINDOWSTATE_MASK_SIZE; + } + else + { + aWindowStateInfo.aDockSize = aSize; + nMask |= WINDOWSTATE_MASK_DOCKSIZE; + } + } + } + } + break; + + case PROPERTY_UINAME: + { + OUString aValue; + if ( a >>= aValue ) + { + nMask |= WINDOWSTATE_MASK_UINAME; + aWindowStateInfo.aUIName = aValue; + } + } + break; + + case PROPERTY_INTERNALSTATE: + { + sal_Int32 nValue = 0; + if ( a >>= nValue ) + { + nMask |= WINDOWSTATE_MASK_INTERNALSTATE; + aWindowStateInfo.nInternalState = sal_uInt32( nValue ); + } + } + break; + + case PROPERTY_STYLE: + { + sal_Int32 nValue = 0; + if ( a >>= nValue ) + { + nMask |= WINDOWSTATE_MASK_STYLE; + aWindowStateInfo.nStyle = sal_uInt16( nValue ); + } + } + break; + + default: + assert( false && "Wrong value for ConfigurationAccess_WindowState. Who has forgotten to add this new property!" ); + } + } + catch( const css::container::NoSuchElementException& ) + { + } + catch ( const css::lang::WrappedTargetException& ) + { + } + } + + aWindowStateInfo.nMask = nMask; + ResourceURLToInfoCache::iterator pIter = m_aResourceURLToInfoCache.emplace( rResourceURL, aWindowStateInfo ).first; + return pIter->second; +} + +Any ConfigurationAccess_WindowState::impl_getWindowStateFromResourceURL( const OUString& rResourceURL ) +{ + if ( !m_bConfigAccessInitialized ) + { + impl_initializeConfigAccess(); + m_bConfigAccessInitialized = true; + } + + try + { + // Try to ask our configuration access + if ( m_xConfigAccess.is() && m_xConfigAccess->hasByName( rResourceURL ) ) + { + + Reference< XNameAccess > xNameAccess( m_xConfigAccess->getByName( rResourceURL ), UNO_QUERY ); + if ( xNameAccess.is() ) + return impl_insertCacheAndReturnSequence( rResourceURL, xNameAccess ); + } + } + catch( const css::container::NoSuchElementException& ) + { + } + catch ( const css::lang::WrappedTargetException& ) + { + } + + return Any(); +} + +void ConfigurationAccess_WindowState::impl_fillStructFromSequence( WindowStateInfo& rWinStateInfo, const Sequence< PropertyValue >& rSeq ) +{ + sal_Int32 nCompareCount( m_aPropArray.size() ); + sal_Int32 nCount( rSeq.getLength() ); + sal_Int32 i( 0 ); + + for ( i = 0; i < nCount; i++ ) + { + for ( sal_Int32 j = 0; j < nCompareCount; j++ ) + { + if ( rSeq[i].Name == m_aPropArray[j] ) + { + switch ( j ) + { + case PROPERTY_LOCKED: + case PROPERTY_DOCKED: + case PROPERTY_VISIBLE: + case PROPERTY_CONTEXT: + case PROPERTY_HIDEFROMMENU: + case PROPERTY_NOCLOSE: + case PROPERTY_SOFTCLOSE: + case PROPERTY_CONTEXTACTIVE: + { + bool bValue; + if ( rSeq[i].Value >>= bValue ) + { + sal_Int32 nValue( 1 << j ); + rWinStateInfo.nMask |= nValue; + switch ( j ) + { + case PROPERTY_LOCKED: + rWinStateInfo.bLocked = bValue; + break; + case PROPERTY_DOCKED: + rWinStateInfo.bDocked = bValue; + break; + case PROPERTY_VISIBLE: + rWinStateInfo.bVisible = bValue; + break; + case PROPERTY_CONTEXT: + rWinStateInfo.bContext = bValue; + break; + case PROPERTY_HIDEFROMMENU: + rWinStateInfo.bHideFromMenu = bValue; + break; + case PROPERTY_NOCLOSE: + rWinStateInfo.bNoClose = bValue; + break; + case PROPERTY_SOFTCLOSE: + rWinStateInfo.bSoftClose = bValue; + break; + case PROPERTY_CONTEXTACTIVE: + rWinStateInfo.bContextActive = bValue; + break; + } + } + } + break; + + case PROPERTY_DOCKINGAREA: + { + css::ui::DockingArea eDockingArea; + if ( rSeq[i].Value >>= eDockingArea ) + { + rWinStateInfo.aDockingArea = eDockingArea; + rWinStateInfo.nMask |= WINDOWSTATE_MASK_DOCKINGAREA; + } + } + break; + + case PROPERTY_POS: + case PROPERTY_DOCKPOS: + { + css::awt::Point aPoint; + if ( rSeq[i].Value >>= aPoint ) + { + if ( j == PROPERTY_POS ) + { + rWinStateInfo.aPos = aPoint; + rWinStateInfo.nMask |= WINDOWSTATE_MASK_POS; + } + else + { + rWinStateInfo.aDockPos = aPoint; + rWinStateInfo.nMask |= WINDOWSTATE_MASK_DOCKPOS; + } + } + } + break; + + case PROPERTY_SIZE: + case PROPERTY_DOCKSIZE: + { + css::awt::Size aSize; + if ( rSeq[i].Value >>= aSize ) + { + if ( j == PROPERTY_SIZE ) + { + rWinStateInfo.aSize = aSize; + rWinStateInfo.nMask |= WINDOWSTATE_MASK_SIZE; + } + else + { + rWinStateInfo.aDockSize = aSize; + rWinStateInfo.nMask |= WINDOWSTATE_MASK_DOCKSIZE; + } + } + } + break; + + case PROPERTY_UINAME: + { + OUString aValue; + if ( rSeq[i].Value >>= aValue ) + { + rWinStateInfo.aUIName = aValue; + rWinStateInfo.nMask |= WINDOWSTATE_MASK_UINAME; + } + } + break; + + case PROPERTY_INTERNALSTATE: + { + sal_Int32 nValue = 0; + if ( rSeq[i].Value >>= nValue ) + { + rWinStateInfo.nInternalState = sal_uInt32( nValue ); + rWinStateInfo.nMask |= WINDOWSTATE_MASK_INTERNALSTATE; + } + } + break; + + case PROPERTY_STYLE: + { + sal_Int32 nValue = 0; + if ( rSeq[i].Value >>= nValue ) + { + rWinStateInfo.nStyle = sal_uInt16( nValue ); + rWinStateInfo.nMask |= WINDOWSTATE_MASK_STYLE; + } + } + break; + + default: + assert( false && "Wrong value for ConfigurationAccess_WindowState. Who has forgotten to add this new property!" ); + } + + break; + } + } + } +} + +void ConfigurationAccess_WindowState::impl_putPropertiesFromStruct( const WindowStateInfo& rWinStateInfo, Reference< XPropertySet > const & xPropSet ) +{ + sal_Int32 i( 0 ); + sal_Int32 nCount( m_aPropArray.size() ); + OUString aDelim( "," ); + + for ( i = 0; i < nCount; i++ ) + { + if ( rWinStateInfo.nMask & ( 1 << i )) + { + try + { + // put values into the property set + switch ( i ) + { + case PROPERTY_LOCKED: + xPropSet->setPropertyValue( m_aPropArray[i], Any( rWinStateInfo.bLocked ) ); break; + case PROPERTY_DOCKED: + xPropSet->setPropertyValue( m_aPropArray[i], Any( rWinStateInfo.bDocked ) ); break; + case PROPERTY_VISIBLE: + xPropSet->setPropertyValue( m_aPropArray[i], Any( rWinStateInfo.bVisible ) ); break; + case PROPERTY_CONTEXT: + xPropSet->setPropertyValue( m_aPropArray[i], Any( rWinStateInfo.bContext ) ); break; + case PROPERTY_HIDEFROMMENU: + xPropSet->setPropertyValue( m_aPropArray[i], Any( rWinStateInfo.bHideFromMenu ) ); break; + case PROPERTY_NOCLOSE: + xPropSet->setPropertyValue( m_aPropArray[i], Any( rWinStateInfo.bNoClose ) ); break; + case PROPERTY_SOFTCLOSE: + xPropSet->setPropertyValue( m_aPropArray[i], Any( rWinStateInfo.bSoftClose ) ); break; + case PROPERTY_CONTEXTACTIVE: + xPropSet->setPropertyValue( m_aPropArray[i], Any( rWinStateInfo.bContextActive ) ); break; + case PROPERTY_DOCKINGAREA: + xPropSet->setPropertyValue( m_aPropArray[i], Any( sal_Int16( rWinStateInfo.aDockingArea ) ) ); break; + case PROPERTY_POS: + case PROPERTY_DOCKPOS: + { + OUString aPosStr; + if ( i == PROPERTY_POS ) + aPosStr = OUString::number( rWinStateInfo.aPos.X ); + else + aPosStr = OUString::number( rWinStateInfo.aDockPos.X ); + aPosStr += aDelim; + if ( i == PROPERTY_POS ) + aPosStr += OUString::number( rWinStateInfo.aPos.Y ); + else + aPosStr += OUString::number( rWinStateInfo.aDockPos.Y ); + xPropSet->setPropertyValue( m_aPropArray[i], Any( aPosStr ) ); + break; + } + case PROPERTY_SIZE: + case PROPERTY_DOCKSIZE: + { + OUString aSizeStr; + if ( i == PROPERTY_SIZE ) + aSizeStr = OUString::number( rWinStateInfo.aSize.Width ); + else + aSizeStr = OUString::number( rWinStateInfo.aDockSize.Width ); + aSizeStr += aDelim; + if ( i == PROPERTY_SIZE ) + aSizeStr += OUString::number( rWinStateInfo.aSize.Height ); + else + aSizeStr += OUString::number( rWinStateInfo.aDockSize.Height ); + xPropSet->setPropertyValue( m_aPropArray[i], Any( aSizeStr ) ); + break; + } + case PROPERTY_UINAME: + xPropSet->setPropertyValue( m_aPropArray[i], Any( rWinStateInfo.aUIName ) ); break; + case PROPERTY_INTERNALSTATE: + xPropSet->setPropertyValue( m_aPropArray[i], Any( sal_Int32( rWinStateInfo.nInternalState )) ); break; + case PROPERTY_STYLE: + xPropSet->setPropertyValue( m_aPropArray[i], Any( sal_Int32( rWinStateInfo.nStyle )) ); break; + default: + assert( false && "Wrong value for ConfigurationAccess_WindowState. Who has forgotten to add this new property!" ); + } + } + catch( const Exception& ) + { + } + } + } +} + +void ConfigurationAccess_WindowState::impl_initializeConfigAccess() +{ + try + { + Sequence aArgs(comphelper::InitAnyPropertySequence( + { + {"nodepath", Any(m_aConfigWindowAccess)} + })); + m_xConfigAccess.set( m_xConfigProvider->createInstanceWithArguments( + "com.sun.star.configuration.ConfigurationUpdateAccess", aArgs ), UNO_QUERY ); + if ( m_xConfigAccess.is() ) + { + // Add as container listener + Reference< XContainer > xContainer( m_xConfigAccess, UNO_QUERY ); + if ( xContainer.is() ) + { + m_xConfigListener = new WeakContainerListener(this); + xContainer->addContainerListener(m_xConfigListener); + } + } + } + catch ( const WrappedTargetException& ) + { + } + catch ( const Exception& ) + { + } +} + +typedef comphelper::WeakComponentImplHelper< css::container::XNameAccess, + css::lang::XServiceInfo> WindowStateConfiguration_BASE; + +class WindowStateConfiguration : public WindowStateConfiguration_BASE +{ +public: + explicit WindowStateConfiguration( const css::uno::Reference< css::uno::XComponentContext >& rxContext ); + virtual ~WindowStateConfiguration() override; + + virtual OUString SAL_CALL getImplementationName() override + { + return "com.sun.star.comp.framework.WindowStateConfiguration"; + } + + virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override + { + return cppu::supportsService(this, ServiceName); + } + + virtual css::uno::Sequence SAL_CALL getSupportedServiceNames() override + { + return {"com.sun.star.ui.WindowStateConfiguration"}; + } + + // 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; + + // XElementAccess + virtual css::uno::Type SAL_CALL getElementType() override; + virtual sal_Bool SAL_CALL hasElements() override; + + typedef std::unordered_map< OUString, + OUString > ModuleToWindowStateFileMap; + + typedef std::unordered_map< OUString, + css::uno::Reference< css::container::XNameAccess > > ModuleToWindowStateConfigHashMap; + +private: + css::uno::Reference< css::uno::XComponentContext> m_xContext; + ModuleToWindowStateFileMap m_aModuleToFileHashMap; + ModuleToWindowStateConfigHashMap m_aModuleToWindowStateHashMap; +}; + +WindowStateConfiguration::WindowStateConfiguration( const Reference< XComponentContext >& rxContext ) : + m_xContext( rxContext ) +{ + css::uno::Reference< css::frame::XModuleManager2 > xModuleManager = + ModuleManager::create( m_xContext ); + Reference< XNameAccess > xEmptyNameAccess; + Sequence< OUString > aElementNames; + try + { + aElementNames = xModuleManager->getElementNames(); + } + catch (const css::uno::RuntimeException &) + { + } + Sequence< PropertyValue > aSeq; + + for ( OUString const & aModuleIdentifier : std::as_const(aElementNames) ) + { + if ( xModuleManager->getByName( aModuleIdentifier ) >>= aSeq ) + { + OUString aWindowStateFileStr; + for ( PropertyValue const & rProp : std::as_const(aSeq) ) + { + if ( rProp.Name == "ooSetupFactoryWindowStateConfigRef" ) + { + rProp.Value >>= aWindowStateFileStr; + break; + } + } + + if ( !aWindowStateFileStr.isEmpty() ) + { + // Create first mapping ModuleIdentifier ==> Window state configuration file + m_aModuleToFileHashMap.emplace( aModuleIdentifier, aWindowStateFileStr ); + + // Create second mapping Command File ==> Window state configuration instance + ModuleToWindowStateConfigHashMap::iterator pIter = m_aModuleToWindowStateHashMap.find( aWindowStateFileStr ); + if ( pIter == m_aModuleToWindowStateHashMap.end() ) + m_aModuleToWindowStateHashMap.emplace( aWindowStateFileStr, xEmptyNameAccess ); + } + } + } +} + +WindowStateConfiguration::~WindowStateConfiguration() +{ + std::unique_lock g(m_aMutex); + m_aModuleToFileHashMap.clear(); + m_aModuleToWindowStateHashMap.clear(); +} + +Any SAL_CALL WindowStateConfiguration::getByName( const OUString& aModuleIdentifier ) +{ + std::unique_lock g(m_aMutex); + + ModuleToWindowStateFileMap::const_iterator pIter = m_aModuleToFileHashMap.find( aModuleIdentifier ); + if ( pIter != m_aModuleToFileHashMap.end() ) + { + Any a; + OUString aWindowStateConfigFile( pIter->second ); + + ModuleToWindowStateConfigHashMap::iterator pModuleIter = m_aModuleToWindowStateHashMap.find( aWindowStateConfigFile ); + if ( pModuleIter != m_aModuleToWindowStateHashMap.end() ) + { + if ( pModuleIter->second.is() ) + a <<= pModuleIter->second; + else + { + Reference< XNameAccess > xResourceURLWindowState = new ConfigurationAccess_WindowState( aWindowStateConfigFile, m_xContext ); + pModuleIter->second = xResourceURLWindowState; + a <<= xResourceURLWindowState; + } + + return a; + } + } + + throw NoSuchElementException(); +} + +Sequence< OUString > SAL_CALL WindowStateConfiguration::getElementNames() +{ + std::unique_lock g(m_aMutex); + + return comphelper::mapKeysToSequence( m_aModuleToFileHashMap ); +} + +sal_Bool SAL_CALL WindowStateConfiguration::hasByName( const OUString& aName ) +{ + std::unique_lock g(m_aMutex); + + ModuleToWindowStateFileMap::const_iterator pIter = m_aModuleToFileHashMap.find( aName ); + return ( pIter != m_aModuleToFileHashMap.end() ); +} + +// XElementAccess +Type SAL_CALL WindowStateConfiguration::getElementType() +{ + return cppu::UnoType::get(); +} + +sal_Bool SAL_CALL WindowStateConfiguration::hasElements() +{ + // We always have at least one module. So it is valid to return true! + return true; +} + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_framework_WindowStateConfiguration_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence const &) +{ + return cppu::acquire(new WindowStateConfiguration(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uielement/FixedImageToolbarController.cxx b/framework/source/uielement/FixedImageToolbarController.cxx new file mode 100644 index 0000000000..c694cda3b8 --- /dev/null +++ b/framework/source/uielement/FixedImageToolbarController.cxx @@ -0,0 +1,138 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include +#include +#include +#include +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::awt; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::frame; +using namespace ::com::sun::star::util; + +namespace framework +{ +class FixedImageControl final : public InterimItemWindow +{ +public: + FixedImageControl(vcl::Window* pParent, const OUString& rCommand); + virtual ~FixedImageControl() override; + virtual void dispose() override; + DECL_LINK(KeyInputHdl, const ::KeyEvent&, bool); + +private: + std::unique_ptr m_xWidget; +}; + +FixedImageControl::FixedImageControl(vcl::Window* pParent, const OUString& rCommand) + : InterimItemWindow(pParent, "svt/ui/fixedimagecontrol.ui", "FixedImageControl") + , m_xWidget(m_xBuilder->weld_image("image")) +{ + InitControlBase(m_xWidget.get()); + + m_xWidget->connect_key_press(LINK(this, FixedImageControl, KeyInputHdl)); + + bool bBigImages(SvtMiscOptions::AreCurrentSymbolsLarge()); + auto xImage + = Graphic(AddonsOptions().GetImageFromURL(rCommand, bBigImages, true)).GetXGraphic(); + m_xWidget->set_image(xImage); + + SetSizePixel(get_preferred_size()); +} + +IMPL_LINK(FixedImageControl, KeyInputHdl, const ::KeyEvent&, rKEvt, bool) +{ + return ChildKeyInput(rKEvt); +} + +FixedImageControl::~FixedImageControl() { disposeOnce(); } + +void FixedImageControl::dispose() +{ + m_xWidget.reset(); + InterimItemWindow::dispose(); +} + +FixedImageToolbarController::FixedImageToolbarController( + const Reference& rxContext, const Reference& rFrame, + ToolBox* pToolbar, ToolBoxItemId nID, const OUString& rCommand) + : ComplexToolbarController(rxContext, rFrame, pToolbar, nID, rCommand) + , m_eSymbolSize(SvtMiscOptions::GetCurrentSymbolsSize()) +{ + m_pFixedImageControl = VclPtr::Create(m_xToolbar, rCommand); + m_xToolbar->SetItemWindow(m_nID, m_pFixedImageControl); + + SvtMiscOptions().AddListenerLink(LINK(this, FixedImageToolbarController, MiscOptionsChanged)); +} + +void SAL_CALL FixedImageToolbarController::dispose() +{ + SolarMutexGuard aSolarMutexGuard; + SvtMiscOptions().RemoveListenerLink( + LINK(this, FixedImageToolbarController, MiscOptionsChanged)); + m_xToolbar->SetItemWindow(m_nID, nullptr); + m_pFixedImageControl.disposeAndClear(); + ComplexToolbarController::dispose(); +} + +void FixedImageToolbarController::executeControlCommand(const css::frame::ControlCommand&) {} + +void FixedImageToolbarController::CheckAndUpdateImages() +{ + SolarMutexGuard aSolarMutexGuard; + + const sal_Int16 eNewSymbolSize = SvtMiscOptions::GetCurrentSymbolsSize(); + + if (m_eSymbolSize == eNewSymbolSize) + return; + + m_eSymbolSize = eNewSymbolSize; + + // Refresh images if requested + ::Size aSize(16, 16); + if (m_eSymbolSize == SFX_SYMBOLS_SIZE_LARGE) + { + aSize.setWidth(26); + aSize.setHeight(26); + } + else if (m_eSymbolSize == SFX_SYMBOLS_SIZE_32) + { + aSize.setWidth(32); + aSize.setHeight(32); + } + m_pFixedImageControl->SetSizePixel(aSize); +} + +IMPL_LINK_NOARG(FixedImageToolbarController, MiscOptionsChanged, LinkParamNone*, void) +{ + CheckAndUpdateImages(); +} + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uielement/FixedTextToolbarController.cxx b/framework/source/uielement/FixedTextToolbarController.cxx new file mode 100644 index 0000000000..02bfd4c5d6 --- /dev/null +++ b/framework/source/uielement/FixedTextToolbarController.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 + +#include +#include +#include +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::awt; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::frame; +using namespace ::com::sun::star::util; + +namespace framework +{ +class FixedTextControl final : public InterimItemWindow +{ +public: + FixedTextControl(vcl::Window* pParent); + virtual ~FixedTextControl() override; + virtual void dispose() override; + OUString get_label() const { return m_xWidget->get_label(); } + void set_label(const OUString& rLabel) { return m_xWidget->set_label(rLabel); } + DECL_LINK(KeyInputHdl, const ::KeyEvent&, bool); + +private: + std::unique_ptr m_xWidget; +}; + +FixedTextControl::FixedTextControl(vcl::Window* pParent) + : InterimItemWindow(pParent, "svt/ui/fixedtextcontrol.ui", "FixedTextControl") + , m_xWidget(m_xBuilder->weld_label("label")) +{ + InitControlBase(m_xWidget.get()); + + m_xWidget->connect_key_press(LINK(this, FixedTextControl, KeyInputHdl)); +} + +IMPL_LINK(FixedTextControl, KeyInputHdl, const ::KeyEvent&, rKEvt, bool) +{ + return ChildKeyInput(rKEvt); +} + +FixedTextControl::~FixedTextControl() { disposeOnce(); } + +void FixedTextControl::dispose() +{ + m_xWidget.reset(); + InterimItemWindow::dispose(); +} + +FixedTextToolbarController::FixedTextToolbarController( + const Reference& rxContext, const Reference& rFrame, + ToolBox* pToolbar, ToolBoxItemId nID, const OUString& aCommand) + : ComplexToolbarController(rxContext, rFrame, pToolbar, nID, aCommand) +{ + m_pFixedTextControl = VclPtr::Create(m_xToolbar); + m_xToolbar->SetItemWindow(m_nID, m_pFixedTextControl); + m_xToolbar->SetItemBits(m_nID, ToolBoxItemBits::AUTOSIZE | m_xToolbar->GetItemBits(m_nID)); +} + +void SAL_CALL FixedTextToolbarController::dispose() +{ + SolarMutexGuard aSolarMutexGuard; + m_xToolbar->SetItemWindow(m_nID, nullptr); + m_pFixedTextControl.disposeAndClear(); + ComplexToolbarController::dispose(); +} + +Sequence FixedTextToolbarController::getExecuteArgs(sal_Int16 KeyModifier) const +{ + const OUString aSelectedText = m_pFixedTextControl->get_label(); + + // Add key modifier to argument list + Sequence aArgs{ comphelper::makePropertyValue("KeyModifier", KeyModifier), + comphelper::makePropertyValue("Text", aSelectedText) }; + return aArgs; +} + +void FixedTextToolbarController::executeControlCommand( + const css::frame::ControlCommand& rControlCommand) +{ + SolarMutexGuard aSolarMutexGuard; + + if (rControlCommand.Command != "SetText") + return; + + for (const NamedValue& rArg : rControlCommand.Arguments) + { + if (rArg.Name == "Text") + { + OUString aText; + rArg.Value >>= aText; + m_pFixedTextControl->set_label(aText); + m_pFixedTextControl->SetSizePixel(m_pFixedTextControl->get_preferred_size()); + + // send notification + notifyTextChanged(aText); + break; + } + } +} + +} // namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uielement/addonstoolbarwrapper.cxx b/framework/source/uielement/addonstoolbarwrapper.cxx new file mode 100644 index 0000000000..f0bb99549f --- /dev/null +++ b/framework/source/uielement/addonstoolbarwrapper.cxx @@ -0,0 +1,163 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include + +#include +#include + +#include + +#include +#include + +using namespace com::sun::star::uno; +using namespace com::sun::star::beans; +using namespace com::sun::star::frame; +using namespace com::sun::star::lang; +using namespace com::sun::star::container; +using namespace com::sun::star::awt; +using namespace ::com::sun::star::ui; + +namespace framework +{ + +AddonsToolBarWrapper::AddonsToolBarWrapper( const Reference< XComponentContext >& xContext ) : + UIElementWrapperBase( UIElementType::TOOLBAR ), + m_xContext( xContext ), + m_bCreatedImages( false ) +{ +} + +AddonsToolBarWrapper::~AddonsToolBarWrapper() +{ +} + +// XComponent +void SAL_CALL AddonsToolBarWrapper::dispose() +{ + Reference< XComponent > xThis(this); + + css::lang::EventObject aEvent( xThis ); + m_aListenerContainer.disposeAndClear( aEvent ); + + SolarMutexGuard g; + + if ( m_xToolBarManager.is() ) + m_xToolBarManager->dispose(); + m_xToolBarManager.clear(); + + m_bDisposed = true; +} + +// XInitialization +void SAL_CALL AddonsToolBarWrapper::initialize( const Sequence< Any >& aArguments ) +{ + SolarMutexGuard g; + + if ( m_bDisposed ) + throw DisposedException(); + + if ( m_bInitialized ) + return; + + UIElementWrapperBase::initialize( aArguments ); + + for ( const Any& rArg : aArguments ) + { + PropertyValue aPropValue; + if ( rArg >>= aPropValue ) + { + if ( aPropValue.Name == "ConfigurationData" ) + aPropValue.Value >>= m_aConfigData; + } + } + + Reference< XFrame > xFrame( m_xWeakFrame ); + if ( !(xFrame.is() && m_aConfigData.hasElements()) ) + return; + + // Create VCL based toolbar which will be filled with settings data + VclPtr pToolBar; + rtl::Reference pToolBarManager; + { + SolarMutexGuard aSolarMutexGuard; + VclPtr pWindow = VCLUnoHelper::GetWindow( xFrame->getContainerWindow() ); + if ( pWindow ) + { + sal_uLong nStyles = WB_BORDER | WB_SCROLL | WB_MOVEABLE | WB_3DLOOK | WB_DOCKABLE | WB_SIZEABLE | WB_CLOSEABLE; + + pToolBar = VclPtr::Create( pWindow, nStyles ); + pToolBar->SetLineSpacing(true); + pToolBarManager = new ToolBarManager( m_xContext, xFrame, m_aResourceURL, pToolBar ); + m_xToolBarManager = pToolBarManager; + } + } + + try + { + if ( m_aConfigData.hasElements() && pToolBar && pToolBarManager ) + { + // Fill toolbar with container contents + pToolBarManager->FillAddonToolbar( m_aConfigData ); + pToolBar->EnableCustomize(); + ::Size aActSize( pToolBar->GetSizePixel() ); + ::Size aSize( pToolBar->CalcWindowSizePixel() ); + aSize.setWidth( aActSize.Width() ); + pToolBar->SetSizePixel( aSize ); + } + } + catch ( const NoSuchElementException& ) + { + } +} + +// XUIElement interface +Reference< XInterface > SAL_CALL AddonsToolBarWrapper::getRealInterface() +{ + SolarMutexGuard g; + + if ( m_xToolBarManager ) + { + vcl::Window* pWindow = m_xToolBarManager->GetToolBar(); + return Reference< XInterface >( VCLUnoHelper::GetInterface( pWindow ), UNO_QUERY ); + } + + return Reference< XInterface >(); +} + +// allow late population of images for add-on toolbars +void AddonsToolBarWrapper::populateImages() +{ + SolarMutexGuard g; + + if (m_bCreatedImages) + return; + + if (m_xToolBarManager) + { + m_xToolBarManager->RequestImages(); + m_bCreatedImages = true; + } +} + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uielement/buttontoolbarcontroller.cxx b/framework/source/uielement/buttontoolbarcontroller.cxx new file mode 100644 index 0000000000..d100ee1711 --- /dev/null +++ b/framework/source/uielement/buttontoolbarcontroller.cxx @@ -0,0 +1,273 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +using namespace ::com::sun::star; +using namespace css::awt; +using namespace css::uno; +using namespace css::beans; +using namespace css::lang; +using namespace css::frame; +using namespace css::util; + +namespace framework +{ + +ButtonToolbarController::ButtonToolbarController( + uno::Reference< uno::XComponentContext > xContext, + ToolBox* pToolBar, + OUString aCommand ) : + m_bInitialized( false ), + m_bDisposed( false ), + m_aCommandURL(std::move( aCommand )), + m_xContext(std::move( xContext )), + m_pToolbar( pToolBar ) +{ +} + +ButtonToolbarController::~ButtonToolbarController() +{ +} + + // XInterface +uno::Any SAL_CALL ButtonToolbarController::queryInterface( const uno::Type& rType ) +{ + Any a = ::cppu::queryInterface( + rType , + static_cast< frame::XStatusListener* >( this ), + static_cast< frame::XToolbarController* >( this ), + static_cast< lang::XInitialization* >( this ), + static_cast< lang::XComponent* >( this ), + static_cast< util::XUpdatable* >( this )); + + if ( a.hasValue() ) + return a; + + return cppu::OWeakObject::queryInterface( rType ); +} + +void SAL_CALL ButtonToolbarController::acquire() noexcept +{ + cppu::OWeakObject::acquire(); +} + +void SAL_CALL ButtonToolbarController::release() noexcept +{ + cppu::OWeakObject::release(); +} + +// XInitialization +void SAL_CALL ButtonToolbarController::initialize( + const css::uno::Sequence< css::uno::Any >& aArguments ) +{ + SolarMutexGuard aSolarMutexGuard; + + if ( m_bDisposed ) + throw DisposedException(); + + if ( m_bInitialized ) + return; + + m_bInitialized = true; + + PropertyValue aPropValue; + for ( const css::uno::Any& rArg : aArguments ) + { + if ( rArg >>= aPropValue ) + { + if ( aPropValue.Name == "Frame" ) + m_xFrame.set(aPropValue.Value,UNO_QUERY); + else if ( aPropValue.Name == "CommandURL" ) + aPropValue.Value >>= m_aCommandURL; + else if ( aPropValue.Name == "ServiceManager" ) + { + Reference xServiceManager(aPropValue.Value,UNO_QUERY); + m_xContext = comphelper::getComponentContext(xServiceManager); + } + } + } +} + +// XComponent +void SAL_CALL ButtonToolbarController::dispose() +{ + Reference< XComponent > xThis = this; + + { + SolarMutexGuard aSolarMutexGuard; + if ( m_bDisposed ) + return; + + m_xContext.clear(); + m_xURLTransformer.clear(); + m_xFrame.clear(); + m_pToolbar.clear(); + m_bDisposed = true; + } +} + +void SAL_CALL ButtonToolbarController::addEventListener( + const css::uno::Reference< css::lang::XEventListener >& ) +{ + // do nothing +} + +void SAL_CALL ButtonToolbarController::removeEventListener( + const css::uno::Reference< css::lang::XEventListener >& ) +{ + // do nothing +} + +// XUpdatable +void SAL_CALL ButtonToolbarController::update() +{ + SolarMutexGuard aSolarMutexGuard; + if ( m_bDisposed ) + throw DisposedException(); +} + +// XEventListener +void SAL_CALL ButtonToolbarController::disposing( + const css::lang::EventObject& Source ) +{ + uno::Reference< uno::XInterface > xSource( Source.Source ); + + SolarMutexGuard aSolarMutexGuard; + + if ( m_bDisposed ) + return; + + uno::Reference< uno::XInterface > xIfac( m_xFrame, uno::UNO_QUERY ); + if ( xIfac == xSource ) + m_xFrame.clear(); +} + +void SAL_CALL ButtonToolbarController::statusChanged( const css::frame::FeatureStateEvent& ) +{ + // do nothing + if ( m_bDisposed ) + throw DisposedException(); +} + +// XToolbarController +void SAL_CALL ButtonToolbarController::execute( sal_Int16 KeyModifier ) +{ + uno::Reference< frame::XDispatch > xDispatch; + uno::Reference< frame::XFrame > xFrame; + uno::Reference< util::XURLTransformer > xURLTransformer; + OUString aCommandURL; + css::util::URL aTargetURL; + + { + SolarMutexGuard aSolarMutexGuard; + + if ( m_bDisposed ) + throw DisposedException(); + + if ( m_bInitialized && + m_xFrame.is() && + m_xContext.is() && + !m_aCommandURL.isEmpty() ) + { + if ( !m_xURLTransformer.is() ) + { + m_xURLTransformer = util::URLTransformer::create( m_xContext ); + } + + xFrame = m_xFrame; + aCommandURL = m_aCommandURL; + xURLTransformer = m_xURLTransformer; + } + } + + uno::Reference< frame::XDispatchProvider > xDispatchProvider( xFrame, uno::UNO_QUERY ); + if ( xDispatchProvider.is() ) + { + aTargetURL.Complete = aCommandURL; + xURLTransformer->parseStrict( aTargetURL ); + xDispatch = xDispatchProvider->queryDispatch( aTargetURL, OUString(), 0 ); + } + + if ( !xDispatch.is() ) + return; + + try + { + // Provide key modifier information to dispatch function + Sequence aArgs{ comphelper::makePropertyValue("KeyModifier", KeyModifier) }; + + xDispatch->dispatch( aTargetURL, aArgs ); + } + catch ( const DisposedException& ) + { + } +} + +void SAL_CALL ButtonToolbarController::click() +{ + SolarMutexGuard aSolarMutexGuard; + + if ( m_bDisposed ) + throw DisposedException(); + + sal_Int16 nKeyModifier( static_cast(m_pToolbar->GetModifier()) ); + execute( nKeyModifier ); +} + +void SAL_CALL ButtonToolbarController::doubleClick() +{ + // do nothing + if ( m_bDisposed ) + throw DisposedException(); +} + +uno::Reference< awt::XWindow > SAL_CALL ButtonToolbarController::createPopupWindow() +{ + if ( m_bDisposed ) + throw DisposedException(); + + return uno::Reference< awt::XWindow >(); +} + +uno::Reference< awt::XWindow > SAL_CALL ButtonToolbarController::createItemWindow( + const css::uno::Reference< css::awt::XWindow >& ) +{ + if ( m_bDisposed ) + throw DisposedException(); + + return uno::Reference< awt::XWindow >(); +} + +} // namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uielement/comboboxtoolbarcontroller.cxx b/framework/source/uielement/comboboxtoolbarcontroller.cxx new file mode 100644 index 0000000000..77d6e3e8c8 --- /dev/null +++ b/framework/source/uielement/comboboxtoolbarcontroller.cxx @@ -0,0 +1,333 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include + +#include +#include +#include +#include +#include + +using namespace ::com::sun::star; +using namespace css::uno; +using namespace css::beans; +using namespace css::lang; +using namespace css::frame; +using namespace css::util; + +namespace framework +{ + +// Wrapper class to notify controller about events from combobox. +// Unfortunately the events are notified through virtual methods instead +// of Listeners. + +class ComboBoxControl final : public InterimItemWindow +{ +public: + ComboBoxControl(vcl::Window* pParent, ComboboxToolbarController* pComboboxToolbarController); + virtual ~ComboBoxControl() override; + virtual void dispose() override; + + void set_active_or_entry_text(const OUString& rText); + OUString get_active_text() const { return m_xWidget->get_active_text(); } + + void clear() { m_xWidget->clear(); } + void remove(int nIndex) { m_xWidget->remove(nIndex); } + void append_text(const OUString& rStr) { m_xWidget->append_text(rStr); } + void insert_text(int pos, const OUString& rStr) { m_xWidget->insert_text(pos, rStr); } + int get_count() const { return m_xWidget->get_count(); } + int find_text(const OUString& rStr) const { return m_xWidget->find_text(rStr); } + + DECL_LINK(FocusInHdl, weld::Widget&, void); + DECL_LINK(FocusOutHdl, weld::Widget&, void); + DECL_LINK(ModifyHdl, weld::ComboBox&, void); + DECL_LINK(ActivateHdl, weld::ComboBox&, bool); + DECL_LINK(KeyInputHdl, const ::KeyEvent&, bool); + +private: + std::unique_ptr m_xWidget; + ComboboxToolbarController* m_pComboboxToolbarController; +}; + +ComboBoxControl::ComboBoxControl(vcl::Window* pParent, ComboboxToolbarController* pComboboxToolbarController) + : InterimItemWindow(pParent, "svt/ui/combocontrol.ui", "ComboControl") + , m_xWidget(m_xBuilder->weld_combo_box("combobox")) + , m_pComboboxToolbarController(pComboboxToolbarController) +{ + InitControlBase(m_xWidget.get()); + + m_xWidget->connect_focus_in(LINK(this, ComboBoxControl, FocusInHdl)); + m_xWidget->connect_focus_out(LINK(this, ComboBoxControl, FocusOutHdl)); + m_xWidget->connect_changed(LINK(this, ComboBoxControl, ModifyHdl)); + m_xWidget->connect_entry_activate(LINK(this, ComboBoxControl, ActivateHdl)); + m_xWidget->connect_key_press(LINK(this, ComboBoxControl, KeyInputHdl)); + + m_xWidget->set_entry_width_chars(1); // so a smaller than default width can be used by ComboboxToolbarController + SetSizePixel(get_preferred_size()); +} + +IMPL_LINK(ComboBoxControl, KeyInputHdl, const ::KeyEvent&, rKEvt, bool) +{ + return ChildKeyInput(rKEvt); +} + +void ComboBoxControl::set_active_or_entry_text(const OUString& rText) +{ + const int nFound = m_xWidget->find_text(rText); + if (nFound != -1) + m_xWidget->set_active(nFound); + else + m_xWidget->set_entry_text(rText); +} + +ComboBoxControl::~ComboBoxControl() +{ + disposeOnce(); +} + +void ComboBoxControl::dispose() +{ + m_pComboboxToolbarController = nullptr; + m_xWidget.reset(); + InterimItemWindow::dispose(); +} + +IMPL_LINK_NOARG(ComboBoxControl, ModifyHdl, weld::ComboBox&, void) +{ + if (m_pComboboxToolbarController) + { + if (m_xWidget->get_count() && m_xWidget->changed_by_direct_pick()) + m_pComboboxToolbarController->Select(); + else + m_pComboboxToolbarController->Modify(); + } +} + +IMPL_LINK_NOARG(ComboBoxControl, FocusInHdl, weld::Widget&, void) +{ + if (m_pComboboxToolbarController) + m_pComboboxToolbarController->GetFocus(); +} + +IMPL_LINK_NOARG(ComboBoxControl, FocusOutHdl, weld::Widget&, void) +{ + if (m_pComboboxToolbarController) + m_pComboboxToolbarController->LoseFocus(); +} + +IMPL_LINK_NOARG(ComboBoxControl, ActivateHdl, weld::ComboBox&, bool) +{ + if (m_pComboboxToolbarController) + m_pComboboxToolbarController->Activate(); + return true; +} + +ComboboxToolbarController::ComboboxToolbarController( + const Reference< XComponentContext >& rxContext, + const Reference< XFrame >& rFrame, + ToolBox* pToolbar, + ToolBoxItemId nID, + sal_Int32 nWidth, + const OUString& aCommand ) : + ComplexToolbarController( rxContext, rFrame, pToolbar, nID, aCommand ) + , m_pComboBox( nullptr ) +{ + m_pComboBox = VclPtr::Create(m_xToolbar, this); + if ( nWidth == 0 ) + nWidth = 100; + + // ComboBoxControl ctor has set a suitable height already + auto nHeight = m_pComboBox->GetSizePixel().Height(); + + m_pComboBox->SetSizePixel( ::Size( nWidth, nHeight )); + m_xToolbar->SetItemWindow( m_nID, m_pComboBox ); +} + +ComboboxToolbarController::~ComboboxToolbarController() +{ +} + +void SAL_CALL ComboboxToolbarController::dispose() +{ + SolarMutexGuard aSolarMutexGuard; + + m_xToolbar->SetItemWindow( m_nID, nullptr ); + m_pComboBox.disposeAndClear(); + + ComplexToolbarController::dispose(); +} + +Sequence ComboboxToolbarController::getExecuteArgs(sal_Int16 KeyModifier) const +{ + OUString aSelectedText = m_pComboBox->get_active_text(); + + // Add key modifier to argument list + Sequence aArgs{ comphelper::makePropertyValue("KeyModifier", KeyModifier), + comphelper::makePropertyValue("Text", aSelectedText) }; + return aArgs; +} + +void ComboboxToolbarController::Select() +{ + vcl::Window::PointerState aState = m_pComboBox->GetPointerState(); + + sal_uInt16 nKeyModifier = sal_uInt16( aState.mnState & KEY_MODIFIERS_MASK ); + execute( nKeyModifier ); +} + +void ComboboxToolbarController::Modify() +{ + notifyTextChanged(m_pComboBox->get_active_text()); +} + +void ComboboxToolbarController::GetFocus() +{ + notifyFocusGet(); +} + +void ComboboxToolbarController::LoseFocus() +{ + notifyFocusLost(); +} + +void ComboboxToolbarController::Activate() +{ + // Call execute only with non-empty text + if (!m_pComboBox->get_active_text().isEmpty()) + execute(0); +} + +void ComboboxToolbarController::executeControlCommand( const css::frame::ControlCommand& rControlCommand ) +{ + if ( rControlCommand.Command == "SetText" ) + { + for ( const NamedValue& rArg : rControlCommand.Arguments ) + { + if ( rArg.Name == "Text" ) + { + OUString aText; + rArg.Value >>= aText; + m_pComboBox->set_active_or_entry_text(aText); + + // send notification + notifyTextChanged( aText ); + break; + } + } + } + else if ( rControlCommand.Command == "SetList" ) + { + for ( const NamedValue& rArg : rControlCommand.Arguments ) + { + if ( rArg.Name == "List" ) + { + Sequence< OUString > aList; + m_pComboBox->clear(); + + rArg.Value >>= aList; + for (OUString const & rName : std::as_const(aList)) + m_pComboBox->append_text(rName); + + // send notification + uno::Sequence< beans::NamedValue > aInfo { { "List", css::uno::Any(aList) } }; + addNotifyInfo( "ListChanged", + getDispatchFromCommand( m_aCommandURL ), + aInfo ); + + break; + } + } + } + else if ( rControlCommand.Command == "AddEntry" ) + { + OUString aText; + for ( const NamedValue& rArg : rControlCommand.Arguments ) + { + if ( rArg.Name == "Text" ) + { + if ( rArg.Value >>= aText ) + m_pComboBox->append_text(aText); + break; + } + } + } + else if ( rControlCommand.Command == "InsertEntry" ) + { + sal_Int32 nPos(-1); + OUString aText; + for ( const NamedValue& rArg : rControlCommand.Arguments ) + { + if ( rArg.Name == "Pos" ) + { + sal_Int32 nTmpPos = 0; + if ( rArg.Value >>= nTmpPos ) + { + if (( nTmpPos >= 0 ) && + ( nTmpPos < m_pComboBox->get_count() )) + nPos = nTmpPos; + } + } + else if ( rArg.Name == "Text" ) + rArg.Value >>= aText; + } + + m_pComboBox->insert_text(nPos, aText); + } + else if ( rControlCommand.Command == "RemoveEntryPos" ) + { + for ( const NamedValue& rArg : rControlCommand.Arguments ) + { + if ( rArg.Name == "Pos" ) + { + sal_Int32 nPos( -1 ); + if ( rArg.Value >>= nPos ) + { + if (0 <= nPos && nPos < m_pComboBox->get_count()) + m_pComboBox->remove(nPos); + } + break; + } + } + } + else if ( rControlCommand.Command == "RemoveEntryText" ) + { + for ( const NamedValue& rArg : rControlCommand.Arguments ) + { + if ( rArg.Name == "Text") + { + OUString aText; + if ( rArg.Value >>= aText ) + { + auto nPos = m_pComboBox->find_text(aText); + if (nPos != -1) + m_pComboBox->remove(nPos); + } + break; + } + } + } +} + +} // namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uielement/complextoolbarcontroller.cxx b/framework/source/uielement/complextoolbarcontroller.cxx new file mode 100644 index 0000000000..c3f9f191df --- /dev/null +++ b/framework/source/uielement/complextoolbarcontroller.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 + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +using namespace ::com::sun::star; +using namespace css::awt; +using namespace css::uno; +using namespace css::beans; +using namespace css::lang; +using namespace css::frame; +using namespace css::frame::status; +using namespace css::util; + +namespace framework +{ + +ComplexToolbarController::ComplexToolbarController( + const Reference< XComponentContext >& rxContext, + const Reference< XFrame >& rFrame, + ToolBox* pToolbar, + ToolBoxItemId nID, + const OUString& aCommand ) : + svt::ToolboxController( rxContext, rFrame, aCommand ) + , m_xToolbar( pToolbar ) + , m_nID( nID ) + , m_bMadeInvisible( false ) +{ + m_xURLTransformer.set( URLTransformer::create(m_xContext) ); +} + +ComplexToolbarController::~ComplexToolbarController() +{ +} + +void SAL_CALL ComplexToolbarController::dispose() +{ + SolarMutexGuard aSolarMutexGuard; + + m_xToolbar->SetItemWindow( m_nID, nullptr ); + svt::ToolboxController::dispose(); + + m_xURLTransformer.clear(); + m_xToolbar.clear(); + m_nID = ToolBoxItemId(0); +} + +Sequence ComplexToolbarController::getExecuteArgs(sal_Int16 KeyModifier) const +{ + // Add key modifier to argument list + Sequence aArgs{ comphelper::makePropertyValue("KeyModifier", KeyModifier) }; + return aArgs; +} + +void SAL_CALL ComplexToolbarController::execute( sal_Int16 KeyModifier ) +{ + Reference< XDispatch > xDispatch; + Reference< XURLTransformer > xURLTransformer; + css::util::URL aTargetURL; + Sequence aArgs; + + { + SolarMutexGuard aSolarMutexGuard; + + if ( m_bDisposed ) + throw DisposedException(); + + if ( m_bInitialized && + m_xFrame.is() && + !m_aCommandURL.isEmpty() ) + { + xURLTransformer = m_xURLTransformer; + xDispatch = getDispatchFromCommand( m_aCommandURL ); + aTargetURL = getInitializedURL(); + aArgs = getExecuteArgs(KeyModifier); + } + } + + if ( xDispatch.is() && !aTargetURL.Complete.isEmpty() ) + { + // Execute dispatch asynchronously + ExecuteInfo* pExecuteInfo = new ExecuteInfo; + pExecuteInfo->xDispatch = xDispatch; + pExecuteInfo->aTargetURL = aTargetURL; + pExecuteInfo->aArgs = aArgs; + Application::PostUserEvent( LINK(nullptr, ComplexToolbarController , ExecuteHdl_Impl), pExecuteInfo ); + } +} + +void ComplexToolbarController::statusChanged( const FeatureStateEvent& Event ) +{ + SolarMutexGuard aSolarMutexGuard; + + if ( m_bDisposed ) + return; + + if ( !m_xToolbar ) + return; + + m_xToolbar->EnableItem( m_nID, Event.IsEnabled ); + + ToolBoxItemBits nItemBits = m_xToolbar->GetItemBits( m_nID ); + nItemBits &= ~ToolBoxItemBits::CHECKABLE; + TriState eTri = TRISTATE_FALSE; + + bool bValue; + OUString aStrValue; + ItemStatus aItemState; + Visibility aItemVisibility; + ControlCommand aControlCommand; + + if ( Event.State >>= bValue ) + { + // Boolean, treat it as checked/unchecked + if ( m_bMadeInvisible ) + m_xToolbar->ShowItem( m_nID ); + m_xToolbar->CheckItem( m_nID, bValue ); + if ( bValue ) + eTri = TRISTATE_TRUE; + nItemBits |= ToolBoxItemBits::CHECKABLE; + } + else if ( Event.State >>= aStrValue ) + { + OUString aText( MnemonicGenerator::EraseAllMnemonicChars( aStrValue ) ); + m_xToolbar->SetItemText( m_nID, aText ); + m_xToolbar->SetQuickHelpText( m_nID, aText ); + + if ( m_bMadeInvisible ) + m_xToolbar->ShowItem( m_nID ); + } + else if ( Event.State >>= aItemState ) + { + eTri = TRISTATE_INDET; + nItemBits |= ToolBoxItemBits::CHECKABLE; + if ( m_bMadeInvisible ) + m_xToolbar->ShowItem( m_nID ); + } + else if ( Event.State >>= aItemVisibility ) + { + m_xToolbar->ShowItem( m_nID, aItemVisibility.bVisible ); + m_bMadeInvisible = !aItemVisibility.bVisible; + } + else if ( Event.State >>= aControlCommand ) + { + if (aControlCommand.Command == "SetQuickHelpText") + { + for (NamedValue const & rArg : std::as_const(aControlCommand.Arguments)) + { + if (rArg.Name == "HelpText") + { + OUString aHelpText; + rArg.Value >>= aHelpText; + m_xToolbar->SetQuickHelpText(m_nID, aHelpText); + break; + } + } + } + else + { + executeControlCommand( aControlCommand ); + } + if ( m_bMadeInvisible ) + m_xToolbar->ShowItem( m_nID ); + } + + else if ( m_bMadeInvisible ) + m_xToolbar->ShowItem( m_nID ); + + m_xToolbar->SetItemState( m_nID, eTri ); + m_xToolbar->SetItemBits( m_nID, nItemBits ); +} + +IMPL_STATIC_LINK( ComplexToolbarController, ExecuteHdl_Impl, void*, p, void ) +{ + ExecuteInfo* pExecuteInfo = static_cast(p); + SolarMutexReleaser aReleaser; + 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 ( const Exception& ) + { + } + + delete pExecuteInfo; +} + +IMPL_STATIC_LINK( ComplexToolbarController, Notify_Impl, void*, p, void ) +{ + NotifyInfo* pNotifyInfo = static_cast(p); + SolarMutexReleaser aReleaser; + 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! + frame::ControlEvent aEvent; + aEvent.aURL = pNotifyInfo->aSourceURL; + aEvent.Event = pNotifyInfo->aEventName; + aEvent.aInformation = pNotifyInfo->aInfoSeq; + pNotifyInfo->xNotifyListener->controlEvent( aEvent ); + } + catch ( const Exception& ) + { + } + + delete pNotifyInfo; +} + +void ComplexToolbarController::addNotifyInfo( + const OUString& aEventName, + const uno::Reference< frame::XDispatch >& xDispatch, + const uno::Sequence< beans::NamedValue >& rInfo ) +{ + uno::Reference< frame::XControlNotificationListener > xControlNotify( xDispatch, uno::UNO_QUERY ); + + if ( !xControlNotify.is() ) + return; + + // Execute notification asynchronously + NotifyInfo* pNotifyInfo = new NotifyInfo; + + pNotifyInfo->aEventName = aEventName; + pNotifyInfo->xNotifyListener = xControlNotify; + pNotifyInfo->aSourceURL = getInitializedURL(); + + // Add frame as source to the information sequence + sal_Int32 nCount = rInfo.getLength(); + uno::Sequence< beans::NamedValue > aInfoSeq( rInfo ); + aInfoSeq.realloc( nCount+1 ); + auto pInfoSeq = aInfoSeq.getArray(); + pInfoSeq[nCount].Name = "Source"; + pInfoSeq[nCount].Value <<= getFrameInterface(); + pNotifyInfo->aInfoSeq = aInfoSeq; + + Application::PostUserEvent( LINK(nullptr, ComplexToolbarController, Notify_Impl), pNotifyInfo ); +} + +uno::Reference< frame::XDispatch > ComplexToolbarController::getDispatchFromCommand( const OUString& aCommand ) const +{ + uno::Reference< frame::XDispatch > xDispatch; + + if ( m_bInitialized && m_xFrame.is() && !aCommand.isEmpty() ) + { + URLToDispatchMap::const_iterator pIter = m_aListenerMap.find( aCommand ); + if ( pIter != m_aListenerMap.end() ) + xDispatch = pIter->second; + } + + return xDispatch; +} + +const css::util::URL& ComplexToolbarController::getInitializedURL() +{ + if ( m_aURL.Complete.isEmpty() ) + { + m_aURL.Complete = m_aCommandURL; + m_xURLTransformer->parseStrict( m_aURL ); + } + return m_aURL; +} + +void ComplexToolbarController::notifyFocusGet() +{ + // send focus get notification + uno::Sequence< beans::NamedValue > aInfo; + addNotifyInfo( "FocusSet", + getDispatchFromCommand( m_aCommandURL ), + aInfo ); +} + +void ComplexToolbarController::notifyFocusLost() +{ + // send focus lost notification + uno::Sequence< beans::NamedValue > aInfo; + addNotifyInfo( "FocusLost", + getDispatchFromCommand( m_aCommandURL ), + aInfo ); +} + +void ComplexToolbarController::notifyTextChanged( const OUString& aText ) +{ + // send text changed notification + uno::Sequence< beans::NamedValue > aInfo { { "Text", css::uno::Any(aText) } }; + addNotifyInfo( "TextChanged", + getDispatchFromCommand( m_aCommandURL ), + aInfo ); +} + +} // namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uielement/controlmenucontroller.cxx b/framework/source/uielement/controlmenucontroller.cxx new file mode 100644 index 0000000000..83f80d242a --- /dev/null +++ b/framework/source/uielement/controlmenucontroller.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 + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +static const char* aCommands[] = +{ + ".uno:ConvertToEdit", + ".uno:ConvertToButton", + ".uno:ConvertToFixed", + ".uno:ConvertToList", + ".uno:ConvertToCheckBox", + ".uno:ConvertToRadio", + ".uno:ConvertToGroup", + ".uno:ConvertToCombo", + ".uno:ConvertToImageBtn", + ".uno:ConvertToFileControl", + ".uno:ConvertToDate", + ".uno:ConvertToTime", + ".uno:ConvertToNumeric", + ".uno:ConvertToCurrency", + ".uno:ConvertToPattern", + ".uno:ConvertToImageControl", + ".uno:ConvertToFormatted", + ".uno:ConvertToScrollBar", + ".uno:ConvertToSpinButton", + ".uno:ConvertToNavigationBar" +}; + +static TranslateId aLabels[] = +{ + RID_STR_PROPTITLE_EDIT, + RID_STR_PROPTITLE_PUSHBUTTON, + RID_STR_PROPTITLE_FIXEDTEXT, + RID_STR_PROPTITLE_LISTBOX, + RID_STR_PROPTITLE_CHECKBOX, + RID_STR_PROPTITLE_RADIOBUTTON, + RID_STR_PROPTITLE_GROUPBOX, + RID_STR_PROPTITLE_COMBOBOX, + RID_STR_PROPTITLE_IMAGEBUTTON, + RID_STR_PROPTITLE_FILECONTROL, + RID_STR_PROPTITLE_DATEFIELD, + RID_STR_PROPTITLE_TIMEFIELD, + RID_STR_PROPTITLE_NUMERICFIELD, + RID_STR_PROPTITLE_CURRENCYFIELD, + RID_STR_PROPTITLE_PATTERNFIELD, + RID_STR_PROPTITLE_IMAGECONTROL, + RID_STR_PROPTITLE_FORMATTED, + RID_STR_PROPTITLE_SCROLLBAR, + RID_STR_PROPTITLE_SPINBUTTON, + RID_STR_PROPTITLE_NAVBAR +}; + +constexpr OUString aImgIds[] +{ + RID_SVXBMP_EDITBOX, + RID_SVXBMP_BUTTON, + RID_SVXBMP_FIXEDTEXT, + RID_SVXBMP_LISTBOX, + RID_SVXBMP_CHECKBOX, + RID_SVXBMP_RADIOBUTTON, + RID_SVXBMP_GROUPBOX, + RID_SVXBMP_COMBOBOX, + RID_SVXBMP_IMAGEBUTTON, + RID_SVXBMP_FILECONTROL, + RID_SVXBMP_DATEFIELD, + RID_SVXBMP_TIMEFIELD, + RID_SVXBMP_NUMERICFIELD, + RID_SVXBMP_CURRENCYFIELD, + RID_SVXBMP_PATTERNFIELD, + RID_SVXBMP_IMAGECONTROL, + RID_SVXBMP_FORMATTEDFIELD, + RID_SVXBMP_SCROLLBAR, + RID_SVXBMP_SPINBUTTON, + RID_SVXBMP_NAVIGATIONBAR +}; + +using namespace css; +using namespace css::uno; +using namespace css::lang; +using namespace css::frame; +using namespace css::beans; + +namespace { + +class ControlMenuController : public svt::PopupMenuControllerBase +{ + using svt::PopupMenuControllerBase::disposing; + +public: + explicit ControlMenuController( const uno::Reference< uno::XComponentContext >& xContext ); + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override + { + return "com.sun.star.comp.framework.ControlMenuController"; + } + + virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override + { + return cppu::supportsService(this, ServiceName); + } + + virtual css::uno::Sequence SAL_CALL getSupportedServiceNames() override + { + return {"com.sun.star.frame.PopupMenuController"}; + } + + // XPopupMenuController + virtual void SAL_CALL updatePopupMenu() override; + + // XStatusListener + virtual void SAL_CALL statusChanged( const frame::FeatureStateEvent& Event ) override; + + // XMenuListener + virtual void SAL_CALL itemActivated( const awt::MenuEvent& rEvent ) override; + + // XEventListener + virtual void SAL_CALL disposing( const lang::EventObject& Source ) override; + +private: + // XInitialization + virtual void initializeImpl( std::unique_lock& rGuard, const uno::Sequence< uno::Any >& aArguments ) override; + + class UrlToDispatchMap : public std::unordered_map< OUString, + uno::Reference< frame::XDispatch > > + { + public: + void free() + { + UrlToDispatchMap().swap( *this );// get rid of reserved capacity + } + }; + + void updateImagesPopupMenu(Reference const& rPopupMenu); + void fillPopupMenu(uno::Reference const& rPopupMenu); + + bool m_bShowMenuImages : 1; + UrlToDispatchMap m_aURLToDispatchMap; +}; + +ControlMenuController::ControlMenuController(const css::uno::Reference< css::uno::XComponentContext >& xContext) + : svt::PopupMenuControllerBase(xContext) +{ + const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings(); + m_bShowMenuImages = rSettings.GetUseImagesInMenus(); + +} + +// private function +void ControlMenuController::updateImagesPopupMenu(Reference const& rPopupMenu) +{ + if (!rPopupMenu) + return; + for (size_t i=0; i < std::size(aCommands); ++i) + { + sal_Int16 nItemId = i + 1; + if (m_bShowMenuImages) + { + Image aImage(StockImage::Yes, aImgIds[i]); + Graphic aGraphic(aImage); + rPopupMenu->setItemImage(nItemId, aGraphic.GetXGraphic(), false); + } + else + rPopupMenu->setItemImage(nItemId, nullptr, false); + } +} + +// private function +void ControlMenuController::fillPopupMenu( Reference< css::awt::XPopupMenu > const & rPopupMenu ) +{ + resetPopupMenu( rPopupMenu ); + + for (size_t i=0; i < SAL_N_ELEMENTS(aCommands); ++i) + { + sal_Int16 nItemId = i + 1; + OUString sCommand(OUString::createFromAscii(aCommands[i])); + rPopupMenu->insertItem(nItemId, FwkResId(aLabels[i]), 0, i); + rPopupMenu->setCommand(nItemId, sCommand); + rPopupMenu->enableItem(nItemId, false); + } + + updateImagesPopupMenu(rPopupMenu); + + rPopupMenu->hideDisabledEntries(true); +} + +} + +// XEventListener +void SAL_CALL ControlMenuController::disposing( const EventObject& ) +{ + Reference< css::awt::XMenuListener > xHolder(this); + + std::unique_lock aLock( m_aMutex ); + m_xFrame.clear(); + m_xDispatch.clear(); + + if ( m_xPopupMenu.is() ) + m_xPopupMenu->removeMenuListener( Reference< css::awt::XMenuListener >(this) ); + m_xPopupMenu.clear(); +} + +// XStatusListener +void SAL_CALL ControlMenuController::statusChanged( const FeatureStateEvent& Event ) +{ + std::unique_lock aLock( m_aMutex ); + + if (!m_xPopupMenu) + return; + + sal_Int16 nItemId = 0; + for (size_t i=0; i < SAL_N_ELEMENTS(aCommands); ++i) + { + if ( Event.FeatureURL.Complete.equalsAscii( aCommands[i] )) + { + nItemId = i + 1; + break; + } + } + + if (!nItemId) + return; + + m_xPopupMenu->enableItem(nItemId, Event.IsEnabled); +} + +// XMenuListener +void SAL_CALL ControlMenuController::itemActivated( const css::awt::MenuEvent& ) +{ + std::unique_lock aLock( m_aMutex ); + + SolarMutexGuard aSolarMutexGuard; + + // Check if some modes have changed so we have to update our menu images + const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings(); + bool bShowMenuImages = rSettings.GetUseImagesInMenus(); + + if (bShowMenuImages != m_bShowMenuImages) + { + m_bShowMenuImages = bShowMenuImages; + updateImagesPopupMenu(m_xPopupMenu); + } +} + +// XPopupMenuController +void SAL_CALL ControlMenuController::updatePopupMenu() +{ + std::unique_lock aLock( m_aMutex ); + + throwIfDisposed(aLock); + + if ( !(m_xFrame.is() && m_xPopupMenu.is()) ) + return; + + css::util::URL aTargetURL; + Reference< XDispatchProvider > xDispatchProvider( m_xFrame, UNO_QUERY ); + fillPopupMenu( m_xPopupMenu ); + m_aURLToDispatchMap.free(); + + for (const char* aCommand : aCommands) + { + aTargetURL.Complete = OUString::createFromAscii( aCommand ); + m_xURLTransformer->parseStrict( aTargetURL ); + + Reference< XDispatch > xDispatch = xDispatchProvider->queryDispatch( aTargetURL, OUString(), 0 ); + if ( xDispatch.is() ) + { + aLock.unlock(); // the addStatusListener will call back into ::statusChanged + xDispatch->addStatusListener( static_cast< XStatusListener* >(this), aTargetURL ); + xDispatch->removeStatusListener( static_cast< XStatusListener* >(this), aTargetURL ); + aLock.lock(); + m_aURLToDispatchMap.emplace( aTargetURL.Complete, xDispatch ); + } + } +} + +// XInitialization +void ControlMenuController::initializeImpl( std::unique_lock& rGuard, const Sequence< Any >& aArguments ) +{ + svt::PopupMenuControllerBase::initializeImpl(rGuard, aArguments); + m_aBaseURL.clear(); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_framework_ControlMenuController_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence const &) +{ + return cppu::acquire(new ControlMenuController(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uielement/dropdownboxtoolbarcontroller.cxx b/framework/source/uielement/dropdownboxtoolbarcontroller.cxx new file mode 100644 index 0000000000..b3478a7b1e --- /dev/null +++ b/framework/source/uielement/dropdownboxtoolbarcontroller.cxx @@ -0,0 +1,282 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include + +#include +#include +#include +#include +#include + +using namespace ::com::sun::star; +using namespace css::awt; +using namespace css::uno; +using namespace css::beans; +using namespace css::lang; +using namespace css::frame; +using namespace css::util; + +namespace framework +{ + +// Wrapper class to notify controller about events from ListBox. +// Unfortunaltly the events are notified through virtual methods instead +// of Listeners. + +class ListBoxControl final : public InterimItemWindow +{ +public: + ListBoxControl(vcl::Window* pParent, DropdownToolbarController* pListBoxListener); + virtual ~ListBoxControl() override; + virtual void dispose() override; + + void set_active(int nPos) { m_xWidget->set_active(nPos); } + void append_text(const OUString& rStr) { m_xWidget->append_text(rStr); } + void insert_text(int nPos, const OUString& rStr) { m_xWidget->insert_text(nPos, rStr); } + int get_count() const { return m_xWidget->get_count(); } + int find_text(const OUString& rStr) const { return m_xWidget->find_text(rStr); } + OUString get_active_text() const { return m_xWidget->get_active_text(); } + void clear() { return m_xWidget->clear(); } + void remove(int nPos) { m_xWidget->remove(nPos); } + + DECL_LINK(FocusInHdl, weld::Widget&, void); + DECL_LINK(FocusOutHdl, weld::Widget&, void); + DECL_LINK(ModifyHdl, weld::ComboBox&, void); + DECL_LINK(KeyInputHdl, const ::KeyEvent&, bool); + +private: + std::unique_ptr m_xWidget; + DropdownToolbarController* m_pListBoxListener; +}; + +ListBoxControl::ListBoxControl(vcl::Window* pParent, DropdownToolbarController* pListBoxListener) + : InterimItemWindow(pParent, "svt/ui/listcontrol.ui", "ListControl") + , m_xWidget(m_xBuilder->weld_combo_box("listbox")) + , m_pListBoxListener( pListBoxListener ) +{ + InitControlBase(m_xWidget.get()); + + m_xWidget->connect_focus_in(LINK(this, ListBoxControl, FocusInHdl)); + m_xWidget->connect_focus_out(LINK(this, ListBoxControl, FocusOutHdl)); + m_xWidget->connect_changed(LINK(this, ListBoxControl, ModifyHdl)); + m_xWidget->connect_key_press(LINK(this, ListBoxControl, KeyInputHdl)); + + m_xWidget->set_size_request(42, -1); // so a later narrow size request can stick + SetSizePixel(get_preferred_size()); +} + +IMPL_LINK(ListBoxControl, KeyInputHdl, const ::KeyEvent&, rKEvt, bool) +{ + return ChildKeyInput(rKEvt); +} + +ListBoxControl::~ListBoxControl() +{ + disposeOnce(); +} + +void ListBoxControl::dispose() +{ + m_pListBoxListener = nullptr; + m_xWidget.reset(); + InterimItemWindow::dispose(); +} + +IMPL_LINK_NOARG(ListBoxControl, ModifyHdl, weld::ComboBox&, void) +{ + if (m_pListBoxListener) + m_pListBoxListener->Select(); +} + +IMPL_LINK_NOARG(ListBoxControl, FocusInHdl, weld::Widget&, void) +{ + if (m_pListBoxListener) + m_pListBoxListener->GetFocus(); +} + +IMPL_LINK_NOARG(ListBoxControl, FocusOutHdl, weld::Widget&, void) +{ + if (m_pListBoxListener) + m_pListBoxListener->LoseFocus(); +} + +DropdownToolbarController::DropdownToolbarController( + const Reference< XComponentContext >& rxContext, + const Reference< XFrame >& rFrame, + ToolBox* pToolbar, + ToolBoxItemId nID, + sal_Int32 nWidth, + const OUString& aCommand ) : + ComplexToolbarController( rxContext, rFrame, pToolbar, nID, aCommand ) + , m_pListBoxControl( nullptr ) +{ + m_pListBoxControl = VclPtr::Create(m_xToolbar, this); + if ( nWidth == 0 ) + nWidth = 100; + + // ListBoxControl ctor has set a suitable height already + auto nHeight = m_pListBoxControl->GetSizePixel().Height(); + + m_pListBoxControl->SetSizePixel( ::Size( nWidth, nHeight )); + m_xToolbar->SetItemWindow( m_nID, m_pListBoxControl ); +} + +DropdownToolbarController::~DropdownToolbarController() +{ +} + +void SAL_CALL DropdownToolbarController::dispose() +{ + SolarMutexGuard aSolarMutexGuard; + + m_xToolbar->SetItemWindow( m_nID, nullptr ); + m_pListBoxControl.disposeAndClear(); + + ComplexToolbarController::dispose(); +} + +Sequence DropdownToolbarController::getExecuteArgs(sal_Int16 KeyModifier) const +{ + OUString aSelectedText = m_pListBoxControl->get_active_text(); + + // Add key modifier to argument list + Sequence aArgs{ comphelper::makePropertyValue("KeyModifier", KeyModifier), + comphelper::makePropertyValue("Text", aSelectedText) }; + return aArgs; +} + +void DropdownToolbarController::Select() +{ + if (m_pListBoxControl->get_count() > 0) + execute(0); +} + +void DropdownToolbarController::GetFocus() +{ + notifyFocusGet(); +} + +void DropdownToolbarController::LoseFocus() +{ + notifyFocusLost(); +} + +void DropdownToolbarController::executeControlCommand( const css::frame::ControlCommand& rControlCommand ) +{ + if ( rControlCommand.Command == "SetList" ) + { + for ( const NamedValue& rArg : rControlCommand.Arguments ) + { + if ( rArg.Name == "List" ) + { + Sequence< OUString > aList; + m_pListBoxControl->clear(); + + rArg.Value >>= aList; + for (OUString const & rName : std::as_const(aList)) + m_pListBoxControl->append_text(rName); + + m_pListBoxControl->set_active(0); + + // send notification + uno::Sequence< beans::NamedValue > aInfo { { "List", css::uno::Any(aList) } }; + addNotifyInfo( "ListChanged", + getDispatchFromCommand( m_aCommandURL ), + aInfo ); + + break; + } + } + } + else if ( rControlCommand.Command == "AddEntry" ) + { + OUString aText; + for ( const NamedValue& rArg : rControlCommand.Arguments ) + { + if ( rArg.Name == "Text" ) + { + if ( rArg.Value >>= aText ) + m_pListBoxControl->append_text(aText); + break; + } + } + } + else if ( rControlCommand.Command == "InsertEntry" ) + { + sal_Int32 nPos(-1); + OUString aText; + for ( const NamedValue& rArg : rControlCommand.Arguments ) + { + if ( rArg.Name == "Pos" ) + { + sal_Int32 nTmpPos = 0; + if ( rArg.Value >>= nTmpPos ) + { + if (( nTmpPos >= 0 ) && + ( nTmpPos < m_pListBoxControl->get_count() )) + nPos = nTmpPos; + } + } + else if ( rArg.Name == "Text" ) + rArg.Value >>= aText; + } + + m_pListBoxControl->insert_text(nPos, aText); + } + else if ( rControlCommand.Command == "RemoveEntryPos" ) + { + for ( const NamedValue& rArg : rControlCommand.Arguments ) + { + if ( rArg.Name == "Pos" ) + { + sal_Int32 nPos( -1 ); + if ( rArg.Value >>= nPos ) + { + if ( 0 <= nPos && nPos < m_pListBoxControl->get_count() ) + m_pListBoxControl->remove(nPos); + } + break; + } + } + } + else if ( rControlCommand.Command == "RemoveEntryText" ) + { + for ( const NamedValue& rArg : rControlCommand.Arguments ) + { + if ( rArg.Name == "Text" ) + { + OUString aText; + if ( rArg.Value >>= aText ) + { + auto nPos = m_pListBoxControl->find_text(aText); + if (nPos != -1) + m_pListBoxControl->remove(nPos); + } + break; + } + } + } +} + +} // namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uielement/edittoolbarcontroller.cxx b/framework/source/uielement/edittoolbarcontroller.cxx new file mode 100644 index 0000000000..c056516649 --- /dev/null +++ b/framework/source/uielement/edittoolbarcontroller.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 + +#include + +#include +#include +#include +#include +#include +#include + +using namespace ::com::sun::star; +using namespace css::uno; +using namespace css::beans; +using namespace css::lang; +using namespace css::frame; +using namespace css::util; + +namespace framework +{ + +// Wrapper class to notify controller about events from edit. +// Unfortunaltly the events are notified through virtual methods instead +// of Listeners. + +class EditControl final : public InterimItemWindow +{ +public: + EditControl(vcl::Window* pParent, EditToolbarController* pEditToolbarController); + virtual ~EditControl() override; + virtual void dispose() override; + + OUString get_text() const { return m_xWidget->get_text(); } + void set_text(const OUString& rText) { m_xWidget->set_text(rText); } + +private: + std::unique_ptr m_xWidget; + EditToolbarController* m_pEditToolbarController; + + DECL_LINK(FocusInHdl, weld::Widget&, void); + DECL_LINK(FocusOutHdl, weld::Widget&, void); + DECL_LINK(ModifyHdl, weld::Entry&, void); + DECL_LINK(ActivateHdl, weld::Entry&, bool); + DECL_LINK(KeyInputHdl, const ::KeyEvent&, bool); +}; + +EditControl::EditControl(vcl::Window* pParent, EditToolbarController* pEditToolbarController) + : InterimItemWindow(pParent, "svt/ui/editcontrol.ui", "EditControl") + , m_xWidget(m_xBuilder->weld_entry("entry")) + , m_pEditToolbarController(pEditToolbarController) +{ + OUString sEmpty; + m_xWidget->set_help_id(sEmpty); + m_xContainer->set_help_id(sEmpty); + + InitControlBase(m_xWidget.get()); + + m_xWidget->connect_focus_in(LINK(this, EditControl, FocusInHdl)); + m_xWidget->connect_focus_out(LINK(this, EditControl, FocusOutHdl)); + m_xWidget->connect_changed(LINK(this, EditControl, ModifyHdl)); + m_xWidget->connect_activate(LINK(this, EditControl, ActivateHdl)); + m_xWidget->connect_key_press(LINK(this, EditControl, KeyInputHdl)); + + SetSizePixel(get_preferred_size()); +} + +IMPL_LINK(EditControl, KeyInputHdl, const ::KeyEvent&, rKEvt, bool) +{ + return ChildKeyInput(rKEvt); +} + +EditControl::~EditControl() +{ + disposeOnce(); +} + +void EditControl::dispose() +{ + m_pEditToolbarController = nullptr; + m_xWidget.reset(); + InterimItemWindow::dispose(); +} + +IMPL_LINK_NOARG(EditControl, ModifyHdl, weld::Entry&, void) +{ + if (m_pEditToolbarController) + m_pEditToolbarController->Modify(); +} + +IMPL_LINK_NOARG(EditControl, FocusInHdl, weld::Widget&, void) +{ + if (m_pEditToolbarController) + m_pEditToolbarController->GetFocus(); +} + +IMPL_LINK_NOARG(EditControl, FocusOutHdl, weld::Widget&, void) +{ + if ( m_pEditToolbarController ) + m_pEditToolbarController->LoseFocus(); +} + +IMPL_LINK_NOARG(EditControl, ActivateHdl, weld::Entry&, bool) +{ + if (m_pEditToolbarController) + m_pEditToolbarController->Activate(); + return true; +} + +EditToolbarController::EditToolbarController( + const Reference< XComponentContext >& rxContext, + const Reference< XFrame >& rFrame, + ToolBox* pToolbar, + ToolBoxItemId nID, + sal_Int32 nWidth, + const OUString& aCommand ) : + ComplexToolbarController( rxContext, rFrame, pToolbar, nID, aCommand ) + , m_pEditControl( nullptr ) +{ + m_pEditControl = VclPtr::Create(m_xToolbar, this); + if ( nWidth == 0 ) + nWidth = 100; + + // EditControl ctor has set a suitable height already + auto nHeight = m_pEditControl->GetSizePixel().Height(); + + m_pEditControl->SetSizePixel( ::Size( nWidth, nHeight )); + m_xToolbar->SetItemWindow( m_nID, m_pEditControl ); +} + +EditToolbarController::~EditToolbarController() +{ +} + +void SAL_CALL EditToolbarController::dispose() +{ + SolarMutexGuard aSolarMutexGuard; + + m_xToolbar->SetItemWindow( m_nID, nullptr ); + m_pEditControl.disposeAndClear(); + + ComplexToolbarController::dispose(); +} + +Sequence EditToolbarController::getExecuteArgs(sal_Int16 KeyModifier) const +{ + OUString aSelectedText = m_pEditControl->get_text(); + + // Add key modifier to argument list + Sequence aArgs{ comphelper::makePropertyValue("KeyModifier", KeyModifier), + comphelper::makePropertyValue("Text", aSelectedText) }; + return aArgs; +} + +void EditToolbarController::Modify() +{ + notifyTextChanged(m_pEditControl->get_text()); +} + +void EditToolbarController::GetFocus() +{ + notifyFocusGet(); +} + +void EditToolbarController::LoseFocus() +{ + notifyFocusLost(); +} + +void EditToolbarController::Activate() +{ + // Call execute only with non-empty text + if (!m_pEditControl->get_text().isEmpty()) + execute(0); +} + +void EditToolbarController::executeControlCommand( const css::frame::ControlCommand& rControlCommand ) +{ + if ( !rControlCommand.Command.startsWith( "SetText" )) + return; + + for ( const NamedValue& rArg : rControlCommand.Arguments ) + { + if ( rArg.Name.startsWith( "Text" )) + { + OUString aText; + rArg.Value >>= aText; + m_pEditControl->set_text(aText); + + // send notification + notifyTextChanged( aText ); + break; + } + } +} + +} // namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uielement/fontmenucontroller.cxx b/framework/source/uielement/fontmenucontroller.cxx new file mode 100644 index 0000000000..0190053563 --- /dev/null +++ b/framework/source/uielement/fontmenucontroller.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 + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +// Defines + +using namespace css::uno; +using namespace css::lang; +using namespace css::frame; +using namespace css::beans; +using namespace css::util; + +static bool lcl_I18nCompareString(const OUString& rStr1, const OUString& rStr2) +{ + const vcl::I18nHelper& rI18nHelper = Application::GetSettings().GetUILocaleI18nHelper(); + return rI18nHelper.CompareString( rStr1, rStr2 ) < 0; +} + +namespace framework +{ + +// XInterface, XTypeProvider, XServiceInfo + +OUString SAL_CALL FontMenuController::getImplementationName() +{ + return "com.sun.star.comp.framework.FontMenuController"; +} + +sal_Bool SAL_CALL FontMenuController::supportsService( const OUString& sServiceName ) +{ + return cppu::supportsService(this, sServiceName); +} + +css::uno::Sequence< OUString > SAL_CALL FontMenuController::getSupportedServiceNames() +{ + return { SERVICENAME_POPUPMENUCONTROLLER }; +} + +FontMenuController::FontMenuController( const css::uno::Reference< css::uno::XComponentContext >& xContext ) : + svt::PopupMenuControllerBase( xContext ) +{ +} + +FontMenuController::~FontMenuController() +{ +} + +// private function +void FontMenuController::fillPopupMenu( const Sequence< OUString >& rFontNameSeq, Reference< css::awt::XPopupMenu > const & rPopupMenu ) +{ + SolarMutexGuard aSolarMutexGuard; + + resetPopupMenu( rPopupMenu ); + + std::vector aVector; + aVector.reserve(rFontNameSeq.getLength()); + for ( OUString const & s : rFontNameSeq ) + { + aVector.push_back(MnemonicGenerator::EraseAllMnemonicChars(s)); + } + sort(aVector.begin(), aVector.end(), lcl_I18nCompareString ); + + static constexpr OUStringLiteral aFontNameCommandPrefix( u".uno:CharFontName?CharFontName.FamilyName:string=" ); + const sal_Int16 nCount = static_cast(aVector.size()); + for ( sal_Int16 i = 0; i < nCount; i++ ) + { + const OUString& rName = aVector[i]; + m_xPopupMenu->insertItem( i+1, rName, css::awt::MenuItemStyle::RADIOCHECK | css::awt::MenuItemStyle::AUTOCHECK, i ); + if ( rName == m_aFontFamilyName ) + m_xPopupMenu->checkItem( i+1, true ); + OUString aFontNameCommand = aFontNameCommandPrefix + INetURLObject::encode( rName, INetURLObject::PART_HTTP_QUERY, INetURLObject::EncodeMechanism::All ); + m_xPopupMenu->setCommand(i + 1, aFontNameCommand); // Store font name into item command. + } +} + +// XEventListener +void SAL_CALL FontMenuController::disposing( const EventObject& ) +{ + Reference< css::awt::XMenuListener > xHolder(this); + + std::unique_lock aLock( m_aMutex ); + m_xFrame.clear(); + m_xDispatch.clear(); + m_xFontListDispatch.clear(); + + if ( m_xPopupMenu.is() ) + m_xPopupMenu->removeMenuListener( Reference< css::awt::XMenuListener >(this) ); + m_xPopupMenu.clear(); +} + +// XStatusListener +void SAL_CALL FontMenuController::statusChanged( const FeatureStateEvent& Event ) +{ + css::awt::FontDescriptor aFontDescriptor; + Sequence< OUString > aFontNameSeq; + + if ( Event.State >>= aFontDescriptor ) + { + std::unique_lock aLock( m_aMutex ); + m_aFontFamilyName = aFontDescriptor.Name; + } + else if ( Event.State >>= aFontNameSeq ) + { + std::unique_lock aLock( m_aMutex ); + if ( m_xPopupMenu.is() ) + fillPopupMenu( aFontNameSeq, m_xPopupMenu ); + } +} + +// XMenuListener +void SAL_CALL FontMenuController::itemActivated( const css::awt::MenuEvent& ) +{ + std::unique_lock aLock( m_aMutex ); + + if ( !m_xPopupMenu.is() ) + return; + + // find new font name and set check mark! + sal_uInt16 nChecked = 0; + sal_uInt16 nItemCount = m_xPopupMenu->getItemCount(); + for( sal_uInt16 i = 0; i < nItemCount; i++ ) + { + sal_uInt16 nItemId = m_xPopupMenu->getItemId( i ); + + if ( m_xPopupMenu->isItemChecked( nItemId ) ) + nChecked = nItemId; + + OUString aText = m_xPopupMenu->getItemText( nItemId ); + + // TODO: must be replaced by implementation of VCL, when available + sal_Int32 nIndex = aText.indexOf( '~' ); + if ( nIndex >= 0 ) + aText = aText.replaceAt( nIndex, 1, u"" ); + // TODO: must be replaced by implementation of VCL, when available + + if ( aText == m_aFontFamilyName ) + { + m_xPopupMenu->checkItem( nItemId, true ); + return; + } + } + + if ( nChecked ) + m_xPopupMenu->checkItem( nChecked, false ); +} + +// XPopupMenuController +void FontMenuController::impl_setPopupMenu() +{ + Reference< XDispatchProvider > xDispatchProvider( m_xFrame, UNO_QUERY ); + + css::util::URL aTargetURL; + // Register for font list updates to get the current font list from the controller + aTargetURL.Complete = ".uno:FontNameList"; + m_xURLTransformer->parseStrict( aTargetURL ); + m_xFontListDispatch = xDispatchProvider->queryDispatch( aTargetURL, OUString(), 0 ); +} + +void SAL_CALL FontMenuController::updatePopupMenu() +{ + svt::PopupMenuControllerBase::updatePopupMenu(); + + std::unique_lock aLock( m_aMutex ); + Reference< XDispatch > xDispatch( m_xFontListDispatch ); + css::util::URL aTargetURL; + aTargetURL.Complete = ".uno:FontNameList"; + m_xURLTransformer->parseStrict( aTargetURL ); + aLock.unlock(); + + if ( xDispatch.is() ) + { + xDispatch->addStatusListener( static_cast< XStatusListener* >(this), aTargetURL ); + xDispatch->removeStatusListener( static_cast< XStatusListener* >(this), aTargetURL ); + } +} + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +framework_FontMenuController_get_implementation( + css::uno::XComponentContext* context, css::uno::Sequence const& ) +{ + return cppu::acquire(new framework::FontMenuController(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uielement/fontsizemenucontroller.cxx b/framework/source/uielement/fontsizemenucontroller.cxx new file mode 100644 index 0000000000..10234d61cb --- /dev/null +++ b/framework/source/uielement/fontsizemenucontroller.cxx @@ -0,0 +1,286 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Defines + +using namespace com::sun::star::uno; +using namespace com::sun::star::lang; +using namespace com::sun::star::frame; +using namespace com::sun::star::beans; +using namespace com::sun::star::util; +using namespace com::sun::star::view; + +namespace framework +{ + +OUString SAL_CALL FontSizeMenuController::getImplementationName() +{ + return "com.sun.star.comp.framework.FontSizeMenuController"; +} + +sal_Bool SAL_CALL FontSizeMenuController::supportsService( const OUString& sServiceName ) +{ + return cppu::supportsService(this, sServiceName); +} + +css::uno::Sequence< OUString > SAL_CALL FontSizeMenuController::getSupportedServiceNames() +{ + return { SERVICENAME_POPUPMENUCONTROLLER }; +} + +FontSizeMenuController::FontSizeMenuController( const css::uno::Reference< css::uno::XComponentContext >& xContext ) : + svt::PopupMenuControllerBase( xContext ) +{ +} + +FontSizeMenuController::~FontSizeMenuController() +{ +} + +// private function +OUString FontSizeMenuController::retrievePrinterName( css::uno::Reference< css::frame::XFrame > const & rFrame ) +{ + OUString aPrinterName; + + if ( rFrame.is() ) + { + Reference< XController > xController = m_xFrame->getController(); + if ( xController.is() ) + { + Reference< XPrintable > xPrintable( xController->getModel(), UNO_QUERY ); + if ( xPrintable.is() ) + { + const Sequence< PropertyValue > aPrinterSeq = xPrintable->getPrinter(); + for ( PropertyValue const & prop : aPrinterSeq ) + { + if ( prop.Name == "Name" ) + { + prop.Value >>= aPrinterName; + break; + } + } + } + } + } + + return aPrinterName; +} + +// private function +void FontSizeMenuController::setCurHeight( tools::Long nHeight, Reference< css::awt::XPopupMenu > const & rPopupMenu ) +{ + // check menu item + sal_uInt16 nChecked = 0; + sal_uInt16 nItemCount = rPopupMenu->getItemCount(); + for( sal_uInt16 i = 0; i < nItemCount; i++ ) + { + sal_uInt16 nItemId = rPopupMenu->getItemId( i ); + + if ( m_aHeightArray[i] == nHeight ) + { + rPopupMenu->checkItem( nItemId, true ); + return; + } + + if ( rPopupMenu->isItemChecked( nItemId ) ) + nChecked = nItemId; + } + + if ( nChecked ) + rPopupMenu->checkItem( nChecked, false ); +} + +// private function +void FontSizeMenuController::fillPopupMenu( Reference< css::awt::XPopupMenu > const & rPopupMenu ) +{ + resetPopupMenu( rPopupMenu ); + + std::unique_ptr pFontList; + ScopedVclPtr pInfoPrinter; + OUString aPrinterName; + + SolarMutexGuard aSolarMutexGuard; + + // try to retrieve printer name of document + aPrinterName = retrievePrinterName( m_xFrame ); + if ( !aPrinterName.isEmpty() ) + { + pInfoPrinter.disposeAndReset(VclPtr::Create( aPrinterName )); + if ( pInfoPrinter && pInfoPrinter->GetFontFaceCollectionCount() > 0 ) + pFontList.reset(new FontList( pInfoPrinter.get() )); + } + + if ( !pFontList ) + pFontList.reset(new FontList( Application::GetDefaultDevice() )); + + // setup font size array + m_aHeightArray.clear(); + + sal_uInt16 nPos = 0; // Id is nPos+1 + static constexpr OUString aFontHeightCommand( u".uno:FontHeight?FontHeight.Height:float="_ustr ); + + // first insert font size names (for simplified/traditional chinese) + FontSizeNames aFontSizeNames( Application::GetSettings().GetUILanguageTag().getLanguageType() ); + OUString aCommand; + + if (!aFontSizeNames.IsEmpty()) + { + // for scalable fonts all font size names + sal_Int32 nCount = aFontSizeNames.Count(); + for( sal_Int32 i = 0; i < nCount; i++ ) + { + OUString aSizeName = aFontSizeNames.GetIndexName( i ); + sal_Int32 nSize = aFontSizeNames.GetIndexSize( i ); + m_aHeightArray.push_back(nSize); + rPopupMenu->insertItem(nPos + 1, aSizeName, css::awt::MenuItemStyle::RADIOCHECK | css::awt::MenuItemStyle::AUTOCHECK, nPos); + + // Create dispatchable .uno command and set it + float fPoint = float(nSize) / 10; + aCommand = aFontHeightCommand + OUString::number( fPoint ); + rPopupMenu->setCommand(nPos + 1, aCommand); + + ++nPos; + } + } + + // then insert numerical font size values + const vcl::I18nHelper& rI18nHelper = Application::GetSettings().GetUILocaleI18nHelper(); + const int* pAry = FontList::GetStdSizeAry(); + const int* pTempAry = pAry; + while ( *pTempAry ) + { + m_aHeightArray.push_back(*pTempAry); + rPopupMenu->insertItem(nPos + 1, rI18nHelper.GetNum(*pTempAry, 1, true, false), + css::awt::MenuItemStyle::RADIOCHECK | css::awt::MenuItemStyle::AUTOCHECK, nPos); + + // Create dispatchable .uno command and set it + float fPoint = float(*pTempAry) / 10; + aCommand = aFontHeightCommand + OUString::number( fPoint ); + rPopupMenu->setCommand(nPos + 1, aCommand); + + ++nPos; + pTempAry++; + } + + setCurHeight( tools::Long( m_aFontHeight.Height * 10), rPopupMenu ); +} + +// XEventListener +void SAL_CALL FontSizeMenuController::disposing( const EventObject& ) +{ + Reference< css::awt::XMenuListener > xHolder(this); + + std::unique_lock aLock( m_aMutex ); + m_xFrame.clear(); + m_xDispatch.clear(); + m_xCurrentFontDispatch.clear(); + if ( m_xPopupMenu.is() ) + m_xPopupMenu->removeMenuListener( Reference< css::awt::XMenuListener >(this) ); + m_xPopupMenu.clear(); +} + +// XStatusListener +void SAL_CALL FontSizeMenuController::statusChanged( const FeatureStateEvent& Event ) +{ + css::awt::FontDescriptor aFontDescriptor; + css::frame::status::FontHeight aFontHeight; + + if ( Event.State >>= aFontDescriptor ) + { + std::unique_lock aLock( m_aMutex ); + + if ( m_xPopupMenu.is() ) + fillPopupMenu( m_xPopupMenu ); + } + else if ( Event.State >>= aFontHeight ) + { + std::unique_lock aLock( m_aMutex ); + m_aFontHeight = aFontHeight; + + if ( m_xPopupMenu.is() ) + { + SolarMutexGuard aSolarMutexGuard; + setCurHeight( tools::Long( m_aFontHeight.Height * 10), m_xPopupMenu ); + } + } +} + +// XPopupMenuController +void FontSizeMenuController::impl_setPopupMenu() +{ + Reference< XDispatchProvider > xDispatchProvider( m_xFrame, UNO_QUERY ); + css::util::URL aTargetURL; + // Register for font name updates which gives us info about the current font! + aTargetURL.Complete = ".uno:CharFontName"; + m_xURLTransformer->parseStrict( aTargetURL ); + m_xCurrentFontDispatch = xDispatchProvider->queryDispatch( aTargetURL, OUString(), 0 ); +} + +void SAL_CALL FontSizeMenuController::updatePopupMenu() +{ + std::unique_lock aLock( m_aMutex ); + + throwIfDisposed(aLock); + + Reference< XDispatch > xDispatch( m_xCurrentFontDispatch ); + css::util::URL aTargetURL; + aTargetURL.Complete = ".uno:CharFontName"; + m_xURLTransformer->parseStrict( aTargetURL ); + aLock.unlock(); + + if ( xDispatch.is() ) + { + xDispatch->addStatusListener( static_cast< XStatusListener* >(this), aTargetURL ); + xDispatch->removeStatusListener( static_cast< XStatusListener* >(this), aTargetURL ); + } + + svt::PopupMenuControllerBase::updatePopupMenu(); +} +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +framework_FontSizeMenuController_get_implementation( + css::uno::XComponentContext* context, css::uno::Sequence const& ) +{ + return cppu::acquire(new framework::FontSizeMenuController(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uielement/footermenucontroller.cxx b/framework/source/uielement/footermenucontroller.cxx new file mode 100644 index 0000000000..9a941a8258 --- /dev/null +++ b/framework/source/uielement/footermenucontroller.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 + +#include + +#include +#include + +// Defines + +using namespace com::sun::star::uno; +using namespace com::sun::star::lang; +using namespace com::sun::star::beans; +using namespace com::sun::star::util; +using namespace com::sun::star::style; +using namespace com::sun::star::container; + +namespace framework +{ + +// XInterface, XTypeProvider, XServiceInfo + +OUString SAL_CALL FooterMenuController::getImplementationName() +{ + return "com.sun.star.comp.framework.FooterMenuController"; +} + +sal_Bool SAL_CALL FooterMenuController::supportsService( const OUString& sServiceName ) +{ + return cppu::supportsService(this, sServiceName); +} + +css::uno::Sequence< OUString > SAL_CALL FooterMenuController::getSupportedServiceNames() +{ + return { SERVICENAME_POPUPMENUCONTROLLER }; +} + +FooterMenuController::FooterMenuController( const css::uno::Reference< css::uno::XComponentContext >& xContext ) : + HeaderMenuController( xContext,true ) +{ +} + +FooterMenuController::~FooterMenuController() +{ +} +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +framework_FooterMenuController_get_implementation( + css::uno::XComponentContext* context, css::uno::Sequence const& ) +{ + return cppu::acquire(new framework::FooterMenuController(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uielement/genericstatusbarcontroller.cxx b/framework/source/uielement/genericstatusbarcontroller.cxx new file mode 100644 index 0000000000..4a5aa46055 --- /dev/null +++ b/framework/source/uielement/genericstatusbarcontroller.cxx @@ -0,0 +1,157 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +using namespace ::cppu; +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::frame; + +namespace framework +{ + +GenericStatusbarController::GenericStatusbarController( + const Reference< XComponentContext >& rxContext, + const Reference< XFrame >& rxFrame, + const Reference< ui::XStatusbarItem >& rxItem, + AddonStatusbarItemData *pItemData ) + : svt::StatusbarController( rxContext, rxFrame, OUString(), 0 ) + , m_bEnabled( false ) + , m_bOwnerDraw( false ) + , m_pItemData( pItemData ) +{ + m_xStatusbarItem = rxItem; + if ( m_xStatusbarItem.is() ) + { + assert(m_aCommandURL.pData); + m_aCommandURL = m_xStatusbarItem->getCommand(); + m_nID = m_xStatusbarItem->getItemId(); + m_bOwnerDraw = ( m_xStatusbarItem->getStyle() & ui::ItemStyle::OWNER_DRAW ) == ui::ItemStyle::OWNER_DRAW; + if ( !m_bOwnerDraw && m_pItemData && m_pItemData->aLabel.getLength() ) + m_xStatusbarItem->setText( m_pItemData->aLabel ); + } +} + +GenericStatusbarController::~GenericStatusbarController() +{ +} + +void SAL_CALL GenericStatusbarController::dispose() +{ + svt::StatusbarController::dispose(); + + SolarMutexGuard aGuard; + m_pItemData = nullptr; + m_xGraphic.clear(); + m_xStatusbarItem.clear(); + +} + +void SAL_CALL GenericStatusbarController::statusChanged( + const FeatureStateEvent& rEvent) +{ + SolarMutexGuard aGuard; + + if ( m_bDisposed || !m_xStatusbarItem.is() ) + return; + + m_bEnabled = rEvent.IsEnabled; + + OUString aStrValue; + Reference< graphic::XGraphic > aGraphic; + + if ( rEvent.State >>= aStrValue ) + { + if ( !m_bOwnerDraw ) + m_xStatusbarItem->setText( aStrValue ); + else + { + if ( aStrValue.getLength() ) + { + m_xStatusbarItem->setQuickHelpText( aStrValue ); + } + } + } + else if ( ( rEvent.State >>= aGraphic ) && m_bOwnerDraw ) + { + m_xGraphic = aGraphic; + } + + // when the status is updated, and the controller is responsible for + // painting the statusbar item content, we must trigger a repaint + if ( m_bOwnerDraw && m_xStatusbarItem->getVisible() ) + { + m_xStatusbarItem->repaint(); + } +} + +void SAL_CALL GenericStatusbarController::paint( + const Reference< awt::XGraphics >& xGraphics, + const awt::Rectangle& rOutputRectangle, + ::sal_Int32 /*nStyle*/ ) +{ + SolarMutexGuard aGuard; + + const Reference< awt::XGraphics2 > xGraphics2(xGraphics, UNO_QUERY); + + if ( !m_xStatusbarItem.is() || !xGraphics2.is() ) + return; + + Reference< beans::XPropertySet > xGraphicProps( m_xGraphic, UNO_QUERY ); + + if ( xGraphicProps.is() && m_xGraphic->getType() != graphic::GraphicType::EMPTY ) + { + awt::Size aGraphicSize; + xGraphicProps->getPropertyValue( "SizePixel" ) >>= aGraphicSize; + OSL_ENSURE( aGraphicSize.Height > 0 && aGraphicSize.Width > 0, "Empty status bar graphic!" ); + + sal_Int32 nOffset = m_xStatusbarItem->getOffset( ); + awt::Point aPos; + aPos.X = ( rOutputRectangle.Width + nOffset ) / 2 - aGraphicSize.Width / 2; + aPos.Y = rOutputRectangle.Height / 2 - aGraphicSize.Height / 2; + + xGraphics2->drawImage( rOutputRectangle.X + aPos.X, + rOutputRectangle.Y + aPos.Y, + aGraphicSize.Width, + aGraphicSize.Height, + m_bEnabled ? awt::ImageDrawMode::NONE : awt::ImageDrawMode::DISABLE, + m_xGraphic ); + } + else + { + xGraphics2->clear( rOutputRectangle ); + } +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uielement/generictoolbarcontroller.cxx b/framework/source/uielement/generictoolbarcontroller.cxx new file mode 100644 index 0000000000..827991f722 --- /dev/null +++ b/framework/source/uielement/generictoolbarcontroller.cxx @@ -0,0 +1,434 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::frame; +using namespace ::com::sun::star::frame::status; + +namespace framework +{ + +static bool isEnumCommand( std::u16string_view rCommand ) +{ + INetURLObject aURL( rCommand ); + + return ( aURL.GetProtocol() == INetProtocol::Uno ) && + ( aURL.GetURLPath().indexOf( '.' ) != -1); +} + +static OUString getEnumCommand( std::u16string_view rCommand ) +{ + INetURLObject aURL( rCommand ); + + OUString aEnumCommand; + OUString aURLPath = aURL.GetURLPath(); + sal_Int32 nIndex = aURLPath.indexOf( '.' ); + if (( nIndex > 0 ) && ( nIndex < aURLPath.getLength() )) + aEnumCommand = aURLPath.copy( nIndex+1 ); + + return aEnumCommand; +} + +static OUString getMasterCommand( const OUString& rCommand ) +{ + OUString aMasterCommand( rCommand ); + INetURLObject aURL( rCommand ); + if ( aURL.GetProtocol() == INetProtocol::Uno ) + { + sal_Int32 nIndex = aURL.GetURLPath().indexOf( '.' ); + if ( nIndex ) + { + aURL.SetURLPath( aURL.GetURLPath().subView( 0, nIndex ) ); + aMasterCommand = aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ); + } + } + return aMasterCommand; +} + +GenericToolbarController::GenericToolbarController( const Reference< XComponentContext >& rxContext, + const Reference< XFrame >& rFrame, + ToolBox* pToolbar, + ToolBoxItemId nID, + const OUString& aCommand ) : + svt::ToolboxController( rxContext, rFrame, aCommand ) + , m_xToolbar( pToolbar ) + , m_nID( nID ) + , m_bEnumCommand( isEnumCommand( aCommand )) + , m_bMirrored( false ) + , m_bMadeInvisible( false ) + , m_aEnumCommand( getEnumCommand( aCommand )) +{ + if ( m_bEnumCommand ) + addStatusListener( getMasterCommand( aCommand ) ); + + addStatusListener( aCommand ); + + // Initialization is done through ctor + m_bInitialized = true; +} + +GenericToolbarController::GenericToolbarController( const Reference< XComponentContext >& rxContext, + const Reference< XFrame >& rFrame, + weld::Toolbar& rToolbar, + const OUString& aCommand ) : + GenericToolbarController( rxContext, rFrame, nullptr, ToolBoxItemId(0), aCommand ) +{ + m_pToolbar = &rToolbar; +} + +GenericToolbarController::~GenericToolbarController() +{ +} + +void SAL_CALL GenericToolbarController::dispose() +{ + SolarMutexGuard aSolarMutexGuard; + + svt::ToolboxController::dispose(); + + m_pToolbar = nullptr; + m_xToolbar.clear(); + m_nID = ToolBoxItemId(0); +} + +void SAL_CALL GenericToolbarController::execute( sal_Int16 KeyModifier ) +{ + Reference< XDispatch > xDispatch; + OUString aCommandURL; + + { + SolarMutexGuard aSolarMutexGuard; + + if ( m_bDisposed ) + throw DisposedException(); + + if ( m_bInitialized && + m_xFrame.is() && + !m_aCommandURL.isEmpty() ) + { + aCommandURL = m_aCommandURL; + URLToDispatchMap::iterator pIter = m_aListenerMap.find( m_aCommandURL ); + if ( pIter != m_aListenerMap.end() ) + xDispatch = pIter->second; + } + } + + if ( !xDispatch.is() ) + return; + + css::util::URL aTargetURL; + + // Add key modifier to argument list + Sequence aArgs{ comphelper::makePropertyValue("KeyModifier", KeyModifier) }; + + // handle also command aliases + auto aProperties = vcl::CommandInfoProvider::GetCommandProperties(m_aCommandURL, + vcl::CommandInfoProvider::GetModuleIdentifier(m_xFrame)); + OUString sRealCommand = vcl::CommandInfoProvider::GetRealCommandForCommand(aProperties); + + aTargetURL.Complete = sRealCommand.isEmpty() ? aCommandURL : sRealCommand; + if ( m_xUrlTransformer.is() ) + m_xUrlTransformer->parseStrict( aTargetURL ); + + // Execute dispatch asynchronously + ExecuteInfo* pExecuteInfo = new ExecuteInfo; + pExecuteInfo->xDispatch = xDispatch; + pExecuteInfo->aTargetURL = aTargetURL; + pExecuteInfo->aArgs = aArgs; + Application::PostUserEvent( LINK(nullptr, GenericToolbarController , ExecuteHdl_Impl), pExecuteInfo ); +} + +void GenericToolbarController::statusChanged( const FeatureStateEvent& Event ) +{ + SolarMutexGuard aSolarMutexGuard; + + if ( m_bDisposed ) + return; + + if ( m_pToolbar ) + { + m_pToolbar->set_item_sensitive(m_aCommandURL, Event.IsEnabled); + + bool bValue; + OUString aStrValue; + SfxImageItem aImageItem; + + if ( Event.State >>= bValue ) + { + // Boolean, treat it as checked/unchecked + m_pToolbar->set_item_active(m_aCommandURL, bValue); + } + else if ( Event.State >>= aStrValue ) + { + m_pToolbar->set_item_label(m_aCommandURL, aStrValue); + } + else if ( aImageItem.PutValue( Event.State, 0 ) && aImageItem.IsMirrored() != m_bMirrored ) + { + m_pToolbar->set_item_image_mirrored(m_aCommandURL, aImageItem.IsMirrored()); + auto xGraphic(vcl::CommandInfoProvider::GetXGraphicForCommand(m_aCommandURL, m_xFrame, m_pToolbar->get_icon_size())); + m_pToolbar->set_item_image(m_aCommandURL, xGraphic); + m_bMirrored = !m_bMirrored; + } + else + m_pToolbar->set_item_active(m_aCommandURL, false); + + return; + } + + if ( !m_xToolbar ) + return; + + m_xToolbar->EnableItem( m_nID, Event.IsEnabled ); + + ToolBoxItemBits nItemBits = m_xToolbar->GetItemBits( m_nID ); + nItemBits &= ~ToolBoxItemBits::CHECKABLE; + TriState eTri = TRISTATE_FALSE; + + bool bValue; + OUString aStrValue; + ItemStatus aItemState; + Visibility aItemVisibility; + ControlCommand aControlCommand; + SfxImageItem aImageItem; + + if (( Event.State >>= bValue ) && !m_bEnumCommand ) + { + // Boolean, treat it as checked/unchecked + if ( m_bMadeInvisible ) + m_xToolbar->ShowItem( m_nID ); + m_xToolbar->CheckItem( m_nID, bValue ); + if ( bValue ) + eTri = TRISTATE_TRUE; + nItemBits |= ToolBoxItemBits::CHECKABLE; + } + else if ( Event.State >>= aStrValue ) + { + if ( m_bEnumCommand ) + { + bValue = aStrValue == m_aEnumCommand; + + m_xToolbar->CheckItem( m_nID, bValue ); + if ( bValue ) + eTri = TRISTATE_TRUE; + nItemBits |= ToolBoxItemBits::CHECKABLE; + } + else + { + // Replacement for place holders + if ( aStrValue.startsWith("($1)") ) + { + aStrValue = FwkResId(STR_UPDATEDOC) + " " + aStrValue.subView( 4 ); + } + else if ( aStrValue.startsWith("($2)") ) + { + aStrValue = FwkResId(STR_CLOSEDOC_ANDRETURN) + aStrValue.subView( 4 ); + } + else if ( aStrValue.startsWith("($3)") ) + { + aStrValue = FwkResId(STR_SAVECOPYDOC) + aStrValue.subView( 4 ); + } + m_xToolbar->SetItemText( m_nID, aStrValue ); + // tdf#124267 strip mnemonic from tooltip + m_xToolbar->SetQuickHelpText(m_nID, aStrValue.replaceFirst("~", "")); + } + + if ( m_bMadeInvisible ) + m_xToolbar->ShowItem( m_nID ); + } + else if (( Event.State >>= aItemState ) && !m_bEnumCommand ) + { + eTri = TRISTATE_INDET; + nItemBits |= ToolBoxItemBits::CHECKABLE; + if ( m_bMadeInvisible ) + m_xToolbar->ShowItem( m_nID ); + } + else if ( Event.State >>= aItemVisibility ) + { + m_xToolbar->ShowItem( m_nID, aItemVisibility.bVisible ); + m_bMadeInvisible = !aItemVisibility.bVisible; + } + else if ( Event.State >>= aControlCommand ) + { + if (aControlCommand.Command == "SetQuickHelpText") + { + for ( NamedValue const & rArg : std::as_const(aControlCommand.Arguments) ) + { + if (rArg.Name == "HelpText") + { + OUString aHelpText; + rArg.Value >>= aHelpText; + m_xToolbar->SetQuickHelpText(m_nID, aHelpText); + break; + } + } + } + if ( m_bMadeInvisible ) + m_xToolbar->ShowItem( m_nID ); + } + else if ( aImageItem.PutValue( Event.State, 0 ) && aImageItem.IsMirrored() != m_bMirrored ) + { + m_xToolbar->SetItemImageMirrorMode( m_nID, aImageItem.IsMirrored() ); + Image aImage( vcl::CommandInfoProvider::GetImageForCommand( m_aCommandURL, m_xFrame, m_xToolbar->GetImageSize() )); + m_xToolbar->SetItemImage( m_nID, aImage ); + m_bMirrored = !m_bMirrored; + if ( m_bMadeInvisible ) + m_xToolbar->ShowItem( m_nID ); + } + else if ( m_bMadeInvisible ) + m_xToolbar->ShowItem( m_nID ); + + m_xToolbar->SetItemState( m_nID, eTri ); + m_xToolbar->SetItemBits( m_nID, nItemBits ); +} + +IMPL_STATIC_LINK( GenericToolbarController, ExecuteHdl_Impl, void*, p, void ) +{ + ExecuteInfo* pExecuteInfo = static_cast(p); + SolarMutexReleaser aReleaser; + 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 ( const Exception& ) + { + } + + delete pExecuteInfo; +} + +ImageOrientationController::ImageOrientationController(const Reference& rContext, + const Reference& rFrame, + const Reference& rParentWindow, + const OUString& rModuleName) + : ToolboxController(rContext, rFrame, ".uno:ImageOrientation") + , m_nRotationAngle(0_deg10) + , m_bMirrored(false) +{ + m_sModuleName = rModuleName; + m_xParentWindow = rParentWindow; + initialize({}); + if (!m_pToolbar) + VCLUnoHelper::GetWindow(getParent())->AddEventListener(LINK(this, ImageOrientationController, WindowEventListener)); +} + +void ImageOrientationController::dispose() +{ + ToolboxController::dispose(); + VclPtr pWindow = VCLUnoHelper::GetWindow(getParent()); + if (pWindow) + pWindow->RemoveEventListener(LINK(this, ImageOrientationController, WindowEventListener)); +} + +IMPL_LINK(ImageOrientationController, WindowEventListener, VclWindowEvent&, rWindowEvent, void) +{ + if (m_bDisposed || rWindowEvent.GetId() != VclEventId::ToolboxItemAdded) + return; + + ToolBox* pToolBox = static_cast(rWindowEvent.GetWindow()); + ToolBoxItemId nItemId = pToolBox->GetItemId(reinterpret_cast(rWindowEvent.GetData())); + OUString aCommand = pToolBox->GetItemCommand(nItemId); + + if (vcl::CommandInfoProvider::IsMirrored(aCommand, getModuleName())) + pToolBox->SetItemImageMirrorMode(nItemId, m_bMirrored); + if (vcl::CommandInfoProvider::IsRotated(aCommand, getModuleName())) + pToolBox->SetItemImageAngle(nItemId, m_nRotationAngle); +} + +void ImageOrientationController::statusChanged(const css::frame::FeatureStateEvent& rEvent) +{ + if (m_bDisposed) + throw DisposedException(); + + SfxImageItem aItem; + aItem.PutValue(rEvent.State, 0); + + if (m_bMirrored == aItem.IsMirrored() && m_nRotationAngle == aItem.GetRotation()) + return; + + m_bMirrored = aItem.IsMirrored(); + m_nRotationAngle = aItem.GetRotation(); + + if (m_pToolbar) + { + for (int i = 0, nCount = m_pToolbar->get_n_items(); i < nCount; ++i) + { + OUString aCommand = m_pToolbar->get_item_ident(i); + if (vcl::CommandInfoProvider::IsMirrored(aCommand, getModuleName())) + { + m_pToolbar->set_item_image_mirrored(aCommand, m_bMirrored); + auto xGraphic(vcl::CommandInfoProvider::GetXGraphicForCommand( + aCommand, m_xFrame, m_pToolbar->get_icon_size())); + m_pToolbar->set_item_image(aCommand, xGraphic); + } + } + } + else + { + ToolBox* pToolBox = static_cast(VCLUnoHelper::GetWindow(getParent())); + for (ToolBox::ImplToolItems::size_type i = 0; i < pToolBox->GetItemCount(); ++i) + { + ToolBoxItemId nItemId = pToolBox->GetItemId(i); + OUString aCommand = pToolBox->GetItemCommand(nItemId); + bool bModified = false; + if (vcl::CommandInfoProvider::IsMirrored(aCommand, getModuleName())) + { + pToolBox->SetItemImageMirrorMode(nItemId, m_bMirrored); + bModified = true; + } + if (vcl::CommandInfoProvider::IsRotated(aCommand, getModuleName())) + { + pToolBox->SetItemImageAngle(nItemId, m_nRotationAngle); + bModified = true; + } + if (bModified) + { + Image aImage(vcl::CommandInfoProvider::GetImageForCommand(aCommand, m_xFrame, pToolBox->GetImageSize())); + pToolBox->SetItemImage(nItemId, aImage); + } + } + } +} + +} // namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uielement/headermenucontroller.cxx b/framework/source/uielement/headermenucontroller.cxx new file mode 100644 index 0000000000..985fbd3826 --- /dev/null +++ b/framework/source/uielement/headermenucontroller.cxx @@ -0,0 +1,237 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +// Defines + +using namespace com::sun::star::uno; +using namespace com::sun::star::lang; +using namespace com::sun::star::frame; +using namespace com::sun::star::beans; +using namespace com::sun::star::util; +using namespace com::sun::star::style; +using namespace com::sun::star::container; + +const sal_uInt16 ALL_MENUITEM_ID = 1; + +namespace framework +{ + +// XInterface, XTypeProvider, XServiceInfo + +OUString SAL_CALL HeaderMenuController::getImplementationName() +{ + return "com.sun.star.comp.framework.HeaderMenuController"; +} + +sal_Bool SAL_CALL HeaderMenuController::supportsService( const OUString& sServiceName ) +{ + return cppu::supportsService(this, sServiceName); +} + +css::uno::Sequence< OUString > SAL_CALL HeaderMenuController::getSupportedServiceNames() +{ + return { SERVICENAME_POPUPMENUCONTROLLER }; +} + +HeaderMenuController::HeaderMenuController( const css::uno::Reference< css::uno::XComponentContext >& xContext, bool _bFooter ) : + svt::PopupMenuControllerBase( xContext ) + ,m_bFooter(_bFooter) +{ +} + +HeaderMenuController::~HeaderMenuController() +{ +} + +// private function +void HeaderMenuController::fillPopupMenu( const Reference< css::frame::XModel >& rModel, Reference< css::awt::XPopupMenu > const & rPopupMenu ) +{ + SolarMutexGuard aSolarMutexGuard; + + resetPopupMenu( rPopupMenu ); + + Reference< XStyleFamiliesSupplier > xStyleFamiliesSupplier( rModel, UNO_QUERY ); + if (!xStyleFamiliesSupplier.is()) + return; + + Reference< XNameAccess > xStyleFamilies = xStyleFamiliesSupplier->getStyleFamilies(); + + OUString aCmd( ".uno:InsertPageHeader" ); + OUString aHeaderFooterIsOnStr( "HeaderIsOn" ); + if ( m_bFooter ) + { + aCmd = ".uno:InsertPageFooter"; + aHeaderFooterIsOnStr = "FooterIsOn"; + } + static constexpr OUStringLiteral aIsPhysicalStr( u"IsPhysical" ); + static constexpr OUStringLiteral aDisplayNameStr( u"DisplayName" ); + + try + { + Reference< XNameContainer > xNameContainer; + if ( xStyleFamilies->getByName("PageStyles") >>= xNameContainer ) + { + Sequence< OUString > aSeqNames = xNameContainer->getElementNames(); + + sal_uInt16 nId = 2; + sal_uInt16 nCount = 0; + bool bAllOneState( true ); + bool bLastCheck( true ); + bool bFirstChecked( false ); + bool bFirstItemInserted( false ); + for ( sal_Int32 n = 0; n < aSeqNames.getLength(); n++ ) + { + OUString aName = aSeqNames[n]; + Reference< XPropertySet > xPropSet( xNameContainer->getByName( aName ), UNO_QUERY ); + if ( xPropSet.is() ) + { + bool bIsPhysical( false ); + if (( xPropSet->getPropertyValue( aIsPhysicalStr ) >>= bIsPhysical ) && bIsPhysical ) + { + OUString aDisplayName; + bool bHeaderIsOn( false ); + xPropSet->getPropertyValue( aDisplayNameStr ) >>= aDisplayName; + xPropSet->getPropertyValue( aHeaderFooterIsOnStr ) >>= bHeaderIsOn; + + OUStringBuffer aStrBuf( aCmd + + "?PageStyle:string=" + + aDisplayName + + "&On:bool=" ); + if ( !bHeaderIsOn ) + aStrBuf.append( "true" ); + else + aStrBuf.append( "false" ); + OUString aCommand( aStrBuf.makeStringAndClear() ); + rPopupMenu->insertItem(nId, aDisplayName, css::awt::MenuItemStyle::CHECKABLE, nCount); + if ( !bFirstItemInserted ) + { + bFirstItemInserted = true; + bFirstChecked = bHeaderIsOn; + } + + rPopupMenu->setCommand(nId, aCommand); + rPopupMenu->checkItem(nId, bHeaderIsOn); + ++nId; + + // Check if all entries have the same state + if( bAllOneState && n && bHeaderIsOn != bLastCheck ) + bAllOneState = false; + bLastCheck = bHeaderIsOn; + ++nCount; + } + } + } + + if ( bAllOneState && ( nCount > 1 )) + { + // Insert special item for all command + rPopupMenu->insertItem(ALL_MENUITEM_ID, FwkResId(STR_MENU_HEADFOOTALL), 0, 0); + + OUStringBuffer aStrBuf( aCmd + "?On:bool=" ); + + // Command depends on check state of first menu item entry + if ( !bFirstChecked ) + aStrBuf.append( "true" ); + else + aStrBuf.append( "false" ); + + rPopupMenu->setCommand(1, aStrBuf.makeStringAndClear()); + rPopupMenu->insertSeparator(1); + } + } + } + catch ( const css::container::NoSuchElementException& ) + { + } +} + +// XEventListener +void SAL_CALL HeaderMenuController::disposing( const EventObject& ) +{ + Reference< css::awt::XMenuListener > xHolder(this); + + std::unique_lock aLock( m_aMutex ); + m_xFrame.clear(); + m_xDispatch.clear(); + + if ( m_xPopupMenu.is() ) + m_xPopupMenu->removeMenuListener( Reference< css::awt::XMenuListener >(this) ); + m_xPopupMenu.clear(); +} + +// XStatusListener +void SAL_CALL HeaderMenuController::statusChanged( const FeatureStateEvent& Event ) +{ + Reference< css::frame::XModel > xModel; + + if ( Event.State >>= xModel ) + { + std::unique_lock aLock( m_aMutex ); + m_xModel = xModel; + if ( m_xPopupMenu.is() ) + fillPopupMenu( xModel, m_xPopupMenu ); + } +} + +// XMenuListener +void SAL_CALL HeaderMenuController::updatePopupMenu() +{ + std::unique_lock aLock( m_aMutex ); + + throwIfDisposed(aLock); + + Reference< css::frame::XModel > xModel( m_xModel ); + aLock.unlock(); + + if ( !xModel.is() ) + svt::PopupMenuControllerBase::updatePopupMenu(); + + aLock.lock(); + if ( m_xPopupMenu.is() && m_xModel.is() ) + fillPopupMenu( m_xModel, m_xPopupMenu ); +} + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +framework_HeaderMenuController_get_implementation( + css::uno::XComponentContext* context, css::uno::Sequence const& ) +{ + return cppu::acquire(new framework::HeaderMenuController(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uielement/imagebuttontoolbarcontroller.cxx b/framework/source/uielement/imagebuttontoolbarcontroller.cxx new file mode 100644 index 0000000000..a91b0123a5 --- /dev/null +++ b/framework/source/uielement/imagebuttontoolbarcontroller.cxx @@ -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 . + */ + +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::awt; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::frame; +using namespace ::com::sun::star::util; + +const ::Size aImageSizeSmall( 16, 16 ); +const ::Size aImageSizeBig( 26, 26 ); + +namespace framework +{ + +static void SubstituteVariables( OUString& aURL ) +{ + aURL = comphelper::getExpandedUri( + comphelper::getProcessComponentContext(), aURL); +} + +ImageButtonToolbarController::ImageButtonToolbarController( + const Reference< XComponentContext >& rxContext, + const Reference< XFrame >& rFrame, + ToolBox* pToolbar, + ToolBoxItemId nID, + const OUString& aCommand ) : + ComplexToolbarController( rxContext, rFrame, pToolbar, nID, aCommand ) +{ + bool bBigImages( SvtMiscOptions::AreCurrentSymbolsLarge() ); + + Image aImage(AddonsOptions().GetImageFromURL(aCommand, bBigImages, true)); + + // Height will be controlled by scaling according to button height + m_xToolbar->SetItemImage( m_nID, aImage ); +} + +ImageButtonToolbarController::~ImageButtonToolbarController() +{ +} + +void SAL_CALL ImageButtonToolbarController::dispose() +{ + SolarMutexGuard aSolarMutexGuard; + ComplexToolbarController::dispose(); +} + +void ImageButtonToolbarController::executeControlCommand( const css::frame::ControlCommand& rControlCommand ) +{ + SolarMutexGuard aSolarMutexGuard; + // i73486 to be downward compatible use old and "wrong" also! + if( rControlCommand.Command != "SetImag" && + rControlCommand.Command != "SetImage" ) + return; + + for ( const NamedValue& rArg : rControlCommand.Arguments ) + { + if ( rArg.Name == "URL" ) + { + OUString aURL; + rArg.Value >>= aURL; + + SubstituteVariables( aURL ); + + Image aImage; + if ( ReadImageFromURL( SvtMiscOptions::AreCurrentSymbolsLarge(), + aURL, + aImage )) + { + m_xToolbar->SetItemImage( m_nID, aImage ); + + // send notification + uno::Sequence< beans::NamedValue > aInfo { { "URL", css::uno::Any(aURL) } }; + addNotifyInfo( "ImageChanged", + getDispatchFromCommand( m_aCommandURL ), + aInfo ); + break; + } + } + } +} + +bool ImageButtonToolbarController::ReadImageFromURL( bool bBigImage, const OUString& aImageURL, Image& aImage ) +{ + std::unique_ptr pStream(utl::UcbStreamHelper::CreateStream( aImageURL, StreamMode::STD_READ )); + if ( !pStream || ( pStream->GetErrorCode() != ERRCODE_NONE )) + return false; + + // Use graphic class to also support more graphic formats (bmp,png,...) + Graphic aGraphic; + + GraphicFilter& rGF = GraphicFilter::GetGraphicFilter(); + rGF.ImportGraphic( aGraphic, u"", *pStream ); + + BitmapEx aBitmapEx = aGraphic.GetBitmapEx(); + + const ::Size aSize = bBigImage ? aImageSizeBig : aImageSizeSmall; // Sizes used for toolbar images + + ::Size aBmpSize = aBitmapEx.GetSizePixel(); + if ( !aBmpSize.IsEmpty() ) + { + ::Size aNoScaleSize( aBmpSize.Width(), aSize.Height() ); + if ( aBmpSize != aNoScaleSize ) + aBitmapEx.Scale( aNoScaleSize, BmpScaleFlag::BestQuality ); + aImage = Image( aBitmapEx ); + return true; + } + + return false; +} + +} // namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uielement/langselectionmenucontroller.cxx b/framework/source/uielement/langselectionmenucontroller.cxx new file mode 100644 index 0000000000..9a7ec12b5c --- /dev/null +++ b/framework/source/uielement/langselectionmenucontroller.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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include + +// Defines + +using namespace ::com::sun::star; +using namespace com::sun::star::uno; +using namespace com::sun::star::lang; +using namespace com::sun::star::frame; +using namespace com::sun::star::beans; +using namespace com::sun::star::util; + +namespace framework +{ + +// XInterface, XTypeProvider, XServiceInfo + +OUString SAL_CALL LanguageSelectionMenuController::getImplementationName() +{ + return "com.sun.star.comp.framework.LanguageSelectionMenuController"; +} + +sal_Bool SAL_CALL LanguageSelectionMenuController::supportsService( const OUString& sServiceName ) +{ + return cppu::supportsService(this, sServiceName); +} + +css::uno::Sequence< OUString > SAL_CALL LanguageSelectionMenuController::getSupportedServiceNames() +{ + return { SERVICENAME_POPUPMENUCONTROLLER }; +} + + +LanguageSelectionMenuController::LanguageSelectionMenuController( const css::uno::Reference< css::uno::XComponentContext >& xContext ) + : svt::PopupMenuControllerBase(xContext) + , m_bShowMenu(true) + , m_nScriptType(SvtScriptType::LATIN | SvtScriptType::ASIAN | SvtScriptType::COMPLEX) + , m_aLangGuessHelper(xContext) +{ +} + +LanguageSelectionMenuController::~LanguageSelectionMenuController() +{ +} + +// XEventListener +void SAL_CALL LanguageSelectionMenuController::disposing( const EventObject& ) +{ + Reference< css::awt::XMenuListener > xHolder(this); + + std::unique_lock aLock( m_aMutex ); + m_xFrame.clear(); + m_xDispatch.clear(); + m_xLanguageDispatch.clear(); + + if ( m_xPopupMenu.is() ) + m_xPopupMenu->removeMenuListener( Reference< css::awt::XMenuListener >(this) ); + m_xPopupMenu.clear(); +} + +// XStatusListener +void SAL_CALL LanguageSelectionMenuController::statusChanged( const FeatureStateEvent& Event ) +{ + SolarMutexGuard aSolarMutexGuard; + + if (m_bDisposed) + return; + + m_bShowMenu = true; + m_nScriptType = SvtScriptType::LATIN | SvtScriptType::ASIAN | SvtScriptType::COMPLEX; //set the default value + + Sequence< OUString > aSeq; + + if ( Event.State >>= aSeq ) + { + if ( aSeq.getLength() == 4 ) + { + // Retrieve all other values from the sequence and + // store it members! + m_aCurLang = aSeq[0]; + m_nScriptType = static_cast< SvtScriptType >(aSeq[1].toInt32()); + m_aKeyboardLang = aSeq[2]; + m_aGuessedTextLang = aSeq[3]; + } + } + else if ( !Event.State.hasValue() ) + { + m_bShowMenu = false; // no language -> no sub-menu entries -> disable menu + } +} + +// XPopupMenuController +void LanguageSelectionMenuController::impl_setPopupMenu() +{ + Reference< XDispatchProvider > xDispatchProvider( m_xFrame, UNO_QUERY ); + + css::util::URL aTargetURL; + + // Register for language updates + aTargetURL.Complete = m_aLangStatusCommandURL; + m_xURLTransformer->parseStrict( aTargetURL ); + m_xLanguageDispatch = xDispatchProvider->queryDispatch( aTargetURL, OUString(), 0 ); + + // Register for setting languages and opening language dialog + aTargetURL.Complete = m_aMenuCommandURL_Lang; + m_xURLTransformer->parseStrict( aTargetURL ); + m_xMenuDispatch_Lang = xDispatchProvider->queryDispatch( aTargetURL, OUString(), 0 ); + + // Register for opening character dialog + aTargetURL.Complete = m_aMenuCommandURL_Font; + m_xURLTransformer->parseStrict( aTargetURL ); + m_xMenuDispatch_Font = xDispatchProvider->queryDispatch( aTargetURL, OUString(), 0 ); + + // Register for opening character dialog with preselected paragraph + aTargetURL.Complete = m_aMenuCommandURL_CharDlgForParagraph; + m_xURLTransformer->parseStrict( aTargetURL ); + m_xMenuDispatch_CharDlgForParagraph = xDispatchProvider->queryDispatch( aTargetURL, OUString(), 0 ); +} + +void LanguageSelectionMenuController::fillPopupMenu( Reference< css::awt::XPopupMenu > const & rPopupMenu , const Mode eMode ) +{ + SolarMutexGuard aSolarMutexGuard; + + resetPopupMenu( rPopupMenu ); + if (!m_bShowMenu) + return; + + OUString aCmd_Dialog; + OUString aCmd_Language; + if( eMode == MODE_SetLanguageSelectionMenu ) + { + aCmd_Dialog += ".uno:FontDialog?Page:string=font"; + aCmd_Language += ".uno:LanguageStatus?Language:string=Current_"; + } + else if ( eMode == MODE_SetLanguageParagraphMenu ) + { + aCmd_Dialog += ".uno:FontDialogForParagraph"; + aCmd_Language += ".uno:LanguageStatus?Language:string=Paragraph_"; + } + else if ( eMode == MODE_SetLanguageAllTextMenu ) + { + aCmd_Dialog += ".uno:LanguageStatus?Language:string=*"; + aCmd_Language += ".uno:LanguageStatus?Language:string=Default_"; + } + + // get languages to be displayed in the menu + std::set< OUString > aLangItems; + FillLangItems( aLangItems, m_xFrame, m_aLangGuessHelper, + m_nScriptType, m_aCurLang, m_aKeyboardLang, m_aGuessedTextLang ); + + // now add menu entries + // the different menus purpose will be handled by the different string + // for aCmd_Dialog and aCmd_Language + sal_Int16 nItemId = 0; // in this control the item id is not important for executing the command + static constexpr OUStringLiteral sAsterisk(u"*"); // multiple languages in current selection + const OUString sNone( SvtLanguageTable::GetLanguageString( LANGUAGE_NONE )); + for (auto const& langItem : aLangItems) + { + if (langItem != sNone && + langItem != sAsterisk && + !langItem.isEmpty()) // 'no language found' from language guessing + { + ++nItemId; + rPopupMenu->insertItem(nItemId, langItem, css::awt::MenuItemStyle::CHECKABLE, nItemId - 1); + OUString aCmd = aCmd_Language + langItem; + rPopupMenu->setCommand(nItemId, aCmd); + bool bChecked = langItem == m_aCurLang && eMode == MODE_SetLanguageSelectionMenu; + //make a sign for the current language + rPopupMenu->checkItem(nItemId, bChecked); + } + } + + // entry for LANGUAGE_NONE + ++nItemId; + rPopupMenu->insertItem(nItemId, FwkResId(STR_LANGSTATUS_NONE), 0, nItemId - 1); + OUString aCmd = aCmd_Language + "LANGUAGE_NONE"; + rPopupMenu->setCommand(nItemId, aCmd); + + // entry for 'Reset to default language' + ++nItemId; + rPopupMenu->insertItem(nItemId, FwkResId(STR_RESET_TO_DEFAULT_LANGUAGE), 0, nItemId - 1); + aCmd = aCmd_Language + "RESET_LANGUAGES"; + rPopupMenu->setCommand(nItemId, aCmd); + + // entry for opening the Format/Character dialog + ++nItemId; + rPopupMenu->insertItem(nItemId, FwkResId(STR_LANGSTATUS_MORE), 0, nItemId - 1); + rPopupMenu->setCommand(nItemId, aCmd_Dialog); +} + +void SAL_CALL LanguageSelectionMenuController::updatePopupMenu() +{ + svt::PopupMenuControllerBase::updatePopupMenu(); + + // Force status update to get information about the current languages + std::unique_lock aLock( m_aMutex ); + Reference< XDispatch > xDispatch( m_xLanguageDispatch ); + css::util::URL aTargetURL; + aTargetURL.Complete = m_aLangStatusCommandURL; + m_xURLTransformer->parseStrict( aTargetURL ); + aLock.unlock(); + + if ( xDispatch.is() ) + { + xDispatch->addStatusListener( static_cast< XStatusListener* >(this), aTargetURL ); + xDispatch->removeStatusListener( static_cast< XStatusListener* >(this), aTargetURL ); + } + + // TODO: Fill menu with the information retrieved by the status update + + if ( m_aCommandURL == ".uno:SetLanguageSelectionMenu" ) + { + fillPopupMenu(m_xPopupMenu, MODE_SetLanguageSelectionMenu ); + } + else if ( m_aCommandURL == ".uno:SetLanguageParagraphMenu" ) + { + fillPopupMenu(m_xPopupMenu, MODE_SetLanguageParagraphMenu ); + } + else if ( m_aCommandURL == ".uno:SetLanguageAllTextMenu" ) + { + fillPopupMenu(m_xPopupMenu, MODE_SetLanguageAllTextMenu ); + } +} + +// XInitialization +void LanguageSelectionMenuController::initializeImpl( std::unique_lock& rGuard, const Sequence< Any >& aArguments ) +{ + bool bInitialized( m_bInitialized ); + if ( !bInitialized ) + { + svt::PopupMenuControllerBase::initializeImpl(rGuard, aArguments); + + if ( m_bInitialized ) + { + m_aLangStatusCommandURL = ".uno:LanguageStatus"; + m_aMenuCommandURL_Lang = m_aLangStatusCommandURL; + m_aMenuCommandURL_Font = ".uno:FontDialog"; + m_aMenuCommandURL_CharDlgForParagraph = ".uno:FontDialogForParagraph"; + } + } +} + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +framework_LanguageSelectionMenuController_get_implementation( + css::uno::XComponentContext* context, css::uno::Sequence const& ) +{ + return cppu::acquire(new framework::LanguageSelectionMenuController(context)); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uielement/langselectionstatusbarcontroller.cxx b/framework/source/uielement/langselectionstatusbarcontroller.cxx new file mode 100644 index 0000000000..f913688526 --- /dev/null +++ b/framework/source/uielement/langselectionstatusbarcontroller.cxx @@ -0,0 +1,362 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include + +#include + +#include +#include + +using namespace ::cppu; +using namespace ::com::sun::star; +using namespace css::uno; +using namespace css::lang; +using namespace css::frame; +using namespace css::i18n; +using namespace css::document; +using namespace framework; + +namespace { + +class LangSelectionStatusbarController: + public svt::StatusbarController +{ +public: + explicit LangSelectionStatusbarController( const css::uno::Reference< css::uno::XComponentContext >& xContext ); + LangSelectionStatusbarController(const LangSelectionStatusbarController&) = delete; + LangSelectionStatusbarController& operator=(const LangSelectionStatusbarController&) = delete; + + // XInitialization + virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override; + + // XStatusListener + virtual void SAL_CALL statusChanged( const css::frame::FeatureStateEvent& Event ) override; + + // XStatusbarController + virtual void SAL_CALL command( const css::awt::Point& aPos, + ::sal_Int32 nCommand, + sal_Bool bMouseEvent, + const css::uno::Any& aData ) override; + virtual void SAL_CALL click( const css::awt::Point& aPos ) override; + +private: + virtual ~LangSelectionStatusbarController() override {} + + bool m_bShowMenu; // if the menu is to be displayed or not (depending on the selected object/text) + SvtScriptType m_nScriptType; // the flags for the different script types available in the selection, LATIN = 0x0001, ASIAN = 0x0002, COMPLEX = 0x0004 + OUString m_aCurLang; // the language of the current selection, "*" if there are more than one languages + OUString m_aKeyboardLang; // the keyboard language + OUString m_aGuessedTextLang; // the 'guessed' language for the selection, "" if none could be guessed + LanguageGuessingHelper m_aLangGuessHelper; + + /// @throws css::uno::RuntimeException + void LangMenu( const css::awt::Point& aPos ); +}; + +LangSelectionStatusbarController::LangSelectionStatusbarController( const uno::Reference< uno::XComponentContext >& xContext ) : + svt::StatusbarController( xContext, uno::Reference< frame::XFrame >(), OUString(), 0 ), + m_bShowMenu( true ), + m_nScriptType( SvtScriptType::LATIN | SvtScriptType::ASIAN | SvtScriptType::COMPLEX ), + m_aLangGuessHelper( xContext ) +{ +} + +void SAL_CALL LangSelectionStatusbarController::initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) +{ + SolarMutexGuard aSolarMutexGuard; + + svt::StatusbarController::initialize( aArguments ); + + if ( m_xStatusbarItem.is() ) + { + m_xStatusbarItem->setText( FwkResId(STR_LANGSTATUS_MULTIPLE_LANGUAGES) ); + m_xStatusbarItem->setQuickHelpText(FwkResId(STR_LANGSTATUS_HINT)); + } +} + +void LangSelectionStatusbarController::LangMenu( + const css::awt::Point& aPos ) +{ + if (!m_bShowMenu) + return; + + const Reference xService(m_xFrame->getController()->getModel(), UNO_QUERY); + bool bCalc = xService.is() && xService->supportsService("com.sun.star.sheet.SpreadsheetDocument"); + bool bWriter = xService.is() && xService->supportsService("com.sun.star.text.GenericTextDocument"); + //add context menu + Reference< awt::XPopupMenu > xPopupMenu( awt::PopupMenu::create( m_xContext ) ); + //sub menu that contains all items except the last two items: Separator + Set Language for Paragraph + Reference< awt::XPopupMenu > subPopupMenu( awt::PopupMenu::create( m_xContext ) ); + + // get languages to be displayed in the menu + std::set< OUString > aLangItems; + FillLangItems( aLangItems, m_xFrame, m_aLangGuessHelper, + m_nScriptType, m_aCurLang, m_aKeyboardLang, m_aGuessedTextLang ); + + // add first few entries to main menu + sal_Int16 nItemId = static_cast< sal_Int16 >(MID_LANG_SEL_1); + static constexpr OUString sAsterisk(u"*"_ustr); // multiple languages in current selection + const OUString sNone( SvtLanguageTable::GetLanguageString( LANGUAGE_NONE )); + std::map< sal_Int16, OUString > aLangMap; + for (auto const& langItem : aLangItems) + { + if ( langItem != sNone && + langItem != sAsterisk && + !langItem.isEmpty()) // 'no language found' from language guessing + { + SAL_WARN_IF( MID_LANG_SEL_1 > nItemId || nItemId > MID_LANG_SEL_9, + "fwk.uielement", "nItemId outside of expected range!" ); + xPopupMenu->insertItem( nItemId, langItem, 0, nItemId ); + if ( langItem == m_aCurLang ) + { + //make a sign for the current language + xPopupMenu->checkItem( nItemId, true ); + } + aLangMap[ nItemId ] = langItem; + ++nItemId; + } + } + + if (bWriter) + { + xPopupMenu->insertItem( MID_LANG_SEL_NONE, FwkResId(STR_LANGSTATUS_NONE), 0, MID_LANG_SEL_NONE ); + if ( sNone == m_aCurLang ) + xPopupMenu->checkItem( MID_LANG_SEL_NONE, true ); + xPopupMenu->insertItem( MID_LANG_SEL_RESET, FwkResId(STR_RESET_TO_DEFAULT_LANGUAGE), 0, MID_LANG_SEL_RESET ); + xPopupMenu->insertItem( MID_LANG_SEL_MORE, FwkResId(STR_LANGSTATUS_MORE), 0, MID_LANG_SEL_MORE ); + + // add entries to submenu ('set language for paragraph') + nItemId = static_cast< sal_Int16 >(MID_LANG_PARA_1); + for (auto const& langItem : aLangItems) + { + if( langItem != sNone && + langItem != sAsterisk && + !langItem.isEmpty()) // 'no language found' from language guessing + { + SAL_WARN_IF( MID_LANG_PARA_1 > nItemId || nItemId > MID_LANG_PARA_9, + "fwk.uielement", "nItemId outside of expected range!" ); + subPopupMenu->insertItem( nItemId, langItem, 0, nItemId ); + aLangMap[nItemId] = langItem; + ++nItemId; + } + } + subPopupMenu->insertItem( MID_LANG_PARA_NONE, FwkResId(STR_LANGSTATUS_NONE), 0, MID_LANG_PARA_NONE ); + subPopupMenu->insertItem( MID_LANG_PARA_RESET, FwkResId(STR_RESET_TO_DEFAULT_LANGUAGE), 0, MID_LANG_PARA_RESET ); + subPopupMenu->insertItem( MID_LANG_PARA_MORE, FwkResId(STR_LANGSTATUS_MORE), 0, MID_LANG_PARA_MORE ); + + // add last two entries to main menu + xPopupMenu->insertSeparator( MID_LANG_PARA_SEPARATOR ); + xPopupMenu->insertItem( MID_LANG_PARA_STRING, FwkResId(STR_SET_LANGUAGE_FOR_PARAGRAPH), 0, MID_LANG_PARA_STRING ); + xPopupMenu->setPopupMenu( MID_LANG_PARA_STRING, subPopupMenu ); + } + else + { + xPopupMenu->insertItem( MID_LANG_DEF_NONE, FwkResId(STR_LANGSTATUS_NONE), 0, MID_LANG_DEF_NONE ); + if ( sNone == m_aCurLang ) + xPopupMenu->checkItem( MID_LANG_DEF_NONE, true ); + xPopupMenu->insertItem( MID_LANG_DEF_RESET, FwkResId(STR_RESET_TO_DEFAULT_LANGUAGE), 0, MID_LANG_DEF_RESET ); + xPopupMenu->insertItem( MID_LANG_DEF_MORE, FwkResId(STR_LANGSTATUS_MORE), 0, MID_LANG_DEF_MORE ); + } + + // now display the popup menu and execute every command ... + + Reference< awt::XWindowPeer > xParent( m_xParentWindow, UNO_QUERY ); + css::awt::Rectangle aRect( aPos.X, aPos.Y, 0, 0 ); + sal_Int16 nId = xPopupMenu->execute( xParent, aRect, css::awt::PopupMenuDirection::EXECUTE_UP+16 ); + //click "More..." + if ( !(nId && m_xFrame.is()) ) + return; + + OUStringBuffer aBuff; + //set selected language as current language for selection + const OUString aSelectedLang = aLangMap[nId]; + + if (MID_LANG_SEL_1 <= nId && nId <= MID_LANG_SEL_9) + { + if (bWriter) + aBuff.append( ".uno:LanguageStatus?Language:string=Current_" ); + else + aBuff.append( ".uno:LanguageStatus?Language:string=Default_" ); + + aBuff.append( aSelectedLang ); + } + else if (nId == MID_LANG_SEL_NONE) + { + //set None as current language for selection + aBuff.append( ".uno:LanguageStatus?Language:string=Current_LANGUAGE_NONE" ); + } + else if (nId == MID_LANG_SEL_RESET) + { + // reset language attributes for selection + aBuff.append( ".uno:LanguageStatus?Language:string=Current_RESET_LANGUAGES" ); + } + else if (nId == MID_LANG_SEL_MORE) + { + //open the dialog "format/character" for current selection + aBuff.append( ".uno:FontDialog?Page:string=font" ); + } + else if (nId == MID_LANG_DEF_NONE) + { + aBuff.append( ".uno:LanguageStatus?Language:string=Default_LANGUAGE_NONE" ); + } + else if (nId == MID_LANG_DEF_RESET) + { + aBuff.append( ".uno:LanguageStatus?Language:string=Default_RESET_LANGUAGES" ); + } + else if (nId == MID_LANG_DEF_MORE) + { + if (bCalc) + aBuff.append( ".uno:FormatCellDialog" ); + else + aBuff.append( ".uno:LanguageStatus?Language:string=*" ); + } + else if (MID_LANG_PARA_1 <= nId && nId <= MID_LANG_PARA_9) + { + aBuff.append( ".uno:LanguageStatus?Language:string=Paragraph_" + aSelectedLang ); + } + else if (nId == MID_LANG_PARA_NONE) + { + //set None as language for current paragraph + aBuff.append( ".uno:LanguageStatus?Language:string=Paragraph_LANGUAGE_NONE" ); + } + else if (nId == MID_LANG_PARA_RESET) + { + // reset language attributes for paragraph + aBuff.append( ".uno:LanguageStatus?Language:string=Paragraph_RESET_LANGUAGES" ); + } + else if (nId == MID_LANG_PARA_MORE) + { + //open the dialog "format/character" for current paragraph + aBuff.append( ".uno:FontDialogForParagraph" ); + } + + const Sequence< beans::PropertyValue > aDummyArgs; + execute( aBuff.makeStringAndClear(), aDummyArgs ); +} + +void SAL_CALL LangSelectionStatusbarController::command( + const css::awt::Point& aPos, + ::sal_Int32 nCommand, + sal_Bool /*bMouseEvent*/, + const css::uno::Any& /*aData*/ ) +{ + if ( nCommand & ::awt::Command::CONTEXTMENU ) + { + LangMenu( aPos ); + } +} + +void SAL_CALL LangSelectionStatusbarController::click( + const css::awt::Point& aPos ) +{ + LangMenu( aPos ); +} + +// XStatusListener +void SAL_CALL LangSelectionStatusbarController::statusChanged( const FeatureStateEvent& Event ) +{ + // This function will be called when observed data changes, + // for example the selection or keyboard language. + // - It displays the language in use in the status bar + // - and it stores the relevant data for creating the menu + // at some later point in the member variables + // m_nScriptType, m_aCurLang, m_aKeyboardLang, m_aGuessedText + + SolarMutexGuard aSolarMutexGuard; + + if ( m_bDisposed ) + return; + + m_bShowMenu = true; + m_nScriptType = SvtScriptType::LATIN | SvtScriptType::ASIAN | SvtScriptType::COMPLEX; //set the default value + + if ( !m_xStatusbarItem.is() ) + return; + + OUString aStrValue; + Sequence< OUString > aSeq; + + if ( Event.State >>= aStrValue ) + { + m_xStatusbarItem->setText( aStrValue ); + m_xStatusbarItem->setQuickHelpText(FwkResId(STR_LANGSTATUS_HINT)); + m_aCurLang = aStrValue; + } + else if ( Event.State >>= aSeq ) + { + if ( aSeq.getLength() == 4 ) + { + OUString aStatusText = aSeq[0]; + if (aStatusText == "*") + { + aStatusText = FwkResId(STR_LANGSTATUS_MULTIPLE_LANGUAGES); + } + m_xStatusbarItem->setText( aStatusText ); + m_xStatusbarItem->setQuickHelpText(FwkResId(STR_LANGSTATUS_HINT)); + + // Retrieve all other values from the sequence and + // store it members! + m_aCurLang = aSeq[0]; + m_nScriptType = static_cast< SvtScriptType >( aSeq[1].toInt32() ); + m_aKeyboardLang = aSeq[2]; + m_aGuessedTextLang = aSeq[3]; + } + } + else if ( !Event.State.hasValue() ) + { + m_xStatusbarItem->setText( OUString() ); + m_xStatusbarItem->setQuickHelpText(u""_ustr); + m_bShowMenu = false; // no language -> no menu + } +} + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_framework_LangSelectionStatusbarController_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence const &) +{ + return cppu::acquire(new LangSelectionStatusbarController(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uielement/macrosmenucontroller.cxx b/framework/source/uielement/macrosmenucontroller.cxx new file mode 100644 index 0000000000..0cc8fba505 --- /dev/null +++ b/framework/source/uielement/macrosmenucontroller.cxx @@ -0,0 +1,171 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace com::sun::star::uno; +using namespace com::sun::star::lang; +using namespace com::sun::star::frame; +using namespace com::sun::star::beans; +using namespace com::sun::star::util; +using namespace com::sun::star::style; +using namespace com::sun::star::container; + +namespace framework +{ + +// XInterface, XTypeProvider, XServiceInfo + +OUString SAL_CALL MacrosMenuController::getImplementationName() +{ + return "com.sun.star.comp.framework.MacrosMenuController"; +} + +sal_Bool SAL_CALL MacrosMenuController::supportsService( const OUString& sServiceName ) +{ + return cppu::supportsService(this, sServiceName); +} + +css::uno::Sequence< OUString > SAL_CALL MacrosMenuController::getSupportedServiceNames() +{ + return { SERVICENAME_POPUPMENUCONTROLLER }; +} + +MacrosMenuController::MacrosMenuController( const css::uno::Reference< css::uno::XComponentContext >& xContext ) : + svt::PopupMenuControllerBase( xContext ), + m_xContext( xContext) +{ +} + +MacrosMenuController::~MacrosMenuController() +{ +} + +// private function +void MacrosMenuController::fillPopupMenu( Reference< css::awt::XPopupMenu > const & rPopupMenu ) +{ + bool bMacrosDisabled = officecfg::Office::Common::Security::Scripting::DisableMacrosExecution::get(); + if (bMacrosDisabled) + return; + + SolarMutexGuard aSolarMutexGuard; + + resetPopupMenu(rPopupMenu); + assert(rPopupMenu->getItemCount() == 0); + + // insert basic + OUString aCommand(".uno:MacroDialog"); + auto aProperties = vcl::CommandInfoProvider::GetCommandProperties(aCommand, m_aModuleName); + OUString aDisplayName = vcl::CommandInfoProvider::GetMenuLabelForCommand(aProperties); + rPopupMenu->insertItem(2, aDisplayName, 0, 0); + rPopupMenu->setCommand(2, aCommand); + + // insert providers but not basic or java + addScriptItems(rPopupMenu, 4); +} + +// XEventListener +void SAL_CALL MacrosMenuController::disposing( const EventObject& ) +{ + Reference< css::awt::XMenuListener > xHolder(this); + + std::unique_lock aLock( m_aMutex ); + m_xFrame.clear(); + m_xDispatch.clear(); + m_xContext.clear(); + + if ( m_xPopupMenu.is() ) + { + m_xPopupMenu->removeMenuListener( Reference< css::awt::XMenuListener >(this) ); + } + m_xPopupMenu.clear(); +} + +// XStatusListener +void SAL_CALL MacrosMenuController::statusChanged( const FeatureStateEvent& ) +{ + std::unique_lock aLock( m_aMutex ); + if ( m_xPopupMenu.is() ) + { + fillPopupMenu( m_xPopupMenu ); + } +} + +void MacrosMenuController::addScriptItems(const Reference& rPopupMenu, sal_uInt16 startItemId) +{ + static constexpr OUStringLiteral aCmdBase(u".uno:ScriptOrganizer?ScriptOrganizer.Language:string="); + static constexpr OUStringLiteral ellipsis( u"..." ); + static constexpr OUString providerKey(u"com.sun.star.script.provider.ScriptProviderFor"_ustr); + sal_uInt16 itemId = startItemId; + Reference< XContentEnumerationAccess > xEnumAccess( m_xContext->getServiceManager(), UNO_QUERY_THROW ); + Reference< XEnumeration > xEnum = xEnumAccess->createContentEnumeration ( "com.sun.star.script.provider.LanguageScriptProvider" ); + + sal_Int16 nPos = rPopupMenu->getItemCount(); + + while ( xEnum->hasMoreElements() ) + { + Reference< XServiceInfo > xServiceInfo; + if ( !( xEnum->nextElement() >>= xServiceInfo ) ) + { + break; + } + const Sequence< OUString > serviceNames = xServiceInfo->getSupportedServiceNames(); + + for ( OUString const & serviceName : serviceNames ) + { + if ( serviceName.startsWith( providerKey ) ) + { + OUString aCommand = aCmdBase; + OUString aDisplayName = serviceName.copy( providerKey.getLength() ); + if( aDisplayName == "Java" || aDisplayName == "Basic" ) + { + // no entries for Java & Basic added elsewhere + break; + } + aCommand += aDisplayName; + aDisplayName += ellipsis; + rPopupMenu->insertItem(itemId, aDisplayName, 0, nPos++); + rPopupMenu->setCommand(itemId, aCommand); + itemId++; + break; + } + } + } +} + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +framework_MacrosMenuController_get_implementation( + css::uno::XComponentContext* context, css::uno::Sequence const& ) +{ + return cppu::acquire(new framework::MacrosMenuController(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uielement/menubarmanager.cxx b/framework/source/uielement/menubarmanager.cxx new file mode 100644 index 0000000000..2abd584348 --- /dev/null +++ b/framework/source/uielement/menubarmanager.cxx @@ -0,0 +1,1592 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace ::cppu; +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::frame; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::ui; + +const sal_uInt16 ADDONMENU_MERGE_ITEMID_START = 1500; +const sal_uInt16 ITEMID_ADDONLIST = 6678; // used to be a SID in sfx2, now just a unique id... + +namespace framework +{ + +constexpr OUString aCmdHelpIndex = u".uno:HelpIndex"_ustr; +constexpr OUStringLiteral aCmdToolsMenu = u".uno:ToolsMenu"; +constexpr OUStringLiteral aCmdHelpMenu = u".uno:HelpMenu"; +constexpr OUStringLiteral aSpecialWindowCommand = u".uno:WindowList"; + +MenuBarManager::MenuBarManager( + const Reference< XComponentContext >& rxContext, + const Reference< XFrame >& rFrame, + const Reference< XURLTransformer >& _xURLTransformer, + const Reference< XDispatchProvider >& rDispatchProvider, + const OUString& rModuleIdentifier, + Menu* pMenu, bool bDelete, bool bHasMenuBar ): + m_bRetrieveImages( false ) + , m_bAcceleratorCfg( false ) + , m_bHasMenuBar( bHasMenuBar ) + , m_xContext(rxContext) + , m_xURLTransformer(_xURLTransformer) + , m_sIconTheme( SvtMiscOptions::GetIconTheme() ) + , m_aAsyncSettingsTimer( "framework::MenuBarManager::Deactivate m_aAsyncSettingsTimer" ) +{ + m_xPopupMenuControllerFactory = frame::thePopupMenuControllerFactory::get(m_xContext); + FillMenuManager( pMenu, rFrame, rDispatchProvider, rModuleIdentifier, bDelete ); +} + +Any SAL_CALL MenuBarManager::getMenuHandle( const Sequence< sal_Int8 >& /*ProcessId*/, sal_Int16 SystemType ) +{ + SolarMutexGuard aSolarGuard; + + if ( m_bDisposed ) + throw css::lang::DisposedException(); + + Any a; + + if ( m_pVCLMenu ) + { + SystemMenuData aSystemMenuData; + + m_pVCLMenu->GetSystemMenuData( &aSystemMenuData ); +#ifdef _WIN32 + if( SystemType == SystemDependent::SYSTEM_WIN32 ) + { + a <<= sal_Int64( + reinterpret_cast(aSystemMenuData.hMenu)); + } +#else + (void) SystemType; +#endif + } + + return a; +} + +MenuBarManager::~MenuBarManager() +{ + // stop asynchronous settings timer + m_xDeferredItemContainer.clear(); + m_aAsyncSettingsTimer.Stop(); + + SAL_WARN_IF( OWeakObject::m_refCount != 0, "fwk.uielement", "Who wants to delete an object with refcount > 0!" ); +} + +// XComponent +void MenuBarManager::disposing(std::unique_lock& ) +{ + Reference< XComponent > xThis( this ); + + SolarMutexGuard g; + + // stop asynchronous settings timer and + // release deferred item container reference + m_aAsyncSettingsTimer.Stop(); + m_xDeferredItemContainer.clear(); + RemoveListener(); + + m_aMenuItemHandlerVector.clear(); + + if ( m_bDeleteMenu ) + { + m_pVCLMenu.disposeAndClear(); + } + + if ( m_xDocImageManager.is() ) + { + try + { + m_xDocImageManager->removeConfigurationListener( + Reference< XUIConfigurationListener >(this) ); + } + catch ( const Exception& ) + { + } + } + if ( m_xModuleImageManager.is() ) + { + try + { + m_xModuleImageManager->removeConfigurationListener( + Reference< XUIConfigurationListener >(this) ); + } + catch ( const Exception& ) + { + } + } + m_xDocImageManager.clear(); + m_xModuleImageManager.clear(); + m_xGlobalAcceleratorManager.clear(); + m_xModuleAcceleratorManager.clear(); + m_xDocAcceleratorManager.clear(); + m_xPopupMenuControllerFactory.clear(); + m_xContext.clear(); +} + +void SAL_CALL MenuBarManager::elementInserted( const css::ui::ConfigurationEvent& Event ) +{ + SolarMutexGuard g; + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + if ( m_bDisposed ) + return; + + sal_Int16 nImageType = sal_Int16(); + if (( Event.aInfo >>= nImageType ) && nImageType == 0 ) + RequestImages(); +} + +void SAL_CALL MenuBarManager::elementRemoved( const css::ui::ConfigurationEvent& Event ) +{ + elementInserted(Event); +} + +void SAL_CALL MenuBarManager::elementReplaced( const css::ui::ConfigurationEvent& Event ) +{ + elementInserted(Event); +} + +// XFrameActionListener +void SAL_CALL MenuBarManager::frameAction( const FrameActionEvent& Action ) +{ + SolarMutexGuard g; + + if ( m_bDisposed ) + throw css::lang::DisposedException(); + + if ( Action.Action != FrameAction_CONTEXT_CHANGED ) + return; + + for (auto const& menuItemHandler : m_aMenuItemHandlerVector) + { + // Clear dispatch reference as we will requery it later + if ( menuItemHandler->xMenuItemDispatch.is() ) + { + URL aTargetURL; + aTargetURL.Complete = menuItemHandler->aMenuItemURL; + m_xURLTransformer->parseStrict( aTargetURL ); + + menuItemHandler->xMenuItemDispatch->removeStatusListener( this, aTargetURL ); + } + menuItemHandler->xMenuItemDispatch.clear(); + } +} + +// XStatusListener +void SAL_CALL MenuBarManager::statusChanged( const FeatureStateEvent& Event ) +{ + OUString aFeatureURL = Event.FeatureURL.Complete; + + SolarMutexGuard aSolarGuard; + { + if ( m_bDisposed ) + return; + + // We have to check all menu entries as there can be identical entries in a popup menu. + for (auto const& menuItemHandler : m_aMenuItemHandlerVector) + { + if ( menuItemHandler->aParsedItemURL == aFeatureURL ) + { + bool bCheckmark( false ); + bool bMenuItemEnabled( m_pVCLMenu->IsItemEnabled( menuItemHandler->nItemId )); + bool bEnabledItem( Event.IsEnabled ); + OUString aItemText; + status::Visibility aVisibilityStatus; + + #ifdef UNIX + //enable some slots hardly, because UNIX clipboard does not notify all changes + // Can be removed if follow up task will be fixed directly within applications. + // Note: PasteSpecial is handled specifically by calc + // Calc also disables Paste under some circumstances, do not override. + /* TODO: is this workaround even needed anymore? Was introduced + * in 2009 with commit 426ab2c0e8f6e3fe2b766f74f6b8da873d860260 + * as some "metropatch" and the other places it touched seem to + * be gone. */ + if ( (menuItemHandler->aMenuItemURL == ".uno:Paste" && + m_aModuleIdentifier != "com.sun.star.sheet.SpreadsheetDocument") + || menuItemHandler->aMenuItemURL == ".uno:PasteClipboard" ) // special for draw/impress + bEnabledItem = true; + #endif + + // Enable/disable item + if ( bEnabledItem != bMenuItemEnabled ) + { + m_pVCLMenu->EnableItem( menuItemHandler->nItemId, bEnabledItem ); + + // Remove "checked" mark for disabled menu items. + // Initially disabled but checkable menu items do not receive + // checked/unchecked state, so can appear inconsistently after + // enabling/disabling. Since we can not pass checked state for disabled + // items, we will just reset checked state for them, anyway correct state + // will be transferred from controller once item enabled. + if ( !bEnabledItem && m_pVCLMenu->IsItemChecked( menuItemHandler->nItemId ) ) + m_pVCLMenu->CheckItem( menuItemHandler->nItemId, false ); + } + + if ( Event.State >>= bCheckmark ) + { + // Checkmark or RadioButton + m_pVCLMenu->CheckItem( menuItemHandler->nItemId, bCheckmark ); + // If not already designated RadioButton set as CheckMark + MenuItemBits nBits = m_pVCLMenu->GetItemBits( menuItemHandler->nItemId ); + if (!(nBits & MenuItemBits::RADIOCHECK)) + m_pVCLMenu->SetItemBits( menuItemHandler->nItemId, nBits | MenuItemBits::CHECKABLE ); + + if ( menuItemHandler->bMadeInvisible ) + m_pVCLMenu->ShowItem( menuItemHandler->nItemId ); + } + else if ( Event.State >>= aItemText ) + { + INetURLObject aURL( aFeatureURL ); + OUString aEnumPart = aURL.GetURLPath().getToken( 1, '.' ); + if ( !aEnumPart.isEmpty() && aURL.GetProtocol() == INetProtocol::Uno ) + { + // Checkmark or RadioButton + m_pVCLMenu->CheckItem( menuItemHandler->nItemId, aItemText == aEnumPart ); + // If not already designated RadioButton set as CheckMark + MenuItemBits nBits = m_pVCLMenu->GetItemBits( menuItemHandler->nItemId ); + if (!(nBits & MenuItemBits::RADIOCHECK)) + m_pVCLMenu->SetItemBits( menuItemHandler->nItemId, nBits | MenuItemBits::CHECKABLE ); + } + else + { + // Replacement for place holders + if ( aItemText.startsWith("($1)") ) + { + aItemText = FwkResId(STR_UPDATEDOC) + " " + aItemText.subView( 4 ); + } + else if ( aItemText.startsWith("($2)") ) + { + aItemText = FwkResId(STR_CLOSEDOC_ANDRETURN) + aItemText.subView( 4 ); + } + else if ( aItemText.startsWith("($3)") ) + { + aItemText = FwkResId(STR_SAVECOPYDOC) + aItemText.subView( 4 ); + } + + m_pVCLMenu->SetItemText( menuItemHandler->nItemId, aItemText ); + } + + if ( menuItemHandler->bMadeInvisible ) + m_pVCLMenu->ShowItem( menuItemHandler->nItemId ); + } + else if ( Event.State >>= aVisibilityStatus ) + { + // Visibility + m_pVCLMenu->ShowItem( menuItemHandler->nItemId, aVisibilityStatus.bVisible ); + menuItemHandler->bMadeInvisible = !aVisibilityStatus.bVisible; + } + else if ( menuItemHandler->bMadeInvisible ) + m_pVCLMenu->ShowItem( menuItemHandler->nItemId ); + } + + if ( Event.Requery ) + { + // Release dispatch object - will be required on the next activate! + menuItemHandler->xMenuItemDispatch.clear(); + } + } + } +} + +// Helper to retrieve own structure from item ID +MenuBarManager::MenuItemHandler* MenuBarManager::GetMenuItemHandler( sal_uInt16 nItemId ) +{ + SolarMutexGuard g; + + for (auto const& menuItemHandler : m_aMenuItemHandlerVector) + { + if ( menuItemHandler->nItemId == nItemId ) + return menuItemHandler.get(); + } + + return nullptr; +} + +// Helper to set request images flag +void MenuBarManager::RequestImages() +{ + + m_bRetrieveImages = true; + for (auto const& menuItemHandler : m_aMenuItemHandlerVector) + { + if ( menuItemHandler->xSubMenuManager.is() ) + menuItemHandler->xSubMenuManager->RequestImages(); + } +} + +// Helper to reset objects to prepare shutdown +void MenuBarManager::RemoveListener() +{ + SolarMutexGuard g; + + for (auto const& menuItemHandler : m_aMenuItemHandlerVector) + { + if ( menuItemHandler->xMenuItemDispatch.is() ) + { + URL aTargetURL; + aTargetURL.Complete = menuItemHandler->aMenuItemURL; + m_xURLTransformer->parseStrict( aTargetURL ); + + menuItemHandler->xMenuItemDispatch->removeStatusListener( + static_cast< XStatusListener* >( this ), aTargetURL ); + } + + menuItemHandler->xMenuItemDispatch.clear(); + + if ( menuItemHandler->xPopupMenu.is() ) + { + { + // Remove popup menu from menu structure + m_pVCLMenu->SetPopupMenu( menuItemHandler->nItemId, nullptr ); + } + + Reference< css::lang::XEventListener > xEventListener( menuItemHandler->xPopupMenuController, UNO_QUERY ); + if ( xEventListener.is() ) + { + EventObject aEventObject; + aEventObject.Source = static_cast(this); + xEventListener->disposing( aEventObject ); + } + + // We now provide a popup menu controller to external code. + // Therefore the life-time must be explicitly handled via + // dispose!! + try + { + Reference< XComponent > xComponent( menuItemHandler->xPopupMenuController, UNO_QUERY ); + if ( xComponent.is() ) + xComponent->dispose(); + } + catch ( const RuntimeException& ) + { + throw; + } + catch ( const Exception& ) + { + } + + // Release references to controller and popup menu + menuItemHandler->xPopupMenuController.clear(); + menuItemHandler->xPopupMenu.clear(); + } + + if ( menuItemHandler->xSubMenuManager ) + menuItemHandler->xSubMenuManager->dispose(); + } + + try + { + if ( m_xFrame.is() ) + m_xFrame->removeFrameActionListener( Reference< XFrameActionListener >(this) ); + } + catch ( const Exception& ) + { + } + + m_xFrame = nullptr; +} + +void SAL_CALL MenuBarManager::disposing( const EventObject& Source ) +{ + MenuItemHandler* pMenuItemDisposing = nullptr; + + SolarMutexGuard g; + + for (auto const& menuItemHandler : m_aMenuItemHandlerVector) + { + if ( menuItemHandler->xMenuItemDispatch.is() && + menuItemHandler->xMenuItemDispatch == Source.Source ) + { + // disposing called from menu item dispatcher, remove listener + pMenuItemDisposing = menuItemHandler.get(); + break; + } + } + + if ( pMenuItemDisposing ) + { + // Release references to the dispatch object + URL aTargetURL; + aTargetURL.Complete = pMenuItemDisposing->aMenuItemURL; + + m_xURLTransformer->parseStrict( aTargetURL ); + + pMenuItemDisposing->xMenuItemDispatch->removeStatusListener( + static_cast< XStatusListener* >( this ), aTargetURL ); + pMenuItemDisposing->xMenuItemDispatch.clear(); + if ( pMenuItemDisposing->xPopupMenu.is() ) + { + Reference< css::lang::XEventListener > xEventListener( pMenuItemDisposing->xPopupMenuController, UNO_QUERY ); + if ( xEventListener.is() ) + xEventListener->disposing( Source ); + + { + // Remove popup menu from menu structure as we release our reference to + // the controller. + m_pVCLMenu->SetPopupMenu( pMenuItemDisposing->nItemId, nullptr ); + } + + pMenuItemDisposing->xPopupMenuController.clear(); + pMenuItemDisposing->xPopupMenu.clear(); + } + return; + } + else if ( Source.Source == m_xFrame ) + { + // Our frame gets disposed. We have to remove all our listeners + RemoveListener(); + } + else if ( Source.Source == Reference< XInterface >( m_xDocImageManager, UNO_QUERY )) + m_xDocImageManager.clear(); + else if ( Source.Source == Reference< XInterface >( m_xModuleImageManager, UNO_QUERY )) + m_xModuleImageManager.clear(); +} + +static void lcl_CheckForChildren(Menu* pMenu, sal_uInt16 nItemId) +{ + if (PopupMenu* pThisPopup = pMenu->GetPopupMenu( nItemId )) + pMenu->EnableItem( nItemId, pThisPopup->GetItemCount() != 0 && pThisPopup->HasValidEntries(true)); +} + +// vcl handler + +namespace { + +class QuietInteractionContext: + public cppu::WeakImplHelper< css::uno::XCurrentContext > +{ +public: + explicit QuietInteractionContext( + css::uno::Reference< css::uno::XCurrentContext > context): + context_(std::move(context)) {} + QuietInteractionContext(const QuietInteractionContext&) = delete; + QuietInteractionContext& operator=(const QuietInteractionContext&) = delete; + +private: + virtual ~QuietInteractionContext() override {} + + virtual css::uno::Any SAL_CALL getValueByName( + OUString const & Name) override + { + return Name != JAVA_INTERACTION_HANDLER_NAME && context_.is() + ? context_->getValueByName(Name) + : css::uno::Any(); + } + + css::uno::Reference< css::uno::XCurrentContext > + context_; +}; + +} + +IMPL_LINK( MenuBarManager, Activate, Menu *, pMenu, bool ) +{ + if ( pMenu != m_pVCLMenu ) + return true; + + css::uno::ContextLayer layer( + new QuietInteractionContext( + css::uno::getCurrentContext())); + + // set/unset hiding disabled menu entries + bool bDontHide = officecfg::Office::Common::View::Menu::DontHideDisabledEntry::get(); + const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings(); + bool bShowMenuImages = rSettings.GetUseImagesInMenus(); + bool bShowShortcuts = m_bHasMenuBar || rSettings.GetContextMenuShortcuts(); + bool bHasDisabledEntries = SvtCommandOptions().HasEntriesDisabled(); + + SolarMutexGuard g; + + MenuFlags nFlag = pMenu->GetMenuFlags(); + if ( bDontHide ) + nFlag &= ~MenuFlags::HideDisabledEntries; + else + nFlag |= MenuFlags::HideDisabledEntries; + pMenu->SetMenuFlags( nFlag ); + + if ( m_bActive ) + return false; + + m_bActive = true; + + // Check if some modes have changed so we have to update our menu images + OUString sIconTheme = SvtMiscOptions::GetIconTheme(); + + if ( m_bRetrieveImages || + bShowMenuImages != m_bShowMenuImages || + sIconTheme != m_sIconTheme ) + { + m_bShowMenuImages = bShowMenuImages; + m_bRetrieveImages = false; + m_sIconTheme = sIconTheme; + FillMenuImages( m_xFrame, pMenu, bShowMenuImages ); + } + + // Try to map commands to labels + for ( sal_uInt16 nPos = 0; nPos < pMenu->GetItemCount(); nPos++ ) + { + sal_uInt16 nItemId = pMenu->GetItemId( nPos ); + if (( pMenu->GetItemType( nPos ) != MenuItemType::SEPARATOR ) && + ( pMenu->GetItemText( nItemId ).isEmpty() )) + { + OUString aCommand = pMenu->GetItemCommand( nItemId ); + if ( !aCommand.isEmpty() ) { + pMenu->SetItemText( nItemId, RetrieveLabelFromCommand( aCommand )); + } + } + } + + // Try to set accelerator keys + { + if ( bShowShortcuts ) + RetrieveShortcuts( m_aMenuItemHandlerVector ); + + for (auto const& menuItemHandler : m_aMenuItemHandlerVector) + { + if ( !bShowShortcuts ) + { + pMenu->SetAccelKey( menuItemHandler->nItemId, vcl::KeyCode() ); + } + else if ( menuItemHandler->aMenuItemURL == aCmdHelpIndex ) + { + // Set key code, workaround for hard-coded shortcut F1 mapped to .uno:HelpIndex + // Only non-popup menu items can have a short-cut + vcl::KeyCode aKeyCode( KEY_F1 ); + pMenu->SetAccelKey( menuItemHandler->nItemId, aKeyCode ); + } + else if ( pMenu->GetPopupMenu( menuItemHandler->nItemId ) == nullptr ) + pMenu->SetAccelKey( menuItemHandler->nItemId, menuItemHandler->aKeyCode ); + } + } + + URL aTargetURL; + + // Use provided dispatch provider => fallback to frame as dispatch provider + Reference< XDispatchProvider > xDispatchProvider; + if ( m_xDispatchProvider.is() ) + xDispatchProvider = m_xDispatchProvider; + else + xDispatchProvider.set( m_xFrame, UNO_QUERY ); + + if ( !xDispatchProvider.is() ) + return true; + + SvtCommandOptions aCmdOptions; + for (auto const& menuItemHandler : m_aMenuItemHandlerVector) + { + if (menuItemHandler) + { + if ( !menuItemHandler->xMenuItemDispatch.is() && + !menuItemHandler->xSubMenuManager.is() ) + { + Reference< XDispatch > xMenuItemDispatch; + + aTargetURL.Complete = menuItemHandler->aMenuItemURL; + + m_xURLTransformer->parseStrict( aTargetURL ); + + if ( bHasDisabledEntries ) + { + if ( aCmdOptions.LookupDisabled( aTargetURL.Path )) + pMenu->HideItem( menuItemHandler->nItemId ); + } + + if ( aTargetURL.Complete.startsWith( ".uno:StyleApply?" ) ) + xMenuItemDispatch = new StyleDispatcher( m_xFrame, m_xURLTransformer, aTargetURL ); + else + xMenuItemDispatch = xDispatchProvider->queryDispatch( aTargetURL, menuItemHandler->aTargetFrame, 0 ); + + bool bPopupMenu( false ); + if ( !menuItemHandler->xPopupMenuController.is() && + m_xPopupMenuControllerFactory->hasController( menuItemHandler->aMenuItemURL, m_aModuleIdentifier ) ) + { + if( xMenuItemDispatch.is() || menuItemHandler->aMenuItemURL != ".uno:RecentFileList" ) + bPopupMenu = CreatePopupMenuController(menuItemHandler.get(), m_xDispatchProvider, m_aModuleIdentifier); + + if (bPopupMenu && menuItemHandler->xPopupMenuController.is()) + { + if (PopupMenu* pThisPopup = pMenu->GetPopupMenu(menuItemHandler->nItemId)) + { + pThisPopup->Activate(); + pThisPopup->Deactivate(); + } + } + } + else if ( menuItemHandler->xPopupMenuController.is() ) + { + // Force update of popup menu + menuItemHandler->xPopupMenuController->updatePopupMenu(); + bPopupMenu = true; + if (PopupMenu* pThisPopup = pMenu->GetPopupMenu( menuItemHandler->nItemId )) + { + pThisPopup->Activate(); + pThisPopup->Deactivate(); + } + } + lcl_CheckForChildren(pMenu, menuItemHandler->nItemId); + + if ( xMenuItemDispatch.is() ) + { + menuItemHandler->xMenuItemDispatch = xMenuItemDispatch; + menuItemHandler->aParsedItemURL = aTargetURL.Complete; + + if ( !bPopupMenu ) + { + xMenuItemDispatch->addStatusListener( static_cast< XStatusListener* >( this ), aTargetURL ); + // For the menubar, we have to keep status listening to support Ubuntu's HUD. + if ( !m_bHasMenuBar ) + xMenuItemDispatch->removeStatusListener( static_cast< XStatusListener* >( this ), aTargetURL ); + } + } + else if ( !bPopupMenu ) + pMenu->EnableItem( menuItemHandler->nItemId, false ); + } + else if ( menuItemHandler->xPopupMenuController.is() ) + { + // Force update of popup menu + menuItemHandler->xPopupMenuController->updatePopupMenu(); + if (PopupMenu* pThisPopup = pMenu->GetPopupMenu(menuItemHandler->nItemId)) + { + pThisPopup->Activate(); + pThisPopup->Deactivate(); + } + lcl_CheckForChildren(pMenu, menuItemHandler->nItemId); + } + else if ( menuItemHandler->xMenuItemDispatch.is() ) + { + // We need an update to reflect the current state + try + { + aTargetURL.Complete = menuItemHandler->aMenuItemURL; + m_xURLTransformer->parseStrict( aTargetURL ); + + menuItemHandler->xMenuItemDispatch->addStatusListener( + static_cast< XStatusListener* >( this ), aTargetURL ); + menuItemHandler->xMenuItemDispatch->removeStatusListener( + static_cast< XStatusListener* >( this ), aTargetURL ); + } + catch ( const Exception& ) + { + } + } + else if (menuItemHandler->xSubMenuManager.is()) + { + MenuBarManager* pMenuBarManager = menuItemHandler->xSubMenuManager.get(); + if (pMenuBarManager) + { + pMenuBarManager->Activate(pMenuBarManager->GetMenuBar()); + pMenuBarManager->Deactivate(pMenuBarManager->GetMenuBar()); + } + lcl_CheckForChildren(pMenu, menuItemHandler->nItemId); + } + } + } + + return true; +} + +IMPL_LINK( MenuBarManager, Deactivate, Menu *, pMenu, bool ) +{ + if ( pMenu == m_pVCLMenu ) + { + m_bActive = false; + if ( pMenu->IsMenuBar() && m_xDeferredItemContainer.is() ) + { + // Start timer to handle settings asynchronous + // Changing the menu inside this handler leads to + // a crash under X! + m_aAsyncSettingsTimer.SetInvokeHandler(LINK(this, MenuBarManager, AsyncSettingsHdl)); + m_aAsyncSettingsTimer.SetTimeout(10); + m_aAsyncSettingsTimer.Start(); + } + } + + return true; +} + +IMPL_LINK_NOARG( MenuBarManager, AsyncSettingsHdl, Timer*, void) +{ + SolarMutexGuard g; + Reference< XInterface > xSelfHold( + static_cast< ::cppu::OWeakObject* >( this ), UNO_QUERY_THROW ); + + m_aAsyncSettingsTimer.Stop(); + if ( !m_bActive && m_xDeferredItemContainer.is() ) + { + SetItemContainer( m_xDeferredItemContainer ); + m_xDeferredItemContainer.clear(); + } +} + +IMPL_LINK( MenuBarManager, Select, Menu *, pMenu, bool ) +{ + URL aTargetURL; + Sequence aArgs; + Reference< XDispatch > xDispatch; + + { + SolarMutexGuard g; + + sal_uInt16 nCurItemId = pMenu->GetCurItemId(); + sal_uInt16 nCurPos = pMenu->GetItemPos( nCurItemId ); + if ( pMenu == m_pVCLMenu && + pMenu->GetItemType( nCurPos ) != MenuItemType::SEPARATOR ) + { + MenuItemHandler* pMenuItemHandler = GetMenuItemHandler( nCurItemId ); + if ( pMenuItemHandler && pMenuItemHandler->xMenuItemDispatch.is() ) + { + aTargetURL.Complete = pMenuItemHandler->aMenuItemURL; + m_xURLTransformer->parseStrict( aTargetURL ); + + if ( pMenu->GetUserValue( nCurItemId ) ) + { + // addon menu item selected + aArgs = { comphelper::makePropertyValue("Referer", OUString("private:user")) }; + } + + xDispatch = pMenuItemHandler->xMenuItemDispatch; + } + } + } + + // tdf#126054 don't let dispatch destroy this until after function completes + rtl::Reference xKeepAlive(this); + if (xDispatch.is()) + { + SolarMutexReleaser aReleaser; + xDispatch->dispatch( aTargetURL, aArgs ); + } + + if ( !m_bHasMenuBar ) + // Standalone (non-native) popup menu doesn't fire deactivate event + // in this case, so we have to reset the active flag here. + m_bActive = false; + + return true; +} + +bool MenuBarManager::MustBeHidden( PopupMenu* pPopupMenu, const Reference< XURLTransformer >& rTransformer ) +{ + if ( !pPopupMenu ) + return true; + + URL aTargetURL; + SvtCommandOptions aCmdOptions; + + sal_uInt16 nCount = pPopupMenu->GetItemCount(); + sal_uInt16 nHideCount( 0 ); + + for ( sal_uInt16 i = 0; i < nCount; i++ ) + { + sal_uInt16 nId = pPopupMenu->GetItemId( i ); + if ( nId > 0 ) + { + PopupMenu* pSubPopupMenu = pPopupMenu->GetPopupMenu( nId ); + if ( pSubPopupMenu ) + { + if ( MustBeHidden( pSubPopupMenu, rTransformer )) + { + pPopupMenu->HideItem( nId ); + ++nHideCount; + } + } + else + { + aTargetURL.Complete = pPopupMenu->GetItemCommand( nId ); + rTransformer->parseStrict( aTargetURL ); + + if ( aCmdOptions.LookupDisabled( aTargetURL.Path )) + ++nHideCount; + } + } + else + ++nHideCount; + } + + return ( nCount == nHideCount ); +} + +OUString MenuBarManager::RetrieveLabelFromCommand(const OUString& rCmdURL) +{ + auto aProperties = vcl::CommandInfoProvider::GetCommandProperties(rCmdURL, m_aModuleIdentifier); + if ( !m_bHasMenuBar ) + { + // This is a context menu, prefer "PopupLabel" over "Label". + return vcl::CommandInfoProvider::GetPopupLabelForCommand(aProperties); + } + return vcl::CommandInfoProvider::GetMenuLabelForCommand(aProperties); +} + +bool MenuBarManager::CreatePopupMenuController( MenuItemHandler* pMenuItemHandler, + const css::uno::Reference< css::frame::XDispatchProvider >& rDispatchProvider, + const OUString& rModuleIdentifier ) +{ + OUString aItemCommand( pMenuItemHandler->aMenuItemURL ); + + // Try instantiate a popup menu controller. It is stored in the menu item handler. + if ( !m_xPopupMenuControllerFactory.is() ) + return false; + + auto aSeq( comphelper::InitAnyPropertySequence( { + { "DispatchProvider", Any(rDispatchProvider) }, + { "ModuleIdentifier", Any(rModuleIdentifier) }, + { "Frame", Any(m_xFrame) }, + { "InToolbar", Any(!m_bHasMenuBar) } + } ) ); + + Reference< XPopupMenuController > xPopupMenuController( + m_xPopupMenuControllerFactory->createInstanceWithArgumentsAndContext( + aItemCommand, + aSeq, + m_xContext ), + UNO_QUERY ); + + if ( xPopupMenuController.is() ) + { + // Provide our awt popup menu to the popup menu controller + pMenuItemHandler->xPopupMenuController = xPopupMenuController; + xPopupMenuController->setPopupMenu( pMenuItemHandler->xPopupMenu ); + return true; + } + + return false; +} + +void MenuBarManager::FillMenuManager( Menu* pMenu, const Reference< XFrame >& rFrame, + const Reference< XDispatchProvider >& rDispatchProvider, + const OUString& rModuleIdentifier, bool bDelete ) +{ + m_xFrame = rFrame; + m_bActive = false; + m_bDeleteMenu = bDelete; + m_pVCLMenu = pMenu; + m_xDispatchProvider = rDispatchProvider; + + const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings(); + m_bShowMenuImages = rSettings.GetUseImagesInMenus(); + m_bRetrieveImages = false; + + // Set module identifier when provided from outside + if (!rModuleIdentifier.isEmpty()) + m_aModuleIdentifier = rModuleIdentifier; + else + m_aModuleIdentifier = vcl::CommandInfoProvider::GetModuleIdentifier(m_xFrame); + + // Add root as ui configuration listener + RetrieveImageManagers(); + + if ( pMenu->IsMenuBar() && rFrame.is() ) + { + // First merge all addon popup menus into our structure + sal_uInt16 nPos = 0; + for ( nPos = 0; nPos < pMenu->GetItemCount(); nPos++ ) + { + sal_uInt16 nItemId = pMenu->GetItemId( nPos ); + OUString aCommand = pMenu->GetItemCommand( nItemId ); + if ( aCommand == aSpecialWindowCommand || aCommand == aCmdHelpMenu ) + { + // Retrieve addon popup menus and add them to our menu bar + framework::AddonMenuManager::MergeAddonPopupMenus( rFrame, nPos, static_cast(pMenu) ); + break; + } + } + + // Merge the Add-Ons help menu items into the Office help menu + framework::AddonMenuManager::MergeAddonHelpMenu( rFrame, static_cast(pMenu) ); + } + + bool bAccessibilityEnabled( Application::GetSettings().GetMiscSettings().GetEnableATToolSupport() ); + sal_uInt16 nItemCount = pMenu->GetItemCount(); + OUString aItemCommand; + m_aMenuItemHandlerVector.reserve(nItemCount); + for ( sal_uInt16 i = 0; i < nItemCount; i++ ) + { + sal_uInt16 nItemId = FillItemCommand(aItemCommand,pMenu, i ); + + if (( pMenu->IsMenuBar() || bAccessibilityEnabled ) && + ( pMenu->GetItemText( nItemId ).isEmpty() )) + { + if ( !aItemCommand.isEmpty() ) + pMenu->SetItemText( nItemId, RetrieveLabelFromCommand( aItemCommand )); + } + + // Command can be just an alias to another command. + auto aProperties = vcl::CommandInfoProvider::GetCommandProperties(aItemCommand, m_aModuleIdentifier); + OUString aRealCommand = vcl::CommandInfoProvider::GetRealCommandForCommand(aProperties); + if ( !aRealCommand.isEmpty() ) + aItemCommand = aRealCommand; + + Reference< XDispatch > xDispatch; + VclPtr pPopup = pMenu->GetPopupMenu( nItemId ); + // overwrite the show icons on menu option? + MenuItemBits nBits = pMenu->GetItemBits( nItemId ) & ( MenuItemBits::ICON | MenuItemBits::TEXT ); + bool bItemShowMenuImages = ( m_bShowMenuImages && nBits != MenuItemBits::TEXT ) || nBits & MenuItemBits::ICON; + + if ( pPopup ) + { + // Retrieve module identifier from Help Command entry + OUString aModuleIdentifier( rModuleIdentifier ); + if (!pMenu->GetHelpCommand(nItemId).isEmpty()) + { + aModuleIdentifier = pMenu->GetHelpCommand( nItemId ); + pMenu->SetHelpCommand( nItemId, "" ); + } + + // Retrieve possible attributes struct + Reference< XDispatchProvider > xPopupMenuDispatchProvider( rDispatchProvider ); + MenuAttributes* pAttributes = static_cast(pMenu->GetUserValue( nItemId )); + if ( pAttributes ) + xPopupMenuDispatchProvider = pAttributes->xDispatchProvider; + + if ( m_xPopupMenuControllerFactory.is() && + m_xPopupMenuControllerFactory->hasController( aItemCommand, aModuleIdentifier ) + ) + { + // Check if we have to create a popup menu for a uno based popup menu controller. + // We have to set an empty popup menu into our menu structure so the controller also + // works with inplace OLE. + MenuItemHandler* pItemHandler = new MenuItemHandler( nItemId, nullptr, xDispatch ); + rtl::Reference pVCLXPopupMenu = new VCLXPopupMenu(pPopup); + pItemHandler->xPopupMenu = pVCLXPopupMenu; + pItemHandler->aMenuItemURL = aItemCommand; + m_aMenuItemHandlerVector.push_back( std::unique_ptr(pItemHandler) ); + + if ( bAccessibilityEnabled || pMenu->IsMenuBar()) + { + if ( CreatePopupMenuController( pItemHandler, xPopupMenuDispatchProvider, aModuleIdentifier )) + pItemHandler->xPopupMenuController->updatePopupMenu(); + } + lcl_CheckForChildren(pMenu, nItemId); + } + else + { + // Check if this is the tools menu. Add menu item if needed + if ( aItemCommand == aCmdToolsMenu && AddonMenuManager::HasAddonMenuElements() ) + { + // Create addon popup menu if there exist elements and this is the tools popup menu + VclPtr pSubMenu = AddonMenuManager::CreateAddonMenu(rFrame); + if ( pSubMenu && ( pSubMenu->GetItemCount() > 0 )) + { + if ( pPopup->GetItemType( pPopup->GetItemCount() - 1 ) != MenuItemType::SEPARATOR ) + pPopup->InsertSeparator(); + + pPopup->InsertItem( ITEMID_ADDONLIST, OUString() ); + pPopup->SetPopupMenu( ITEMID_ADDONLIST, pSubMenu ); + pPopup->SetItemCommand( ITEMID_ADDONLIST, ".uno:Addons" ); + } + else + pSubMenu.disposeAndClear(); + } + + rtl::Reference pSubMenuManager = new MenuBarManager( m_xContext, rFrame, m_xURLTransformer, + xPopupMenuDispatchProvider, aModuleIdentifier, pPopup, false, m_bHasMenuBar ); + + AddMenu(pSubMenuManager.get(), aItemCommand, nItemId); + } + } + else if ( pMenu->GetItemType( i ) != MenuItemType::SEPARATOR ) + { + if ( bItemShowMenuImages ) + m_bRetrieveImages = true; + + std::unique_ptr pItemHandler(new MenuItemHandler( nItemId, nullptr, xDispatch )); + // Retrieve possible attributes struct + MenuAttributes* pAttributes = static_cast(pMenu->GetUserValue( nItemId )); + if ( pAttributes ) + pItemHandler->aTargetFrame = pAttributes->aTargetFrame; + pItemHandler->aMenuItemURL = aItemCommand; + + if ( m_xPopupMenuControllerFactory.is() && + m_xPopupMenuControllerFactory->hasController( aItemCommand, m_aModuleIdentifier ) ) + { + // Check if we have to create a popup menu for a uno based popup menu controller. + // We have to set an empty popup menu into our menu structure so the controller also + // works with inplace OLE. + rtl::Reference pVCLXPopupMenu = new VCLXPopupMenu; + PopupMenu* pPopupMenu = static_cast(pVCLXPopupMenu->GetMenu()); + pMenu->SetPopupMenu( pItemHandler->nItemId, pPopupMenu ); + pItemHandler->xPopupMenu = pVCLXPopupMenu; + + if ( bAccessibilityEnabled && CreatePopupMenuController( pItemHandler.get(), m_xDispatchProvider, m_aModuleIdentifier ) ) + { + pItemHandler->xPopupMenuController->updatePopupMenu(); + } + + lcl_CheckForChildren(pMenu, pItemHandler->nItemId); + } + + m_aMenuItemHandlerVector.push_back( std::move(pItemHandler) ); + } + } + + if ( m_bHasMenuBar && bAccessibilityEnabled ) + { + RetrieveShortcuts( m_aMenuItemHandlerVector ); + for (auto const& menuItemHandler : m_aMenuItemHandlerVector) + { + // Set key code, workaround for hard-coded shortcut F1 mapped to .uno:HelpIndex + // Only non-popup menu items can have a short-cut + if ( menuItemHandler->aMenuItemURL == aCmdHelpIndex ) + { + vcl::KeyCode aKeyCode( KEY_F1 ); + pMenu->SetAccelKey( menuItemHandler->nItemId, aKeyCode ); + } + else if ( pMenu->GetPopupMenu( menuItemHandler->nItemId ) == nullptr ) + pMenu->SetAccelKey( menuItemHandler->nItemId, menuItemHandler->aKeyCode ); + } + } + + SetHdl(); +} + +void MenuBarManager::impl_RetrieveShortcutsFromConfiguration( + const Reference< XAcceleratorConfiguration >& rAccelCfg, + const Sequence< OUString >& rCommands, + std::vector< std::unique_ptr >& aMenuShortCuts ) +{ + if ( !rAccelCfg.is() ) + return; + + try + { + css::awt::KeyEvent aKeyEvent; + Sequence< Any > aSeqKeyCode = rAccelCfg->getPreferredKeyEventsForCommandList( rCommands ); + for ( sal_Int32 i = 0; i < aSeqKeyCode.getLength(); i++ ) + { + if ( aSeqKeyCode[i] >>= aKeyEvent ) + aMenuShortCuts[i]->aKeyCode = svt::AcceleratorExecute::st_AWTKey2VCLKey( aKeyEvent ); + } + } + catch ( const IllegalArgumentException& ) + { + } +} + +void MenuBarManager::RetrieveShortcuts( std::vector< std::unique_ptr >& aMenuShortCuts ) +{ + Reference< XAcceleratorConfiguration > xDocAccelCfg( m_xDocAcceleratorManager ); + Reference< XAcceleratorConfiguration > xModuleAccelCfg( m_xModuleAcceleratorManager ); + Reference< XAcceleratorConfiguration > xGlobalAccelCfg( m_xGlobalAcceleratorManager ); + + if ( !m_bAcceleratorCfg ) + { + // Retrieve references on demand + m_bAcceleratorCfg = true; + if ( !xDocAccelCfg.is() ) + { + Reference< XController > xController = m_xFrame->getController(); + Reference< XModel > xModel; + if ( xController.is() ) + { + xModel = xController->getModel(); + if ( xModel.is() ) + { + Reference< XUIConfigurationManagerSupplier > xSupplier( xModel, UNO_QUERY ); + if ( xSupplier.is() ) + { + Reference< XUIConfigurationManager > xDocUICfgMgr = xSupplier->getUIConfigurationManager(); + if ( xDocUICfgMgr.is() ) + { + xDocAccelCfg = xDocUICfgMgr->getShortCutManager(); + m_xDocAcceleratorManager = xDocAccelCfg; + } + } + } + } + } + + if ( !xModuleAccelCfg.is() ) + { + Reference< XModuleUIConfigurationManagerSupplier > xModuleCfgMgrSupplier = + theModuleUIConfigurationManagerSupplier::get( m_xContext ); + try + { + Reference< XUIConfigurationManager > xUICfgMgr = xModuleCfgMgrSupplier->getUIConfigurationManager( m_aModuleIdentifier ); + if ( xUICfgMgr.is() ) + { + xModuleAccelCfg = xUICfgMgr->getShortCutManager(); + m_xModuleAcceleratorManager = xModuleAccelCfg; + } + } + catch ( const RuntimeException& ) + { + throw; + } + catch ( const Exception& ) + { + } + } + + if ( !xGlobalAccelCfg.is() ) try + { + xGlobalAccelCfg = GlobalAcceleratorConfiguration::create( m_xContext ); + m_xGlobalAcceleratorManager = xGlobalAccelCfg; + } + catch ( const css::uno::DeploymentException& ) + { + SAL_WARN("fwk.uielement", "GlobalAcceleratorConfiguration" + " not available. This should happen only on mobile platforms."); + } + } + + vcl::KeyCode aEmptyKeyCode; + Sequence< OUString > aSeq( aMenuShortCuts.size() ); + auto aSeqRange = asNonConstRange(aSeq); + const sal_uInt32 nCount = aMenuShortCuts.size(); + for ( sal_uInt32 i = 0; i < nCount; ++i ) + { + aSeqRange[i] = aMenuShortCuts[i]->aMenuItemURL; + aMenuShortCuts[i]->aKeyCode = aEmptyKeyCode; + } + + if ( m_xGlobalAcceleratorManager.is() ) + impl_RetrieveShortcutsFromConfiguration( xGlobalAccelCfg, aSeq, aMenuShortCuts ); + if ( m_xModuleAcceleratorManager.is() ) + impl_RetrieveShortcutsFromConfiguration( xModuleAccelCfg, aSeq, aMenuShortCuts ); + if ( m_xDocAcceleratorManager.is() ) + impl_RetrieveShortcutsFromConfiguration( xDocAccelCfg, aSeq, aMenuShortCuts ); +} + +void MenuBarManager::RetrieveImageManagers() +{ + if ( !m_xDocImageManager.is() ) + { + Reference< XController > xController = m_xFrame->getController(); + Reference< XModel > xModel; + if ( xController.is() ) + { + xModel = xController->getModel(); + if ( xModel.is() ) + { + Reference< XUIConfigurationManagerSupplier > xSupplier( xModel, UNO_QUERY ); + if ( xSupplier.is() ) + { + Reference< XUIConfigurationManager > xDocUICfgMgr = xSupplier->getUIConfigurationManager(); + m_xDocImageManager.set( xDocUICfgMgr->getImageManager(), UNO_QUERY ); + m_xDocImageManager->addConfigurationListener( + Reference< XUIConfigurationListener >(this) ); + } + } + } + } + + if ( !m_xModuleImageManager.is() ) + { + Reference< XModuleUIConfigurationManagerSupplier > xModuleCfgMgrSupplier = + theModuleUIConfigurationManagerSupplier::get( m_xContext ); + Reference< XUIConfigurationManager > xUICfgMgr = xModuleCfgMgrSupplier->getUIConfigurationManager( m_aModuleIdentifier ); + m_xModuleImageManager.set( xUICfgMgr->getImageManager(), UNO_QUERY ); + m_xModuleImageManager->addConfigurationListener( Reference< XUIConfigurationListener >(this) ); + } +} + +void MenuBarManager::FillMenuWithConfiguration( + sal_uInt16& nId, + Menu* pMenu, + const OUString& rModuleIdentifier, + const Reference< XIndexAccess >& rItemContainer, + const Reference< XURLTransformer >& rTransformer ) +{ + Reference< XDispatchProvider > xEmptyDispatchProvider; + MenuBarManager::FillMenu( nId, pMenu, rModuleIdentifier, rItemContainer, xEmptyDispatchProvider ); + + // Merge add-on menu entries into the menu bar + MenuBarManager::MergeAddonMenus( pMenu, + AddonsOptions().GetMergeMenuInstructions(), + rModuleIdentifier ); + + bool bHasDisabledEntries = SvtCommandOptions().HasEntriesDisabled(); + if ( !bHasDisabledEntries ) + return; + + sal_uInt16 nCount = pMenu->GetItemCount(); + for ( sal_uInt16 i = 0; i < nCount; i++ ) + { + sal_uInt16 nID = pMenu->GetItemId( i ); + if ( nID > 0 ) + { + PopupMenu* pPopupMenu = pMenu->GetPopupMenu( nID ); + if ( pPopupMenu ) + { + if ( MustBeHidden( pPopupMenu, rTransformer )) + pMenu->HideItem( nId ); + } + } + } +} + +void MenuBarManager::FillMenu( + sal_uInt16& nId, + Menu* pMenu, + const OUString& rModuleIdentifier, + const Reference< XIndexAccess >& rItemContainer, + const Reference< XDispatchProvider >& rDispatchProvider ) +{ + // Fill menu bar with container contents + for ( sal_Int32 n = 0; n < rItemContainer->getCount(); n++ ) + { + Sequence< PropertyValue > aProps; + OUString aCommandURL; + OUString aLabel; + OUString aModuleIdentifier( rModuleIdentifier ); + sal_uInt16 nType = 0; + Reference< XIndexAccess > xIndexContainer; + Reference< XDispatchProvider > xDispatchProvider( rDispatchProvider ); + sal_Int16 nStyle = 0; + try + { + if ( rItemContainer->getByIndex( n ) >>= aProps ) + { + bool bShow = true; + bool bEnabled = true; + + for ( beans::PropertyValue const & rProp : std::as_const(aProps) ) + { + OUString aPropName = rProp.Name; + if ( aPropName == "CommandURL" ) + rProp.Value >>= aCommandURL; + else if ( aPropName == "ItemDescriptorContainer" ) + rProp.Value >>= xIndexContainer; + else if ( aPropName == "Label" ) + rProp.Value >>= aLabel; + else if ( aPropName == "Type" ) + rProp.Value >>= nType; + else if ( aPropName == "ModuleIdentifier" ) + rProp.Value >>= aModuleIdentifier; + else if ( aPropName == "DispatchProvider" ) + rProp.Value >>= xDispatchProvider; + else if ( aPropName == "Style" ) + rProp.Value >>= nStyle; + else if ( aPropName == "IsVisible" ) + rProp.Value >>= bShow; + else if ( aPropName == "Enabled" ) + rProp.Value >>= bEnabled; + } + + if (!aCommandURL.isEmpty() && vcl::CommandInfoProvider::IsExperimental(aCommandURL, rModuleIdentifier) && + !officecfg::Office::Common::Misc::ExperimentalMode::get()) + { + continue; + } + + if ( nType == css::ui::ItemType::DEFAULT ) + { + pMenu->InsertItem( nId, aLabel ); + pMenu->SetItemCommand( nId, aCommandURL ); + + if ( nStyle ) + { + MenuItemBits nBits = pMenu->GetItemBits( nId ); + if ( nStyle & css::ui::ItemStyle::ICON ) + nBits |= MenuItemBits::ICON; + if ( nStyle & css::ui::ItemStyle::TEXT ) + nBits |= MenuItemBits::TEXT; + if ( nStyle & css::ui::ItemStyle::RADIO_CHECK ) + nBits |= MenuItemBits::RADIOCHECK; + pMenu->SetItemBits( nId, nBits ); + } + + if ( !bShow ) + pMenu->HideItem( nId ); + + if ( !bEnabled) + pMenu->EnableItem( nId, false ); + + if ( xIndexContainer.is() ) + { + VclPtr pNewPopupMenu = VclPtr::Create(); + pMenu->SetPopupMenu( nId, pNewPopupMenu ); + // Use the command URL as the Help ID for the sub menu + pNewPopupMenu->SetHelpId(aCommandURL); + + if ( xDispatchProvider.is() ) + { + // Use attributes struct to transport special dispatch provider + void* nAttributePtr = MenuAttributes::CreateAttribute(xDispatchProvider); + pMenu->SetUserValue(nId, nAttributePtr, MenuAttributes::ReleaseAttribute); + } + + // Use help command to transport module identifier + if ( !aModuleIdentifier.isEmpty() ) + pMenu->SetHelpCommand( nId, aModuleIdentifier ); + + ++nId; + FillMenu( nId, pNewPopupMenu, aModuleIdentifier, xIndexContainer, xDispatchProvider ); + } + else + ++nId; + } + else + { + pMenu->InsertSeparator(); + ++nId; + } + } + } + catch ( const IndexOutOfBoundsException& ) + { + break; + } + } +} + +void MenuBarManager::MergeAddonMenus( + Menu* pMenuBar, + const MergeMenuInstructionContainer& aMergeInstructionContainer, + const OUString& rModuleIdentifier ) +{ + // set start value for the item ID for the new addon menu items + sal_uInt16 nItemId = ADDONMENU_MERGE_ITEMID_START; + + const sal_uInt32 nCount = aMergeInstructionContainer.size(); + for ( sal_uInt32 i = 0; i < nCount; i++ ) + { + const MergeMenuInstruction& rMergeInstruction = aMergeInstructionContainer[i]; + + if ( MenuBarMerger::IsCorrectContext( rMergeInstruction.aMergeContext, rModuleIdentifier )) + { + ::std::vector< OUString > aMergePath; + + // retrieve the merge path from the merge point string + MenuBarMerger::RetrieveReferencePath( rMergeInstruction.aMergePoint, aMergePath ); + + // convert the sequence/sequence property value to a more convenient vector<> + AddonMenuContainer aMergeMenuItems; + MenuBarMerger::GetSubMenu( rMergeInstruction.aMergeMenu, aMergeMenuItems ); + + // try to find the reference point for our merge operation + Menu* pMenu = pMenuBar; + ReferencePathInfo aResult = MenuBarMerger::FindReferencePath( aMergePath, pMenu ); + + if ( aResult.eResult == RP_OK ) + { + // normal merge operation + MenuBarMerger::ProcessMergeOperation( aResult.pPopupMenu, + aResult.nPos, + nItemId, + rMergeInstruction.aMergeCommand, + rMergeInstruction.aMergeCommandParameter, + rModuleIdentifier, + aMergeMenuItems ); + } + else + { + // fallback + MenuBarMerger::ProcessFallbackOperation( aResult, + nItemId, + rMergeInstruction.aMergeCommand, + rMergeInstruction.aMergeFallback, + aMergePath, + rModuleIdentifier, + aMergeMenuItems ); + } + } + } +} + +void MenuBarManager::SetItemContainer( const Reference< XIndexAccess >& rItemContainer ) +{ + SolarMutexGuard aSolarMutexGuard; + + Reference< XFrame > xFrame = m_xFrame; + + // Clear MenuBarManager structures + { + // Check active state as we cannot change our VCL menu during activation by the user + if ( m_bActive ) + { + m_xDeferredItemContainer = rItemContainer; + return; + } + + RemoveListener(); + m_aMenuItemHandlerVector.clear(); + m_pVCLMenu->Clear(); + + sal_uInt16 nId = 1; + + // Fill menu bar with container contents + FillMenuWithConfiguration( nId, m_pVCLMenu, m_aModuleIdentifier, rItemContainer, m_xURLTransformer ); + + // Refill menu manager again + Reference< XDispatchProvider > xDispatchProvider; + FillMenuManager( m_pVCLMenu, xFrame, xDispatchProvider, m_aModuleIdentifier, false ); + + // add itself as frame action listener + m_xFrame->addFrameActionListener( Reference< XFrameActionListener >(this) ); + } +} + +void MenuBarManager::GetPopupController( PopupControllerCache& rPopupController ) +{ + + SolarMutexGuard aSolarMutexGuard; + + for (auto const& menuItemHandler : m_aMenuItemHandlerVector) + { + if ( menuItemHandler->xPopupMenuController.is() ) + { + Reference< XDispatchProvider > xDispatchProvider( menuItemHandler->xPopupMenuController, UNO_QUERY ); + + PopupControllerEntry aPopupControllerEntry; + aPopupControllerEntry.m_xDispatchProvider = xDispatchProvider; + + // Just use the main part of the URL for popup menu controllers + sal_Int32 nSchemePart( 0 ); + OUString aMenuURL( menuItemHandler->aMenuItemURL ); + + nSchemePart = aMenuURL.indexOf( ':' ); + if (( nSchemePart > 0 ) && + ( aMenuURL.getLength() > ( nSchemePart+1 ))) + { + OUString aMainURL( "vnd.sun.star.popup:" ); + sal_Int32 nQueryPart = aMenuURL.indexOf( '?', nSchemePart ); + if ( nQueryPart > 0 ) + aMainURL += aMenuURL.subView( nSchemePart, nQueryPart-nSchemePart ); + else if ( nQueryPart == -1 ) + aMainURL += aMenuURL.subView( nSchemePart+1 ); + + rPopupController.emplace( aMainURL, aPopupControllerEntry ); + } + } + if ( menuItemHandler->xSubMenuManager ) + { + menuItemHandler->xSubMenuManager->GetPopupController( rPopupController ); + } + } +} + +void MenuBarManager::AddMenu(MenuBarManager* pSubMenuManager,const OUString& _sItemCommand,sal_uInt16 _nItemId) +{ + Reference< XStatusListener > xSubMenuManager( pSubMenuManager ); + m_xFrame->addFrameActionListener( Reference< XFrameActionListener >( xSubMenuManager, UNO_QUERY )); + + Reference< XDispatch > xDispatch; + std::unique_ptr pMenuItemHandler(new MenuItemHandler( + _nItemId, + pSubMenuManager, + xDispatch )); + pMenuItemHandler->aMenuItemURL = _sItemCommand; + m_aMenuItemHandlerVector.push_back( std::move(pMenuItemHandler) ); +} + +sal_uInt16 MenuBarManager::FillItemCommand(OUString& _rItemCommand, Menu* _pMenu,sal_uInt16 _nIndex) const +{ + sal_uInt16 nItemId = _pMenu->GetItemId( _nIndex ); + + _rItemCommand = _pMenu->GetItemCommand( nItemId ); + if ( _rItemCommand.isEmpty() ) + { + _rItemCommand = "slot:" + OUString::number( nItemId ); + _pMenu->SetItemCommand( nItemId, _rItemCommand ); + } + return nItemId; +} + +void MenuBarManager::SetHdl() +{ + m_pVCLMenu->SetActivateHdl( LINK( this, MenuBarManager, Activate )); + m_pVCLMenu->SetDeactivateHdl( LINK( this, MenuBarManager, Deactivate )); + m_pVCLMenu->SetSelectHdl( LINK( this, MenuBarManager, Select )); + + if ( !m_xURLTransformer.is() && m_xContext.is() ) + m_xURLTransformer.set( URLTransformer::create( m_xContext) ); +} + +void MenuBarManager::FillMenuImages(Reference< XFrame > const & _xFrame, Menu* _pMenu,bool bShowMenuImages) +{ + AddonsOptions aAddonOptions; + + for ( sal_uInt16 nPos = 0; nPos < _pMenu->GetItemCount(); nPos++ ) + { + sal_uInt16 nId = _pMenu->GetItemId( nPos ); + if ( _pMenu->GetItemType( nPos ) != MenuItemType::SEPARATOR ) + { + // overwrite the show icons on menu option? + MenuItemBits nBits = _pMenu->GetItemBits( nId ) & ( MenuItemBits::ICON | MenuItemBits::TEXT ); + bool bTmpShowMenuImages = ( bShowMenuImages && nBits != MenuItemBits::TEXT ) || nBits & MenuItemBits::ICON; + + if ( bTmpShowMenuImages ) + { + OUString aMenuItemCommand = _pMenu->GetItemCommand( nId ); + Image aImage = vcl::CommandInfoProvider::GetImageForCommand(aMenuItemCommand, _xFrame); + if ( !aImage ) + aImage = Image(aAddonOptions.GetImageFromURL(aMenuItemCommand, false)); + + _pMenu->SetItemImage( nId, aImage ); + } + else + _pMenu->SetItemImage( nId, Image() ); + } + } +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uielement/menubarmerger.cxx b/framework/source/uielement/menubarmerger.cxx new file mode 100644 index 0000000000..eebf61aa73 --- /dev/null +++ b/framework/source/uielement/menubarmerger.cxx @@ -0,0 +1,430 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include + +using namespace ::com::sun::star; + +const char SEPARATOR_STRING[] = "private:separator"; + +const char16_t MERGECOMMAND_ADDAFTER[] = u"AddAfter"; +const char16_t MERGECOMMAND_ADDBEFORE[] = u"AddBefore"; +const char16_t MERGECOMMAND_REPLACE[] = u"Replace"; +const char16_t MERGECOMMAND_REMOVE[] = u"Remove"; + +const char16_t MERGEFALLBACK_ADDPATH[] = u"AddPath"; +const char16_t MERGEFALLBACK_IGNORE[] = u"Ignore"; + +namespace framework +{ + +/** + Check whether a module identifier is part of a context + defined by a colon separated list of module identifier. + + @param + rContext + + Describes a context string list where all contexts + are delimited by a colon. For more information about + the module identifier used as context strings see the + IDL description of css::frame::XModuleManager + + @param + rModuleIdentifier + + A string describing a module identifier. See IDL + description of css::frame::XModuleManager. + +*/ +bool MenuBarMerger::IsCorrectContext( + std::u16string_view rContext, std::u16string_view rModuleIdentifier ) +{ + return ( rContext.empty() || ( rContext.find( rModuleIdentifier ) != std::u16string_view::npos )); +} + +void MenuBarMerger::RetrieveReferencePath( + std::u16string_view rReferencePathString, + ::std::vector< OUString >& rReferencePath ) +{ + const char aDelimiter = '\\'; + + rReferencePath.clear(); + sal_Int32 nIndex( 0 ); + do + { + OUString aToken( o3tl::getToken(rReferencePathString, 0, aDelimiter, nIndex ) ); + if ( !aToken.isEmpty() ) + rReferencePath.push_back( aToken ); + } + while ( nIndex >= 0 ); +} + +ReferencePathInfo MenuBarMerger::FindReferencePath( + const ::std::vector< OUString >& rReferencePath, + Menu* pMenu ) +{ + sal_uInt32 i( 0 ); + const sal_uInt32 nCount( rReferencePath.size() ); + + ReferencePathInfo aResult; + if ( !nCount ) + { + aResult.pPopupMenu = nullptr; + aResult.nPos = 0; + aResult.nLevel = -1; + aResult.eResult = RP_MENUITEM_NOT_FOUND; + return aResult; + } + + Menu* pCurrMenu( pMenu ); + RPResultInfo eResult( RP_OK ); + + sal_Int32 nLevel( - 1 ); + sal_uInt16 nPos( MENU_ITEM_NOTFOUND ); + do + { + ++nLevel; + OUString aCmd( rReferencePath[i] ); + + if ( i == nCount-1 ) + { + // Check last reference path element. Must be a leave (menu item). + sal_uInt16 nTmpPos = FindMenuItem( aCmd, pCurrMenu ); + if ( nTmpPos != MENU_ITEM_NOTFOUND ) + nPos = nTmpPos; + eResult = ( nTmpPos != MENU_ITEM_NOTFOUND ) ? RP_OK : RP_MENUITEM_NOT_FOUND; + } + else + { + // Check reference path element. Must be a node (popup menu)! + sal_uInt16 nTmpPos = FindMenuItem( aCmd, pCurrMenu ); + if ( nTmpPos != MENU_ITEM_NOTFOUND ) + { + sal_uInt16 nItemId = pCurrMenu->GetItemId( nTmpPos ); + Menu* pTmpMenu = pCurrMenu->GetPopupMenu( nItemId ); + if ( pTmpMenu != nullptr ) + pCurrMenu = pTmpMenu; + else + { + nPos = nTmpPos; + eResult = RP_MENUITEM_INSTEAD_OF_POPUPMENU_FOUND; + } + } + else + eResult = RP_POPUPMENU_NOT_FOUND; + } + i++; + } + while ((i < nCount) && (eResult == RP_OK)); + + aResult.pPopupMenu = pCurrMenu; + aResult.nPos = nPos; + aResult.nLevel = nLevel; + aResult.eResult = eResult; + + return aResult; +} + +sal_uInt16 MenuBarMerger::FindMenuItem( std::u16string_view rCmd, Menu const * pCurrMenu ) +{ + for ( sal_uInt16 i = 0; i < pCurrMenu->GetItemCount(); i++ ) + { + const sal_uInt16 nItemId = pCurrMenu->GetItemId( i ); + if ( nItemId > 0 ) + { + if ( rCmd == pCurrMenu->GetItemCommand( nItemId ) ) + return i; + } + } + + return MENU_ITEM_NOTFOUND; +} + +bool MenuBarMerger::CreateSubMenu( + Menu* pSubMenu, + sal_uInt16& nItemId, + const OUString& rModuleIdentifier, + const AddonMenuContainer& rAddonSubMenu ) +{ + const sal_uInt32 nSize = rAddonSubMenu.size(); + for ( sal_uInt32 i = 0; i < nSize; i++ ) + { + const AddonMenuItem& rMenuItem = rAddonSubMenu[i]; + + if ( IsCorrectContext( rMenuItem.aContext, rModuleIdentifier )) + { + if ( rMenuItem.aURL == SEPARATOR_STRING ) + { + pSubMenu->InsertSeparator(); + } + else + { + pSubMenu->InsertItem(nItemId, rMenuItem.aTitle); + pSubMenu->SetItemCommand( nItemId, rMenuItem.aURL ); + if ( !rMenuItem.aSubMenu.empty() ) + { + VclPtr pPopupMenu = VclPtr::Create(); + pSubMenu->SetPopupMenu( nItemId, pPopupMenu ); + ++nItemId; + + CreateSubMenu( pPopupMenu, nItemId, rModuleIdentifier, rMenuItem.aSubMenu ); + } + else + ++nItemId; + } + } + } + + return true; +} + +bool MenuBarMerger::MergeMenuItems( + Menu* pMenu, + sal_uInt16 nPos, + sal_uInt16 nModIndex, + sal_uInt16& nItemId, + const OUString& rModuleIdentifier, + const AddonMenuContainer& rAddonMenuItems ) +{ + sal_uInt16 nIndex( 0 ); + const sal_uInt32 nSize = rAddonMenuItems.size(); + for ( sal_uInt32 i = 0; i < nSize; i++ ) + { + const AddonMenuItem& rMenuItem = rAddonMenuItems[i]; + + if ( IsCorrectContext( rMenuItem.aContext, rModuleIdentifier )) + { + if ( rMenuItem.aURL == SEPARATOR_STRING ) + { + pMenu->InsertSeparator({}, nPos + nModIndex + nIndex); + } + else + { + pMenu->InsertItem(nItemId, rMenuItem.aTitle, MenuItemBits::NONE, {}, nPos + nModIndex + nIndex); + pMenu->SetItemCommand( nItemId, rMenuItem.aURL ); + if ( !rMenuItem.aSubMenu.empty() ) + { + VclPtr pSubMenu = VclPtr::Create(); + pMenu->SetPopupMenu( nItemId, pSubMenu ); + ++nItemId; + + CreateSubMenu( pSubMenu, nItemId, rModuleIdentifier, rMenuItem.aSubMenu ); + } + else + ++nItemId; + } + ++nIndex; + } + } + + return true; +} + +bool MenuBarMerger::ReplaceMenuItem( + Menu* pMenu, + sal_uInt16 nPos, + sal_uInt16& rItemId, + const OUString& rModuleIdentifier, + const AddonMenuContainer& rAddonMenuItems ) +{ + // There is no replace available. Therefore we first have to + // remove the old menu entry, + pMenu->RemoveItem( nPos ); + + return MergeMenuItems( pMenu, nPos, 0, rItemId, rModuleIdentifier, rAddonMenuItems ); +} + +bool MenuBarMerger::RemoveMenuItems( + Menu* pMenu, + sal_uInt16 nPos, + std::u16string_view rMergeCommandParameter ) +{ + const sal_uInt16 nParam( sal_uInt16( o3tl::toInt32(rMergeCommandParameter) )); + sal_uInt16 nCount = std::max( nParam, sal_uInt16(1) ); + + sal_uInt16 i = 0; + while (( nPos < pMenu->GetItemCount() ) && ( i < nCount )) + { + pMenu->RemoveItem( nPos ); + ++i; + } + + return true; +} + +bool MenuBarMerger::ProcessMergeOperation( + Menu* pMenu, + sal_uInt16 nPos, + sal_uInt16& nItemId, + std::u16string_view rMergeCommand, + std::u16string_view rMergeCommandParameter, + const OUString& rModuleIdentifier, + const AddonMenuContainer& rAddonMenuItems ) +{ + sal_uInt16 nModIndex( 0 ); + + if ( rMergeCommand == MERGECOMMAND_ADDBEFORE ) + { + nModIndex = 0; + return MergeMenuItems( pMenu, nPos, nModIndex, nItemId, rModuleIdentifier, rAddonMenuItems ); + } + else if ( rMergeCommand == MERGECOMMAND_ADDAFTER ) + { + nModIndex = 1; + return MergeMenuItems( pMenu, nPos, nModIndex, nItemId, rModuleIdentifier, rAddonMenuItems ); + } + else if ( rMergeCommand == MERGECOMMAND_REPLACE ) + { + return ReplaceMenuItem( pMenu, nPos, nItemId, rModuleIdentifier, rAddonMenuItems ); + } + else if ( rMergeCommand == MERGECOMMAND_REMOVE ) + { + return RemoveMenuItems( pMenu, nPos, rMergeCommandParameter ); + } + + return false; +} + +bool MenuBarMerger::ProcessFallbackOperation( + const ReferencePathInfo& aRefPathInfo, + sal_uInt16& rItemId, + std::u16string_view rMergeCommand, + std::u16string_view rMergeFallback, + const ::std::vector< OUString >& rReferencePath, + const std::u16string_view rModuleIdentifier, + const AddonMenuContainer& rAddonMenuItems ) +{ + if (( rMergeFallback == MERGEFALLBACK_IGNORE ) || + ( rMergeCommand == MERGECOMMAND_REPLACE ) || + ( rMergeCommand == MERGECOMMAND_REMOVE ) ) + { + return true; + } + else if ( rMergeFallback == MERGEFALLBACK_ADDPATH ) + { + Menu* pCurrMenu( aRefPathInfo.pPopupMenu ); + sal_Int32 nLevel( aRefPathInfo.nLevel ); + const sal_Int32 nSize( rReferencePath.size() ); + bool bFirstLevel( true ); + + while ( nLevel < nSize ) + { + if ( nLevel == nSize-1 ) + { + const sal_uInt32 nCount = rAddonMenuItems.size(); + for ( sal_uInt32 i = 0; i < nCount; ++i ) + { + const AddonMenuItem& rMenuItem = rAddonMenuItems[i]; + if ( IsCorrectContext( rMenuItem.aContext, rModuleIdentifier )) + { + if ( rMenuItem.aURL == SEPARATOR_STRING ) + pCurrMenu->InsertSeparator(); + else + { + pCurrMenu->InsertItem(rItemId, rMenuItem.aTitle); + pCurrMenu->SetItemCommand( rItemId, rMenuItem.aURL ); + ++rItemId; + } + } + } + } + else + { + const OUString aCmd( rReferencePath[nLevel] ); + + VclPtr pPopupMenu = VclPtr::Create(); + + if ( bFirstLevel && ( aRefPathInfo.eResult == RP_MENUITEM_INSTEAD_OF_POPUPMENU_FOUND )) + { + // special case: menu item without popup + sal_uInt16 nInsPos = aRefPathInfo.nPos; + sal_uInt16 nSetItemId = pCurrMenu->GetItemId( nInsPos ); + pCurrMenu->SetItemCommand( nSetItemId, aCmd ); + pCurrMenu->SetPopupMenu( nSetItemId, pPopupMenu ); + } + else + { + // normal case: insert a new item with popup + pCurrMenu->InsertItem(rItemId, OUString()); + pCurrMenu->SetItemCommand( rItemId, aCmd ); + pCurrMenu->SetPopupMenu( rItemId, pPopupMenu ); + } + + pCurrMenu = pPopupMenu; + ++rItemId; + bFirstLevel = false; + } + ++nLevel; + } + return true; + } + + return false; +} + +void MenuBarMerger::GetMenuEntry( + const uno::Sequence< beans::PropertyValue >& rAddonMenuEntry, + AddonMenuItem& rAddonMenuItem ) +{ + // Reset submenu member + rAddonMenuItem.aSubMenu.clear(); + + for ( const beans::PropertyValue& rProp : rAddonMenuEntry ) + { + OUString aMenuEntryPropName = rProp.Name; + if ( aMenuEntryPropName == ADDONSMENUITEM_STRING_URL ) + rProp.Value >>= rAddonMenuItem.aURL; + else if ( aMenuEntryPropName == ADDONSMENUITEM_STRING_TITLE ) + rProp.Value >>= rAddonMenuItem.aTitle; + else if ( aMenuEntryPropName == ADDONSMENUITEM_STRING_SUBMENU ) + { + uno::Sequence< uno::Sequence< beans::PropertyValue > > aSubMenu; + rProp.Value >>= aSubMenu; + GetSubMenu( aSubMenu, rAddonMenuItem.aSubMenu ); + } + else if ( aMenuEntryPropName == ADDONSMENUITEM_STRING_CONTEXT ) + rProp.Value >>= rAddonMenuItem.aContext; + } +} + +void MenuBarMerger::GetSubMenu( + const uno::Sequence< uno::Sequence< beans::PropertyValue > >& rSubMenuEntries, + AddonMenuContainer& rSubMenu ) +{ + rSubMenu.clear(); + + const sal_Int32 nCount = rSubMenuEntries.getLength(); + rSubMenu.reserve(rSubMenu.size() + nCount); + for ( sal_Int32 i = 0; i < nCount; i++ ) + { + const uno::Sequence< beans::PropertyValue >& rMenuEntry = rSubMenuEntries[ i ]; + + AddonMenuItem aMenuItem; + GetMenuEntry( rMenuEntry, aMenuItem ); + rSubMenu.push_back( aMenuItem ); + } +} + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uielement/menubarwrapper.cxx b/framework/source/uielement/menubarwrapper.cxx new file mode 100644 index 0000000000..24f4d738bf --- /dev/null +++ b/framework/source/uielement/menubarwrapper.cxx @@ -0,0 +1,285 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +using namespace com::sun::star; +using namespace com::sun::star::uno; +using namespace com::sun::star::beans; +using namespace com::sun::star::frame; +using namespace com::sun::star::lang; +using namespace com::sun::star::container; +using namespace com::sun::star::awt; +using namespace com::sun::star::util; +using namespace ::com::sun::star::ui; + +namespace framework +{ + +MenuBarWrapper::MenuBarWrapper( + css::uno::Reference< css::uno::XComponentContext > xContext + ) +: MenuBarWrapper_Base( UIElementType::MENUBAR ), + m_bRefreshPopupControllerCache( true ), + m_xContext(std::move( xContext )) +{ +} + +MenuBarWrapper::~MenuBarWrapper() +{ +} + +void SAL_CALL MenuBarWrapper::dispose() +{ + Reference< XComponent > xThis(this); + + css::lang::EventObject aEvent( xThis ); + m_aListenerContainer.disposeAndClear( aEvent ); + + SolarMutexGuard g; + + m_xMenuBarManager->dispose(); + m_xMenuBarManager.clear(); + m_xConfigSource.clear(); + m_xConfigData.clear(); + + m_xMenuBar.clear(); + m_bDisposed = true; +} + +// XInitialization +void SAL_CALL MenuBarWrapper::initialize( const Sequence< Any >& aArguments ) +{ + SolarMutexGuard g; + + if ( m_bDisposed ) + throw DisposedException(); + + if ( m_bInitialized ) + return; + + OUString aModuleIdentifier; + UIConfigElementWrapperBase::initialize( aArguments ); + + Reference< XFrame > xFrame( m_xWeakFrame ); + if ( !(xFrame.is() && m_xConfigSource.is()) ) + return; + + // Create VCL menubar which will be filled with settings data + VclPtr pVCLMenuBar; + { + SolarMutexGuard aSolarMutexGuard; + pVCLMenuBar = VclPtr::Create(); + } + + Reference< XModuleManager2 > xModuleManager = ModuleManager::create( m_xContext ); + + try + { + aModuleIdentifier = xModuleManager->identify( xFrame ); + } + catch( const Exception& ) + { + } + + Reference< XURLTransformer > xTrans; + try + { + xTrans.set( URLTransformer::create(m_xContext) ); + m_xConfigData = m_xConfigSource->getSettings( m_aResourceURL, false ); + if ( m_xConfigData.is() ) + { + // Fill menubar with container contents + sal_uInt16 nId = 1; + MenuBarManager::FillMenuWithConfiguration( nId, pVCLMenuBar, aModuleIdentifier, m_xConfigData, xTrans ); + } + } + catch ( const NoSuchElementException& ) + { + } + + bool bMenuOnly( false ); + for ( const Any& rArg : aArguments ) + { + PropertyValue aPropValue; + if ( rArg >>= aPropValue ) + { + if ( aPropValue.Name == "MenuOnly" ) + aPropValue.Value >>= bMenuOnly; + } + } + + if ( !bMenuOnly ) + { + // Initialize menubar manager with our vcl menu bar. There are some situations where we only want to get the menu without any + // interaction which is done by the menu bar manager. This must be requested by a special property called "MenuOnly". Be careful + // a menu bar created with this property is not fully supported. It must be attached to a real menu bar manager to have full + // support. This feature is currently used for "Inplace editing"! + Reference< XDispatchProvider > xDispatchProvider; + + m_xMenuBarManager = new MenuBarManager( m_xContext, + xFrame, + xTrans, + xDispatchProvider, + aModuleIdentifier, + pVCLMenuBar, + false ); + } + + // Initialize toolkit menu bar implementation to have awt::XMenuBar for data exchange. + // Don't use this toolkit menu bar or one of its functions. It is only used as a data container! + m_xMenuBar = new VCLXMenuBar( pVCLMenuBar ); +} + +// XUIElementSettings +void SAL_CALL MenuBarWrapper::updateSettings() +{ + SolarMutexGuard g; + + if ( m_bDisposed ) + throw DisposedException(); + + if ( !m_xMenuBarManager.is() ) + return; + + if ( m_xConfigSource.is() && m_bPersistent ) + { + try + { + m_xConfigData = m_xConfigSource->getSettings( m_aResourceURL, false ); + if ( m_xConfigData.is() ) + m_xMenuBarManager->SetItemContainer( m_xConfigData ); + } + catch ( const NoSuchElementException& ) + { + } + } + else if ( !m_bPersistent ) + { + // Transient menubar: do nothing + } +} +void MenuBarWrapper::impl_fillNewData() +{ + // Transient menubar => Fill menubar with new data + if ( m_xMenuBarManager ) + m_xMenuBarManager->SetItemContainer( m_xConfigData ); +} + +void MenuBarWrapper::fillPopupControllerCache() +{ + if ( m_bRefreshPopupControllerCache ) + { + if ( m_xMenuBarManager ) + m_xMenuBarManager->GetPopupController( m_aPopupControllerCache ); + if ( !m_aPopupControllerCache.empty() ) + m_bRefreshPopupControllerCache = false; + } +} + +// XElementAccess +Type SAL_CALL MenuBarWrapper::getElementType() +{ + return cppu::UnoType::get(); +} + +sal_Bool SAL_CALL MenuBarWrapper::hasElements() +{ + SolarMutexGuard g; + + if ( m_bDisposed ) + throw DisposedException(); + + fillPopupControllerCache(); + return ( !m_aPopupControllerCache.empty() ); +} + +// XNameAccess +Any SAL_CALL MenuBarWrapper::getByName( + const OUString& aName ) +{ + SolarMutexGuard g; + + if ( m_bDisposed ) + throw DisposedException(); + + fillPopupControllerCache(); + + PopupControllerCache::const_iterator pIter = m_aPopupControllerCache.find( aName ); + if ( pIter == m_aPopupControllerCache.end() ) + throw container::NoSuchElementException(); + + uno::Reference< frame::XDispatchProvider > xDispatchProvider = pIter->second.m_xDispatchProvider; + return uno::Any( xDispatchProvider ); +} + +Sequence< OUString > SAL_CALL MenuBarWrapper::getElementNames() +{ + SolarMutexGuard g; + + if ( m_bDisposed ) + throw DisposedException(); + + fillPopupControllerCache(); + + return comphelper::mapKeysToSequence( m_aPopupControllerCache ); +} + +sal_Bool SAL_CALL MenuBarWrapper::hasByName( + const OUString& aName ) +{ + SolarMutexGuard g; + + if ( m_bDisposed ) + throw DisposedException(); + + fillPopupControllerCache(); + + PopupControllerCache::const_iterator pIter = m_aPopupControllerCache.find( aName ); + if ( pIter != m_aPopupControllerCache.end() ) + return true; + else + return false; +} + +// XUIElement +Reference< XInterface > SAL_CALL MenuBarWrapper::getRealInterface() +{ + if ( m_bDisposed ) + throw DisposedException(); + + return Reference< XInterface >( static_cast(m_xMenuBarManager.get()), UNO_QUERY ); +} + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uielement/newmenucontroller.cxx b/framework/source/uielement/newmenucontroller.cxx new file mode 100644 index 0000000000..fb133540c3 --- /dev/null +++ b/framework/source/uielement/newmenucontroller.cxx @@ -0,0 +1,466 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Defines +constexpr OUString aSlotNewDocDirect = u".uno:AddDirect"_ustr; +constexpr OUStringLiteral aSlotAutoPilot = u".uno:AutoPilotMenu"; + +using namespace com::sun::star::uno; +using namespace com::sun::star::lang; +using namespace com::sun::star::frame; +using namespace com::sun::star::beans; +using namespace com::sun::star::util; +using namespace com::sun::star::container; +using namespace com::sun::star::ui; + +namespace framework +{ + +OUString SAL_CALL NewMenuController::getImplementationName() +{ + return "com.sun.star.comp.framework.NewMenuController"; +} + +sal_Bool SAL_CALL NewMenuController::supportsService( const OUString& sServiceName ) +{ + return cppu::supportsService(this, sServiceName); +} + +css::uno::Sequence< OUString > SAL_CALL NewMenuController::getSupportedServiceNames() +{ + return { SERVICENAME_POPUPMENUCONTROLLER }; +} + +void NewMenuController::setMenuImages( PopupMenu* pPopupMenu, bool bSetImages ) +{ + sal_uInt16 nItemCount = pPopupMenu->GetItemCount(); + Reference< XFrame > xFrame( m_xFrame ); + + for ( sal_uInt16 i = 0; i < nItemCount; i++ ) + { + sal_uInt16 nItemId = pPopupMenu->GetItemId( i ); + if ( nItemId != 0 ) + { + if ( bSetImages ) + { + OUString aImageId; + OUString aCmd( pPopupMenu->GetItemCommand( nItemId ) ); + void* nAttributePtr = pPopupMenu->GetUserValue( nItemId ); + MenuAttributes* pAttributes = static_cast(nAttributePtr); + if (pAttributes) + aImageId = pAttributes->aImageId; + + INetURLObject aURLObj( aImageId.isEmpty() ? aCmd : aImageId ); + Image aImage = SvFileInformationManager::GetImageNoDefault( aURLObj ); + if ( !aImage ) + aImage = vcl::CommandInfoProvider::GetImageForCommand(aCmd, xFrame); + + if ( !!aImage ) + pPopupMenu->SetItemImage( nItemId, aImage ); + } + else + pPopupMenu->SetItemImage( nItemId, Image() ); + } + } +} + +void NewMenuController::determineAndSetNewDocAccel(const css::awt::KeyEvent& rKeyCode) +{ + sal_uInt16 nCount(m_xPopupMenu->getItemCount()); + sal_uInt16 nId( 0 ); + OUString aCommand; + + if ( !m_aEmptyDocURL.isEmpty() ) + { + // Search for the empty document URL + + for ( sal_uInt16 i = 0; i < nCount; i++ ) + { + if (m_xPopupMenu->getItemType(i) != css::awt::MenuItemType_SEPARATOR) + { + nId = m_xPopupMenu->getItemId(i); + aCommand = m_xPopupMenu->getCommand(nId); + if ( aCommand.startsWith( m_aEmptyDocURL ) ) + { + m_xPopupMenu->setAcceleratorKeyEvent(nId, rKeyCode); + break; + } + } + } + } +} + +void NewMenuController::setAccelerators() +{ + if ( !m_bModuleIdentified ) + return; + + Reference< XAcceleratorConfiguration > xDocAccelCfg( m_xDocAcceleratorManager ); + Reference< XAcceleratorConfiguration > xModuleAccelCfg( m_xModuleAcceleratorManager ); + Reference< XAcceleratorConfiguration > xGlobalAccelCfg( m_xGlobalAcceleratorManager ); + + if ( !m_bAcceleratorCfg ) + { + // Retrieve references on demand + m_bAcceleratorCfg = true; + if ( !xDocAccelCfg.is() ) + { + Reference< XController > xController = m_xFrame->getController(); + Reference< XModel > xModel; + if ( xController.is() ) + { + xModel = xController->getModel(); + if ( xModel.is() ) + { + Reference< XUIConfigurationManagerSupplier > xSupplier( xModel, UNO_QUERY ); + if ( xSupplier.is() ) + { + Reference< XUIConfigurationManager > xDocUICfgMgr = xSupplier->getUIConfigurationManager(); + if ( xDocUICfgMgr.is() ) + { + xDocAccelCfg = xDocUICfgMgr->getShortCutManager(); + m_xDocAcceleratorManager = xDocAccelCfg; + } + } + } + } + } + + if ( !xModuleAccelCfg.is() ) + { + Reference< XModuleUIConfigurationManagerSupplier > xModuleCfgMgrSupplier = + theModuleUIConfigurationManagerSupplier::get( m_xContext ); + Reference< XUIConfigurationManager > xUICfgMgr = xModuleCfgMgrSupplier->getUIConfigurationManager( m_aModuleIdentifier ); + if ( xUICfgMgr.is() ) + { + xModuleAccelCfg = xUICfgMgr->getShortCutManager(); + m_xModuleAcceleratorManager = xModuleAccelCfg; + } + } + + if ( !xGlobalAccelCfg.is() ) + { + xGlobalAccelCfg = GlobalAcceleratorConfiguration::create( m_xContext ); + m_xGlobalAcceleratorManager = xGlobalAccelCfg; + } + } + + vcl::KeyCode aEmptyKeyCode; + sal_uInt16 nItemCount(m_xPopupMenu->getItemCount()); + std::vector< vcl::KeyCode > aMenuShortCuts; + std::vector< OUString > aCmds; + std::vector< sal_uInt16 > aIds; + for ( sal_uInt16 i = 0; i < nItemCount; i++ ) + { + if (m_xPopupMenu->getItemType(i) != css::awt::MenuItemType_SEPARATOR) + { + sal_uInt16 nId(m_xPopupMenu->getItemId(i)); + aIds.push_back( nId ); + aMenuShortCuts.push_back( aEmptyKeyCode ); + aCmds.push_back(m_xPopupMenu->getCommand(nId)); + } + } + + sal_uInt32 nSeqCount( aIds.size() ); + + if ( m_bNewMenu ) + nSeqCount+=1; + + Sequence< OUString > aSeq( nSeqCount ); + auto aSeqRange = asNonConstRange(aSeq); + + // Add a special command for our "New" menu. + if ( m_bNewMenu ) + { + aSeqRange[nSeqCount-1] = m_aCommandURL; + aMenuShortCuts.push_back( aEmptyKeyCode ); + } + + const sal_uInt32 nCount = aCmds.size(); + for ( sal_uInt32 i = 0; i < nCount; i++ ) + aSeqRange[i] = aCmds[i]; + + if ( m_xGlobalAcceleratorManager.is() ) + retrieveShortcutsFromConfiguration( xGlobalAccelCfg, aSeq, aMenuShortCuts ); + if ( m_xModuleAcceleratorManager.is() ) + retrieveShortcutsFromConfiguration( xModuleAccelCfg, aSeq, aMenuShortCuts ); + if ( m_xDocAcceleratorManager.is() ) + retrieveShortcutsFromConfiguration( xDocAccelCfg, aSeq, aMenuShortCuts ); + + const sal_uInt32 nCount2 = aIds.size(); + for ( sal_uInt32 i = 0; i < nCount2; i++ ) + m_xPopupMenu->setAcceleratorKeyEvent(aIds[i], svt::AcceleratorExecute::st_VCLKey2AWTKey(aMenuShortCuts[i])); + + // Special handling for "New" menu short-cut should be set at the + // document which will be opened using it. + if ( m_bNewMenu ) + { + if ( aMenuShortCuts[nSeqCount-1] != aEmptyKeyCode ) + determineAndSetNewDocAccel(svt::AcceleratorExecute::st_VCLKey2AWTKey(aMenuShortCuts[nSeqCount-1])); + } +} + +void NewMenuController::retrieveShortcutsFromConfiguration( + const Reference< XAcceleratorConfiguration >& rAccelCfg, + const Sequence< OUString >& rCommands, + std::vector< vcl::KeyCode >& aMenuShortCuts ) +{ + if ( !rAccelCfg.is() ) + return; + + try + { + css::awt::KeyEvent aKeyEvent; + Sequence< Any > aSeqKeyCode = rAccelCfg->getPreferredKeyEventsForCommandList( rCommands ); + for ( sal_Int32 i = 0; i < aSeqKeyCode.getLength(); i++ ) + { + if ( aSeqKeyCode[i] >>= aKeyEvent ) + aMenuShortCuts[i] = svt::AcceleratorExecute::st_AWTKey2VCLKey( aKeyEvent ); + } + } + catch ( const IllegalArgumentException& ) + { + } +} + +NewMenuController::NewMenuController( const css::uno::Reference< css::uno::XComponentContext >& xContext ) : + svt::PopupMenuControllerBase( xContext ), + m_bShowImages( true ), + m_bNewMenu( false ), + m_bModuleIdentified( false ), + m_bAcceleratorCfg( false ), + m_aTargetFrame( "_default" ), + m_xContext( xContext ) +{ +} + +NewMenuController::~NewMenuController() +{ +} + +// private function +void NewMenuController::fillPopupMenu( Reference< css::awt::XPopupMenu > const & rPopupMenu ) +{ + VCLXPopupMenu* pPopupMenu = static_cast(dynamic_cast( rPopupMenu.get() )); + PopupMenu* pVCLPopupMenu = nullptr; + + SolarMutexGuard aSolarMutexGuard; + + resetPopupMenu( rPopupMenu ); + if ( pPopupMenu ) + pVCLPopupMenu = static_cast(pPopupMenu->GetMenu()); + + if ( !pVCLPopupMenu ) + return; + + Reference< XDispatchProvider > xDispatchProvider( m_xFrame, UNO_QUERY ); + URL aTargetURL; + aTargetURL.Complete = m_bNewMenu ? aSlotNewDocDirect : OUString(aSlotAutoPilot); + m_xURLTransformer->parseStrict( aTargetURL ); + Reference< XDispatch > xMenuItemDispatch = xDispatchProvider->queryDispatch( aTargetURL, OUString(), 0 ); + if(xMenuItemDispatch == nullptr) + return; + + const std::vector< SvtDynMenuEntry > aDynamicMenuEntries = + SvtDynamicMenuOptions::GetMenu( m_bNewMenu ? EDynamicMenuType::NewMenu : EDynamicMenuType::WizardMenu ); + + sal_uInt16 nItemId = 1; + + for ( const auto& aDynamicMenuEntry : aDynamicMenuEntries ) + { + if ( aDynamicMenuEntry.sTitle.isEmpty() && aDynamicMenuEntry.sURL.isEmpty() ) + continue; + + if ( aDynamicMenuEntry.sURL == "private:separator" ) + rPopupMenu->insertSeparator(-1); + else + { + rPopupMenu->insertItem(nItemId, aDynamicMenuEntry.sTitle, 0, -1); + rPopupMenu->setCommand(nItemId, aDynamicMenuEntry.sURL); + + void* nAttributePtr = MenuAttributes::CreateAttribute( aDynamicMenuEntry.sTargetName, aDynamicMenuEntry.sImageIdentifier ); + pPopupMenu->setUserValue(nItemId, nAttributePtr, MenuAttributes::ReleaseAttribute); + + nItemId++; + } + } + + if ( m_bShowImages ) + setMenuImages( pVCLPopupMenu, m_bShowImages ); +} + +// XEventListener +void SAL_CALL NewMenuController::disposing( const EventObject& ) +{ + Reference< css::awt::XMenuListener > xHolder(this); + + std::unique_lock aLock( m_aMutex ); + m_xFrame.clear(); + m_xDispatch.clear(); + m_xContext.clear(); + + if ( m_xPopupMenu.is() ) + m_xPopupMenu->removeMenuListener( Reference< css::awt::XMenuListener >(this) ); + m_xPopupMenu.clear(); +} + +// XStatusListener +void SAL_CALL NewMenuController::statusChanged( const FeatureStateEvent& Event ) +{ + Event.State >>= m_aEmptyDocURL; +} + +// XMenuListener +void SAL_CALL NewMenuController::itemSelected( const css::awt::MenuEvent& rEvent ) +{ + Reference< css::awt::XPopupMenu > xPopupMenu; + Reference< XComponentContext > xContext; + + { + std::unique_lock aLock(m_aMutex); + xPopupMenu = m_xPopupMenu; + xContext = m_xContext; + } + + if ( !xPopupMenu.is() ) + return; + + VCLXPopupMenu* pPopupMenu = static_cast(dynamic_cast( xPopupMenu.get() )); + if ( !pPopupMenu ) + return; + + OUString aURL; + OUString aTargetFrame( m_aTargetFrame ); + + { + SolarMutexGuard aSolarMutexGuard; + aURL = pPopupMenu->getCommand(rEvent.MenuId); + void* nAttributePtr = pPopupMenu->getUserValue(rEvent.MenuId); + MenuAttributes* pAttributes = static_cast(nAttributePtr); + if (pAttributes) + aTargetFrame = pAttributes->aTargetFrame; + } + + Sequence< PropertyValue > aArgsList{ comphelper::makePropertyValue("Referer", + OUString( "private:user" )) }; + + dispatchCommand( aURL, aArgsList, aTargetFrame ); +} + +void SAL_CALL NewMenuController::itemActivated( const css::awt::MenuEvent& ) +{ + SolarMutexGuard aSolarMutexGuard; + if ( !(m_xFrame.is() && m_xPopupMenu.is()) ) + return; + + const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings(); + bool bShowImages( rSettings.GetUseImagesInMenus() ); + OUString aIconTheme( rSettings.DetermineIconTheme() ); + + PopupMenu* pVCLPopupMenu = static_cast(m_xPopupMenu->GetMenu()); + + if ( m_bShowImages != bShowImages || m_aIconTheme != aIconTheme ) + { + m_bShowImages = bShowImages; + m_aIconTheme = aIconTheme; + setMenuImages( pVCLPopupMenu, m_bShowImages ); + } + + setAccelerators(); +} + +// XPopupMenuController +void NewMenuController::impl_setPopupMenu() +{ + + if ( m_xPopupMenu.is() ) + fillPopupMenu( m_xPopupMenu ); + + // Identify module that we are attach to. It's our context that we need to know. + Reference< XModuleManager2 > xModuleManager = ModuleManager::create( m_xContext ); + try + { + m_aModuleIdentifier = xModuleManager->identify( m_xFrame ); + m_bModuleIdentified = true; + } + catch ( const RuntimeException& ) + { + throw; + } + catch ( const Exception& ) + { + } +} + +// XInitialization +void NewMenuController::initializeImpl( std::unique_lock& rGuard, const Sequence< Any >& aArguments ) +{ + bool bInitialized( m_bInitialized ); + if ( bInitialized ) + return; + + svt::PopupMenuControllerBase::initializeImpl( rGuard, aArguments ); + + if ( m_bInitialized ) + { + const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings(); + + m_bShowImages = rSettings.GetUseImagesInMenus(); + m_aIconTheme = rSettings.DetermineIconTheme(); + m_bNewMenu = m_aCommandURL == aSlotNewDocDirect; + } +} + +} + + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +framework_NewMenuController_get_implementation( + css::uno::XComponentContext* context, css::uno::Sequence const& ) +{ + return cppu::acquire(new framework::NewMenuController(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uielement/objectmenucontroller.cxx b/framework/source/uielement/objectmenucontroller.cxx new file mode 100644 index 0000000000..9c86a09133 --- /dev/null +++ b/framework/source/uielement/objectmenucontroller.cxx @@ -0,0 +1,137 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +using namespace com::sun::star::uno; +using namespace com::sun::star::lang; +using namespace com::sun::star::frame; +using namespace com::sun::star::beans; +using namespace com::sun::star::util; +using namespace framework; + +namespace { + +class ObjectMenuController : public svt::PopupMenuControllerBase +{ + using svt::PopupMenuControllerBase::disposing; + +public: + explicit ObjectMenuController( const css::uno::Reference< css::uno::XComponentContext >& xContext ); + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override + { + return "com.sun.star.comp.framework.ObjectMenuController"; + } + + virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override + { + return cppu::supportsService(this, ServiceName); + } + + virtual css::uno::Sequence SAL_CALL getSupportedServiceNames() override + { + return {"com.sun.star.frame.PopupMenuController"}; + } + + // XStatusListener + virtual void SAL_CALL statusChanged( const css::frame::FeatureStateEvent& Event ) override; + + // XEventListener + virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override; + +private: + void fillPopupMenu( const css::uno::Sequence< css::embed::VerbDescriptor >& rVerbCommandSeq, css::uno::Reference< css::awt::XPopupMenu > const & rPopupMenu ); +}; + +ObjectMenuController::ObjectMenuController( const css::uno::Reference< css::uno::XComponentContext >& xContext ) : + svt::PopupMenuControllerBase( xContext ) +{ +} + +// private function +void ObjectMenuController::fillPopupMenu( const Sequence< css::embed::VerbDescriptor >& rVerbCommandSeq, Reference< css::awt::XPopupMenu > const & rPopupMenu ) +{ + const css::embed::VerbDescriptor* pVerbCommandArray = rVerbCommandSeq.getConstArray(); + + SolarMutexGuard aSolarMutexGuard; + + resetPopupMenu( rPopupMenu ); + + static constexpr OUStringLiteral aVerbCommand( u".uno:ObjectMenue?VerbID:short=" ); + for ( sal_Int32 i = 0; i < rVerbCommandSeq.getLength(); i++ ) + { + const css::embed::VerbDescriptor& rVerb = pVerbCommandArray[i]; + if ( rVerb.VerbAttributes & css::embed::VerbAttributes::MS_VERBATTR_ONCONTAINERMENU ) + { + m_xPopupMenu->insertItem( i+1, rVerb.VerbName, 0, i ); + OUString aCommand = aVerbCommand + OUString::number( rVerb.VerbID ); + m_xPopupMenu->setCommand( i+1, aCommand ); // Store verb command + } + } +} + +// XEventListener +void SAL_CALL ObjectMenuController::disposing( const EventObject& ) +{ + Reference< css::awt::XMenuListener > xHolder(this); + + std::unique_lock aLock( m_aMutex ); + m_xFrame.clear(); + m_xDispatch.clear(); + + if ( m_xPopupMenu.is() ) + m_xPopupMenu->removeMenuListener( Reference< css::awt::XMenuListener >(this) ); + m_xPopupMenu.clear(); +} + +// XStatusListener +void SAL_CALL ObjectMenuController::statusChanged( const FeatureStateEvent& Event ) +{ + Sequence < css::embed::VerbDescriptor > aVerbCommandSeq; + if ( Event.State >>= aVerbCommandSeq ) + { + std::unique_lock aLock( m_aMutex ); + if ( m_xPopupMenu.is() ) + fillPopupMenu( aVerbCommandSeq, m_xPopupMenu ); + } +} + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_framework_ObjectMenuController_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence const &) +{ + return cppu::acquire(new ObjectMenuController(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uielement/popuptoolbarcontroller.cxx b/framework/source/uielement/popuptoolbarcontroller.cxx new file mode 100644 index 0000000000..b17e8a6bfc --- /dev/null +++ b/framework/source/uielement/popuptoolbarcontroller.cxx @@ -0,0 +1,794 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace framework; + +namespace +{ + +typedef cppu::ImplInheritanceHelper< svt::ToolboxController, + css::lang::XServiceInfo > + ToolBarBase; + +class PopupMenuToolbarController : public ToolBarBase +{ +public: + // XComponent + virtual void SAL_CALL dispose() override; + // XInitialization + virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override; + // XToolbarController + virtual css::uno::Reference< css::awt::XWindow > SAL_CALL createPopupWindow() override; + // XStatusListener + virtual void SAL_CALL statusChanged( const css::frame::FeatureStateEvent& rEvent ) override; + +protected: + PopupMenuToolbarController( const css::uno::Reference< css::uno::XComponentContext >& rxContext, + OUString aPopupCommand = OUString() ); + virtual void functionExecuted( const OUString &rCommand ); + virtual ToolBoxItemBits getDropDownStyle() const; + void createPopupMenuController(); + + bool m_bHasController; + bool m_bResourceURL; + OUString m_aPopupCommand; + rtl::Reference< VCLXPopupMenu > m_xPopupMenu; + +private: + css::uno::Reference< css::frame::XUIControllerFactory > m_xPopupMenuFactory; + css::uno::Reference< css::frame::XPopupMenuController > m_xPopupMenuController; +}; + +PopupMenuToolbarController::PopupMenuToolbarController( + const css::uno::Reference< css::uno::XComponentContext >& xContext, + OUString aPopupCommand ) + : ToolBarBase( xContext, css::uno::Reference< css::frame::XFrame >(), /*aCommandURL*/OUString() ) + , m_bHasController( false ) + , m_bResourceURL( false ) + , m_aPopupCommand(std::move( aPopupCommand )) +{ +} + +void SAL_CALL PopupMenuToolbarController::dispose() +{ + svt::ToolboxController::dispose(); + + osl::MutexGuard aGuard( m_aMutex ); + if( m_xPopupMenuController.is() ) + { + css::uno::Reference< css::lang::XComponent > xComponent( + m_xPopupMenuController, css::uno::UNO_QUERY ); + if( xComponent.is() ) + { + try + { + xComponent->dispose(); + } + catch (...) + {} + } + m_xPopupMenuController.clear(); + } + + m_xContext.clear(); + m_xPopupMenuFactory.clear(); + m_xPopupMenu.clear(); +} + +void SAL_CALL PopupMenuToolbarController::initialize( + const css::uno::Sequence< css::uno::Any >& aArguments ) +{ + ToolboxController::initialize( aArguments ); + + osl::MutexGuard aGuard( m_aMutex ); + if ( !m_aPopupCommand.getLength() ) + m_aPopupCommand = m_aCommandURL; + + try + { + m_xPopupMenuFactory.set( + css::frame::thePopupMenuControllerFactory::get( m_xContext ) ); + m_bHasController = m_xPopupMenuFactory->hasController( + m_aPopupCommand, getModuleName() ); + } + catch (const css::uno::Exception&) + { + TOOLS_INFO_EXCEPTION( "fwk.uielement", "" ); + } + + if ( !m_bHasController && m_aPopupCommand.startsWith( "private:resource/" ) ) + { + m_bResourceURL = true; + m_bHasController = true; + } + + SolarMutexGuard aSolarLock; + ToolBox* pToolBox = nullptr; + ToolBoxItemId nItemId; + if ( getToolboxId( nItemId, &pToolBox ) ) + { + ToolBoxItemBits nCurStyle( pToolBox->GetItemBits( nItemId ) ); + ToolBoxItemBits nSetStyle( getDropDownStyle() ); + pToolBox->SetItemBits( nItemId, + m_bHasController ? + nCurStyle | nSetStyle : + nCurStyle & ~nSetStyle ); + } + +} + +void SAL_CALL PopupMenuToolbarController::statusChanged( const css::frame::FeatureStateEvent& rEvent ) +{ + if ( m_bResourceURL ) + return; + + ToolBox* pToolBox = nullptr; + ToolBoxItemId nItemId; + if ( getToolboxId( nItemId, &pToolBox ) ) + { + SolarMutexGuard aSolarLock; + pToolBox->EnableItem( nItemId, rEvent.IsEnabled ); + bool bValue; + if ( rEvent.State >>= bValue ) + pToolBox->CheckItem( nItemId, bValue ); + } +} + +css::uno::Reference< css::awt::XWindow > SAL_CALL +PopupMenuToolbarController::createPopupWindow() +{ + css::uno::Reference< css::awt::XWindow > xRet; + + osl::MutexGuard aGuard( m_aMutex ); + if ( !m_bHasController ) + return xRet; + + createPopupMenuController(); + + SolarMutexGuard aSolarLock; + VclPtr< ToolBox > pToolBox = static_cast< ToolBox* >( VCLUnoHelper::GetWindow( getParent() ) ); + if ( !pToolBox ) + return xRet; + + pToolBox->SetItemDown( m_nToolBoxId, true ); + WindowAlign eAlign( pToolBox->GetAlign() ); + + // If the parent ToolBox is in popup mode (e.g. sub toolbar, overflow popup), + // its ToolBarManager can be disposed along with our controller, destroying + // m_xPopupMenu, while the latter still in execute. This should be fixed at a + // different level, for now just hold it here so it won't crash. + css::uno::Reference< css::awt::XPopupMenu > xPopupMenu ( m_xPopupMenu ); + sal_uInt16 nId = xPopupMenu->execute( + css::uno::Reference< css::awt::XWindowPeer >( getParent(), css::uno::UNO_QUERY ), + VCLUnoHelper::ConvertToAWTRect( pToolBox->GetItemRect( m_nToolBoxId ) ), + ( eAlign == WindowAlign::Top || eAlign == WindowAlign::Bottom ) ? + css::awt::PopupMenuDirection::EXECUTE_DOWN : + css::awt::PopupMenuDirection::EXECUTE_RIGHT ); + pToolBox->SetItemDown( m_nToolBoxId, false ); + + if ( nId ) + functionExecuted( xPopupMenu->getCommand( nId ) ); + + return xRet; +} + +void PopupMenuToolbarController::functionExecuted( const OUString &/*rCommand*/) +{ +} + +ToolBoxItemBits PopupMenuToolbarController::getDropDownStyle() const +{ + return ToolBoxItemBits::DROPDOWN; +} + +void PopupMenuToolbarController::createPopupMenuController() +{ + if( !m_bHasController ) + return; + + if ( m_xPopupMenuController.is() ) + { + m_xPopupMenuController->updatePopupMenu(); + } + else + { + css::uno::Sequence aArgs { + css::uno::Any(comphelper::makePropertyValue("Frame", m_xFrame)), + css::uno::Any(comphelper::makePropertyValue("ModuleIdentifier", m_sModuleName)), + css::uno::Any(comphelper::makePropertyValue("InToolbar", true)) + }; + + try + { + m_xPopupMenu = new VCLXPopupMenu(); + + if (m_bResourceURL) + { + sal_Int32 nAppendIndex = aArgs.getLength(); + aArgs.realloc(nAppendIndex + 1); + aArgs.getArray()[nAppendIndex] <<= comphelper::makePropertyValue("ResourceURL", m_aPopupCommand); + + m_xPopupMenuController.set( m_xContext->getServiceManager()->createInstanceWithArgumentsAndContext( + "com.sun.star.comp.framework.ResourceMenuController", aArgs, m_xContext), css::uno::UNO_QUERY_THROW ); + } + else + { + m_xPopupMenuController.set( m_xPopupMenuFactory->createInstanceWithArgumentsAndContext( + m_aPopupCommand, aArgs, m_xContext), css::uno::UNO_QUERY_THROW ); + } + + m_xPopupMenuController->setPopupMenu( m_xPopupMenu ); + } + catch ( const css::uno::Exception & ) + { + TOOLS_INFO_EXCEPTION( "fwk.uielement", "" ); + m_xPopupMenu.clear(); + } + } +} + +class GenericPopupToolbarController : public PopupMenuToolbarController +{ +public: + GenericPopupToolbarController( const css::uno::Reference< css::uno::XComponentContext >& rxContext, + const css::uno::Sequence< css::uno::Any >& rxArgs ); + + // XInitialization + virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& rxArgs ) override; + + // XStatusListener + virtual void SAL_CALL statusChanged( const css::frame::FeatureStateEvent& rEvent ) override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + + virtual sal_Bool SAL_CALL supportsService(OUString const & rServiceName) override; + + virtual css::uno::Sequence SAL_CALL getSupportedServiceNames() override; + +private: + bool m_bSplitButton, m_bReplaceWithLast; + void functionExecuted(const OUString &rCommand) override; + ToolBoxItemBits getDropDownStyle() const override; +}; + +GenericPopupToolbarController::GenericPopupToolbarController( + const css::uno::Reference< css::uno::XComponentContext >& xContext, + const css::uno::Sequence< css::uno::Any >& rxArgs ) + : PopupMenuToolbarController( xContext ) + , m_bReplaceWithLast( false ) +{ + css::beans::PropertyValue aPropValue; + for ( const auto& arg: rxArgs ) + { + if ( ( arg >>= aPropValue ) && aPropValue.Name == "Value" ) + { + sal_Int32 nIdx{ 0 }; + OUString aValue; + aPropValue.Value >>= aValue; + m_aPopupCommand = aValue.getToken(0, ';', nIdx); + m_bReplaceWithLast = aValue.getToken(0, ';', nIdx).toBoolean(); + break; + } + } + m_bSplitButton = m_bReplaceWithLast || !m_aPopupCommand.isEmpty(); +} + +OUString GenericPopupToolbarController::getImplementationName() +{ + return "com.sun.star.comp.framework.GenericPopupToolbarController"; +} + +sal_Bool GenericPopupToolbarController::supportsService(OUString const & rServiceName) +{ + return cppu::supportsService( this, rServiceName ); +} + +css::uno::Sequence GenericPopupToolbarController::getSupportedServiceNames() +{ + return {"com.sun.star.frame.ToolbarController"}; +} + +void GenericPopupToolbarController::initialize( const css::uno::Sequence< css::uno::Any >& rxArgs ) +{ + PopupMenuToolbarController::initialize( rxArgs ); + if ( m_bReplaceWithLast ) + // Create early, so we can use the menu is statusChanged method. + createPopupMenuController(); +} + +void GenericPopupToolbarController::statusChanged( const css::frame::FeatureStateEvent& rEvent ) +{ + SolarMutexGuard aGuard; + + if ( m_bReplaceWithLast && !rEvent.IsEnabled && m_xPopupMenu.is() ) + { + ToolBox* pToolBox = nullptr; + ToolBoxItemId nId; + if ( getToolboxId( nId, &pToolBox ) && pToolBox->IsItemEnabled( nId ) ) + { + Menu* pVclMenu = m_xPopupMenu->GetMenu(); + pVclMenu->Activate(); + pVclMenu->Deactivate(); + } + + for (sal_uInt16 i = 0, nCount = m_xPopupMenu->getItemCount(); i < nCount; ++i ) + { + sal_uInt16 nItemId = m_xPopupMenu->getItemId(i); + if (nItemId && m_xPopupMenu->isItemEnabled(nItemId) && !m_xPopupMenu->getPopupMenu(nItemId).is()) + { + functionExecuted(m_xPopupMenu->getCommand(nItemId)); + return; + } + } + } + + PopupMenuToolbarController::statusChanged( rEvent ); +} + +void GenericPopupToolbarController::functionExecuted( const OUString& rCommand ) +{ + if ( !m_bReplaceWithLast ) + return; + + removeStatusListener( m_aCommandURL ); + + auto aProperties = vcl::CommandInfoProvider::GetCommandProperties(rCommand, m_sModuleName); + OUString aRealCommand( vcl::CommandInfoProvider::GetRealCommandForCommand(aProperties) ); + m_aCommandURL = aRealCommand.isEmpty() ? rCommand : aRealCommand; + addStatusListener( m_aCommandURL ); + + ToolBox* pToolBox = nullptr; + ToolBoxItemId nId; + if ( getToolboxId( nId, &pToolBox ) ) + { + pToolBox->SetItemCommand( nId, rCommand ); + pToolBox->SetHelpText( nId, OUString() ); // Will retrieve the new one from help. + pToolBox->SetItemText(nId, vcl::CommandInfoProvider::GetLabelForCommand(aProperties)); + pToolBox->SetQuickHelpText(nId, vcl::CommandInfoProvider::GetTooltipForCommand(rCommand, aProperties, m_xFrame)); + + Image aImage = vcl::CommandInfoProvider::GetImageForCommand(rCommand, m_xFrame, pToolBox->GetImageSize()); + if ( !!aImage ) + pToolBox->SetItemImage( nId, aImage ); + } +} + +ToolBoxItemBits GenericPopupToolbarController::getDropDownStyle() const +{ + return m_bSplitButton ? ToolBoxItemBits::DROPDOWN : ToolBoxItemBits::DROPDOWNONLY; +} + +class SaveToolbarController : public cppu::ImplInheritanceHelper< PopupMenuToolbarController, + css::frame::XSubToolbarController, + css::util::XModifyListener > +{ +public: + explicit SaveToolbarController( const css::uno::Reference< css::uno::XComponentContext >& rxContext ); + + // XInitialization + virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override; + + // XSubToolbarController + // Make ToolBarManager ask our controller for updated image, in case of icon theme change. + virtual sal_Bool SAL_CALL opensSubToolbar() override; + virtual OUString SAL_CALL getSubToolbarName() override; + virtual void SAL_CALL functionSelected( const OUString& aCommand ) override; + virtual void SAL_CALL updateImage() override; + + // XStatusListener + virtual void SAL_CALL statusChanged( const css::frame::FeatureStateEvent& rEvent ) override; + + // XModifyListener + virtual void SAL_CALL modified( const css::lang::EventObject& rEvent ) override; + + // XEventListener + virtual void SAL_CALL disposing( const css::lang::EventObject& rEvent ) override; + + // XComponent + virtual void SAL_CALL dispose() override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual sal_Bool SAL_CALL supportsService( OUString const & rServiceName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + +private: + bool m_bReadOnly; + bool m_bModified; + css::uno::Reference< css::frame::XStorable > m_xStorable; + css::uno::Reference< css::util::XModifiable > m_xModifiable; +}; + +SaveToolbarController::SaveToolbarController( const css::uno::Reference< css::uno::XComponentContext >& rxContext ) + : ImplInheritanceHelper( rxContext, ".uno:SaveAsMenu" ) + , m_bReadOnly( false ) + , m_bModified( false ) +{ +} + +void SaveToolbarController::initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) +{ + PopupMenuToolbarController::initialize( aArguments ); + + ToolBox* pToolBox = nullptr; + ToolBoxItemId nId; + if ( !getToolboxId( nId, &pToolBox ) ) + return; + + css::uno::Reference< css::frame::XController > xController = m_xFrame->getController(); + if ( xController.is() ) + m_xModifiable.set( xController->getModel(), css::uno::UNO_QUERY ); + + if ( m_xModifiable.is() && pToolBox->GetItemCommand( nId ) == m_aCommandURL ) + // Will also enable the save as only mode. + m_xStorable.set( m_xModifiable, css::uno::UNO_QUERY ); + else if ( !m_xModifiable.is() ) + // Can be in table/query design. + m_xModifiable.set( xController, css::uno::UNO_QUERY ); + else + // Simple save button, without the dropdown. + pToolBox->SetItemBits( nId, pToolBox->GetItemBits( nId ) & ~ ToolBoxItemBits::DROPDOWN ); + + if ( m_xModifiable.is() ) + { + m_xModifiable->addModifyListener( this ); + modified( css::lang::EventObject() ); + } +} + +sal_Bool SaveToolbarController::opensSubToolbar() +{ + return true; +} + +OUString SaveToolbarController::getSubToolbarName() +{ + return OUString(); +} + +void SaveToolbarController::functionSelected( const OUString& /*aCommand*/ ) +{ +} + +void SaveToolbarController::updateImage() +{ + SolarMutexGuard aGuard; + ToolBox* pToolBox = nullptr; + ToolBoxItemId nId; + if ( !getToolboxId( nId, &pToolBox ) ) + return; + + vcl::ImageType eImageType = pToolBox->GetImageSize(); + + Image aImage; + + if ( m_bReadOnly ) + { + aImage = vcl::CommandInfoProvider::GetImageForCommand(".uno:SaveAs", m_xFrame, eImageType); + } + else if ( m_bModified ) + { + if (eImageType == vcl::ImageType::Size26) + aImage = Image(StockImage::Yes, BMP_SAVEMODIFIED_LARGE); + else if (eImageType == vcl::ImageType::Size32) + aImage = Image(StockImage::Yes, BMP_SAVEMODIFIED_EXTRALARGE); + else + aImage = Image(StockImage::Yes, BMP_SAVEMODIFIED_SMALL); + } + + if ( !aImage ) + aImage = vcl::CommandInfoProvider::GetImageForCommand(m_aCommandURL, m_xFrame, eImageType); + + if ( !!aImage ) + pToolBox->SetItemImage( nId, aImage ); +} + +void SaveToolbarController::statusChanged( const css::frame::FeatureStateEvent& rEvent ) +{ + ToolBox* pToolBox = nullptr; + ToolBoxItemId nId; + if ( !getToolboxId( nId, &pToolBox ) ) + return; + + bool bLastReadOnly = m_bReadOnly; + m_bReadOnly = m_xStorable.is() && m_xStorable->isReadonly(); + if ( bLastReadOnly != m_bReadOnly ) + { + OUString sCommand = m_bReadOnly ? OUString( ".uno:SaveAs" ) : m_aCommandURL; + auto aProperties = vcl::CommandInfoProvider::GetCommandProperties(sCommand, + vcl::CommandInfoProvider::GetModuleIdentifier(m_xFrame)); + pToolBox->SetQuickHelpText( nId, + vcl::CommandInfoProvider::GetTooltipForCommand(sCommand, aProperties, m_xFrame) ); + pToolBox->SetItemBits( nId, pToolBox->GetItemBits( nId ) & ~( m_bReadOnly ? ToolBoxItemBits::DROPDOWN : ToolBoxItemBits::DROPDOWNONLY ) ); + pToolBox->SetItemBits( nId, pToolBox->GetItemBits( nId ) | ( m_bReadOnly ? ToolBoxItemBits::DROPDOWNONLY : ToolBoxItemBits::DROPDOWN ) ); + updateImage(); + } + + if ( !m_bReadOnly ) + pToolBox->EnableItem( nId, rEvent.IsEnabled ); +} + +void SaveToolbarController::modified( const css::lang::EventObject& /*rEvent*/ ) +{ + bool bLastModified = m_bModified; + m_bModified = m_xModifiable->isModified(); + if ( bLastModified != m_bModified ) + updateImage(); +} + +void SaveToolbarController::disposing( const css::lang::EventObject& rEvent ) +{ + if ( rEvent.Source == m_xModifiable ) + { + m_xModifiable.clear(); + m_xStorable.clear(); + } + else + PopupMenuToolbarController::disposing( rEvent ); +} + +void SaveToolbarController::dispose() +{ + PopupMenuToolbarController::dispose(); + if ( m_xModifiable.is() ) + { + m_xModifiable->removeModifyListener( this ); + m_xModifiable.clear(); + } + m_xStorable.clear(); +} + +OUString SaveToolbarController::getImplementationName() +{ + return "com.sun.star.comp.framework.SaveToolbarController"; +} + +sal_Bool SaveToolbarController::supportsService( OUString const & rServiceName ) +{ + return cppu::supportsService( this, rServiceName ); +} + +css::uno::Sequence< OUString > SaveToolbarController::getSupportedServiceNames() +{ + return {"com.sun.star.frame.ToolbarController"}; +} + +class NewToolbarController : public cppu::ImplInheritanceHelper +{ +public: + explicit NewToolbarController( const css::uno::Reference< css::uno::XComponentContext >& rxContext ); + + // XServiceInfo + OUString SAL_CALL getImplementationName() override; + + virtual sal_Bool SAL_CALL supportsService(OUString const & rServiceName) override; + + css::uno::Sequence SAL_CALL getSupportedServiceNames() override; + + void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override; + + // XSubToolbarController + // Make ToolBarManager ask our controller for updated image, in case of icon theme change. + sal_Bool SAL_CALL opensSubToolbar() override { return true; } + OUString SAL_CALL getSubToolbarName() override { return OUString(); } + void SAL_CALL functionSelected( const OUString& ) override {} + void SAL_CALL updateImage() override; + +private: + void functionExecuted( const OUString &rCommand ) override; + void SAL_CALL statusChanged( const css::frame::FeatureStateEvent& rEvent ) override; + void SAL_CALL execute( sal_Int16 KeyModifier ) override; + sal_uInt16 getMenuIdForCommand( std::u16string_view rCommand ); + + sal_uInt16 m_nMenuId; +}; + +NewToolbarController::NewToolbarController( + const css::uno::Reference< css::uno::XComponentContext >& xContext ) + : ImplInheritanceHelper( xContext ) + , m_nMenuId( 0 ) +{ +} + +OUString NewToolbarController::getImplementationName() +{ + return "org.apache.openoffice.comp.framework.NewToolbarController"; +} + +sal_Bool NewToolbarController::supportsService(OUString const & rServiceName) +{ + return cppu::supportsService( this, rServiceName ); +} + +css::uno::Sequence NewToolbarController::getSupportedServiceNames() +{ + return {"com.sun.star.frame.ToolbarController"}; +} + +void SAL_CALL NewToolbarController::initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) +{ + PopupMenuToolbarController::initialize( aArguments ); + + osl::MutexGuard aGuard( m_aMutex ); + createPopupMenuController(); +} + +void SAL_CALL NewToolbarController::statusChanged( const css::frame::FeatureStateEvent& rEvent ) +{ + if ( rEvent.IsEnabled ) + { + OUString aState; + rEvent.State >>= aState; + try + { + // set the image even if the state is not a string + // the toolbar item command will be used as a fallback + functionExecuted( aState ); + } + catch (const css::ucb::CommandFailedException&) + { + } + catch (const css::ucb::ContentCreationException&) + { + } + } + + enable( rEvent.IsEnabled ); +} + +void SAL_CALL NewToolbarController::execute( sal_Int16 /*KeyModifier*/ ) +{ + osl::MutexGuard aGuard( m_aMutex ); + + OUString aURL, aTarget; + if ( m_xPopupMenu.is() && m_nMenuId ) + { + SolarMutexGuard aSolarMutexGuard; + aURL = m_xPopupMenu->getCommand(m_nMenuId); + + // TODO investigate how to wrap Get/SetUserValue in css::awt::XMenu + MenuAttributes* pMenuAttributes(static_cast(m_xPopupMenu->getUserValue(m_nMenuId))); + if ( pMenuAttributes ) + aTarget = pMenuAttributes->aTargetFrame; + else + aTarget = "_default"; + } + else + aURL = m_aCommandURL; + + css::uno::Sequence< css::beans::PropertyValue > aArgs{ comphelper::makePropertyValue( + "Referer", OUString( "private:user" )) }; + + dispatchCommand( aURL, aArgs, aTarget ); +} + +void NewToolbarController::functionExecuted( const OUString &rCommand ) +{ + m_nMenuId = getMenuIdForCommand( rCommand ); + updateImage(); +} + +sal_uInt16 NewToolbarController::getMenuIdForCommand( std::u16string_view rCommand ) +{ + if ( m_xPopupMenu.is() && !rCommand.empty() ) + { + sal_uInt16 nCount = m_xPopupMenu->getItemCount(); + for ( sal_uInt16 n = 0; n < nCount; ++n ) + { + sal_uInt16 nId = m_xPopupMenu->getItemId(n); + OUString aCmd(m_xPopupMenu->getCommand(nId)); + + // match even if the menu command is more detailed + // (maybe an additional query) #i28667# + if ( aCmd.match( rCommand ) ) + return nId; + } + } + + return 0; +} + +void SAL_CALL NewToolbarController::updateImage() +{ + SolarMutexGuard aSolarLock; + VclPtr< ToolBox> pToolBox = static_cast< ToolBox* >( VCLUnoHelper::GetWindow( getParent() ) ); + if ( !pToolBox ) + return; + + OUString aURL, aImageId; + if ( m_xPopupMenu.is() && m_nMenuId ) + { + aURL = m_xPopupMenu->getCommand(m_nMenuId); + MenuAttributes* pMenuAttributes(static_cast(m_xPopupMenu->getUserValue(m_nMenuId))); + if ( pMenuAttributes ) + aImageId = pMenuAttributes->aImageId; + } + else + aURL = m_aCommandURL; + + INetURLObject aURLObj( aImageId.isEmpty() ? aURL : aImageId ); + vcl::ImageType eImageType( pToolBox->GetImageSize() ); + Image aImage = SvFileInformationManager::GetImageNoDefault( aURLObj, eImageType ); + if ( !aImage ) + aImage = vcl::CommandInfoProvider::GetImageForCommand( aURL, m_xFrame, eImageType ); + + if ( !aImage ) + return; + + pToolBox->SetItemImage( m_nToolBoxId, aImage ); +} + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_framework_GenericPopupToolbarController_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence const &args) +{ + return cppu::acquire(new GenericPopupToolbarController(context, args)); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_framework_SaveToolbarController_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence const &) +{ + return cppu::acquire(new SaveToolbarController(context)); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +org_apache_openoffice_comp_framework_NewToolbarController_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence const &) +{ + return cppu::acquire(new NewToolbarController(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uielement/progressbarwrapper.cxx b/framework/source/uielement/progressbarwrapper.cxx new file mode 100644 index 0000000000..ad147111ff --- /dev/null +++ b/framework/source/uielement/progressbarwrapper.cxx @@ -0,0 +1,310 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include + +#include +#include + +#include +#include +#include + +using namespace ::com::sun::star; + +namespace framework{ + +ProgressBarWrapper::ProgressBarWrapper() : +UIElementWrapperBase( css::ui::UIElementType::PROGRESSBAR ) + , m_bOwnsInstance( false ) + , m_nRange( 100 ) + , m_nValue( 0 ) +{ +} + +ProgressBarWrapper::~ProgressBarWrapper() +{ +} + +// public interfaces +void ProgressBarWrapper::setStatusBar( const uno::Reference< awt::XWindow >& rStatusBar, bool bOwnsInstance ) +{ + SolarMutexGuard g; + + if ( m_bDisposed ) + return; + + if ( m_bOwnsInstance ) + { + // dispose XWindow reference of our status bar + try + { + if ( m_xStatusBar.is() ) + m_xStatusBar->dispose(); + } + catch ( const uno::Exception& ) + { + } + m_xStatusBar.clear(); + } + + m_bOwnsInstance = bOwnsInstance; + m_xStatusBar = rStatusBar; +} + +uno::Reference< awt::XWindow > ProgressBarWrapper::getStatusBar() const +{ + SolarMutexGuard g; + + if ( m_bDisposed ) + return uno::Reference< awt::XWindow >(); + + return m_xStatusBar; +} + +// wrapped methods of css::task::XStatusIndicator +void ProgressBarWrapper::start( const OUString& Text, ::sal_Int32 Range ) +{ + uno::Reference< awt::XWindow > xWindow; + sal_Int32 nValue( 0 ); + + { + SolarMutexGuard g; + + if ( m_bDisposed ) + return; + + xWindow = m_xStatusBar; + m_nValue = 0; + m_nRange = Range; + nValue = m_nValue; + } + + if ( !xWindow.is() ) + return; + + SolarMutexGuard aSolarMutexGuard; + VclPtr pWindow = VCLUnoHelper::GetWindow( xWindow ); + if ( !(pWindow && pWindow->GetType() == WindowType::STATUSBAR) ) + return; + + StatusBar* pStatusBar = static_cast(pWindow.get()); + if ( !pStatusBar->IsProgressMode() ) + pStatusBar->StartProgressMode( Text ); + else + { + pStatusBar->SetUpdateMode( false ); + pStatusBar->EndProgressMode(); + pStatusBar->StartProgressMode( Text ); + pStatusBar->SetProgressValue( sal_uInt16( nValue )); + pStatusBar->SetUpdateMode( true ); + } + pStatusBar->Show( true, ShowFlags::NoFocusChange | ShowFlags::NoActivate ); +} + +void ProgressBarWrapper::end() +{ + uno::Reference< awt::XWindow > xWindow; + + { + SolarMutexGuard g; + + if ( m_bDisposed ) + return; + + xWindow = m_xStatusBar; + m_nRange = 100; + m_nValue = 0; + } + + if ( xWindow.is() ) + { + SolarMutexGuard aSolarMutexGuard; + VclPtr pWindow = VCLUnoHelper::GetWindow( xWindow ); + if ( pWindow && pWindow->GetType() == WindowType::STATUSBAR ) + { + StatusBar* pStatusBar = static_cast(pWindow.get()); + if ( pStatusBar->IsProgressMode() ) + pStatusBar->EndProgressMode(); + } + } +} + +void ProgressBarWrapper::setText( const OUString& Text ) +{ + uno::Reference< awt::XWindow > xWindow; + sal_Int32 nValue( 0 ); + + { + SolarMutexGuard g; + + if ( m_bDisposed ) + return; + + xWindow = m_xStatusBar; + m_aText = Text; + nValue = m_nValue; + } + + if ( !xWindow.is() ) + return; + + SolarMutexGuard aSolarMutexGuard; + VclPtr pWindow = VCLUnoHelper::GetWindow( xWindow ); + if ( !(pWindow && pWindow->GetType() == WindowType::STATUSBAR) ) + return; + + StatusBar* pStatusBar = static_cast(pWindow.get()); + if( pStatusBar->IsProgressMode() ) + { + pStatusBar->SetUpdateMode( false ); + pStatusBar->EndProgressMode(); + pStatusBar->StartProgressMode( Text ); + pStatusBar->SetProgressValue( sal_uInt16( nValue )); + pStatusBar->SetUpdateMode( true ); + } + else + pStatusBar->SetText( Text ); +} + +void ProgressBarWrapper::setValue( ::sal_Int32 nValue ) +{ + uno::Reference< awt::XWindow > xWindow; + OUString aText; + bool bSetValue( false ); + + { + SolarMutexGuard g; + + if ( m_bDisposed ) + return; + + xWindow = m_xStatusBar; + + double fVal( 0 ); + if ( m_nRange > 0 ) + { + fVal = ( double( nValue ) / double( m_nRange )) * 100; + fVal = std::clamp( fVal, 0.0, 100.0 ); + } + + if ( m_nValue != sal_Int32( fVal )) + { + m_nValue = sal_Int32( fVal ); + bSetValue = true; + } + + nValue = m_nValue; + aText = m_aText; + } + + if ( xWindow.is() && bSetValue ) + { + SolarMutexGuard aSolarMutexGuard; + VclPtr pWindow = VCLUnoHelper::GetWindow( xWindow ); + if ( pWindow && pWindow->GetType() == WindowType::STATUSBAR ) + { + StatusBar* pStatusBar = static_cast(pWindow.get()); + if ( !pStatusBar->IsProgressMode() ) + pStatusBar->StartProgressMode( aText ); + pStatusBar->SetProgressValue( sal_uInt16( nValue )); + } + } +} + +void ProgressBarWrapper::reset() +{ + setText( OUString() ); + setValue( 0 ); +} + +// XInitialization +void SAL_CALL ProgressBarWrapper::initialize( const uno::Sequence< uno::Any >& ) +{ + // dummy - do nothing +} + +// XUpdatable +void SAL_CALL ProgressBarWrapper::update() +{ + // dummy - do nothing +} + +// XComponent +void SAL_CALL ProgressBarWrapper::dispose() +{ + uno::Reference< lang::XComponent > xThis(this); + + { + SolarMutexGuard g; + + if ( m_bDisposed ) + return; + } + + { + lang::EventObject aEvent( xThis ); + m_aListenerContainer.disposeAndClear( aEvent ); + + SolarMutexGuard g; + if ( m_bOwnsInstance ) + { + try + { + if ( m_xStatusBar.is() ) + m_xStatusBar->dispose(); + } + catch ( const lang::DisposedException& ) + { + } + } + + m_xStatusBar.clear(); + m_bDisposed = true; + } +} + +// XUIElement +uno::Reference< uno::XInterface > SAL_CALL ProgressBarWrapper::getRealInterface() +{ + SolarMutexGuard g; + + if ( m_bDisposed ) + return uno::Reference< uno::XInterface >(); + else + { + uno::Reference< uno::XInterface > xComp( m_xProgressBarIfacWrapper ); + if ( !xComp.is() ) + { + rtl::Reference pWrapper = + new StatusIndicatorInterfaceWrapper( uno::Reference< lang::XComponent >(this) ); + xComp.set(static_cast< cppu::OWeakObject* >( pWrapper.get() ), + uno::UNO_QUERY ); + m_xProgressBarIfacWrapper = xComp; + } + + return xComp; + } +} + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uielement/recentfilesmenucontroller.cxx b/framework/source/uielement/recentfilesmenucontroller.cxx new file mode 100644 index 0000000000..4355069c68 --- /dev/null +++ b/framework/source/uielement/recentfilesmenucontroller.cxx @@ -0,0 +1,479 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace css; +using namespace com::sun::star::uno; +using namespace com::sun::star::lang; +using namespace com::sun::star::frame; +using namespace com::sun::star::beans; +using namespace com::sun::star::util; + +#define MAX_MENU_ITEMS 99 +#define MAX_MENU_ITEMS_PER_MODULE 5 + +namespace { + +constexpr OUString CMD_CLEAR_LIST = u".uno:ClearRecentFileList"_ustr; +constexpr OUString CMD_OPEN_AS_TEMPLATE = u".uno:OpenTemplate"_ustr; +constexpr OUString CMD_OPEN_REMOTE = u".uno:OpenRemote"_ustr; + +class RecentFilesMenuController : public svt::PopupMenuControllerBase +{ + using svt::PopupMenuControllerBase::disposing; + +public: + RecentFilesMenuController( const uno::Reference< uno::XComponentContext >& xContext, + const uno::Sequence< uno::Any >& args ); + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override + { + return "com.sun.star.comp.framework.RecentFilesMenuController"; + } + + virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override + { + return cppu::supportsService(this, ServiceName); + } + + virtual css::uno::Sequence SAL_CALL getSupportedServiceNames() override + { + return {"com.sun.star.frame.PopupMenuController"}; + } + + // XStatusListener + virtual void SAL_CALL statusChanged( const frame::FeatureStateEvent& Event ) override; + + // XMenuListener + virtual void SAL_CALL itemSelected( const awt::MenuEvent& rEvent ) override; + virtual void SAL_CALL itemActivated( const awt::MenuEvent& rEvent ) override; + + // XDispatchProvider + virtual uno::Reference< frame::XDispatch > SAL_CALL queryDispatch( const util::URL& aURL, const OUString& sTarget, sal_Int32 nFlags ) override; + + // XDispatch + virtual void SAL_CALL dispatch( const util::URL& aURL, const uno::Sequence< beans::PropertyValue >& seqProperties ) override; + + // XEventListener + virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override; + +private: + virtual void impl_setPopupMenu() override; + void fillPopupMenu( css::uno::Reference< css::awt::XPopupMenu > const & rPopupMenu ); + void executeEntry( sal_Int32 nIndex ); + + std::vector> m_aRecentFilesItems; + bool m_bDisabled : 1; + bool m_bShowToolbarEntries; +}; + +RecentFilesMenuController::RecentFilesMenuController( const uno::Reference< uno::XComponentContext >& xContext, + const uno::Sequence< uno::Any >& args ) : + svt::PopupMenuControllerBase( xContext ), + m_bDisabled( false ), + m_bShowToolbarEntries( false ) +{ + css::beans::PropertyValue aPropValue; + for ( uno::Any const & arg : args ) + { + arg >>= aPropValue; + if ( aPropValue.Name == "InToolbar" ) + { + aPropValue.Value >>= m_bShowToolbarEntries; + break; + } + } +} + +void InsertItem(const css::uno::Reference& rPopupMenu, + const OUString& rCommand, + const css::uno::Reference& rFrame) +{ + sal_uInt16 nItemId = rPopupMenu->getItemCount() + 1; + + if (rFrame.is()) + { + OUString aModuleName(vcl::CommandInfoProvider::GetModuleIdentifier(rFrame)); + auto aProperties = vcl::CommandInfoProvider::GetCommandProperties(rCommand, aModuleName); + OUString aLabel(vcl::CommandInfoProvider::GetPopupLabelForCommand(aProperties)); + OUString aTooltip(vcl::CommandInfoProvider::GetTooltipForCommand(rCommand, aProperties, rFrame)); + css::uno::Reference xGraphic(vcl::CommandInfoProvider::GetXGraphicForCommand(rCommand, rFrame)); + + rPopupMenu->insertItem(nItemId, aLabel, 0, -1); + rPopupMenu->setItemImage(nItemId, xGraphic, false); + rPopupMenu->setHelpText(nItemId, aTooltip); + } + else + rPopupMenu->insertItem(nItemId, OUString(), 0, -1); + + rPopupMenu->setCommand(nItemId, rCommand); +} + + +// private function +void RecentFilesMenuController::fillPopupMenu( Reference< css::awt::XPopupMenu > const & rPopupMenu ) +{ + SolarMutexGuard aSolarMutexGuard; + + resetPopupMenu( rPopupMenu ); + + std::vector< SvtHistoryOptions::HistoryItem > aHistoryList = SvtHistoryOptions::GetList( EHistoryType::PickList ); + + int nPickListMenuItems = std::min( aHistoryList.size(), MAX_MENU_ITEMS ); + m_aRecentFilesItems.clear(); + + // tdf#56696 - retrieve expert configuration option if the recent document + // list should show only files that can be handled by the current module + const bool bShowCurrentModuleOnly + = officecfg::Office::Common::History::ShowCurrentModuleOnly::get(); + + size_t nItemPosModule = 0; + size_t nItemPosPinned = 0; + if (( nPickListMenuItems > 0 ) && !m_bDisabled ) + { + size_t nItemPos = 0; + + // tdf#155699 - create a lambda to insert a recent document item at a specified position + auto insertHistoryItemAtPos = + [&](const SvtHistoryOptions::HistoryItem& rPickListEntry, const size_t aInsertPosition) + { + m_aRecentFilesItems.insert(m_aRecentFilesItems.begin() + aInsertPosition, + { rPickListEntry.sURL, rPickListEntry.isReadOnly }); + nItemPos++; + }; + + if (m_aModuleName != "com.sun.star.frame.StartModule") + { + ::comphelper::MimeConfigurationHelper aConfigHelper( + comphelper::getProcessComponentContext()); + + // Show the first MAX_MENU_ITEMS_PER_MODULE items of the current module + // on top of the recent document list. + for (int i = 0; i < nPickListMenuItems; i++) + { + const SvtHistoryOptions::HistoryItem& rPickListEntry = aHistoryList[i]; + + // tdf#155699 - insert pinned document at the beginning of the list + if (rPickListEntry.isPinned) + { + insertHistoryItemAtPos(rPickListEntry, nItemPosPinned); + nItemPosPinned++; + nItemPosModule++; + } + // tdf#56696 - insert documents of the current module + else if ((bShowCurrentModuleOnly + || (nItemPosModule - nItemPosPinned) < MAX_MENU_ITEMS_PER_MODULE) + && aConfigHelper.GetDocServiceNameFromFilter(rPickListEntry.sFilter) + == m_aModuleName) + { + insertHistoryItemAtPos(rPickListEntry, nItemPosModule); + nItemPosModule++; + } + // Insert all other documents at the end of the list if the expert option is not set + else if (!bShowCurrentModuleOnly) + insertHistoryItemAtPos(rPickListEntry, nItemPos); + } + } + else + { + for (int i = 0; i < nPickListMenuItems; i++) + { + const SvtHistoryOptions::HistoryItem& rPickListEntry = aHistoryList[i]; + // tdf#155699 - insert pinned document at the beginning of the list + insertHistoryItemAtPos(rPickListEntry, + rPickListEntry.isPinned ? nItemPosModule++ : nItemPos); + } + } + } + + if ( !m_aRecentFilesItems.empty() ) + { + const sal_uInt32 nCount = m_aRecentFilesItems.size(); + StyleSettings aIconSettings; + bool bIsIconsAllowed = aIconSettings.GetUseImagesInMenus(); + + for ( sal_uInt32 i = 0; i < nCount; i++ ) + { + + OUStringBuffer aMenuShortCut; + if ( i <= 9 ) + { + if ( i == 9 ) + aMenuShortCut.append( "1~0. " ); + else + { + aMenuShortCut.append( "~N. " ); + aMenuShortCut[ 1 ] = sal_Unicode( i + '1' ); + } + } + else + { + aMenuShortCut.append( OUString::number(sal_Int32( i + 1 ) ) + ". " ); + } + + OUString aURLString = "vnd.sun.star.popup:RecentFileList?entry=" + OUString::number(i); + + // Abbreviate URL + OUString aMenuTitle; + INetURLObject const aURL(m_aRecentFilesItems[i].first); + OUString aTipHelpText( aURL.getFSysPath( FSysStyle::Detect ) ); + + if ( aURL.GetProtocol() == INetProtocol::File ) + { + // Do handle file URL differently: don't show the protocol, just the file name + aMenuTitle = aURL.GetLastName(INetURLObject::DecodeMechanism::WithCharset); + } + else + { + // In all other URLs show the protocol name before the file name + aMenuTitle = INetURLObject::GetSchemeName(aURL.GetProtocol()) + ": " + aURL.getName(); + } + + aMenuShortCut.append( aMenuTitle ); + + rPopupMenu->insertItem(sal_uInt16( i+1 ), aMenuShortCut.makeStringAndClear(), 0, -1); + + if ( bIsIconsAllowed ) { + // tdf#146219: don't use SvFileInformationManager::GetImageId, + // which needs to access the URL to detect if it's a directory + BitmapEx aThumbnail(SvFileInformationManager::GetFileImageId(aURL)); + rPopupMenu->setItemImage(sal_uInt16(i + 1), Graphic(aThumbnail).GetXGraphic(), false); + } + + rPopupMenu->setTipHelpText(sal_uInt16(i + 1), aTipHelpText); + rPopupMenu->setCommand(sal_uInt16(i + 1), aURLString); + + // tdf#155699 - show a separator after the pinned recent document items + if (nItemPosPinned > 0 && i == nItemPosPinned - 1) + rPopupMenu->insertSeparator(-1); + + // Show a separator after the MAX_MENU_ITEMS_PER_MODULE recent document items + if (nItemPosModule > 0 && i == nItemPosModule - 1) + rPopupMenu->insertSeparator(-1); + } + + rPopupMenu->insertSeparator(-1); + // Clear List menu entry + rPopupMenu->insertItem(sal_uInt16(nCount + 1), FwkResId(STR_CLEAR_RECENT_FILES), 0, -1); + rPopupMenu->setCommand(sal_uInt16(nCount + 1), CMD_CLEAR_LIST); + rPopupMenu->setHelpText(sal_uInt16(nCount + 1), FwkResId(STR_CLEAR_RECENT_FILES_HELP)); + + // Open remote menu entry + if ( m_bShowToolbarEntries ) + { + rPopupMenu->insertSeparator(-1); + InsertItem(rPopupMenu, CMD_OPEN_AS_TEMPLATE, m_xFrame); + InsertItem(rPopupMenu, CMD_OPEN_REMOTE, m_xFrame); + } + } + else + { + if ( m_bShowToolbarEntries ) + { + InsertItem(rPopupMenu, CMD_OPEN_AS_TEMPLATE, m_xFrame); + InsertItem(rPopupMenu, CMD_OPEN_REMOTE, m_xFrame); + } + else + { + // Add InsertSeparator(), otherwise it will display + // the first item icon of recent files instead of displaying no icon. + rPopupMenu->insertSeparator(-1); + // No recent documents => insert "no documents" string + // Do not disable it, otherwise the Toolbar controller and MenuButton + // will display SV_RESID_STRING_NOSELECTIONPOSSIBLE instead of STR_NODOCUMENT + rPopupMenu->insertItem(1, FwkResId(STR_NODOCUMENT), static_cast(MenuItemBits::NOSELECT), -1); + } + } +} + +void RecentFilesMenuController::executeEntry( sal_Int32 nIndex ) +{ + if (( nIndex < 0 ) || + ( nIndex >= sal::static_int_cast( m_aRecentFilesItems.size() ))) + return; + + Sequence< PropertyValue > aArgsList{ + comphelper::makePropertyValue("Referer", OUString( "private:user" )), + + // documents in the picklist will never be opened as templates + comphelper::makePropertyValue("AsTemplate", false), + + // Type detection needs to know which app we are opening it from. + comphelper::makePropertyValue("DocumentService", m_aModuleName) + }; + if (m_aRecentFilesItems[nIndex].second) // tdf#149170 only add if true + { + aArgsList.realloc(aArgsList.size()+1); + aArgsList.getArray()[aArgsList.size()-1] = comphelper::makePropertyValue("ReadOnly", true); + } + dispatchCommand(m_aRecentFilesItems[nIndex].first, aArgsList, "_default"); +} + +// XEventListener +void SAL_CALL RecentFilesMenuController::disposing( const EventObject& ) +{ + Reference< css::awt::XMenuListener > xHolder(this); + + std::unique_lock aLock( m_aMutex ); + m_xFrame.clear(); + m_xDispatch.clear(); + + if ( m_xPopupMenu.is() ) + m_xPopupMenu->removeMenuListener( Reference< css::awt::XMenuListener >(this) ); + m_xPopupMenu.clear(); +} + +// XStatusListener +void SAL_CALL RecentFilesMenuController::statusChanged( const FeatureStateEvent& Event ) +{ + std::unique_lock aLock( m_aMutex ); + m_bDisabled = !Event.IsEnabled; +} + +void SAL_CALL RecentFilesMenuController::itemSelected( const css::awt::MenuEvent& rEvent ) +{ + Reference< css::awt::XPopupMenu > xPopupMenu; + + { + std::unique_lock aLock(m_aMutex); + xPopupMenu = m_xPopupMenu; + } + + if ( !xPopupMenu.is() ) + return; + + const OUString aCommand( xPopupMenu->getCommand( rEvent.MenuId ) ); + + if ( aCommand == CMD_CLEAR_LIST ) + { + SvtHistoryOptions::Clear( EHistoryType::PickList, false ); + dispatchCommand( + "vnd.org.libreoffice.recentdocs:ClearRecentFileList", + css::uno::Sequence< css::beans::PropertyValue >() ); + } + else if ( aCommand == CMD_OPEN_REMOTE ) + { + Sequence< PropertyValue > aArgsList( 0 ); + dispatchCommand( CMD_OPEN_REMOTE, aArgsList ); + } + else if ( aCommand == CMD_OPEN_AS_TEMPLATE ) + { + Sequence< PropertyValue > aArgsList( 0 ); + dispatchCommand( CMD_OPEN_AS_TEMPLATE, aArgsList ); + } + else + executeEntry( rEvent.MenuId-1 ); +} + +void SAL_CALL RecentFilesMenuController::itemActivated( const css::awt::MenuEvent& ) +{ + std::unique_lock aLock( m_aMutex ); + impl_setPopupMenu(); +} + +// XPopupMenuController +void RecentFilesMenuController::impl_setPopupMenu() +{ + if ( m_xPopupMenu.is() ) + fillPopupMenu( m_xPopupMenu ); +} + +// XDispatchProvider +Reference< XDispatch > SAL_CALL RecentFilesMenuController::queryDispatch( + const URL& aURL, + const OUString& /*sTarget*/, + sal_Int32 /*nFlags*/ ) +{ + std::unique_lock aLock( m_aMutex ); + + throwIfDisposed(aLock); + + if ( aURL.Complete.startsWith( m_aBaseURL ) ) + return Reference< XDispatch >( this ); + else + return Reference< XDispatch >(); +} + +// XDispatch +void SAL_CALL RecentFilesMenuController::dispatch( + const URL& aURL, + const Sequence< PropertyValue >& /*seqProperties*/ ) +{ + std::unique_lock aLock( m_aMutex ); + + throwIfDisposed(aLock); + + if ( !aURL.Complete.startsWith( m_aBaseURL ) ) + return; + + // Parse URL to retrieve entry argument and its value + sal_Int32 nQueryPart = aURL.Complete.indexOf( '?', m_aBaseURL.getLength() ); + if ( nQueryPart <= 0 ) + return; + + static constexpr OUString aEntryArgStr( u"entry="_ustr ); + sal_Int32 nEntryArg = aURL.Complete.indexOf( aEntryArgStr, nQueryPart ); + sal_Int32 nEntryPos = nEntryArg + aEntryArgStr.getLength(); + if (( nEntryArg <= 0 ) || ( nEntryPos >= aURL.Complete.getLength() )) + return; + + sal_Int32 nAddArgs = aURL.Complete.indexOf( '&', nEntryPos ); + std::u16string_view aEntryArg; + + if ( nAddArgs < 0 ) + aEntryArg = aURL.Complete.subView( nEntryPos ); + else + aEntryArg = aURL.Complete.subView( nEntryPos, nAddArgs-nEntryPos ); + + sal_Int32 nEntry = o3tl::toInt32(aEntryArg); + executeEntry( nEntry ); +} + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_framework_RecentFilesMenuController_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence const &args) +{ + return cppu::acquire(new RecentFilesMenuController(context, args)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uielement/resourcemenucontroller.cxx b/framework/source/uielement/resourcemenucontroller.cxx new file mode 100644 index 0000000000..065a97c63a --- /dev/null +++ b/framework/source/uielement/resourcemenucontroller.cxx @@ -0,0 +1,581 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace { + +class ResourceMenuController : public cppu::ImplInheritanceHelper< svt::PopupMenuControllerBase, css::ui::XUIConfigurationListener > +{ +public: + ResourceMenuController( const css::uno::Reference< css::uno::XComponentContext >& rxContext, + const css::uno::Sequence< css::uno::Any >& rxArgs, bool bToolbarContainer ); + + // XPopupMenuController + virtual void SAL_CALL updatePopupMenu() override; + + // XStatusListener + virtual void SAL_CALL statusChanged( const css::frame::FeatureStateEvent& rEvent ) override; + + // XEventListener + virtual void SAL_CALL disposing( const css::lang::EventObject& rEvent ) override; + + // XUIConfigurationListener + virtual void SAL_CALL elementInserted( const css::ui::ConfigurationEvent& rEvent ) override; + virtual void SAL_CALL elementRemoved( const css::ui::ConfigurationEvent& rEvent ) override; + virtual void SAL_CALL elementReplaced( const css::ui::ConfigurationEvent& rEvent ) override; + + // XMenuListener + virtual void SAL_CALL itemActivated( const css::awt::MenuEvent& rEvent ) override; + virtual void SAL_CALL itemSelected( const css::awt::MenuEvent& rEvent ) override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + +private: + OUString m_aMenuURL; + bool m_bContextMenu; + bool m_bInToolbar; + bool m_bToolbarContainer; + sal_uInt16 m_nNewMenuId; + rtl::Reference< framework::MenuBarManager > m_xMenuBarManager; + css::uno::Reference< css::frame::XDispatchProvider > m_xDispatchProvider; + css::uno::Reference< css::container::XIndexAccess > m_xMenuContainer; + css::uno::Reference< css::ui::XUIConfigurationManager > m_xConfigManager, m_xModuleConfigManager; + void addVerbs( const css::uno::Sequence< css::embed::VerbDescriptor >& rVerbs ); + virtual void disposing(std::unique_lock& rGuard) override; + +protected: + css::uno::Reference< css::uno::XComponentContext > m_xContext; +}; + +ResourceMenuController::ResourceMenuController( const css::uno::Reference< css::uno::XComponentContext >& rxContext, + const css::uno::Sequence< css::uno::Any >& rxArgs, bool bToolbarContainer ) : + ImplInheritanceHelper( rxContext ), + m_bContextMenu( false ), + m_bInToolbar( false ), + m_bToolbarContainer( bToolbarContainer ), + m_nNewMenuId( 1 ), + m_xContext( rxContext ) +{ + for ( const auto& arg: rxArgs ) + { + css::beans::PropertyValue aPropValue; + if ( arg >>= aPropValue ) + { + if ( aPropValue.Name == "Value" ) + { + OUString aMenuName; + aPropValue.Value >>= aMenuName; + if ( aMenuName.isEmpty() ) + continue; + + if ( m_bToolbarContainer ) + m_aMenuURL = "private:resource/toolbar/" + aMenuName; + else + m_aMenuURL = "private:resource/popupmenu/" + aMenuName; + } + else if ( aPropValue.Name == "ResourceURL" ) + aPropValue.Value >>= m_aMenuURL; + else if ( aPropValue.Name == "Frame" ) + aPropValue.Value >>= m_xFrame; + else if ( aPropValue.Name == "ModuleIdentifier" ) + aPropValue.Value >>= m_aModuleName; + else if ( aPropValue.Name == "DispatchProvider" ) + aPropValue.Value >>= m_xDispatchProvider; + else if ( aPropValue.Name == "IsContextMenu" ) + aPropValue.Value >>= m_bContextMenu; + else if ( aPropValue.Name == "InToolbar" ) + aPropValue.Value >>= m_bInToolbar; + } + } + if ( m_xFrame.is() ) + // No need to initialize again through initialize method. + m_bInitialized = true; +} + +void ResourceMenuController::updatePopupMenu() +{ + if ( ( m_xMenuContainer.is() && !m_bContextMenu ) || m_aMenuURL.isEmpty() ) + return; + + if ( m_aModuleName.isEmpty() ) + { + try + { + css::uno::Reference< css::frame::XModuleManager > xModuleManager( css::frame::ModuleManager::create( m_xContext ) ); + m_aModuleName = xModuleManager->identify( m_xFrame ); + } + catch( const css::uno::Exception& ) + {} + } + + if ( !m_xConfigManager.is() ) + { + try + { + css::uno::Reference< css::frame::XController > xController( m_xFrame->getController() ); + css::uno::Reference< css::frame::XModel > xModel( xController->getModel() ); + css::uno::Reference< css::ui::XUIConfigurationManagerSupplier > xSupplier( xModel, css::uno::UNO_QUERY_THROW ); + m_xConfigManager.set( xSupplier->getUIConfigurationManager() ); + css::uno::Reference< css::ui::XUIConfiguration > xConfig( m_xConfigManager, css::uno::UNO_QUERY_THROW ); + xConfig->addConfigurationListener( this ); + } + catch( const css::uno::RuntimeException& ) + {} + } + + if ( !m_xModuleConfigManager.is() ) + { + try + { + css::uno::Reference< css::ui::XModuleUIConfigurationManagerSupplier > xModuleCfgMgrSupplier( + css::ui::theModuleUIConfigurationManagerSupplier::get( m_xContext ) ); + m_xModuleConfigManager.set( xModuleCfgMgrSupplier->getUIConfigurationManager( m_aModuleName ) ); + css::uno::Reference< css::ui::XUIConfiguration > xConfig( m_xModuleConfigManager, css::uno::UNO_QUERY_THROW ); + xConfig->addConfigurationListener( this ); + } + catch ( const css::container::NoSuchElementException& ) + { + SAL_WARN( "fwk.uielement", "Invalid module identifier: " << m_aModuleName ); + } + catch( const css::uno::RuntimeException& ) + {} + } + + if ( !m_xMenuContainer.is() && m_xConfigManager.is() ) + { + try + { + m_xMenuContainer.set( m_xConfigManager->getSettings( m_aMenuURL, false ) ); + } + catch ( const css::container::NoSuchElementException& ) + { + // Not an error - element may exist only in the module. + } + catch ( const css::lang::IllegalArgumentException& ) + { + SAL_WARN( "fwk.uielement", "The given URL is not valid: " << m_aMenuURL ); + return; + } + } + + if ( !m_xMenuContainer.is() && m_xModuleConfigManager.is() ) + { + try + { + m_xMenuContainer.set( m_xModuleConfigManager->getSettings( m_aMenuURL, false ) ); + } + catch ( const css::container::NoSuchElementException& ) + { + SAL_WARN( "fwk.uielement", "Can not find settings for " << m_aMenuURL ); + return; + } + catch ( const css::lang::IllegalArgumentException& ) + { + SAL_WARN( "fwk.uielement", "The given URL is not valid: " << m_aMenuURL ); + return; + } + } + + if ( !m_xMenuContainer.is() ) + return; + + // Clear previous content. + if ( m_xMenuBarManager.is() ) + { + m_xMenuBarManager->dispose(); + m_xMenuBarManager.clear(); + } + resetPopupMenu( m_xPopupMenu ); + m_nNewMenuId = 1; + + // Now fill the menu with the configuration data. + framework::MenuBarManager::FillMenu( m_nNewMenuId, m_xPopupMenu->GetMenu(), m_aModuleName, m_xMenuContainer, m_xDispatchProvider ); + + // For context menus, add object verbs. + if ( !m_bContextMenu ) + return; + + css::util::URL aObjectMenuURL; + aObjectMenuURL.Complete = ".uno:ObjectMenue"; + m_xURLTransformer->parseStrict( aObjectMenuURL ); + css::uno::Reference< css::frame::XDispatchProvider > xDispatchProvider( m_xFrame, css::uno::UNO_QUERY ); + css::uno::Reference< css::frame::XDispatch > xDispatch( xDispatchProvider->queryDispatch( aObjectMenuURL, OUString(), 0 ) ); + if ( xDispatch.is() ) + { + xDispatch->addStatusListener( this, aObjectMenuURL ); + xDispatch->removeStatusListener( this, aObjectMenuURL ); + } +} + +void ResourceMenuController::statusChanged( const css::frame::FeatureStateEvent& rEvent ) +{ + css::uno::Sequence< css::embed::VerbDescriptor > aVerbs; + if ( rEvent.IsEnabled && ( rEvent.State >>= aVerbs ) ) + addVerbs( aVerbs ); +} + +void ResourceMenuController::addVerbs( const css::uno::Sequence< css::embed::VerbDescriptor >& rVerbs ) +{ + // Check if the document is read-only. + css::uno::Reference< css::frame::XController > xController( m_xFrame->getController() ); + css::uno::Reference< css::frame::XStorable > xStorable; + if ( xController.is() ) + xStorable.set( xController->getModel(), css::uno::UNO_QUERY ); + + bool bReadOnly = xStorable.is() && xStorable->isReadonly(); + Menu* pVCLMenu = m_xPopupMenu->GetMenu(); + + for ( const auto& rVerb : rVerbs ) + { + if ( !( rVerb.VerbAttributes & css::embed::VerbAttributes::MS_VERBATTR_ONCONTAINERMENU ) || + ( bReadOnly && !( rVerb.VerbAttributes & css::embed::VerbAttributes::MS_VERBATTR_NEVERDIRTIES ) ) ) + continue; + + pVCLMenu->InsertItem( m_nNewMenuId, rVerb.VerbName ); + pVCLMenu->SetItemCommand( m_nNewMenuId, ".uno:ObjectMenue?VerbID:short=" + OUString::number( rVerb.VerbID ) ); + ++m_nNewMenuId; + } +} + +void ResourceMenuController::itemActivated( const css::awt::MenuEvent& /*rEvent*/ ) +{ + // Must initialize MenuBarManager here, because we want to let the app do context menu interception before. + if ( !m_xMenuBarManager.is() ) + { + m_xMenuBarManager.set( new framework::MenuBarManager( + m_xContext, m_xFrame, m_xURLTransformer, m_xDispatchProvider, m_aModuleName, m_xPopupMenu->GetMenu(), false, !m_bContextMenu && !m_bInToolbar ) ); + m_xFrame->addFrameActionListener( m_xMenuBarManager ); + } +} + +void ResourceMenuController::itemSelected( const css::awt::MenuEvent& /*rEvent*/ ) +{ + // Must override this, because we are managed by MenuBarManager, so don't want the handler found in the base class. +} + +void ResourceMenuController::elementInserted( const css::ui::ConfigurationEvent& rEvent ) +{ + if ( rEvent.ResourceURL == m_aMenuURL ) + m_xMenuContainer.clear(); +} + +void ResourceMenuController::elementRemoved( const css::ui::ConfigurationEvent& rEvent ) +{ + elementInserted( rEvent ); +} + +void ResourceMenuController::elementReplaced( const css::ui::ConfigurationEvent& rEvent ) +{ + elementInserted( rEvent ); +} + +void ResourceMenuController::disposing( const css::lang::EventObject& rEvent ) +{ + if ( rEvent.Source == m_xConfigManager ) + m_xConfigManager.clear(); + else if ( rEvent.Source == m_xModuleConfigManager ) + m_xModuleConfigManager.clear(); + else + { + if ( m_xMenuBarManager.is() ) + { + if (m_xFrame.is()) + m_xFrame->removeFrameActionListener( m_xMenuBarManager ); + + m_xMenuBarManager->dispose(); + m_xMenuBarManager.clear(); + } + svt::PopupMenuControllerBase::disposing( rEvent ); + } +} + +void ResourceMenuController::disposing(std::unique_lock& rGuard) +{ + css::uno::Reference< css::ui::XUIConfiguration > xConfig( m_xConfigManager, css::uno::UNO_QUERY ); + if ( xConfig.is() ) + xConfig->removeConfigurationListener( this ); + + css::uno::Reference< css::ui::XUIConfiguration > xModuleConfig( m_xModuleConfigManager, css::uno::UNO_QUERY ); + if ( xModuleConfig.is() ) + xModuleConfig->removeConfigurationListener( this ); + + m_xConfigManager.clear(); + m_xModuleConfigManager.clear(); + m_xMenuContainer.clear(); + m_xDispatchProvider.clear(); + if ( m_xMenuBarManager.is() ) + { + if (m_xFrame.is()) + m_xFrame->removeFrameActionListener( m_xMenuBarManager ); + + m_xMenuBarManager->dispose(); + m_xMenuBarManager.clear(); + } + + svt::PopupMenuControllerBase::disposing(rGuard); +} + +OUString ResourceMenuController::getImplementationName() +{ + if ( m_bToolbarContainer ) + return "com.sun.star.comp.framework.ToolbarAsMenuController"; + + return "com.sun.star.comp.framework.ResourceMenuController"; +} + +css::uno::Sequence< OUString > ResourceMenuController::getSupportedServiceNames() +{ + return { "com.sun.star.frame.PopupMenuController" }; +} + +class SaveAsMenuController : public ResourceMenuController +{ +public: + SaveAsMenuController( const css::uno::Reference< css::uno::XComponentContext >& rContext, + const css::uno::Sequence< css::uno::Any >& rArgs ); + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + +private: + virtual void impl_setPopupMenu() override; +}; + +SaveAsMenuController::SaveAsMenuController( const css::uno::Reference< css::uno::XComponentContext >& rContext, + const css::uno::Sequence< css::uno::Any >& rArgs ) + : ResourceMenuController( rContext, rArgs, false ) +{ +} + +void InsertItem(const css::uno::Reference& rPopupMenu, + const OUString& rCommand) +{ + sal_uInt16 nItemId = rPopupMenu->getItemCount() + 1; + rPopupMenu->insertItem(nItemId, OUString(), 0, -1); + rPopupMenu->setCommand(nItemId, rCommand); +} + +void SaveAsMenuController::impl_setPopupMenu() +{ + SolarMutexGuard aGuard; + + InsertItem(m_xPopupMenu, ".uno:SaveAs"); + InsertItem(m_xPopupMenu, ".uno:ExportTo"); + InsertItem(m_xPopupMenu, ".uno:SaveACopy"); + InsertItem(m_xPopupMenu, ".uno:SaveAsTemplate"); + m_xPopupMenu->insertSeparator(-1); + InsertItem(m_xPopupMenu, ".uno:SaveAsRemote"); +} + +OUString SaveAsMenuController::getImplementationName() +{ + return "com.sun.star.comp.framework.SaveAsMenuController"; +} + +class WindowListMenuController : public ResourceMenuController +{ +public: + WindowListMenuController( const css::uno::Reference< css::uno::XComponentContext >& rxContext, + const css::uno::Sequence< css::uno::Any >& rxArgs ) + : ResourceMenuController(rxContext, rxArgs, false) {} + + // XMenuListener + void SAL_CALL itemActivated( const css::awt::MenuEvent& rEvent ) override; + void SAL_CALL itemSelected( const css::awt::MenuEvent& rEvent ) override; + + // XServiceInfo + OUString SAL_CALL getImplementationName() override; + +private: + void impl_setPopupMenu() override; +}; + +constexpr sal_uInt16 START_ITEMID_WINDOWLIST = 4600; +constexpr sal_uInt16 END_ITEMID_WINDOWLIST = 4699; + +void WindowListMenuController::itemActivated( const css::awt::MenuEvent& rEvent ) +{ + ResourceMenuController::itemActivated( rEvent ); + + // update window list + ::std::vector< OUString > aNewWindowListVector; + + css::uno::Reference< css::frame::XDesktop2 > xDesktop = css::frame::Desktop::create( m_xContext ); + + sal_uInt16 nActiveItemId = 0; + sal_uInt16 nItemId = START_ITEMID_WINDOWLIST; + + css::uno::Reference< css::frame::XFrame > xCurrentFrame = xDesktop->getCurrentFrame(); + css::uno::Reference< css::container::XIndexAccess > xList = xDesktop->getFrames(); + sal_Int32 nFrameCount = xList->getCount(); + aNewWindowListVector.reserve(nFrameCount); + for (sal_Int32 i=0; i xFrame; + xList->getByIndex(i) >>= xFrame; + + if (xFrame.is()) + { + if ( xFrame == xCurrentFrame ) + nActiveItemId = nItemId; + + VclPtr pWin = VCLUnoHelper::GetWindow( xFrame->getContainerWindow() ); + OUString sWindowTitle; + if ( pWin && pWin->IsVisible() ) + sWindowTitle = pWin->GetText(); + + // tdf#101658 In case the frame is embedded somewhere, LO has no control over it. + // So we just skip it. + if ( sWindowTitle.isEmpty() ) + continue; + + aNewWindowListVector.push_back( sWindowTitle ); + ++nItemId; + } + } + + { + SolarMutexGuard g; + + Menu* pVCLMenu = m_xPopupMenu->GetMenu(); + int nItemCount = pVCLMenu->GetItemCount(); + + if ( nItemCount > 0 ) + { + // remove all old window list entries from menu + sal_uInt16 nPos = pVCLMenu->GetItemPos( START_ITEMID_WINDOWLIST ); + for ( sal_uInt16 n = nPos; n < pVCLMenu->GetItemCount(); ) + pVCLMenu->RemoveItem( n ); + + if ( pVCLMenu->GetItemType( pVCLMenu->GetItemCount()-1 ) == MenuItemType::SEPARATOR ) + pVCLMenu->RemoveItem( pVCLMenu->GetItemCount()-1 ); + } + + if ( !aNewWindowListVector.empty() ) + { + // append new window list entries to menu + pVCLMenu->InsertSeparator(); + nItemId = START_ITEMID_WINDOWLIST; + const sal_uInt32 nCount = aNewWindowListVector.size(); + for ( sal_uInt32 i = 0; i < nCount; i++ ) + { + pVCLMenu->InsertItem( nItemId, aNewWindowListVector.at( i ), MenuItemBits::RADIOCHECK ); + if ( nItemId == nActiveItemId ) + pVCLMenu->CheckItem( nItemId ); + ++nItemId; + } + } + } +} + +void WindowListMenuController::itemSelected( const css::awt::MenuEvent& rEvent ) +{ + if ( rEvent.MenuId < START_ITEMID_WINDOWLIST || rEvent.MenuId > END_ITEMID_WINDOWLIST ) + return; + + // window list menu item selected + css::uno::Reference< css::frame::XDesktop2 > xDesktop = css::frame::Desktop::create( m_xContext ); + + sal_uInt16 nTaskId = START_ITEMID_WINDOWLIST; + css::uno::Reference< css::container::XIndexAccess > xList = xDesktop->getFrames(); + sal_Int32 nCount = xList->getCount(); + for ( sal_Int32 i=0; i xFrame; + xList->getByIndex(i) >>= xFrame; + if ( xFrame.is() && nTaskId == rEvent.MenuId ) + { + VclPtr pWin = VCLUnoHelper::GetWindow( xFrame->getContainerWindow() ); + pWin->GrabFocus(); + pWin->ToTop( ToTopFlags::RestoreWhenMin ); + break; + } + + nTaskId++; + } +} + +void WindowListMenuController::impl_setPopupMenu() +{ + // Make this controller work also with initially empty + // menu, which PopupMenu::ImplExecute doesn't allow. + if (m_xPopupMenu.is() && !m_xPopupMenu->getItemCount()) + m_xPopupMenu->insertSeparator(0); +} + +OUString WindowListMenuController::getImplementationName() +{ + return "com.sun.star.comp.framework.WindowListMenuController"; +} + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_framework_ResourceMenuController_get_implementation( + css::uno::XComponentContext* context, + css::uno::Sequence< css::uno::Any > const & args ) +{ + return cppu::acquire( new ResourceMenuController( context, args, false ) ); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_framework_ToolbarAsMenuController_get_implementation( + css::uno::XComponentContext* context, + css::uno::Sequence< css::uno::Any > const & args ) +{ + return cppu::acquire( new ResourceMenuController( context, args, true ) ); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_framework_WindowListMenuController_get_implementation( + css::uno::XComponentContext* context, + css::uno::Sequence< css::uno::Any > const & args ) +{ + return cppu::acquire( new WindowListMenuController( context, args ) ); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_framework_SaveAsMenuController_get_implementation( + css::uno::XComponentContext* context, + css::uno::Sequence< css::uno::Any > const & args ) +{ + return cppu::acquire( new SaveAsMenuController( context, args ) ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uielement/spinfieldtoolbarcontroller.cxx b/framework/source/uielement/spinfieldtoolbarcontroller.cxx new file mode 100644 index 0000000000..9ba295f309 --- /dev/null +++ b/framework/source/uielement/spinfieldtoolbarcontroller.cxx @@ -0,0 +1,452 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::frame; +using namespace ::com::sun::star::util; + +namespace framework +{ + +// Wrapper class to notify controller about events from combobox. +// Unfortunaltly the events are notified through virtual methods instead +// of Listeners. + +class SpinfieldControl final : public InterimItemWindow +{ +public: + SpinfieldControl(vcl::Window* pParent, SpinfieldToolbarController* pSpinfieldToolbarController); + virtual ~SpinfieldControl() override; + virtual void dispose() override; + + Formatter& GetFormatter() + { + return m_xWidget->GetFormatter(); + } + + OUString get_entry_text() const { return m_xWidget->get_text(); } + + DECL_LINK(ValueChangedHdl, weld::FormattedSpinButton&, void); + DECL_LINK(FormatOutputHdl, LinkParamNone*, bool); + DECL_LINK(ParseInputHdl, sal_Int64*, TriState); + DECL_LINK(ModifyHdl, weld::Entry&, void); + DECL_LINK(ActivateHdl, weld::Entry&, bool); + DECL_LINK(FocusInHdl, weld::Widget&, void); + DECL_LINK(FocusOutHdl, weld::Widget&, void); + DECL_LINK(KeyInputHdl, const ::KeyEvent&, bool); + +private: + std::unique_ptr m_xWidget; + SpinfieldToolbarController* m_pSpinfieldToolbarController; +}; + +SpinfieldControl::SpinfieldControl(vcl::Window* pParent, SpinfieldToolbarController* pSpinfieldToolbarController) + : InterimItemWindow(pParent, "svt/ui/spinfieldcontrol.ui", "SpinFieldControl") + , m_xWidget(m_xBuilder->weld_formatted_spin_button("spinbutton")) + , m_pSpinfieldToolbarController(pSpinfieldToolbarController) +{ + InitControlBase(m_xWidget.get()); + + m_xWidget->connect_focus_in(LINK(this, SpinfieldControl, FocusInHdl)); + m_xWidget->connect_focus_out(LINK(this, SpinfieldControl, FocusOutHdl)); + Formatter& rFormatter = m_xWidget->GetFormatter(); + rFormatter.SetOutputHdl(LINK(this, SpinfieldControl, FormatOutputHdl)); + rFormatter.SetInputHdl(LINK(this, SpinfieldControl, ParseInputHdl)); + m_xWidget->connect_value_changed(LINK(this, SpinfieldControl, ValueChangedHdl)); + m_xWidget->connect_changed(LINK(this, SpinfieldControl, ModifyHdl)); + m_xWidget->connect_activate(LINK(this, SpinfieldControl, ActivateHdl)); + m_xWidget->connect_key_press(LINK(this, SpinfieldControl, KeyInputHdl)); + + // so a later narrow size request can stick + m_xWidget->set_width_chars(3); + m_xWidget->set_size_request(42, -1); + + SetSizePixel(get_preferred_size()); +} + +IMPL_LINK(SpinfieldControl, KeyInputHdl, const ::KeyEvent&, rKEvt, bool) +{ + return ChildKeyInput(rKEvt); +} + +IMPL_LINK(SpinfieldControl, ParseInputHdl, sal_Int64*, result, TriState) +{ + *result = m_xWidget->get_text().toDouble() * weld::SpinButton::Power10(m_xWidget->GetFormatter().GetDecimalDigits()); + return TRISTATE_TRUE; +} + +SpinfieldControl::~SpinfieldControl() +{ + disposeOnce(); +} + +void SpinfieldControl::dispose() +{ + m_pSpinfieldToolbarController = nullptr; + m_xWidget.reset(); + InterimItemWindow::dispose(); +} + +IMPL_LINK_NOARG(SpinfieldControl, ValueChangedHdl, weld::FormattedSpinButton&, void) +{ + if (m_pSpinfieldToolbarController) + m_pSpinfieldToolbarController->execute(0); +} + +IMPL_LINK_NOARG(SpinfieldControl, ModifyHdl, weld::Entry&, void) +{ + if (m_pSpinfieldToolbarController) + m_pSpinfieldToolbarController->Modify(); +} + +IMPL_LINK_NOARG(SpinfieldControl, FocusInHdl, weld::Widget&, void) +{ + if (m_pSpinfieldToolbarController) + m_pSpinfieldToolbarController->GetFocus(); +} + +IMPL_LINK_NOARG(SpinfieldControl, FocusOutHdl, weld::Widget&, void) +{ + if (m_pSpinfieldToolbarController) + m_pSpinfieldToolbarController->LoseFocus(); +} + +IMPL_LINK_NOARG(SpinfieldControl, ActivateHdl, weld::Entry&, bool) +{ + bool bConsumed = false; + if (m_pSpinfieldToolbarController) + { + m_pSpinfieldToolbarController->Activate(); + bConsumed = true; + } + return bConsumed; +} + +IMPL_LINK_NOARG(SpinfieldControl, FormatOutputHdl, LinkParamNone*, bool) +{ + OUString aText = m_pSpinfieldToolbarController->FormatOutputString(m_xWidget->GetFormatter().GetValue()); + m_xWidget->set_text(aText); + return true; +} + +SpinfieldToolbarController::SpinfieldToolbarController( + const Reference< XComponentContext >& rxContext, + const Reference< XFrame >& rFrame, + ToolBox* pToolbar, + ToolBoxItemId nID, + sal_Int32 nWidth, + const OUString& aCommand ) : + ComplexToolbarController( rxContext, rFrame, pToolbar, nID, aCommand ) + , m_bFloat( false ) + , m_nMax( 0.0 ) + , m_nMin( 0.0 ) + , m_nValue( 0.0 ) + , m_nStep( 0.0 ) + , m_pSpinfieldControl( nullptr ) +{ + m_pSpinfieldControl = VclPtr::Create(m_xToolbar, this); + if ( nWidth == 0 ) + nWidth = 100; + + // SpinFieldControl ctor has set a suitable height already + auto nHeight = m_pSpinfieldControl->GetSizePixel().Height(); + + m_pSpinfieldControl->SetSizePixel( ::Size( nWidth, nHeight )); + m_xToolbar->SetItemWindow( m_nID, m_pSpinfieldControl ); +} + +SpinfieldToolbarController::~SpinfieldToolbarController() +{ +} + +void SAL_CALL SpinfieldToolbarController::dispose() +{ + SolarMutexGuard aSolarMutexGuard; + + m_xToolbar->SetItemWindow( m_nID, nullptr ); + m_pSpinfieldControl.disposeAndClear(); + + ComplexToolbarController::dispose(); +} + +Sequence SpinfieldToolbarController::getExecuteArgs(sal_Int16 KeyModifier) const +{ + OUString aSpinfieldText = m_pSpinfieldControl->get_entry_text(); + + // Add key modifier to argument list + auto aArgs0 = comphelper::makePropertyValue("KeyModifier", KeyModifier); + auto aArgs1 = comphelper::makePropertyValue("Value", m_bFloat ? Any(aSpinfieldText.toDouble()) + : Any(aSpinfieldText.toInt32())); + return { aArgs0, aArgs1 }; +} + +void SpinfieldToolbarController::Modify() +{ + notifyTextChanged(m_pSpinfieldControl->get_entry_text()); +} + +void SpinfieldToolbarController::GetFocus() +{ + notifyFocusGet(); +} + +void SpinfieldToolbarController::LoseFocus() +{ + notifyFocusLost(); +} + +void SpinfieldToolbarController::Activate() +{ + // Call execute only with non-empty text + if (!m_pSpinfieldControl->get_entry_text().isEmpty()) + execute(0); +} + +void SpinfieldToolbarController::executeControlCommand( const css::frame::ControlCommand& rControlCommand ) +{ + OUString aValue; + OUString aMax; + OUString aMin; + OUString aStep; + bool bFloatValue( false ); + + if ( rControlCommand.Command == "SetStep" ) + { + for ( auto const & arg : rControlCommand.Arguments ) + { + if ( arg.Name == "Step" ) + { + sal_Int32 nValue; + double fValue; + bool bFloat( false ); + if ( impl_getValue( arg.Value, nValue, fValue, bFloat )) + aStep = bFloat ? OUString::number( fValue ) : + OUString( OUString::number( nValue )); + break; + } + } + } + else if ( rControlCommand.Command == "SetValue" ) + { + for ( auto const & arg : rControlCommand.Arguments ) + { + if ( arg.Name == "Value" ) + { + sal_Int32 nValue; + double fValue; + bool bFloat( false ); + + if ( impl_getValue( arg.Value, nValue, fValue, bFloat )) + { + aValue = bFloat ? OUString::number( fValue ) : + OUString( OUString::number( nValue )); + bFloatValue = bFloat; + } + break; + } + } + } + else if ( rControlCommand.Command == "SetValues" ) + { + for ( auto const & arg : rControlCommand.Arguments ) + { + sal_Int32 nValue; + double fValue; + bool bFloat( false ); + + OUString aName = arg.Name; + if ( impl_getValue( arg.Value, nValue, fValue, bFloat )) + { + if ( aName == "Value" ) + { + aValue = bFloat ? OUString::number( fValue ) : + OUString( OUString::number( nValue )); + bFloatValue = bFloat; + } + else if ( aName == "Step" ) + aStep = bFloat ? OUString::number( fValue ) : + OUString( OUString::number( nValue )); + else if ( aName == "LowerLimit" ) + aMin = bFloat ? OUString::number( fValue ) : + OUString( OUString::number( nValue )); + else if ( aName == "UpperLimit" ) + aMax = bFloat ? OUString::number( fValue ) : + OUString( OUString::number( nValue )); + } + else if ( aName == "OutputFormat" ) + arg.Value >>= m_aOutFormat; + } + } + else if ( rControlCommand.Command == "SetLowerLimit" ) + { + for ( auto const & arg : rControlCommand.Arguments ) + { + if ( arg.Name == "LowerLimit" ) + { + sal_Int32 nValue; + double fValue; + bool bFloat( false ); + if ( impl_getValue( arg.Value, nValue, fValue, bFloat )) + aMin = bFloat ? OUString::number( fValue ) : + OUString( OUString::number( nValue )); + break; + } + } + } + else if ( rControlCommand.Command == "SetUpperLimit" ) + { + for ( auto const & arg : rControlCommand.Arguments ) + { + if ( arg.Name == "UpperLimit" ) + { + sal_Int32 nValue; + double fValue; + bool bFloat( false ); + if ( impl_getValue( arg.Value, nValue, fValue, bFloat )) + aMax = bFloat ? OUString::number( fValue ) : + OUString( OUString::number( nValue )); + break; + } + } + } + else if ( rControlCommand.Command == "SetOutputFormat" ) + { + for ( auto const & arg : rControlCommand.Arguments ) + { + if ( arg.Name == "OutputFormat" ) + { + arg.Value >>= m_aOutFormat; + break; + } + } + } + + Formatter& rFormatter = m_pSpinfieldControl->GetFormatter(); + + // Check values and set members + if (bFloatValue) + rFormatter.SetDecimalDigits(2); + if ( !aValue.isEmpty() ) + { + m_bFloat = bFloatValue; + m_nValue = aValue.toDouble(); + rFormatter.SetValue(m_nValue); + } + if ( !aMax.isEmpty() ) + { + m_nMax = aMax.toDouble(); + rFormatter.SetMaxValue(m_nMax); + } + if ( !aMin.isEmpty() ) + { + m_nMin = aMin.toDouble(); + rFormatter.SetMinValue(m_nMin); + } + if ( !aStep.isEmpty() ) + { + m_nStep = aStep.toDouble(); + rFormatter.SetSpinSize(m_nStep); + } +} + +bool SpinfieldToolbarController::impl_getValue( + const Any& rAny, sal_Int32& nValue, double& fValue, bool& bFloat ) +{ + using ::com::sun::star::uno::TypeClass; + + bool bValueValid( false ); + + bFloat = false; + TypeClass aTypeClass = rAny.getValueTypeClass(); + if (( aTypeClass == TypeClass( typelib_TypeClass_LONG )) || + ( aTypeClass == TypeClass( typelib_TypeClass_SHORT )) || + ( aTypeClass == TypeClass( typelib_TypeClass_BYTE ))) + bValueValid = rAny >>= nValue; + else if (( aTypeClass == TypeClass( typelib_TypeClass_FLOAT )) || + ( aTypeClass == TypeClass( typelib_TypeClass_DOUBLE ))) + { + bValueValid = rAny >>= fValue; + bFloat = true; + } + + return bValueValid; +} + +OUString SpinfieldToolbarController::FormatOutputString( double fValue ) +{ + if ( m_aOutFormat.isEmpty() ) + { + if ( m_bFloat ) + return OUString::number( fValue ); + else + return OUString::number( sal_Int32( fValue )); + } + else + { +#ifdef _WIN32 + sal_Unicode aBuffer[128]; + + aBuffer[0] = 0; + if ( m_bFloat ) + _snwprintf( o3tl::toW(aBuffer), SAL_N_ELEMENTS(aBuffer), o3tl::toW(m_aOutFormat.getStr()), fValue ); + else + _snwprintf( o3tl::toW(aBuffer), SAL_N_ELEMENTS(aBuffer), o3tl::toW(m_aOutFormat.getStr()), sal_Int32( fValue )); + + return OUString(aBuffer); +#else + // Currently we have no support for a format string using sal_Unicode. wchar_t + // is 32 bit on Unix platform! + char aBuffer[128]; + + OString aFormat = OUStringToOString( m_aOutFormat, osl_getThreadTextEncoding() ); + if ( m_bFloat ) + snprintf( aBuffer, 128, aFormat.getStr(), fValue ); + else + snprintf( aBuffer, 128, aFormat.getStr(), static_cast( fValue )); + + sal_Int32 nSize = strlen( aBuffer ); + std::string_view aTmp( aBuffer, nSize ); + return OStringToOUString( aTmp, osl_getThreadTextEncoding() ); +#endif + } +} + +} // namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uielement/statusbar.cxx b/framework/source/uielement/statusbar.cxx new file mode 100644 index 0000000000..7189e6615a --- /dev/null +++ b/framework/source/uielement/statusbar.cxx @@ -0,0 +1,89 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include + +namespace framework +{ + +FrameworkStatusBar::FrameworkStatusBar( + vcl::Window* pParent, + WinBits nWinBits ) : + StatusBar( pParent, nWinBits ), + m_pMgr( nullptr ) +{ + // set optimal size + SetOutputSizePixel( CalcWindowSizePixel() ); +} + +void FrameworkStatusBar::SetStatusBarManager( StatusBarManager* pStatusBarManager ) +{ + SolarMutexGuard aSolarMutexGuard; + m_pMgr = pStatusBarManager; +} + +void FrameworkStatusBar::UserDraw(const UserDrawEvent& rUDEvt) +{ + if ( m_pMgr ) + m_pMgr->UserDraw( rUDEvt ); +} + +void FrameworkStatusBar::Command( const CommandEvent& rEvt ) +{ + if ( m_pMgr ) + m_pMgr->Command( rEvt ); +} + +void FrameworkStatusBar::StateChanged( StateChangedType ) +{ +} + +void FrameworkStatusBar::DataChanged( const DataChangedEvent& rDCEvt ) +{ + StatusBar::DataChanged( rDCEvt ); + if ( m_pMgr ) + m_pMgr->DataChanged( rDCEvt ); +} + +void FrameworkStatusBar::MouseMove( const MouseEvent& rMEvt ) +{ + StatusBar::MouseMove( rMEvt ); + if ( m_pMgr ) + m_pMgr->MouseMove( rMEvt ); +} + +void FrameworkStatusBar::MouseButtonDown( const MouseEvent& rMEvt ) +{ + StatusBar::MouseButtonDown( rMEvt ); + if ( m_pMgr ) + m_pMgr->MouseButtonDown( rMEvt ); +} + +void FrameworkStatusBar::MouseButtonUp( const MouseEvent& rMEvt ) +{ + StatusBar::MouseButtonUp( rMEvt ); + if ( m_pMgr ) + m_pMgr->MouseButtonUp( rMEvt ); +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uielement/statusbaritem.cxx b/framework/source/uielement/statusbaritem.cxx new file mode 100644 index 0000000000..d9c4b2ccfd --- /dev/null +++ b/framework/source/uielement/statusbaritem.cxx @@ -0,0 +1,234 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include + +#include + +using namespace com::sun::star::ui; + +namespace framework +{ + +namespace +{ +sal_uInt16 impl_convertItemBitsToItemStyle( StatusBarItemBits nItemBits ) +{ + sal_uInt16 nStyle( 0 ); + + if ( nItemBits & StatusBarItemBits::Right ) + nStyle |= ItemStyle::ALIGN_RIGHT; + else if ( nItemBits & StatusBarItemBits::Left ) + nStyle |= ItemStyle::ALIGN_LEFT; + else + nStyle |= ItemStyle::ALIGN_CENTER; + + if ( nItemBits & StatusBarItemBits::Flat ) + nStyle |= ItemStyle::DRAW_FLAT; + else if ( nItemBits & StatusBarItemBits::Out ) + nStyle |= ItemStyle::DRAW_OUT3D; + else + nStyle |= ItemStyle::DRAW_IN3D; + + if ( nItemBits & StatusBarItemBits::AutoSize ) + nStyle |= ItemStyle::AUTO_SIZE; + + if ( nItemBits & StatusBarItemBits::UserDraw ) + nStyle |= ItemStyle::OWNER_DRAW; + + return nStyle; +} +} + +StatusbarItem::StatusbarItem( + StatusBar *pStatusBar, + sal_uInt16 nId, + OUString aCommand ) + : m_pStatusBar( pStatusBar ) + , m_nId( nId ) + , m_nStyle( 0 ) + , m_aCommand(std::move( aCommand )) +{ + if ( m_pStatusBar ) + m_nStyle = impl_convertItemBitsToItemStyle( + m_pStatusBar->GetItemBits( m_nId ) ); +} + +StatusbarItem::~StatusbarItem() +{ +} + +void StatusbarItem::disposing(std::unique_lock&) +{ + m_pStatusBar = nullptr; +} + +OUString SAL_CALL StatusbarItem::getCommand() +{ + return m_aCommand; +} + +::sal_uInt16 SAL_CALL StatusbarItem::getItemId() +{ + return m_nId; +} + +::sal_uInt32 SAL_CALL StatusbarItem::getWidth() +{ + SolarMutexGuard aGuard; + if ( m_pStatusBar ) + return m_pStatusBar->GetItemWidth( m_nId ); + + return ::sal_uInt32(0); +} + +::sal_uInt16 SAL_CALL StatusbarItem::getStyle() +{ + std::unique_lock aGuard( m_aMutex ); + return m_nStyle; +} + +::sal_Int32 SAL_CALL StatusbarItem::getOffset() +{ + SolarMutexGuard aGuard; + if ( m_pStatusBar ) + return m_pStatusBar->GetItemOffset( m_nId ); + + return 0; +} + +css::awt::Rectangle SAL_CALL StatusbarItem::getItemRect() +{ + SolarMutexGuard aGuard; + css::awt::Rectangle aAWTRect; + if ( m_pStatusBar ) + { + tools::Rectangle aRect = m_pStatusBar->GetItemRect( m_nId ); + return css::awt::Rectangle( aRect.Left(), + aRect.Top(), + aRect.GetWidth(), + aRect.GetHeight() ); + } + + return aAWTRect; +} + +OUString SAL_CALL StatusbarItem::getText() +{ + SolarMutexGuard aGuard; + if ( m_pStatusBar ) + return m_pStatusBar->GetItemText( m_nId ); + + return OUString(); +} + +void SAL_CALL StatusbarItem::setText( const OUString& rText ) +{ + SolarMutexGuard aGuard; + if ( m_pStatusBar ) + m_pStatusBar->SetItemText( m_nId, rText ); +} + +OUString SAL_CALL StatusbarItem::getHelpText() +{ + SolarMutexGuard aGuard; + if ( m_pStatusBar ) + return m_pStatusBar->GetHelpText( m_nId ); + + return OUString(); +} + +void SAL_CALL StatusbarItem::setHelpText( const OUString& rHelpText ) +{ + SolarMutexGuard aGuard; + if ( m_pStatusBar ) + m_pStatusBar->SetHelpText( m_nId, rHelpText ); +} + +OUString SAL_CALL StatusbarItem::getQuickHelpText() +{ + SolarMutexGuard aGuard; + if ( m_pStatusBar ) + return m_pStatusBar->GetHelpText( m_nId ); + + return OUString(); +} + +void SAL_CALL StatusbarItem::setQuickHelpText( const OUString& rQuickHelpText ) +{ + SolarMutexGuard aGuard; + if ( m_pStatusBar ) + m_pStatusBar->SetQuickHelpText( m_nId, rQuickHelpText ); +} + +OUString SAL_CALL StatusbarItem::getAccessibleName() +{ + SolarMutexGuard aGuard; + if ( m_pStatusBar ) + return m_pStatusBar->GetAccessibleName( m_nId ); + + return OUString(); +} + +void SAL_CALL StatusbarItem::setAccessibleName( const OUString& rAccessibleName ) +{ + SolarMutexGuard aGuard; + if ( m_pStatusBar ) + m_pStatusBar->SetAccessibleName( m_nId, rAccessibleName ); +} + +sal_Bool SAL_CALL StatusbarItem::getVisible() +{ + SolarMutexGuard aGuard; + if ( m_pStatusBar ) + return m_pStatusBar->IsItemVisible( m_nId ); + + return false; +} + +void SAL_CALL StatusbarItem::setVisible( sal_Bool bVisible ) +{ + SolarMutexGuard aGuard; + if ( !m_pStatusBar ) + return; + + if ( bool(bVisible) != m_pStatusBar->IsItemVisible( m_nId ) ) + { + if ( bVisible ) + m_pStatusBar->ShowItem( m_nId ); + else + m_pStatusBar->HideItem( m_nId ); + } +} + +void SAL_CALL StatusbarItem::repaint( ) +{ + SolarMutexGuard aGuard; + if ( m_pStatusBar ) + { + m_pStatusBar->RedrawItem( m_nId ); + } +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uielement/statusbarmanager.cxx b/framework/source/uielement/statusbarmanager.cxx new file mode 100644 index 0000000000..dbc305c696 --- /dev/null +++ b/framework/source/uielement/statusbarmanager.cxx @@ -0,0 +1,656 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace ::com::sun::star; + +namespace framework +{ + +namespace +{ + +template< class MAP > +struct lcl_UpdateController +{ + void operator()( typename MAP::value_type &rElement ) const + { + try + { + if ( rElement.second.is() ) + rElement.second->update(); + } + catch ( uno::Exception& ) + { + } + } +}; + +template< class MAP > +struct lcl_RemoveController +{ + void operator()( typename MAP::value_type &rElement ) const + { + try + { + if ( rElement.second.is() ) + rElement.second->dispose(); + } + catch ( uno::Exception& ) + { + } + } +}; + +StatusBarItemBits impl_convertItemStyleToItemBits( sal_Int16 nStyle ) +{ + StatusBarItemBits nItemBits( StatusBarItemBits::NONE ); + + if (( nStyle & css::ui::ItemStyle::ALIGN_RIGHT ) == css::ui::ItemStyle::ALIGN_RIGHT ) + nItemBits |= StatusBarItemBits::Right; + else if ( nStyle & css::ui::ItemStyle::ALIGN_LEFT ) + nItemBits |= StatusBarItemBits::Left; + else + nItemBits |= StatusBarItemBits::Center; + + if (( nStyle & css::ui::ItemStyle::DRAW_FLAT ) == css::ui::ItemStyle::DRAW_FLAT ) + nItemBits |= StatusBarItemBits::Flat; + else if ( nStyle & css::ui::ItemStyle::DRAW_OUT3D ) + nItemBits |= StatusBarItemBits::Out; + else + nItemBits |= StatusBarItemBits::In; + + if (( nStyle & css::ui::ItemStyle::AUTO_SIZE ) == css::ui::ItemStyle::AUTO_SIZE ) + nItemBits |= StatusBarItemBits::AutoSize; + if ( nStyle & css::ui::ItemStyle::OWNER_DRAW ) + nItemBits |= StatusBarItemBits::UserDraw; + + if ( nStyle & css::ui::ItemStyle::MANDATORY ) + nItemBits |= StatusBarItemBits::Mandatory; + + return nItemBits; +} + +} + +StatusBarManager::StatusBarManager( + uno::Reference< uno::XComponentContext > xContext, + uno::Reference< frame::XFrame > rFrame, + StatusBar* pStatusBar ) : + m_bDisposed( false ), + m_bFrameActionRegistered( false ), + m_bUpdateControllers( false ), + m_pStatusBar( pStatusBar ), + m_xFrame(std::move( rFrame )), + m_xContext(std::move( xContext )) +{ + + m_xStatusbarControllerFactory = frame::theStatusbarControllerFactory::get( + ::comphelper::getProcessComponentContext()); + + m_pStatusBar->AdjustItemWidthsForHiDPI(); + m_pStatusBar->SetClickHdl( LINK( this, StatusBarManager, Click ) ); + m_pStatusBar->SetDoubleClickHdl( LINK( this, StatusBarManager, DoubleClick ) ); +} + +StatusBarManager::~StatusBarManager() +{ +} + +StatusBar* StatusBarManager::GetStatusBar() const +{ + SolarMutexGuard g; + return m_pStatusBar; +} + +void StatusBarManager::frameAction( const frame::FrameActionEvent& Action ) +{ + SolarMutexGuard g; + if ( Action.Action == frame::FrameAction_CONTEXT_CHANGED ) + UpdateControllers(); +} + +void SAL_CALL StatusBarManager::disposing( const lang::EventObject& Source ) +{ + SolarMutexGuard g; + + if ( m_bDisposed ) + return; + + RemoveControllers(); + + if ( Source.Source == uno::Reference< uno::XInterface >( m_xFrame, uno::UNO_QUERY )) + m_xFrame.clear(); + + m_xContext.clear(); +} + +// XComponent +void SAL_CALL StatusBarManager::dispose() +{ + uno::Reference< lang::XComponent > xThis(this ); + + { + lang::EventObject aEvent( xThis ); + std::unique_lock aGuard(m_mutex); + m_aListenerContainer.disposeAndClear( aGuard, aEvent ); + } + { + SolarMutexGuard g; + if ( m_bDisposed ) + return; + + RemoveControllers(); + + // destroy the item data + for ( sal_uInt16 n = 0; n < m_pStatusBar->GetItemCount(); n++ ) + { + AddonStatusbarItemData *pUserData = static_cast< AddonStatusbarItemData *>( + m_pStatusBar->GetItemData( m_pStatusBar->GetItemId( n ) ) ); + delete pUserData; + } + + m_pStatusBar.disposeAndClear(); + + if ( m_bFrameActionRegistered && m_xFrame.is() ) + { + try + { + m_xFrame->removeFrameActionListener( uno::Reference< frame::XFrameActionListener >(this) ); + } + catch ( const uno::Exception& ) + { + } + } + + m_xFrame.clear(); + m_xContext.clear(); + + m_bDisposed = true; + } +} + +void SAL_CALL StatusBarManager::addEventListener( const uno::Reference< lang::XEventListener >& xListener ) +{ + SolarMutexGuard g; + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + if ( m_bDisposed ) + throw lang::DisposedException(); + + std::unique_lock aGuard(m_mutex); + m_aListenerContainer.addInterface( aGuard, xListener ); +} + +void SAL_CALL StatusBarManager::removeEventListener( const uno::Reference< lang::XEventListener >& xListener ) +{ + std::unique_lock aGuard(m_mutex); + m_aListenerContainer.removeInterface( aGuard, xListener ); +} + +// XUIConfigurationListener +void SAL_CALL StatusBarManager::elementInserted( const css::ui::ConfigurationEvent& ) +{ + SolarMutexGuard g; + + if ( m_bDisposed ) + return; +} + +void SAL_CALL StatusBarManager::elementRemoved( const css::ui::ConfigurationEvent& ) +{ + SolarMutexGuard g; + + if ( m_bDisposed ) + return; +} + +void SAL_CALL StatusBarManager::elementReplaced( const css::ui::ConfigurationEvent& ) +{ + SolarMutexGuard g; + + if ( m_bDisposed ) + return; +} + +void StatusBarManager::UpdateControllers() +{ + if ( !m_bUpdateControllers ) + { + m_bUpdateControllers = true; + std::for_each( m_aControllerMap.begin(), + m_aControllerMap.end(), + lcl_UpdateController< StatusBarControllerMap >() ); + } + m_bUpdateControllers = false; +} + +void StatusBarManager::RemoveControllers() +{ + DBG_TESTSOLARMUTEX(); + assert(!m_bDisposed); + + std::for_each( m_aControllerMap.begin(), + m_aControllerMap.end(), + lcl_RemoveController< StatusBarControllerMap >() ); + m_aControllerMap.clear(); +} + +void StatusBarManager::CreateControllers() +{ + uno::Reference< awt::XWindow > xStatusbarWindow = VCLUnoHelper::GetInterface( m_pStatusBar ); + + for ( sal_uInt16 i = 0; i < m_pStatusBar->GetItemCount(); i++ ) + { + sal_uInt16 nId = m_pStatusBar->GetItemId( i ); + if ( nId == 0 ) + continue; + + OUString aCommandURL( m_pStatusBar->GetItemCommand( nId )); + bool bInit( true ); + uno::Reference< frame::XStatusbarController > xController; + AddonStatusbarItemData *pItemData = static_cast< AddonStatusbarItemData *>( m_pStatusBar->GetItemData( nId ) ); + uno::Reference< ui::XStatusbarItem > xStatusbarItem = new StatusbarItem( m_pStatusBar, nId, aCommandURL ); + + beans::PropertyValue aPropValue; + std::vector< uno::Any > aPropVector; + + aPropValue.Name = "CommandURL"; + aPropValue.Value <<= aCommandURL; + aPropVector.push_back( uno::Any( aPropValue ) ); + + aPropValue.Name = "ModuleIdentifier"; + aPropValue.Value <<= OUString(); + aPropVector.push_back( uno::Any( aPropValue ) ); + + aPropValue.Name = "Frame"; + aPropValue.Value <<= m_xFrame; + aPropVector.push_back( uno::Any( aPropValue ) ); + + // TODO remove this + aPropValue.Name = "ServiceManager"; + aPropValue.Value <<= uno::Reference(m_xContext->getServiceManager(), uno::UNO_QUERY_THROW); + aPropVector.push_back( uno::Any( aPropValue ) ); + + aPropValue.Name = "ParentWindow"; + aPropValue.Value <<= xStatusbarWindow; + aPropVector.push_back( uno::Any( aPropValue ) ); + + // TODO still needing with the css::ui::XStatusbarItem? + aPropValue.Name = "Identifier"; + aPropValue.Value <<= nId; + aPropVector.push_back( uno::Any( aPropValue ) ); + + aPropValue.Name = "StatusbarItem"; + aPropValue.Value <<= xStatusbarItem; + aPropVector.push_back( uno::Any( aPropValue ) ); + + uno::Sequence< uno::Any > aArgs( comphelper::containerToSequence( aPropVector ) ); + + // 1) UNO Statusbar controllers, registered in Controllers.xcu + if ( m_xStatusbarControllerFactory.is() && + m_xStatusbarControllerFactory->hasController( aCommandURL, "" )) + { + xController.set(m_xStatusbarControllerFactory->createInstanceWithArgumentsAndContext( + aCommandURL, aArgs, m_xContext ), + uno::UNO_QUERY ); + bInit = false; // Initialization is done through the factory service + } + + if ( !xController.is() ) + { + // 2) Old SFX2 Statusbar controllers + xController = CreateStatusBarController( m_xFrame, m_pStatusBar, nId, aCommandURL ); + if ( !xController ) + { + // 3) Is Add-on? Generic statusbar controller + if ( pItemData ) + { + xController = new GenericStatusbarController( m_xContext, + m_xFrame, + xStatusbarItem, + pItemData ); + } + else + { + // 4) Default Statusbar controller + xController = new svt::StatusbarController( m_xContext, m_xFrame, aCommandURL, nId ); + } + } + } + + m_aControllerMap[nId] = xController; + if ( bInit ) + { + xController->initialize( aArgs ); + } + } + + // add frame action listeners + if ( !m_bFrameActionRegistered && m_xFrame.is() ) + { + m_bFrameActionRegistered = true; + m_xFrame->addFrameActionListener( uno::Reference< frame::XFrameActionListener >(this) ); + } +} + +void StatusBarManager::FillStatusBar( const uno::Reference< container::XIndexAccess >& rItemContainer ) +{ + SolarMutexGuard g; + + if ( m_bDisposed || !m_pStatusBar ) + return; + + sal_uInt16 nId( 1 ); + + RemoveControllers(); + + // reset and fill command map + m_pStatusBar->Clear(); + m_aControllerMap.clear();// TODO already done in RemoveControllers + + for ( sal_Int32 n = 0; n < rItemContainer->getCount(); n++ ) + { + uno::Sequence< beans::PropertyValue > aProps; + OUString aCommandURL; + sal_Int16 nOffset( 0 ); + sal_Int16 nStyle( 0 ); + sal_Int16 nWidth( 0 ); + sal_uInt16 nType( css::ui::ItemType::DEFAULT ); + + try + { + if ( rItemContainer->getByIndex( n ) >>= aProps ) + { + for ( beans::PropertyValue const & prop : std::as_const(aProps) ) + { + if ( prop.Name == "CommandURL" ) + { + prop.Value >>= aCommandURL; + } + else if ( prop.Name == "Style" ) + { + prop.Value >>= nStyle; + } + else if ( prop.Name == "Type" ) + { + prop.Value >>= nType; + } + else if ( prop.Name == "Width" ) + { + prop.Value >>= nWidth; + } + else if ( prop.Name == "Offset" ) + { + prop.Value >>= nOffset; + } + } + + if (( nType == css::ui::ItemType::DEFAULT ) && !aCommandURL.isEmpty() ) + { + auto aProperties = vcl::CommandInfoProvider::GetCommandProperties(aCommandURL, ""); + OUString aString(vcl::CommandInfoProvider::GetLabelForCommand(aProperties)); + StatusBarItemBits nItemBits( impl_convertItemStyleToItemBits( nStyle )); + + m_pStatusBar->InsertItem( nId, nWidth, nItemBits, nOffset ); + m_pStatusBar->SetItemCommand( nId, aCommandURL ); + m_pStatusBar->SetAccessibleName( nId, aString ); + ++nId; + } + } + } + catch ( const css::lang::IndexOutOfBoundsException& ) + { + break; + } + } + + // Statusbar Merging + constexpr sal_uInt16 STATUSBAR_ITEM_STARTID = 1000; + MergeStatusbarInstructionContainer aMergeInstructions = AddonsOptions().GetMergeStatusbarInstructions(); + if ( !aMergeInstructions.empty() ) + { + const sal_uInt32 nCount = aMergeInstructions.size(); + sal_uInt16 nItemId( STATUSBAR_ITEM_STARTID ); + + for ( sal_uInt32 i = 0; i < nCount; i++ ) + { + MergeStatusbarInstruction &rInstruction = aMergeInstructions[i]; + if ( !StatusbarMerger::IsCorrectContext( rInstruction.aMergeContext ) ) + continue; + + AddonStatusbarItemContainer aItems; + StatusbarMerger::ConvertSeqSeqToVector( rInstruction.aMergeStatusbarItems, aItems ); + + sal_uInt16 nRefPos = StatusbarMerger::FindReferencePos( m_pStatusBar, rInstruction.aMergePoint ); + if ( nRefPos != STATUSBAR_ITEM_NOTFOUND ) + { + StatusbarMerger::ProcessMergeOperation( m_pStatusBar, + nRefPos, + nItemId, + rInstruction.aMergeCommand, + rInstruction.aMergeCommandParameter, + aItems ); + } + else + { + StatusbarMerger::ProcessMergeFallback( m_pStatusBar, + nItemId, + rInstruction.aMergeCommand, + rInstruction.aMergeCommandParameter, + aItems ); + } + } + } + + // Create controllers + CreateControllers(); + + // Notify controllers that they are now correctly initialized and can start listening + UpdateControllers(); +} + +void StatusBarManager::DataChanged( const DataChangedEvent& rDCEvt ) +{ + SolarMutexClearableGuard aGuard; + + if ((( rDCEvt.GetType() == DataChangedEventType::SETTINGS ) || + ( rDCEvt.GetType() == DataChangedEventType::FONTS ) || + ( rDCEvt.GetType() == DataChangedEventType::FONTSUBSTITUTION ) || + ( rDCEvt.GetType() == DataChangedEventType::DISPLAY )) && + ( rDCEvt.GetFlags() & AllSettingsFlags::STYLE )) + { + css::uno::Reference< css::frame::XLayoutManager > xLayoutManager; + css::uno::Reference< css::beans::XPropertySet > xPropSet( m_xFrame, css::uno::UNO_QUERY ); + if ( xPropSet.is() ) + xPropSet->getPropertyValue("LayoutManager") >>= xLayoutManager; + if ( xLayoutManager.is() ) + { + aGuard.clear(); + xLayoutManager->doLayout(); + } + } +} + +void StatusBarManager::UserDraw( const UserDrawEvent& rUDEvt ) +{ + SolarMutexClearableGuard aGuard; + + if ( m_bDisposed ) + return; + + sal_uInt16 nId( rUDEvt.GetItemId() ); + StatusBarControllerMap::const_iterator it = m_aControllerMap.find( nId ); + if (( nId <= 0 ) || ( it == m_aControllerMap.end() )) + return; + + uno::Reference< frame::XStatusbarController > xController( it->second ); + if (xController.is() && rUDEvt.GetRenderContext()) + { + uno::Reference< awt::XGraphics > xGraphics = rUDEvt.GetRenderContext()->CreateUnoGraphics(); + + awt::Rectangle aRect( rUDEvt.GetRect().Left(), + rUDEvt.GetRect().Top(), + rUDEvt.GetRect().GetWidth(), + rUDEvt.GetRect().GetHeight() ); + aGuard.clear(); + xController->paint(xGraphics, aRect, 0); + } +} + +void StatusBarManager::Command( const CommandEvent& rEvt ) +{ + SolarMutexGuard g; + + if ( m_bDisposed ) + return; + + if ( rEvt.GetCommand() != CommandEventId::ContextMenu ) + return; + + sal_uInt16 nId = m_pStatusBar->GetItemId( rEvt.GetMousePosPixel() ); + StatusBarControllerMap::const_iterator it = m_aControllerMap.find( nId ); + if (( nId > 0 ) && ( it != m_aControllerMap.end() )) + { + uno::Reference< frame::XStatusbarController > xController( it->second ); + if ( xController.is() ) + { + awt::Point aPos; + aPos.X = rEvt.GetMousePosPixel().X(); + aPos.Y = rEvt.GetMousePosPixel().Y(); + xController->command( aPos, awt::Command::CONTEXTMENU, true, uno::Any() ); + } + } +} + +void StatusBarManager::MouseMove( const MouseEvent& rMEvt ) +{ + MouseButton(rMEvt,&frame::XStatusbarController::mouseMove); +} + +void StatusBarManager::MouseButton( const MouseEvent& rMEvt ,sal_Bool ( SAL_CALL frame::XStatusbarController::*_pMethod )(const css::awt::MouseEvent&)) +{ + SolarMutexGuard g; + + if ( m_bDisposed ) + return; + + sal_uInt16 nId = m_pStatusBar->GetItemId( rMEvt.GetPosPixel() ); + StatusBarControllerMap::const_iterator it = m_aControllerMap.find( nId ); + if (( nId <= 0 ) || ( it == m_aControllerMap.end() )) + return; + + uno::Reference< frame::XStatusbarController > xController( it->second ); + if ( xController.is() ) + { + css::awt::MouseEvent aMouseEvent; + aMouseEvent.Buttons = rMEvt.GetButtons(); + aMouseEvent.X = rMEvt.GetPosPixel().X(); + aMouseEvent.Y = rMEvt.GetPosPixel().Y(); + aMouseEvent.ClickCount = rMEvt.GetClicks(); + (xController.get()->*_pMethod)( aMouseEvent); + } +} + +void StatusBarManager::MouseButtonDown( const MouseEvent& rMEvt ) +{ + MouseButton(rMEvt,&frame::XStatusbarController::mouseButtonDown); +} + +void StatusBarManager::MouseButtonUp( const MouseEvent& rMEvt ) +{ + MouseButton(rMEvt,&frame::XStatusbarController::mouseButtonUp); +} + +IMPL_LINK_NOARG(StatusBarManager, Click, StatusBar*, void) +{ + SolarMutexGuard g; + + if ( m_bDisposed ) + return; + + sal_uInt16 nId = m_pStatusBar->GetCurItemId(); + StatusBarControllerMap::const_iterator it = m_aControllerMap.find( nId ); + if (( nId > 0 ) && ( it != m_aControllerMap.end() )) + { + uno::Reference< frame::XStatusbarController > xController( it->second ); + if ( xController.is() ) + { + const Point aVCLPos = m_pStatusBar->GetPointerPosPixel(); + const awt::Point aAWTPoint( aVCLPos.X(), aVCLPos.Y() ); + xController->click( aAWTPoint ); + } + } +} + +IMPL_LINK_NOARG(StatusBarManager, DoubleClick, StatusBar*, void) +{ + SolarMutexGuard g; + + if ( m_bDisposed ) + return; + + sal_uInt16 nId = m_pStatusBar->GetCurItemId(); + StatusBarControllerMap::const_iterator it = m_aControllerMap.find( nId ); + if (( nId > 0 ) && ( it != m_aControllerMap.end() )) + { + uno::Reference< frame::XStatusbarController > xController( it->second ); + if ( xController.is() ) + { + const Point aVCLPos = m_pStatusBar->GetPointerPosPixel(); + const awt::Point aAWTPoint( aVCLPos.X(), aVCLPos.Y() ); + xController->doubleClick( aAWTPoint ); + } + } +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uielement/statusbarmerger.cxx b/framework/source/uielement/statusbarmerger.cxx new file mode 100644 index 0000000000..c8e6633be9 --- /dev/null +++ b/framework/source/uielement/statusbarmerger.cxx @@ -0,0 +1,237 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#include +#include + +using com::sun::star::beans::PropertyValue; +using com::sun::star::uno::Sequence; + +namespace framework +{ +namespace { + +const char16_t MERGECOMMAND_ADDAFTER[] = u"AddAfter"; +const char16_t MERGECOMMAND_ADDBEFORE[] = u"AddBefore"; +const char16_t MERGECOMMAND_REPLACE[] = u"Replace"; +const char16_t MERGECOMMAND_REMOVE[] = u"Remove"; + +void lcl_ConvertSequenceToValues( + const Sequence< PropertyValue > &rSequence, + AddonStatusbarItem &rItem ) +{ + OUString sAlignment; + bool bAutoSize = false; + bool bOwnerDraw = false; + bool bMandatory = true; + + for ( PropertyValue const & aPropVal : rSequence ) + { + if ( aPropVal.Name == "URL" ) + aPropVal.Value >>= rItem.aCommandURL; + else if ( aPropVal.Name == "Title" ) + aPropVal.Value >>= rItem.aLabel; + else if ( aPropVal.Name == "Context" ) + aPropVal.Value >>= rItem.aContext; + else if ( aPropVal.Name == "Alignment" ) + aPropVal.Value >>= sAlignment; + else if ( aPropVal.Name == "AutoSize" ) + aPropVal.Value >>= bAutoSize; + else if ( aPropVal.Name == "OwnerDraw" ) + aPropVal.Value >>= bOwnerDraw; + else if ( aPropVal.Name == "Mandatory" ) + aPropVal.Value >>= bMandatory; + else if ( aPropVal.Name == "Width" ) + { + sal_Int32 aWidth = 0; + aPropVal.Value >>= aWidth; + rItem.nWidth = sal_uInt16( aWidth ); + } + } + + StatusBarItemBits nItemBits(StatusBarItemBits::NONE); + if ( bAutoSize ) + nItemBits |= StatusBarItemBits::AutoSize; + if ( bOwnerDraw ) + nItemBits |= StatusBarItemBits::UserDraw; + if ( bMandatory ) + nItemBits |= StatusBarItemBits::Mandatory; + if ( sAlignment == "center" ) + nItemBits |= StatusBarItemBits::Center; + else if ( sAlignment == "right" ) + nItemBits |= StatusBarItemBits::Right; + else + // if unset, defaults to left alignment + nItemBits |= StatusBarItemBits::Left; + rItem.nItemBits = nItemBits; +} + +void lcl_CreateStatusbarItem( StatusBar* pStatusbar, + sal_uInt16 nPos, + sal_uInt16 nItemId, + const AddonStatusbarItem& rAddonItem ) +{ + pStatusbar->InsertItem( nItemId, + rAddonItem.nWidth, + rAddonItem.nItemBits, + STATUSBAR_OFFSET, + nPos ); + pStatusbar->SetItemCommand( nItemId, rAddonItem.aCommandURL ); + pStatusbar->SetQuickHelpText( nItemId, rAddonItem.aLabel ); + pStatusbar->SetAccessibleName( nItemId, rAddonItem.aLabel ); + + // add-on specific data + AddonStatusbarItemData *pUserData = new AddonStatusbarItemData; + pUserData->aLabel = rAddonItem.aLabel; + pStatusbar->SetItemData( nItemId, pUserData ); +} + +bool lcl_MergeItems( StatusBar* pStatusbar, + sal_uInt16 nPos, + sal_uInt16 nModIndex, + sal_uInt16& rItemId, + const AddonStatusbarItemContainer& rAddonItems ) +{ + const sal_uInt16 nSize( rAddonItems.size() ); + for ( sal_Int32 i = 0; i < nSize; i++ ) + { + const AddonStatusbarItem& rItem = rAddonItems[i]; + if ( !StatusbarMerger::IsCorrectContext( rItem.aContext ) ) + continue; + + sal_uInt16 nInsPos = nPos + nModIndex + i; + if ( nInsPos > pStatusbar->GetItemCount() ) + nInsPos = STATUSBAR_APPEND; + + lcl_CreateStatusbarItem( pStatusbar, nInsPos, rItemId, rItem ); + ++rItemId; + } + + return true; +} + +bool lcl_ReplaceItem( StatusBar* pStatusbar, + sal_uInt16 nPos, + sal_uInt16& rItemId, + const AddonStatusbarItemContainer& rAddonToolbarItems ) +{ + pStatusbar->RemoveItem( pStatusbar->GetItemId( nPos ) ); + return lcl_MergeItems( pStatusbar, nPos, 0, rItemId, rAddonToolbarItems ); +} + +bool lcl_RemoveItems( StatusBar* pStatusbar, + sal_uInt16 nPos, + std::u16string_view rMergeCommandParameter ) +{ + sal_Int32 nCount = o3tl::toInt32(rMergeCommandParameter); + if ( nCount > 0 ) + { + for ( sal_Int32 i = 0; i < nCount; i++ ) + { + if ( nPos < pStatusbar->GetItemCount() ) + pStatusbar->RemoveItem( nPos ); + } + } + return true; +} + +} + +bool StatusbarMerger::IsCorrectContext( + std::u16string_view rContext ) +{ + return rContext.empty(); +} + +bool StatusbarMerger::ConvertSeqSeqToVector( + const Sequence< Sequence< PropertyValue > > &rSequence, + AddonStatusbarItemContainer& rContainer ) +{ + for ( auto const & i : rSequence ) + { + AddonStatusbarItem aStatusBarItem; + lcl_ConvertSequenceToValues( i, aStatusBarItem ); + rContainer.push_back( aStatusBarItem ); + } + + return true; +} + +sal_uInt16 StatusbarMerger::FindReferencePos( + StatusBar* pStatusbar, + std::u16string_view rReferencePoint ) +{ + for ( sal_uInt16 nPos = 0; nPos < pStatusbar->GetItemCount(); nPos++ ) + { + const OUString rCmd = pStatusbar->GetItemCommand( pStatusbar->GetItemId( nPos ) ); + if ( rReferencePoint == rCmd ) + return nPos; + } + + return STATUSBAR_ITEM_NOTFOUND; +} + +bool StatusbarMerger::ProcessMergeOperation( + StatusBar* pStatusbar, + sal_uInt16 nPos, + sal_uInt16& rItemId, + std::u16string_view rMergeCommand, + std::u16string_view rMergeCommandParameter, + const AddonStatusbarItemContainer& rItems ) +{ + if ( rMergeCommand == MERGECOMMAND_ADDAFTER ) + return lcl_MergeItems( pStatusbar, nPos, 1, rItemId, rItems ); + else if ( rMergeCommand == MERGECOMMAND_ADDBEFORE ) + return lcl_MergeItems( pStatusbar, nPos, 0, rItemId, rItems ); + else if ( rMergeCommand == MERGECOMMAND_REPLACE ) + return lcl_ReplaceItem( pStatusbar, nPos, rItemId, rItems ); + else if ( rMergeCommand == MERGECOMMAND_REMOVE ) + return lcl_RemoveItems( pStatusbar, nPos, rMergeCommandParameter ); + + return false; +} + +bool StatusbarMerger::ProcessMergeFallback( + StatusBar* pStatusbar, + sal_uInt16& rItemId, + std::u16string_view rMergeCommand, + std::u16string_view rMergeFallback, + const AddonStatusbarItemContainer& rItems ) +{ + // fallback IGNORE or REPLACE/REMOVE item not found + if (( rMergeFallback == u"Ignore" ) || + ( rMergeCommand == MERGECOMMAND_REPLACE ) || + ( rMergeCommand == MERGECOMMAND_REMOVE ) ) + { + return true; + } + else if (( rMergeCommand == MERGECOMMAND_ADDBEFORE ) || + ( rMergeCommand == MERGECOMMAND_ADDAFTER ) ) + { + if ( rMergeFallback == u"AddFirst" ) + return lcl_MergeItems( pStatusbar, 0, 0, rItemId, rItems ); + else if ( rMergeFallback == u"AddLast" ) + return lcl_MergeItems( pStatusbar, STATUSBAR_APPEND, 0, rItemId, rItems ); + } + + return false; +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uielement/statusbarwrapper.cxx b/framework/source/uielement/statusbarwrapper.cxx new file mode 100644 index 0000000000..845a0b9269 --- /dev/null +++ b/framework/source/uielement/statusbarwrapper.cxx @@ -0,0 +1,165 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include + +#include + +#include + +#include +#include +#include + +using namespace com::sun::star::uno; +using namespace com::sun::star::beans; +using namespace com::sun::star::frame; +using namespace com::sun::star::lang; +using namespace com::sun::star::container; +using namespace com::sun::star::awt; +using namespace ::com::sun::star::ui; + +namespace framework +{ + +StatusBarWrapper::StatusBarWrapper( + css::uno::Reference< css::uno::XComponentContext > xContext + ) + : UIConfigElementWrapperBase( UIElementType::STATUSBAR ), + m_xContext(std::move( xContext )) +{ +} + +StatusBarWrapper::~StatusBarWrapper() +{ +} + +void SAL_CALL StatusBarWrapper::dispose() +{ + Reference< XComponent > xThis(this); + + css::lang::EventObject aEvent( xThis ); + m_aListenerContainer.disposeAndClear( aEvent ); + + SolarMutexGuard g; + if ( m_bDisposed ) + return; + + if ( m_xStatusBarManager.is() ) + m_xStatusBarManager->dispose(); + m_xStatusBarManager.clear(); + m_xConfigSource.clear(); + m_xConfigData.clear(); + m_xContext.clear(); + + m_bDisposed = true; + +} + +// XInitialization +void SAL_CALL StatusBarWrapper::initialize( const Sequence< Any >& aArguments ) +{ + SolarMutexGuard g; + + if ( m_bDisposed ) + throw DisposedException(); + + if ( m_bInitialized ) + return; + + UIConfigElementWrapperBase::initialize( aArguments ); + + Reference< XFrame > xFrame( m_xWeakFrame ); + if ( !(xFrame.is() && m_xConfigSource.is()) ) + return; + + // Create VCL based toolbar which will be filled with settings data + StatusBar* pStatusBar( nullptr ); + rtl::Reference pStatusBarManager; + { + SolarMutexGuard aSolarMutexGuard; + VclPtr pWindow = VCLUnoHelper::GetWindow( xFrame->getContainerWindow() ); + if ( pWindow ) + { + sal_uLong nStyles = WinBits( WB_LEFT | WB_3DLOOK ); + + pStatusBar = VclPtr::Create( pWindow, nStyles ); + pStatusBarManager = new StatusBarManager( m_xContext, xFrame, pStatusBar ); + static_cast(pStatusBar)->SetStatusBarManager( pStatusBarManager.get() ); + m_xStatusBarManager = pStatusBarManager; + } + } + + try + { + m_xConfigData = m_xConfigSource->getSettings( m_aResourceURL, false ); + if ( m_xConfigData.is() && pStatusBar && pStatusBarManager ) + { + // Fill statusbar with container contents + pStatusBarManager->FillStatusBar( m_xConfigData ); + } + } + catch ( const NoSuchElementException& ) + { + } +} + +// XUIElementSettings +void SAL_CALL StatusBarWrapper::updateSettings() +{ + SolarMutexGuard g; + + if ( m_bDisposed ) + throw DisposedException(); + + if ( !(m_bPersistent && + m_xConfigSource.is() && + m_xStatusBarManager.is()) ) + return; + + try + { + m_xConfigData = m_xConfigSource->getSettings( m_aResourceURL, false ); + if ( m_xConfigData.is() ) + m_xStatusBarManager->FillStatusBar( m_xConfigData ); + } + catch ( const NoSuchElementException& ) + { + } +} + +Reference< XInterface > SAL_CALL StatusBarWrapper::getRealInterface() +{ + SolarMutexGuard g; + + if ( m_xStatusBarManager ) + { + vcl::Window* pWindow = m_xStatusBarManager->GetStatusBar(); + if ( pWindow ) + return Reference< XInterface >( VCLUnoHelper::GetInterface( pWindow ), UNO_QUERY ); + } + + return Reference< XInterface >(); +} + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uielement/statusindicatorinterfacewrapper.cxx b/framework/source/uielement/statusindicatorinterfacewrapper.cxx new file mode 100644 index 0000000000..ba796036b0 --- /dev/null +++ b/framework/source/uielement/statusindicatorinterfacewrapper.cxx @@ -0,0 +1,102 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include + +using namespace cppu; +using namespace com::sun::star::uno; +using namespace com::sun::star::lang; +using namespace com::sun::star::beans; + +namespace framework +{ + +StatusIndicatorInterfaceWrapper::StatusIndicatorInterfaceWrapper( + const css::uno::Reference< css::lang::XComponent >& rStatusIndicatorImpl ) : + m_xStatusIndicatorImpl( rStatusIndicatorImpl ) +{ +} + +StatusIndicatorInterfaceWrapper::~StatusIndicatorInterfaceWrapper() +{ +} + +void SAL_CALL StatusIndicatorInterfaceWrapper::start( + const OUString& sText, + sal_Int32 nRange ) +{ + Reference< XComponent > xComp( m_xStatusIndicatorImpl ); + if ( xComp.is() ) + { + ProgressBarWrapper* pProgressBar = static_cast(xComp.get()); + if ( pProgressBar ) + pProgressBar->start( sText, nRange ); + } +} + +void SAL_CALL StatusIndicatorInterfaceWrapper::end() +{ + Reference< XComponent > xComp( m_xStatusIndicatorImpl ); + if ( xComp.is() ) + { + ProgressBarWrapper* pProgressBar = static_cast(xComp.get()); + if ( pProgressBar ) + pProgressBar->end(); + } +} + +void SAL_CALL StatusIndicatorInterfaceWrapper::reset() +{ + Reference< XComponent > xComp( m_xStatusIndicatorImpl ); + if ( xComp.is() ) + { + ProgressBarWrapper* pProgressBar = static_cast(xComp.get()); + if ( pProgressBar ) + pProgressBar->reset(); + } +} + +void SAL_CALL StatusIndicatorInterfaceWrapper::setText( + const OUString& sText ) +{ + Reference< XComponent > xComp( m_xStatusIndicatorImpl ); + if ( xComp.is() ) + { + ProgressBarWrapper* pProgressBar = static_cast(xComp.get()); + if ( pProgressBar ) + pProgressBar->setText( sText ); + } +} + +void SAL_CALL StatusIndicatorInterfaceWrapper::setValue( + sal_Int32 nValue ) +{ + Reference< XComponent > xComp( m_xStatusIndicatorImpl ); + if ( xComp.is() ) + { + ProgressBarWrapper* pProgressBar = static_cast(xComp.get()); + if ( pProgressBar ) + pProgressBar->setValue( nValue ); + } +} + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uielement/styletoolbarcontroller.cxx b/framework/source/uielement/styletoolbarcontroller.cxx new file mode 100644 index 0000000000..3ff1e777eb --- /dev/null +++ b/framework/source/uielement/styletoolbarcontroller.cxx @@ -0,0 +1,244 @@ +/* -*- 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 + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace { + +OUString MapFamilyToCommand( std::u16string_view rFamily ) +{ + if ( rFamily == u"ParagraphStyles" || + rFamily == u"CellStyles" || // In sc + rFamily == u"graphics" ) // In sd + return ".uno:ParaStyle"; + else if ( rFamily == u"CharacterStyles" ) + return ".uno:CharStyle"; + else if ( rFamily == u"PageStyles" ) + return ".uno:PageStyle"; + else if ( rFamily == u"FrameStyles" || + rFamily == u"GraphicStyles" ) // In sc + return ".uno:FrameStyle"; + else if ( rFamily == u"NumberingStyles" ) + return ".uno:ListStyle"; + else if ( rFamily == u"TableStyles" ) + return ".uno:TableStyle"; + + return OUString(); +} + +OUString GetDisplayFromInternalName( const css::uno::Reference< css::frame::XFrame >& rFrame, + const OUString& rStyleName, + const OUString& rFamilyName ) +{ + try + { + css::uno::Reference< css::frame::XController > xController( + rFrame->getController(), css::uno::UNO_SET_THROW ); + css::uno::Reference< css::style::XStyleFamiliesSupplier > xStylesSupplier( + xController->getModel(), css::uno::UNO_QUERY_THROW ); + css::uno::Reference< css::container::XNameAccess > xFamilies( + xStylesSupplier->getStyleFamilies(), css::uno::UNO_SET_THROW ); + + css::uno::Reference< css::container::XNameAccess > xStyleSet; + xFamilies->getByName( rFamilyName ) >>= xStyleSet; + css::uno::Reference< css::beans::XPropertySet > xStyle; + xStyleSet->getByName( rStyleName ) >>= xStyle; + + OUString aDisplayName; + if ( xStyle.is() ) + xStyle->getPropertyValue( "DisplayName" ) >>= aDisplayName; + return aDisplayName; + } + catch ( const css::uno::Exception& ) + { + // We couldn't get the display name. As a last resort we'll + // try to use the internal name, as was specified in the URL. + } + + return rStyleName; +} + +} + +namespace framework { + +StyleDispatcher::StyleDispatcher( const css::uno::Reference< css::frame::XFrame >& rFrame, + css::uno::Reference< css::util::XURLTransformer > xUrlTransformer, + const css::util::URL& rURL ) + : m_aCommand( rURL.Complete ) + , m_xUrlTransformer(std::move( xUrlTransformer )) + , m_xFrame( rFrame, css::uno::UNO_QUERY ) +{ + SAL_WARN_IF( !m_aCommand.startsWith( ".uno:StyleApply?" ), "fwk.uielement", "Wrong dispatcher!" ); + + OUString aParams = rURL.Arguments; + OUString aStyleName, aFamilyName; + sal_Int32 nIndex = 0; + do + { + std::u16string_view aParam = o3tl::getToken(aParams, 0, '&', nIndex ); + + sal_Int32 nParamIndex = 0; + std::u16string_view aParamName = o3tl::getToken(aParam, 0, '=', nParamIndex ); + if ( nParamIndex < 0 ) + break; + + if ( aParamName == u"Style:string" ) + { + std::u16string_view aValue = o3tl::getToken(aParam, 0, '=', nParamIndex ); + aStyleName = INetURLObject::decode( aValue, INetURLObject::DecodeMechanism::WithCharset ); + } + else if ( aParamName == u"FamilyName:string" ) + { + aFamilyName = o3tl::getToken(aParam, 0, '=', nParamIndex ); + } + + } while ( nIndex >= 0 ); + + m_aStatusCommand = MapFamilyToCommand( aFamilyName ); + if ( m_aStatusCommand.isEmpty() || aStyleName.isEmpty() ) + { + // We can't provide status updates for this command, but just executing + // the command should still work (given that the command is valid). + SAL_WARN( "fwk.uielement", "Unable to parse as a style command: " << m_aCommand ); + return; + } + + m_aStyleName = GetDisplayFromInternalName( rFrame, aStyleName, aFamilyName ); + if ( m_xFrame.is() ) + { + css::util::URL aStatusURL; + aStatusURL.Complete = m_aStatusCommand; + m_xUrlTransformer->parseStrict( aStatusURL ); + m_xStatusDispatch = m_xFrame->queryDispatch( aStatusURL, OUString(), 0 ); + } +} + +void StyleDispatcher::dispatch( const css::util::URL& rURL, + const css::uno::Sequence< css::beans::PropertyValue >& rArguments ) +{ + if ( !m_xFrame.is() ) + return; + + css::uno::Reference< css::frame::XDispatch > xDispatch( m_xFrame->queryDispatch( rURL, OUString(), 0 ) ); + if ( xDispatch.is() ) + xDispatch->dispatch( rURL, rArguments ); +} + +void StyleDispatcher::addStatusListener( const css::uno::Reference< css::frame::XStatusListener >& rListener, + const css::util::URL& /*rURL*/ ) +{ + if ( m_xStatusDispatch.is() ) + { + if ( !m_xOwner.is() ) + m_xOwner.set( rListener ); + + css::util::URL aStatusURL; + aStatusURL.Complete = m_aStatusCommand; + m_xUrlTransformer->parseStrict( aStatusURL ); + m_xStatusDispatch->addStatusListener( this, aStatusURL ); + } +} + +void StyleDispatcher::removeStatusListener( const css::uno::Reference< css::frame::XStatusListener >& /*rListener*/, + const css::util::URL& /*rURL*/ ) +{ + if ( m_xStatusDispatch.is() ) + { + css::util::URL aStatusURL; + aStatusURL.Complete = m_aStatusCommand; + m_xUrlTransformer->parseStrict( aStatusURL ); + m_xStatusDispatch->removeStatusListener( this, aStatusURL ); + } +} + +void StyleDispatcher::statusChanged( const css::frame::FeatureStateEvent& rEvent ) +{ + css::frame::status::Template aTemplate; + rEvent.State >>= aTemplate; + + css::frame::FeatureStateEvent aEvent; + aEvent.FeatureURL.Complete = m_aCommand; + m_xUrlTransformer->parseStrict( aEvent.FeatureURL ); + + aEvent.IsEnabled = rEvent.IsEnabled; + aEvent.Requery = rEvent.Requery; + aEvent.State <<= m_aStyleName == aTemplate.StyleName; + m_xOwner->statusChanged( aEvent ); +} + +void StyleDispatcher::disposing( const css::lang::EventObject& /*rSource*/ ) +{ + m_xStatusDispatch.clear(); +} + +StyleToolbarController::StyleToolbarController( const css::uno::Reference< css::uno::XComponentContext >& rContext, + const css::uno::Reference< css::frame::XFrame >& rFrame, + const OUString& rCommand ) + : ToolboxController( rContext, rFrame, rCommand ) +{ +} + +void StyleToolbarController::update() +{ + if ( m_bDisposed ) + throw css::lang::DisposedException(); + + css::util::URL aURL; + aURL.Complete = m_aCommandURL; + m_xUrlTransformer->parseStrict( aURL ); + + auto& xDispatcher = m_aListenerMap[m_aCommandURL]; + if ( xDispatcher.is() ) + xDispatcher->removeStatusListener( this, aURL ); + + xDispatcher.set( new StyleDispatcher( m_xFrame, m_xUrlTransformer, aURL ) ); + xDispatcher->addStatusListener( this, aURL ); +} + +void StyleToolbarController::statusChanged( const css::frame::FeatureStateEvent& rEvent ) +{ + SolarMutexGuard aGuard; + + if ( m_bDisposed ) + throw css::lang::DisposedException(); + + ToolBox* pToolBox = nullptr; + ToolBoxItemId nItemId; + if ( getToolboxId( nItemId, &pToolBox ) ) + { + bool bChecked = false; + rEvent.State >>= bChecked; + pToolBox->CheckItem( nItemId, bChecked ); + pToolBox->EnableItem( nItemId, rEvent.IsEnabled ); + } +} + +void StyleToolbarController::dispose() +{ + ToolboxController::dispose(); + m_aListenerMap.clear(); // Break the cycle with StyleDispatcher. +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/framework/source/uielement/subtoolbarcontroller.cxx b/framework/source/uielement/subtoolbarcontroller.cxx new file mode 100644 index 0000000000..b04b9609e7 --- /dev/null +++ b/framework/source/uielement/subtoolbarcontroller.cxx @@ -0,0 +1,547 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +typedef cppu::ImplInheritanceHelper< svt::PopupWindowController, + css::frame::XSubToolbarController, + css::awt::XDockableWindowListener> ToolBarBase; + +namespace { + +class SubToolBarController : public ToolBarBase +{ + OUString m_aSubTbName; + OUString m_aLastCommand; + css::uno::Reference< css::ui::XUIElement > m_xUIElement; + void disposeUIElement(); +public: + explicit SubToolBarController( const rtl::Reference< com::sun::star::uno::XComponentContext >& rxContext, + const css::uno::Sequence< css::uno::Any >& rxArgs ); + virtual ~SubToolBarController() override; + + void PopoverDestroyed(); + + // XInitialization + virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& rxArgs ) override; + + // XStatusListener + virtual void SAL_CALL statusChanged( const css::frame::FeatureStateEvent& Event ) override; + + // XToolbarController + virtual void SAL_CALL execute( sal_Int16 nKeyModifier ) override; + + // PopupWindowController + virtual VclPtr createVclPopupWindow(vcl::Window* pParent) override; + virtual std::unique_ptr weldPopupWindow() override; + + // XSubToolbarController + virtual sal_Bool SAL_CALL opensSubToolbar() override; + virtual OUString SAL_CALL getSubToolbarName() override; + virtual void SAL_CALL functionSelected( const OUString& rCommand ) override; + virtual void SAL_CALL updateImage() override; + + // XDockableWindowListener + virtual void SAL_CALL startDocking( const css::awt::DockingEvent& e ) override; + virtual css::awt::DockingData SAL_CALL docking( const css::awt::DockingEvent& e ) override; + virtual void SAL_CALL endDocking( const css::awt::EndDockingEvent& e ) override; + virtual sal_Bool SAL_CALL prepareToggleFloatingMode( const css::lang::EventObject& e ) override; + virtual void SAL_CALL toggleFloatingMode( const css::lang::EventObject& e ) override; + virtual void SAL_CALL closed( const css::lang::EventObject& e ) override; + virtual void SAL_CALL endPopupMode( const css::awt::EndPopupModeEvent& e ) override; + + // XEventListener + virtual void SAL_CALL disposing( const css::lang::EventObject& e ) override; + + // XComponent + virtual void SAL_CALL dispose() override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual sal_Bool SAL_CALL supportsService( const OUString& rServiceName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; +}; + +} + +SubToolBarController::SubToolBarController( + const rtl::Reference< com::sun::star::uno::XComponentContext >& rxContext, + const css::uno::Sequence< css::uno::Any >& rxArgs +) : ToolBarBase( + rxContext, + rtl::Reference< css::frame::XFrame >(), + "" + ) +{ + for ( css::uno::Any const & arg : rxArgs ) + { + css::beans::PropertyValue aPropValue; + arg >>= aPropValue; + if ( aPropValue.Name == "Value" ) + { + sal_Int32 nIdx{ 0 }; + OUString aValue; + aPropValue.Value >>= aValue; + m_aSubTbName = aValue.getToken(0, ';', nIdx); + m_aCommandURL = m_aSubTbName; + m_aLastCommand = aValue.getToken(0, ';', nIdx); + break; + } + } + if ( !m_aLastCommand.isEmpty() ) + addStatusListener( m_aLastCommand ); +} + +SubToolBarController::~SubToolBarController() +{ + disposeUIElement(); + m_xUIElement = nullptr; +} + +void SubToolBarController::disposeUIElement() +{ + if ( m_xUIElement.is() ) + { + css::uno::Reference< css::lang::XComponent > xComponent( m_xUIElement, css::uno::UNO_QUERY ); + xComponent->dispose(); + } +} + +void SubToolBarController::statusChanged( const css::frame::FeatureStateEvent& Event ) +{ + SolarMutexGuard aSolarMutexGuard; + + if ( m_bDisposed ) + return; + + ToolBox* pToolBox = nullptr; + ToolBoxItemId nId; + if ( !getToolboxId( nId, &pToolBox ) ) + return; + + ToolBoxItemBits nItemBits = pToolBox->GetItemBits( nId ); + nItemBits &= ~ToolBoxItemBits::CHECKABLE; + TriState eTri = TRISTATE_FALSE; + + if ( Event.FeatureURL.Complete == m_aCommandURL ) + { + pToolBox->EnableItem( nId, Event.IsEnabled ); + + OUString aStrValue; + css::frame::status::Visibility aItemVisibility; + if ( Event.State >>= aStrValue ) + { + // Enum command, such as the current custom shape, + // toggle checked state. + if ( m_aLastCommand == Concat2View( m_aCommandURL + "." + aStrValue ) ) + { + eTri = TRISTATE_TRUE; + nItemBits |= ToolBoxItemBits::CHECKABLE; + } + } + else if ( Event.State >>= aItemVisibility ) + { + pToolBox->ShowItem( nId, aItemVisibility.bVisible ); + } + } + else + { + bool bValue; + if ( Event.State >>= bValue ) + { + // Boolean, treat it as checked/unchecked + if ( bValue ) + eTri = TRISTATE_TRUE; + nItemBits |= ToolBoxItemBits::CHECKABLE; + } + } + + pToolBox->SetItemState( nId, eTri ); + pToolBox->SetItemBits( nId, nItemBits ); +} + +void SubToolBarController::execute( sal_Int16 nKeyModifier ) +{ + if ( !m_aLastCommand.isEmpty() ) + { + auto aArgs( comphelper::InitPropertySequence( { + { "KeyModifier", css::uno::Any( nKeyModifier ) } + } ) ); + dispatchCommand( m_aLastCommand, aArgs ); + } +} + +namespace { +class SubToolbarControl final : public WeldToolbarPopup +{ +public: + explicit SubToolbarControl(SubToolBarController& rController, weld::Widget* pParent); + virtual ~SubToolbarControl() override; + + virtual void GrabFocus() override; + + weld::Container* GetContainer() { return m_xTargetContainer.get(); } + +private: + SubToolBarController& m_rController; + std::unique_ptr m_xTargetContainer; +}; +} + +SubToolbarControl::SubToolbarControl(SubToolBarController& rController, + weld::Widget* pParent) + : WeldToolbarPopup(rController.getFrameInterface(), pParent, "svt/ui/subtoolbar.ui", "subtoolbar") + , m_rController(rController) + , m_xTargetContainer(m_xBuilder->weld_container("container")) +{ +} + +void SubToolbarControl::GrabFocus() +{ + // TODO +} + +SubToolbarControl::~SubToolbarControl() +{ + m_rController.PopoverDestroyed(); +} + +std::unique_ptr SubToolBarController::weldPopupWindow() +{ + SolarMutexGuard aGuard; + + auto pPopup = std::make_unique(*this, m_pToolbar); + + css::uno::Reference< css::frame::XFrame > xFrame ( getFrameInterface() ); + + // create element with factory + static css::uno::WeakReference< css::ui::XUIElementFactoryManager > xWeakUIElementFactory; + css::uno::Reference< css::ui::XUIElementFactoryManager > xUIElementFactory = xWeakUIElementFactory; + if ( !xUIElementFactory.is() ) + { + xUIElementFactory = css::ui::theUIElementFactoryManager::get( m_xContext ); + xWeakUIElementFactory = xUIElementFactory; + } + + css::uno::Reference< css::awt::XWindow > xParent = new weld::TransportAsXWindow(pPopup->GetContainer()); + + auto aPropSeq( comphelper::InitPropertySequence( { + { "Frame", css::uno::Any( xFrame ) }, + { "ParentWindow", css::uno::Any( xParent ) }, + { "Persistent", css::uno::Any( false ) }, + { "PopupMode", css::uno::Any( true ) } + } ) ); + + try + { + m_xUIElement = xUIElementFactory->createUIElement( "private:resource/toolbar/" + m_aSubTbName, aPropSeq ); + } + catch ( css::container::NoSuchElementException& ) + {} + catch ( css::lang::IllegalArgumentException& ) + {} + + return pPopup; +} + +VclPtr SubToolBarController::createVclPopupWindow(vcl::Window* /*pParent*/) +{ + SolarMutexGuard aGuard; + + ToolBox* pToolBox = nullptr; + ToolBoxItemId nId; + if ( getToolboxId( nId, &pToolBox ) ) + { + css::uno::Reference< css::frame::XFrame > xFrame ( getFrameInterface() ); + + // create element with factory + static css::uno::WeakReference< css::ui::XUIElementFactoryManager > xWeakUIElementFactory; + css::uno::Reference< css::ui::XUIElement > xUIElement; + css::uno::Reference< css::ui::XUIElementFactoryManager > xUIElementFactory = xWeakUIElementFactory; + if ( !xUIElementFactory.is() ) + { + xUIElementFactory = css::ui::theUIElementFactoryManager::get( m_xContext ); + xWeakUIElementFactory = xUIElementFactory; + } + + auto aPropSeq( comphelper::InitPropertySequence( { + { "Frame", css::uno::Any( xFrame ) }, + { "ParentWindow", css::uno::Any( m_xParentWindow ) }, + { "Persistent", css::uno::Any( false ) }, + { "PopupMode", css::uno::Any( true ) } + } ) ); + + try + { + xUIElement = xUIElementFactory->createUIElement( "private:resource/toolbar/" + m_aSubTbName, aPropSeq ); + } + catch ( css::container::NoSuchElementException& ) + {} + catch ( css::lang::IllegalArgumentException& ) + {} + + if ( xUIElement.is() ) + { + css::uno::Reference< css::awt::XWindow > xSubToolBar( xUIElement->getRealInterface(), css::uno::UNO_QUERY ); + if ( xSubToolBar.is() ) + { + css::uno::Reference< css::awt::XDockableWindow > xDockWindow( xSubToolBar, css::uno::UNO_QUERY ); + xDockWindow->addDockableWindowListener( css::uno::Reference< css::awt::XDockableWindowListener >(this) ); + xDockWindow->enableDocking( true ); + + // keep reference to UIElement to avoid its destruction + disposeUIElement(); + m_xUIElement = xUIElement; + + VclPtr pTbxWindow = VCLUnoHelper::GetWindow( xSubToolBar ); + if ( pTbxWindow && pTbxWindow->GetType() == WindowType::TOOLBOX ) + { + ToolBox* pToolBar = static_cast< ToolBox* >( pTbxWindow.get() ); + // calc and set size for popup mode + Size aSize = pToolBar->CalcPopupWindowSizePixel(); + pToolBar->SetSizePixel( aSize ); + // open subtoolbox in popup mode + vcl::Window::GetDockingManager()->StartPopupMode( pToolBox, pToolBar ); + } + } + } + } + return nullptr; +} + +sal_Bool SubToolBarController::opensSubToolbar() +{ + return !m_aLastCommand.isEmpty(); +} + +OUString SubToolBarController::getSubToolbarName() +{ + return m_aSubTbName; +} + +void SubToolBarController::functionSelected( const OUString& rCommand ) +{ + if ( !m_aLastCommand.isEmpty() && m_aLastCommand != rCommand ) + { + removeStatusListener( m_aLastCommand ); + m_aLastCommand = rCommand; + addStatusListener( m_aLastCommand ); + updateImage(); + } +} + +void SubToolBarController::updateImage() +{ + SolarMutexGuard aGuard; + if ( !m_aLastCommand.isEmpty() ) + { + ToolBox* pToolBox = nullptr; + ToolBoxItemId nId; + if ( getToolboxId( nId, &pToolBox ) ) + { + vcl::ImageType eImageType = pToolBox->GetImageSize(); + Image aImage = vcl::CommandInfoProvider::GetImageForCommand(m_aLastCommand, getFrameInterface(), eImageType); + if ( !!aImage ) + pToolBox->SetItemImage( nId, aImage ); + } + } +} + +void SubToolBarController::startDocking( const css::awt::DockingEvent& ) +{ +} + +css::awt::DockingData SubToolBarController::docking( const css::awt::DockingEvent& ) +{ + return css::awt::DockingData(); +} + +void SubToolBarController::endDocking( const css::awt::EndDockingEvent& ) +{ +} + +sal_Bool SubToolBarController::prepareToggleFloatingMode( const css::lang::EventObject& ) +{ + return false; +} + +void SubToolBarController::toggleFloatingMode( const css::lang::EventObject& ) +{ +} + +void SubToolBarController::closed( const css::lang::EventObject& ) +{ +} + +void SubToolBarController::endPopupMode( const css::awt::EndPopupModeEvent& e ) +{ + SolarMutexGuard aGuard; + + OUString aSubToolBarResName; + if ( m_xUIElement.is() ) + { + css::uno::Reference< css::beans::XPropertySet > xPropSet( m_xUIElement, css::uno::UNO_QUERY ); + if ( xPropSet.is() ) + { + try + { + xPropSet->getPropertyValue("ResourceURL") >>= aSubToolBarResName; + } + catch ( css::beans::UnknownPropertyException& ) + {} + catch ( css::lang::WrappedTargetException& ) + {} + } + disposeUIElement(); + } + m_xUIElement = nullptr; + + // if the toolbar was teared-off recreate it and place it at the given position + if( !e.bTearoff ) + return; + + css::uno::Reference< css::ui::XUIElement > xUIElement; + css::uno::Reference< css::frame::XLayoutManager > xLayoutManager = getLayoutManager(); + + if ( !xLayoutManager.is() ) + return; + + xLayoutManager->createElement( aSubToolBarResName ); + xUIElement = xLayoutManager->getElement( aSubToolBarResName ); + if ( !xUIElement.is() ) + return; + + css::uno::Reference< css::awt::XWindow > xSubToolBar( xUIElement->getRealInterface(), css::uno::UNO_QUERY ); + css::uno::Reference< css::beans::XPropertySet > xProp( xUIElement, css::uno::UNO_QUERY ); + if ( !(xSubToolBar.is() && xProp.is()) ) + return; + + try + { + VclPtr pTbxWindow = VCLUnoHelper::GetWindow( xSubToolBar ); + if ( pTbxWindow && pTbxWindow->GetType() == WindowType::TOOLBOX ) + { + OUString aPersistentString( "Persistent" ); + css::uno::Any a = xProp->getPropertyValue( aPersistentString ); + xProp->setPropertyValue( aPersistentString, css::uno::Any( false ) ); + + xLayoutManager->hideElement( aSubToolBarResName ); + xLayoutManager->floatWindow( aSubToolBarResName ); + + xLayoutManager->setElementPos( aSubToolBarResName, e.FloatingPosition ); + xLayoutManager->showElement( aSubToolBarResName ); + + xProp->setPropertyValue("Persistent", a ); + } + } + catch ( css::uno::RuntimeException& ) + { + throw; + } + catch ( css::uno::Exception& ) + {} +} + +void SubToolBarController::disposing( const css::lang::EventObject& e ) +{ + svt::ToolboxController::disposing( e ); +} + +void SubToolBarController::initialize( const css::uno::Sequence< css::uno::Any >& rxArgs ) +{ + svt::PopupWindowController::initialize( rxArgs ); + + ToolBox* pToolBox = nullptr; + ToolBoxItemId nId; + if ( getToolboxId( nId, &pToolBox ) ) + { + if ( m_aLastCommand.isEmpty() ) + pToolBox->SetItemBits( nId, pToolBox->GetItemBits( nId ) | ToolBoxItemBits::DROPDOWNONLY ); + else + pToolBox->SetItemBits( nId, pToolBox->GetItemBits( nId ) | ToolBoxItemBits::DROPDOWN ); + } + + if (m_pToolbar) + { + mxPopoverContainer.reset(new ToolbarPopupContainer(m_pToolbar)); + m_pToolbar->set_item_popover(m_aCommandURL, mxPopoverContainer->getTopLevel()); + } + + updateImage(); +} + +void SubToolBarController::PopoverDestroyed() +{ + disposeUIElement(); + m_xUIElement = nullptr; +} + +void SubToolBarController::dispose() +{ + if ( m_bDisposed ) + return; + + svt::PopupWindowController::dispose(); + disposeUIElement(); + m_xUIElement = nullptr; +} + +OUString SubToolBarController::getImplementationName() +{ + return "com.sun.star.comp.framework.SubToolBarController"; +} + +sal_Bool SubToolBarController::supportsService( const OUString& rServiceName ) +{ + return cppu::supportsService( this, rServiceName ); +} + +css::uno::Sequence< OUString > SubToolBarController::getSupportedServiceNames() +{ + return {"com.sun.star.frame.ToolbarController"}; +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_framework_SubToolBarController_get_implementation( + css::uno::XComponentContext* rxContext, + css::uno::Sequence const & rxArgs ) +{ + return cppu::acquire( new SubToolBarController( rxContext, rxArgs ) ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uielement/thesaurusmenucontroller.cxx b/framework/source/uielement/thesaurusmenucontroller.cxx new file mode 100644 index 0000000000..6a834757c3 --- /dev/null +++ b/framework/source/uielement/thesaurusmenucontroller.cxx @@ -0,0 +1,186 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace { + +class ThesaurusMenuController : public svt::PopupMenuControllerBase +{ +public: + explicit ThesaurusMenuController( const css::uno::Reference< css::uno::XComponentContext >& rxContext ); + + // XStatusListener + virtual void SAL_CALL statusChanged( const css::frame::FeatureStateEvent& rEvent ) override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + +private: + void fillPopupMenu(); + void getMeanings( std::vector< OUString >& rSynonyms, const OUString& rWord, const css::lang::Locale& rLocale, size_t nMaxSynonms ); + OUString getThesImplName( const css::lang::Locale& rLocale ) const; + css::uno::Reference< css::linguistic2::XLinguServiceManager2 > m_xLinguServiceManager; + css::uno::Reference< css::linguistic2::XThesaurus > m_xThesaurus; + OUString m_aLastWord; +}; + +} + +ThesaurusMenuController::ThesaurusMenuController( const css::uno::Reference< css::uno::XComponentContext >& rxContext ) : + svt::PopupMenuControllerBase( rxContext ), + m_xLinguServiceManager( css::linguistic2::LinguServiceManager::create( rxContext ) ), + m_xThesaurus( m_xLinguServiceManager->getThesaurus() ) +{ +} + +void ThesaurusMenuController::statusChanged( const css::frame::FeatureStateEvent& rEvent ) +{ + rEvent.State >>= m_aLastWord; + m_xPopupMenu->clear(); + if ( rEvent.IsEnabled ) + fillPopupMenu(); +} + +void ThesaurusMenuController::fillPopupMenu() +{ + sal_Int32 nIdx{ 0 }; + OUString aText = m_aLastWord.getToken(0, '#', nIdx); + OUString aIsoLang = m_aLastWord.getToken(0, '#', nIdx); + if ( aText.isEmpty() || aIsoLang.isEmpty() ) + return; + + std::vector< OUString > aSynonyms; + css::lang::Locale aLocale = LanguageTag::convertToLocale( aIsoLang ); + getMeanings( aSynonyms, aText, aLocale, 7 /*max number of synonyms to retrieve*/ ); + + m_xPopupMenu->enableAutoMnemonics(false); + if ( aSynonyms.empty() ) + return; + + SvtLinguConfig aCfg; + css::uno::Reference xGraphic; + OUString aThesImplName( getThesImplName( aLocale ) ); + OUString aSynonymsImageUrl( aCfg.GetSynonymsContextImage( aThesImplName ) ); + if (!aThesImplName.isEmpty() && !aSynonymsImageUrl.isEmpty()) + { + try + { + css::uno::Reference xContext(::comphelper::getProcessComponentContext()); + css::uno::Reference xProvider(css::graphic::GraphicProvider::create(xContext)); + xGraphic = xProvider->queryGraphic({ comphelper::makePropertyValue("URL", aSynonymsImageUrl) }); + } + catch (const css::uno::Exception&) + { + DBG_UNHANDLED_EXCEPTION("fwk"); + } + } + + sal_uInt16 nId = 1; + for ( const auto& aSynonym : aSynonyms ) + { + OUString aItemText( linguistic::GetThesaurusReplaceText( aSynonym ) ); + m_xPopupMenu->insertItem(nId, aItemText, 0, -1); + m_xPopupMenu->setCommand(nId, ".uno:ThesaurusFromContext?WordReplace:string=" + aItemText); + + if (xGraphic.is()) + m_xPopupMenu->setItemImage(nId, xGraphic, false); + + nId++; + } + + m_xPopupMenu->insertSeparator(-1); + OUString aThesaurusDialogCmd( ".uno:ThesaurusDialog" ); + auto aProperties = vcl::CommandInfoProvider::GetCommandProperties(aThesaurusDialogCmd, m_aModuleName); + m_xPopupMenu->insertItem(nId, vcl::CommandInfoProvider::GetPopupLabelForCommand(aProperties), 0, -1); + m_xPopupMenu->setCommand(nId, aThesaurusDialogCmd); +} + +void ThesaurusMenuController::getMeanings( std::vector< OUString >& rSynonyms, const OUString& rWord, + const css::lang::Locale& rLocale, size_t nMaxSynonms ) +{ + rSynonyms.clear(); + if ( !(m_xThesaurus.is() && m_xThesaurus->hasLocale( rLocale ) && !rWord.isEmpty() && nMaxSynonms > 0) ) + return; + + try + { + const css::uno::Sequence< css::uno::Reference< css::linguistic2::XMeaning > > aMeaningSeq( + m_xThesaurus->queryMeanings( rWord, rLocale, css::uno::Sequence< css::beans::PropertyValue >() ) ); + + for ( const auto& xMeaning : aMeaningSeq ) + { + const css::uno::Sequence< OUString > aSynonymSeq( xMeaning->querySynonyms() ); + for ( const auto& aSynonym : aSynonymSeq ) + { + rSynonyms.push_back( aSynonym ); + if ( rSynonyms.size() == nMaxSynonms ) + return; + } + } + } + catch ( const css::uno::Exception& ) + { + SAL_WARN( "fwk.uielement", "Failed to get synonyms" ); + } +} + +OUString ThesaurusMenuController::getThesImplName( const css::lang::Locale& rLocale ) const +{ + css::uno::Sequence< OUString > aServiceNames = + m_xLinguServiceManager->getConfiguredServices( "com.sun.star.linguistic2.Thesaurus", rLocale ); + SAL_WARN_IF( aServiceNames.getLength() > 1, "fwk.uielement", "Only one thesaurus is allowed per locale, but found more!" ); + if ( aServiceNames.getLength() == 1 ) + return aServiceNames[0]; + + return OUString(); +} + +OUString ThesaurusMenuController::getImplementationName() +{ + return "com.sun.star.comp.framework.ThesaurusMenuController"; +} + +css::uno::Sequence< OUString > ThesaurusMenuController::getSupportedServiceNames() +{ + return { "com.sun.star.frame.PopupMenuController" }; +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_framework_ThesaurusMenuController_get_implementation( + css::uno::XComponentContext* xContext, + css::uno::Sequence< css::uno::Any > const & ) +{ + return cppu::acquire( new ThesaurusMenuController( xContext ) ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uielement/togglebuttontoolbarcontroller.cxx b/framework/source/uielement/togglebuttontoolbarcontroller.cxx new file mode 100644 index 0000000000..c17b08efee --- /dev/null +++ b/framework/source/uielement/togglebuttontoolbarcontroller.cxx @@ -0,0 +1,270 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::awt; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::frame; +using namespace ::com::sun::star::util; + +namespace framework +{ + +ToggleButtonToolbarController::ToggleButtonToolbarController( + const Reference< XComponentContext >& rxContext, + const Reference< XFrame >& rFrame, + ToolBox* pToolbar, + ToolBoxItemId nID, + Style eStyle, + const OUString& aCommand ) : + ComplexToolbarController( rxContext, rFrame, pToolbar, nID, aCommand ) +{ + if ( eStyle == Style::DropDownButton ) + m_xToolbar->SetItemBits( m_nID, ToolBoxItemBits::DROPDOWNONLY | m_xToolbar->GetItemBits( m_nID ) ); + else // Style::ToggleDropDownButton + m_xToolbar->SetItemBits( m_nID, ToolBoxItemBits::DROPDOWN | m_xToolbar->GetItemBits( m_nID ) ); +} + +ToggleButtonToolbarController::~ToggleButtonToolbarController() +{ +} + +void SAL_CALL ToggleButtonToolbarController::dispose() +{ + SolarMutexGuard aSolarMutexGuard; + ComplexToolbarController::dispose(); +} + +Sequence ToggleButtonToolbarController::getExecuteArgs(sal_Int16 KeyModifier) const +{ + Sequence aArgs{ // Add key modifier to argument list + comphelper::makePropertyValue("KeyModifier", KeyModifier), + comphelper::makePropertyValue("Text", m_aCurrentSelection) }; + return aArgs; +} + +uno::Reference< awt::XWindow > SAL_CALL ToggleButtonToolbarController::createPopupWindow() +{ + uno::Reference< awt::XWindow > xWindow; + + SolarMutexGuard aSolarMutexGuard; + + // create popup menu + ScopedVclPtrInstance<::PopupMenu> aPopup; + const sal_uInt32 nCount = m_aDropdownMenuList.size(); + for ( sal_uInt32 i = 0; i < nCount; i++ ) + { + const OUString & rLabel = m_aDropdownMenuList[i].mLabel; + aPopup->InsertItem( sal_uInt16( i+1 ), rLabel ); + if ( rLabel == m_aCurrentSelection ) + aPopup->CheckItem( sal_uInt16( i+1 ) ); + else + aPopup->CheckItem( sal_uInt16( i+1 ), false ); + + if ( !m_aDropdownMenuList[i].mTipHelpText.isEmpty() ) + aPopup->SetTipHelpText( sal_uInt16( i+1 ), m_aDropdownMenuList[i].mTipHelpText ); + } + + m_xToolbar->SetItemDown( m_nID, true ); + aPopup->SetSelectHdl( LINK( this, ToggleButtonToolbarController, MenuSelectHdl )); + aPopup->Execute( m_xToolbar, m_xToolbar->GetItemRect( m_nID )); + m_xToolbar->SetItemDown( m_nID, false ); + + return xWindow; +} + +void ToggleButtonToolbarController::executeControlCommand( const css::frame::ControlCommand& rControlCommand ) +{ + SolarMutexGuard aSolarMutexGuard; + + if ( rControlCommand.Command == "SetList" ) + { + for ( auto const & arg : rControlCommand.Arguments ) + { + if ( arg.Name == "List" ) + { + Sequence< OUString > aList; + m_aDropdownMenuList.clear(); + m_aCurrentSelection.clear(); + + arg.Value >>= aList; + for ( OUString const & label : std::as_const(aList) ) + { + m_aDropdownMenuList.push_back( DropdownMenuItem() ); + m_aDropdownMenuList.back().mLabel = label; + } + + // send notification + uno::Sequence< beans::NamedValue > aInfo { { "List", css::uno::Any(aList) } }; + addNotifyInfo( "ListChanged", + getDispatchFromCommand( m_aCommandURL ), + aInfo ); + + break; + } + } + } + else if ( rControlCommand.Command == "CheckItemPos" ) + { + for ( auto const & arg : rControlCommand.Arguments ) + { + if ( arg.Name == "Pos" ) + { + sal_Int32 nPos( -1 ); + + arg.Value >>= nPos; + if ( nPos >= 0 && + ( sal::static_int_cast< sal_uInt32 >(nPos) + < m_aDropdownMenuList.size() ) ) + { + m_aCurrentSelection = m_aDropdownMenuList[nPos].mLabel; + + // send notification + uno::Sequence< beans::NamedValue > aInfo { { "ItemChecked", css::uno::Any(nPos) } }; + addNotifyInfo( "Pos", + getDispatchFromCommand( m_aCommandURL ), + aInfo ); + } + break; + } + } + } + else if ( rControlCommand.Command == "AddEntry" ) + { + OUString aText; + OUString aTipHelpText; + + for ( auto const & arg : rControlCommand.Arguments ) + { + if ( arg.Name == "Text" ) + { + arg.Value >>= aText; + } + else if ( arg.Name == "TipHelpText" ) + { + arg.Value >>= aTipHelpText; + } + } + + if (!aText.isEmpty()) + { + m_aDropdownMenuList.push_back( DropdownMenuItem() ); + m_aDropdownMenuList.back().mLabel = aText; + m_aDropdownMenuList.back().mTipHelpText = aTipHelpText; + } + } + else if ( rControlCommand.Command == "InsertEntry" ) + { + sal_Int32 nPos(0); + sal_Int32 nSize = sal_Int32( m_aDropdownMenuList.size() ); + OUString aText; + for ( auto const & arg : rControlCommand.Arguments ) + { + if ( arg.Name == "Pos" ) + { + sal_Int32 nTmpPos = 0; + if ( arg.Value >>= nTmpPos ) + { + if (( nTmpPos >= 0 ) && ( nTmpPos < nSize )) + nPos = nTmpPos; + } + } + else if ( arg.Name == "Text" ) + arg.Value >>= aText; + } + + std::vector< DropdownMenuItem >::iterator aIter = m_aDropdownMenuList.begin(); + aIter += nPos; + aIter = m_aDropdownMenuList.insert(aIter, DropdownMenuItem()); + if (aIter != m_aDropdownMenuList.end()) + aIter->mLabel = aText; + } + else if ( rControlCommand.Command == "RemoveEntryPos" ) + { + for ( auto const & arg : rControlCommand.Arguments ) + { + if ( arg.Name == "Pos" ) + { + sal_Int32 nPos( -1 ); + if ( arg.Value >>= nPos ) + { + if ( nPos < sal_Int32( m_aDropdownMenuList.size() )) + { + m_aDropdownMenuList.erase(m_aDropdownMenuList.begin() + nPos); + } + } + break; + } + } + } + else if ( rControlCommand.Command == "RemoveEntryText" ) + { + for ( auto const & arg : rControlCommand.Arguments ) + { + if ( arg.Name == "Text" ) + { + OUString aText; + if ( arg.Value >>= aText ) + { + sal_Int32 nSize = sal_Int32( m_aDropdownMenuList.size() ); + for ( sal_Int32 j = 0; j < nSize; j++ ) + { + if ( m_aDropdownMenuList[j].mLabel == aText ) + { + m_aDropdownMenuList.erase(m_aDropdownMenuList.begin() + j); + break; + } + } + } + break; + } + } + } + else if ( rControlCommand.Command == "createPopupMenu" ) + { + createPopupWindow(); + } +} + +IMPL_LINK( ToggleButtonToolbarController, MenuSelectHdl, Menu *, pMenu, bool ) +{ + SolarMutexGuard aGuard; + + sal_uInt16 nItemId = pMenu->GetCurItemId(); + if ( nItemId > 0 && nItemId <= m_aDropdownMenuList.size() ) + { + m_aCurrentSelection = m_aDropdownMenuList[nItemId-1].mLabel; + + execute( 0 ); + } + return false; +} + +} // namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uielement/toolbarmanager.cxx b/framework/source/uielement/toolbarmanager.cxx new file mode 100644 index 0000000000..cde8b89f41 --- /dev/null +++ b/framework/source/uielement/toolbarmanager.cxx @@ -0,0 +1,2340 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// namespaces + +using namespace ::com::sun::star::awt; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::frame; +using namespace ::com::sun::star::graphic; +using namespace ::com::sun::star::util; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::ui; +using namespace ::com::sun::star; + +namespace framework +{ + +const char ITEM_DESCRIPTOR_COMMANDURL[] = "CommandURL"; +const char ITEM_DESCRIPTOR_VISIBLE[] = "IsVisible"; + +const sal_uInt16 STARTID_CUSTOMIZE_POPUPMENU = 1000; + +static css::uno::Reference< css::frame::XLayoutManager > getLayoutManagerFromFrame( + css::uno::Reference< css::frame::XFrame > const & rFrame ) +{ + css::uno::Reference< css::frame::XLayoutManager > xLayoutManager; + + Reference< XPropertySet > xPropSet( rFrame, UNO_QUERY ); + if ( xPropSet.is() ) + { + try + { + xPropSet->getPropertyValue("LayoutManager") >>= xLayoutManager; + } + catch (const RuntimeException&) + { + throw; + } + catch (const Exception&) + { + } + } + + return xLayoutManager; +} +namespace +{ + +sal_Int16 getCurrentImageType() +{ + sal_Int16 nImageType = css::ui::ImageType::SIZE_DEFAULT; + sal_Int16 nCurrentSymbolSize = SvtMiscOptions::GetCurrentSymbolsSize(); + if (nCurrentSymbolSize == SFX_SYMBOLS_SIZE_LARGE) + nImageType |= css::ui::ImageType::SIZE_LARGE; + else if (nCurrentSymbolSize == SFX_SYMBOLS_SIZE_32) + nImageType |= css::ui::ImageType::SIZE_32; + return nImageType; +} + +class VclToolBarManager : public ToolBarManagerImpl +{ + DECL_LINK(Click, ToolBox*, void); + +public: + VclToolBarManager(VclPtr pToolbar) + : m_pToolBar(std::move(pToolbar)) + , m_bAddedToTaskPaneList(true) + , m_pManager(nullptr) + {} + + ~VclToolBarManager() + { + OSL_ASSERT( !m_bAddedToTaskPaneList ); + } + + virtual void Init() override + { + vcl::Window* pWindow = m_pToolBar; + while ( pWindow && !pWindow->IsSystemWindow() ) + pWindow = pWindow->GetParent(); + + if ( pWindow ) + static_cast(pWindow)->GetTaskPaneList()->AddWindow( m_pToolBar ); + } + + virtual void Destroy() override + { + OSL_ASSERT( m_pToolBar != nullptr ); + SolarMutexGuard g; + if ( m_bAddedToTaskPaneList ) + { + vcl::Window* pWindow = m_pToolBar; + while ( pWindow && !pWindow->IsSystemWindow() ) + pWindow = pWindow->GetParent(); + + if ( pWindow ) + static_cast(pWindow)->GetTaskPaneList()->RemoveWindow( m_pToolBar ); + m_bAddedToTaskPaneList = false; + } + + // Delete the additional add-ons data + for ( ToolBox::ImplToolItems::size_type i = 0; i < m_pToolBar->GetItemCount(); i++ ) + { + ToolBoxItemId nItemId = m_pToolBar->GetItemId( i ); + if ( nItemId > ToolBoxItemId(0) ) + delete static_cast< AddonsParams* >( m_pToolBar->GetItemData( nItemId )); + } + + // #i93173# note we can still be in one of the toolbar's handlers + m_pToolBar->SetSelectHdl( Link() ); + m_pToolBar->SetActivateHdl( Link() ); + m_pToolBar->SetDeactivateHdl( Link() ); + m_pToolBar->SetClickHdl( Link() ); + m_pToolBar->SetDropdownClickHdl( Link() ); + m_pToolBar->SetDoubleClickHdl( Link() ); + m_pToolBar->SetStateChangedHdl( Link() ); + m_pToolBar->SetDataChangedHdl( Link() ); + + m_pToolBar.disposeAndClear(); + } + + virtual css::uno::Reference GetInterface() override + { + return VCLUnoHelper::GetInterface(m_pToolBar); + } + + virtual void ConnectCallbacks(ToolBarManager* pManager) override + { + m_pManager = pManager; + m_pToolBar->SetSelectHdl( LINK( pManager, ToolBarManager, Select) ); + m_pToolBar->SetClickHdl( LINK( this, VclToolBarManager, Click ) ); + m_pToolBar->SetDropdownClickHdl( LINK( pManager, ToolBarManager, DropdownClick ) ); + m_pToolBar->SetDoubleClickHdl( LINK( pManager, ToolBarManager, DoubleClick ) ); + m_pToolBar->SetStateChangedHdl( LINK( pManager, ToolBarManager, StateChanged ) ); + m_pToolBar->SetDataChangedHdl( LINK( pManager, ToolBarManager, DataChanged ) ); + + m_pToolBar->SetMenuButtonHdl( LINK( pManager, ToolBarManager, MenuButton ) ); + m_pToolBar->SetMenuExecuteHdl( LINK( pManager, ToolBarManager, MenuPreExecute ) ); + m_pToolBar->GetMenu()->SetSelectHdl( LINK( pManager, ToolBarManager, MenuSelect ) ); + } + + virtual void InsertItem(ToolBoxItemId nId, + const OUString& rCommandURL, + const OUString& rTooltip, + const OUString& rLabel, + ToolBoxItemBits nItemBits) override + { + m_pToolBar->InsertItem( nId, rLabel, rCommandURL, nItemBits ); + m_pToolBar->SetQuickHelpText(nId, rTooltip); + m_pToolBar->EnableItem( nId ); + m_pToolBar->SetItemState( nId, TRISTATE_FALSE ); + } + + virtual void InsertSeparator() override + { + m_pToolBar->InsertSeparator(); + } + + virtual void InsertSpace() override + { + m_pToolBar->InsertSpace(); + } + + virtual void InsertBreak() override + { + m_pToolBar->InsertBreak(); + } + + virtual ToolBoxItemId GetItemId(sal_uInt16 nPos) override + { + return m_pToolBar->GetItemId(nPos); + } + + virtual ToolBoxItemId GetCurItemId() override + { + return m_pToolBar->GetCurItemId(); + } + + virtual OUString GetItemCommand(ToolBoxItemId nId) override + { + return m_pToolBar->GetItemCommand(nId); + } + + virtual sal_uInt16 GetItemCount() override + { + return m_pToolBar->GetItemCount(); + } + + virtual void SetItemCheckable(ToolBoxItemId nId) override + { + m_pToolBar->SetItemBits( nId, m_pToolBar->GetItemBits( nId ) | ToolBoxItemBits::CHECKABLE ); + } + + virtual void HideItem(ToolBoxItemId nId, const OUString& /*rCommandURL*/) override + { + m_pToolBar->HideItem( nId ); + } + + virtual bool IsItemVisible(ToolBoxItemId nId, const OUString& /*rCommandURL*/) override + { + return m_pToolBar->IsItemVisible(nId); + } + + virtual void Clear() override + { + m_pToolBar->Clear(); + } + + virtual void SetName(const OUString& rName) override + { + m_pToolBar->SetText( rName ); + } + + virtual void SetHelpId(const OUString& rHelpId) override + { + m_pToolBar->SetHelpId( rHelpId ); + } + + virtual bool WillUsePopupMode() override + { + return m_pToolBar->WillUsePopupMode(); + } + + virtual bool IsReallyVisible() override + { + return m_pToolBar->IsReallyVisible(); + } + + virtual void SetIconSize(ToolBoxButtonSize eSize) override + { + m_pToolBar->SetToolboxButtonSize(eSize); + } + + virtual vcl::ImageType GetImageSize() override + { + return m_pToolBar->GetImageSize(); + } + + virtual void SetMenuType(ToolBoxMenuType eType) override + { + m_pToolBar->SetMenuType( eType ); + } + + virtual void MergeToolbar(ToolBoxItemId & rItemId, sal_uInt16 nFirstItem, + const OUString& rModuleIdentifier, + CommandToInfoMap& rCommandMap, + MergeToolbarInstruction& rInstruction) override + { + ReferenceToolbarPathInfo aRefPoint = ToolBarMerger::FindReferencePoint( m_pToolBar, nFirstItem, rInstruction.aMergePoint ); + + // convert the sequence< sequence< propertyvalue > > structure to + // something we can better handle. A vector with item data + AddonToolbarItemContainer aItems; + ToolBarMerger::ConvertSeqSeqToVector( rInstruction.aMergeToolbarItems, aItems ); + + if ( aRefPoint.bResult ) + { + ToolBarMerger::ProcessMergeOperation( m_pToolBar, + aRefPoint.nPos, + rItemId, + rCommandMap, + rModuleIdentifier, + rInstruction.aMergeCommand, + rInstruction.aMergeCommandParameter, + aItems ); + } + else + { + ToolBarMerger::ProcessMergeFallback( m_pToolBar, + rItemId, + rCommandMap, + rModuleIdentifier, + rInstruction.aMergeCommand, + rInstruction.aMergeFallback, + aItems ); + } + } + + virtual void SetItemImage(ToolBoxItemId nId, + const OUString& /*rCommandURL*/, + const Image& rImage) override + { + m_pToolBar->SetItemImage(nId, rImage); + } + + virtual void UpdateSize() override + { + ::Size aSize = m_pToolBar->CalcWindowSizePixel(); + m_pToolBar->SetOutputSizePixel( aSize ); + } + + virtual void SetItemWindow(ToolBoxItemId nItemId, vcl::Window* pNewWindow) override + { + m_pToolBar->SetItemWindow( nItemId, pNewWindow ); + } + +private: + VclPtr m_pToolBar; + bool m_bAddedToTaskPaneList; + ToolBarManager* m_pManager; +}; + +IMPL_LINK_NOARG(VclToolBarManager, Click, ToolBox*, void) +{ + m_pManager->OnClick(); +} + +class WeldToolBarManager : public ToolBarManagerImpl +{ + DECL_LINK(Click, const OUString&, void); + DECL_LINK(ToggleMenuHdl, const OUString&, void); + +public: + WeldToolBarManager(weld::Toolbar* pToolbar, + weld::Builder* pBuilder) + : m_pWeldedToolBar(pToolbar) + , m_pBuilder(pBuilder) + , m_pManager(nullptr) + , m_nCurrentId(0) + {} + + virtual void Init() override {} + + virtual void Destroy() override {} + + virtual css::uno::Reference GetInterface() override + { + return new weld::TransportAsXWindow(m_pWeldedToolBar, m_pBuilder); + } + + virtual void ConnectCallbacks(ToolBarManager* pManager) override + { + m_pManager = pManager; + m_pWeldedToolBar->connect_clicked(LINK(this, WeldToolBarManager, Click)); + m_pWeldedToolBar->connect_menu_toggled(LINK(this, WeldToolBarManager, ToggleMenuHdl)); + } + + virtual void InsertItem(ToolBoxItemId nId, + const OUString& rCommandURL, + const OUString& rTooltip, + const OUString& rLabel, + ToolBoxItemBits /*nItemBits*/) override + { + m_aCommandToId[rCommandURL] = nId; + m_aIdToCommand[nId] = rCommandURL; + m_aCommandOrder.push_back(rCommandURL); + + m_pWeldedToolBar->insert_item(m_aCommandOrder.size(), rCommandURL); + m_pWeldedToolBar->set_item_tooltip_text(rCommandURL, rTooltip); + m_pWeldedToolBar->set_item_label(rCommandURL, rLabel); + m_pWeldedToolBar->set_item_sensitive(rCommandURL, true); + m_pWeldedToolBar->set_item_active(rCommandURL, false); + } + + virtual void InsertSeparator() override + { + m_pWeldedToolBar->append_separator(""); + } + + virtual void InsertSpace() override {} + + virtual void InsertBreak() override {} + + virtual ToolBoxItemId GetItemId(sal_uInt16 nPos) override + { + return m_aCommandToId[m_aCommandOrder[nPos]]; + } + + virtual ToolBoxItemId GetCurItemId() override + { + return m_nCurrentId; + } + + virtual OUString GetItemCommand(ToolBoxItemId nId) override + { + return m_aIdToCommand[nId]; + } + + virtual sal_uInt16 GetItemCount() override + { + return m_aCommandOrder.size(); + } + + virtual void SetItemCheckable(ToolBoxItemId /*nId*/) override {} + + virtual void HideItem(ToolBoxItemId /*nId*/, const OUString& rCommandURL) override + { + m_pWeldedToolBar->set_item_visible(rCommandURL, false); + } + + virtual bool IsItemVisible(ToolBoxItemId /*nId*/, const OUString& rCommandURL) override + { + return m_pWeldedToolBar->get_item_visible(rCommandURL); + } + + virtual void Clear() override {} + + virtual void SetName(const OUString& /*rName*/) override {} + + virtual void SetHelpId(const OUString& /*rHelpId*/) override {} + + virtual bool WillUsePopupMode() override { return true; } + + virtual bool IsReallyVisible() override { return true; } + + virtual void SetIconSize(ToolBoxButtonSize /*eSize*/) override {} + + virtual vcl::ImageType GetImageSize() override + { + return vcl::ImageType::Size32; + } + + virtual void SetMenuType(ToolBoxMenuType /*eType*/) override {} + + virtual void MergeToolbar(ToolBoxItemId & /*rItemId*/, + sal_uInt16 /*nFirstItem*/, + const OUString& /*rModuleIdentifier*/, + CommandToInfoMap& /*rCommandMap*/, + MergeToolbarInstruction& /*rInstruction*/) override {} + + virtual void SetItemImage(ToolBoxItemId /*nId*/, + const OUString& rCommandURL, + const Image& rImage) override + { + m_pWeldedToolBar->set_item_image(rCommandURL, Graphic(rImage).GetXGraphic()); + } + + virtual void UpdateSize() override {} + + virtual void SetItemWindow(ToolBoxItemId /*nItemId*/, vcl::Window* /*pNewWindow*/) override {} + +private: + weld::Toolbar* m_pWeldedToolBar; + weld::Builder* m_pBuilder; + ToolBarManager* m_pManager; + ToolBoxItemId m_nCurrentId; + std::map m_aCommandToId; + std::map m_aIdToCommand; + std::vector m_aCommandOrder; +}; + +IMPL_LINK(WeldToolBarManager, Click, const OUString&, rCommand, void) +{ + m_nCurrentId = m_aCommandToId[rCommand]; + m_pManager->OnClick(true); +} + +IMPL_LINK(WeldToolBarManager, ToggleMenuHdl, const OUString&, rCommand, void) +{ + m_nCurrentId = m_aCommandToId[rCommand]; + m_pManager->OnDropdownClick(false); +} + +} // end anonymous namespace + +// XInterface, XTypeProvider, XServiceInfo + +ToolBarManager::ToolBarManager( const Reference< XComponentContext >& rxContext, + const Reference< XFrame >& rFrame, + OUString aResourceName, + ToolBox* pToolBar ) : + m_bDisposed( false ), + m_bFrameActionRegistered( false ), + m_bUpdateControllers( false ), + m_eSymbolSize(SvtMiscOptions::GetCurrentSymbolsSize()), + m_nContextMinPos(0), + m_pImpl( new VclToolBarManager( pToolBar ) ), + m_pToolBar( pToolBar ), + m_pWeldedToolBar( nullptr ), + m_aResourceName(std::move( aResourceName )), + m_xFrame( rFrame ), + m_xContext( rxContext ), + m_aAsyncUpdateControllersTimer( "framework::ToolBarManager m_aAsyncUpdateControllersTimer" ), + m_sIconTheme( SvtMiscOptions::GetIconTheme() ) +{ + Init(); +} + +ToolBarManager::ToolBarManager( const Reference< XComponentContext >& rxContext, + const Reference< XFrame >& rFrame, + OUString aResourceName, + weld::Toolbar* pToolBar, + weld::Builder* pBuilder ) : + m_bDisposed( false ), + m_bFrameActionRegistered( false ), + m_bUpdateControllers( false ), + m_eSymbolSize( SvtMiscOptions::GetCurrentSymbolsSize() ), + m_nContextMinPos(0), + m_pImpl( new WeldToolBarManager( pToolBar, pBuilder ) ), + m_pWeldedToolBar( pToolBar ), + m_aResourceName(std::move( aResourceName )), + m_xFrame( rFrame ), + m_xContext( rxContext ), + m_aAsyncUpdateControllersTimer( "framework::ToolBarManager m_aAsyncUpdateControllersTimer" ), + m_sIconTheme( SvtMiscOptions::GetIconTheme() ) +{ + Init(); +} + +void ToolBarManager::Init() +{ + OSL_ASSERT( m_xContext.is() ); + + m_pImpl->Init(); + + m_xToolbarControllerFactory = frame::theToolbarControllerFactory::get( m_xContext ); + m_xURLTransformer = URLTransformer::create( m_xContext ); + + m_pImpl->ConnectCallbacks(this); + + if (m_eSymbolSize == SFX_SYMBOLS_SIZE_LARGE) + m_pImpl->SetIconSize(ToolBoxButtonSize::Large); + else if (m_eSymbolSize == SFX_SYMBOLS_SIZE_32) + m_pImpl->SetIconSize(ToolBoxButtonSize::Size32); + else + m_pImpl->SetIconSize(ToolBoxButtonSize::Small); + + // enables a menu for clipped items and customization + SvtCommandOptions aCmdOptions; + ToolBoxMenuType nMenuType = ToolBoxMenuType::ClippedItems; + if ( !aCmdOptions.LookupDisabled( "CreateDialog")) + nMenuType |= ToolBoxMenuType::Customize; + + m_pImpl->SetMenuType( nMenuType ); + + // set name for testtool, the useful part is after the last '/' + sal_Int32 idx = m_aResourceName.lastIndexOf('/'); + idx++; // will become 0 if '/' not found: use full string + std::u16string_view aToolbarName = m_aResourceName.subView( idx ); + OUString aHelpIdAsString = ".HelpId:" + OUString::Concat(aToolbarName); + m_pImpl->SetHelpId( aHelpIdAsString ); + + m_aAsyncUpdateControllersTimer.SetTimeout( 50 ); + m_aAsyncUpdateControllersTimer.SetInvokeHandler( LINK( this, ToolBarManager, AsyncUpdateControllersHdl ) ); + + SvtMiscOptions().AddListenerLink( LINK( this, ToolBarManager, MiscOptionsChanged ) ); +} + +ToolBarManager::~ToolBarManager() +{ + assert(!m_aAsyncUpdateControllersTimer.IsActive()); + assert(!m_pToolBar); // must be disposed by ToolbarLayoutManager +} + +void ToolBarManager::Destroy() +{ + m_pImpl->Destroy(); + + SvtMiscOptions().RemoveListenerLink( LINK( this, ToolBarManager, MiscOptionsChanged ) ); +} + +ToolBox* ToolBarManager::GetToolBar() const +{ + SolarMutexGuard g; + return m_pToolBar; +} + +void ToolBarManager::CheckAndUpdateImages() +{ + SolarMutexGuard g; + bool bRefreshImages = false; + + sal_Int16 eNewSymbolSize = SvtMiscOptions::GetCurrentSymbolsSize(); + + if (m_eSymbolSize != eNewSymbolSize ) + { + bRefreshImages = true; + m_eSymbolSize = eNewSymbolSize; + } + + const OUString& sCurrentIconTheme = SvtMiscOptions::GetIconTheme(); + if ( m_sIconTheme != sCurrentIconTheme ) + { + bRefreshImages = true; + m_sIconTheme = sCurrentIconTheme; + } + + // Refresh images if requested + if ( bRefreshImages ) + RefreshImages(); +} + +void ToolBarManager::RefreshImages() +{ + SolarMutexGuard g; + + if (m_eSymbolSize == SFX_SYMBOLS_SIZE_LARGE) + m_pImpl->SetIconSize(ToolBoxButtonSize::Large); + else if (m_eSymbolSize == SFX_SYMBOLS_SIZE_32) + m_pImpl->SetIconSize(ToolBoxButtonSize::Size32); + else + m_pImpl->SetIconSize(ToolBoxButtonSize::Small); + + for ( auto const& it : m_aControllerMap ) + { + Reference< 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 + { + OUString aCommandURL = m_pImpl->GetItemCommand( it.first ); + vcl::ImageType eImageType = m_pImpl->GetImageSize(); + Image aImage = vcl::CommandInfoProvider::GetImageForCommand(aCommandURL, m_xFrame, eImageType); + // Try also to query for add-on images before giving up and use an + // empty image. + bool bBigImages = eImageType != vcl::ImageType::Size16; + if ( !aImage ) + aImage = Image(framework::AddonsOptions().GetImageFromURL(aCommandURL, bBigImages)); + m_pImpl->SetItemImage( it.first, aCommandURL, aImage ); + } + } + + m_pImpl->UpdateSize(); +} + +void ToolBarManager::UpdateControllers() +{ + + if( officecfg::Office::Common::Misc::DisableUICustomization::get() ) + { + Any a; + Reference< XLayoutManager > xLayoutManager; + Reference< XPropertySet > xFramePropSet( m_xFrame, UNO_QUERY ); + if ( xFramePropSet.is() ) + a = xFramePropSet->getPropertyValue("LayoutManager"); + a >>= xLayoutManager; + Reference< XDockableWindow > xDockable( m_pImpl->GetInterface(), UNO_QUERY ); + if ( xLayoutManager.is() && xDockable.is() ) + { + css::awt::Point aPoint; + aPoint.X = aPoint.Y = SAL_MAX_INT32; + xLayoutManager->dockWindow( m_aResourceName, DockingArea_DOCKINGAREA_DEFAULT, aPoint ); + xLayoutManager->lockWindow( m_aResourceName ); + } + } + + if ( !m_bUpdateControllers ) + { + m_bUpdateControllers = true; + for (auto const& controller : m_aControllerMap) + { + try + { + Reference< XUpdatable > xUpdatable( controller.second, UNO_QUERY ); + if ( xUpdatable.is() ) + xUpdatable->update(); + } + catch (const Exception&) + { + } + } + } + m_bUpdateControllers = false; +} + +//for update toolbar controller via Support Visible +void ToolBarManager::UpdateController( const css::uno::Reference< css::frame::XToolbarController >& xController) +{ + + if ( !m_bUpdateControllers ) + { + m_bUpdateControllers = true; + try + { if(xController.is()) + { + Reference< XUpdatable > xUpdatable( xController, UNO_QUERY ); + if ( xUpdatable.is() ) + xUpdatable->update(); + } + } + catch (const Exception&) + { + } + + } + m_bUpdateControllers = false; +} + +void ToolBarManager::frameAction( const FrameActionEvent& Action ) +{ + SolarMutexGuard g; + if ( Action.Action == FrameAction_CONTEXT_CHANGED && !m_bDisposed ) + { + if (m_aImageController) + m_aImageController->update(); + m_aAsyncUpdateControllersTimer.Start(); + } +} + +void SAL_CALL ToolBarManager::disposing( const EventObject& Source ) +{ + SolarMutexGuard g; + + if ( m_bDisposed ) + return; + + RemoveControllers(); + + if ( m_xDocImageManager.is() ) + { + try + { + m_xDocImageManager->removeConfigurationListener( + Reference< XUIConfigurationListener >(this) ); + } + catch (const Exception&) + { + } + } + + if ( m_xModuleImageManager.is() ) + { + try + { + m_xModuleImageManager->removeConfigurationListener( + Reference< XUIConfigurationListener >(this) ); + } + catch (const Exception&) + { + } + } + + m_xDocImageManager.clear(); + m_xModuleImageManager.clear(); + + if ( Source.Source == Reference< XInterface >( m_xFrame, UNO_QUERY )) + m_xFrame.clear(); + + m_xContext.clear(); +} + +// XComponent +void SAL_CALL ToolBarManager::dispose() +{ + Reference< XComponent > xThis(this); + + { + EventObject aEvent( xThis ); + std::unique_lock aGuard(m_mutex); + m_aListenerContainer.disposeAndClear( aGuard, aEvent ); + } + { + SolarMutexGuard g; + + if (m_bDisposed) + { + return; + } + + RemoveControllers(); + + if ( m_xDocImageManager.is() ) + { + try + { + m_xDocImageManager->removeConfigurationListener( + Reference< XUIConfigurationListener >(this) ); + } + catch (const Exception&) + { + } + } + m_xDocImageManager.clear(); + if ( m_xModuleImageManager.is() ) + { + try + { + m_xModuleImageManager->removeConfigurationListener( + Reference< XUIConfigurationListener >(this) ); + } + catch (const Exception&) + { + } + } + m_xModuleImageManager.clear(); + + if ( m_aOverflowManager.is() ) + { + m_aOverflowManager->dispose(); + m_aOverflowManager.clear(); + } + + // We have to destroy our toolbar instance now. + Destroy(); + m_pToolBar.clear(); + + if ( m_bFrameActionRegistered && m_xFrame.is() ) + { + try + { + m_xFrame->removeFrameActionListener( Reference< XFrameActionListener >(this) ); + } + catch (const Exception&) + { + } + } + + m_xFrame.clear(); + m_xContext.clear(); + + // stop timer to prevent timer events after dispose + // do it last because other calls could restart timer in StateChanged() + m_aAsyncUpdateControllersTimer.Stop(); + + m_bDisposed = true; + } +} + +void SAL_CALL ToolBarManager::addEventListener( const Reference< XEventListener >& xListener ) +{ + SolarMutexGuard g; + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + if ( m_bDisposed ) + throw DisposedException(); + + std::unique_lock aGuard(m_mutex); + m_aListenerContainer.addInterface( aGuard, xListener ); +} + +void SAL_CALL ToolBarManager::removeEventListener( const Reference< XEventListener >& xListener ) +{ + std::unique_lock aGuard(m_mutex); + m_aListenerContainer.removeInterface( aGuard, xListener ); +} + +// XUIConfigurationListener +void SAL_CALL ToolBarManager::elementInserted( const css::ui::ConfigurationEvent& Event ) +{ + impl_elementChanged(false,Event); +} + +void SAL_CALL ToolBarManager::elementRemoved( const css::ui::ConfigurationEvent& Event ) +{ + impl_elementChanged(true,Event); +} +void ToolBarManager::impl_elementChanged(bool const isRemove, + const css::ui::ConfigurationEvent& Event) +{ + SolarMutexGuard g; + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + if ( m_bDisposed ) + return; + + Reference< XNameAccess > xNameAccess; + sal_Int16 nImageType = sal_Int16(); + sal_Int16 nCurrentImageType = getCurrentImageType(); + + if (!(( Event.aInfo >>= nImageType ) && + ( nImageType == nCurrentImageType ) && + ( Event.Element >>= xNameAccess ))) + return; + + sal_Int16 nImageInfo( 1 ); + Reference< XInterface > xIfacDocImgMgr( m_xDocImageManager, UNO_QUERY ); + if ( xIfacDocImgMgr == Event.Source ) + nImageInfo = 0; + + const Sequence< OUString > aSeq = xNameAccess->getElementNames(); + for ( OUString const & commandName : aSeq ) + { + CommandToInfoMap::iterator pIter = m_aCommandMap.find( commandName ); + if ( pIter != m_aCommandMap.end() && ( pIter->second.nImageInfo >= nImageInfo )) + { + if (isRemove) + { + Image aImage; + if (( pIter->second.nImageInfo == 0 ) && ( pIter->second.nImageInfo == nImageInfo )) + { + // Special case: An image from the document image manager has been removed. + // It is possible that we have an image at our module image manager. Before + // we can remove our image we have to ask our module image manager. + Sequence< OUString > aCmdURLSeq{ pIter->first }; + Sequence< Reference< XGraphic > > aGraphicSeq; + aGraphicSeq = m_xModuleImageManager->getImages( nImageType, aCmdURLSeq ); + aImage = Image( aGraphicSeq[0] ); + } + + setToolBarImage(aImage,pIter); + } // if (isRemove) + else + { + Reference< XGraphic > xGraphic; + if ( xNameAccess->getByName( commandName ) >>= xGraphic ) + { + Image aImage( xGraphic ); + setToolBarImage(aImage,pIter); + } + pIter->second.nImageInfo = nImageInfo; + } + } + } +} +void ToolBarManager::setToolBarImage(const Image& rImage, + const CommandToInfoMap::const_iterator& rIter) +{ + const ::std::vector& rIDs = rIter->second.aIds; + m_pImpl->SetItemImage( rIter->second.nId, rIter->first, rImage ); + for (auto const& it : rIDs) + { + m_pImpl->SetItemImage(it, rIter->first, rImage); + } +} + +void SAL_CALL ToolBarManager::elementReplaced( const css::ui::ConfigurationEvent& Event ) +{ + impl_elementChanged(false,Event); +} + +void ToolBarManager::RemoveControllers() +{ + DBG_TESTSOLARMUTEX(); + assert(!m_bDisposed); + + m_aSubToolBarControllerMap.clear(); + + if (m_aImageController) + m_aImageController->dispose(); + m_aImageController.clear(); + + // i90033 + // Remove item window pointers from the toolbar. They were + // destroyed by the dispose() at the XComponent. This is needed + // as VCL code later tries to access the item window data in certain + // dtors where the item window is already invalid! + for ( ToolBox::ImplToolItems::size_type i = 0; i < m_pImpl->GetItemCount(); i++ ) + { + ToolBoxItemId nItemId = m_pImpl->GetItemId( i ); + if ( nItemId > ToolBoxItemId(0) ) + { + Reference< XComponent > xComponent( m_aControllerMap[ nItemId ], UNO_QUERY ); + if ( xComponent.is() ) + { + try + { + xComponent->dispose(); + } + catch (const Exception&) + { + } + } + m_pImpl->SetItemWindow(nItemId, nullptr); + } + } + m_aControllerMap.clear(); +} + +void ToolBarManager::CreateControllers() +{ + Reference< XWindow > xToolbarWindow = m_pImpl->GetInterface(); + + css::util::URL aURL; + bool bHasDisabledEntries = SvtCommandOptions().HasEntriesDisabled(); + SvtCommandOptions aCmdOptions; + + for ( ToolBox::ImplToolItems::size_type i = 0; i < m_pImpl->GetItemCount(); i++ ) + { + ToolBoxItemId nId = m_pImpl->GetItemId( i ); + if ( nId == ToolBoxItemId(0) ) + continue; + + bool bInit( true ); + bool bCreate( true ); + Reference< XStatusListener > xController; + + OUString aCommandURL( m_pImpl->GetItemCommand( nId ) ); + // Command can be just an alias to another command. + auto aProperties = vcl::CommandInfoProvider::GetCommandProperties(aCommandURL, m_aModuleIdentifier); + OUString aRealCommandURL( vcl::CommandInfoProvider::GetRealCommandForCommand(aProperties) ); + if ( !aRealCommandURL.isEmpty() ) + aCommandURL = aRealCommandURL; + + if ( bHasDisabledEntries ) + { + aURL.Complete = aCommandURL; + m_xURLTransformer->parseStrict( aURL ); + if ( aCmdOptions.LookupDisabled( aURL.Path )) + { + m_aControllerMap[ nId ] = xController; + m_pImpl->HideItem( nId, aCommandURL ); + continue; + } + } + + if ( m_xToolbarControllerFactory.is() && + m_xToolbarControllerFactory->hasController( aCommandURL, m_aModuleIdentifier )) + { + Reference xMSF(m_xContext->getServiceManager(), UNO_QUERY_THROW); + Sequence< Any > aArgs( comphelper::InitAnyPropertySequence( { + { "ModuleIdentifier", Any(m_aModuleIdentifier) }, + { "Frame", Any(m_xFrame) }, + { "ServiceManager", Any(xMSF) }, + { "ParentWindow", Any(xToolbarWindow) }, + { "Identifier", Any(sal_uInt16(nId)) }, + } )); + xController.set( m_xToolbarControllerFactory->createInstanceWithArgumentsAndContext( aCommandURL, aArgs, m_xContext ), + UNO_QUERY ); + bInit = false; // Initialization is done through the factory service + } + + if (( aCommandURL == ".uno:OpenUrl" ) && ( !m_pImpl->IsItemVisible(nId, aCommandURL))) + bCreate = false; + + if ( !xController.is() && bCreate ) + { + if ( m_pToolBar ) + xController = CreateToolBoxController( m_xFrame, m_pToolBar, nId, aCommandURL ); + if ( !xController ) + { + if ( aCommandURL.startsWith( ".uno:StyleApply?" ) ) + { + xController.set( new StyleToolbarController( m_xContext, m_xFrame, aCommandURL )); + m_pImpl->SetItemCheckable( nId ); + } + else if ( aCommandURL.startsWith( "private:resource/" ) ) + { + xController.set( m_xContext->getServiceManager()->createInstanceWithContext( + "com.sun.star.comp.framework.GenericPopupToolbarController", m_xContext ), UNO_QUERY ); + } + else if ( m_pToolBar && m_pToolBar->GetItemData( nId ) != nullptr ) + { + // retrieve additional parameters + OUString aControlType = static_cast< AddonsParams* >( m_pToolBar->GetItemData( nId ))->aControlType; + sal_uInt16 nWidth = static_cast< AddonsParams* >( m_pToolBar->GetItemData( nId ))->nWidth; + + Reference< XStatusListener > xStatusListener( + ToolBarMerger::CreateController( m_xContext, + m_xFrame, + m_pToolBar, + aCommandURL, + nId, + nWidth, + aControlType ).get(), UNO_QUERY ); + + xController = xStatusListener; + } + else + { + if ( m_pToolBar ) + xController.set( new GenericToolbarController( m_xContext, m_xFrame, m_pToolBar, nId, aCommandURL )); + else + xController.set( new GenericToolbarController( m_xContext, m_xFrame, *m_pWeldedToolBar, aCommandURL )); + } + } + } + + // Accessibility support: Set toggle button role for specific commands + const sal_Int32 nProps = vcl::CommandInfoProvider::GetPropertiesForCommand(aCommandURL, m_aModuleIdentifier); + if (nProps & UICOMMANDDESCRIPTION_PROPERTIES_TOGGLEBUTTON) + m_pImpl->SetItemCheckable(nId); + + // Associate ID and controller to be able to retrieve + // the controller from the ID later. + m_aControllerMap[ nId ] = xController; + + // Fill sub-toolbars into our hash-map + Reference< XSubToolbarController > xSubToolBar( xController, UNO_QUERY ); + if ( xSubToolBar.is() && xSubToolBar->opensSubToolbar() ) + { + OUString aSubToolBarName = xSubToolBar->getSubToolbarName(); + if ( !aSubToolBarName.isEmpty() ) + { + SubToolBarToSubToolBarControllerMap::iterator pIter = + m_aSubToolBarControllerMap.find( aSubToolBarName ); + if ( pIter == m_aSubToolBarControllerMap.end() ) + { + SubToolBarControllerVector aSubToolBarVector; + aSubToolBarVector.push_back( xSubToolBar ); + m_aSubToolBarControllerMap.emplace( + aSubToolBarName, aSubToolBarVector ); + } + else + pIter->second.push_back( xSubToolBar ); + } + } + + Reference< XInitialization > xInit( xController, UNO_QUERY ); + if ( xInit.is() ) + { + if ( bInit ) + { + Reference xMSF(m_xContext->getServiceManager(), UNO_QUERY_THROW); + Sequence< Any > aArgs( comphelper::InitAnyPropertySequence( { + { "Frame", Any(m_xFrame) }, + { "CommandURL", Any(aCommandURL) }, + { "ServiceManager", Any(xMSF) }, + { "ParentWindow", Any(xToolbarWindow) }, + { "ModuleIdentifier", Any(m_aModuleIdentifier) }, + { "Identifier", Any(sal_uInt16(nId)) }, + } )); + + xInit->initialize( aArgs ); + } + + // Request an item window from the toolbar controller and set it at the VCL toolbar + Reference< XToolbarController > xTbxController( xController, UNO_QUERY ); + if ( xTbxController.is() && xToolbarWindow.is() ) + { + Reference< XWindow > xWindow = xTbxController->createItemWindow( xToolbarWindow ); + if ( xWindow.is() ) + { + VclPtr pItemWin = VCLUnoHelper::GetWindow( xWindow ); + if ( pItemWin ) + { + WindowType nType = pItemWin->GetType(); + if ( m_pToolBar && (nType == WindowType::LISTBOX || nType == WindowType::MULTILISTBOX || nType == WindowType::COMBOBOX) ) + pItemWin->SetAccessibleName( m_pToolBar->GetItemText( nId ) ); + m_pImpl->SetItemWindow( nId, pItemWin ); + } + } + } + } + + //for update Controller via support visible state + Reference< XPropertySet > xPropSet( xController, UNO_QUERY ); + if ( xPropSet.is() ) + { + try + { + bool bSupportVisible = true; + Any a( xPropSet->getPropertyValue("SupportsVisible") ); + a >>= bSupportVisible; + if (bSupportVisible) + { + Reference< XToolbarController > xTbxController( xController, UNO_QUERY ); + UpdateController(xTbxController); + } + } + catch (const RuntimeException&) + { + throw; + } + catch (const Exception&) + { + } + } + } + + AddFrameActionListener(); +} + +void ToolBarManager::AddFrameActionListener() +{ + if ( !m_bFrameActionRegistered && m_xFrame.is() ) + { + m_bFrameActionRegistered = true; + m_xFrame->addFrameActionListener( Reference< XFrameActionListener >(this) ); + } +} + +ToolBoxItemBits ToolBarManager::ConvertStyleToToolboxItemBits( sal_Int32 nStyle ) +{ + ToolBoxItemBits nItemBits( ToolBoxItemBits::NONE ); + if ( nStyle & css::ui::ItemStyle::RADIO_CHECK ) + nItemBits |= ToolBoxItemBits::RADIOCHECK; + if ( nStyle & css::ui::ItemStyle::ALIGN_LEFT ) + nItemBits |= ToolBoxItemBits::LEFT; + if ( nStyle & css::ui::ItemStyle::AUTO_SIZE ) + nItemBits |= ToolBoxItemBits::AUTOSIZE; + if ( nStyle & css::ui::ItemStyle::DROP_DOWN ) + nItemBits |= ToolBoxItemBits::DROPDOWN; + if ( nStyle & css::ui::ItemStyle::REPEAT ) + nItemBits |= ToolBoxItemBits::REPEAT; + if ( nStyle & css::ui::ItemStyle::DROPDOWN_ONLY ) + nItemBits |= ToolBoxItemBits::DROPDOWNONLY; + if ( nStyle & css::ui::ItemStyle::TEXT ) + nItemBits |= ToolBoxItemBits::TEXT_ONLY; + if ( nStyle & css::ui::ItemStyle::ICON ) + nItemBits |= ToolBoxItemBits::ICON_ONLY; + + return nItemBits; +} + +void ToolBarManager::InitImageManager() +{ + Reference< XModuleManager2 > xModuleManager = ModuleManager::create( m_xContext ); + if ( !m_xDocImageManager.is() ) + { + Reference< XModel > xModel( GetModelFromFrame() ); + if ( xModel.is() ) + { + Reference< XUIConfigurationManagerSupplier > xSupplier( xModel, UNO_QUERY ); + if ( xSupplier.is() ) + { + Reference< XUIConfigurationManager > xDocUICfgMgr = xSupplier->getUIConfigurationManager(); + m_xDocImageManager.set( xDocUICfgMgr->getImageManager(), UNO_QUERY ); + m_xDocImageManager->addConfigurationListener( + Reference< XUIConfigurationListener >(this) ); + } + } + } + + try + { + m_aModuleIdentifier = xModuleManager->identify( Reference< XInterface >( m_xFrame, UNO_QUERY ) ); + } + catch (const Exception&) + { + } + + if ( !m_xModuleImageManager.is() ) + { + Reference< XModuleUIConfigurationManagerSupplier > xModuleCfgMgrSupplier = + theModuleUIConfigurationManagerSupplier::get( m_xContext ); + Reference< XUIConfigurationManager > xUICfgMgr = xModuleCfgMgrSupplier->getUIConfigurationManager( m_aModuleIdentifier ); + m_xModuleImageManager.set( xUICfgMgr->getImageManager(), UNO_QUERY ); + m_xModuleImageManager->addConfigurationListener( Reference< XUIConfigurationListener >(this) ); + } +} + +void ToolBarManager::FillToolbar( const Reference< XIndexAccess >& rItemContainer, + const Reference< XIndexAccess >& rContextData, + const OUString& rContextToolbarName ) +{ + OString aTbxName = OUStringToOString( m_aResourceName, RTL_TEXTENCODING_ASCII_US ); + SAL_INFO( "fwk.uielement", "framework (cd100003) ::ToolBarManager::FillToolbar " << aTbxName ); + + SolarMutexGuard g; + + if ( m_bDisposed ) + return; + + InitImageManager(); + + RemoveControllers(); + + // reset and fill command map + m_pImpl->Clear(); + m_aControllerMap.clear(); + m_aCommandMap.clear(); + + ToolBoxItemId nId(1), nAddonId(1000); + FillToolbarFromContainer( rItemContainer, m_aResourceName, nId, nAddonId ); + m_aContextResourceName = rContextToolbarName; + if ( rContextData.is() ) + { + m_pImpl->InsertSeparator(); + FillToolbarFromContainer( rContextData, m_aContextResourceName, nId, nAddonId ); + } + + // Request images for all toolbar items. Must be done before CreateControllers as + // some controllers need access to the image. + RequestImages(); + + // Create controllers after we set the images. There are controllers which needs + // an image at the toolbar at creation time! + CreateControllers(); + + // Notify controllers that they are now correctly initialized and can start listening + // toolbars that will open in popup mode will be updated immediately to avoid flickering + if( m_pImpl->WillUsePopupMode() ) + UpdateControllers(); + else if ( m_pImpl->IsReallyVisible() ) + { + m_aAsyncUpdateControllersTimer.Start(); + } + + // Try to retrieve UIName from the container property set and set it as the title + // if it is not empty. + Reference< XPropertySet > xPropSet( rItemContainer, UNO_QUERY ); + if ( !xPropSet.is() ) + return; + + try + { + OUString aUIName; + xPropSet->getPropertyValue("UIName") >>= aUIName; + if ( !aUIName.isEmpty() ) + m_pImpl->SetName( aUIName ); + } + catch (const Exception&) + { + } +} + +void ToolBarManager::FillToolbarFromContainer( const Reference< XIndexAccess >& rItemContainer, + const OUString& rResourceName, ToolBoxItemId& nId, ToolBoxItemId& nAddonId ) +{ + m_nContextMinPos = m_pImpl->GetItemCount(); + CommandInfo aCmdInfo; + for ( sal_Int32 n = 0; n < rItemContainer->getCount(); n++ ) + { + Sequence< PropertyValue > aProps; + OUString aCommandURL; + OUString aLabel; + OUString aTooltip; + sal_uInt16 nType( css::ui::ItemType::DEFAULT ); + sal_uInt32 nStyle( 0 ); + + try + { + if ( rItemContainer->getByIndex( n ) >>= aProps ) + { + bool bIsVisible( true ); + for ( PropertyValue const & prop : std::as_const(aProps) ) + { + if ( prop.Name == ITEM_DESCRIPTOR_COMMANDURL ) + prop.Value >>= aCommandURL; + else if ( prop.Name == "Label" ) + prop.Value >>= aLabel; + else if ( prop.Name == "Tooltip" ) + prop.Value >>= aTooltip; + else if ( prop.Name == "Type" ) + prop.Value >>= nType; + else if ( prop.Name == ITEM_DESCRIPTOR_VISIBLE ) + prop.Value >>= bIsVisible; + else if ( prop.Name == "Style" ) + prop.Value >>= nStyle; + } + + if (!aCommandURL.isEmpty() && vcl::CommandInfoProvider::IsExperimental(aCommandURL, m_aModuleIdentifier) && + !officecfg::Office::Common::Misc::ExperimentalMode::get()) + { + continue; + } + + if (( nType == css::ui::ItemType::DEFAULT ) && !aCommandURL.isEmpty() ) + { + auto aProperties = vcl::CommandInfoProvider::GetCommandProperties(aCommandURL, m_aModuleIdentifier); + if (!aProperties.hasElements()) // E.g., user-provided macro command? + aProperties = aProps; // Use existing info, including user-provided Label + + ToolBoxItemBits nItemBits = ConvertStyleToToolboxItemBits( nStyle ); + + if ( aTooltip.isEmpty() ) + aTooltip = vcl::CommandInfoProvider::GetTooltipForCommand(aCommandURL, aProperties, m_xFrame); + + if ( aLabel.isEmpty() ) + aLabel = vcl::CommandInfoProvider::GetLabelForCommand(aProperties); + + m_pImpl->InsertItem(nId, aCommandURL, aTooltip, aLabel, nItemBits); + + // Fill command map. It stores all our commands and from what + // image manager we got our image. So we can decide if we have to use an + // image from a notification message. + auto pIter = m_aCommandMap.emplace( aCommandURL, aCmdInfo ); + if ( pIter.second ) + { + aCmdInfo.nId = nId; + pIter.first->second.nId = nId; + } + else + { + pIter.first->second.aIds.push_back( nId ); + } + + if ( !bIsVisible ) + m_pImpl->HideItem( nId, aCommandURL ); + + ++nId; + } + else if ( nType == css::ui::ItemType::SEPARATOR_LINE ) + { + m_pImpl->InsertSeparator(); + } + else if ( nType == css::ui::ItemType::SEPARATOR_SPACE ) + { + m_pImpl->InsertSpace(); + } + else if ( nType == css::ui::ItemType::SEPARATOR_LINEBREAK ) + { + m_pImpl->InsertBreak(); + } + } + } + catch (const css::lang::IndexOutOfBoundsException&) + { + break; + } + } + + // Support add-on toolbar merging here. Working directly on the toolbar object is much + // simpler and faster. + MergeToolbarInstructionContainer aMergeInstructionContainer; + + // Retrieve the toolbar name from the resource name + OUString aToolbarName( rResourceName ); + sal_Int32 nIndex = aToolbarName.lastIndexOf( '/' ); + if (( nIndex > 0 ) && ( nIndex < aToolbarName.getLength() )) + aToolbarName = aToolbarName.copy( nIndex+1 ); + + AddonsOptions().GetMergeToolbarInstructions( aToolbarName, aMergeInstructionContainer ); + + if ( !aMergeInstructionContainer.empty() ) + { + const sal_uInt32 nCount = aMergeInstructionContainer.size(); + for ( sal_uInt32 i=0; i < nCount; i++ ) + { + MergeToolbarInstruction& rInstruction = aMergeInstructionContainer[i]; + if ( ToolBarMerger::IsCorrectContext( rInstruction.aMergeContext, m_aModuleIdentifier )) + { + m_pImpl->MergeToolbar(nAddonId, m_nContextMinPos, m_aModuleIdentifier, m_aCommandMap, rInstruction); + } + } + } +} + +void ToolBarManager::FillAddonToolbar( const Sequence< Sequence< PropertyValue > >& rAddonToolbar ) +{ + if (!m_pToolBar) + return; + + SolarMutexGuard g; + + if ( m_bDisposed ) + return; + + InitImageManager(); + + RemoveControllers(); + + // reset and fill command map + m_pToolBar->Clear(); + m_aControllerMap.clear(); + m_aCommandMap.clear(); + + ToolBoxItemId nId( 1 ); + CommandInfo aCmdInfo; + for ( const Sequence< PropertyValue >& rSeq : rAddonToolbar ) + { + OUString aURL; + OUString aTitle; + OUString aContext; + OUString aTarget; + OUString aControlType; + sal_uInt16 nWidth( 0 ); + + ToolBarMerger::ConvertSequenceToValues( rSeq, aURL, aTitle, aTarget, aContext, aControlType, nWidth ); + + if ( ToolBarMerger::IsCorrectContext( aContext, m_aModuleIdentifier ) ) + { + if ( aURL == "private:separator" ) + { + ToolBox::ImplToolItems::size_type nCount = m_pToolBar->GetItemCount(); + if ( nCount > 0 && m_pToolBar->GetItemType( nCount-1 ) != ToolBoxItemType::SEPARATOR ) + m_pToolBar->InsertSeparator(); + } + else + { + m_pToolBar->InsertItem( nId, aTitle, aURL ); + + OUString aShortcut(vcl::CommandInfoProvider::GetCommandShortcut(aURL, m_xFrame)); + if (!aShortcut.isEmpty()) + m_pToolBar->SetQuickHelpText(nId, aTitle + " (" + aShortcut + ")"); + + // Create AddonsParams to hold additional information we will need in the future + AddonsParams* pRuntimeItemData = new AddonsParams; + pRuntimeItemData->aControlType = aControlType; + pRuntimeItemData->nWidth = nWidth; + m_pToolBar->SetItemData( nId, pRuntimeItemData ); + + // Fill command map. It stores all our commands and from what + // image manager we got our image. So we can decide if we have to use an + // image from a notification message. + auto pIter = m_aCommandMap.emplace( aURL, aCmdInfo ); + if ( pIter.second ) + { + aCmdInfo.nId = nId; + pIter.first->second.nId = nId; + } + else + { + pIter.first->second.aIds.push_back( nId ); + } + ++nId; + } + } + } + + // Don't setup images yet, AddonsToolbarWrapper::populateImages does that. + // (But some controllers might need an image at the toolbar at creation time!) + CreateControllers(); + + // Notify controllers that they are now correctly initialized and can start listening. + UpdateControllers(); +} + +void ToolBarManager::FillOverflowToolbar( ToolBox const * pParent ) +{ + if (!m_pToolBar) + return; + + CommandInfo aCmdInfo; + bool bInsertSeparator = false; + for ( ToolBox::ImplToolItems::size_type i = 0; i < pParent->GetItemCount(); ++i ) + { + ToolBoxItemId nId = pParent->GetItemId( i ); + if ( pParent->IsItemClipped( nId ) ) + { + if ( bInsertSeparator ) + { + m_pToolBar->InsertSeparator(); + bInsertSeparator = false; + } + + const OUString aCommandURL( pParent->GetItemCommand( nId ) ); + m_pToolBar->InsertItem( nId, pParent->GetItemText( nId ), aCommandURL ); + m_pToolBar->SetQuickHelpText( nId, pParent->GetQuickHelpText( nId ) ); + + // Handle possible add-on controls. + AddonsParams* pAddonParams = static_cast< AddonsParams* >( pParent->GetItemData( nId ) ); + if ( pAddonParams ) + m_pToolBar->SetItemData( nId, new AddonsParams( *pAddonParams ) ); + + // Fill command map. It stores all our commands and from what + // image manager we got our image. So we can decide if we have to use an + // image from a notification message. + auto pIter = m_aCommandMap.emplace( aCommandURL, aCmdInfo ); + if ( pIter.second ) + { + aCmdInfo.nId = nId; + pIter.first->second.nId = nId; + } + else + { + pIter.first->second.aIds.push_back( nId ); + } + } + else + { + ToolBoxItemType eType = pParent->GetItemType( i ); + if ( m_pToolBar->GetItemCount() && + ( eType == ToolBoxItemType::SEPARATOR || eType == ToolBoxItemType::BREAK ) ) + bInsertSeparator = true; + } + } + + InitImageManager(); + + // Request images for all toolbar items. Must be done before CreateControllers as + // some controllers need access to the image. + RequestImages(); + + // Create controllers after we set the images. There are controllers which needs + // an image at the toolbar at creation time! + CreateControllers(); + + // Notify controllers that they are now correctly initialized and can start listening + // toolbars that will open in popup mode will be updated immediately to avoid flickering + UpdateControllers(); +} + +void ToolBarManager::RequestImages() +{ + + // Request images from image manager + Sequence< OUString > aCmdURLSeq( comphelper::mapKeysToSequence(m_aCommandMap) ); + Sequence< Reference< XGraphic > > aDocGraphicSeq; + Sequence< Reference< XGraphic > > aModGraphicSeq; + + sal_Int16 nImageType = getCurrentImageType(); + + if ( m_xDocImageManager.is() ) + aDocGraphicSeq = m_xDocImageManager->getImages(nImageType, aCmdURLSeq); + aModGraphicSeq = m_xModuleImageManager->getImages(nImageType, aCmdURLSeq); + + sal_uInt32 i = 0; + CommandToInfoMap::iterator pIter = m_aCommandMap.begin(); + CommandToInfoMap::iterator pEnd = m_aCommandMap.end(); + while ( pIter != pEnd ) + { + Image aImage; + if ( aDocGraphicSeq.hasElements() ) + aImage = Image( aDocGraphicSeq[i] ); + if ( !aImage ) + { + aImage = Image( aModGraphicSeq[i] ); + // Try also to query for add-on images before giving up and use an + // empty image. + if ( !aImage ) + aImage = Image(framework::AddonsOptions().GetImageFromURL(aCmdURLSeq[i], SvtMiscOptions::AreCurrentSymbolsLarge())); + + pIter->second.nImageInfo = 1; // mark image as module based + } + else + { + pIter->second.nImageInfo = 0; // mark image as document based + } + setToolBarImage(aImage,pIter); + ++pIter; + ++i; + } + + assert(!m_aImageController); // an existing one isn't disposed here + m_aImageController = new ImageOrientationController(m_xContext, m_xFrame, m_pImpl->GetInterface(), m_aModuleIdentifier); + m_aImageController->update(); +} + +void ToolBarManager::notifyRegisteredControllers( const OUString& aUIElementName, const OUString& aCommand ) +{ + SolarMutexClearableGuard aGuard; + if ( m_aSubToolBarControllerMap.empty() ) + return; + + SubToolBarToSubToolBarControllerMap::const_iterator pIter = + m_aSubToolBarControllerMap.find( aUIElementName ); + + if ( pIter == m_aSubToolBarControllerMap.end() ) + return; + + const SubToolBarControllerVector& rSubToolBarVector = pIter->second; + if ( rSubToolBarVector.empty() ) + return; + + SubToolBarControllerVector aNotifyVector = rSubToolBarVector; + aGuard.clear(); + + const sal_uInt32 nCount = aNotifyVector.size(); + for ( sal_uInt32 i=0; i < nCount; i++ ) + { + try + { + Reference< XSubToolbarController > xController = aNotifyVector[i]; + if ( xController.is() ) + xController->functionSelected( aCommand ); + } + catch (const RuntimeException&) + { + throw; + } + catch (const Exception&) + { + } + } +} + +void ToolBarManager::HandleClick(ClickAction eClickAction) +{ + SolarMutexGuard g; + + if ( m_bDisposed ) + return; + + ToolBoxItemId nId( m_pImpl->GetCurItemId() ); + ToolBarControllerMap::const_iterator pIter = m_aControllerMap.find( nId ); + if ( pIter == m_aControllerMap.end() ) + return; + + Reference< XToolbarController > xController( pIter->second, UNO_QUERY ); + + if ( xController.is() ) + { + switch (eClickAction) + { + case ClickAction::Click: + xController->click(); + break; + + case ClickAction::DblClick: + xController->doubleClick(); + break; + + case ClickAction::Execute: + xController->execute(0); + break; + } + } +} + +void ToolBarManager::OnClick(bool bUseExecute) +{ + if (bUseExecute) + HandleClick(ClickAction::Execute); + else + HandleClick(ClickAction::Click); +} + +IMPL_LINK_NOARG(ToolBarManager, DropdownClick, ToolBox *, void) +{ + OnDropdownClick(true); +} + +void ToolBarManager::OnDropdownClick(bool bCreatePopupWindow) +{ + SolarMutexGuard g; + + if ( m_bDisposed ) + return; + + ToolBoxItemId nId( m_pImpl->GetCurItemId() ); + ToolBarControllerMap::const_iterator pIter = m_aControllerMap.find( nId ); + if ( pIter == m_aControllerMap.end() ) + return; + + Reference< XToolbarController > xController( pIter->second, UNO_QUERY ); + + if ( xController.is() ) + { + if (bCreatePopupWindow) + { + Reference< XWindow > xWin = xController->createPopupWindow(); + if ( xWin.is() ) + xWin->setFocus(); + } + else + { + xController->click(); + } + } +} + +IMPL_LINK_NOARG(ToolBarManager, DoubleClick, ToolBox *, void) +{ + HandleClick(ClickAction::DblClick); +} + +Reference< XModel > ToolBarManager::GetModelFromFrame() const +{ + Reference< XController > xController = m_xFrame->getController(); + Reference< XModel > xModel; + if ( xController.is() ) + xModel = xController->getModel(); + + return xModel; +} + +bool ToolBarManager::IsPluginMode() const +{ + bool bPluginMode( false ); + + if ( m_xFrame.is() ) + { + Reference< XModel > xModel = GetModelFromFrame(); + if ( xModel.is() ) + { + Sequence< PropertyValue > aSeq = xModel->getArgs(); + utl::MediaDescriptor aMediaDescriptor( aSeq ); + bPluginMode = aMediaDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_VIEWONLY, false ); + } + } + + return bPluginMode; +} + +void ToolBarManager::AddCustomizeMenuItems(ToolBox const * pToolBar) +{ + if (!m_pToolBar) + return; + + // No config menu entries if command ".uno:ConfigureDialog" is not enabled + Reference< XDispatch > xDisp; + css::util::URL aURL; + if ( m_xFrame.is() ) + { + Reference< XDispatchProvider > xProv( m_xFrame, UNO_QUERY ); + aURL.Complete = ".uno:ConfigureDialog"; + m_xURLTransformer->parseStrict( aURL ); + if ( xProv.is() ) + xDisp = xProv->queryDispatch( aURL, OUString(), 0 ); + + if ( !xDisp.is() || IsPluginMode() ) + return; + } + + // popup menu for quick customization + bool bHideDisabledEntries = !officecfg::Office::Common::View::Menu::DontHideDisabledEntry::get(); + + ::PopupMenu *pMenu = pToolBar->GetMenu(); + + // copy all menu items 'Visible buttons, Customize toolbar, Dock toolbar, + // Dock all Toolbars) from the loaded resource into the toolbar menu + sal_uInt16 nGroupLen = pMenu->GetItemCount(); + if (nGroupLen) + pMenu->InsertSeparator(); + + VclPtr xVisibleItemsPopupMenu; + + if (!m_aResourceName.startsWith("private:resource/toolbar/addon_")) + { + pMenu->InsertItem(MENUITEM_TOOLBAR_VISIBLEBUTTON, FwkResId(STR_TOOLBAR_VISIBLE_BUTTONS)); + xVisibleItemsPopupMenu = VclPtr::Create(); + pMenu->SetPopupMenu(MENUITEM_TOOLBAR_VISIBLEBUTTON, xVisibleItemsPopupMenu); + + if (m_pToolBar->IsCustomize()) + { + pMenu->InsertItem(MENUITEM_TOOLBAR_CUSTOMIZETOOLBAR, FwkResId(STR_TOOLBAR_CUSTOMIZE_TOOLBAR)); + pMenu->SetItemCommand(MENUITEM_TOOLBAR_CUSTOMIZETOOLBAR, ".uno:ConfigureToolboxVisible"); + } + pMenu->InsertSeparator(); + } + + if (pToolBar->IsFloatingMode()) + { + pMenu->InsertItem(MENUITEM_TOOLBAR_DOCKTOOLBAR, FwkResId(STR_TOOLBAR_DOCK_TOOLBAR)); + pMenu->SetAccelKey(MENUITEM_TOOLBAR_DOCKTOOLBAR, vcl::KeyCode(KEY_F10, true, true, false, false)); + } + else + { + pMenu->InsertItem(MENUITEM_TOOLBAR_UNDOCKTOOLBAR, FwkResId(STR_TOOLBAR_UNDOCK_TOOLBAR)); + pMenu->SetAccelKey(MENUITEM_TOOLBAR_UNDOCKTOOLBAR, vcl::KeyCode(KEY_F10, true, true, false, false)); + } + + pMenu->InsertItem(MENUITEM_TOOLBAR_DOCKALLTOOLBAR, FwkResId(STR_TOOLBAR_DOCK_ALL_TOOLBARS)); + pMenu->InsertSeparator(); + pMenu->InsertItem(MENUITEM_TOOLBAR_LOCKTOOLBARPOSITION, FwkResId(STR_TOOLBAR_LOCK_TOOLBAR), MenuItemBits::CHECKABLE); + pMenu->InsertItem(MENUITEM_TOOLBAR_CLOSE, FwkResId(STR_TOOLBAR_CLOSE_TOOLBAR)); + + if (m_pToolBar->IsCustomize()) + { + bool bIsFloating( false ); + + DockingManager* pDockMgr = vcl::Window::GetDockingManager(); + if ( pDockMgr ) + bIsFloating = pDockMgr->IsFloating( m_pToolBar ); + + if ( !bIsFloating ) + { + pMenu->EnableItem(MENUITEM_TOOLBAR_DOCKALLTOOLBAR, false); + Reference< XDockableWindow > xDockable( VCLUnoHelper::GetInterface( m_pToolBar ), UNO_QUERY ); + if( xDockable.is() ) + pMenu->CheckItem(MENUITEM_TOOLBAR_LOCKTOOLBARPOSITION, xDockable->isLocked()); + } + else + pMenu->EnableItem(MENUITEM_TOOLBAR_LOCKTOOLBARPOSITION, false); + + if (officecfg::Office::Common::Misc::DisableUICustomization::get()) + { + pMenu->EnableItem(MENUITEM_TOOLBAR_VISIBLEBUTTON, false); + pMenu->EnableItem(MENUITEM_TOOLBAR_CUSTOMIZETOOLBAR, false); + pMenu->EnableItem(MENUITEM_TOOLBAR_LOCKTOOLBARPOSITION, false); + } + + // Disable menu item CLOSE if the toolbar has no closer + if( !(pToolBar->GetFloatStyle() & WB_CLOSEABLE) ) + pMenu->EnableItem(MENUITEM_TOOLBAR_CLOSE, false); + + // Temporary stores a Command --> Url map to update contextual menu with the + // correct icons. The popup icons are by default the same as those in the + // toolbar. They are not correct for contextual popup menu. + std::map< OUString, Image > commandToImage; + + if (xVisibleItemsPopupMenu) + { + // Go through all toolbar items and add them to the context menu + for ( ToolBox::ImplToolItems::size_type nPos = 0; nPos < m_pToolBar->GetItemCount(); ++nPos ) + { + if ( m_pToolBar->GetItemType(nPos) == ToolBoxItemType::BUTTON ) + { + ToolBoxItemId nId = m_pToolBar->GetItemId(nPos); + OUString aCommandURL = m_pToolBar->GetItemCommand( nId ); + xVisibleItemsPopupMenu->InsertItem( STARTID_CUSTOMIZE_POPUPMENU+nPos, m_pToolBar->GetItemText( nId ), MenuItemBits::CHECKABLE ); + xVisibleItemsPopupMenu->CheckItem( STARTID_CUSTOMIZE_POPUPMENU+nPos, m_pToolBar->IsItemVisible( nId ) ); + xVisibleItemsPopupMenu->SetItemCommand( STARTID_CUSTOMIZE_POPUPMENU+nPos, aCommandURL ); + Image aImage(vcl::CommandInfoProvider::GetImageForCommand(aCommandURL, m_xFrame)); + commandToImage[aCommandURL] = aImage; + xVisibleItemsPopupMenu->SetItemImage( STARTID_CUSTOMIZE_POPUPMENU+nPos, aImage ); + vcl::KeyCode aKeyCodeShortCut = vcl::CommandInfoProvider::GetCommandKeyCodeShortcut( aCommandURL, m_xFrame ); + xVisibleItemsPopupMenu->SetAccelKey( STARTID_CUSTOMIZE_POPUPMENU+nPos, aKeyCodeShortCut ); + } + else + { + xVisibleItemsPopupMenu->InsertSeparator(); + } + } + } + + // Now we go through all the contextual menu to update the icons + // and accelerator key shortcuts + std::map< OUString, Image >::iterator it; + for ( sal_uInt16 nPos = 0; nPos < pMenu->GetItemCount(); ++nPos ) + { + sal_uInt16 nId = pMenu->GetItemId( nPos ); + OUString cmdUrl = pMenu->GetItemCommand( nId ); + it = commandToImage.find( cmdUrl ); + if (it != commandToImage.end()) { + pMenu->SetItemImage( nId, it->second ); + } + vcl::KeyCode aKeyCodeShortCut = vcl::CommandInfoProvider::GetCommandKeyCodeShortcut( cmdUrl, m_xFrame ); + if ( aKeyCodeShortCut.GetFullCode() != 0 ) + pMenu->SetAccelKey( nId, aKeyCodeShortCut ); + } + } + + // Set the title of the menu + pMenu->SetText( pToolBar->GetText() ); + + if ( bHideDisabledEntries ) + pMenu->RemoveDisabledEntries(); +} + +void ToolBarManager::ToggleButton( const OUString& rResourceName, std::u16string_view rCommand ) +{ + Reference< XLayoutManager > xLayoutManager = getLayoutManagerFromFrame( m_xFrame ); + if ( !xLayoutManager.is() ) + return; + + Reference< XUIElementSettings > xUIElementSettings( xLayoutManager->getElement( rResourceName ), UNO_QUERY ); + if ( !xUIElementSettings.is() ) + return; + + Reference< XIndexContainer > xItemContainer( xUIElementSettings->getSettings( true ), UNO_QUERY ); + sal_Int32 nCount = xItemContainer->getCount(); + for ( sal_Int32 i = 0; i < nCount; i++ ) + { + Sequence< PropertyValue > aProp; + sal_Int32 nVisibleIndex( -1 ); + OUString aCommandURL; + bool bVisible( false ); + + if ( xItemContainer->getByIndex( i ) >>= aProp ) + { + for ( sal_Int32 j = 0; j < aProp.getLength(); j++ ) + { + if ( aProp[j].Name == ITEM_DESCRIPTOR_COMMANDURL ) + { + aProp[j].Value >>= aCommandURL; + } + else if ( aProp[j].Name == ITEM_DESCRIPTOR_VISIBLE ) + { + aProp[j].Value >>= bVisible; + nVisibleIndex = j; + } + } + + if (( aCommandURL == rCommand ) && ( nVisibleIndex >= 0 )) + { + // We have found the requested item, toggle the visible flag + // and write back the configuration settings to the toolbar + aProp.getArray()[nVisibleIndex].Value <<= !bVisible; + try + { + xItemContainer->replaceByIndex( i, Any( aProp )); + xUIElementSettings->setSettings( xItemContainer ); + Reference< XPropertySet > xPropSet( xUIElementSettings, UNO_QUERY ); + if ( xPropSet.is() ) + { + Reference< XUIConfigurationPersistence > xUICfgMgr; + if (( xPropSet->getPropertyValue("ConfigurationSource") >>= xUICfgMgr ) && ( xUICfgMgr.is() )) + xUICfgMgr->store(); + } + } + catch (const Exception&) + { + } + break; + } + } + } +} + +IMPL_LINK( ToolBarManager, MenuButton, ToolBox*, pToolBar, void ) +{ + SolarMutexGuard g; + + if ( m_bDisposed ) + return; + + assert( !m_aOverflowManager.is() ); + + VclPtrInstance pOverflowToolBar( pToolBar, WB_BORDER | WB_SCROLL ); + pOverflowToolBar->SetLineSpacing(true); + m_aOverflowManager.set( new ToolBarManager( m_xContext, m_xFrame, OUString(), pOverflowToolBar ) ); + m_aOverflowManager->FillOverflowToolbar( pToolBar ); + + ::Size aActSize( pOverflowToolBar->GetSizePixel() ); + ::Size aSize( pOverflowToolBar->CalcWindowSizePixel() ); + aSize.setWidth( aActSize.Width() ); + pOverflowToolBar->SetOutputSizePixel( aSize ); + + aSize = pOverflowToolBar->CalcPopupWindowSizePixel(); + pOverflowToolBar->SetSizePixel( aSize ); + + pOverflowToolBar->EnableDocking(); + pOverflowToolBar->AddEventListener( LINK( this, ToolBarManager, OverflowEventListener ) ); + vcl::Window::GetDockingManager()->StartPopupMode( pToolBar, pOverflowToolBar, FloatWinPopupFlags::AllMouseButtonClose ); + + // send HOME key to subtoolbar in order to select first item if keyboard activated + if(pToolBar->IsKeyEvent() ) + { + ::KeyEvent aEvent( 0, vcl::KeyCode( KEY_HOME ) ); + pOverflowToolBar->KeyInput(aEvent); + } +} + +IMPL_LINK( ToolBarManager, OverflowEventListener, VclWindowEvent&, rWindowEvent, void ) +{ + if ( rWindowEvent.GetId() != VclEventId::WindowEndPopupMode ) + return; + + if ( m_aOverflowManager.is() ) + { + m_aOverflowManager->dispose(); + m_aOverflowManager.clear(); + } +} + +IMPL_LINK( ToolBarManager, MenuPreExecute, ToolBox*, pToolBar, void ) +{ + SolarMutexGuard g; + + if ( m_bDisposed ) + return; + + AddCustomizeMenuItems( pToolBar ); +} + +IMPL_LINK( ToolBarManager, MenuSelect, Menu*, pMenu, bool ) +{ + // We have to hold a reference to ourself as it is possible that we will be disposed and + // our refcount could be zero (destruction) otherwise. + Reference< XInterface > xKeepAlive( static_cast< OWeakObject* >( this ), UNO_QUERY ); + + { + // The guard must be in its own context as the we can get destroyed when our + // own xInterface reference get destroyed! + SolarMutexGuard g; + + if ( m_bDisposed ) + return true; + + switch ( pMenu->GetCurItemId() ) + { + case MENUITEM_TOOLBAR_CUSTOMIZETOOLBAR: + { + Reference< XDispatch > xDisp; + css::util::URL aURL; + if ( m_xFrame.is() ) + { + Reference< XDispatchProvider > xProv( m_xFrame, UNO_QUERY ); + aURL.Complete = ".uno:ConfigureDialog"; + m_xURLTransformer->parseStrict( aURL ); + if ( xProv.is() ) + xDisp = xProv->queryDispatch( aURL, OUString(), 0 ); + } + + if ( xDisp.is() ) + { + Sequence< PropertyValue > aPropSeq{ comphelper::makePropertyValue( + "ResourceURL", m_aResourceName) }; + + xDisp->dispatch( aURL, aPropSeq ); + } + break; + } + + case MENUITEM_TOOLBAR_UNDOCKTOOLBAR: + { + ExecuteInfo* pExecuteInfo = new ExecuteInfo; + + pExecuteInfo->aToolbarResName = m_aResourceName; + pExecuteInfo->nCmd = EXEC_CMD_UNDOCKTOOLBAR; + pExecuteInfo->xLayoutManager = getLayoutManagerFromFrame( m_xFrame ); + + Application::PostUserEvent( LINK(nullptr, ToolBarManager, ExecuteHdl_Impl), pExecuteInfo ); + break; + } + + case MENUITEM_TOOLBAR_DOCKTOOLBAR: + { + ExecuteInfo* pExecuteInfo = new ExecuteInfo; + + pExecuteInfo->aToolbarResName = m_aResourceName; + pExecuteInfo->nCmd = EXEC_CMD_DOCKTOOLBAR; + pExecuteInfo->xLayoutManager = getLayoutManagerFromFrame( m_xFrame ); + + Application::PostUserEvent( LINK(nullptr, ToolBarManager, ExecuteHdl_Impl), pExecuteInfo ); + break; + } + + case MENUITEM_TOOLBAR_DOCKALLTOOLBAR: + { + ExecuteInfo* pExecuteInfo = new ExecuteInfo; + + pExecuteInfo->aToolbarResName = m_aResourceName; + pExecuteInfo->nCmd = EXEC_CMD_DOCKALLTOOLBARS; + pExecuteInfo->xLayoutManager = getLayoutManagerFromFrame( m_xFrame ); + + Application::PostUserEvent( LINK(nullptr, ToolBarManager, ExecuteHdl_Impl), pExecuteInfo ); + break; + } + + case MENUITEM_TOOLBAR_LOCKTOOLBARPOSITION: + { + Reference< XLayoutManager > xLayoutManager = getLayoutManagerFromFrame( m_xFrame ); + if ( xLayoutManager.is() ) + { + Reference< XDockableWindow > xDockable( VCLUnoHelper::GetInterface( m_pToolBar ), UNO_QUERY ); + + if( xDockable->isLocked() ) + xLayoutManager->unlockWindow( m_aResourceName ); + else + xLayoutManager->lockWindow( m_aResourceName ); + } + break; + } + + case MENUITEM_TOOLBAR_CLOSE: + { + ExecuteInfo* pExecuteInfo = new ExecuteInfo; + + pExecuteInfo->aToolbarResName = m_aResourceName; + pExecuteInfo->nCmd = EXEC_CMD_CLOSETOOLBAR; + pExecuteInfo->xLayoutManager = getLayoutManagerFromFrame( m_xFrame ); + pExecuteInfo->xWindow = VCLUnoHelper::GetInterface( m_pToolBar ); + + Application::PostUserEvent( LINK(nullptr, ToolBarManager, ExecuteHdl_Impl), pExecuteInfo ); + break; + } + + default: + { + sal_uInt16 nId = pMenu->GetCurItemId(); + if(( nId > 0 ) && ( nId < TOOLBOX_MENUITEM_START )) + // Items in the "enable/disable" sub-menu + { + // toggle toolbar button visibility + OUString aCommand = pMenu->GetItemCommand( nId ); + if (m_aContextResourceName.isEmpty() || + nId - STARTID_CUSTOMIZE_POPUPMENU < m_nContextMinPos) + ToggleButton(m_aResourceName, aCommand); + else + ToggleButton(m_aContextResourceName, aCommand); + } + break; + } + } + } + + return true; +} + +IMPL_LINK_NOARG(ToolBarManager, Select, ToolBox *, void) +{ + if ( m_bDisposed ) + return; + + sal_Int16 nKeyModifier( static_cast(m_pToolBar->GetModifier()) ); + ToolBoxItemId nId( m_pToolBar->GetCurItemId() ); + + ToolBarControllerMap::const_iterator pIter = m_aControllerMap.find( nId ); + if ( pIter != m_aControllerMap.end() ) + { + Reference< XToolbarController > xController( pIter->second, UNO_QUERY ); + + if ( xController.is() ) + xController->execute( nKeyModifier ); + } +} + +IMPL_LINK( ToolBarManager, StateChanged, StateChangedType const *, pStateChangedType, void ) +{ + if ( m_bDisposed ) + return; + + if ( *pStateChangedType == StateChangedType::ControlBackground ) + { + CheckAndUpdateImages(); + } + else if ( *pStateChangedType == StateChangedType::Visible ) + { + if ( m_pToolBar->IsReallyVisible() ) + { + m_aAsyncUpdateControllersTimer.Start(); + } + } + else if ( *pStateChangedType == StateChangedType::InitShow ) + { + m_aAsyncUpdateControllersTimer.Start(); + } +} + +IMPL_LINK( ToolBarManager, DataChanged, DataChangedEvent const *, pDataChangedEvent, void ) +{ + if ((( pDataChangedEvent->GetType() == DataChangedEventType::SETTINGS ) || + ( pDataChangedEvent->GetType() == DataChangedEventType::DISPLAY )) && + ( pDataChangedEvent->GetFlags() & AllSettingsFlags::STYLE )) + { + CheckAndUpdateImages(); + } + + for ( ToolBox::ImplToolItems::size_type nPos = 0; nPos < m_pToolBar->GetItemCount(); ++nPos ) + { + const ToolBoxItemId nId = m_pToolBar->GetItemId(nPos); + vcl::Window* pWindow = m_pToolBar->GetItemWindow( nId ); + if ( pWindow ) + { + const DataChangedEvent& rDCEvt( *pDataChangedEvent ); + pWindow->DataChanged( rDCEvt ); + } + } + + if ( !m_pToolBar->IsFloatingMode() && + m_pToolBar->IsVisible() ) + { + // Resize toolbar, layout manager is resize listener and will calc + // the layout automatically. + ::Size aSize( m_pToolBar->CalcWindowSizePixel() ); + m_pToolBar->SetOutputSizePixel( aSize ); + } +} + +IMPL_LINK_NOARG(ToolBarManager, MiscOptionsChanged, LinkParamNone*, void) +{ + CheckAndUpdateImages(); +} + +IMPL_LINK_NOARG(ToolBarManager, AsyncUpdateControllersHdl, Timer *, void) +{ + // The guard must be in its own context as the we can get destroyed when our + // own xInterface reference get destroyed! + Reference< XComponent > xThis(this); + + SolarMutexGuard g; + + if ( m_bDisposed ) + return; + + // Request to update our controllers + m_aAsyncUpdateControllersTimer.Stop(); + UpdateControllers(); +} + +IMPL_STATIC_LINK( ToolBarManager, ExecuteHdl_Impl, void*, p, void ) +{ + ExecuteInfo* pExecuteInfo = static_cast(p); + try + { + // Asynchronous execution as this can lead to our own destruction! + if (( pExecuteInfo->nCmd == EXEC_CMD_CLOSETOOLBAR ) && + ( pExecuteInfo->xLayoutManager.is() ) && + ( pExecuteInfo->xWindow.is() )) + { + // Use docking window close to close the toolbar. The toolbar layout manager is + // listener and will react correctly according to the context sensitive + // flag of our toolbar. + VclPtr pWin = VCLUnoHelper::GetWindow( pExecuteInfo->xWindow ); + DockingWindow* pDockWin = dynamic_cast< DockingWindow* >( pWin.get() ); + if ( pDockWin ) + pDockWin->Close(); + } + else if (( pExecuteInfo->nCmd == EXEC_CMD_UNDOCKTOOLBAR ) && + ( pExecuteInfo->xLayoutManager.is() )) + { + pExecuteInfo->xLayoutManager->floatWindow( pExecuteInfo->aToolbarResName ); + } + else if (( pExecuteInfo->nCmd == EXEC_CMD_DOCKTOOLBAR ) && + ( pExecuteInfo->xLayoutManager.is() )) + { + css::awt::Point aPoint; + aPoint.X = aPoint.Y = SAL_MAX_INT32; + pExecuteInfo->xLayoutManager->dockWindow( pExecuteInfo->aToolbarResName, + DockingArea_DOCKINGAREA_DEFAULT, + aPoint ); + } + else if (( pExecuteInfo->nCmd == EXEC_CMD_DOCKALLTOOLBARS ) && + ( pExecuteInfo->xLayoutManager.is() )) + { + pExecuteInfo->xLayoutManager->dockAllWindows( UIElementType::TOOLBAR ); + } + } + catch (const Exception&) + { + } + + delete pExecuteInfo; +} +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uielement/toolbarmerger.cxx b/framework/source/uielement/toolbarmerger.cxx new file mode 100644 index 0000000000..5588ff0522 --- /dev/null +++ b/framework/source/uielement/toolbarmerger.cxx @@ -0,0 +1,648 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace framework +{ + +const char MERGE_TOOLBAR_URL[] = "URL"; +const char MERGE_TOOLBAR_TITLE[] = "Title"; +const char MERGE_TOOLBAR_CONTEXT[] = "Context"; +const char MERGE_TOOLBAR_TARGET[] = "Target"; +const char MERGE_TOOLBAR_CONTROLTYPE[] = "ControlType"; +const char MERGE_TOOLBAR_WIDTH[] = "Width"; + +const char16_t MERGECOMMAND_ADDAFTER[] = u"AddAfter"; +const char16_t MERGECOMMAND_ADDBEFORE[] = u"AddBefore"; +const char16_t MERGECOMMAND_REPLACE[] = u"Replace"; +const char16_t MERGECOMMAND_REMOVE[] = u"Remove"; + +const char16_t MERGEFALLBACK_ADDLAST[] = u"AddLast"; +const char16_t MERGEFALLBACK_ADDFIRST[] = u"AddFirst"; +const char16_t MERGEFALLBACK_IGNORE[] = u"Ignore"; + +const char16_t TOOLBARCONTROLLER_BUTTON[] = u"Button"; +const char16_t TOOLBARCONTROLLER_COMBOBOX[] = u"Combobox"; +const char16_t TOOLBARCONTROLLER_EDIT[] = u"Editfield"; +const char16_t TOOLBARCONTROLLER_SPINFIELD[] = u"Spinfield"; +const char16_t TOOLBARCONTROLLER_IMGBUTTON[] = u"ImageButton"; +const char16_t TOOLBARCONTROLLER_DROPDOWNBOX[] = u"Dropdownbox"; +const char16_t TOOLBARCONTROLLER_DROPDOWNBTN[] = u"DropdownButton"; +const char16_t TOOLBARCONTROLLER_TOGGLEDDBTN[] = u"ToggleDropdownButton"; +const char16_t TOOLBARCONTROLLER_FIXEDIMAGE[] = u"FixedImage"; +const char16_t TOOLBARCONTROLLER_FIXEDTEXT[] = u"FixedText"; + +const char TOOLBOXITEM_SEPARATOR_STR[] = "private:separator"; + +using namespace ::com::sun::star; + +/** + Check whether a module identifier is part of a context + defined by a colon separated list of module identifier. + + @param + rContext + + Describes a context string list where all contexts + are delimited by a colon. For more information about + the module identifier used as context strings see the + IDL description of css::frame::XModuleManager + + @param + rModuleIdentifier + + A string describing a module identifier. See IDL + description of css::frame::XModuleManager. + + @result + The result is true if the rContext is an empty string + or rModuleIdentifier is part of the context string. + +*/ +bool ToolBarMerger::IsCorrectContext( + std::u16string_view rContext, + std::u16string_view rModuleIdentifier ) +{ + return ( rContext.empty() || ( rContext.find( rModuleIdentifier ) != std::u16string_view::npos )); +} + +/** + Converts a sequence, sequence of property values to + a vector of structs. + + @param + rSequence + + Provides a sequence, sequence of property values. + + @param + rContainer + + A vector of AddonToolbarItems which will hold the + conversion from the rSequence argument. +*/ +void ToolBarMerger::ConvertSeqSeqToVector( + const uno::Sequence< uno::Sequence< beans::PropertyValue > >& rSequence, + AddonToolbarItemContainer& rContainer ) +{ + sal_Int32 nLen( rSequence.getLength() ); + for ( sal_Int32 i = 0; i < nLen; i++ ) + { + AddonToolbarItem aAddonToolbarItem; + ConvertSequenceToValues( rSequence[i], + aAddonToolbarItem.aCommandURL, + aAddonToolbarItem.aLabel, + aAddonToolbarItem.aTarget, + aAddonToolbarItem.aContext, + aAddonToolbarItem.aControlType, + aAddonToolbarItem.nWidth ); + rContainer.push_back( aAddonToolbarItem ); + } +} + +/** + Converts a sequence of property values to single + values. + + @param + rSequence + + Provides a sequence of property values. + + @param + rCommandURL + + Contains the value of the property with + Name="CommandURL". + + @param + rLabel + + Contains the value of the property with + Name="Title" + + @param + rTarget + + Contains the value of the property with + Name="Target" + + @param + rContext + + Contains the value of the property with + Name="Context" + + @param + rControlType + + Contains the value of the property with + Name="ControlType" + + @result + All possible mapping between sequence of property + values and the single values are done. + +*/ +void ToolBarMerger::ConvertSequenceToValues( + const uno::Sequence< beans::PropertyValue >& rSequence, + OUString& rCommandURL, + OUString& rLabel, + OUString& rTarget, + OUString& rContext, + OUString& rControlType, + sal_uInt16& rWidth ) +{ + for ( beans::PropertyValue const & prop : rSequence ) + { + if ( prop.Name == MERGE_TOOLBAR_URL ) + prop.Value >>= rCommandURL; + else if ( prop.Name == MERGE_TOOLBAR_TITLE ) + prop.Value >>= rLabel; + else if ( prop.Name == MERGE_TOOLBAR_CONTEXT ) + prop.Value >>= rContext; + else if ( prop.Name == MERGE_TOOLBAR_TARGET ) + prop.Value >>= rTarget; + else if ( prop.Name == MERGE_TOOLBAR_CONTROLTYPE ) + prop.Value >>= rControlType; + else if ( prop.Name == MERGE_TOOLBAR_WIDTH ) + { + sal_Int32 aValue = 0; + prop.Value >>= aValue; + rWidth = sal_uInt16( aValue ); + } + } +} + +/** + Tries to find the reference point provided and delivers + position and result of the search process. + + @param + pToolbar + + Must be a valid pointer to a toolbar with items which + should be searched. +@param + nFirstItem + + First toolbar item to search from. + + @param + rReferencePoint + + A command URL which should be the reference point for + the coming merge operation. + + @result + Provides information about the search result, the + position of the reference point and the toolbar used. +*/ +ReferenceToolbarPathInfo ToolBarMerger::FindReferencePoint( + const ToolBox* pToolbar, sal_uInt16 nFirstItem, + std::u16string_view rReferencePoint ) +{ + ReferenceToolbarPathInfo aResult; + aResult.bResult = false; + aResult.nPos = ToolBox::ITEM_NOTFOUND; + + const ToolBox::ImplToolItems::size_type nSize( pToolbar->GetItemCount() ); + + for ( ToolBox::ImplToolItems::size_type i = nFirstItem; i < nSize; i++ ) + { + const ToolBoxItemId nItemId = pToolbar->GetItemId( i ); + if ( nItemId > ToolBoxItemId(0) ) + { + const OUString rCmd = pToolbar->GetItemCommand( nItemId ); + if ( rCmd == rReferencePoint ) + { + aResult.bResult = true; + aResult.nPos = i; + return aResult; + } + } + } + + return aResult; +} + +/** + Processes a merge operation. + + @param + pToolbar + + A valid pointer to the toolbar where the merge + operation is applied to. + + @param + nPos + + The reference position of the toolbar item for + the merge operation. Value must be between + 0 and number of toolbar items - 1. + + @param + rItemId + + A unique item ID. + + @param + rModuleIdentifier + + The current application module context. + + @param + rMergeCommand + + A merge command. + + @param + rMergeCommandParameter. + + An optional argument for the merge command. + + @param + rItems + + Toolbar items which are associated to the merge + command. + + @result + Returns true for a successful operation otherwise + false. +*/ +bool ToolBarMerger::ProcessMergeOperation( + ToolBox* pToolbar, + ToolBox::ImplToolItems::size_type nPos, + ToolBoxItemId& rItemId, + CommandToInfoMap& rCommandMap, + std::u16string_view rModuleIdentifier, + std::u16string_view rMergeCommand, + std::u16string_view rMergeCommandParameter, + const AddonToolbarItemContainer& rItems ) +{ + if ( rMergeCommand == MERGECOMMAND_ADDAFTER ) + MergeItems( pToolbar, nPos, 1, rItemId, rCommandMap, rModuleIdentifier, rItems ); + else if ( rMergeCommand == MERGECOMMAND_ADDBEFORE ) + MergeItems( pToolbar, nPos, 0, rItemId, rCommandMap, rModuleIdentifier, rItems ); + else if ( rMergeCommand == MERGECOMMAND_REPLACE ) + ReplaceItem( pToolbar, nPos, rItemId, rCommandMap, rModuleIdentifier, rItems ); + else if ( rMergeCommand == MERGECOMMAND_REMOVE ) + RemoveItems( pToolbar, nPos, rMergeCommandParameter ); + else + return false; + return true; +} + +/** + Processes a merge fallback operation. + + @param + pToolbar + + A valid pointer to the toolbar where the merge + fall back operation is applied to. + + @param + nPos + + The reference position of the toolbar item for + the merge operation. Value must be between + 0 and number of toolbar items - 1. + + @param + rItemId + + A unique item ID. + + @param + rModuleIdentifier + + The current application module context. + + @param + rMergeCommand + + A merge command. + + @param + rItems + + Toolbar items which are associated to the merge + command. + + @result + Returns true for a successful operation otherwise + false. +*/ +bool ToolBarMerger::ProcessMergeFallback( + ToolBox* pToolbar, + ToolBoxItemId& rItemId, + CommandToInfoMap& rCommandMap, + std::u16string_view rModuleIdentifier, + std::u16string_view rMergeCommand, + std::u16string_view rMergeFallback, + const AddonToolbarItemContainer& rItems ) +{ + if (( rMergeFallback == MERGEFALLBACK_IGNORE ) || + ( rMergeCommand == MERGECOMMAND_REPLACE ) || + ( rMergeCommand == MERGECOMMAND_REMOVE ) ) + { + return true; + } + else if (( rMergeCommand == MERGECOMMAND_ADDBEFORE ) || + ( rMergeCommand == MERGECOMMAND_ADDAFTER ) ) + { + if ( rMergeFallback == MERGEFALLBACK_ADDFIRST ) + MergeItems( pToolbar, 0, 0, rItemId, rCommandMap, rModuleIdentifier, rItems ); + else if ( rMergeFallback == MERGEFALLBACK_ADDLAST ) + MergeItems( pToolbar, ToolBox::APPEND, 0, rItemId, rCommandMap, rModuleIdentifier, rItems ); + else + return false; + return true; + } + + return false; +} + +/** + Merges (adds) toolbar items into an existing toolbar. + + @param + pToolbar + + A valid pointer to the toolbar where the merge + fall back operation is applied to. + + @param + nPos + + The reference position of the toolbar item for + the merge operation. Value must be between + 0 and number of toolbar items - 1. + + @param + rItemId + + A unique item ID. + + @param + rModuleIdentifier + + The current application module context. + + @param + rItems + + Toolbar items which are associated to the merge + command. +*/ +void ToolBarMerger::MergeItems( + ToolBox* pToolbar, + ToolBox::ImplToolItems::size_type nPos, + sal_uInt16 nModIndex, + ToolBoxItemId& rItemId, + CommandToInfoMap& rCommandMap, + std::u16string_view rModuleIdentifier, + const AddonToolbarItemContainer& rAddonToolbarItems ) +{ + const sal_Int32 nSize( rAddonToolbarItems.size() ); + + for ( sal_Int32 i = 0; i < nSize; i++ ) + { + const AddonToolbarItem& rItem = rAddonToolbarItems[i]; + if ( IsCorrectContext( rItem.aContext, rModuleIdentifier )) + { + ToolBox::ImplToolItems::size_type nInsPos = nPos; + if (nInsPos != ToolBox::APPEND) + { + nInsPos += nModIndex+i; + if ( nInsPos > pToolbar->GetItemCount() ) + nInsPos = ToolBox::APPEND; + } + + if ( rItem.aCommandURL == TOOLBOXITEM_SEPARATOR_STR ) + pToolbar->InsertSeparator( nInsPos ); + else + { + CommandToInfoMap::iterator pIter = rCommandMap.find( rItem.aCommandURL ); + if ( pIter == rCommandMap.end()) + { + CommandInfo aCmdInfo; + aCmdInfo.nId = rItemId; + const CommandToInfoMap::value_type aValue( rItem.aCommandURL, aCmdInfo ); + rCommandMap.insert( aValue ); + } + else + { + pIter->second.aIds.push_back( rItemId ); + } + + ToolBarMerger::CreateToolbarItem( pToolbar, nInsPos, rItemId, rItem ); + } + + ++rItemId; + } + } +} + +/** + Replaces a toolbar item with new items for an + existing toolbar. + + @param + pToolbar + + A valid pointer to the toolbar where the merge + fall back operation is applied to. + + @param + nPos + + The reference position of the toolbar item for + the merge operation. Value must be between + 0 and number of toolbar items - 1. + + @param + rItemId + + A unique item ID. + + @param + rModuleIdentifier + + The current application module context. + + @param + rItems + + Toolbar items which are associated to the merge + command. +*/ +void ToolBarMerger::ReplaceItem( + ToolBox* pToolbar, + ToolBox::ImplToolItems::size_type nPos, + ToolBoxItemId& rItemId, + CommandToInfoMap& rCommandMap, + std::u16string_view rModuleIdentifier, + const AddonToolbarItemContainer& rAddonToolbarItems ) +{ + pToolbar->RemoveItem( nPos ); + MergeItems( pToolbar, nPos, 0, rItemId, rCommandMap, rModuleIdentifier, rAddonToolbarItems ); +} + +/** + Removes toolbar items from an existing toolbar. + + @param + pToolbar + + A valid pointer to the toolbar where the merge + fall back operation is applied to. + + @param + nPos + + The reference position of the toolbar item for + the merge operation. Value must be between + 0 and number of toolbar items - 1. + + @param + rMergeCommandParameter. + + An optional argument for the merge command. +*/ +void ToolBarMerger::RemoveItems( + ToolBox* pToolbar, + ToolBox::ImplToolItems::size_type nPos, + std::u16string_view rMergeCommandParameter ) +{ + sal_Int32 nCount = o3tl::toInt32(rMergeCommandParameter); + if ( nCount > 0 ) + { + for ( sal_Int32 i = 0; i < nCount; i++ ) + { + if ( nPos < pToolbar->GetItemCount() ) + pToolbar->RemoveItem( nPos ); + } + } +} + +/** + Removes toolbar items from an existing toolbar. + + @param + pToolbar + + A valid pointer to the toolbar where the merge + fall back operation is applied to. + + @param + nPos + + The reference position of the toolbar item for + the merge operation. Value must be between + 0 and number of toolbar items - 1. + + @param + rMergeCommandParameter. + + An optional argument for the merge command. + + @result + Returns true for a successful operation otherwise + false. +*/ +rtl::Reference<::cppu::OWeakObject> ToolBarMerger::CreateController( + const uno::Reference< uno::XComponentContext >& rxContext, + const uno::Reference< frame::XFrame > & xFrame, + ToolBox* pToolbar, + const OUString& rCommandURL, + ToolBoxItemId nId, + sal_uInt16 nWidth, + std::u16string_view rControlType ) +{ + rtl::Reference<::cppu::OWeakObject> pResult; + + if ( rControlType == TOOLBARCONTROLLER_BUTTON ) + pResult = new ButtonToolbarController( rxContext, pToolbar, rCommandURL ); + else if ( rControlType == TOOLBARCONTROLLER_COMBOBOX ) + pResult = new ComboboxToolbarController( rxContext, xFrame, pToolbar, nId, nWidth, rCommandURL ); + else if ( rControlType == TOOLBARCONTROLLER_EDIT ) + pResult = new EditToolbarController( rxContext, xFrame, pToolbar, nId, nWidth, rCommandURL ); + else if ( rControlType == TOOLBARCONTROLLER_SPINFIELD ) + pResult = new SpinfieldToolbarController( rxContext, xFrame, pToolbar, nId, nWidth, rCommandURL ); + else if ( rControlType == TOOLBARCONTROLLER_IMGBUTTON ) + pResult = new ImageButtonToolbarController( rxContext, xFrame, pToolbar, nId, rCommandURL ); + else if ( rControlType == TOOLBARCONTROLLER_DROPDOWNBOX ) + pResult = new DropdownToolbarController( rxContext, xFrame, pToolbar, nId, nWidth, rCommandURL ); + else if ( rControlType == TOOLBARCONTROLLER_DROPDOWNBTN ) + pResult = new ToggleButtonToolbarController( rxContext, xFrame, pToolbar, nId, + ToggleButtonToolbarController::Style::DropDownButton, rCommandURL ); + else if ( rControlType == TOOLBARCONTROLLER_FIXEDIMAGE ) + pResult = new FixedImageToolbarController( rxContext, xFrame, pToolbar, nId, rCommandURL ); + else if ( rControlType == TOOLBARCONTROLLER_FIXEDTEXT ) + pResult = new FixedTextToolbarController( rxContext, xFrame, pToolbar, nId, rCommandURL ); + else if ( rControlType == TOOLBARCONTROLLER_TOGGLEDDBTN ) + pResult = new ToggleButtonToolbarController( rxContext, xFrame, pToolbar, nId, + ToggleButtonToolbarController::Style::ToggleDropDownButton, rCommandURL ); + else + pResult = new GenericToolbarController( rxContext, xFrame, pToolbar, nId, rCommandURL ); + + return pResult; +} + +void ToolBarMerger::CreateToolbarItem( ToolBox* pToolbar, ToolBox::ImplToolItems::size_type nPos, ToolBoxItemId nItemId, const AddonToolbarItem& rItem ) +{ + assert(pToolbar->GetItemData(nItemId) == nullptr); // that future would contain a double free + + pToolbar->InsertItem( nItemId, rItem.aLabel, rItem.aCommandURL, ToolBoxItemBits::NONE, nPos ); + pToolbar->SetQuickHelpText( nItemId, rItem.aLabel ); + pToolbar->SetItemText( nItemId, rItem.aLabel ); + pToolbar->EnableItem( nItemId ); + pToolbar->SetItemState( nItemId, TRISTATE_FALSE ); + + // Use the user data to store add-on specific data with the toolbar item + AddonsParams* pAddonParams = new AddonsParams; + pAddonParams->aControlType = rItem.aControlType; + pAddonParams->nWidth = rItem.nWidth; + pToolbar->SetItemData( nItemId, pAddonParams ); +} + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uielement/toolbarmodemenucontroller.cxx b/framework/source/uielement/toolbarmodemenucontroller.cxx new file mode 100644 index 0000000000..62cfdc04dd --- /dev/null +++ b/framework/source/uielement/toolbarmodemenucontroller.cxx @@ -0,0 +1,296 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include + +#include +#include +#include +#include + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Defines + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::frame; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::util; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::ui; + +namespace framework +{ + +// XInterface, XTypeProvider, XServiceInfo + +OUString SAL_CALL ToolbarModeMenuController::getImplementationName() +{ + return "com.sun.star.comp.framework.ToolbarModeMenuController"; +} + +sal_Bool SAL_CALL ToolbarModeMenuController::supportsService( const OUString& sServiceName ) +{ + return cppu::supportsService(this, sServiceName); +} + +css::uno::Sequence< OUString > SAL_CALL ToolbarModeMenuController::getSupportedServiceNames() +{ + return { SERVICENAME_POPUPMENUCONTROLLER }; +} + + +ToolbarModeMenuController::ToolbarModeMenuController( const css::uno::Reference< css::uno::XComponentContext >& xContext ) : + svt::PopupMenuControllerBase( xContext ), + m_xContext( xContext ) +{ +} + +ToolbarModeMenuController::~ToolbarModeMenuController() +{ +} + +void ToolbarModeMenuController::fillPopupMenu( Reference< css::awt::XPopupMenu > const & rPopupMenu ) +{ + if ( officecfg::Office::Common::Misc::DisableUICustomization::get() ) + return; + + SolarMutexGuard aSolarMutexGuard; + resetPopupMenu( rPopupMenu ); + + const Reference xContext (::comphelper::getProcessComponentContext() ); + const Reference xModuleManager = frame::ModuleManager::create( xContext ); + vcl::EnumContext::Application eApp = vcl::EnumContext::GetApplicationEnum(xModuleManager->identify(m_xFrame)); + + OUStringBuffer aPath("org.openoffice.Office.UI.ToolbarMode/Applications/"); + switch ( eApp ) + { + case vcl::EnumContext::Application::Writer: + aPath.append("Writer"); + break; + case vcl::EnumContext::Application::Calc: + aPath.append("Calc"); + break; + case vcl::EnumContext::Application::Impress: + aPath.append("Impress"); + break; + case vcl::EnumContext::Application::Draw: + aPath.append("Draw"); + break; + case vcl::EnumContext::Application::Formula: + aPath.append("Formula"); + break; + case vcl::EnumContext::Application::Base: + aPath.append("Base"); + break; + default: + break; + } + aPath.append("/Modes"); + + const utl::OConfigurationTreeRoot aModesNode( + m_xContext, + aPath.makeStringAndClear(), + false); + if ( !aModesNode.isValid() ) + return; + + const Sequence aModeNodeNames (aModesNode.getNodeNames()); + const sal_Int32 nCount(aModeNodeNames.getLength()); + tools::Long nCountToolbar = 0; + + for ( sal_Int32 nReadIndex = 0; nReadIndex < nCount; ++nReadIndex ) + { + const utl::OConfigurationNode aModeNode(aModesNode.openNode(aModeNodeNames[nReadIndex])); + if ( !aModeNode.isValid() ) + continue; + + OUString aLabel = comphelper::getString( aModeNode.getNodeValue( "Label" ) ); + OUString aCommandArg = comphelper::getString( aModeNode.getNodeValue( "CommandArg" ) ); + tools::Long nPosition = comphelper::getINT32( aModeNode.getNodeValue( "MenuPosition" ) ); + bool isExperimental = comphelper::getBOOL( aModeNode.getNodeValue( "IsExperimental" ) ); + bool hasNotebookbar = comphelper::getBOOL( aModeNode.getNodeValue( "HasNotebookbar" ) ); + + // Allow Notebookbar only in experimental mode + if ( isExperimental && !officecfg::Office::Common::Misc::ExperimentalMode::get() ) + continue; + if (!hasNotebookbar) + nCountToolbar++; + + m_xPopupMenu->insertItem( nReadIndex+1, aLabel, css::awt::MenuItemStyle::RADIOCHECK, nPosition ); + rPopupMenu->setCommand( nReadIndex+1, aCommandArg ); + } + rPopupMenu->insertSeparator(nCountToolbar); +} + +// XEventListener +void SAL_CALL ToolbarModeMenuController::disposing( const EventObject& ) +{ + Reference< css::awt::XMenuListener > xHolder(this); + + std::unique_lock aLock( m_aMutex ); + m_xFrame.clear(); + m_xDispatch.clear(); + + if ( m_xPopupMenu.is() ) + m_xPopupMenu->removeMenuListener( Reference< css::awt::XMenuListener >(this) ); + m_xPopupMenu.clear(); +} + +// XStatusListener +void SAL_CALL ToolbarModeMenuController::statusChanged( const FeatureStateEvent& Event ) +{ + OUString aFeatureURL( Event.FeatureURL.Complete ); + + // All other status events will be processed here + std::unique_lock aLock( m_aMutex ); + Reference< css::awt::XPopupMenu > xPopupMenu( m_xPopupMenu ); + aLock.unlock(); + + if ( !xPopupMenu.is() ) + return; + + SolarMutexGuard aGuard; + + bool bSetCheckmark = false; + bool bCheckmark = false; + for (sal_Int16 i = 0, nCount = xPopupMenu->getItemCount(); i < nCount; ++i) + { + sal_Int16 nId = xPopupMenu->getItemId(i); + if ( nId == 0 ) + continue; + + OUString aCmd = xPopupMenu->getCommand(nId); + if ( aCmd == aFeatureURL ) + { + // Enable/disable item + xPopupMenu->enableItem(nId, Event.IsEnabled); + + // Checkmark + if ( Event.State >>= bCheckmark ) + bSetCheckmark = true; + + if ( bSetCheckmark ) + xPopupMenu->checkItem(nId, bCheckmark); + else + { + OUString aItemText; + + if ( Event.State >>= aItemText ) + xPopupMenu->setItemText(nId, aItemText); + } + } + } +} + +// XMenuListener +void SAL_CALL ToolbarModeMenuController::itemSelected( const css::awt::MenuEvent& rEvent ) +{ + auto aArgs(comphelper::InitPropertySequence({{"Mode", Any(m_xPopupMenu->getCommand(rEvent.MenuId))}})); + dispatchCommand(m_aCommandURL, aArgs); +} + +void SAL_CALL ToolbarModeMenuController::itemActivated( const css::awt::MenuEvent& ) +{ + const Reference xModuleManager = frame::ModuleManager::create( m_xContext ); + vcl::EnumContext::Application eApp = vcl::EnumContext::GetApplicationEnum(xModuleManager->identify(m_xFrame)); + + OUStringBuffer aPath("org.openoffice.Office.UI.ToolbarMode/Applications/"); + switch ( eApp ) + { + case vcl::EnumContext::Application::Writer: + aPath.append("Writer"); + break; + case vcl::EnumContext::Application::Calc: + aPath.append("Calc"); + break; + case vcl::EnumContext::Application::Impress: + aPath.append("Impress"); + break; + case vcl::EnumContext::Application::Draw: + aPath.append("Draw"); + break; + case vcl::EnumContext::Application::Formula: + aPath.append("Formula"); + break; + case vcl::EnumContext::Application::Base: + aPath.append("Base"); + break; + default: + break; + } + + const utl::OConfigurationTreeRoot aModesNode( + m_xContext, + aPath.makeStringAndClear(), + false); + if ( !aModesNode.isValid() ) + return; + + OUString aMode = comphelper::getString( aModesNode.getNodeValue( "Active" ) ); + + for ( int i = 0; i < m_xPopupMenu->getItemCount(); ++i ) + { + sal_Int16 nItemId(m_xPopupMenu->getItemId(i)); + m_xPopupMenu->checkItem(nItemId, aMode == m_xPopupMenu->getCommand(nItemId)); + } +} + +// XPopupMenuController +void SAL_CALL ToolbarModeMenuController::setPopupMenu( const Reference< css::awt::XPopupMenu >& xPopupMenu ) +{ + std::unique_lock aLock( m_aMutex ); + + throwIfDisposed(aLock); + + if ( m_xFrame.is() && !m_xPopupMenu.is() ) + { + // Create popup menu on demand + SolarMutexGuard aSolarMutexGuard; + + m_xPopupMenu = dynamic_cast(xPopupMenu.get()); + assert(bool(xPopupMenu) == bool(m_xPopupMenu) && "we only support VCLXPopupMenu"); + m_xPopupMenu->addMenuListener( Reference< css::awt::XMenuListener >(this) ); + fillPopupMenu( m_xPopupMenu ); + } +} + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +framework_ToolbarModeMenuController_get_implementation( + css::uno::XComponentContext* context, css::uno::Sequence const& ) +{ + return cppu::acquire(new framework::ToolbarModeMenuController(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uielement/toolbarsmenucontroller.cxx b/framework/source/uielement/toolbarsmenucontroller.cxx new file mode 100644 index 0000000000..e19a7ec40b --- /dev/null +++ b/framework/source/uielement/toolbarsmenucontroller.cxx @@ -0,0 +1,791 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Defines + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::frame; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::util; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::ui; + +constexpr OUString CMD_RESTOREVISIBILITY = u".cmd:RestoreVisibility"_ustr; +constexpr OUStringLiteral CMD_LOCKTOOLBARS = u".uno:ToolbarLock"; + +constexpr OUString STATIC_CMD_PART = u".uno:AvailableToolbars?Toolbar:string="_ustr; +const char STATIC_INTERNAL_CMD_PART[] = ".cmd:"; + +namespace framework +{ + +typedef std::unordered_map< OUString, OUString > ToolbarHashMap; + +namespace { + +struct ToolBarEntry +{ + OUString aUIName; + OUString aCommand; + bool bVisible; + const CollatorWrapper* pCollatorWrapper; +}; + +} + +static bool CompareToolBarEntry( const ToolBarEntry& aOne, const ToolBarEntry& aTwo ) +{ + sal_Int32 nComp = aOne.pCollatorWrapper->compareString( aOne.aUIName, aTwo.aUIName ); + + return nComp < 0; +} + +static Reference< XLayoutManager > getLayoutManagerFromFrame( const Reference< XFrame >& rFrame ) +{ + Reference< XPropertySet > xPropSet( rFrame, UNO_QUERY ); + Reference< XLayoutManager > xLayoutManager; + + try + { + xPropSet->getPropertyValue("LayoutManager") >>= xLayoutManager; + } + catch ( const UnknownPropertyException& ) + { + } + + return xLayoutManager; +} + +namespace { + +struct ToolBarInfo +{ + OUString aToolBarResName; + OUString aToolBarUIName; +}; + +} + +// XInterface, XTypeProvider, XServiceInfo + +OUString SAL_CALL ToolbarsMenuController::getImplementationName() +{ + return "com.sun.star.comp.framework.ToolBarsMenuController"; +} + +sal_Bool SAL_CALL ToolbarsMenuController::supportsService( const OUString& sServiceName ) +{ + return cppu::supportsService(this, sServiceName); +} + +css::uno::Sequence< OUString > SAL_CALL ToolbarsMenuController::getSupportedServiceNames() +{ + return { SERVICENAME_POPUPMENUCONTROLLER }; +} + +constexpr OUString g_aPropUIName( u"UIName"_ustr ); +constexpr OUString g_aPropResourceURL( u"ResourceURL"_ustr ); + +ToolbarsMenuController::ToolbarsMenuController( const css::uno::Reference< css::uno::XComponentContext >& xContext ) : + svt::PopupMenuControllerBase( xContext ), + m_xContext( xContext ), + m_bResetActive( false ), + m_aIntlWrapper(SvtSysLocale().GetUILanguageTag()) +{ +} + +ToolbarsMenuController::~ToolbarsMenuController() +{ +} + +void ToolbarsMenuController::addCommand( + Reference< css::awt::XPopupMenu > const & rPopupMenu, const OUString& rCommandURL, const OUString& rLabel ) +{ + sal_uInt16 nItemId = m_xPopupMenu->getItemCount()+1; + + OUString aLabel; + if ( rLabel.isEmpty() ) + { + auto aProperties = vcl::CommandInfoProvider::GetCommandProperties(rCommandURL, m_aModuleName); + aLabel = vcl::CommandInfoProvider::GetMenuLabelForCommand(aProperties); + } + else + aLabel = rLabel; + + rPopupMenu->insertItem( nItemId, aLabel, 0, nItemId ); + rPopupMenu->setCommand( nItemId, rCommandURL ); + + bool bInternal = rCommandURL.startsWith( STATIC_INTERNAL_CMD_PART ); + if ( !bInternal ) + { + if ( !getDispatchFromCommandURL( rCommandURL ).is() ) + m_xPopupMenu->enableItem( nItemId, false ); + } + + SolarMutexGuard aSolarMutexGuard; + + css::uno::Reference xGraphic; + const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings(); + + if ( rSettings.GetUseImagesInMenus() ) + xGraphic = vcl::CommandInfoProvider::GetXGraphicForCommand(rCommandURL, m_xFrame); + + if (xGraphic.is()) + rPopupMenu->setItemImage(nItemId, xGraphic, false); + + m_aCommandVector.push_back( rCommandURL ); +} + +Reference< XDispatch > ToolbarsMenuController::getDispatchFromCommandURL( const OUString& rCommandURL ) +{ + URL aTargetURL; + Reference< XURLTransformer > xURLTransformer; + Reference< XFrame > xFrame; + + { + SolarMutexGuard aSolarMutexGuard; + xURLTransformer = m_xURLTransformer; + xFrame = m_xFrame; + } + + aTargetURL.Complete = rCommandURL; + xURLTransformer->parseStrict( aTargetURL ); + Reference< XDispatchProvider > xDispatchProvider( xFrame, UNO_QUERY ); + if ( xDispatchProvider.is() ) + return xDispatchProvider->queryDispatch( aTargetURL, OUString(), 0 ); + else + return Reference< XDispatch >(); +} + +static void fillHashMap( const Sequence< Sequence< css::beans::PropertyValue > >& rSeqToolBars, + ToolbarHashMap& rHashMap ) +{ + for ( Sequence< css::beans::PropertyValue > const & props : rSeqToolBars ) + { + OUString aResourceURL; + OUString aUIName; + for ( css::beans::PropertyValue const & prop : props ) + { + if ( prop.Name == "ResourceURL" ) + prop.Value >>= aResourceURL; + else if ( prop.Name == "UIName" ) + prop.Value >>= aUIName; + } + + if ( !aResourceURL.isEmpty() && + rHashMap.find( aResourceURL ) == rHashMap.end() ) + rHashMap.emplace( aResourceURL, aUIName ); + } +} + +// private function +Sequence< Sequence< css::beans::PropertyValue > > ToolbarsMenuController::getLayoutManagerToolbars( const Reference< css::frame::XLayoutManager >& rLayoutManager ) +{ + std::vector< ToolBarInfo > aToolBarArray; + const Sequence< Reference< XUIElement > > aUIElements = rLayoutManager->getElements(); + for ( Reference< XUIElement > const & xUIElement : aUIElements ) + { + Reference< XPropertySet > xPropSet( xUIElement, UNO_QUERY ); + if ( xPropSet.is() && xUIElement.is() ) + { + try + { + OUString aResName; + sal_Int16 nType( -1 ); + xPropSet->getPropertyValue("Type") >>= nType; + xPropSet->getPropertyValue("ResourceURL") >>= aResName; + + if (( nType == css::ui::UIElementType::TOOLBAR ) && + !aResName.isEmpty() ) + { + ToolBarInfo aToolBarInfo; + + aToolBarInfo.aToolBarResName = aResName; + + SolarMutexGuard aGuard; + Reference< css::awt::XWindow > xWindow( xUIElement->getRealInterface(), UNO_QUERY ); + VclPtr pWindow = VCLUnoHelper::GetWindow( xWindow ); + if ( pWindow ) + aToolBarInfo.aToolBarUIName = pWindow->GetText(); + + aToolBarArray.push_back( aToolBarInfo ); + } + } + catch ( const Exception& ) + { + } + } + } + + Sequence< Sequence< css::beans::PropertyValue > > aSeq( aToolBarArray.size() ); + auto pSeq = aSeq.getArray(); + const sal_uInt32 nCount = aToolBarArray.size(); + for ( sal_uInt32 i = 0; i < nCount; i++ ) + { + Sequence< css::beans::PropertyValue > aTbSeq{ + comphelper::makePropertyValue(g_aPropUIName, aToolBarArray[i].aToolBarUIName), + comphelper::makePropertyValue(g_aPropResourceURL, aToolBarArray[i].aToolBarResName) + }; + pSeq[i] = aTbSeq; + } + + return aSeq; +} + + +void ToolbarsMenuController::fillPopupMenu( Reference< css::awt::XPopupMenu > const & rPopupMenu ) +{ + if( officecfg::Office::Common::Misc::DisableUICustomization::get() ) + return; + + SolarMutexGuard aSolarMutexGuard; + resetPopupMenu( rPopupMenu ); + + m_aCommandVector.clear(); + + // Retrieve layout manager for additional information + Reference< XLayoutManager > xLayoutManager( getLayoutManagerFromFrame( m_xFrame )); + + m_bResetActive = false; + if ( !xLayoutManager.is() ) + return; + + ToolbarHashMap aToolbarHashMap; + + if ( m_xDocCfgMgr.is() ) + { + Sequence< Sequence< css::beans::PropertyValue > > aSeqDocToolBars = + m_xDocCfgMgr->getUIElementsInfo( UIElementType::TOOLBAR ); + fillHashMap( aSeqDocToolBars, aToolbarHashMap ); + } + + if ( m_xModuleCfgMgr.is() ) + { + Sequence< Sequence< css::beans::PropertyValue > > aSeqToolBars = + m_xModuleCfgMgr->getUIElementsInfo( UIElementType::TOOLBAR ); + fillHashMap( aSeqToolBars, aToolbarHashMap ); + } + + std::vector< ToolBarEntry > aSortedTbs; + OUString aStaticCmdPart( STATIC_CMD_PART ); + + Sequence< Sequence< css::beans::PropertyValue > > aSeqFrameToolBars = getLayoutManagerToolbars( xLayoutManager ); + fillHashMap( aSeqFrameToolBars, aToolbarHashMap ); + + for (auto const& toolbar : aToolbarHashMap) + { + OUString aUIName = toolbar.second; + bool bHideFromMenu( false ); + bool bContextSensitive( false ); + if ( aUIName.isEmpty() && + m_xPersistentWindowState.is() ) + { + bool bVisible( false ); + + try + { + Sequence< PropertyValue > aWindowState; + Any a( m_xPersistentWindowState->getByName( toolbar.first )); + + if ( a >>= aWindowState ) + { + for ( PropertyValue const & prop : std::as_const(aWindowState) ) + { + if ( prop.Name == WINDOWSTATE_PROPERTY_UINAME ) + prop.Value >>= aUIName; + else if ( prop.Name == WINDOWSTATE_PROPERTY_HIDEFROMENU ) + prop.Value >>= bHideFromMenu; + else if ( prop.Name == WINDOWSTATE_PROPERTY_CONTEXT ) + prop.Value >>= bContextSensitive; + else if ( prop.Name == WINDOWSTATE_PROPERTY_VISIBLE ) + prop.Value >>= bVisible; + } + } + } + catch ( const Exception& ) + { + } + + // Check if we have to enable/disable "Reset" menu item + if ( bContextSensitive && !bVisible ) + m_bResetActive = true; + + } + + if ( !aUIName.isEmpty() && !bHideFromMenu ) + { + ToolBarEntry aTbEntry; + aTbEntry.aUIName = aUIName; + aTbEntry.aCommand = toolbar.first; + aTbEntry.bVisible = xLayoutManager->isElementVisible( toolbar.first ); + aTbEntry.pCollatorWrapper = m_aIntlWrapper.getCaseCollator(); + aSortedTbs.push_back( aTbEntry ); + } + } + + // sort toolbars + std::sort( aSortedTbs.begin(), aSortedTbs.end(), CompareToolBarEntry ); + + sal_Int16 nIndex( 1 ); + const sal_uInt32 nCount = aSortedTbs.size(); + for ( sal_uInt32 i = 0; i < nCount; i++ ) + { + sal_uInt16 nItemCount = m_xPopupMenu->getItemCount(); + m_xPopupMenu->insertItem( nIndex, aSortedTbs[i].aUIName, css::awt::MenuItemStyle::CHECKABLE, nItemCount ); + if ( aSortedTbs[i].bVisible ) + m_xPopupMenu->checkItem( nIndex, true ); + + OUStringBuffer aStrBuf( aStaticCmdPart ); + + sal_Int32 n = aSortedTbs[i].aCommand.lastIndexOf( '/' ); + if (( n > 0 ) && (( n+1 ) < aSortedTbs[i].aCommand.getLength() )) + aStrBuf.append( aSortedTbs[i].aCommand.subView(n+1) ); + + OUString aCmd( aStrBuf.makeStringAndClear() ); + + // Store complete uno-command so it can also be dispatched. This is necessary to support + // the test tool! + rPopupMenu->setCommand( nIndex, aCmd ); + ++nIndex; + } + + // Create commands for non-toolbars + + bool bAddCommand( true ); + SvtCommandOptions aCmdOptions; + + if ( aCmdOptions.HasEntriesDisabled() && aCmdOptions.LookupDisabled("ConfigureDialog")) + bAddCommand = false; + + if ( bAddCommand ) + { + // Create command for configure + if ( m_xPopupMenu->getItemCount() > 0 ) + { + sal_uInt16 nItemCount = m_xPopupMenu->getItemCount(); + m_xPopupMenu->insertSeparator( nItemCount+1 ); + } + + addCommand( m_xPopupMenu, ".uno:ConfigureDialog", "" ); + } + + // Add separator if no configure has been added + if ( !bAddCommand ) + { + // Create command for configure + if ( m_xPopupMenu->getItemCount() > 0 ) + { + sal_uInt16 nItemCount = m_xPopupMenu->getItemCount(); + m_xPopupMenu->insertSeparator( nItemCount+1 ); + } + } + + OUString aLabelStr(FwkResId(STR_RESTORE_TOOLBARS)); + addCommand( m_xPopupMenu, CMD_RESTOREVISIBILITY, aLabelStr ); + aLabelStr = FwkResId(STR_LOCK_TOOLBARS); + addCommand( m_xPopupMenu, CMD_LOCKTOOLBARS, aLabelStr ); +} + +// XEventListener +void SAL_CALL ToolbarsMenuController::disposing( const EventObject& ) +{ + Reference< css::awt::XMenuListener > xHolder(this); + + std::unique_lock aLock( m_aMutex ); + m_xFrame.clear(); + m_xDispatch.clear(); + m_xDocCfgMgr.clear(); + m_xModuleCfgMgr.clear(); + m_xContext.clear(); + + if ( m_xPopupMenu.is() ) + m_xPopupMenu->removeMenuListener( Reference< css::awt::XMenuListener >(this) ); + m_xPopupMenu.clear(); +} + +// XStatusListener +void SAL_CALL ToolbarsMenuController::statusChanged( const FeatureStateEvent& Event ) +{ + OUString aFeatureURL( Event.FeatureURL.Complete ); + + // All other status events will be processed here + std::unique_lock aLock( m_aMutex ); + Reference< css::awt::XPopupMenu > xPopupMenu( m_xPopupMenu ); + aLock.unlock(); + + if ( !xPopupMenu.is() ) + return; + + SolarMutexGuard aGuard; + + bool bSetCheckmark = false; + bool bCheckmark = false; + for (sal_Int16 i = 0, nCount = xPopupMenu->getItemCount(); i < nCount; ++i) + { + sal_Int16 nId = xPopupMenu->getItemId(i); + if ( nId == 0 ) + continue; + + OUString aCmd = xPopupMenu->getCommand(nId); + if ( aCmd == aFeatureURL ) + { + // Enable/disable item + xPopupMenu->enableItem(nId, Event.IsEnabled); + + // Checkmark + if ( Event.State >>= bCheckmark ) + bSetCheckmark = true; + + if ( bSetCheckmark ) + xPopupMenu->checkItem(nId, bCheckmark); + else + { + OUString aItemText; + + if ( Event.State >>= aItemText ) + xPopupMenu->setItemText(nId, aItemText); + } + } + } +} + +// XMenuListener +void SAL_CALL ToolbarsMenuController::itemSelected( const css::awt::MenuEvent& rEvent ) +{ + Reference< css::awt::XPopupMenu > xPopupMenu; + Reference< XComponentContext > xContext; + Reference< XURLTransformer > xURLTransformer; + Reference< XFrame > xFrame; + Reference< XNameAccess > xPersistentWindowState; + + { + std::unique_lock aLock(m_aMutex); + xPopupMenu = m_xPopupMenu; + xContext = m_xContext; + xURLTransformer = m_xURLTransformer; + xFrame = m_xFrame; + xPersistentWindowState = m_xPersistentWindowState; + } + + if ( !xPopupMenu.is() ) + return; + + SolarMutexGuard aSolarMutexGuard; + + OUString aCmd(xPopupMenu->getCommand(rEvent.MenuId)); + if ( aCmd.startsWith( STATIC_INTERNAL_CMD_PART ) ) + { + // Command to restore the visibility of all context sensitive toolbars + Reference< XNameReplace > xNameReplace( xPersistentWindowState, UNO_QUERY ); + if ( xPersistentWindowState.is() && xNameReplace.is() ) + { + try + { + Sequence< OUString > aElementNames = xPersistentWindowState->getElementNames(); + sal_Int32 nCount = aElementNames.getLength(); + bool bRefreshToolbars( false ); + + for ( sal_Int32 i = 0; i < nCount; i++ ) + { + try + { + OUString aElementName = aElementNames[i]; + Sequence< PropertyValue > aWindowState; + + if ( xPersistentWindowState->getByName( aElementName ) >>= aWindowState ) + { + bool bVisible( false ); + bool bContextSensitive( false ); + sal_Int32 nVisibleIndex( -1 ); + for ( sal_Int32 j = 0; j < aWindowState.getLength(); j++ ) + { + if ( aWindowState[j].Name == WINDOWSTATE_PROPERTY_VISIBLE ) + { + aWindowState[j].Value >>= bVisible; + nVisibleIndex = j; + } + else if ( aWindowState[j].Name == WINDOWSTATE_PROPERTY_CONTEXT ) + aWindowState[j].Value >>= bContextSensitive; + } + + if ( !bVisible && bContextSensitive && nVisibleIndex >= 0 ) + { + // Default is: Every context sensitive toolbar is visible + aWindowState.getArray()[nVisibleIndex].Value <<= true; + xNameReplace->replaceByName( aElementName, Any( aWindowState )); + bRefreshToolbars = true; + } + } + } + catch ( const NoSuchElementException& ) + { + } + } + + if ( bRefreshToolbars ) + { + Reference< XLayoutManager > xLayoutManager( getLayoutManagerFromFrame( xFrame )); + if ( xLayoutManager.is() ) + { + Reference< XPropertySet > xPropSet( xLayoutManager, UNO_QUERY ); + if ( xPropSet.is() ) + { + try + { + xPropSet->setPropertyValue("RefreshContextToolbarVisibility", Any( true )); + } + catch ( const RuntimeException& ) + { + } + catch ( const Exception& ) + { + } + } + } + RefreshToolbars( xFrame ); + } + } + catch ( const RuntimeException& ) + { + throw; + } + catch ( const Exception& ) + { + } + } + } + else if ( aCmd.indexOf( STATIC_CMD_PART ) < 0 ) + { + URL aTargetURL; + Sequence aArgs; + + aTargetURL.Complete = aCmd; + xURLTransformer->parseStrict( aTargetURL ); + Reference< XDispatchProvider > xDispatchProvider( m_xFrame, UNO_QUERY ); + if ( xDispatchProvider.is() ) + { + Reference< XDispatch > xDispatch = xDispatchProvider->queryDispatch( + aTargetURL, OUString(), 0 ); + + ExecuteInfo* pExecuteInfo = new ExecuteInfo; + pExecuteInfo->xDispatch = xDispatch; + pExecuteInfo->aTargetURL = aTargetURL; + pExecuteInfo->aArgs = aArgs; + Application::PostUserEvent( LINK(nullptr, ToolbarsMenuController, ExecuteHdl_Impl), pExecuteInfo ); + } + } + else + { + Reference< XLayoutManager > xLayoutManager( getLayoutManagerFromFrame( xFrame )); + if ( xLayoutManager.is() ) + { + // Extract toolbar name from the combined uno-command. + sal_Int32 nIndex = aCmd.indexOf( '=' ); + if (( nIndex > 0 ) && (( nIndex+1 ) < aCmd.getLength() )) + { + OUString aToolBarResName = OUString::Concat("private:resource/toolbar/") + aCmd.subView(nIndex+1); + + const bool bShow(!xPopupMenu->isItemChecked(rEvent.MenuId)); + if ( bShow ) + { + xLayoutManager->createElement( aToolBarResName ); + xLayoutManager->showElement( aToolBarResName ); + } + else + { + // closing means: + // hide and destroy element + xLayoutManager->hideElement( aToolBarResName ); + xLayoutManager->destroyElement( aToolBarResName ); + } + } + } + } +} + +void SAL_CALL ToolbarsMenuController::itemActivated( const css::awt::MenuEvent& ) +{ + std::vector< OUString > aCmdVector; + Reference< XDispatchProvider > xDispatchProvider( m_xFrame, UNO_QUERY ); + Reference< XURLTransformer > xURLTransformer( m_xURLTransformer ); + { + std::unique_lock aLock( m_aMutex ); + fillPopupMenu( m_xPopupMenu ); + aCmdVector = m_aCommandVector; + } + + // Update status for all commands inside our toolbars popup menu + const sal_uInt32 nCount = aCmdVector.size(); + for ( sal_uInt32 i = 0; i < nCount; i++ ) + { + bool bInternal = aCmdVector[i].startsWith( STATIC_INTERNAL_CMD_PART ); + + if ( !bInternal ) + { + URL aTargetURL; + aTargetURL.Complete = aCmdVector[i]; + xURLTransformer->parseStrict( aTargetURL ); + Reference< XDispatch > xDispatch = xDispatchProvider->queryDispatch( aTargetURL, OUString(), 0 ); + if ( xDispatch.is() ) + { + xDispatch->addStatusListener( static_cast< XStatusListener* >(this), aTargetURL ); + xDispatch->removeStatusListener( static_cast< XStatusListener* >(this), aTargetURL ); + } + } + else if ( aCmdVector[i] == CMD_RESTOREVISIBILITY ) + { + // Special code to determine the enable/disable state of this command + FeatureStateEvent aFeatureStateEvent; + aFeatureStateEvent.FeatureURL.Complete = aCmdVector[i]; + aFeatureStateEvent.IsEnabled = m_bResetActive; // is context sensitive toolbar non visible + statusChanged( aFeatureStateEvent ); + } + } +} + +// XPopupMenuController +void SAL_CALL ToolbarsMenuController::setPopupMenu( const Reference< css::awt::XPopupMenu >& xPopupMenu ) +{ + std::unique_lock aLock( m_aMutex ); + + throwIfDisposed(aLock); + + if ( m_xFrame.is() && !m_xPopupMenu.is() ) + { + // Create popup menu on demand + SolarMutexGuard aSolarMutexGuard; + + m_xPopupMenu = dynamic_cast(xPopupMenu.get()); + assert(bool(xPopupMenu) == bool(m_xPopupMenu) && "we only support VCLXPopupMenu"); + m_xPopupMenu->addMenuListener( Reference< css::awt::XMenuListener >(this) ); + fillPopupMenu( m_xPopupMenu ); + } +} + +// XInitialization +void ToolbarsMenuController::initializeImpl( std::unique_lock& rGuard, const Sequence< Any >& aArguments ) +{ + bool bInitialized( m_bInitialized ); + if ( bInitialized ) + return; + + svt::PopupMenuControllerBase::initializeImpl(rGuard, aArguments); + + if ( !m_bInitialized ) + return; + + Reference< XModuleManager2 > xModuleManager = ModuleManager::create( m_xContext ); + Reference< XNameAccess > xPersistentWindowStateSupplier = css::ui::theWindowStateConfiguration::get( m_xContext ); + + // Retrieve persistent window state reference for our module + try + { + OUString aModuleIdentifier = xModuleManager->identify( m_xFrame ); + xPersistentWindowStateSupplier->getByName( aModuleIdentifier ) >>= m_xPersistentWindowState; + + Reference< XModuleUIConfigurationManagerSupplier > xModuleCfgSupplier = + theModuleUIConfigurationManagerSupplier::get( m_xContext ); + m_xModuleCfgMgr = xModuleCfgSupplier->getUIConfigurationManager( aModuleIdentifier ); + + Reference< XController > xController = m_xFrame->getController(); + Reference< XModel > xModel; + if ( xController.is() ) + xModel = xController->getModel(); + if ( xModel.is() ) + { + Reference< XUIConfigurationManagerSupplier > xUIConfigurationManagerSupplier( xModel, UNO_QUERY ); + if ( xUIConfigurationManagerSupplier.is() ) + m_xDocCfgMgr = xUIConfigurationManagerSupplier->getUIConfigurationManager(); + } + } + catch ( const Exception& ) + { + } +} + +IMPL_STATIC_LINK( ToolbarsMenuController, ExecuteHdl_Impl, void*, p, void ) +{ + ExecuteInfo* pExecuteInfo = static_cast(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! + if ( pExecuteInfo->xDispatch.is() ) + { + pExecuteInfo->xDispatch->dispatch( pExecuteInfo->aTargetURL, pExecuteInfo->aArgs ); + } + } + catch ( const Exception& ) + { + } + + delete pExecuteInfo; +} + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +framework_ToolbarsMenuController_get_implementation( + css::uno::XComponentContext* context, css::uno::Sequence const& ) +{ + return cppu::acquire(new framework::ToolbarsMenuController(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uielement/toolbarwrapper.cxx b/framework/source/uielement/toolbarwrapper.cxx new file mode 100644 index 0000000000..ae988744c1 --- /dev/null +++ b/framework/source/uielement/toolbarwrapper.cxx @@ -0,0 +1,363 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include + +using namespace com::sun::star; +using namespace com::sun::star::uno; +using namespace com::sun::star::beans; +using namespace com::sun::star::frame; +using namespace com::sun::star::lang; +using namespace com::sun::star::container; +using namespace com::sun::star::awt; +using namespace ::com::sun::star::ui; + +namespace framework +{ + +ToolBarWrapper::ToolBarWrapper( const Reference< XComponentContext >& rxContext ) : + ImplInheritanceHelper( UIElementType::TOOLBAR ), + m_xContext( rxContext ) +{ +} + +ToolBarWrapper::~ToolBarWrapper() +{ + m_xWeldedToolbar.reset(nullptr); + m_xTopLevel.reset(nullptr); + m_xBuilder.reset(nullptr); +} + +// XComponent +void SAL_CALL ToolBarWrapper::dispose() +{ + Reference< XComponent > xThis(this); + + { + SolarMutexGuard g; + if ( m_bDisposed ) + return; + } + + css::lang::EventObject aEvent( xThis ); + m_aListenerContainer.disposeAndClear( aEvent ); + + SolarMutexGuard g; + + auto xMultiplexer( ContextChangeEventMultiplexer::get( m_xContext ) ); + xMultiplexer->removeAllContextChangeEventListeners( this ); + + Reference< XComponent > xComponent( m_xSubElement, UNO_QUERY ); + if ( xComponent.is() ) + xComponent->removeEventListener( Reference< XUIConfigurationListener >( this )); + m_xSubElement.clear(); + + if ( m_xToolBarManager.is() ) + m_xToolBarManager->dispose(); + m_xToolBarManager.clear(); + m_xConfigSource.clear(); + m_xConfigData.clear(); + + m_bDisposed = true; +} + +// XInitialization +void SAL_CALL ToolBarWrapper::initialize( const Sequence< Any >& aArguments ) +{ + SolarMutexGuard g; + + if ( m_bDisposed ) + throw DisposedException(); + + if ( m_bInitialized ) + return; + + UIConfigElementWrapperBase::initialize( aArguments ); + + bool bPopupMode( false ); + Reference< XWindow > xParentWindow; + for ( Any const & arg : aArguments ) + { + PropertyValue aPropValue; + if ( arg >>= aPropValue ) + { + if ( aPropValue.Name == "PopupMode" ) + aPropValue.Value >>= bPopupMode; + else if ( aPropValue.Name == "ParentWindow" ) + xParentWindow.set( aPropValue.Value, UNO_QUERY ); + } + } + + Reference< XFrame > xFrame( m_xWeakFrame ); + if ( !(xFrame.is() && m_xConfigSource.is()) ) + return; + + OUString aContextPart; + if ( m_aResourceURL.startsWith( "private:resource/toolbar/singlemode", &aContextPart ) && aContextPart.isEmpty() ) + { + auto xMultiplexer( ContextChangeEventMultiplexer::get( m_xContext ) ); + try + { + xMultiplexer->addContextChangeEventListener( this, xFrame->getController() ); + } + catch( const Exception& ) + { + } + // Avoid flickering on context change + bPopupMode = true; + } + + // Create VCL based toolbar which will be filled with settings data + VclPtr pToolBar; + rtl::Reference pToolBarManager; + if ( aContextPart.isEmpty() ) + { + SolarMutexGuard aSolarMutexGuard; + if ( !xParentWindow.is() ) + xParentWindow.set( xFrame->getContainerWindow() ); + VclPtr pWindow = VCLUnoHelper::GetWindow( xParentWindow ); + if ( pWindow ) + { + sal_uLong nStyles = WB_BORDER | WB_SCROLL | WB_MOVEABLE | WB_3DLOOK | WB_DOCKABLE | WB_SIZEABLE | WB_CLOSEABLE; + + pToolBar = VclPtr::Create( pWindow, nStyles ); + pToolBar->SetLineSpacing(true); + pToolBarManager = new ToolBarManager( m_xContext, xFrame, m_aResourceURL, pToolBar ); + m_xToolBarManager = pToolBarManager; + pToolBar->WillUsePopupMode( bPopupMode ); + } + else if (weld::TransportAsXWindow* pTunnel = dynamic_cast(xParentWindow.get())) + { + m_xBuilder = Application::CreateBuilder(pTunnel->getWidget(), "svt/ui/managedtoolbar.ui"); + m_xTopLevel = m_xBuilder->weld_container("toolbarcontainer"); + m_xWeldedToolbar = m_xBuilder->weld_toolbar("managedtoolbar"); + if ( m_xWeldedToolbar ) + { + pToolBarManager = new ToolBarManager( m_xContext, xFrame, m_aResourceURL, m_xWeldedToolbar.get(), m_xBuilder.get() ); + m_xToolBarManager = pToolBarManager; + } + } + } + + try + { + m_xConfigData = m_xConfigSource->getSettings( m_aResourceURL, false ); + if ( m_xConfigData.is() && (pToolBar || m_xWeldedToolbar) && pToolBarManager ) + { + // Fill toolbar with container contents + impl_fillNewData(); + if (pToolBar) + { + pToolBar->EnableCustomize(); + ::Size aActSize( pToolBar->GetSizePixel() ); + ::Size aSize( pToolBar->CalcWindowSizePixel() ); + aSize.setWidth( aActSize.Width() ); + pToolBar->SetOutputSizePixel( aSize ); + } + } + } + catch ( const NoSuchElementException& ) + { + // No settings in our configuration manager. This means we are + // a transient toolbar which has no persistent settings. + m_bPersistent = false; + if ( pToolBar && pToolBarManager ) + { + pToolBar->EnableCustomize(); + ::Size aActSize( pToolBar->GetSizePixel() ); + ::Size aSize( pToolBar->CalcWindowSizePixel() ); + aSize.setWidth( aActSize.Width() ); + pToolBar->SetOutputSizePixel( aSize ); + } + } +} + +// XEventListener +void SAL_CALL ToolBarWrapper::disposing( const css::lang::EventObject& aEvent ) +{ + if ( aEvent.Source == m_xSubElement ) + m_xSubElement.clear(); +} + +// XUpdatable +void SAL_CALL ToolBarWrapper::update() +{ + SolarMutexGuard g; + + if ( m_bDisposed ) + throw DisposedException(); + + if ( m_xToolBarManager ) + m_xToolBarManager->CheckAndUpdateImages(); +} + +// XUIElementSettings +void SAL_CALL ToolBarWrapper::updateSettings() +{ + SolarMutexGuard g; + + if ( m_bDisposed ) + throw DisposedException(); + + if ( m_xConfigSource.is() && m_bPersistent ) + { + try + { + m_xConfigData = m_xConfigSource->getSettings( m_aResourceURL, false ); + if ( m_xConfigData.is() ) + impl_fillNewData(); + } + catch ( const NoSuchElementException& ) + { + } + + auto pContainer( m_aListenerContainer.getContainer( cppu::UnoType< XEventListener >::get() ) ); + if ( pContainer ) + pContainer->forEach< XUIElementSettings >([]( const Reference& xListener ){ xListener->updateSettings(); }); + } + else if ( !m_bPersistent ) + { + // Transient toolbar: do nothing + } +} + +void ToolBarWrapper::impl_fillNewData() +{ + if ( m_xToolBarManager ) + { + Reference< XUIElementSettings > xUIElementSettings( m_xSubElement, UNO_QUERY ); + Reference< XIndexAccess > xContextData = xUIElementSettings.is() ? xUIElementSettings->getSettings( false ) : nullptr; + OUString aContextToolbar = xContextData.is() ? m_xSubElement->getResourceURL() : OUString(); + m_xToolBarManager->FillToolbar( m_xConfigData, xContextData, aContextToolbar ); + } +} + +//XContextChangeEventListener +void SAL_CALL ToolBarWrapper::notifyContextChangeEvent( const ContextChangeEventObject& aEvent ) +{ + SolarMutexGuard g; + + if ( m_bDisposed ) + throw DisposedException(); + + if ( aEvent.ContextName.isEmpty() || aEvent.ContextName == "default" ) + return; + + const OUString aContextToolbar( m_aResourceURL + "-" + aEvent.ContextName.toAsciiLowerCase() ); + if ( m_xSubElement.is() && m_xSubElement->getResourceURL() == aContextToolbar ) + return; + + Reference< XComponent > xComponent( m_xSubElement, UNO_QUERY ); + if ( xComponent.is() ) + xComponent->removeEventListener( Reference< XUIConfigurationListener >( this )); + m_xSubElement.clear(); + + Reference< XLayoutManager > xLayoutManager; + Reference< XPropertySet > xPropSet( m_xWeakFrame.get(), UNO_QUERY ); + if ( xPropSet.is() ) + xPropSet->getPropertyValue("LayoutManager") >>= xLayoutManager; + if ( !xLayoutManager.is() ) + return; + + xLayoutManager->createElement( aContextToolbar ); + m_xSubElement.set( xLayoutManager->getElement( aContextToolbar ) ); + xComponent.set( m_xSubElement, UNO_QUERY ); + if ( xComponent.is() ) + xComponent->addEventListener( Reference< XUIConfigurationListener >( this )); + + if ( m_xConfigData.is() ) + { + xLayoutManager->lock(); + impl_fillNewData(); + xLayoutManager->unlock(); + } +} + +// XUIElement interface +Reference< XInterface > SAL_CALL ToolBarWrapper::getRealInterface( ) +{ + SolarMutexGuard g; + + if ( m_xToolBarManager ) + { + vcl::Window* pWindow = m_xToolBarManager->GetToolBar(); + return Reference< XInterface >( VCLUnoHelper::GetInterface( pWindow ), UNO_QUERY ); + } + + return Reference< XInterface >(); +} + +//XUIFunctionExecute +void SAL_CALL ToolBarWrapper::functionExecute( + const OUString& aUIElementName, + const OUString& aCommand ) +{ + SolarMutexGuard g; + + if ( m_xToolBarManager ) + m_xToolBarManager->notifyRegisteredControllers( aUIElementName, aCommand ); +} + +void SAL_CALL ToolBarWrapper::setFastPropertyValue_NoBroadcast( sal_Int32 nHandle, const css::uno::Any& aValue ) +{ + SolarMutexResettableGuard aLock; + bool bNoClose( m_bNoClose ); + aLock.clear(); + + UIConfigElementWrapperBase::setFastPropertyValue_NoBroadcast( nHandle, aValue ); + + aLock.reset(); + + bool bNewNoClose( m_bNoClose ); + if ( !(m_xToolBarManager.is() && !m_bDisposed && ( bNewNoClose != bNoClose ))) + return; + + if ( !m_xToolBarManager ) + return; + + ToolBox* pToolBox = m_xToolBarManager->GetToolBar(); + if ( !pToolBox ) + return; + + if ( bNewNoClose ) + { + pToolBox->SetStyle( pToolBox->GetStyle() & ~WB_CLOSEABLE ); + pToolBox->SetFloatStyle( pToolBox->GetFloatStyle() & ~WB_CLOSEABLE ); + } + else + { + pToolBox->SetStyle( pToolBox->GetStyle() | WB_CLOSEABLE ); + pToolBox->SetFloatStyle( pToolBox->GetFloatStyle() | WB_CLOSEABLE ); + } +} + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uielement/uicommanddescription.cxx b/framework/source/uielement/uicommanddescription.cxx new file mode 100644 index 0000000000..bbeb21851d --- /dev/null +++ b/framework/source/uielement/uicommanddescription.cxx @@ -0,0 +1,725 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include + +#include + +#include + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +using namespace com::sun::star::uno; +using namespace com::sun::star::lang; +using namespace com::sun::star::beans; +using namespace com::sun::star::configuration; +using namespace com::sun::star::container; +using namespace ::com::sun::star::frame; + +// Namespace + +const char CONFIGURATION_ROOT_ACCESS[] = "/org.openoffice.Office.UI."; + +// Special resource URLs to retrieve additional information +constexpr OUString PRIVATE_RESOURCE_URL = u"private:"_ustr; + +const sal_Int32 COMMAND_PROPERTY_IMAGE = 1; +const sal_Int32 COMMAND_PROPERTY_ROTATE = 2; +const sal_Int32 COMMAND_PROPERTY_MIRROR = 4; + +namespace framework +{ + +// Configuration access class for PopupMenuControllerFactory implementation + +namespace { + +class ConfigurationAccess_UICommand : // Order is necessary for right initialization! + public ::cppu::WeakImplHelper +{ + std::mutex m_aMutex; + public: + ConfigurationAccess_UICommand( std::u16string_view aModuleName, const Reference< XNameAccess >& xGenericUICommands, const Reference< XComponentContext >& rxContext ); + virtual ~ConfigurationAccess_UICommand() 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; + + // XElementAccess + virtual css::uno::Type SAL_CALL getElementType() override; + + virtual sal_Bool SAL_CALL hasElements() override; + + // container.XContainerListener + virtual void SAL_CALL elementInserted( const ContainerEvent& aEvent ) override; + virtual void SAL_CALL elementRemoved ( const ContainerEvent& aEvent ) override; + virtual void SAL_CALL elementReplaced( const ContainerEvent& aEvent ) override; + + // lang.XEventListener + virtual void SAL_CALL disposing( const EventObject& aEvent ) override; + + protected: + css::uno::Any getByNameImpl( const OUString& aName ); + + struct CmdToInfoMap + { + CmdToInfoMap() : bPopup( false ), + bCommandNameCreated( false ), + bIsExperimental( false ), + nProperties( 0 ) {} + + OUString aLabel; + OUString aContextLabel; + OUString aCommandName; + OUString aPopupLabel; + OUString aTooltipLabel; + OUString aTargetURL; + bool bPopup : 1, + bCommandNameCreated : 1; + bool bIsExperimental; + sal_Int32 nProperties; + }; + + Any getSequenceFromCache( const OUString& aCommandURL ); + Any getInfoFromCommand( const OUString& rCommandURL ); + void fillInfoFromResult( CmdToInfoMap& rCmdInfo, const OUString& aLabel ); + Sequence< OUString > getAllCommands(); + void fillCache(); + void addGenericInfoToCache(); + void impl_fill(const Reference< XNameAccess >& _xConfigAccess,bool _bPopup, + std::vector< OUString >& aImageCommandVector, + std::vector< OUString >& aImageRotateVector, + std::vector< OUString >& aImageMirrorVector); + + private: + typedef std::unordered_map< OUString, + CmdToInfoMap > CommandToInfoCache; + + void initializeConfigAccess(); + + OUString m_aConfigCmdAccess; + OUString m_aConfigPopupAccess; + OUString m_aPropProperties; + Reference< XNameAccess > m_xGenericUICommands; + Reference< XMultiServiceFactory > m_xConfigProvider; + Reference< XNameAccess > m_xConfigAccess; + Reference< XContainerListener > m_xConfigListener; + Reference< XNameAccess > m_xConfigAccessPopups; + Reference< XContainerListener > m_xConfigAccessListener; + Sequence< OUString > m_aCommandImageList; + Sequence< OUString > m_aCommandRotateImageList; + Sequence< OUString > m_aCommandMirrorImageList; + CommandToInfoCache m_aCmdInfoCache; + bool m_bConfigAccessInitialized; + bool m_bCacheFilled; + bool m_bGenericDataRetrieved; +}; + +} + + +// XInterface, XTypeProvider + +ConfigurationAccess_UICommand::ConfigurationAccess_UICommand( std::u16string_view aModuleName, const Reference< XNameAccess >& rGenericUICommands, const Reference< XComponentContext>& rxContext ) : + // Create configuration hierarchical access name + m_aConfigCmdAccess( + OUString::Concat(CONFIGURATION_ROOT_ACCESS) + aModuleName + "/UserInterface/Commands"), + m_aConfigPopupAccess( + OUString::Concat(CONFIGURATION_ROOT_ACCESS) + aModuleName + "/UserInterface/Popups"), + m_aPropProperties( "Properties" ), + m_xGenericUICommands( rGenericUICommands ), + m_xConfigProvider( theDefaultProvider::get( rxContext ) ), + m_bConfigAccessInitialized( false ), + m_bCacheFilled( false ), + m_bGenericDataRetrieved( false ) +{ +} + +ConfigurationAccess_UICommand::~ConfigurationAccess_UICommand() +{ + // SAFE + std::unique_lock g(m_aMutex); + Reference< XContainer > xContainer( m_xConfigAccess, UNO_QUERY ); + if ( xContainer.is() ) + xContainer->removeContainerListener(m_xConfigListener); + xContainer.set( m_xConfigAccessPopups, UNO_QUERY ); + if ( xContainer.is() ) + xContainer->removeContainerListener(m_xConfigAccessListener); +} + +// XNameAccess +Any ConfigurationAccess_UICommand::getByNameImpl( const OUString& rCommandURL ) +{ + std::unique_lock g(m_aMutex); + if ( !m_bConfigAccessInitialized ) + { + initializeConfigAccess(); + m_bConfigAccessInitialized = true; + fillCache(); + } + + if ( rCommandURL.startsWith( PRIVATE_RESOURCE_URL ) ) + { + // special keys to retrieve information about a set of commands + // SAFE + addGenericInfoToCache(); + + if ( rCommandURL.equalsIgnoreAsciiCase( UICOMMANDDESCRIPTION_NAMEACCESS_COMMANDIMAGELIST )) + return Any( m_aCommandImageList ); + else if ( rCommandURL.equalsIgnoreAsciiCase( UICOMMANDDESCRIPTION_NAMEACCESS_COMMANDROTATEIMAGELIST )) + return Any( m_aCommandRotateImageList ); + else if ( rCommandURL.equalsIgnoreAsciiCase( UICOMMANDDESCRIPTION_NAMEACCESS_COMMANDMIRRORIMAGELIST )) + return Any( m_aCommandMirrorImageList ); + else + return Any(); + } + else + { + // SAFE + return getInfoFromCommand( rCommandURL ); + } +} + +Any SAL_CALL ConfigurationAccess_UICommand::getByName( const OUString& rCommandURL ) +{ + Any aRet( getByNameImpl( rCommandURL ) ); + if( !aRet.hasValue() ) + throw NoSuchElementException(); + + return aRet; +} + +Sequence< OUString > SAL_CALL ConfigurationAccess_UICommand::getElementNames() +{ + return getAllCommands(); +} + +sal_Bool SAL_CALL ConfigurationAccess_UICommand::hasByName( const OUString& rCommandURL ) +{ + return getByNameImpl( rCommandURL ).hasValue(); +} + +// XElementAccess +Type SAL_CALL ConfigurationAccess_UICommand::getElementType() +{ + return cppu::UnoType>::get(); +} + +sal_Bool SAL_CALL ConfigurationAccess_UICommand::hasElements() +{ + // There must are global commands! + return true; +} + +void ConfigurationAccess_UICommand::fillInfoFromResult( CmdToInfoMap& rCmdInfo, const OUString& aLabel ) +{ + OUString aStr(aLabel.replaceAll("%PRODUCTNAME", utl::ConfigManager::getProductName())); + rCmdInfo.aLabel = aStr; + aStr = comphelper::string::stripEnd(aStr, '.'); // Remove "..." from string + rCmdInfo.aCommandName = MnemonicGenerator::EraseAllMnemonicChars(aStr); + rCmdInfo.bCommandNameCreated = true; +} + +Any ConfigurationAccess_UICommand::getSequenceFromCache( const OUString& aCommandURL ) +{ + CommandToInfoCache::iterator pIter = m_aCmdInfoCache.find( aCommandURL ); + if ( pIter != m_aCmdInfoCache.end() ) + { + if ( !pIter->second.bCommandNameCreated ) + fillInfoFromResult( pIter->second, pIter->second.aLabel ); + + static constexpr OUString sLabel = u"Label"_ustr; + static constexpr OUString sName = u"Name"_ustr; + static constexpr OUString sPopup = u"Popup"_ustr; + static constexpr OUString sPopupLabel = u"PopupLabel"_ustr; + static constexpr OUString sTooltipLabel = u"TooltipLabel"_ustr; + static constexpr OUString sTargetURL = u"TargetURL"_ustr; + static constexpr OUString sIsExperimental = u"IsExperimental"_ustr; + Sequence< PropertyValue > aPropSeq{ + comphelper::makePropertyValue(sLabel, !pIter->second.aContextLabel.isEmpty() + ? Any(pIter->second.aContextLabel) + : Any(pIter->second.aLabel)), + comphelper::makePropertyValue(sName, pIter->second.aCommandName), + comphelper::makePropertyValue(sPopup, pIter->second.bPopup), + comphelper::makePropertyValue(m_aPropProperties, pIter->second.nProperties), + comphelper::makePropertyValue(sPopupLabel, pIter->second.aPopupLabel), + comphelper::makePropertyValue(sTooltipLabel, pIter->second.aTooltipLabel), + comphelper::makePropertyValue(sTargetURL, pIter->second.aTargetURL), + comphelper::makePropertyValue(sIsExperimental, pIter->second.bIsExperimental) + }; + return Any( aPropSeq ); + } + + return Any(); +} +void ConfigurationAccess_UICommand::impl_fill(const Reference< XNameAccess >& _xConfigAccess,bool _bPopup, + std::vector< OUString >& aImageCommandVector, + std::vector< OUString >& aImageRotateVector, + std::vector< OUString >& aImageMirrorVector) +{ + if ( !_xConfigAccess.is() ) + return; + + Sequence< OUString> aNameSeq = _xConfigAccess->getElementNames(); + const sal_Int32 nCount = aNameSeq.getLength(); + for ( sal_Int32 i = 0; i < nCount; i++ ) + { + try + { + Reference< XNameAccess > xNameAccess(_xConfigAccess->getByName( aNameSeq[i] ),UNO_QUERY); + if ( xNameAccess.is() ) + { + CmdToInfoMap aCmdToInfo; + + aCmdToInfo.bPopup = _bPopup; + xNameAccess->getByName( "Label" ) >>= aCmdToInfo.aLabel; + xNameAccess->getByName( "ContextLabel" ) >>= aCmdToInfo.aContextLabel; + xNameAccess->getByName( "PopupLabel" ) >>= aCmdToInfo.aPopupLabel; + xNameAccess->getByName( "TooltipLabel" ) >>= aCmdToInfo.aTooltipLabel; + xNameAccess->getByName( "TargetURL" ) >>= aCmdToInfo.aTargetURL; + xNameAccess->getByName( "IsExperimental" ) >>= aCmdToInfo.bIsExperimental; + xNameAccess->getByName( m_aPropProperties ) >>= aCmdToInfo.nProperties; + + m_aCmdInfoCache.emplace( aNameSeq[i], aCmdToInfo ); + + if ( aCmdToInfo.nProperties & COMMAND_PROPERTY_IMAGE ) + aImageCommandVector.push_back( aNameSeq[i] ); + if ( aCmdToInfo.nProperties & COMMAND_PROPERTY_ROTATE ) + aImageRotateVector.push_back( aNameSeq[i] ); + if ( aCmdToInfo.nProperties & COMMAND_PROPERTY_MIRROR ) + aImageMirrorVector.push_back( aNameSeq[i] ); + } + } + catch (const css::lang::WrappedTargetException&) + { + } + catch (const css::container::NoSuchElementException&) + { + } + } +} +void ConfigurationAccess_UICommand::fillCache() +{ + + if ( m_bCacheFilled ) + return; + + std::vector< OUString > aImageCommandVector; + std::vector< OUString > aImageRotateVector; + std::vector< OUString > aImageMirrorVector; + + impl_fill(m_xConfigAccess,false,aImageCommandVector,aImageRotateVector,aImageMirrorVector); + impl_fill(m_xConfigAccessPopups,true,aImageCommandVector,aImageRotateVector,aImageMirrorVector); + // Create cached sequences for fast retrieving + m_aCommandImageList = comphelper::containerToSequence( aImageCommandVector ); + m_aCommandRotateImageList = comphelper::containerToSequence( aImageRotateVector ); + m_aCommandMirrorImageList = comphelper::containerToSequence( aImageMirrorVector ); + + m_bCacheFilled = true; +} + +void ConfigurationAccess_UICommand::addGenericInfoToCache() +{ + if ( !m_xGenericUICommands.is() || m_bGenericDataRetrieved ) + return; + + Sequence< OUString > aCommandNameSeq; + try + { + if ( m_xGenericUICommands->getByName( + UICOMMANDDESCRIPTION_NAMEACCESS_COMMANDROTATEIMAGELIST ) >>= aCommandNameSeq ) + m_aCommandRotateImageList = comphelper::concatSequences< OUString >( m_aCommandRotateImageList, aCommandNameSeq ); + } + catch (const RuntimeException&) + { + throw; + } + catch (const Exception&) + { + } + + try + { + if ( m_xGenericUICommands->getByName( + UICOMMANDDESCRIPTION_NAMEACCESS_COMMANDMIRRORIMAGELIST ) >>= aCommandNameSeq ) + m_aCommandMirrorImageList = comphelper::concatSequences< OUString >( m_aCommandMirrorImageList, aCommandNameSeq ); + } + catch (const RuntimeException&) + { + throw; + } + catch (const Exception&) + { + } + + m_bGenericDataRetrieved = true; +} + +Any ConfigurationAccess_UICommand::getInfoFromCommand( const OUString& rCommandURL ) +{ + Any a; + + try + { + a = getSequenceFromCache( rCommandURL ); + if ( !a.hasValue() ) + { + // First try to ask our global commands configuration access. It also caches maybe + // we find the entry in its cache first. + if ( m_xGenericUICommands.is() && m_xGenericUICommands->hasByName( rCommandURL ) ) + { + try + { + return m_xGenericUICommands->getByName( rCommandURL ); + } + catch (const css::lang::WrappedTargetException&) + { + } + catch (const css::container::NoSuchElementException&) + { + } + } + } + } + catch (const css::container::NoSuchElementException&) + { + } + catch (const css::lang::WrappedTargetException&) + { + } + + return a; +} + +Sequence< OUString > ConfigurationAccess_UICommand::getAllCommands() +{ + // SAFE + std::unique_lock g(m_aMutex); + + if ( !m_bConfigAccessInitialized ) + { + initializeConfigAccess(); + m_bConfigAccessInitialized = true; + fillCache(); + } + + if ( m_xConfigAccess.is() ) + { + try + { + Sequence< OUString > aNameSeq = m_xConfigAccess->getElementNames(); + + if ( m_xGenericUICommands.is() ) + { + // Create concat list of supported user interface commands of the module + Sequence< OUString > aGenericNameSeq = m_xGenericUICommands->getElementNames(); + sal_uInt32 nCount1 = aNameSeq.getLength(); + sal_uInt32 nCount2 = aGenericNameSeq.getLength(); + + aNameSeq.realloc( nCount1 + nCount2 ); + OUString* pNameSeq = aNameSeq.getArray(); + const OUString* pGenericSeq = aGenericNameSeq.getConstArray(); + for ( sal_uInt32 i = 0; i < nCount2; i++ ) + pNameSeq[nCount1+i] = pGenericSeq[i]; + } + + return aNameSeq; + } + catch (const css::container::NoSuchElementException&) + { + } + catch (const css::lang::WrappedTargetException&) + { + } + } + + return Sequence< OUString >(); +} + +void ConfigurationAccess_UICommand::initializeConfigAccess() +{ + try + { + Sequence aArgs(comphelper::InitAnyPropertySequence( + { + {"nodepath", Any(m_aConfigCmdAccess)} + })); + m_xConfigAccess.set( m_xConfigProvider->createInstanceWithArguments( + "com.sun.star.configuration.ConfigurationAccess", aArgs ),UNO_QUERY ); + if ( m_xConfigAccess.is() ) + { + // Add as container listener + Reference< XContainer > xContainer( m_xConfigAccess, UNO_QUERY ); + if ( xContainer.is() ) + { + m_xConfigListener = new WeakContainerListener(this); + xContainer->addContainerListener(m_xConfigListener); + } + } + + Sequence aArgs2(comphelper::InitAnyPropertySequence( + { + {"nodepath", Any(m_aConfigPopupAccess)} + })); + m_xConfigAccessPopups.set( m_xConfigProvider->createInstanceWithArguments( + "com.sun.star.configuration.ConfigurationAccess", aArgs2 ),UNO_QUERY ); + if ( m_xConfigAccessPopups.is() ) + { + // Add as container listener + Reference< XContainer > xContainer( m_xConfigAccessPopups, UNO_QUERY ); + if ( xContainer.is() ) + { + m_xConfigAccessListener = new WeakContainerListener(this); + xContainer->addContainerListener(m_xConfigAccessListener); + } + } + } + catch (const WrappedTargetException&) + { + } + catch (const Exception&) + { + } +} + +// container.XContainerListener +void SAL_CALL ConfigurationAccess_UICommand::elementInserted( const ContainerEvent& ) +{ + std::unique_lock g(m_aMutex); + m_bCacheFilled = false; + fillCache(); +} + +void SAL_CALL ConfigurationAccess_UICommand::elementRemoved( const ContainerEvent& ) +{ + std::unique_lock g(m_aMutex); + m_bCacheFilled = false; + fillCache(); +} + +void SAL_CALL ConfigurationAccess_UICommand::elementReplaced( const ContainerEvent& ) +{ + std::unique_lock g(m_aMutex); + m_bCacheFilled = false; + fillCache(); +} + +// lang.XEventListener +void SAL_CALL ConfigurationAccess_UICommand::disposing( const EventObject& aEvent ) +{ + // SAFE + // remove our reference to the config access + std::unique_lock g(m_aMutex); + + Reference< XInterface > xIfac1( aEvent.Source, UNO_QUERY ); + Reference< XInterface > xIfac2( m_xConfigAccess, UNO_QUERY ); + if ( xIfac1 == xIfac2 ) + m_xConfigAccess.clear(); + else + { + xIfac1.set( m_xConfigAccessPopups, UNO_QUERY ); + if ( xIfac1 == xIfac2 ) + m_xConfigAccessPopups.clear(); + } +} + +void UICommandDescription::ensureGenericUICommandsForLanguage(const LanguageTag& rLanguage) +{ + auto xGenericUICommands = m_xGenericUICommands.find(rLanguage); + if (xGenericUICommands == m_xGenericUICommands.end()) + { + Reference< XNameAccess > xEmpty; + m_xGenericUICommands[rLanguage] = new ConfigurationAccess_UICommand( u"GenericCommands", xEmpty, m_xContext ); + } +} + +UICommandDescription::UICommandDescription(const Reference< XComponentContext >& rxContext) + : m_aPrivateResourceURL(PRIVATE_RESOURCE_URL) + , m_xContext(rxContext) +{ + SvtSysLocale aSysLocale; + const LanguageTag& rCurrentLanguage = aSysLocale.GetUILanguageTag(); + + ensureGenericUICommandsForLanguage(rCurrentLanguage); + + impl_fillElements("ooSetupFactoryCommandConfigRef"); + + // insert generic commands + auto& rMap = m_aUICommandsHashMap[rCurrentLanguage]; + UICommandsHashMap::iterator pIter = rMap.find( "GenericCommands" ); + if ( pIter != rMap.end() ) + pIter->second = m_xGenericUICommands[rCurrentLanguage]; +} + +UICommandDescription::UICommandDescription(const Reference< XComponentContext >& rxContext, bool) + : m_xContext(rxContext) +{ +} + +UICommandDescription::~UICommandDescription() +{ + std::unique_lock g(m_aMutex); + m_aModuleToCommandFileMap.clear(); + m_aUICommandsHashMap.clear(); + m_xGenericUICommands.clear(); +} +void UICommandDescription::impl_fillElements(const char* _pName) +{ + m_xModuleManager.set( ModuleManager::create( m_xContext ) ); + const Sequence< OUString > aElementNames = m_xModuleManager->getElementNames(); + + SvtSysLocale aSysLocale; + + for ( OUString const & aModuleIdentifier : aElementNames ) + { + Sequence< PropertyValue > aSeq; + if ( m_xModuleManager->getByName( aModuleIdentifier ) >>= aSeq ) + { + OUString aCommandStr; + for ( PropertyValue const & prop : std::as_const(aSeq) ) + { + if ( prop.Name.equalsAscii(_pName) ) + { + prop.Value >>= aCommandStr; + break; + } + } + + // Create first mapping ModuleIdentifier ==> Command File + m_aModuleToCommandFileMap.emplace( aModuleIdentifier, aCommandStr ); + + // Create second mapping Command File ==> commands instance + const LanguageTag& rCurrentLanguage = aSysLocale.GetUILanguageTag(); + auto& rMap = m_aUICommandsHashMap[rCurrentLanguage]; + UICommandsHashMap::iterator pIter = rMap.find( aCommandStr ); + if ( pIter == rMap.end() ) + rMap.emplace( aCommandStr, Reference< XNameAccess >() ); + } + } // for ( sal_Int32 i = 0; i < aElementNames.(); i++ ) +} + +Any SAL_CALL UICommandDescription::getByName( const OUString& aName ) +{ + SvtSysLocale aSysLocale; + const LanguageTag& rCurrentLanguage = aSysLocale.GetUILanguageTag(); + Any a; + + std::unique_lock g(m_aMutex); + + ModuleToCommandFileMap::const_iterator pM2CIter = m_aModuleToCommandFileMap.find( aName ); + if ( pM2CIter != m_aModuleToCommandFileMap.end() ) + { + OUString aCommandFile( pM2CIter->second ); + auto pMapIter = m_aUICommandsHashMap.find( rCurrentLanguage ); + if ( pMapIter == m_aUICommandsHashMap.end() ) + impl_fillElements("ooSetupFactoryCommandConfigRef"); + + auto& rMap = m_aUICommandsHashMap[rCurrentLanguage]; + UICommandsHashMap::iterator pIter = rMap.find( aCommandFile ); + if ( pIter != rMap.end() ) + { + if ( pIter->second.is() ) + a <<= pIter->second; + else + { + ensureGenericUICommandsForLanguage(rCurrentLanguage); + + Reference< XNameAccess > xUICommands = new ConfigurationAccess_UICommand( aCommandFile, + m_xGenericUICommands[rCurrentLanguage], + m_xContext ); + pIter->second = xUICommands; + a <<= xUICommands; + } + } + } + else if ( !m_aPrivateResourceURL.isEmpty() && aName.startsWith( m_aPrivateResourceURL ) ) + { + ensureGenericUICommandsForLanguage(rCurrentLanguage); + + // special keys to retrieve information about a set of commands + return m_xGenericUICommands[rCurrentLanguage]->getByName( aName ); + } + else + { + throw NoSuchElementException(); + } + + return a; +} + +Sequence< OUString > SAL_CALL UICommandDescription::getElementNames() +{ + std::unique_lock g(m_aMutex); + + return comphelper::mapKeysToSequence( m_aModuleToCommandFileMap ); +} + +sal_Bool SAL_CALL UICommandDescription::hasByName( const OUString& aName ) +{ + std::unique_lock g(m_aMutex); + + ModuleToCommandFileMap::const_iterator pIter = m_aModuleToCommandFileMap.find( aName ); + return ( pIter != m_aModuleToCommandFileMap.end() ); +} + +// XElementAccess +Type SAL_CALL UICommandDescription::getElementType() +{ + return cppu::UnoType::get(); +} + +sal_Bool SAL_CALL UICommandDescription::hasElements() +{ + // generic UI commands are always available! + return true; +} + +} // namespace framework + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_framework_UICommandDescription_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence const &) +{ + return cppu::acquire(new framework::UICommandDescription(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uifactory/addonstoolbarfactory.cxx b/framework/source/uifactory/addonstoolbarfactory.cxx new file mode 100644 index 0000000000..b9a1c95c90 --- /dev/null +++ b/framework/source/uifactory/addonstoolbarfactory.cxx @@ -0,0 +1,206 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +using namespace com::sun::star::uno; +using namespace com::sun::star::lang; +using namespace com::sun::star::frame; +using namespace com::sun::star::beans; +using namespace com::sun::star::util; +using namespace ::com::sun::star::ui; +using namespace framework; + +namespace { + +class AddonsToolBarFactory : public ::cppu::WeakImplHelper< css::lang::XServiceInfo , + css::ui::XUIElementFactory > +{ +public: + explicit AddonsToolBarFactory( const css::uno::Reference< css::uno::XComponentContext >& xContext ); + + virtual OUString SAL_CALL getImplementationName() override + { + return "com.sun.star.comp.framework.AddonsToolBarFactory"; + } + + virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override + { + return cppu::supportsService(this, ServiceName); + } + + virtual css::uno::Sequence SAL_CALL getSupportedServiceNames() override + { + return {"com.sun.star.ui.ToolBarFactory"}; + } + + // XUIElementFactory + virtual css::uno::Reference< css::ui::XUIElement > SAL_CALL createUIElement( const OUString& ResourceURL, const css::uno::Sequence< css::beans::PropertyValue >& Args ) override; + + bool hasButtonsInContext( const css::uno::Sequence< css::uno::Sequence< css::beans::PropertyValue > >& rPropSeq, + const css::uno::Reference< css::frame::XFrame >& rFrame ); + +private: + css::uno::Reference< css::uno::XComponentContext > m_xContext; + css::uno::Reference< css::frame::XModuleManager2 > m_xModuleManager; +}; + +AddonsToolBarFactory::AddonsToolBarFactory( + const css::uno::Reference< css::uno::XComponentContext >& xContext ) : + m_xContext( xContext ) + , m_xModuleManager( ModuleManager::create( xContext ) ) +{ +} + +bool IsCorrectContext( std::u16string_view rModuleIdentifier, std::u16string_view aContextList ) +{ + if ( aContextList.empty() ) + return true; + + if ( !rModuleIdentifier.empty() ) + { + return aContextList.find( rModuleIdentifier ) != std::u16string_view::npos; + } + + return false; +} + +bool AddonsToolBarFactory::hasButtonsInContext( + const Sequence< Sequence< PropertyValue > >& rPropSeqSeq, + const Reference< XFrame >& rFrame ) +{ + OUString aModuleIdentifier; + try + { + aModuleIdentifier = m_xModuleManager->identify( rFrame ); + } + catch ( const RuntimeException& ) + { + throw; + } + catch ( const Exception& ) + { + } + + // Check before we create a toolbar that we have at least one button in + // the current frame context. + for ( Sequence const & props : rPropSeqSeq ) + { + bool bIsButton( true ); + bool bIsCorrectContext( false ); + sal_uInt32 nPropChecked( 0 ); + + for ( PropertyValue const & prop : props ) + { + if ( prop.Name == "Context" ) + { + OUString aContextList; + if ( prop.Value >>= aContextList ) + bIsCorrectContext = IsCorrectContext( aModuleIdentifier, aContextList ); + nPropChecked++; + } + else if ( prop.Name == "URL" ) + { + OUString aURL; + prop.Value >>= aURL; + bIsButton = aURL != "private:separator"; + nPropChecked++; + } + + if ( nPropChecked == 2 ) + break; + } + + if ( bIsButton && bIsCorrectContext ) + return true; + } + + return false; +} + +// XUIElementFactory +Reference< XUIElement > SAL_CALL AddonsToolBarFactory::createUIElement( + const OUString& ResourceURL, + const Sequence< PropertyValue >& Args ) +{ + SolarMutexGuard g; + + Sequence< Sequence< PropertyValue > > aConfigData; + Reference< XFrame > xFrame; + OUString aResourceURL( ResourceURL ); + + for ( PropertyValue const & arg : Args ) + { + if ( arg.Name == "ConfigurationData" ) + arg.Value >>= aConfigData; + else if ( arg.Name == "Frame" ) + arg.Value >>= xFrame; + else if ( arg.Name == "ResourceURL" ) + arg.Value >>= aResourceURL; + } + + if ( !aResourceURL.startsWith("private:resource/toolbar/addon_") ) + throw IllegalArgumentException(); + + // Identify frame and determine module identifier to look for context based buttons + Reference< css::ui::XUIElement > xToolBar; + if ( xFrame.is() && + aConfigData.hasElements() && + hasButtonsInContext( aConfigData, xFrame )) + { + Sequence< Any > aPropSeq{ Any(comphelper::makePropertyValue("Frame", xFrame)), + Any(comphelper::makePropertyValue("ConfigurationData", + aConfigData)), + Any(comphelper::makePropertyValue("ResourceURL", aResourceURL)) }; + + SolarMutexGuard aGuard; + rtl::Reference pToolBarWrapper = new AddonsToolBarWrapper( m_xContext ); + xToolBar = pToolBarWrapper; + pToolBarWrapper->initialize( aPropSeq ); + } + + return xToolBar; +} + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_framework_AddonsToolBarFactory_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence const &) +{ + return cppu::acquire(new AddonsToolBarFactory(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uifactory/factoryconfiguration.cxx b/framework/source/uifactory/factoryconfiguration.cxx new file mode 100644 index 0000000000..047a6b6edb --- /dev/null +++ b/framework/source/uifactory/factoryconfiguration.cxx @@ -0,0 +1,289 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include + +#include +#include + +#include + +#include +#include +#include +#include + +#include +#include + +// Defines + +using namespace com::sun::star; +using namespace com::sun::star::uno; +using namespace com::sun::star::lang; +using namespace com::sun::star::beans; +using namespace com::sun::star::container; + +// Namespace + +namespace framework +{ +static OUString getHashKeyFromStrings( + std::u16string_view aCommandURL, std::u16string_view aModuleName ) +{ + return OUString::Concat(aCommandURL) + "-" + aModuleName; +} + +// XInterface, XTypeProvider + +ConfigurationAccess_ControllerFactory::ConfigurationAccess_ControllerFactory( const Reference< XComponentContext >& rxContext, OUString _sRoot ) : + m_aPropCommand( "Command" ), + m_aPropModule( "Module" ), + m_aPropController( "Controller" ), + m_aPropValue( "Value" ), + m_sRoot(std::move(_sRoot)), + m_bConfigAccessInitialized( false ) +{ + m_xConfigProvider = configuration::theDefaultProvider::get( rxContext ); +} + +ConfigurationAccess_ControllerFactory::~ConfigurationAccess_ControllerFactory() +{ + std::unique_lock g(m_mutex); + + Reference< XContainer > xContainer( m_xConfigAccess, UNO_QUERY ); + if ( xContainer.is() ) + xContainer->removeContainerListener(m_xConfigAccessListener); +} + +OUString ConfigurationAccess_ControllerFactory::getServiceFromCommandModule( std::u16string_view rCommandURL, std::u16string_view rModule ) const +{ + std::unique_lock g(m_mutex); + MenuControllerMap::const_iterator pIter = m_aMenuControllerMap.find( getHashKeyFromStrings( rCommandURL, rModule )); + + if ( pIter != m_aMenuControllerMap.end() ) + return pIter->second.m_aImplementationName; + else if ( !rModule.empty() ) + { + // Try to detect if we have a generic popup menu controller + pIter = m_aMenuControllerMap.find( + getHashKeyFromStrings( rCommandURL, std::u16string_view() )); + + if ( pIter != m_aMenuControllerMap.end() ) + return pIter->second.m_aImplementationName; + } + + return OUString(); +} +OUString ConfigurationAccess_ControllerFactory::getValueFromCommandModule( std::u16string_view rCommandURL, std::u16string_view rModule ) const +{ + std::unique_lock g(m_mutex); + + MenuControllerMap::const_iterator pIter = m_aMenuControllerMap.find( getHashKeyFromStrings( rCommandURL, rModule )); + + if ( pIter != m_aMenuControllerMap.end() ) + return pIter->second.m_aValue; + else if ( !rModule.empty() ) + { + // Try to detect if we have a generic popup menu controller + pIter = m_aMenuControllerMap.find( + getHashKeyFromStrings( rCommandURL, std::u16string_view() )); + + if ( pIter != m_aMenuControllerMap.end() ) + return pIter->second.m_aValue; + } + + return OUString(); +} + +void ConfigurationAccess_ControllerFactory::addServiceToCommandModule( + std::u16string_view rCommandURL, + std::u16string_view rModule, + const OUString& rServiceSpecifier ) +{ + std::unique_lock g(m_mutex); + + OUString aHashKey = getHashKeyFromStrings( rCommandURL, rModule ); + m_aMenuControllerMap.emplace( aHashKey,ControllerInfo(rServiceSpecifier,OUString()) ); +} + +void ConfigurationAccess_ControllerFactory::removeServiceFromCommandModule( + std::u16string_view rCommandURL, + std::u16string_view rModule ) +{ + std::unique_lock g(m_mutex); + + OUString aHashKey = getHashKeyFromStrings( rCommandURL, rModule ); + m_aMenuControllerMap.erase( aHashKey ); +} + +// container.XContainerListener +void SAL_CALL ConfigurationAccess_ControllerFactory::elementInserted( const ContainerEvent& aEvent ) +{ + OUString aCommand; + OUString aModule; + OUString aService; + OUString aValue; + + std::unique_lock g(m_mutex); + + if ( impl_getElementProps( aEvent.Element, aCommand, aModule, aService, aValue )) + { + // Create hash key from command and module as they are together a primary key to + // the UNO service that implements the popup menu controller. + OUString aHashKey( getHashKeyFromStrings( aCommand, aModule )); + ControllerInfo& rControllerInfo = m_aMenuControllerMap[ aHashKey ]; + rControllerInfo.m_aImplementationName = aService; + rControllerInfo.m_aValue = aValue; + } +} + +void SAL_CALL ConfigurationAccess_ControllerFactory::elementRemoved ( const ContainerEvent& aEvent ) +{ + OUString aCommand; + OUString aModule; + OUString aService; + OUString aValue; + + std::unique_lock g(m_mutex); + + if ( impl_getElementProps( aEvent.Element, aCommand, aModule, aService, aValue )) + { + // Create hash key from command and module as they are together a primary key to + // the UNO service that implements the popup menu controller. + OUString aHashKey( getHashKeyFromStrings( aCommand, aModule )); + m_aMenuControllerMap.erase( aHashKey ); + } +} + +void SAL_CALL ConfigurationAccess_ControllerFactory::elementReplaced( const ContainerEvent& aEvent ) +{ + elementInserted(aEvent); +} + +// lang.XEventListener +void SAL_CALL ConfigurationAccess_ControllerFactory::disposing( const EventObject& ) +{ + // remove our reference to the config access + std::unique_lock g(m_mutex); + m_xConfigAccess.clear(); +} + +void ConfigurationAccess_ControllerFactory::readConfigurationData() +{ + // SAFE + std::unique_lock aLock( m_mutex ); + + if ( !m_bConfigAccessInitialized ) + { + uno::Sequence aArgs(comphelper::InitAnyPropertySequence( + { + {"nodepath", uno::Any(m_sRoot)} + })); + try + { + m_xConfigAccess.set( m_xConfigProvider->createInstanceWithArguments(SERVICENAME_CFGREADACCESS,aArgs ), UNO_QUERY ); + } + catch ( const WrappedTargetException& ) + { + } + + m_bConfigAccessInitialized = true; + } + + if ( !m_xConfigAccess.is() ) + return; + + // Read and update configuration data + updateConfigurationDataImpl(); + + uno::Reference< container::XContainer > xContainer( m_xConfigAccess, uno::UNO_QUERY ); + // UNSAFE + aLock.unlock(); + + if ( xContainer.is() ) + { + m_xConfigAccessListener = new WeakContainerListener(this); + xContainer->addContainerListener(m_xConfigAccessListener); + } +} + +void ConfigurationAccess_ControllerFactory::updateConfigurationDataImpl() +{ + const Sequence< OUString > aPopupMenuControllers = m_xConfigAccess->getElementNames(); + + OUString aCommand; + OUString aModule; + OUString aService; + OUString aHashKey; + OUString aValue; + + m_aMenuControllerMap.clear(); + for ( OUString const & name : aPopupMenuControllers ) + { + try + { + if ( impl_getElementProps( m_xConfigAccess->getByName( name ), aCommand, aModule, aService,aValue )) + { + // Create hash key from command and module as they are together a primary key to + // the UNO service that implements the popup menu controller. + aHashKey = getHashKeyFromStrings( aCommand, aModule ); + m_aMenuControllerMap.emplace( aHashKey, ControllerInfo(aService,aValue) ); + } + } + catch ( const NoSuchElementException& ) + { + } + catch ( const WrappedTargetException& ) + { + } + } +} + +bool ConfigurationAccess_ControllerFactory::impl_getElementProps( const Any& aElement, OUString& aCommand, OUString& aModule, OUString& aServiceSpecifier,OUString& aValue ) const +{ + Reference< XPropertySet > xPropertySet; + aElement >>= xPropertySet; + + if ( !xPropertySet.is() ) + return true; + + try + { + xPropertySet->getPropertyValue( m_aPropCommand ) >>= aCommand; + xPropertySet->getPropertyValue( m_aPropModule ) >>= aModule; + xPropertySet->getPropertyValue( m_aPropController ) >>= aServiceSpecifier; + xPropertySet->getPropertyValue( m_aPropValue ) >>= aValue; + } + catch ( const css::beans::UnknownPropertyException& ) + { + return false; + } + catch ( const css::lang::WrappedTargetException& ) + { + return false; + } + + return true; +} +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uifactory/menubarfactory.cxx b/framework/source/uifactory/menubarfactory.cxx new file mode 100644 index 0000000000..febc330668 --- /dev/null +++ b/framework/source/uifactory/menubarfactory.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 + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +using namespace com::sun::star::uno; +using namespace com::sun::star::lang; +using namespace com::sun::star::frame; +using namespace com::sun::star::beans; +using namespace com::sun::star::util; +using namespace ::com::sun::star::ui; + +namespace framework +{ + +MenuBarFactory::MenuBarFactory( css::uno::Reference< css::uno::XComponentContext > xContext ) + : m_xContext(std::move( xContext )) +{ +} + +MenuBarFactory::~MenuBarFactory() +{ +} + +// XUIElementFactory +Reference< XUIElement > SAL_CALL MenuBarFactory::createUIElement( + const OUString& ResourceURL, + const Sequence< PropertyValue >& Args ) +{ + Reference< css::ui::XUIElement > xMenuBar = new MenuBarWrapper(m_xContext); + CreateUIElement(ResourceURL, Args, u"private:resource/menubar/", xMenuBar, m_xContext); + return xMenuBar; +} + +void MenuBarFactory::CreateUIElement(const OUString& ResourceURL + ,const Sequence< PropertyValue >& Args + ,std::u16string_view ResourceType + ,const Reference< css::ui::XUIElement >& _xMenuBar + ,const css::uno::Reference< css::uno::XComponentContext >& _rxContext) +{ + sal_Int32 nConfigPropertyIndex( Args.getLength() ); + sal_Int32 nURLPropertyIndex( Args.getLength() ); + Reference< XUIConfigurationManager > xCfgMgr; + Reference< XFrame > xFrame; + OUString aResourceURL( ResourceURL ); + + for ( sal_Int32 n = 0; n < Args.getLength(); n++ ) + { + if ( Args[n].Name == "ConfigurationSource" ) + { + nConfigPropertyIndex = n; + Args[n].Value >>= xCfgMgr; + } + else if ( Args[n].Name == "ResourceURL" ) + { + nURLPropertyIndex = n; + Args[n].Value >>= aResourceURL; + } + else if ( Args[n].Name == "Frame" ) + Args[n].Value >>= xFrame; + } + if (!aResourceURL.startsWith(ResourceType)) + throw IllegalArgumentException(); + + // Identify frame and determine document based ui configuration manager/module ui configuration manager + if ( xFrame.is() && !xCfgMgr.is() ) + { + bool bHasSettings( false ); + Reference< XModel > xModel; + + Reference< XController > xController = xFrame->getController(); + if ( xController.is() ) + xModel = xController->getModel(); + + if ( xModel.is() ) + { + Reference< XUIConfigurationManagerSupplier > xUIConfigurationManagerSupplier( xModel, UNO_QUERY ); + if ( xUIConfigurationManagerSupplier.is() ) + { + xCfgMgr = xUIConfigurationManagerSupplier->getUIConfigurationManager(); + bHasSettings = xCfgMgr->hasSettings( aResourceURL ); + } + } + + if ( !bHasSettings ) + { + Reference< css::frame::XModuleManager2 > xModuleManager = + ModuleManager::create( _rxContext ); + OUString aModuleIdentifier = xModuleManager->identify( Reference( xFrame, UNO_QUERY ) ); + if ( !aModuleIdentifier.isEmpty() ) + { + Reference< XModuleUIConfigurationManagerSupplier > xModuleCfgSupplier = + theModuleUIConfigurationManagerSupplier::get( _rxContext ); + xCfgMgr = xModuleCfgSupplier->getUIConfigurationManager( aModuleIdentifier ); + } + } + } + + sal_Int32 nSeqLength( Args.getLength() ); + if ( Args.getLength() == nConfigPropertyIndex ) + nSeqLength++; + if ( Args.getLength() == nURLPropertyIndex ) + nSeqLength++; + if ( nConfigPropertyIndex == nURLPropertyIndex ) + nURLPropertyIndex++; + + Sequence< Any > aPropSeq( nSeqLength ); + auto aPropSeqRange = asNonConstRange(aPropSeq); + for ( sal_Int32 n = 0; n < aPropSeq.getLength(); n++ ) + { + PropertyValue aPropValue; + if ( n == nURLPropertyIndex ) + { + aPropValue.Name = "ResourceURL"; + aPropValue.Value <<= aResourceURL; + } + else if ( n == nConfigPropertyIndex ) + { + aPropValue.Name = "ConfigurationSource"; + aPropValue.Value <<= xCfgMgr; + } + else + aPropValue = Args[n]; + + aPropSeqRange[n] <<= aPropValue; + } + + SolarMutexGuard aGuard; + Reference< XInitialization > xInit( _xMenuBar, UNO_QUERY ); + xInit->initialize( aPropSeq ); +} + +} // namespace framework + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_framework_MenuBarFactory_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence const &) +{ + return cppu::acquire(new framework::MenuBarFactory(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uifactory/statusbarfactory.cxx b/framework/source/uifactory/statusbarfactory.cxx new file mode 100644 index 0000000000..2914dc4cc4 --- /dev/null +++ b/framework/source/uifactory/statusbarfactory.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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include + +#include + +using namespace com::sun::star::uno; +using namespace com::sun::star::lang; +using namespace com::sun::star::beans; +using namespace com::sun::star::util; +using namespace ::com::sun::star::ui; + +using namespace framework; + +namespace { + +class StatusBarFactory : public MenuBarFactory +{ +public: + explicit StatusBarFactory( const css::uno::Reference< css::uno::XComponentContext >& xContext ); + + virtual OUString SAL_CALL getImplementationName() override + { + return "com.sun.star.comp.framework.StatusBarFactory"; + } + + virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override + { + return cppu::supportsService(this, ServiceName); + } + + virtual css::uno::Sequence SAL_CALL getSupportedServiceNames() override + { + return {"com.sun.star.ui.StatusBarFactory"}; + } + + // XUIElementFactory + virtual css::uno::Reference< css::ui::XUIElement > SAL_CALL createUIElement( const OUString& ResourceURL, const css::uno::Sequence< css::beans::PropertyValue >& Args ) override; +}; + +StatusBarFactory::StatusBarFactory( const css::uno::Reference< css::uno::XComponentContext >& xContext ) : + MenuBarFactory( xContext ) +{ +} + +// XUIElementFactory +Reference< XUIElement > SAL_CALL StatusBarFactory::createUIElement( + const OUString& ResourceURL, + const Sequence< PropertyValue >& Args ) +{ + Reference< css::ui::XUIElement > xStatusBar = new StatusBarWrapper(m_xContext); + MenuBarFactory::CreateUIElement(ResourceURL, Args, u"private:resource/statusbar/", xStatusBar, m_xContext); + return xStatusBar; +} + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_framework_StatusBarFactory_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence const &) +{ + return cppu::acquire(new StatusBarFactory(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uifactory/toolbarfactory.cxx b/framework/source/uifactory/toolbarfactory.cxx new file mode 100644 index 0000000000..1f9bb61142 --- /dev/null +++ b/framework/source/uifactory/toolbarfactory.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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include + +using namespace com::sun::star::uno; +using namespace com::sun::star::lang; +using namespace com::sun::star::beans; +using namespace com::sun::star::util; +using namespace ::com::sun::star::ui; +using namespace framework; + +namespace { + +class ToolBarFactory : public MenuBarFactory +{ +public: + explicit ToolBarFactory( const css::uno::Reference< css::uno::XComponentContext >& xContext ); + + virtual OUString SAL_CALL getImplementationName() override + { + return "com.sun.star.comp.framework.ToolBarFactory"; + } + + virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override + { + return cppu::supportsService(this, ServiceName); + } + + virtual css::uno::Sequence SAL_CALL getSupportedServiceNames() override + { + return {"com.sun.star.ui.ToolBarFactory"}; + } + + // XUIElementFactory + virtual css::uno::Reference< css::ui::XUIElement > SAL_CALL createUIElement( + const OUString& ResourceURL, const css::uno::Sequence< css::beans::PropertyValue >& Args ) override; +}; + +ToolBarFactory::ToolBarFactory( const css::uno::Reference< css::uno::XComponentContext >& xContext ) : + MenuBarFactory( xContext ) +{ +} + +// XUIElementFactory +Reference< XUIElement > SAL_CALL ToolBarFactory::createUIElement( + const OUString& ResourceURL, + const Sequence< PropertyValue >& Args ) +{ + Reference< css::ui::XUIElement > xToolBar = new ToolBarWrapper(m_xContext); + CreateUIElement(ResourceURL, Args, u"private:resource/toolbar/", xToolBar, m_xContext); + return xToolBar; +} + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_framework_ToolBarFactory_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence const &) +{ + return cppu::acquire(new ToolBarFactory(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uifactory/uicontrollerfactory.cxx b/framework/source/uifactory/uicontrollerfactory.cxx new file mode 100644 index 0000000000..fa7c76d611 --- /dev/null +++ b/framework/source/uifactory/uicontrollerfactory.cxx @@ -0,0 +1,341 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include + +#include + +#include +#include +#include + +#include +#include +#include + +using namespace css::uno; +using namespace css::lang; +using namespace css::beans; +using namespace css::container; +using namespace css::frame; +using namespace framework; + +namespace { + +typedef comphelper::WeakComponentImplHelper< + css::lang::XServiceInfo, + css::frame::XUIControllerFactory > UIControllerFactory_BASE; + +class UIControllerFactory : public UIControllerFactory_BASE +{ +public: + virtual ~UIControllerFactory() override; + + // XMultiComponentFactory + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL createInstanceWithContext( const OUString& aServiceSpecifier, const css::uno::Reference< css::uno::XComponentContext >& Context ) override; + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL createInstanceWithArgumentsAndContext( const OUString& ServiceSpecifier, const css::uno::Sequence< css::uno::Any >& Arguments, const css::uno::Reference< css::uno::XComponentContext >& Context ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getAvailableServiceNames() override; + + // XUIControllerRegistration + virtual sal_Bool SAL_CALL hasController( const OUString& aCommandURL, const OUString& aModuleName ) override; + virtual void SAL_CALL registerController( const OUString& aCommandURL, const OUString& aModuleName, const OUString& aControllerImplementationName ) override; + virtual void SAL_CALL deregisterController( const OUString& aCommandURL, const OUString& aModuleName ) override; + +protected: + UIControllerFactory( const css::uno::Reference< css::uno::XComponentContext >& xContext, std::u16string_view rUINode ); + bool m_bConfigRead; + css::uno::Reference< css::uno::XComponentContext > m_xContext; + rtl::Reference m_pConfigAccess; + +private: + virtual void disposing(std::unique_lock&) final override; +}; + +UIControllerFactory::UIControllerFactory( + const Reference< XComponentContext >& xContext, + std::u16string_view rConfigurationNode ) + : m_bConfigRead( false ) + , m_xContext( xContext ) +{ + m_pConfigAccess = new ConfigurationAccess_ControllerFactory(m_xContext, + OUString::Concat("/org.openoffice.Office.UI.Controller/Registered/") + + rConfigurationNode); +} + +UIControllerFactory::~UIControllerFactory() +{ + std::unique_lock g(m_aMutex); + disposing(g); +} + +void UIControllerFactory::disposing(std::unique_lock&) +{ + m_pConfigAccess.clear(); +} + +// XMultiComponentFactory +Reference< XInterface > SAL_CALL UIControllerFactory::createInstanceWithContext( + const OUString& aServiceSpecifier, + const Reference< XComponentContext >& ) +{ + // SAFE + std::unique_lock g(m_aMutex); + + if ( !m_bConfigRead ) + { + m_bConfigRead = true; + m_pConfigAccess->readConfigurationData(); + } + + OUString aServiceName = m_pConfigAccess->getServiceFromCommandModule( aServiceSpecifier, std::u16string_view() ); + if ( !aServiceName.isEmpty() ) + return m_xContext->getServiceManager()->createInstanceWithContext( aServiceName, m_xContext ); + else + return Reference< XInterface >(); + // SAFE +} + +Reference< XInterface > SAL_CALL UIControllerFactory::createInstanceWithArgumentsAndContext( + const OUString& ServiceSpecifier, + const Sequence< Any >& Arguments, + const Reference< XComponentContext >& ) +{ + static constexpr OUStringLiteral aPropModuleName( u"ModuleIdentifier" ); + + OUString aPropName; + PropertyValue aPropValue; + + // Retrieve the optional module name from the Arguments sequence. It is used as a part of + // the hash map key to support different controller implementation for the same URL but different + // module!! + for ( Any const & arg : Arguments ) + { + if (( arg >>= aPropValue ) && ( aPropValue.Name == aPropModuleName )) + { + aPropValue.Value >>= aPropName; + break; + } + } + + Sequence< Any > aNewArgs( Arguments ); + + sal_Int32 nAppendIndex = aNewArgs.getLength(); + aNewArgs.realloc( aNewArgs.getLength() + 2 ); + auto pNewArgs = aNewArgs.getArray(); + + // Append the command URL to the Arguments sequence so that one controller can be + // used for more than one command URL. + aPropValue.Name = "CommandURL"; + aPropValue.Value <<= ServiceSpecifier; + pNewArgs[nAppendIndex] <<= aPropValue; + + // Append the optional value argument. It's an empty string if no additional info + // is provided to the controller. + OUString aValue = m_pConfigAccess->getValueFromCommandModule( ServiceSpecifier, aPropName ); + aPropValue.Name = "Value"; + aPropValue.Value <<= aValue; + pNewArgs[nAppendIndex+1] <<= aPropValue; + + { + OUString aServiceName; + { // SAFE + std::unique_lock g(m_aMutex); + + if ( !m_bConfigRead ) + { + m_bConfigRead = true; + m_pConfigAccess->readConfigurationData(); + } + + aServiceName = m_pConfigAccess->getServiceFromCommandModule( ServiceSpecifier, aPropName ); + } // SAFE + + if ( !aServiceName.isEmpty() ) + return m_xContext->getServiceManager()->createInstanceWithArgumentsAndContext( aServiceName, aNewArgs, m_xContext ); + else + return Reference< XInterface >(); + } +} + +Sequence< OUString > SAL_CALL UIControllerFactory::getAvailableServiceNames() +{ + return Sequence< OUString >(); +} + +// XUIControllerRegistration +sal_Bool SAL_CALL UIControllerFactory::hasController( + const OUString& aCommandURL, + const OUString& aModuleName ) +{ + std::unique_lock g(m_aMutex); + + if ( !m_bConfigRead ) + { + m_bConfigRead = true; + m_pConfigAccess->readConfigurationData(); + } + + return ( !m_pConfigAccess->getServiceFromCommandModule( aCommandURL, aModuleName ).isEmpty() ); +} + +void SAL_CALL UIControllerFactory::registerController( + const OUString& aCommandURL, + const OUString& aModuleName, + const OUString& aControllerImplementationName ) +{ + // SAFE + std::unique_lock g(m_aMutex); + + if ( !m_bConfigRead ) + { + m_bConfigRead = true; + m_pConfigAccess->readConfigurationData(); + } + + m_pConfigAccess->addServiceToCommandModule( aCommandURL, aModuleName, aControllerImplementationName ); + // SAFE +} + +void SAL_CALL UIControllerFactory::deregisterController( + const OUString& aCommandURL, + const OUString& aModuleName ) +{ + // SAFE + std::unique_lock g(m_aMutex); + + if ( !m_bConfigRead ) + { + m_bConfigRead = true; + m_pConfigAccess->readConfigurationData(); + } + + m_pConfigAccess->removeServiceFromCommandModule( aCommandURL, aModuleName ); + // SAFE +} + +class PopupMenuControllerFactory : public UIControllerFactory +{ +public: + explicit PopupMenuControllerFactory( const css::uno::Reference< css::uno::XComponentContext >& xContext ); + + virtual OUString SAL_CALL getImplementationName() override + { + return "com.sun.star.comp.framework.PopupMenuControllerFactory"; + } + + virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override + { + return cppu::supportsService(this, ServiceName); + } + + virtual css::uno::Sequence SAL_CALL getSupportedServiceNames() override + { + return {"com.sun.star.frame.PopupMenuControllerFactory"}; + } + +}; + +PopupMenuControllerFactory::PopupMenuControllerFactory( const Reference< XComponentContext >& xContext ) : + UIControllerFactory( xContext, u"PopupMenu" ) +{ +} + +class ToolbarControllerFactory : public UIControllerFactory +{ +public: + explicit ToolbarControllerFactory( const css::uno::Reference< css::uno::XComponentContext >& xContext ); + + virtual OUString SAL_CALL getImplementationName() override + { + return "com.sun.star.comp.framework.ToolBarControllerFactory"; + } + + virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override + { + return cppu::supportsService(this, ServiceName); + } + + virtual css::uno::Sequence SAL_CALL getSupportedServiceNames() override + { + return {"com.sun.star.frame.ToolbarControllerFactory"}; + } + +}; + +ToolbarControllerFactory::ToolbarControllerFactory( const Reference< XComponentContext >& xContext ) : + UIControllerFactory( xContext, u"ToolBar" ) +{ +} + +class StatusbarControllerFactory : public UIControllerFactory +{ +public: + explicit StatusbarControllerFactory( const css::uno::Reference< css::uno::XComponentContext >& xContext ); + + virtual OUString SAL_CALL getImplementationName() override + { + return "com.sun.star.comp.framework.StatusBarControllerFactory"; + } + + virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override + { + return cppu::supportsService(this, ServiceName); + } + + virtual css::uno::Sequence SAL_CALL getSupportedServiceNames() override + { + return {"com.sun.star.frame.StatusbarControllerFactory"}; + } + +}; + +StatusbarControllerFactory::StatusbarControllerFactory( const Reference< XComponentContext >& xContext ) : + UIControllerFactory( xContext, u"StatusBar" ) +{ +} + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_framework_PopupMenuControllerFactory_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence const &) +{ + return cppu::acquire(new PopupMenuControllerFactory(context)); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_framework_ToolBarControllerFactory_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence const &) +{ + return cppu::acquire(new ToolbarControllerFactory(context)); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_framework_StatusBarControllerFactory_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence const &) +{ + return cppu::acquire(new StatusbarControllerFactory(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uifactory/uielementfactorymanager.cxx b/framework/source/uifactory/uielementfactorymanager.cxx new file mode 100644 index 0000000000..b8a8ea46ed --- /dev/null +++ b/framework/source/uifactory/uielementfactorymanager.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 + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +using namespace com::sun::star::uno; +using namespace com::sun::star::lang; +using namespace com::sun::star::beans; +using namespace com::sun::star::frame; +using namespace com::sun::star::configuration; +using namespace com::sun::star::container; +using namespace ::com::sun::star::ui; +using namespace framework; + +namespace framework +{ + +// global function needed by both implementations +static OUString getHashKeyFromStrings( std::u16string_view aType, std::u16string_view aName, std::u16string_view aModuleName ) +{ + return OUString::Concat(aType) + "^" + aName + "^" + aModuleName; +} + +ConfigurationAccess_FactoryManager::ConfigurationAccess_FactoryManager( const Reference< XComponentContext >& rxContext, OUString _sRoot ) : + m_aPropType( "Type" ), + m_aPropName( "Name" ), + m_aPropModule( "Module" ), + m_aPropFactory( "FactoryImplementation" ), + m_sRoot(std::move(_sRoot)), + m_bConfigAccessInitialized( false ) +{ + m_xConfigProvider = theDefaultProvider::get( rxContext ); +} + +ConfigurationAccess_FactoryManager::~ConfigurationAccess_FactoryManager() +{ + // SAFE + std::unique_lock g(m_aMutex); + + Reference< XContainer > xContainer( m_xConfigAccess, UNO_QUERY ); + if ( xContainer.is() ) + xContainer->removeContainerListener(m_xConfigListener); +} + +OUString ConfigurationAccess_FactoryManager::getFactorySpecifierFromTypeNameModule( std::u16string_view rType, std::u16string_view rName, std::u16string_view rModule ) const +{ + // SAFE + std::unique_lock g(m_aMutex); + + FactoryManagerMap::const_iterator pIter = + m_aFactoryManagerMap.find( getHashKeyFromStrings( rType, rName, rModule )); + if ( pIter != m_aFactoryManagerMap.end() ) + return pIter->second; + else + { + pIter = m_aFactoryManagerMap.find( + getHashKeyFromStrings( rType, rName, std::u16string_view() )); + if ( pIter != m_aFactoryManagerMap.end() ) + return pIter->second; + else + { + // Support factories which uses a defined prefix before the ui name. + size_t nIndex = rName.find( '_' ); + if ( nIndex > 0 && nIndex != std::u16string_view::npos) + { + std::u16string_view aName = rName.substr( 0, nIndex+1 ); + pIter = m_aFactoryManagerMap.find( getHashKeyFromStrings( rType, aName, std::u16string_view() )); + if ( pIter != m_aFactoryManagerMap.end() ) + return pIter->second; + } + + pIter = m_aFactoryManagerMap.find( getHashKeyFromStrings( rType, std::u16string_view(), std::u16string_view() )); + if ( pIter != m_aFactoryManagerMap.end() ) + return pIter->second; + } + } + + return OUString(); +} + +void ConfigurationAccess_FactoryManager::addFactorySpecifierToTypeNameModule( std::u16string_view rType, std::u16string_view rName, std::u16string_view rModule, const OUString& rServiceSpecifier ) +{ + // SAFE + std::unique_lock g(m_aMutex); + + OUString aHashKey = getHashKeyFromStrings( rType, rName, rModule ); + + FactoryManagerMap::const_iterator pIter = m_aFactoryManagerMap.find( aHashKey ); + + if ( pIter != m_aFactoryManagerMap.end() ) + throw ElementExistException(); + m_aFactoryManagerMap.emplace( aHashKey, rServiceSpecifier ); +} + +void ConfigurationAccess_FactoryManager::removeFactorySpecifierFromTypeNameModule( std::u16string_view rType, std::u16string_view rName, std::u16string_view rModule ) +{ + // SAFE + std::unique_lock g(m_aMutex); + + OUString aHashKey = getHashKeyFromStrings( rType, rName, rModule ); + + FactoryManagerMap::const_iterator pIter = m_aFactoryManagerMap.find( aHashKey ); + + if ( pIter == m_aFactoryManagerMap.end() ) + throw NoSuchElementException(); + m_aFactoryManagerMap.erase( aHashKey ); +} + +Sequence< Sequence< PropertyValue > > ConfigurationAccess_FactoryManager::getFactoriesDescription() const +{ + // SAFE + std::unique_lock g(m_aMutex); + + Sequence< Sequence< PropertyValue > > aSeqSeq; + + sal_Int32 nIndex( 0 ); + for ( const auto& rEntry : m_aFactoryManagerMap ) + { + OUString aFactory = rEntry.first; + if ( !aFactory.isEmpty() ) + { + sal_Int32 nToken = 0; + + aSeqSeq.realloc( aSeqSeq.getLength() + 1 ); + Sequence< PropertyValue > aSeq{ comphelper::makePropertyValue( + m_aPropType, aFactory.getToken( 0, '^', nToken )) }; + if ( nToken > 0 ) + { + aSeq.realloc( 2 ); + aSeq.getArray()[1] + = comphelper::makePropertyValue(m_aPropName, + aFactory.getToken( 0, '^', nToken )); + if ( nToken > 0 ) + { + aSeq.realloc( 3 ); + aSeq.getArray()[2] + = comphelper::makePropertyValue(m_aPropModule, + aFactory.getToken( 0, '^', nToken )); + } + } + + aSeqSeq.getArray()[nIndex++] = aSeq; + } + } + + return aSeqSeq; +} + +// container.XContainerListener +void SAL_CALL ConfigurationAccess_FactoryManager::elementInserted( const ContainerEvent& aEvent ) +{ + OUString aType; + OUString aName; + OUString aModule; + OUString aService; + + // SAFE + std::unique_lock g(m_aMutex); + + if ( impl_getElementProps( aEvent.Element, aType, aName, aModule, aService )) + { + // Create hash key from type, name and module as they are together a primary key to + // the UNO service that implements a user interface factory. + OUString aHashKey( getHashKeyFromStrings( aType, aName, aModule )); + m_aFactoryManagerMap.emplace( aHashKey, aService ); + } +} + +void SAL_CALL ConfigurationAccess_FactoryManager::elementRemoved ( const ContainerEvent& aEvent ) +{ + OUString aType; + OUString aName; + OUString aModule; + OUString aService; + + // SAFE + std::unique_lock g(m_aMutex); + + if ( impl_getElementProps( aEvent.Element, aType, aName, aModule, aService )) + { + // Create hash key from command and model as they are together a primary key to + // the UNO service that implements the popup menu controller. + OUString aHashKey( getHashKeyFromStrings( aType, aName, aModule )); + m_aFactoryManagerMap.erase( aHashKey ); + } +} + +void SAL_CALL ConfigurationAccess_FactoryManager::elementReplaced( const ContainerEvent& aEvent ) +{ + OUString aType; + OUString aName; + OUString aModule; + OUString aService; + + // SAFE + std::unique_lock g(m_aMutex); + + if ( impl_getElementProps( aEvent.Element, aType, aName, aModule, aService )) + { + // Create hash key from command and model as they are together a primary key to + // the UNO service that implements the popup menu controller. + OUString aHashKey( getHashKeyFromStrings( aType, aName, aModule )); + m_aFactoryManagerMap.erase( aHashKey ); + m_aFactoryManagerMap.emplace( aHashKey, aService ); + } +} + +// lang.XEventListener +void SAL_CALL ConfigurationAccess_FactoryManager::disposing( const EventObject& ) +{ + // SAFE + // remove our reference to the config access + std::unique_lock g(m_aMutex); + m_xConfigAccess.clear(); +} + +void ConfigurationAccess_FactoryManager::readConfigurationData() +{ + // SAFE + std::unique_lock g(m_aMutex); + + if ( !m_bConfigAccessInitialized ) + { + Sequence aArgs(comphelper::InitAnyPropertySequence( + { + {"nodepath", Any(m_sRoot)} + })); + + try + { + m_xConfigAccess.set( m_xConfigProvider->createInstanceWithArguments( + "com.sun.star.configuration.ConfigurationAccess", aArgs ), UNO_QUERY ); + } + catch ( const WrappedTargetException& ) + { + } + + m_bConfigAccessInitialized = true; + } + + if ( !m_xConfigAccess.is() ) + return; + + const Sequence< OUString > aUIElementFactories = m_xConfigAccess->getElementNames(); + + OUString aType; + OUString aName; + OUString aModule; + OUString aService; + OUString aHashKey; + for ( OUString const & factoryName : aUIElementFactories ) + { + if ( impl_getElementProps( m_xConfigAccess->getByName( factoryName ), aType, aName, aModule, aService )) + { + // Create hash key from type, name and module as they are together a primary key to + // the UNO service that implements the user interface element factory. + aHashKey = getHashKeyFromStrings( aType, aName, aModule ); + m_aFactoryManagerMap.emplace( aHashKey, aService ); + } + } + + Reference< XContainer > xContainer( m_xConfigAccess, UNO_QUERY ); + if ( xContainer.is() ) + { + m_xConfigListener = new WeakContainerListener(this); + xContainer->addContainerListener(m_xConfigListener); + } +} + +bool ConfigurationAccess_FactoryManager::impl_getElementProps( const Any& aElement, OUString& rType, OUString& rName, OUString& rModule, OUString& rServiceSpecifier ) const +{ + Reference< XPropertySet > xPropertySet; + aElement >>= xPropertySet; + + if ( !xPropertySet.is() ) + return true; + + try + { + xPropertySet->getPropertyValue( m_aPropType ) >>= rType; + xPropertySet->getPropertyValue( m_aPropName ) >>= rName; + xPropertySet->getPropertyValue( m_aPropModule ) >>= rModule; + xPropertySet->getPropertyValue( m_aPropFactory ) >>= rServiceSpecifier; + } + catch ( const css::beans::UnknownPropertyException& ) + { + return false; + } + catch ( const css::lang::WrappedTargetException& ) + { + return false; + } + + return true; +} + +} // framework + +namespace { + +typedef comphelper::WeakComponentImplHelper< + css::lang::XServiceInfo, + css::ui::XUIElementFactoryManager> UIElementFactoryManager_BASE; + +class UIElementFactoryManager : public UIElementFactoryManager_BASE +{ + virtual void disposing(std::unique_lock&) override; +public: + explicit UIElementFactoryManager( const css::uno::Reference< css::uno::XComponentContext >& rxContext ); + + virtual OUString SAL_CALL getImplementationName() override + { + return "com.sun.star.comp.framework.UIElementFactoryManager"; + } + + virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override + { + return cppu::supportsService(this, ServiceName); + } + + virtual css::uno::Sequence SAL_CALL getSupportedServiceNames() override + { + return {"com.sun.star.ui.UIElementFactoryManager"}; + } + + // XUIElementFactory + virtual css::uno::Reference< css::ui::XUIElement > SAL_CALL createUIElement( const OUString& ResourceURL, const css::uno::Sequence< css::beans::PropertyValue >& Args ) override; + + // XUIElementFactoryRegistration + virtual css::uno::Sequence< css::uno::Sequence< css::beans::PropertyValue > > SAL_CALL getRegisteredFactories( ) override; + virtual css::uno::Reference< css::ui::XUIElementFactory > SAL_CALL getFactory( const OUString& ResourceURL, const OUString& ModuleIdentifier ) override; + virtual void SAL_CALL registerFactory( const OUString& aType, const OUString& aName, const OUString& aModuleIdentifier, const OUString& aFactoryImplementationName ) override; + virtual void SAL_CALL deregisterFactory( const OUString& aType, const OUString& aName, const OUString& aModuleIdentifier ) override; + +private: + bool m_bConfigRead; + css::uno::Reference< css::uno::XComponentContext > m_xContext; + rtl::Reference m_pConfigAccess; +}; + +UIElementFactoryManager::UIElementFactoryManager( const Reference< XComponentContext >& rxContext ) : + m_bConfigRead( false ), + m_xContext(rxContext), + m_pConfigAccess( + new ConfigurationAccess_FactoryManager( + rxContext, + "/org.openoffice.Office.UI.Factories/Registered/UIElementFactories")) +{} + +void UIElementFactoryManager::disposing(std::unique_lock&) +{ + m_pConfigAccess.clear(); +} + +// XUIElementFactory +Reference< XUIElement > SAL_CALL UIElementFactoryManager::createUIElement( + const OUString& ResourceURL, + const Sequence< PropertyValue >& Args ) +{ + Reference< XFrame > xFrame; + OUString aModuleId; + { // SAFE + std::unique_lock g(m_aMutex); + if (m_bDisposed) { + throw css::lang::DisposedException( + "disposed", static_cast(this)); + } + + if ( !m_bConfigRead ) + { + m_bConfigRead = true; + m_pConfigAccess->readConfigurationData(); + } + + // Retrieve the frame instance from the arguments to determine the module identifier. This must be provided + // to the search function. An empty module identifier is provided if the frame is missing or the module id cannot + // retrieve from it. + for ( auto const & arg : Args ) + { + if ( arg.Name == "Frame") + arg.Value >>= xFrame; + if (arg.Name == "Module") + arg.Value >>= aModuleId; + } + } // SAFE + + Reference< XModuleManager2 > xManager = ModuleManager::create( m_xContext ); + + // Determine the module identifier + try + { + if ( aModuleId.isEmpty() && xFrame.is() && xManager.is() ) + aModuleId = xManager->identify( Reference( xFrame, UNO_QUERY ) ); + + Reference< XUIElementFactory > xUIElementFactory = getFactory( ResourceURL, aModuleId ); + if ( xUIElementFactory.is() ) + return xUIElementFactory->createUIElement( ResourceURL, Args ); + } + catch ( const UnknownModuleException& ) + { + } + + throw NoSuchElementException(); +} + +// XUIElementFactoryRegistration +Sequence< Sequence< PropertyValue > > SAL_CALL UIElementFactoryManager::getRegisteredFactories() +{ + // SAFE + std::unique_lock g(m_aMutex); + if (m_bDisposed) { + throw css::lang::DisposedException( + "disposed", static_cast(this)); + } + + if ( !m_bConfigRead ) + { + m_bConfigRead = true; + m_pConfigAccess->readConfigurationData(); + } + + return m_pConfigAccess->getFactoriesDescription(); +} + +Reference< XUIElementFactory > SAL_CALL UIElementFactoryManager::getFactory( const OUString& aResourceURL, const OUString& aModuleId ) +{ + OUString aServiceSpecifier; + { // SAFE + std::unique_lock g(m_aMutex); + if (m_bDisposed) { + throw css::lang::DisposedException( + "disposed", static_cast(this)); + } + + if ( !m_bConfigRead ) + { + m_bConfigRead = true; + m_pConfigAccess->readConfigurationData(); + } + + OUString aType; + OUString aName; + RetrieveTypeNameFromResourceURL( aResourceURL, aType, aName ); + aServiceSpecifier = m_pConfigAccess->getFactorySpecifierFromTypeNameModule( aType, aName, aModuleId ); + } // SAFE + + if ( !aServiceSpecifier.isEmpty() ) try + { + Reference< XUIElementFactory > xFactory(m_xContext->getServiceManager()-> + createInstanceWithContext(aServiceSpecifier, m_xContext), UNO_QUERY); + SAL_WARN_IF(!xFactory.is(), "fwk.uielement", "could not create factory: " << aServiceSpecifier); + return xFactory; + } + catch ( const css::loader::CannotActivateFactoryException& ) + { + SAL_WARN("fwk.uielement", aServiceSpecifier << + " not available. This should happen only on mobile platforms."); + } + return Reference< XUIElementFactory >(); +} + +void SAL_CALL UIElementFactoryManager::registerFactory( const OUString& aType, const OUString& aName, const OUString& aModuleId, const OUString& aFactoryImplementationName ) +{ + // SAFE + std::unique_lock g(m_aMutex); + if (m_bDisposed) { + throw css::lang::DisposedException( + "disposed", static_cast(this)); + } + + if ( !m_bConfigRead ) + { + m_bConfigRead = true; + m_pConfigAccess->readConfigurationData(); + } + + m_pConfigAccess->addFactorySpecifierToTypeNameModule( aType, aName, aModuleId, aFactoryImplementationName ); + // SAFE +} + +void SAL_CALL UIElementFactoryManager::deregisterFactory( const OUString& aType, const OUString& aName, const OUString& aModuleId ) +{ + // SAFE + std::unique_lock g(m_aMutex); + if (m_bDisposed) { + throw css::lang::DisposedException( + "disposed", static_cast(this)); + } + + if ( !m_bConfigRead ) + { + m_bConfigRead = true; + m_pConfigAccess->readConfigurationData(); + } + + m_pConfigAccess->removeFactorySpecifierFromTypeNameModule( aType, aName, aModuleId ); + // SAFE +} + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_framework_UIElementFactoryManager_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence const &) +{ + return cppu::acquire(new UIElementFactoryManager(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/uifactory/windowcontentfactorymanager.cxx b/framework/source/uifactory/windowcontentfactorymanager.cxx new file mode 100644 index 0000000000..1918ae6fdd --- /dev/null +++ b/framework/source/uifactory/windowcontentfactorymanager.cxx @@ -0,0 +1,204 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +using namespace ::com::sun::star; +using namespace framework; + +namespace { + +typedef comphelper::WeakComponentImplHelper< + css::lang::XServiceInfo, + css::lang::XSingleComponentFactory > WindowContentFactoryManager_BASE; + +class WindowContentFactoryManager : public WindowContentFactoryManager_BASE +{ +public: + explicit WindowContentFactoryManager( css::uno::Reference< css::uno::XComponentContext> xContext ); + + virtual OUString SAL_CALL getImplementationName() override + { + return "com.sun.star.comp.framework.WindowContentFactoryManager"; + } + + virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override + { + return cppu::supportsService(this, ServiceName); + } + + virtual css::uno::Sequence SAL_CALL getSupportedServiceNames() override + { + return {"com.sun.star.ui.WindowContentFactoryManager"}; + } + + // XSingleComponentFactory + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL createInstanceWithContext( const css::uno::Reference< css::uno::XComponentContext >& Context ) override; + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL createInstanceWithArgumentsAndContext( const css::uno::Sequence< css::uno::Any >& Arguments, const css::uno::Reference< css::uno::XComponentContext >& Context ) override; + +private: + virtual void disposing(std::unique_lock&) override; + + css::uno::Reference< css::uno::XComponentContext > m_xContext; + bool m_bConfigRead; + rtl::Reference m_pConfigAccess; +}; + +WindowContentFactoryManager::WindowContentFactoryManager( uno::Reference< uno::XComponentContext > xContext ) : + m_xContext(std::move( xContext )), + m_bConfigRead( false ), + m_pConfigAccess( + new ConfigurationAccess_FactoryManager( + m_xContext, + "/org.openoffice.Office.UI.WindowContentFactories/Registered/ContentFactories")) +{} + +void WindowContentFactoryManager::disposing(std::unique_lock&) +{ + m_pConfigAccess.clear(); +} + +// XSingleComponentFactory +uno::Reference< uno::XInterface > SAL_CALL WindowContentFactoryManager::createInstanceWithContext( + const uno::Reference< uno::XComponentContext >& /*xContext*/ ) +{ + uno::Reference< uno::XInterface > xWindow; + return xWindow; +} + +uno::Reference< uno::XInterface > SAL_CALL WindowContentFactoryManager::createInstanceWithArgumentsAndContext( + const uno::Sequence< uno::Any >& Arguments, const uno::Reference< uno::XComponentContext >& Context ) +{ + uno::Reference< uno::XInterface > xWindow; + uno::Reference< frame::XFrame > xFrame; + OUString aResourceURL; + + for (auto const & arg : Arguments ) + { + beans::PropertyValue aPropValue; + if ( arg >>= aPropValue ) + { + if ( aPropValue.Name == "Frame" ) + aPropValue.Value >>= xFrame; + else if ( aPropValue.Name == "ResourceURL" ) + aPropValue.Value >>= aResourceURL; + } + } + + // Determine the module identifier + OUString aType; + OUString aName; + OUString aModuleId; + uno::Reference< frame::XModuleManager2 > xModuleManager = + frame::ModuleManager::create( m_xContext ); + try + { + if ( xFrame.is() && xModuleManager.is() ) + aModuleId = xModuleManager->identify( uno::Reference< uno::XInterface >( xFrame, uno::UNO_QUERY ) ); + } + catch ( const frame::UnknownModuleException& ) + { + } + + RetrieveTypeNameFromResourceURL( aResourceURL, aType, aName ); + if ( !aType.isEmpty() && + !aName.isEmpty() && + !aModuleId.isEmpty() ) + { + OUString aImplementationName; + uno::Reference< uno::XInterface > xHolder( static_cast(this), uno::UNO_QUERY ); + + // Determine the implementation name of the window content factory dependent on the + // module identifier, user interface element type and name + { // SAFE + std::unique_lock g(m_aMutex); + if (m_bDisposed) { + throw css::lang::DisposedException( + "disposed", static_cast(this)); + } + if ( !m_bConfigRead ) + { + m_bConfigRead = true; + m_pConfigAccess->readConfigurationData(); + } + aImplementationName = m_pConfigAccess->getFactorySpecifierFromTypeNameModule( aType, aName, aModuleId ); + } // SAFE + + if ( !aImplementationName.isEmpty() ) + { + uno::Reference< lang::XMultiServiceFactory > xServiceManager( Context->getServiceManager(), uno::UNO_QUERY ); + if ( xServiceManager.is() ) + { + uno::Reference< lang::XSingleComponentFactory > xFactory( + xServiceManager->createInstance( aImplementationName ), uno::UNO_QUERY ); + if ( xFactory.is() ) + { + // Be careful: We call external code. Therefore here we have to catch all exceptions + try + { + xWindow = xFactory->createInstanceWithArgumentsAndContext( Arguments, Context ); + } + catch ( uno::Exception& ) + { + DBG_UNHANDLED_EXCEPTION("fwk"); + } + } + } + } + } + + // UNSAFE + if ( !xWindow.is()) + { + // Fallback: Use internal factory code to create a toolkit dialog as a content window + xWindow = createInstanceWithContext(Context); + } + + return xWindow; +} + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_framework_WindowContentFactoryManager_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence const &) +{ + return cppu::acquire(new WindowContentFactoryManager(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/xml/acceleratorconfigurationreader.cxx b/framework/source/xml/acceleratorconfigurationreader.cxx new file mode 100644 index 0000000000..7cbb81de9b --- /dev/null +++ b/framework/source/xml/acceleratorconfigurationreader.cxx @@ -0,0 +1,258 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include + +#include +#include + +#include +#include +#include + +namespace framework{ + +/* Throws a SaxException in case a wrong formatted XML + structure was detected. + + This macro combined the given comment with a generic + way to find out the XML line (where the error occurred) + to format a suitable message. + + @param COMMENT + an ascii string, which describe the problem. + */ +#define THROW_PARSEEXCEPTION(COMMENT) \ + { \ + throw css::xml::sax::SAXException( \ + implts_getErrorLineString() + COMMENT, \ + static_cast< css::xml::sax::XDocumentHandler* >(this), \ + css::uno::Any()); \ + } + +AcceleratorConfigurationReader::AcceleratorConfigurationReader(AcceleratorCache& rContainer) + : m_rContainer (rContainer ) + , m_bInsideAcceleratorList(false ) + , m_bInsideAcceleratorItem(false ) +{ +} + +AcceleratorConfigurationReader::~AcceleratorConfigurationReader() +{ +} + +void SAL_CALL AcceleratorConfigurationReader::startDocument() +{ +} + +void SAL_CALL AcceleratorConfigurationReader::endDocument() +{ + // The xml file seems to be corrupted. + // Because we found no end-tags ... at least for + // one list or item. + if (m_bInsideAcceleratorList || m_bInsideAcceleratorItem) + { + THROW_PARSEEXCEPTION("No matching start or end element 'acceleratorlist' found!") + } +} + +void SAL_CALL AcceleratorConfigurationReader::startElement(const OUString& sElement , + const css::uno::Reference< css::xml::sax::XAttributeList >& xAttributeList) +{ + EXMLElement eElement = AcceleratorConfigurationReader::implst_classifyElement(sElement); + + // Note: We handle "accel:item" before "accel:acceleratorlist" to perform this operation. + // Because an item occurs very often ... a list should occur one times only! + if (eElement == E_ELEMENT_ITEM) + { + if (!m_bInsideAcceleratorList) + THROW_PARSEEXCEPTION("An element \"accel:item\" must be embedded into 'accel:acceleratorlist'.") + if (m_bInsideAcceleratorItem) + THROW_PARSEEXCEPTION("An element \"accel:item\" is not a container.") + m_bInsideAcceleratorItem = true; + + OUString sCommand; + css::awt::KeyEvent aEvent; + + sal_Int16 c = xAttributeList->getLength(); + sal_Int16 i = 0; + for (i=0; igetNameByIndex(i); + OUString sValue = xAttributeList->getValueByIndex(i); + EXMLAttribute eAttribute = AcceleratorConfigurationReader::implst_classifyAttribute(sAttribute); + switch(eAttribute) + { + case E_ATTRIBUTE_URL : + sCommand = sValue; + break; + + case E_ATTRIBUTE_KEYCODE : + aEvent.KeyCode = KeyMapping::get().mapIdentifierToCode(sValue); + break; + + case E_ATTRIBUTE_MOD_SHIFT : + aEvent.Modifiers |= css::awt::KeyModifier::SHIFT; + break; + + case E_ATTRIBUTE_MOD_MOD1 : + aEvent.Modifiers |= css::awt::KeyModifier::MOD1; + break; + + case E_ATTRIBUTE_MOD_MOD2 : + aEvent.Modifiers |= css::awt::KeyModifier::MOD2; + break; + + case E_ATTRIBUTE_MOD_MOD3 : + aEvent.Modifiers |= css::awt::KeyModifier::MOD3; + } + } + + // validate command and key event. + if ( + sCommand.isEmpty() || + (aEvent.KeyCode == 0 ) + ) + { + THROW_PARSEEXCEPTION("XML element does not describe a valid accelerator nor a valid command.") + } + + // register key event + command inside cache ... + // Check for already existing items there. + if (!m_rContainer.hasKey(aEvent)) + m_rContainer.setKeyCommandPair(aEvent, sCommand); + else + { + // Attention: It's not really a reason to throw an exception and kill the office, if the configuration contains + // multiple registrations for the same key :-) Show a warning ... and ignore the second item. + // THROW_PARSEEXCEPTION("Command is registered for the same key more than once.") + SAL_INFO("fwk", + "AcceleratorConfigurationReader::startElement(): Double registration detected. Command=\"" << + sCommand << + "\" KeyCode=" << + aEvent.KeyCode << + "Modifiers=" << + aEvent.Modifiers); + } + } + + if (eElement == E_ELEMENT_ACCELERATORLIST) + { + if (m_bInsideAcceleratorList) + THROW_PARSEEXCEPTION("An element \"accel:acceleratorlist\" cannot be used recursive.") + m_bInsideAcceleratorList = true; + return; + } +} + +void SAL_CALL AcceleratorConfigurationReader::endElement(const OUString& sElement) +{ + EXMLElement eElement = AcceleratorConfigurationReader::implst_classifyElement(sElement); + + // Note: We handle "accel:item" before "accel:acceleratorlist" to perform this operation. + // Because an item occurs very often ... a list should occur one times only! + if (eElement == E_ELEMENT_ITEM) + { + if (!m_bInsideAcceleratorItem) + THROW_PARSEEXCEPTION("Found end element 'accel:item', but no start element.") + m_bInsideAcceleratorItem = false; + } + + if (eElement == E_ELEMENT_ACCELERATORLIST) + { + if (!m_bInsideAcceleratorList) + THROW_PARSEEXCEPTION("Found end element 'accel:acceleratorlist', but no start element.") + m_bInsideAcceleratorList = false; + } +} + +void SAL_CALL AcceleratorConfigurationReader::characters(const OUString&) +{ +} + +void SAL_CALL AcceleratorConfigurationReader::ignorableWhitespace(const OUString&) +{ +} + +void SAL_CALL AcceleratorConfigurationReader::processingInstruction(const OUString& /*sTarget*/, + const OUString& /*sData*/ ) +{ +} + +void SAL_CALL AcceleratorConfigurationReader::setDocumentLocator(const css::uno::Reference< css::xml::sax::XLocator >& xLocator) +{ + m_xLocator = xLocator; +} + +AcceleratorConfigurationReader::EXMLElement AcceleratorConfigurationReader::implst_classifyElement(std::u16string_view sElement) +{ + AcceleratorConfigurationReader::EXMLElement eElement; + + if (sElement == u"http://openoffice.org/2001/accel^acceleratorlist") + eElement = E_ELEMENT_ACCELERATORLIST; + else if (sElement == u"http://openoffice.org/2001/accel^item") + eElement = E_ELEMENT_ITEM; + else + throw css::uno::RuntimeException( + "Unknown XML element detected!", + css::uno::Reference< css::xml::sax::XDocumentHandler >()); + + return eElement; +} + +AcceleratorConfigurationReader::EXMLAttribute AcceleratorConfigurationReader::implst_classifyAttribute(std::u16string_view sAttribute) +{ + AcceleratorConfigurationReader::EXMLAttribute eAttribute; + + if (sAttribute == u"http://openoffice.org/2001/accel^code") + eAttribute = E_ATTRIBUTE_KEYCODE; + else if (sAttribute == u"http://openoffice.org/2001/accel^shift") + eAttribute = E_ATTRIBUTE_MOD_SHIFT; + else if (sAttribute == u"http://openoffice.org/2001/accel^mod1") + eAttribute = E_ATTRIBUTE_MOD_MOD1; + else if (sAttribute == u"http://openoffice.org/2001/accel^mod2") + eAttribute = E_ATTRIBUTE_MOD_MOD2; + else if (sAttribute == u"http://openoffice.org/2001/accel^mod3") + eAttribute = E_ATTRIBUTE_MOD_MOD3; + else if (sAttribute == u"http://www.w3.org/1999/xlink^href") + eAttribute = E_ATTRIBUTE_URL; + else + throw css::uno::RuntimeException( + "Unknown XML attribute detected!", + css::uno::Reference< css::xml::sax::XDocumentHandler >()); + + return eAttribute; +} + +OUString AcceleratorConfigurationReader::implts_getErrorLineString() +{ + if (!m_xLocator.is()) + return "Error during parsing XML. (No further info available ...)"; + + return "Error during parsing XML in\nline = " + + OUString::number(m_xLocator->getLineNumber()) + + "\ncolumn = " + + OUString::number(m_xLocator->getColumnNumber()) + + "."; +} + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/xml/acceleratorconfigurationwriter.cxx b/framework/source/xml/acceleratorconfigurationwriter.cxx new file mode 100644 index 0000000000..645fd479c3 --- /dev/null +++ b/framework/source/xml/acceleratorconfigurationwriter.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 + +#include +#include +#include + +#include + +#include +#include + +#include +#include + +namespace framework{ + +AcceleratorConfigurationWriter::AcceleratorConfigurationWriter(const AcceleratorCache& rContainer, + css::uno::Reference< css::xml::sax::XDocumentHandler > xConfig ) + : m_xConfig (std::move(xConfig )) + , m_rContainer (rContainer ) +{ +} + +AcceleratorConfigurationWriter::~AcceleratorConfigurationWriter() +{ +} + +void AcceleratorConfigurationWriter::flush() +{ + css::uno::Reference< css::xml::sax::XExtendedDocumentHandler > xExtendedCFG(m_xConfig, css::uno::UNO_QUERY_THROW); + + // prepare attribute list + rtl::Reference<::comphelper::AttributeList> pAttribs = new ::comphelper::AttributeList; + + pAttribs->AddAttribute( + "xmlns:accel", + "http://openoffice.org/2001/accel"); + pAttribs->AddAttribute( + "xmlns:xlink", "http://www.w3.org/1999/xlink"); + + // generate xml + xExtendedCFG->startDocument(); + + xExtendedCFG->unknown( + ""); + xExtendedCFG->ignorableWhitespace(OUString()); + + xExtendedCFG->startElement(AL_ELEMENT_ACCELERATORLIST, pAttribs); + xExtendedCFG->ignorableWhitespace(OUString()); + + // TODO think about threadsafe using of cache + AcceleratorCache::TKeyList lKeys = m_rContainer.getAllKeys(); + for (auto const& lKey : lKeys) + { + const OUString& rCommand = m_rContainer.getCommandByKey(lKey); + impl_ts_writeKeyCommandPair(lKey, rCommand, xExtendedCFG); + } + + /* TODO write key-command list + for (auto const& writeAccelerator : m_aWriteAcceleratorList) + WriteAcceleratorItem(writeAccelerator); + */ + + xExtendedCFG->ignorableWhitespace(OUString()); + xExtendedCFG->endElement(AL_ELEMENT_ACCELERATORLIST); + xExtendedCFG->ignorableWhitespace(OUString()); + xExtendedCFG->endDocument(); +} + +void AcceleratorConfigurationWriter::impl_ts_writeKeyCommandPair(const css::awt::KeyEvent& aKey , + const OUString& sCommand, + const css::uno::Reference< css::xml::sax::XDocumentHandler >& xConfig ) +{ + rtl::Reference<::comphelper::AttributeList> pAttribs = new ::comphelper::AttributeList; + + OUString sKey = KeyMapping::get().mapCodeToIdentifier(aKey.KeyCode); + // TODO check if key is empty! + + pAttribs->AddAttribute("accel:code", sKey ); + pAttribs->AddAttribute("xlink:href", sCommand); + + if ((aKey.Modifiers & css::awt::KeyModifier::SHIFT) == css::awt::KeyModifier::SHIFT) + pAttribs->AddAttribute("accel:shift", "true"); + + if ((aKey.Modifiers & css::awt::KeyModifier::MOD1) == css::awt::KeyModifier::MOD1) + pAttribs->AddAttribute("accel:mod1", "true"); + + if ((aKey.Modifiers & css::awt::KeyModifier::MOD2) == css::awt::KeyModifier::MOD2) + pAttribs->AddAttribute("accel:mod2", "true"); + + if ((aKey.Modifiers & css::awt::KeyModifier::MOD3) == css::awt::KeyModifier::MOD3) + pAttribs->AddAttribute("accel:mod3", "true"); + + xConfig->ignorableWhitespace(OUString()); + xConfig->startElement(AL_ELEMENT_ITEM, pAttribs); + xConfig->ignorableWhitespace(OUString()); + xConfig->endElement(AL_ELEMENT_ITEM); + xConfig->ignorableWhitespace(OUString()); +} + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/xml/imagesconfiguration.cxx b/framework/source/xml/imagesconfiguration.cxx new file mode 100644 index 0000000000..6b4773c216 --- /dev/null +++ b/framework/source/xml/imagesconfiguration.cxx @@ -0,0 +1,104 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include + +#include +#include +#include +#include +#include + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::xml::sax; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::io; + +namespace framework +{ +bool ImagesConfiguration::LoadImages( + const css::uno::Reference& rxContext, + const css::uno::Reference& rInputStream, ImageItemDescriptorList& rItems) +{ + Reference xParser = Parser::create(rxContext); + + // connect stream to input stream to the parser + InputSource aInputSource; + + aInputSource.aInputStream = rInputStream; + + // create namespace filter and set document handler inside to support xml namespaces + Reference xDocHandler(new OReadImagesDocumentHandler(rItems)); + Reference xFilter(new SaxNamespaceFilter(xDocHandler)); + + // connect parser and filter + xParser->setDocumentHandler(xFilter); + + try + { + xParser->parseStream(aInputSource); + return true; + } + catch (const RuntimeException&) + { + return false; + } + catch (const SAXException&) + { + return false; + } + catch (const css::io::IOException&) + { + return false; + } +} + +bool ImagesConfiguration::StoreImages( + const css::uno::Reference& rxContext, + const css::uno::Reference& rOutputStream, + const ImageItemDescriptorList& rItems) +{ + Reference xWriter = Writer::create(rxContext); + xWriter->setOutputStream(rOutputStream); + + try + { + OWriteImagesDocumentHandler aWriteImagesDocumentHandler(rItems, xWriter); + aWriteImagesDocumentHandler.WriteImagesDocument(); + return true; + } + catch (const RuntimeException&) + { + return false; + } + catch (const SAXException&) + { + return false; + } + catch (const css::io::IOException&) + { + return false; + } +} +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/source/xml/imagesdocumenthandler.cxx b/framework/source/xml/imagesdocumenthandler.cxx new file mode 100644 index 0000000000..ff5799f5c7 --- /dev/null +++ b/framework/source/xml/imagesdocumenthandler.cxx @@ -0,0 +1,366 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include + +#include +#include + +#include + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::xml::sax; + +#define ELEMENT_IMAGECONTAINER "imagescontainer" +#define ELEMENT_IMAGES "images" +#define ELEMENT_ENTRY "entry" +#define ELEMENT_EXTERNALIMAGES "externalimages" +#define ELEMENT_EXTERNALENTRY "externalentry" + +constexpr OUString ELEMENT_NS_IMAGESCONTAINER = u"image:imagescontainer"_ustr; +constexpr OUString ELEMENT_NS_IMAGES = u"image:images"_ustr; +constexpr OUString ELEMENT_NS_ENTRY = u"image:entry"_ustr; + +#define ATTRIBUTE_HREF "href" +#define ATTRIBUTE_MASKCOLOR "maskcolor" +#define ATTRIBUTE_COMMAND "command" +#define ATTRIBUTE_BITMAPINDEX "bitmap-index" +#define ATTRIBUTE_MASKURL "maskurl" +#define ATTRIBUTE_MASKMODE "maskmode" +#define ATTRIBUTE_HIGHCONTRASTURL "highcontrasturl" +#define ATTRIBUTE_HIGHCONTRASTMASKURL "highcontrastmaskurl" + +constexpr OUStringLiteral ATTRIBUTE_XMLNS_IMAGE = u"xmlns:image"; +constexpr OUStringLiteral ATTRIBUTE_XMLNS_XLINK = u"xmlns:xlink"; + +constexpr OUStringLiteral ATTRIBUTE_XLINK_TYPE = u"xlink:type"; +constexpr OUStringLiteral ATTRIBUTE_XLINK_TYPE_VALUE = u"simple"; + +constexpr OUString XMLNS_IMAGE = u"http://openoffice.org/2001/image"_ustr; +constexpr OUString XMLNS_XLINK = u"http://www.w3.org/1999/xlink"_ustr; +constexpr OUStringLiteral XMLNS_IMAGE_PREFIX = u"image:"; + +constexpr OUStringLiteral XMLNS_FILTER_SEPARATOR = u"^"; + +constexpr OUStringLiteral IMAGES_DOCTYPE = u""; + +namespace framework +{ + +namespace { + +struct ImageXMLEntryProperty +{ + OReadImagesDocumentHandler::Image_XML_Namespace nNamespace; + char aEntryName[20]; +}; + +} + +ImageXMLEntryProperty const ImagesEntries[OReadImagesDocumentHandler::IMG_XML_ENTRY_COUNT] = +{ + { OReadImagesDocumentHandler::IMG_NS_IMAGE, ELEMENT_IMAGECONTAINER }, + { OReadImagesDocumentHandler::IMG_NS_IMAGE, ELEMENT_IMAGES }, + { OReadImagesDocumentHandler::IMG_NS_IMAGE, ELEMENT_ENTRY }, + { OReadImagesDocumentHandler::IMG_NS_IMAGE, ELEMENT_EXTERNALIMAGES }, + { OReadImagesDocumentHandler::IMG_NS_IMAGE, ELEMENT_EXTERNALENTRY }, + { OReadImagesDocumentHandler::IMG_NS_XLINK, ATTRIBUTE_HREF }, + { OReadImagesDocumentHandler::IMG_NS_IMAGE, ATTRIBUTE_MASKCOLOR }, + { OReadImagesDocumentHandler::IMG_NS_IMAGE, ATTRIBUTE_COMMAND }, + { OReadImagesDocumentHandler::IMG_NS_IMAGE, ATTRIBUTE_BITMAPINDEX }, + { OReadImagesDocumentHandler::IMG_NS_IMAGE, ATTRIBUTE_MASKURL }, + { OReadImagesDocumentHandler::IMG_NS_IMAGE, ATTRIBUTE_MASKMODE }, + { OReadImagesDocumentHandler::IMG_NS_IMAGE, ATTRIBUTE_HIGHCONTRASTURL }, + { OReadImagesDocumentHandler::IMG_NS_IMAGE, ATTRIBUTE_HIGHCONTRASTMASKURL } +}; + +OReadImagesDocumentHandler::OReadImagesDocumentHandler( ImageItemDescriptorList& rItems ) : + m_rImageList( rItems ) +{ + // create hash map to speed up lookup + for ( int i = 0; i < IMG_XML_ENTRY_COUNT; i++ ) + { + OUStringBuffer temp( 20 ); + + if ( ImagesEntries[i].nNamespace == IMG_NS_IMAGE ) + temp.append( XMLNS_IMAGE ); + else + temp.append( XMLNS_XLINK ); + + temp.append( XMLNS_FILTER_SEPARATOR ); + temp.appendAscii( ImagesEntries[i].aEntryName ); + m_aImageMap.emplace( temp.makeStringAndClear(), static_cast(i) ); + } + + // reset states + m_bImageContainerStartFound = false; + m_bImageContainerEndFound = false; + m_bImagesStartFound = false; +} + +OReadImagesDocumentHandler::~OReadImagesDocumentHandler() +{ +} + +// XDocumentHandler +void SAL_CALL OReadImagesDocumentHandler::startDocument() +{ +} + +void SAL_CALL OReadImagesDocumentHandler::endDocument() +{ + if (m_bImageContainerStartFound != m_bImageContainerEndFound) + { + OUString aErrorMessage = getErrorLineString() + "No matching start or end element 'image:imagecontainer' found!"; + throw SAXException( aErrorMessage, Reference< XInterface >(), Any() ); + } +} + +void SAL_CALL OReadImagesDocumentHandler::startElement( + const OUString& aName, const Reference< XAttributeList > &xAttribs ) +{ + ImageHashMap::const_iterator pImageEntry = m_aImageMap.find( aName ); + if ( pImageEntry == m_aImageMap.end() ) + return; + + switch ( pImageEntry->second ) + { + case IMG_ELEMENT_IMAGECONTAINER: + { + // image:imagecontainer element (container element for all further image elements) + if ( m_bImageContainerStartFound ) + { + OUString aErrorMessage = getErrorLineString() + "Element 'image:imagecontainer' cannot be embedded into 'image:imagecontainer'!"; + throw SAXException( aErrorMessage, Reference< XInterface >(), Any() ); + } + + m_bImageContainerStartFound = true; + } + break; + + case IMG_ELEMENT_IMAGES: + { + if ( !m_bImageContainerStartFound ) + { + OUString aErrorMessage = getErrorLineString() + "Element 'image:images' must be embedded into element 'image:imagecontainer'!"; + throw SAXException( aErrorMessage, Reference< XInterface >(), Any() ); + } + + if ( m_bImagesStartFound ) + { + OUString aErrorMessage = getErrorLineString() + "Element 'image:images' cannot be embedded into 'image:images'!"; + throw SAXException( aErrorMessage, Reference< XInterface >(), Any() ); + } + + m_bImagesStartFound = true; + } + break; + + case IMG_ELEMENT_ENTRY: + { + // Check that image:entry is embedded into image:images! + if ( !m_bImagesStartFound ) + { + OUString aErrorMessage = getErrorLineString() + "Element 'image:entry' must be embedded into element 'image:images'!"; + throw SAXException( aErrorMessage, Reference< XInterface >(), Any() ); + } + + // Create new image item descriptor + ImageItemDescriptor aItem; + + // Read attributes for this image definition + for ( sal_Int16 n = 0; n < xAttribs->getLength(); n++ ) + { + pImageEntry = m_aImageMap.find( xAttribs->getNameByIndex( n ) ); + if ( pImageEntry != m_aImageMap.end() ) + { + switch ( pImageEntry->second ) + { + case IMG_ATTRIBUTE_COMMAND: + { + aItem.aCommandURL = xAttribs->getValueByIndex( n ); + } + break; + + default: + break; + } + } + } + + // Check required attribute "command" + if ( aItem.aCommandURL.isEmpty() ) + { + OUString aErrorMessage = getErrorLineString() + "Required attribute 'image:command' must have a value!"; + throw SAXException( aErrorMessage, Reference< XInterface >(), Any() ); + } + + m_rImageList.push_back( aItem ); + } + break; + + default: + break; + } +} + +void SAL_CALL OReadImagesDocumentHandler::endElement(const OUString& aName) +{ + ImageHashMap::const_iterator pImageEntry = m_aImageMap.find( aName ); + if ( pImageEntry == m_aImageMap.end() ) + return; + + switch ( pImageEntry->second ) + { + case IMG_ELEMENT_IMAGECONTAINER: + { + m_bImageContainerEndFound = true; + } + break; + + case IMG_ELEMENT_IMAGES: + { + m_bImagesStartFound = false; + } + break; + + default: break; + } +} + +void SAL_CALL OReadImagesDocumentHandler::characters(const OUString&) +{ +} + +void SAL_CALL OReadImagesDocumentHandler::ignorableWhitespace(const OUString&) +{ +} + +void SAL_CALL OReadImagesDocumentHandler::processingInstruction( + const OUString& /*aTarget*/, const OUString& /*aData*/ ) +{ +} + +void SAL_CALL OReadImagesDocumentHandler::setDocumentLocator( + const Reference< XLocator > &xLocator) +{ + m_xLocator = xLocator; +} + +OUString OReadImagesDocumentHandler::getErrorLineString() +{ + if ( m_xLocator.is() ) + { + return "Line: " + + OUString::number(m_xLocator->getLineNumber()) + + " - "; + } + else + return OUString(); +} + +// OWriteImagesDocumentHandler + +OWriteImagesDocumentHandler::OWriteImagesDocumentHandler( + const ImageItemDescriptorList& rItems, + Reference< XDocumentHandler > const & rWriteDocumentHandler ) : + m_rImageItemList( rItems ), + m_xWriteDocumentHandler( rWriteDocumentHandler ) +{ + m_xEmptyList = new ::comphelper::AttributeList; + m_aXMLImageNS = XMLNS_IMAGE_PREFIX; + m_aAttributeXlinkType = ATTRIBUTE_XLINK_TYPE; + m_aAttributeValueSimple = ATTRIBUTE_XLINK_TYPE_VALUE; +} + +OWriteImagesDocumentHandler::~OWriteImagesDocumentHandler() +{ +} + +void OWriteImagesDocumentHandler::WriteImagesDocument() +{ + m_xWriteDocumentHandler->startDocument(); + + // write DOCTYPE line! + Reference< XExtendedDocumentHandler > xExtendedDocHandler( m_xWriteDocumentHandler, UNO_QUERY ); + if ( xExtendedDocHandler.is() ) + { + xExtendedDocHandler->unknown( IMAGES_DOCTYPE ); + m_xWriteDocumentHandler->ignorableWhitespace( OUString() ); + } + + rtl::Reference<::comphelper::AttributeList> pList = new ::comphelper::AttributeList; + + pList->AddAttribute( ATTRIBUTE_XMLNS_IMAGE, + XMLNS_IMAGE ); + + pList->AddAttribute( ATTRIBUTE_XMLNS_XLINK, + XMLNS_XLINK ); + + m_xWriteDocumentHandler->startElement( ELEMENT_NS_IMAGESCONTAINER, pList ); + m_xWriteDocumentHandler->ignorableWhitespace( OUString() ); + + WriteImageList( &m_rImageItemList ); + + m_xWriteDocumentHandler->ignorableWhitespace( OUString() ); + m_xWriteDocumentHandler->endElement( ELEMENT_NS_IMAGESCONTAINER ); + m_xWriteDocumentHandler->ignorableWhitespace( OUString() ); + m_xWriteDocumentHandler->endDocument(); +} + +// protected member functions + +void OWriteImagesDocumentHandler::WriteImageList( const ImageItemDescriptorList* pImageList ) +{ + rtl::Reference<::comphelper::AttributeList> pList = new ::comphelper::AttributeList; + + // save required attributes + pList->AddAttribute( m_aAttributeXlinkType, + m_aAttributeValueSimple ); + + m_xWriteDocumentHandler->startElement( ELEMENT_NS_IMAGES, pList ); + m_xWriteDocumentHandler->ignorableWhitespace( OUString() ); + + for (const ImageItemDescriptor & i : *pImageList) + WriteImage( &i ); + + m_xWriteDocumentHandler->endElement( ELEMENT_NS_IMAGES ); + m_xWriteDocumentHandler->ignorableWhitespace( OUString() ); +} + +void OWriteImagesDocumentHandler::WriteImage( const ImageItemDescriptor* pImage ) +{ + rtl::Reference<::comphelper::AttributeList> pList = new ::comphelper::AttributeList; + + pList->AddAttribute( m_aXMLImageNS + ATTRIBUTE_COMMAND, + pImage->aCommandURL ); + + m_xWriteDocumentHandler->startElement( ELEMENT_NS_ENTRY, pList ); + m_xWriteDocumentHandler->ignorableWhitespace( OUString() ); + + m_xWriteDocumentHandler->endElement( ELEMENT_NS_ENTRY ); + m_xWriteDocumentHandler->ignorableWhitespace( OUString() ); +} + +} // namespace framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/uiconfig/startmodule/menubar/menubar.xml b/framework/uiconfig/startmodule/menubar/menubar.xml new file mode 100644 index 0000000000..72f0354a79 --- /dev/null +++ b/framework/uiconfig/startmodule/menubar/menubar.xml @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/framework/util/fwk.component b/framework/util/fwk.component new file mode 100644 index 0000000000..b8fef41270 --- /dev/null +++ b/framework/util/fwk.component @@ -0,0 +1,325 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/framework/util/fwk.component.autorecovery b/framework/util/fwk.component.autorecovery new file mode 100644 index 0000000000..9f178cd0e6 --- /dev/null +++ b/framework/util/fwk.component.autorecovery @@ -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.framework.AutoRecovery -- cgit v1.2.3